Compare commits

..

25 Commits

Author SHA1 Message Date
Mira Weller
840cc6edda Fix log entry details for datasync logs without external link (Z#23210015) 2025-10-07 13:41:35 +02:00
Martin Gross
22f351cb89 OCM: Ignore already canceled addons in remaining item calculation (Z#23209824) 2025-10-02 10:30:33 +02:00
Raphael Michel
2611ff74a5 Badges: Fix incorrect offsets for DURABLE 8334-02 2025-10-02 09:57:59 +02:00
Hijiri Umemoto
cc1c7e1c23 Translations: Update Japanese
Currently translated at 100.0% (6076 of 6076 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ja/

powered by weblate
2025-10-02 09:50:39 +02:00
CVZ-es
e2eedac93b Translations: Update Spanish
Currently translated at 100.0% (6076 of 6076 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2025-10-02 09:50:39 +02:00
CVZ-es
432064c3ae Translations: Update French
Currently translated at 100.0% (6076 of 6076 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/fr/

powered by weblate
2025-10-02 09:50:39 +02:00
Raphael Michel
457115f4ca Add refund comment to gift card transaction text (Z#23208349) (#5499)
* Add refund comment to gift card transaction text (Z#23208349)

* Update src/pretix/base/models/giftcards.py

Co-authored-by: luelista <weller@rami.io>

---------

Co-authored-by: luelista <weller@rami.io>
2025-10-02 09:38:32 +02:00
luelista
9d5563018e Add "bulk" argument to order_placed signal (#5505)
* datasync: add immediate parameter to enqueue_order

* interactive argument for order_placed signal

The ``interactive`` argument specifies whether the order was
placed interactively, by a customer (as opposed to via a bulk
import or the REST API).

* use bulk=True instead of interactive=False to mark bulk imports
2025-10-02 09:36:02 +02:00
luelista
425f4da1f1 datasync: add immediate parameter to enqueue_order (#5504) 2025-10-02 09:34:56 +02:00
dependabot[bot]
aa0ea27d6c Update isort requirement from ==6.0.* to ==6.1.* (#5507) 2025-10-02 09:32:10 +02:00
dependabot[bot]
5a2219124a Bump @babel/core from 7.28.3 to 7.28.4 in /src/pretix/static/npm_dir (#5506)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.28.3 to 7.28.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.28.4/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-version: 7.28.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-02 09:32:02 +02:00
luelista
f79813ea32 datasync: return a reference to newly create queue item (#5502) 2025-09-30 17:25:25 +02:00
luelista
ba62db7a19 Fix version number (2024 -> 2025) 2025-09-30 16:01:27 +02:00
Jan Van Haver
aa8b699b89 Translations: Update Dutch
Currently translated at 96.9% (5892 of 6076 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl/

powered by weblate
2025-09-30 09:19:26 +02:00
dependabot[bot]
6adabd54dc Update beautifulsoup4 requirement from ==4.13.* to ==4.14.*
Updates the requirements on [beautifulsoup4](https://www.crummy.com/software/BeautifulSoup/bs4/) to permit the latest version.

---
updated-dependencies:
- dependency-name: beautifulsoup4
  dependency-version: 4.14.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-30 09:19:21 +02:00
Raphael Michel
e61f8035d3 Fix typo 2025-09-29 16:21:33 +02:00
Raphael Michel
fc4a9406e1 Invoicing: Allow plugins to add data (#5452)
* Allow plugins to add data to invoices

* Add documentation
2025-09-29 13:15:42 +02:00
Raphael Michel
9d2ef94389 Invoicing: Configurable service date 2025-09-29 13:15:42 +02:00
Raphael Michel
c060322f2f Translations: Update Catalan
Currently translated at 31.1% (1895 of 6076 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ca/

powered by weblate
2025-09-29 10:43:22 +02:00
Yasunobu YesNo Kawaguchi
7629bbbb6a Translations: Update Japanese
Currently translated at 99.8% (6064 of 6076 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ja/

powered by weblate
2025-09-29 10:43:22 +02:00
Raphael Michel
21d084c3fa Translations: Update Italian
Currently translated at 37.2% (2261 of 6076 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2025-09-29 10:43:22 +02:00
Raphael Michel
b8d09a15e2 Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (6076 of 6076 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2025-09-29 10:43:22 +02:00
Raphael Michel
af2d35cc5a Translations: Update German
Currently translated at 100.0% (6076 of 6076 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2025-09-29 10:43:22 +02:00
Yasunobu YesNo Kawaguchi
cbe18608e4 Translations: Update Japanese
Currently translated at 100.0% (6068 of 6068 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ja/

powered by weblate
2025-09-29 10:43:22 +02:00
Raphael Michel
37d0a0de22 Bump version to 2024.9.0.dev0 2025-09-29 09:37:14 +02:00
37 changed files with 1157 additions and 796 deletions

View File

@@ -80,17 +80,12 @@ 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).
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.
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_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
@@ -274,6 +269,8 @@ 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",
@@ -420,6 +417,8 @@ 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",

View File

@@ -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, 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, build_invoice_data, invoice_line_text
Check-ins
"""""""""

View File

@@ -28,7 +28,7 @@ classifiers = [
dependencies = [
"arabic-reshaper==3.0.0", # Support for Arabic in reportlab
"babel",
"BeautifulSoup4==4.13.*",
"BeautifulSoup4==4.14.*",
"bleach==6.2.*",
"celery==5.5.*",
"chardet==5.2.*",
@@ -113,7 +113,7 @@ dev = [
"fakeredis==2.31.*",
"flake8==7.3.*",
"freezegun",
"isort==6.0.*",
"isort==6.1.*",
"pep8-naming==0.15.*",
"potypo",
"pytest-asyncio>=0.24",

View File

@@ -19,4 +19,4 @@
# 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/>.
#
__version__ = "2025.8.0"
__version__ = "2025.9.0.dev0"

View File

@@ -805,6 +805,7 @@ class EventSettingsSerializer(SettingsSerializer):
'invoice_reissue_after_modify',
'invoice_include_free',
'invoice_generate',
'invoice_period',
'invoice_numbers_consecutive',
'invoice_numbers_prefix',
'invoice_numbers_prefix_cancellations',

View File

@@ -1757,12 +1757,14 @@ 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', 'gross_value', 'tax_value', 'tax_rate', 'tax_code', 'tax_name', 'fee_type',
'fee_internal_type', 'event_location')
'event_date_to', 'period_start', 'period_end', 'gross_value', 'tax_value', 'tax_rate', 'tax_code',
'tax_name', 'fee_type', 'fee_internal_type', 'event_location')
class InvoiceSerializer(I18nAwareModelSerializer):

View File

@@ -743,7 +743,7 @@ class EventOrderViewSet(OrderViewSetMixin, viewsets.ModelViewSet):
user=request.user if request.user.is_authenticated else None,
auth=request.auth,
)
order_placed.send(self.request.event, order=order)
order_placed.send(self.request.event, order=order, bulk=False)
if order.status == Order.STATUS_PAID:
order_paid.send(self.request.event, order=order)
order.log_action(

View File

@@ -106,7 +106,7 @@ class OutboundSyncProvider:
return str(cls.identifier)
@classmethod
def enqueue_order(cls, order, triggered_by, not_before=None):
def enqueue_order(cls, order, triggered_by, not_before=None, immediate=False):
"""
Adds an order to the sync queue. May only be called on derived classes which define an ``identifier`` attribute.
@@ -119,10 +119,14 @@ class OutboundSyncProvider:
:param order: the Order that should be synced
:param triggered_by: the reason why the order should be synced, e.g. name of the signal
(currently only used internally for logging)
:param immediate: whether a new sync task should run immediately for this order, instead
of waiting for the next periodic_task interval
:return: Return a tuple (queue_item, created), where created is a boolean
specifying whether a new queue item was created.
"""
if not hasattr(cls, 'identifier'):
raise TypeError('Call this method on a derived class that defines an "identifier" attribute.')
OrderSyncQueue.objects.update_or_create(
queue_item, created = OrderSyncQueue.objects.update_or_create(
order=order,
sync_provider=cls.identifier,
in_flight=False,
@@ -133,6 +137,10 @@ class OutboundSyncProvider:
"need_manual_retry": None,
},
)
if immediate:
from pretix.base.services.datasync import sync_single
sync_single.apply_async(args=(queue_item.pk,))
return queue_item, created
@classmethod
def get_external_link_info(cls, event, external_link_href, external_link_display_name):

View File

@@ -19,6 +19,7 @@
# 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 math
import re
@@ -526,6 +527,20 @@ 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)
@@ -539,25 +554,17 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
p_size = p.wrap(self.event_width, self.event_height)
return txt
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()
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 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)
)
elif d_from:
p_str = shorten(self.invoice.event.name) + '\n' + date_format(d_from, "DATE_FORMAT")
else:
p_str = shorten(self.invoice.event.name)
@@ -685,7 +692,14 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
return story
def _get_story(self, doc):
has_taxes = any(il.tax_value for il in self.invoice.lines.all()) or self.invoice.reverse_charge
all_lines = list(self.invoice.lines.all())
has_taxes = any(il.tax_value for il in all_lines) 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 all_lines
)) > 1
request_show_service_date = False
story = [
NextPageTemplate('FirstPage'),
@@ -729,15 +743,75 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
)]
def _group_key(line):
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)
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()
total = Decimal('0.00')
for (description, tax_rate, tax_name, net_value, gross_value, *ignored), lines in addon_aware_groupby(
self.invoice.lines.all(),
for (description, tax_rate, tax_name, net_value, gross_value, subevent, period_start, period_end), lines in addon_aware_groupby(
all_lines,
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.
period_start_day = day(period_start)
period_end_day = day(period_end)
if period_start and period_end and period_end_day != period_start_day:
# It's a multi-day period, such as the validity of the ticket or an event date period
if period_start_day == header_dates[0] and period_end_day == 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) == period_start_day and
day(subevent.date_to) == period_end_day):
# For subevents, build_invoice already includes the date in the description in the event-default format.
period_line = ""
else:
period_line = f"{date_format(period_start_day, 'SHORT_DATE_FORMAT')} {date_format(period_end_day, 'SHORT_DATE_FORMAT')}"
elif period_start or period_end:
# It's a single-day period
delivery_day = period_end_day or period_start_day
if delivery_day in header_dates:
# 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
elif period_line:
description += "\n" + period_line
lines = list(lines)
if has_taxes:
if len(lines) > 1:
@@ -746,6 +820,7 @@ 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']),
@@ -850,6 +925,12 @@ 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),

View File

@@ -0,0 +1,78 @@
# 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"],
)
cache.delete_many(flush_queue)
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",
)
)
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,
)
]

View File

@@ -0,0 +1,18 @@
# 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),
),
]

View File

@@ -195,20 +195,21 @@ class GiftCardTransaction(models.Model):
return response
if self.order_id:
if not self.text:
if not customer_facing:
return format_html(
'<a href="{}">{}</a>',
reverse(
"control:event.order",
kwargs={
"event": self.order.event.slug,
"organizer": self.order.event.organizer.slug,
"code": self.order.code,
}
),
self.order.full_code
)
if not customer_facing:
return format_html(
'<a href="{}">{}</a> {}',
reverse(
"control:event.order",
kwargs={
"event": self.order.event.slug,
"organizer": self.order.event.organizer.slug,
"code": self.order.code,
}
),
self.order.full_code,
self.text or "",
)
elif not self.text:
return self.order.full_code
else:
return self.text

View File

@@ -33,6 +33,7 @@
# License for the specific language governing permissions and limitations under the License.
import string
import warnings
from decimal import Decimal
import pycountry
@@ -201,6 +202,7 @@ 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')
@@ -404,10 +406,10 @@ class InvoiceLine(models.Model):
:type tax_name: str
:param subevent: The subevent this line refers to
:type subevent: SubEvent
: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 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_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
@@ -426,8 +428,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)
event_date_from = models.DateTimeField(null=True)
event_date_to = models.DateTimeField(null=True)
period_start = models.DateTimeField(null=True)
period_end = 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)
@@ -444,3 +446,35 @@ 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

View File

@@ -1627,6 +1627,7 @@ class GiftCardPayment(BasePaymentProvider):
order=refund.order,
refund=refund,
acceptor=self.event.organizer,
text=refund.comment,
)
refund.info_data = {
'gift_card': gc.pk,

View File

@@ -61,7 +61,9 @@ from pretix.base.models.tax import EU_CURRENCIES
from pretix.base.services.tasks import (
TransactionAwareProfiledEventTask, TransactionAwareTask,
)
from pretix.base.signals import invoice_line_text, periodic_task
from pretix.base.signals import (
build_invoice_data, 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
@@ -82,6 +84,10 @@ 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')
@@ -208,7 +214,9 @@ 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').order_by('positionid', 'id')
).prefetch_related(
'answers', 'answers__options', 'answers__question', 'granted_memberships',
).order_by('positionid', 'id')
)
reverse_charge = False
@@ -267,6 +275,10 @@ 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,
@@ -277,8 +289,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,
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,
period_start=period_start,
period_end=period_end,
event_location=location if invoice.event.settings.invoice_event_location else None,
tax_rate=p.tax_rate,
tax_code=p.tax_code,
@@ -301,13 +313,29 @@ 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,
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,
period_start=period_start,
period_end=period_end,
event_location=(
None if invoice.event.has_subevents
else (str(invoice.event.location)
@@ -336,6 +364,7 @@ def build_invoice(invoice: Invoice) -> Invoice:
invoice.reverse_charge = reverse_charge
invoice.save()
build_invoice_data.send(sender=invoice.event, invoice=invoice)
return invoice
@@ -351,6 +380,55 @@ 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 and position.valid_until:
period_start = position.valid_from
period_end = position.valid_until
elif position.valid_from:
period_start = position.valid_from
period_end = position.valid_from # weird, but we have nothing else to base this on
elif position.valid_until:
period_start = min(invoice.order.datetime, position.valid_until)
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.")
@@ -456,6 +534,12 @@ 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(),
@@ -506,8 +590,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,
event_date_from=event.date_from,
event_date_to=event.date_to,
period_start=period_start,
period_end=period_end,
event_location=event.settings.invoice_event_location,
)
else:
@@ -515,8 +599,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,
event_date_from=event.date_from,
event_date_to=event.date_to,
period_start=period_start,
period_end=period_end,
event_location=event.settings.invoice_event_location,
)

View File

@@ -221,7 +221,7 @@ def import_orders(event: Event, fileid: str, settings: dict, locale: str, user,
for o in orders:
with language(o.locale, event.settings.region):
order_placed.send(event, order=o)
order_placed.send(event, order=o, bulk=True)
if o.status == Order.STATUS_PAID:
order_paid.send(event, order=o)

View File

@@ -1091,7 +1091,7 @@ def _create_order(event: Event, *, email: str, positions: List[CartPosition], no
for msg in meta_info.get('confirm_messages', []):
order.log_action('pretix.event.order.consent', data={'msg': msg})
order_placed.send(event, order=order)
order_placed.send(event, order=order, bulk=False)
return order, payments
@@ -2825,7 +2825,7 @@ class OrderChangeManager:
def _check_complete_cancel(self):
current = self.order.positions.count()
cancels = sum([
1 + o.position.addons.count() for o in self._operations if isinstance(o, self.CancelOperation)
1 + o.position.addons.filter(canceled=False).count() for o in self._operations if isinstance(o, self.CancelOperation)
]) + len([
o for o in self._operations if isinstance(o, self.SplitOperation)
])

View File

@@ -1098,6 +1098,35 @@ 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,

View File

@@ -596,6 +596,18 @@ 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``,
@@ -653,12 +665,13 @@ As with all event-plugin signals, the ``sender`` keyword argument will contain t
order_placed = EventPluginSignal()
"""
Arguments: ``order``
Arguments: ``order``, ``bulk``
This signal is sent out every time an order is placed. The order object is given
as the first argument. This signal is *not* sent out if an order is created through
splitting an existing order, so you can not expect to see all orders by listening
to this signal.
as the first argument. The ``bulk`` argument specifies whether the order was placed
as part of a bulk action, e.g. an import from a file.
This signal is *not* sent out if an order is created through splitting an existing order,
so you can not expect to see all orders by listening to this signal.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""

View File

@@ -857,6 +857,7 @@ 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',

View File

@@ -455,7 +455,7 @@ class OrderDataSyncSuccessLogEntryType(OrderDataSyncLogEntryType):
links.append(", ".join(
prov.get_external_link_html(logentry.event, obj['external_link_href'], obj['external_link_display_name'])
for obj in objs
if obj and 'external_link_href' in obj and 'external_link_display_name' in obj
if obj and obj.get('external_link_href') and obj.get('external_link_display_name')
))
return mark_safe(escape(super().display(logentry, data)) + "".join("<p>" + link + "</p>" for link in links))

View File

@@ -15,6 +15,19 @@
{% 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" %}

View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-26 11:16+0000\n"
"PO-Revision-Date: 2025-04-22 15:36+0000\n"
"Last-Translator: Paul Berschick <paul@plainschwarz.com>\n"
"PO-Revision-Date: 2025-09-29 07:39+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix/"
"ca/>\n"
"Language: ca\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.11\n"
"X-Generator: Weblate 5.13.3\n"
#: pretix/_base_settings.py:87
msgid "English"
@@ -35318,11 +35318,6 @@ msgstr ""
"Tingueu en compte que heu de fer el pagament per a finalitzar el procés."
#: pretix/presale/templates/pretixpresale/event/order.html:55
#, fuzzy
#| msgid ""
#| "Please bookmark or save the link to this exact page if you want to access "
#| "your order later. We also sent you an email containing the link to the "
#| "address you specified."
msgid ""
"Please bookmark or save the link to this exact page if you want to access "
"your order later. We also sent you an email to the address you specified "

View File

@@ -5,7 +5,7 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-26 11:16+0000\n"
"PO-Revision-Date: 2025-09-23 16:13+0000\n"
"PO-Revision-Date: 2025-09-26 13:02+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix/de/"
">\n"
@@ -808,28 +808,23 @@ msgstr ""
"Sie zunächst die E-Mail-Adresse in Ihrem Kundenkonto."
#: pretix/base/datasync/datasync.py:255
#, fuzzy, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
#, python-brace-format
msgid ""
"Field \"{field_name}\" does not exist. Please check your {provider_name} "
"settings."
msgstr ""
"Feld \"{field_name}\" ist nicht gültig für {available_inputs}. Bitte prüfen "
"Sie die Einstellungen für {provider_name}."
"Feld \"{field_name}\" existiert nicht. Bitte prüfen Sie die Einstellungen "
"für {provider_name}."
#: pretix/base/datasync/datasync.py:262
#, fuzzy, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
#, python-brace-format
msgid ""
"Field \"{field_name}\" requires {required_input}, but only got "
"{available_inputs}. Please check your {provider_name} settings."
msgstr ""
"Feld \"{field_name}\" ist nicht gültig für {available_inputs}. Bitte prüfen "
"Sie die Einstellungen für {provider_name}."
"Feld \"{field_name}\" erfordert, {required_input}, aber nur "
"{available_inputs} sind vorhanden. Bitte prüfen Sie die Einstellungen für "
"{provider_name}."
#: pretix/base/datasync/datasync.py:273
#, python-brace-format
@@ -3548,6 +3543,10 @@ msgid ""
"in accordance with the procedures and terms set forth in No. 89757/2018 of "
"April 30, 2018, issued by the Director of the Revenue Agency."
msgstr ""
"Diese PDF-Datei ist eine Sichtkopie der Rechnung und stellt keine Rechnung "
"für Umsatzsteuerzwecke dar. Die Rechnung wurde im XML-Format ausgestellt und "
"im Einklang mit den Regularien aus dem Dekret Nr. 89757/2018 vom 30. April "
"2018 des Direktors der Finanzbehörde übermittelt."
#: pretix/base/invoicing/pdf.py:141
#, python-format
@@ -3817,12 +3816,9 @@ msgid "Peppol participant ID"
msgstr "Peppol-Teilnehmer-ID"
#: pretix/base/invoicing/peppol.py:170
#, fuzzy
#| msgctxt "italian_invoice"
#| msgid "Fiscal code"
msgctxt "peppol_invoice"
msgid "Visual copy"
msgstr "Steuernummer"
msgstr "Sichtkopie"
#: pretix/base/invoicing/peppol.py:175
msgctxt "peppol_invoice"
@@ -3831,6 +3827,9 @@ msgid ""
"invoice for VAT purposes. The original invoice is issued in XML format and "
"transmitted through the Peppol network."
msgstr ""
"Diese PDF-Datei ist eine Sichtkopie der Rechnung und stellt keine Rechnung "
"für Umsatzsteuerzwecke dar. Die Originalrechnung wurde im XML-Format "
"ausgestellt und über das Peppol-Netzwerk übertragen."
#: pretix/base/logentrytype_registry.py:43
msgid ""
@@ -14406,10 +14405,8 @@ msgid "Canceled (fully or with paid fee)"
msgstr "Storniert (komplett oder mit Gebühr)"
#: pretix/control/forms/filter.py:228
#, fuzzy
#| msgid "Cancel this position"
msgid "Canceled (at least one position)"
msgstr "Diese Position stornieren"
msgstr "storniert (mindestens eine Position)"
#: pretix/control/forms/filter.py:229
msgid "Cancellation requested"
@@ -17149,10 +17146,9 @@ msgid "The voucher has been deleted."
msgstr "Der Gutschein wurde gelöscht."
#: pretix/control/logdisplay.py:584
#, fuzzy, python-brace-format
#| msgid "The voucher has been sent to {email} through the waiting list."
#, python-brace-format
msgid "The voucher has been assigned to {email} through the waiting list."
msgstr "Der Gutschein wurde über die Warteliste an {email} verschickt."
msgstr "Der Gutschein wurde über die Warteliste an {email} zugewiesen."
#: pretix/control/logdisplay.py:593
#, python-brace-format
@@ -27691,11 +27687,11 @@ msgstr ""
"ausgeführt."
#: pretix/control/views/datasync.py:90 pretix/control/views/datasync.py:104
#, fuzzy
#| msgid "The voucher \"{voucher}\" has been used in the meantime."
msgid ""
"The sync job could not be found. It may have been processed in the meantime."
msgstr "Der Gutschein \"{voucher}\" wurde zwischenzeitlich verwendet."
msgstr ""
"Der Synchronisationsauftrag wurde nicht gefunden. Möglicherweise wurde er "
"zwischenzeitlich bearbeitet."
#: pretix/control/views/datasync.py:93 pretix/control/views/datasync.py:107
msgid "The sync job is already in progress."
@@ -28689,12 +28685,12 @@ msgstr ""
"im Anhang dieser E-Mail finden Sie einen neuen Bericht für {name}."
#: pretix/control/views/orders.py:2849 pretix/control/views/organizer.py:2207
#, fuzzy
#| msgid "You do not have sufficient permission to perform this export."
msgid ""
"Your user account does not have sufficient permission to run this report, "
"therefore you cannot schedule it."
msgstr "Sie haben keine Berechtigung, um diesen Export durchzuführen."
msgstr ""
"Sie haben nicht genug Berechtigungen um diesen Bericht auszuführen, daher "
"können Sie ihn nicht einplanen."
#: pretix/control/views/orders.py:2902 pretix/control/views/organizer.py:2259
msgid ""
@@ -29061,17 +29057,13 @@ msgstr ""
"Der Termin kann nicht gelöscht werden, da es bereits Bestellungen dafür gibt."
#: pretix/control/views/subevents.py:203
#, fuzzy
#| msgid ""
#| "The channel could not be deleted as some constraints (e.g. data created "
#| "by plug-ins) did not allow it."
msgctxt "subevent"
msgid ""
"The date could not be deleted as some constraints (e.g. data created by plug-"
"ins) did not allow it. The date was disabled instead."
msgstr ""
"Der Verkaufskanal konnte nicht gelöscht werden, da einige Bedingungen (z.B. "
"von Plugins erstellte Daten) es nicht erlauben."
"Der Termin konnte nicht gelöscht werden, da einige Bedingungen (z.B. Daten "
"von Plugins) es verhindert haben. Der Termin wurde stattdessen deaktiviert."
#: pretix/control/views/subevents.py:207
msgctxt "subevent"
@@ -33875,18 +33867,12 @@ msgstr ""
"Bestellung bestätigt werden kann und einen gültigen Vertrag darstellt."
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:192
#, fuzzy
#| msgid ""
#| "After you submitted your order using the button below, it will require "
#| "approval by the event organizer before it can be confirmed and forms a "
#| "valid contract."
msgid ""
"After you submitted your order using the button below, it will require "
"approval by the event organizer."
msgstr ""
"Nachdem Sie Ihre Bestellung mit dem untenstehenden Button abgeschickt haben, "
"erfordert sie noch eine Freigabe durch den Veranstalter, bevor die "
"Bestellung bestätigt werden kann und einen gültigen Vertrag darstellt."
"erfordert sie noch eine Freigabe durch den Veranstalter."
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:195
msgid ""
@@ -36491,10 +36477,8 @@ msgid "The selected date does not exist in this event series."
msgstr "Der ausgewählte Termin gehört nicht zu dieser Veranstaltungsreihe."
#: pretix/presale/views/widget.py:412
#, fuzzy
#| msgid "The selected seat \"{seat}\" is not available."
msgid "The selected date is not available."
msgstr "Der ausgewählte Sitzplatz \"{seat}\" ist nicht verfügbar."
msgstr "Der gewählte Termin ist nicht verfügbar."
#: pretix/presale/views/widget.py:476
#, python-format

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-26 11:16+0000\n"
"PO-Revision-Date: 2025-09-23 16:13+0000\n"
"PO-Revision-Date: 2025-09-26 13:02+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
"pretix/pretix/de_Informal/>\n"
@@ -809,28 +809,23 @@ msgstr ""
"zunächst die E-Mail-Adresse in deinem Kundenkonto."
#: pretix/base/datasync/datasync.py:255
#, fuzzy, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
#, python-brace-format
msgid ""
"Field \"{field_name}\" does not exist. Please check your {provider_name} "
"settings."
msgstr ""
"Feld \"{field_name}\" ist nicht gültig für {available_inputs}. Bitte prüfe "
"die Einstellungen für {provider_name}."
"Feld \"{field_name}\" existiert nicht. Bitte prüfe die Einstellungen für "
"{provider_name}."
#: pretix/base/datasync/datasync.py:262
#, fuzzy, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
#, python-brace-format
msgid ""
"Field \"{field_name}\" requires {required_input}, but only got "
"{available_inputs}. Please check your {provider_name} settings."
msgstr ""
"Feld \"{field_name}\" ist nicht gültig für {available_inputs}. Bitte prüfe "
"die Einstellungen für {provider_name}."
"Feld \"{field_name}\" erfordert {required_input}, aber hat nur "
"{available_inputs} sind verfügbar. Bitte prüfe die Einstellungen für "
"{provider_name}."
#: pretix/base/datasync/datasync.py:273
#, python-brace-format
@@ -3548,6 +3543,10 @@ msgid ""
"in accordance with the procedures and terms set forth in No. 89757/2018 of "
"April 30, 2018, issued by the Director of the Revenue Agency."
msgstr ""
"Diese PDF-Datei ist eine Sichtkopie der Rechnung und stellt keine Rechnung "
"für Umsatzsteuerzwecke dar. Die Rechnung wurde im XML-Format ausgestellt und "
"im Einklang mit den Regularien aus dem Dekret Nr. 89757/2018 vom 30. April "
"2018 des Direktors der Finanzbehörde übermittelt."
#: pretix/base/invoicing/pdf.py:141
#, python-format
@@ -3817,12 +3816,9 @@ msgid "Peppol participant ID"
msgstr "Peppol-Teilnehmer-ID"
#: pretix/base/invoicing/peppol.py:170
#, fuzzy
#| msgctxt "italian_invoice"
#| msgid "Fiscal code"
msgctxt "peppol_invoice"
msgid "Visual copy"
msgstr "Steuernummer"
msgstr "Sichtkopie"
#: pretix/base/invoicing/peppol.py:175
msgctxt "peppol_invoice"
@@ -3831,6 +3827,9 @@ msgid ""
"invoice for VAT purposes. The original invoice is issued in XML format and "
"transmitted through the Peppol network."
msgstr ""
"Diese PDF-Datei ist eine Sichtkopie der Rechnung und stellt keine Rechnung "
"für Umsatzsteuerzwecke dar. Die Originalrechnung wurde im XML-Format "
"ausgestellt und über das Peppol-Netzwerk übertragen."
#: pretix/base/logentrytype_registry.py:43
msgid ""
@@ -14382,10 +14381,8 @@ msgid "Canceled (fully or with paid fee)"
msgstr "Storniert (komplett oder mit Gebühr)"
#: pretix/control/forms/filter.py:228
#, fuzzy
#| msgid "Cancel this position"
msgid "Canceled (at least one position)"
msgstr "Diese Position stornieren"
msgstr "Storniert (mindestens eine Position)"
#: pretix/control/forms/filter.py:229
msgid "Cancellation requested"
@@ -17125,10 +17122,9 @@ msgid "The voucher has been deleted."
msgstr "Der Gutschein wurde gelöscht."
#: pretix/control/logdisplay.py:584
#, fuzzy, python-brace-format
#| msgid "The voucher has been sent to {email} through the waiting list."
#, python-brace-format
msgid "The voucher has been assigned to {email} through the waiting list."
msgstr "Der Gutschein wurde über die Warteliste an {email} verschickt."
msgstr "Der Gutschein wurde über die Warteliste an {email} zugewiesen."
#: pretix/control/logdisplay.py:593
#, python-brace-format
@@ -27650,11 +27646,11 @@ msgstr ""
"ausgeführt."
#: pretix/control/views/datasync.py:90 pretix/control/views/datasync.py:104
#, fuzzy
#| msgid "The voucher \"{voucher}\" has been used in the meantime."
msgid ""
"The sync job could not be found. It may have been processed in the meantime."
msgstr "Der Gutschein \"{voucher}\" wurde zwischenzeitlich verwendet."
msgstr ""
"Der Synchronisationsauftrag wurde nicht gefunden. Möglicherweise wurde er "
"zwischenzeitlich bearbeitet."
#: pretix/control/views/datasync.py:93 pretix/control/views/datasync.py:107
msgid "The sync job is already in progress."
@@ -28645,12 +28641,12 @@ msgstr ""
"im Anhang dieser E-Mail findest du einen neuen Bericht für {name}."
#: pretix/control/views/orders.py:2849 pretix/control/views/organizer.py:2207
#, fuzzy
#| msgid "You do not have sufficient permission to perform this export."
msgid ""
"Your user account does not have sufficient permission to run this report, "
"therefore you cannot schedule it."
msgstr "Du hast nicht die nötige Berechtigung, um diesen Export durchzuführen."
msgstr ""
"Du hast nicht genug Berechtigungen um diesen Bericht auszuführen, daher "
"kannst du ihn nicht einplanen."
#: pretix/control/views/orders.py:2902 pretix/control/views/organizer.py:2259
msgid ""
@@ -29017,17 +29013,13 @@ msgstr ""
"Der Termin kann nicht gelöscht werden, da es bereits Bestellungen dafür gibt."
#: pretix/control/views/subevents.py:203
#, fuzzy
#| msgid ""
#| "The channel could not be deleted as some constraints (e.g. data created "
#| "by plug-ins) did not allow it."
msgctxt "subevent"
msgid ""
"The date could not be deleted as some constraints (e.g. data created by plug-"
"ins) did not allow it. The date was disabled instead."
msgstr ""
"Der Verkaufskanal konnte nicht gelöscht werden, da einige Bedingungen (z.B. "
"von Plugins erstellte Daten) es nicht erlauben."
"Der Termin konnte nicht gelöscht werden, da einige Bedingungen (z.B. Daten "
"von Plugins) es verhindert haben. Der Termin wurde stattdessen deaktiviert."
#: pretix/control/views/subevents.py:207
msgctxt "subevent"
@@ -33820,18 +33812,12 @@ msgstr ""
"Bestellung bestätigt werden kann und einen gültigen Vertrag darstellt."
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:192
#, fuzzy
#| msgid ""
#| "After you submitted your order using the button below, it will require "
#| "approval by the event organizer before it can be confirmed and forms a "
#| "valid contract."
msgid ""
"After you submitted your order using the button below, it will require "
"approval by the event organizer."
msgstr ""
"Nachdem du deine Bestellung mit dem untenstehenden Button abgeschickt hast, "
"erfordert sie noch eine Freigabe durch den Veranstalter, bevor die "
"Bestellung bestätigt werden kann und einen gültigen Vertrag darstellt."
"erfordert sie noch eine Freigabe durch den Veranstalter."
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:195
msgid ""
@@ -36431,10 +36417,8 @@ msgid "The selected date does not exist in this event series."
msgstr "Der ausgewählte Termin gehört nicht zu dieser Veranstaltungsreihe."
#: pretix/presale/views/widget.py:412
#, fuzzy
#| msgid "The selected seat \"{seat}\" is not available."
msgid "The selected date is not available."
msgstr "Der ausgewählte Sitzplatz \"{seat}\" ist nicht verfügbar."
msgstr "Der gewählte Termin ist nicht verfügbar."
#: pretix/presale/views/widget.py:476
#, python-format

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-26 11:16+0000\n"
"PO-Revision-Date: 2025-08-30 02:00+0000\n"
"PO-Revision-Date: 2025-09-30 16:00+0000\n"
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix/"
"es/>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.13\n"
"X-Generator: Weblate 5.13.3\n"
#: pretix/_base_settings.py:87
msgid "English"
@@ -810,28 +810,23 @@ msgstr ""
"Primero verifique la dirección de correo electrónico en su cuenta de cliente."
#: pretix/base/datasync/datasync.py:255
#, fuzzy, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
#, python-brace-format
msgid ""
"Field \"{field_name}\" does not exist. Please check your {provider_name} "
"settings."
msgstr ""
"El campo «{field_name}» no es válido para {available_inputs}. Comprueba la "
"configuración de {provider_name}."
"El campo «{field_name}» no existe. Comprueba la configuración de "
"{provider_name}."
#: pretix/base/datasync/datasync.py:262
#, fuzzy, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
#, python-brace-format
msgid ""
"Field \"{field_name}\" requires {required_input}, but only got "
"{available_inputs}. Please check your {provider_name} settings."
msgstr ""
"El campo «{field_name}» no es válido para {available_inputs}. Comprueba la "
"configuración de {provider_name}."
"El campo «{field_name}» requiere {required_input}, pero solo se ha "
"introducido {available_inputs}. Comprueba la configuración de "
"{provider_name}."
#: pretix/base/datasync/datasync.py:273
#, python-brace-format
@@ -3559,6 +3554,11 @@ msgid ""
"in accordance with the procedures and terms set forth in No. 89757/2018 of "
"April 30, 2018, issued by the Director of the Revenue Agency."
msgstr ""
"Este documento PDF es una copia visual de la factura y no constituye una "
"factura a efectos del IVA. La factura se emite en formato XML, transmitida "
"de acuerdo con los procedimientos y términos establecidos en el n.º 89757/"
"2018, de 30 de abril de 2018, emitido por el Director de la Agencia "
"Tributaria."
#: pretix/base/invoicing/pdf.py:141
#, python-format
@@ -3829,12 +3829,9 @@ msgid "Peppol participant ID"
msgstr "Identificador de participante Peppol"
#: pretix/base/invoicing/peppol.py:170
#, fuzzy
#| msgctxt "italian_invoice"
#| msgid "Fiscal code"
msgctxt "peppol_invoice"
msgid "Visual copy"
msgstr "Código fiscal"
msgstr "Copia visual"
#: pretix/base/invoicing/peppol.py:175
msgctxt "peppol_invoice"
@@ -3843,6 +3840,9 @@ msgid ""
"invoice for VAT purposes. The original invoice is issued in XML format and "
"transmitted through the Peppol network."
msgstr ""
"Este documento PDF es una copia visual de la factura y no constituye una "
"factura a efectos del IVA. La factura original se emite en formato XML y se "
"transmite a través de la red Peppol."
#: pretix/base/logentrytype_registry.py:43
msgid ""
@@ -14387,10 +14387,8 @@ msgid "Canceled (fully or with paid fee)"
msgstr "Cancelado (totalmente o con tarifa pagada)"
#: pretix/control/forms/filter.py:228
#, fuzzy
#| msgid "Cancel this position"
msgid "Canceled (at least one position)"
msgstr "Cancelar posición"
msgstr "Cancelado (al menos una posición)"
#: pretix/control/forms/filter.py:229
msgid "Cancellation requested"
@@ -17130,10 +17128,9 @@ msgid "The voucher has been deleted."
msgstr "El vale de compra fue eliminado."
#: pretix/control/logdisplay.py:584
#, fuzzy, python-brace-format
#| msgid "The voucher has been sent to {email} through the waiting list."
#, python-brace-format
msgid "The voucher has been assigned to {email} through the waiting list."
msgstr "El vale se ha enviado a {email} a través de la lista de espera."
msgstr "El vale se ha asignado a {email} a través de la lista de espera."
#: pretix/control/logdisplay.py:593
#, python-brace-format
@@ -27677,11 +27674,11 @@ msgstr ""
"próximos minutos."
#: pretix/control/views/datasync.py:90 pretix/control/views/datasync.py:104
#, fuzzy
#| msgid "The voucher \"{voucher}\" has been used in the meantime."
msgid ""
"The sync job could not be found. It may have been processed in the meantime."
msgstr "El vale de compra {voucher} ya ha sido utilizado."
msgstr ""
"No se ha encontrado la tarea de sincronización. Es posible que se haya "
"procesado mientras tanto."
#: pretix/control/views/datasync.py:93 pretix/control/views/datasync.py:107
msgid "The sync job is already in progress."
@@ -28674,12 +28671,12 @@ msgstr ""
"programado para {name}."
#: pretix/control/views/orders.py:2849 pretix/control/views/organizer.py:2207
#, fuzzy
#| msgid "You do not have sufficient permission to perform this export."
msgid ""
"Your user account does not have sufficient permission to run this report, "
"therefore you cannot schedule it."
msgstr "No tiene permiso suficiente para realizar esta exportación."
msgstr ""
"Su cuenta de usuario no tiene permisos suficientes para ejecutar este "
"informe, por lo que no puede programarlo."
#: pretix/control/views/orders.py:2902 pretix/control/views/organizer.py:2259
msgid ""
@@ -29044,17 +29041,14 @@ msgid "A date can not be deleted if orders already have been placed."
msgstr "No se puede borrar una fecha si ya se han realizado pedidos."
#: pretix/control/views/subevents.py:203
#, fuzzy
#| msgid ""
#| "The channel could not be deleted as some constraints (e.g. data created "
#| "by plug-ins) did not allow it."
msgctxt "subevent"
msgid ""
"The date could not be deleted as some constraints (e.g. data created by plug-"
"ins) did not allow it. The date was disabled instead."
msgstr ""
"El canal no ha podido borrarse porque algunas restricciones (por ejemplo, "
"datos creados por plug-ins) no lo permitían."
"La fecha no se pudo eliminar debido a que algunas restricciones (por "
"ejemplo, datos creados por complementos) no lo permitían. En su lugar, se "
"desactivó la fecha."
#: pretix/control/views/subevents.py:207
msgctxt "subevent"
@@ -33878,18 +33872,12 @@ msgstr ""
"formar un contrato válido."
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:192
#, fuzzy
#| msgid ""
#| "After you submitted your order using the button below, it will require "
#| "approval by the event organizer before it can be confirmed and forms a "
#| "valid contract."
msgid ""
"After you submitted your order using the button below, it will require "
"approval by the event organizer."
msgstr ""
"Después de enviar su pedido usando el botón a continuación, requerirá la "
"aprobación del organizador del evento antes de que pueda confirmarse y "
"formar un contrato válido."
"Después de enviar el pedido mediante el botón que aparece a continuación, "
"será necesario que el organizador del evento lo apruebe."
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:195
msgid ""
@@ -36448,10 +36436,8 @@ msgid "The selected date does not exist in this event series."
msgstr "La fecha seleccionada no existe en esta serie de eventos."
#: pretix/presale/views/widget.py:412
#, fuzzy
#| msgid "The selected seat \"{seat}\" is not available."
msgid "The selected date is not available."
msgstr "El asiento seleccionado {seat} no está disponible."
msgstr "La fecha seleccionada no está disponible."
#: pretix/presale/views/widget.py:476
#, python-format

View File

@@ -4,8 +4,8 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-26 11:16+0000\n"
"PO-Revision-Date: 2025-08-29 17:00+0000\n"
"Last-Translator: patch-works-be <webmaster@patch-works.be>\n"
"PO-Revision-Date: 2025-09-30 16:00+0000\n"
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix/fr/"
">\n"
"Language: fr\n"
@@ -13,7 +13,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 5.13\n"
"X-Generator: Weblate 5.13.3\n"
#: pretix/_base_settings.py:87
msgid "English"
@@ -810,28 +810,22 @@ msgstr ""
"mail dans votre espace client."
#: pretix/base/datasync/datasync.py:255
#, fuzzy, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
#, python-brace-format
msgid ""
"Field \"{field_name}\" does not exist. Please check your {provider_name} "
"settings."
msgstr ""
"Le champ « {field_name} » n'est pas valide pour {available_inputs}. Veuillez "
"vérifier vos paramètres {provider_name}."
"Le champ « {field_name} » n'existe pas. Veuillez vérifier vos paramètres "
"pour {provider_name}."
#: pretix/base/datasync/datasync.py:262
#, fuzzy, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
#, python-brace-format
msgid ""
"Field \"{field_name}\" requires {required_input}, but only got "
"{available_inputs}. Please check your {provider_name} settings."
msgstr ""
"Le champ « {field_name} » n'est pas valide pour {available_inputs}. Veuillez "
"vérifier vos paramètres {provider_name}."
"Le champ « {field_name} » nécessite {required_input}, mais n'a reçu que "
"{available_inputs}. Veuillez vérifier vos paramètres pour {provider_name}."
#: pretix/base/datasync/datasync.py:273
#, python-brace-format
@@ -3564,6 +3558,10 @@ msgid ""
"in accordance with the procedures and terms set forth in No. 89757/2018 of "
"April 30, 2018, issued by the Director of the Revenue Agency."
msgstr ""
"Ce document PDF est une copie visuelle de la facture et ne constitue pas une "
"facture aux fins de la TVA. La facture est émise au format XML, transmise "
"conformément aux procédures et conditions énoncées dans le n° 89757/2018 du "
"30 avril 2018, émis par le directeur de l'Agence des recettes."
#: pretix/base/invoicing/pdf.py:141
#, python-format
@@ -3834,12 +3832,9 @@ msgid "Peppol participant ID"
msgstr "Identifiant participant Peppol"
#: pretix/base/invoicing/peppol.py:170
#, fuzzy
#| msgctxt "italian_invoice"
#| msgid "Fiscal code"
msgctxt "peppol_invoice"
msgid "Visual copy"
msgstr "Code fiscal"
msgstr "Copie visuelle"
#: pretix/base/invoicing/peppol.py:175
msgctxt "peppol_invoice"
@@ -3848,6 +3843,9 @@ msgid ""
"invoice for VAT purposes. The original invoice is issued in XML format and "
"transmitted through the Peppol network."
msgstr ""
"Ce document PDF est une reproduction visuelle de la facture et ne constitue "
"pas une facture au sens de la TVA. La facture originale est émise au format "
"XML et transmise via le réseau Peppol."
#: pretix/base/logentrytype_registry.py:43
msgid ""
@@ -14513,10 +14511,8 @@ msgid "Canceled (fully or with paid fee)"
msgstr "Annulé (entièrement ou avec des frais payés)"
#: pretix/control/forms/filter.py:228
#, fuzzy
#| msgid "Cancel this position"
msgid "Canceled (at least one position)"
msgstr "Annuler cette position"
msgstr "Annulé (au moins pour une position)"
#: pretix/control/forms/filter.py:229
msgid "Cancellation requested"
@@ -17268,10 +17264,9 @@ msgid "The voucher has been deleted."
msgstr "Le bon a été supprimé."
#: pretix/control/logdisplay.py:584
#, fuzzy, python-brace-format
#| msgid "The voucher has been sent to {email} through the waiting list."
#, python-brace-format
msgid "The voucher has been assigned to {email} through the waiting list."
msgstr "Le bon a été envoyé à {email} via la liste d'attente."
msgstr "Le bon a été attribué à {email} via de la liste d'attente."
#: pretix/control/logdisplay.py:593
#, python-brace-format
@@ -27886,11 +27881,11 @@ msgstr ""
"les prochaines minutes."
#: pretix/control/views/datasync.py:90 pretix/control/views/datasync.py:104
#, fuzzy
#| msgid "The voucher \"{voucher}\" has been used in the meantime."
msgid ""
"The sync job could not be found. It may have been processed in the meantime."
msgstr "Le bon de réduction \"{voucher}\" a été utilisé entre-temps."
msgstr ""
"La tâche de synchronisation est introuvable. Elle a peut-être été traitée "
"entre-temps."
#: pretix/control/views/datasync.py:93 pretix/control/views/datasync.py:107
msgid "The sync job is already in progress."
@@ -28894,14 +28889,12 @@ msgstr ""
"planifié pour {name}."
#: pretix/control/views/orders.py:2849 pretix/control/views/organizer.py:2207
#, fuzzy
#| msgid "You do not have sufficient permission to perform this export."
msgid ""
"Your user account does not have sufficient permission to run this report, "
"therefore you cannot schedule it."
msgstr ""
"Vous ne disposez pas des autorisations suffisantes pour effectuer cette "
"exportation."
"Votre compte utilisateur ne dispose pas des autorisations suffisantes pour "
"exécuter ce rapport, vous ne pouvez donc pas le planifier."
#: pretix/control/views/orders.py:2902 pretix/control/views/organizer.py:2259
msgid ""
@@ -29272,17 +29265,14 @@ msgid "A date can not be deleted if orders already have been placed."
msgstr "Une date ne peut pas être supprimée si des ordres ont déjà été passés."
#: pretix/control/views/subevents.py:203
#, fuzzy
#| msgid ""
#| "The channel could not be deleted as some constraints (e.g. data created "
#| "by plug-ins) did not allow it."
msgctxt "subevent"
msgid ""
"The date could not be deleted as some constraints (e.g. data created by plug-"
"ins) did not allow it. The date was disabled instead."
msgstr ""
"Le canal de vente n'a pas pu être supprimé car certaines contraintes (par "
"exemple, les données créées par les plug-ins) ne le permettent pas."
"La date n'a pas pu être supprimée car certaines contraintes (par exemple, "
"les données créées par les plug-ins) ne le permettaient pas. La date a donc "
"été désactivée."
#: pretix/control/views/subevents.py:207
msgctxt "subevent"
@@ -34147,18 +34137,12 @@ msgstr ""
"puisse être confirmée et forme un contrat valide."
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:192
#, fuzzy
#| msgid ""
#| "After you submitted your order using the button below, it will require "
#| "approval by the event organizer before it can be confirmed and forms a "
#| "valid contract."
msgid ""
"After you submitted your order using the button below, it will require "
"approval by the event organizer."
msgstr ""
"Après avoir soumis votre commande en utilisant le bouton ci-dessous, elle "
"nécessitera lapprobation de lorganisateur de lévénement avant quelle "
"puisse être confirmée et forme un contrat valide."
"Une fois que vous aurez soumis votre commande à l'aide du bouton ci-dessous, "
"celle-ci devra être approuvée par l'organisateur de l'événement."
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:195
msgid ""
@@ -36776,10 +36760,8 @@ msgid "The selected date does not exist in this event series."
msgstr "La date sélectionnée nexiste pas dans cette série dévénements."
#: pretix/presale/views/widget.py:412
#, fuzzy
#| msgid "The selected seat \"{seat}\" is not available."
msgid "The selected date is not available."
msgstr "La place {seat} sélectionné n'est pas disponible."
msgstr "La date sélectionnée n'est pas disponible."
#: pretix/presale/views/widget.py:476
#, python-format

View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-26 11:16+0000\n"
"PO-Revision-Date: 2025-09-10 21:00+0000\n"
"Last-Translator: Davide Wayan Mores <racecondition99@gmail.com>\n"
"PO-Revision-Date: 2025-09-26 13:02+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: Italian <https://translate.pretix.eu/projects/pretix/pretix/"
"it/>\n"
"Language: it\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.13.2\n"
"X-Generator: Weblate 5.13.3\n"
#: pretix/_base_settings.py:87
msgid "English"
@@ -3588,6 +3588,10 @@ msgid ""
"in accordance with the procedures and terms set forth in No. 89757/2018 of "
"April 30, 2018, issued by the Director of the Revenue Agency."
msgstr ""
"Il presente documento pdf non costituisce fattura ai fini IVA. La fattura si "
"intende emessa nel formato xml trasmesso nei modi e termini di cui al "
"Provvedimento n. 89757/2018 del 30 aprile 2018 del direttore dell'Agenzia "
"delle Entrate."
#: pretix/base/invoicing/pdf.py:141
#, python-format

File diff suppressed because it is too large Load Diff

View File

@@ -7,16 +7,16 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-26 11:16+0000\n"
"PO-Revision-Date: 2025-08-26 19:00+0000\n"
"PO-Revision-Date: 2025-09-30 01:00+0000\n"
"Last-Translator: Jan Van Haver <jan.van.haver@gmail.com>\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/"
">\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/>"
"\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.13\n"
"X-Generator: Weblate 5.13.3\n"
#: pretix/_base_settings.py:87
msgid "English"
@@ -3808,6 +3808,8 @@ msgstr "Evenementdatum: {date_range}"
msgid ""
"A Peppol participant ID always starts with a prefix, followed by a colon (:)."
msgstr ""
"Een Peppol deelnemer-ID begint altijd met een prefix, gevolgd door een "
"dubbele punt (:)."
#: pretix/base/invoicing/peppol.py:132
#, python-format
@@ -3815,6 +3817,8 @@ msgid ""
"The Peppol participant ID prefix %(number)s is not known to our system. "
"Please reach out to us if you are sure this ID is correct."
msgstr ""
"De prefix van de Peppol deelnemer-ID %(number)s is niet bekend in ons "
"systeem. Neem contact met ons op als u zeker weet dat deze ID correct is."
#: pretix/base/invoicing/peppol.py:136
#, python-format
@@ -3822,17 +3826,18 @@ msgid ""
"The Peppol participant ID does not match the validation rules for the prefix "
"%(number)s. Please reach out to us if you are sure this ID is correct."
msgstr ""
"De Peppol deelnemer-ID komt niet overeen met de validatieregels voor het "
"prefix %(number)s. Neem contact met ons op als u zeker weet dat deze ID "
"correct is."
#: pretix/base/invoicing/peppol.py:156
msgid "Peppol participant ID"
msgstr ""
msgstr "Peppol deelnemer-ID"
#: pretix/base/invoicing/peppol.py:170
#, fuzzy
#| msgid "Gift card code"
msgctxt "peppol_invoice"
msgid "Visual copy"
msgstr "Cadeauboncode"
msgstr "Visuele kopie"
#: pretix/base/invoicing/peppol.py:175
msgctxt "peppol_invoice"
@@ -3841,6 +3846,9 @@ msgid ""
"invoice for VAT purposes. The original invoice is issued in XML format and "
"transmitted through the Peppol network."
msgstr ""
"Dit PDF-document is een visuele kopie van de factuur en vormt geen factuur "
"voor BTW-doeleinden. De originele factuur wordt opgemaakt in XML-formaat en "
"verzonden via het Peppol-netwerk."
#: pretix/base/logentrytype_registry.py:43
msgid ""

View File

@@ -170,7 +170,7 @@ OPTIONS = OrderedDict([
'cols': 2,
'rows': 6,
'margins': [28.5 * mm, 30 * mm, 28.5 * mm, 30 * mm],
'offsets': [93 * mm, 60 * mm],
'offsets': [75 * mm, 40 * mm],
'pagesize': pagesizes.A4,
}),
('herma_50x80', {

View File

@@ -8,7 +8,7 @@
"name": "pretix",
"version": "0.0.0",
"dependencies": {
"@babel/core": "^7.28.3",
"@babel/core": "^7.28.4",
"@babel/preset-env": "^7.28.3",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^16.0.1",
@@ -18,18 +18,6 @@
"vue-template-compiler": "^2.7.16"
}
},
"node_modules/@ampproject/remapping": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
"integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
"dependencies": {
"@jridgewell/gen-mapping": "^0.1.0",
"@jridgewell/trace-mapping": "^0.3.9"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
@@ -53,20 +41,20 @@
}
},
"node_modules/@babel/core": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz",
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz",
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.3",
"@babel/helper-compilation-targets": "^7.27.2",
"@babel/helper-module-transforms": "^7.28.3",
"@babel/helpers": "^7.28.3",
"@babel/parser": "^7.28.3",
"@babel/helpers": "^7.28.4",
"@babel/parser": "^7.28.4",
"@babel/template": "^7.27.2",
"@babel/traverse": "^7.28.3",
"@babel/types": "^7.28.2",
"@babel/traverse": "^7.28.4",
"@babel/types": "^7.28.4",
"@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -115,15 +103,6 @@
"node": ">=6.9.0"
}
},
"node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": {
"version": "0.3.12",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
"integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@babel/helper-annotate-as-pure": {
"version": "7.27.3",
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
@@ -401,23 +380,23 @@
}
},
"node_modules/@babel/helpers": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz",
"integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
"integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
"dependencies": {
"@babel/template": "^7.27.2",
"@babel/types": "^7.28.2"
"@babel/types": "^7.28.4"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
"integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
"integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
"dependencies": {
"@babel/types": "^7.28.2"
"@babel/types": "^7.28.4"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -1475,16 +1454,16 @@
}
},
"node_modules/@babel/traverse": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
"integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz",
"integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==",
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.3",
"@babel/helper-globals": "^7.28.0",
"@babel/parser": "^7.28.3",
"@babel/parser": "^7.28.4",
"@babel/template": "^7.27.2",
"@babel/types": "^7.28.2",
"@babel/types": "^7.28.4",
"debug": "^4.3.1"
},
"engines": {
@@ -1492,9 +1471,9 @@
}
},
"node_modules/@babel/types": {
"version": "7.28.2",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
"integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
@@ -1504,15 +1483,21 @@
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
"integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dependencies": {
"@jridgewell/set-array": "^1.0.0",
"@jridgewell/sourcemap-codec": "^1.4.10"
},
"engines": {
"node": ">=6.0.0"
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/remapping": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/resolve-uri": {
@@ -1523,14 +1508,6 @@
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
@@ -3802,15 +3779,6 @@
}
},
"dependencies": {
"@ampproject/remapping": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
"integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
"requires": {
"@jridgewell/gen-mapping": "^0.1.0",
"@jridgewell/trace-mapping": "^0.3.9"
}
},
"@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
@@ -3827,20 +3795,20 @@
"integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw=="
},
"@babel/core": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz",
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz",
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
"requires": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.3",
"@babel/helper-compilation-targets": "^7.27.2",
"@babel/helper-module-transforms": "^7.28.3",
"@babel/helpers": "^7.28.3",
"@babel/parser": "^7.28.3",
"@babel/helpers": "^7.28.4",
"@babel/parser": "^7.28.4",
"@babel/template": "^7.27.2",
"@babel/traverse": "^7.28.3",
"@babel/types": "^7.28.2",
"@babel/traverse": "^7.28.4",
"@babel/types": "^7.28.4",
"@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -3870,17 +3838,6 @@
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
},
"dependencies": {
"@jridgewell/gen-mapping": {
"version": "0.3.12",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
"integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
"requires": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
}
}
},
"@babel/helper-annotate-as-pure": {
@@ -4074,20 +4031,20 @@
}
},
"@babel/helpers": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz",
"integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
"integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
"requires": {
"@babel/template": "^7.27.2",
"@babel/types": "^7.28.2"
"@babel/types": "^7.28.4"
}
},
"@babel/parser": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
"integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
"integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
"requires": {
"@babel/types": "^7.28.2"
"@babel/types": "^7.28.4"
}
},
"@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
@@ -4718,35 +4675,44 @@
}
},
"@babel/traverse": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
"integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz",
"integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==",
"requires": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.3",
"@babel/helper-globals": "^7.28.0",
"@babel/parser": "^7.28.3",
"@babel/parser": "^7.28.4",
"@babel/template": "^7.27.2",
"@babel/types": "^7.28.2",
"@babel/types": "^7.28.4",
"debug": "^4.3.1"
}
},
"@babel/types": {
"version": "7.28.2",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
"integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
"requires": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
}
},
"@jridgewell/gen-mapping": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
"integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"requires": {
"@jridgewell/set-array": "^1.0.0",
"@jridgewell/sourcemap-codec": "^1.4.10"
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"@jridgewell/remapping": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"requires": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"@jridgewell/resolve-uri": {
@@ -4754,11 +4720,6 @@
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
},
"@jridgewell/set-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="
},
"@jridgewell/sourcemap-codec": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",

View File

@@ -4,7 +4,7 @@
"private": true,
"scripts": {},
"dependencies": {
"@babel/core": "^7.28.3",
"@babel/core": "^7.28.4",
"@babel/preset-env": "^7.28.3",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^16.0.1",

View File

@@ -231,7 +231,9 @@ TEST_INVOICE_RES = {
"description": "Budget Ticket<br />Attendee: Peter",
'subevent': None,
'event_date_from': '2017-12-27T10:00:00Z',
'event_date_to': None,
'event_date_to': '2017-12-27T10:00:00Z',
'period_start': '2017-12-27T10:00:00Z',
'period_end': '2017-12-27T10:00:00Z',
'event_location': None,
'attendee_name': 'Peter',
'item': None,
@@ -249,7 +251,9 @@ TEST_INVOICE_RES = {
"description": "Payment fee",
'subevent': None,
'event_date_from': '2017-12-27T10:00:00Z',
'event_date_to': None,
'event_date_to': '2017-12-27T10:00:00Z',
'period_start': '2017-12-27T10:00:00Z',
'period_end': '2017-12-27T10:00:00Z',
'event_location': None,
'attendee_name': None,
'fee_type': "payment",

View File

@@ -608,7 +608,9 @@ 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': None,
'event_date_to': '2017-12-27T10:00:00Z',
'period_start': '2017-12-27T10:00:00Z',
'period_end': '2017-12-27T10:00:00Z',
'event_location': None,
'fee_type': None,
'fee_internal_type': None,
@@ -626,7 +628,9 @@ 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': None,
'event_date_to': '2017-12-27T10:00:00Z',
'period_start': '2017-12-27T10:00:00Z',
'period_end': '2017-12-27T10:00:00Z',
'event_location': None,
'fee_type': "payment",
'fee_internal_type': None,

View File

@@ -33,7 +33,7 @@
# License for the specific language governing permissions and limitations under the License.
import json
from datetime import date, timedelta
from datetime import date, datetime, timedelta, timezone
from decimal import Decimal
import pytest
@@ -42,6 +42,7 @@ 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 (
@@ -62,7 +63,8 @@ def env():
with scope(organizer=o):
event = Event.objects.create(
organizer=o, name='Dummy', slug='dummy',
date_from=now(), plugins='pretix.plugins.banktransfer'
date_from=datetime(2024, 12, 1, 9, 0, 0, tzinfo=timezone.utc),
plugins='pretix.plugins.banktransfer'
)
o = Order.objects.create(
code='FOO', event=event, email='dummy@dummy.test',
@@ -660,3 +662,154 @@ 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

View File

@@ -54,7 +54,7 @@ def test_sales_channel_all(event, item, order, checkin_list):
mode=AutoCheckinRule.MODE_PLACED,
all_sales_channels=True,
)
order_placed.send(event, order=order)
order_placed.send(event, order=order, bulk=False)
assert order.positions.first().checkins.exists()
@@ -67,12 +67,12 @@ def test_sales_channel_limit(event, item, order, checkin_list):
all_sales_channels=False,
)
order_placed.send(event, order=order)
order_placed.send(event, order=order, bulk=False)
assert not order.positions.first().checkins.exists()
acr.limit_sales_channels.add(order.sales_channel)
order_placed.send(event, order=order)
order_placed.send(event, order=order, bulk=False)
assert order.positions.first().checkins.exists()
@@ -84,7 +84,7 @@ def test_items_all(event, item, order, checkin_list):
mode=AutoCheckinRule.MODE_PLACED,
all_products=True,
)
order_placed.send(event, order=order)
order_placed.send(event, order=order, bulk=False)
assert order.positions.first().checkins.exists()
@@ -97,12 +97,12 @@ def test_items_limit(event, item, order, checkin_list):
all_products=False,
)
order_placed.send(event, order=order)
order_placed.send(event, order=order, bulk=False)
assert not order.positions.first().checkins.exists()
acr.limit_products.add(item)
order_placed.send(event, order=order)
order_placed.send(event, order=order, bulk=False)
assert order.positions.first().checkins.exists()
@@ -124,7 +124,7 @@ def test_variations_limit_mixed_order(event, item, order, checkin_list):
)
acr.limit_variations.add(var)
order_placed.send(event, order=order)
order_placed.send(event, order=order, bulk=False)
assert order.positions.first().checkins.exists()
assert not order.positions.last().checkins.exists()
@@ -143,19 +143,19 @@ def test_variations_limit(event, item, order, checkin_list):
all_products=False,
)
order_placed.send(event, order=order)
order_placed.send(event, order=order, bulk=False)
assert not order.positions.first().checkins.exists()
acr.limit_variations.add(var)
order_placed.send(event, order=order)
order_placed.send(event, order=order, bulk=False)
assert order.positions.first().checkins.exists()
order.positions.first().checkins.all().delete()
acr.limit_products.add(item)
acr.limit_variations.clear()
order_placed.send(event, order=order)
order_placed.send(event, order=order, bulk=False)
assert order.positions.first().checkins.exists()
@@ -170,7 +170,7 @@ def test_mode_placed(event, item, order, checkin_list):
order_paid.send(event, order=order)
assert not order.positions.first().checkins.exists()
order_placed.send(event, order=order)
order_placed.send(event, order=order, bulk=False)
assert order.positions.first().checkins.exists()
@@ -182,7 +182,7 @@ def test_mode_paid(event, item, order, checkin_list):
mode=AutoCheckinRule.MODE_PAID,
)
order_placed.send(event, order=order)
order_placed.send(event, order=order, bulk=False)
assert not order.positions.first().checkins.exists()
order_paid.send(event, order=order)