mirror of
https://github.com/pretix/pretix.git
synced 2025-12-05 21:32:28 +00:00
Compare commits
10 Commits
74a960e239
...
e1f5678d7c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1f5678d7c | ||
|
|
609b7c82ee | ||
|
|
8d66e1e732 | ||
|
|
c925f094f2 | ||
|
|
5caaa8586d | ||
|
|
1b1cf1557d | ||
|
|
35d8a7eec5 | ||
|
|
d428c3e1a4 | ||
|
|
63850f3139 | ||
|
|
04c8270d43 |
@@ -211,7 +211,7 @@ The line-based computation has a few significant advantages:
|
||||
|
||||
The main disadvantage is that the tax looks "wrong" when computed from the sum. Taking the sum of net prices (420.15)
|
||||
and multiplying it with the tax rate (19%) yields a tax amount of 79.83 (instead of 79.85) and a gross sum of 499.98
|
||||
(instead of 499.98). This becomes a problem when juristictions, data formats, or external systems expect this calculation
|
||||
(instead of 500.00). This becomes a problem when juristictions, data formats, or external systems expect this calculation
|
||||
to work on the level of the entire order. A prominent example is the EN 16931 standard for e-invoicing that
|
||||
does not allow the computation as created by pretix.
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ dependencies = [
|
||||
"redis==6.4.*",
|
||||
"reportlab==4.4.*",
|
||||
"requests==2.32.*",
|
||||
"sentry-sdk==2.46.*",
|
||||
"sentry-sdk==2.47.*",
|
||||
"sepaxml==2.7.*",
|
||||
"stripe==7.9.*",
|
||||
"text-unidecode==1.*",
|
||||
|
||||
@@ -47,6 +47,19 @@ class DataImportError(LazyLocaleException):
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
def rename_duplicates(values):
|
||||
used = set()
|
||||
had_duplicates = False
|
||||
for i, value in enumerate(values):
|
||||
c = 0
|
||||
while values[i] in used:
|
||||
c += 1
|
||||
values[i] = f'{value}__{c}'
|
||||
had_duplicates = True
|
||||
used.add(values[i])
|
||||
return had_duplicates
|
||||
|
||||
|
||||
def parse_csv(file, length=None, mode="strict", charset=None):
|
||||
file.seek(0)
|
||||
data = file.read(length)
|
||||
@@ -70,6 +83,7 @@ def parse_csv(file, length=None, mode="strict", charset=None):
|
||||
return None
|
||||
|
||||
reader = csv.DictReader(io.StringIO(data), dialect=dialect)
|
||||
reader._had_duplicates = rename_duplicates(reader.fieldnames)
|
||||
return reader
|
||||
|
||||
|
||||
|
||||
@@ -1361,6 +1361,11 @@ class CartManager:
|
||||
deleted_positions.add(op.position.pk)
|
||||
addons.delete()
|
||||
op.position.delete()
|
||||
if op.position.is_bundled:
|
||||
deleted_positions |= {a.pk for a in op.position.addon_to.addons.all()}
|
||||
deleted_positions.add(op.position.addon_to.pk)
|
||||
op.position.addon_to.addons.all().delete()
|
||||
op.position.addon_to.delete()
|
||||
else:
|
||||
raise AssertionError("ExtendOperation cannot affect more than one item")
|
||||
elif isinstance(op, self.VoucherOperation):
|
||||
|
||||
@@ -113,6 +113,11 @@ def assign_automatically(event: Event, user_id: int=None, subevent_id: int=None)
|
||||
|
||||
lock_objects(quotas, shared_lock_objects=[event])
|
||||
for wle in qs:
|
||||
# add this event to wle.item as it is not yet cached and is needed in check_quotas
|
||||
wle.item.event = event
|
||||
if wle.variation:
|
||||
wle.variation.item = wle.item
|
||||
|
||||
if (wle.item_id, wle.variation_id, wle.subevent_id) in gone:
|
||||
continue
|
||||
ev = (wle.subevent or event)
|
||||
|
||||
@@ -146,7 +146,7 @@ class BaseProcessView(AsyncAction, FormView):
|
||||
else:
|
||||
charset = None
|
||||
try:
|
||||
return parse_csv(self.file.file, 1024 * 1024, charset=charset)
|
||||
reader = parse_csv(self.file.file, 1024 * 1024, charset=charset)
|
||||
except UnicodeDecodeError:
|
||||
messages.warning(
|
||||
self.request,
|
||||
@@ -155,7 +155,16 @@ class BaseProcessView(AsyncAction, FormView):
|
||||
"Some characters were replaced with a placeholder."
|
||||
)
|
||||
)
|
||||
return parse_csv(self.file.file, 1024 * 1024, "replace", charset=charset)
|
||||
reader = parse_csv(self.file.file, 1024 * 1024, "replace", charset=charset)
|
||||
if reader._had_duplicates:
|
||||
messages.warning(
|
||||
self.request,
|
||||
_(
|
||||
"Multiple columns of the CSV file have the same name and were renamed automatically. We "
|
||||
"recommend that you rename these in your source file to avoid problems during import."
|
||||
)
|
||||
)
|
||||
return reader
|
||||
|
||||
@cached_property
|
||||
def parsed_list(self):
|
||||
|
||||
210
src/pretix/helpers/payment.py
Normal file
210
src/pretix/helpers/payment.py
Normal file
@@ -0,0 +1,210 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-today pretix GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from urllib.parse import quote, urlencode
|
||||
|
||||
import text_unidecode
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
def dotdecimal(value):
|
||||
return str(value).replace(",", ".")
|
||||
|
||||
|
||||
def commadecimal(value):
|
||||
return str(value).replace(".", ",")
|
||||
|
||||
|
||||
def generate_payment_qr_codes(
|
||||
event,
|
||||
code,
|
||||
amount,
|
||||
bank_details_sepa_bic,
|
||||
bank_details_sepa_name,
|
||||
bank_details_sepa_iban,
|
||||
):
|
||||
out = []
|
||||
for method in [
|
||||
swiss_qrbill,
|
||||
czech_spayd,
|
||||
euro_epc_qr,
|
||||
euro_bezahlcode,
|
||||
]:
|
||||
data = method(
|
||||
event,
|
||||
code,
|
||||
amount,
|
||||
bank_details_sepa_bic,
|
||||
bank_details_sepa_name,
|
||||
bank_details_sepa_iban,
|
||||
)
|
||||
if data:
|
||||
out.append(data)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def euro_epc_qr(
|
||||
event,
|
||||
code,
|
||||
amount,
|
||||
bank_details_sepa_bic,
|
||||
bank_details_sepa_name,
|
||||
bank_details_sepa_iban,
|
||||
):
|
||||
if event.currency != 'EUR' or not bank_details_sepa_iban:
|
||||
return
|
||||
|
||||
return {
|
||||
"id": "girocode",
|
||||
"label": "EPC-QR",
|
||||
"qr_data": "\n".join(text_unidecode.unidecode(str(d or '')) for d in [
|
||||
"BCD", # Service Tag: ‘BCD’
|
||||
"002", # Version: V2
|
||||
"2", # Character set: ISO 8859-1
|
||||
"SCT", # Identification code: ‘SCT‘
|
||||
bank_details_sepa_bic, # AT-23 BIC of the Beneficiary Bank
|
||||
bank_details_sepa_name, # AT-21 Name of the Beneficiary
|
||||
bank_details_sepa_iban, # AT-20 Account number of the Beneficiary
|
||||
f"{event.currency}{dotdecimal(amount)}", # AT-04 Amount of the Credit Transfer in Euro
|
||||
"", # AT-44 Purpose of the Credit Transfer
|
||||
"", # AT-05 Remittance Information (Structured)
|
||||
code, # AT-05 Remittance Information (Unstructured)
|
||||
"", # Beneficiary to originator information
|
||||
"",
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
def euro_bezahlcode(
|
||||
event,
|
||||
code,
|
||||
amount,
|
||||
bank_details_sepa_bic,
|
||||
bank_details_sepa_name,
|
||||
bank_details_sepa_iban,
|
||||
):
|
||||
if not bank_details_sepa_iban or bank_details_sepa_iban[:2] != 'DE':
|
||||
return
|
||||
if event.currency != 'EUR':
|
||||
return
|
||||
|
||||
qr_data = "bank://singlepaymentsepa?" + urlencode({
|
||||
"name": str(bank_details_sepa_name),
|
||||
"iban": str(bank_details_sepa_iban),
|
||||
"bic": str(bank_details_sepa_bic),
|
||||
"amount": commadecimal(amount),
|
||||
"reason": str(code),
|
||||
"currency": str(event.currency),
|
||||
}, quote_via=quote)
|
||||
return {
|
||||
"id": "bezahlcode",
|
||||
"label": "BezahlCode",
|
||||
"qr_data": mark_safe(qr_data),
|
||||
"link": qr_data,
|
||||
"link_aria_label": _("Open BezahlCode in your banking app to start the payment process."),
|
||||
}
|
||||
|
||||
|
||||
def swiss_qrbill(
|
||||
event,
|
||||
code,
|
||||
amount,
|
||||
bank_details_sepa_bic,
|
||||
bank_details_sepa_name,
|
||||
bank_details_sepa_iban,
|
||||
):
|
||||
if not bank_details_sepa_iban or not bank_details_sepa_iban[:2] in ('CH', 'LI'):
|
||||
return
|
||||
if event.currency not in ('EUR', 'CHF'):
|
||||
return
|
||||
if not event.settings.invoice_address_from or not event.settings.invoice_address_from_country:
|
||||
return
|
||||
|
||||
data_fields = [
|
||||
'SPC',
|
||||
'0200',
|
||||
'1',
|
||||
bank_details_sepa_iban,
|
||||
'K',
|
||||
bank_details_sepa_name[:70],
|
||||
event.settings.invoice_address_from.replace('\n', ', ')[:70],
|
||||
(event.settings.invoice_address_from_zipcode + ' ' + event.settings.invoice_address_from_city)[:70],
|
||||
'',
|
||||
'',
|
||||
str(event.settings.invoice_address_from_country),
|
||||
'', # rfu
|
||||
'', # rfu
|
||||
'', # rfu
|
||||
'', # rfu
|
||||
'', # rfu
|
||||
'', # rfu
|
||||
'', # rfu
|
||||
str(amount),
|
||||
event.currency,
|
||||
'', # debtor address
|
||||
'', # debtor address
|
||||
'', # debtor address
|
||||
'', # debtor address
|
||||
'', # debtor address
|
||||
'', # debtor address
|
||||
'', # debtor address
|
||||
'NON',
|
||||
'', # structured reference
|
||||
code,
|
||||
'EPD',
|
||||
]
|
||||
|
||||
data_fields = [text_unidecode.unidecode(d or '') for d in data_fields]
|
||||
qr_data = '\r\n'.join(data_fields)
|
||||
return {
|
||||
"id": "qrbill",
|
||||
"label": "QR-bill",
|
||||
"html_prefix": mark_safe(
|
||||
'<svg class="banktransfer-swiss-cross" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.8 19.8">'
|
||||
'<path stroke="#fff" stroke-width="1.436" d="M.7.7h18.4v18.4H.7z"/><path fill="#fff" d="M8.3 4h3.3v11H8.3z"/>'
|
||||
'<path fill="#fff" d="M4.4 7.9h11v3.3h-11z"/></svg>'
|
||||
),
|
||||
"qr_data": qr_data,
|
||||
"css_class": "banktransfer-swiss-cross-overlay",
|
||||
}
|
||||
|
||||
|
||||
def czech_spayd(
|
||||
event,
|
||||
code,
|
||||
amount,
|
||||
bank_details_sepa_bic,
|
||||
bank_details_sepa_name,
|
||||
bank_details_sepa_iban,
|
||||
):
|
||||
if not bank_details_sepa_iban or not bank_details_sepa_iban[:2] in ('CZ', 'SK'):
|
||||
return
|
||||
if event.currency not in ('EUR', 'CZK'):
|
||||
return
|
||||
|
||||
qr_data = f"SPD*1.0*ACC:{bank_details_sepa_iban}*AM:{dotdecimal(amount)}*CC:{event.currency}*MSG:{code}"
|
||||
return {
|
||||
"id": "spayd",
|
||||
"label": "SPAYD",
|
||||
"qr_data": qr_data,
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,16 +8,16 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-11-20 10:37+0000\n"
|
||||
"PO-Revision-Date: 2022-02-22 22:00+0000\n"
|
||||
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n"
|
||||
"Language-Team: Galician <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
"js/gl/>\n"
|
||||
"PO-Revision-Date: 2025-12-03 23:00+0000\n"
|
||||
"Last-Translator: sandra r <sandrarial@gestiontickets.online>\n"
|
||||
"Language-Team: Galician <https://translate.pretix.eu/projects/pretix/"
|
||||
"pretix-js/gl/>\n"
|
||||
"Language: gl\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 4.8\n"
|
||||
"X-Generator: Weblate 5.14.3\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -31,106 +31,104 @@ msgstr "Comentario:"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:34
|
||||
msgid "PayPal"
|
||||
msgstr ""
|
||||
msgstr "PayPal"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:35
|
||||
msgid "Venmo"
|
||||
msgstr ""
|
||||
msgstr "Venmo"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||
msgid "Apple Pay"
|
||||
msgstr ""
|
||||
msgstr "Apple Pay"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:37
|
||||
msgid "Itaú"
|
||||
msgstr ""
|
||||
msgstr "Itaú"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:38
|
||||
msgid "PayPal Credit"
|
||||
msgstr ""
|
||||
msgstr "Crédito PayPal"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:39
|
||||
msgid "Credit Card"
|
||||
msgstr ""
|
||||
msgstr "Tarxeta de crédito"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:40
|
||||
msgid "PayPal Pay Later"
|
||||
msgstr ""
|
||||
msgstr "PayPal Pagar Máis Tarde"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:41
|
||||
msgid "iDEAL"
|
||||
msgstr ""
|
||||
msgstr "iDEAL"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:42
|
||||
msgid "SEPA Direct Debit"
|
||||
msgstr ""
|
||||
msgstr "Débito directo SEPA"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:43
|
||||
msgid "Bancontact"
|
||||
msgstr ""
|
||||
msgstr "Bancontact"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:44
|
||||
msgid "giropay"
|
||||
msgstr ""
|
||||
msgstr "giropay"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:45
|
||||
msgid "SOFORT"
|
||||
msgstr ""
|
||||
msgstr "SOFORT"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:46
|
||||
#, fuzzy
|
||||
#| msgid "Yes"
|
||||
msgid "eps"
|
||||
msgstr "Si"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:47
|
||||
msgid "MyBank"
|
||||
msgstr ""
|
||||
msgstr "MyBank"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:48
|
||||
msgid "Przelewy24"
|
||||
msgstr ""
|
||||
msgstr "Przelewy24"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:49
|
||||
msgid "Verkkopankki"
|
||||
msgstr ""
|
||||
msgstr "Verkkopankki"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:50
|
||||
msgid "PayU"
|
||||
msgstr ""
|
||||
msgstr "PayU"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:51
|
||||
msgid "BLIK"
|
||||
msgstr ""
|
||||
msgstr "BLIK"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:52
|
||||
msgid "Trustly"
|
||||
msgstr ""
|
||||
msgstr "De confianza"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:53
|
||||
msgid "Zimpler"
|
||||
msgstr ""
|
||||
msgstr "Zimpler"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:54
|
||||
msgid "Maxima"
|
||||
msgstr ""
|
||||
msgstr "Máxima"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:55
|
||||
msgid "OXXO"
|
||||
msgstr ""
|
||||
msgstr "OXXO"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:56
|
||||
msgid "Boleto"
|
||||
msgstr ""
|
||||
msgstr "Ticket"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:57
|
||||
msgid "WeChat Pay"
|
||||
msgstr ""
|
||||
msgstr "Pagar con WeChat"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:58
|
||||
msgid "Mercado Pago"
|
||||
msgstr ""
|
||||
msgstr "Mercado Pago"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:50
|
||||
@@ -149,7 +147,7 @@ msgstr "Confirmando o pagamento…"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:254
|
||||
msgid "Payment method unavailable"
|
||||
msgstr ""
|
||||
msgstr "O método de pago non está dispoñible"
|
||||
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
|
||||
@@ -240,11 +238,11 @@ msgstr "Cancelado"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:46
|
||||
msgid "Confirmed"
|
||||
msgstr ""
|
||||
msgstr "Confirmado"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:47
|
||||
msgid "Approval pending"
|
||||
msgstr ""
|
||||
msgstr "Aprobación pendente"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
|
||||
msgid "Redeemed"
|
||||
@@ -300,16 +298,12 @@ msgid "Ticket code revoked/changed"
|
||||
msgstr "Código de tícket revogado/cambiado"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
|
||||
#, fuzzy
|
||||
#| msgid "Ticket not paid"
|
||||
msgid "Ticket blocked"
|
||||
msgstr "Tícket pendente de pago"
|
||||
msgstr "Ticket bloqueado"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
|
||||
#, fuzzy
|
||||
#| msgid "Ticket not paid"
|
||||
msgid "Ticket not valid at this time"
|
||||
msgstr "Tícket pendente de pago"
|
||||
msgstr "O ticket non é válido neste momento"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
|
||||
msgid "Order canceled"
|
||||
@@ -317,11 +311,11 @@ msgstr "Pedido cancelado"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
|
||||
msgid "Ticket code is ambiguous on list"
|
||||
msgstr ""
|
||||
msgstr "O código do ticket é ambiguo na lista"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
|
||||
msgid "Order not approved"
|
||||
msgstr ""
|
||||
msgstr "Orde non aprobada"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
|
||||
msgid "Checked-in Tickets"
|
||||
@@ -422,7 +416,7 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:276
|
||||
msgid "If this takes longer than a few minutes, please contact us."
|
||||
msgstr ""
|
||||
msgstr "Se isto leva máis duns minutos, póñase en contacto connosco."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:331
|
||||
msgid "Close message"
|
||||
@@ -452,7 +446,7 @@ msgstr "está despois"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:40
|
||||
msgid "="
|
||||
msgstr ""
|
||||
msgstr "="
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:99
|
||||
msgid "Product"
|
||||
@@ -464,7 +458,7 @@ msgstr "Ver variacións do produto"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:107
|
||||
msgid "Gate"
|
||||
msgstr ""
|
||||
msgstr "Porta"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:111
|
||||
msgid "Current date and time"
|
||||
@@ -472,11 +466,11 @@ msgstr "Data e hora actual"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:115
|
||||
msgid "Current day of the week (1 = Monday, 7 = Sunday)"
|
||||
msgstr ""
|
||||
msgstr "Día actual da semana (1 = luns, 7 = domingo)"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:119
|
||||
msgid "Current entry status"
|
||||
msgstr ""
|
||||
msgstr "Estado de entrada actual"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:123
|
||||
msgid "Number of previous entries"
|
||||
@@ -487,40 +481,32 @@ msgid "Number of previous entries since midnight"
|
||||
msgstr "Número de entradas previas desde a medianoite"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:131
|
||||
#, fuzzy
|
||||
#| msgid "Number of previous entries"
|
||||
msgid "Number of previous entries since"
|
||||
msgstr "Número de entradas previas"
|
||||
msgstr "Número de entradas anteriores desde"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:135
|
||||
#, fuzzy
|
||||
#| msgid "Number of previous entries"
|
||||
msgid "Number of previous entries before"
|
||||
msgstr "Número de entradas previas"
|
||||
msgstr "Número de entradas anteriores antes de"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:139
|
||||
msgid "Number of days with a previous entry"
|
||||
msgstr "Número de días cunha entrada previa"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:143
|
||||
#, fuzzy
|
||||
#| msgid "Number of days with a previous entry"
|
||||
msgid "Number of days with a previous entry since"
|
||||
msgstr "Número de días cunha entrada previa"
|
||||
msgstr "Número de días cunha entrada previa desde"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:147
|
||||
#, fuzzy
|
||||
#| msgid "Number of days with a previous entry"
|
||||
msgid "Number of days with a previous entry before"
|
||||
msgstr "Número de días cunha entrada previa"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:151
|
||||
msgid "Minutes since last entry (-1 on first entry)"
|
||||
msgstr ""
|
||||
msgstr "Minutos desde a última entrada (-1 na primeira entrada)"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:155
|
||||
msgid "Minutes since first entry (-1 on first entry)"
|
||||
msgstr ""
|
||||
msgstr "Minutos desde a primeira entrada (-1 na primeira entrada)"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:182
|
||||
msgid "All of the conditions below (AND)"
|
||||
@@ -564,25 +550,25 @@ msgstr "minutos"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:192
|
||||
msgid "Duplicate"
|
||||
msgstr ""
|
||||
msgstr "Duplicar"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:193
|
||||
msgctxt "entry_status"
|
||||
msgid "present"
|
||||
msgstr ""
|
||||
msgstr "presente"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:194
|
||||
msgctxt "entry_status"
|
||||
msgid "absent"
|
||||
msgstr ""
|
||||
msgstr "ausente"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:289
|
||||
msgid "Error: Product not found!"
|
||||
msgstr ""
|
||||
msgstr "Erro: Non se atopou o produto!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:296
|
||||
msgid "Error: Variation not found!"
|
||||
msgstr ""
|
||||
msgstr "Erro: Variación non atopada!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:171
|
||||
msgid "Check-in QR"
|
||||
@@ -597,16 +583,12 @@ msgid "Group of objects"
|
||||
msgstr "Grupo de obxectos"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:909
|
||||
#, fuzzy
|
||||
#| msgid "Text object"
|
||||
msgid "Text object (deprecated)"
|
||||
msgstr "Obxecto de texto"
|
||||
msgstr "Obxecto de texto (obsoleto)"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:911
|
||||
#, fuzzy
|
||||
#| msgid "Text object"
|
||||
msgid "Text box"
|
||||
msgstr "Obxecto de texto"
|
||||
msgstr "Caixa de texto"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:913
|
||||
msgid "Barcode area"
|
||||
@@ -655,26 +637,26 @@ msgid "Unknown error."
|
||||
msgstr "Erro descoñecido."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
||||
#, fuzzy
|
||||
#| msgid "Your color has great contrast and is very easy to read!"
|
||||
msgid "Your color has great contrast and will provide excellent accessibility."
|
||||
msgstr "A túa cor ten moito contraste e é moi doada de ler!"
|
||||
msgstr ""
|
||||
"A túa cor ten un gran contraste e proporcionará unha excelente "
|
||||
"accesibilidade."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
||||
#, fuzzy
|
||||
#| msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgid ""
|
||||
"Your color has decent contrast and is sufficient for minimum accessibility "
|
||||
"requirements."
|
||||
msgstr ""
|
||||
"A túa cor ten un contraste axeitado e probablemente sexa suficientemente "
|
||||
"lexible!"
|
||||
"A túa cor ten un contraste decente e é suficiente para os requisitos mínimos "
|
||||
"de accesibilidade."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
||||
msgid ""
|
||||
"Your color has insufficient contrast to white. Accessibility of your site "
|
||||
"will be impacted."
|
||||
msgstr ""
|
||||
"A túa cor non ten suficiente contraste co branco. A accesibilidade do teu "
|
||||
"sitio web verase afectada."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:443
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:463
|
||||
@@ -695,11 +677,11 @@ msgstr "Soamente seleccionados"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:839
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
msgstr "Introduza o número de páxina entre 1 e %(max)s."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:842
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
msgstr "Número de páxina non válido."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1000
|
||||
msgid "Use a different name internally"
|
||||
@@ -718,10 +700,8 @@ msgid "Calculating default price…"
|
||||
msgstr "Calculando o prezo por defecto…"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/plugins.js:69
|
||||
#, fuzzy
|
||||
#| msgid "Search results"
|
||||
msgid "No results"
|
||||
msgstr "Resultados da procura"
|
||||
msgstr "Sen resultados"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:41
|
||||
msgid "Others"
|
||||
@@ -752,7 +732,7 @@ msgstr "O carro da compra caducou"
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:58
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:84
|
||||
msgid "Your cart is about to expire."
|
||||
msgstr ""
|
||||
msgstr "O teu carriño está a piques de caducar."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:62
|
||||
msgid "The items in your cart are reserved for you for one minute."
|
||||
@@ -762,16 +742,10 @@ msgstr[1] ""
|
||||
"Os artigos da túa cesta están reservados para ti durante {num} minutos."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:83
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "Your cart has expired."
|
||||
msgstr "O carro da compra caducou"
|
||||
msgstr "O carro da compra caducou."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:86
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "The items in your cart are no longer reserved for you. You can still "
|
||||
#| "complete your order as long as they’re available."
|
||||
msgid ""
|
||||
"The items in your cart are no longer reserved for you. You can still "
|
||||
"complete your order as long as they're available."
|
||||
@@ -781,11 +755,11 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:87
|
||||
msgid "Do you want to renew the reservation period?"
|
||||
msgstr ""
|
||||
msgstr "Queres renovar o período de reserva?"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:90
|
||||
msgid "Renew reservation"
|
||||
msgstr ""
|
||||
msgstr "Renovar reserva"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:194
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
@@ -805,71 +779,66 @@ msgstr "A súa hora local:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||
msgid "Google Pay"
|
||||
msgstr ""
|
||||
msgstr "Google Pay"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:16
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
msgstr ""
|
||||
msgstr "Cantidade"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||
msgctxt "widget"
|
||||
msgid "Decrease quantity"
|
||||
msgstr ""
|
||||
msgstr "Diminuír a cantidade"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:18
|
||||
msgctxt "widget"
|
||||
msgid "Increase quantity"
|
||||
msgstr ""
|
||||
msgstr "Aumentar a cantidade"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
||||
msgctxt "widget"
|
||||
msgid "Filter events by"
|
||||
msgstr ""
|
||||
msgstr "Filtrar eventos por"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
||||
msgctxt "widget"
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
msgstr "Filtro"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
||||
msgctxt "widget"
|
||||
msgid "Price"
|
||||
msgstr ""
|
||||
msgstr "Prezo"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:22
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Original price: %s"
|
||||
msgstr ""
|
||||
msgstr "Prezo orixinal: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:23
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "New price: %s"
|
||||
msgstr ""
|
||||
msgstr "Novo prezo: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:24
|
||||
#, fuzzy
|
||||
#| msgid "Selected only"
|
||||
msgctxt "widget"
|
||||
msgid "Select"
|
||||
msgstr "Soamente seleccionados"
|
||||
msgstr "Seleccione"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:25
|
||||
#, fuzzy, javascript-format
|
||||
#| msgid "Selected only"
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select %s"
|
||||
msgstr "Soamente seleccionados"
|
||||
msgstr "Seleccione %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:26
|
||||
#, fuzzy, javascript-format
|
||||
#| msgctxt "widget"
|
||||
#| msgid "See variations"
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select variant %s"
|
||||
msgstr "Ver variacións"
|
||||
msgstr "Seleccione a variante %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:27
|
||||
msgctxt "widget"
|
||||
@@ -905,7 +874,7 @@ msgstr "dende %(currency)s %(price)s"
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Image of %s"
|
||||
msgstr ""
|
||||
msgstr "Imaxe de %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:34
|
||||
msgctxt "widget"
|
||||
@@ -940,25 +909,19 @@ msgstr "Só dispoñible mediante vale"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:40
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:43
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "currently available: %s"
|
||||
msgctxt "widget"
|
||||
msgid "Not yet available"
|
||||
msgstr "dispoñible actualmente: %s"
|
||||
msgstr "Aínda non dispoñible"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:41
|
||||
msgctxt "widget"
|
||||
msgid "Not available anymore"
|
||||
msgstr ""
|
||||
msgstr "Xa non está dispoñible"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:42
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "currently available: %s"
|
||||
msgctxt "widget"
|
||||
msgid "Currently not available"
|
||||
msgstr "dispoñible actualmente: %s"
|
||||
msgstr "Non dispoñible actualmente"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:44
|
||||
#, javascript-format
|
||||
@@ -991,9 +954,6 @@ msgid "Open ticket shop"
|
||||
msgstr "Abrir a tenda de tíckets"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "Resume checkout"
|
||||
msgctxt "widget"
|
||||
msgid "Checkout"
|
||||
msgstr "Continuar co pagamento"
|
||||
@@ -1053,17 +1013,14 @@ msgid "Close"
|
||||
msgstr "Cerrar"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "Resume checkout"
|
||||
msgctxt "widget"
|
||||
msgid "Close checkout"
|
||||
msgstr "Continuar co pagamento"
|
||||
msgstr "Pagamento pechado"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
msgctxt "widget"
|
||||
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
||||
msgstr ""
|
||||
msgstr "Non podes cancelar esta operación. Agarda a que remate a carga."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
msgctxt "widget"
|
||||
@@ -1071,20 +1028,14 @@ msgid "Continue"
|
||||
msgstr "Continuar"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "See variations"
|
||||
msgctxt "widget"
|
||||
msgid "Show variants"
|
||||
msgstr "Ver variacións"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "See variations"
|
||||
msgctxt "widget"
|
||||
msgid "Hide variants"
|
||||
msgstr "Ver variacións"
|
||||
msgstr "Ocultar variantes"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||
msgctxt "widget"
|
||||
@@ -1133,6 +1084,9 @@ msgid ""
|
||||
"add yourself to the waiting list. We will then notify if seats are available "
|
||||
"again."
|
||||
msgstr ""
|
||||
"Algunhas ou todas as categorías de entradas están esgotadas. Se queres, "
|
||||
"podes engadirte á lista de espera. Despois avisarémosche se volven quedar "
|
||||
"asentos dispoñibles."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||
msgctxt "widget"
|
||||
@@ -1169,31 +1123,31 @@ msgstr "Dom"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
||||
msgid "Monday"
|
||||
msgstr ""
|
||||
msgstr "Luns"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
||||
msgid "Tuesday"
|
||||
msgstr ""
|
||||
msgstr "Martes"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
||||
msgid "Wednesday"
|
||||
msgstr ""
|
||||
msgstr "Mércores"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
||||
msgid "Thursday"
|
||||
msgstr ""
|
||||
msgstr "Xoves"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
||||
msgid "Friday"
|
||||
msgstr ""
|
||||
msgstr "Venres"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
||||
msgid "Saturday"
|
||||
msgstr ""
|
||||
msgstr "Sábado"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
||||
msgid "Sunday"
|
||||
msgstr ""
|
||||
msgstr "Domingo"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:94
|
||||
msgid "January"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-11-27 13:57+0000\n"
|
||||
"PO-Revision-Date: 2025-12-03 13:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-03 23:00+0000\n"
|
||||
"Last-Translator: SJang1 <git@sjang.dev>\n"
|
||||
"Language-Team: Korean <https://translate.pretix.eu/projects/pretix/pretix/ko/"
|
||||
">\n"
|
||||
@@ -3749,7 +3749,7 @@ msgid ""
|
||||
msgstr "{date}에 {authority}에서 발표한 1:{rate}의 변환율을 사용하면 다음과 같습니다:"
|
||||
|
||||
#: pretix/base/invoicing/pdf.py:1115
|
||||
#, python-brace-format
|
||||
#, fuzzy, python-brace-format
|
||||
msgctxt "invoice"
|
||||
msgid ""
|
||||
"Using the conversion rate of 1:{rate} as published by the {authority} on "
|
||||
|
||||
@@ -46,12 +46,12 @@ from i18nfield.forms import I18nTextInput
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from localflavor.generic.forms import BICFormField, IBANFormField
|
||||
from localflavor.generic.validators import IBANValidator
|
||||
from text_unidecode import unidecode
|
||||
|
||||
from pretix.base.forms import I18nMarkdownTextarea
|
||||
from pretix.base.models import InvoiceAddress, Order, OrderPayment, OrderRefund
|
||||
from pretix.base.payment import BasePaymentProvider
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.helpers.payment import generate_payment_qr_codes
|
||||
from pretix.plugins.banktransfer.templatetags.ibanformat import ibanformat
|
||||
from pretix.presale.views.cart import cart_session
|
||||
|
||||
@@ -313,51 +313,6 @@ class BankTransfer(BasePaymentProvider):
|
||||
t += str(self.settings.get('bank_details', as_type=LazyI18nString))
|
||||
return t
|
||||
|
||||
def swiss_qrbill(self, payment):
|
||||
if not self.settings.get('bank_details_sepa_iban') or not self.settings.get('bank_details_sepa_iban')[:2] in ('CH', 'LI'):
|
||||
return
|
||||
if self.event.currency not in ('EUR', 'CHF'):
|
||||
return
|
||||
if not self.event.settings.invoice_address_from or not self.event.settings.invoice_address_from_country:
|
||||
return
|
||||
|
||||
data_fields = [
|
||||
'SPC',
|
||||
'0200',
|
||||
'1',
|
||||
self.settings.get('bank_details_sepa_iban'),
|
||||
'K',
|
||||
self.settings.get('bank_details_sepa_name')[:70],
|
||||
self.event.settings.invoice_address_from.replace('\n', ', ')[:70],
|
||||
(self.event.settings.invoice_address_from_zipcode + ' ' + self.event.settings.invoice_address_from_city)[:70],
|
||||
'',
|
||||
'',
|
||||
str(self.event.settings.invoice_address_from_country),
|
||||
'', # rfu
|
||||
'', # rfu
|
||||
'', # rfu
|
||||
'', # rfu
|
||||
'', # rfu
|
||||
'', # rfu
|
||||
'', # rfu
|
||||
str(payment.amount),
|
||||
self.event.currency,
|
||||
'', # debtor address
|
||||
'', # debtor address
|
||||
'', # debtor address
|
||||
'', # debtor address
|
||||
'', # debtor address
|
||||
'', # debtor address
|
||||
'', # debtor address
|
||||
'NON',
|
||||
'', # structured reference
|
||||
self._code(payment.order),
|
||||
'EPD',
|
||||
]
|
||||
|
||||
data_fields = [unidecode(d or '') for d in data_fields]
|
||||
return '\r\n'.join(data_fields)
|
||||
|
||||
def payment_pending_render(self, request: HttpRequest, payment: OrderPayment):
|
||||
template = get_template('pretixplugins/banktransfer/pending.html')
|
||||
ctx = {
|
||||
@@ -367,13 +322,18 @@ class BankTransfer(BasePaymentProvider):
|
||||
'amount': payment.amount,
|
||||
'payment_info': payment.info_data,
|
||||
'settings': self.settings,
|
||||
'swiss_qrbill': self.swiss_qrbill(payment),
|
||||
'eu_barcodes': self.event.currency == 'EUR',
|
||||
'payment_qr_codes': generate_payment_qr_codes(
|
||||
event=self.event,
|
||||
code=self._code(payment.order),
|
||||
amount=payment.amount,
|
||||
bank_details_sepa_bic=self.settings.get('bank_details_sepa_bic'),
|
||||
bank_details_sepa_name=self.settings.get('bank_details_sepa_name'),
|
||||
bank_details_sepa_iban=self.settings.get('bank_details_sepa_iban'),
|
||||
) if self.settings.bank_details_type == "sepa" else None,
|
||||
'pending_description': self.settings.get('pending_description', as_type=LazyI18nString),
|
||||
'details': self.settings.get('bank_details', as_type=LazyI18nString),
|
||||
'has_invoices': payment.order.invoices.exists(),
|
||||
}
|
||||
ctx['any_barcodes'] = ctx['swiss_qrbill'] or ctx['eu_barcodes']
|
||||
return template.render(ctx, request=request)
|
||||
|
||||
def payment_control_render(self, request: HttpRequest, payment: OrderPayment) -> str:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load commadecimal %}
|
||||
{% load static %}
|
||||
{% load dotdecimal %}
|
||||
{% load ibanformat %}
|
||||
{% load money %}
|
||||
@@ -17,7 +16,7 @@
|
||||
{% endblocktrans %}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="{% if settings.bank_details_type == "sepa" %}col-md-6{% else %}col-md-12{% endif %} col-xs-12">
|
||||
<div class="{% if payment_qr_codes %}col-md-6{% else %}col-md-12{% endif %} col-xs-12">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Reference code (important):" %}</dt><dd><b>{{ code }}</b></dd>
|
||||
<dt>{% trans "Amount:" %}</dt><dd>{{ amount|money:event.currency }}</dd>
|
||||
@@ -36,94 +35,7 @@
|
||||
{% trans "We will send you an email as soon as we received your payment." %}
|
||||
</p>
|
||||
</div>
|
||||
{% if settings.bank_details_type == "sepa" and any_barcodes %}
|
||||
<div class="tabcontainer col-md-6 col-sm-6 hidden-xs text-center js-only blank-after">
|
||||
<div id="banktransfer_qrcodes_tabs_content" class="tabpanels blank-after">
|
||||
{% if swiss_qrbill %}
|
||||
<div id="banktransfer_qrcodes_qrbill"
|
||||
role="tabpanel"
|
||||
tabindex="0"
|
||||
aria-labelledby="banktransfer_qrcodes_qrbill_tab"
|
||||
>
|
||||
<div class="banktransfer-swiss-cross-overlay" role="figure" aria-labelledby="banktransfer_qrcodes_qrbill_tab banktransfer_qrcodes_label">
|
||||
<svg class="banktransfer-swiss-cross" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.8 19.8"><path stroke="#fff" stroke-width="1.436" d="M.7.7h18.4v18.4H.7z"/><path fill="#fff" d="M8.3 4h3.3v11H8.3z"/><path fill="#fff" d="M4.4 7.9h11v3.3h-11z"/></svg>
|
||||
<script type="text/plain" data-size="150" data-replace-with-qr data-desc="{% trans 'Scan this image with your banking app’s QR-Reader to start the payment process.' %}">{{swiss_qrbill}}</script>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if eu_barcodes %}
|
||||
<div id="banktransfer_qrcodes_girocode"
|
||||
role="tabpanel"
|
||||
tabindex="0"
|
||||
{{ swiss_qrbill|yesno:'hidden,' }}
|
||||
aria-labelledby="banktransfer_qrcodes_girocode_tab"
|
||||
>
|
||||
<div role="figure" aria-labelledby="banktransfer_qrcodes_girocode_tab banktransfer_qrcodes_label">
|
||||
<script type="text/plain" data-size="150" data-replace-with-qr data-desc="{% trans 'Scan this image with your banking app’s QR-Reader to start the payment process.' %}">BCD
|
||||
002
|
||||
2
|
||||
SCT
|
||||
{{ settings.bank_details_sepa_bic }}
|
||||
{{ settings.bank_details_sepa_name|unidecode }}
|
||||
{{ settings.bank_details_sepa_iban }}
|
||||
{{ event.currency }}{{ amount|dotdecimal }}
|
||||
|
||||
|
||||
{{ code }}
|
||||
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<div id="banktransfer_qrcodes_bezahlcode"
|
||||
role="tabpanel"
|
||||
tabindex="0"
|
||||
hidden
|
||||
aria-labelledby="banktransfer_qrcodes_bezahlcode_tab"
|
||||
>
|
||||
<a aria-label="{% trans "Open BezahlCode in your banking app to start the payment process." %}" href="bank://singlepaymentsepa?name={{ settings.bank_details_sepa_name|urlencode }}&iban={{ settings.bank_details_sepa_iban }}&bic={{ settings.bank_details_sepa_bic }}&amount={{ amount|commadecimal }}&reason={{ code }}¤cy={{ event.currency }}">
|
||||
<div role="figure" aria-labelledby="banktransfer_qrcodes_bezahlcode_tab banktransfer_qrcodes_label">
|
||||
<script type="text/plain" data-size="150" data-replace-with-qr data-desc="{% trans 'Scan this image with your banking app’s QR-Reader to start the payment process.' %}">bank://singlepaymentsepa?name={{ settings.bank_details_sepa_name|urlencode }}&iban={{ settings.bank_details_sepa_iban }}&bic={{ settings.bank_details_sepa_bic }}&amount={{ amount|commadecimal }}&reason={{ code }}¤cy={{ event.currency }}</script>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="banktransfer_qrcodes_tabs" role="tablist" aria-labelledby="banktransfer_qrcodes_label" class="blank-after btn-group">
|
||||
{% if swiss_qrbill %}
|
||||
<button
|
||||
class="btn btn-default"
|
||||
id="banktransfer_qrcodes_qrbill_tab"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="banktransfer_qrcodes_qrbill"
|
||||
aria-selected="true"
|
||||
tabindex="-1">QR-bill</button>
|
||||
{% endif %}
|
||||
{% if eu_barcodes %}
|
||||
<button
|
||||
class="btn btn-default"
|
||||
id="banktransfer_qrcodes_girocode_tab"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="banktransfer_qrcodes_girocode"
|
||||
aria-selected="{{ swiss_qrbill|yesno:"false,true" }}"
|
||||
tabindex="-1">EPC-QR</button>
|
||||
<button
|
||||
class="btn btn-default"
|
||||
id="banktransfer_qrcodes_bezahlcode_tab"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="banktransfer_qrcodes_bezahlcode"
|
||||
aria-selected="false"
|
||||
tabindex="-1">BezahlCode</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p class="text-muted" id="banktransfer_qrcodes_label">
|
||||
{% trans "Scan the QR code with your banking app" %}
|
||||
</p>
|
||||
</div>
|
||||
{% if payment_qr_codes %}
|
||||
{% include "pretixpresale/event/payment_qr_codes.html" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if swiss_qrbill %}
|
||||
<link rel="stylesheet" href="{% static "pretixplugins/banktransfer/swisscross.css" %}">
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -644,7 +644,9 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
FontFallbackParagraph(
|
||||
_("Pending payments at {datetime}").format(
|
||||
datetime=date_format(
|
||||
df_start - datetime.timedelta.resolution,
|
||||
(df_start - datetime.timedelta.resolution).astimezone(
|
||||
self.timezone
|
||||
),
|
||||
"SHORT_DATETIME_FORMAT",
|
||||
)
|
||||
),
|
||||
@@ -694,7 +696,9 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
Paragraph(
|
||||
_("Pending payments at {datetime}").format(
|
||||
datetime=date_format(
|
||||
(df_end or now()) - datetime.timedelta.resolution,
|
||||
((df_end or now()) - datetime.timedelta.resolution).astimezone(
|
||||
self.timezone
|
||||
),
|
||||
"SHORT_DATETIME_FORMAT",
|
||||
)
|
||||
),
|
||||
@@ -751,7 +755,9 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
Paragraph(
|
||||
_("Total gift card value at {datetime}").format(
|
||||
datetime=date_format(
|
||||
df_start - datetime.timedelta.resolution,
|
||||
(df_start - datetime.timedelta.resolution).astimezone(
|
||||
self.timezone
|
||||
),
|
||||
"SHORT_DATETIME_FORMAT",
|
||||
)
|
||||
),
|
||||
@@ -789,7 +795,9 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
Paragraph(
|
||||
_("Total gift card value at {datetime}").format(
|
||||
datetime=date_format(
|
||||
(df_end or now()) - datetime.timedelta.resolution,
|
||||
((df_end or now()) - datetime.timedelta.resolution).astimezone(
|
||||
self.timezone
|
||||
),
|
||||
"SHORT_DATETIME_FORMAT",
|
||||
)
|
||||
),
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
{% if payment_qr_codes %}
|
||||
<div class="tabcontainer col-md-6 col-sm-6 hidden-xs text-center js-only blank-after">
|
||||
<div id="banktransfer_qrcodes_tabs_content" class="tabpanels blank-after">
|
||||
{% for code_info in payment_qr_codes %}
|
||||
<div id="banktransfer_qrcodes_{{ code_info.id }}"
|
||||
role="tabpanel"
|
||||
tabindex="0"
|
||||
{% if not forloop.first %}hidden{% endif %}
|
||||
aria-labelledby="banktransfer_qrcodes_{{ code_info.id }}_tab"
|
||||
>
|
||||
{% if code_info.link %}<a aria-label="{{ code_info.link_aria_label }}" href="{{ code_info.link }}">{% endif %}
|
||||
<div class="{{ code_info.css_class }}" role="figure" aria-labelledby="banktransfer_qrcodes_{{ code_info.id }}_tab banktransfer_qrcodes_label">
|
||||
{{ code_info.html_prefix }}
|
||||
<script type="text/plain" data-size="150" data-replace-with-qr data-desc="{% trans 'Scan this image with your banking app’s QR-Reader to start the payment process.' %}">{{ code_info.qr_data }}</script>
|
||||
</div>
|
||||
{% if code_info.link %}</a>{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div id="banktransfer_qrcodes_tabs" role="tablist" aria-labelledby="banktransfer_qrcodes_label" class="blank-after btn-group">
|
||||
{% for code_info in payment_qr_codes %}
|
||||
<button
|
||||
class="btn btn-default"
|
||||
id="banktransfer_qrcodes_{{ code_info.id }}_tab"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="banktransfer_qrcodes_{{ code_info.id }}"
|
||||
aria-selected="{{ forloop.first|yesno:"true,false" }}"
|
||||
tabindex="-1">{{ code_info.label }}</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p class="text-muted" id="banktransfer_qrcodes_label">
|
||||
{% trans "Scan the QR code with your banking app" %}
|
||||
</p>
|
||||
</div>
|
||||
{% for code_info in payment_qr_codes %}
|
||||
{% if code_info.id == "qrbill" %}
|
||||
<link rel="stylesheet" href="{% static "pretixplugins/banktransfer/swisscross.css" %}">
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
141
src/tests/helpers/test_payment.py
Normal file
141
src/tests/helpers/test_payment.py
Normal file
@@ -0,0 +1,141 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-today pretix GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
from django.utils.timezone import now
|
||||
|
||||
from pretix.base.models import Event, Organizer
|
||||
from pretix.helpers.payment import generate_payment_qr_codes
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def env():
|
||||
o = Organizer.objects.create(name='Verein für Testzwecke e.V.', slug='testverein')
|
||||
event = Event.objects.create(
|
||||
organizer=o, name='Testveranstaltung', slug='testveranst',
|
||||
date_from=now() + timedelta(days=10),
|
||||
live=True, is_public=False, currency='EUR',
|
||||
)
|
||||
event.settings.invoice_address_from = 'Verein für Testzwecke e.V.'
|
||||
event.settings.invoice_address_from_zipcode = '1234'
|
||||
event.settings.invoice_address_from_city = 'Testhausen'
|
||||
event.settings.invoice_address_from_country = 'CH'
|
||||
|
||||
return o, event
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_payment_qr_codes_euro(env):
|
||||
o, event = env
|
||||
codes = generate_payment_qr_codes(
|
||||
event=event,
|
||||
code='TESTVERANST-12345',
|
||||
amount=Decimal('123.00'),
|
||||
bank_details_sepa_bic='BYLADEM1MIL',
|
||||
bank_details_sepa_iban='DE37796500000069799047',
|
||||
bank_details_sepa_name='Verein für Testzwecke e.V.',
|
||||
)
|
||||
assert len(codes) == 2
|
||||
assert codes[0]['label'] == 'EPC-QR'
|
||||
assert codes[0]['qr_data'] == '''BCD
|
||||
002
|
||||
2
|
||||
SCT
|
||||
BYLADEM1MIL
|
||||
Verein fur Testzwecke e.V.
|
||||
DE37796500000069799047
|
||||
EUR123.00
|
||||
|
||||
|
||||
TESTVERANST-12345
|
||||
|
||||
'''
|
||||
|
||||
assert codes[1]['label'] == 'BezahlCode'
|
||||
assert codes[1]['qr_data'] == ('bank://singlepaymentsepa?name=Verein%20f%C3%BCr%20Testzwecke%20e.V.&iban=DE37796500000069799047'
|
||||
'&bic=BYLADEM1MIL&amount=123%2C00&reason=TESTVERANST-12345¤cy=EUR')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_payment_qr_codes_swiss(env):
|
||||
o, event = env
|
||||
codes = generate_payment_qr_codes(
|
||||
event=event,
|
||||
code='TESTVERANST-12345',
|
||||
amount=Decimal('123.00'),
|
||||
bank_details_sepa_bic='TESTCHXXXXX',
|
||||
bank_details_sepa_iban='CH6389144757654882127',
|
||||
bank_details_sepa_name='Verein für Testzwecke e.V.',
|
||||
)
|
||||
assert codes[0]['label'] == 'QR-bill'
|
||||
assert codes[0]['qr_data'] == "\r\n".join([
|
||||
"SPC",
|
||||
"0200",
|
||||
"1",
|
||||
"CH6389144757654882127",
|
||||
"K",
|
||||
"Verein fur Testzwecke e.V.",
|
||||
"Verein fur Testzwecke e.V.",
|
||||
"1234 Testhausen",
|
||||
"",
|
||||
"",
|
||||
"CH",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"123.00",
|
||||
"EUR",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"NON",
|
||||
"",
|
||||
"TESTVERANST-12345",
|
||||
"EPD",
|
||||
])
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_payment_qr_codes_spayd(env):
|
||||
o, event = env
|
||||
codes = generate_payment_qr_codes(
|
||||
event=event,
|
||||
code='TESTVERANST-12345',
|
||||
amount=Decimal('123.00'),
|
||||
bank_details_sepa_bic='TESTCZXXXXX',
|
||||
bank_details_sepa_iban='CZ7450513769129174398769',
|
||||
bank_details_sepa_name='Verein für Testzwecke e.V.',
|
||||
)
|
||||
assert len(codes) == 2
|
||||
assert codes[0]['label'] == 'SPAYD'
|
||||
assert codes[0]['qr_data'] == 'SPD*1.0*ACC:CZ7450513769129174398769*AM:123.00*CC:EUR*MSG:TESTVERANST-12345'
|
||||
assert codes[1]['label'] == 'EPC-QR'
|
||||
@@ -1428,6 +1428,29 @@ class CartTest(CartTestMixin, TestCase):
|
||||
self.assertEqual(cp2.expires, now() + self.cart_reservation_time)
|
||||
self.assertEqual(cp2.max_extend, now() + 11 * self.cart_reservation_time)
|
||||
|
||||
def test_expired_cart_extend_fails_partially_on_bundled(self):
|
||||
start_time = datetime.datetime(2024, 1, 1, 10, 00, 00, tzinfo=datetime.timezone.utc)
|
||||
max_extend = start_time + 11 * self.cart_reservation_time
|
||||
self.quota_shirts.size = 0
|
||||
self.quota_shirts.save()
|
||||
with scopes_disabled():
|
||||
cp1 = CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
price=23, expires=max_extend, max_extend=max_extend
|
||||
)
|
||||
cp2 = CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.shirt, variation=self.shirt_blue,
|
||||
price=23, expires=max_extend, max_extend=max_extend, addon_to=cp1, is_bundled=True,
|
||||
)
|
||||
with freezegun.freeze_time(max_extend + timedelta(hours=1)):
|
||||
response = self.client.post('/%s/%s/cart/extend' % (self.orga.slug, self.event.slug), {
|
||||
}, follow=True)
|
||||
doc = BeautifulSoup(response.rendered_content, "lxml")
|
||||
self.assertIn('no longer available', doc.select('.alert-danger')[0].text)
|
||||
with scopes_disabled():
|
||||
self.assertFalse(CartPosition.objects.filter(id=cp1.id).exists())
|
||||
self.assertFalse(CartPosition.objects.filter(id=cp2.id).exists())
|
||||
|
||||
def test_subevent_renew_expired_successfully(self):
|
||||
self.event.has_subevents = True
|
||||
self.event.save()
|
||||
|
||||
Reference in New Issue
Block a user