Allow country specific tax rules (#1714)

This commit is contained in:
Raphael Michel
2020-07-08 15:00:13 +02:00
committed by GitHub
parent 1c9a1b5e02
commit 6e9d921af6
20 changed files with 716 additions and 161 deletions

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.0.6 on 2020-06-28 19:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0155_quota_release_after_exit'),
]
operations = [
migrations.AddField(
model_name='cartposition',
name='override_tax_rate',
field=models.DecimalField(decimal_places=2, max_digits=10, null=True),
),
]

View File

@@ -448,24 +448,32 @@ class Item(LoggedModel):
return self.event.settings.show_quota_left
return self.show_quota_left
def tax(self, price=None, base_price_is='auto', currency=None, include_bundled=False):
def tax(self, price=None, base_price_is='auto', currency=None, invoice_address=None, override_tax_rate=None, include_bundled=False):
price = price if price is not None else self.default_price
if not self.tax_rule:
t = TaxedPrice(gross=price, net=price, tax=Decimal('0.00'),
rate=Decimal('0.00'), name='')
else:
t = self.tax_rule.tax(price, base_price_is=base_price_is,
currency=currency or self.event.currency)
t = self.tax_rule.tax(price, base_price_is=base_price_is, invoice_address=invoice_address,
override_tax_rate=override_tax_rate, currency=currency or self.event.currency)
if include_bundled:
for b in self.bundles.all():
if b.designated_price and b.bundled_item.tax_rule_id != self.tax_rule_id:
if b.bundled_variation:
bprice = b.bundled_variation.tax(b.designated_price * b.count, base_price_is='gross', currency=currency)
bprice = b.bundled_variation.tax(b.designated_price * b.count, base_price_is='gross',
invoice_address=invoice_address,
currency=currency)
else:
bprice = b.bundled_item.tax(b.designated_price * b.count, base_price_is='gross', currency=currency)
compare_price = self.tax_rule.tax(b.designated_price * b.count, base_price_is='gross', currency=currency)
bprice = b.bundled_item.tax(b.designated_price * b.count,
invoice_address=invoice_address,
base_price_is='gross',
currency=currency)
compare_price = self.tax_rule.tax(b.designated_price * b.count,
override_tax_rate=override_tax_rate,
invoice_address=invoice_address,
currency=currency)
t.net += bprice.net - compare_price.net
t.tax += bprice.tax - compare_price.tax
t.name = "MIXED!"
@@ -673,23 +681,31 @@ class ItemVariation(models.Model):
def price(self):
return self.default_price if self.default_price is not None else self.item.default_price
def tax(self, price=None, base_price_is='auto', currency=None, include_bundled=False):
def tax(self, price=None, base_price_is='auto', currency=None, include_bundled=False, override_tax_rate=None,
invoice_address=None):
price = price if price is not None else self.price
if not self.item.tax_rule:
t = TaxedPrice(gross=price, net=price, tax=Decimal('0.00'),
rate=Decimal('0.00'), name='')
else:
t = self.item.tax_rule.tax(price, base_price_is=base_price_is, currency=currency)
t = self.item.tax_rule.tax(price, base_price_is=base_price_is, currency=currency,
override_tax_rate=override_tax_rate,
invoice_address=invoice_address)
if include_bundled:
for b in self.item.bundles.all():
if b.designated_price and b.bundled_item.tax_rule_id != self.item.tax_rule_id:
if b.bundled_variation:
bprice = b.bundled_variation.tax(b.designated_price * b.count, base_price_is='gross', currency=currency)
bprice = b.bundled_variation.tax(b.designated_price * b.count, base_price_is='gross',
currency=currency,
invoice_address=invoice_address)
else:
bprice = b.bundled_item.tax(b.designated_price * b.count, base_price_is='gross', currency=currency)
compare_price = self.item.tax_rule.tax(b.designated_price * b.count, base_price_is='gross', currency=currency)
bprice = b.bundled_item.tax(b.designated_price * b.count, base_price_is='gross',
currency=currency,
invoice_address=invoice_address)
compare_price = self.item.tax_rule.tax(b.designated_price * b.count, base_price_is='gross',
currency=currency, invoice_address=invoice_address)
t.net += bprice.net - compare_price.net
t.tax += bprice.tax - compare_price.tax
t.name = "MIXED!"

View File

