diff --git a/.isort.cfg b/.isort.cfg index 98b216f74..6d359cb17 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -9,4 +9,4 @@ line_length=88 known_odoo=odoo known_odoo_addons=odoo.addons sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER -known_third_party=setuptools +known_third_party=dateutil,setuptools diff --git a/mrp_production_grouped_by_product/README.rst b/mrp_production_grouped_by_product/README.rst new file mode 100644 index 000000000..d4859f473 --- /dev/null +++ b/mrp_production_grouped_by_product/README.rst @@ -0,0 +1,147 @@ +============================= +Production Grouped By Product +============================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/13.0/mrp_production_grouped_by_product + :alt: OCA/manufacture +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/manufacture-13-0/manufacture-13-0-mrp_production_grouped_by_product + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/129/13.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +When you have several sales orders with make to order (MTO) products that +require to be manufactured, you end up with one manufacturing order for each of +these sales orders, which is very bad for the management. + +With this module, each time an MTO manufacturing order is required to be +created, it first checks that there's no other existing order not yet started +for the same product and bill of materials inside the specied time frame , and +if there's one, then the quantity of that order is increased instead of +creating a new one. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure the time frame for grouping manufacturing order: + +#. Go to *Inventory > Configuration > Warehouse Management > Operation Types* +#. Locate the manufacturing type you are using (default one is called + "Manufacturing"). +#. Open it and change these 2 values: + + * MO grouping max. hour (UTC): The maximum hour (between 0 and 23) for + considering new manufacturing orders inside the same interval period, and + thus being grouped on the same MO. IMPORTANT: The hour should be expressed + in UTC. + * MO grouping interval (days): The number of days for grouping together on + the same manufacturing order. + + Example: If you leave the default values 19 and 1, all the planned orders + between 19:00:01 of the previous day and 20:00:00 of the target date will + be grouped together. + +Known issues / Roadmap +====================== + +* Add a check in the product form for excluding it from being grouped. + +Changelog +========= + +13.0.1.0.0 (2020-01-09) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [MIG] Migration to v13. + +12.0.1.0.0 (2019-04-17) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [MIG] Migration to v12: + +11.0.2.0.1 (2018-07-02) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [FIX] fix test in mrp_production_grouped_by_product + +11.0.2.0.0 (2018-06-04) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [IMP] mrp_production_grouped_by_product: Time frames + +11.0.1.0.1 (2018-05-11) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [IMP] mrp_production_grouped_by_company: Context evaluation on mrp.production + tests + +11.0.1.0.0 (2018-05-11) +~~~~~~~~~~~~~~~~~~~~~~~ + +* Start of the history. + +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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Tecnativa + +Contributors +~~~~~~~~~~~~ + +* Tecnativa __ + + * David Vidal + * Pedro M. Baeza + +* `Ecosoft `__: + + * Pimolnat Suntian + +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/mrp_production_grouped_by_product/__init__.py b/mrp_production_grouped_by_product/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/mrp_production_grouped_by_product/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/mrp_production_grouped_by_product/__manifest__.py b/mrp_production_grouped_by_product/__manifest__.py new file mode 100644 index 000000000..bfb6ba0e9 --- /dev/null +++ b/mrp_production_grouped_by_product/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2018 Tecnativa - David Vidal +# Copyright 2018 Tecnativa - Pedro M. Baeza +# Copyright 2019 Rubén Bravo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Production Grouped By Product", + "version": "13.0.1.0.0", + "category": "MRP", + "author": "Tecnativa, " "Odoo Community Association (OCA)", + "website": "https://github.com/oca/manufacture", + "license": "AGPL-3", + "depends": ["mrp"], + "data": ["views/stock_picking_type_views.xml"], + "installable": True, +} diff --git a/mrp_production_grouped_by_product/i18n/de.po b/mrp_production_grouped_by_product/i18n/de.po new file mode 100644 index 000000000..0e7acb69f --- /dev/null +++ b/mrp_production_grouped_by_product/i18n/de.po @@ -0,0 +1,81 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_production_grouped_by_product +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2018-12-09 10:43+0000\n" +"Last-Translator: Maria Sparenberg \n" +"Language-Team: none\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.3\n" + +#. module: mrp_production_grouped_by_product +#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_interval +msgid "MO grouping interval (days)" +msgstr "Gruppierungsintervall für Fertigungsaufträge (Tage)" + +#. module: mrp_production_grouped_by_product +#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_max_hour +msgid "MO grouping max. hour (UTC)" +msgstr "Späteste Uhrzeit (UTC) für die Gruppierung von Fertigungsaufträgen" + +#. module: mrp_production_grouped_by_product +#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_picking_type +msgid "Picking Type" +msgstr "" + +#. module: mrp_production_grouped_by_product +#: model:ir.model,name:mrp_production_grouped_by_product.model_mrp_production +#, fuzzy +#| msgid "Manufacturing Order" +msgid "Production Order" +msgstr "Fertigungsauftrag" + +#. module: mrp_production_grouped_by_product +#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_rule +msgid "Stock Rule" +msgstr "" + +#. module: mrp_production_grouped_by_product +#: model:ir.model.fields,help:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_max_hour +msgid "" +"The maximum hour (between 0 and 23) for considering new manufacturing orders " +"inside the same interval period, and thus being grouped on the same MO. " +"IMPORTANT: The hour should be expressed in UTC." +msgstr "" +"Dies ist die späteste Uhrzeit (zwischen 0 und 23) zur Berücksichtigung von " +"neuen Fertigungsaufträgen innerhalb desselben Intervals, die dabei zum " +"selben Fertigungsauftrag gruppiert werden. WICHTIG: Die Uhrzeit muss in UTC " +"angegeben werden." + +#. module: mrp_production_grouped_by_product +#: model:ir.model.fields,help:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_interval +msgid "" +"The number of days for grouping together on the same manufacturing order." +msgstr "" +"Dies ist die Anzahl von Tagen zur Gruppierung in demselben Fertigungsauftrag." + +#. module: mrp_production_grouped_by_product +#: code:addons/mrp_production_grouped_by_product/models/stock_picking_type.py:36 +#, python-format +msgid "You have to enter a positive value for interval." +msgstr "Sie müssen für das Intervall einen Wert größer 0 eingeben." + +#. module: mrp_production_grouped_by_product +#: code:addons/mrp_production_grouped_by_product/models/stock_picking_type.py:29 +#, python-format +msgid "You have to enter a valid hour between 0 and 23." +msgstr "Sie müssen eine gültige Uhrzeit zwischen 0 und 23 eingeben." + +#~ msgid "Procurement Rule" +#~ msgstr "Beschaffungsregel" + +#~ msgid "The operation type determines the picking view" +#~ msgstr "Der Vorgangstyp legt die Pick-Ansicht fest" diff --git a/mrp_production_grouped_by_product/i18n/es.po b/mrp_production_grouped_by_product/i18n/es.po new file mode 100644 index 000000000..97f059b24 --- /dev/null +++ b/mrp_production_grouped_by_product/i18n/es.po @@ -0,0 +1,79 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_production_grouped_by_product +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-06-03 23:16+0000\n" +"PO-Revision-Date: 2018-06-03 23:16+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: mrp_production_grouped_by_product +#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_interval +msgid "MO grouping interval (days)" +msgstr "Intervalo de agrupación de OFs (días)" + +#. module: mrp_production_grouped_by_product +#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_max_hour +msgid "MO grouping max. hour (UTC)" +msgstr "Hora máx. agrupación OFs (UTC)" + +#. module: mrp_production_grouped_by_product +#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_picking_type +msgid "Picking Type" +msgstr "" + +#. module: mrp_production_grouped_by_product +#: model:ir.model,name:mrp_production_grouped_by_product.model_mrp_production +#, fuzzy +#| msgid "Manufacturing Order" +msgid "Production Order" +msgstr "Orden de fabricación" + +#. module: mrp_production_grouped_by_product +#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_rule +msgid "Stock Rule" +msgstr "" + +#. module: mrp_production_grouped_by_product +#: model:ir.model.fields,help:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_max_hour +msgid "" +"The maximum hour (between 0 and 23) for considering new manufacturing orders " +"inside the same interval period, and thus being grouped on the same MO. " +"IMPORTANT: The hour should be expressed in UTC." +msgstr "" +"La hora máxima (entre 0 y 23) para considerar nuevas órdenes de fabricación " +"dentro del mismo periodo de tiempo, y por tanto siendo agrupadas dentro de " +"la misma OF. IMPORTANTE: La hora debe expresarse en UTC." + +#. module: mrp_production_grouped_by_product +#: model:ir.model.fields,help:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_interval +msgid "" +"The number of days for grouping together on the same manufacturing order." +msgstr "El número de días para agrupar juntas las órdenes de fabricación." + +#. module: mrp_production_grouped_by_product +#: code:addons/mrp_production_grouped_by_product/models/stock_picking_type.py:36 +#, python-format +msgid "You have to enter a positive value for interval." +msgstr "Debe introducir un valor positivo para el intervalo." + +#. module: mrp_production_grouped_by_product +#: code:addons/mrp_production_grouped_by_product/models/stock_picking_type.py:29 +#, python-format +msgid "You have to enter a valid hour between 0 and 23." +msgstr "Debe introducir una hora válida entre 0 y 23." + +#~ msgid "Procurement Rule" +#~ msgstr "Regla de abastecimiento" + +#~ msgid "The operation type determines the picking view" +#~ msgstr "El tipo de operación determina la vista de la operación" diff --git a/mrp_production_grouped_by_product/i18n/mrp_production_grouped_by_product.pot b/mrp_production_grouped_by_product/i18n/mrp_production_grouped_by_product.pot new file mode 100644 index 000000000..5a3cae00e --- /dev/null +++ b/mrp_production_grouped_by_product/i18n/mrp_production_grouped_by_product.pot @@ -0,0 +1,62 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_production_grouped_by_product +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \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: mrp_production_grouped_by_product +#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_interval +msgid "MO grouping interval (days)" +msgstr "" + +#. module: mrp_production_grouped_by_product +#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_max_hour +msgid "MO grouping max. hour (UTC)" +msgstr "" + +#. module: mrp_production_grouped_by_product +#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_picking_type +msgid "Picking Type" +msgstr "" + +#. module: mrp_production_grouped_by_product +#: model:ir.model,name:mrp_production_grouped_by_product.model_mrp_production +msgid "Production Order" +msgstr "" + +#. module: mrp_production_grouped_by_product +#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_rule +msgid "Stock Rule" +msgstr "" + +#. module: mrp_production_grouped_by_product +#: model:ir.model.fields,help:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_max_hour +msgid "The maximum hour (between 0 and 23) for considering new manufacturing orders inside the same interval period, and thus being grouped on the same MO. IMPORTANT: The hour should be expressed in UTC." +msgstr "" + +#. module: mrp_production_grouped_by_product +#: model:ir.model.fields,help:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_interval +msgid "The number of days for grouping together on the same manufacturing order." +msgstr "" + +#. module: mrp_production_grouped_by_product +#: code:addons/mrp_production_grouped_by_product/models/stock_picking_type.py:36 +#, python-format +msgid "You have to enter a positive value for interval." +msgstr "" + +#. module: mrp_production_grouped_by_product +#: code:addons/mrp_production_grouped_by_product/models/stock_picking_type.py:29 +#, python-format +msgid "You have to enter a valid hour between 0 and 23." +msgstr "" + diff --git a/mrp_production_grouped_by_product/models/__init__.py b/mrp_production_grouped_by_product/models/__init__.py new file mode 100644 index 000000000..7624a2753 --- /dev/null +++ b/mrp_production_grouped_by_product/models/__init__.py @@ -0,0 +1,3 @@ +from . import mrp_production +from . import stock_rule +from . import stock_picking_type diff --git a/mrp_production_grouped_by_product/models/mrp_production.py b/mrp_production_grouped_by_product/models/mrp_production.py new file mode 100644 index 000000000..fab3f4f95 --- /dev/null +++ b/mrp_production_grouped_by_product/models/mrp_production.py @@ -0,0 +1,88 @@ +# Copyright 2018 Tecnativa - David Vidal +# Copyright 2018 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models +from odoo.tools import config + + +class MrpProduction(models.Model): + _inherit = "mrp.production" + + def _post_mo_merging_adjustments(self, vals): + """Called when a new MO is merged onto existing one for adjusting the + needed values according this merging. + + :param self: Single record of the target record where merging. + :param vals: Dictionary with the new record values. + """ + self.ensure_one() + new_vals = {"origin": (self.origin or "") + ",%s" % vals["origin"]} + if vals.get("move_dest_ids"): + new_vals["move_dest_ids"] = vals["move_dest_ids"] + self.move_finished_ids.move_dest_ids = vals["move_dest_ids"] + self.write(new_vals) + + def _get_grouping_target_domain(self, vals): + """Get the domain for searching manufacturing orders that can match + with the criteria we want to use. + + :param vals: Values dictionary of the MO to be created. + + :return: Odoo domain. + """ + domain = [ + ("product_id", "=", vals["product_id"]), + ("picking_type_id", "=", vals["picking_type_id"]), + ("bom_id", "=", vals.get("bom_id", False)), + ("routing_id", "=", vals.get("routing_id", False)), + ("company_id", "=", vals.get("company_id", False)), + ("state", "=", "draft"), + ] + if not vals.get("date_planned_finished"): + return domain + date = fields.Datetime.from_string(vals["date_planned_finished"]) + pt = self.env["stock.picking.type"].browse(vals["picking_type_id"]) + if date.hour < pt.mo_grouping_max_hour: + date_end = date.replace(hour=pt.mo_grouping_max_hour, minute=0, second=0) + else: + date_end = date.replace( + day=date.day + 1, hour=pt.mo_grouping_max_hour, minute=0, second=0 + ) + date_start = date_end - relativedelta(days=pt.mo_grouping_interval) + domain += [ + ("date_planned_finished", ">", fields.Datetime.to_string(date_start)), + ("date_planned_finished", "<=", fields.Datetime.to_string(date_end)), + ] + return domain + + def _find_grouping_target(self, vals): + """Return the matching order for grouping. + + :param vals: Values dictionary of the MO to be created. + + :return: Target manufacturing order record (or empty record). + """ + return self.env["mrp.production"].search( + self._get_grouping_target_domain(vals), limit=1 + ) + + @api.model + def create(self, vals): + context = self.env.context + if context.get("group_mo_by_product") and ( + not config["test_enable"] or context.get("test_group_mo") + ): + mo = self._find_grouping_target(vals) + if mo: + self.env["change.production.qty"].create( + { + "mo_id": mo.id, + "product_qty": mo.product_qty + vals["product_qty"], + } + ).change_prod_qty() + mo._post_mo_merging_adjustments(vals) + return mo + return super(MrpProduction, self).create(vals) diff --git a/mrp_production_grouped_by_product/models/stock_picking_type.py b/mrp_production_grouped_by_product/models/stock_picking_type.py new file mode 100644 index 000000000..80a5acb41 --- /dev/null +++ b/mrp_production_grouped_by_product/models/stock_picking_type.py @@ -0,0 +1,37 @@ +# Copyright 2018 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, exceptions, fields, models + + +class StockPickingType(models.Model): + _inherit = "stock.picking.type" + + mo_grouping_max_hour = fields.Integer( + string="MO grouping max. hour (UTC)", + help="The maximum hour (between 0 and 23) for considering new " + "manufacturing orders inside the same interval period, and thus " + "being grouped on the same MO. IMPORTANT: The hour should be " + "expressed in UTC.", + default=19, + ) + mo_grouping_interval = fields.Integer( + string="MO grouping interval (days)", + help="The number of days for grouping together on the same " + "manufacturing order.", + default=1, + ) + + @api.constrains("mo_grouping_max_hour") + def _check_mo_grouping_max_hour(self): + if self.mo_grouping_max_hour < 0 or self.mo_grouping_max_hour > 23: + raise exceptions.ValidationError( + _("You have to enter a valid hour between 0 and 23.") + ) + + @api.constrains("mo_grouping_interval") + def _check_mo_grouping_interval(self): + if self.mo_grouping_interval < 0: + raise exceptions.ValidationError( + _("You have to enter a positive value for interval.") + ) diff --git a/mrp_production_grouped_by_product/models/stock_rule.py b/mrp_production_grouped_by_product/models/stock_rule.py new file mode 100644 index 000000000..1c1eb06b2 --- /dev/null +++ b/mrp_production_grouped_by_product/models/stock_rule.py @@ -0,0 +1,14 @@ +# Copyright 2018 Tecnativa - David Vidal +# Copyright 2018 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class StockRule(models.Model): + _inherit = "stock.rule" + + def _run_manufacture(self, procurements): + return super( + StockRule, self.with_context(group_mo_by_product=True) + )._run_manufacture(procurements) diff --git a/mrp_production_grouped_by_product/readme/CONFIGURE.rst b/mrp_production_grouped_by_product/readme/CONFIGURE.rst new file mode 100644 index 000000000..163bf6c3b --- /dev/null +++ b/mrp_production_grouped_by_product/readme/CONFIGURE.rst @@ -0,0 +1,17 @@ +To configure the time frame for grouping manufacturing order: + +#. Go to *Inventory > Configuration > Warehouse Management > Operation Types* +#. Locate the manufacturing type you are using (default one is called + "Manufacturing"). +#. Open it and change these 2 values: + + * MO grouping max. hour (UTC): The maximum hour (between 0 and 23) for + considering new manufacturing orders inside the same interval period, and + thus being grouped on the same MO. IMPORTANT: The hour should be expressed + in UTC. + * MO grouping interval (days): The number of days for grouping together on + the same manufacturing order. + + Example: If you leave the default values 19 and 1, all the planned orders + between 19:00:01 of the previous day and 20:00:00 of the target date will + be grouped together. diff --git a/mrp_production_grouped_by_product/readme/CONTRIBUTORS.rst b/mrp_production_grouped_by_product/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..60cb3d685 --- /dev/null +++ b/mrp_production_grouped_by_product/readme/CONTRIBUTORS.rst @@ -0,0 +1,8 @@ +* Tecnativa __ + + * David Vidal + * Pedro M. Baeza + +* `Ecosoft `__: + + * Pimolnat Suntian diff --git a/mrp_production_grouped_by_product/readme/DESCRIPTION.rst b/mrp_production_grouped_by_product/readme/DESCRIPTION.rst new file mode 100644 index 000000000..50962a392 --- /dev/null +++ b/mrp_production_grouped_by_product/readme/DESCRIPTION.rst @@ -0,0 +1,9 @@ +When you have several sales orders with make to order (MTO) products that +require to be manufactured, you end up with one manufacturing order for each of +these sales orders, which is very bad for the management. + +With this module, each time an MTO manufacturing order is required to be +created, it first checks that there's no other existing order not yet started +for the same product and bill of materials inside the specied time frame , and +if there's one, then the quantity of that order is increased instead of +creating a new one. diff --git a/mrp_production_grouped_by_product/readme/HISTORY.rst b/mrp_production_grouped_by_product/readme/HISTORY.rst new file mode 100644 index 000000000..f31a6b9ad --- /dev/null +++ b/mrp_production_grouped_by_product/readme/HISTORY.rst @@ -0,0 +1,29 @@ +13.0.1.0.0 (2020-01-09) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [MIG] Migration to v13. + +12.0.1.0.0 (2019-04-17) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [MIG] Migration to v12: + +11.0.2.0.1 (2018-07-02) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [FIX] fix test in mrp_production_grouped_by_product + +11.0.2.0.0 (2018-06-04) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [IMP] mrp_production_grouped_by_product: Time frames + +11.0.1.0.1 (2018-05-11) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [IMP] mrp_production_grouped_by_company: Context evaluation on mrp.production + tests + +11.0.1.0.0 (2018-05-11) +~~~~~~~~~~~~~~~~~~~~~~~ + +* Start of the history. diff --git a/mrp_production_grouped_by_product/readme/ROADMAP.rst b/mrp_production_grouped_by_product/readme/ROADMAP.rst new file mode 100644 index 000000000..7845179c4 --- /dev/null +++ b/mrp_production_grouped_by_product/readme/ROADMAP.rst @@ -0,0 +1 @@ +* Add a check in the product form for excluding it from being grouped. diff --git a/mrp_production_grouped_by_product/static/description/icon.png b/mrp_production_grouped_by_product/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/mrp_production_grouped_by_product/static/description/icon.png differ diff --git a/mrp_production_grouped_by_product/static/description/index.html b/mrp_production_grouped_by_product/static/description/index.html new file mode 100644 index 000000000..68bfb69cd --- /dev/null +++ b/mrp_production_grouped_by_product/static/description/index.html @@ -0,0 +1,514 @@ + + + + + + +Production Grouped By Product + + + +
+

