From dc744bb492fdbb0191d3c64cb07ec06e8b2baaf0 Mon Sep 17 00:00:00 2001 From: DavidJForgeFlow Date: Mon, 28 Oct 2024 09:30:17 +0100 Subject: [PATCH] [IMP] repair_stock_move: relate lot_id and common methods The value of lot id in repair lines is not related to the one of the move related. The idea is to make them correlated to improve visibility. Also added stock_rule._get_custom_move_fields method from repair_picking as it should be added in the module that adds the field. --- repair_stock_move/models/__init__.py | 1 + repair_stock_move/models/repair_line.py | 12 +++ repair_stock_move/models/stock_rule.py | 13 +++ .../tests/test_repair_stock_move.py | 102 +++++++++++++++++- 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 repair_stock_move/models/stock_rule.py diff --git a/repair_stock_move/models/__init__.py b/repair_stock_move/models/__init__.py index 3e8851515c7..6c1b1822a23 100644 --- a/repair_stock_move/models/__init__.py +++ b/repair_stock_move/models/__init__.py @@ -1,3 +1,4 @@ from . import repair_order from . import repair_line from . import stock_move +from . import stock_rule diff --git a/repair_stock_move/models/repair_line.py b/repair_stock_move/models/repair_line.py index 02f6bc05371..58d2e583647 100644 --- a/repair_stock_move/models/repair_line.py +++ b/repair_stock_move/models/repair_line.py @@ -11,6 +11,8 @@ class RepairLine(models.Model): comodel_name="stock.move", inverse_name="repair_line_id", ) + # Actually the lot_id does not matches the value of the related move.line + lot_id = fields.Many2one(compute="_compute_lot_id", readonly=False, store=True) def create_stock_move(self): self.ensure_one() @@ -50,5 +52,15 @@ def _onchange_location(self): if self.state == "draft": self.location_id = self.repair_id.location_id + @api.depends("move_id.move_line_ids.lot_id", "move_id.move_orig_ids.state") + def _compute_lot_id(self): + for rec in self: + # The repair line only can contain 1 lot and the stock.move can have multiple, so + # if the lot id in the repair line is not present in the stock.move, + # it will be substituted by the first lot in the stock.move. + if rec.move_id.move_line_ids: + lots = rec.move_id.move_line_ids.mapped("lot_id") + rec.lot_id = lots[0] if len(lots) else False + # TODO: write qty - update stock move. # TODO: default repair location in repair lines. diff --git a/repair_stock_move/models/stock_rule.py b/repair_stock_move/models/stock_rule.py new file mode 100644 index 00000000000..acd70b13b3e --- /dev/null +++ b/repair_stock_move/models/stock_rule.py @@ -0,0 +1,13 @@ +# Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import models + + +class StockRule(models.Model): + _inherit = "stock.rule" + + def _get_custom_move_fields(self): + fields = super(StockRule, self)._get_custom_move_fields() + fields += ["repair_line_id"] + return fields diff --git a/repair_stock_move/tests/test_repair_stock_move.py b/repair_stock_move/tests/test_repair_stock_move.py index 80b1a126334..6e3e1a37afb 100644 --- a/repair_stock_move/tests/test_repair_stock_move.py +++ b/repair_stock_move/tests/test_repair_stock_move.py @@ -25,12 +25,21 @@ def setUpClass(cls): cls.product_3 = cls.env["product.product"].create( {"name": "Large Cabinet", "type": "product"} ) + cls.product_with_lot = cls.env["product.product"].create( + {"name": "Product Lot", "type": "product", "tracking": "serial"} + ) cls.service = cls.env["product.product"].create( { "name": "Repair Services", "type": "service", } ) + cls.lot1 = cls.env["stock.production.lot"].create( + {"product_id": cls.product_with_lot.id, "name": "1"} + ) + cls.lot2 = cls.env["stock.production.lot"].create( + {"product_id": cls.product_with_lot.id, "name": "2"} + ) # Location cls.stock_warehouse = cls.env["stock.warehouse"].search( @@ -53,6 +62,12 @@ def setUpClass(cls): cls.env["stock.quant"]._update_available_quantity( cls.product_3, cls.stock_location_14, 1 ) + cls.env["stock.quant"]._update_available_quantity( + cls.product_with_lot, cls.stock_location_14, 1, lot_id=cls.lot1 + ) + cls.env["stock.quant"]._update_available_quantity( + cls.product_with_lot, cls.stock_location_14, 1, lot_id=cls.lot2 + ) # Repair Orders dest_loc_id = cls.product_1.property_stock_production.id @@ -104,7 +119,7 @@ def setUpClass(cls): cls.env.user.groups_id |= cls.env.ref("stock.group_stock_user") - def test_stock_move_state(self): + def test_01_stock_move_state(self): # Validate Repair Order self.repair1.action_validate() self.assertEqual( @@ -144,6 +159,91 @@ def test_stock_move_state(self): "Repair line state should be done", ) + def test_02_lot_assignation(self): + repair1 = self.env["repair.order"].create( + { + "address_id": self.res_partner_address_1.id, + "guarantee_limit": "2019-01-01", + "invoice_method": "none", + "user_id": False, + "product_id": self.product_1.id, + "product_uom": self.env.ref("uom.product_uom_unit").id, + "partner_invoice_id": self.res_partner_address_1.id, + "location_id": self.stock_location_14.id, + "operations": [ + ( + 0, + 0, + { + "location_dest_id": self.product_1.property_stock_production.id, + "location_id": self.stock_location_14.id, + "name": self.product_1.display_name, + "product_id": self.product_with_lot.id, + "product_uom": self.env.ref("uom.product_uom_unit").id, + "product_uom_qty": 1.0, + "lot_id": self.lot1.id, + "price_unit": 50.0, + "state": "draft", + "type": "add", + "company_id": self.env.company.id, + }, + ) + ], + "partner_id": self.res_partner_12.id, + } + ) + repair1.action_validate() + repair1.action_assign() + self.assertEqual(repair1.operations.lot_id, self.lot1) + self.assertEqual( + repair1.operations.lot_id, repair1.operations.move_id.move_line_ids.lot_id + ) + repair1.action_repair_cancel() + repair2 = self.env["repair.order"].create( + { + "address_id": self.res_partner_address_1.id, + "guarantee_limit": "2019-01-01", + "invoice_method": "none", + "user_id": False, + "product_id": self.product_1.id, + "product_uom": self.env.ref("uom.product_uom_unit").id, + "partner_invoice_id": self.res_partner_address_1.id, + "location_id": self.stock_location_14.id, + "operations": [ + ( + 0, + 0, + { + "location_dest_id": self.product_1.property_stock_production.id, + "location_id": self.stock_location_14.id, + "name": self.product_1.display_name, + "product_id": self.product_with_lot.id, + "product_uom": self.env.ref("uom.product_uom_unit").id, + "product_uom_qty": 1.0, + "lot_id": self.lot2.id, + "price_unit": 50.0, + "state": "draft", + "type": "add", + "company_id": self.env.company.id, + }, + ) + ], + "partner_id": self.res_partner_12.id, + } + ) + repair2.action_validate() + repair2.action_assign() + self.assertEqual(repair2.operations.lot_id, self.lot2) + self.assertEqual( + repair2.operations.lot_id, repair2.operations.move_id.move_line_ids.lot_id + ) + repair2.operations.move_id.move_line_ids.lot_id = self.lot1 + + self.assertEqual(repair2.operations.lot_id, self.lot1) + self.assertEqual( + repair2.operations.lot_id, repair2.operations.move_id.move_line_ids.lot_id + ) + def _create_simple_repair_order(self, invoice_method): product_to_repair = self.product_1 partner = self.res_partner_address_1