@@ -1813,13 +1813,9 @@ class OrderFee(models.Model):
self.tax_rule = self.order.event.settings.tax_rate_default
if self.tax_rule:
if self.tax_rule.tax_applicable(ia):
tax = self.tax_rule.tax(self.value, base_price_is='gross')
self.tax_rate = tax.rate
self.tax_value = tax.tax
else:
self.tax_value = Decimal('0.00')
self.tax_rate = Decimal('0.00')
tax = self.tax_rule.tax(self.value, base_price_is='gross', invoice_address=ia)
self.tax_rate = tax.rate
self.tax_value = tax.tax
else:
self.tax_value = Decimal('0.00')
self.tax_rate = Decimal('0.00')
@@ -1966,13 +1962,9 @@ class OrderPosition(AbstractPosition):
except InvoiceAddress.DoesNotExist:
ia = None
if self.tax_rule:
if self.tax_rule.tax_applicable(ia):
tax = self.tax_rule.tax(self.price, base_price_is='gross')
self.tax_rate = tax.rate
self.tax_value = tax.tax
else:
self.tax_value = Decimal('0.00')
self.tax_rate = Decimal('0.00')
tax = self.tax_rule.tax(self.price, invoice_address=ia, base_price_is='gross')
self.tax_rate = tax.rate
self.tax_value = tax.tax
else:
self.tax_value = Decimal('0.00')
self.tax_rate = Decimal('0.00')
@@ -2111,6 +2103,10 @@ class CartPosition(AbstractPosition):
includes_tax = models.BooleanField(
default=True
)
override_tax_rate = models.DecimalField(
max_digits=10, decimal_places=2,
null=True, blank=True
)
is_bundled = models.BooleanField(default=False)
objects = ScopedManager(organizer='event__organizer')
@@ -2127,6 +2123,8 @@ class CartPosition(AbstractPosition):
@property
def tax_rate(self):
if self.includes_tax:
if self.override_tax_rate is not None:
return self.override_tax_rate
return self.item.tax(self.price, base_price_is='gross').rate
else:
return Decimal('0.00')
@@ -2134,7 +2132,7 @@ class CartPosition(AbstractPosition):
@property
def tax_value(self):
if self.includes_tax:
return self.item.tax(self.price, base_price_is='gross').tax
return self.item.tax(self.price, override_tax_rate=self.override_tax_rate, base_price_is='gross').tax
else:
return Decimal('0.00')

View File

