mirror of
https://github.com/pretix/pretix.git
synced 2026-05-09 15:54:03 +00:00
@@ -1,19 +1,33 @@
|
||||
import copy
|
||||
from decimal import Decimal
|
||||
import json
|
||||
import logging
|
||||
import urllib.error
|
||||
from datetime import date, timedelta
|
||||
from decimal import ROUND_HALF_UP, Decimal
|
||||
|
||||
import vat_moss.exchange_rates
|
||||
from django.conf import settings
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.db import transaction
|
||||
from django.db.models import Count
|
||||
from django.dispatch import receiver
|
||||
from django.utils import timezone
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import pgettext, ugettext as _
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import Invoice, InvoiceAddress, InvoiceLine, Order
|
||||
from pretix.base.models.tax import EU_CURRENCIES
|
||||
from pretix.base.services.async import TransactionAwareTask
|
||||
from pretix.base.settings import GlobalSettingsObject
|
||||
from pretix.base.signals import periodic_task
|
||||
from pretix.celery_app import app
|
||||
from pretix.helpers.database import rolledback_transaction
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
def build_invoice(invoice: Invoice) -> Invoice:
|
||||
@@ -33,6 +47,7 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
invoice.payment_provider_text = str(payment).replace('\n', '<br />')
|
||||
|
||||
try:
|
||||
ia = invoice.order.invoice_address
|
||||
addr_template = pgettext("invoice", """{i.company}
|
||||
{i.name}
|
||||
{i.street}
|
||||
@@ -44,7 +59,31 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
).strip()
|
||||
if invoice.order.invoice_address.vat_id:
|
||||
invoice.invoice_to += "\n" + pgettext("invoice", "VAT-ID: %s") % invoice.order.invoice_address.vat_id
|
||||
|
||||
cc = str(invoice.order.invoice_address.country)
|
||||
|
||||
if cc in EU_CURRENCIES and EU_CURRENCIES[cc] != invoice.event.currency:
|
||||
invoice.foreign_currency_display = EU_CURRENCIES[cc]
|
||||
|
||||
if settings.FETCH_ECB_RATES:
|
||||
gs = GlobalSettingsObject()
|
||||
rates_date = gs.settings.get('ecb_rates_date', as_type=date)
|
||||
rates_dict = gs.settings.get('ecb_rates_dict', as_type=dict)
|
||||
convert = (
|
||||
rates_date and rates_dict and
|
||||
rates_date > (now() - timedelta(days=7)).date() and
|
||||
invoice.event.currency in rates_dict and
|
||||
invoice.foreign_currency_display in rates_dict
|
||||
)
|
||||
if convert:
|
||||
invoice.foreign_currency_rate = (
|
||||
Decimal(rates_dict[invoice.foreign_currency_display])
|
||||
/ Decimal(rates_dict[invoice.event.currency])
|
||||
).quantize(Decimal('0.0001'), ROUND_HALF_UP)
|
||||
invoice.foreign_currency_rate_date = rates_date
|
||||
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
ia = None
|
||||
invoice.invoice_to = ""
|
||||
|
||||
invoice.file = None
|
||||
@@ -52,10 +91,13 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
invoice.lines.all().delete()
|
||||
|
||||
positions = list(
|
||||
invoice.order.positions.select_related('addon_to', 'item', 'variation').annotate(
|
||||
invoice.order.positions.select_related('addon_to', 'item', 'tax_rule', 'variation').annotate(
|
||||
addon_c=Count('addons')
|
||||
)
|
||||
)
|
||||
|
||||
reverse_charge = False
|
||||
|
||||
positions.sort(key=lambda p: p.sort_key)
|
||||
for p in positions:
|
||||
if not invoice.event.settings.invoice_include_free and p.price == Decimal('0.00') and not p.addon_c:
|
||||
@@ -69,15 +111,29 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
InvoiceLine.objects.create(
|
||||
invoice=invoice, description=desc,
|
||||
gross_value=p.price, tax_value=p.tax_value,
|
||||
tax_rate=p.tax_rate
|
||||
tax_rate=p.tax_rate, tax_name=p.tax_rule.name if p.tax_rule else ''
|
||||
)
|
||||
|
||||
if p.tax_rule and p.tax_rule.is_reverse_charge(ia) and p.price and not p.tax_value:
|
||||
reverse_charge = True
|
||||
|
||||
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."
|
||||
)
|
||||
invoice.save()
|
||||
|
||||
if invoice.order.payment_fee:
|
||||
InvoiceLine.objects.create(
|
||||
invoice=invoice,
|
||||
description=_('Payment via {method}').format(method=str(payment_provider.verbose_name)),
|
||||
gross_value=invoice.order.payment_fee, tax_value=invoice.order.payment_fee_tax_value,
|
||||
tax_rate=invoice.order.payment_fee_tax_rate
|
||||
tax_rate=invoice.order.payment_fee_tax_rate,
|
||||
tax_name=invoice.order.payment_fee_tax_rule.name if invoice.order.payment_fee_tax_rule else ''
|
||||
)
|
||||
|
||||
return invoice
|
||||
@@ -200,3 +256,20 @@ def build_preview_invoice_pdf(event):
|
||||
tax_rate=19
|
||||
)
|
||||
return event.invoice_renderer.generate(invoice)
|
||||
|
||||
|
||||
@receiver(signal=periodic_task)
|
||||
def fetch_ecb_rates(sender, **kwargs):
|
||||
if not settings.FETCH_ECB_RATES:
|
||||
return
|
||||
|
||||
gs = GlobalSettingsObject()
|
||||
if gs.settings.ecb_rates_date == now().strftime("%Y-%m-%d"):
|
||||
return
|
||||
|
||||
try:
|
||||
date, rates = vat_moss.exchange_rates.fetch()
|
||||
gs.settings.ecb_rates_date = date
|
||||
gs.settings.ecb_rates_dict = json.dumps(rates, cls=DjangoJSONEncoder)
|
||||
except urllib.error.URLError:
|
||||
logger.exception('Could not retrieve rates from ECB')
|
||||
|
||||
Reference in New Issue
Block a user