mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +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'),
|
||||
|
||||
@@ -86,7 +86,7 @@ def get_event_navigation(request: HttpRequest):
|
||||
'active': url.url_name == 'event.settings.mail',
|
||||
},
|
||||
{
|
||||
'label': _('Tax rules'),
|
||||
'label': _('Taxes'),
|
||||
'url': reverse('control:event.settings.tax', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
|
||||
@@ -243,7 +243,6 @@
|
||||
{% bootstrap_field sform.show_times layout="control" %}
|
||||
<h4>{% trans "Product list" %}</h4>
|
||||
{% bootstrap_field sform.show_quota_left layout="control" %}
|
||||
{% bootstrap_field sform.display_net_prices layout="control" %}
|
||||
{% bootstrap_field sform.show_variations_expanded layout="control" %}
|
||||
{% bootstrap_field sform.hide_sold_out layout="control" %}
|
||||
|
||||
|
||||
120
src/pretix/control/templates/pretixcontrol/event/tax.html
Normal file
120
src/pretix/control/templates/pretixcontrol/event/tax.html
Normal file
@@ -0,0 +1,120 @@
|
||||
{% extends "pretixcontrol/event/settings_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% block title %}{% trans "Taxes" %}{% endblock %}
|
||||
{% block inside %}
|
||||
<h1>{% trans "Taxes" %}</h1>
|
||||
{% bootstrap_form_errors form layout="control" %}
|
||||
<fieldset>
|
||||
<legend>{% trans "Tax rules" %}</legend>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Tax rules define different taxation scenarios that can then be assigned to the individual products.
|
||||
Each tax rule contains a default tax rate and can optionally contain additional rules that depend
|
||||
on the customer's country and type.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
{% if taxrules|length == 0 %}
|
||||
<div class="empty-collection">
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You haven't created any tax rules yet.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<a href="{% url "control:event.settings.tax.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new tax rule" %}</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-quotas">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Default" %}</th>
|
||||
<th>{% trans "Usage" %}</th>
|
||||
<th>{% trans "Rate" %}</th>
|
||||
<th class="action-col-2"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for tr in taxrules %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong><a
|
||||
href="{% url "control:event.settings.tax.edit" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}">
|
||||
{{ tr.internal_name|default:tr.name }}
|
||||
</a></strong>
|
||||
</td>
|
||||
<td>
|
||||
{% if tr.default %}
|
||||
<span class="text-success">
|
||||
<span class="fa fa-check"></span>
|
||||
{% trans "Default" %}
|
||||
</span>
|
||||
{% else %}
|
||||
<form class="form-inline" method="post"
|
||||
action="{% url "control:event.settings.tax.default" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}">
|
||||
{% csrf_token %}
|
||||
<button class="btn btn-default btn-sm">
|
||||
{% trans "Make default" %}
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% blocktrans trimmed count count=tr.c_items %}
|
||||
{{ count }} product
|
||||
{% plural %}
|
||||
{{ count }} products
|
||||
{% endblocktrans %}
|
||||
</td>
|
||||
<td>
|
||||
{% if tr.price_includes_tax %}
|
||||
{% blocktrans with rate=tr.rate %}incl. {{ rate }} %{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans with rate=tr.rate %}excl. {{ rate }} %{% endblocktrans %}
|
||||
{% endif %}
|
||||
{% if tr.has_custom_rules %}
|
||||
<br><small>{% trans "with custom rules" %}</small>
|
||||
{% elif tr.eu_reverse_charge %}
|
||||
<br><small>{% trans "reverse charge enabled" %}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url "control:event.settings.tax.edit" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.settings.tax.delete" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}"
|
||||
class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<a href="{% url "control:event.settings.tax.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new tax rule" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
<fieldset>
|
||||
<legend>{% trans "Tax settings" %}</legend>
|
||||
{% bootstrap_field form.tax_rounding layout="control" %}
|
||||
{% bootstrap_field form.display_net_prices layout="control" %}
|
||||
</fieldset>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -1,77 +0,0 @@
|
||||
{% extends "pretixcontrol/event/settings_base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Tax rules" %}{% endblock %}
|
||||
{% block inside %}
|
||||
<h1>{% trans "Tax rules" %}</h1>
|
||||
{% if taxrules|length == 0 %}
|
||||
<div class="empty-collection">
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You haven't created any tax rules yet.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<a href="{% url "control:event.settings.tax.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new tax rule" %}</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
<a href="{% url "control:event.settings.tax.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new tax rule" %}
|
||||
</a>
|
||||
</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-quotas">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Default" %}</th>
|
||||
<th>{% trans "Rate" %}</th>
|
||||
<th class="action-col-2"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for tr in taxrules %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong><a href="{% url "control:event.settings.tax.edit" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}">
|
||||
{{ tr.internal_name|default:tr.name }}
|
||||
</a></strong>
|
||||
</td>
|
||||
<td>
|
||||
{% if tr.default %}
|
||||
<span class="text-success">
|
||||
<span class="fa fa-check"></span>
|
||||
{% trans "Default" %}
|
||||
</span>
|
||||
{% else %}
|
||||
<form class="form-inline" method="post"
|
||||
action="{% url "control:event.settings.tax.default" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}">
|
||||
{% csrf_token %}
|
||||
<button class="btn btn-default btn-sm">
|
||||
{% trans "Make default" %}
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if tr.price_includes_tax %}
|
||||
{% blocktrans with rate=tr.rate%}incl. {{ rate }} %{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans with rate=tr.rate%}excl. {{ rate }} %{% endblocktrans %}
|
||||
{% endif %}
|
||||
{% if tr.eu_reverse_charge %}
|
||||
({% trans "reverse charge enabled" %})
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url "control:event.settings.tax.edit" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.settings.tax.delete" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "pretixcontrol/pagination.html" %}
|
||||
{% endblock %}
|
||||
@@ -681,6 +681,16 @@
|
||||
</small>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if django_settings.DEBUG %}
|
||||
<br/>
|
||||
<small class="admin-only">
|
||||
price = {{ line.price|floatformat:2 }}<br>
|
||||
rounding_correction = {{ line.price_includes_rounding_correction|floatformat:2 }}<br>
|
||||
tax_value = {{ line.tax_value|floatformat:2 }}<br>
|
||||
tax_value_rounding_correction = {{ line.tax_value_includes_rounding_correction|floatformat:2 }}<br>
|
||||
voucher_budget_use = {{ line.voucher_budget_use|floatformat:2 }}
|
||||
</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
@@ -721,6 +731,16 @@
|
||||
</small>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if django_settings.DEBUG %}
|
||||
<br/>
|
||||
<small class="admin-only">
|
||||
price = {{ fee.value|floatformat:2 }}<br>
|
||||
rounding_correction = {{ fee.value_includes_rounding_correction|floatformat:2 }}<br>
|
||||
tax_value = {{ fee.tax_value|floatformat:2 }}<br>
|
||||
tax_value_rounding_correction = {{ fee.tax_value_includes_rounding_correction|floatformat:2 }}<br>
|
||||
voucher_budget_use = {{ fee.voucher_budget_use|floatformat:2 }}
|
||||
</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
@@ -749,6 +769,10 @@
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 col-md-offset-5 price">
|
||||
<strong>{{ items.total|money:event.currency }}</strong>
|
||||
<br/>
|
||||
<small class="admin-only">
|
||||
tax_rounding_mode = {{ order.tax_rounding_mode }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
@@ -56,8 +56,26 @@
|
||||
<td>{{ t.get_tax_code_display }}</td>
|
||||
<td class="text-right flip">{{ t.count }} ×</td>
|
||||
<td class="text-right flip">{{ t.price|money:request.event.currency }}</td>
|
||||
<td class="text-right flip">{{ t.full_tax_value|money:request.event.currency }}</td>
|
||||
<td class="text-right flip">{{ t.full_price|money:request.event.currency }}</td>
|
||||
<td class="text-right flip">
|
||||
{{ t.full_tax_value|money:request.event.currency }}
|
||||
{% if t.full_price_includes_rounding_correction %}
|
||||
<br><small>
|
||||
{% blocktrans trimmed with amount=t.full_tax_value_includes_rounding_correction|money:request.event.currency %}
|
||||
incl. {{ amount }} rounding correction
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
{{ t.full_price|money:request.event.currency }}
|
||||
{% if t.full_price_includes_rounding_correction %}
|
||||
<br><small>
|
||||
{% blocktrans trimmed with amount=t.full_price_includes_rounding_correction|money:request.event.currency %}
|
||||
incl. {{ amount }} rounding correction
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@@ -290,7 +290,7 @@ urlpatterns = [
|
||||
re_path(r'^settings/invoice$', event.InvoiceSettings.as_view(), name='event.settings.invoice'),
|
||||
re_path(r'^settings/invoice/preview$', event.InvoicePreview.as_view(), name='event.settings.invoice.preview'),
|
||||
re_path(r'^settings/display', event.DisplaySettings.as_view(), name='event.settings.display'),
|
||||
re_path(r'^settings/tax/$', event.TaxList.as_view(), name='event.settings.tax'),
|
||||
re_path(r'^settings/tax/$', event.TaxSettings.as_view(), name='event.settings.tax'),
|
||||
re_path(r'^settings/tax/(?P<rule>\d+)/$', event.TaxUpdate.as_view(), name='event.settings.tax.edit'),
|
||||
re_path(r'^settings/tax/add$', event.TaxCreate.as_view(), name='event.settings.tax.add'),
|
||||
re_path(r'^settings/tax/(?P<rule>\d+)/delete$', event.TaxDelete.as_view(), name='event.settings.tax.delete'),
|
||||
|
||||
@@ -54,7 +54,7 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.files import File
|
||||
from django.db import transaction
|
||||
from django.db.models import ProtectedError
|
||||
from django.db.models import Count, ProtectedError
|
||||
from django.forms import inlineformset_factory
|
||||
from django.http import (
|
||||
Http404, HttpResponse, HttpResponseBadRequest, HttpResponseNotAllowed,
|
||||
@@ -90,7 +90,7 @@ from pretix.control.forms.event import (
|
||||
EventFooterLinkFormset, EventMetaValueForm, EventSettingsForm,
|
||||
EventUpdateForm, InvoiceSettingsForm, ItemMetaPropertyForm,
|
||||
MailSettingsForm, PaymentSettingsForm, ProviderForm, QuickSetupForm,
|
||||
QuickSetupProductFormSet, TaxRuleForm, TaxRuleLineFormSet,
|
||||
QuickSetupProductFormSet, TaxRuleForm, TaxRuleLineFormSet, TaxSettingsForm,
|
||||
TicketSettingsForm, WidgetCodeForm,
|
||||
)
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
@@ -648,6 +648,25 @@ class PaymentSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
return context
|
||||
|
||||
|
||||
class TaxSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
template_name = 'pretixcontrol/event/tax.html'
|
||||
form_class = TaxSettingsForm
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:event.settings.tax', kwargs={
|
||||
'organizer': self.request.organizer.slug,
|
||||
'event': self.request.event.slug,
|
||||
})
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
context['taxrules'] = self.request.event.tax_rules.annotate(
|
||||
c_items=Count("item")
|
||||
).all()
|
||||
return context
|
||||
|
||||
|
||||
class InvoiceSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
model = Event
|
||||
form_class = InvoiceSettingsForm
|
||||
@@ -1263,16 +1282,6 @@ class EventComment(EventPermissionRequiredMixin, View):
|
||||
})
|
||||
|
||||
|
||||
class TaxList(EventSettingsViewMixin, EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
model = TaxRule
|
||||
context_object_name = 'taxrules'
|
||||
template_name = 'pretixcontrol/event/tax_index.html'
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.event.tax_rules.all()
|
||||
|
||||
|
||||
class TaxCreate(EventSettingsViewMixin, EventPermissionRequiredMixin, CreateView):
|
||||
model = TaxRule
|
||||
form_class = TaxRuleForm
|
||||
|
||||
@@ -633,7 +633,9 @@ class OrderTransactions(OrderView):
|
||||
ctx['sums'] = self.order.transactions.aggregate(
|
||||
sum_count=Sum('count'),
|
||||
full_price=Sum(F('count') * F('price')),
|
||||
full_price_includes_rounding_correction=Sum(F('count') * F('price_includes_rounding_correction')),
|
||||
full_tax_value=Sum(F('count') * F('tax_value')),
|
||||
full_tax_value_includes_rounding_correction=Sum(F('count') * F('tax_value_includes_rounding_correction')),
|
||||
)
|
||||
return ctx
|
||||
|
||||
|
||||
Reference in New Issue
Block a user