diff --git a/rma/__manifest__.py b/rma/__manifest__.py index 015ed11d1..9993ce8f2 100644 --- a/rma/__manifest__.py +++ b/rma/__manifest__.py @@ -3,7 +3,7 @@ { "name": "RMA (Return Merchandise Authorization)", - "version": "16.0.1.0.0", + "version": "16.0.1.1.0", "license": "LGPL-3", "category": "RMA", "summary": "Introduces the return merchandise authorization (RMA) process in odoo", diff --git a/rma/data/rma_operation.xml b/rma/data/rma_operation.xml index 92e3fec00..fb642255a 100644 --- a/rma/data/rma_operation.xml +++ b/rma/data/rma_operation.xml @@ -8,6 +8,8 @@ customer + True + False @@ -18,6 +20,8 @@ supplier + False + True diff --git a/rma/migrations/16.0.1.1.0/post-migration.py b/rma/migrations/16.0.1.1.0/post-migration.py new file mode 100644 index 000000000..55cf245b8 --- /dev/null +++ b/rma/migrations/16.0.1.1.0/post-migration.py @@ -0,0 +1,28 @@ +# Copyright 2024 ForgeFlow S.L. (https://www.forgeflow.com) +import logging + +_logger = logging.getLogger(__name__) + + +def _update_rma_operations(cr): + _logger.info( + "Updating rma operations to preset in_force_same_lot and out_force_same_lot" + ) + cr.execute( + """ + UPDATE rma_operation + SET in_force_same_lot=True + WHERE type='customer'; + """ + ) + cr.execute( + """ + UPDATE rma_operation + SET out_force_same_lot=True + WHERE type='supplier'; + """ + ) + + +def migrate(cr, version): + _update_rma_operations(cr) diff --git a/rma/models/rma_operation.py b/rma/models/rma_operation.py index bd3d0f167..2806fcb32 100644 --- a/rma/models/rma_operation.py +++ b/rma/models/rma_operation.py @@ -94,3 +94,22 @@ def _default_routes(self): required=True, default=lambda self: self.env.user.company_id, ) + in_force_same_lot = fields.Boolean( + string="Force same lot in incoming shipments", + help="Forces the same lot to be used " + "in incoming pickings as the one indicated in the RMA", + ) + out_force_same_lot = fields.Boolean( + string="Force same lot in outgoing shipments", + help="Forces the same lot to be used " + "in outgoing pickings as the one indicated in the RMA", + ) + + @api.onchange("type") + def _onchange_type(self): + if self.type == "customer": + self.in_force_same_lot = True + self.out_force_same_lot = False + elif self.type == "supplier": + self.in_force_same_lot = False + self.out_force_same_lot = True diff --git a/rma/models/stock_move.py b/rma/models/stock_move.py index 9a839f180..52cbf72fe 100644 --- a/rma/models/stock_move.py +++ b/rma/models/stock_move.py @@ -62,6 +62,7 @@ def _get_available_quantity( not lot_id and self.rma_line_id.lot_id and self.location_id.usage == "internal" + and self.rma_line_id.operation_id.out_force_same_lot ): # In supplier RMA deliveries we can only send the RMA lot/serial. lot_id = self.rma_line_id.lot_id @@ -88,6 +89,7 @@ def _update_reserved_quantity( not lot_id and self.rma_line_id.lot_id and self.location_id.usage == "internal" + and self.rma_line_id.operation_id.out_force_same_lot ): # In supplier RMA deliveries we can only send the RMA lot/serial. lot_id = self.rma_line_id.lot_id diff --git a/rma/views/rma_operation_view.xml b/rma/views/rma_operation_view.xml index c2bb1b59e..08a00f286 100644 --- a/rma/views/rma_operation_view.xml +++ b/rma/views/rma_operation_view.xml @@ -53,6 +53,10 @@ name="customer_to_supplier" attrs="{'invisible':[('type', '=', 'supplier')]}" /> + @@ -61,6 +65,10 @@ name="supplier_to_customer" attrs="{'invisible':[('type', '=', 'customer')]}" /> + diff --git a/rma/wizards/rma_make_picking.py b/rma/wizards/rma_make_picking.py index 637caea9b..2982b75f0 100644 --- a/rma/wizards/rma_make_picking.py +++ b/rma/wizards/rma_make_picking.py @@ -210,15 +210,16 @@ def action_create_picking(self): else: pickings = self.mapped("item_ids.line_id")._get_in_pickings() action = self.item_ids.line_id.action_view_in_shipments() - # Force the reservation of the RMA specific lot for incoming shipments. - # FIXME: still needs fixing, not reserving appropriate serials. + for move in pickings.move_ids.filtered( lambda x: x.state not in ("draft", "cancel", "done", "waiting") and x.rma_line_id and x.product_id.tracking in ("lot", "serial") and x.rma_line_id.lot_id + and x.rma_line_id.operation_id.in_force_same_lot + and x.location_dest_id.usage == "internal" ): - # Force the reservation of the RMA specific lot for incoming shipments. + # Force the reservation of the RMA specific lot for incoming shipments if required. move.move_line_ids.unlink() if move.product_id.tracking == "serial": move.write( @@ -226,14 +227,10 @@ def action_create_picking(self): "lot_ids": [(6, 0, move.rma_line_id.lot_id.ids)], } ) - quants = self.env["stock.quant"]._gather( - move.product_id, move.location_id, lot_id=move.rma_line_id.lot_id - ) move.move_line_ids.write( { - "reserved_uom_qty": 1 if picking_type == "incoming" else 0, + "reserved_uom_qty": 1, "qty_done": 0, - "package_id": len(quants) == 1 and quants.package_id.id, } ) elif move.product_id.tracking == "lot": @@ -251,10 +248,11 @@ def action_create_picking(self): "lot_id": move.rma_line_id.lot_id.id, "product_uom_id": move.product_id.uom_id.id, "qty_done": 0, - "reserved_uom_qty": qty if picking_type == "incoming" else 0, + "reserved_uom_qty": qty, } ) move_line_model.create(move_line_data) + pickings.with_context(force_no_bypass_reservation=True).action_assign() return action diff --git a/rma_put_away/tests/test_rma_put_away.py b/rma_put_away/tests/test_rma_put_away.py index b3bec91e2..16d3514e1 100644 --- a/rma_put_away/tests/test_rma_put_away.py +++ b/rma_put_away/tests/test_rma_put_away.py @@ -84,6 +84,8 @@ def setUpClass(cls): "put_away_location_id": cls.put_away_loc.id, "in_route_id": cls.rma_route_cust.id, "out_route_id": cls.rma_route_cust.id, + "out_force_same_lot": True, + "in_force_same_lot": True, } ) cls.operation_2 = cls.rma_op_obj.create( @@ -97,6 +99,8 @@ def setUpClass(cls): "put_away_location_id": cls.put_away_loc.id, "in_route_id": cls.rma_route_cust.id, "out_route_id": cls.rma_route_cust.id, + "out_force_same_lot": True, + "in_force_same_lot": True, } )