Production Grouped By Product

+ + +

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

+

When you have several sales orders with make to order (MTO) products that +require to be manufactured, you end up with one manufacturing order for each of +these sales orders, which is very bad for the management.

+

With this module, each time an MTO manufacturing order is required to be +created, it first checks that there’s no other existing order not yet started +for the same product and bill of materials inside the specied time frame , and +if there’s one, then the quantity of that order is increased instead of +creating a new one.

+

Table of contents

+ +
+

Configuration

+

To configure the time frame for grouping manufacturing order:

+
    +
  1. Go to Inventory > Configuration > Warehouse Management > Operation Types

    +
  2. +
  3. Locate the manufacturing type you are using (default one is called +“Manufacturing”).

    +
  4. +
  5. Open it and change these 2 values:

    +
      +
    • MO grouping max. hour (UTC): The maximum hour (between 0 and 23) for +considering new manufacturing orders inside the same interval period, and +thus being grouped on the same MO. IMPORTANT: The hour should be expressed +in UTC.
    • +
    • MO grouping interval (days): The number of days for grouping together on +the same manufacturing order.
    • +
    +

    Example: If you leave the default values 19 and 1, all the planned orders +between 19:00:01 of the previous day and 20:00:00 of the target date will +be grouped together.

    +
  6. +
