From 08da4d847b6311c7a174394c3c95a682e7cb8082 Mon Sep 17 00:00:00 2001 From: Carlos Roca Date: Tue, 9 Jul 2024 13:36:22 +0200 Subject: [PATCH] [ADD] purchase_mrp_distribution: New module to distribute qty of move in diferent products This module aims to address the need to receive a generic product, but until the moment of reception, we do not know the quantity that we might receive of its specific products. Using kits was not suitable for us, because to account for a unit of the kit product, the quantity specified in the BoM must be received. Therefore, we opted to define a new type of BoM called distribution. In this way, upon receiving a unit of any product added to the BoM, it will be accounted for as a unit of the generic product and will be reflected in the svls as separate products, but they will be accounted for in the purchase line as part of the product. --- purchase_mrp_distribution/README.rst | 132 +++++ purchase_mrp_distribution/__init__.py | 2 + purchase_mrp_distribution/__manifest__.py | 20 + purchase_mrp_distribution/i18n/es.po | 189 +++++++ .../i18n/purchase_mrp_distribution.pot | 176 +++++++ purchase_mrp_distribution/models/__init__.py | 3 + purchase_mrp_distribution/models/mrp_bom.py | 13 + purchase_mrp_distribution/models/purchase.py | 39 ++ .../models/stock_move.py | 50 ++ .../readme/CONFIGURE.rst | 9 + .../readme/CONTRIBUTORS.rst | 4 + .../readme/DESCRIPTION.rst | 2 + purchase_mrp_distribution/readme/HISTORY.rst | 12 + purchase_mrp_distribution/readme/ROADMAP.rst | 5 + purchase_mrp_distribution/readme/USAGE.rst | 10 + .../security/ir.model.access.csv | 3 + .../static/description/index.html | 482 ++++++++++++++++++ purchase_mrp_distribution/tests/__init__.py | 1 + .../tests/test_purchase_mrp_distribution.py | 105 ++++ .../views/mrp_bom_views.xml | 23 + .../views/stock_picking_views.xml | 21 + purchase_mrp_distribution/wizards/__init__.py | 1 + .../wizards/stock_move_distribution_wiz.py | 98 ++++ .../stock_move_distribution_wiz_views.xml | 54 ++ .../odoo/addons/purchase_mrp_distribution | 1 + setup/purchase_mrp_distribution/setup.py | 6 + 26 files changed, 1461 insertions(+) create mode 100644 purchase_mrp_distribution/README.rst create mode 100644 purchase_mrp_distribution/__init__.py create mode 100644 purchase_mrp_distribution/__manifest__.py create mode 100644 purchase_mrp_distribution/i18n/es.po create mode 100644 purchase_mrp_distribution/i18n/purchase_mrp_distribution.pot create mode 100644 purchase_mrp_distribution/models/__init__.py create mode 100644 purchase_mrp_distribution/models/mrp_bom.py create mode 100644 purchase_mrp_distribution/models/purchase.py create mode 100644 purchase_mrp_distribution/models/stock_move.py create mode 100644 purchase_mrp_distribution/readme/CONFIGURE.rst create mode 100644 purchase_mrp_distribution/readme/CONTRIBUTORS.rst create mode 100644 purchase_mrp_distribution/readme/DESCRIPTION.rst create mode 100644 purchase_mrp_distribution/readme/HISTORY.rst create mode 100644 purchase_mrp_distribution/readme/ROADMAP.rst create mode 100644 purchase_mrp_distribution/readme/USAGE.rst create mode 100644 purchase_mrp_distribution/security/ir.model.access.csv create mode 100644 purchase_mrp_distribution/static/description/index.html create mode 100644 purchase_mrp_distribution/tests/__init__.py create mode 100644 purchase_mrp_distribution/tests/test_purchase_mrp_distribution.py create mode 100644 purchase_mrp_distribution/views/mrp_bom_views.xml create mode 100644 purchase_mrp_distribution/views/stock_picking_views.xml create mode 100644 purchase_mrp_distribution/wizards/__init__.py create mode 100644 purchase_mrp_distribution/wizards/stock_move_distribution_wiz.py create mode 100644 purchase_mrp_distribution/wizards/stock_move_distribution_wiz_views.xml create mode 120000 setup/purchase_mrp_distribution/odoo/addons/purchase_mrp_distribution create mode 100644 setup/purchase_mrp_distribution/setup.py diff --git a/purchase_mrp_distribution/README.rst b/purchase_mrp_distribution/README.rst new file mode 100644 index 0000000000..3bb3e645cb --- /dev/null +++ b/purchase_mrp_distribution/README.rst @@ -0,0 +1,132 @@ +========================= +Purchase MRP Distribution +========================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:51d9b4cab80068fcea738841c68a570388273d7cde664d9603cad40b83184c13 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github + :target: https://github.com/OCA/manufacture/tree/15.0/purchase_mrp_distribution + :alt: OCA/manufacture +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/manufacture-15-0/manufacture-15-0-purchase_mrp_distribution + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/manufacture&target_branch=15.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows you to buy a product that will be divided into others without +knowing the exact quantity of the others. That quantity will be filled in the picking. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To use this module, we will need to configure n different distribution-type Bills of +Materials (BoM) for n products. To do this, we will follow these steps: + +#. Go to Manufacturing > Products > Bills of Materials +#. Click on Create +#. Select the product to be distributed in the receptions +#. Select the BoM type "Distribution" +#. In the components, add the products that can be filled in the reception (it is not + necessary to mark the quantity as it will default to 0) + +Usage +===== + +To see the module's functionality: + +1. Create a new purchase order +2. Add the product for which the BoM has been created +3. Confirm the order +4. Go to the created delivery order +5. You will see a new button in the move that contains the product, click on it +6. A wizard will open to define the quantity of each product to distribute +7. By clicking on Distribute, the move lines will be added with the products from the + BoM + +Known issues / Roadmap +====================== + +#. The current module is not designed to handle multi-step receipts. It operates on a + single-step process, making it unsuitable for more complex inventory management + scenarios. +#. The current module is not designed to handle multiple lots for a product. +#. The current module is not designed to handle multiple packages for a product. + +Changelog +========= + +This module aims to address the need to receive a generic product, but until the +moment of reception, we do not know the quantity that we might receive of its +specific products. + +Using kits was not suitable for us, because to account for a unit of the kit product, +the quantity specified in the BoM must be received. Therefore, we opted to define a +new type of BoM called distribution. + +In this way, upon receiving a unit of any product added to the BoM, it will be +accounted for as a unit of the generic product and will be reflected in the svls as +separate products, but they will be accounted for in the purchase line as part of the +product. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Tecnaiva + +Contributors +~~~~~~~~~~~~ + +* `Tecnativa `_: + + * Pedro M. Baeza + * Carlos Roca + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/manufacture `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/purchase_mrp_distribution/__init__.py b/purchase_mrp_distribution/__init__.py new file mode 100644 index 0000000000..aee8895e7a --- /dev/null +++ b/purchase_mrp_distribution/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/purchase_mrp_distribution/__manifest__.py b/purchase_mrp_distribution/__manifest__.py new file mode 100644 index 0000000000..e18a27e80c --- /dev/null +++ b/purchase_mrp_distribution/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2024 Tecnatva - Carlos Roca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Purchase MRP Distribution", + "version": "15.0.1.0.0", + "author": "Tecnaiva, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/manufacture", + "category": "Manufacturing", + "depends": ["purchase_mrp"], + "data": [ + "security/ir.model.access.csv", + "views/mrp_bom_views.xml", + "views/stock_picking_views.xml", + "wizards/stock_move_distribution_wiz_views.xml", + ], + "license": "AGPL-3", + "installable": True, + "application": False, +} diff --git a/purchase_mrp_distribution/i18n/es.po b/purchase_mrp_distribution/i18n/es.po new file mode 100644 index 0000000000..3821e6f14d --- /dev/null +++ b/purchase_mrp_distribution/i18n/es.po @@ -0,0 +1,189 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_mrp_distribution +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-07-09 09:38+0000\n" +"PO-Revision-Date: 2024-07-09 11:50+0200\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.0.1\n" + +#. module: purchase_mrp_distribution +#: model_terms:ir.ui.view,arch_db:purchase_mrp_distribution.purchase_order_form +msgid "" +"A BoM of type distribution is used to select the quantities of components." +msgstr "" +"Una lista de materiales (BoM) de tipo distribución se utiliza para " +"seleccionar las cantidades de componentes." + +#. module: purchase_mrp_distribution +#: model_terms:ir.ui.view,arch_db:purchase_mrp_distribution.purchase_order_form +msgid "At a Stock Transfer." +msgstr "En una transferencia de stock." + +#. module: purchase_mrp_distribution +#: model:ir.model,name:purchase_mrp_distribution.model_mrp_bom +msgid "Bill of Material" +msgstr "Lista de material" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_mrp_bom__type +msgid "BoM Type" +msgstr "Tipo de LdM" + +#. module: purchase_mrp_distribution +#: model_terms:ir.ui.view,arch_db:purchase_mrp_distribution.stock_move_distribution_wiz_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__create_uid +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__create_date +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__demand_quantity +msgid "Demand" +msgstr "Demanda" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__display_name +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__display_name +msgid "Display Name" +msgstr "Nombre a mostrar" + +#. module: purchase_mrp_distribution +#: model_terms:ir.ui.view,arch_db:purchase_mrp_distribution.stock_move_distribution_wiz_form +msgid "Distribute" +msgstr "Distribuir" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields.selection,name:purchase_mrp_distribution.selection__mrp_bom__type__distribution +msgid "Distribution" +msgstr "Distribución" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move__distribution_bom_id +msgid "Distribution Bom" +msgstr "LdM de distribución" + +#. module: purchase_mrp_distribution +#: code:addons/purchase_mrp_distribution/models/stock_move.py:0 +#, python-format +msgid "Distribution for %s" +msgstr "Distribución para %s" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__id +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__id +msgid "ID" +msgstr "ID" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz____last_update +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__write_uid +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__write_date +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__line_ids +msgid "Line" +msgstr "Linea" + +#. module: purchase_mrp_distribution +#: model:ir.model,name:purchase_mrp_distribution.model_stock_move_distribution_wiz_line +msgid "Lines of wizard to distribute product qty to diferent products" +msgstr "" +"Líneas del asistente para distribuir la cantidad del producto a diferentes " +"productos" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__move_id +msgid "Move" +msgstr "Movimiento" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__product_id +msgid "Product" +msgstr "Producto" + +#. module: purchase_mrp_distribution +#: model:ir.model,name:purchase_mrp_distribution.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Línea de pedido de venta" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__quantity +msgid "Quantity" +msgstr "Cantidad" + +#. module: purchase_mrp_distribution +#: model:ir.model,name:purchase_mrp_distribution.model_stock_move +msgid "Stock Move" +msgstr "Movimiento de Stock" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,help:purchase_mrp_distribution.field_stock_move_distribution_wiz__demand_quantity +msgid "" +"This is the quantity of products from an inventory point of view. For moves " +"in the state 'done', this is the quantity of products that were actually " +"moved. For other moves, this is the quantity of product that is planned to " +"be moved. Lowering this quantity does not generate a backorder. Changing " +"this quantity on assigned moves affects the product reservation, and should " +"be done with care." +msgstr "" +"Ésta es la cantidad de productos desde un punto de vista de inventario. " +"Para movimientos en el estado 'Realizado', ésta es la cantidad de productos " +"que se movieron realmente. Para otros movimientos, ésta es la cantidad de " +"producto que está planeado mover. Disminuyendo esta cantidad no se genera " +"un pedido en espera. Cambiando esta cantidad en movimientos asignados, " +"afecta la reserva de producto, y debe ser realizado con cuidado." + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__move_uom_id +msgid "UoM" +msgstr "UdM" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__uom_id +msgid "Uom" +msgstr "UdM" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__wizard_id +msgid "Wizard" +msgstr "Asistente" + +#. module: purchase_mrp_distribution +#: model:ir.model,name:purchase_mrp_distribution.model_stock_move_distribution_wiz +msgid "Wizard to distribute product qty to diferent products" +msgstr "" +"Asistente para distribuir la cantidad de producto a diferentes productos" diff --git a/purchase_mrp_distribution/i18n/purchase_mrp_distribution.pot b/purchase_mrp_distribution/i18n/purchase_mrp_distribution.pot new file mode 100644 index 0000000000..52d0922f47 --- /dev/null +++ b/purchase_mrp_distribution/i18n/purchase_mrp_distribution.pot @@ -0,0 +1,176 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_mrp_distribution +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-07-09 09:38+0000\n" +"PO-Revision-Date: 2024-07-09 09:38+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: purchase_mrp_distribution +#: model_terms:ir.ui.view,arch_db:purchase_mrp_distribution.purchase_order_form +msgid "" +"A BoM of type distribution is used to select the quantities of components." +msgstr "" + +#. module: purchase_mrp_distribution +#: model_terms:ir.ui.view,arch_db:purchase_mrp_distribution.purchase_order_form +msgid "At a Stock Transfer." +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model,name:purchase_mrp_distribution.model_mrp_bom +msgid "Bill of Material" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_mrp_bom__type +msgid "BoM Type" +msgstr "" + +#. module: purchase_mrp_distribution +#: model_terms:ir.ui.view,arch_db:purchase_mrp_distribution.stock_move_distribution_wiz_form +msgid "Cancel" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__create_uid +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__create_uid +msgid "Created by" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__create_date +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__create_date +msgid "Created on" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__demand_quantity +msgid "Demand" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__display_name +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__display_name +msgid "Display Name" +msgstr "" + +#. module: purchase_mrp_distribution +#: model_terms:ir.ui.view,arch_db:purchase_mrp_distribution.stock_move_distribution_wiz_form +msgid "Distribute" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields.selection,name:purchase_mrp_distribution.selection__mrp_bom__type__distribution +msgid "Distribution" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move__distribution_bom_id +msgid "Distribution Bom" +msgstr "" + +#. module: purchase_mrp_distribution +#: code:addons/purchase_mrp_distribution/models/stock_move.py:0 +#, python-format +msgid "Distribution for %s" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__id +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__id +msgid "ID" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz____last_update +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line____last_update +msgid "Last Modified on" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__write_uid +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__write_date +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__write_date +msgid "Last Updated on" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__line_ids +msgid "Line" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model,name:purchase_mrp_distribution.model_stock_move_distribution_wiz_line +msgid "Lines of wizard to distribute product qty to diferent products" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__move_id +msgid "Move" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__product_id +msgid "Product" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model,name:purchase_mrp_distribution.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__quantity +msgid "Quantity" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model,name:purchase_mrp_distribution.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,help:purchase_mrp_distribution.field_stock_move_distribution_wiz__demand_quantity +msgid "" +"This is the quantity of products from an inventory point of view. For moves " +"in the state 'done', this is the quantity of products that were actually " +"moved. For other moves, this is the quantity of product that is planned to " +"be moved. Lowering this quantity does not generate a backorder. Changing " +"this quantity on assigned moves affects the product reservation, and should " +"be done with care." +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz__move_uom_id +msgid "UoM" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__uom_id +msgid "Uom" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model.fields,field_description:purchase_mrp_distribution.field_stock_move_distribution_wiz_line__wizard_id +msgid "Wizard" +msgstr "" + +#. module: purchase_mrp_distribution +#: model:ir.model,name:purchase_mrp_distribution.model_stock_move_distribution_wiz +msgid "Wizard to distribute product qty to diferent products" +msgstr "" diff --git a/purchase_mrp_distribution/models/__init__.py b/purchase_mrp_distribution/models/__init__.py new file mode 100644 index 0000000000..8ef1c3dafe --- /dev/null +++ b/purchase_mrp_distribution/models/__init__.py @@ -0,0 +1,3 @@ +from . import mrp_bom +from . import purchase +from . import stock_move diff --git a/purchase_mrp_distribution/models/mrp_bom.py b/purchase_mrp_distribution/models/mrp_bom.py new file mode 100644 index 0000000000..ba59cb9cf6 --- /dev/null +++ b/purchase_mrp_distribution/models/mrp_bom.py @@ -0,0 +1,13 @@ +# Copyright 2024 Tecnativa - Carlos Roca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class MrpBom(models.Model): + _inherit = "mrp.bom" + + type = fields.Selection( + selection_add=[("distribution", "Distribution")], + ondelete={"distribution": "set default"}, + ) diff --git a/purchase_mrp_distribution/models/purchase.py b/purchase_mrp_distribution/models/purchase.py new file mode 100644 index 0000000000..0bf396ade4 --- /dev/null +++ b/purchase_mrp_distribution/models/purchase.py @@ -0,0 +1,39 @@ +# Copyright 2024 Tecnativa - Carlos Roca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import models + + +class PurchaseOrderLine(models.Model): + _inherit = "purchase.order.line" + + def _prepare_stock_move_vals( + self, picking, price_unit, product_uom_qty, product_uom + ): + res = super()._prepare_stock_move_vals( + picking, price_unit, product_uom_qty, product_uom + ) + bom = ( + self.env["mrp.bom"] + .sudo() + ._bom_find( + self.product_id, company_id=self.company_id.id, bom_type="distribution" + )[self.product_id] + ) + if bom: + res["distribution_bom_id"] = bom.id + return res + + def _get_po_line_moves(self): + res = super()._get_po_line_moves() + bom = ( + self.env["mrp.bom"] + .sudo() + ._bom_find( + self.product_id, company_id=self.company_id.id, bom_type="distribution" + )[self.product_id] + ) + res |= self.move_ids.filtered( + lambda m: m.product_id in bom.bom_line_ids.product_id + ) + return res diff --git a/purchase_mrp_distribution/models/stock_move.py b/purchase_mrp_distribution/models/stock_move.py new file mode 100644 index 0000000000..0888ec0943 --- /dev/null +++ b/purchase_mrp_distribution/models/stock_move.py @@ -0,0 +1,50 @@ +# Copyright 2024 Tecnativa - Carlos Roca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import _, fields, models + + +class StockMove(models.Model): + _inherit = "stock.move" + + distribution_bom_id = fields.Many2one("mrp.bom") + + def action_open_distribution_wizard(self): + return { + "name": _("Distribution for %s") % self.product_id.display_name, + "type": "ir.actions.act_window", + "view_mode": "form", + "res_model": "stock.move.distribution.wiz", + "target": "new", + "context": {"active_model": "stock.move", "active_id": self.id}, + } + + def _action_done(self, cancel_backorder=False): + distribution_moves = self.filtered(lambda m: m.distribution_bom_id) + new_moves = self.env["stock.move"] + for move in distribution_moves: + cancel_move = False + for sml in move.move_line_ids.filtered( + lambda ml, move=move: ml.product_id != move.product_id + ): + # Create new moves with the corresponding product and the linked sml + new_moves += self.env["stock.move"].create( + { + "picking_id": sml.picking_id.id, + "product_id": sml.product_id.id, + "name": sml.product_id.display_name, + "purchase_line_id": move.purchase_line_id.id, + "product_uom": sml.product_uom_id.id, + "state": "assigned", + "location_id": sml.location_id.id, + "location_dest_id": sml.location_dest_id.id, + "price_unit": move.price_unit, + "move_line_ids": [(4, sml.id)], + } + ) + cancel_move = True + if cancel_move: + move.state = "cancel" + return super(StockMove, self + new_moves)._action_done( + cancel_backorder=cancel_backorder + ) diff --git a/purchase_mrp_distribution/readme/CONFIGURE.rst b/purchase_mrp_distribution/readme/CONFIGURE.rst new file mode 100644 index 0000000000..3079b442e1 --- /dev/null +++ b/purchase_mrp_distribution/readme/CONFIGURE.rst @@ -0,0 +1,9 @@ +To use this module, we will need to configure n different distribution-type Bills of +Materials (BoM) for n products. To do this, we will follow these steps: + +#. Go to Manufacturing > Products > Bills of Materials +#. Click on Create +#. Select the product to be distributed in the receptions +#. Select the BoM type "Distribution" +#. In the components, add the products that can be filled in the reception (it is not + necessary to mark the quantity as it will default to 0) diff --git a/purchase_mrp_distribution/readme/CONTRIBUTORS.rst b/purchase_mrp_distribution/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..42f94d59e8 --- /dev/null +++ b/purchase_mrp_distribution/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* `Tecnativa `_: + + * Pedro M. Baeza + * Carlos Roca diff --git a/purchase_mrp_distribution/readme/DESCRIPTION.rst b/purchase_mrp_distribution/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..aef6ff65ae --- /dev/null +++ b/purchase_mrp_distribution/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module allows you to buy a product that will be divided into others without +knowing the exact quantity of the others. That quantity will be filled in the picking. diff --git a/purchase_mrp_distribution/readme/HISTORY.rst b/purchase_mrp_distribution/readme/HISTORY.rst new file mode 100644 index 0000000000..5db4cc093c --- /dev/null +++ b/purchase_mrp_distribution/readme/HISTORY.rst @@ -0,0 +1,12 @@ +This module aims to address the need to receive a generic product, but until the +moment of reception, we do not know the quantity that we might receive of its +specific products. + +Using kits was not suitable for us, because to account for a unit of the kit product, +the quantity specified in the BoM must be received. Therefore, we opted to define a +new type of BoM called distribution. + +In this way, upon receiving a unit of any product added to the BoM, it will be +accounted for as a unit of the generic product and will be reflected in the svls as +separate products, but they will be accounted for in the purchase line as part of the +product. diff --git a/purchase_mrp_distribution/readme/ROADMAP.rst b/purchase_mrp_distribution/readme/ROADMAP.rst new file mode 100644 index 0000000000..40d31618b2 --- /dev/null +++ b/purchase_mrp_distribution/readme/ROADMAP.rst @@ -0,0 +1,5 @@ +#. The current module is not designed to handle multi-step receipts. It operates on a + single-step process, making it unsuitable for more complex inventory management + scenarios. +#. The current module is not designed to handle multiple lots for a product. +#. The current module is not designed to handle multiple packages for a product. diff --git a/purchase_mrp_distribution/readme/USAGE.rst b/purchase_mrp_distribution/readme/USAGE.rst new file mode 100644 index 0000000000..75de1ea2a5 --- /dev/null +++ b/purchase_mrp_distribution/readme/USAGE.rst @@ -0,0 +1,10 @@ +To see the module's functionality: + +1. Create a new purchase order +2. Add the product for which the BoM has been created +3. Confirm the order +4. Go to the created delivery order +5. You will see a new button in the move that contains the product, click on it +6. A wizard will open to define the quantity of each product to distribute +7. By clicking on Distribute, the move lines will be added with the products from the + BoM diff --git a/purchase_mrp_distribution/security/ir.model.access.csv b/purchase_mrp_distribution/security/ir.model.access.csv new file mode 100644 index 0000000000..5119cda001 --- /dev/null +++ b/purchase_mrp_distribution/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +purchase_mrp_distribution.access_stock_move_distribution_wiz,access_stock_move_distribution_wiz,purchase_mrp_distribution.model_stock_move_distribution_wiz,stock.group_stock_user,1,1,1,1 +purchase_mrp_distribution.access_stock_move_distribution_wiz_line,access_stock_move_distribution_wiz_line,purchase_mrp_distribution.model_stock_move_distribution_wiz_line,stock.group_stock_user,1,1,1,1 diff --git a/purchase_mrp_distribution/static/description/index.html b/purchase_mrp_distribution/static/description/index.html new file mode 100644 index 0000000000..f9a0553e85 --- /dev/null +++ b/purchase_mrp_distribution/static/description/index.html @@ -0,0 +1,482 @@ + + + + + +Purchase MRP Distribution + + + +
+

