From f1e4bfe52c38eed5bd354725d8f51a53eaa45d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Wed, 7 Sep 2022 10:43:54 +0200 Subject: [PATCH 01/28] [ADD] mrp_lot_number_propagation --- mrp_lot_number_propagation/README.rst | 1 + mrp_lot_number_propagation/__init__.py | 1 + mrp_lot_number_propagation/__manifest__.py | 20 +++ mrp_lot_number_propagation/models/__init__.py | 4 + mrp_lot_number_propagation/models/mrp_bom.py | 74 +++++++++ .../models/mrp_bom_line.py | 62 +++++++ .../models/mrp_production.py | 152 ++++++++++++++++++ .../models/stock_move.py | 13 ++ .../readme/CONTRIBUTORS.rst | 2 + .../readme/DESCRIPTION.rst | 1 + mrp_lot_number_propagation/readme/ROADMAP.rst | 1 + mrp_lot_number_propagation/tests/__init__.py | 1 + mrp_lot_number_propagation/tests/common.py | 92 +++++++++++ .../tests/test_mrp_bom.py | 36 +++++ .../tests/test_mrp_production.py | 44 +++++ mrp_lot_number_propagation/views/mrp_bom.xml | 28 ++++ .../views/mrp_production.xml | 25 +++ 17 files changed, 557 insertions(+) create mode 100644 mrp_lot_number_propagation/README.rst create mode 100644 mrp_lot_number_propagation/__init__.py create mode 100644 mrp_lot_number_propagation/__manifest__.py create mode 100644 mrp_lot_number_propagation/models/__init__.py create mode 100644 mrp_lot_number_propagation/models/mrp_bom.py create mode 100644 mrp_lot_number_propagation/models/mrp_bom_line.py create mode 100644 mrp_lot_number_propagation/models/mrp_production.py create mode 100644 mrp_lot_number_propagation/models/stock_move.py create mode 100644 mrp_lot_number_propagation/readme/CONTRIBUTORS.rst create mode 100644 mrp_lot_number_propagation/readme/DESCRIPTION.rst create mode 100644 mrp_lot_number_propagation/readme/ROADMAP.rst create mode 100644 mrp_lot_number_propagation/tests/__init__.py create mode 100644 mrp_lot_number_propagation/tests/common.py create mode 100644 mrp_lot_number_propagation/tests/test_mrp_bom.py create mode 100644 mrp_lot_number_propagation/tests/test_mrp_production.py create mode 100644 mrp_lot_number_propagation/views/mrp_bom.xml create mode 100644 mrp_lot_number_propagation/views/mrp_production.xml diff --git a/mrp_lot_number_propagation/README.rst b/mrp_lot_number_propagation/README.rst new file mode 100644 index 000000000..9c558e357 --- /dev/null +++ b/mrp_lot_number_propagation/README.rst @@ -0,0 +1 @@ +. diff --git a/mrp_lot_number_propagation/__init__.py b/mrp_lot_number_propagation/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/mrp_lot_number_propagation/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/mrp_lot_number_propagation/__manifest__.py b/mrp_lot_number_propagation/__manifest__.py new file mode 100644 index 000000000..8e15c07e4 --- /dev/null +++ b/mrp_lot_number_propagation/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +{ + "name": "MRP Serial Number Propagation", + "version": "15.0.0.1.0", + "development_status": "Alpha", + "license": "AGPL-3", + "author": "Camptocamp, Odoo Community Association (OCA)", + "maintainers": ["sebalix"], + "summary": "Propagate a serial number from a component to a finished product", + "website": "https://github.com/OCA/manufacture", + "category": "Manufacturing", + "depends": ["mrp"], + "data": [ + "views/mrp_bom.xml", + "views/mrp_production.xml", + ], + "installable": True, + "application": False, +} diff --git a/mrp_lot_number_propagation/models/__init__.py b/mrp_lot_number_propagation/models/__init__.py new file mode 100644 index 000000000..6028eeffd --- /dev/null +++ b/mrp_lot_number_propagation/models/__init__.py @@ -0,0 +1,4 @@ +from . import mrp_bom +from . import mrp_bom_line +from . import mrp_production +from . import stock_move diff --git a/mrp_lot_number_propagation/models/mrp_bom.py b/mrp_lot_number_propagation/models/mrp_bom.py new file mode 100644 index 000000000..e9e574891 --- /dev/null +++ b/mrp_lot_number_propagation/models/mrp_bom.py @@ -0,0 +1,74 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import api, fields, models, tools + + +class MrpBom(models.Model): + _inherit = "mrp.bom" + + lot_number_propagation = fields.Boolean( + default=False, + help=( + "Allow to propagate the lot/serial number " + "from a component to the finished product." + ), + ) + display_lot_number_propagation = fields.Boolean( + compute="_compute_display_lot_number_propagation" + ) + + @api.depends( + "type", + "product_tmpl_id.tracking", + "product_qty", + "product_uom_id", + "bom_line_ids.product_id.tracking", + "bom_line_ids.product_qty", + "bom_line_ids.product_uom_id", + ) + def _compute_display_lot_number_propagation(self): + """Check if a lot number can be propagated. + + A lot number can be propagated from a component to the finished product if: + - the type of the BoM is normal (Manufacture this product) + - the finished product is tracked by serial number + - the quantity of the finished product is 1 and its UoM is unit + - there is at least one bom line, with a component tracked by serial, + having a quantity of 1 and its UoM is unit + """ + uom_unit = self.env.ref("uom.product_uom_unit") + for bom in self: + bom.display_lot_number_propagation = ( + bom.type in self._get_lot_number_propagation_bom_types() + and bom.product_tmpl_id.tracking == "serial" + and tools.float_compare( + bom.product_qty, 1, precision_rounding=bom.product_uom_id.rounding + ) + == 0 + and bom.product_uom_id == uom_unit + and bom._has_tracked_product_to_propagate() + ) + + def _get_lot_number_propagation_bom_types(self): + return ["normal"] + + def _has_tracked_product_to_propagate(self): + self.ensure_one() + uom_unit = self.env.ref("uom.product_uom_unit") + for line in self.bom_line_ids: + if ( + line.product_id.tracking == "serial" + and tools.float_compare( + line.product_qty, 1, precision_rounding=line.product_uom_id.rounding + ) + == 0 + and line.product_uom_id == uom_unit + ): + return True + return False + + @api.onchange("display_lot_number_propagation") + def onchange_display_lot_number_propagation(self): + if not self.display_lot_number_propagation: + self.lot_number_propagation = False diff --git a/mrp_lot_number_propagation/models/mrp_bom_line.py b/mrp_lot_number_propagation/models/mrp_bom_line.py new file mode 100644 index 000000000..093cf0979 --- /dev/null +++ b/mrp_lot_number_propagation/models/mrp_bom_line.py @@ -0,0 +1,62 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class MrpBomLine(models.Model): + _inherit = "mrp.bom.line" + + propagate_lot_number = fields.Boolean( + default=False, + ) + display_propagate_lot_number = fields.Boolean( + compute="_compute_display_propagate_lot_number" + ) + + @api.depends( + "bom_id.display_lot_number_propagation", + "bom_id.lot_number_propagation", + ) + def _compute_display_propagate_lot_number(self): + for line in self: + line.display_propagate_lot_number = ( + line.bom_id.display_lot_number_propagation + and line.bom_id.lot_number_propagation + ) + + @api.constrains( + "propagate_lot_number", + "bom_id.lot_number_propagation", + "product_id.tracking", + "bom_id.product_tmpl_id.tracking", + ) + def _check_propagate_lot_number(self): + """ + This function should check: + + - if the bom has lot_number_propagation marked, there is one and + only one line of this bom with propagate_lot_number marked. + - the bom line being marked with lot_number_propagation is of the same + tracking type as the finished product + """ + for line in self: + lines_to_propagate = line.bom_id.bom_line_ids.filtered( + lambda o: o.propagate_lot_number + ) + if line.bom_id.lot_number_propagation: + if len(lines_to_propagate) > 1: + raise ValidationError( + _( + "Only one BoM line can propagate its lot/serial number " + "to the finished product." + ) + ) + if line.propagate_lot_number and line.product_id.tracking != "serial": + raise ValidationError( + _( + "Only components tracked by serial number can propagate " + "its lot/serial number to the finished product." + ) + ) diff --git a/mrp_lot_number_propagation/models/mrp_production.py b/mrp_lot_number_propagation/models/mrp_production.py new file mode 100644 index 000000000..3bc066377 --- /dev/null +++ b/mrp_lot_number_propagation/models/mrp_production.py @@ -0,0 +1,152 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from lxml import etree + +from odoo import _, api, fields, models, tools +from odoo.exceptions import UserError +from odoo.osv import expression +from odoo.tools.safe_eval import safe_eval + +from odoo.addons.base.models.ir_ui_view import ( + transfer_modifiers_to_node, + transfer_node_to_modifiers, +) + + +class MrpProduction(models.Model): + _inherit = "mrp.production" + + is_lot_number_propagated = fields.Boolean( + default=False, + readonly=True, + help=( + "Lot/serial number is propagated " + "from a component to the finished product." + ), + ) + propagated_lot_producing = fields.Char( + compute="_compute_propagated_lot_producing", + help=( + "The BoM used on this manufacturing order is set to propagate " + "lot number from one of its components. The value will be " + "computed once the corresponding component is selected." + ), + ) + + @api.depends( + "move_raw_ids.propagate_lot_number", + "move_raw_ids.move_line_ids.qty_done", + "move_raw_ids.move_line_ids.lot_id", + ) + def _compute_propagated_lot_producing(self): + for order in self: + order.propagated_lot_producing = False + move_with_lot = order.move_raw_ids.filtered( + lambda o: o.propagate_lot_number + ) + line_with_sn = move_with_lot.move_line_ids.filtered( + lambda l: ( + l.lot_id + and l.product_id.tracking == "serial" + and tools.float_compare( + l.qty_done, 1, precision_rounding=l.product_uom_id.rounding + ) + == 0 + ) + ) + if len(line_with_sn) == 1: + order.propagated_lot_producing = line_with_sn.lot_id.name + + @api.onchange("bom_id") + def _onchange_bom_id_lot_number_propagation(self): + self.is_lot_number_propagated = self.bom_id.lot_number_propagation + + def action_confirm(self): + res = super().action_confirm() + self._set_lot_number_propagation_data_from_bom() + return res + + def _set_lot_number_propagation_data_from_bom(self): + """Copy information from BoM to the manufacturing order.""" + for order in self: + order.is_lot_number_propagated = order.bom_id.lot_number_propagation + for move in order.move_raw_ids: + move.propagate_lot_number = move.bom_line_id.propagate_lot_number + + def _post_inventory(self, cancel_backorder=False): + self._create_and_assign_propagated_lot_number() + return super()._post_inventory(cancel_backorder=cancel_backorder) + + def _create_and_assign_propagated_lot_number(self): + for order in self: + if not order.is_lot_number_propagated or order.lot_producing_id: + continue + finish_moves = order.move_finished_ids.filtered( + lambda m: m.product_id == order.product_id + and m.state not in ("done", "cancel") + ) + if finish_moves and not finish_moves.quantity_done: + lot = self.env["stock.production.lot"].create( + { + "product_id": order.product_id.id, + "company_id": order.company_id.id, + "name": order.propagated_lot_producing, + } + ) + order.with_context(lot_propagation=True).lot_producing_id = lot + + def write(self, vals): + for order in self: + if ( + order.is_lot_number_propagated + and "lot_producing_id" in vals + and not self.env.context.get("lot_propagation") + ): + raise UserError( + _( + "Lot/Serial number is propagated from a component, " + "you are not allowed to change it." + ) + ) + return super().write(vals) + + def fields_view_get( + self, view_id=None, view_type="form", toolbar=False, submenu=False + ): + # Override to hide the "lot_producing_id" field + "action_generate_serial" + # button if the MO is configured to propagate a serial number + result = super().fields_view_get( + view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu + ) + if result.get("name") in self._views_to_adapt(): + result["arch"] = self._fields_view_get_adapt_lot_tags_attrs(result) + return result + + def _views_to_adapt(self): + """Return the form view names bound to 'mrp.production' to adapt.""" + return ["mrp.production.form"] + + def _fields_view_get_adapt_lot_tags_attrs(self, view): + """Hide elements related to lot if it is automatically propagated.""" + doc = etree.XML(view["arch"]) + tags = ( + "//label[@for='lot_producing_id']", + "//field[@name='lot_producing_id']/..", # parent
+ ) + for xpath_expr in tags: + attrs_key = "invisible" + nodes = doc.xpath(xpath_expr) + for field in nodes: + attrs = safe_eval(field.attrib.get("attrs", "{}")) + if not attrs[attrs_key]: + continue + invisible_domain = expression.OR( + [attrs[attrs_key], [("is_lot_number_propagated", "=", True)]] + ) + attrs[attrs_key] = invisible_domain + field.set("attrs", str(attrs)) + modifiers = {} + transfer_node_to_modifiers(field, modifiers, self.env.context) + transfer_modifiers_to_node(modifiers, field) + return etree.tostring(doc, encoding="unicode") diff --git a/mrp_lot_number_propagation/models/stock_move.py b/mrp_lot_number_propagation/models/stock_move.py new file mode 100644 index 000000000..cda1fba97 --- /dev/null +++ b/mrp_lot_number_propagation/models/stock_move.py @@ -0,0 +1,13 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import fields, models + + +class StockMove(models.Model): + _inherit = "stock.move" + + propagate_lot_number = fields.Boolean( + default=False, + readonly=True, + ) diff --git a/mrp_lot_number_propagation/readme/CONTRIBUTORS.rst b/mrp_lot_number_propagation/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..313df49ab --- /dev/null +++ b/mrp_lot_number_propagation/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Akim Juillerat +* Sébastien Alix diff --git a/mrp_lot_number_propagation/readme/DESCRIPTION.rst b/mrp_lot_number_propagation/readme/DESCRIPTION.rst new file mode 100644 index 000000000..d2563bc4a --- /dev/null +++ b/mrp_lot_number_propagation/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Allow to propagate a lot number from a component to a finished product. diff --git a/mrp_lot_number_propagation/readme/ROADMAP.rst b/mrp_lot_number_propagation/readme/ROADMAP.rst new file mode 100644 index 000000000..38015d2db --- /dev/null +++ b/mrp_lot_number_propagation/readme/ROADMAP.rst @@ -0,0 +1 @@ +* Add compatibility with lot number (in addition to serial number) diff --git a/mrp_lot_number_propagation/tests/__init__.py b/mrp_lot_number_propagation/tests/__init__.py new file mode 100644 index 000000000..a16beef0b --- /dev/null +++ b/mrp_lot_number_propagation/tests/__init__.py @@ -0,0 +1 @@ +from . import test_mrp_bom diff --git a/mrp_lot_number_propagation/tests/common.py b/mrp_lot_number_propagation/tests/common.py new file mode 100644 index 000000000..9a81d7d85 --- /dev/null +++ b/mrp_lot_number_propagation/tests/common.py @@ -0,0 +1,92 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import random +import string + +from odoo import fields +from odoo.tests import common + + +class Common(common.TransactionCase): + + LOT_NAME = "PROPAGATED-LOT" + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.bom = cls.env.ref("mrp.mrp_bom_desk") + cls.product_tracked_by_lot = cls.env.ref( + "mrp.product_product_computer_desk_leg" + ) + cls.product_tracked_by_sn = cls.env.ref( + "mrp.product_product_computer_desk_head" + ) + cls.line_tracked_by_lot = cls.bom.bom_line_ids.filtered( + lambda o: o.product_id == cls.product_tracked_by_lot + ) + cls.line_tracked_by_sn = cls.bom.bom_line_ids.filtered( + lambda o: o.product_id == cls.product_tracked_by_sn + ) + cls.line_no_tracking = fields.first( + cls.bom.bom_line_ids.filtered(lambda o: o.product_id.tracking == "none") + ) + + @classmethod + def _update_qty_in_location( + cls, location, product, quantity, package=None, lot=None, in_date=None + ): + quants = cls.env["stock.quant"]._gather( + product, location, lot_id=lot, package_id=package, strict=True + ) + # this method adds the quantity to the current quantity, so remove it + quantity -= sum(quants.mapped("quantity")) + cls.env["stock.quant"]._update_available_quantity( + product, + location, + quantity, + package_id=package, + lot_id=lot, + in_date=in_date, + ) + + @classmethod + def _update_stock_component_qty(cls, order=None, bom=None, location=None): + if not order and not bom: + return + if order: + bom = order.bom_id + if not location: + location = cls.env.ref("stock.stock_location_stock") + for line in bom.bom_line_ids: + if line.product_id.type != "product": + continue + lot = None + if line.product_id.tracking != "none": + lot_name = "".join( + random.choice(string.ascii_lowercase) for i in range(10) + ) + if line.propagate_lot_number: + lot_name = cls.LOT_NAME + vals = { + "product_id": line.product_id.id, + "company_id": line.company_id.id, + "name": lot_name, + } + lot = cls.env["stock.production.lot"].create(vals) + cls._update_qty_in_location( + location, + line.product_id, + line.product_qty, + lot=lot, + ) + + @classmethod + def _get_lot_quants(cls, lot, location=None): + quants = lot.quant_ids.filtered(lambda q: q.quantity > 0) + if location: + quants = quants.filtered( + lambda q: q.location_id.parent_path in location.parent_path + ) + return quants diff --git a/mrp_lot_number_propagation/tests/test_mrp_bom.py b/mrp_lot_number_propagation/tests/test_mrp_bom.py new file mode 100644 index 000000000..49f644aed --- /dev/null +++ b/mrp_lot_number_propagation/tests/test_mrp_bom.py @@ -0,0 +1,36 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo.exceptions import ValidationError + +from .common import Common + + +class TestMrpBom(Common): + def test_bom_display_lot_number_propagation(self): + self.assertTrue(self.bom.display_lot_number_propagation) + self.bom.product_tmpl_id.tracking = "none" + self.assertFalse(self.bom.display_lot_number_propagation) + + def test_bom_line_check_propagate_lot_number_multi(self): + self.bom.lot_number_propagation = True + # Flag more than one line to propagate + with self.assertRaisesRegex(ValidationError, "Only one BoM"): + self.bom.bom_line_ids.write({"propagate_lot_number": True}) + + def test_bom_line_check_propagate_lot_number_not_tracked(self): + self.bom.lot_number_propagation = True + # Flag a line that can't be propagated + with self.assertRaisesRegex(ValidationError, "Only components tracked"): + self.line_no_tracking.propagate_lot_number = True + + def test_bom_line_check_propagate_lot_number_tracked_by_lot(self): + self.bom.lot_number_propagation = True + # Flag a line tracked by lot (not SN) which is not supported + with self.assertRaisesRegex(ValidationError, "Only components tracked"): + self.line_tracked_by_lot.propagate_lot_number = True + + def test_bom_line_check_propagate_lot_number_same_tracking(self): + self.bom.lot_number_propagation = True + # Flag a line whose tracking type is the same than the finished product + self.line_tracked_by_sn.propagate_lot_number = True diff --git a/mrp_lot_number_propagation/tests/test_mrp_production.py b/mrp_lot_number_propagation/tests/test_mrp_production.py new file mode 100644 index 000000000..fd16b1802 --- /dev/null +++ b/mrp_lot_number_propagation/tests/test_mrp_production.py @@ -0,0 +1,44 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo.exceptions import UserError +from odoo.tests.common import Form + +from .common import Common + + +class TestMrpProduction(Common): + @classmethod + def setUpClass(cls): + super().setUpClass() + # Configure the BoM to propagate lot number + cls.bom.lot_number_propagation = True + cls.line_tracked_by_sn.propagate_lot_number = True + with Form(cls.env["mrp.production"]) as form: + form.bom_id = cls.bom + cls.order = form.save() + + def _set_qty_done(self, order): + for line in order.move_raw_ids.move_line_ids: + line.qty_done = line.product_uom_qty + order.qty_producing = order.product_qty + + def test_order_propagated_lot_producing(self): + self.assertTrue(self.order.is_lot_number_propagated) # set by onchange + self._update_stock_component_qty(self.order) + self.order.action_confirm() + self.assertTrue(self.order.is_lot_number_propagated) # set by action_confirm + self.assertTrue(any(self.order.move_raw_ids.mapped("propagate_lot_number"))) + self._set_qty_done(self.order) + self.assertEqual(self.order.propagated_lot_producing, self.LOT_NAME) + + def test_order_write_lot_producing_id_not_allowed(self): + with self.assertRaisesRegex(UserError, "not allowed"): + self.order.write({"lot_producing_id": False}) + + def test_order_post_inventory(self): + self._update_stock_component_qty(self.order) + self.order.action_confirm() + self._set_qty_done(self.order) + self.order.button_mark_done() + self.assertEqual(self.order.lot_producing_id.name, self.LOT_NAME) diff --git a/mrp_lot_number_propagation/views/mrp_bom.xml b/mrp_lot_number_propagation/views/mrp_bom.xml new file mode 100644 index 000000000..5f3f6f6a9 --- /dev/null +++ b/mrp_lot_number_propagation/views/mrp_bom.xml @@ -0,0 +1,28 @@ + + + + + + mrp.bom.form.inherit + mrp.bom + + + + + + + + + + + + + + diff --git a/mrp_lot_number_propagation/views/mrp_production.xml b/mrp_lot_number_propagation/views/mrp_production.xml new file mode 100644 index 000000000..62509a1dc --- /dev/null +++ b/mrp_lot_number_propagation/views/mrp_production.xml @@ -0,0 +1,25 @@ + + + + + + mrp.production.form.inherit + mrp.production + + + + + + + + + + + From 55eccc2e4be83d3e8a092eaba0b737b07dad2528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Sat, 22 Oct 2022 13:01:04 +0200 Subject: [PATCH 02/28] fixup! [ADD] mrp_lot_number_propagation --- mrp_lot_number_propagation/models/__init__.py | 2 + mrp_lot_number_propagation/models/mrp_bom.py | 16 ++++- .../models/mrp_bom_line.py | 34 +++++----- .../models/product_product.py | 13 ++++ .../models/product_template.py | 42 +++++++++++++ .../tests/test_mrp_bom.py | 63 ++++++++++++++++--- .../tests/test_mrp_production.py | 8 ++- 7 files changed, 148 insertions(+), 30 deletions(-) create mode 100644 mrp_lot_number_propagation/models/product_product.py create mode 100644 mrp_lot_number_propagation/models/product_template.py diff --git a/mrp_lot_number_propagation/models/__init__.py b/mrp_lot_number_propagation/models/__init__.py index 6028eeffd..b92e6ec25 100644 --- a/mrp_lot_number_propagation/models/__init__.py +++ b/mrp_lot_number_propagation/models/__init__.py @@ -1,4 +1,6 @@ from . import mrp_bom from . import mrp_bom_line from . import mrp_production +from . import product_product +from . import product_template from . import stock_move diff --git a/mrp_lot_number_propagation/models/mrp_bom.py b/mrp_lot_number_propagation/models/mrp_bom.py index e9e574891..a084dcf12 100644 --- a/mrp_lot_number_propagation/models/mrp_bom.py +++ b/mrp_lot_number_propagation/models/mrp_bom.py @@ -1,7 +1,8 @@ # Copyright 2022 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import api, fields, models, tools +from odoo import _, api, fields, models, tools +from odoo.exceptions import ValidationError class MrpBom(models.Model): @@ -72,3 +73,16 @@ class MrpBom(models.Model): def onchange_display_lot_number_propagation(self): if not self.display_lot_number_propagation: self.lot_number_propagation = False + + @api.constrains("lot_number_propagation") + def _check_propagate_lot_number(self): + for bom in self: + if not bom.lot_number_propagation: + continue + if not bom.bom_line_ids.filtered("propagate_lot_number"): + raise ValidationError( + _( + "With 'Lot Number Propagation' enabled, a line has " + "to be configured with the 'Propagate Lot Number' option." + ) + ) diff --git a/mrp_lot_number_propagation/models/mrp_bom_line.py b/mrp_lot_number_propagation/models/mrp_bom_line.py index 093cf0979..b8b02a403 100644 --- a/mrp_lot_number_propagation/models/mrp_bom_line.py +++ b/mrp_lot_number_propagation/models/mrp_bom_line.py @@ -26,12 +26,7 @@ class MrpBomLine(models.Model): and line.bom_id.lot_number_propagation ) - @api.constrains( - "propagate_lot_number", - "bom_id.lot_number_propagation", - "product_id.tracking", - "bom_id.product_tmpl_id.tracking", - ) + @api.constrains("propagate_lot_number") def _check_propagate_lot_number(self): """ This function should check: @@ -42,21 +37,22 @@ class MrpBomLine(models.Model): tracking type as the finished product """ for line in self: + if not line.bom_id.lot_number_propagation: + continue lines_to_propagate = line.bom_id.bom_line_ids.filtered( lambda o: o.propagate_lot_number ) - if line.bom_id.lot_number_propagation: - if len(lines_to_propagate) > 1: - raise ValidationError( - _( - "Only one BoM line can propagate its lot/serial number " - "to the finished product." - ) + if len(lines_to_propagate) > 1: + raise ValidationError( + _( + "Only one BoM line can propagate its lot/serial number " + "to the finished product." ) - if line.propagate_lot_number and line.product_id.tracking != "serial": - raise ValidationError( - _( - "Only components tracked by serial number can propagate " - "its lot/serial number to the finished product." - ) + ) + if line.propagate_lot_number and line.product_id.tracking != "serial": + raise ValidationError( + _( + "Only components tracked by serial number can propagate " + "its lot/serial number to the finished product." ) + ) diff --git a/mrp_lot_number_propagation/models/product_product.py b/mrp_lot_number_propagation/models/product_product.py new file mode 100644 index 000000000..69c72b19d --- /dev/null +++ b/mrp_lot_number_propagation/models/product_product.py @@ -0,0 +1,13 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import api, models + + +class ProductProduct(models.Model): + _inherit = "product.product" + + @api.constrains("tracking") + def _check_bom_propagate_lot_number(self): + for product in self: + product.product_tmpl_id._check_bom_propagate_lot_number() diff --git a/mrp_lot_number_propagation/models/product_template.py b/mrp_lot_number_propagation/models/product_template.py new file mode 100644 index 000000000..7a42adbb5 --- /dev/null +++ b/mrp_lot_number_propagation/models/product_template.py @@ -0,0 +1,42 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import _, api, models +from odoo.exceptions import ValidationError + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + @api.constrains("tracking") + def _check_bom_propagate_lot_number(self): + """Block tracking type updates if the product is used by a BoM.""" + for product in self: + if product.tracking == "serial": + continue + # Check BoMs + for bom in product.bom_ids: + if bom.lot_number_propagation: + raise ValidationError( + _( + "A BoM propagating serial numbers requires " + "this product to be tracked as such." + ) + ) + # Check lines of BoMs + bom_lines = self.env["mrp.bom.line"].search( + [ + ("product_id", "in", product.product_variant_ids.ids), + ("propagate_lot_number", "=", True), + ("bom_id.lot_number_propagation", "=", True), + ] + ) + if bom_lines: + boms = "\n- ".join(bom_lines.mapped("bom_id.display_name")) + boms = "\n- " + boms + raise ValidationError( + _( + "This component is configured to propagate its " + "serial number in the following Bill of Materials:{boms}'" + ).format(boms=boms) + ) diff --git a/mrp_lot_number_propagation/tests/test_mrp_bom.py b/mrp_lot_number_propagation/tests/test_mrp_bom.py index 49f644aed..18a4604f7 100644 --- a/mrp_lot_number_propagation/tests/test_mrp_bom.py +++ b/mrp_lot_number_propagation/tests/test_mrp_bom.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo.exceptions import ValidationError +from odoo.tests.common import Form from .common import Common @@ -13,24 +14,70 @@ class TestMrpBom(Common): self.assertFalse(self.bom.display_lot_number_propagation) def test_bom_line_check_propagate_lot_number_multi(self): - self.bom.lot_number_propagation = True + form = Form(self.bom) + form.lot_number_propagation = True # Flag more than one line to propagate + for i in range(len(form.bom_line_ids)): + line_form = form.bom_line_ids.edit(i) + line_form.propagate_lot_number = True + line_form.save() with self.assertRaisesRegex(ValidationError, "Only one BoM"): - self.bom.bom_line_ids.write({"propagate_lot_number": True}) + form.save() def test_bom_line_check_propagate_lot_number_not_tracked(self): - self.bom.lot_number_propagation = True + form = Form(self.bom) + form.lot_number_propagation = True # Flag a line that can't be propagated + line_form = form.bom_line_ids.edit(2) # line without tracking + line_form.propagate_lot_number = True + line_form.save() with self.assertRaisesRegex(ValidationError, "Only components tracked"): - self.line_no_tracking.propagate_lot_number = True + form.save() def test_bom_line_check_propagate_lot_number_tracked_by_lot(self): - self.bom.lot_number_propagation = True + form = Form(self.bom) + form.lot_number_propagation = True # Flag a line tracked by lot (not SN) which is not supported + line_form = form.bom_line_ids.edit(1) + line_form.propagate_lot_number = True + line_form.save() with self.assertRaisesRegex(ValidationError, "Only components tracked"): - self.line_tracked_by_lot.propagate_lot_number = True + form.save() def test_bom_line_check_propagate_lot_number_same_tracking(self): - self.bom.lot_number_propagation = True + form = Form(self.bom) + form.lot_number_propagation = True # Flag a line whose tracking type is the same than the finished product - self.line_tracked_by_sn.propagate_lot_number = True + line_form = form.bom_line_ids.edit(0) + line_form.propagate_lot_number = True + line_form.save() + form.save() + + def test_bom_check_propagate_lot_number(self): + # Configure the BoM to propagate the lot/SN without enabling any line + with self.assertRaisesRegex(ValidationError, "a line has to be configured"): + self.bom.lot_number_propagation = True + + def test_reset_tracking_on_bom_product(self): + # Configure the BoM to propagate the lot/SN + with Form(self.bom) as form: + form.lot_number_propagation = True + line_form = form.bom_line_ids.edit(0) # Line tracked by SN + line_form.propagate_lot_number = True + line_form.save() + form.save() + # Reset the tracking on the finished product + with self.assertRaisesRegex(ValidationError, "A BoM propagating"): + self.bom.product_tmpl_id.tracking = "none" + + def test_reset_tracking_on_bom_component(self): + # Configure the BoM to propagate the lot/SN + with Form(self.bom) as form: + form.lot_number_propagation = True + line_form = form.bom_line_ids.edit(0) # Line tracked by SN + line_form.propagate_lot_number = True + line_form.save() + form.save() + # Reset the tracking on the component which propagates the SN + with self.assertRaisesRegex(ValidationError, "This component is"): + self.line_tracked_by_sn.product_id.tracking = "none" diff --git a/mrp_lot_number_propagation/tests/test_mrp_production.py b/mrp_lot_number_propagation/tests/test_mrp_production.py index fd16b1802..ffbad6446 100644 --- a/mrp_lot_number_propagation/tests/test_mrp_production.py +++ b/mrp_lot_number_propagation/tests/test_mrp_production.py @@ -12,8 +12,12 @@ class TestMrpProduction(Common): def setUpClass(cls): super().setUpClass() # Configure the BoM to propagate lot number - cls.bom.lot_number_propagation = True - cls.line_tracked_by_sn.propagate_lot_number = True + with Form(cls.bom) as form: + form.lot_number_propagation = True + line_form = form.bom_line_ids.edit(0) # Line tracked by SN + line_form.propagate_lot_number = True + line_form.save() + form.save() with Form(cls.env["mrp.production"]) as form: form.bom_id = cls.bom cls.order = form.save() From c09354e6e24608e5df5dd00ebce61b5b41de681f Mon Sep 17 00:00:00 2001 From: oca-ci Date: Fri, 30 Dec 2022 15:38:35 +0000 Subject: [PATCH 03/28] [UPD] Update mrp_lot_number_propagation.pot --- .../i18n/mrp_lot_number_propagation.pot | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 mrp_lot_number_propagation/i18n/mrp_lot_number_propagation.pot diff --git a/mrp_lot_number_propagation/i18n/mrp_lot_number_propagation.pot b/mrp_lot_number_propagation/i18n/mrp_lot_number_propagation.pot new file mode 100644 index 000000000..a52ed845c --- /dev/null +++ b/mrp_lot_number_propagation/i18n/mrp_lot_number_propagation.pot @@ -0,0 +1,149 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_lot_number_propagation +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.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_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/product_template.py:0 +#, python-format +msgid "" +"A BoM propagating serial numbers requires this product to be tracked as " +"such." +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_bom__lot_number_propagation +msgid "" +"Allow to propagate the lot/serial number from a component to the finished " +"product." +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_bom +msgid "Bill of Material" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_bom_line +msgid "Bill of Material Line" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__display_lot_number_propagation +msgid "Display Lot Number Propagation" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom_line__display_propagate_lot_number +msgid "Display Propagate Lot Number" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_production__is_lot_number_propagated +msgid "Is Lot Number Propagated" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__lot_number_propagation +msgid "Lot Number Propagation" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model_terms:ir.ui.view,arch_db:mrp_lot_number_propagation.mrp_production_form_view +msgid "Lot/Serial Number" +msgstr "" + +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0 +#, python-format +msgid "" +"Lot/Serial number is propagated from a component, you are not allowed to " +"change it." +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_production__is_lot_number_propagated +msgid "" +"Lot/serial number is propagated from a component to the finished product." +msgstr "" + +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_bom_line.py:0 +#, python-format +msgid "" +"Only components tracked by serial number can propagate its lot/serial number" +" to the finished product." +msgstr "" + +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_bom_line.py:0 +#, python-format +msgid "" +"Only one BoM line can propagate its lot/serial number to the finished " +"product." +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model,name:mrp_lot_number_propagation.model_product_product +msgid "Product" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model,name:mrp_lot_number_propagation.model_product_template +msgid "Product Template" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_production +msgid "Production Order" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom_line__propagate_lot_number +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_stock_move__propagate_lot_number +msgid "Propagate Lot Number" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_production__propagated_lot_producing +msgid "Propagated Lot Producing" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model,name:mrp_lot_number_propagation.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_production__propagated_lot_producing +msgid "" +"The BoM used on this manufacturing order is set to propagate lot number from" +" one of its components. The value will be computed once the corresponding " +"component is selected." +msgstr "" + +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/product_template.py:0 +#, python-format +msgid "" +"This component is configured to propagate its serial number in the following" +" Bill of Materials:{boms}'" +msgstr "" + +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_bom.py:0 +#, python-format +msgid "" +"With 'Lot Number Propagation' enabled, a line has to be configured with the " +"'Propagate Lot Number' option." +msgstr "" From 4181e20024159bfa2095da483404afc908116a51 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 30 Dec 2022 15:44:20 +0000 Subject: [PATCH 04/28] [UPD] README.rst --- mrp_lot_number_propagation/README.rst | 93 +++- .../static/description/index.html | 435 ++++++++++++++++++ 2 files changed, 527 insertions(+), 1 deletion(-) create mode 100644 mrp_lot_number_propagation/static/description/index.html diff --git a/mrp_lot_number_propagation/README.rst b/mrp_lot_number_propagation/README.rst index 9c558e357..a8c440515 100644 --- a/mrp_lot_number_propagation/README.rst +++ b/mrp_lot_number_propagation/README.rst @@ -1 +1,92 @@ -. +============================= +MRP Serial Number Propagation +============================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |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/mrp_lot_number_propagation + :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-mrp_lot_number_propagation + :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/15.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Allow to propagate a lot number from a component to a finished product. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Known issues / Roadmap +====================== + +* Add compatibility with lot number (in addition to serial number) + +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 +~~~~~~~ + +* Camptocamp + +Contributors +~~~~~~~~~~~~ + +* Akim Juillerat +* Sébastien Alix + +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. + +.. |maintainer-sebalix| image:: https://github.com/sebalix.png?size=40px + :target: https://github.com/sebalix + :alt: sebalix + +Current `maintainer `__: + +|maintainer-sebalix| + +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_lot_number_propagation/static/description/index.html b/mrp_lot_number_propagation/static/description/index.html new file mode 100644 index 000000000..374465026 --- /dev/null +++ b/mrp_lot_number_propagation/static/description/index.html @@ -0,0 +1,435 @@ + + + + + + +MRP Serial Number Propagation + + + +
+

