mirror of
https://github.com/pretix/pretix.git
synced 2026-01-11 22:32:27 +00:00
Compare commits
1 Commits
py310
...
rounding-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5818d6d66f |
@@ -286,6 +286,11 @@ gross prices stay the same.
|
||||
|
||||
**This is less confusing to consumers and the end result is still compliant to EN 16931, so we recommend this for e-invoicing when (primarily) consumers are involved.**
|
||||
|
||||
Some gross prices **cannot** stay the same. For example, it is impossible to represent €15.00 incl. 19%, since a net
|
||||
price of €12.60 would lead to €14.99 gross and a net price of €12.61 would lead to €15.01 gross. Since this algorithm
|
||||
is supposed to be consumer-friendly, it has a bias for choosing €14.99 in this case, even if €15.01 would be a little
|
||||
less of a difference.
|
||||
|
||||
The main downside is that it might be confusing when selling to business customers, since the prices of the identical tickets appear to be different.
|
||||
Full computation for the example above:
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -871,7 +871,8 @@ class TaxSettingsForm(EventSettingsValidationMixin, SettingsForm):
|
||||
"Recommended for e-invoicing when you primarily sell to consumers. "
|
||||
"The gross or net price of some products may be changed automatically to ensure correct "
|
||||
"rounding of the order total. The system attempts to keep gross prices as configured whenever "
|
||||
"possible. Gross prices may still change if they are impossible to derive from a rounded net price."
|
||||
"possible. Gross prices may still change if they are impossible to derive from a rounded net price, "
|
||||
"but the system will prefer rounding them down instead of up."
|
||||
),
|
||||
}
|
||||
self.fields["tax_rounding"].choices = (
|
||||
|
||||
@@ -134,17 +134,12 @@ def test_revert_net_keep_gross_rounding_to_single_line(sample_lines):
|
||||
assert l.tax_rate == Decimal("19.00")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("rounding_mode", [
|
||||
"sum_by_net",
|
||||
"sum_by_net_keep_gross",
|
||||
])
|
||||
def test_rounding_of_impossible_gross_price(rounding_mode):
|
||||
def test_rounding_of_impossible_gross_price():
|
||||
l = OrderPosition(
|
||||
price=Decimal("23.00"),
|
||||
)
|
||||
l._calculate_tax(tax_rule=TaxRule(rate=Decimal("7.00")), invoice_address=InvoiceAddress())
|
||||
apply_rounding(rounding_mode, "EUR", [l])
|
||||
apply_rounding("sum_by_net", "EUR", [l])
|
||||
assert l.price == Decimal("23.01")
|
||||
assert l.price_includes_rounding_correction == Decimal("0.01")
|
||||
assert l.tax_value == Decimal("1.51")
|
||||
@@ -152,6 +147,19 @@ def test_rounding_of_impossible_gross_price(rounding_mode):
|
||||
assert l.tax_rate == Decimal("7.00")
|
||||
|
||||
|
||||
def test_rounding_of_impossible_gross_price_keep_gross():
|
||||
l = OrderPosition(
|
||||
price=Decimal("23.00"),
|
||||
)
|
||||
l._calculate_tax(tax_rule=TaxRule(rate=Decimal("7.00")), invoice_address=InvoiceAddress())
|
||||
apply_rounding("sum_by_net_keep_gross", "EUR", [l])
|
||||
assert l.price == Decimal("22.99")
|
||||
assert l.price_includes_rounding_correction == Decimal("-0.01")
|
||||
assert l.tax_value == Decimal("1.50")
|
||||
assert l.tax_value_includes_rounding_correction == Decimal("0.00")
|
||||
assert l.tax_rate == Decimal("7.00")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_round_down():
|
||||
lines = [OrderPosition(
|
||||
@@ -236,10 +244,8 @@ def test_do_not_touch_free(rounding_mode):
|
||||
)
|
||||
l2._calculate_tax(tax_rule=TaxRule(rate=Decimal("7.00")), invoice_address=InvoiceAddress())
|
||||
apply_rounding(rounding_mode, "EUR", [l1, l2])
|
||||
assert l2.price == Decimal("23.01")
|
||||
assert l2.price_includes_rounding_correction == Decimal("0.01")
|
||||
assert l2.tax_value == Decimal("1.51")
|
||||
assert l2.tax_value_includes_rounding_correction == Decimal("0.01")
|
||||
assert l2.price == Decimal("23.01") or l2.price == Decimal("22.99")
|
||||
assert l2.price_includes_rounding_correction != Decimal("0.00") or l2.tax_value_includes_rounding_correction != Decimal("0.00")
|
||||
assert l2.tax_rate == Decimal("7.00")
|
||||
assert l1.price == Decimal("0.00")
|
||||
assert l1.price_includes_rounding_correction == Decimal("0.00")
|
||||
|
||||
Reference in New Issue
Block a user