From b0f3dd97e55177d350f31646e2756043d12d11e7 Mon Sep 17 00:00:00 2001 From: Jordi Ballester Alomar Date: Wed, 23 Nov 2022 15:26:03 +0100 Subject: [PATCH 1/3] [IMP] centralize the logic to get the correct cost of the RMA. --- rma/__manifest__.py | 2 +- rma/models/rma_order_line.py | 16 +++++++-- rma/models/stock_rule.py | 8 +---- rma_account/__manifest__.py | 2 +- rma_account/models/procurement.py | 38 --------------------- rma_account/models/rma_order_line.py | 16 +++++++++ rma_purchase/__manifest__.py | 2 +- rma_purchase/models/procurement.py | 49 --------------------------- rma_purchase/models/rma_order_line.py | 22 ++++++++++++ rma_sale/__manifest__.py | 2 +- rma_sale/models/procurement.py | 28 +-------------- rma_sale/models/rma_order_line.py | 30 ++++++++++++++++ 12 files changed, 88 insertions(+), 127 deletions(-) diff --git a/rma/__manifest__.py b/rma/__manifest__.py index 72999929..63a041f0 100644 --- a/rma/__manifest__.py +++ b/rma/__manifest__.py @@ -3,7 +3,7 @@ { "name": "RMA (Return Merchandise Authorization)", - "version": "14.0.1.1.0", + "version": "14.0.1.1.1", "license": "LGPL-3", "category": "RMA", "summary": "Introduces the return merchandise authorization (RMA) process " diff --git a/rma/models/rma_order_line.py b/rma/models/rma_order_line.py index a8b23ba0..82d80b40 100644 --- a/rma/models/rma_order_line.py +++ b/rma/models/rma_order_line.py @@ -308,7 +308,10 @@ class RmaOrderLine(models.Model): states={"draft": [("readonly", False)]}, ) price_unit = fields.Monetary( - string="Price Unit", readonly=True, states={"draft": [("readonly", False)]} + string="Unit cost", + readonly=True, + states={"draft": [("readonly", False)]}, + help="Unit cost of the items under RMA", ) in_shipment_count = fields.Integer( compute="_compute_in_shipment_count", string="# of Shipments" @@ -651,15 +654,24 @@ class RmaOrderLine(models.Model): ) return super(RmaOrderLine, self).create(vals) + def _get_price_unit(self): + """The price unit corresponds to the cost of that product""" + self.ensure_one() + if self.reference_move_id: + price_unit = self.reference_move_id.price_unit + else: + price_unit = self.product_id.with_company(self.company_id).standard_price + return price_unit + @api.onchange("product_id") def _onchange_product_id(self): result = {} if not self.product_id: return result self.uom_id = self.product_id.uom_id.id - self.price_unit = self.product_id.standard_price if not self.type: self.type = self._get_default_type() + self.price_unit = self._get_price_unit() if self.type == "customer": self.operation_id = ( self.product_id.rma_customer_operation_id diff --git a/rma/models/stock_rule.py b/rma/models/stock_rule.py index 678d971e..0c7119f5 100644 --- a/rma/models/stock_rule.py +++ b/rma/models/stock_rule.py @@ -36,11 +36,5 @@ class StockRule(models.Model): res["partner_id"] = line.delivery_address_id.id else: res["partner_id"] = line.rma_id.partner_id.id - # We are not checking the reference move here because if stock account - # is not installed, there is no way to know the cost of the stock move - # so better use the standard cost in this case. - company_id = res["company_id"] - company = self.env["res.company"].browse(company_id) - cost = product_id.with_company(company).standard_price - res["price_unit"] = cost + res["price_unit"] = line._get_price_unit() return res diff --git a/rma_account/__manifest__.py b/rma_account/__manifest__.py index d15e1694..f4e4b01d 100644 --- a/rma_account/__manifest__.py +++ b/rma_account/__manifest__.py @@ -3,7 +3,7 @@ { "name": "RMA Account", - "version": "14.0.1.0.0", + "version": "14.0.1.0.1", "license": "LGPL-3", "category": "RMA", "summary": "Integrates RMA with Invoice Processing", diff --git a/rma_account/models/procurement.py b/rma_account/models/procurement.py index 517cda76..e065cfe9 100644 --- a/rma_account/models/procurement.py +++ b/rma_account/models/procurement.py @@ -4,44 +4,6 @@ from odoo import fields, models -class StockRule(models.Model): - _inherit = "stock.rule" - - def _get_stock_move_values( - self, - product_id, - product_qty, - product_uom, - location_id, - name, - origin, - company_id, - values, - ): - res = super(StockRule, self)._get_stock_move_values( - product_id, - product_qty, - product_uom, - location_id, - name, - origin, - company_id, - values, - ) - if "rma_line_id" in values: - line = values.get("rma_line_id") - line = self.env["rma.order.line"].browse([line]) - move = line.reference_move_id - if move and move.stock_valuation_layer_ids: - layers = move.stock_valuation_layer_ids - price_unit = sum(layers.mapped("value")) / sum( - layers.mapped("quantity") - ) - - res["price_unit"] = price_unit - return res - - class ProcurementGroup(models.Model): _inherit = "procurement.group" diff --git a/rma_account/models/rma_order_line.py b/rma_account/models/rma_order_line.py index 68ebfea5..0a5b4007 100644 --- a/rma_account/models/rma_order_line.py +++ b/rma_account/models/rma_order_line.py @@ -336,3 +336,19 @@ class RmaOrderLine(models.Model): ) # Reconcile. amls.reconcile() + + def _get_price_unit(self): + self.ensure_one() + price_unit = super(RmaOrderLine, self)._get_price_unit() + if self.reference_move_id: + move = self.reference_move_id + layers = move.sudo().stock_valuation_layer_ids + if layers: + price_unit = sum(layers.mapped("value")) / sum( + layers.mapped("quantity") + ) + price_unit = price_unit + elif self.account_move_line_id and self.type == "supplier": + # We get the cost from the original invoice line + price_unit = self.account_move_line_id.price_unit + return price_unit diff --git a/rma_purchase/__manifest__.py b/rma_purchase/__manifest__.py index 0d31698f..6c2bff9d 100644 --- a/rma_purchase/__manifest__.py +++ b/rma_purchase/__manifest__.py @@ -2,7 +2,7 @@ # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) { "name": "RMA Purchase", - "version": "14.0.1.0.0", + "version": "14.0.1.0.1", "category": "RMA", "summary": "RMA from PO", "license": "LGPL-3", diff --git a/rma_purchase/models/procurement.py b/rma_purchase/models/procurement.py index b551873e..e065cfe9 100644 --- a/rma_purchase/models/procurement.py +++ b/rma_purchase/models/procurement.py @@ -4,55 +4,6 @@ from odoo import fields, models -class StockRule(models.Model): - _inherit = "stock.rule" - - def _get_stock_move_values( - self, - product_id, - product_qty, - product_uom, - location_id, - name, - origin, - company_id, - values, - ): - res = super(StockRule, self)._get_stock_move_values( - product_id, - product_qty, - product_uom, - location_id, - name, - origin, - company_id, - values, - ) - if "rma_line_id" in values: - line = values.get("rma_line_id") - line = self.env["rma.order.line"].browse([line]) - if line.reference_move_id: - return res - if line.purchase_order_line_id: - moves = line.purchase_order_line_id.move_ids - if moves: - # TODO: Should we be smart in the choice of the move? - layers = moves.mapped("stock_valuation_layer_ids") - if layers: - cost = layers[-1].unit_cost - res["price_unit"] = cost - elif line.account_move_line_id.purchase_line_id: - purchase_lines = line.account_move_line_id.purchase_line_id - moves = purchase_lines.mapped("move_ids") - if moves: - layers = moves.mapped("stock_valuation_layer_ids") - if layers: - cost = layers[-1].unit_cost - # TODO: Should we be smart in the choice of the move? - res["price_unit"] = cost - return res - - class ProcurementGroup(models.Model): _inherit = "procurement.group" diff --git a/rma_purchase/models/rma_order_line.py b/rma_purchase/models/rma_order_line.py index 3d626f59..0bf5997a 100644 --- a/rma_purchase/models/rma_order_line.py +++ b/rma_purchase/models/rma_order_line.py @@ -228,3 +228,25 @@ class RmaOrderLine(models.Model): ): qty += self.uom_id._compute_quantity(line.product_qty, line.product_uom) return qty + + def _get_price_unit(self): + self.ensure_one() + price_unit = super(RmaOrderLine, self)._get_price_unit() + if self.purchase_order_line_id: + moves = self.purchase_order_line_id.move_ids + if moves: + layers = moves.sudo().mapped("stock_valuation_layer_ids") + if layers: + price_unit = sum(layers.mapped("value")) / sum( + layers.mapped("quantity") + ) + elif self.account_move_line_id.purchase_line_id: + purchase_lines = self.account_move_line_id.purchase_line_id + moves = purchase_lines.mapped("move_ids") + if moves: + layers = moves.sudo().mapped("stock_valuation_layer_ids") + if layers: + price_unit = sum(layers.mapped("value")) / sum( + layers.mapped("quantity") + ) + return price_unit diff --git a/rma_sale/__manifest__.py b/rma_sale/__manifest__.py index f680b783..a7c41c67 100644 --- a/rma_sale/__manifest__.py +++ b/rma_sale/__manifest__.py @@ -3,7 +3,7 @@ { "name": "RMA Sale", - "version": "14.0.1.0.0", + "version": "14.0.1.0.1", "license": "LGPL-3", "category": "RMA", "summary": "Links RMA with Sales Orders", diff --git a/rma_sale/models/procurement.py b/rma_sale/models/procurement.py index 9fd83f17..bc9a29ee 100644 --- a/rma_sale/models/procurement.py +++ b/rma_sale/models/procurement.py @@ -33,31 +33,5 @@ class StockRule(models.Model): line = self.env["rma.order.line"].browse([line]) if line.reference_move_id: return res - if line.sale_line_id: - moves = line.sale_line_id.move_ids.filtered( - lambda x: x.state == "done" - and x.location_id.usage in ("internal", "supplier") - and x.location_dest_id.usage == "customer" - ) - if moves: - layers = moves.mapped("stock_valuation_layer_ids") - if layers: - price_unit = sum(layers.mapped("value")) / sum( - layers.mapped("quantity") - ) - res["price_unit"] = price_unit - elif line.account_move_line_id: - sale_lines = line.account_move_line_id.sale_line_ids - moves = sale_lines.mapped("move_ids").filtered( - lambda x: x.state == "done" - and x.location_id.usage in ("internal", "supplier") - and x.location_dest_id.usage == "customer" - ) - if moves: - layers = moves.mapped("stock_valuation_layer_ids") - if layers: - price_unit = sum(layers.mapped("value")) / sum( - layers.mapped("quantity") - ) - res["price_unit"] = price_unit + res["price_unit"] = line._get_price_unit() return res diff --git a/rma_sale/models/rma_order_line.py b/rma_sale/models/rma_order_line.py index 722a7e6d..dd0118cb 100644 --- a/rma_sale/models/rma_order_line.py +++ b/rma_sale/models/rma_order_line.py @@ -218,3 +218,33 @@ class RmaOrderLine(models.Model): ): qty += sale_line.product_uom_qty return qty + + def _get_price_unit(self): + self.ensure_one() + price_unit = super(RmaOrderLine, self)._get_price_unit() + if self.sale_line_id: + moves = self.sale_line_id.move_ids.filtered( + lambda x: x.state == "done" + and x.location_id.usage in ("internal", "supplier") + and x.location_dest_id.usage == "customer" + ) + if moves: + layers = moves.sudo().mapped("stock_valuation_layer_ids") + if layers: + price_unit = sum(layers.mapped("value")) / sum( + layers.mapped("quantity") + ) + elif self.account_move_line_id: + sale_lines = self.account_move_line_id.sale_line_ids + moves = sale_lines.mapped("move_ids").filtered( + lambda x: x.state == "done" + and x.location_id.usage in ("internal", "supplier") + and x.location_dest_id.usage == "customer" + ) + if moves: + layers = moves.sudo().mapped("stock_valuation_layer_ids") + if layers: + price_unit = sum(layers.mapped("value")) / sum( + layers.mapped("quantity") + ) + return price_unit From 4dd2316d67d5ced088b04786a4a86ecd22f567ad Mon Sep 17 00:00:00 2001 From: Jordi Ballester Alomar Date: Wed, 23 Nov 2022 20:12:34 +0100 Subject: [PATCH 2/3] [FIX] rma_account: Auto-reconcile supplier refunds --- rma_account/models/account_move.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/rma_account/models/account_move.py b/rma_account/models/account_move.py index ac9ad0c8..5208a878 100644 --- a/rma_account/models/account_move.py +++ b/rma_account/models/account_move.py @@ -99,15 +99,19 @@ class AccountMove(models.Model): def _stock_account_get_last_step_stock_moves(self): rslt = super(AccountMove, self)._stock_account_get_last_step_stock_moves() - for invoice in self.filtered(lambda x: x.move_type == "out_invoice"): - rslt += invoice.mapped("line_ids.rma_line_id.move_ids").filtered( - lambda x: x.state == "done" and x.location_dest_id.usage == "customer" - ) - for invoice in self.filtered(lambda x: x.move_type == "out_refund"): - # Add refunds generated from the RMA + for invoice in self.filtered( + lambda x: x.move_type in ("out_invoice", "out_refund") + ): rslt += invoice.mapped("line_ids.rma_line_id.move_ids").filtered( lambda x: x.state == "done" and x.location_id.usage == "customer" ) + for invoice in self.filtered( + lambda x: x.move_type in ("in_invoice", "in_refund") + ): + # Add refunds generated from the RMA + rslt += invoice.mapped("line_ids.rma_line_id.move_ids").filtered( + lambda x: x.state == "done" and x.location_dest_id.usage == "supplier" + ) return rslt From dd29c431e91490c64d7994c2079b0c4bb9541db0 Mon Sep 17 00:00:00 2001 From: Jordi Ballester Alomar Date: Thu, 24 Nov 2022 18:04:05 +0100 Subject: [PATCH 3/3] [FIX] fix rma management using multi step routes --- rma/models/rma_order_line.py | 20 +++- rma/models/stock_move.py | 6 ++ rma/models/stock_rule.py | 5 + rma/tests/test_rma.py | 109 ++++++++++++++++---- rma/wizards/rma_make_picking.py | 3 +- rma_account/tests/test_rma_stock_account.py | 84 +++++++++++++++ 6 files changed, 203 insertions(+), 24 deletions(-) diff --git a/rma/models/rma_order_line.py b/rma/models/rma_order_line.py index 82d80b40..cb73c55e 100644 --- a/rma/models/rma_order_line.py +++ b/rma/models/rma_order_line.py @@ -70,6 +70,18 @@ class RmaOrderLine(models.Model): moves |= moves return moves + @api.model + def _get_out_moves(self): + moves = self.env["stock.move"] + for move in self.move_ids: + first_usage = move._get_first_usage() + last_usage = move._get_last_usage() + if first_usage == "internal" and last_usage != "internal": + moves |= move + elif first_usage == "supplier" and last_usage == "customer": + moves |= moves + return moves + @api.model def _get_out_pickings(self): pickings = self.env["stock.picking"] @@ -97,12 +109,10 @@ class RmaOrderLine(models.Model): product_obj = self.env["uom.uom"] qty = 0.0 if direction == "in": - op = ops["="] + moves = rec._get_in_moves() else: - op = ops["!="] - for move in rec.move_ids.filtered( - lambda m: m.state in states and op(m.location_id.usage, rec.type) - ): + moves = rec._get_out_moves() + for move in moves.filtered(lambda m: m.state in states): # If the move is part of a chain don't count it if direction == "out" and move.move_orig_ids: continue diff --git a/rma/models/stock_move.py b/rma/models/stock_move.py index c17b0170..4b675429 100644 --- a/rma/models/stock_move.py +++ b/rma/models/stock_move.py @@ -104,3 +104,9 @@ class StockMove(models.Model): def _prepare_merge_moves_distinct_fields(self): res = super()._prepare_merge_moves_distinct_fields() return res + ["rma_line_id"] + + def _prepare_procurement_values(self): + self.ensure_one() + res = super(StockMove, self)._prepare_procurement_values() + res["rma_line_id"] = self.rma_line_id.id + return res diff --git a/rma/models/stock_rule.py b/rma/models/stock_rule.py index 0c7119f5..d167fcdf 100644 --- a/rma/models/stock_rule.py +++ b/rma/models/stock_rule.py @@ -28,9 +28,14 @@ class StockRule(models.Model): company_id, values, ) + line = self.env["rma.order.line"] if "rma_line_id" in values: line = values.get("rma_line_id") line = self.env["rma.order.line"].browse([line]) + elif "group_id" in values: + pg = values["group_id"] + line = pg.rma_line_id + if line: res["rma_line_id"] = line.id if line.delivery_address_id: res["partner_id"] = line.delivery_address_id.id diff --git a/rma/tests/test_rma.py b/rma/tests/test_rma.py index d3768252..0a3dcf35 100644 --- a/rma/tests/test_rma.py +++ b/rma/tests/test_rma.py @@ -26,6 +26,9 @@ class TestRma(common.SavepointCase): cls.rma_cust_replace_op_id = cls.env.ref("rma.rma_operation_customer_replace") cls.rma_sup_replace_op_id = cls.env.ref("rma.rma_operation_supplier_replace") cls.rma_ds_replace_op_id = cls.env.ref("rma.rma_operation_ds_replace") + cls.customer_route = cls.env.ref("rma.route_rma_customer") + cls.input_location = cls.env.ref("stock.stock_location_company") + cls.output_location = cls.env.ref("stock.stock_location_output") cls.category = cls._create_product_category( "one_step", cls.rma_cust_replace_op_id, cls.rma_sup_replace_op_id ) @@ -82,7 +85,7 @@ class TestRma(common.SavepointCase): @classmethod def _create_user(cls, login, groups, company): group_ids = [group.id for group in groups] - user = cls.res_users_model.create( + user = cls.res_users_model.with_context(no_reset_password=True).create( { "name": login, "login": login, @@ -105,13 +108,15 @@ class TestRma(common.SavepointCase): } ).create({}) wizard._create_picking() - res = rma_line_ids.action_view_in_shipments() - picking = cls.env["stock.picking"].browse(res["res_id"]) - picking.action_assign() - for mv in picking.move_lines: - mv.quantity_done = mv.product_uom_qty - picking._action_done() - return picking + pickings = rma_line_ids._get_in_pickings() + pickings.action_assign() + for picking in pickings: + for mv in picking.move_lines: + mv.quantity_done = mv.product_uom_qty + # In case of two step pickings, ship in two steps: + while pickings.filtered(lambda p: p.state == "assigned"): + pickings._action_done() + return pickings @classmethod def _deliver_rma(cls, rma_line_ids): @@ -124,13 +129,13 @@ class TestRma(common.SavepointCase): } ).create({}) wizard._create_picking() - res = rma_line_ids.action_view_out_shipments() - picking = cls.env["stock.picking"].browse(res["res_id"]) - picking.action_assign() - for mv in picking.move_lines: - mv.quantity_done = mv.product_uom_qty - picking._action_done() - return picking + pickings = rma_line_ids._get_out_pickings() + pickings.action_assign() + for picking in pickings: + for mv in picking.move_lines: + mv.quantity_done = mv.product_uom_qty + pickings._action_done() + return pickings @classmethod def _create_product_category( @@ -773,9 +778,7 @@ class TestRma(common.SavepointCase): } ).create({}) wizard._create_picking() - res = self.rma_supplier_id.rma_line_ids.action_view_out_shipments() - self.assertTrue("res_id" in res, "Incorrect number of pickings" "created") - picking = self.env["stock.picking"].browse(res["res_id"]) + picking = self.rma_supplier_id.rma_line_ids._get_out_pickings() moves = picking.move_lines self.assertEqual(len(moves), 3, "Incorrect number of moves created") @@ -1073,3 +1076,73 @@ class TestRma(common.SavepointCase): ).create({}) with self.assertRaisesRegex(ValidationError, "No quantity to transfer"): wizard._create_picking() + + def test_08_customer_rma_multi_step(self): + """ + Receive a product and then return it using a multi-step route. + """ + # Alter the customer RMA route to make it multi-step + # Get rid of the duplicated rule + self.env.ref("rma.rule_rma_customer_out_pull").active = False + self.env.ref("rma.rule_rma_customer_in_pull").active = False + cust_in_pull_rule = self.customer_route.rule_ids.filtered( + lambda r: r.location_id == self.stock_rma_location + ) + cust_in_pull_rule.location_id = self.input_location + cust_out_pull_rule = self.customer_route.rule_ids.filtered( + lambda r: r.location_src_id == self.env.ref("rma.location_rma") + ) + cust_out_pull_rule.location_src_id = self.output_location + cust_out_pull_rule.procure_method = "make_to_order" + self.env["stock.rule"].create( + { + "name": "RMA->Output", + "action": "pull", + "warehouse_id": self.wh.id, + "location_src_id": self.env.ref("rma.location_rma").id, + "location_id": self.output_location.id, + "procure_method": "make_to_stock", + "route_id": self.customer_route.id, + "picking_type_id": self.env.ref("stock.picking_type_internal").id, + } + ) + self.env["stock.rule"].create( + { + "name": "Output->RMA", + "action": "pull", + "warehouse_id": self.wh.id, + "location_src_id": self.input_location.id, + "location_id": self.env.ref("rma.location_rma").id, + "procure_method": "make_to_order", + "route_id": self.customer_route.id, + "picking_type_id": self.env.ref("stock.picking_type_internal").id, + } + ) + # Set a standard price on the products + self.product_1.standard_price = 10 + self._create_inventory( + self.product_1, 20.0, self.env.ref("stock.stock_location_customers") + ) + products2move = [ + (self.product_1, 3), + ] + self.product_1.categ_id.rma_customer_operation_id = self.rma_cust_replace_op_id + rma_customer_id = self._create_rma_from_move( + products2move, + "customer", + self.env.ref("base.res_partner_2"), + dropship=False, + ) + rma = rma_customer_id.rma_line_ids + rma.action_rma_to_approve() + self.assertEqual(rma.qty_to_receive, 3) + self.assertEqual(rma.qty_received, 0) + self._receive_rma(rma) + self.assertEqual(len(rma.move_ids), 2) + self.assertEqual(rma.qty_to_receive, 0) + self.assertEqual(rma.qty_received, 3) + self.assertEqual(rma.qty_to_deliver, 3) + self._deliver_rma(rma) + self.assertEqual(rma.qty_to_deliver, 0) + self.assertEqual(rma.qty_delivered, 3) + self.assertEqual(len(rma.move_ids), 4) diff --git a/rma/wizards/rma_make_picking.py b/rma/wizards/rma_make_picking.py index c6ec5fcf..602b2595 100644 --- a/rma/wizards/rma_make_picking.py +++ b/rma/wizards/rma_make_picking.py @@ -73,8 +73,9 @@ class RmaMakePicking(models.TransientModel): "partner_id": item.line_id.partner_id.id, "name": item.line_id.rma_id.name or item.line_id.name, "rma_id": item.line_id.rma_id and item.line_id.rma_id.id or False, - "rma_line_id": item.line_id.id if not item.line_id.rma_id else False, } + if not item.line_id.rma_id: + group_data["rma_line_id"] = item.line_id and item.line_id.id or False return group_data @api.model diff --git a/rma_account/tests/test_rma_stock_account.py b/rma_account/tests/test_rma_stock_account.py index 64102366..6b4d92f1 100644 --- a/rma_account/tests/test_rma_stock_account.py +++ b/rma_account/tests/test_rma_stock_account.py @@ -240,3 +240,87 @@ class TestRmaStockAccount(TestRma): self.assertEqual(gdni_balance, 0.0) # The GDNI entries should be now reconciled self.assertEqual(all(gdni_amls.mapped("reconciled")), True) + + def test_04_cost_from_move_multi_step(self): + """ + Receive a product and then return it using a multi-step route. + The Goods Delivered Not Invoiced should result in 0 + """ + # Alter the customer RMA route to make it multi-step + # Get rid of the duplicated rule + self.env.ref("rma.rule_rma_customer_out_pull").active = False + self.env.ref("rma.rule_rma_customer_in_pull").active = False + cust_in_pull_rule = self.customer_route.rule_ids.filtered( + lambda r: r.location_id == self.stock_rma_location + ) + cust_in_pull_rule.location_id = self.input_location + cust_out_pull_rule = self.customer_route.rule_ids.filtered( + lambda r: r.location_src_id == self.env.ref("rma.location_rma") + ) + cust_out_pull_rule.location_src_id = self.output_location + cust_out_pull_rule.procure_method = "make_to_order" + self.env["stock.rule"].create( + { + "name": "RMA->Output", + "action": "pull", + "warehouse_id": self.wh.id, + "location_src_id": self.env.ref("rma.location_rma").id, + "location_id": self.output_location.id, + "procure_method": "make_to_stock", + "route_id": self.customer_route.id, + "picking_type_id": self.env.ref("stock.picking_type_internal").id, + } + ) + self.env["stock.rule"].create( + { + "name": "Output->RMA", + "action": "pull", + "warehouse_id": self.wh.id, + "location_src_id": self.input_location.id, + "location_id": self.env.ref("rma.location_rma").id, + "procure_method": "make_to_order", + "route_id": self.customer_route.id, + "picking_type_id": self.env.ref("stock.picking_type_internal").id, + } + ) + # Set a standard price on the products + self.product_fifo_1.standard_price = 10 + self._create_inventory( + self.product_fifo_1, 20.0, self.env.ref("stock.stock_location_customers") + ) + products2move = [ + (self.product_fifo_1, 3), + ] + self.product_fifo_1.categ_id.rma_customer_operation_id = ( + self.rma_cust_replace_op_id + ) + rma_customer_id = self._create_rma_from_move( + products2move, + "customer", + self.env.ref("base.res_partner_2"), + dropship=False, + ) + # Set an incorrect price in the RMA (this should not affect cost) + rma = rma_customer_id.rma_line_ids + rma.price_unit = 999 + rma.action_rma_to_approve() + self._receive_rma(rma) + layers = rma.move_ids.sudo().stock_valuation_layer_ids + gdni_amls = layers.account_move_id.line_ids.filtered( + lambda l: l.account_id == self.account_gdni + ) + gdni_balance = sum(gdni_amls.mapped("balance")) + self.assertEqual(len(gdni_amls), 1) + # Balance should be -30, as we have only received + self.assertEqual(gdni_balance, -30.0) + self._deliver_rma(rma) + layers = rma.move_ids.sudo().stock_valuation_layer_ids + gdni_amls = layers.account_move_id.line_ids.filtered( + lambda l: l.account_id == self.account_gdni + ) + gdni_balance = sum(gdni_amls.mapped("balance")) + self.assertEqual(len(gdni_amls), 2) + # Balance should be 0, as we have received and shipped + self.assertEqual(gdni_balance, 0.0) + # The GDNI entries should be now reconciled + self.assertEqual(all(gdni_amls.mapped("reconciled")), True)