mirror of
https://github.com/pretix/pretix.git
synced 2026-08-10 10:47:00 +00:00
Replace algorithm
This commit is contained in:
@@ -198,7 +198,9 @@ pretix internally always stores taxes on a per-line level, like this:
|
||||
Sum 420.15 79.85 500.00
|
||||
========== ========== =========== ======= =============
|
||||
|
||||
This has a few significant advantages:
|
||||
Whether the net price is computed from the gross price or vice versa is configured on the tax rule and may differ for every line.
|
||||
|
||||
The line-based computation has a few significant advantages:
|
||||
|
||||
- We can report both net and gross prices for every individual ticket.
|
||||
|
||||
@@ -207,42 +209,33 @@ This has a few significant advantages:
|
||||
|
||||
- When splitting the order into two, both net price and gross price are split without any changes in rounding.
|
||||
|
||||
The main problem with this approach is that some external systems, formats, or jurisdictions expect a rounding scheme
|
||||
that works differently. For example, the EN 16931 standard for electronic invoicing expects us to build the sum of
|
||||
net values and then compute the tax on the document level, like this:
|
||||
The main disadvantage is that the tax looks "wrong" when computed from the sum. Taking the sum of net prices (420.15)
|
||||
and multiplying it with the tax rate (19%) yields a tax amount of 79.83 (instead of 79.85) and a gross sum of 499.98
|
||||
(instead of 499.98). This becomes a problem when juristictions, data formats, or external systems expect this calculation
|
||||
to work on the level of the entire order. A prominent example is the EN 16931 standard for e-invoicing that
|
||||
does not allow the computation as created by pretix.
|
||||
|
||||
================= ========== ===========
|
||||
Product Tax rate Net price
|
||||
================= ========== ===========
|
||||
Ticket A 19 % 84.03
|
||||
Ticket B 19 % 84.03
|
||||
Ticket C 19 % 84.03
|
||||
Ticket D 19 % 84.03
|
||||
Ticket E 19 % 84.03
|
||||
Net sum 420.15
|
||||
Taxes on 420.15 79.83
|
||||
Gross sum 499.98
|
||||
================= ========== ===========
|
||||
|
||||
As the example shows, this causes a difference in the end price that needs to be paid by the end user.
|
||||
So depending on the rounding scheme, a different payment amount might be due.
|
||||
This has significant disadvantages:
|
||||
However, calculating the tax rate from the net total has significant disadvantages:
|
||||
|
||||
- It is impossible to guarantee a stable gross price this way, i.e. if you advertise a price of €100 per ticket to
|
||||
consumers, they will be confused when they only need to pay €499.98.
|
||||
consumers, they will be confused when they only need to pay €499.98 for 5 tickets.
|
||||
|
||||
- When splitting the order, the sum of the new orders might require additional payment or refund of a few cents.
|
||||
- Some prices are impossible, e.g. you cannot sell a ticket for a gross price of €99.99 at a 19% tax rate, since there
|
||||
is no two-decimal net price that would be computed to a gross price of €99.99.
|
||||
|
||||
pretix therefore allows you to choose between the following options:
|
||||
- When splitting an order into two, the combined of the new orders is not guaranteed to be the same as the total of the
|
||||
original order. Therefore, additional payments or refunds of very small amounts might be necessary.
|
||||
|
||||
Rounding every line individually
|
||||
""""""""""""""""""""""""""""""""
|
||||
To allow organizers to make your own choices on this matter, pretix provides following options:
|
||||
|
||||
Compute taxes for every line individually
|
||||
"""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Algorithm identifier: ``line``
|
||||
|
||||
This is our original algorithm where the tax value is rounded for every line individually.
|
||||
|
||||
**This is our current algorithm and we recommend it whenever you do not have different requirements.**
|
||||
**This is our current default algorithm and we recommend it whenever you do not have different requirements** (see below).
|
||||
For the example above:
|
||||
|
||||
========== ========== =========== ======= =============
|
||||
@@ -257,17 +250,19 @@ For the example above:
|
||||
========== ========== =========== ======= =============
|
||||
|
||||
|
||||
Rounding by order total, keeping net prices stable
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Compute taxes based on net total
|
||||
""""""""""""""""""""""""""""""""
|
||||
|
||||
Algorithm identifier: ``sum_by_net``
|
||||
|
||||
In this algorithm, the gross prices of some the individual lines will be modified such that the end value will be
|
||||
compliant with the computation scheme expected by e.g. EN16931. This will lead to different gross prices to be shown
|
||||
for tickets of the same type, but the net price will stay always the same.
|
||||
In this algorithm, the tax value and gross total are computed from the sum of the net prices. To accomplish this within
|
||||
our data model, the gross price and tax of some of the tickets will be changed by the minimum currency unit (e.g. €0.01).
|
||||
The net price of the tickets always stay the same.
|
||||
|
||||
**This is our current algorithm and we recommend it whenever you need to round taxes on document-level and primarily deal with business customers.**
|
||||
For the example above:
|
||||
**This is the algorithm intended by EN 16931 invoices and our recommendation to use for e-invoicing when (primarily) business customers are involved.**
|
||||
|
||||
The main downside is that it might be confusing when selling to consumers, since the amounts to be paid change in unexpected ways.
|
||||
For the example above, the customer expects to pay 5 times 500.00, but they are are in fact charged 499.98:
|
||||
|
||||
========== ========== =========== ============================== ==============================
|
||||
Product Tax rate Net price Tax Gross price
|
||||
@@ -280,18 +275,19 @@ For the example above:
|
||||
Sum 420.15 78.83 499.98
|
||||
========== ========== =========== ============================== ==============================
|
||||
|
||||
Compute taxes based on net total with stable gross prices
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Rounding by order total, keeping gross prices stable
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Algorithm identifier: ``sum_by_net_keep_gross``
|
||||
|
||||
Algorithm identifier: ``sum_by_gross``
|
||||
In this algorithm, the tax value and gross total are computed from the sum of the net prices. However, the net prices
|
||||
of some of the tickets will be changed automatically by the minimum currency unit (e.g. €0.01) such that the resulting
|
||||
gross prices stay the same.
|
||||
|
||||
In this algorithm, the net prices of some the individual lines will be modified such that the end value will be
|
||||
compliant with the computation scheme expected by e.g. EN16931. This will lead to different net prices to be shown
|
||||
for tickets of the same type, but the gross price will stay always 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.**
|
||||
|
||||
**This is our current algorithm and we recommend it whenever you need to round taxes on document-level and primarily deal with consumers.**
|
||||
For the example above:
|
||||
The main downside is that it might be confusing when sellin got business customers, since the prices of the identical tickets appear to be different.
|
||||
Full computation for the example above:
|
||||
|
||||
========== ========== ============================= ============================== =============
|
||||
Product Tax rate Net price Tax Gross price
|
||||
|
||||
@@ -209,7 +209,7 @@ def apply_discounts(event: Event, sales_channel: Union[str, SalesChannel],
|
||||
return [new_prices.get(idx, (p[3], None)) for idx, p in enumerate(positions)]
|
||||
|
||||
|
||||
def apply_rounding(rounding_mode: Literal["line", "sum_by_gross", "sum_by_net"], currency: str,
|
||||
def apply_rounding(rounding_mode: Literal["line", "sum_by_net", "sum_by_net_keep_gross"], currency: str,
|
||||
lines: List[Union[OrderPosition, CartPosition, OrderFee]]) -> list:
|
||||
"""
|
||||
Given a a list of order positions / cart positions / order fees (may be mixed), applies the given rounding mode
|
||||
@@ -218,13 +218,13 @@ def apply_rounding(rounding_mode: Literal["line", "sum_by_gross", "sum_by_net"],
|
||||
|
||||
When rounding mode is set to ``"line"``, the tax will be computed and rounded individually for every line.
|
||||
|
||||
When rounding mode is set to ``"sum_by_gross"``, the tax values of the individual lines will be adjusted such that
|
||||
the per-taxrate/taxcode subtotal is rounded correctly. The gross prices will stay constant.
|
||||
When rounding mode is set to ``"sum_by_net_keep_gross"``, the tax values of the individual lines will be adjusted
|
||||
such that the per-taxrate/taxcode subtotal is rounded correctly. The gross prices will stay constant.
|
||||
|
||||
When rounding mode is set to ``"sum_by_net"``, the gross prices and tax values of the individual lines will be
|
||||
adjusted such that the per-taxrate/taxcode subtotal is rounded correctly. The net prices will stay constant.
|
||||
|
||||
:param rounding_mode: One of ``"line"``, ``"sum_by_gross"``, or ``"sum_by_net"``.
|
||||
:param rounding_mode: One of ``"line"``, ``"sum_by_net"``, or ``"sum_by_net_keep_gross"``.
|
||||
:param currency: Currency that will be used to determine rounding precision
|
||||
:param lines: List of order/cart contents
|
||||
:return: Collection of ``lines`` members that have been changed and may need to be persisted to the database.
|
||||
@@ -240,20 +240,26 @@ def apply_rounding(rounding_mode: Literal["line", "sum_by_gross", "sum_by_net"],
|
||||
if rounding_mode == "sum_by_net":
|
||||
for (tax_rate, tax_code), lines in groupby(sorted(lines, key=_key), key=_key):
|
||||
lines = list(sorted(lines, key=lambda l: -(l.price - l.price_includes_rounding_correction)))
|
||||
|
||||
# Compute the net and gross total of the line-based computation method
|
||||
net_total = sum(
|
||||
l.price - l.price_includes_rounding_correction - l.tax_value + l.tax_value_includes_rounding_correction
|
||||
for l in lines
|
||||
)
|
||||
gross_total = sum(l.price - l.price_includes_rounding_correction for l in lines)
|
||||
|
||||
# Compute the gross total we need to achieve based on the net total
|
||||
target_gross_total = round_decimal((net_total * (1 + tax_rate / 100)), currency)
|
||||
|
||||
# Add/subtract the smallest possible from both gross prices and tax values (so net values stay the same)
|
||||
# until the values align
|
||||
diff = target_gross_total - gross_total
|
||||
diff_sgn = -1 if diff < 0 else 1
|
||||
for l in lines:
|
||||
if diff:
|
||||
apply_diff = diff_sgn * minimum_unit
|
||||
l.price = l.price - l.price_includes_rounding_correction + apply_diff
|
||||
l.price_includes_rounding_correction = diff_sgn * minimum_unit
|
||||
l.price_includes_rounding_correction = apply_diff
|
||||
l.tax_value = l.tax_value - l.tax_value_includes_rounding_correction + apply_diff
|
||||
l.tax_value_includes_rounding_correction = apply_diff
|
||||
diff -= apply_diff
|
||||
@@ -265,27 +271,46 @@ def apply_rounding(rounding_mode: Literal["line", "sum_by_gross", "sum_by_net"],
|
||||
l.tax_value_includes_rounding_correction = Decimal("0.00")
|
||||
changed.append(l)
|
||||
|
||||
elif rounding_mode == "sum_by_gross":
|
||||
elif rounding_mode == "sum_by_net_keep_gross":
|
||||
for (tax_rate, tax_code), lines in groupby(sorted(lines, key=_key), key=_key):
|
||||
lines = list(sorted(lines, key=lambda l: -(l.price - l.price_includes_rounding_correction)))
|
||||
|
||||
# Compute the net and gross total of the line-based computation method
|
||||
net_total = sum(
|
||||
l.price - l.price_includes_rounding_correction - l.tax_value + l.tax_value_includes_rounding_correction
|
||||
for l in lines
|
||||
)
|
||||
gross_total = sum(l.price - l.price_includes_rounding_correction for l in lines)
|
||||
|
||||
# Compute the net total that would yield the correct gross total (if possible)
|
||||
target_net_total = round_decimal(gross_total - (gross_total * (1 - 100 / (100 + tax_rate))), currency)
|
||||
|
||||
diff = target_net_total - net_total
|
||||
diff_sgn = -1 if diff < 0 else 1
|
||||
# Compute the gross total that would be computed from that net total – this will be different than
|
||||
# gross_total when there is no possible net value for the gross total
|
||||
# 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)
|
||||
|
||||
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:
|
||||
apply_diff = diff_sgn * minimum_unit
|
||||
if diff_gross:
|
||||
apply_diff = diff_gross_sgn * minimum_unit
|
||||
l.price = l.price - l.price_includes_rounding_correction + apply_diff
|
||||
l.price_includes_rounding_correction = apply_diff
|
||||
l.tax_value = l.tax_value - l.tax_value_includes_rounding_correction + apply_diff
|
||||
l.tax_value_includes_rounding_correction = apply_diff
|
||||
changed.append(l)
|
||||
diff_gross -= apply_diff
|
||||
elif diff_net:
|
||||
apply_diff = diff_net_sgn * minimum_unit
|
||||
l.price = l.price - l.price_includes_rounding_correction
|
||||
l.price_includes_rounding_correction = Decimal("0.00")
|
||||
l.tax_value = l.tax_value - l.tax_value_includes_rounding_correction - apply_diff
|
||||
l.tax_value_includes_rounding_correction = -apply_diff
|
||||
diff -= apply_diff
|
||||
changed.append(l)
|
||||
diff_net -= apply_diff
|
||||
elif l.price_includes_rounding_correction or l.tax_value_includes_rounding_correction:
|
||||
l.price = l.price - l.price_includes_rounding_correction
|
||||
l.price_includes_rounding_correction = Decimal("0.00")
|
||||
|
||||
@@ -78,9 +78,10 @@ from pretix.control.forms import (
|
||||
from pretix.helpers.countries import CachedCountries
|
||||
|
||||
ROUNDING_MODES = (
|
||||
('line', _('Round taxes for every line individually')),
|
||||
('sum_by_net', _('Round taxes by order total, keeping net prices stable')),
|
||||
('sum_by_gross', _('Round taxes by order total, keeping gross prices stable')),
|
||||
('line', _('Compute taxes for every line individually')),
|
||||
('sum_by_net', _('Compute taxes based on net total')),
|
||||
('sum_by_net_keep_gross', _('Compute taxes based on net total with stable gross prices')),
|
||||
# We could also have sum_by_gross, but we're not aware of any use-cases for it
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -848,19 +848,23 @@ class TaxSettingsForm(EventSettingsValidationMixin, SettingsForm):
|
||||
self.fields["display_net_prices"].label = _("Prices shown to customer")
|
||||
self.fields["display_net_prices"].widget = DisplayNetPricesBooleanSelect()
|
||||
help_text = {
|
||||
"line": _("Recommended when e-invoicing is not required. Each product will be sold with the advertised "
|
||||
"net and gross price. However, in orders of more than one product, the total tax amount "
|
||||
"can differ from when it would be computed from the order total."),
|
||||
"sum_by_net": _("Recommended for e-invoicing in Europe when you primarily sell to business customers and "
|
||||
"show prices to customers excluding tax. "
|
||||
"For orders of more than one product, the gross price of some products may be changed "
|
||||
"automatically to ensure correct rounding of the order total, while the net prices "
|
||||
"stay as configured. This may cause the actual payment amount to differ from buying the "
|
||||
"products individually."),
|
||||
"sum_by_gross": _("Recommended for e-invoicing in Europe when you primarily sell to consumers. "
|
||||
"For an order of more than one product, the net price of some products may be changed "
|
||||
"automatically to ensure correct rounding of the order total, while the gross prices "
|
||||
"stay as configured."),
|
||||
"line": _(
|
||||
"Recommended when e-invoicing is not required. Each product will be sold with the advertised "
|
||||
"net and gross price. However, in orders of more than one product, the total tax amount "
|
||||
"can differ from when it would be computed from the order total."
|
||||
),
|
||||
"sum_by_net": _(
|
||||
"Recommended for e-invoicing when you primarily sell to business customers and "
|
||||
"show prices to customers excluding tax. "
|
||||
"The gross price of some products may be changed to ensure correct rounding, while the net "
|
||||
"prices will be presented as configured. This may cause the actual payment amount to differ."
|
||||
),
|
||||
"sum_by_net_keep_gross": _(
|
||||
"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."
|
||||
),
|
||||
}
|
||||
self.fields["tax_rounding"].choices = (
|
||||
(k, format_html('{}<br><span class="text-muted">{}</span>', v, help_text.get(k, "")))
|
||||
|
||||
@@ -3127,7 +3127,7 @@ def test_order_create_rounding_mode(token_client, organizer, event, item, quota,
|
||||
assert resp.data["positions"][0]["price"] == "99.99"
|
||||
assert resp.data["positions"][-1]["price"] == "100.00"
|
||||
|
||||
res["tax_rounding_mode"] = "sum_by_gross"
|
||||
res["tax_rounding_mode"] = "sum_by_net_keep_gross"
|
||||
for simulate in (True, False):
|
||||
res["simulate"] = simulate
|
||||
resp = token_client.post(
|
||||
|
||||
@@ -39,7 +39,17 @@ def sample_lines():
|
||||
|
||||
|
||||
def _validate_sample_lines(sample_lines, rounding_mode):
|
||||
apply_rounding(rounding_mode, "EUR", sample_lines)
|
||||
corrections = [
|
||||
(line.tax_value_includes_rounding_correction, line.price_includes_rounding_correction)
|
||||
for line in sample_lines
|
||||
]
|
||||
changed = apply_rounding(rounding_mode, "EUR", sample_lines)
|
||||
for line, original in zip(sample_lines, corrections):
|
||||
if (line.tax_value_includes_rounding_correction, line.price_includes_rounding_correction) != original:
|
||||
assert line in changed
|
||||
else:
|
||||
assert line not in changed
|
||||
|
||||
if rounding_mode == "line":
|
||||
for line in sample_lines:
|
||||
assert line.price == Decimal("100.00")
|
||||
@@ -54,13 +64,15 @@ def _validate_sample_lines(sample_lines, rounding_mode):
|
||||
assert line.tax_rate == Decimal("19.00")
|
||||
assert sum(line.price for line in sample_lines) == Decimal("499.98")
|
||||
assert sum(line.tax_value for line in sample_lines) == Decimal("79.83")
|
||||
elif rounding_mode == "sum_by_gross":
|
||||
assert sum(line.price - line.tax_value for line in sample_lines) == Decimal("420.15")
|
||||
elif rounding_mode == "sum_by_net_keep_gross":
|
||||
for line in sample_lines:
|
||||
assert line.price == Decimal("100.00")
|
||||
# net price may vary
|
||||
assert line.tax_rate == Decimal("19.00")
|
||||
assert sum(line.price for line in sample_lines) == Decimal("500.00")
|
||||
assert sum(line.tax_value for line in sample_lines) == Decimal("79.83")
|
||||
assert sum(line.price - line.tax_value for line in sample_lines) == Decimal("420.17")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -75,13 +87,13 @@ def test_simple_case_by_net(sample_lines):
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_simple_case_by_gross(sample_lines):
|
||||
_validate_sample_lines(sample_lines, "sum_by_gross")
|
||||
_validate_sample_lines(sample_lines, "sum_by_net_keep_gross")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_simple_case_switch_rounding(sample_lines):
|
||||
_validate_sample_lines(sample_lines, "sum_by_net")
|
||||
_validate_sample_lines(sample_lines, "sum_by_gross")
|
||||
_validate_sample_lines(sample_lines, "sum_by_net_keep_gross")
|
||||
_validate_sample_lines(sample_lines, "line")
|
||||
_validate_sample_lines(sample_lines, "sum_by_net")
|
||||
|
||||
@@ -105,7 +117,7 @@ def test_revert_net_rounding_to_single_line(sample_lines):
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_revert_gross_rounding_to_single_line(sample_lines):
|
||||
def test_revert_net_keep_gross_rounding_to_single_line(sample_lines):
|
||||
l = OrderPosition(
|
||||
price=Decimal("100.00"),
|
||||
price_includes_rounding_correction=Decimal("0.00"),
|
||||
@@ -114,7 +126,7 @@ def test_revert_gross_rounding_to_single_line(sample_lines):
|
||||
tax_rate=Decimal("19.00"),
|
||||
tax_code="S",
|
||||
)
|
||||
apply_rounding("sum_by_gross", "EUR", [l])
|
||||
apply_rounding("sum_by_net_keep_gross", "EUR", [l])
|
||||
assert l.price == Decimal("100.00")
|
||||
assert l.price_includes_rounding_correction == Decimal("0.00")
|
||||
assert l.tax_value == Decimal("15.97")
|
||||
@@ -123,14 +135,64 @@ def test_revert_gross_rounding_to_single_line(sample_lines):
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_rounding_of_impossible_price(sample_lines):
|
||||
@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")
|
||||
assert l.tax_value_includes_rounding_correction == Decimal("0.01")
|
||||
assert l.tax_rate == Decimal("7.00")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_round_down():
|
||||
lines = [OrderPosition(
|
||||
price=Decimal("100.00"),
|
||||
tax_value=Decimal("15.97"),
|
||||
tax_rate=Decimal("19.00"),
|
||||
tax_code="S",
|
||||
) for _ in range(5)]
|
||||
assert sum(l.price for l in lines) == Decimal("500.00")
|
||||
assert sum(l.tax_value for l in lines) == Decimal("79.85")
|
||||
assert sum(l.price - l.tax_value for l in lines) == Decimal("420.15")
|
||||
|
||||
apply_rounding("sum_by_net", "EUR", lines)
|
||||
assert sum(l.price for l in lines) == Decimal("499.98")
|
||||
assert sum(l.tax_value for l in lines) == Decimal("79.83")
|
||||
assert sum(l.price - l.tax_value for l in lines) == Decimal("420.15")
|
||||
|
||||
apply_rounding("sum_by_net_keep_gross", "EUR", lines)
|
||||
assert sum(l.price for l in lines) == Decimal("500.00")
|
||||
assert sum(l.tax_value for l in lines) == Decimal("79.83")
|
||||
assert sum(l.price - l.tax_value for l in lines) == Decimal("420.17")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_round_up():
|
||||
lines = [OrderPosition(
|
||||
price=Decimal("99.98"),
|
||||
tax_value=Decimal("15.96"),
|
||||
tax_rate=Decimal("19.00"),
|
||||
tax_code="S",
|
||||
) for _ in range(5)]
|
||||
assert sum(l.price for l in lines) == Decimal("499.90")
|
||||
assert sum(l.tax_value for l in lines) == Decimal("79.80")
|
||||
assert sum(l.price - l.tax_value for l in lines) == Decimal("420.10")
|
||||
|
||||
apply_rounding("sum_by_net", "EUR", lines)
|
||||
assert sum(l.price for l in lines) == Decimal("499.92")
|
||||
assert sum(l.tax_value for l in lines) == Decimal("79.82")
|
||||
assert sum(l.price - l.tax_value for l in lines) == Decimal("420.10")
|
||||
|
||||
apply_rounding("sum_by_net_keep_gross", "EUR", lines)
|
||||
assert sum(l.price for l in lines) == Decimal("499.90")
|
||||
assert sum(l.tax_value for l in lines) == Decimal("79.82")
|
||||
assert sum(l.price - l.tax_value for l in lines) == Decimal("420.08")
|
||||
|
||||
@@ -60,6 +60,19 @@ def test_from_gross_price(event):
|
||||
assert tp.rate == Decimal('10.00')
|
||||
assert tp.code == 'S/standard'
|
||||
|
||||
tr = TaxRule(
|
||||
event=event,
|
||||
rate=Decimal('19.00'),
|
||||
code=None,
|
||||
price_includes_tax=True,
|
||||
)
|
||||
tp = tr.tax(Decimal('99.99'))
|
||||
assert tp.gross == Decimal('99.99')
|
||||
assert tp.net == Decimal('84.03')
|
||||
assert tp.tax == Decimal('15.96')
|
||||
assert tp.rate == Decimal('19.00')
|
||||
assert tp.code is None
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_from_net_price(event):
|
||||
|
||||
@@ -2138,8 +2138,8 @@ class CheckoutTestCase(BaseCheckoutTestCase, TimemachineTestMixin, TestCase):
|
||||
assert op2.tax_value == Decimal("15.97")
|
||||
assert op2.tax_value_includes_rounding_correction == Decimal("0.00")
|
||||
|
||||
def test_rounding_sum_by_gross(self):
|
||||
self.event.settings.tax_rounding = "sum_by_gross"
|
||||
def test_rounding_sum_by_net_keep_gross(self):
|
||||
self.event.settings.tax_rounding = "sum_by_net_keep_gross"
|
||||
self.event.settings.set('payment_banktransfer__enabled', True)
|
||||
self.ticket.default_price = Decimal("100.00")
|
||||
self.ticket.save()
|
||||
|
||||
Reference in New Issue
Block a user