From 4e67c4adeb2ae63e9c43a6ca02437e8df717eb9d Mon Sep 17 00:00:00 2001 From: Thomas Beckers Date: Tue, 14 Jun 2022 14:12:58 +0000 Subject: [PATCH] [FIX] purchase: don't mix PO's lines in generated invoices If we use the auto-complete feature to add PO's lines to an bill or we select multiple PO's then use 'create bill' button, the generated invoice line will copy the sequence line from the PO's line. This can lead to situation where we will have all the first lines of each PO then all the second, etc, ending with a mix of all PO's in the bill. Example: Purchase order 1 - seq 10 line A - seq 11 line B - seq 12 line C Purchase order 2 - seq 10 line A' - seq 11 line B' - seq 12 line C' Invoice created from those PO's - seq 10 PO1:line A - seq 10 PO2:line A' - seq 11 PO1:line B - seq 11 PO2:line B' - seq 12 PO1:line C - seq 12 PO2:line C' After this PR this PR the lines from the same PO's will be contiguous like: Invoice created from those PO 1 and 2 - seq 10 PO1:line A - seq 11 PO1:line B - seq 12 PO1:line C - seq 13 PO2:line A' - seq 14 PO2:line B' - seq 15 PO2:line C' opw-2749682 closes odoo/odoo#95343 X-original-commit: cafe5c1aff1ca1632ce4924a6b027629c283a5e9 Signed-off-by: Steve Van Essche Signed-off-by: Beckers Thomas (tbs) --- addons/purchase/models/account_invoice.py | 6 +- addons/purchase/models/purchase.py | 11 ++- .../purchase/tests/test_purchase_invoice.py | 72 +++++++++++++++++++ 3 files changed, 86 insertions(+), 3 deletions(-) diff --git a/addons/purchase/models/account_invoice.py b/addons/purchase/models/account_invoice.py index 9f9a6fc3820..728a72727cb 100644 --- a/addons/purchase/models/account_invoice.py +++ b/addons/purchase/models/account_invoice.py @@ -53,8 +53,12 @@ class AccountMove(models.Model): # Copy purchase lines. po_lines = self.purchase_id.order_line - self.line_ids.mapped('purchase_line_id') new_lines = self.env['account.move.line'] + sequence = max(self.line_ids.mapped('sequence')) + 1 if self.line_ids else 10 for line in po_lines.filtered(lambda l: not l.display_type): - new_line = new_lines.new(line._prepare_account_move_line(self)) + line_vals = line._prepare_account_move_line(self) + line_vals.update({'sequence': sequence}) + new_line = new_lines.new(line_vals) + sequence += 1 new_line.account_id = new_line._get_computed_account() new_line._onchange_price_subtotal() new_lines += new_line diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index 3a03207fff5..c1d37b4db3f 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -541,6 +541,7 @@ class PurchaseOrder(models.Model): # 1) Prepare invoice vals and clean-up the section lines invoice_vals_list = [] + sequence = 10 for order in self: if order.invoice_status != 'to invoice': continue @@ -556,9 +557,15 @@ class PurchaseOrder(models.Model): continue if not float_is_zero(line.qty_to_invoice, precision_digits=precision): if pending_section: - invoice_vals['invoice_line_ids'].append((0, 0, pending_section._prepare_account_move_line())) + line_vals = pending_section._prepare_account_move_line() + line_vals.update({'sequence': sequence}) + invoice_vals['invoice_line_ids'].append((0, 0, line_vals)) + sequence += 1 pending_section = None - invoice_vals['invoice_line_ids'].append((0, 0, line._prepare_account_move_line())) + line_vals = line._prepare_account_move_line() + line_vals.update({'sequence': sequence}) + invoice_vals['invoice_line_ids'].append((0, 0, line_vals)) + sequence += 1 invoice_vals_list.append(invoice_vals) if not invoice_vals_list: diff --git a/addons/purchase/tests/test_purchase_invoice.py b/addons/purchase/tests/test_purchase_invoice.py index e3fbb8ccd39..630397b7d7a 100644 --- a/addons/purchase/tests/test_purchase_invoice.py +++ b/addons/purchase/tests/test_purchase_invoice.py @@ -327,3 +327,75 @@ class TestPurchaseToInvoice(AccountTestInvoicingCommon): aml = self.env['account.move.line'].search([('purchase_line_id', '=', purchase_order.order_line.id)]) self.assertRecordValues(aml, [{'analytic_account_id': analytic_account_manual.id}]) + + def test_sequence_invoice_lines_from_multiple_purchases(self): + """Test if the invoice lines are sequenced by purchase order when creating an invoice + from multiple selected po's""" + purchase_orders = self.env['purchase.order'] + + for _ in range(3): + pol_vals = [ + (0, 0, { + 'name': self.product_order.name, + 'product_id': self.product_order.id, + 'product_qty': 10.0, + 'product_uom': self.product_order.uom_id.id, + 'price_unit': self.product_order.list_price, + 'taxes_id': False, + 'sequence': sequence_number, + }) for sequence_number in range(10, 13)] + purchase_order = self.env['purchase.order'].with_context(tracking_disable=True).create({ + 'partner_id': self.partner_a.id, + 'order_line': pol_vals, + }) + purchase_order.button_confirm() + purchase_orders |= purchase_order + + action = purchase_orders.action_create_invoice() + invoice = self.env['account.move'].browse(action['res_id']) + + expected_purchase = [ + purchase_orders[0], purchase_orders[0], purchase_orders[0], + purchase_orders[1], purchase_orders[1], purchase_orders[1], + purchase_orders[2], purchase_orders[2], purchase_orders[2], + ] + for line in invoice.invoice_line_ids.sorted('sequence'): + self.assertEqual(line.purchase_order_id, expected_purchase.pop(0)) + + def test_sequence_autocomplete_invoice(self): + """Test if the invoice lines are sequenced by purchase order when using the autocomplete + feature on a bill to add lines from po's""" + purchase_orders = self.env['purchase.order'] + + for _ in range(3): + pol_vals = [ + (0, 0, { + 'name': self.product_order.name, + 'product_id': self.product_order.id, + 'product_qty': 10.0, + 'product_uom': self.product_order.uom_id.id, + 'price_unit': self.product_order.list_price, + 'taxes_id': False, + 'sequence': sequence_number, + }) for sequence_number in range(10, 13)] + purchase_order = self.env['purchase.order'].with_context(tracking_disable=True).create({ + 'partner_id': self.partner_a.id, + 'order_line': pol_vals, + }) + purchase_order.button_confirm() + purchase_orders |= purchase_order + + move_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice')) + PurchaseBillUnion = self.env['purchase.bill.union'] + move_form.purchase_vendor_bill_id = PurchaseBillUnion.browse(-purchase_orders[0].id) + move_form.purchase_vendor_bill_id = PurchaseBillUnion.browse(-purchase_orders[1].id) + move_form.purchase_vendor_bill_id = PurchaseBillUnion.browse(-purchase_orders[2].id) + invoice = move_form.save() + + expected_purchase = [ + purchase_orders[0], purchase_orders[0], purchase_orders[0], + purchase_orders[1], purchase_orders[1], purchase_orders[1], + purchase_orders[2], purchase_orders[2], purchase_orders[2], + ] + for line in invoice.invoice_line_ids.sorted('sequence'): + self.assertEqual(line.purchase_order_id, expected_purchase.pop(0))