From 158dba051fb76fb8333d78d79a1ab1f6efcdd79c Mon Sep 17 00:00:00 2001 From: david Date: Thu, 22 Apr 2021 11:19:25 +0200 Subject: [PATCH 1/2] [FIX] rma_sale_mrp: handle exceptions when qty is forced --- rma_sale_mrp/tests/test_rma_sale_mrp.py | 11 ++++++++++- rma_sale_mrp/wizard/sale_order_rma_wizard.py | 9 ++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/rma_sale_mrp/tests/test_rma_sale_mrp.py b/rma_sale_mrp/tests/test_rma_sale_mrp.py index a08772c0..b64b3c49 100644 --- a/rma_sale_mrp/tests/test_rma_sale_mrp.py +++ b/rma_sale_mrp/tests/test_rma_sale_mrp.py @@ -1,7 +1,7 @@ # Copyright 2020 Tecnativa - David Vidal # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo.tests import Form, SavepointCase -from odoo.exceptions import UserError +from odoo.exceptions import UserError, ValidationError class TestRmaSaleMrp(SavepointCase): @@ -153,3 +153,12 @@ class TestRmaSaleMrp(SavepointCase): wizard_id = order.action_create_rma()["res_id"] wizard = self.env["sale.order.rma.wizard"].browse(wizard_id) self.assertEqual(wizard.line_ids.quantity, 1) + wizard.create_and_open_rma() + # Now we open the wizard again and try to force the RMA qty wich should + # be 0 at this time + wizard_id = order.action_create_rma()["res_id"] + wizard = self.env["sale.order.rma.wizard"].browse(wizard_id) + self.assertEqual(wizard.line_ids.quantity, 0) + wizard.line_ids.quantity = 1 + with self.assertRaises(ValidationError): + wizard.create_and_open_rma() diff --git a/rma_sale_mrp/wizard/sale_order_rma_wizard.py b/rma_sale_mrp/wizard/sale_order_rma_wizard.py index d372b264..d8e89772 100644 --- a/rma_sale_mrp/wizard/sale_order_rma_wizard.py +++ b/rma_sale_mrp/wizard/sale_order_rma_wizard.py @@ -1,6 +1,7 @@ # Copyright 2020 Tecnativa - David Vidal # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError class SaleOrderRmaWizard(models.TransientModel): @@ -50,6 +51,12 @@ class SaleOrderRmaWizard(models.TransientModel): product_kit_component_lines = kit_component_lines.filtered( lambda x: x.product_id == product and x.quantity ) + if not product_kit_component_lines: + raise ValidationError(_( + "The kit corresponding to the product %s can't be " + "put in the RMA. Either all or some of the components " + "where already put in another RMA" + ) % line.product_id.name) qty_to_return = ( product_kit_component_lines[0].per_kit_quantity * line.quantity From 79b67fbce0835acf8b10813c108a96b9a9aedda4 Mon Sep 17 00:00:00 2001 From: david Date: Thu, 22 Apr 2021 12:16:07 +0200 Subject: [PATCH 2/2] [FIX] rma_sale: recurrent RMA If a product was already in an RMA in the past, we should be able to place another RMA in the future. --- rma_sale/models/sale.py | 23 ++++++++++++++++++---- rma_sale/tests/test_rma_sale.py | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/rma_sale/models/sale.py b/rma_sale/models/sale.py index 795efe48..2e694941 100644 --- a/rma_sale/models/sale.py +++ b/rma_sale/models/sale.py @@ -114,6 +114,12 @@ class SaleOrderLine(models.Model): def prepare_sale_rma_data(self): self.ensure_one() + # Method helper to filter chained moves + + def destination_moves(_move): + return _move.mapped("move_dest_ids").filtered( + lambda r: r.state in ["partially_available", "assigned", "done"] + ) product = self.product_id if self.product_id.type not in ['product', 'consu']: return {} @@ -121,11 +127,20 @@ class SaleOrderLine(models.Model): data = [] if moves: for move in moves: + # Look for chained moves to check how many items we can allow + # to return. When a product is re-delivered it should be + # allowed to open an RMA again on it. qty = move.product_uom_qty - move_dest = move.move_dest_ids.filtered( - lambda r: r.state in ['partially_available', - 'assigned', 'done']) - qty -= sum(move_dest.mapped('product_uom_qty')) + qty_returned = 0 + move_dest = destination_moves(move) + while move_dest: + qty_returned -= sum(move_dest.mapped("product_uom_qty")) + move_dest = destination_moves(move_dest) + if move_dest: + qty += sum(move_dest.mapped("product_uom_qty")) + move_dest = destination_moves(move_dest) + # If by chance we get a negative qty we should ignore it + qty = max(0, sum((qty, qty_returned))) data.append({ 'product': move.product_id, 'quantity': qty, diff --git a/rma_sale/tests/test_rma_sale.py b/rma_sale/tests/test_rma_sale.py index ab0198be..4e6284bf 100644 --- a/rma_sale/tests/test_rma_sale.py +++ b/rma_sale/tests/test_rma_sale.py @@ -38,6 +38,10 @@ class TestRmaSale(SavepointCase): cls.order_out_picking.move_lines.quantity_done = 5 cls.order_out_picking.button_validate() + def _rma_sale_wizard(self, order): + wizard_id = order.action_create_rma()["res_id"] + return self.env["sale.order.rma.wizard"].browse(wizard_id) + def test_create_rma_with_so(self): rma_form = Form(self.env['rma']) rma_form.partner_id = self.partner @@ -83,3 +87,34 @@ class TestRmaSale(SavepointCase): rma.reception_move_id.picking_id.action_done() rma.action_refund() self.assertEqual(rma.refund_id.user_id, user) + + def test_create_recurrent_rma(self): + """An RMA of a product that had an RMA in the past should be possible""" + wizard = self._rma_sale_wizard(self.sale_order) + rma = self.env["rma"].browse(wizard.create_and_open_rma()["res_id"]) + rma.reception_move_id.quantity_done = rma.product_uom_qty + rma.reception_move_id.picking_id.action_done() + wizard = self._rma_sale_wizard(self.sale_order) + self.assertEqual( + wizard.line_ids.quantity, 0, + "There shouldn't be any allowed quantities for RMAs" + ) + delivery_form = Form( + self.env["rma.delivery.wizard"].with_context( + active_ids=rma.ids, + rma_delivery_type="return", + ) + ) + delivery_form.product_uom_qty = rma.product_uom_qty + delivery_wizard = delivery_form.save() + delivery_wizard.action_deliver() + picking = rma.delivery_move_ids.picking_id + picking.move_lines.quantity_done = rma.product_uom_qty + picking.action_done() + # The product is returned to the customer, so we should be able to make + # another RMA in the future + wizard = self._rma_sale_wizard(self.sale_order) + self.assertEqual( + wizard.line_ids.quantity, rma.product_uom_qty, + "We should be allowed to return the product again" + )