forked from CGM_Public/pretix_original
@@ -9,22 +9,24 @@ from django.core.files import File
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import transaction
|
||||
from django.http import (
|
||||
HttpResponse, HttpResponseBadRequest, HttpResponseNotAllowed, JsonResponse,
|
||||
Http404, HttpResponse, HttpResponseBadRequest, HttpResponseNotAllowed,
|
||||
JsonResponse,
|
||||
)
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.utils import translation
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import FormView, ListView
|
||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||
from django.views.generic import DeleteView, FormView, ListView
|
||||
from django.views.generic.base import TemplateView, View
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from pytz import timezone
|
||||
|
||||
from pretix.base.models import (
|
||||
CachedTicket, Event, Item, ItemVariation, LogEntry, Order, RequiredAction,
|
||||
Voucher,
|
||||
CachedTicket, Event, Item, ItemVariation, LogEntry, Order, OrderPosition,
|
||||
RequiredAction, TaxRule, Voucher,
|
||||
)
|
||||
from pretix.base.services import tickets
|
||||
from pretix.base.services.invoices import build_preview_invoice_pdf
|
||||
@@ -32,13 +34,13 @@ from pretix.base.signals import event_live_issues, register_ticket_outputs
|
||||
from pretix.control.forms.event import (
|
||||
CommentForm, DisplaySettingsForm, EventSettingsForm, EventUpdateForm,
|
||||
InvoiceSettingsForm, MailSettingsForm, PaymentSettingsForm, ProviderForm,
|
||||
TicketSettingsForm,
|
||||
TaxRuleForm, TicketSettingsForm,
|
||||
)
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
from pretix.helpers.urls import build_absolute_uri
|
||||
from pretix.presale.style import regenerate_css
|
||||
|
||||
from . import UpdateView
|
||||
from . import CreateView, UpdateView
|
||||
from ..logdisplay import OVERVIEW_BLACKLIST
|
||||
|
||||
|
||||
@@ -787,3 +789,129 @@ class EventComment(EventPermissionRequiredMixin, View):
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'event': self.request.event.slug
|
||||
})
|
||||
|
||||
|
||||
class TaxList(EventPermissionRequiredMixin, ListView):
|
||||
model = TaxRule
|
||||
context_object_name = 'taxrules'
|
||||
paginate_by = 30
|
||||
template_name = 'pretixcontrol/event/tax_index.html'
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.event.tax_rules.all()
|
||||
|
||||
|
||||
class TaxCreate(EventPermissionRequiredMixin, CreateView):
|
||||
model = TaxRule
|
||||
form_class = TaxRuleForm
|
||||
template_name = 'pretixcontrol/event/tax_edit.html'
|
||||
permission = 'can_change_event_settings'
|
||||
context_object_name = 'taxrule'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:event.settings.tax', kwargs={
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'event': self.request.event.slug,
|
||||
})
|
||||
|
||||
def get_initial(self):
|
||||
return {
|
||||
'name': LazyI18nString.from_gettext(ugettext('VAT'))
|
||||
}
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
form.instance.event = self.request.event
|
||||
messages.success(self.request, _('The new tax rule has been created.'))
|
||||
ret = super().form_valid(form)
|
||||
form.instance.log_action('pretix.event.taxrule.added', user=self.request.user, data=dict(form.cleaned_data))
|
||||
return ret
|
||||
|
||||
def form_invalid(self, form):
|
||||
messages.error(self.request, _('We could not save your changes. See below for details.'))
|
||||
return super().form_invalid(form)
|
||||
|
||||
|
||||
class TaxUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
model = TaxRule
|
||||
form_class = TaxRuleForm
|
||||
template_name = 'pretixcontrol/event/tax_edit.html'
|
||||
permission = 'can_change_event_settings'
|
||||
context_object_name = 'rule'
|
||||
|
||||
def get_object(self, queryset=None) -> TaxRule:
|
||||
try:
|
||||
return self.request.event.tax_rules.get(
|
||||
id=self.kwargs['rule']
|
||||
)
|
||||
except TaxRule.DoesNotExist:
|
||||
raise Http404(_("The requested tax rule does not exist."))
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
if form.has_changed():
|
||||
self.object.log_action(
|
||||
'pretix.event.taxrule.changed', user=self.request.user, data={
|
||||
k: form.cleaned_data.get(k) for k in form.changed_data
|
||||
}
|
||||
)
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:event.settings.tax', kwargs={
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'event': self.request.event.slug,
|
||||
})
|
||||
|
||||
def form_invalid(self, form):
|
||||
messages.error(self.request, _('We could not save your changes. See below for details.'))
|
||||
return super().form_invalid(form)
|
||||
|
||||
|
||||
class TaxDelete(EventPermissionRequiredMixin, DeleteView):
|
||||
model = TaxRule
|
||||
template_name = 'pretixcontrol/event/tax_delete.html'
|
||||
permission = 'can_change_event_settings'
|
||||
context_object_name = 'taxrule'
|
||||
|
||||
def get_object(self, queryset=None) -> TaxRule:
|
||||
try:
|
||||
return self.request.event.tax_rules.get(
|
||||
id=self.kwargs['rule']
|
||||
)
|
||||
except TaxRule.DoesNotExist:
|
||||
raise Http404(_("The requested tax rule does not exist."))
|
||||
|
||||
@transaction.atomic
|
||||
def delete(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
success_url = self.get_success_url()
|
||||
if self.is_allowed():
|
||||
self.object.log_action(action='pretix.event.taxrule.deleted', user=request.user)
|
||||
self.object.delete()
|
||||
messages.success(self.request, _('The selected tax rule has been deleted.'))
|
||||
else:
|
||||
messages.error(self.request, _('The selected tax rule can not be deleted.'))
|
||||
return redirect(success_url)
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:event.settings.tax', kwargs={
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'event': self.request.event.slug,
|
||||
})
|
||||
|
||||
def is_allowed(self) -> bool:
|
||||
o = self.object
|
||||
return (
|
||||
not self.request.event.orders.filter(payment_fee_tax_rule=o).exists()
|
||||
and not OrderPosition.objects.filter(tax_rule=o, order__event=self.request.event).exists()
|
||||
and not self.request.event.items.filter(tax_rule=o).exists()
|
||||
and self.request.event.settings.tax_rate_default != o
|
||||
)
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
context['possible'] = self.is_allowed()
|
||||
return context
|
||||
|
||||
@@ -775,6 +775,13 @@ class ItemCreate(EventPermissionRequiredMixin, CreateView):
|
||||
'item': self.object.id,
|
||||
})
|
||||
|
||||
def get_initial(self):
|
||||
initial = super().get_initial()
|
||||
trs = list(self.request.event.tax_rules.all())
|
||||
if len(trs) == 1:
|
||||
initial['tax_rule'] = trs[0]
|
||||
return initial
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
|
||||
@@ -6,10 +6,11 @@ from django.http import JsonResponse
|
||||
from django.shortcuts import redirect
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||
from django.views import View
|
||||
from django.views.generic import ListView
|
||||
from formtools.wizard.views import SessionWizardView
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.base.models import Event, Team
|
||||
from pretix.control.forms.event import (
|
||||
@@ -118,6 +119,12 @@ class EventWizard(SessionWizardView):
|
||||
active=True
|
||||
)
|
||||
|
||||
if basics_data['tax_rate']:
|
||||
event.settings.tax_rate_default = event.tax_rules.create(
|
||||
name=LazyI18nString.from_gettext(ugettext('VAT')),
|
||||
rate=basics_data['tax_rate']
|
||||
)
|
||||
|
||||
logdata = {}
|
||||
for f in form_list:
|
||||
logdata.update({
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import logging
|
||||
import mimetypes
|
||||
import os
|
||||
from datetime import timedelta
|
||||
|
||||
import pytz
|
||||
import vat_moss.id
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse
|
||||
@@ -25,6 +27,7 @@ from pretix.base.models import (
|
||||
generate_position_secret, generate_secret,
|
||||
)
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.base.models.tax import EU_COUNTRIES
|
||||
from pretix.base.services.export import export
|
||||
from pretix.base.services.invoices import (
|
||||
generate_cancellation, generate_invoice, invoice_pdf, invoice_qualified,
|
||||
@@ -42,12 +45,15 @@ from pretix.control.forms.filter import EventOrderFilterForm
|
||||
from pretix.control.forms.orders import (
|
||||
CommentForm, ExporterForm, ExtendForm, OrderContactForm, OrderLocaleForm,
|
||||
OrderMailForm, OrderPositionAddForm, OrderPositionChangeForm,
|
||||
OtherOperationsForm,
|
||||
)
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
from pretix.helpers.safedownload import check_token
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
from pretix.presale.signals import question_form_fields
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OrderList(EventPermissionRequiredMixin, ListView):
|
||||
model = Order
|
||||
@@ -144,7 +150,7 @@ class OrderDetail(OrderView):
|
||||
cartpos = queryset.order_by(
|
||||
'item', 'variation'
|
||||
).select_related(
|
||||
'item', 'variation', 'addon_to'
|
||||
'item', 'variation', 'addon_to', 'tax_rule'
|
||||
).prefetch_related(
|
||||
'item__questions', 'answers', 'answers__question', 'checkins'
|
||||
).order_by('positionid')
|
||||
@@ -277,6 +283,54 @@ class OrderInvoiceCreate(OrderView):
|
||||
return HttpResponseNotAllowed(['POST'])
|
||||
|
||||
|
||||
class OrderCheckVATID(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
try:
|
||||
ia = self.order.invoice_address
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
messages.error(self.request, _('No VAT ID specified.'))
|
||||
return redirect(self.get_order_url())
|
||||
else:
|
||||
if not ia.vat_id:
|
||||
messages.error(self.request, _('No VAT ID specified.'))
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
if not ia.country:
|
||||
messages.error(self.request, _('No country specified.'))
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
if str(ia.country) not in EU_COUNTRIES:
|
||||
messages.error(self.request, _('VAT ID could not be checked since a non-EU country has been '
|
||||
'specified.'))
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
if ia.vat_id[:2] != str(ia.country):
|
||||
messages.error(self.request, _('Your VAT ID does not match the selected country.'))
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
try:
|
||||
result = vat_moss.id.validate(ia.vat_id)
|
||||
if result:
|
||||
country_code, normalized_id, company_name = result
|
||||
ia.vat_id_validated = True
|
||||
ia.vat_id = normalized_id
|
||||
ia.save()
|
||||
except vat_moss.errors.InvalidError:
|
||||
messages.error(self.request, _('This VAT ID is not valid.'))
|
||||
except vat_moss.errors.WebServiceUnavailableError:
|
||||
logger.exception('VAT ID checking failed for country {}'.format(ia.country))
|
||||
messages.error(self.request, _('The VAT ID could not be checked, as the VAT checking service of '
|
||||
'the country is currently not available.'))
|
||||
else:
|
||||
messages.success(self.request, _('This VAT ID is valid.'))
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
def get(self, *args, **kwargs): # NOQA
|
||||
return HttpResponseNotAllowed(['POST'])
|
||||
|
||||
|
||||
class OrderInvoiceRegenerate(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
@@ -458,6 +512,11 @@ class OrderChange(OrderView):
|
||||
return self._redirect_back()
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
@cached_property
|
||||
def other_form(self):
|
||||
return OtherOperationsForm(prefix='other', order=self.order,
|
||||
data=self.request.POST if self.request.method == "POST" else None)
|
||||
|
||||
@cached_property
|
||||
def add_form(self):
|
||||
return OrderPositionAddForm(prefix='add', order=self.order,
|
||||
@@ -469,14 +528,28 @@ class OrderChange(OrderView):
|
||||
for p in positions:
|
||||
p.form = OrderPositionChangeForm(prefix='op-{}'.format(p.pk), instance=p,
|
||||
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):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['positions'] = self.positions
|
||||
ctx['add_form'] = self.add_form
|
||||
ctx['other_form'] = self.other_form
|
||||
return ctx
|
||||
|
||||
def _process_other(self, ocm):
|
||||
if not self.other_form.is_valid():
|
||||
return False
|
||||
else:
|
||||
if self.other_form.cleaned_data['recalculate_taxes']:
|
||||
ocm.recalculate_taxes()
|
||||
return True
|
||||
|
||||
def _process_add(self, ocm):
|
||||
if not self.add_form.is_valid():
|
||||
return False
|
||||
@@ -534,7 +607,7 @@ class OrderChange(OrderView):
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
ocm = OrderChangeManager(self.order, self.request.user)
|
||||
form_valid = self._process_add(ocm) and self._process_change(ocm)
|
||||
form_valid = self._process_add(ocm) and self._process_change(ocm) and self._process_other(ocm)
|
||||
|
||||
if not form_valid:
|
||||
messages.error(self.request, _('An error occured. Please see the details below.'))
|
||||
|
||||
Reference in New Issue
Block a user