mirror of
https://github.com/pretix/pretix.git
synced 2026-05-06 15:24:02 +00:00
Allow to round taxes on order-level (#5019)
* Allow to round taxes on order-level * Rename get_cart_total * Persist rounding mode with order * Add general docs * Order creation API * Update fee algorithm * Rounding on payment method change * Round when splitting order * Fix failing tests * Add settings page * Add tests * Replace algorithm * Add test case for currency rounding * Improve order change * Update flowchart * Update discount logic (more hypothetical, we don't store rounding on cart positions atm) * Rename internal method * Fix typo * Update help text * Apply suggestions from code review Co-authored-by: luelista <weller@rami.io> * Order rounding refactor (#5571) * Add RoundingCorrectionMixin providing before-rounding-values as properties * Use gross_price_before_rounding in more places * Update doc/development/algorithms/pricing.rst Co-authored-by: Martin Gross <gross@rami.io> * Allow to override on perform_order * Rebase migration * Fix event cancellation --------- Co-authored-by: luelista <weller@rami.io> Co-authored-by: Martin Gross <gross@rami.io>
This commit is contained in:
@@ -68,7 +68,7 @@ from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
|
||||
from pretix.base.services.placeholders import FormPlaceholderMixin
|
||||
from pretix.base.settings import (
|
||||
COUNTRIES_WITH_STATE_IN_ADDRESS, DEFAULTS, PERSON_NAME_SCHEMES,
|
||||
PERSON_NAME_TITLE_GROUPS, validate_event_settings,
|
||||
PERSON_NAME_TITLE_GROUPS, ROUNDING_MODES, validate_event_settings,
|
||||
)
|
||||
from pretix.base.validators import multimail_validate
|
||||
from pretix.control.forms import (
|
||||
@@ -541,7 +541,6 @@ class EventSettingsForm(EventSettingsValidationMixin, FormPlaceholderMixin, Sett
|
||||
'show_date_to',
|
||||
'show_times',
|
||||
'show_items_outside_presale_period',
|
||||
'display_net_prices',
|
||||
'hide_prices_from_attendees',
|
||||
'presale_start_show_date',
|
||||
'locales',
|
||||
@@ -799,6 +798,80 @@ class PaymentSettingsForm(EventSettingsValidationMixin, SettingsForm):
|
||||
return value
|
||||
|
||||
|
||||
class DisplayNetPricesBooleanSelect(forms.RadioSelect):
|
||||
def __init__(self, attrs=None):
|
||||
choices = (
|
||||
("false", format_html(
|
||||
'{} <br><span class="text-muted">{}</span>',
|
||||
_("Prices including tax"),
|
||||
_("Recommended if you sell tickets at least partly to consumers.")
|
||||
)),
|
||||
("true", format_html(
|
||||
'{} <br><span class="text-muted">{}</span>',
|
||||
_("Prices excluding tax"),
|
||||
_("Recommended only if you sell tickets primarily to business customers.")
|
||||
)),
|
||||
)
|
||||
super().__init__(attrs, choices)
|
||||
|
||||
def format_value(self, value):
|
||||
try:
|
||||
return {
|
||||
True: "true",
|
||||
False: "false",
|
||||
"true": "true",
|
||||
"false": "false",
|
||||
}[value]
|
||||
except KeyError:
|
||||
return "unknown"
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
value = data.get(name)
|
||||
return {
|
||||
True: True,
|
||||
"True": True,
|
||||
"False": False,
|
||||
False: False,
|
||||
"true": True,
|
||||
"false": False,
|
||||
}.get(value)
|
||||
|
||||
|
||||
class TaxSettingsForm(EventSettingsValidationMixin, SettingsForm):
|
||||
auto_fields = [
|
||||
'display_net_prices',
|
||||
'tax_rounding',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
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 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 kept 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. Gross prices may still change if they are impossible to derive from a rounded net price."
|
||||
),
|
||||
}
|
||||
self.fields["tax_rounding"].choices = (
|
||||
(k, format_html('{}<br><span class="text-muted">{}</span>', v, help_text.get(k, "")))
|
||||
for k, v in ROUNDING_MODES
|
||||
)
|
||||
|
||||
|
||||
class ProviderForm(SettingsForm):
|
||||
"""
|
||||
This is a SettingsForm, but if fields are set to required=True, validation
|
||||
@@ -1527,7 +1600,10 @@ class TaxRuleLineForm(I18nForm):
|
||||
rate = forms.DecimalField(
|
||||
label=_('Deviating tax rate'),
|
||||
max_digits=10, decimal_places=2,
|
||||
required=False
|
||||
required=False,
|
||||
widget=forms.NumberInput(attrs={
|
||||
'placeholder': _('Deviating tax rate'),
|
||||
})
|
||||
)
|
||||
invoice_text = I18nFormField(
|
||||
label=_('Text on invoice'),
|
||||
|
||||
Reference in New Issue
Block a user