mirror of
https://github.com/pretix/pretix.git
synced 2026-01-16 23:22:27 +00:00
Compare commits
1 Commits
rounding-d
...
py310
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a61c21feb |
6
.github/workflows/tests.yml
vendored
6
.github/workflows/tests.yml
vendored
@@ -23,13 +23,13 @@ jobs:
|
||||
name: Tests
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.9", "3.10", "3.11"]
|
||||
python-version: ["3.10", "3.11", "3.13"]
|
||||
database: [sqlite, postgres]
|
||||
exclude:
|
||||
- database: sqlite
|
||||
python-version: "3.9"
|
||||
- database: sqlite
|
||||
python-version: "3.10"
|
||||
- database: sqlite
|
||||
python-version: "3.11"
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
|
||||
@@ -286,11 +286,6 @@ 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:
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ name = "pretix"
|
||||
dynamic = ["version"]
|
||||
description = "Reinventing presales, one ticket at a time"
|
||||
readme = "README.rst"
|
||||
requires-python = ">=3.9"
|
||||
requires-python = ">=3.10"
|
||||
license = {file = "LICENSE"}
|
||||
keywords = ["tickets", "web", "shop", "ecommerce"]
|
||||
authors = [
|
||||
|
||||
@@ -284,27 +284,12 @@ 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 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:
|
||||
if 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
|
||||
@@ -327,11 +312,6 @@ 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,8 +871,7 @@ 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, "
|
||||
"but the system will prefer rounding them down instead of up."
|
||||
"possible. Gross prices may still change if they are impossible to derive from a rounded net price."
|
||||
),
|
||||
}
|
||||
self.fields["tax_rounding"].choices = (
|
||||
|
||||
@@ -134,12 +134,17 @@ def test_revert_net_keep_gross_rounding_to_single_line(sample_lines):
|
||||
assert l.tax_rate == Decimal("19.00")
|
||||
|
||||
|
||||
def test_rounding_of_impossible_gross_price():
|
||||
@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):
|
||||
l = OrderPosition(
|
||||
price=Decimal("23.00"),
|
||||
)
|
||||
l._calculate_tax(tax_rule=TaxRule(rate=Decimal("7.00")), invoice_address=InvoiceAddress())
|
||||
apply_rounding("sum_by_net", "EUR", [l])
|
||||
apply_rounding(rounding_mode, "EUR", [l])
|
||||
assert l.price == Decimal("23.01")
|
||||
assert l.price_includes_rounding_correction == Decimal("0.01")
|
||||
assert l.tax_value == Decimal("1.51")
|
||||
@@ -147,19 +152,6 @@ def test_rounding_of_impossible_gross_price():
|
||||
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(
|
||||
@@ -244,8 +236,10 @@ 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") 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.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.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