@@ -164,16 +164,39 @@ class TaxRule(LoggedModel):
def has_custom_rules(self):
return self.custom_rules and self.custom_rules != '[]'
def tax(self, base_price, base_price_is='auto', currency=None):
def tax_rate_for(self, invoice_address):
if not self._tax_applicable(invoice_address):
return Decimal('0.00')
if self.has_custom_rules:
rule = self.get_matching_rule(invoice_address)
if rule.get('action', 'vat') == 'vat' and rule.get('rate') is not None:
return Decimal(rule.get('rate'))
return Decimal(self.rate)
def tax(self, base_price, base_price_is='auto', currency=None, override_tax_rate=None, invoice_address=None,
subtract_from_gross=Decimal('0.00')):
from .event import Event
try:
currency = currency or self.event.currency
except Event.DoesNotExist:
pass
if self.rate == Decimal('0.00'):
rate = Decimal(self.rate)
if override_tax_rate is not None:
rate = override_tax_rate
elif invoice_address:
adjust_rate = self.tax_rate_for(invoice_address)
if adjust_rate != rate:
normal_price = self.tax(base_price, base_price_is, currency, subtract_from_gross=subtract_from_gross)
base_price = normal_price.net
base_price_is = 'net'
subtract_from_gross = Decimal('0.00')
rate = adjust_rate
if rate == Decimal('0.00'):
return TaxedPrice(
net=base_price, gross=base_price, tax=Decimal('0.00'),
rate=self.rate, name=self.name
net=base_price - subtract_from_gross, gross=base_price - subtract_from_gross, tax=Decimal('0.00'),
rate=rate, name=self.name
)
if base_price_is == 'auto':
@@ -183,19 +206,22 @@ class TaxRule(LoggedModel):
base_price_is = 'net'
if base_price_is == 'gross':
gross = base_price
net = round_decimal(gross - (base_price * (1 - 100 / (100 + self.rate))),
gross = max(Decimal('0.00'), base_price - subtract_from_gross)
net = round_decimal(gross - (gross * (1 - 100 / (100 + rate))),
currency)
elif base_price_is == 'net':
net = base_price
gross = round_decimal((net * (1 + self.rate / 100)),
currency)
gross = round_decimal((net * (1 + rate / 100)), currency)
if subtract_from_gross:
gross -= subtract_from_gross
net = round_decimal(gross - (gross * (1 - 100 / (100 + rate))),
currency)
else:
raise ValueError('Unknown base price type: {}'.format(base_price_is))
return TaxedPrice(
net=net, gross=gross, tax=gross - net,
rate=self.rate, name=self.name
rate=rate, name=self.name
)
@property
@@ -243,7 +269,7 @@ class TaxRule(LoggedModel):
return False
def tax_applicable(self, invoice_address):
def _tax_applicable(self, invoice_address):
if self._custom_rules:
rule = self.get_matching_rule(invoice_address)
return rule.get('action', 'vat') == 'vat'

View File

@@ -907,6 +907,7 @@ class CartManager:
price=op.price.gross, expires=self._expiry, cart_id=self.cart_id,
voucher=op.voucher, addon_to=op.addon_to if op.addon_to else None,
subevent=op.subevent, includes_tax=op.includes_tax, seat=op.seat,
override_tax_rate=op.price.rate,
price_before_voucher=op.price_before_voucher.gross if op.price_before_voucher is not None else None
)
if self.event.settings.attendee_names_asked:
@@ -940,7 +941,7 @@ class CartManager:
new_cart_positions.append(CartPosition(
event=self.event, item=b.item, variation=b.variation,
price=b.price.gross, expires=self._expiry, cart_id=self.cart_id,
voucher=None, addon_to=cp,
voucher=None, addon_to=cp, override_tax_rate=b.price.rate,
subevent=b.subevent, includes_tax=b.includes_tax, is_bundled=True
))
@@ -1032,19 +1033,15 @@ def update_tax_rates(event: Event, cart_id: str, invoice_address: InvoiceAddress
for pos in positions:
if not pos.item.tax_rule:
continue
charge_tax = pos.item.tax_rule.tax_applicable(invoice_address)
if pos.includes_tax and not charge_tax:
price = pos.item.tax(pos.price, base_price_is='gross').net
totaldiff += price - pos.price
pos.price = price
pos.includes_tax = False
pos.save(update_fields=['price', 'includes_tax'])
elif charge_tax and not pos.includes_tax:
price = pos.item.tax(pos.price, base_price_is='net').gross
totaldiff += price - pos.price
pos.price = price
pos.includes_tax = True
pos.save(update_fields=['price', 'includes_tax'])
rate = pos.item.tax_rule.tax_rate_for(invoice_address)
if pos.tax_rate != rate:
current_net = pos.price - pos.tax_value
new_gross = pos.item.tax(current_net, base_price_is='net', invoice_address=invoice_address).gross
pos.price = new_gross
pos.includes_tax = rate != Decimal('0.00')
pos.override_tax_rate = rate
pos.save(update_fields=['price', 'includes_tax', 'override_tax_rate'])
return totaldiff
@@ -1092,23 +1089,14 @@ def get_fees(event, request, total, invoice_address, provider, positions):
if payment_fee:
payment_fee_tax_rule = event.settings.tax_rate_default or TaxRule.zero()
if payment_fee_tax_rule.tax_applicable(invoice_address):
payment_fee_tax = payment_fee_tax_rule.tax(payment_fee, base_price_is='gross')
fees.append(OrderFee(
fee_type=OrderFee.FEE_TYPE_PAYMENT,
value=payment_fee,
tax_rate=payment_fee_tax.rate,
tax_value=payment_fee_tax.tax,
tax_rule=payment_fee_tax_rule
))
else:
fees.append(OrderFee(
fee_type=OrderFee.FEE_TYPE_PAYMENT,
value=payment_fee,
tax_rate=Decimal('0.00'),
tax_value=Decimal('0.00'),
tax_rule=payment_fee_tax_rule
))
payment_fee_tax = payment_fee_tax_rule.tax(payment_fee, base_price_is='gross', invoice_address=invoice_address)
fees.append(OrderFee(
fee_type=OrderFee.FEE_TYPE_PAYMENT,
value=payment_fee,
tax_rate=payment_fee_tax.rate,
tax_value=payment_fee_tax.tax,
tax_rule=payment_fee_tax_rule
))
return fees

View File

@@ -24,7 +24,7 @@ from pretix.base.i18n import language
from pretix.base.models import (
Invoice, InvoiceAddress, InvoiceLine, Order, OrderFee,
)
from pretix.base.models.tax import EU_CURRENCIES
from pretix.base.models.tax import EU_COUNTRIES, EU_CURRENCIES
from pretix.base.services.tasks import TransactionAwareTask
from pretix.base.settings import GlobalSettingsObject
from pretix.base.signals import invoice_line_text, periodic_task
@@ -181,11 +181,17 @@ def build_invoice(invoice: Invoice) -> Invoice:
if reverse_charge:
if invoice.additional_text:
invoice.additional_text += "<br /><br />"
invoice.additional_text += pgettext(
"invoice",
"Reverse Charge: According to Article 194, 196 of Council Directive 2006/112/EEC, VAT liability "
"rests with the service recipient."
)
if str(invoice.invoice_to_country) in EU_COUNTRIES:
invoice.additional_text += pgettext(
"invoice",
"Reverse Charge: According to Article 194, 196 of Council Directive 2006/112/EEC, VAT liability "
"rests with the service recipient."
)
else:
invoice.additional_text += pgettext(
"invoice",
"VAT liability rests with the service recipient."
)
invoice.reverse_charge = True
invoice.save()

View File

@@ -34,7 +34,7 @@ from pretix.base.models.orders import (
generate_secret,
)
from pretix.base.models.organizer import TeamAPIToken
from pretix.base.models.tax import TaxedPrice, TaxRule
from pretix.base.models.tax import TaxRule
from pretix.base.payment import BasePaymentProvider, PaymentException
from pretix.base.reldate import RelativeDateWrapper
from pretix.base.services import tickets
@@ -1263,7 +1263,8 @@ class OrderChangeManager:
self._operations.append(self.RegenerateSecretOperation(position))
def change_price(self, position: OrderPosition, price: Decimal):
price = position.item.tax(price, base_price_is='gross')
tax_rule = self._current_tax_rules().get(position.pk, position.tax_rule)
price = tax_rule.tax(price, base_price_is='gross')
if position.issued_gift_cards.exists():
raise OrderError(self.error_messages['gift_card_change'])
@@ -1279,27 +1280,38 @@ class OrderChangeManager:
self._operations.append(self.TaxRuleOperation(position_or_fee, tax_rule))
self._invoice_dirty = True
def recalculate_taxes(self):
def _current_tax_rules(self):
tax_rules = {}
for p in self._operations:
if isinstance(p, self.TaxRuleOperation):
tax_rules[p.position.pk] = p.tax_rule
elif isinstance(p, self.ItemOperation):
tax_rules[p.position.pk] = p.item.tax_rule
return tax_rules
def recalculate_taxes(self, keep='net'):
positions = self.order.positions.select_related('item', 'item__tax_rule')
ia = self._invoice_address
tax_rules = self._current_tax_rules()
for pos in positions:
if not pos.item.tax_rule:
tax_rule = tax_rules.get(pos.pk, pos.tax_rule)
if not tax_rule:
continue
if not pos.price:
continue
charge_tax = pos.item.tax_rule.tax_applicable(ia)
if pos.tax_value and not charge_tax:
net_price = pos.price - pos.tax_value
price = TaxedPrice(gross=net_price, net=net_price, tax=Decimal('0.00'), rate=Decimal('0.00'), name='')
if price.gross != pos.price:
self._totaldiff += price.gross - pos.price
self._operations.append(self.PriceOperation(pos, price))
elif charge_tax and not pos.tax_value:
price = pos.item.tax(pos.price, base_price_is='net')
if price.gross != pos.price:
self._totaldiff += price.gross - pos.price
self._operations.append(self.PriceOperation(pos, price))
new_rate = tax_rule.tax_rate_for(ia)
# We use override_tax_rate to make sure .tax() doesn't get clever and re-adjusts the pricing itself
if new_rate != pos.tax_rate:
if keep == 'net':
new_tax = tax_rule.tax(pos.price - pos.tax_value, base_price_is='net', currency=self.event.currency,
override_tax_rate=new_rate)
else:
new_tax = tax_rule.tax(pos.price, base_price_is='gross', currency=self.event.currency,
override_tax_rate=new_rate)
self._totaldiff += new_tax.gross - pos.price
self._operations.append(self.PriceOperation(pos, new_tax))
def cancel_fee(self, fee: OrderFee):
self._totaldiff -= fee.value
@@ -1345,10 +1357,7 @@ class OrderChangeManager:
if price is None:
price = get_price(item, variation, subevent=subevent, invoice_address=self._invoice_address)
else:
if item.tax_rule and item.tax_rule.tax_applicable(self._invoice_address):
price = item.tax(price, base_price_is='gross')
else:
price = TaxedPrice(gross=price, net=price, tax=Decimal('0.00'), rate=Decimal('0.00'), name='')
price = item.tax(price, base_price_is='gross', invoice_address=self._invoice_address)
if price is None:
raise OrderError(self.error_messages['product_invalid'])
@@ -1599,7 +1608,8 @@ class OrderChangeManager:
'new_price': op.price.gross
})
op.position.price = op.price.gross
op.position._calculate_tax()
op.position.tax_rate = op.price.rate
op.position.tax_value = op.price.tax
op.position.save()
elif isinstance(op, self.TaxRuleOperation):
if isinstance(op.position, OrderPosition):

