From 7b7a8b4f2be86bc59318745103d6779139a8712b Mon Sep 17 00:00:00 2001 From: Lois Rilo Date: Fri, 4 Mar 2022 12:01:02 +0100 Subject: [PATCH 1/3] [FIX] rma: do not force reservation on supplier RMA deliveries --- rma/models/__init__.py | 7 +- rma/models/procurement_group.py | 15 ++++ rma/models/{stock.py => stock_move.py} | 71 ++++++++++++++----- rma/models/{procurement.py => stock_rule.py} | 17 +---- rma/wizards/rma_make_picking.py | 74 ++++++++++---------- 5 files changed, 110 insertions(+), 74 deletions(-) create mode 100644 rma/models/procurement_group.py rename rma/models/{stock.py => stock_move.py} (52%) rename rma/models/{procurement.py => stock_rule.py} (70%) diff --git a/rma/models/__init__.py b/rma/models/__init__.py index ae4f5826..612e4aaa 100644 --- a/rma/models/__init__.py +++ b/rma/models/__init__.py @@ -1,13 +1,12 @@ -# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) - from . import rma_order from . import rma_order_line from . import rma_operation -from . import stock +from . import stock_move from . import stock_warehouse from . import product from . import product_category -from . import procurement +from . import procurement_group +from . import stock_rule from . import res_partner from . import res_company from . import res_config_settings diff --git a/rma/models/procurement_group.py b/rma/models/procurement_group.py new file mode 100644 index 00000000..5cac27ba --- /dev/null +++ b/rma/models/procurement_group.py @@ -0,0 +1,15 @@ +# Copyright (C) 2017-22 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +from odoo import fields, models + + +class ProcurementGroup(models.Model): + _inherit = "procurement.group" + + rma_id = fields.Many2one( + comodel_name="rma.order", string="RMA", ondelete="set null" + ) + rma_line_id = fields.Many2one( + comodel_name="rma.order.line", string="RMA line", ondelete="set null" + ) diff --git a/rma/models/stock.py b/rma/models/stock_move.py similarity index 52% rename from rma/models/stock.py rename to rma/models/stock_move.py index 2cab4bb3..c227e3f9 100644 --- a/rma/models/stock.py +++ b/rma/models/stock_move.py @@ -4,25 +4,6 @@ from odoo import api, fields, models -class StockPicking(models.Model): - _inherit = "stock.picking" - - def action_assign(self): - """When you try to bring back a product from a customer location, - it may happen that there is no quants available to perform the - picking.""" - res = super(StockPicking, self).action_assign() - for picking in self: - for move in picking.move_lines: - if ( - move.rma_line_id - and move.state == "confirmed" - and move.location_id.usage == "customer" - ): - move.force_assign() - return res - - class StockMove(models.Model): _inherit = "stock.move" @@ -66,3 +47,55 @@ class StockMove(models.Model): if self.env.context.get("force_no_bypass_reservation"): return False return res + + def _get_available_quantity( + self, + location_id, + lot_id=None, + package_id=None, + owner_id=None, + strict=False, + allow_negative=False, + ): + if ( + not lot_id + and self.rma_line_id.lot_id + and self.location_id.usage == "internal" + ): + # In supplier RMA deliveries we can only send the RMA lot/serial. + lot_id = self.rma_line_id.lot_id + return super()._get_available_quantity( + location_id, + lot_id=lot_id, + package_id=package_id, + owner_id=owner_id, + strict=strict, + allow_negative=allow_negative, + ) + + def _update_reserved_quantity( + self, + need, + available_quantity, + location_id, + lot_id=None, + package_id=None, + owner_id=None, + strict=True, + ): + if ( + not lot_id + and self.rma_line_id.lot_id + and self.location_id.usage == "internal" + ): + # In supplier RMA deliveries we can only send the RMA lot/serial. + lot_id = self.rma_line_id.lot_id + return super()._update_reserved_quantity( + need, + available_quantity, + location_id, + lot_id=lot_id, + package_id=package_id, + owner_id=owner_id, + strict=strict, + ) diff --git a/rma/models/procurement.py b/rma/models/stock_rule.py similarity index 70% rename from rma/models/procurement.py rename to rma/models/stock_rule.py index 132376a9..bf1340da 100644 --- a/rma/models/procurement.py +++ b/rma/models/stock_rule.py @@ -1,7 +1,7 @@ -# Copyright (C) 2017-20 ForgeFlow S.L. +# Copyright (C) 2017-22 ForgeFlow S.L. # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) -from odoo import fields, models +from odoo import models class StockRule(models.Model): @@ -18,7 +18,7 @@ class StockRule(models.Model): company_id, values, ): - res = super(StockRule, self)._get_stock_move_values( + res = super()._get_stock_move_values( product_id, product_qty, product_uom, @@ -39,14 +39,3 @@ class StockRule(models.Model): if dest_loc.usage == "internal": res["price_unit"] = line.price_unit return res - - -class ProcurementGroup(models.Model): - _inherit = "procurement.group" - - rma_id = fields.Many2one( - comodel_name="rma.order", string="RMA", ondelete="set null" - ) - rma_line_id = fields.Many2one( - comodel_name="rma.order.line", string="RMA line", ondelete="set null" - ) diff --git a/rma/wizards/rma_make_picking.py b/rma/wizards/rma_make_picking.py index 6951950f..2c459e4c 100644 --- a/rma/wizards/rma_make_picking.py +++ b/rma/wizards/rma_make_picking.py @@ -191,43 +191,46 @@ class RmaMakePicking(models.TransientModel): self._create_picking() move_line_model = self.env["stock.move.line"] picking_type = self.env.context.get("picking_type") - pickings = self.env["stock.picking"].browse() if picking_type == "outgoing": pickings = self.mapped("item_ids.line_id")._get_out_pickings() action = self.item_ids.line_id.action_view_out_shipments() else: pickings = self.mapped("item_ids.line_id")._get_in_pickings() action = self.item_ids.line_id.action_view_in_shipments() - for move in pickings.move_lines.filtered( - lambda x: x.state not in ("draft", "cancel", "done") - and x.rma_line_id - and x.product_id.tracking in ("lot", "serial") - and x.rma_line_id.lot_id - ): - move.move_line_ids.unlink() - if move.product_id.tracking == "serial": - move.write({"lot_ids": [(6, 0, move.rma_line_id.lot_id.ids)]}) - move.move_line_ids.write({"product_uom_qty": 1, "qty_done": 0}) - elif move.product_id.tracking == "lot": - if picking_type == "incoming": - qty = self.item_ids.filtered( - lambda x: x.line_id.id == move.rma_line_id.id - ).qty_to_receive - else: - qty = self.item_ids.filtered( - lambda x: x.line_id.id == move.rma_line_id.id - ).qty_to_deliver - move_line_data = move._prepare_move_line_vals() - move_line_data.update( - { - "lot_id": move.rma_line_id.lot_id.id, - "product_uom_id": move.product_id.uom_id.id, - "qty_done": 0, - "product_uom_qty": qty, - } - ) - move_line_model.create(move_line_data) if picking_type == "incoming": + # Force the reservation of the RMA specific lot for incoming shipments. + # FIXME: still needs fixing, not reserving appropriate serials. + for move in pickings.move_lines.filtered( + lambda x: x.state not in ("draft", "cancel", "done") + and x.rma_line_id + and x.product_id.tracking in ("lot", "serial") + and x.rma_line_id.lot_id + ): + # Force the reservation of the RMA specific lot for incoming shipments. + move.move_line_ids.unlink() + if move.product_id.tracking == "serial": + move.write({"lot_ids": [(6, 0, move.rma_line_id.lot_id.ids)]}) + move.move_line_ids.write({"product_uom_qty": 1, "qty_done": 0}) + elif move.product_id.tracking == "lot": + if picking_type == "incoming": + qty = self.item_ids.filtered( + lambda x: x.line_id.id == move.rma_line_id.id + ).qty_to_receive + else: + qty = self.item_ids.filtered( + lambda x: x.line_id.id == move.rma_line_id.id + ).qty_to_deliver + move_line_data = move._prepare_move_line_vals() + move_line_data.update( + { + "lot_id": move.rma_line_id.lot_id.id, + "product_uom_id": move.product_id.uom_id.id, + "qty_done": 0, + "product_uom_qty": qty, + } + ) + move_line_model.create(move_line_data) + pickings.with_context(force_no_bypass_reservation=True).action_assign() return action @@ -241,18 +244,15 @@ class RmaMakePickingItem(models.TransientModel): wiz_id = fields.Many2one("rma_make_picking.wizard", string="Wizard", required=True) line_id = fields.Many2one( - "rma.order.line", string="RMA order Line", readonly=True, ondelete="cascade" + "rma.order.line", string="RMA order Line", ondelete="cascade" ) - rma_id = fields.Many2one( - "rma.order", related="line_id.rma_id", string="RMA Group", readonly=True - ) - product_id = fields.Many2one("product.product", string="Product", readonly=True) + rma_id = fields.Many2one("rma.order", related="line_id.rma_id", string="RMA Group") + product_id = fields.Many2one("product.product", string="Product") product_qty = fields.Float( related="line_id.product_qty", string="Quantity Ordered", copy=False, digits="Product Unit of Measure", - readonly=True, ) qty_to_receive = fields.Float( string="Quantity to Receive", digits="Product Unit of Measure" @@ -260,4 +260,4 @@ class RmaMakePickingItem(models.TransientModel): qty_to_deliver = fields.Float( string="Quantity To Deliver", digits="Product Unit of Measure" ) - uom_id = fields.Many2one("uom.uom", string="Unit of Measure", readonly=True) + uom_id = fields.Many2one("uom.uom", string="Unit of Measure") From 4a4d9d8ec8493a4c6afa22032feff73e69989b6a Mon Sep 17 00:00:00 2001 From: Christopher Ormaza Date: Thu, 3 Mar 2022 11:53:14 -0500 Subject: [PATCH 2/3] [14.0][FIX] rma: separate stock.move by rma_line_id to fix picking association --- rma/models/stock_move.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rma/models/stock_move.py b/rma/models/stock_move.py index c227e3f9..c17b0170 100644 --- a/rma/models/stock_move.py +++ b/rma/models/stock_move.py @@ -99,3 +99,8 @@ class StockMove(models.Model): owner_id=owner_id, strict=strict, ) + + @api.model + def _prepare_merge_moves_distinct_fields(self): + res = super()._prepare_merge_moves_distinct_fields() + return res + ["rma_line_id"] From 07645ab72bd8edf32fa7ef55df8fe201c7525286 Mon Sep 17 00:00:00 2001 From: Christopher Ormaza Date: Mon, 7 Mar 2022 08:23:17 -0500 Subject: [PATCH 3/3] [IMP] write method replaced for backported implementation --- rma/models/stock_move.py | 31 +++++++++++++++++++++++++++++++ rma/wizards/rma_make_picking.py | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/rma/models/stock_move.py b/rma/models/stock_move.py index c17b0170..886f095e 100644 --- a/rma/models/stock_move.py +++ b/rma/models/stock_move.py @@ -104,3 +104,34 @@ class StockMove(models.Model): def _prepare_merge_moves_distinct_fields(self): res = super()._prepare_merge_moves_distinct_fields() return res + ["rma_line_id"] + + def _set_lot_ids(self, lot_ids): + lots = self.env["stock.move"].browse(lot_ids) + self.ensure_one() + for move in self: + if move.product_id.tracking != "serial": + continue + move_lines_commands = [] + if move.picking_type_id.show_reserved is False: + mls = move.move_line_nosuggest_ids + else: + mls = move.move_line_ids + mls = mls.filtered(lambda ml: ml.lot_id) + for ml in mls: + if ml.qty_done and ml.lot_id not in lots: + move_lines_commands.append((2, ml.id)) + ls = move.move_line_ids.lot_id + for lot in lots: + if lot not in ls: + move_line_vals = self._prepare_move_line_vals(quantity=0) + move_line_vals["lot_id"] = lot.id + move_line_vals["lot_name"] = lot.name + move_line_vals["product_uom_id"] = move.product_id.uom_id.id + move_line_vals["qty_done"] = 1 + move_lines_commands.append((0, 0, move_line_vals)) + else: + move_line = move.move_line_ids.filtered( + lambda line: line.lot_id.id == lot.id + ) + move_line.qty_done = 1 + move.write({"move_line_ids": move_lines_commands}) diff --git a/rma/wizards/rma_make_picking.py b/rma/wizards/rma_make_picking.py index 2c459e4c..1848a0bb 100644 --- a/rma/wizards/rma_make_picking.py +++ b/rma/wizards/rma_make_picking.py @@ -209,7 +209,7 @@ class RmaMakePicking(models.TransientModel): # Force the reservation of the RMA specific lot for incoming shipments. move.move_line_ids.unlink() if move.product_id.tracking == "serial": - move.write({"lot_ids": [(6, 0, move.rma_line_id.lot_id.ids)]}) + move._set_lot_ids(move.rma_line_id.lot_id.ids) move.move_line_ids.write({"product_uom_qty": 1, "qty_done": 0}) elif move.product_id.tracking == "lot": if picking_type == "incoming":