MRP Serial Number Propagation

+ + +

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

+

Allow to propagate a lot number from a component to a finished product.

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Known issues / Roadmap

+
    +
  • Add compatibility with lot number (in addition to serial number)
  • +
+
+
+

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

+
    +
  • Camptocamp
  • +
+
+
+

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.

+

Current maintainer:

+

sebalix

+

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.

+
+
+
+ + From 0cb30761fe9903d4fe95157cdb4e4168b8cbd7c1 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 30 Dec 2022 15:44:20 +0000 Subject: [PATCH 05/28] [ADD] icon.png --- .../static/description/icon.png | Bin 0 -> 9455 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 mrp_lot_number_propagation/static/description/icon.png diff --git a/mrp_lot_number_propagation/static/description/icon.png b/mrp_lot_number_propagation/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 zcmW++2RxMjAAjx~&dlBk9S+%}OXg)AGE&Cb*&}d0jUxM@u(PQx^-s)697TX`ehR4?GS^qbkof1cslKgkU)h65qZ9Oc=ml_0temigYLJfnz{IDzUf>bGs4N!v3=Z3jMq&A#7%rM5eQ#dc?k~! zVpnB`o+K7|Al`Q_U;eD$B zfJtP*jH`siUq~{KE)`jP2|#TUEFGRryE2`i0**z#*^6~AI|YzIWy$Cu#CSLW3q=GA z6`?GZymC;dCPk~rBS%eCb`5OLr;RUZ;D`}um=H)BfVIq%7VhiMr)_#G0N#zrNH|__ zc+blN2UAB0=617@>_u;MPHN;P;N#YoE=)R#i$k_`UAA>WWCcEVMh~L_ zj--gtp&|K1#58Yz*AHCTMziU1Jzt_jG0I@qAOHsk$2}yTmVkBp_eHuY$A9)>P6o~I z%aQ?!(GqeQ-Y+b0I(m9pwgi(IIZZzsbMv+9w{PFtd_<_(LA~0H(xz{=FhLB@(1&qHA5EJw1>>=%q2f&^X>IQ{!GJ4e9U z&KlB)z(84HmNgm2hg2C0>WM{E(DdPr+EeU_N@57;PC2&DmGFW_9kP&%?X4}+xWi)( z;)z%wI5>D4a*5XwD)P--sPkoY(a~WBw;E~AW`Yue4kFa^LM3X`8x|}ZUeMnqr}>kH zG%WWW>3ml$Yez?i%)2pbKPI7?5o?hydokgQyZsNEr{a|mLdt;X2TX(#B1j35xPnPW z*bMSSOauW>o;*=kO8ojw91VX!qoOQb)zHJ!odWB}d+*K?#sY_jqPdg{Sm2HdYzdEx zOGVPhVRTGPtv0o}RfVP;Nd(|CB)I;*t&QO8h zFfekr30S!-LHmV_Su-W+rEwYXJ^;6&3|L$mMC8*bQptyOo9;>Qb9Q9`ySe3%V$A*9 zeKEe+b0{#KWGp$F+tga)0RtI)nhMa-K@JS}2krK~n8vJ=Ngm?R!9G<~RyuU0d?nz# z-5EK$o(!F?hmX*2Yt6+coY`6jGbb7tF#6nHA zuKk=GGJ;ZwON1iAfG$E#Y7MnZVmrY|j0eVI(DN_MNFJmyZ|;w4tf@=CCDZ#5N_0K= z$;R~bbk?}TpfDjfB&aiQ$VA}s?P}xPERJG{kxk5~R`iRS(SK5d+Xs9swCozZISbnS zk!)I0>t=A<-^z(cmSFz3=jZ23u13X><0b)P)^1T_))Kr`e!-pb#q&J*Q`p+B6la%C zuVl&0duN<;uOsB3%T9Fp8t{ED108<+W(nOZd?gDnfNBC3>M8WE61$So|P zVvqH0SNtDTcsUdzaMDpT=Ty0pDHHNL@Z0w$Y`XO z2M-_r1S+GaH%pz#Uy0*w$Vdl=X=rQXEzO}d6J^R6zjM1u&c9vYLvLp?W7w(?np9x1 zE_0JSAJCPB%i7p*Wvg)pn5T`8k3-uR?*NT|J`eS#_#54p>!p(mLDvmc-3o0mX*mp_ zN*AeS<>#^-{S%W<*mz^!X$w_2dHWpcJ6^j64qFBft-o}o_Vx80o0>}Du;>kLts;$8 zC`7q$QI(dKYG`Wa8#wl@V4jVWBRGQ@1dr-hstpQL)Tl+aqVpGpbSfN>5i&QMXfiZ> zaA?T1VGe?rpQ@;+pkrVdd{klI&jVS@I5_iz!=UMpTsa~mBga?1r}aRBm1WS;TT*s0f0lY=JBl66Upy)-k4J}lh=P^8(SXk~0xW=T9v*B|gzIhN z>qsO7dFd~mgxAy4V?&)=5ieYq?zi?ZEoj)&2o)RLy=@hbCRcfT5jigwtQGE{L*8<@Yd{zg;CsL5mvzfDY}P-wos_6PfprFVaeqNE%h zKZhLtcQld;ZD+>=nqN~>GvROfueSzJD&BE*}XfU|H&(FssBqY=hPCt`d zH?@s2>I(|;fcW&YM6#V#!kUIP8$Nkdh0A(bEVj``-AAyYgwY~jB zT|I7Bf@%;7aL7Wf4dZ%VqF$eiaC38OV6oy3Z#TER2G+fOCd9Iaoy6aLYbPTN{XRPz z;U!V|vBf%H!}52L2gH_+j;`bTcQRXB+y9onc^wLm5wi3-Be}U>k_u>2Eg$=k!(l@I zcCg+flakT2Nej3i0yn+g+}%NYb?ta;R?(g5SnwsQ49U8Wng8d|{B+lyRcEDvR3+`O{zfmrmvFrL6acVP%yG98X zo&+VBg@px@i)%o?dG(`T;n*$S5*rnyiR#=wW}}GsAcfyQpE|>a{=$Hjg=-*_K;UtD z#z-)AXwSRY?OPefw^iI+ z)AXz#PfEjlwTes|_{sB?4(O@fg0AJ^g8gP}ex9Ucf*@_^J(s_5jJV}c)s$`Myn|Kd z$6>}#q^n{4vN@+Os$m7KV+`}c%4)4pv@06af4-x5#wj!KKb%caK{A&Y#Rfs z-po?Dcb1({W=6FKIUirH&(yg=*6aLCekcKwyfK^JN5{wcA3nhO(o}SK#!CINhI`-I z1)6&n7O&ZmyFMuNwvEic#IiOAwNkR=u5it{B9n2sAJV5pNhar=j5`*N!Na;c7g!l$ z3aYBqUkqqTJ=Re-;)s!EOeij=7SQZ3Hq}ZRds%IM*PtM$wV z@;rlc*NRK7i3y5BETSKuumEN`Xu_8GP1Ri=OKQ$@I^ko8>H6)4rjiG5{VBM>B|%`&&s^)jS|-_95&yc=GqjNo{zFkw%%HHhS~e=s zD#sfS+-?*t|J!+ozP6KvtOl!R)@@-z24}`9{QaVLD^9VCSR2b`b!KC#o;Ki<+wXB6 zx3&O0LOWcg4&rv4QG0)4yb}7BFSEg~=IR5#ZRj8kg}dS7_V&^%#Do==#`u zpy6{ox?jWuR(;pg+f@mT>#HGWHAJRRDDDv~@(IDw&R>9643kK#HN`!1vBJHnC+RM&yIh8{gG2q zA%e*U3|N0XSRa~oX-3EAneep)@{h2vvd3Xvy$7og(sayr@95+e6~Xvi1tUqnIxoIH zVWo*OwYElb#uyW{Imam6f2rGbjR!Y3`#gPqkv57dB6K^wRGxc9B(t|aYDGS=m$&S!NmCtrMMaUg(c zc2qC=2Z`EEFMW-me5B)24AqF*bV5Dr-M5ig(l-WPS%CgaPzs6p_gnCIvTJ=Y<6!gT zVt@AfYCzjjsMEGi=rDQHo0yc;HqoRNnNFeWZgcm?f;cp(6CNylj36DoL(?TS7eU#+ z7&mfr#y))+CJOXQKUMZ7QIdS9@#-}7y2K1{8)cCt0~-X0O!O?Qx#E4Og+;A2SjalQ zs7r?qn0H044=sDN$SRG$arw~n=+T_DNdSrarmu)V6@|?1-ZB#hRn`uilTGPJ@fqEy zGt(f0B+^JDP&f=r{#Y_wi#AVDf-y!RIXU^0jXsFpf>=Ji*TeqSY!H~AMbJdCGLhC) zn7Rx+sXw6uYj;WRYrLd^5IZq@6JI1C^YkgnedZEYy<&4(z%Q$5yv#Boo{AH8n$a zhb4Y3PWdr269&?V%uI$xMcUrMzl=;w<_nm*qr=c3Rl@i5wWB;e-`t7D&c-mcQl7x! zZWB`UGcw=Y2=}~wzrfLx=uet<;m3~=8I~ZRuzvMQUQdr+yTV|ATf1Uuomr__nDf=X zZ3WYJtHp_ri(}SQAPjv+Y+0=fH4krOP@S&=zZ-t1jW1o@}z;xk8 z(Nz1co&El^HK^NrhVHa-_;&88vTU>_J33=%{if;BEY*J#1n59=07jrGQ#IP>@u#3A z;!q+E1Rj3ZJ+!4bq9F8PXJ@yMgZL;>&gYA0%_Kbi8?S=XGM~dnQZQ!yBSgcZhY96H zrWnU;k)qy`rX&&xlDyA%(a1Hhi5CWkmg(`Gb%m(HKi-7Z!LKGRP_B8@`7&hdDy5n= z`OIxqxiVfX@OX1p(mQu>0Ai*v_cTMiw4qRt3~NBvr9oBy0)r>w3p~V0SCm=An6@3n)>@z!|o-$HvDK z|3D2ZMJkLE5loMKl6R^ez@Zz%S$&mbeoqH5`Bb){Ei21q&VP)hWS2tjShfFtGE+$z zzCR$P#uktu+#!w)cX!lWN1XU%K-r=s{|j?)Akf@q#3b#{6cZCuJ~gCxuMXRmI$nGtnH+-h z+GEi!*X=AP<|fG`1>MBdTb?28JYc=fGvAi2I<$B(rs$;eoJCyR6_bc~p!XR@O-+sD z=eH`-ye})I5ic1eL~TDmtfJ|8`0VJ*Yr=hNCd)G1p2MMz4C3^Mj?7;!w|Ly%JqmuW zlIEW^Ft%z?*|fpXda>Jr^1noFZEwFgVV%|*XhH@acv8rdGxeEX{M$(vG{Zw+x(ei@ zmfXb22}8-?Fi`vo-YVrTH*C?a8%M=Hv9MqVH7H^J$KsD?>!SFZ;ZsvnHr_gn=7acz z#W?0eCdVhVMWN12VV^$>WlQ?f;P^{(&pYTops|btm6aj>_Uz+hqpGwB)vWp0Cf5y< zft8-je~nn?W11plq}N)4A{l8I7$!ks_x$PXW-2XaRFswX_BnF{R#6YIwMhAgd5F9X zGmwdadS6(a^fjHtXg8=l?Rc0Sm%hk6E9!5cLVloEy4eh(=FwgP`)~I^5~pBEWo+F6 zSf2ncyMurJN91#cJTy_u8Y}@%!bq1RkGC~-bV@SXRd4F{R-*V`bS+6;W5vZ(&+I<9$;-V|eNfLa5n-6% z2(}&uGRF;p92eS*sE*oR$@pexaqr*meB)VhmIg@h{uzkk$9~qh#cHhw#>O%)b@+(| z^IQgqzuj~Sk(J;swEM-3TrJAPCq9k^^^`q{IItKBRXYe}e0Tdr=Huf7da3$l4PdpwWDop%^}n;dD#K4s#DYA8SHZ z&1!riV4W4R7R#C))JH1~axJ)RYnM$$lIR%6fIVA@zV{XVyx}C+a-Dt8Y9M)^KU0+H zR4IUb2CJ{Hg>CuaXtD50jB(_Tcx=Z$^WYu2u5kubqmwp%drJ6 z?Fo40g!Qd<-l=TQxqHEOuPX0;^z7iX?Ke^a%XT<13TA^5`4Xcw6D@Ur&VT&CUe0d} z1GjOVF1^L@>O)l@?bD~$wzgf(nxX1OGD8fEV?TdJcZc2KoUe|oP1#=$$7ee|xbY)A zDZq+cuTpc(fFdj^=!;{k03C69lMQ(|>uhRfRu%+!k&YOi-3|1QKB z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 From f95435177dd27f01d0015694b84343a39cb85749 Mon Sep 17 00:00:00 2001 From: mymage Date: Sun, 1 Jan 2023 10:53:58 +0000 Subject: [PATCH 06/28] Added translation using Weblate (Italian) --- mrp_lot_number_propagation/i18n/it.po | 150 ++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 mrp_lot_number_propagation/i18n/it.po diff --git a/mrp_lot_number_propagation/i18n/it.po b/mrp_lot_number_propagation/i18n/it.po new file mode 100644 index 000000000..8ad54b0e2 --- /dev/null +++ b/mrp_lot_number_propagation/i18n/it.po @@ -0,0 +1,150 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_lot_number_propagation +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: it\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" + +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/product_template.py:0 +#, python-format +msgid "" +"A BoM propagating serial numbers requires this product to be tracked as " +"such." +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_bom__lot_number_propagation +msgid "" +"Allow to propagate the lot/serial number from a component to the finished " +"product." +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_bom +msgid "Bill of Material" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_bom_line +msgid "Bill of Material Line" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__display_lot_number_propagation +msgid "Display Lot Number Propagation" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom_line__display_propagate_lot_number +msgid "Display Propagate Lot Number" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_production__is_lot_number_propagated +msgid "Is Lot Number Propagated" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__lot_number_propagation +msgid "Lot Number Propagation" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model_terms:ir.ui.view,arch_db:mrp_lot_number_propagation.mrp_production_form_view +msgid "Lot/Serial Number" +msgstr "" + +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0 +#, python-format +msgid "" +"Lot/Serial number is propagated from a component, you are not allowed to " +"change it." +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_production__is_lot_number_propagated +msgid "" +"Lot/serial number is propagated from a component to the finished product." +msgstr "" + +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_bom_line.py:0 +#, python-format +msgid "" +"Only components tracked by serial number can propagate its lot/serial number" +" to the finished product." +msgstr "" + +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_bom_line.py:0 +#, python-format +msgid "" +"Only one BoM line can propagate its lot/serial number to the finished " +"product." +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model,name:mrp_lot_number_propagation.model_product_product +msgid "Product" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model,name:mrp_lot_number_propagation.model_product_template +msgid "Product Template" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model,name:mrp_lot_number_propagation.model_mrp_production +msgid "Production Order" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom_line__propagate_lot_number +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_stock_move__propagate_lot_number +msgid "Propagate Lot Number" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_production__propagated_lot_producing +msgid "Propagated Lot Producing" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model,name:mrp_lot_number_propagation.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: mrp_lot_number_propagation +#: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_production__propagated_lot_producing +msgid "" +"The BoM used on this manufacturing order is set to propagate lot number from" +" one of its components. The value will be computed once the corresponding " +"component is selected." +msgstr "" + +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/product_template.py:0 +#, python-format +msgid "" +"This component is configured to propagate its serial number in the following" +" Bill of Materials:{boms}'" +msgstr "" + +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_bom.py:0 +#, python-format +msgid "" +"With 'Lot Number Propagation' enabled, a line has to be configured with the " +"'Propagate Lot Number' option." +msgstr "" From ec00f94209a0cb5a212ce93f84ee3295b28f4ffc Mon Sep 17 00:00:00 2001 From: mymage Date: Sun, 1 Jan 2023 10:55:36 +0000 Subject: [PATCH 07/28] Translated using Weblate (Italian) Currently translated at 100.0% (22 of 22 strings) Translation: manufacture-15.0/manufacture-15.0-mrp_lot_number_propagation Translate-URL: https://translation.odoo-community.org/projects/manufacture-15-0/manufacture-15-0-mrp_lot_number_propagation/it/ --- mrp_lot_number_propagation/i18n/it.po | 48 +++++++++++++++++++-------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/mrp_lot_number_propagation/i18n/it.po b/mrp_lot_number_propagation/i18n/it.po index 8ad54b0e2..538d4bb38 100644 --- a/mrp_lot_number_propagation/i18n/it.po +++ b/mrp_lot_number_propagation/i18n/it.po @@ -6,13 +6,15 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 15.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: Automatically generated\n" +"PO-Revision-Date: 2023-01-01 13:45+0000\n" +"Last-Translator: mymage \n" "Language-Team: none\n" "Language: it\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 4.14.1\n" #. module: mrp_lot_number_propagation #: code:addons/mrp_lot_number_propagation/models/product_template.py:0 @@ -21,6 +23,8 @@ msgid "" "A BoM propagating serial numbers requires this product to be tracked as " "such." msgstr "" +"Una distinta base che propaga i numeri di serie richiede che questo prodotto " +"sia tracciato come tale." #. module: mrp_lot_number_propagation #: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_bom__lot_number_propagation @@ -28,41 +32,43 @@ msgid "" "Allow to propagate the lot/serial number from a component to the finished " "product." msgstr "" +"Consente di propagare il numero di lotto/matricola da un componente al " +"prodotto finito." #. module: mrp_lot_number_propagation #: model:ir.model,name:mrp_lot_number_propagation.model_mrp_bom msgid "Bill of Material" -msgstr "" +msgstr "Distinta base" #. module: mrp_lot_number_propagation #: model:ir.model,name:mrp_lot_number_propagation.model_mrp_bom_line msgid "Bill of Material Line" -msgstr "" +msgstr "Riga distinta base" #. module: mrp_lot_number_propagation #: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__display_lot_number_propagation msgid "Display Lot Number Propagation" -msgstr "" +msgstr "Visualizza propagazione numero di lotto" #. module: mrp_lot_number_propagation #: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom_line__display_propagate_lot_number msgid "Display Propagate Lot Number" -msgstr "" +msgstr "Visualizza il numero di lotto propagato" #. module: mrp_lot_number_propagation #: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_production__is_lot_number_propagated msgid "Is Lot Number Propagated" -msgstr "" +msgstr "Il numero di lotto è propagato" #. module: mrp_lot_number_propagation #: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__lot_number_propagation msgid "Lot Number Propagation" -msgstr "" +msgstr "Propagazione numero di lotto" #. module: mrp_lot_number_propagation #: model_terms:ir.ui.view,arch_db:mrp_lot_number_propagation.mrp_production_form_view msgid "Lot/Serial Number" -msgstr "" +msgstr "Numero di lotto/matricola" #. module: mrp_lot_number_propagation #: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0 @@ -71,12 +77,15 @@ msgid "" "Lot/Serial number is propagated from a component, you are not allowed to " "change it." msgstr "" +"Il numero di lotto/matricola è propagato da un componente, non è consentito " +"modificarlo." #. module: mrp_lot_number_propagation #: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_production__is_lot_number_propagated msgid "" "Lot/serial number is propagated from a component to the finished product." msgstr "" +"Il numero di lotto/matricola è propagato da un componente al prodotto finito." #. module: mrp_lot_number_propagation #: code:addons/mrp_lot_number_propagation/models/mrp_bom_line.py:0 @@ -85,6 +94,8 @@ msgid "" "Only components tracked by serial number can propagate its lot/serial number" " to the finished product." msgstr "" +"Solo i componenti tracciati da una matricola possono propagare il loro " +"numero di lotto/matricola al prodotto finito." #. module: mrp_lot_number_propagation #: code:addons/mrp_lot_number_propagation/models/mrp_bom_line.py:0 @@ -93,37 +104,39 @@ msgid "" "Only one BoM line can propagate its lot/serial number to the finished " "product." msgstr "" +"Solo una riga di DiBa può propagare il suo numero di lotto/matricola al " +"prodotto finito." #. module: mrp_lot_number_propagation #: model:ir.model,name:mrp_lot_number_propagation.model_product_product msgid "Product" -msgstr "" +msgstr "Prodotto" #. module: mrp_lot_number_propagation #: model:ir.model,name:mrp_lot_number_propagation.model_product_template msgid "Product Template" -msgstr "" +msgstr "Modello prodotto" #. module: mrp_lot_number_propagation #: model:ir.model,name:mrp_lot_number_propagation.model_mrp_production msgid "Production Order" -msgstr "" +msgstr "Ordine produzione" #. module: mrp_lot_number_propagation #: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom_line__propagate_lot_number #: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_stock_move__propagate_lot_number msgid "Propagate Lot Number" -msgstr "" +msgstr "Propaga numero di lotto" #. module: mrp_lot_number_propagation #: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_production__propagated_lot_producing msgid "Propagated Lot Producing" -msgstr "" +msgstr "Produzione lotto propagato" #. module: mrp_lot_number_propagation #: model:ir.model,name:mrp_lot_number_propagation.model_stock_move msgid "Stock Move" -msgstr "" +msgstr "Movimento di magazzino" #. module: mrp_lot_number_propagation #: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_production__propagated_lot_producing @@ -132,6 +145,9 @@ msgid "" " one of its components. The value will be computed once the corresponding " "component is selected." msgstr "" +"La DiBa utilizzata in questo ordine di produzione è impostata per propagare " +"il nomero di lotto da uno dei suoi componenti. Il valore verrà calcolato una " +"volta che il componente corrispondente sarà selezionato." #. module: mrp_lot_number_propagation #: code:addons/mrp_lot_number_propagation/models/product_template.py:0 @@ -140,6 +156,8 @@ msgid "" "This component is configured to propagate its serial number in the following" " Bill of Materials:{boms}'" msgstr "" +"Questo componente è configurato per propagare la sua matricola nella " +"seguente DiBa: {boms}'" #. module: mrp_lot_number_propagation #: code:addons/mrp_lot_number_propagation/models/mrp_bom.py:0 @@ -148,3 +166,5 @@ msgid "" "With 'Lot Number Propagation' enabled, a line has to be configured with the " "'Propagate Lot Number' option." msgstr "" +"Con 'Propagazione numero di lotto' abilitata, una riga deve essere " +"configurata con l'opzione 'Propaga numero di lotto'." From b114c80d27e9e96d6be85366f385ab162756fa4f Mon Sep 17 00:00:00 2001 From: Akim Juillerat Date: Tue, 3 Jan 2023 18:27:53 +0100 Subject: [PATCH 08/28] Allow to propagate lot from multiple BOM lines for variants When defining a single BOM for a product template having multiple product variants, we can have different components where the lot number must be propagated for different product variants. Therefore we need to allow to mark multiple BOM line with propagate_lot_number, and to avoid complicating the check function on the BOM lines, we ensure at the manufacturing order confirmation that only a single component is set to propagate its lot number. --- .../models/mrp_bom_line.py | 10 --- .../models/mrp_production.py | 28 +++++++- mrp_lot_number_propagation/tests/common.py | 71 ++++++++++++++++++- .../tests/test_mrp_bom.py | 11 --- .../tests/test_mrp_production.py | 59 ++++++++++++++- 5 files changed, 152 insertions(+), 27 deletions(-) diff --git a/mrp_lot_number_propagation/models/mrp_bom_line.py b/mrp_lot_number_propagation/models/mrp_bom_line.py index b8b02a403..b0f8d97f0 100644 --- a/mrp_lot_number_propagation/models/mrp_bom_line.py +++ b/mrp_lot_number_propagation/models/mrp_bom_line.py @@ -39,16 +39,6 @@ class MrpBomLine(models.Model): for line in self: if not line.bom_id.lot_number_propagation: continue - lines_to_propagate = line.bom_id.bom_line_ids.filtered( - lambda o: o.propagate_lot_number - ) - if len(lines_to_propagate) > 1: - raise ValidationError( - _( - "Only one BoM line can propagate its lot/serial number " - "to the finished product." - ) - ) if line.propagate_lot_number and line.product_id.tracking != "serial": raise ValidationError( _( diff --git a/mrp_lot_number_propagation/models/mrp_production.py b/mrp_lot_number_propagation/models/mrp_production.py index 3bc066377..d1afe8a98 100644 --- a/mrp_lot_number_propagation/models/mrp_production.py +++ b/mrp_lot_number_propagation/models/mrp_production.py @@ -70,9 +70,31 @@ class MrpProduction(models.Model): def _set_lot_number_propagation_data_from_bom(self): """Copy information from BoM to the manufacturing order.""" for order in self: - order.is_lot_number_propagated = order.bom_id.lot_number_propagation - for move in order.move_raw_ids: - move.propagate_lot_number = move.bom_line_id.propagate_lot_number + propagate_lot = order.bom_id.lot_number_propagation + if not propagate_lot: + continue + order.is_lot_number_propagated = propagate_lot + propagate_move = order.move_raw_ids.filtered( + lambda m: m.bom_line_id.propagate_lot_number + ) + if not propagate_move: + raise UserError( + _( + "Bill of material is marked for lot number propagation, but " + "there are no components propagating lot number. " + "Please check BOM configuration." + ) + ) + elif len(propagate_move) > 1: + raise UserError( + _( + "Bill of material is marked for lot number propagation, but " + "there are multiple components propagating lot number. " + "Please check BOM configuration." + ) + ) + else: + propagate_move.propagate_lot_number = True def _post_inventory(self, cancel_backorder=False): self._create_and_assign_propagated_lot_number() diff --git a/mrp_lot_number_propagation/tests/common.py b/mrp_lot_number_propagation/tests/common.py index 9a81d7d85..19194492e 100644 --- a/mrp_lot_number_propagation/tests/common.py +++ b/mrp_lot_number_propagation/tests/common.py @@ -5,7 +5,7 @@ import random import string from odoo import fields -from odoo.tests import common +from odoo.tests import Form, common class Common(common.TransactionCase): @@ -17,12 +17,19 @@ class Common(common.TransactionCase): super().setUpClass() cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) cls.bom = cls.env.ref("mrp.mrp_bom_desk") + cls.bom_product_template = cls.env.ref( + "mrp.product_product_computer_desk_product_template" + ) + cls.bom_product_product = cls.env.ref("mrp.product_product_computer_desk") cls.product_tracked_by_lot = cls.env.ref( "mrp.product_product_computer_desk_leg" ) cls.product_tracked_by_sn = cls.env.ref( "mrp.product_product_computer_desk_head" ) + cls.product_template_tracked_by_sn = cls.env.ref( + "mrp.product_product_computer_desk_head_product_template" + ) cls.line_tracked_by_lot = cls.bom.bom_line_ids.filtered( lambda o: o.product_id == cls.product_tracked_by_lot ) @@ -90,3 +97,65 @@ class Common(common.TransactionCase): lambda q: q.location_id.parent_path in location.parent_path ) return quants + + @classmethod + def _add_color_and_legs_variants(cls, product_template): + color_attribute = cls.env.ref("product.product_attribute_2") + color_att_value_white = cls.env.ref("product.product_attribute_value_3") + color_att_value_black = cls.env.ref("product.product_attribute_value_4") + legs_attribute = cls.env.ref("product.product_attribute_1") + legs_att_value_steel = cls.env.ref("product.product_attribute_value_1") + legs_att_value_alu = cls.env.ref("product.product_attribute_value_2") + cls._add_variants( + product_template, + { + color_attribute: [color_att_value_white, color_att_value_black], + legs_attribute: [legs_att_value_steel, legs_att_value_alu], + }, + ) + + @classmethod + def _add_variants(cls, product_template, attribute_values_dict): + for attribute, att_values_list in attribute_values_dict.items(): + cls.env["product.template.attribute.line"].create( + { + "product_tmpl_id": product_template.id, + "attribute_id": attribute.id, + "value_ids": [ + fields.Command.set([att_val.id for att_val in att_values_list]) + ], + } + ) + + @classmethod + def _create_bom_with_variants(cls): + attribute_values_dict = { + att_val.product_attribute_value_id.name: att_val.id + for att_val in cls.env["product.template.attribute.value"].search( + [("product_tmpl_id", "=", cls.bom_product_template.id)] + ) + } + new_bom_form = Form(cls.env["mrp.bom"]) + new_bom_form.product_tmpl_id = cls.bom_product_template + new_bom = new_bom_form.save() + bom_line_create_values = [] + for product in cls.product_template_tracked_by_sn.product_variant_ids: + create_values = {"bom_id": new_bom.id} + create_values["product_id"] = product.id + att_values_commands = [] + for att_value in product.product_template_attribute_value_ids: + att_values_commands.append( + fields.Command.link(attribute_values_dict[att_value.name]) + ) + create_values[ + "bom_product_template_attribute_value_ids" + ] = att_values_commands + bom_line_create_values.append(create_values) + cls.env["mrp.bom.line"].create(bom_line_create_values) + new_bom_form = Form(new_bom) + new_bom_form.lot_number_propagation = True + for line_position, _bom_line in enumerate(new_bom.bom_line_ids): + new_bom_line_form = new_bom_form.bom_line_ids.edit(line_position) + new_bom_line_form.propagate_lot_number = True + new_bom_line_form.save() + return new_bom_form.save() diff --git a/mrp_lot_number_propagation/tests/test_mrp_bom.py b/mrp_lot_number_propagation/tests/test_mrp_bom.py index 18a4604f7..6b740ab19 100644 --- a/mrp_lot_number_propagation/tests/test_mrp_bom.py +++ b/mrp_lot_number_propagation/tests/test_mrp_bom.py @@ -13,17 +13,6 @@ class TestMrpBom(Common): self.bom.product_tmpl_id.tracking = "none" self.assertFalse(self.bom.display_lot_number_propagation) - def test_bom_line_check_propagate_lot_number_multi(self): - form = Form(self.bom) - form.lot_number_propagation = True - # Flag more than one line to propagate - for i in range(len(form.bom_line_ids)): - line_form = form.bom_line_ids.edit(i) - line_form.propagate_lot_number = True - line_form.save() - with self.assertRaisesRegex(ValidationError, "Only one BoM"): - form.save() - def test_bom_line_check_propagate_lot_number_not_tracked(self): form = Form(self.bom) form.lot_number_propagation = True diff --git a/mrp_lot_number_propagation/tests/test_mrp_production.py b/mrp_lot_number_propagation/tests/test_mrp_production.py index ffbad6446..90d22ede9 100644 --- a/mrp_lot_number_propagation/tests/test_mrp_production.py +++ b/mrp_lot_number_propagation/tests/test_mrp_production.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo.exceptions import UserError +from odoo.fields import Command from odoo.tests.common import Form from .common import Common @@ -12,15 +13,24 @@ class TestMrpProduction(Common): def setUpClass(cls): super().setUpClass() # Configure the BoM to propagate lot number + cls._configure_bom() + cls.order = cls._create_order(cls.bom_product_product, cls.bom) + + @classmethod + def _configure_bom(cls): with Form(cls.bom) as form: form.lot_number_propagation = True line_form = form.bom_line_ids.edit(0) # Line tracked by SN line_form.propagate_lot_number = True line_form.save() form.save() + + @classmethod + def _create_order(cls, product, bom): with Form(cls.env["mrp.production"]) as form: - form.bom_id = cls.bom - cls.order = form.save() + form.product_id = product + form.bom_id = bom + return form.save() def _set_qty_done(self, order): for line in order.move_raw_ids.move_line_ids: @@ -46,3 +56,48 @@ class TestMrpProduction(Common): self._set_qty_done(self.order) self.order.button_mark_done() self.assertEqual(self.order.lot_producing_id.name, self.LOT_NAME) + + def test_confirm_with_variant_ok(self): + self._add_color_and_legs_variants(self.bom_product_template) + self._add_color_and_legs_variants(self.product_template_tracked_by_sn) + new_bom = self._create_bom_with_variants() + self.assertTrue(new_bom.lot_number_propagation) + # As all variants must have a single component + # where lot must be propagated, there should not be any error + for product in self.bom_product_template.product_variant_ids: + new_order = self._create_order(product, new_bom) + new_order.action_confirm() + + def test_confirm_with_variant_multiple(self): + self._add_color_and_legs_variants(self.bom_product_template) + self._add_color_and_legs_variants(self.product_template_tracked_by_sn) + new_bom = self._create_bom_with_variants() + # Remove application on variant for first bom line + # with this only the first variant of the product template + # will have a single component where lot must be propagated + new_bom.bom_line_ids[0].bom_product_template_attribute_value_ids = [ + Command.clear() + ] + for cnt, product in enumerate(self.bom_product_template.product_variant_ids): + new_order = self._create_order(product, new_bom) + if cnt == 0: + new_order.action_confirm() + else: + with self.assertRaisesRegex(UserError, "multiple components"): + new_order.action_confirm() + + def test_confirm_with_variant_no(self): + self._add_color_and_legs_variants(self.bom_product_template) + self._add_color_and_legs_variants(self.product_template_tracked_by_sn) + new_bom = self._create_bom_with_variants() + # Remove first bom line + # with this the first variant of the product template + # will not have any component where lot must be propagated + new_bom.bom_line_ids[0].unlink() + for cnt, product in enumerate(self.bom_product_template.product_variant_ids): + new_order = self._create_order(product, new_bom) + if cnt == 0: + with self.assertRaisesRegex(UserError, "no component"): + new_order.action_confirm() + else: + new_order.action_confirm() From d15c63f4a01b82ba276e8929d627c921a27ab8a9 Mon Sep 17 00:00:00 2001 From: Akim Juillerat Date: Fri, 17 Mar 2023 09:28:44 +0100 Subject: [PATCH 09/28] mrp_lot_number_propagation: Add hook to get propagating component --- mrp_lot_number_propagation/models/mrp_production.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mrp_lot_number_propagation/models/mrp_production.py b/mrp_lot_number_propagation/models/mrp_production.py index d1afe8a98..40ea6e6fd 100644 --- a/mrp_lot_number_propagation/models/mrp_production.py +++ b/mrp_lot_number_propagation/models/mrp_production.py @@ -42,9 +42,7 @@ class MrpProduction(models.Model): def _compute_propagated_lot_producing(self): for order in self: order.propagated_lot_producing = False - move_with_lot = order.move_raw_ids.filtered( - lambda o: o.propagate_lot_number - ) + move_with_lot = order._get_propagating_component_move() line_with_sn = move_with_lot.move_line_ids.filtered( lambda l: ( l.lot_id @@ -67,6 +65,10 @@ class MrpProduction(models.Model): self._set_lot_number_propagation_data_from_bom() return res + def _get_propagating_component_move(self): + self.ensure_one() + return self.move_raw_ids.filtered(lambda o: o.propagate_lot_number) + def _set_lot_number_propagation_data_from_bom(self): """Copy information from BoM to the manufacturing order.""" for order in self: From a834701720989630da3b6d6d1c5146941eacf1e8 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 17 Mar 2023 11:46:06 +0000 Subject: [PATCH 10/28] mrp_lot_number_propagation 15.0.0.1.1 --- mrp_lot_number_propagation/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrp_lot_number_propagation/__manifest__.py b/mrp_lot_number_propagation/__manifest__.py index 8e15c07e4..3e88cd36a 100644 --- a/mrp_lot_number_propagation/__manifest__.py +++ b/mrp_lot_number_propagation/__manifest__.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) { "name": "MRP Serial Number Propagation", - "version": "15.0.0.1.0", + "version": "15.0.0.1.1", "development_status": "Alpha", "license": "AGPL-3", "author": "Camptocamp, Odoo Community Association (OCA)", From ea041c5bca80d76d9e25e7e3b604bb7a88f474d3 Mon Sep 17 00:00:00 2001 From: oca-ci Date: Fri, 17 Mar 2023 13:01:08 +0000 Subject: [PATCH 11/28] [UPD] Update mrp_lot_number_propagation.pot --- .../i18n/mrp_lot_number_propagation.pot | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/mrp_lot_number_propagation/i18n/mrp_lot_number_propagation.pot b/mrp_lot_number_propagation/i18n/mrp_lot_number_propagation.pot index a52ed845c..2d22b0bca 100644 --- a/mrp_lot_number_propagation/i18n/mrp_lot_number_propagation.pot +++ b/mrp_lot_number_propagation/i18n/mrp_lot_number_propagation.pot @@ -38,6 +38,22 @@ msgstr "" msgid "Bill of Material Line" msgstr "" +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0 +#, python-format +msgid "" +"Bill of material is marked for lot number propagation, but there are " +"multiple components propagating lot number. Please check BOM configuration." +msgstr "" + +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0 +#, python-format +msgid "" +"Bill of material is marked for lot number propagation, but there are no " +"components propagating lot number. Please check BOM configuration." +msgstr "" + #. module: mrp_lot_number_propagation #: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__display_lot_number_propagation msgid "Display Lot Number Propagation" @@ -85,14 +101,6 @@ msgid "" " to the finished product." msgstr "" -#. module: mrp_lot_number_propagation -#: code:addons/mrp_lot_number_propagation/models/mrp_bom_line.py:0 -#, python-format -msgid "" -"Only one BoM line can propagate its lot/serial number to the finished " -"product." -msgstr "" - #. module: mrp_lot_number_propagation #: model:ir.model,name:mrp_lot_number_propagation.model_product_product msgid "Product" From b1c19a5a4d80a066a4381704558979f527ffd7fa Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 17 Mar 2023 13:05:02 +0000 Subject: [PATCH 12/28] mrp_lot_number_propagation 15.0.0.2.0 --- mrp_lot_number_propagation/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrp_lot_number_propagation/__manifest__.py b/mrp_lot_number_propagation/__manifest__.py index 3e88cd36a..5b5771786 100644 --- a/mrp_lot_number_propagation/__manifest__.py +++ b/mrp_lot_number_propagation/__manifest__.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) { "name": "MRP Serial Number Propagation", - "version": "15.0.0.1.1", + "version": "15.0.0.2.0", "development_status": "Alpha", "license": "AGPL-3", "author": "Camptocamp, Odoo Community Association (OCA)", From 759edbf033e499df6e240844629edf61cebd73ea Mon Sep 17 00:00:00 2001 From: Weblate Date: Fri, 17 Mar 2023 13:05:12 +0000 Subject: [PATCH 13/28] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: manufacture-15.0/manufacture-15.0-mrp_lot_number_propagation Translate-URL: https://translation.odoo-community.org/projects/manufacture-15-0/manufacture-15-0-mrp_lot_number_propagation/ --- mrp_lot_number_propagation/i18n/it.po | 49 +++++++++++++++++---------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/mrp_lot_number_propagation/i18n/it.po b/mrp_lot_number_propagation/i18n/it.po index 538d4bb38..7cb544af6 100644 --- a/mrp_lot_number_propagation/i18n/it.po +++ b/mrp_lot_number_propagation/i18n/it.po @@ -20,8 +20,7 @@ msgstr "" #: code:addons/mrp_lot_number_propagation/models/product_template.py:0 #, python-format msgid "" -"A BoM propagating serial numbers requires this product to be tracked as " -"such." +"A BoM propagating serial numbers requires this product to be tracked as such." msgstr "" "Una distinta base che propaga i numeri di serie richiede che questo prodotto " "sia tracciato come tale." @@ -45,6 +44,22 @@ msgstr "Distinta base" msgid "Bill of Material Line" msgstr "Riga distinta base" +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0 +#, python-format +msgid "" +"Bill of material is marked for lot number propagation, but there are " +"multiple components propagating lot number. Please check BOM configuration." +msgstr "" + +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0 +#, python-format +msgid "" +"Bill of material is marked for lot number propagation, but there are no " +"components propagating lot number. Please check BOM configuration." +msgstr "" + #. module: mrp_lot_number_propagation #: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__display_lot_number_propagation msgid "Display Lot Number Propagation" @@ -91,22 +106,12 @@ msgstr "" #: code:addons/mrp_lot_number_propagation/models/mrp_bom_line.py:0 #, python-format msgid "" -"Only components tracked by serial number can propagate its lot/serial number" -" to the finished product." +"Only components tracked by serial number can propagate its lot/serial number " +"to the finished product." msgstr "" "Solo i componenti tracciati da una matricola possono propagare il loro " "numero di lotto/matricola al prodotto finito." -#. module: mrp_lot_number_propagation -#: code:addons/mrp_lot_number_propagation/models/mrp_bom_line.py:0 -#, python-format -msgid "" -"Only one BoM line can propagate its lot/serial number to the finished " -"product." -msgstr "" -"Solo una riga di DiBa può propagare il suo numero di lotto/matricola al " -"prodotto finito." - #. module: mrp_lot_number_propagation #: model:ir.model,name:mrp_lot_number_propagation.model_product_product msgid "Product" @@ -141,8 +146,8 @@ msgstr "Movimento di magazzino" #. module: mrp_lot_number_propagation #: model:ir.model.fields,help:mrp_lot_number_propagation.field_mrp_production__propagated_lot_producing msgid "" -"The BoM used on this manufacturing order is set to propagate lot number from" -" one of its components. The value will be computed once the corresponding " +"The BoM used on this manufacturing order is set to propagate lot number from " +"one of its components. The value will be computed once the corresponding " "component is selected." msgstr "" "La DiBa utilizzata in questo ordine di produzione è impostata per propagare " @@ -153,8 +158,8 @@ msgstr "" #: code:addons/mrp_lot_number_propagation/models/product_template.py:0 #, python-format msgid "" -"This component is configured to propagate its serial number in the following" -" Bill of Materials:{boms}'" +"This component is configured to propagate its serial number in the following " +"Bill of Materials:{boms}'" msgstr "" "Questo componente è configurato per propagare la sua matricola nella " "seguente DiBa: {boms}'" @@ -168,3 +173,11 @@ msgid "" msgstr "" "Con 'Propagazione numero di lotto' abilitata, una riga deve essere " "configurata con l'opzione 'Propaga numero di lotto'." + +#, python-format +#~ msgid "" +#~ "Only one BoM line can propagate its lot/serial number to the finished " +#~ "product." +#~ msgstr "" +#~ "Solo una riga di DiBa può propagare il suo numero di lotto/matricola al " +#~ "prodotto finito." From 3d62d7815598c724f1bd9def27f308e308e1f1ea Mon Sep 17 00:00:00 2001 From: mymage Date: Wed, 22 Mar 2023 13:45:22 +0000 Subject: [PATCH 14/28] Translated using Weblate (Italian) Currently translated at 100.0% (23 of 23 strings) Translation: manufacture-15.0/manufacture-15.0-mrp_lot_number_propagation Translate-URL: https://translation.odoo-community.org/projects/manufacture-15-0/manufacture-15-0-mrp_lot_number_propagation/it/ --- mrp_lot_number_propagation/i18n/it.po | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mrp_lot_number_propagation/i18n/it.po b/mrp_lot_number_propagation/i18n/it.po index 7cb544af6..5155922ee 100644 --- a/mrp_lot_number_propagation/i18n/it.po +++ b/mrp_lot_number_propagation/i18n/it.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 15.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2023-01-01 13:45+0000\n" +"PO-Revision-Date: 2023-03-22 16:29+0000\n" "Last-Translator: mymage \n" "Language-Team: none\n" "Language: it\n" @@ -51,6 +51,9 @@ msgid "" "Bill of material is marked for lot number propagation, but there are " "multiple components propagating lot number. Please check BOM configuration." msgstr "" +"La distinta base è impostata per la propagazione del numero di lotto, ma ci " +"sono più componenti che propagano il numero di lotto. Verificare la " +"configurazione della DB." #. module: mrp_lot_number_propagation #: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0 @@ -59,6 +62,9 @@ msgid "" "Bill of material is marked for lot number propagation, but there are no " "components propagating lot number. Please check BOM configuration." msgstr "" +"La distinta base è impostata per la propagazione del numero di lotto, ma non " +"ci sono componenti che propagano il numero di lotto. Verificare la " +"configurazione della DB." #. module: mrp_lot_number_propagation #: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom__display_lot_number_propagation From a4e5851e241f765422b31e60de28277df7cedf85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Wed, 19 Apr 2023 14:52:38 +0200 Subject: [PATCH 15/28] mrp_lot_number_propagation: re-use existing lot --- .../models/mrp_production.py | 29 ++++++++++--- .../tests/test_mrp_production.py | 41 +++++++++++++++++++ 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/mrp_lot_number_propagation/models/mrp_production.py b/mrp_lot_number_propagation/models/mrp_production.py index 40ea6e6fd..8d8b090d4 100644 --- a/mrp_lot_number_propagation/models/mrp_production.py +++ b/mrp_lot_number_propagation/models/mrp_production.py @@ -111,13 +111,30 @@ class MrpProduction(models.Model): and m.state not in ("done", "cancel") ) if finish_moves and not finish_moves.quantity_done: - lot = self.env["stock.production.lot"].create( - { - "product_id": order.product_id.id, - "company_id": order.company_id.id, - "name": order.propagated_lot_producing, - } + lot_model = self.env["stock.production.lot"] + lot = lot_model.search( + [ + ("product_id", "=", order.product_id.id), + ("company_id", "=", order.company_id.id), + ("name", "=", order.propagated_lot_producing), + ], + limit=1, ) + if lot.quant_ids: + raise UserError( + _( + "Lot/Serial number %s already exists and has been used. " + "Unable to propagate it." + ) + ) + if not lot: + lot = self.env["stock.production.lot"].create( + { + "product_id": order.product_id.id, + "company_id": order.company_id.id, + "name": order.propagated_lot_producing, + } + ) order.with_context(lot_propagation=True).lot_producing_id = lot def write(self, vals): diff --git a/mrp_lot_number_propagation/tests/test_mrp_production.py b/mrp_lot_number_propagation/tests/test_mrp_production.py index 90d22ede9..b464e9a44 100644 --- a/mrp_lot_number_propagation/tests/test_mrp_production.py +++ b/mrp_lot_number_propagation/tests/test_mrp_production.py @@ -57,6 +57,47 @@ class TestMrpProduction(Common): self.order.button_mark_done() self.assertEqual(self.order.lot_producing_id.name, self.LOT_NAME) + def test_order_post_inventory_lot_already_exists_but_not_used(self): + self._update_stock_component_qty(self.order) + self.order.action_confirm() + self._set_qty_done(self.order) + self.assertEqual(self.order.propagated_lot_producing, self.LOT_NAME) + # Create a lot with the same number for the finished product + # without any stock/quants (so not used at all) before validating the MO + existing_lot = self.env["stock.production.lot"].create( + { + "product_id": self.order.product_id.id, + "company_id": self.order.company_id.id, + "name": self.order.propagated_lot_producing, + } + ) + self.order.button_mark_done() + self.assertEqual(self.order.lot_producing_id, existing_lot) + + def test_order_post_inventory_lot_already_exists_and_used(self): + self._update_stock_component_qty(self.order) + self.order.action_confirm() + self._set_qty_done(self.order) + self.assertEqual(self.order.propagated_lot_producing, self.LOT_NAME) + # Create a lot with the same number for the finished product + # with some stock/quants (so it is considered as used) before + # validating the MO + existing_lot = self.env["stock.production.lot"].create( + { + "product_id": self.order.product_id.id, + "company_id": self.order.company_id.id, + "name": self.order.propagated_lot_producing, + } + ) + self._update_qty_in_location( + self.env.ref("stock.stock_location_stock"), + self.order.product_id, + 1, + lot=existing_lot, + ) + with self.assertRaisesRegex(UserError, "already exists and has been used"): + self.order.button_mark_done() + def test_confirm_with_variant_ok(self): self._add_color_and_legs_variants(self.bom_product_template) self._add_color_and_legs_variants(self.product_template_tracked_by_sn) From f911e776674ac9d7df8c9d974841161c3d400b3b Mon Sep 17 00:00:00 2001 From: oca-ci Date: Fri, 21 Apr 2023 07:40:31 +0000 Subject: [PATCH 16/28] [UPD] Update mrp_lot_number_propagation.pot --- .../i18n/mrp_lot_number_propagation.pot | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mrp_lot_number_propagation/i18n/mrp_lot_number_propagation.pot b/mrp_lot_number_propagation/i18n/mrp_lot_number_propagation.pot index 2d22b0bca..3f4b0d5e3 100644 --- a/mrp_lot_number_propagation/i18n/mrp_lot_number_propagation.pot +++ b/mrp_lot_number_propagation/i18n/mrp_lot_number_propagation.pot @@ -79,6 +79,14 @@ msgstr "" msgid "Lot/Serial Number" msgstr "" +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0 +#, python-format +msgid "" +"Lot/Serial number %s already exists and has been used. Unable to propagate " +"it." +msgstr "" + #. module: mrp_lot_number_propagation #: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0 #, python-format From 7470bfe12ab3faefc50c2157243d952522848038 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 21 Apr 2023 07:45:49 +0000 Subject: [PATCH 17/28] mrp_lot_number_propagation 15.0.0.3.0 --- mrp_lot_number_propagation/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrp_lot_number_propagation/__manifest__.py b/mrp_lot_number_propagation/__manifest__.py index 5b5771786..7f28eef70 100644 --- a/mrp_lot_number_propagation/__manifest__.py +++ b/mrp_lot_number_propagation/__manifest__.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) { "name": "MRP Serial Number Propagation", - "version": "15.0.0.2.0", + "version": "15.0.0.3.0", "development_status": "Alpha", "license": "AGPL-3", "author": "Camptocamp, Odoo Community Association (OCA)", From 184fe63d215ea9d7e8ccf7945b729f7de5f39b8b Mon Sep 17 00:00:00 2001 From: Weblate Date: Fri, 21 Apr 2023 10:16:23 +0000 Subject: [PATCH 18/28] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: manufacture-15.0/manufacture-15.0-mrp_lot_number_propagation Translate-URL: https://translation.odoo-community.org/projects/manufacture-15-0/manufacture-15-0-mrp_lot_number_propagation/ --- mrp_lot_number_propagation/i18n/it.po | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mrp_lot_number_propagation/i18n/it.po b/mrp_lot_number_propagation/i18n/it.po index 5155922ee..6f0cece28 100644 --- a/mrp_lot_number_propagation/i18n/it.po +++ b/mrp_lot_number_propagation/i18n/it.po @@ -91,6 +91,14 @@ msgstr "Propagazione numero di lotto" msgid "Lot/Serial Number" msgstr "Numero di lotto/matricola" +#. module: mrp_lot_number_propagation +#: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0 +#, python-format +msgid "" +"Lot/Serial number %s already exists and has been used. Unable to propagate " +"it." +msgstr "" + #. module: mrp_lot_number_propagation #: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0 #, python-format From 557e7703e2347714a591158db77f762978de15d5 Mon Sep 17 00:00:00 2001 From: mymage Date: Tue, 25 Apr 2023 19:05:34 +0000 Subject: [PATCH 19/28] Translated using Weblate (Italian) Currently translated at 100.0% (24 of 24 strings) Translation: manufacture-15.0/manufacture-15.0-mrp_lot_number_propagation Translate-URL: https://translation.odoo-community.org/projects/manufacture-15-0/manufacture-15-0-mrp_lot_number_propagation/it/ --- mrp_lot_number_propagation/i18n/it.po | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mrp_lot_number_propagation/i18n/it.po b/mrp_lot_number_propagation/i18n/it.po index 6f0cece28..753cfd7d8 100644 --- a/mrp_lot_number_propagation/i18n/it.po +++ b/mrp_lot_number_propagation/i18n/it.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 15.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2023-03-22 16:29+0000\n" +"PO-Revision-Date: 2023-04-25 21:34+0000\n" "Last-Translator: mymage \n" "Language-Team: none\n" "Language: it\n" @@ -98,6 +98,7 @@ msgid "" "Lot/Serial number %s already exists and has been used. Unable to propagate " "it." msgstr "" +"Il lotto/seriale %s esiste ed è stato usato. Non è possibile propagarlo." #. module: mrp_lot_number_propagation #: code:addons/mrp_lot_number_propagation/models/mrp_production.py:0 From 9eca80f8c320fc65d7dc79d63f80b878f6b49788 Mon Sep 17 00:00:00 2001 From: Francesco Foresti Date: Mon, 15 May 2023 16:52:39 +0000 Subject: [PATCH 20/28] Translated using Weblate (Italian) Currently translated at 100.0% (24 of 24 strings) Translation: manufacture-15.0/manufacture-15.0-mrp_lot_number_propagation Translate-URL: https://translation.odoo-community.org/projects/manufacture-15-0/manufacture-15-0-mrp_lot_number_propagation/it/ --- mrp_lot_number_propagation/i18n/it.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mrp_lot_number_propagation/i18n/it.po b/mrp_lot_number_propagation/i18n/it.po index 753cfd7d8..be644160e 100644 --- a/mrp_lot_number_propagation/i18n/it.po +++ b/mrp_lot_number_propagation/i18n/it.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 15.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2023-04-25 21:34+0000\n" -"Last-Translator: mymage \n" +"PO-Revision-Date: 2023-05-15 19:35+0000\n" +"Last-Translator: Francesco Foresti \n" "Language-Team: none\n" "Language: it\n" "MIME-Version: 1.0\n" @@ -140,7 +140,7 @@ msgstr "Modello prodotto" #. module: mrp_lot_number_propagation #: model:ir.model,name:mrp_lot_number_propagation.model_mrp_production msgid "Production Order" -msgstr "Ordine produzione" +msgstr "Ordine di produzione" #. module: mrp_lot_number_propagation #: model:ir.model.fields,field_description:mrp_lot_number_propagation.field_mrp_bom_line__propagate_lot_number From 96a2707453cdba4fc8679627685228d119f486f7 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Sun, 3 Sep 2023 14:31:15 +0000 Subject: [PATCH 21/28] [UPD] README.rst --- mrp_lot_number_propagation/README.rst | 15 ++++--- .../static/description/index.html | 40 ++++++++++--------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/mrp_lot_number_propagation/README.rst b/mrp_lot_number_propagation/README.rst index a8c440515..dc57852a9 100644 --- a/mrp_lot_number_propagation/README.rst +++ b/mrp_lot_number_propagation/README.rst @@ -2,10 +2,13 @@ MRP Serial Number Propagation ============================= -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:4d9e995af3bffb562270ff4228e8aa2c1aa3fb4433aa4dea9bcb25f512a96922 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png :target: https://odoo-community.org/page/development-status @@ -19,11 +22,11 @@ MRP Serial Number Propagation .. |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-mrp_lot_number_propagation :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/15.0 - :alt: Try me on Runbot +.. |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| +|badge1| |badge2| |badge3| |badge4| |badge5| Allow to propagate a lot number from a component to a finished product. @@ -47,7 +50,7 @@ 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 +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. diff --git a/mrp_lot_number_propagation/static/description/index.html b/mrp_lot_number_propagation/static/description/index.html index 374465026..aef38a90b 100644 --- a/mrp_lot_number_propagation/static/description/index.html +++ b/mrp_lot_number_propagation/static/description/index.html @@ -1,20 +1,20 @@ - + - + MRP Serial Number Propagation - - -
-

