mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
Allow country specific tax rules (#1714)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user