From b036a33656944bb3400e7fd8d245d14d7defc1dd Mon Sep 17 00:00:00 2001 From: "lase@odoo.com" Date: Tue, 30 Jan 2024 08:44:24 +0000 Subject: [PATCH] [FIX] stock, sale_stock: update delivered quantities for negative SO lines Current behavior: Creating an SO with negative quantities for a storable product with an invoicing policy of type "delivered quantities" automatically generates a return move for the stocks. However, when this delivery is validated, the delivered quantities are not updated on the SO. This is problematic as these quantities are therefore not taken into account on the associated invoice. Expected behavior: The delivered quantities should be updated negatively on the SO to enable the invoicing of these lines. This is already the behavior in the POS application and when you create an SO with positive quantities followed by a return for a larger quantity than the one delivered. Steps to reproduce: Create a storable product with an invoicing policy of type "delivered quantities". Create an SO with 2 lines: - a line with positive quantities for any other product. - a line with negative quantities for the product you created. Confirm and validate the corresponding deliveries. Return to the SO. The quantities for the second line are not updated. Create an invoice. The second line is not taken into account. Cause of the issue: The to_refund field of the stock.move model defined in the stock_account module enables a decrease of the delivered quantities in the associated Sale Order. This field is set to True for "classic" returns but not for the stock.move generated from sale.order.line with negative quantities. Fix: We rely on the _get_custom_move_fields method to add the to_refund field in the procurement 'values' arguments in case the stock_account module is not installed. It is then available to use in the _get_stock_move_values method where we set its value to True if the quantity is negative (so that the move should be considered as a refund) opw-3676045 closes odoo/odoo#154496 X-original-commit: 92917d507a63ee08fc839ed3f7607d5e5060f0a2 Signed-off-by: William Henrotin (whe) --- addons/sale_stock/models/stock.py | 2 +- addons/sale_stock/tests/test_sale_stock.py | 30 ++++++++++++++++++++++ addons/stock/models/stock_rule.py | 4 +++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/addons/sale_stock/models/stock.py b/addons/sale_stock/models/stock.py index 3fd60679493..c4a8e7487ea 100644 --- a/addons/sale_stock/models/stock.py +++ b/addons/sale_stock/models/stock.py @@ -76,7 +76,7 @@ class StockRule(models.Model): def _get_custom_move_fields(self): fields = super(StockRule, self)._get_custom_move_fields() - fields += ['sale_line_id', 'partner_id', 'sequence'] + fields += ['sale_line_id', 'partner_id', 'sequence', 'to_refund'] return fields diff --git a/addons/sale_stock/tests/test_sale_stock.py b/addons/sale_stock/tests/test_sale_stock.py index cc367019650..6a20a91314c 100644 --- a/addons/sale_stock/tests/test_sale_stock.py +++ b/addons/sale_stock/tests/test_sale_stock.py @@ -6,6 +6,7 @@ from odoo.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliation_c from odoo.addons.sale.tests.common import TestSaleCommon from odoo.exceptions import UserError from odoo.tests import Form, tagged +from odoo import Command @tagged('post_install', '-at_install') @@ -1720,3 +1721,32 @@ class TestSaleStock(TestSaleCommon, ValuationReconciliationTestCommon): self.assertEqual(out1.move_line_ids.quantity, 3) self.assertEqual(out2.state, 'assigned') self.assertEqual(out2.move_line_ids.quantity, 1) + + def test_delivery_on_negative_delivered_qty(self): + """ + Tests that returns created from SO lines with negative quantities update the delivered + quantities negatively so that they appear on the corresponding invoice. + """ + product = self.env['product.product'].create({ + 'name': 'Super product', + 'uom_id': self.env.ref('uom.product_uom_unit').id, + 'lst_price': 100.0, + 'detailed_type': 'product', + 'invoice_policy': 'delivery', + }) + sale_order = self.env['sale.order'].create({ + 'partner_id': self.partner_a.id, + 'state': 'draft', + 'order_line':[Command.create({ + 'product_id': product.id, + 'product_uom_qty': -1, + })], + }) + sale_order.action_confirm() + self.assertEqual(sale_order.order_line.qty_delivered, 0.0) + self.assertEqual(sale_order.order_line.qty_to_invoice, 0.0) + picking = self.env['stock.move'].browse(self.env['stock.move'].search([('sale_line_id', '=', sale_order.order_line.id)]).id).picking_id + picking.action_confirm() + picking.button_validate() + self.assertEqual(sale_order.order_line.qty_delivered, -1.0) + self.assertEqual(sale_order.order_line.qty_to_invoice, -1.0) diff --git a/addons/stock/models/stock_rule.py b/addons/stock/models/stock_rule.py index 27ce620f9b5..0d5f58c0ad0 100644 --- a/addons/stock/models/stock_rule.py +++ b/addons/stock/models/stock_rule.py @@ -322,6 +322,10 @@ class StockRule(models.Model): partner = partners move_dest.partner_id = self.location_src_id.warehouse_id.partner_id or self.company_id.partner_id + # If the quantity is negative the move should be considered as a refund + if float_compare(product_qty, 0.0, precision_rounding=product_uom.rounding) < 0: + values['to_refund'] = True + move_values = { 'name': name[:2000], 'company_id': self.company_id.id or self.location_src_id.company_id.id or self.location_dest_id.company_id.id or company_id.id,