View File

@@ -46,33 +46,27 @@ def get_price(item: Item, variation: ItemVariation = None,
price_includes_tax=True,
eu_reverse_charge=False,
)
price = tax_rule.tax(price)
price = tax_rule.tax(price, invoice_address=invoice_address, subtract_from_gross=bundled_sum)
if force_custom_price and custom_price is not None and custom_price != "":
if custom_price_is_net:
price = tax_rule.tax(custom_price, base_price_is='net')
price = tax_rule.tax(custom_price, base_price_is='net', invoice_address=invoice_address,
subtract_from_gross=bundled_sum)
else:
price = tax_rule.tax(custom_price, base_price_is='gross')
price = tax_rule.tax(custom_price, base_price_is='gross', invoice_address=invoice_address,
subtract_from_gross=bundled_sum)
if item.free_price and custom_price is not None and custom_price != "":
if not isinstance(custom_price, Decimal):
custom_price = Decimal(str(custom_price).replace(",", "."))
if custom_price > 100000000:
raise ValueError('price_too_high')
if custom_price_is_net:
price = tax_rule.tax(max(custom_price, price.net), base_price_is='net')
price = tax_rule.tax(max(custom_price, price.net), base_price_is='net',
invoice_address=invoice_address, subtract_from_gross=bundled_sum)
else:
price = tax_rule.tax(max(custom_price, price.gross), base_price_is='gross')
if bundled_sum:
price = price - TaxedPrice(net=bundled_sum, gross=bundled_sum, rate=0, tax=0, name='')
if price.gross < Decimal('0.00'):
return TAXED_ZERO
if invoice_address and not tax_rule.tax_applicable(invoice_address):
price.tax = Decimal('0.00')
price.rate = Decimal('0.00')
price.gross = price.net
price.name = ''
price = tax_rule.tax(max(custom_price, price.gross), base_price_is='gross',
invoice_address=invoice_address, subtract_from_gross=bundled_sum)
price.gross = round_decimal(price.gross, item.event.currency)
price.net = round_decimal(price.net, item.event.currency)

