From 6edb18be0d58a77c760668c006307ddf7dfd4935 Mon Sep 17 00:00:00 2001 From: wan Date: Thu, 1 Aug 2019 13:41:50 +0000 Subject: [PATCH] [FIX] point_of_sale: anglo saxon price unit when invoicing before closing the session Task 2041208 1. I create a product with fifo and real_time valuation (make sure the invoicing policy is 'delivered' and make it available in pos). 2. set the cost of the product to be 5.0 and sale price to be 10.0. 3. I update its inventory and set 5 items. -> total valuation of 25. 4. I change the cost of the product to 1.0. 5. I update again the quantity to 10, making the valuation of the product to 30.0. 6. I configure point of sale to allow invoicing. 7. Open a pos session then sell 7 items of the product (invoice the order). 8. Since the product is fifo, this means that the cost of goods sold is 5 * 5.0 + 2 * 1.0 = 27.0. 9. The correct expense account line should have balance of 27.0. 10. The correct output account line should have balance of -27.0 Followp of https://github.com/odoo/odoo/pull/35335 There was missing the part when an invoice is created from the POS When the invoice is created from action_pos_order_invoice, it only contains invoice_line_ids. The stock valuation lines are present when the account.move is posted through addons/stock_account/models/account_move.py post method. That method requires _stock_account_get_anglo_saxon_price_unit to give the average unit price if it comes from a pos.order, just like it does when a sale.order. closes odoo/odoo#35379 Signed-off-by: Simon Lejeune (sle) --- addons/point_of_sale/models/__init__.py | 1 + addons/point_of_sale/models/account_move.py | 24 +++++++ addons/point_of_sale/models/pos_order.py | 1 + .../point_of_sale/tests/test_anglo_saxon.py | 70 +++++++++++++++---- 4 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 addons/point_of_sale/models/account_move.py diff --git a/addons/point_of_sale/models/__init__.py b/addons/point_of_sale/models/__init__.py index 5cc88b2c693..c7acfe82075 100644 --- a/addons/point_of_sale/models/__init__.py +++ b/addons/point_of_sale/models/__init__.py @@ -4,6 +4,7 @@ from . import account_bank_statement from . import account_journal from . import account_tax +from . import account_move from . import barcode_rule from . import chart_template from . import digest diff --git a/addons/point_of_sale/models/account_move.py b/addons/point_of_sale/models/account_move.py new file mode 100644 index 00000000000..bc8efd2e5a5 --- /dev/null +++ b/addons/point_of_sale/models/account_move.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models + + +class AccountMove(models.Model): + _inherit = 'account.move' + + pos_order_ids = fields.One2many('pos.order', 'account_move') + + +class AccountMoveLine(models.Model): + _inherit = 'account.move.line' + + def _stock_account_get_anglo_saxon_price_unit(self): + self.ensure_one() + if not self.product_id: + return self.price_unit + price_unit = super(AccountMoveLine, self)._stock_account_get_anglo_saxon_price_unit() + order = self.move_id.pos_order_ids + if order: + price_unit = - order._get_pos_anglo_saxon_price_unit(self.product_id, self.move_id.partner_id.id, self.quantity) + return price_unit diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index ed3b41420bc..ce992ea4fcf 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -650,6 +650,7 @@ class PosOrder(models.Model): # considering partner's sale pricelist's currency 'currency_id': order.pricelist_id.currency_id.id, 'invoice_user_id': order.user_id.id, + 'invoice_date': fields.Date.today(), 'fiscal_position_id': order.fiscal_position_id.id, 'invoice_line_ids': [(0, None, order._prepare_invoice_line(line)) for line in order.lines], } diff --git a/addons/point_of_sale/tests/test_anglo_saxon.py b/addons/point_of_sale/tests/test_anglo_saxon.py index aea6a6c44bc..b56f9217697 100644 --- a/addons/point_of_sale/tests/test_anglo_saxon.py +++ b/addons/point_of_sale/tests/test_anglo_saxon.py @@ -20,14 +20,16 @@ class TestAngloSaxonCommon(common.TransactionCase): self.partner = self.env.ref('base.res_partner_1') self.category = self.env.ref('product.product_category_1') self.category = self.category.copy({'name': 'New category','property_valuation': 'real_time'}) - account_type_rcv = self.env['account.account.type'].create({'name': 'RCV type', 'type': 'receivable'}) - account_type_oth = self.env['account.account.type'].create({'name': 'RCV type', 'type': 'other'}) + account_type_rcv = self.env.ref('account.data_account_type_receivable') + account_type_inc = self.env.ref('account.data_account_type_revenue') + account_type_exp = self.env.ref('account.data_account_type_expenses') self.account = self.env['account.account'].create({'name': 'Receivable', 'code': 'RCV00' , 'user_type_id': account_type_rcv.id, 'reconcile': True}) - account_expense = self.env['account.account'].create({'name': 'Expense', 'code': 'EXP00' , 'user_type_id': account_type_oth.id, 'reconcile': True}) - account_output = self.env['account.account'].create({'name': 'Output', 'code': 'OUT00' , 'user_type_id': account_type_oth.id, 'reconcile': True}) - account_valuation = self.env['account.account'].create({'name': 'Valuation', 'code': 'STV00', 'user_type_id': account_type_oth.id, 'reconcile': True}) + account_expense = self.env['account.account'].create({'name': 'Expense', 'code': 'EXP00' , 'user_type_id': account_type_exp.id, 'reconcile': True}) + account_income = self.env['account.account'].create({'name': 'Income', 'code': 'INC00' , 'user_type_id': account_type_inc.id, 'reconcile': True}) + account_output = self.env['account.account'].create({'name': 'Output', 'code': 'OUT00' , 'user_type_id': account_type_exp.id, 'reconcile': True}) + account_valuation = self.env['account.account'].create({'name': 'Valuation', 'code': 'STV00', 'user_type_id': account_type_exp.id, 'reconcile': True}) self.partner.property_account_receivable_id = self.account - self.category.property_account_income_categ_id = self.account + self.category.property_account_income_categ_id = account_income self.category.property_account_expense_categ_id = account_expense self.category.property_stock_account_input_categ_id = self.account self.category.property_stock_account_output_categ_id = account_output @@ -39,11 +41,12 @@ class TestAngloSaxonCommon(common.TransactionCase): self.company.anglo_saxon_accounting = True self.product.categ_id = self.category self.product.property_account_expense_id = account_expense - self.product.property_account_income_id = self.account + self.product.property_account_income_id = account_income sale_journal = self.env['account.journal'].create({'name': 'POS journal', 'type': 'sale', 'code': 'POS00'}) self.pos_config.journal_id = sale_journal self.cash_journal = self.env['account.journal'].create({'name': 'CASH journal', 'type': 'cash', 'code': 'CSH00'}) - self.pos_config.invoice_journal_id = False + self.sale_journal = self.env['account.journal'].create({'name': 'SALE journal', 'type': 'sale', 'code': 'INV00'}) + self.pos_config.invoice_journal_id = self.sale_journal self.pos_config.journal_ids = [self.cash_journal.id] @@ -116,7 +119,10 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon): self.assertEqual(aml_output.credit, self.product.standard_price, "Cost of Good Sold entry missing or mismatching") self.assertEqual(aml_expense.debit, self.product.standard_price, "Cost of Good Sold entry missing or mismatching") - def test_2041208(self): + def _prepare_pos_order(self): + """ Set the cost method of `self.product` as FIFO. Receive 5@5 and 5@1 and + create a `pos.order` record selling 7 units @ 450. + """ # check fifo Costing Method of product.category self.product.categ_id.property_cost_method = 'fifo' self.product.standard_price = 5.0 @@ -137,7 +143,7 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon): self.pos_config.open_session_cb() self.pos_config.module_account = True - self.pos_order_pos0 = self.PosOrder.create({ + pos_order_values = { 'company_id': self.company.id, 'partner_id': self.partner.id, 'pricelist_id': self.company.partner_id.property_product_pricelist.id, @@ -155,19 +161,55 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon): 'amount_tax': 0, 'amount_paid': 0, 'amount_return': 0, + } + + return self.PosOrder.create(pos_order_values) + + def test_fifo_valuation_no_invoice(self): + """Register a payment and validate a session after selling a fifo + product without making an invoice for the customer""" + pos_order_pos0 = self._prepare_pos_order() + context_make_payment = {"active_ids": [pos_order_pos0.id], "active_id": pos_order_pos0.id} + self.pos_make_payment_0 = self.PosMakePayment.with_context(context_make_payment).create({ + 'amount': 7 * 450.0, }) - context_make_payment = {"active_ids": [self.pos_order_pos0.id], "active_id": self.pos_order_pos0.id} + # register the payment + context_payment = {'active_id': pos_order_pos0.id} + self.pos_make_payment_0.with_context(context_payment).check() + + # validate the session + current_session_id = self.pos_config.current_session_id + current_session_id.action_pos_session_validate() + + # check the anglo saxon move lines + line = pos_order_pos0.account_move.line_ids.filtered(lambda l: l.debit and l.account_id == self.category.property_account_expense_categ_id) + self.assertEqual(pos_order_pos0.account_move.journal_id, self.pos_config.journal_id) + self.assertEqual(line.debit, 27, 'As it is a fifo product, the move\'s value should be 5*5 + 2*1') + + def test_fifo_valuation_with_invoice(self): + """Register a payment and validate a session after selling a fifo + product and make an invoice for the customer""" + pos_order_pos0 = self._prepare_pos_order() + context_make_payment = {"active_ids": [pos_order_pos0.id], "active_id": pos_order_pos0.id} self.pos_make_payment_0 = self.PosMakePayment.with_context(context_make_payment).create({ 'amount': 7 * 450.0, 'journal_id': self.cash_journal.id, }) - # I click on the validate button to register the payment. - context_payment = {'active_id': self.pos_order_pos0.id} + # register the payment + context_payment = {'active_id': pos_order_pos0.id} self.pos_make_payment_0.with_context(context_payment).check() + + # Create the customer invoice + pos_order_pos0.action_pos_order_invoice() + pos_order_pos0.account_move.post() + + # validate the session current_session_id = self.pos_config.current_session_id current_session_id.action_pos_session_validate() - line = self.pos_order_pos0.account_move.line_ids.filtered(lambda l: l.debit and l.account_id == self.category.property_account_expense_categ_id) + # check the anglo saxon move lines + line = pos_order_pos0.account_move.line_ids.filtered(lambda l: l.debit and l.account_id == self.category.property_account_expense_categ_id) + self.assertEqual(pos_order_pos0.account_move.journal_id, self.pos_config.invoice_journal_id) self.assertEqual(line.debit, 27, 'As it is a fifo product, the move\'s value should be 5*5 + 2*1')