diff --git a/rma_sale/__manifest__.py b/rma_sale/__manifest__.py index eec2e878..96de8bc1 100644 --- a/rma_sale/__manifest__.py +++ b/rma_sale/__manifest__.py @@ -13,6 +13,7 @@ "depends": ["rma", "sale_stock"], "data": [ "security/ir.model.access.csv", + "views/account_move_views.xml", "views/report_rma.xml", "views/rma_views.xml", "views/sale_views.xml", diff --git a/rma_sale/models/__init__.py b/rma_sale/models/__init__.py index 16972d9b..9047bb78 100644 --- a/rma_sale/models/__init__.py +++ b/rma_sale/models/__init__.py @@ -1,4 +1,5 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from . import account_move from . import res_company from . import res_config_settings from . import rma diff --git a/rma_sale/models/account_move.py b/rma_sale/models/account_move.py new file mode 100644 index 00000000..e5a5a7a8 --- /dev/null +++ b/rma_sale/models/account_move.py @@ -0,0 +1,24 @@ +# Copyright 2023 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class AccountMove(models.Model): + _inherit = "account.move" + + def button_cancel(self): + """If this a refund linked to an RMA, undo the linking of the reception move for + having proper quantities and status. + """ + for rma in self.env["rma"].search([("refund_id", "in", self.ids)]): + if rma.sale_line_id: + rma._unlink_refund_with_reception_move() + return super().button_cancel() + + def button_draft(self): + """Relink the reception move when passing the refund again to draft.""" + for rma in self.env["rma"].search([("refund_id", "in", self.ids)]): + if rma.sale_line_id: + rma._link_refund_with_reception_move() + return super().button_draft() diff --git a/rma_sale/models/rma.py b/rma_sale/models/rma.py index 9286b05b..31b3254e 100644 --- a/rma_sale/models/rma.py +++ b/rma_sale/models/rma.py @@ -1,7 +1,9 @@ # Copyright 2020 Tecnativa - Ernesto Tejeda +# Copyright 2023 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import api, fields, models +from odoo.tools import float_compare class Rma(models.Model): @@ -41,6 +43,8 @@ class Rma(models.Model): domain="order_id and [('id', 'in', allowed_product_ids)] or " "[('type', 'in', ['consu', 'product'])]" ) + # Add index to this field, as we perform a search on it + refund_id = fields.Many2one(index=True) @api.depends("partner_id", "order_id") def _compute_allowed_picking_ids(self): @@ -89,6 +93,45 @@ class Rma(models.Model): def _onchange_order_id(self): self.product_id = self.picking_id = False + def _link_refund_with_reception_move(self): + """Perform the internal operations for linking the RMA reception move with the + sales order line if applicable. + """ + self.ensure_one() + move = self.reception_move_id + if ( + move + and float_compare( + self.product_uom_qty, + move.product_uom_qty, + precision_rounding=move.product_uom.rounding, + ) + == 0 + ): + self.reception_move_id.sale_line_id = self.sale_line_id.id + self.reception_move_id.to_refund = True + + def _unlink_refund_with_reception_move(self): + """Perform the internal operations for unlinking the RMA reception move with the + sales order line. + """ + self.ensure_one() + self.reception_move_id.sale_line_id = False + self.reception_move_id.to_refund = False + + def action_refund(self): + """As we have made a refund, the return move + the refund should be linked to + the source sales order line, to decrease both the delivered and invoiced + quantity. + + NOTE: The refund line is linked to the SO line in `_prepare_refund_line`. + """ + res = super().action_refund() + for rma in self: + if rma.sale_line_id: + rma._link_refund_with_reception_move() + return res + def _prepare_refund(self, invoice_form, origin): """Inject salesman from sales order (if any)""" res = super()._prepare_refund(invoice_form, origin) @@ -110,10 +153,24 @@ class Rma(models.Model): return self.sale_line_id.product_id def _prepare_refund_line(self, line_form): - """Add line data""" + """Add line data and link to the sales order, only if the RMA is for the whole + move quantity. In other cases, incorrect delivered/invoiced quantities will be + logged on the sales order, so better to let the operations not linked. + """ res = super()._prepare_refund_line(line_form) line = self.sale_line_id if line: line_form.discount = line.discount line_form.sequence = line.sequence + move = self.reception_move_id + if ( + move + and float_compare( + self.product_uom_qty, + move.product_uom_qty, + precision_rounding=move.product_uom.rounding, + ) + == 0 + ): + line_form.sale_line_ids.add(line) return res diff --git a/rma_sale/tests/test_rma_sale.py b/rma_sale/tests/test_rma_sale.py index ad526684..45908b6d 100644 --- a/rma_sale/tests/test_rma_sale.py +++ b/rma_sale/tests/test_rma_sale.py @@ -96,16 +96,28 @@ class TestRmaSale(TestRmaSaleBase): rma.reception_move_id.picking_id + self.order_out_picking, order.picking_ids, ) - # Refund the RMA user = self.env["res.users"].create( {"login": "test_refund_with_so", "name": "Test"} ) order.user_id = user.id + # Receive the RMA rma.action_confirm() rma.reception_move_id.quantity_done = rma.product_uom_qty rma.reception_move_id.picking_id._action_done() + # Refund the RMA rma.action_refund() + self.assertEqual(self.order_line.qty_delivered, 0) + self.assertEqual(self.order_line.qty_invoiced, -5) self.assertEqual(rma.refund_id.user_id, user) + self.assertEqual(rma.refund_id.invoice_line_ids.sale_line_ids, self.order_line) + # Cancel the refund + rma.refund_id.button_cancel() + self.assertEqual(self.order_line.qty_delivered, 5) + self.assertEqual(self.order_line.qty_invoiced, 0) + # And put it to draft again + rma.refund_id.button_draft() + self.assertEqual(self.order_line.qty_delivered, 0) + self.assertEqual(self.order_line.qty_invoiced, -5) @users("partner@rma") def test_create_rma_from_so_portal_user(self): diff --git a/rma_sale/views/account_move_views.xml b/rma_sale/views/account_move_views.xml new file mode 100644 index 00000000..96dd435b --- /dev/null +++ b/rma_sale/views/account_move_views.xml @@ -0,0 +1,16 @@ + + + + account.move.form - Add helper sale_line_ids + account.move + + + + + + + + + + +