View File

@@ -1086,6 +1086,11 @@ class TaxRuleLineForm(forms.Form):
('no', _('No VAT')),
],
)
rate = forms.DecimalField(
label=_('Deviating tax rate'),
max_digits=10, decimal_places=2,
required=False
)
TaxRuleLineFormSet = formset_factory(

View File

@@ -183,14 +183,15 @@ class CommentForm(I18nModelForm):
class OtherOperationsForm(forms.Form):
recalculate_taxes = forms.BooleanField(
recalculate_taxes = forms.ChoiceField(
label=_('Re-calculate taxes'),
required=False,
help_text=_(
'This operation re-checks if taxes should be paid to the items due to e.g. configured reverse charge rules '
'and changes the prices and tax values accordingly. This is useful e.g. after an invoice address change. '
'Use with care and only if you need to. Note that rounding differences might occur in this procedure.'
)
choices=(
('', _('Do not re-calculate taxes')),
('gross', _('Re-calculate taxes based on address and product settings, keep gross amount the same.')),
('net', _('Re-calculate taxes based on address and product settings, keep net amount the same.')),
),
widget=forms.RadioSelect
)
reissue_invoice = forms.BooleanField(
label=_('Issue a new invoice if required'),

View File

@@ -59,7 +59,7 @@
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-sm-4">
<div class="col-sm-3">
{% bootstrap_field form.country layout='inline' form_group_class="" %}
</div>
<div class="col-sm-3">
@@ -68,8 +68,11 @@
<div class="col-sm-3">
{% bootstrap_field form.action layout='inline' form_group_class="" %}
</div>
<div class="col-sm-2 text-right flip">
<button type="button" class="btn btn-danger" data-formset-delete-button>
<div class="col-sm-2">
{% bootstrap_field form.rate layout='inline' form_group_class="" %}
</div>
<div class="col-sm-1 text-right flip">
<button type="button" class="btn btn-block btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
@@ -82,7 +85,7 @@
{{ form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-sm-4">
<div class="col-sm-3">
{% bootstrap_field formset.empty_form.country layout='inline' form_group_class="" %}
</div>
<div class="col-sm-3">
@@ -91,8 +94,11 @@
<div class="col-sm-3">
{% bootstrap_field formset.empty_form.action layout='inline' form_group_class="" %}
</div>
<div class="col-sm-2 text-right flip">
<button type="button" class="btn btn-danger" data-formset-delete-button>
<div class="col-sm-2">
{% bootstrap_field formset.empty_form.rate layout='inline' form_group_class="" %}
</div>
<div class="col-sm-1 text-right flip">
<button type="button" class="btn btn-block btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>

View File

@@ -9,6 +9,7 @@ from django.conf import settings
from django.contrib import messages
from django.contrib.contenttypes.models import ContentType
from django.core.files import File
from django.core.serializers.json import DjangoJSONEncoder
from django.db import transaction
from django.db.models import ProtectedError
from django.forms import inlineformset_factory
@@ -1135,7 +1136,7 @@ class TaxUpdate(EventSettingsViewMixin, EventPermissionRequiredMixin, UpdateView
messages.success(self.request, _('Your changes have been saved.'))
form.instance.custom_rules = json.dumps([
f.cleaned_data for f in self.formset if f not in self.formset.deleted_forms
])
], cls=DjangoJSONEncoder)
if form.has_changed():
self.object.log_action(
'pretix.event.taxrule.changed', user=self.request.user, data={

View File

@@ -1397,11 +1397,6 @@ class OrderChange(OrderView):
for f in fees:
f.form = OrderFeeChangeForm(prefix='of-{}'.format(f.pk), instance=f,
data=self.request.POST if self.request.method == "POST" else None)
try:
ia = self.order.invoice_address
except InvoiceAddress.DoesNotExist:
ia = None
f.apply_tax = self.request.event.settings.tax_rate_default and self.request.event.settings.tax_rate_default.tax_applicable(invoice_address=ia)
return fees
@cached_property
@@ -1411,11 +1406,6 @@ class OrderChange(OrderView):
p.form = OrderPositionChangeForm(prefix='op-{}'.format(p.pk), instance=p, items=self.items,
initial={'seat': p.seat.seat_guid if p.seat else None},
data=self.request.POST if self.request.method == "POST" else None)
try:
ia = self.order.invoice_address
except InvoiceAddress.DoesNotExist:
ia = None
p.apply_tax = p.item.tax_rule and p.item.tax_rule.tax_applicable(invoice_address=ia)
return positions
def get_context_data(self, **kwargs):
@@ -1431,7 +1421,9 @@ class OrderChange(OrderView):
return False
else:
if self.other_form.cleaned_data['recalculate_taxes']:
ocm.recalculate_taxes()
ocm.recalculate_taxes(
keep=self.other_form.cleaned_data['recalculate_taxes']
)
return True
def _process_add(self, ocm):
@@ -1523,7 +1515,7 @@ class OrderChange(OrderView):
if p.seat and p.form.cleaned_data['seat'] and p.form.cleaned_data['seat'] != p.seat.seat_guid:
ocm.change_seat(p, p.form.cleaned_data['seat'])
if p.form.cleaned_data['price'] != p.price:
if p.form.cleaned_data['price'] is not None and p.form.cleaned_data['price'] != p.price:
ocm.change_price(p, p.form.cleaned_data['price'])
if p.form.cleaned_data['tax_rule'] and p.form.cleaned_data['tax_rule'] != p.tax_rule: