Price rounding: Make sum_by_net_keep_gross even more consumer-friendly (Z#23220106)

The rounding mode sum_by_net_keep_gross is supposed to be the
consumer-friendly algorithm. However, some consumer-friendly prices are still
impossible, such as 15.00 incl. 19%.  Previously, the system would round
to 15.01, because it's the gross price derived from the closest net
price. With this PR, it rounds to 14.99 instead, because consumers like
paying less more than they like paying more.
This commit is contained in:
Raphael Michel
2026-01-08 10:12:34 +01:00
parent 62bc16f963
commit 5818d6d66f
4 changed files with 45 additions and 13 deletions
+21 -1
View File
@@ -284,12 +284,27 @@ def apply_rounding(rounding_mode: Literal["line", "sum_by_net", "sum_by_net_keep
# e.g. 99.99 at 19% is impossible since 84.03 + 19% = 100.00 and 84.02 + 19% = 99.98
target_gross_total = round_decimal((target_net_total * (1 + tax_rate / 100)), currency)
if target_net_total and target_gross_total > gross_total:
target_net_total -= min((target_gross_total - gross_total), len(lines) * minimum_unit)
try_target_gross_total = round_decimal((target_net_total * (1 + tax_rate / 100)), currency)
if try_target_gross_total <= gross_total:
target_gross_total = try_target_gross_total
diff_gross = target_gross_total - gross_total
diff_net = target_net_total - net_total
diff_gross_sgn = -1 if diff_gross < 0 else 1
diff_net_sgn = -1 if diff_net < 0 else 1
for l in lines:
if diff_gross:
if diff_gross and diff_net:
apply_diff = diff_gross_sgn * minimum_unit
l.price = l.gross_price_before_rounding + apply_diff
l.price_includes_rounding_correction = apply_diff
l.tax_value = l.tax_value_before_rounding
l.tax_value_includes_rounding_correction = Decimal("0.00")
changed.append(l)
diff_gross -= apply_diff
diff_net -= apply_diff
elif diff_gross:
apply_diff = diff_gross_sgn * minimum_unit
l.price = l.gross_price_before_rounding + apply_diff
l.price_includes_rounding_correction = apply_diff
@@ -312,6 +327,11 @@ def apply_rounding(rounding_mode: Literal["line", "sum_by_net", "sum_by_net_keep
l.tax_value_includes_rounding_correction = Decimal("0.00")
changed.append(l)
# Double-check that result is consistent in computing gross from net
new_net_total = sum(l.price - l.tax_value for l in lines)
new_gross_total = sum(l.price for l in lines)
assert new_gross_total == round_decimal((new_net_total * (1 + tax_rate / 100)), currency)
elif rounding_mode == "line":
for l in lines:
if l.price_includes_rounding_correction or l.tax_value_includes_rounding_correction: