From cc5ff7ac8c20b24cb5d2a32bf161653db6809217 Mon Sep 17 00:00:00 2001 From: Adrien Widart Date: Tue, 6 Jul 2021 12:32:18 +0000 Subject: [PATCH] [FIX] sale_stock: set invoice_status value when delivery is canceled Suppose the option "Lock Confirmed Sales" enabled and a product with the invoicing policy set to "Delivered quantities". When cancelling the delivery of such a product, the invoice status of the associated SO should be 'Nothing to Invoice' To reproduce the error: 1. In Settings, enable "Lock Confirmed Sales" 2. Create a product P: - Type: Consumable - Invoicing policy: Delivered quantities 3. Create a SO with one P 4. Confirm the SO 5. Cancel the SO's delivery 6. Check the invoice status (`invoice_status`) - 12.0: Either by modifying the view, or by adding the field with studio or directly via PSQL - 13.0+: The field can be enabled thanks to the options of the tree view Error: The invoice status is "Fully Invoiced" but the product hasn't be delivered and no invoice has been created OPW-2464343 closes odoo/odoo#73558 X-original-commit: b87d06da379abff8fc40352192699f6b44727797 Signed-off-by: William Henrotin Signed-off-by: Adrien Widart --- addons/sale_stock/models/sale_order.py | 10 +++++++- addons/sale_stock/tests/test_sale_stock.py | 29 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py index 8cccb84719e..605433e8e91 100644 --- a/addons/sale_stock/models/sale_order.py +++ b/addons/sale_stock/models/sale_order.py @@ -434,6 +434,14 @@ class SaleOrderLine(models.Model): @api.depends('order_id.state') def _compute_invoice_status(self): + def check_moves_state(moves): + # All moves states are either 'done' or 'cancel', and there is at least one 'done' + at_least_one_done = False + for move in moves: + if move.state not in ['done', 'cancel']: + return False + at_least_one_done = at_least_one_done or move.state == 'done' + return at_least_one_done super(SaleOrderLine, self)._compute_invoice_status() for line in self: # We handle the following specific situation: a physical product is partially delivered, @@ -445,7 +453,7 @@ class SaleOrderLine(models.Model): and line.product_id.type in ['consu', 'product']\ and line.product_id.invoice_policy == 'delivery'\ and line.move_ids \ - and all(move.state in ['done', 'cancel'] for move in line.move_ids): + and check_moves_state(line.move_ids): line.invoice_status = 'invoiced' @api.depends('move_ids') diff --git a/addons/sale_stock/tests/test_sale_stock.py b/addons/sale_stock/tests/test_sale_stock.py index 9f31482fbf7..963327ef27b 100644 --- a/addons/sale_stock/tests/test_sale_stock.py +++ b/addons/sale_stock/tests/test_sale_stock.py @@ -1060,3 +1060,32 @@ class TestSaleStock(TestSaleCommon, ValuationReconciliationTestCommon): }) so.action_confirm() self.assertEqual(so.order_line.move_ids.product_packaging_id, packaging) + + def test_15_cancel_delivery(self): + """ Suppose the option "Lock Confirmed Sales" enabled and a product with the invoicing + policy set to "Delivered quantities". When cancelling the delivery of such a product, the + invoice status of the associated SO should be 'Nothing to Invoice' + """ + group_auto_done = self.env['ir.model.data'].xmlid_to_object('sale.group_auto_done_setting') + self.env.user.groups_id = [(4, group_auto_done.id)] + + product = self.product_a + product.invoice_policy = 'delivery' + partner = self.partner_a + so = self.env['sale.order'].create({ + 'partner_id': partner.id, + 'partner_invoice_id': partner.id, + 'partner_shipping_id': partner.id, + 'order_line': [(0, 0, { + 'name': product.name, + 'product_id': product.id, + 'product_uom_qty': 2, + 'product_uom': product.uom_id.id, + 'price_unit': product.list_price + })], + 'pricelist_id': self.env.ref('product.list0').id, + }) + so.action_confirm() + so.picking_ids.action_cancel() + + self.assertEqual(so.invoice_status, 'no')