+
+
+

Known issues / Roadmap

+
    +
  • Add a check in the product form for excluding it from being grouped.
  • +
+
+
+

Changelog

+
+

13.0.1.0.0 (2020-01-09)

+
    +
  • [MIG] Migration to v13.
  • +
+
+
+

12.0.1.0.0 (2019-04-17)

+
    +
  • [MIG] Migration to v12:
  • +
+
+
+

11.0.2.0.1 (2018-07-02)

+
    +
  • [FIX] fix test in mrp_production_grouped_by_product
  • +
+
+
+

11.0.2.0.0 (2018-06-04)

+
    +
  • [IMP] mrp_production_grouped_by_product: Time frames
  • +
+
+
+

11.0.1.0.1 (2018-05-11)

+
    +
  • [IMP] mrp_production_grouped_by_company: Context evaluation on mrp.production + tests
  • +
+
+
+

11.0.1.0.0 (2018-05-11)

+
    +
  • Start of the history.
  • +
+
+
+
+

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 smashing it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+
+

Contributors

+ +
+
+

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/mrp_production_grouped_by_product/tests/__init__.py b/mrp_production_grouped_by_product/tests/__init__.py new file mode 100644 index 000000000..45f33fc96 --- /dev/null +++ b/mrp_production_grouped_by_product/tests/__init__.py @@ -0,0 +1 @@ +from . import test_mrp_production_grouped_by_product diff --git a/mrp_production_grouped_by_product/tests/test_mrp_production_grouped_by_product.py b/mrp_production_grouped_by_product/tests/test_mrp_production_grouped_by_product.py new file mode 100644 index 000000000..124fa9445 --- /dev/null +++ b/mrp_production_grouped_by_product/tests/test_mrp_production_grouped_by_product.py @@ -0,0 +1,117 @@ +# Copyright 2018 Tecnativa - David Vidal +# Copyright 2018 Tecnativa - Pedro M. Baeza +# Copyright 2019 Rubén Bravo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import exceptions +from odoo.tests import common + + +class TestProductionGroupedByProduct(common.SavepointCase): + at_install = False + post_install = True + + @classmethod + def setUpClass(cls): + super(TestProductionGroupedByProduct, cls).setUpClass() + cls.ProcurementGroup = cls.env["procurement.group"] + cls.MrpProduction = cls.env["mrp.production"] + cls.env.user.company_id.manufacturing_lead = 0 + cls.env.user.tz = False # Make sure there's no timezone in user + + cls.picking_type = cls.env["stock.picking.type"].search( + [ + ("code", "=", "mrp_operation"), + ("sequence_id.company_id", "=", cls.env.user.company_id.id), + ], + limit=1, + ) + cls.product1 = cls.env["product.product"].create( + { + "name": "TEST Muffin", + "route_ids": [ + (6, 0, [cls.env.ref("mrp.route_warehouse0_manufacture").id]) + ], + "type": "product", + "produce_delay": 0, + } + ) + cls.product2 = cls.env["product.product"].create( + {"name": "TEST Paper muffin cup", "type": "product"} + ) + cls.product3 = cls.env["product.product"].create( + {"name": "TEST Muffin paset", "type": "product"} + ) + cls.bom = cls.env["mrp.bom"].create( + { + "product_id": cls.product1.id, + "product_tmpl_id": cls.product1.product_tmpl_id.id, + "type": "normal", + "bom_line_ids": [ + (0, 0, {"product_id": cls.product2.id, "product_qty": 1}), + (0, 0, {"product_id": cls.product3.id, "product_qty": 0.2}), + ], + } + ) + cls.stock_picking_type = cls.env.ref("stock.picking_type_out") + cls.mo = cls.MrpProduction.create( + { + "bom_id": cls.bom.id, + "product_id": cls.product1.id, + "product_qty": 2, + "product_uom_id": cls.product1.uom_id.id, + "date_planned_finished": "2018-06-01 15:00:00", + "date_planned_start": "2018-06-01 15:00:00", + } + ) + cls.warehouse = cls.env["stock.warehouse"].search( + [("company_id", "=", cls.env.user.company_id.id)], limit=1 + ) + # Add an MTO move + cls.move = cls.env["stock.move"].create( + { + "name": cls.product1.name, + "product_id": cls.product1.id, + "product_uom_qty": 10, + "product_uom": cls.product1.uom_id.id, + "location_id": cls.warehouse.lot_stock_id.id, + "location_dest_id": (cls.env.ref("stock.stock_location_customers").id), + "procure_method": "make_to_order", + "warehouse_id": cls.warehouse.id, + "date": "2018-06-01 18:00:00", + "date_expected": "2018-06-01 18:00:00", + } + ) + + def test_mo_by_product(self): + self.move.with_context(test_group_mo=True)._action_confirm(merge=False) + self.ProcurementGroup.with_context(test_group_mo=True).run_scheduler() + mo = self.MrpProduction.search([("product_id", "=", self.product1.id)]) + self.assertEqual(len(mo), 1) + self.assertEqual(mo.product_qty, 12) + # Run again the scheduler to see if quantities are altered + self.ProcurementGroup.with_context(test_group_mo=True).run_scheduler() + mo = self.MrpProduction.search([("product_id", "=", self.product1.id)]) + self.assertEqual(len(mo), 1) + self.assertEqual(mo.product_qty, 12) + + def test_mo_other_date(self): + self.move.write( + {"date_expected": "2018-06-01 20:01:00", "date": "2018-06-01 20:01:00"} + ) + self.move.with_context(test_group_mo=True)._action_confirm(merge=False) + self.ProcurementGroup.with_context(test_group_mo=True).run_scheduler() + mo = self.MrpProduction.search([("product_id", "=", self.product1.id)]) + self.assertEqual(len(mo), 2) + + def test_check_mo_grouping_max_hour(self): + if self.picking_type: + with self.assertRaises(exceptions.ValidationError): + self.picking_type.mo_grouping_max_hour = 25 + with self.assertRaises(exceptions.ValidationError): + self.picking_type.mo_grouping_max_hour = -1 + + def test_check_mo_grouping_interval(self): + if self.picking_type: + with self.assertRaises(exceptions.ValidationError): + self.picking_type.mo_grouping_interval = -1 diff --git a/mrp_production_grouped_by_product/views/stock_picking_type_views.xml b/mrp_production_grouped_by_product/views/stock_picking_type_views.xml new file mode 100644 index 000000000..2b24b05e5 --- /dev/null +++ b/mrp_production_grouped_by_product/views/stock_picking_type_views.xml @@ -0,0 +1,17 @@ + + + + + + stock.picking.type + + + + + + + + + +