[FIX] point_of_sale: Display the price with tax included.

This fix displays the price with tax included on the product screen and not only on the order.

This commit also move the compute all function to models.js.
We allow the retrocompatibility with the compute all  functions from older versions.

closes odoo/odoo#94717

X-original-commit: c837be6556cc49928e1b90cb1de29a2dff7dcd82
Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
This commit is contained in:
rhe-odoo
2022-06-28 09:15:47 +02:00
committed by Joseph Caburnay
parent b71501c7d0
commit 39dedfda44
2 changed files with 192 additions and 165 deletions
@@ -29,7 +29,7 @@ odoo.define('point_of_sale.ProductItem', function(require) {
}
get price() {
const formattedUnitPrice = this.env.pos.format_currency(
this.props.product.get_price(this.pricelist, 1),
this.props.product.get_display_price(this.pricelist, 1),
'Product Price'
);
if (this.props.product.to_weight) {
+191 -164
View File
@@ -888,6 +888,186 @@ class PosGlobalState extends PosModel {
}
}
/**
* Mirror JS method of:
* _compute_amount in addons/account/models/account.py
*/
_compute_all(tax, base_amount, quantity, price_exclude) {
if(price_exclude === undefined)
var price_include = tax.price_include;
else
var price_include = !price_exclude;
if (tax.amount_type === 'fixed') {
// Use sign on base_amount and abs on quantity to take into account the sign of the base amount,
// which includes the sign of the quantity and the sign of the price_unit
// Amount is the fixed price for the tax, it can be negative
// Base amount included the sign of the quantity and the sign of the unit price and when
// a product is returned, it can be done either by changing the sign of quantity or by changing the
// sign of the price unit.
// When the price unit is equal to 0, the sign of the quantity is absorbed in base_amount then
// a "else" case is needed.
if (base_amount)
return Math.sign(base_amount) * Math.abs(quantity) * tax.amount;
else
return quantity * tax.amount;
}
if (tax.amount_type === 'percent' && !price_include){
return base_amount * tax.amount / 100;
}
if (tax.amount_type === 'percent' && price_include){
return base_amount - (base_amount / (1 + tax.amount / 100));
}
if (tax.amount_type === 'division' && !price_include) {
return base_amount / (1 - tax.amount / 100) - base_amount;
}
if (tax.amount_type === 'division' && price_include) {
return base_amount - (base_amount * (tax.amount / 100));
}
return false;
}
/**
* Mirror JS method of:
* compute_all in addons/account/models/account.py
*
* Read comments in the python side method for more details about each sub-methods.
*/
compute_all(taxes, price_unit, quantity, currency_rounding, handle_price_include=true) {
var self = this;
// 1) Flatten the taxes.
var _collect_taxes = function(taxes, all_taxes){
taxes = [...taxes].sort(function (tax1, tax2) {
return tax1.sequence - tax2.sequence;
});
_(taxes).each(function(tax){
if(tax.amount_type === 'group')
all_taxes = _collect_taxes(tax.children_tax_ids, all_taxes);
else
all_taxes.push(tax);
});
return all_taxes;
}
var collect_taxes = function(taxes){
return _collect_taxes(taxes, []);
}
taxes = collect_taxes(taxes);
// 2) Deal with the rounding methods
var round_tax = this.company.tax_calculation_rounding_method != 'round_globally';
var initial_currency_rounding = currency_rounding;
if(!round_tax)
currency_rounding = currency_rounding * 0.00001;
// 3) Iterate the taxes in the reversed sequence order to retrieve the initial base of the computation.
var recompute_base = function(base_amount, fixed_amount, percent_amount, division_amount){
return (base_amount - fixed_amount) / (1.0 + percent_amount / 100.0) * (100 - division_amount) / 100;
}
var base = round_pr(price_unit * quantity, initial_currency_rounding);
var sign = 1;
if(base < 0){
base = -base;
sign = -1;
}
var total_included_checkpoints = {};
var i = taxes.length - 1;
var store_included_tax_total = true;
var incl_fixed_amount = 0.0;
var incl_percent_amount = 0.0;
var incl_division_amount = 0.0;
var cached_tax_amounts = {};
if (handle_price_include){
_(taxes.reverse()).each(function(tax){
if(tax.include_base_amount){
base = recompute_base(base, incl_fixed_amount, incl_percent_amount, incl_division_amount);
incl_fixed_amount = 0.0;
incl_percent_amount = 0.0;
incl_division_amount = 0.0;
store_included_tax_total = true;
}
if(tax.price_include){
if(tax.amount_type === 'percent')
incl_percent_amount += tax.amount;
else if(tax.amount_type === 'division')
incl_division_amount += tax.amount;
else if(tax.amount_type === 'fixed')
incl_fixed_amount += Math.abs(quantity) * tax.amount
else{
var tax_amount = self._compute_all(tax, base, quantity);
incl_fixed_amount += tax_amount;
cached_tax_amounts[i] = tax_amount;
}
if(store_included_tax_total){
total_included_checkpoints[i] = base;
store_included_tax_total = false;
}
}
i -= 1;
});
}
var total_excluded = round_pr(recompute_base(base, incl_fixed_amount, incl_percent_amount, incl_division_amount), initial_currency_rounding);
var total_included = total_excluded;
// 4) Iterate the taxes in the sequence order to fill missing base/amount values.
base = total_excluded;
var skip_checkpoint = false;
var taxes_vals = [];
i = 0;
var cumulated_tax_included_amount = 0;
_(taxes.reverse()).each(function(tax){
if(tax.price_include || tax.is_base_affected)
var tax_base_amount = base;
else
var tax_base_amount = total_excluded;
if(!skip_checkpoint && tax.price_include && total_included_checkpoints[i] !== undefined){
var tax_amount = total_included_checkpoints[i] - (base + cumulated_tax_included_amount);
cumulated_tax_included_amount = 0;
}else
var tax_amount = self._compute_all(tax, tax_base_amount, quantity, true);
tax_amount = round_pr(tax_amount, currency_rounding);
if(tax.price_include && total_included_checkpoints[i] === undefined)
cumulated_tax_included_amount += tax_amount;
taxes_vals.push({
'id': tax.id,
'name': tax.name,
'amount': sign * tax_amount,
'base': sign * round_pr(tax_base_amount, currency_rounding),
});
if(tax.include_base_amount){
base += tax_amount;
if(!tax.price_include)
skip_checkpoint = true;
}
total_included += tax_amount;
i += 1;
});
return {
'taxes': taxes_vals,
'total_excluded': sign * round_pr(total_excluded, this.currency.rounding),
'total_included': sign * round_pr(total_included, this.currency.rounding),
}
}
/**
* TODO: We can probably remove this here and put it somewhere else.
* And that somewhere else becomes the parent of the proxy.
@@ -1132,6 +1312,15 @@ class Product extends PosModel {
// pricelist that have base == 'pricelist'.
return price;
}
get_display_price(pricelist, quantity) {
if (this.pos.config.iface_tax_included === 'total') {
const taxes = this.taxes_id.map(id => this.pos.taxes_by_id[id]);
const allPrices = this.pos.compute_all(taxes, this.get_price(pricelist, quantity), 1, this.pos.currency.rounding);
return allPrices.total_included;
} else {
return this.get_price(pricelist, quantity);
}
}
}
Registries.Model.add(Product);
@@ -1684,37 +1873,7 @@ class Orderline extends PosModel {
* _compute_amount in addons/account/models/account.py
*/
_compute_all(tax, base_amount, quantity, price_exclude) {
if(price_exclude === undefined)
var price_include = tax.price_include;
else
var price_include = !price_exclude;
if (tax.amount_type === 'fixed') {
// Use sign on base_amount and abs on quantity to take into account the sign of the base amount,
// which includes the sign of the quantity and the sign of the price_unit
// Amount is the fixed price for the tax, it can be negative
// Base amount included the sign of the quantity and the sign of the unit price and when
// a product is returned, it can be done either by changing the sign of quantity or by changing the
// sign of the price unit.
// When the price unit is equal to 0, the sign of the quantity is absorbed in base_amount then
// a "else" case is needed.
if (base_amount)
return Math.sign(base_amount) * Math.abs(quantity) * tax.amount;
else
return quantity * tax.amount;
}
if (tax.amount_type === 'percent' && !price_include){
return base_amount * tax.amount / 100;
}
if (tax.amount_type === 'percent' && price_include){
return base_amount - (base_amount / (1 + tax.amount / 100));
}
if (tax.amount_type === 'division' && !price_include) {
return base_amount / (1 - tax.amount / 100) - base_amount;
}
if (tax.amount_type === 'division' && price_include) {
return base_amount - (base_amount * (tax.amount / 100));
}
return false;
return this.pos._compute_all(tax, base_amount, quantity, price_exclude);
}
/**
* Mirror JS method of:
@@ -1723,139 +1882,7 @@ class Orderline extends PosModel {
* Read comments in the python side method for more details about each sub-methods.
*/
compute_all(taxes, price_unit, quantity, currency_rounding, handle_price_include=true) {
var self = this;
// 1) Flatten the taxes.
var _collect_taxes = function(taxes, all_taxes){
taxes = [...taxes].sort(function (tax1, tax2) {
return tax1.sequence - tax2.sequence;
});
_(taxes).each(function(tax){
if(tax.amount_type === 'group')
all_taxes = _collect_taxes(tax.children_tax_ids, all_taxes);
else
all_taxes.push(tax);
});
return all_taxes;
}
var collect_taxes = function(taxes){
return _collect_taxes(taxes, []);
}
taxes = collect_taxes(taxes);
// 2) Deal with the rounding methods
var round_tax = this.pos.company.tax_calculation_rounding_method != 'round_globally';
var initial_currency_rounding = currency_rounding;
if(!round_tax)
currency_rounding = currency_rounding * 0.00001;
// 3) Iterate the taxes in the reversed sequence order to retrieve the initial base of the computation.
var recompute_base = function(base_amount, fixed_amount, percent_amount, division_amount){
return (base_amount - fixed_amount) / (1.0 + percent_amount / 100.0) * (100 - division_amount) / 100;
}
var base = round_pr(price_unit * quantity, initial_currency_rounding);
var sign = 1;
if(base < 0){
base = -base;
sign = -1;
}
var total_included_checkpoints = {};
var i = taxes.length - 1;
var store_included_tax_total = true;
var incl_fixed_amount = 0.0;
var incl_percent_amount = 0.0;
var incl_division_amount = 0.0;
var cached_tax_amounts = {};
if (handle_price_include){
_(taxes.reverse()).each(function(tax){
if(tax.include_base_amount){
base = recompute_base(base, incl_fixed_amount, incl_percent_amount, incl_division_amount);
incl_fixed_amount = 0.0;
incl_percent_amount = 0.0;
incl_division_amount = 0.0;
store_included_tax_total = true;
}
if(tax.price_include){
if(tax.amount_type === 'percent')
incl_percent_amount += tax.amount;
else if(tax.amount_type === 'division')
incl_division_amount += tax.amount;
else if(tax.amount_type === 'fixed')
incl_fixed_amount += Math.abs(quantity) * tax.amount
else{
var tax_amount = self._compute_all(tax, base, quantity);
incl_fixed_amount += tax_amount;
cached_tax_amounts[i] = tax_amount;
}
if(store_included_tax_total){
total_included_checkpoints[i] = base;
store_included_tax_total = false;
}
}
i -= 1;
});
}
var total_excluded = round_pr(recompute_base(base, incl_fixed_amount, incl_percent_amount, incl_division_amount), initial_currency_rounding);
var total_included = total_excluded;
// 4) Iterate the taxes in the sequence order to fill missing base/amount values.
base = total_excluded;
var skip_checkpoint = false;
var taxes_vals = [];
i = 0;
var cumulated_tax_included_amount = 0;
_(taxes.reverse()).each(function(tax){
if(tax.price_include || tax.is_base_affected)
var tax_base_amount = base;
else
var tax_base_amount = total_excluded;
if(!skip_checkpoint && tax.price_include && total_included_checkpoints[i] !== undefined){
var tax_amount = total_included_checkpoints[i] - (base + cumulated_tax_included_amount);
cumulated_tax_included_amount = 0;
}else
var tax_amount = self._compute_all(tax, tax_base_amount, quantity, true);
tax_amount = round_pr(tax_amount, currency_rounding);
if(tax.price_include && total_included_checkpoints[i] === undefined)
cumulated_tax_included_amount += tax_amount;
taxes_vals.push({
'id': tax.id,
'name': tax.name,
'amount': sign * tax_amount,
'base': sign * round_pr(tax_base_amount, currency_rounding),
});
if(tax.include_base_amount){
base += tax_amount;
if(!tax.price_include)
skip_checkpoint = true;
}
total_included += tax_amount;
i += 1;
});
return {
'taxes': taxes_vals,
'total_excluded': sign * round_pr(total_excluded, this.pos.currency.rounding),
'total_included': sign * round_pr(total_included, this.pos.currency.rounding),
}
return this.pos.compute_all(taxes, price_unit, quantity, currency_rounding, handle_price_include);
}
get_all_prices(qty = this.get_quantity()){
var price_unit = this.get_unit_price() * (1.0 - (this.get_discount() / 100.0));