Purchase MRP Distribution

+ + +

Beta License: AGPL-3 OCA/manufacture Translate me on Weblate Try me on Runboat

+

This module allows you to buy a product that will be divided into others without +knowing the exact quantity of the others. That quantity will be filled in the picking.

+

Table of contents

+ +
+

Configuration

+

To use this module, we will need to configure n different distribution-type Bills of +Materials (BoM) for n products. To do this, we will follow these steps:

+
    +
  1. Go to Manufacturing > Products > Bills of Materials
  2. +
  3. Click on Create
  4. +
  5. Select the product to be distributed in the receptions
  6. +
  7. Select the BoM type “Distribution”
  8. +
  9. In the components, add the products that can be filled in the reception (it is not +necessary to mark the quantity as it will default to 0)
  10. +
+
+
+

Usage

+

To see the module’s functionality:

+
    +
  1. Create a new purchase order
  2. +
  3. Add the product for which the BoM has been created
  4. +
  5. Confirm the order
  6. +
  7. Go to the created delivery order
  8. +
  9. You will see a new button in the move that contains the product, click on it
  10. +
  11. A wizard will open to define the quantity of each product to distribute
  12. +
  13. By clicking on Distribute, the move lines will be added with the products from the +BoM
  14. +
+
+
+

Known issues / Roadmap

