From 92393a6b8e64e99591ff4ead166e93caa8da2dcb Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Mon, 13 May 2019 07:32:55 +0000 Subject: [PATCH] [FIX] purchase_stock: discount on invoice - Create a product P Costing Method: FIFO Inventory Valuation: Perpetual (automated) Price Difference Account set - Create a PO with P, set a price of 100 (no tax). - Receive the product 2 journal items are generated: | Account | Debit| Credit| |-------------------------|--------|--------| | Stock Valuation | 100.0| 0.0| | Stock Interim (Received)| 0.0| 100.0| - Create the corresponding invoice - Change the: Discount: 10 - Validate the invoice (for a total of 90.0) 2 journal items are generated: | Account | Debit| Credit| |-------------------------|--------|--------| | Account Payable | 0.0| 90.0| | Stock Interim (Received)| 90.0| 0.0| This is not correct, it should be: | Account | Debit| Credit| |-------------------------|--------|--------| | Account Payable | 0.0| 90.0| | Difference Account | 0.0| 10.0| | Stock Interim (Received)| 100.0| 0.0| This comes from the fact that the valuation price is compared to the price unit without the discount. opw-1974002 closes odoo/odoo#33296 Signed-off-by: Nicolas Martinelli (nim) --- .../purchase_stock/models/account_invoice.py | 29 +++--- .../tests/test_stockvaluation.py | 98 +++++++++++++++++++ 2 files changed, 112 insertions(+), 15 deletions(-) diff --git a/addons/purchase_stock/models/account_invoice.py b/addons/purchase_stock/models/account_invoice.py index 6bb077ce5d7..a6042686a1e 100644 --- a/addons/purchase_stock/models/account_invoice.py +++ b/addons/purchase_stock/models/account_invoice.py @@ -96,26 +96,25 @@ class AccountInvoice(models.Model): interim_account_price = valuation_price_unit * line['quantity'] invoice_cur_prec = inv.currency_id.decimal_places - if float_compare(valuation_price_unit, i_line.price_unit, precision_digits=invoice_cur_prec) != 0 and float_compare(line['price_unit'], i_line.price_unit, precision_digits=invoice_cur_prec) == 0: - - # price with discount and without tax included - price_unit = i_line.price_unit * (1 - (i_line.discount or 0.0) / 100.0) - tax_ids = [] - if line['tax_ids']: - #line['tax_ids'] is like [(4, tax_id, None), (4, tax_id2, None)...] - taxes = self.env['account.tax'].browse([x[1] for x in line['tax_ids']]) - price_unit = taxes.compute_all(price_unit, currency=inv.currency_id, quantity=1.0)['total_excluded'] - for tax in taxes: - tax_ids.append((4, tax.id, None)) - for child in tax.children_tax_ids: - if child.type_tax_use != 'none': - tax_ids.append((4, child.id, None)) + # price with discount and without tax included + price_unit = i_line.price_unit * (1 - (i_line.discount or 0.0) / 100.0) + tax_ids = [] + if line['tax_ids']: + #line['tax_ids'] is like [(4, tax_id, None), (4, tax_id2, None)...] + taxes = self.env['account.tax'].browse([x[1] for x in line['tax_ids']]) + price_unit = taxes.compute_all(price_unit, currency=inv.currency_id, quantity=1.0)['total_excluded'] + for tax in taxes: + tax_ids.append((4, tax.id, None)) + for child in tax.children_tax_ids: + if child.type_tax_use != 'none': + tax_ids.append((4, child.id, None)) + if float_compare(valuation_price_unit, price_unit, precision_digits=invoice_cur_prec) != 0 and float_compare(line['price_unit'], i_line.price_unit, precision_digits=invoice_cur_prec) == 0: price_before = line.get('price', 0.0) price_unit_val_dif = price_unit - valuation_price_unit price_val_dif = price_before - interim_account_price - if inv.currency_id.compare_amounts(i_line.price_unit, valuation_price_unit) != 0 and acc: + if inv.currency_id.compare_amounts(price_unit, valuation_price_unit) != 0 and acc: # If the unit prices have not changed and we have a # valuation difference, it means this difference is due to exchange rates, # so we don't create anything, the exchange rate entries will diff --git a/addons/purchase_stock/tests/test_stockvaluation.py b/addons/purchase_stock/tests/test_stockvaluation.py index de3f8540327..46cb357ed50 100644 --- a/addons/purchase_stock/tests/test_stockvaluation.py +++ b/addons/purchase_stock/tests/test_stockvaluation.py @@ -1168,3 +1168,101 @@ class TestStockValuationWithCOA(AccountingTestCase): self.assertEquals(len(input_aml), 2, "Only two lines should have been generated in stock input account: one when receiving the product, one when making the invoice.") self.assertAlmostEquals(sum(input_aml.mapped('debit')), 110, "Total debit value on stock input account should be equal to the original PO price of the product.") self.assertAlmostEquals(sum(input_aml.mapped('credit')), 110, "Total credit value on stock input account should be equal to the original PO price of the product.") + + def test_anglosaxon_valuation_discount(self): + """ + PO: price unit: 100 + Inv: price unit: 100 + discount: 10 + """ + self.env.user.company_id.anglo_saxon_accounting = True + self.product1.product_tmpl_id.cost_method = 'fifo' + self.product1.product_tmpl_id.valuation = 'real_time' + self.product1.product_tmpl_id.invoice_policy = 'delivery' + self.product1.property_account_creditor_price_difference = self.price_diff_account + + # Create PO + po_form = Form(self.env['purchase.order']) + po_form.partner_id = self.partner_id + with po_form.order_line.new() as po_line: + po_line.product_id = self.product1 + po_line.product_qty = 1 + po_line.price_unit = 100.0 + order = po_form.save() + order.button_confirm() + + # Receive the goods + receipt = order.picking_ids[0] + receipt.move_lines.quantity_done = 1 + receipt.button_validate() + + # Create an invoice with a different price and a discount + invoice = self.env['account.invoice'].create({ + 'partner_id': order.partner_id.id, + 'purchase_id': order.id, + 'account_id': order.partner_id.property_account_payable_id.id, + 'type': 'in_invoice', + }) + invoice.purchase_order_change() + invoice.invoice_line_ids[0].discount = 10.0 + invoice.action_invoice_open() + + # Check what was posted in the price difference account + price_diff_aml = self.env['account.move.line'].search([('account_id','=', self.price_diff_account.id)]) + self.assertEquals(len(price_diff_aml), 1, "Only one line should have been generated in the price difference account.") + self.assertAlmostEquals(price_diff_aml.credit, 10, "Price difference should be equal to 20 (110-90)") + + # Check what was posted in stock input account + input_aml = self.env['account.move.line'].search([('account_id','=', self.stock_input_account.id)]) + self.assertEquals(len(input_aml), 2, "Only two lines should have been generated in stock input account: one when receiving the product, one when making the invoice.") + self.assertAlmostEquals(sum(input_aml.mapped('debit')), 100, "Total debit value on stock input account should be equal to the original PO price of the product.") + self.assertAlmostEquals(sum(input_aml.mapped('credit')), 100, "Total credit value on stock input account should be equal to the original PO price of the product.") + + def test_anglosaxon_valuation_price_unit_diff_discount(self): + """ + PO: price unit: 90 + Inv: price unit: 100 + discount: 10 + """ + self.env.user.company_id.anglo_saxon_accounting = True + self.product1.product_tmpl_id.cost_method = 'fifo' + self.product1.product_tmpl_id.valuation = 'real_time' + self.product1.product_tmpl_id.invoice_policy = 'delivery' + self.product1.property_account_creditor_price_difference = self.price_diff_account + + # Create PO + po_form = Form(self.env['purchase.order']) + po_form.partner_id = self.partner_id + with po_form.order_line.new() as po_line: + po_line.product_id = self.product1 + po_line.product_qty = 1 + po_line.price_unit = 90.0 + order = po_form.save() + order.button_confirm() + + # Receive the goods + receipt = order.picking_ids[0] + receipt.move_lines.quantity_done = 1 + receipt.button_validate() + + # Create an invoice with a different price and a discount + invoice = self.env['account.invoice'].create({ + 'partner_id': order.partner_id.id, + 'purchase_id': order.id, + 'account_id': order.partner_id.property_account_payable_id.id, + 'type': 'in_invoice', + }) + invoice.purchase_order_change() + invoice.invoice_line_ids[0].price_unit = 100.0 + invoice.invoice_line_ids[0].discount = 10.0 + invoice.action_invoice_open() + + # Check if something was posted in the price difference account + price_diff_aml = self.env['account.move.line'].search([('account_id','=', self.price_diff_account.id)]) + self.assertEquals(len(price_diff_aml), 0, "No line should have been generated in the price difference account.") + + # Check what was posted in stock input account + input_aml = self.env['account.move.line'].search([('account_id','=', self.stock_input_account.id)]) + self.assertEquals(len(input_aml), 2, "Only two lines should have been generated in stock input account: one when receiving the product, one when making the invoice.") + self.assertAlmostEquals(sum(input_aml.mapped('debit')), 90, "Total debit value on stock input account should be equal to the original PO price of the product.") + self.assertAlmostEquals(sum(input_aml.mapped('credit')), 90, "Total credit value on stock input account should be equal to the original PO price of the product.")