From 1dd4ec4f95b9bdb4c800582de7d79e6b85795921 Mon Sep 17 00:00:00 2001 From: mariadforgeflow Date: Mon, 27 Feb 2023 13:28:52 +0100 Subject: [PATCH 1/2] [FIX] rma_purchase: write-off differences in price between rma line and vendor refund --- rma_purchase/models/account_move.py | 103 ++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/rma_purchase/models/account_move.py b/rma_purchase/models/account_move.py index c173139f..7884d94a 100644 --- a/rma_purchase/models/account_move.py +++ b/rma_purchase/models/account_move.py @@ -2,6 +2,7 @@ # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) from odoo import models +from odoo.tools.float_utils import float_compare, float_is_zero class AccountMove(models.Model): @@ -40,3 +41,105 @@ class AccountMove(models.Model): ) amls.reconcile() return res + + def _stock_account_prepare_anglo_saxon_in_lines_vals(self): + lines_vals_list_rma = [] + rma_refunds = self.env["account.move"] + price_unit_prec = self.env["decimal.precision"].precision_get("Product Price") + for move in self: + if ( + move.move_type != "in_refund" + or not move.company_id.anglo_saxon_accounting + ): + continue + move = move.with_company(move.company_id) + for line in move.invoice_line_ids.filtered(lambda l: l.rma_line_id): + if ( + line.product_id.type != "product" + or line.product_id.valuation != "real_time" + ): + continue + # Retrieve accounts needed to generate the price difference. + debit_pdiff_account = ( + line.product_id.property_account_creditor_price_difference + or line.product_id.categ_id.property_account_creditor_price_difference_categ + ) + debit_pdiff_account = move.fiscal_position_id.map_account( + debit_pdiff_account + ) + if not debit_pdiff_account: + continue + rma_line = line.rma_line_id + valuation_price_unit = rma_line._get_price_unit() + price_unit = line.price_unit * (1 - (line.discount or 0.0) / 100.0) + price_unit_val_dif = price_unit - valuation_price_unit + price_subtotal = line.quantity * price_unit_val_dif + if ( + not move.currency_id.is_zero(price_subtotal) + and not float_is_zero( + price_unit_val_dif, precision_digits=price_unit_prec + ) + and float_compare( + line["price_unit"], + line.price_unit, + precision_digits=price_unit_prec, + ) + == 0 + ): + # Add price difference account line. + vals = { + "name": line.name[:64], + "move_id": move.id, + "partner_id": line.partner_id.id + or move.commercial_partner_id.id, + "currency_id": line.currency_id.id, + "product_id": line.product_id.id, + "product_uom_id": line.product_uom_id.id, + "quantity": line.quantity, + "price_unit": price_unit_val_dif, + "price_subtotal": line.quantity * price_unit_val_dif, + "account_id": debit_pdiff_account.id, + "analytic_account_id": line.analytic_account_id.id, + "analytic_tag_ids": [(6, 0, line.analytic_tag_ids.ids)], + "exclude_from_invoice_tab": True, + "is_anglo_saxon_line": True, + "rma_line_id": line.rma_line_id.id, + } + vals.update( + line._get_fields_onchange_subtotal( + price_subtotal=vals["price_subtotal"] + ) + ) + lines_vals_list_rma.append(vals) + + # Correct the amount of the current line. + vals = { + "name": line.name[:64], + "move_id": move.id, + "partner_id": line.partner_id.id + or move.commercial_partner_id.id, + "currency_id": line.currency_id.id, + "product_id": line.product_id.id, + "product_uom_id": line.product_uom_id.id, + "quantity": line.quantity, + "price_unit": -price_unit_val_dif, + "price_subtotal": line.quantity * -price_unit_val_dif, + "account_id": line.account_id.id, + "analytic_account_id": line.analytic_account_id.id, + "analytic_tag_ids": [(6, 0, line.analytic_tag_ids.ids)], + "exclude_from_invoice_tab": True, + "is_anglo_saxon_line": True, + "rma_line_id": line.rma_line_id.id, + } + vals.update( + line._get_fields_onchange_subtotal( + price_subtotal=vals["price_subtotal"] + ) + ) + lines_vals_list_rma.append(vals) + rma_refunds |= move + lines_vals_list = super( + AccountMove, self - rma_refunds + )._stock_account_prepare_anglo_saxon_in_lines_vals() + lines_vals_list += lines_vals_list_rma + return lines_vals_list From 15c85ee91baff7fad5064b79c4a6488bf05a619c Mon Sep 17 00:00:00 2001 From: mariadforgeflow Date: Mon, 27 Feb 2023 15:07:54 +0100 Subject: [PATCH 2/2] [IMP] rma_purchase: add test return_and_refund_diff_price --- .../tests/test_rma_stock_account_purchase.py | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/rma_purchase/tests/test_rma_stock_account_purchase.py b/rma_purchase/tests/test_rma_stock_account_purchase.py index 023b08af..ec4a64fe 100644 --- a/rma_purchase/tests/test_rma_stock_account_purchase.py +++ b/rma_purchase/tests/test_rma_stock_account_purchase.py @@ -17,6 +17,10 @@ class TestRmaStockAccountPurchase(TestRmaStockAccount): cls.rma_operation_supplier_refund = cls.env.ref( "rma_account.rma_operation_supplier_refund" ) + acc_type = cls._create_account_type("expense", "other") + cls.account_price_diff = cls._create_account( + acc_type, "Refund Price Difference Expense", "rpde", cls.company, False + ) def test_01_cost_from_po_move(self): """ @@ -140,3 +144,77 @@ class TestRmaStockAccountPurchase(TestRmaStockAccount): ) self.assertEqual(sum(grni_amls.mapped("balance")), 0.0) self.assertTrue(all(grni_amls.mapped("reconciled"))) + + def test_03_return_and_refund_diff_price(self): + """ + Purchase a product. + Then create an RMA to return it and get the refund from the supplier with + a different price than the original purchase price + """ + self.product_fifo_1.categ_id.update( + { + "property_account_creditor_price_difference_categ": self.account_price_diff + } + ) + self.product_fifo_1.standard_price = 1234 + po = self.po_model.create( + { + "partner_id": self.partner_id.id, + } + ) + pol_1 = self.pol_model.create( + { + "name": self.product_fifo_1.name, + "order_id": po.id, + "product_id": self.product_fifo_1.id, + "product_qty": 10.0, + "product_uom": self.product_fifo_1.uom_id.id, + "price_unit": 100.0, + "date_planned": Datetime.now(), + } + ) + po.button_confirm() + self._do_picking(po.picking_ids) + self.product_fifo_1.standard_price = 1234 # this should not be taken + supplier_view = self.env.ref("rma_purchase.view_rma_line_form") + rma_line = Form( + self.rma_line.with_context(supplier=1).with_user(self.rma_basic_user), + view=supplier_view.id, + ) + rma_line.partner_id = po.partner_id + rma_line.purchase_order_line_id = pol_1 + rma_line.price_unit = 4356 + rma_line.operation_id = self.rma_operation_supplier_refund + rma_line = rma_line.save() + rma_line.action_rma_to_approve() + self._deliver_rma(rma_line) + + with Form( + self.env["account.move"].with_context(default_move_type="in_refund") + ) as bill_form: + bill_form.partner_id = rma_line.partner_id + bill_form.invoice_date = Date.today() + bill_form.add_rma_line_id = rma_line + bill = bill_form.save() + bill_form = Form(bill) + with bill_form.invoice_line_ids.edit(0) as line_form: + line_form.price_unit = 110 + bill = bill_form.save() + bill.action_post() + self.assertEquals(len(bill.invoice_line_ids), 1) + self.assertEquals(bill.invoice_line_ids.rma_line_id, rma_line) + grni_amls = self.env["account.move.line"].search( + [ + ("account_id", "=", self.account_grni.id), + ("rma_line_id", "=", rma_line.id), + ] + ) + self.assertEqual(sum(grni_amls.mapped("balance")), 0.0) + self.assertTrue(all(grni_amls.mapped("reconciled"))) + price_diff_amls = self.env["account.move.line"].search( + [ + ("account_id", "=", self.account_price_diff.id), + ("rma_line_id", "=", rma_line.id), + ] + ) + self.assertEqual(sum(price_diff_amls.mapped("balance")), -100)