[FIX] point_of_sale: wrong fixed taxes sign for zero amount products
Before this commit: If a fixed tax is used for a product with the price of zero, the tax line sign would be positive and lead to an unbalanced move. The solution is to use its sign in its context for correct calculation. Also, in the PoS front-end, when you want to return a zero amount product with a fixed tax, its sign would be positive, which is incorrect. Steps to reproduce the first issue: - Install 'Point of Sale' module - Create a new 'Tax' X with a fixed price (e.g.: 1 $) - Create a new 'Product' with zero Sales Price - Create a PoS session - Add the product to the order - Close the session It creates a line for the "Difference at closing PoS session," and doesn't prevent closing. But the problem is that the tax capture as debit is incorrect. The tax for the invoice must be credit. Steps to reproduce the second issue: - Install 'Point of Sale' module - Create a new 'Tax' X with a fixed price (e.g.: 1 $) - Create a new 'Product' with zero Sales Price - Create a PoS session - Add the product to the order - Change the quantity to -1 (refund) The final amount is positive, while it should be negative. Solution The sign must be sent in the context, and for the PoS front-end, it should consider zero amount cases. opw-2833590 closes odoo/odoo#95067 X-original-commit: c5fb4f778db95fb33845b9a5e0c2190d4d9037ee Signed-off-by: Masereel Pierre <pim@odoo.com> Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
This commit is contained in:
@@ -1155,7 +1155,7 @@ class PosSession(models.Model):
|
||||
# may arise in 'Round Globally'.
|
||||
check_refund = lambda x: x.qty * x.price_unit < 0
|
||||
is_refund = check_refund(order_line)
|
||||
tax_data = tax_ids.compute_all(price_unit=price, quantity=abs(order_line.qty), currency=self.currency_id, is_refund=is_refund)
|
||||
tax_data = tax_ids.with_context(force_sign=sign).compute_all(price_unit=price, quantity=abs(order_line.qty), currency=self.currency_id, is_refund=is_refund)
|
||||
taxes = tax_data['taxes']
|
||||
# For Cash based taxes, use the account from the repartition line immediately as it has been paid already
|
||||
for tax in taxes:
|
||||
|
||||
@@ -113,3 +113,31 @@ odoo.define('point_of_sale.tour.ProductScreen', function (require) {
|
||||
|
||||
Tour.register('ProductScreenTour', { test: true, url: '/pos/ui' }, getSteps());
|
||||
});
|
||||
|
||||
odoo.define('point_of_sale.tour.FixedPriceNegativeQty', function (require) {
|
||||
'use strict';
|
||||
|
||||
const { ProductScreen } = require('point_of_sale.tour.ProductScreenTourMethods');
|
||||
const { PaymentScreen } = require('point_of_sale.tour.PaymentScreenTourMethods');
|
||||
const { ReceiptScreen } = require('point_of_sale.tour.ReceiptScreenTourMethods');
|
||||
const { getSteps, startSteps } = require('point_of_sale.tour.utils');
|
||||
var Tour = require('web_tour.tour');
|
||||
|
||||
startSteps();
|
||||
|
||||
ProductScreen.do.clickHomeCategory();
|
||||
|
||||
ProductScreen.do.clickDisplayedProduct('Zero Amount Product');
|
||||
ProductScreen.check.selectedOrderlineHas('Zero Amount Product', '1.0', '1.0');
|
||||
ProductScreen.do.pressNumpad('+/- 1');
|
||||
ProductScreen.check.selectedOrderlineHas('Zero Amount Product', '-1.0', '-1.0');
|
||||
|
||||
ProductScreen.do.clickPayButton();
|
||||
PaymentScreen.do.clickPaymentMethod('Bank');
|
||||
PaymentScreen.check.remainingIs('0.00');
|
||||
PaymentScreen.do.clickValidate();
|
||||
|
||||
ReceiptScreen.check.receiptIsThere();
|
||||
|
||||
Tour.register('FixedTaxNegativeQty', { test: true, url: '/pos/ui' }, getSteps());
|
||||
});
|
||||
|
||||
@@ -524,3 +524,60 @@ class TestUi(TestPointOfSaleHttpCommon):
|
||||
def test_05_ticket_screen(self):
|
||||
self.main_pos_config.open_session_cb()
|
||||
self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'TicketScreenTour', login="accountman")
|
||||
|
||||
def test_fixed_tax_negative_qty(self):
|
||||
""" Assert the negative amount of a negative-quantity orderline
|
||||
with zero-amount product with fixed tax.
|
||||
"""
|
||||
|
||||
# setup the zero-amount product
|
||||
tax_received_account = self.env['account.account'].create({
|
||||
'name': 'TAX_BASE',
|
||||
'code': 'TBASE',
|
||||
'user_type_id': self.env.ref('account.data_account_type_current_assets').id,
|
||||
'company_id': self.env.company.id,
|
||||
})
|
||||
fixed_tax = self.env['account.tax'].create({
|
||||
'name': 'fixed amount tax',
|
||||
'amount_type': 'fixed',
|
||||
'amount': 1,
|
||||
'invoice_repartition_line_ids': [
|
||||
(0, 0, {
|
||||
'factor_percent': 100,
|
||||
'repartition_type': 'base',
|
||||
}),
|
||||
(0, 0, {
|
||||
'factor_percent': 100,
|
||||
'repartition_type': 'tax',
|
||||
'account_id': tax_received_account.id,
|
||||
}),
|
||||
],
|
||||
})
|
||||
zero_amount_product = self.env['product.product'].create({
|
||||
'name': 'Zero Amount Product',
|
||||
'available_in_pos': True,
|
||||
'list_price': 0,
|
||||
'taxes_id': [(6, 0, [fixed_tax.id])],
|
||||
})
|
||||
|
||||
# Make an order with the zero-amount product from the frontend.
|
||||
# We need to do this because of the fix in the "compute_all" port.
|
||||
self.main_pos_config.write({'iface_tax_included': 'total'})
|
||||
self.main_pos_config.open_session_cb()
|
||||
self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'FixedTaxNegativeQty', login="accountman")
|
||||
pos_session = self.main_pos_config.current_session_id
|
||||
|
||||
# Close the session and check the session journal entry.
|
||||
pos_session.action_pos_session_validate()
|
||||
|
||||
lines = pos_session.move_id.line_ids.sorted('balance')
|
||||
|
||||
# order in the tour is paid using the bank payment method.
|
||||
bank_pm = self.main_pos_config.payment_method_ids.filtered(lambda pm: pm.name == 'Bank')
|
||||
|
||||
self.assertEqual(lines[0].account_id, bank_pm.receivable_account_id or self.env.company.account_default_pos_receivable_account_id)
|
||||
self.assertAlmostEqual(lines[0].balance, -1)
|
||||
self.assertEqual(lines[1].account_id, zero_amount_product.categ_id.property_account_income_categ_id)
|
||||
self.assertAlmostEqual(lines[1].balance, 0)
|
||||
self.assertEqual(lines[2].account_id, tax_received_account)
|
||||
self.assertAlmostEqual(lines[2].balance, 1)
|
||||
|
||||
@@ -501,3 +501,45 @@ class TestPoSProductsWithTax(TestPoSCommon):
|
||||
{'account_id': self.tax_received_account.id, 'balance': 15.14, 'tax_ids': [], 'tax_tag_ids': self.tax_tag_refund_tax.ids},
|
||||
{'account_id': self.sale_account.id, 'balance': 72.07, 'tax_ids': tax_21_incl.ids, 'tax_tag_ids': self.tax_tag_refund_base.ids},
|
||||
])
|
||||
|
||||
def test_fixed_tax_positive_qty(self):
|
||||
|
||||
fixed_tax = self.env['account.tax'].create({
|
||||
'name': 'fixed amount tax',
|
||||
'amount_type': 'fixed',
|
||||
'amount': 1,
|
||||
'invoice_repartition_line_ids': [
|
||||
(0, 0, {
|
||||
'factor_percent': 100,
|
||||
'repartition_type': 'base',
|
||||
'tag_ids': [(6, 0, self.tax_tag_invoice_base.ids)],
|
||||
}),
|
||||
(0, 0, {
|
||||
'factor_percent': 100,
|
||||
'repartition_type': 'tax',
|
||||
'account_id': self.tax_received_account.id,
|
||||
'tag_ids': [(6, 0, self.tax_tag_invoice_tax.ids)],
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
zero_amount_product = self.env['product.product'].create({
|
||||
'name': 'Zero Amount Product',
|
||||
'available_in_pos': True,
|
||||
'list_price': 0,
|
||||
'taxes_id': [(6, 0, [fixed_tax.id])],
|
||||
})
|
||||
|
||||
self.open_new_session()
|
||||
self.env['pos.order'].create_from_ui([self.create_ui_order_data([
|
||||
(zero_amount_product, 1),
|
||||
])])
|
||||
self.pos_session.action_pos_session_validate()
|
||||
|
||||
lines = self.pos_session.move_id.line_ids.sorted('balance')
|
||||
|
||||
self.assertRecordValues(lines, [
|
||||
{'account_id': self.tax_received_account.id, 'balance': -1},
|
||||
{'account_id': self.sale_account.id, 'balance': 0},
|
||||
{'account_id': self.cash_pm1.receivable_account_id.id, 'balance': 1},
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user