+
    +
  1. The current module is not designed to handle multi-step receipts. It operates on a +single-step process, making it unsuitable for more complex inventory management +scenarios.
  2. +
  3. The current module is not designed to handle multiple lots for a product.
  4. +
  5. The current module is not designed to handle multiple packages for a product.
  6. +
+
+
+

Changelog

+

This module aims to address the need to receive a generic product, but until the +moment of reception, we do not know the quantity that we might receive of its +specific products.

+

Using kits was not suitable for us, because to account for a unit of the kit product, +the quantity specified in the BoM must be received. Therefore, we opted to define a +new type of BoM called distribution.

+

In this way, upon receiving a unit of any product added to the BoM, it will be +accounted for as a unit of the generic product and will be reflected in the svls as +separate products, but they will be accounted for in the purchase line as part of the +product.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnaiva
  • +
+
+
+

Contributors

+
    +
  • Tecnativa:
      +
    • Pedro M. Baeza
    • +
    • Carlos Roca
    • +
    +
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/manufacture project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/purchase_mrp_distribution/tests/__init__.py b/purchase_mrp_distribution/tests/__init__.py new file mode 100644 index 0000000000..a914df78fe --- /dev/null +++ b/purchase_mrp_distribution/tests/__init__.py @@ -0,0 +1 @@ +from . import test_purchase_mrp_distribution diff --git a/purchase_mrp_distribution/tests/test_purchase_mrp_distribution.py b/purchase_mrp_distribution/tests/test_purchase_mrp_distribution.py new file mode 100644 index 0000000000..012a606115 --- /dev/null +++ b/purchase_mrp_distribution/tests/test_purchase_mrp_distribution.py @@ -0,0 +1,105 @@ +# Copyright 2024 Tecnativa - Carlos Roca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo.tests.common import Form, TransactionCase + + +class TestPurchaseMrpDistribution(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.category = cls.env["product.category"].create( + { + "name": "AVCO", + "property_cost_method": "average", + } + ) + cls.product = cls.env["product.template"].create( + { + "name": "General product", + "categ_id": cls.category.id, + } + ) + cls.subproduct_1 = cls.env["product.template"].create( + { + "name": "Product 1", + "categ_id": cls.category.id, + } + ) + cls.subproduct_2 = cls.env["product.template"].create( + { + "name": "Product 2", + "categ_id": cls.category.id, + } + ) + cls.bom = cls.env["mrp.bom"].create( + { + "product_tmpl_id": cls.product.id, + "type": "distribution", + "bom_line_ids": [ + ( + 0, + 0, + { + "product_id": cls.subproduct_1.product_variant_id.id, + }, + ), + ( + 0, + 0, + { + "product_id": cls.subproduct_2.product_variant_id.id, + }, + ), + ], + } + ) + cls.partner = cls.env["res.partner"].create( + { + "name": "Partner", + } + ) + + def _create_po_picking(self): + purchase_form = Form(self.env["purchase.order"]) + purchase_form.partner_id = self.partner + with purchase_form.order_line.new() as line_form: + line_form.product_id = self.product.product_variant_id + line_form.product_qty = 6 + line_form.price_unit = 2 + purchase = purchase_form.save() + purchase.button_confirm() + return purchase, purchase.picking_ids + + def test_distribute_process(self): + purchase, picking = self._create_po_picking() + wiz_action = picking.move_lines.action_open_distribution_wizard() + wiz = ( + self.env["stock.move.distribution.wiz"] + .with_context(**wiz_action["context"]) + .create({}) + ) + self.assertIn(self.subproduct_1.product_variant_id, wiz.line_ids.product_id) + self.assertIn(self.subproduct_2.product_variant_id, wiz.line_ids.product_id) + wiz.line_ids.quantity = 3 + wiz.button_distribute_qty() + self.assertEqual(len(picking.move_lines), 1) + self.assertEqual(len(picking.move_lines.move_line_ids), 2) + for sml in picking.move_lines.move_line_ids: + self.assertEqual(sml.qty_done, 3) + original_move = picking.move_lines + picking.button_validate() + self.assertEqual(len(picking.move_lines), 3) + self.assertEqual(original_move.state, "cancel") + self.assertIn( + self.subproduct_1.product_variant_id, picking.move_lines.product_id + ) + self.assertIn( + self.subproduct_2.product_variant_id, picking.move_lines.product_id + ) + self.assertEqual(purchase.order_line.qty_received, 6) + svl_action = picking.action_view_stock_valuation_layers() + svls = self.env["stock.valuation.layer"].search(svl_action["domain"]) + for svl in svls: + self.assertEqual(svl.quantity, 3) + self.assertEqual(svl.value, 6) diff --git a/purchase_mrp_distribution/views/mrp_bom_views.xml b/purchase_mrp_distribution/views/mrp_bom_views.xml new file mode 100644 index 0000000000..e2ae5681df --- /dev/null +++ b/purchase_mrp_distribution/views/mrp_bom_views.xml @@ -0,0 +1,23 @@ + + + + mrp.bom + + + +

+ A BoM of type distribution is used to select the quantities of components. +

    +
  • + At a Stock Transfer. +
  • +
+

+
+
+
+
diff --git a/purchase_mrp_distribution/views/stock_picking_views.xml b/purchase_mrp_distribution/views/stock_picking_views.xml new file mode 100644 index 0000000000..b563ef7c0d --- /dev/null +++ b/purchase_mrp_distribution/views/stock_picking_views.xml @@ -0,0 +1,21 @@ + + + + stock.picking + + + + +