mirror of
https://github.com/pretix/pretix.git
synced 2026-01-11 22:32:27 +00:00
Compare commits
14 Commits
invoice-pl
...
fix-5455
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9811c0202 | ||
|
|
cd6fbd886c | ||
|
|
0bb390f0a9 | ||
|
|
0183f3d40f | ||
|
|
82fcc4fe42 | ||
|
|
d42f8ece53 | ||
|
|
a8bffbd402 | ||
|
|
991b116026 | ||
|
|
2374d9b78c | ||
|
|
80785bee54 | ||
|
|
ea530ac6bf | ||
|
|
2dd8cc82f2 | ||
|
|
38fae12c37 | ||
|
|
e34a3ab2ce |
@@ -80,12 +80,17 @@ lines list of objects The actual invo
|
||||
for all invoice lines
|
||||
created before this field was introduced as well as for
|
||||
all lines not created by a fee (e.g. a product).
|
||||
├ period_start datetime Start date of the service or delivery period of the invoice line.
|
||||
Can be ``null`` if not known.
|
||||
├ period_end datetime End date of the service or delivery period of the invoice line.
|
||||
Can be ``null`` if not known.
|
||||
├ event_date_from datetime Deprecated alias of ``period_start``.
|
||||
├ event_date_to datetime Deprecated alias of ``period_end``.
|
||||
├ event_date_from datetime Start date of the (sub)event this line was created for as it
|
||||
was set during invoice creation. Can be ``null`` for all invoice
|
||||
lines created before this was introduced as well as for lines in
|
||||
an event series not created by a product (e.g. shipping or
|
||||
cancellation fees).
|
||||
├ event_date_to datetime End date of the (sub)event this line was created for as it
|
||||
was set during invoice creation. Can be ``null`` for all invoice
|
||||
lines created before this was introduced as well as for lines in
|
||||
an event series not created by a product (e.g. shipping or
|
||||
cancellation fees) as well as whenever the respective (sub)event
|
||||
has no end date set.
|
||||
├ event_location string Location of the (sub)event this line was created for as it
|
||||
was set during invoice creation. Can be ``null`` for all invoice
|
||||
lines created before this was introduced as well as for lines in
|
||||
@@ -269,8 +274,6 @@ List of all invoices
|
||||
"fee_internal_type": null,
|
||||
"event_date_from": "2017-12-27T10:00:00Z",
|
||||
"event_date_to": null,
|
||||
"period_start": "2017-12-27T10:00:00Z",
|
||||
"period_end": "2017-12-27T10:00:00Z",
|
||||
"event_location": "Heidelberg",
|
||||
"attendee_name": null,
|
||||
"gross_value": "23.00",
|
||||
@@ -417,8 +420,6 @@ Fetching individual invoices
|
||||
"fee_internal_type": null,
|
||||
"event_date_from": "2017-12-27T10:00:00Z",
|
||||
"event_date_to": null,
|
||||
"period_start": "2017-12-27T10:00:00Z",
|
||||
"period_end": "2017-12-27T10:00:00Z",
|
||||
"event_location": "Heidelberg",
|
||||
"attendee_name": null,
|
||||
"gross_value": "23.00",
|
||||
|
||||
@@ -23,7 +23,7 @@ There are multiple signals that will be sent out in the ordering cycle:
|
||||
|
||||
.. automodule:: pretix.base.signals
|
||||
:no-index:
|
||||
:members: validate_cart, validate_cart_addons, validate_order, order_valid_if_pending, order_fee_calculation, order_paid, order_placed, order_canceled, order_reactivated, order_expired, order_expiry_changed, order_modified, order_changed, order_approved, order_denied, order_fee_type_name, allow_ticket_download, order_split, order_gracefully_delete, build_invoice_data, invoice_line_text
|
||||
:members: validate_cart, validate_cart_addons, validate_order, order_valid_if_pending, order_fee_calculation, order_paid, order_placed, order_canceled, order_reactivated, order_expired, order_expiry_changed, order_modified, order_changed, order_approved, order_denied, order_fee_type_name, allow_ticket_download, order_split, order_gracefully_delete, invoice_line_text
|
||||
|
||||
Check-ins
|
||||
"""""""""
|
||||
|
||||
@@ -120,7 +120,7 @@ dev = [
|
||||
"pytest-cache",
|
||||
"pytest-cov",
|
||||
"pytest-django==4.*",
|
||||
"pytest-mock==3.14.*",
|
||||
"pytest-mock==3.15.*",
|
||||
"pytest-sugar",
|
||||
"pytest-xdist==3.8.*",
|
||||
"pytest==8.4.*",
|
||||
|
||||
@@ -805,7 +805,6 @@ class EventSettingsSerializer(SettingsSerializer):
|
||||
'invoice_reissue_after_modify',
|
||||
'invoice_include_free',
|
||||
'invoice_generate',
|
||||
'invoice_period',
|
||||
'invoice_numbers_consecutive',
|
||||
'invoice_numbers_prefix',
|
||||
'invoice_numbers_prefix_cancellations',
|
||||
|
||||
@@ -1757,14 +1757,12 @@ class LinePositionField(serializers.IntegerField):
|
||||
|
||||
class InlineInvoiceLineSerializer(I18nAwareModelSerializer):
|
||||
position = LinePositionField(read_only=True)
|
||||
event_date_from = serializers.DateTimeField(read_only=True, source="period_start")
|
||||
event_date_to = serializers.DateTimeField(read_only=True, source="period_end")
|
||||
|
||||
class Meta:
|
||||
model = InvoiceLine
|
||||
fields = ('position', 'description', 'item', 'variation', 'subevent', 'attendee_name', 'event_date_from',
|
||||
'event_date_to', 'period_start', 'period_end', 'gross_value', 'tax_value', 'tax_rate', 'tax_code',
|
||||
'tax_name', 'fee_type', 'fee_internal_type', 'event_location')
|
||||
'event_date_to', 'gross_value', 'tax_value', 'tax_rate', 'tax_code', 'tax_name', 'fee_type',
|
||||
'fee_internal_type', 'event_location')
|
||||
|
||||
|
||||
class InvoiceSerializer(I18nAwareModelSerializer):
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import datetime
|
||||
import logging
|
||||
import re
|
||||
import unicodedata
|
||||
@@ -523,20 +522,6 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
textobject.textLine(self._normalize(self._upper(pgettext('invoice', 'Event'))))
|
||||
canvas.drawText(textobject)
|
||||
|
||||
def _date_range_in_header(self):
|
||||
if self.invoice.event.has_subevents or not self.invoice.event.settings.show_dates_on_frontpage:
|
||||
return None, None
|
||||
tz = self.invoice.event.timezone
|
||||
show_end_date = (
|
||||
self.invoice.event.settings.show_date_to and
|
||||
self.invoice.event.date_to and
|
||||
self.invoice.event.date_to.astimezone(tz).date() != self.invoice.event.date_from.astimezone(tz).date()
|
||||
)
|
||||
if show_end_date:
|
||||
return self.invoice.event.date_from.astimezone(tz).date(), self.invoice.event.date_to.astimezone(tz).date()
|
||||
else:
|
||||
return self.invoice.event.date_from.astimezone(tz).date(), None
|
||||
|
||||
def _draw_event(self, canvas):
|
||||
def shorten(txt):
|
||||
txt = str(txt)
|
||||
@@ -550,17 +535,25 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
p_size = p.wrap(self.event_width, self.event_height)
|
||||
return txt
|
||||
|
||||
d_from, d_to = self._date_range_in_header()
|
||||
if d_from and d_to:
|
||||
p_str = (
|
||||
shorten(self.invoice.event.name) + '\n' +
|
||||
pgettext('invoice', '{from_date}\nuntil {to_date}').format(
|
||||
from_date=date_format(d_from, "DATE_FORMAT"),
|
||||
to_date=date_format(d_to, "DATE_FORMAT"),
|
||||
)
|
||||
if not self.invoice.event.has_subevents and self.invoice.event.settings.show_dates_on_frontpage:
|
||||
tz = self.invoice.event.timezone
|
||||
show_end_date = (
|
||||
self.invoice.event.settings.show_date_to and
|
||||
self.invoice.event.date_to and
|
||||
self.invoice.event.date_to.astimezone(tz).date() != self.invoice.event.date_from.astimezone(tz).date()
|
||||
)
|
||||
elif d_from:
|
||||
p_str = shorten(self.invoice.event.name) + '\n' + date_format(d_from, "DATE_FORMAT")
|
||||
if show_end_date:
|
||||
p_str = (
|
||||
shorten(self.invoice.event.name) + '\n' +
|
||||
pgettext('invoice', '{from_date}\nuntil {to_date}').format(
|
||||
from_date=self.invoice.event.get_date_from_display(show_times=False),
|
||||
to_date=self.invoice.event.get_date_to_display(show_times=False)
|
||||
)
|
||||
)
|
||||
else:
|
||||
p_str = (
|
||||
shorten(self.invoice.event.name) + '\n' + self.invoice.event.get_date_from_display(show_times=False)
|
||||
)
|
||||
else:
|
||||
p_str = shorten(self.invoice.event.name)
|
||||
|
||||
@@ -664,12 +657,6 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
|
||||
def _get_story(self, doc):
|
||||
has_taxes = any(il.tax_value for il in self.invoice.lines.all()) or self.invoice.reverse_charge
|
||||
header_dates = self._date_range_in_header()
|
||||
tz = self.invoice.event.timezone
|
||||
has_multiple_service_dates = len(set(
|
||||
(il.period_start, il.period_end) for il in self.invoice.lines.all()
|
||||
)) > 1
|
||||
request_show_service_date = False
|
||||
|
||||
story = [
|
||||
NextPageTemplate('FirstPage'),
|
||||
@@ -713,73 +700,15 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
)]
|
||||
|
||||
def _group_key(line):
|
||||
return (line.description, line.tax_rate, line.tax_name, line.net_value, line.gross_value, line.subevent,
|
||||
line.period_start, line.period_end)
|
||||
|
||||
def day(dt: datetime.datetime) -> datetime.date:
|
||||
if dt is None:
|
||||
return None
|
||||
return dt.astimezone(tz).date()
|
||||
return (line.description, line.tax_rate, line.tax_name, line.net_value, line.gross_value, line.subevent_id,
|
||||
line.event_date_from, line.event_date_to)
|
||||
|
||||
total = Decimal('0.00')
|
||||
for (description, tax_rate, tax_name, net_value, gross_value, subevent, period_start, period_end), lines in addon_aware_groupby(
|
||||
for (description, tax_rate, tax_name, net_value, gross_value, *ignored), lines in addon_aware_groupby(
|
||||
self.invoice.lines.all(),
|
||||
key=_group_key,
|
||||
is_addon=lambda l: l.description.startswith(" +"),
|
||||
):
|
||||
# Try to be clever and figure out when organizers would want to show the period. This heuristic is
|
||||
# not perfect and the only "fully correct" way would be to include the period on every line always,
|
||||
# however this will cause confusion (a) due to useless repetition of the same date all over the invoice
|
||||
# (b) due to not respecting the show_date_to setting of events in cases where we could have respected it.
|
||||
# Still, we want to show the date explicitly if its different to the event or invoice date.
|
||||
if period_start and period_end and day(period_end) != day(period_start):
|
||||
# It's a multi-day period, such as the validity of the ticket or an event date period
|
||||
|
||||
if day(period_start) == header_dates[0] and day(period_end) == header_dates[1]:
|
||||
# This is the exact event period we already printed in the header, no need to repeat it.
|
||||
period_line = ""
|
||||
|
||||
elif (self.event.has_subevents and subevent and day(subevent.date_from) == day(period_start) and
|
||||
day(subevent.date_to) == day(period_end)):
|
||||
# For subevents, build_invoice already includes the date in the description in the event-default format.
|
||||
period_line = ""
|
||||
|
||||
else:
|
||||
period_line = f"{date_format(day(period_start), 'SHORT_DATE_FORMAT')} – {date_format(day(period_end), 'SHORT_DATE_FORMAT')}"
|
||||
|
||||
elif period_start or period_end:
|
||||
# It's a single-day period
|
||||
|
||||
delivery_day = day(period_end or period_start)
|
||||
if delivery_day in (header_dates[0], header_dates[1]):
|
||||
# This is the event date we already printed in the header, no need to repeat it.
|
||||
period_line = ""
|
||||
|
||||
elif self.event.has_subevents and subevent and delivery_day in (day(subevent.date_from), day(subevent.date_to)):
|
||||
# For subevents, build_invoice already includes the date in the description in the event-default format.
|
||||
period_line = ""
|
||||
|
||||
elif (delivery_day == self.invoice.date) and header_dates[0] is None:
|
||||
# This is a shop that doesn't show the date of the event in the header, and the period is the invoice
|
||||
# date. We assume that this is an 'everything is executed immediately' situation and do not want to
|
||||
# confuse with showing additional dates on the invoice. This is the case that is not guaranteed to be
|
||||
# correct in all cases and might need to change in the future. If customers have legal concerns, a
|
||||
# quick fix is including a sentence like "Delivery date is the invoice date unless otherwise indicated:"
|
||||
# in a custom text on the invoice.
|
||||
period_line = ""
|
||||
|
||||
else:
|
||||
period_line = date_format(delivery_day, 'SHORT_DATE_FORMAT')
|
||||
else:
|
||||
# No period known
|
||||
period_line = ""
|
||||
|
||||
if not has_multiple_service_dates and period_line:
|
||||
# Group together at the end of the invoice
|
||||
request_show_service_date = period_line
|
||||
else:
|
||||
description += "\n" + period_line
|
||||
|
||||
lines = list(lines)
|
||||
if has_taxes:
|
||||
if len(lines) > 1:
|
||||
@@ -788,7 +717,6 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
gross_price=money_filter(gross_value, self.invoice.event.currency),
|
||||
)
|
||||
description = description + "\n" + single_price_line
|
||||
|
||||
tdata.append((
|
||||
FontFallbackParagraph(
|
||||
self._clean_text(description, tags=['br']),
|
||||
@@ -893,12 +821,6 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
|
||||
story.append(Spacer(1, 10 * mm))
|
||||
|
||||
if request_show_service_date:
|
||||
story.append(FontFallbackParagraph(
|
||||
self._normalize(pgettext('invoice', 'Invoice period: {daterange}').format(daterange=request_show_service_date)),
|
||||
self.stylesheet['Normal']
|
||||
))
|
||||
|
||||
if self.invoice.payment_provider_text:
|
||||
story.append(FontFallbackParagraph(
|
||||
self._normalize(self.invoice.payment_provider_text),
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
# Generated by Django 4.2.17 on 2025-09-08 08:14
|
||||
from django.core.cache import cache
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def set_invoice_period(apps, schema_editor):
|
||||
EventSettingsStore = apps.get_model("pretixbase", "Event_SettingsStore")
|
||||
ev_seen = set()
|
||||
insert_queue = []
|
||||
flush_queue = []
|
||||
|
||||
def store():
|
||||
EventSettingsStore.objects.bulk_create(
|
||||
insert_queue,
|
||||
update_conflicts=True,
|
||||
update_fields=["value"],
|
||||
unique_fields=["object", "key"],
|
||||
)
|
||||
for f in flush_queue:
|
||||
cache.delete(f)
|
||||
flush_queue.clear()
|
||||
insert_queue.clear()
|
||||
|
||||
# Existing events that use pretix-zugferd and have explicitly disabled delivery dates
|
||||
for setting in EventSettingsStore.objects.filter(key="zugferd_include_delivery_date", value="False"):
|
||||
flush_queue.append("hierarkey_{}_{}".format("event", setting.object_id))
|
||||
insert_queue.append(
|
||||
EventSettingsStore(
|
||||
object_id=setting.object_id,
|
||||
key="invoice_period",
|
||||
value="invoice_date",
|
||||
)
|
||||
)
|
||||
ev_seen.add(setting.object_id)
|
||||
|
||||
if len(insert_queue) > 1000:
|
||||
store()
|
||||
|
||||
# Existing events that previously hid their date on invoices
|
||||
for setting in EventSettingsStore.objects.filter(key="show_dates_on_frontpage", value="False"):
|
||||
if setting.object_id in ev_seen:
|
||||
continue
|
||||
|
||||
flush_queue.append("hierarkey_{}_{}".format("event", setting.object_id))
|
||||
insert_queue.append(
|
||||
EventSettingsStore(
|
||||
object_id=setting.object_id,
|
||||
key="invoice_period",
|
||||
value="auto_no_event",
|
||||
)
|
||||
)
|
||||
ev_seen.add(setting.object_id)
|
||||
|
||||
if len(insert_queue) > 1000:
|
||||
store()
|
||||
|
||||
store()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("pretixbase", "0288_invoice_transmission"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name="invoiceline",
|
||||
old_name="event_date_to",
|
||||
new_name="period_end",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="invoiceline",
|
||||
old_name="event_date_from",
|
||||
new_name="period_start",
|
||||
),
|
||||
migrations.RunPython(
|
||||
set_invoice_period,
|
||||
migrations.RunPython.noop,
|
||||
)
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 4.2.17 on 2025-09-09 09:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0289_invoiceline_period"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="invoice",
|
||||
name="plugin_data",
|
||||
field=models.JSONField(default=dict),
|
||||
),
|
||||
]
|
||||
@@ -33,7 +33,6 @@
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
import string
|
||||
import warnings
|
||||
from decimal import Decimal
|
||||
|
||||
import pycountry
|
||||
@@ -43,6 +42,7 @@ from django.db.models.functions import Cast
|
||||
from django.utils import timezone
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _, pgettext
|
||||
from django_scopes import ScopedManager
|
||||
|
||||
@@ -201,7 +201,6 @@ class Invoice(models.Model):
|
||||
transmission_info = models.JSONField(null=True, blank=True)
|
||||
|
||||
file = models.FileField(null=True, blank=True, upload_to=invoice_filename, max_length=255)
|
||||
plugin_data = models.JSONField(default=dict)
|
||||
|
||||
objects = ScopedManager(organizer='event__organizer')
|
||||
|
||||
@@ -370,6 +369,22 @@ class Invoice(models.Model):
|
||||
from pretix.base.invoicing.transmission import transmission_types
|
||||
return transmission_types.get(identifier=self.transmission_type)[0]
|
||||
|
||||
def set_transmission_failed(self, provider, data):
|
||||
self.transmission_status = Invoice.TRANSMISSION_STATUS_FAILED
|
||||
self.transmission_date = now()
|
||||
if not self.transmission_provider and provider:
|
||||
self.transmission_provider = provider
|
||||
self.save(update_fields=["transmission_status", "transmission_date", "transmission_provider"])
|
||||
self.order.log_action(
|
||||
"pretix.event.order.invoice.sending_failed",
|
||||
data={
|
||||
"full_invoice_no": self.full_invoice_no,
|
||||
"transmission_provider": provider,
|
||||
"transmission_type": self.transmission_type,
|
||||
"data": data,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class InvoiceLine(models.Model):
|
||||
"""
|
||||
@@ -389,10 +404,10 @@ class InvoiceLine(models.Model):
|
||||
:type tax_name: str
|
||||
:param subevent: The subevent this line refers to
|
||||
:type subevent: SubEvent
|
||||
:param period_start: Start if service period invoiced
|
||||
:type period_start: datetime
|
||||
:param period_end: End of service period invoiced
|
||||
:type period_end: datetime
|
||||
:param event_date_from: Event date of the (sub)event at the time the invoice was created
|
||||
:type event_date_from: datetime
|
||||
:param event_date_to: Event end date of the (sub)event at the time the invoice was created
|
||||
:type event_date_to: datetime
|
||||
:param event_location: Event location of the (sub)event at the time the invoice was created
|
||||
:type event_location: str
|
||||
:param item: The item this line refers to
|
||||
@@ -411,8 +426,8 @@ class InvoiceLine(models.Model):
|
||||
tax_name = models.CharField(max_length=190)
|
||||
tax_code = models.CharField(max_length=190, null=True, blank=True)
|
||||
subevent = models.ForeignKey('SubEvent', null=True, blank=True, on_delete=models.PROTECT)
|
||||
period_start = models.DateTimeField(null=True)
|
||||
period_end = models.DateTimeField(null=True)
|
||||
event_date_from = models.DateTimeField(null=True)
|
||||
event_date_to = models.DateTimeField(null=True)
|
||||
event_location = models.TextField(null=True, blank=True)
|
||||
item = models.ForeignKey('Item', null=True, blank=True, on_delete=models.PROTECT)
|
||||
variation = models.ForeignKey('ItemVariation', null=True, blank=True, on_delete=models.PROTECT)
|
||||
@@ -429,35 +444,3 @@ class InvoiceLine(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return 'Line {} of invoice {}'.format(self.position, self.invoice)
|
||||
|
||||
@property
|
||||
def event_date_from(self):
|
||||
warnings.warn(
|
||||
'InvoiceLine.event_date_from is deprecated, use period_start instead,',
|
||||
category=DeprecationWarning,
|
||||
)
|
||||
return self.period_start
|
||||
|
||||
@event_date_from.setter
|
||||
def event_date_from(self, value):
|
||||
warnings.warn(
|
||||
'InvoiceLine.event_date_from is deprecated, use period_start instead,',
|
||||
category=DeprecationWarning,
|
||||
)
|
||||
self.period_start = value
|
||||
|
||||
@property
|
||||
def event_date_to(self):
|
||||
warnings.warn(
|
||||
'InvoiceLine.event_date_to is deprecated, use period_end instead,',
|
||||
category=DeprecationWarning,
|
||||
)
|
||||
return self.period_end
|
||||
|
||||
@event_date_to.setter
|
||||
def event_date_to(self, value):
|
||||
warnings.warn(
|
||||
'InvoiceLine.event_date_to is deprecated, use period_end instead,',
|
||||
category=DeprecationWarning,
|
||||
)
|
||||
self.period_to = value
|
||||
|
||||
@@ -61,9 +61,7 @@ from pretix.base.models.tax import EU_CURRENCIES
|
||||
from pretix.base.services.tasks import (
|
||||
TransactionAwareProfiledEventTask, TransactionAwareTask,
|
||||
)
|
||||
from pretix.base.signals import (
|
||||
build_invoice_data, invoice_line_text, periodic_task,
|
||||
)
|
||||
from pretix.base.signals import invoice_line_text, periodic_task
|
||||
from pretix.celery_app import app
|
||||
from pretix.helpers.database import OF_SELF, rolledback_transaction
|
||||
from pretix.helpers.models import modelcopy
|
||||
@@ -84,10 +82,6 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
|
||||
lp = invoice.order.payments.last()
|
||||
|
||||
min_period_start = None
|
||||
max_period_end = None
|
||||
now_dt = now()
|
||||
|
||||
with (language(invoice.locale, invoice.event.settings.region)):
|
||||
invoice.invoice_from = invoice.event.settings.get('invoice_address_from')
|
||||
invoice.invoice_from_name = invoice.event.settings.get('invoice_address_from_name')
|
||||
@@ -214,9 +208,7 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
positions = list(
|
||||
invoice.order.positions.select_related('addon_to', 'item', 'tax_rule', 'subevent', 'variation').annotate(
|
||||
addon_c=Count('addons')
|
||||
).prefetch_related(
|
||||
'answers', 'answers__options', 'answers__question', 'granted_memberships',
|
||||
).order_by('positionid', 'id')
|
||||
).prefetch_related('answers', 'answers__options', 'answers__question').order_by('positionid', 'id')
|
||||
)
|
||||
|
||||
reverse_charge = False
|
||||
@@ -275,10 +267,6 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
location=_location_oneliner(location)
|
||||
)
|
||||
|
||||
period_start, period_end = _service_period_for_position(invoice, p, now_dt)
|
||||
min_period_start = min(min_period_start or period_start, period_start)
|
||||
max_period_end = min(max_period_end or period_end, period_end)
|
||||
|
||||
InvoiceLine.objects.create(
|
||||
position=i,
|
||||
invoice=invoice,
|
||||
@@ -289,8 +277,8 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
item=p.item,
|
||||
variation=p.variation,
|
||||
attendee_name=p.attendee_name if invoice.event.settings.invoice_attendee_name else None,
|
||||
period_start=period_start,
|
||||
period_end=period_end,
|
||||
event_date_from=p.subevent.date_from if invoice.event.has_subevents else invoice.event.date_from,
|
||||
event_date_to=p.subevent.date_to if invoice.event.has_subevents else invoice.event.date_to,
|
||||
event_location=location if invoice.event.settings.invoice_event_location else None,
|
||||
tax_rate=p.tax_rate,
|
||||
tax_code=p.tax_code,
|
||||
@@ -313,29 +301,13 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
fee_title = _(fee.get_fee_type_display())
|
||||
if fee.description:
|
||||
fee_title += " - " + fee.description
|
||||
|
||||
if min_period_start and max_period_end:
|
||||
# Consider fees to have the same service period as the products sold
|
||||
period_start = min_period_start
|
||||
period_end = max_period_end
|
||||
else:
|
||||
# Usually can only happen if everything except a cancellation fee is removed
|
||||
if invoice.event.settings.invoice_period in ("auto", "auto_no_event", "event_date") and not invoice.event.has_subevents:
|
||||
# Non-series event, let's be backwards-compatible and tag everything with the event period
|
||||
period_start = invoice.event.date_from
|
||||
period_end = invoice.event.date_to
|
||||
else:
|
||||
# We could try to work from the canceled positions, but it doesn't really make sense. A cancellation
|
||||
# fee is not "delivered" at the event date, it is rather effective right now.
|
||||
period_start = period_end = now()
|
||||
|
||||
InvoiceLine.objects.create(
|
||||
position=i + offset,
|
||||
invoice=invoice,
|
||||
description=fee_title,
|
||||
gross_value=fee.value,
|
||||
period_start=period_start,
|
||||
period_end=period_end,
|
||||
event_date_from=None if invoice.event.has_subevents else invoice.event.date_from,
|
||||
event_date_to=None if invoice.event.has_subevents else invoice.event.date_to,
|
||||
event_location=(
|
||||
None if invoice.event.has_subevents
|
||||
else (str(invoice.event.location)
|
||||
@@ -364,7 +336,6 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
invoice.reverse_charge = reverse_charge
|
||||
invoice.save()
|
||||
|
||||
build_invoice_data.send(sender=invoice.event, invoice=invoice)
|
||||
return invoice
|
||||
|
||||
|
||||
@@ -380,49 +351,6 @@ def build_cancellation(invoice: Invoice):
|
||||
return invoice
|
||||
|
||||
|
||||
def _service_period_for_position(invoice, position, invoice_dt):
|
||||
if invoice.event.settings.invoice_period in ("auto", "auto_no_event"):
|
||||
if position.valid_from or position.valid_until:
|
||||
period_start = position.valid_from or now()
|
||||
period_end = position.valid_until
|
||||
elif memberships := list(position.granted_memberships.all()):
|
||||
period_start = min(m.date_start for m in memberships)
|
||||
period_end = max(m.date_end for m in memberships)
|
||||
elif invoice.event.has_subevents:
|
||||
if position.subevent:
|
||||
period_start = position.subevent.date_from
|
||||
period_end = position.subevent.date_to
|
||||
else:
|
||||
# Currently impossible case, but might not be in the future and never makes
|
||||
# sense to use the event date here
|
||||
period_start = invoice_dt
|
||||
period_end = invoice_dt
|
||||
elif invoice.event.settings.invoice_period == "auto_no_event":
|
||||
period_start = invoice_dt
|
||||
period_end = invoice_dt
|
||||
else:
|
||||
period_start = invoice.event.date_from
|
||||
period_end = invoice.event.date_to
|
||||
elif invoice.event.settings.invoice_period == "order_date":
|
||||
period_start = invoice.order.datetime
|
||||
period_end = invoice.order.datetime
|
||||
elif invoice.event.settings.invoice_period == "event_date":
|
||||
if position.subevent:
|
||||
period_start = position.subevent.date_from
|
||||
period_end = position.subevent.date_to
|
||||
else:
|
||||
period_start = invoice.event.date_from
|
||||
period_end = invoice.event.date_to
|
||||
elif invoice.event.settings.invoice_period == "invoice_date":
|
||||
period_start = period_end = invoice_dt
|
||||
else:
|
||||
raise ValueError(f"Invalid invoice period setting '{invoice.event.settings.invoice_period}'")
|
||||
|
||||
if not period_end:
|
||||
period_end = period_start
|
||||
return period_start, period_end
|
||||
|
||||
|
||||
def generate_cancellation(invoice: Invoice, trigger_pdf=True):
|
||||
if invoice.canceled:
|
||||
raise ValueError("Invoice should not be canceled twice.")
|
||||
@@ -528,12 +456,6 @@ def build_preview_invoice_pdf(event):
|
||||
if not locale or locale == '__user__':
|
||||
locale = event.settings.locale
|
||||
|
||||
if event.settings.invoice_period in ("auto", "auto_no_event", "event_date"):
|
||||
period_start = event.date_from
|
||||
period_end = event.date_to or event.date_from
|
||||
else:
|
||||
period_start = period_end = timezone.now()
|
||||
|
||||
with rolledback_transaction(), language(locale, event.settings.region):
|
||||
order = event.orders.create(
|
||||
status=Order.STATUS_PENDING, datetime=timezone.now(),
|
||||
@@ -584,8 +506,8 @@ def build_preview_invoice_pdf(event):
|
||||
invoice=invoice, description=_("Sample product {}").format(i + 1),
|
||||
gross_value=tax.gross, tax_value=tax.tax,
|
||||
tax_rate=tax.rate, tax_name=tax.name, tax_code=tax.code,
|
||||
period_start=period_start,
|
||||
period_end=period_end,
|
||||
event_date_from=event.date_from,
|
||||
event_date_to=event.date_to,
|
||||
event_location=event.settings.invoice_event_location,
|
||||
)
|
||||
else:
|
||||
@@ -593,8 +515,8 @@ def build_preview_invoice_pdf(event):
|
||||
InvoiceLine.objects.create(
|
||||
invoice=invoice, description=_("Sample product A"),
|
||||
gross_value=100, tax_value=0, tax_rate=0, tax_code=None,
|
||||
period_start=period_start,
|
||||
period_end=period_end,
|
||||
event_date_from=event.date_from,
|
||||
event_date_to=event.date_to,
|
||||
event_location=event.settings.invoice_event_location,
|
||||
)
|
||||
|
||||
@@ -742,20 +664,7 @@ def transmit_invoice(sender, invoice_id, allow_retransmission=True, **kwargs):
|
||||
break
|
||||
|
||||
if not provider:
|
||||
invoice.transmission_status = Invoice.TRANSMISSION_STATUS_FAILED
|
||||
invoice.transmission_date = now()
|
||||
invoice.save(update_fields=["transmission_status", "transmission_date"])
|
||||
invoice.order.log_action(
|
||||
"pretix.event.order.invoice.sending_failed",
|
||||
data={
|
||||
"full_invoice_no": invoice.full_invoice_no,
|
||||
"transmission_provider": None,
|
||||
"transmission_type": invoice.transmission_type,
|
||||
"data": {
|
||||
"reason": "no_provider",
|
||||
},
|
||||
}
|
||||
)
|
||||
invoice.set_transmission_failed(provider=None, data={"reason": "no_provider"})
|
||||
return
|
||||
|
||||
if invoice.order.testmode and not provider.testmode_supported:
|
||||
@@ -776,18 +685,7 @@ def transmit_invoice(sender, invoice_id, allow_retransmission=True, **kwargs):
|
||||
provider.transmit(invoice)
|
||||
except Exception as e:
|
||||
logger.exception(f"Transmission of invoice {invoice.pk} failed with exception.")
|
||||
invoice.transmission_status = Invoice.TRANSMISSION_STATUS_FAILED
|
||||
invoice.transmission_date = now()
|
||||
invoice.save(update_fields=["transmission_status", "transmission_date"])
|
||||
invoice.order.log_action(
|
||||
"pretix.event.order.invoice.sending_failed",
|
||||
data={
|
||||
"full_invoice_no": invoice.full_invoice_no,
|
||||
"transmission_provider": None,
|
||||
"transmission_type": invoice.transmission_type,
|
||||
"data": {
|
||||
"reason": "exception",
|
||||
"exception": str(e),
|
||||
},
|
||||
}
|
||||
)
|
||||
invoice.set_transmission_failed(provider=provider.identifier, data={
|
||||
"reason": "exception",
|
||||
"exception": str(e),
|
||||
})
|
||||
|
||||
@@ -495,7 +495,7 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
|
||||
email = email_filter.send_chained(event, 'message', message=email, order=order, user=user)
|
||||
|
||||
invoices_sent = []
|
||||
invoices_to_mark_transmitted = []
|
||||
if invoices:
|
||||
invoices = Invoice.objects.filter(pk__in=invoices)
|
||||
for inv in invoices:
|
||||
@@ -516,7 +516,23 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
inv.file.file.read(),
|
||||
'application/pdf'
|
||||
)
|
||||
invoices_sent.append(inv)
|
||||
|
||||
if inv.transmission_type == "email":
|
||||
# Mark invoice as sent when it was sent to the requested address *either* at the time of
|
||||
# invoice creation *or* as of right now.
|
||||
expected_recipients = [
|
||||
(inv.invoice_to_transmission_info or {}).get("transmission_email_address")
|
||||
or inv.order.email,
|
||||
]
|
||||
try:
|
||||
expected_recipients.append(
|
||||
(inv.order.invoice_address.transmission_info or {}).get("transmission_email_address")
|
||||
or inv.order.email
|
||||
)
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
pass
|
||||
if any(t in expected_recipients for t in to):
|
||||
invoices_to_mark_transmitted.append(inv)
|
||||
except:
|
||||
logger.exception('Could not attach invoice to email')
|
||||
pass
|
||||
@@ -589,6 +605,11 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
for i in invoices_to_mark_transmitted:
|
||||
i.set_transmission_failed(provider="email_pdf", data={
|
||||
"reason": "exception",
|
||||
"exception": "SMTP code {}, max retries exceeded".format(e.smtp_code),
|
||||
})
|
||||
raise e
|
||||
|
||||
logger.exception('Error sending email')
|
||||
@@ -602,6 +623,11 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
for i in invoices_to_mark_transmitted:
|
||||
i.set_transmission_failed(provider="email_pdf", data={
|
||||
"reason": "exception",
|
||||
"exception": "SMTP code {}".format(e.smtp_code),
|
||||
})
|
||||
|
||||
raise SendMailException('Failed to send an email to {}.'.format(to))
|
||||
except smtplib.SMTPRecipientsRefused as e:
|
||||
@@ -633,6 +659,11 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
for i in invoices_to_mark_transmitted:
|
||||
i.set_transmission_failed(provider="email_pdf", data={
|
||||
"reason": "exception",
|
||||
"exception": "SMTP error",
|
||||
})
|
||||
|
||||
raise SendMailException('Failed to send an email to {}.'.format(to))
|
||||
except Exception as e:
|
||||
@@ -650,6 +681,11 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
for i in invoices_to_mark_transmitted:
|
||||
i.set_transmission_failed(provider="email_pdf", data={
|
||||
"reason": "exception",
|
||||
"exception": "Internal error",
|
||||
})
|
||||
raise e
|
||||
if log_target:
|
||||
log_target.log_action(
|
||||
@@ -661,59 +697,52 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
for i in invoices_to_mark_transmitted:
|
||||
i.set_transmission_failed(provider="email_pdf", data={
|
||||
"reason": "exception",
|
||||
"exception": "Internal error",
|
||||
})
|
||||
logger.exception('Error sending email')
|
||||
raise SendMailException('Failed to send an email to {}.'.format(to))
|
||||
else:
|
||||
for i in invoices_sent:
|
||||
if i.transmission_type == "email":
|
||||
# Mark invoice as sent when it was sent to the requested address *either* at the time of invoice
|
||||
# creation *or* as of right now.
|
||||
expected_recipients = [
|
||||
(i.invoice_to_transmission_info or {}).get("transmission_email_address") or i.order.email,
|
||||
]
|
||||
try:
|
||||
expected_recipients.append((i.order.invoice_address.transmission_info or {}).get("transmission_email_address") or i.order.email)
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
pass
|
||||
if not any(t in expected_recipients for t in to):
|
||||
continue
|
||||
if i.transmission_status != Invoice.TRANSMISSION_STATUS_COMPLETED:
|
||||
i.transmission_date = now()
|
||||
i.transmission_status = Invoice.TRANSMISSION_STATUS_COMPLETED
|
||||
i.transmission_provider = "email_pdf"
|
||||
i.transmission_info = {
|
||||
"sent": [
|
||||
{
|
||||
"recipients": to,
|
||||
"datetime": now().isoformat(),
|
||||
}
|
||||
]
|
||||
}
|
||||
i.save(update_fields=[
|
||||
"transmission_date", "transmission_provider", "transmission_status",
|
||||
"transmission_info"
|
||||
])
|
||||
elif i.transmission_provider == "email_pdf":
|
||||
i.transmission_info["sent"].append(
|
||||
for i in invoices_to_mark_transmitted:
|
||||
if i.transmission_status != Invoice.TRANSMISSION_STATUS_COMPLETED:
|
||||
i.transmission_date = now()
|
||||
i.transmission_status = Invoice.TRANSMISSION_STATUS_COMPLETED
|
||||
i.transmission_provider = "email_pdf"
|
||||
i.transmission_info = {
|
||||
"sent": [
|
||||
{
|
||||
"recipients": to,
|
||||
"datetime": now().isoformat(),
|
||||
}
|
||||
)
|
||||
i.save(update_fields=[
|
||||
"transmission_info"
|
||||
])
|
||||
i.order.log_action(
|
||||
"pretix.event.order.invoice.sent",
|
||||
data={
|
||||
"full_invoice_no": i.full_invoice_no,
|
||||
"transmission_provider": "email_pdf",
|
||||
"transmission_type": "email",
|
||||
"data": {
|
||||
"recipients": [to],
|
||||
},
|
||||
]
|
||||
}
|
||||
i.save(update_fields=[
|
||||
"transmission_date", "transmission_provider", "transmission_status",
|
||||
"transmission_info"
|
||||
])
|
||||
elif i.transmission_provider == "email_pdf":
|
||||
i.transmission_info["sent"].append(
|
||||
{
|
||||
"recipients": to,
|
||||
"datetime": now().isoformat(),
|
||||
}
|
||||
)
|
||||
i.save(update_fields=[
|
||||
"transmission_info"
|
||||
])
|
||||
i.order.log_action(
|
||||
"pretix.event.order.invoice.sent",
|
||||
data={
|
||||
"full_invoice_no": i.full_invoice_no,
|
||||
"transmission_provider": "email_pdf",
|
||||
"transmission_type": "email",
|
||||
"data": {
|
||||
"recipients": [to],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def mail_send(*args, **kwargs):
|
||||
|
||||
@@ -1098,35 +1098,6 @@ DEFAULTS = {
|
||||
help_text=_("Invoices will never be automatically generated for free orders.")
|
||||
)
|
||||
},
|
||||
'invoice_period': {
|
||||
'default': 'auto',
|
||||
'type': str,
|
||||
'form_class': forms.ChoiceField,
|
||||
'serializer_class': serializers.ChoiceField,
|
||||
'serializer_kwargs': dict(
|
||||
choices=(
|
||||
('auto', _('Automatic based on ticket-specific validity, membership validity, event series date, or event date)')),
|
||||
('auto_no_event', _('Automatic, but prefer invoice date over event date')),
|
||||
('event_date', _('Event date')),
|
||||
('order_date', _('Order date')),
|
||||
('invoice_date', _('Invoice date')),
|
||||
),
|
||||
),
|
||||
'form_kwargs': dict(
|
||||
label=_("Date of service"),
|
||||
widget=forms.RadioSelect,
|
||||
choices=(
|
||||
('auto', _('Automatic based on ticket-specific validity, membership validity, event series date, or event date)')),
|
||||
('auto_no_event', _('Automatic, but prefer invoice date over event date')),
|
||||
('event_date', _('Event date')),
|
||||
('order_date', _('Order date')),
|
||||
('invoice_date', _('Invoice date')),
|
||||
),
|
||||
help_text=_("This controls what dates are shown on the invoice, but is especially important for "
|
||||
"electronic invoicing."),
|
||||
required=True,
|
||||
)
|
||||
},
|
||||
'invoice_reissue_after_modify': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
|
||||
@@ -596,18 +596,6 @@ multiple events. Receivers should return a subclass of pretix.base.exporter.Base
|
||||
The ``sender`` keyword argument will contain an organizer.
|
||||
"""
|
||||
|
||||
build_invoice_data = EventPluginSignal()
|
||||
"""
|
||||
Arguments: ``invoice``
|
||||
|
||||
This signal is sent out every time an invoice is built, after the invoice model was created
|
||||
and filled and before the PDF generation task is started. You can use this to make changes
|
||||
to the invoice, but we recommend to mostly use it to add content to ``Invoice.plugin_data``.
|
||||
You are responsible for saving any changes to the database.
|
||||
|
||||
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
|
||||
"""
|
||||
|
||||
validate_order = EventPluginSignal()
|
||||
"""
|
||||
Arguments: ``payments``, ``positions``, ``email``, ``locale``, ``invoice_address``,
|
||||
|
||||
@@ -857,7 +857,6 @@ class InvoiceSettingsForm(EventSettingsValidationMixin, SettingsForm):
|
||||
'invoice_show_payments',
|
||||
'invoice_reissue_after_modify',
|
||||
'invoice_generate',
|
||||
'invoice_period',
|
||||
'invoice_attendee_name',
|
||||
'invoice_event_location',
|
||||
'invoice_include_expire_date',
|
||||
|
||||
@@ -236,7 +236,7 @@ class VoucherForm(I18nModelForm):
|
||||
try:
|
||||
Voucher.clean_max_usages(data, self.instance.redeemed)
|
||||
except ValidationError as e:
|
||||
raise ValidationError({"max_usages": e.message})
|
||||
raise ValidationError({"max_usages": e})
|
||||
check_quota = Voucher.clean_quota_needs_checking(
|
||||
data, self.initial_instance_data,
|
||||
item_changed=data.get('itemvar') != self.initial.get('itemvar'),
|
||||
|
||||
@@ -581,7 +581,7 @@ class CoreOrderLogEntryType(OrderLogEntryType):
|
||||
'The voucher has been set to expire because the recipient removed themselves from the waiting list.'),
|
||||
'pretix.voucher.changed': _('The voucher has been changed.'),
|
||||
'pretix.voucher.deleted': _('The voucher has been deleted.'),
|
||||
'pretix.voucher.added.waitinglist': _('The voucher has been sent to {email} through the waiting list.'),
|
||||
'pretix.voucher.added.waitinglist': _('The voucher has been assigned to {email} through the waiting list.'),
|
||||
})
|
||||
class CoreVoucherLogEntryType(VoucherLogEntryType):
|
||||
pass
|
||||
|
||||
@@ -15,19 +15,6 @@
|
||||
{% bootstrap_field form.invoice_email_attachment layout="control" %}
|
||||
{% bootstrap_field form.invoice_email_organizer layout="control" %}
|
||||
{% bootstrap_field form.invoice_language layout="control" %}
|
||||
{% bootstrap_field form.invoice_period layout="control" %}
|
||||
|
||||
{% if not request.event.settings.show_dates_on_frontpage %}
|
||||
<div data-display-dependency="input[name=invoice_period][value=auto],input[name=invoice_period][value=event_date]">
|
||||
<div class="alert alert-warning dynamic">
|
||||
{% blocktrans trimmed %}
|
||||
You configured that your shop is not an event and the event date should not be shown.
|
||||
Therefore, we recommend that you set the date of service to a different option.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% bootstrap_field form.invoice_include_free layout="control" %}
|
||||
{% bootstrap_field form.invoice_show_payments layout="control" %}
|
||||
{% bootstrap_field form.invoice_reissue_after_modify layout="control" %}
|
||||
|
||||
@@ -64,6 +64,16 @@ def daterange(df, dt, as_html=False):
|
||||
return format_html(base_format, _date(df, "j."), mark_safe(until.strip()), _date(dt, "j. F Y"))
|
||||
elif df.year == dt.year:
|
||||
return format_html(base_format, _date(df, "j. F"), until, _date(dt, "j. F Y"))
|
||||
elif lng == "en-nz":
|
||||
if df.year == dt.year and df.month == dt.month and df.day == dt.day:
|
||||
# Mon, 15 January 2024
|
||||
return format_html(base_format, _date(df, "D, j F Y"))
|
||||
elif df.year == dt.year and df.month == dt.month:
|
||||
# 1 – 3 January 2024
|
||||
return format_html(base_format, _date(df, "j"), until, _date(dt, "j F Y"))
|
||||
elif df.year == dt.year:
|
||||
# 1 January – 3 April 2024
|
||||
return format_html(base_format, _date(df, "j F"), until, _date(dt, "j F Y"))
|
||||
elif lng.startswith("en"):
|
||||
if df.year == dt.year and df.month == dt.month and df.day == dt.day:
|
||||
return format_html(base_format, _date(df, "D, N jS, Y"))
|
||||
|
||||
21
src/pretix/helpers/formats/en_NZ/__init__.py
Normal file
21
src/pretix/helpers/formats/en_NZ/__init__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
50
src/pretix/helpers/formats/en_NZ/formats.py
Normal file
50
src/pretix/helpers/formats/en_NZ/formats.py
Normal file
@@ -0,0 +1,50 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# Date according to https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||
# Following NZ government guidance from https://www.digital.govt.nz/standards-and-guidance/design-and-ux/content-design-guidance/writing-style/numbers
|
||||
DATE_FORMAT = "j F Y" # 12 December 2015
|
||||
DATETIME_FORMAT = "j F Y, g:ia" # 12 December 2015, 5:30pm
|
||||
TIME_FORMAT = "g:ia" # 5:30pm
|
||||
YEAR_MONTH_FORMAT = "F Y" # December 2015
|
||||
MONTH_DAY_FORMAT = "j F" # 12 December
|
||||
SHORT_DATE_FORMAT = "j F Y" # same as DATE_FORMAT per guidance
|
||||
SHORT_DATETIME_FORMAT = "j F Y, g:ia"
|
||||
WEEKDAY_FORMAT = "l" # Monday
|
||||
WEEKDAY_DATE_FORMAT = "l, j F Y" # Friday, 23 November 2018
|
||||
WEEK_FORMAT = "\\W W, o" # ISO week: "W 52, 2024"
|
||||
WEEK_DAY_FORMAT = "D, j M" # Abbrev weekday and month: "Mon, 5 Feb"
|
||||
SHORT_MONTH_DAY_FORMAT = "j/n" # Numeric day/month: "5/2"
|
||||
|
||||
# Parsing inputs; keep d/m/Y and ISO
|
||||
DATE_INPUT_FORMATS = [
|
||||
"%d/%m/%Y",
|
||||
"%Y-%m-%d",
|
||||
"%d/%m/%y",
|
||||
]
|
||||
|
||||
TIME_INPUT_FORMATS = [
|
||||
"%I:%M%p", # 5:30pm
|
||||
"%H:%M:%S",
|
||||
"%H:%M:%S.%f",
|
||||
"%H:%M",
|
||||
]
|
||||
@@ -8,20 +8,20 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-19 16:35+0000\n"
|
||||
"PO-Revision-Date: 2025-05-16 17:00+0000\n"
|
||||
"Last-Translator: David <davemachala@gmail.com>\n"
|
||||
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix/cs/"
|
||||
">\n"
|
||||
"PO-Revision-Date: 2025-09-09 04:00+0000\n"
|
||||
"Last-Translator: Alois Pospíšil <alois.pospisil@gmail.com>\n"
|
||||
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix/cs/>"
|
||||
"\n"
|
||||
"Language: cs\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 5.11.4\n"
|
||||
"X-Generator: Weblate 5.13.2\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
msgstr "Angličtina"
|
||||
msgstr "AngličDodací adresatina"
|
||||
|
||||
#: pretix/_base_settings.py:88
|
||||
msgid "German"
|
||||
@@ -145,7 +145,7 @@ msgstr "Španělština"
|
||||
|
||||
#: pretix/_base_settings.py:118
|
||||
msgid "Spanish (Latin America)"
|
||||
msgstr ""
|
||||
msgstr "uzamčeno (pouze obchody)"
|
||||
|
||||
#: pretix/_base_settings.py:119
|
||||
msgid "Turkish"
|
||||
@@ -688,7 +688,7 @@ msgstr "{system} Uživatel"
|
||||
#: pretix/presale/templates/pretixpresale/event/checkout_customer.html:31
|
||||
#: pretix/presale/templates/pretixpresale/event/order.html:300
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
msgstr "E-mail"
|
||||
|
||||
#: pretix/base/auth.py:157 pretix/base/forms/auth.py:164
|
||||
#: pretix/base/forms/auth.py:218 pretix/base/models/auth.py:675
|
||||
@@ -1083,7 +1083,7 @@ msgstr "Krátká forma"
|
||||
#: pretix/control/templates/pretixcontrol/events/index.html:68
|
||||
#: pretix/control/templates/pretixcontrol/organizers/detail.html:64
|
||||
msgid "Event name"
|
||||
msgstr "Název akce"
|
||||
msgstr "Název události"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:446
|
||||
#: pretix/base/exporters/invoices.py:326
|
||||
@@ -1107,7 +1107,7 @@ msgstr "Datum ukončení akce"
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_voucher_form.html:12
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_voucher_form.html:20
|
||||
msgid "Voucher code"
|
||||
msgstr "Kód poukázky"
|
||||
msgstr "Kód poukazu"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:473 pretix/base/pdf.py:116
|
||||
msgid "Order code and position number"
|
||||
@@ -1254,7 +1254,7 @@ msgstr "Nahrání souborů s odpověďmi na otázky"
|
||||
#: pretix/plugins/reports/exporters.py:662
|
||||
msgctxt "export_category"
|
||||
msgid "Order data"
|
||||
msgstr "Údaje o objednávce"
|
||||
msgstr "Data objednávky"
|
||||
|
||||
#: pretix/base/exporters/answers.py:56
|
||||
msgid ""
|
||||
@@ -1414,7 +1414,7 @@ msgstr "Celé jméno"
|
||||
#: pretix/presale/templates/pretixpresale/event/checkout_customer.html:35
|
||||
#: pretix/presale/templates/pretixpresale/event/order.html:312
|
||||
msgid "Name"
|
||||
msgstr "Jméno"
|
||||
msgstr "Název"
|
||||
|
||||
#: pretix/base/exporters/customers.py:77 pretix/base/models/customers.py:99
|
||||
msgid "Account active"
|
||||
@@ -2310,7 +2310,7 @@ msgstr ""
|
||||
#: pretix/base/exporters/waitinglist.py:115 pretix/control/forms/event.py:1671
|
||||
#: pretix/control/forms/organizer.py:116
|
||||
msgid "Event slug"
|
||||
msgstr "Odkaz akce"
|
||||
msgstr "Zkratka události"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:262
|
||||
#: pretix/base/exporters/orderlist.py:452
|
||||
@@ -2419,7 +2419,7 @@ msgstr "Čísla faktur"
|
||||
#: pretix/control/forms/filter.py:585
|
||||
#: pretix/control/templates/pretixcontrol/order/index.html:190
|
||||
msgid "Sales channel"
|
||||
msgstr "B2B Kanál"
|
||||
msgstr "Prodejní kanál"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:286
|
||||
#: pretix/base/exporters/orderlist.py:630 pretix/base/models/orders.py:277
|
||||
@@ -2579,12 +2579,12 @@ msgstr "ID sedadla"
|
||||
#: pretix/base/exporters/orderlist.py:622
|
||||
#: pretix/plugins/checkinlists/exporters.py:527
|
||||
msgid "Seat name"
|
||||
msgstr "Název sedadla"
|
||||
msgstr "Název místa"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:623
|
||||
#: pretix/plugins/checkinlists/exporters.py:528
|
||||
msgid "Seat zone"
|
||||
msgstr "Sedací zóna"
|
||||
msgstr "dd.MM HH:mm"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:624
|
||||
#: pretix/plugins/checkinlists/exporters.py:529
|
||||
@@ -3568,7 +3568,7 @@ msgstr "Příjemce"
|
||||
#, python-format
|
||||
msgctxt "invoice"
|
||||
msgid "Page %d of %d"
|
||||
msgstr "Stránka %d z %d"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/invoicing/pdf.py:378
|
||||
msgctxt "invoice"
|
||||
@@ -4780,7 +4780,7 @@ msgstr ""
|
||||
|
||||
#: pretix/base/models/event.py:583 pretix/base/models/organizer.py:89
|
||||
msgid "The slug may only contain letters, numbers, dots and dashes."
|
||||
msgstr "Krátká forma může obsahovat pouze písmena, číslice, tečky a pomlčky."
|
||||
msgstr "Zkratka smí obsahovat pouze písmena, čísla, tečky a pomlčky."
|
||||
|
||||
#: pretix/base/models/event.py:600 pretix/base/models/event.py:1495
|
||||
msgid "Show in lists"
|
||||
@@ -4837,7 +4837,7 @@ msgstr "Série událostí"
|
||||
|
||||
#: pretix/base/models/event.py:652 pretix/base/models/event.py:1544
|
||||
msgid "Seating plan"
|
||||
msgstr "Plán usazení"
|
||||
msgstr "Plán míst k sezení"
|
||||
|
||||
#: pretix/base/models/event.py:659 pretix/base/models/items.py:675
|
||||
msgid "Sell on all sales channels"
|
||||
@@ -5866,7 +5866,7 @@ msgstr "Upload souborů"
|
||||
#: pretix/base/models/items.py:1657
|
||||
#: pretix/control/templates/pretixcontrol/event/settings.html:240
|
||||
msgid "Date and time"
|
||||
msgstr "Datum a hodina"
|
||||
msgstr "Datum a čas"
|
||||
|
||||
#: pretix/base/models/items.py:1658
|
||||
msgid "Country code (ISO 3166-1 alpha-2)"
|
||||
@@ -6027,7 +6027,7 @@ msgstr ""
|
||||
#: pretix/base/models/items.py:1961
|
||||
#: pretix/control/templates/pretixcontrol/items/question.html:90
|
||||
msgid "Answer"
|
||||
msgstr "Odpověď"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/models/items.py:1985
|
||||
#, python-brace-format
|
||||
@@ -6667,7 +6667,6 @@ msgstr "Pozvání do týmu \"{team}\" pro \"{email}\""
|
||||
|
||||
#: pretix/base/models/organizer.py:604
|
||||
#: pretix/control/templates/pretixcontrol/organizers/channels.html:23
|
||||
#, fuzzy
|
||||
msgid "Identifier"
|
||||
msgstr "Identifikátor"
|
||||
|
||||
@@ -8003,7 +8002,7 @@ msgstr "Přízemí, řada 3, sedadlo 4"
|
||||
#: pretix/base/pdf.py:500 pretix/base/pdf.py:506
|
||||
#: pretix/control/forms/orders.py:344
|
||||
msgid "General admission"
|
||||
msgstr "Všeobecné vstupné"
|
||||
msgstr "včetně všech daní"
|
||||
|
||||
#: pretix/base/pdf.py:503
|
||||
msgid "Seat: zone"
|
||||
@@ -8175,7 +8174,7 @@ msgstr "Nevybrali jste žádné produkty."
|
||||
|
||||
#: pretix/base/services/cart.py:106
|
||||
msgid "Unknown cart position."
|
||||
msgstr "Neznámá pozice produktu ve vozíku."
|
||||
msgstr "Neznámá pozice produktu v košíku."
|
||||
|
||||
#: pretix/base/services/cart.py:107
|
||||
msgctxt "subevent"
|
||||
@@ -8381,7 +8380,8 @@ msgid ""
|
||||
"Applying a voucher to the whole cart should not be combined with other "
|
||||
"operations."
|
||||
msgstr ""
|
||||
"Použití poukazu na celý vozík by se nemělo kombinovat s jinými operacemi."
|
||||
"Použití poukazu na všechny produkty v košíku by se nemělo kombinovat s "
|
||||
"jinými operacemi."
|
||||
|
||||
#: pretix/base/services/cart.py:179
|
||||
msgid ""
|
||||
@@ -16471,7 +16471,7 @@ msgstr "Bylo změněno nastavení metody stahování lístků."
|
||||
|
||||
#: pretix/control/logdisplay.py:492
|
||||
msgid "Blocked manually"
|
||||
msgstr "Ručně zablokováno"
|
||||
msgstr "Ručně blokováno"
|
||||
|
||||
#: pretix/control/logdisplay.py:494
|
||||
msgid "Blocked because of an API integration"
|
||||
@@ -26626,7 +26626,7 @@ msgstr ""
|
||||
#: pretix/plugins/stripe/views.py:682
|
||||
#: pretix/plugins/ticketoutputpdf/views.py:132
|
||||
msgid "We could not save your changes. See below for details."
|
||||
msgstr ""
|
||||
msgstr "Vaše změny se nepodařilo uložit. Podrobnosti najdete níže."
|
||||
|
||||
#: pretix/control/views/checkin.py:420 pretix/control/views/checkin.py:457
|
||||
msgid "The requested list does not exist."
|
||||
@@ -30204,7 +30204,7 @@ msgstr "Stránka %d z %d"
|
||||
#: pretix/plugins/reports/exporters.py:211
|
||||
#, python-format
|
||||
msgid "Page %d"
|
||||
msgstr "Strana %d"
|
||||
msgstr "Strana %dd"
|
||||
|
||||
#: pretix/plugins/reports/exporters.py:213
|
||||
#, python-format
|
||||
@@ -31294,7 +31294,7 @@ msgstr "giropay přes Stripe"
|
||||
|
||||
#: pretix/plugins/stripe/payment.py:1480
|
||||
msgid "giropay"
|
||||
msgstr "giropay"
|
||||
msgstr "Smazat"
|
||||
|
||||
#: pretix/plugins/stripe/payment.py:1483
|
||||
msgid ""
|
||||
@@ -31499,37 +31499,37 @@ msgstr ""
|
||||
#: pretix/plugins/stripe/signals.py:108
|
||||
#, python-brace-format
|
||||
msgid "Dispute updated. Reason: {}"
|
||||
msgstr ""
|
||||
msgstr "Spor aktualizován. Důvod: {}"
|
||||
|
||||
#: pretix/plugins/stripe/signals.py:110
|
||||
#, python-brace-format
|
||||
msgid "Dispute closed. Status: {}"
|
||||
msgstr ""
|
||||
msgstr "Spor uzavřen. Stav: {}"
|
||||
|
||||
#: pretix/plugins/stripe/signals.py:113
|
||||
#, python-brace-format
|
||||
msgid "Stripe reported an event: {}"
|
||||
msgstr ""
|
||||
msgstr "Stripe nahlásil událost: {}"
|
||||
|
||||
#: pretix/plugins/stripe/signals.py:124
|
||||
msgid "Stripe Connect: Client ID"
|
||||
msgstr ""
|
||||
msgstr "Stripe Connect: ID klienta"
|
||||
|
||||
#: pretix/plugins/stripe/signals.py:131
|
||||
msgid "Stripe Connect: Secret key"
|
||||
msgstr ""
|
||||
msgstr "Stripe Connect: tajný klíč"
|
||||
|
||||
#: pretix/plugins/stripe/signals.py:138
|
||||
msgid "Stripe Connect: Publishable key"
|
||||
msgstr ""
|
||||
msgstr "Stripe Connect: veřejný klíč"
|
||||
|
||||
#: pretix/plugins/stripe/signals.py:145
|
||||
msgid "Stripe Connect: Secret key (test)"
|
||||
msgstr ""
|
||||
msgstr "Stripe Connect: tajný klíč (test)"
|
||||
|
||||
#: pretix/plugins/stripe/signals.py:152
|
||||
msgid "Stripe Connect: Publishable key (test)"
|
||||
msgstr ""
|
||||
msgstr "Stripe Connect: veřejný klíč (test)"
|
||||
|
||||
#: pretix/plugins/stripe/signals.py:178
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/oauth_disconnect.html:3
|
||||
@@ -31556,7 +31556,7 @@ msgstr "Celková částka bude vybrána z vašeho bankovního účtu."
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:18
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:23
|
||||
msgid "Banking Institution"
|
||||
msgstr ""
|
||||
msgstr "bankovní instituce"
|
||||
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:20
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:25
|
||||
@@ -31635,6 +31635,16 @@ msgid ""
|
||||
"statement that you can obtain from your bank. You agree to receive "
|
||||
"notifications for future debits up to 2 days before they occur."
|
||||
msgstr ""
|
||||
"Poskytnutím svých platebních údajů a potvrzením této platby opravňujete (A) "
|
||||
"%(sepa_creditor_name)s a společnost Stripe, našeho poskytovatele platebních "
|
||||
"služeb a/nebo PPRO, jeho místního poskytovatele služeb, aby vaší bance "
|
||||
"posílali pokyny k inkasu vašeho účtu a (B) vaši banku, aby váš účet "
|
||||
"inkasovala v souladu s těmito pokyny. V rámci vašich práv máte nárok na "
|
||||
"vrácení peněz od své banky podle podmínek vaší smlouvy s bankou. Nárok na "
|
||||
"vrácení peněz musí být uplatněn do 8 týdnů od data, kdy byl vaš účet "
|
||||
"zatížen. Vaše práva jsou vysvětlená v prohlášení, které můžete získat od své "
|
||||
"banky. Souhlasíte s tím, že budete dostávat oznámení o budoucích inkasech až "
|
||||
"2 dny předtím, než k nim dojde."
|
||||
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/control.html:7
|
||||
msgid "Charge ID"
|
||||
@@ -31655,10 +31665,8 @@ msgid "Payer name"
|
||||
msgstr "Jméno poplatníka"
|
||||
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/control.html:91
|
||||
#, fuzzy
|
||||
#| msgid "Payment fee"
|
||||
msgid "Payment receipt"
|
||||
msgstr "Poplatek za platbu"
|
||||
msgstr "Potvrzení o platbě"
|
||||
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/oauth_disconnect.html:12
|
||||
msgid "Do you really want to disconnect your Stripe account?"
|
||||
@@ -31669,9 +31677,8 @@ msgid "Disconnect"
|
||||
msgstr "Odpojit"
|
||||
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/pending.html:6
|
||||
#, fuzzy
|
||||
msgid "Payment instructions"
|
||||
msgstr "Nastavení platby"
|
||||
msgstr "Pokyny k platbě"
|
||||
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/pending.html:9
|
||||
msgid ""
|
||||
@@ -31700,8 +31707,8 @@ msgid ""
|
||||
"We're waiting for an answer from the payment provider regarding your "
|
||||
"payment. Please contact us if this takes more than a few days."
|
||||
msgstr ""
|
||||
"Čekáme na odpověď poskytovatele platební služby ohledně vaší platby. Pokud "
|
||||
"to bude trvat déle než několik dní, kontaktujte nás prosím."
|
||||
"Čekáme na odpověď od poskytovatele plateb ohledně vaší platby. Pokud to bude "
|
||||
"trvat déle než několik dní, kontaktujte nás."
|
||||
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/pending.html:42
|
||||
msgid ""
|
||||
@@ -31731,7 +31738,7 @@ msgstr "Transakci se nepodařilo dokončit z následujícího důvodu:"
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/sca.html:22
|
||||
#, python-format
|
||||
msgid "Confirm payment: %(code)s"
|
||||
msgstr "Potvrzení platby: %(code)s"
|
||||
msgstr "Potvrdit platbu: %(code)s"
|
||||
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/sca_return.html:20
|
||||
msgid "Confirming your payment…"
|
||||
@@ -31778,7 +31785,7 @@ msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/views.py:590 pretix/plugins/stripe/views.py:593
|
||||
msgid "Sorry, there was an error in the payment process."
|
||||
msgstr "Omlouváme se, při platbě došlo k chybě."
|
||||
msgstr "Omlouváme se, při platebním procesu došlo k chybě."
|
||||
|
||||
#: pretix/plugins/ticketoutputpdf/apps.py:44
|
||||
#: pretix/plugins/ticketoutputpdf/apps.py:47
|
||||
@@ -32238,10 +32245,9 @@ msgid "No other variations of this product exist."
|
||||
msgstr "Žádné další varianty tohoto produktu neexistují."
|
||||
|
||||
#: pretix/presale/forms/organizer.py:70
|
||||
#, fuzzy
|
||||
msgctxt "filter_empty"
|
||||
msgid "all"
|
||||
msgstr "Všechny"
|
||||
msgstr "Vše"
|
||||
|
||||
#: pretix/presale/forms/renderers.py:51
|
||||
msgctxt "form"
|
||||
@@ -32387,10 +32393,8 @@ msgstr "Informace"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/base.html:222
|
||||
#: pretix/presale/templates/pretixpresale/organizers/base.html:100
|
||||
#, fuzzy
|
||||
#| msgid "Contact:"
|
||||
msgid "Contact"
|
||||
msgstr "Kontakt:"
|
||||
msgstr "Kontakt"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/base.html:225
|
||||
#: pretix/presale/templates/pretixpresale/fragment_modals.html:118
|
||||
@@ -33139,13 +33143,13 @@ msgstr "Pokračujte k platbě"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_cart_box.html:63
|
||||
msgid "Empty cart"
|
||||
msgstr "Vyprázdnit vozík"
|
||||
msgstr "Vyprázdnit košík"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_cart_box.html:68
|
||||
#: pretix/presale/templates/pretixpresale/event/index.html:248
|
||||
#: pretix/presale/templates/pretixpresale/event/voucher_form.html:16
|
||||
msgid "Redeem a voucher"
|
||||
msgstr "Uplatnit poukázku"
|
||||
msgstr "Uplatnit poukaz"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_cart_box.html:71
|
||||
msgid "We're applying this voucher to your cart..."
|
||||
@@ -33154,7 +33158,7 @@ msgstr "Uplatňujeme tento poukaz ve vašem košíku..."
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_cart_box.html:79
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_voucher_form.html:27
|
||||
msgid "Redeem voucher"
|
||||
msgstr "Uplatnění poukázky"
|
||||
msgstr "Uplatnit poukaz"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_change_confirm.html:10
|
||||
msgid "Change summary"
|
||||
@@ -33700,10 +33704,6 @@ msgstr ""
|
||||
"vám také zaslali e-mail s odkazem."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/order.html:59
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Please save the following link if you want to access your order later. We "
|
||||
#| "also sent you an email containing the link to the address you specified."
|
||||
msgid ""
|
||||
"Please save the following link if you want to access your order later. We "
|
||||
"also sent you an email to the address you specified containing the link."
|
||||
@@ -34485,6 +34485,8 @@ msgid ""
|
||||
"Please tick a checkbox or enter a quantity for one of the ticket types to "
|
||||
"add to the cart."
|
||||
msgstr ""
|
||||
"Zaškrtněte políčko nebo zadejte množství pro jeden z typů vstupenek, které "
|
||||
"chcete přidat do košíku."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/fragment_week_calendar.html:82
|
||||
#, python-format
|
||||
@@ -34603,10 +34605,8 @@ msgid "Change password"
|
||||
msgstr "Změnit heslo"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_base.html:41
|
||||
#, fuzzy
|
||||
#| msgid "Customer account registration"
|
||||
msgid "customer account information"
|
||||
msgstr "Registrace účtu zákazníka"
|
||||
msgstr "informace o zákaznickém účtu"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_info.html:6
|
||||
msgid "Account information"
|
||||
@@ -34640,10 +34640,8 @@ msgid "transferable"
|
||||
msgstr "Přenos"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:42
|
||||
#, fuzzy
|
||||
#| msgid "not answered"
|
||||
msgid "not transferable"
|
||||
msgstr "není odpovězeno"
|
||||
msgstr "nepřenosné"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:122
|
||||
#, fuzzy
|
||||
@@ -34929,9 +34927,8 @@ msgid "You are not allowed to access time machine mode."
|
||||
msgstr "Na tuto stránku nemáte přístup."
|
||||
|
||||
#: pretix/presale/views/event.py:975
|
||||
#, fuzzy
|
||||
msgid "This feature is only available in test mode."
|
||||
msgstr "Tuto dárkovou poukázku lze použít pouze v testovacím režimu."
|
||||
msgstr "Objednávku nesmíte změnit tak, aby vyžadovala vrácení peněz."
|
||||
|
||||
#: pretix/presale/views/event.py:992
|
||||
msgid "Time machine disabled!"
|
||||
@@ -35011,9 +35008,8 @@ msgid "You may not change your order in a way that changes the total price."
|
||||
msgstr "Objednávku nemůžete změnit tak, aby se změnila celková cena."
|
||||
|
||||
#: pretix/presale/views/order.py:1634
|
||||
#, fuzzy
|
||||
msgid "You may not change your order in a way that would require a refund."
|
||||
msgstr "Objednávku nesmíte změnit tak, aby se snížila celková cena."
|
||||
msgstr "Objednávku nesmíte změnit tak, aby vyžadovala vrácení peněz."
|
||||
|
||||
#: pretix/presale/views/order.py:1642
|
||||
msgid ""
|
||||
@@ -35024,17 +35020,14 @@ msgstr ""
|
||||
"objednávku měnit tak, aby se zvýšila celková cena."
|
||||
|
||||
#: pretix/presale/views/order.py:1648
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "You may not change your order in a way that increases the total price "
|
||||
#| "since payments are no longer being accepted for this event."
|
||||
msgid ""
|
||||
"You may not change your order in a way that requires additional payment "
|
||||
"while we are processing your current payment. Please check back after your "
|
||||
"current payment has been accepted."
|
||||
msgstr ""
|
||||
"Vzhledem k tomu, že platby za tuto akci již nepřijímáme, nemůžete svou "
|
||||
"objednávku měnit tak, aby se zvýšila celková cena."
|
||||
"Nemůžete změnit svou objednávku způsobem, který vyžaduje dodatečnou platbu, "
|
||||
"zatímco zpracováváme vaši aktuální platbu. Vraťte se prosím poté, co bude "
|
||||
"vaše platba přijata."
|
||||
|
||||
#: pretix/presale/views/order.py:1664 pretix/presale/views/order.py:1695
|
||||
msgid "You cannot change this order."
|
||||
@@ -35075,6 +35068,8 @@ msgid ""
|
||||
"No ticket types are available for the waiting list, have a look at the "
|
||||
"ticket shop instead."
|
||||
msgstr ""
|
||||
"Pro čekací listinu nejsou k dispozici žádné typy vstupenek, podívejte se "
|
||||
"místo toho do obchodu se vstupenkami."
|
||||
|
||||
#: pretix/presale/views/waiting.py:137 pretix/presale/views/waiting.py:161
|
||||
msgid "Waiting lists are disabled for this event."
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-19 16:02+0000\n"
|
||||
"PO-Revision-Date: 2025-05-16 17:00+0000\n"
|
||||
"Last-Translator: David <davemachala@gmail.com>\n"
|
||||
"PO-Revision-Date: 2025-09-08 18:57+0000\n"
|
||||
"Last-Translator: Alois Pospíšil <alois.pospisil@gmail.com>\n"
|
||||
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
"cs/>\n"
|
||||
"Language: cs\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 5.11.4\n"
|
||||
"X-Generator: Weblate 5.13\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -640,19 +640,15 @@ msgid "Unknown error."
|
||||
msgstr "Neznámá chyba."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:292
|
||||
#, fuzzy
|
||||
#| msgid "Your color has great contrast and is very easy to read!"
|
||||
msgid "Your color has great contrast and will provide excellent accessibility."
|
||||
msgstr "Tato barva má velmi dobrý kontrast a je velmi dobře čitelná!"
|
||||
msgstr "Tato barva má velmi dobrý kontrast a je velmi dobře čitelná."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:296
|
||||
#, fuzzy
|
||||
#| msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgid ""
|
||||
"Your color has decent contrast and is sufficient for minimum accessibility "
|
||||
"requirements."
|
||||
msgstr ""
|
||||
"Tato barva má slušný kontrast a pravděpodobně je dostatečně dobře čitelná!"
|
||||
"Tato barva má slušný kontrast a pravděpodobně je dostatečně dobře čitelná."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:300
|
||||
msgid ""
|
||||
@@ -735,7 +731,7 @@ msgstr "Nákupní košík vypršel"
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:58
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:84
|
||||
msgid "Your cart is about to expire."
|
||||
msgstr ""
|
||||
msgstr "Nákupní košík brzy vyprší."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:62
|
||||
msgid "The items in your cart are reserved for you for one minute."
|
||||
@@ -748,10 +744,8 @@ msgstr[2] ""
|
||||
"Produkty v nákupním košíku jsou pro vás rezervovány na dalších {num} minut."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:83
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "Your cart has expired."
|
||||
msgstr "Nákupní košík vypršel"
|
||||
msgstr "Nákupní košík vypršel."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:86
|
||||
#, fuzzy
|
||||
@@ -811,12 +805,12 @@ msgstr "Zvýšit počet"
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
||||
msgctxt "widget"
|
||||
msgid "Filter events by"
|
||||
msgstr ""
|
||||
msgstr "Filtrovat události"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
||||
msgctxt "widget"
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
msgstr "Filtrovat"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
||||
msgctxt "widget"
|
||||
@@ -933,7 +927,7 @@ msgstr "Již není k dispozici"
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:42
|
||||
msgctxt "widget"
|
||||
msgid "Currently not available"
|
||||
msgstr "Momentálně není k dispozici."
|
||||
msgstr "Momentálně není k dispozici"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:44
|
||||
#, javascript-format
|
||||
@@ -968,7 +962,7 @@ msgstr "Obchod vstupenek otevřit"
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
||||
msgctxt "widget"
|
||||
msgid "Checkout"
|
||||
msgstr "Checkout"
|
||||
msgstr "Přejít k platbě"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
||||
msgctxt "widget"
|
||||
@@ -1090,7 +1084,7 @@ msgstr "Předchozí týden"
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr "Otevřete výběr sedadla"
|
||||
msgstr "Otevřete výběr míst"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||
msgctxt "widget"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-19 16:35+0000\n"
|
||||
"PO-Revision-Date: 2025-08-28 23:00+0000\n"
|
||||
"PO-Revision-Date: 2025-09-09 04:00+0000\n"
|
||||
"Last-Translator: Yasunobu YesNo Kawaguchi <kawaguti@gmail.com>\n"
|
||||
"Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"ja/>\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Weblate 5.13\n"
|
||||
"X-Generator: Weblate 5.13.2\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -1384,7 +1384,7 @@ msgstr "言語"
|
||||
#: pretix/control/templates/pretixcontrol/organizers/customer.html:72
|
||||
#: pretix/control/templates/pretixcontrol/organizers/reusable_medium.html:68
|
||||
msgid "Notes"
|
||||
msgstr "注記"
|
||||
msgstr "備考"
|
||||
|
||||
#: pretix/base/exporters/customers.py:100
|
||||
#: pretix/base/exporters/customers.py:101 pretix/base/exporters/events.py:83
|
||||
@@ -4413,7 +4413,7 @@ msgstr "障害を持つ"
|
||||
#: pretix/base/models/customers.py:310 pretix/base/models/orders.py:1538
|
||||
#: pretix/base/models/orders.py:3280 pretix/base/settings.py:1150
|
||||
msgid "Company name"
|
||||
msgstr "企業名"
|
||||
msgstr "会社名"
|
||||
|
||||
#: pretix/base/models/customers.py:314 pretix/base/models/orders.py:1542
|
||||
#: pretix/base/models/orders.py:3287 pretix/base/settings.py:83
|
||||
@@ -14119,7 +14119,7 @@ msgstr "検索クエリ"
|
||||
#: pretix/control/templates/pretixcontrol/organizers/giftcard_acceptance_list.html:133
|
||||
#: pretix/control/templates/pretixcontrol/organizers/reusable_medium.html:39
|
||||
msgid "active"
|
||||
msgstr "アクティブ"
|
||||
msgstr "有効"
|
||||
|
||||
#: pretix/control/forms/filter.py:1499
|
||||
#: pretix/control/templates/pretixcontrol/organizers/customer.html:44
|
||||
@@ -20414,7 +20414,7 @@ msgstr "理由"
|
||||
#: pretix/control/templates/pretixcontrol/event/tax_edit.html:137
|
||||
#: pretix/control/templates/pretixcontrol/subevents/bulk.html:251
|
||||
msgid "Add a new rule"
|
||||
msgstr "新しいルールを追加する"
|
||||
msgstr "新しいルールを追加"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/event/tax_edit.html:153
|
||||
#: pretix/control/templates/pretixcontrol/organizers/edit.html:391
|
||||
|
||||
@@ -48,8 +48,13 @@ function async_task_on_success(data) {
|
||||
history.replaceState({}, "pretix", async_task_old_url);
|
||||
}
|
||||
}
|
||||
if (!async_task_dont_redirect)
|
||||
if (!async_task_dont_redirect) {
|
||||
$(window).one("pageshow", function (e) {
|
||||
// hide waitingDialog when using browser's history back
|
||||
waitingDialog.hide();
|
||||
});
|
||||
location.href = data.redirect;
|
||||
}
|
||||
$(this).trigger('pretix:async-task-success', data);
|
||||
}
|
||||
|
||||
|
||||
@@ -225,6 +225,16 @@ var form_handlers = function (el) {
|
||||
};
|
||||
|
||||
function setup_basics(el) {
|
||||
el.find("form").attr("novalidate", true).on("submit", function (e) {
|
||||
if (!this.checkValidity()) {
|
||||
var input = this.querySelector(":invalid:not(fieldset)");
|
||||
(input.labels[0] || input).scrollIntoView();
|
||||
// only use reportValidity, which usually sets focus on element
|
||||
// input.focus() opens dropdowns, which is not what we want
|
||||
input.reportValidity();
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
el.find("input[data-toggle=radiocollapse]").change(function () {
|
||||
$($(this).attr("data-parent")).find(".collapse.in").collapse('hide');
|
||||
$($(this).attr("data-target")).collapse('show');
|
||||
|
||||
31
src/pretix/testutils/mail.py
Normal file
31
src/pretix/testutils/mail.py
Normal file
@@ -0,0 +1,31 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import smtplib
|
||||
|
||||
from django.core.mail.backends.locmem import EmailBackend
|
||||
|
||||
|
||||
class FailingEmailBackend(EmailBackend):
|
||||
def send_messages(self, email_messages):
|
||||
raise smtplib.SMTPRecipientsRefused({
|
||||
'recipient@example.org': (450, b'Recipient unknown')
|
||||
})
|
||||
@@ -231,9 +231,7 @@ TEST_INVOICE_RES = {
|
||||
"description": "Budget Ticket<br />Attendee: Peter",
|
||||
'subevent': None,
|
||||
'event_date_from': '2017-12-27T10:00:00Z',
|
||||
'event_date_to': '2017-12-27T10:00:00Z',
|
||||
'period_start': '2017-12-27T10:00:00Z',
|
||||
'period_end': '2017-12-27T10:00:00Z',
|
||||
'event_date_to': None,
|
||||
'event_location': None,
|
||||
'attendee_name': 'Peter',
|
||||
'item': None,
|
||||
@@ -251,9 +249,7 @@ TEST_INVOICE_RES = {
|
||||
"description": "Payment fee",
|
||||
'subevent': None,
|
||||
'event_date_from': '2017-12-27T10:00:00Z',
|
||||
'event_date_to': '2017-12-27T10:00:00Z',
|
||||
'period_start': '2017-12-27T10:00:00Z',
|
||||
'period_end': '2017-12-27T10:00:00Z',
|
||||
'event_date_to': None,
|
||||
'event_location': None,
|
||||
'attendee_name': None,
|
||||
'fee_type': "payment",
|
||||
|
||||
@@ -608,9 +608,7 @@ def test_order_create_invoice(token_client, organizer, event, order):
|
||||
'description': 'Budget Ticket<br />Attendee: Peter',
|
||||
'subevent': None,
|
||||
'event_date_from': '2017-12-27T10:00:00Z',
|
||||
'event_date_to': '2017-12-27T10:00:00Z',
|
||||
'period_start': '2017-12-27T10:00:00Z',
|
||||
'period_end': '2017-12-27T10:00:00Z',
|
||||
'event_date_to': None,
|
||||
'event_location': None,
|
||||
'fee_type': None,
|
||||
'fee_internal_type': None,
|
||||
@@ -628,9 +626,7 @@ def test_order_create_invoice(token_client, organizer, event, order):
|
||||
'description': 'Payment fee',
|
||||
'subevent': None,
|
||||
'event_date_from': '2017-12-27T10:00:00Z',
|
||||
'event_date_to': '2017-12-27T10:00:00Z',
|
||||
'period_start': '2017-12-27T10:00:00Z',
|
||||
'period_end': '2017-12-27T10:00:00Z',
|
||||
'event_date_to': None,
|
||||
'event_location': None,
|
||||
'fee_type': "payment",
|
||||
'fee_internal_type': None,
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
import json
|
||||
from datetime import date, datetime, timedelta, timezone
|
||||
from datetime import date, timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
@@ -42,7 +42,6 @@ from django.utils.itercompat import is_iterable
|
||||
from django.utils.timezone import now
|
||||
from django_countries.fields import Country
|
||||
from django_scopes import scope, scopes_disabled
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.base.invoice import addon_aware_groupby
|
||||
from pretix.base.models import (
|
||||
@@ -63,8 +62,7 @@ def env():
|
||||
with scope(organizer=o):
|
||||
event = Event.objects.create(
|
||||
organizer=o, name='Dummy', slug='dummy',
|
||||
date_from=datetime(2024, 12, 1, 9, 0, 0, tzinfo=timezone.utc),
|
||||
plugins='pretix.plugins.banktransfer'
|
||||
date_from=now(), plugins='pretix.plugins.banktransfer'
|
||||
)
|
||||
o = Order.objects.create(
|
||||
code='FOO', event=event, email='dummy@dummy.test',
|
||||
@@ -662,154 +660,3 @@ def test_addon_aware_groupby():
|
||||
[True, 102, 3.00],
|
||||
]],
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("period", ["auto", "event_date"])
|
||||
def test_period_from_event_start(env, period):
|
||||
event, order = env
|
||||
event.settings.invoice_period = period
|
||||
inv = generate_invoice(order)
|
||||
l1 = inv.lines.first()
|
||||
assert l1.period_start == event.date_from
|
||||
assert l1.period_end == event.date_from
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("period", ["auto", "event_date"])
|
||||
def test_period_from_event_range(env, period):
|
||||
event, order = env
|
||||
event.date_to = event.date_from + timedelta(days=1)
|
||||
event.settings.invoice_period = period
|
||||
inv = generate_invoice(order)
|
||||
l1 = inv.lines.first()
|
||||
assert l1.period_start == event.date_from
|
||||
assert l1.period_end == event.date_to
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("period", ["auto", "auto_no_event"])
|
||||
def test_period_from_ticket_validity(env, period):
|
||||
event, order = env
|
||||
p1 = order.positions.first()
|
||||
p1.valid_from = datetime(2025, 1, 1, 0, 0, 0, tzinfo=event.timezone)
|
||||
p1.valid_until = datetime(2025, 12, 31, 23, 59, 59, tzinfo=event.timezone)
|
||||
p1.save()
|
||||
event.date_to = event.date_from + timedelta(days=1)
|
||||
event.settings.invoice_period = period
|
||||
inv = generate_invoice(order)
|
||||
l1 = inv.lines.first()
|
||||
assert l1.period_start == p1.valid_from
|
||||
assert l1.period_end == p1.valid_until
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("period", ["auto", "auto_no_event"])
|
||||
def test_period_from_subevent(env, period):
|
||||
event, order = env
|
||||
event.has_subevents = True
|
||||
event.save()
|
||||
se1 = event.subevents.create(
|
||||
name=event.name,
|
||||
active=True,
|
||||
date_from=datetime((now().year + 1), 7, 31, 9, 0, 0, tzinfo=timezone.utc),
|
||||
date_to=datetime((now().year + 1), 7, 31, 17, 0, 0, tzinfo=timezone.utc),
|
||||
)
|
||||
p1 = order.positions.first()
|
||||
p1.subevent = se1
|
||||
p1.save()
|
||||
event.date_to = event.date_from + timedelta(days=1)
|
||||
event.settings.invoice_period = period
|
||||
inv = generate_invoice(order)
|
||||
l1 = inv.lines.first()
|
||||
assert l1.period_start == se1.date_from
|
||||
assert l1.period_end == se1.date_to
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("period", ["auto", "auto_no_event"])
|
||||
def test_period_from_memberships(env, period):
|
||||
event, order = env
|
||||
event.date_to = event.date_from + timedelta(days=1)
|
||||
event.settings.invoice_period = period
|
||||
p1 = order.positions.first()
|
||||
membershiptype = event.organizer.membership_types.create(
|
||||
name=LazyI18nString({"en": "Week pass"}),
|
||||
transferable=True,
|
||||
allow_parallel_usage=False,
|
||||
max_usages=15,
|
||||
)
|
||||
customer = event.organizer.customers.create(
|
||||
identifier="8WSAJCJ",
|
||||
email="foo@example.org",
|
||||
name_parts={"_legacy": "Foo"},
|
||||
name_cached="Foo",
|
||||
is_verified=False,
|
||||
)
|
||||
m = customer.memberships.create(
|
||||
membership_type=membershiptype,
|
||||
granted_in=p1,
|
||||
date_start=datetime(2021, 4, 1, 0, 0, 0, 0, tzinfo=timezone.utc),
|
||||
date_end=datetime(2021, 4, 8, 23, 59, 59, 999999, tzinfo=timezone.utc),
|
||||
attendee_name_parts={
|
||||
"_scheme": "given_family",
|
||||
'given_name': 'John',
|
||||
'family_name': 'Doe',
|
||||
}
|
||||
)
|
||||
inv = generate_invoice(order)
|
||||
l1 = inv.lines.first()
|
||||
assert l1.period_start == m.date_start
|
||||
assert l1.period_end == m.date_end
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_period_auto_no_event_from_invoice(env):
|
||||
event, order = env
|
||||
event.settings.invoice_period = "auto_no_event"
|
||||
inv = generate_invoice(order)
|
||||
l1 = inv.lines.first()
|
||||
assert abs(l1.period_start - now()) < timedelta(seconds=10)
|
||||
assert abs(l1.period_end - now()) < timedelta(seconds=10)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_period_always_invoice_date(env):
|
||||
event, order = env
|
||||
p1 = order.positions.first()
|
||||
p1.valid_from = datetime(2025, 1, 1, 0, 0, 0, tzinfo=event.timezone)
|
||||
p1.valid_until = datetime(2025, 12, 31, 23, 59, 59, tzinfo=event.timezone)
|
||||
p1.save()
|
||||
event.settings.invoice_period = "invoice_date"
|
||||
inv = generate_invoice(order)
|
||||
l1 = inv.lines.first()
|
||||
assert abs(l1.period_start - now()) < timedelta(seconds=10)
|
||||
assert abs(l1.period_end - now()) < timedelta(seconds=10)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_period_always_event_date(env):
|
||||
event, order = env
|
||||
p1 = order.positions.first()
|
||||
p1.valid_from = datetime(2025, 1, 1, 0, 0, 0, tzinfo=event.timezone)
|
||||
p1.valid_until = datetime(2025, 12, 31, 23, 59, 59, tzinfo=event.timezone)
|
||||
p1.save()
|
||||
event.settings.invoice_period = "event_date"
|
||||
inv = generate_invoice(order)
|
||||
l1 = inv.lines.first()
|
||||
assert l1.period_start == event.date_from
|
||||
assert l1.period_end == event.date_from
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_period_always_order_date(env):
|
||||
event, order = env
|
||||
p1 = order.positions.first()
|
||||
p1.valid_from = datetime(2025, 1, 1, 0, 0, 0, tzinfo=event.timezone)
|
||||
p1.valid_until = datetime(2025, 12, 31, 23, 59, 59, tzinfo=event.timezone)
|
||||
p1.save()
|
||||
event.settings.invoice_period = "order_date"
|
||||
inv = generate_invoice(order)
|
||||
l1 = inv.lines.first()
|
||||
assert l1.period_start == order.datetime
|
||||
assert l1.period_end == order.datetime
|
||||
|
||||
@@ -29,7 +29,7 @@ import pytest
|
||||
from django.conf import settings
|
||||
from django.core import mail as djmail
|
||||
from django.db.models import F, Sum
|
||||
from django.test import TestCase
|
||||
from django.test import TestCase, override_settings
|
||||
from django.utils.timezone import make_aware, now
|
||||
from django_countries.fields import Country
|
||||
from django_scopes import scope
|
||||
@@ -811,6 +811,57 @@ def test_mark_invoices_as_sent(event):
|
||||
assert i.transmission_provider == "email_pdf"
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
@override_settings(EMAIL_BACKEND='pretix.testutils.mail.FailingEmailBackend')
|
||||
def test_mark_invoices_as_failed(event):
|
||||
djmail.outbox = []
|
||||
event.settings.invoice_address_asked = True
|
||||
event.settings.invoice_address_required = True
|
||||
event.settings.invoice_generate = "True"
|
||||
event.settings.invoice_email_attachment = True
|
||||
o1 = Order.objects.create(
|
||||
code='FOO', event=event, email='dummy@dummy.test',
|
||||
status=Order.STATUS_PENDING,
|
||||
datetime=now(), expires=now() - timedelta(days=10),
|
||||
total=10, locale='en',
|
||||
sales_channel=event.organizer.sales_channels.get(identifier="web"),
|
||||
)
|
||||
ticket = Item.objects.create(event=event, name='Early-bird ticket',
|
||||
default_price=Decimal('23.00'), admission=True)
|
||||
OrderPosition.objects.create(
|
||||
order=o1, item=ticket, variation=None, price=Decimal("23.00"),
|
||||
attendee_name_parts={'full_name': "Peter"},
|
||||
positionid=1
|
||||
)
|
||||
ia = InvoiceAddress.objects.create(
|
||||
order=o1,
|
||||
is_business=True,
|
||||
country=Country('AT'),
|
||||
transmission_type="email",
|
||||
transmission_info={
|
||||
"transmission_email_address": "invoice@example.org",
|
||||
}
|
||||
)
|
||||
o1.create_transactions()
|
||||
i = generate_invoice(o1)
|
||||
assert i.transmission_type == "email"
|
||||
assert i.transmission_status == Invoice.TRANSMISSION_STATUS_PENDING
|
||||
assert not i.transmission_provider
|
||||
|
||||
# If no other address is there, order address will be accepted
|
||||
ia.transmission_info = {}
|
||||
ia.save()
|
||||
o1.send_mail(
|
||||
subject=LazyI18nString({"en": "Hey"}),
|
||||
template=LazyI18nString({"en": "Just wanted to send this invoice"}),
|
||||
context={},
|
||||
invoices=[i]
|
||||
)
|
||||
i.refresh_from_db()
|
||||
assert i.transmission_status == Invoice.TRANSMISSION_STATUS_FAILED
|
||||
assert i.transmission_provider == "email_pdf"
|
||||
|
||||
|
||||
class PaymentReminderTests(TestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
Reference in New Issue
Block a user