From 3eb4e84db43fbda035e92749d20ef98ee8aded8b Mon Sep 17 00:00:00 2001 From: Tobias Kunze Date: Wed, 7 Sep 2016 00:33:27 +0200 Subject: [PATCH 1/4] Allow payment providers to supply invoice content --- .../0037_invoice_payment_provider_text.py | 20 ++++++++++ src/pretix/base/models/invoices.py | 3 ++ src/pretix/base/payment.py | 16 ++++++++ src/pretix/base/services/invoices.py | 40 +++++++++++-------- 4 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 src/pretix/base/migrations/0037_invoice_payment_provider_text.py diff --git a/src/pretix/base/migrations/0037_invoice_payment_provider_text.py b/src/pretix/base/migrations/0037_invoice_payment_provider_text.py new file mode 100644 index 0000000000..9659a910ec --- /dev/null +++ b/src/pretix/base/migrations/0037_invoice_payment_provider_text.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.9 on 2016-09-06 21:31 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0036_auto_20160902_0755'), + ] + + operations = [ + migrations.AddField( + model_name='invoice', + name='payment_provider_text', + field=models.TextField(blank=True), + ), + ] diff --git a/src/pretix/base/models/invoices.py b/src/pretix/base/models/invoices.py index ec73130b77..b65b688f22 100644 --- a/src/pretix/base/models/invoices.py +++ b/src/pretix/base/models/invoices.py @@ -43,6 +43,8 @@ class Invoice(models.Model): :type introductory_text: str :param additional_text: Additional text for the invoice :type additional_text: str + :param payment_provider_text: A payment provider specific text + :type payment_provider_text: str :param footer_text: A footer text, displayed smaller and centered on every page :type footer_text: str :param file: The filename of the rendered invoice @@ -59,6 +61,7 @@ class Invoice(models.Model): locale = models.CharField(max_length=50, default='en') introductory_text = models.TextField(blank=True) additional_text = models.TextField(blank=True) + payment_provider_text = models.TextField(blank=True) footer_text = models.TextField(blank=True) file = models.FileField(null=True, blank=True, upload_to=invoice_filename) diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py index 3b8f636969..5abdfdae09 100644 --- a/src/pretix/base/payment.py +++ b/src/pretix/base/payment.py @@ -12,6 +12,7 @@ from django.template.loader import get_template from django.utils.translation import ugettext_lazy as _ from pretix.base.decimal import round_decimal +from pretix.base.i18n import I18nFormField, I18nTextarea, LazyI18nString from pretix.base.models import CartPosition, Event, Order, Quota from pretix.base.settings import SettingsSandbox from pretix.base.signals import register_payment_providers @@ -134,6 +135,13 @@ class BasePaymentProvider: 'above!'), required=False )), + ('_invoice_text', + I18nFormField( + label=_('Text on invoices'), + help_text=_('Will be printed just below the payment figures and above the closing text on invoices.'), + required=False, + widget=I18nTextarea, + )), ]) def settings_content_render(self, request: HttpRequest) -> str: @@ -144,6 +152,14 @@ class BasePaymentProvider: """ pass + def render_invoice_text(self, order: Order) -> str: + """ + This is called when an invoice for an order with this payment provider is generated. + The default implementation returns the content of the _invoice_text configuration + variable (an I18nString). + """ + return self.settings.get('_invoice_text', as_type=LazyI18nString) + @property def payment_form_fields(self) -> dict: """ diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py index c9dde9c6c1..4cd43972d9 100644 --- a/src/pretix/base/services/invoices.py +++ b/src/pretix/base/services/invoices.py @@ -33,6 +33,7 @@ def generate_cancellation(invoice: Invoice): cancellation.date = date.today() cancellation.refers = invoice cancellation.invoice_no = None + cancellation.payment_provider_text = '' cancellation.save() for line in invoice.lines.all(): line.pk = None @@ -48,15 +49,24 @@ def generate_cancellation(invoice: Invoice): @transaction.atomic def regenerate_invoice(invoice: Invoice): with language(invoice.locale): + responses = register_payment_providers.send(invoice.event) + for receiver, response in responses: + provider = response(invoice.event) + if provider.identifier == invoice.order.payment_provider: + payment_provider = provider + break + invoice.invoice_from = invoice.event.settings.get('invoice_address_from') introductory = invoice.event.settings.get('invoice_introductory_text', as_type=LazyI18nString) additional = invoice.event.settings.get('invoice_additional_text', as_type=LazyI18nString) footer = invoice.event.settings.get('invoice_footer_text', as_type=LazyI18nString) + payment = payment_provider.render_invoice_text(invoice.order) invoice.introductory_text = str(introductory).replace('\n', '
') - invoice.additional_text = str(additional).replace('\n', '
') invoice.footer_text = str(footer) + invoice.payment_provider_text = str(payment).replace('\n', '
') try: addr_template = pgettext("invoice", """{i.company} @@ -73,13 +83,6 @@ def regenerate_invoice(invoice: Invoice): invoice.file = None invoice.save() - responses = register_payment_providers.send(invoice.event) - for receiver, response in responses: - provider = response(invoice.event) - if provider.identifier == invoice.order.payment_provider: - payment_provider = provider - break - invoice.lines.all().delete() for p in invoice.order.positions.all(): desc = str(p.item.name) @@ -110,16 +113,25 @@ def generate_invoice(order: Order): locale = order.locale with language(locale): + responses = register_payment_providers.send(order.event) + for receiver, response in responses: + provider = response(order.event) + if provider.identifier == order.payment_provider: + payment_provider = provider + break + i = Invoice(order=order, event=order.event) i.invoice_from = order.event.settings.get('invoice_address_from') introductory = i.event.settings.get('invoice_introductory_text', as_type=LazyI18nString) additional = i.event.settings.get('invoice_additional_text', as_type=LazyI18nString) footer = i.event.settings.get('invoice_footer_text', as_type=LazyI18nString) + payment = payment_provider.render_invoice_text(i.order) i.introductory_text = str(introductory).replace('\n', '
') - i.additional_text = str(additional).replace('\n', '
') i.footer_text = str(footer) + i.payment_provider_text = str(payment).replace('\n', '
') try: addr_template = pgettext("invoice", """{i.company} @@ -137,13 +149,6 @@ def generate_invoice(order: Order): i.locale = locale i.save() - responses = register_payment_providers.send(order.event) - for receiver, response in responses: - provider = response(order.event) - if provider.identifier == order.payment_provider: - payment_provider = provider - break - for p in order.positions.all(): desc = str(p.item.name) if p.variation: @@ -362,6 +367,9 @@ def _invoice_generate_german(invoice, f): story.append(Spacer(1, 15 * mm)) + if invoice.payment_provider_text: + story.append(Paragraph(invoice.payment_provider_text, styles['Normal'])) + if invoice.additional_text: story.append(Paragraph(invoice.additional_text, styles['Normal'])) story.append(Spacer(1, 15 * mm)) From 65db55f21bba348bc3aeaca6f7ad4937eb2bcd04 Mon Sep 17 00:00:00 2001 From: Tobias Kunze Date: Wed, 7 Sep 2016 00:50:10 +0200 Subject: [PATCH 2/4] Extract common code of generate_invoice and regenerate_invoice Only change is that a regenerated invoice's date is updated to the current date, which would seem to be better to me, anyways. --- src/pretix/base/services/invoices.py | 75 ++++++---------------------- 1 file changed, 14 insertions(+), 61 deletions(-) diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py index 4cd43972d9..d5b848e7cb 100644 --- a/src/pretix/base/services/invoices.py +++ b/src/pretix/base/services/invoices.py @@ -47,8 +47,8 @@ def generate_cancellation(invoice: Invoice): @transaction.atomic -def regenerate_invoice(invoice: Invoice): - with language(invoice.locale): +def build_invoice(invoice, locale): + with language(locale): responses = register_payment_providers.send(invoice.event) for receiver, response in responses: provider = response(invoice.event) @@ -81,9 +81,11 @@ def regenerate_invoice(invoice: Invoice): invoice.invoice_to = "" invoice.file = None + invoice.date = date.today() + invoice.locale = locale invoice.save() - invoice.lines.all().delete() + for p in invoice.order.positions.all(): desc = str(p.item.name) if p.variation: @@ -101,73 +103,24 @@ def regenerate_invoice(invoice: Invoice): tax_rate=invoice.order.payment_fee_tax_rate ) - invoice_pdf(invoice.pk) + return invoice + + +def regenerate_invoice(invoice: Invoice): + invoice = build_invoice(invoice, invoice.locale) + invoice_pdf(invoice.pk) return invoice -@transaction.atomic def generate_invoice(order: Order): locale = order.event.settings.get('invoice_language') if locale: if locale == '__user__': locale = order.locale - with language(locale): - responses = register_payment_providers.send(order.event) - for receiver, response in responses: - provider = response(order.event) - if provider.identifier == order.payment_provider: - payment_provider = provider - break - - i = Invoice(order=order, event=order.event) - i.invoice_from = order.event.settings.get('invoice_address_from') - - introductory = i.event.settings.get('invoice_introductory_text', as_type=LazyI18nString) - additional = i.event.settings.get('invoice_additional_text', as_type=LazyI18nString) - footer = i.event.settings.get('invoice_footer_text', as_type=LazyI18nString) - payment = payment_provider.render_invoice_text(i.order) - - i.introductory_text = str(introductory).replace('\n', '
') - i.additional_text = str(additional).replace('\n', '
') - i.footer_text = str(footer) - i.payment_provider_text = str(payment).replace('\n', '
') - - try: - addr_template = pgettext("invoice", """{i.company} -{i.name} -{i.street} -{i.zipcode} {i.city} -{i.country}""") - i.invoice_to = addr_template.format(i=order.invoice_address).strip() - if order.invoice_address.vat_id: - i.invoice_to += "\n" + pgettext("invoice", "VAT-ID: %s") % order.invoice_address.vat_id - except InvoiceAddress.DoesNotExist: - i.invoice_to = "" - - i.date = date.today() - i.locale = locale - i.save() - - for p in order.positions.all(): - desc = str(p.item.name) - if p.variation: - desc += " - " + str(p.variation.value) - InvoiceLine.objects.create( - invoice=i, description=desc, - gross_value=p.price, tax_value=p.tax_value, - tax_rate=p.tax_rate - ) - - if order.payment_fee: - InvoiceLine.objects.create( - invoice=i, description=_('Payment via {method}').format(method=str(payment_provider.verbose_name)), - gross_value=order.payment_fee, tax_value=order.payment_fee_tax_value, - tax_rate=order.payment_fee_tax_rate - ) - - invoice_pdf(i.pk) - return i + invoice = build_invoice(Invoice(order=order, event=order.event), locale=locale) + invoice_pdf(invoice.pk) + return invoice def _invoice_get_stylesheet(): From 988dfc32a38e43b6d2e28fab6692a0ffef4db91d Mon Sep 17 00:00:00 2001 From: Tobias Kunze Date: Wed, 7 Sep 2016 15:03:06 +0200 Subject: [PATCH 3/4] Use @transaction.atomic instead of @transaction.atomic() --- doc/development/implementation/logging.rst | 4 ++-- src/pretix/base/services/locking.py | 2 +- src/pretix/base/services/orders.py | 2 +- src/pretix/control/views/event.py | 14 ++++++------- src/pretix/control/views/item.py | 24 +++++++++++----------- src/pretix/control/views/vouchers.py | 8 ++++---- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/doc/development/implementation/logging.rst b/doc/development/implementation/logging.rst index f68efd9471..5c9c8a3bbd 100644 --- a/doc/development/implementation/logging.rst +++ b/doc/development/implementation/logging.rst @@ -29,7 +29,7 @@ Logging form actions A very common use case is to log the changes to a model that have been done in a ``ModelForm``. In this case, we generally use a custom ``form_valid`` method on our ``FormView`` that looks like this:: - @transaction.atomic() + @transaction.atomic def form_valid(self, form): if form.has_changed(): self.request.event.log_action('pretix.event.changed', user=self.request.user, data={ @@ -40,7 +40,7 @@ we generally use a custom ``form_valid`` method on our ``FormView`` that looks l It gets a little bit more complicated if your form allows file uploads:: - @transaction.atomic() + @transaction.atomic def form_valid(self, form): if form.has_changed(): self.request.event.log_action( diff --git a/src/pretix/base/services/locking.py b/src/pretix/base/services/locking.py index ebdf5ca5a3..565df6ed23 100644 --- a/src/pretix/base/services/locking.py +++ b/src/pretix/base/services/locking.py @@ -80,7 +80,7 @@ def lock_event_db(event): raise EventLock.LockTimeoutException() -@transaction.atomic() +@transaction.atomic def release_event_db(event): if not hasattr(event, '_lock') or not event._lock: raise EventLock.LockReleaseException('Lock is not owned by this thread') diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index a1ec0d91fa..cdb2ca0731 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -258,7 +258,7 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio raise OrderError(err) -@transaction.atomic() +@transaction.atomic def _create_order(event: Event, email: str, positions: List[CartPosition], now_dt: datetime, payment_provider: BasePaymentProvider, locale: str=None): total = sum([c.price for c in positions]) diff --git a/src/pretix/control/views/event.py b/src/pretix/control/views/event.py index 6842d6f174..c621648c90 100644 --- a/src/pretix/control/views/event.py +++ b/src/pretix/control/views/event.py @@ -57,7 +57,7 @@ class EventUpdate(EventPermissionRequiredMixin, UpdateView): context['sform'] = self.sform return context - @transaction.atomic() + @transaction.atomic def form_valid(self, form): self.sform.save() if self.sform.has_changed(): @@ -187,7 +187,7 @@ class PaymentSettings(EventPermissionRequiredMixin, TemplateView, SingleObjectMi context['providers'] = self.provider_forms return self.render_to_response(context) - @transaction.atomic() + @transaction.atomic def post(self, request, *args, **kwargs): self.object = self.get_object() success = self.sform.is_valid() @@ -234,7 +234,7 @@ class EventSettingsFormView(EventPermissionRequiredMixin, FormView): kwargs['obj'] = self.request.event return kwargs - @transaction.atomic() + @transaction.atomic def post(self, request, *args, **kwargs): form = self.get_form() if form.is_valid(): @@ -279,7 +279,7 @@ class DisplaySettings(EventSettingsFormView): 'event': self.request.event.slug }) - @transaction.atomic() + @transaction.atomic def post(self, request, *args, **kwargs): form = self.get_form() if form.is_valid(): @@ -314,7 +314,7 @@ class MailSettings(EventSettingsFormView): 'event': self.request.event.slug }) - @transaction.atomic() + @transaction.atomic def post(self, request, *args, **kwargs): form = self.get_form() if form.is_valid(): @@ -374,7 +374,7 @@ class TicketSettings(EventPermissionRequiredMixin, FormView): form.prepare_fields() return form - @transaction.atomic() + @transaction.atomic def post(self, request, *args, **kwargs): success = True for provider in self.provider_forms: @@ -470,7 +470,7 @@ class EventPermissions(EventPermissionRequiredMixin, TemplateView): ctx['add_form'] = self.add_form return ctx - @transaction.atomic() + @transaction.atomic def post(self, *args, **kwargs): if self.formset.is_valid() and self.add_form.is_valid(): if self.add_form.has_changed(): diff --git a/src/pretix/control/views/item.py b/src/pretix/control/views/item.py index 3a5d278da2..d817c7e6f2 100644 --- a/src/pretix/control/views/item.py +++ b/src/pretix/control/views/item.py @@ -99,7 +99,7 @@ class CategoryDelete(EventPermissionRequiredMixin, DeleteView): except ItemCategory.DoesNotExist: raise Http404(_("The requested product category does not exist.")) - @transaction.atomic() + @transaction.atomic def delete(self, request, *args, **kwargs): self.object = self.get_object() for item in self.object.items.all(): @@ -134,7 +134,7 @@ class CategoryUpdate(EventPermissionRequiredMixin, UpdateView): except ItemCategory.DoesNotExist: raise Http404(_("The requested product category does not exist.")) - @transaction.atomic() + @transaction.atomic def form_valid(self, form): messages.success(self.request, _('Your changes have been saved.')) if form.has_changed(): @@ -165,7 +165,7 @@ class CategoryCreate(EventPermissionRequiredMixin, CreateView): 'event': self.request.event.slug, }) - @transaction.atomic() + @transaction.atomic def form_valid(self, form): form.instance.event = self.request.event messages.success(self.request, _('The new category has been created.')) @@ -299,7 +299,7 @@ class QuestionDelete(EventPermissionRequiredMixin, DeleteView): context['dependent'] = list(self.get_object().items.all()) return context - @transaction.atomic() + @transaction.atomic def delete(self, request, *args, **kwargs): self.object = self.get_object() success_url = self.get_success_url() @@ -390,7 +390,7 @@ class QuestionUpdate(EventPermissionRequiredMixin, QuestionMixin, UpdateView): except Question.DoesNotExist: raise Http404(_("The requested question does not exist.")) - @transaction.atomic() + @transaction.atomic def form_valid(self, form): if form.cleaned_data.get('type') in ('M', 'C'): if not self.save_formset(self.get_object()): @@ -433,7 +433,7 @@ class QuestionCreate(EventPermissionRequiredMixin, QuestionMixin, CreateView): def get_object(self, **kwargs): return None - @transaction.atomic() + @transaction.atomic def form_valid(self, form): if form.cleaned_data.get('type') in ('M', 'C'): if not self.formset.is_valid(): @@ -480,7 +480,7 @@ class QuotaEditorMixin: item.field = self.get_form(QuotaForm)['item_%s' % item.id] return context - @transaction.atomic() + @transaction.atomic def form_valid(self, form): res = super().form_valid(form) items = self.object.items.all() @@ -516,7 +516,7 @@ class QuotaCreate(EventPermissionRequiredMixin, QuotaEditorMixin, CreateView): 'event': self.request.event.slug, }) - @transaction.atomic() + @transaction.atomic def form_valid(self, form): form.instance.event = self.request.event messages.success(self.request, _('The new quota has been created.')) @@ -540,7 +540,7 @@ class QuotaUpdate(EventPermissionRequiredMixin, QuotaEditorMixin, UpdateView): except Quota.DoesNotExist: raise Http404(_("The requested quota does not exist.")) - @transaction.atomic() + @transaction.atomic def form_valid(self, form): messages.success(self.request, _('Your changes have been saved.')) if form.has_changed(): @@ -577,7 +577,7 @@ class QuotaDelete(EventPermissionRequiredMixin, DeleteView): context['dependent'] = list(self.get_object().items.all()) return context - @transaction.atomic() + @transaction.atomic def delete(self, request, *args, **kwargs): self.object = self.get_object() success_url = self.get_success_url() @@ -621,7 +621,7 @@ class ItemCreate(EventPermissionRequiredMixin, CreateView): 'item': self.object.id, }) - @transaction.atomic() + @transaction.atomic def form_valid(self, form): messages.success(self.request, _('Your changes have been saved.')) ret = super().form_valid(form) @@ -655,7 +655,7 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, UpdateVie 'item': self.get_object().id, }) - @transaction.atomic() + @transaction.atomic def form_valid(self, form): messages.success(self.request, _('Your changes have been saved.')) if form.has_changed(): diff --git a/src/pretix/control/views/vouchers.py b/src/pretix/control/views/vouchers.py index 66a3e3789d..2f1f502075 100644 --- a/src/pretix/control/views/vouchers.py +++ b/src/pretix/control/views/vouchers.py @@ -126,7 +126,7 @@ class VoucherDelete(EventPermissionRequiredMixin, DeleteView): return HttpResponseRedirect(self.get_success_url()) return super().get(request, *args, **kwargs) - @transaction.atomic() + @transaction.atomic def delete(self, request, *args, **kwargs): self.object = self.get_object() success_url = self.get_success_url() @@ -168,7 +168,7 @@ class VoucherUpdate(EventPermissionRequiredMixin, UpdateView): except Voucher.DoesNotExist: raise Http404(_("The requested voucher does not exist.")) - @transaction.atomic() + @transaction.atomic def form_valid(self, form): messages.success(self.request, _('Your changes have been saved.')) if form.has_changed(): @@ -210,7 +210,7 @@ class VoucherCreate(EventPermissionRequiredMixin, CreateView): kwargs['instance'] = Voucher(event=self.request.event) return kwargs - @transaction.atomic() + @transaction.atomic def form_valid(self, form): form.instance.event = self.request.event messages.success(self.request, _('The new voucher has been created.')) @@ -241,7 +241,7 @@ class VoucherBulkCreate(EventPermissionRequiredMixin, CreateView): kwargs['instance'] = Voucher(event=self.request.event) return kwargs - @transaction.atomic() + @transaction.atomic def form_valid(self, form): for o in form.save(self.request.event): o.log_action('pretix.voucher.added', data=form.cleaned_data, user=self.request.user) From 55120fc4ea57900a6c666ad9da02e91dfb2dbfbf Mon Sep 17 00:00:00 2001 From: Tobias Kunze Date: Wed, 7 Sep 2016 15:52:15 +0200 Subject: [PATCH 4/4] Proper regeneration behavior for cancellations --- doc/development/api/payment.rst | 2 + src/pretix/base/payment.py | 4 +- src/pretix/base/services/invoices.py | 66 +++++++++++++++++----------- 3 files changed, 44 insertions(+), 28 deletions(-) diff --git a/doc/development/api/payment.rst b/doc/development/api/payment.rst index e9b95de938..870d58ebe2 100644 --- a/doc/development/api/payment.rst +++ b/doc/development/api/payment.rst @@ -62,6 +62,8 @@ The provider class .. automethod:: settings_content_render + .. automethod:: render_invoice_text + .. automethod:: payment_form_render .. automethod:: payment_form diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py index 5abdfdae09..e0ded320e5 100644 --- a/src/pretix/base/payment.py +++ b/src/pretix/base/payment.py @@ -156,9 +156,9 @@ class BasePaymentProvider: """ This is called when an invoice for an order with this payment provider is generated. The default implementation returns the content of the _invoice_text configuration - variable (an I18nString). + variable (an I18nString), or an empty string if unconfigured. """ - return self.settings.get('_invoice_text', as_type=LazyI18nString) + return self.settings.get('_invoice_text', as_type=LazyI18nString, default='') @property def payment_form_fields(self) -> dict: diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py index d5b848e7cb..9099058f5b 100644 --- a/src/pretix/base/services/invoices.py +++ b/src/pretix/base/services/invoices.py @@ -26,29 +26,9 @@ from pretix.base.models import Invoice, InvoiceAddress, InvoiceLine, Order from pretix.base.signals import register_payment_providers -def generate_cancellation(invoice: Invoice): - cancellation = copy.copy(invoice) - cancellation.pk = None - cancellation.is_cancellation = True - cancellation.date = date.today() - cancellation.refers = invoice - cancellation.invoice_no = None - cancellation.payment_provider_text = '' - cancellation.save() - for line in invoice.lines.all(): - line.pk = None - line.invoice = cancellation - line.gross_value *= -1 - line.tax_value *= -1 - line.save() - - invoice_pdf(cancellation.pk) - return cancellation - - @transaction.atomic -def build_invoice(invoice, locale): - with language(locale): +def build_invoice(invoice: Invoice) -> Invoice: + with language(invoice.locale): responses = register_payment_providers.send(invoice.event) for receiver, response in responses: provider = response(invoice.event) @@ -81,8 +61,6 @@ def build_invoice(invoice, locale): invoice.invoice_to = "" invoice.file = None - invoice.date = date.today() - invoice.locale = locale invoice.save() invoice.lines.all().delete() @@ -106,8 +84,38 @@ def build_invoice(invoice, locale): return invoice +def build_cancellation(invoice: Invoice): + invoice.lines.all().delete() + + for line in invoice.refers.lines.all(): + line.pk = None + line.invoice = invoice + line.gross_value *= -1 + line.tax_value *= -1 + line.save() + return invoice + + +def generate_cancellation(invoice: Invoice): + cancellation = copy.copy(invoice) + cancellation.pk = None + cancellation.invoice_no = None + cancellation.refers = invoice + cancellation.is_cancellation = True + cancellation.date = date.today() + cancellation.payment_provider_text = '' + cancellation.save() + + cancellation = build_cancellation(cancellation) + invoice_pdf(cancellation.pk) + return cancellation + + def regenerate_invoice(invoice: Invoice): - invoice = build_invoice(invoice, invoice.locale) + if invoice.is_cancellation: + invoice = build_cancellation(invoice) + else: + invoice = build_invoice(invoice) invoice_pdf(invoice.pk) return invoice @@ -118,7 +126,13 @@ def generate_invoice(order: Order): if locale == '__user__': locale = order.locale - invoice = build_invoice(Invoice(order=order, event=order.event), locale=locale) + invoice = Invoice( + order=order, + event=order.event, + date=date.today(), + locale=locale + ) + invoice = build_invoice(invoice) invoice_pdf(invoice.pk) return invoice