mirror of
https://github.com/pretix/pretix.git
synced 2026-05-06 15:24:02 +00:00
Allow not to ask for invoice addresses on free orders
This commit is contained in:
@@ -49,6 +49,10 @@ DEFAULTS = {
|
||||
'default': 'True',
|
||||
'type': bool,
|
||||
},
|
||||
'invoice_address_not_asked_free': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
},
|
||||
'invoice_name_required': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from decimal import Decimal
|
||||
|
||||
from django import forms
|
||||
from django.core.files.uploadedfile import UploadedFile
|
||||
@@ -186,25 +187,35 @@ class OrderQuestionsViewMixin(BaseQuestionsViewMixin):
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
return InvoiceAddress(order=self.order)
|
||||
|
||||
@cached_property
|
||||
def address_asked(self):
|
||||
return self.request.event.settings.invoice_address_asked and (
|
||||
self.order.total != Decimal('0.00') or not self.request.event.settings.invoice_address_not_asked_free
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def invoice_form(self):
|
||||
if not self.request.event.settings.invoice_address_asked and self.request.event.settings.invoice_name_required:
|
||||
if not self.address_asked and self.request.event.settings.invoice_name_required:
|
||||
return self.invoice_name_form_class(
|
||||
data=self.request.POST if self.request.method == "POST" else None,
|
||||
event=self.request.event,
|
||||
instance=self.invoice_address, validate_vat_id=False,
|
||||
all_optional=self.all_optional
|
||||
)
|
||||
return self.invoice_form_class(
|
||||
data=self.request.POST if self.request.method == "POST" else None,
|
||||
event=self.request.event,
|
||||
instance=self.invoice_address, validate_vat_id=False,
|
||||
all_optional=self.all_optional,
|
||||
)
|
||||
if self.address_asked:
|
||||
return self.invoice_form_class(
|
||||
data=self.request.POST if self.request.method == "POST" else None,
|
||||
event=self.request.event,
|
||||
instance=self.invoice_address, validate_vat_id=False,
|
||||
all_optional=self.all_optional,
|
||||
)
|
||||
else:
|
||||
return forms.Form(data=self.request.POST if self.request.method == "POST" else None)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['order'] = self.order
|
||||
ctx['formgroups'] = self.formdict.items()
|
||||
ctx['invoice_form'] = self.invoice_form
|
||||
ctx['invoice_address_asked'] = self.address_asked
|
||||
return ctx
|
||||
|
||||
@@ -621,6 +621,10 @@ class InvoiceSettingsForm(SettingsForm):
|
||||
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_invoice_address_asked'}),
|
||||
required=False
|
||||
)
|
||||
invoice_address_not_asked_free = forms.BooleanField(
|
||||
label=_('Do not ask for invoice address if an order is free'),
|
||||
required=False
|
||||
)
|
||||
invoice_include_free = forms.BooleanField(
|
||||
label=_("Show free products on invoices"),
|
||||
help_text=_("Note that invoices will never be generated for orders that contain only free "
|
||||
|
||||
@@ -43,6 +43,7 @@ class QuestionForm(I18nModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['items'].queryset = self.instance.event.items.all()
|
||||
self.fields['items'].required = True
|
||||
self.fields['dependency_question'].queryset = self.instance.event.questions.filter(
|
||||
type__in=(Question.TYPE_BOOLEAN, Question.TYPE_CHOICE, Question.TYPE_CHOICE_MULTIPLE)
|
||||
)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
{% bootstrap_field form.invoice_address_company_required layout="control" %}
|
||||
{% bootstrap_field form.invoice_address_vatid layout="control" %}
|
||||
{% bootstrap_field form.invoice_address_beneficiary layout="control" %}
|
||||
{% bootstrap_field form.invoice_address_not_asked_free layout="control" %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Your invoice details" %}</legend>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<form method="post" class="form-horizontal" href="" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="panel-group" id="questions_accordion">
|
||||
{% if request.event.settings.invoice_address_asked or order.invoice_address or request.event.settings.invoice_name_required %}
|
||||
{% if invoice_address_asked or order.invoice_address or request.event.settings.invoice_name_required %}
|
||||
<details class="panel panel-default" open>
|
||||
<summary class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
|
||||
@@ -1305,7 +1305,8 @@ class OrderModifyInformation(OrderQuestionsViewMixin, OrderView):
|
||||
messages.error(self.request,
|
||||
_("We had difficulties processing your input. Please review the errors below."))
|
||||
return self.get(request, *args, **kwargs)
|
||||
self.invoice_form.save()
|
||||
if hasattr(self.invoice_form, 'save'):
|
||||
self.invoice_form.save()
|
||||
self.order.log_action('pretix.event.order.modified', {
|
||||
'invoice_data': self.invoice_form.cleaned_data,
|
||||
'data': [{
|
||||
|
||||
@@ -28,7 +28,9 @@ from pretix.presale.signals import (
|
||||
checkout_all_optional, checkout_confirm_messages, checkout_flow_steps,
|
||||
contact_form_fields, order_meta_from_request, question_form_fields,
|
||||
)
|
||||
from pretix.presale.views import CartMixin, get_cart, get_cart_total
|
||||
from pretix.presale.views import (
|
||||
CartMixin, get_cart, get_cart_is_free, get_cart_total,
|
||||
)
|
||||
from pretix.presale.views.cart import (
|
||||
cart_session, create_empty_cart_id, get_or_create_cart_id,
|
||||
)
|
||||
@@ -351,7 +353,7 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
||||
'city': self.cart_session.get('widget_data', {}).get('invoice-address-city', ''),
|
||||
'country': self.cart_session.get('widget_data', {}).get('invoice-address-country', ''),
|
||||
}
|
||||
if not self.request.event.settings.invoice_address_asked and self.request.event.settings.invoice_name_required:
|
||||
if not self.address_asked and self.request.event.settings.invoice_name_required:
|
||||
return InvoiceNameForm(data=self.request.POST if self.request.method == "POST" else None,
|
||||
event=self.request.event,
|
||||
request=self.request,
|
||||
@@ -365,10 +367,17 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
||||
instance=self.invoice_address,
|
||||
validate_vat_id=self.eu_reverse_charge_relevant, all_optional=self.all_optional)
|
||||
|
||||
@cached_property
|
||||
def address_asked(self):
|
||||
return (
|
||||
self.request.event.settings.invoice_address_asked
|
||||
and (not self.request.event.settings.invoice_address_not_asked_free or not get_cart_is_free(self.request))
|
||||
)
|
||||
|
||||
def post(self, request):
|
||||
self.request = request
|
||||
failed = not self.save() or not self.contact_form.is_valid()
|
||||
if request.event.settings.invoice_address_asked or self.request.event.settings.invoice_name_required:
|
||||
if self.address_asked or self.request.event.settings.invoice_name_required:
|
||||
failed = failed or not self.invoice_form.is_valid()
|
||||
if failed:
|
||||
messages.error(request,
|
||||
@@ -376,7 +385,7 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
||||
return self.render()
|
||||
self.cart_session['email'] = self.contact_form.cleaned_data['email']
|
||||
self.cart_session['contact_form_data'] = self.contact_form.cleaned_data
|
||||
if request.event.settings.invoice_address_asked or self.request.event.settings.invoice_name_required:
|
||||
if self.address_asked or self.request.event.settings.invoice_name_required:
|
||||
addr = self.invoice_form.save()
|
||||
self.cart_session['invoice_address'] = addr.pk
|
||||
|
||||
@@ -404,9 +413,11 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
||||
return False
|
||||
|
||||
if not self.all_optional:
|
||||
if request.event.settings.invoice_address_required and (not self.invoice_address or not self.invoice_address.street):
|
||||
messages.warning(request, _('Please enter your invoicing address.'))
|
||||
return False
|
||||
|
||||
if self.address_asked:
|
||||
if request.event.settings.invoice_address_required and (not self.invoice_address or not self.invoice_address.street):
|
||||
messages.warning(request, _('Please enter your invoicing address.'))
|
||||
return False
|
||||
|
||||
if request.event.settings.invoice_name_required and (not self.invoice_address or not self.invoice_address.name):
|
||||
messages.warning(request, _('Please enter your name.'))
|
||||
@@ -471,6 +482,7 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
||||
ctx['reverse_charge_relevant'] = self.eu_reverse_charge_relevant
|
||||
ctx['cart'] = self.get_cart()
|
||||
ctx['cart_session'] = self.cart_session
|
||||
ctx['invoice_address_asked'] = self.address_asked
|
||||
return ctx
|
||||
|
||||
|
||||
@@ -591,6 +603,13 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
|
||||
def is_completed(self, request, warn=False):
|
||||
pass
|
||||
|
||||
@cached_property
|
||||
def address_asked(self):
|
||||
return (
|
||||
self.request.event.settings.invoice_address_asked
|
||||
and (not self.request.event.settings.invoice_address_not_asked_free or not get_cart_is_free(self.request))
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['cart'] = self.get_cart(answers=True)
|
||||
@@ -601,6 +620,7 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
|
||||
ctx['addr'] = self.invoice_address
|
||||
ctx['confirm_messages'] = self.confirm_messages
|
||||
ctx['cart_session'] = self.cart_session
|
||||
ctx['invoice_address_asked'] = self.address_asked
|
||||
|
||||
email = self.cart_session.get('contact_form_data', {}).get('email')
|
||||
if email != settings.PRETIX_EMAIL_NONE_VALUE:
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
{% endif %}
|
||||
{% eventsignal event "pretix.presale.signals.checkout_confirm_page_content" request=request %}
|
||||
<div class="row">
|
||||
{% if request.event.settings.invoice_address_asked %}
|
||||
{% if invoice_address_asked %}
|
||||
<div class="col-md-6 col-xs-12">
|
||||
<div class="panel panel-primary panel-contact">
|
||||
<div class="panel-heading">
|
||||
@@ -111,7 +111,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="{% if request.event.settings.invoice_address_asked %}col-md-6{% endif %} col-xs-12">
|
||||
<div class="{% if invoice_address_asked %}col-md-6{% endif %} col-xs-12">
|
||||
<div class="panel panel-primary panel-contact">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right">
|
||||
@@ -125,7 +125,7 @@
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% if not event.settings.invoice_address_asked and event.settings.invoice_name_required %}
|
||||
{% if not asked and event.settings.invoice_name_required %}
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ addr.name }}</dd>
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
<div id="contact">
|
||||
<div class="panel-body">
|
||||
{% bootstrap_form contact_form layout="horizontal" %}
|
||||
{% if not event.settings.invoice_address_asked and event.settings.invoice_name_required %}
|
||||
{% if not invoice_address_asked and event.settings.invoice_name_required %}
|
||||
{% bootstrap_form invoice_form layout="horizontal" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
{% if event.settings.invoice_address_asked %}
|
||||
{% if invoice_address_asked %}
|
||||
<details class="panel panel-default" {% if event.settings.invoice_address_required or event.settings.invoice_name_required %}open{% endif %}>
|
||||
<summary class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
{% eventsignal event "pretix.presale.signals.order_info" order=order %}
|
||||
<div class="row">
|
||||
{% if invoices %}
|
||||
<div class="col-xs-12 {% if request.event.settings.invoice_address_asked or request.event.settings.invoice_name_required %}col-md-6{% endif %}">
|
||||
<div class="col-xs-12 {% if invoice_address_asked or request.event.settings.invoice_name_required %}col-md-6{% endif %}">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
@@ -180,7 +180,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% elif can_generate_invoice %}
|
||||
<div class="col-xs-12 {% if request.event.settings.invoice_address_asked or request.event.settings.invoice_name_required %}col-md-6{% endif %}">
|
||||
<div class="col-xs-12 {% if invoice_address_asked or request.event.settings.invoice_name_required %}col-md-6{% endif %}">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
@@ -199,7 +199,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if request.event.settings.invoice_address_asked or request.event.settings.invoice_name_required %}
|
||||
{% if invoice_address_asked or request.event.settings.invoice_name_required %}
|
||||
<div class="col-xs-12 {% if invoices or can_generate_invoice %}col-md-6{% endif %}">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
@@ -221,13 +221,13 @@
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
{% if request.event.settings.invoice_address_asked %}
|
||||
{% if invoice_address_asked %}
|
||||
<dt>{% trans "Company" %}</dt>
|
||||
<dd>{{ order.invoice_address.company }}</dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ order.invoice_address.name }}</dd>
|
||||
{% if request.event.settings.invoice_address_asked %}
|
||||
{% if invoice_address_asked %}
|
||||
<dt>{% trans "Address" %}</dt>
|
||||
<dd>{{ order.invoice_address.street|linebreaksbr }}</dd>
|
||||
<dt>{% trans "ZIP code and city" %}</dt>
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
<form class="form-horizontal" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="panel-group" id="questions_accordion">
|
||||
{% if event.settings.invoice_address_asked or event.settings.invoice_name_required %}
|
||||
{% if event.settings.invoice_address_asked %}
|
||||
{% if invoice_address_asked or event.settings.invoice_name_required %}
|
||||
{% if invoice_address_asked %}
|
||||
<div class="alert alert-info">
|
||||
{% blocktrans trimmed %}
|
||||
Modifying your invoice address will not automatically generate a new invoice.
|
||||
@@ -25,7 +25,7 @@
|
||||
<summary class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<strong>
|
||||
{% if request.event.settings.invoice_address_asked %}
|
||||
{% if invoice_address_asked %}
|
||||
{% trans "Invoice information" %}{% if not event.settings.invoice_address_required %}
|
||||
{% trans "(optional)" %}
|
||||
{% endif %}
|
||||
|
||||
@@ -205,6 +205,34 @@ def get_cart_total(request):
|
||||
return request._cart_total_cache
|
||||
|
||||
|
||||
def get_cart_invoice_address(request):
|
||||
from pretix.presale.views.cart import cart_session
|
||||
|
||||
if not hasattr(request, '_checkout_flow_invoice_address'):
|
||||
cs = cart_session(request)
|
||||
iapk = cs.get('invoice_address')
|
||||
if not iapk:
|
||||
request._checkout_flow_invoice_address = InvoiceAddress()
|
||||
else:
|
||||
try:
|
||||
request._checkout_flow_invoice_address = InvoiceAddress.objects.get(pk=iapk, order__isnull=True)
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
request._checkout_flow_invoice_address = InvoiceAddress()
|
||||
return request._checkout_flow_invoice_address
|
||||
|
||||
|
||||
def get_cart_is_free(request):
|
||||
from pretix.presale.views.cart import cart_session
|
||||
|
||||
if not hasattr(request, '_cart_free_cache'):
|
||||
cs = cart_session(request)
|
||||
ia = get_cart_invoice_address(request)
|
||||
total = get_cart_total(request)
|
||||
fees = get_fees(request.event, request, total, ia, cs.get('payment'))
|
||||
request._cart_free_cache = total + sum(f.value for f in fees) == Decimal('0.00')
|
||||
return request._cart_free_cache
|
||||
|
||||
|
||||
class EventViewMixin:
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
@@ -122,6 +122,9 @@ class OrderDetails(EventViewMixin, OrderDetailMixin, CartMixin, TemplateView):
|
||||
'secret': self.order.secret
|
||||
}
|
||||
)
|
||||
ctx['invoice_address_asked'] = self.request.event.settings.invoice_address_asked and (
|
||||
self.order.total != Decimal('0.00') or not self.request.event.settings.invoice_address_not_asked_free
|
||||
)
|
||||
|
||||
if self.order.status == Order.STATUS_PENDING:
|
||||
ctx['pending_sum'] = self.order.pending_sum
|
||||
@@ -526,7 +529,8 @@ class OrderModify(EventViewMixin, OrderDetailMixin, OrderQuestionsViewMixin, Tem
|
||||
messages.error(self.request,
|
||||
_("We had difficulties processing your input. Please review the errors below."))
|
||||
return self.get(request, *args, **kwargs)
|
||||
self.invoice_form.save()
|
||||
if hasattr(self.invoice_form, 'save'):
|
||||
self.invoice_form.save()
|
||||
self.order.log_action('pretix.event.order.modified', {
|
||||
'invoice_data': self.invoice_form.cleaned_data,
|
||||
'data': [{
|
||||
|
||||
Reference in New Issue
Block a user