MRP Serial Number Propagation

- - -

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

-

Allow to propagate a lot number from a component to a finished product.

-

Table of contents

- -
-

Known issues / Roadmap

-
    -
  • Add compatibility with lot number (in addition to serial number)
  • -
-
-
-

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

-
    -
  • Camptocamp
  • -
-
-
-

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.

-

Current maintainer:

-

sebalix

-

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_lot_number_propagation/tests/common.py b/mrp_lot_number_propagation/tests/common.py index 19194492e..72cb3cf84 100644 --- a/mrp_lot_number_propagation/tests/common.py +++ b/mrp_lot_number_propagation/tests/common.py @@ -8,7 +8,7 @@ from odoo import fields from odoo.tests import Form, common -class Common(common.TransactionCase): +class Common(common.SavepointCase): LOT_NAME = "PROPAGATED-LOT" @@ -121,9 +121,7 @@ class Common(common.TransactionCase): { "product_tmpl_id": product_template.id, "attribute_id": attribute.id, - "value_ids": [ - fields.Command.set([att_val.id for att_val in att_values_list]) - ], + "value_ids": [(6, 0, [att_val.id for att_val in att_values_list])], } ) @@ -145,7 +143,7 @@ class Common(common.TransactionCase): att_values_commands = [] for att_value in product.product_template_attribute_value_ids: att_values_commands.append( - fields.Command.link(attribute_values_dict[att_value.name]) + (4, attribute_values_dict[att_value.name], 0) ) create_values[ "bom_product_template_attribute_value_ids" diff --git a/mrp_lot_number_propagation/tests/test_mrp_production.py b/mrp_lot_number_propagation/tests/test_mrp_production.py index b464e9a44..c4030e134 100644 --- a/mrp_lot_number_propagation/tests/test_mrp_production.py +++ b/mrp_lot_number_propagation/tests/test_mrp_production.py @@ -2,7 +2,6 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo.exceptions import UserError -from odoo.fields import Command from odoo.tests.common import Form from .common import Common @@ -41,6 +40,7 @@ class TestMrpProduction(Common): self.assertTrue(self.order.is_lot_number_propagated) # set by onchange self._update_stock_component_qty(self.order) self.order.action_confirm() + self.order.action_assign() self.assertTrue(self.order.is_lot_number_propagated) # set by action_confirm self.assertTrue(any(self.order.move_raw_ids.mapped("propagate_lot_number"))) self._set_qty_done(self.order) @@ -53,13 +53,16 @@ class TestMrpProduction(Common): def test_order_post_inventory(self): self._update_stock_component_qty(self.order) self.order.action_confirm() + self.order.action_assign() self._set_qty_done(self.order) + self.assertTrue(self.order.is_lot_number_propagated) # set by action_confirm self.order.button_mark_done() self.assertEqual(self.order.lot_producing_id.name, self.LOT_NAME) def test_order_post_inventory_lot_already_exists_but_not_used(self): self._update_stock_component_qty(self.order) self.order.action_confirm() + self.order.action_assign() self._set_qty_done(self.order) self.assertEqual(self.order.propagated_lot_producing, self.LOT_NAME) # Create a lot with the same number for the finished product @@ -77,6 +80,7 @@ class TestMrpProduction(Common): def test_order_post_inventory_lot_already_exists_and_used(self): self._update_stock_component_qty(self.order) self.order.action_confirm() + self.order.action_assign() self._set_qty_done(self.order) self.assertEqual(self.order.propagated_lot_producing, self.LOT_NAME) # Create a lot with the same number for the finished product @@ -116,9 +120,7 @@ class TestMrpProduction(Common): # Remove application on variant for first bom line # with this only the first variant of the product template # will have a single component where lot must be propagated - new_bom.bom_line_ids[0].bom_product_template_attribute_value_ids = [ - Command.clear() - ] + new_bom.bom_line_ids[0].bom_product_template_attribute_value_ids = [(5, 0, 0)] for cnt, product in enumerate(self.bom_product_template.product_variant_ids): new_order = self._create_order(product, new_bom) if cnt == 0: @@ -131,14 +133,15 @@ class TestMrpProduction(Common): self._add_color_and_legs_variants(self.bom_product_template) self._add_color_and_legs_variants(self.product_template_tracked_by_sn) new_bom = self._create_bom_with_variants() - # Remove first bom line - # with this the first variant of the product template - # will not have any component where lot must be propagated - new_bom.bom_line_ids[0].unlink() for cnt, product in enumerate(self.bom_product_template.product_variant_ids): new_order = self._create_order(product, new_bom) if cnt == 0: + # Fake the case with no component by removing the flag + new_order.move_raw_ids.bom_line_id.write( + {"propagate_lot_number": False} + ) with self.assertRaisesRegex(UserError, "no component"): new_order.action_confirm() + new_order.action_assign() else: new_order.action_confirm()