From f022fffc3ab8d783b0b8c382bd4db668540501e7 Mon Sep 17 00:00:00 2001 From: roen-odoo Date: Mon, 28 Aug 2023 09:22:42 +0000 Subject: [PATCH] [FIX] point_of_sale: ship later with multiple expense account Current behavior: If you had 2 products with different expense account and using real-time inventory valuation, there was an error when validating the picking. This was happening because move_vals we were trying to assign multiple moves to one pos_order here https://github.com/odoo/odoo/blob/95cec6ea3daebce6491cc2a8a69d9688322989ba/addons/point_of_sale/models/stock_picking.py#L155 The account move is actually reserved for the invoicing of the order. So we just need to remove that line. Steps to reproduce: - Create a product with expense account A - Create a product with expense account B - Make sure both products are set to real-time inventory valuation - Activate ship later in the PoS - Open the PoS and add both products to the order - Validate the order with ship later and no invoice - Close the PoS and try to validate the picking of the order. opw-3428033 closes odoo/odoo#136254 X-original-commit: a2c8ea0adbd93cb9d178977f73e93492e84dfec3 Signed-off-by: Joseph Caburnay (jcb) Signed-off-by: Robin Engels (roen) --- addons/point_of_sale/models/pos_session.py | 3 +- addons/point_of_sale/models/stock_picking.py | 3 +- .../point_of_sale/tests/test_anglo_saxon.py | 22 ++--- .../tests/test_point_of_sale_flow.py | 85 +++++++++++++++++++ 4 files changed, 99 insertions(+), 14 deletions(-) diff --git a/addons/point_of_sale/models/pos_session.py b/addons/point_of_sale/models/pos_session.py index 1b47a7ddc04..1ea777a2b18 100644 --- a/addons/point_of_sale/models/pos_session.py +++ b/addons/point_of_sale/models/pos_session.py @@ -1468,7 +1468,8 @@ class PosSession(models.Model): # we are querying over the account.move.line because its 'ref' is indexed. # And yes, we are only concern for split bank payment methods. diff_lines_ref = [self._get_diff_account_move_ref(pm) for pm in self.payment_method_ids if pm.type == 'bank' and pm.split_transactions] - return self.env['account.move.line'].search([('ref', 'in', diff_lines_ref)]).mapped('move_id') + cost_move_lines = ['pos_order_'+str(rec.id) for rec in self.order_ids] + return self.env['account.move.line'].search([('ref', 'in', diff_lines_ref + cost_move_lines)]).mapped('move_id') def _get_related_account_moves(self): pickings = self.picking_ids | self.order_ids.mapped('picking_ids') diff --git a/addons/point_of_sale/models/stock_picking.py b/addons/point_of_sale/models/stock_picking.py index 0eced516254..28c5e3c7486 100644 --- a/addons/point_of_sale/models/stock_picking.py +++ b/addons/point_of_sale/models/stock_picking.py @@ -136,7 +136,7 @@ class StockPicking(models.Model): move_vals.append({ 'journal_id': rec.pos_order_id.sale_journal.id, 'date': rec.pos_order_id.date_order, - 'ref': rec.pos_order_id.name, + 'ref': 'pos_order_'+str(rec.pos_order_id.id), 'line_ids': [ (0, 0, { 'name': rec.pos_order_id.name, @@ -153,7 +153,6 @@ class StockPicking(models.Model): ], }) move = self.env['account.move'].create(move_vals) - rec.pos_order_id.write({'account_move': move.id}) move.action_post() return res diff --git a/addons/point_of_sale/tests/test_anglo_saxon.py b/addons/point_of_sale/tests/test_anglo_saxon.py index bf199de8d84..22d5ca18038 100644 --- a/addons/point_of_sale/tests/test_anglo_saxon.py +++ b/addons/point_of_sale/tests/test_anglo_saxon.py @@ -289,18 +289,18 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon): self.assertEqual(len(aml_output), 3, "There should be 3 output account move lines") # 2 moves in POS journal (Pos order + manual entry at delivery) - self.assertEqual(aml_output[:2].move_id.journal_id, self.pos_config.journal_id) + self.assertEqual(len(aml_output.move_id.filtered(lambda l: l.journal_id == self.pos_config.journal_id)), 2) # 1 move in stock journal (delivery from stock layers) - self.assertEqual(aml_output[2].move_id.journal_id, self.category.property_stock_journal) + self.assertEqual(len(aml_output.move_id.filtered(lambda l: l.journal_id == self.category.property_stock_journal)), 1) #Check the lines created after the picking validation - self.assertEqual(aml_output[0].credit, self.product.standard_price, "Cost of Good Sold entry missing or mismatching") - self.assertEqual(aml_output[0].debit, 0.0, "Cost of Good Sold entry missing or mismatching") - self.assertEqual(aml_output[2].debit, self.product.standard_price, "Cost of Good Sold entry missing or mismatching") - self.assertEqual(aml_output[2].credit, 0.0, "Cost of Good Sold entry missing or mismatching") - self.assertEqual(aml_expense[0].debit, self.product.standard_price, "Cost of Good Sold entry missing or mismatching") - self.assertEqual(aml_expense[0].credit, 0.0, "Cost of Good Sold entry missing or mismatching") - #Check the lines created by the PoS session - self.assertEqual(aml_output[1].debit, 0.0, "Cost of Good Sold entry missing or mismatching") + self.assertEqual(aml_output[2].credit, self.product.standard_price, "Cost of Good Sold entry missing or mismatching") + self.assertEqual(aml_output[2].debit, 0.0, "Cost of Good Sold entry missing or mismatching") + self.assertEqual(aml_output[1].debit, self.product.standard_price, "Cost of Good Sold entry missing or mismatching") self.assertEqual(aml_output[1].credit, 0.0, "Cost of Good Sold entry missing or mismatching") + self.assertEqual(aml_expense[1].debit, self.product.standard_price, "Cost of Good Sold entry missing or mismatching") self.assertEqual(aml_expense[1].credit, 0.0, "Cost of Good Sold entry missing or mismatching") - self.assertEqual(aml_expense[1].debit, 0.0, "Cost of Good Sold entry missing or mismatching") + #Check the lines created by the PoS session + self.assertEqual(aml_output[0].debit, 0.0, "Cost of Good Sold entry missing or mismatching") + self.assertEqual(aml_output[0].credit, 0.0, "Cost of Good Sold entry missing or mismatching") + self.assertEqual(aml_expense[0].credit, 0.0, "Cost of Good Sold entry missing or mismatching") + self.assertEqual(aml_expense[0].debit, 0.0, "Cost of Good Sold entry missing or mismatching") diff --git a/addons/point_of_sale/tests/test_point_of_sale_flow.py b/addons/point_of_sale/tests/test_point_of_sale_flow.py index e5b97b17311..9720b9ed854 100644 --- a/addons/point_of_sale/tests/test_point_of_sale_flow.py +++ b/addons/point_of_sale/tests/test_point_of_sale_flow.py @@ -1692,3 +1692,88 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon): aml = session_id.move_id.line_ids.filtered(lambda x: x.account_id == self.account1 and x.tax_ids == self.tax1) self.assertEqual(aml.price_total, 220) self.assertEqual(aml.price_subtotal, 200) + + def test_multi_exp_account_real_time(self): + + #Create a real time valuation product category + self.real_time_categ = self.env['product.category'].create({ + 'name': 'test category', + 'parent_id': False, + 'property_cost_method': 'fifo', + 'property_valuation': 'real_time', + }) + + #Create 2 accounts to be used for each product + self.account1 = self.env['account.account'].create({ + 'name': 'Account 1', + 'code': 'AC1', + 'reconcile': True, + 'account_type': 'expense', + }) + self.account2 = self.env['account.account'].create({ + 'name': 'Account 1', + 'code': 'AC2', + 'reconcile': True, + 'account_type': 'expense', + }) + + self.product_a = self.env['product.product'].create({ + 'name': 'Product A', + 'type': 'product', + 'categ_id': self.real_time_categ.id, + 'property_account_expense_id': self.account1.id, + 'property_account_income_id': self.account1.id, + }) + self.product_b = self.env['product.product'].create({ + 'name': 'Product B', + 'type': 'product', + 'categ_id': self.real_time_categ.id, + 'property_account_expense_id': self.account2.id, + 'property_account_income_id': self.account2.id, + }) + + #Create an order with the 2 products + self.pos_config.open_ui() + order = self.PosOrder.create({ + 'company_id': self.env.company.id, + 'session_id': self.pos_config.current_session_id.id, + 'partner_id': self.partner1.id, + 'lines': [(0, 0, { + 'name': "OL/0001", + 'product_id': self.product_a.id, + 'price_unit': 100, + 'discount': 0, + 'qty': 1, + 'tax_ids': [], + 'price_subtotal': 100, + 'price_subtotal_incl': 100, + }), (0, 0, { + 'name': "OL/0002", + 'product_id': self.product_b.id, + 'price_unit': 100, + 'discount': 0, + 'qty': 1, + 'tax_ids': [], + 'price_subtotal': 100, + 'price_subtotal_incl': 100, + })], + 'pricelist_id': self.pos_config.pricelist_id.id, + 'amount_paid': 200.0, + 'amount_total': 200.0, + 'amount_tax': 0.0, + 'amount_return': 0.0, + 'to_invoice': False, + 'shipping_date': fields.Date.today(), + }) + #make payment + payment_context = {"active_ids": order.ids, "active_id": order.id} + order_payment = self.PosMakePayment.with_context(**payment_context).create({ + 'amount': order.amount_total, + 'payment_method_id': self.cash_payment_method.id + }) + order_payment.with_context(**payment_context).check() + self.pos_config.current_session_id.action_pos_session_closing_control() + order.picking_ids._action_done() + + moves = self.env['account.move'].search([('ref', '=', f'pos_order_{order.id}')]) + self.assertEqual(len(moves), 2)