Tax rounding: Allow to apply only for B2B (Z#23220106) (#5810)

* Tax rounding: Allow to apply only for B2B (Z#23220106)

Most effective in combination with #5807

* Update src/pretix/base/settings.py

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

---------

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
This commit is contained in:
Raphael Michel
2026-01-30 11:53:38 +01:00
committed by GitHub
parent 8c4e0bdb82
commit d58a6e2503
10 changed files with 100 additions and 26 deletions

View File

@@ -1743,6 +1743,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
rounding_mode = self.context["event"].settings.tax_rounding rounding_mode = self.context["event"].settings.tax_rounding
changed = apply_rounding( changed = apply_rounding(
rounding_mode, rounding_mode,
ia,
self.context["event"].currency, self.context["event"].currency,
[*pos_map.values(), *fees] [*pos_map.values(), *fees]
) )

View File

@@ -1639,7 +1639,7 @@ def get_fees(event, request, _total_ignored_=None, invoice_address=None, payment
if fee.tax_rule and not fee.tax_rule.pk: if fee.tax_rule and not fee.tax_rule.pk:
fee.tax_rule = None # TODO: deprecate fee.tax_rule = None # TODO: deprecate
apply_rounding(event.settings.tax_rounding, event.currency, [*positions, *fees]) apply_rounding(event.settings.tax_rounding, invoice_address, event.currency, [*positions, *fees])
total = sum([c.price for c in positions]) + sum([f.value for f in fees]) total = sum([c.price for c in positions]) + sum([f.value for f in fees])
if total != 0 and payments: if total != 0 and payments:
@@ -1679,7 +1679,7 @@ def get_fees(event, request, _total_ignored_=None, invoice_address=None, payment
fees.append(pf) fees.append(pf)
# Re-apply rounding as grand total has changed # Re-apply rounding as grand total has changed
apply_rounding(event.settings.tax_rounding, event.currency, [*positions, *fees]) apply_rounding(event.settings.tax_rounding, invoice_address, event.currency, [*positions, *fees])
total = sum([c.price for c in positions]) + sum([f.value for f in fees]) total = sum([c.price for c in positions]) + sum([f.value for f in fees])
# Re-calculate to_pay as grand total has changed # Re-calculate to_pay as grand total has changed

View File

@@ -955,7 +955,7 @@ def _apply_rounding_and_fees(positions: List[CartPosition], payment_requests: Li
fee.tax_rule = None # TODO: deprecate fee.tax_rule = None # TODO: deprecate
# Apply rounding to get final total in case no payment fees will be added # Apply rounding to get final total in case no payment fees will be added
apply_rounding(event.settings.tax_rounding, event.currency, [*positions, *fees]) apply_rounding(event.settings.tax_rounding, address, event.currency, [*positions, *fees])
total = sum([c.price for c in positions]) + sum([f.value for f in fees]) total = sum([c.price for c in positions]) + sum([f.value for f in fees])
payments_assigned = Decimal("0.00") payments_assigned = Decimal("0.00")
@@ -982,7 +982,7 @@ def _apply_rounding_and_fees(positions: List[CartPosition], payment_requests: Li
p['fee'] = pf p['fee'] = pf
# Re-apply rounding as grand total has changed # Re-apply rounding as grand total has changed
apply_rounding(event.settings.tax_rounding, event.currency, [*positions, *fees]) apply_rounding(event.settings.tax_rounding, address, event.currency, [*positions, *fees])
total = sum([c.price for c in positions]) + sum([f.value for f in fees]) total = sum([c.price for c in positions]) + sum([f.value for f in fees])
# Re-calculate to_pay as grand total has changed # Re-calculate to_pay as grand total has changed
@@ -1610,6 +1610,7 @@ class OrderChangeManager:
ChangeValidUntilOperation = namedtuple('ChangeValidUntilOperation', ('position', 'valid_until')) ChangeValidUntilOperation = namedtuple('ChangeValidUntilOperation', ('position', 'valid_until'))
AddBlockOperation = namedtuple('AddBlockOperation', ('position', 'block_name', 'ignore_from_quota_while_blocked')) AddBlockOperation = namedtuple('AddBlockOperation', ('position', 'block_name', 'ignore_from_quota_while_blocked'))
RemoveBlockOperation = namedtuple('RemoveBlockOperation', ('position', 'block_name', 'ignore_from_quota_while_blocked')) RemoveBlockOperation = namedtuple('RemoveBlockOperation', ('position', 'block_name', 'ignore_from_quota_while_blocked'))
ForceRecomputeOperation = namedtuple('ForceRecomputeOperation', tuple())
class AddPositionResult: class AddPositionResult:
_position: Optional[OrderPosition] _position: Optional[OrderPosition]
@@ -1773,6 +1774,7 @@ class OrderChangeManager:
positions = self.order.positions.select_related('item', 'item__tax_rule') positions = self.order.positions.select_related('item', 'item__tax_rule')
ia = self._invoice_address ia = self._invoice_address
tax_rules = self._current_tax_rules() tax_rules = self._current_tax_rules()
self._operations.append(self.ForceRecomputeOperation())
for pos in positions: for pos in positions:
tax_rule = tax_rules.get(pos.pk, pos.tax_rule) tax_rule = tax_rules.get(pos.pk, pos.tax_rule)
@@ -2611,6 +2613,10 @@ class OrderChangeManager:
except BlockedTicketSecret.DoesNotExist: except BlockedTicketSecret.DoesNotExist:
pass pass
# todo: revoke list handling # todo: revoke list handling
elif isinstance(op, self.ForceRecomputeOperation):
self.order.log_action('pretix.event.order.changed.recomputed', user=self.user, auth=self.auth, data={})
else:
raise TypeError(f"Unknown operation {type(op)}")
for p in secret_dirty: for p in secret_dirty:
assign_ticket_secret( assign_ticket_secret(
@@ -2665,7 +2671,10 @@ class OrderChangeManager:
fees.append(new_fee) fees.append(new_fee)
changed_by_rounding = set(apply_rounding( changed_by_rounding = set(apply_rounding(
self.order.tax_rounding_mode, self.event.currency, [p for p in split_positions if not p.canceled] + fees self.order.tax_rounding_mode,
self._invoice_address,
self.event.currency,
[p for p in split_positions if not p.canceled] + fees
)) ))
split_order.total = sum([p.price for p in split_positions if not p.canceled]) split_order.total = sum([p.price for p in split_positions if not p.canceled])
@@ -2687,7 +2696,10 @@ class OrderChangeManager:
fee.delete() fee.delete()
changed_by_rounding |= set(apply_rounding( changed_by_rounding |= set(apply_rounding(
self.order.tax_rounding_mode, self.event.currency, [p for p in split_positions if not p.canceled] + fees self.order.tax_rounding_mode,
self._invoice_address,
self.event.currency,
[p for p in split_positions if not p.canceled] + fees
)) ))
split_order.total = sum([p.price for p in split_positions if not p.canceled]) + sum([f.value for f in fees]) split_order.total = sum([p.price for p in split_positions if not p.canceled]) + sum([f.value for f in fees])
@@ -2804,7 +2816,12 @@ class OrderChangeManager:
if fee_changed: if fee_changed:
fees = list(self.order.fees.all()) fees = list(self.order.fees.all())
changed = apply_rounding(self.order.tax_rounding_mode, self.order.event.currency, [*positions, *fees]) changed = apply_rounding(
self.order.tax_rounding_mode,
self._invoice_address,
self.order.event.currency,
[*positions, *fees]
)
for l in changed: for l in changed:
if isinstance(l, OrderPosition): if isinstance(l, OrderPosition):
l.save(update_fields=[ l.save(update_fields=[
@@ -3240,8 +3257,12 @@ def change_payment_provider(order: Order, payment_provider, amount=None, new_pay
positions = list(order.positions.all()) positions = list(order.positions.all())
fees = list(order.fees.all()) fees = list(order.fees.all())
try:
ia = order.invoice_address
except InvoiceAddress.DoesNotExist:
ia = None
rounding_changed = set(apply_rounding( rounding_changed = set(apply_rounding(
order.tax_rounding_mode, order.event.currency, [*positions, *[f for f in fees if f.pk != fee.pk]] order.tax_rounding_mode, ia, order.event.currency, [*positions, *[f for f in fees if f.pk != fee.pk]]
)) ))
total_without_fee = sum(c.price for c in positions) + sum(f.value for f in fees if f.pk != fee.pk) total_without_fee = sum(c.price for c in positions) + sum(f.value for f in fees if f.pk != fee.pk)
pending_sum_without_fee = max(Decimal("0.00"), total_without_fee - already_paid) pending_sum_without_fee = max(Decimal("0.00"), total_without_fee - already_paid)
@@ -3266,7 +3287,7 @@ def change_payment_provider(order: Order, payment_provider, amount=None, new_pay
fee = None fee = None
rounding_changed |= set(apply_rounding( rounding_changed |= set(apply_rounding(
order.tax_rounding_mode, order.event.currency, [*positions, *fees] order.tax_rounding_mode, ia, order.event.currency, [*positions, *fees]
)) ))
for l in rounding_changed: for l in rounding_changed:
if isinstance(l, OrderPosition): if isinstance(l, OrderPosition):

View File

@@ -211,7 +211,8 @@ def apply_discounts(event: Event, sales_channel: Union[str, SalesChannel],
return [new_prices.get(idx, (p[3], None)) for idx, p in enumerate(positions)] return [new_prices.get(idx, (p[3], None)) for idx, p in enumerate(positions)]
def apply_rounding(rounding_mode: Literal["line", "sum_by_net", "sum_by_net_keep_gross"], currency: str, def apply_rounding(rounding_mode: Literal["line", "sum_by_net", "sum_by_net_only_business", "sum_by_net_keep_gross"],
invoice_address: Optional[InvoiceAddress], currency: str,
lines: List[Union[OrderPosition, CartPosition, OrderFee]]) -> list: lines: List[Union[OrderPosition, CartPosition, OrderFee]]) -> list:
""" """
Given a list of order positions / cart positions / order fees (may be mixed), applies the given rounding mode Given a list of order positions / cart positions / order fees (may be mixed), applies the given rounding mode
@@ -226,11 +227,17 @@ def apply_rounding(rounding_mode: Literal["line", "sum_by_net", "sum_by_net_keep
When rounding mode is set to ``"sum_by_net"``, the gross prices and tax values of the individual lines will be 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. 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_net"``, or ``"sum_by_net_keep_gross"``. :param rounding_mode: One of ``"line"``, ``"sum_by_net"``, ``"sum_by_net_only_business"``, or ``"sum_by_net_keep_gross"``.
:param invoice_address: The invoice address, or ``None``
:param currency: Currency that will be used to determine rounding precision :param currency: Currency that will be used to determine rounding precision
:param lines: List of order/cart contents :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. :return: Collection of ``lines`` members that have been changed and may need to be persisted to the database.
""" """
if rounding_mode == "sum_by_net_only_business":
if invoice_address and invoice_address.is_business:
rounding_mode = "sum_by_net"
else:
rounding_mode = "line"
def _key(line): def _key(line):
return (line.tax_rate, line.tax_code or "") return (line.tax_rate, line.tax_code or "")

View File

@@ -81,6 +81,7 @@ from pretix.helpers.countries import CachedCountries, pycountry_add
ROUNDING_MODES = ( ROUNDING_MODES = (
('line', _('Compute taxes for every line individually')), ('line', _('Compute taxes for every line individually')),
('sum_by_net', _('Compute taxes based on net total')), ('sum_by_net', _('Compute taxes based on net total')),
('sum_by_net_only_business', _('For business customers, compute taxes based on net total. For individuals, use line-based rounding')),
('sum_by_net_keep_gross', _('Compute taxes based on net total with stable gross prices')), ('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 # We could also have sum_by_gross, but we're not aware of any use-cases for it
) )

View File

@@ -867,6 +867,11 @@ class TaxSettingsForm(EventSettingsValidationMixin, SettingsForm):
"The gross price of some products may be changed to ensure correct rounding, while the net " "The gross price of some products may be changed to ensure correct rounding, while the net "
"prices will be kept as configured. This may cause the actual payment amount to differ." "prices will be kept as configured. This may cause the actual payment amount to differ."
), ),
"sum_by_net_only_business": _(
"Same as above, but only applied to business customers. Line-based rounding will be used for consumers. "
"Recommended when e-invoicing is only used for business customers and consumers do not receive "
"invoices. This can cause the payment amount to change when the invoice address is changed."
),
"sum_by_net_keep_gross": _( "sum_by_net_keep_gross": _(
"Recommended for e-invoicing when you primarily sell to consumers. " "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 " "The gross or net price of some products may be changed automatically to ensure correct "

View File

@@ -170,6 +170,12 @@ class OrderFeeAdded(OrderChangeLogEntryType):
plain = _('A fee has been added') plain = _('A fee has been added')
@log_entry_types.new()
class OrderRecomputed(OrderChangeLogEntryType):
action_type = 'pretix.event.order.changed.recomputed'
plain = _('Taxes and rounding have been recomputed')
@log_entry_types.new() @log_entry_types.new()
class OrderFeeChanged(OrderChangeLogEntryType): class OrderFeeChanged(OrderChangeLogEntryType):
action_type = 'pretix.event.order.changed.feevalue' action_type = 'pretix.event.order.changed.feevalue'

View File

@@ -493,7 +493,18 @@
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% if cart.show_rounding_info %}
<div class="text-muted">
<small>
{% icon "info-circle" %}
{% blocktrans trimmed %}
Since you entered a business address, your price was computed from the
VAT-exclusive price. Due to rounding, this caused a small change to the
total price.
{% endblocktrans %}
</small>
</div>
{% endif %}
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
{% if not cart.is_ordered %} {% if not cart.is_ordered %}

View File

@@ -167,7 +167,7 @@ class CartMixin:
fees = [] fees = []
if not order: if not order:
apply_rounding(self.request.event.settings.tax_rounding, self.request.event.currency, [*lcp, *fees]) apply_rounding(self.request.event.settings.tax_rounding, self.invoice_address, self.request.event.currency, [*lcp, *fees])
total = sum([c.price for c in lcp]) + sum([f.value for f in fees]) total = sum([c.price for c in lcp]) + sum([f.value for f in fees])
net_total = sum(p.price - p.tax_value for p in lcp) + sum([f.net_value for f in fees]) net_total = sum(p.price - p.tax_value for p in lcp) + sum([f.net_value for f in fees])
@@ -264,6 +264,11 @@ class CartMixin:
'max_expiry_extend': max_expiry_extend, 'max_expiry_extend': max_expiry_extend,
'is_ordered': bool(order), 'is_ordered': bool(order),
'itemcount': sum(c.count for c in positions if not c.addon_to), 'itemcount': sum(c.count for c in positions if not c.addon_to),
'show_rounding_info': (
self.request.event.settings.tax_rounding == "sum_by_net_only_business" and
not self.request.event.settings.display_net_prices and
sum(c.price_includes_rounding_correction for c in positions) + sum(f.price_includes_rounding_correction for f in fees)
),
'itemvarsums': itemvarsums, 'itemvarsums': itemvarsums,
'current_selected_payments': [ 'current_selected_payments': [
p for p in self.current_selected_payments(lcp, fees, self.invoice_address) p for p in self.current_selected_payments(lcp, fees, self.invoice_address)
@@ -275,7 +280,7 @@ class CartMixin:
raw_payments = copy.deepcopy(self.cart_session.get('payments', [])) raw_payments = copy.deepcopy(self.cart_session.get('payments', []))
fees = [f for f in fees if f.fee_type != OrderFee.FEE_TYPE_PAYMENT] # we re-compute these here fees = [f for f in fees if f.fee_type != OrderFee.FEE_TYPE_PAYMENT] # we re-compute these here
apply_rounding(self.request.event.settings.tax_rounding, self.request.event.currency, [*positions, *fees]) apply_rounding(self.request.event.settings.tax_rounding, invoice_address, self.request.event.currency, [*positions, *fees])
total = sum([c.price for c in positions]) + sum([f.value for f in fees]) total = sum([c.price for c in positions]) + sum([f.value for f in fees])
payments = [] payments = []
@@ -329,7 +334,7 @@ class CartMixin:
fees.append(pf) fees.append(pf)
# Re-apply rounding as grand total has changed # Re-apply rounding as grand total has changed
apply_rounding(self.request.event.settings.tax_rounding, self.request.event.currency, [*positions, *fees]) apply_rounding(self.request.event.settings.tax_rounding, invoice_address, self.request.event.currency, [*positions, *fees])
total = sum([c.price for c in positions]) + sum([f.value for f in fees]) total = sum([c.price for c in positions]) + sum([f.value for f in fees])
# Re-calculate to_pay as grand total has changed # Re-calculate to_pay as grand total has changed

View File

@@ -43,7 +43,7 @@ def _validate_sample_lines(sample_lines, rounding_mode):
(line.tax_value_includes_rounding_correction, line.price_includes_rounding_correction) (line.tax_value_includes_rounding_correction, line.price_includes_rounding_correction)
for line in sample_lines for line in sample_lines
] ]
changed = apply_rounding(rounding_mode, "EUR", sample_lines) changed = apply_rounding(rounding_mode, None, "EUR", sample_lines)
for line, original in zip(sample_lines, corrections): for line, original in zip(sample_lines, corrections):
if (line.tax_value_includes_rounding_correction, line.price_includes_rounding_correction) != original: if (line.tax_value_includes_rounding_correction, line.price_includes_rounding_correction) != original:
assert line in changed assert line in changed
@@ -108,7 +108,7 @@ def test_revert_net_rounding_to_single_line(sample_lines):
tax_rate=Decimal("19.00"), tax_rate=Decimal("19.00"),
tax_code="S", tax_code="S",
) )
apply_rounding("sum_by_net", "EUR", [l]) apply_rounding("sum_by_net", None, "EUR", [l])
assert l.price == Decimal("100.00") assert l.price == Decimal("100.00")
assert l.price_includes_rounding_correction == Decimal("0.00") assert l.price_includes_rounding_correction == Decimal("0.00")
assert l.tax_value == Decimal("15.97") assert l.tax_value == Decimal("15.97")
@@ -126,7 +126,7 @@ def test_revert_net_keep_gross_rounding_to_single_line(sample_lines):
tax_rate=Decimal("19.00"), tax_rate=Decimal("19.00"),
tax_code="S", tax_code="S",
) )
apply_rounding("sum_by_net_keep_gross", "EUR", [l]) apply_rounding("sum_by_net_keep_gross", None, "EUR", [l])
assert l.price == Decimal("100.00") assert l.price == Decimal("100.00")
assert l.price_includes_rounding_correction == Decimal("0.00") assert l.price_includes_rounding_correction == Decimal("0.00")
assert l.tax_value == Decimal("15.97") assert l.tax_value == Decimal("15.97")
@@ -144,7 +144,7 @@ def test_rounding_of_impossible_gross_price(rounding_mode):
price=Decimal("23.00"), price=Decimal("23.00"),
) )
l._calculate_tax(tax_rule=TaxRule(rate=Decimal("7.00")), invoice_address=InvoiceAddress()) l._calculate_tax(tax_rule=TaxRule(rate=Decimal("7.00")), invoice_address=InvoiceAddress())
apply_rounding(rounding_mode, "EUR", [l]) apply_rounding(rounding_mode, None, "EUR", [l])
assert l.price == Decimal("23.01") assert l.price == Decimal("23.01")
assert l.price_includes_rounding_correction == Decimal("0.01") assert l.price_includes_rounding_correction == Decimal("0.01")
assert l.tax_value == Decimal("1.51") assert l.tax_value == Decimal("1.51")
@@ -164,12 +164,12 @@ def test_round_down():
assert sum(l.tax_value for l in lines) == Decimal("79.85") 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") assert sum(l.price - l.tax_value for l in lines) == Decimal("420.15")
apply_rounding("sum_by_net", "EUR", lines) apply_rounding("sum_by_net", None, "EUR", lines)
assert sum(l.price for l in lines) == Decimal("499.98") 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.tax_value for l in lines) == Decimal("79.83")
assert sum(l.price - l.tax_value for l in lines) == Decimal("420.15") assert sum(l.price - l.tax_value for l in lines) == Decimal("420.15")
apply_rounding("sum_by_net_keep_gross", "EUR", lines) apply_rounding("sum_by_net_keep_gross", None, "EUR", lines)
assert sum(l.price for l in lines) == Decimal("500.00") 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.tax_value for l in lines) == Decimal("79.83")
assert sum(l.price - l.tax_value for l in lines) == Decimal("420.17") assert sum(l.price - l.tax_value for l in lines) == Decimal("420.17")
@@ -187,12 +187,12 @@ def test_round_up():
assert sum(l.tax_value for l in lines) == Decimal("79.80") 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") assert sum(l.price - l.tax_value for l in lines) == Decimal("420.10")
apply_rounding("sum_by_net", "EUR", lines) apply_rounding("sum_by_net", None, "EUR", lines)
assert sum(l.price for l in lines) == Decimal("499.92") 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.tax_value for l in lines) == Decimal("79.82")
assert sum(l.price - l.tax_value for l in lines) == Decimal("420.10") assert sum(l.price - l.tax_value for l in lines) == Decimal("420.10")
apply_rounding("sum_by_net_keep_gross", "EUR", lines) apply_rounding("sum_by_net_keep_gross", None, "EUR", lines)
assert sum(l.price for l in lines) == Decimal("499.90") 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.tax_value for l in lines) == Decimal("79.82")
assert sum(l.price - l.tax_value for l in lines) == Decimal("420.08") assert sum(l.price - l.tax_value for l in lines) == Decimal("420.08")
@@ -210,12 +210,12 @@ def test_round_currency_without_decimals():
assert sum(l.tax_value for l in lines) == Decimal("7980.00") assert sum(l.tax_value for l in lines) == Decimal("7980.00")
assert sum(l.price - l.tax_value for l in lines) == Decimal("42010.00") assert sum(l.price - l.tax_value for l in lines) == Decimal("42010.00")
apply_rounding("sum_by_net", "JPY", lines) apply_rounding("sum_by_net", None, "JPY", lines)
assert sum(l.price for l in lines) == Decimal("49992.00") assert sum(l.price for l in lines) == Decimal("49992.00")
assert sum(l.tax_value for l in lines) == Decimal("7982.00") assert sum(l.tax_value for l in lines) == Decimal("7982.00")
assert sum(l.price - l.tax_value for l in lines) == Decimal("42010.00") assert sum(l.price - l.tax_value for l in lines) == Decimal("42010.00")
apply_rounding("sum_by_net_keep_gross", "JPY", lines) apply_rounding("sum_by_net_keep_gross", None, "JPY", lines)
assert sum(l.price for l in lines) == Decimal("49990.00") assert sum(l.price for l in lines) == Decimal("49990.00")
assert sum(l.tax_value for l in lines) == Decimal("7982.00") assert sum(l.tax_value for l in lines) == Decimal("7982.00")
assert sum(l.price - l.tax_value for l in lines) == Decimal("42008.00") assert sum(l.price - l.tax_value for l in lines) == Decimal("42008.00")
@@ -235,7 +235,7 @@ def test_do_not_touch_free(rounding_mode):
price=Decimal("23.00"), price=Decimal("23.00"),
) )
l2._calculate_tax(tax_rule=TaxRule(rate=Decimal("7.00")), invoice_address=InvoiceAddress()) l2._calculate_tax(tax_rule=TaxRule(rate=Decimal("7.00")), invoice_address=InvoiceAddress())
apply_rounding(rounding_mode, "EUR", [l1, l2]) apply_rounding(rounding_mode, None, "EUR", [l1, l2])
assert l2.price == Decimal("23.01") assert l2.price == Decimal("23.01")
assert l2.price_includes_rounding_correction == Decimal("0.01") assert l2.price_includes_rounding_correction == Decimal("0.01")
assert l2.tax_value == Decimal("1.51") assert l2.tax_value == Decimal("1.51")
@@ -245,3 +245,20 @@ def test_do_not_touch_free(rounding_mode):
assert l1.price_includes_rounding_correction == Decimal("0.00") assert l1.price_includes_rounding_correction == Decimal("0.00")
assert l1.tax_value == Decimal("0.00") assert l1.tax_value == Decimal("0.00")
assert l1.tax_value_includes_rounding_correction == Decimal("0.00") assert l1.tax_value_includes_rounding_correction == Decimal("0.00")
@pytest.mark.django_db
def test_only_business():
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")
apply_rounding("sum_by_net_only_business", None, "EUR", lines)
assert sum(l.price for l in lines) == Decimal("500.00")
apply_rounding("sum_by_net_only_business", InvoiceAddress(is_business=True), "EUR", lines)
assert sum(l.price for l in lines) == Decimal("499.98")