mirror of
https://github.com/pretix/pretix.git
synced 2025-12-05 21:32:28 +00:00
Compare commits
21 Commits
sendmail-i
...
c491c8232e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c491c8232e | ||
|
|
aa02cc7968 | ||
|
|
cfa13d6b9d | ||
|
|
af4eabc800 | ||
|
|
e1f5678d7c | ||
|
|
609b7c82ee | ||
|
|
8d66e1e732 | ||
|
|
c925f094f2 | ||
|
|
5caaa8586d | ||
|
|
1b1cf1557d | ||
|
|
35d8a7eec5 | ||
|
|
d428c3e1a4 | ||
|
|
63850f3139 | ||
|
|
04c8270d43 | ||
|
|
74a960e239 | ||
|
|
5a1bcae085 | ||
|
|
051eb78312 | ||
|
|
15808e55fd | ||
|
|
c886c0b415 | ||
|
|
47472447eb | ||
|
|
1a40215e91 |
@@ -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.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ dependencies = [
|
||||
"babel",
|
||||
"BeautifulSoup4==4.14.*",
|
||||
"bleach==6.2.*",
|
||||
"celery==5.5.*",
|
||||
"celery==5.6.*",
|
||||
"chardet==5.2.*",
|
||||
"cryptography>=44.0.0",
|
||||
"css-inline==0.18.*",
|
||||
@@ -62,7 +62,7 @@ dependencies = [
|
||||
"importlib_metadata==8.*", # Polyfill, we can probably drop this once we require Python 3.10+
|
||||
"isoweek",
|
||||
"jsonschema",
|
||||
"kombu==5.5.*",
|
||||
"kombu==5.6.*",
|
||||
"libsass==0.23.*",
|
||||
"lxml",
|
||||
"markdown==3.9", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
|
||||
@@ -92,14 +92,13 @@ 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.*",
|
||||
"tlds>=2020041600",
|
||||
"tqdm==4.*",
|
||||
"ua-parser==1.0.*",
|
||||
"vat_moss_forked==2020.3.20.0.11.0",
|
||||
"vobject==0.9.*",
|
||||
"webauthn==2.7.*",
|
||||
"zeep==4.3.*"
|
||||
|
||||
@@ -795,6 +795,7 @@ class EventSettingsSerializer(SettingsSerializer):
|
||||
'invoice_address_asked',
|
||||
'invoice_address_required',
|
||||
'invoice_address_vatid',
|
||||
'invoice_address_vatid_required_countries',
|
||||
'invoice_address_company_required',
|
||||
'invoice_address_beneficiary',
|
||||
'invoice_address_custom_field',
|
||||
@@ -943,6 +944,7 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
|
||||
'invoice_address_asked',
|
||||
'invoice_address_required',
|
||||
'invoice_address_vatid',
|
||||
'invoice_address_vatid_required_countries',
|
||||
'invoice_address_company_required',
|
||||
'invoice_address_beneficiary',
|
||||
'invoice_address_custom_field',
|
||||
|
||||
@@ -567,7 +567,7 @@ class QuotaViewSet(ConditionalListView, viewsets.ModelViewSet):
|
||||
write_permission = 'can_change_items'
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.event.quotas.all()
|
||||
return self.request.event.quotas.select_related('subevent').prefetch_related('items', 'variations').all()
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
queryset = self.filter_queryset(self.get_queryset()).distinct()
|
||||
|
||||
@@ -721,7 +721,7 @@ class MembershipViewSet(viewsets.ModelViewSet):
|
||||
def get_queryset(self):
|
||||
return Membership.objects.filter(
|
||||
customer__organizer=self.request.organizer
|
||||
)
|
||||
).select_related('customer')
|
||||
|
||||
def get_serializer_context(self):
|
||||
ctx = super().get_serializer_context()
|
||||
|
||||
@@ -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/>.
|
||||
#
|
||||
|
||||
from django.db import transaction
|
||||
from django.db.models import F, Q
|
||||
from django.utils.timezone import now
|
||||
@@ -64,8 +65,13 @@ class VoucherViewSet(viewsets.ModelViewSet):
|
||||
permission = 'can_view_vouchers'
|
||||
write_permission = 'can_change_vouchers'
|
||||
|
||||
@scopes_disabled() # we have an event check here, and we can save some performance on subqueries
|
||||
def get_queryset(self):
|
||||
return self.request.event.vouchers.select_related('seat').all()
|
||||
return Voucher.annotate_budget_used(
|
||||
self.request.event.vouchers
|
||||
).select_related(
|
||||
'item', 'quota', 'seat', 'variation'
|
||||
)
|
||||
|
||||
@transaction.atomic()
|
||||
def create(self, request, *args, **kwargs):
|
||||
|
||||
@@ -83,7 +83,7 @@ from pretix.base.invoicing.transmission import (
|
||||
from pretix.base.models import InvoiceAddress, Item, Question, QuestionOption
|
||||
from pretix.base.models.tax import ask_for_vat_id
|
||||
from pretix.base.services.tax import (
|
||||
VATIDFinalError, VATIDTemporaryError, validate_vat_id,
|
||||
VATIDFinalError, VATIDTemporaryError, normalize_vat_id, validate_vat_id,
|
||||
)
|
||||
from pretix.base.settings import (
|
||||
COUNTRIES_WITH_STATE_IN_ADDRESS, COUNTRY_STATE_LABEL,
|
||||
@@ -1165,13 +1165,11 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
self.fields['vat_id'].help_text = '<br/>'.join([
|
||||
str(_('Optional, but depending on the country you reside in we might need to charge you '
|
||||
'additional taxes if you do not enter it.')),
|
||||
str(_('If you are registered in Switzerland, you can enter your UID instead.')),
|
||||
])
|
||||
else:
|
||||
self.fields['vat_id'].help_text = '<br/>'.join([
|
||||
str(_('Optional, but it might be required for you to claim tax benefits on your invoice '
|
||||
'depending on your and the seller’s country of residence.')),
|
||||
str(_('If you are registered in Switzerland, you can enter your UID instead.')),
|
||||
])
|
||||
|
||||
transmission_type_choices = [
|
||||
@@ -1358,13 +1356,24 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
"transmission method.")}
|
||||
)
|
||||
|
||||
vat_id_applicable = (
|
||||
'vat_id' in self.fields and
|
||||
data.get('is_business') and
|
||||
ask_for_vat_id(data.get('country'))
|
||||
)
|
||||
vat_id_required = vat_id_applicable and str(data.get('country')) in self.event.settings.invoice_address_vatid_required_countries
|
||||
if vat_id_required and not data.get('vat_id'):
|
||||
raise ValidationError({
|
||||
"vat_id": _("This field is required.")
|
||||
})
|
||||
|
||||
if self.validate_vat_id and self.instance.vat_id_validated and 'vat_id' not in self.changed_data:
|
||||
pass
|
||||
elif self.validate_vat_id and data.get('is_business') and ask_for_vat_id(data.get('country')) and data.get('vat_id'):
|
||||
pass # Skip re-validation if it is validated
|
||||
elif self.validate_vat_id and vat_id_applicable:
|
||||
try:
|
||||
normalized_id = validate_vat_id(data.get('vat_id'), str(data.get('country')))
|
||||
self.instance.vat_id_validated = True
|
||||
self.instance.vat_id = normalized_id
|
||||
self.instance.vat_id = data['vat_id'] = normalized_id
|
||||
except VATIDFinalError as e:
|
||||
if self.all_optional:
|
||||
self.instance.vat_id_validated = False
|
||||
@@ -1372,6 +1381,9 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
else:
|
||||
raise ValidationError({"vat_id": e.message})
|
||||
except VATIDTemporaryError as e:
|
||||
# We couldn't check it online, but we can still normalize it
|
||||
normalized_id = normalize_vat_id(data.get('vat_id'), str(data.get('country')))
|
||||
self.instance.vat_id = data['vat_id'] = normalized_id
|
||||
self.instance.vat_id_validated = False
|
||||
if self.request and self.vat_warning:
|
||||
messages.warning(self.request, e.message)
|
||||
|
||||
@@ -32,7 +32,6 @@ from itertools import groupby
|
||||
from typing import Tuple
|
||||
|
||||
import bleach
|
||||
import vat_moss.exchange_rates
|
||||
from bidi import get_display
|
||||
from django.contrib.staticfiles import finders
|
||||
from django.db.models import Sum
|
||||
@@ -1059,7 +1058,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
|
||||
def fmt(val):
|
||||
try:
|
||||
return vat_moss.exchange_rates.format(val, self.invoice.foreign_currency_display)
|
||||
return money_filter(val, self.invoice.foreign_currency_display)
|
||||
except ValueError:
|
||||
return localize(val) + ' ' + self.invoice.foreign_currency_display
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -623,7 +623,7 @@ class Voucher(LoggedModel):
|
||||
return max(1, self.min_usages - self.redeemed)
|
||||
|
||||
@classmethod
|
||||
def annotate_budget_used_orders(cls, qs):
|
||||
def annotate_budget_used(cls, qs):
|
||||
opq = OrderPosition.objects.filter(
|
||||
voucher_id=OuterRef('pk'),
|
||||
voucher_budget_use__isnull=False,
|
||||
@@ -632,7 +632,7 @@ class Voucher(LoggedModel):
|
||||
Order.STATUS_PENDING
|
||||
]
|
||||
).order_by().values('voucher_id').annotate(s=Sum('voucher_budget_use')).values('s')
|
||||
return qs.annotate(budget_used_orders=Coalesce(Subquery(opq, output_field=models.DecimalField(max_digits=13, decimal_places=2)), Decimal('0.00')))
|
||||
return qs.annotate(budget_used=Coalesce(Subquery(opq, output_field=models.DecimalField(max_digits=13, decimal_places=2)), Decimal('0.00')))
|
||||
|
||||
def budget_used(self):
|
||||
ops = OrderPosition.objects.filter(
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -27,7 +27,6 @@ from decimal import Decimal
|
||||
from xml.etree import ElementTree
|
||||
|
||||
import requests
|
||||
import vat_moss.id
|
||||
from django.conf import settings
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from zeep import Client, Transport
|
||||
@@ -42,14 +41,142 @@ logger = logging.getLogger(__name__)
|
||||
error_messages = {
|
||||
'unavailable': _(
|
||||
'Your VAT ID could not be checked, as the VAT checking service of '
|
||||
'your country is currently not available. We will therefore '
|
||||
'need to charge VAT on your invoice. You can get the tax amount '
|
||||
'back via the VAT reimbursement process.'
|
||||
'your country is currently not available. We will therefore need to '
|
||||
'charge you the same tax rate as if you did not enter a VAT ID.'
|
||||
),
|
||||
'invalid': _('This VAT ID is not valid. Please re-check your input.'),
|
||||
'country_mismatch': _('Your VAT ID does not match the selected country.'),
|
||||
}
|
||||
|
||||
VAT_ID_PATTERNS = {
|
||||
# Patterns generated by consulting the following URLs:
|
||||
#
|
||||
# - http://en.wikipedia.org/wiki/VAT_identification_number
|
||||
# - http://ec.europa.eu/taxation_customs/vies/faq.html
|
||||
# - https://euipo.europa.eu/tunnel-web/secure/webdav/guest/document_library/Documents/COSME/VAT%20numbers%20EU.pdf
|
||||
# - http://www.skatteetaten.no/en/International-pages/Felles-innhold-benyttes-i-flere-malgrupper/Brochure/Guide-to-value-added-tax-in-Norway/?chapter=7159
|
||||
'AT': { # Austria
|
||||
'regex': '^U\\d{8}$',
|
||||
'country_code': 'AT'
|
||||
},
|
||||
'BE': { # Belgium
|
||||
'regex': '^(1|0?)\\d{9}$',
|
||||
'country_code': 'BE'
|
||||
},
|
||||
'BG': { # Bulgaria
|
||||
'regex': '^\\d{9,10}$',
|
||||
'country_code': 'BG'
|
||||
},
|
||||
'CH': { # Switzerland
|
||||
'regex': '^\\dE{9}$',
|
||||
'country_code': 'CH'
|
||||
},
|
||||
'CY': { # Cyprus
|
||||
'regex': '^\\d{8}[A-Z]$',
|
||||
'country_code': 'CY'
|
||||
},
|
||||
'CZ': { # Czech Republic
|
||||
'regex': '^\\d{8,10}$',
|
||||
'country_code': 'CZ'
|
||||
},
|
||||
'DE': { # Germany
|
||||
'regex': '^\\d{9}$',
|
||||
'country_code': 'DE'
|
||||
},
|
||||
'DK': { # Denmark
|
||||
'regex': '^\\d{8}$',
|
||||
'country_code': 'DK'
|
||||
},
|
||||
'EE': { # Estonia
|
||||
'regex': '^\\d{9}$',
|
||||
'country_code': 'EE'
|
||||
},
|
||||
'EL': { # Greece
|
||||
'regex': '^\\d{9}$',
|
||||
'country_code': 'GR'
|
||||
},
|
||||
'ES': { # Spain
|
||||
'regex': '^[A-Z0-9]\\d{7}[A-Z0-9]$',
|
||||
'country_code': 'ES'
|
||||
},
|
||||
'FI': { # Finland
|
||||
'regex': '^\\d{8}$',
|
||||
'country_code': 'FI'
|
||||
},
|
||||
'FR': { # France
|
||||
'regex': '^[A-Z0-9]{2}\\d{9}$',
|
||||
'country_code': 'FR'
|
||||
},
|
||||
'GB': { # United Kingdom
|
||||
'regex': '^(GD\\d{3}|HA\\d{3}|\\d{9}|\\d{12})$',
|
||||
'country_code': 'GB'
|
||||
},
|
||||
'HR': { # Croatia
|
||||
'regex': '^\\d{11}$',
|
||||
'country_code': 'HR'
|
||||
},
|
||||
'HU': { # Hungary
|
||||
'regex': '^\\d{8}$',
|
||||
'country_code': 'HU'
|
||||
},
|
||||
'IE': { # Ireland
|
||||
'regex': '^(\\d{7}[A-Z]{1,2}|\\d[A-Z+*]\\d{5}[A-Z])$',
|
||||
'country_code': 'IE'
|
||||
},
|
||||
'IT': { # Italy
|
||||
'regex': '^\\d{11}$',
|
||||
'country_code': 'IT'
|
||||
},
|
||||
'LT': { # Lithuania
|
||||
'regex': '^(\\d{9}|\\d{12})$',
|
||||
'country_code': 'LT'
|
||||
},
|
||||
'LU': { # Luxembourg
|
||||
'regex': '^\\d{8}$',
|
||||
'country_code': 'LU'
|
||||
},
|
||||
'LV': { # Latvia
|
||||
'regex': '^\\d{11}$',
|
||||
'country_code': 'LV'
|
||||
},
|
||||
'MT': { # Malta
|
||||
'regex': '^\\d{8}$',
|
||||
'country_code': 'MT'
|
||||
},
|
||||
'NL': { # Netherlands
|
||||
'regex': '^\\d{9}B\\d{2}$',
|
||||
'country_code': 'NL'
|
||||
},
|
||||
'NO': { # Norway
|
||||
'regex': '^\\d{9}MVA$',
|
||||
'country_code': 'NO'
|
||||
},
|
||||
'PL': { # Poland
|
||||
'regex': '^\\d{10}$',
|
||||
'country_code': 'PL'
|
||||
},
|
||||
'PT': { # Portugal
|
||||
'regex': '^\\d{9}$',
|
||||
'country_code': 'PT'
|
||||
},
|
||||
'RO': { # Romania
|
||||
'regex': '^\\d{2,10}$',
|
||||
'country_code': 'RO'
|
||||
},
|
||||
'SE': { # Sweden
|
||||
'regex': '^\\d{12}$',
|
||||
'country_code': 'SE'
|
||||
},
|
||||
'SI': { # Slovenia
|
||||
'regex': '^\\d{8}$',
|
||||
'country_code': 'SI'
|
||||
},
|
||||
'SK': { # Slovakia
|
||||
'regex': '^\\d{10}$',
|
||||
'country_code': 'SK'
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class VATIDError(Exception):
|
||||
def __init__(self, message):
|
||||
@@ -64,13 +191,57 @@ class VATIDTemporaryError(VATIDError):
|
||||
pass
|
||||
|
||||
|
||||
def normalize_vat_id(vat_id, country_code):
|
||||
"""
|
||||
Accepts a VAT ID and normaizes it, getting rid of spaces, periods, dashes
|
||||
etc and converting it to upper case.
|
||||
|
||||
Original function from https://github.com/wbond/vat_moss-python
|
||||
Copyright (c) 2015 Will Bond <will@wbond.net>
|
||||
MIT License
|
||||
"""
|
||||
if not vat_id:
|
||||
return None
|
||||
|
||||
if not isinstance(vat_id, str):
|
||||
raise TypeError('VAT ID is not a string')
|
||||
|
||||
if len(vat_id) < 3:
|
||||
raise ValueError('VAT ID must be at least three character long')
|
||||
|
||||
# Normalize the ID for simpler regexes
|
||||
vat_id = re.sub('\\s+', '', vat_id)
|
||||
vat_id = vat_id.replace('-', '')
|
||||
vat_id = vat_id.replace('.', '')
|
||||
vat_id = vat_id.upper()
|
||||
|
||||
# Clean the different shapes a number can take in Switzerland depending on purpse
|
||||
if country_code == "CH":
|
||||
vat_id = re.sub('[^A-Z0-9]', '', vat_id.replace('HR', '').replace('MWST', ''))
|
||||
|
||||
# Fix people using GR prefix for Greece
|
||||
if vat_id[0:2] == "GR" and country_code == "GR":
|
||||
vat_id = "EL" + vat_id[2:]
|
||||
|
||||
# Check if we already have a valid country prefix. If not, we try to figure out if we can
|
||||
# add one, since in some countries (e.g. Italy) it's very custom to enter it without the prefix
|
||||
if vat_id[:2] in VAT_ID_PATTERNS and re.match(VAT_ID_PATTERNS[vat_id[0:2]]['regex'], vat_id[2:]):
|
||||
# Prefix set and prefix matches pattern, nothing to do
|
||||
pass
|
||||
elif re.match(VAT_ID_PATTERNS[cc_to_vat_prefix(country_code)]['regex'], vat_id):
|
||||
# Prefix not set but adding it fixes pattern
|
||||
vat_id = cc_to_vat_prefix(country_code) + vat_id
|
||||
else:
|
||||
# We have no idea what this is
|
||||
pass
|
||||
|
||||
return vat_id
|
||||
|
||||
|
||||
def _validate_vat_id_NO(vat_id, country_code):
|
||||
# Inspired by vat_moss library
|
||||
if not vat_id.startswith("NO"):
|
||||
# prefix is not usually used in Norway, but expected by vat_moss library
|
||||
vat_id = "NO" + vat_id
|
||||
try:
|
||||
vat_id = vat_moss.id.normalize(vat_id)
|
||||
vat_id = normalize_vat_id(vat_id, country_code)
|
||||
except ValueError:
|
||||
raise VATIDFinalError(error_messages['invalid'])
|
||||
|
||||
@@ -104,7 +275,7 @@ def _validate_vat_id_NO(vat_id, country_code):
|
||||
def _validate_vat_id_EU(vat_id, country_code):
|
||||
# Inspired by vat_moss library
|
||||
try:
|
||||
vat_id = vat_moss.id.normalize(vat_id)
|
||||
vat_id = normalize_vat_id(vat_id, country_code)
|
||||
except ValueError:
|
||||
raise VATIDFinalError(error_messages['invalid'])
|
||||
|
||||
@@ -112,11 +283,10 @@ def _validate_vat_id_EU(vat_id, country_code):
|
||||
raise VATIDFinalError(error_messages['invalid'])
|
||||
|
||||
number = vat_id[2:]
|
||||
|
||||
if vat_id[:2] != cc_to_vat_prefix(country_code):
|
||||
raise VATIDFinalError(error_messages['country_mismatch'])
|
||||
|
||||
if not re.match(vat_moss.id.ID_PATTERNS[cc_to_vat_prefix(country_code)]['regex'], number):
|
||||
if not re.match(VAT_ID_PATTERNS[cc_to_vat_prefix(country_code)]['regex'], number):
|
||||
raise VATIDFinalError(error_messages['invalid'])
|
||||
|
||||
# We are relying on the country code of the normalized VAT-ID and not the user/InvoiceAddress-provided
|
||||
@@ -175,9 +345,12 @@ def _validate_vat_id_EU(vat_id, country_code):
|
||||
|
||||
def _validate_vat_id_CH(vat_id, country_code):
|
||||
if vat_id[:3] != 'CHE':
|
||||
raise VATIDFinalError(_('Your VAT ID does not match the selected country.'))
|
||||
raise VATIDFinalError(error_messages['country_mismatch'])
|
||||
|
||||
vat_id = re.sub('[^A-Z0-9]', '', vat_id.replace('HR', '').replace('MWST', ''))
|
||||
try:
|
||||
vat_id = normalize_vat_id(vat_id, country_code)
|
||||
except ValueError:
|
||||
raise VATIDFinalError(error_messages['invalid'])
|
||||
try:
|
||||
transport = Transport(
|
||||
cache=SqliteCache(os.path.join(settings.CACHE_DIR, "validate_vat_id_ch_zeep_cache.db")),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -629,13 +629,40 @@ DEFAULTS = {
|
||||
'form_kwargs': dict(
|
||||
label=_("Ask for VAT ID"),
|
||||
help_text=format_lazy(
|
||||
_("Only works if an invoice address is asked for. VAT ID is never required and only requested from "
|
||||
"business customers in the following countries: {countries}"),
|
||||
_("Only works if an invoice address is asked for. VAT ID is only requested from business customers "
|
||||
"in the following countries: {countries}."),
|
||||
countries=lazy(lambda *args: ', '.join(sorted(gettext(Country(cc).name) for cc in VAT_ID_COUNTRIES)), str)()
|
||||
),
|
||||
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_invoice_address_asked'}),
|
||||
)
|
||||
},
|
||||
'invoice_address_vatid_required_countries': {
|
||||
'default': ['IT', 'GR'],
|
||||
'type': list,
|
||||
'form_class': forms.MultipleChoiceField,
|
||||
'serializer_class': serializers.MultipleChoiceField,
|
||||
'serializer_kwargs': dict(
|
||||
choices=lazy(
|
||||
lambda *args: sorted([(cc, gettext(Country(cc).name)) for cc in VAT_ID_COUNTRIES], key=lambda c: c[1]),
|
||||
list
|
||||
)(),
|
||||
),
|
||||
'form_kwargs': dict(
|
||||
label=_("Require VAT ID in"),
|
||||
choices=lazy(
|
||||
lambda *args: sorted([(cc, gettext(Country(cc).name)) for cc in VAT_ID_COUNTRIES], key=lambda c: c[1]),
|
||||
list
|
||||
)(),
|
||||
help_text=format_lazy(
|
||||
_("VAT ID is optional by default, because not all businesses are assigned a VAT ID in all countries. "
|
||||
"VAT ID will be required for all business addresses in the selected countries."),
|
||||
),
|
||||
widget=forms.CheckboxSelectMultiple(attrs={
|
||||
"class": "scrolling-multiple-choice",
|
||||
'data-display-dependency': '#id_invoice_address_vatid'
|
||||
}),
|
||||
)
|
||||
},
|
||||
'invoice_address_explanation_text': {
|
||||
'default': '',
|
||||
'type': LazyI18nString,
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
import pycountry
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import pgettext
|
||||
from django.utils.translation import gettext, pgettext, pgettext_lazy
|
||||
from django_countries.fields import Country
|
||||
from django_scopes import scope
|
||||
|
||||
@@ -36,6 +36,22 @@ from pretix.base.settings import (
|
||||
COUNTRIES_WITH_STATE_IN_ADDRESS, COUNTRY_STATE_LABEL,
|
||||
)
|
||||
|
||||
VAT_ID_LABELS = {
|
||||
# VAT ID is a EU concept and Switzerland has a distinct, but differently-named concept
|
||||
"CH": pgettext_lazy("tax_id_swiss", "UID"), # Translators: Only translate to French (IDE) and Italien (IDI), otherwise keep the same
|
||||
|
||||
# Awareness around VAT IDs differes by EU country. For example, in Germany the VAT ID is assigned
|
||||
# separately to each company and only used in cross-country transactions. Therefore, it makes sense
|
||||
# to call it just "VAT ID" on the form, and people will either know their VAT ID or they don't.
|
||||
# In contrast, in Italy the EU-compatible VAT ID is not separately assigned, but is just "IT" + the national tax
|
||||
# number (Partita IVA) and also used on domestic transactions. So someone who never purchased something international
|
||||
# for their company, might still know the value, if we call it the right way and not just "VAT ID".
|
||||
"IT": pgettext_lazy("tax_id_italy", "VAT ID / P.IVA"), # Translators: Translate to only "P.IVA" in Italian, keep second part as-is in other languages
|
||||
"GR": pgettext_lazy("tax_id_greece", "VAT ID / TIN"), # Translators: Translate to only "ΑΦΜ" in Greek
|
||||
"ES": pgettext_lazy("tax_id_spain", "VAT ID / NIF"), # Translators: Translate to only "NIF" in Spanish
|
||||
"PT": pgettext_lazy("tax_id_portugal", "VAT ID / NIF"), # Translators: Translate to only "NIF" in Portuguese
|
||||
}
|
||||
|
||||
|
||||
def _info(cc):
|
||||
info = {
|
||||
@@ -47,7 +63,12 @@ def _info(cc):
|
||||
'required': 'if_any' if cc in COUNTRIES_WITH_STATE_IN_ADDRESS else False,
|
||||
'label': COUNTRY_STATE_LABEL.get(cc, pgettext('address', 'State')),
|
||||
},
|
||||
'vat_id': {'visible': cc in VAT_ID_COUNTRIES, 'required': False},
|
||||
'vat_id': {
|
||||
'visible': cc in VAT_ID_COUNTRIES,
|
||||
'required': False,
|
||||
'label': VAT_ID_LABELS.get(cc, gettext("VAT ID")),
|
||||
'helptext_visible': True,
|
||||
},
|
||||
}
|
||||
if cc not in COUNTRIES_WITH_STATE_IN_ADDRESS:
|
||||
return {'data': [], **info}
|
||||
@@ -124,4 +145,10 @@ def address_form(request):
|
||||
"required": transmission_type.identifier == selected_transmission_type and k in required
|
||||
}
|
||||
|
||||
if is_business and country in event.settings.invoice_address_vatid_required_countries and info["vat_id"]["visible"]:
|
||||
info["vat_id"]["required"] = True
|
||||
if info["vat_id"]["required"]:
|
||||
# The help text explains that it is optional, so we want to hide that if it is required
|
||||
info["vat_id"]["helptext_visible"] = False
|
||||
|
||||
return JsonResponse(info)
|
||||
|
||||
@@ -927,6 +927,7 @@ class InvoiceSettingsForm(EventSettingsValidationMixin, SettingsForm):
|
||||
'invoice_address_asked',
|
||||
'invoice_address_required',
|
||||
'invoice_address_vatid',
|
||||
'invoice_address_vatid_required_countries',
|
||||
'invoice_address_company_required',
|
||||
'invoice_address_beneficiary',
|
||||
'invoice_address_custom_field',
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
{% bootstrap_field form.invoice_name_required layout="control" %}
|
||||
{% bootstrap_field form.invoice_address_company_required layout="control" %}
|
||||
{% bootstrap_field form.invoice_address_vatid layout="control" %}
|
||||
{% bootstrap_field form.invoice_address_vatid_required_countries layout="control" %}
|
||||
{% bootstrap_field form.invoice_address_beneficiary layout="control" %}
|
||||
{% bootstrap_field form.invoice_address_not_asked_free layout="control" %}
|
||||
{% bootstrap_field form.invoice_address_custom_field layout="control" %}
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
{% if v.budget|default_if_none:"NONE" != "NONE" %}
|
||||
<br>
|
||||
<small class="text-muted">
|
||||
{{ v.budget_used_orders|money:request.event.currency }} / {{ v.budget|money:request.event.currency }}
|
||||
{{ v.budget_used|money:request.event.currency }} / {{ v.budget|money:request.event.currency }}
|
||||
</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -87,7 +87,7 @@ class VoucherList(PaginationMixin, EventPermissionRequiredMixin, ListView):
|
||||
|
||||
@scopes_disabled() # we have an event check here, and we can save some performance on subqueries
|
||||
def get_queryset(self):
|
||||
qs = Voucher.annotate_budget_used_orders(self.request.event.vouchers.exclude(
|
||||
qs = Voucher.annotate_budget_used(self.request.event.vouchers.exclude(
|
||||
Exists(WaitingListEntry.objects.filter(voucher_id=OuterRef('pk')))
|
||||
).select_related(
|
||||
'item', 'variation', 'seat'
|
||||
|
||||
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,8 +8,8 @@ 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-11-18 17:00+0000\n"
|
||||
"Last-Translator: Hijiri Umemoto <hijiri@umemoto.org>\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"
|
||||
"Language: ko\n"
|
||||
@@ -290,33 +290,27 @@ msgstr "묶음 상품은 그 자체로 또 다른 묶음을 포함 할 수 없
|
||||
|
||||
#: pretix/api/serializers/item.py:235
|
||||
msgid "The program start must not be empty."
|
||||
msgstr ""
|
||||
msgstr "프로그램 시작일정은 비어 있어서는 안 됩니다."
|
||||
|
||||
#: pretix/api/serializers/item.py:239
|
||||
msgid "The program end must not be empty."
|
||||
msgstr ""
|
||||
msgstr "프로그램 종료일정은 비어 있어서는 안 됩니다."
|
||||
|
||||
#: pretix/api/serializers/item.py:242 pretix/base/models/items.py:2321
|
||||
#, fuzzy
|
||||
#| msgid "The maximum date must not be before the minimum value."
|
||||
msgid "The program end must not be before the program start."
|
||||
msgstr "종료일(최대 날짜)은 시작일(최소값)보다 앞서면 안됩니다."
|
||||
msgstr "종료일은 시작일보다 앞서면 안됩니다."
|
||||
|
||||
#: pretix/api/serializers/item.py:247 pretix/base/models/items.py:2315
|
||||
#, fuzzy
|
||||
#| msgid "You can not select a subevent if your event is not an event series."
|
||||
msgid "You cannot use program times on an event series."
|
||||
msgstr ""
|
||||
"당신의이벤트가 이벤트 시리즈가 아닌 경우 하위 이벤트를 선택할 수 없습니다."
|
||||
msgstr "이벤트 시리즈에 있는 시간은 사용하실 수 없습니다."
|
||||
|
||||
#: pretix/api/serializers/item.py:337
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Updating add-ons, bundles, program times or variations via PATCH/PUT is not "
|
||||
"supported. Please use the dedicated nested endpoint."
|
||||
msgstr ""
|
||||
"추가 기능, 묶음 상품들, 또는 변형은 PATCH/PUT를 통해 업데이트 할 수 없습니"
|
||||
"다. 전용 중첩은 마지막 지점에서 사용하세요"
|
||||
"추가 기능, 묶음 상품들, 또는 변형은 PATCH/PUT를 통해 업데이트 할 수 없습니다."
|
||||
" 전용 중첩은 마지막 지점에서 사용하세요."
|
||||
|
||||
#: pretix/api/serializers/item.py:345
|
||||
msgid "Only admission products can currently be personalized."
|
||||
@@ -573,22 +567,15 @@ msgid "Event series date deleted"
|
||||
msgstr "이벤트 시리즈 날짜 삭제"
|
||||
|
||||
#: pretix/api/webhooks.py:374
|
||||
#, fuzzy
|
||||
#| msgid "Product name"
|
||||
msgid "Product changed"
|
||||
msgstr "제품명"
|
||||
msgstr "제품 변경됨"
|
||||
|
||||
#: pretix/api/webhooks.py:375
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Product changed (including product added or deleted and including changes "
|
||||
#| "to nested objects like variations or bundles)"
|
||||
msgid ""
|
||||
"This includes product added or deleted and changes to nested objects like "
|
||||
"variations or bundles."
|
||||
msgstr ""
|
||||
"제품 변경(제품 추가 또는 삭제, 변형 또는 번들과 같은 중첩된 객체에 대한 변경 "
|
||||
"포함)"
|
||||
msgstr "여기에는 추가되거나 삭제되거나 변경된 중첩오브젝트나 번들과 같은 사항이 포함"
|
||||
"됩니다."
|
||||
|
||||
#: pretix/api/webhooks.py:380
|
||||
msgid "Shop taken live"
|
||||
@@ -623,16 +610,12 @@ msgid "Waiting list entry received voucher"
|
||||
msgstr "대기자 명단 항목이 바우처를 받았습니다"
|
||||
|
||||
#: pretix/api/webhooks.py:412
|
||||
#, fuzzy
|
||||
#| msgid "Voucher code"
|
||||
msgid "Voucher added"
|
||||
msgstr "바우처 코드"
|
||||
msgstr "바우처 추가됨"
|
||||
|
||||
#: pretix/api/webhooks.py:416
|
||||
#, fuzzy
|
||||
#| msgid "Voucher assigned"
|
||||
msgid "Voucher changed"
|
||||
msgstr "바우처 할당"
|
||||
msgstr "바우처 변경됨"
|
||||
|
||||
#: pretix/api/webhooks.py:417
|
||||
msgid ""
|
||||
@@ -643,10 +626,8 @@ msgstr ""
|
||||
"하지 않습니다."
|
||||
|
||||
#: pretix/api/webhooks.py:421
|
||||
#, fuzzy
|
||||
#| msgid "Voucher redeemed"
|
||||
msgid "Voucher deleted"
|
||||
msgstr "바우처 상환"
|
||||
msgstr "바우처 제거됨"
|
||||
|
||||
#: pretix/api/webhooks.py:425
|
||||
msgid "Customer account created"
|
||||
@@ -671,7 +652,7 @@ msgstr "고객 계정 익명화되었습니다"
|
||||
#: pretix/plugins/banktransfer/payment.py:513
|
||||
#: pretix/presale/forms/customer.py:152
|
||||
msgid "This field is required."
|
||||
msgstr "이 필드는 필수입니다"
|
||||
msgstr "이 필드는 필수입니다."
|
||||
|
||||
#: pretix/base/addressvalidation.py:213
|
||||
msgid "Enter a postal code in the format XXX."
|
||||
@@ -721,7 +702,7 @@ msgstr "비밀번호"
|
||||
|
||||
#: pretix/base/auth.py:176 pretix/base/auth.py:183
|
||||
msgid "Your password must contain both numeric and alphabetic characters."
|
||||
msgstr "비밀번호는 숫자와 알파벳 문자가 모두 포함되어야 합니다"
|
||||
msgstr "비밀번호는 숫자와 알파벳 문자가 모두 포함되어야 합니다."
|
||||
|
||||
#: pretix/base/auth.py:202 pretix/base/auth.py:212
|
||||
#, python-format
|
||||
@@ -815,28 +796,21 @@ msgstr ""
|
||||
"소를 확인해 주십시요."
|
||||
|
||||
#: pretix/base/datasync/datasync.py:263
|
||||
#, 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 ""
|
||||
"필드 \"{field_name}\"은 {available_inputs}에 유효하지 않습니다. "
|
||||
"{provider_name} 설정을 확인해 주세요."
|
||||
msgstr "필드 \"{field_name}\"은 존재하지 않습니다. 당신의 {provider_name} 설정을 확인"
|
||||
"해 주세요."
|
||||
|
||||
#: pretix/base/datasync/datasync.py:270
|
||||
#, 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 ""
|
||||
"필드 \"{field_name}\"은 {available_inputs}에 유효하지 않습니다. "
|
||||
"{provider_name} 설정을 확인해 주세요."
|
||||
"필드 \"{field_name}\"는 {required_input}을 필요로 하지만, {available_inputs}"
|
||||
"만 받았습니다. 당신의 {provider_name} 설정을 확인해 주세요."
|
||||
|
||||
#: pretix/base/datasync/datasync.py:281
|
||||
#, python-brace-format
|
||||
@@ -848,16 +822,12 @@ msgstr ""
|
||||
"지 않았습니다"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:128
|
||||
#, fuzzy
|
||||
#| msgid "Order positions"
|
||||
msgid "Order position details"
|
||||
msgstr "주문 위치"
|
||||
msgstr "주문 위치 세부 정보"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:129
|
||||
#, fuzzy
|
||||
#| msgid "Attendee email"
|
||||
msgid "Attendee details"
|
||||
msgstr "참석자 이메일"
|
||||
msgstr "참석자 정보들"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:130 pretix/base/exporters/answers.py:66
|
||||
#: pretix/base/models/items.py:1766 pretix/control/navigation.py:172
|
||||
@@ -867,10 +837,8 @@ msgid "Questions"
|
||||
msgstr "질문들"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:131
|
||||
#, fuzzy
|
||||
#| msgid "Product data"
|
||||
msgid "Product details"
|
||||
msgstr "상품 데이터"
|
||||
msgstr "상품 정보"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:132
|
||||
#: pretix/control/templates/pretixcontrol/event/settings.html:279
|
||||
@@ -895,17 +863,13 @@ msgid "Invoice address"
|
||||
msgstr "송장 주소"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:134
|
||||
#, fuzzy
|
||||
#| msgid "Meta information"
|
||||
msgid "Event information"
|
||||
msgstr "메타 정보(데이타에 대한 정보)"
|
||||
msgstr "이벤트 정보"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:135
|
||||
#, fuzzy
|
||||
#| msgid "Send recovery information"
|
||||
msgctxt "subevent"
|
||||
msgid "Event or date information"
|
||||
msgstr "복구 정보를 전송하다"
|
||||
msgstr "이벤트 또는 날짜 정보"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:175
|
||||
#: pretix/base/exporters/orderlist.py:605
|
||||
@@ -930,10 +894,8 @@ msgstr "참석자 이름"
|
||||
#: pretix/base/datasync/sourcefields.py:187
|
||||
#: pretix/base/datasync/sourcefields.py:604
|
||||
#: pretix/base/datasync/sourcefields.py:628
|
||||
#, fuzzy
|
||||
#| msgid "Attendee name"
|
||||
msgid "Attendee"
|
||||
msgstr "참석자 이름"
|
||||
msgstr "참석자"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:207
|
||||
#: pretix/base/exporters/orderlist.py:612 pretix/base/forms/questions.py:687
|
||||
@@ -947,10 +909,8 @@ msgid "Attendee email"
|
||||
msgstr "참석자 이메일"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:219
|
||||
#, fuzzy
|
||||
#| msgid "Attendee email"
|
||||
msgid "Attendee or order email"
|
||||
msgstr "참석자 이메일"
|
||||
msgstr "참석자 또는 구매 이메일"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:232
|
||||
#: pretix/base/exporters/orderlist.py:613 pretix/base/pdf.py:189
|
||||
@@ -963,28 +923,20 @@ msgid "Attendee company"
|
||||
msgstr "참석자 회사"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:241
|
||||
#, fuzzy
|
||||
#| msgid "Attendee address"
|
||||
msgid "Attendee address street"
|
||||
msgstr "참석자 주소"
|
||||
msgstr "참석자 주소 도로명"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:250
|
||||
#, fuzzy
|
||||
#| msgid "Attendee ZIP code"
|
||||
msgid "Attendee address ZIP code"
|
||||
msgstr "참석자 우편번호"
|
||||
msgstr "참석자 주소 우편번호"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:259
|
||||
#, fuzzy
|
||||
#| msgid "Attendee address"
|
||||
msgid "Attendee address city"
|
||||
msgstr "참석자 주소"
|
||||
msgstr "참석자 주소 도시"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:268
|
||||
#, fuzzy
|
||||
#| msgid "Attendee address"
|
||||
msgid "Attendee address country"
|
||||
msgstr "참석자 주소"
|
||||
msgstr "참석자 주소 국가"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:279
|
||||
#: pretix/base/exporters/orderlist.py:653 pretix/base/pdf.py:347
|
||||
@@ -1020,16 +972,12 @@ msgid "Invoice address country"
|
||||
msgstr "송장 주소 국가"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:353
|
||||
#, fuzzy
|
||||
#| msgid "Order total"
|
||||
msgid "Order email"
|
||||
msgstr "주문 합계"
|
||||
msgstr "주문 이메일"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:362
|
||||
#, fuzzy
|
||||
#| msgid "Order time"
|
||||
msgid "Order email domain"
|
||||
msgstr "주문 시간"
|
||||
msgstr "주문 이메일 도메인"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:371
|
||||
#: pretix/base/exporters/invoices.py:203 pretix/base/exporters/invoices.py:332
|
||||
@@ -1061,10 +1009,8 @@ msgid "Order code"
|
||||
msgstr "주문 코드"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:380
|
||||
#, fuzzy
|
||||
#| msgid "Event end date and time"
|
||||
msgid "Event and order code"
|
||||
msgstr "이벤트 종료 날짜 및 시간"
|
||||
msgstr "이벤트와 주문 번호"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:389
|
||||
#: pretix/base/exporters/orderlist.py:263 pretix/base/notifications.py:201
|
||||
@@ -1076,10 +1022,8 @@ msgid "Order total"
|
||||
msgstr "주문 합계"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:398
|
||||
#, fuzzy
|
||||
#| msgid "Product name and variation"
|
||||
msgid "Product and variation name"
|
||||
msgstr "제품명 및 변형"
|
||||
msgstr "제품명 및 변형 이름"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:410 pretix/base/exporters/items.py:57
|
||||
#: pretix/base/exporters/orderlist.py:598
|
||||
@@ -1089,16 +1033,12 @@ msgid "Product ID"
|
||||
msgstr "상품 식별 아이디"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:419
|
||||
#, fuzzy
|
||||
#| msgid "Count add-on products"
|
||||
msgid "Product is admission product"
|
||||
msgstr "추가된 제품을 포함합니다"
|
||||
msgstr "상품은 입장 상품입니다"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:428
|
||||
#, fuzzy
|
||||
#| msgid "Short form"
|
||||
msgid "Event short form"
|
||||
msgstr "짧은 형식"
|
||||
msgstr "이벤트 짧은 형식"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:437 pretix/base/exporters/events.py:57
|
||||
#: pretix/base/exporters/orderlist.py:263
|
||||
@@ -1141,10 +1081,8 @@ msgid "Order code and position number"
|
||||
msgstr "주문 코드 및 위치 번호"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:482
|
||||
#, fuzzy
|
||||
#| msgid "Ticket code"
|
||||
msgid "Ticket price"
|
||||
msgstr "티켓 코드"
|
||||
msgstr "티켓 가격"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:491 pretix/base/notifications.py:204
|
||||
#: pretix/control/forms/filter.py:216 pretix/control/forms/modelimport.py:90
|
||||
@@ -1152,22 +1090,16 @@ msgid "Order status"
|
||||
msgstr "주문 상태"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:500
|
||||
#, fuzzy
|
||||
#| msgid "Device status"
|
||||
msgid "Ticket status"
|
||||
msgstr "기기 상태"
|
||||
msgstr "티켓 상태"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:509
|
||||
#, fuzzy
|
||||
#| msgid "Purchase date and time"
|
||||
msgid "Order date and time"
|
||||
msgstr "구매 날짜 및 시간"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:518
|
||||
#, fuzzy
|
||||
#| msgid "Printing date and time"
|
||||
msgid "Payment date and time"
|
||||
msgstr "인쇄 날짜 및 시간"
|
||||
msgstr "결제 날짜 및 시간"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:527
|
||||
#: pretix/base/exporters/orderlist.py:272
|
||||
@@ -1178,35 +1110,27 @@ msgid "Order locale"
|
||||
msgstr "주문 지역 설정"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:536
|
||||
#, fuzzy
|
||||
#| msgid "Order position"
|
||||
msgid "Order position ID"
|
||||
msgstr "주문 위치"
|
||||
msgstr "주문 위치 ID"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:545
|
||||
#: pretix/base/exporters/orderlist.py:292
|
||||
#, fuzzy
|
||||
#| msgid "Order time"
|
||||
msgid "Order link"
|
||||
msgstr "주문 시간"
|
||||
msgstr "주문 링크"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:560
|
||||
#, fuzzy
|
||||
#| msgid "Ticket design"
|
||||
msgid "Ticket link"
|
||||
msgstr "티켓 디자인"
|
||||
msgstr "티켓 링크"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:578
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Check-in list {val}"
|
||||
#, python-brace-format
|
||||
msgid "Check-in datetime on list {}"
|
||||
msgstr "체크인 목록 {val}"
|
||||
msgstr "{} 리스트에 있는 체크인 날짜와 시간"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:590
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Question {val}"
|
||||
#, python-brace-format
|
||||
msgid "Question: {name}"
|
||||
msgstr "질문 {val}"
|
||||
msgstr "질문: {name}"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py:604
|
||||
#: pretix/base/datasync/sourcefields.py:614 pretix/base/settings.py:3642
|
||||
@@ -2422,9 +2346,9 @@ msgid "Fees"
|
||||
msgstr "수수료"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:277
|
||||
#, fuzzy, python-brace-format
|
||||
#, python-brace-format
|
||||
msgid "Gross at {rate} % tax"
|
||||
msgstr "세율{%}의 세금으로 총합"
|
||||
msgstr "세율 {rate}%의 세금으로 총합"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:278
|
||||
#, python-brace-format
|
||||
@@ -2472,9 +2396,9 @@ msgid "External customer ID"
|
||||
msgstr "외부고객 아이디"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:296
|
||||
#, fuzzy, python-brace-format
|
||||
#, python-brace-format
|
||||
msgid "Paid by {method}"
|
||||
msgstr "{방법}에 의해 결제됨"
|
||||
msgstr "{method}에 의해 결제됨"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:458
|
||||
#: pretix/base/exporters/orderlist.py:914
|
||||
@@ -2677,10 +2601,8 @@ msgid "Check-in lists"
|
||||
msgstr "체크인 목록"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:672
|
||||
#, fuzzy
|
||||
#| msgid "Additional footer link"
|
||||
msgid "Position order link"
|
||||
msgstr "추가 하단 링크"
|
||||
msgstr "주문 링크 위치"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:841
|
||||
msgid "Order transaction data"
|
||||
@@ -3320,15 +3242,13 @@ msgid "Repeat password"
|
||||
msgstr "반복 비밀번호"
|
||||
|
||||
#: pretix/base/forms/auth.py:220 pretix/base/forms/user.py:99
|
||||
#, fuzzy
|
||||
#| msgid "Email address"
|
||||
msgid "Your email address"
|
||||
msgstr "이메일 주소"
|
||||
msgstr "당신의 이메일 주소"
|
||||
|
||||
#: pretix/base/forms/auth.py:327 pretix/control/forms/orders.py:1041
|
||||
#: pretix/control/templates/pretixcontrol/shredder/download.html:53
|
||||
msgid "Confirmation code"
|
||||
msgstr ""
|
||||
msgstr "확인 코드"
|
||||
|
||||
#: pretix/base/forms/questions.py:137 pretix/base/forms/questions.py:264
|
||||
msgctxt "name_salutation"
|
||||
@@ -3411,14 +3331,12 @@ msgstr ""
|
||||
"야 할 수도 있습니다."
|
||||
|
||||
#: pretix/base/forms/questions.py:1181
|
||||
#, fuzzy
|
||||
#| msgid "Cancellation requested"
|
||||
msgid "No invoice requested"
|
||||
msgstr "취소 요청"
|
||||
msgstr "청구서 요청되지 않음"
|
||||
|
||||
#: pretix/base/forms/questions.py:1183
|
||||
msgid "Invoice transmission method"
|
||||
msgstr ""
|
||||
msgstr "청구서 전송 방식"
|
||||
|
||||
#: pretix/base/forms/questions.py:1329
|
||||
msgid "You need to provide a company name."
|
||||
@@ -3432,21 +3350,20 @@ msgstr "이름을 입력해야 합니다."
|
||||
msgid ""
|
||||
"If you enter an invoice address, you also need to select an invoice "
|
||||
"transmission method."
|
||||
msgstr ""
|
||||
msgstr "당신이 청구서 주소를 입력하신다면, 청구서 수신 방법도 선택하셔야 합니다."
|
||||
|
||||
#: pretix/base/forms/questions.py:1385
|
||||
#, fuzzy
|
||||
#| msgid "The selected media type is not enabled in your organizer settings."
|
||||
msgid ""
|
||||
"The selected transmission type is not available in your country or for your "
|
||||
"type of address."
|
||||
msgstr "선택한 미디어 유형이 정리함 설정에서 활성화되지 않았습니다."
|
||||
msgstr "선택한 전송 유형은 당신의 국가 또는 지역에서 이용하실 수 없습니다."
|
||||
|
||||
#: pretix/base/forms/questions.py:1394
|
||||
msgid ""
|
||||
"The selected type of invoice transmission requires a field that is currently "
|
||||
"not available, please reach out to the organizer."
|
||||
msgstr ""
|
||||
msgstr "선택하신 청구서 전송 유형은 현재 사용할 수 없는 필드의 입력을 필요로 하니, 주"
|
||||
"최자에게 문의 해 주세요."
|
||||
|
||||
#: pretix/base/forms/questions.py:1398
|
||||
msgid "This field is required for the selected type of invoice transmission."
|
||||
@@ -3466,10 +3383,8 @@ msgstr ""
|
||||
"대가 대신 사용됩니다."
|
||||
|
||||
#: pretix/base/forms/user.py:77
|
||||
#, fuzzy
|
||||
#| msgid "Attendee email address"
|
||||
msgid "Change email address"
|
||||
msgstr "참석자 이메일 주소"
|
||||
msgstr "이메일 주소 변경"
|
||||
|
||||
#: pretix/base/forms/user.py:83
|
||||
msgid "Device name"
|
||||
@@ -3518,16 +3433,12 @@ msgstr ""
|
||||
"이 이메일 주소와 관련된 계정이 이미 있습니다. 다른 계정을 선택해 주세요."
|
||||
|
||||
#: pretix/base/forms/user.py:179
|
||||
#, fuzzy
|
||||
#| msgid "Email address"
|
||||
msgid "Old email address"
|
||||
msgstr "이메일 주소"
|
||||
msgstr "이전 이메일 주소"
|
||||
|
||||
#: pretix/base/forms/user.py:180
|
||||
#, fuzzy
|
||||
#| msgid "Email address"
|
||||
msgid "New email address"
|
||||
msgstr "이메일 주소"
|
||||
msgstr "새 이메일 주소"
|
||||
|
||||
#: pretix/base/forms/validators.py:51
|
||||
msgid ""
|
||||
@@ -3572,23 +3483,22 @@ msgstr "개별 고객"
|
||||
|
||||
#: pretix/base/invoicing/email.py:50
|
||||
msgid "Email invoice directly to accounting department"
|
||||
msgstr ""
|
||||
msgstr "청구서를 회계 부서로 이메일로 바로 보내기"
|
||||
|
||||
#: pretix/base/invoicing/email.py:51
|
||||
msgid ""
|
||||
"If not selected, the invoice will be sent to you using the email address "
|
||||
"listed above."
|
||||
msgstr ""
|
||||
msgstr "선택되지 않은 경우, 청구서는 위의 이메일 주소를 통해 당신에게 보내질 것 입니"
|
||||
"다."
|
||||
|
||||
#: pretix/base/invoicing/email.py:55
|
||||
#, fuzzy
|
||||
#| msgid "Email address verified"
|
||||
msgid "Email address for invoice"
|
||||
msgstr "이메일 주소 확인"
|
||||
msgstr "청구서용 이메일 주소"
|
||||
|
||||
#: pretix/base/invoicing/email.py:91
|
||||
msgid "PDF via email"
|
||||
msgstr ""
|
||||
msgstr "이메일로 PDF"
|
||||
|
||||
#: pretix/base/invoicing/national.py:37
|
||||
msgctxt "italian_invoice"
|
||||
@@ -3613,11 +3523,9 @@ msgid "Address for certified electronic mail"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/invoicing/national.py:57
|
||||
#, fuzzy
|
||||
#| msgid "Recipient"
|
||||
msgctxt "italian_invoice"
|
||||
msgid "Recipient code"
|
||||
msgstr "영수증"
|
||||
msgstr "영수증 코드"
|
||||
|
||||
#: pretix/base/invoicing/national.py:81
|
||||
msgctxt "italian_invoice"
|
||||
@@ -3697,7 +3605,6 @@ msgid ""
|
||||
"until {to_date}"
|
||||
msgstr ""
|
||||
"{from_date}\n"
|
||||
"\n"
|
||||
"{to_date}까지"
|
||||
|
||||
#: pretix/base/invoicing/pdf.py:609 pretix/base/services/mail.py:512
|
||||
@@ -3777,10 +3684,10 @@ msgid "Single price: {net_price} net / {gross_price} gross"
|
||||
msgstr "단일 가격: {net_price} 순 / {gross_price} 총합"
|
||||
|
||||
#: pretix/base/invoicing/pdf.py:901
|
||||
#, fuzzy, python-brace-format
|
||||
#, python-brace-format
|
||||
msgctxt "invoice"
|
||||
msgid "Single price: {price}"
|
||||
msgstr "단일 가격: {가격}"
|
||||
msgstr "단일 가격: {price}"
|
||||
|
||||
#: pretix/base/invoicing/pdf.py:944 pretix/base/invoicing/pdf.py:949
|
||||
msgctxt "invoice"
|
||||
@@ -3808,12 +3715,10 @@ msgid "Remaining amount"
|
||||
msgstr "잔액"
|
||||
|
||||
#: pretix/base/invoicing/pdf.py:1009
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgctxt "invoice"
|
||||
#| msgid "Event date: {date_range}"
|
||||
#, python-brace-format
|
||||
msgctxt "invoice"
|
||||
msgid "Invoice period: {daterange}"
|
||||
msgstr "이벤트 날짜: {date_range}"
|
||||
msgstr "청구서 기간: {daterange}"
|
||||
|
||||
#: pretix/base/invoicing/pdf.py:1040
|
||||
msgctxt "invoice"
|
||||
@@ -3836,13 +3741,12 @@ msgid "Included taxes"
|
||||
msgstr "세금 포함"
|
||||
|
||||
#: pretix/base/invoicing/pdf.py:1100
|
||||
#, fuzzy, python-brace-format
|
||||
#, python-brace-format
|
||||
msgctxt "invoice"
|
||||
msgid ""
|
||||
"Using the conversion rate of 1:{rate} as published by the {authority} on "
|
||||
"{date}, this corresponds to:"
|
||||
msgstr ""
|
||||
"{날짜}에 {당국}에서 발표한 1:{세율}의 변환율을 사용하면 다음과 같습니다:"
|
||||
msgstr "{date}에 {authority}에서 발표한 1:{rate}의 변환율을 사용하면 다음과 같습니다:"
|
||||
|
||||
#: pretix/base/invoicing/pdf.py:1115
|
||||
#, fuzzy, python-brace-format
|
||||
@@ -3850,9 +3754,8 @@ msgctxt "invoice"
|
||||
msgid ""
|
||||
"Using the conversion rate of 1:{rate} as published by the {authority} on "
|
||||
"{date}, the invoice total corresponds to {total}."
|
||||
msgstr ""
|
||||
"{날짜}에 {당국}에서 게시한 1:{세율}의 변환율을 사용하면 송장 총액이 {총합}에 "
|
||||
"해당합니다."
|
||||
msgstr "{date}에 {authority}에서 게시한 1:{rate}의 변환율을 사용하면 송장 총액이 "
|
||||
"{total}에 해당합니다."
|
||||
|
||||
#: pretix/base/invoicing/pdf.py:1129
|
||||
msgid "Default invoice renderer (European-style letter)"
|
||||
|
||||
@@ -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-09-10 05:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-04 18:00+0000\n"
|
||||
"Last-Translator: Renne Rocha <renne@rocha.dev.br>\n"
|
||||
"Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/"
|
||||
"pretix/pretix/pt_BR/>\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.14.3\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -3286,10 +3286,8 @@ msgid "Repeat password"
|
||||
msgstr "Repita a senha"
|
||||
|
||||
#: pretix/base/forms/auth.py:220 pretix/base/forms/user.py:99
|
||||
#, fuzzy
|
||||
#| msgid "Email address"
|
||||
msgid "Your email address"
|
||||
msgstr "Endereço de email"
|
||||
msgstr "Seu endereço de e-mail"
|
||||
|
||||
#: pretix/base/forms/auth.py:327 pretix/control/forms/orders.py:1041
|
||||
#: pretix/control/templates/pretixcontrol/shredder/download.html:53
|
||||
@@ -3433,10 +3431,8 @@ msgstr ""
|
||||
"as exibições de eventos, o fuso horário do evento é usado."
|
||||
|
||||
#: pretix/base/forms/user.py:77
|
||||
#, fuzzy
|
||||
#| msgid "Attendee email address"
|
||||
msgid "Change email address"
|
||||
msgstr "Email do participante"
|
||||
msgstr "Alterar endereço de e-mail"
|
||||
|
||||
#: pretix/base/forms/user.py:83
|
||||
msgid "Device name"
|
||||
@@ -3486,16 +3482,12 @@ msgstr ""
|
||||
"um diferente."
|
||||
|
||||
#: pretix/base/forms/user.py:179
|
||||
#, fuzzy
|
||||
#| msgid "Email address"
|
||||
msgid "Old email address"
|
||||
msgstr "Endereço de email"
|
||||
msgstr "Endereço de e-mail antigo"
|
||||
|
||||
#: pretix/base/forms/user.py:180
|
||||
#, fuzzy
|
||||
#| msgid "Email address"
|
||||
msgid "New email address"
|
||||
msgstr "Endereço de email"
|
||||
msgstr "Endereço de e-mail novo"
|
||||
|
||||
#: pretix/base/forms/validators.py:51
|
||||
msgid ""
|
||||
@@ -3540,22 +3532,17 @@ msgid "Individual customer"
|
||||
msgstr "Cliente pessoa física"
|
||||
|
||||
#: pretix/base/invoicing/email.py:50
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "To send the invoice directly to your accounting department, please enter "
|
||||
#| "their email address:"
|
||||
msgid "Email invoice directly to accounting department"
|
||||
msgstr ""
|
||||
"Para enviar a fatura diretamente ao seu departamento de contabilidade, "
|
||||
"insira o endereço de e-mail:"
|
||||
"Enviar e-mail com a fatura diretamente para departamento de contabilidade"
|
||||
|
||||
#: pretix/base/invoicing/email.py:51
|
||||
#, fuzzy
|
||||
#| msgid "The invoice was sent to the designated email address."
|
||||
msgid ""
|
||||
"If not selected, the invoice will be sent to you using the email address "
|
||||
"listed above."
|
||||
msgstr "A fatura foi enviada para o endereço de e-mail designado."
|
||||
msgstr ""
|
||||
"Se não selecionado, a fatura será enviada para o endereço de e-mail listado "
|
||||
"a seguir."
|
||||
|
||||
#: pretix/base/invoicing/email.py:55
|
||||
msgid "Email address for invoice"
|
||||
@@ -3778,12 +3765,10 @@ msgid "Remaining amount"
|
||||
msgstr "Valor restante"
|
||||
|
||||
#: pretix/base/invoicing/pdf.py:1009
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgctxt "invoice"
|
||||
#| msgid "Event date: {date_range}"
|
||||
#, python-brace-format
|
||||
msgctxt "invoice"
|
||||
msgid "Invoice period: {daterange}"
|
||||
msgstr "Data do evento: {date_range}"
|
||||
msgstr "Período da fatura: {daterange}"
|
||||
|
||||
#: pretix/base/invoicing/pdf.py:1040
|
||||
msgctxt "invoice"
|
||||
@@ -3868,12 +3853,9 @@ msgid "Peppol participant ID"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/invoicing/peppol.py:170
|
||||
#, fuzzy
|
||||
#| msgctxt "italian_invoice"
|
||||
#| msgid "Fiscal code"
|
||||
msgctxt "peppol_invoice"
|
||||
msgid "Visual copy"
|
||||
msgstr "Código fiscal"
|
||||
msgstr "Cópia visual"
|
||||
|
||||
#: pretix/base/invoicing/peppol.py:175
|
||||
msgctxt "peppol_invoice"
|
||||
@@ -4357,10 +4339,8 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/models/auth.py:392
|
||||
#, fuzzy
|
||||
#| msgid "Confirmation code"
|
||||
msgid "pretix confirmation code"
|
||||
msgstr "Código de confirmação"
|
||||
msgstr "código de confirmação do pretix"
|
||||
|
||||
#: pretix/base/models/auth.py:435
|
||||
#: pretix/control/templates/pretixcontrol/auth/forgot.html:7
|
||||
@@ -6298,9 +6278,6 @@ msgstr ""
|
||||
"insira um valor possível por linha."
|
||||
|
||||
#: pretix/base/models/items.py:2310
|
||||
#, fuzzy
|
||||
#| msgctxt "timeframe"
|
||||
#| msgid "Start"
|
||||
msgid "Start"
|
||||
msgstr "Início"
|
||||
|
||||
@@ -7912,10 +7889,8 @@ msgid "123.45 EUR"
|
||||
msgstr "123.45 BRL"
|
||||
|
||||
#: pretix/base/pdf.py:166
|
||||
#, fuzzy
|
||||
#| msgid "Price including add-ons"
|
||||
msgid "Price including bundled products"
|
||||
msgstr "Preço incluindo complementos"
|
||||
msgstr "Preços incluindo produtos empacotados"
|
||||
|
||||
#: pretix/base/pdf.py:175
|
||||
#, fuzzy
|
||||
@@ -9878,10 +9853,8 @@ msgid "Require a phone number per order"
|
||||
msgstr "Exigir um número de telefone por pedido"
|
||||
|
||||
#: pretix/base/settings.py:482
|
||||
#, fuzzy
|
||||
#| msgid "including all taxes"
|
||||
msgid "Rounding of taxes"
|
||||
msgstr "incluindo todos os impostos"
|
||||
msgstr "Arredondamento dos impostos"
|
||||
|
||||
#: pretix/base/settings.py:486
|
||||
msgid ""
|
||||
@@ -10424,18 +10397,12 @@ msgid "Automatic, but prefer invoice date over event date"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/settings.py:1142 pretix/base/settings.py:1153
|
||||
#, fuzzy
|
||||
#| msgctxt "invoice"
|
||||
#| msgid "Invoice date"
|
||||
msgid "Invoice date"
|
||||
msgstr "Data da fatura"
|
||||
|
||||
#: pretix/base/settings.py:1146
|
||||
#, fuzzy
|
||||
#| msgctxt "subevent"
|
||||
#| msgid "Date ordering"
|
||||
msgid "Date of service"
|
||||
msgstr "Ordenação de datas"
|
||||
msgstr "Data do serviço"
|
||||
|
||||
#: pretix/base/settings.py:1155
|
||||
msgid ""
|
||||
@@ -12166,17 +12133,7 @@ msgid "Invoice {invoice_number}"
|
||||
msgstr "Fatura {invoice_number}"
|
||||
|
||||
#: pretix/base/settings.py:2789
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid ""
|
||||
#| "Hello,\n"
|
||||
#| "\n"
|
||||
#| "somebody requested a list of your orders for {event}.\n"
|
||||
#| "The list is as follows:\n"
|
||||
#| "\n"
|
||||
#| "{orders}\n"
|
||||
#| "\n"
|
||||
#| "Best regards, \n"
|
||||
#| "Your {event} team"
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Hello,\n"
|
||||
"\n"
|
||||
@@ -12189,12 +12146,12 @@ msgid ""
|
||||
msgstr ""
|
||||
"Olá,\n"
|
||||
"\n"
|
||||
"Segue anexo uma nova fatura para o pedido {code} para {evento} . Este pedido "
|
||||
"foi feito por {order_email}.\n"
|
||||
"Em anexo você encontrará uma nova fatura para o pedido {code} para {event}. "
|
||||
"Este pedido foi feito por {order_email}.\n"
|
||||
"\n"
|
||||
"Atenciosamente, \n"
|
||||
"\n"
|
||||
"Equipe organizadora de {event}"
|
||||
"Organização {event}"
|
||||
|
||||
#: pretix/base/settings.py:2807 pretix/base/settings.py:2823
|
||||
#, python-brace-format
|
||||
@@ -14023,21 +13980,16 @@ msgstr ""
|
||||
"que o cartão-presente é emitido."
|
||||
|
||||
#: pretix/control/forms/event.py:813
|
||||
#, fuzzy
|
||||
#| msgid "including all taxes"
|
||||
msgid "Prices including tax"
|
||||
msgstr "incluindo todos os impostos"
|
||||
msgstr "Preços incluindo impostos"
|
||||
|
||||
#: pretix/control/forms/event.py:814
|
||||
msgid "Recommended if you sell tickets at least partly to consumers."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/control/forms/event.py:818
|
||||
#, fuzzy
|
||||
#| msgctxt "reporting_timeframe"
|
||||
#| msgid "All future (excluding today)"
|
||||
msgid "Prices excluding tax"
|
||||
msgstr "Todos os futuros (excluindo hoje)"
|
||||
msgstr "Preços excluindo impostos"
|
||||
|
||||
#: pretix/control/forms/event.py:819
|
||||
msgid "Recommended only if you sell tickets primarily to business customers."
|
||||
@@ -17410,10 +17362,9 @@ msgstr ""
|
||||
"O endereço de e-mail foi alterado de \"{old_email}\" para \"{new_email}\"."
|
||||
|
||||
#: pretix/control/logdisplay.py:673
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Your email address has been updated."
|
||||
#, python-brace-format
|
||||
msgid "Your email address {email} has been confirmed."
|
||||
msgstr "Seu endereço de email foi atualizado."
|
||||
msgstr "Seu endereço de e-mail {email} foi confirmado."
|
||||
|
||||
#: pretix/control/logdisplay.py:685
|
||||
#, python-brace-format
|
||||
@@ -19690,6 +19641,20 @@ msgid ""
|
||||
"Best regards,\n"
|
||||
"Your pretix team\n"
|
||||
msgstr ""
|
||||
"Olá,\n"
|
||||
"\n"
|
||||
"%(reason)s\n"
|
||||
"\n"
|
||||
" %(code)s\n"
|
||||
"\n"
|
||||
"Por favor, nunca forneça este código para outra pessoa. Nosso time de "
|
||||
"suporte nunca irá solicitar este código.\n"
|
||||
"\n"
|
||||
"Se você não solicitou este código, por favor entre em contato conosco "
|
||||
"imediatamente.\n"
|
||||
"\n"
|
||||
"Atenciosamente,\n"
|
||||
"Time pretix\n"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/email/email_setup.txt:1
|
||||
#, python-format
|
||||
@@ -21314,10 +21279,8 @@ msgid "with custom rules"
|
||||
msgstr "Regras personalizadas"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/event/tax.html:110
|
||||
#, fuzzy
|
||||
#| msgid "Base settings"
|
||||
msgid "Tax settings"
|
||||
msgstr "Configurações base"
|
||||
msgstr "Configurações de impostos"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/event/tax_delete.html:4
|
||||
#: pretix/control/templates/pretixcontrol/event/tax_delete.html:6
|
||||
@@ -26144,7 +26107,7 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/shredder/download.html:29
|
||||
msgid "Download data"
|
||||
msgstr ""
|
||||
msgstr "Baixar dados"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/shredder/download.html:34
|
||||
msgid "Step 2: Confirm deletion"
|
||||
@@ -26475,7 +26438,7 @@ msgstr "Adicionar um dispositivo de autenticação de dois fatores"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_confirm_totp.html:8
|
||||
msgid "To set up this device, please follow the following steps:"
|
||||
msgstr ""
|
||||
msgstr "Para configurar este dispositivo, por favor siga os passos a seguir:"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_confirm_totp.html:12
|
||||
msgid "Download the Google Authenticator application to your phone:"
|
||||
@@ -26901,11 +26864,11 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/staff_session_edit.html:6
|
||||
msgid "Session notes"
|
||||
msgstr ""
|
||||
msgstr "Notas de sessão"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/staff_session_edit.html:17
|
||||
msgid "Audit log"
|
||||
msgstr ""
|
||||
msgstr "Log de auditoria"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/staff_session_edit.html:30
|
||||
msgid "Method"
|
||||
@@ -27949,7 +27912,7 @@ msgstr ""
|
||||
|
||||
#: pretix/control/views/item.py:237
|
||||
msgid "The selected category has been deleted."
|
||||
msgstr ""
|
||||
msgstr "A categoria selecionada foi excluída"
|
||||
|
||||
#: pretix/control/views/item.py:322
|
||||
msgid "The new category has been created."
|
||||
@@ -29874,7 +29837,7 @@ msgstr ""
|
||||
|
||||
#: pretix/plugins/banktransfer/payment.py:239
|
||||
msgid "Please fill out your bank account details."
|
||||
msgstr ""
|
||||
msgstr "Por favor, preencha os detalhes da sua conta bancária."
|
||||
|
||||
#: pretix/plugins/banktransfer/payment.py:243
|
||||
msgid "Please enter your bank account details."
|
||||
@@ -30175,7 +30138,7 @@ msgstr ""
|
||||
|
||||
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/pending.html:122
|
||||
msgid "Scan the QR code with your banking app"
|
||||
msgstr ""
|
||||
msgstr "Escanear o QR Code com o seu aplicativo bancário"
|
||||
|
||||
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/refund_export.html:5
|
||||
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/refund_export.html:7
|
||||
@@ -30746,6 +30709,8 @@ msgid ""
|
||||
"We're waiting for an answer from PayPal regarding your payment. Please "
|
||||
"contact us, if this takes more than a few hours."
|
||||
msgstr ""
|
||||
"Estamos aguardando por informações do PayPal referentes ao seu pagamento. "
|
||||
"Por favor, entre em contato conosco se isso demorar mais que algumas horas."
|
||||
|
||||
#: pretix/plugins/paypal/templates/pretixplugins/paypal/redirect.html:17
|
||||
#: pretix/plugins/paypal2/templates/pretixplugins/paypal2/redirect.html:17
|
||||
@@ -33370,19 +33335,17 @@ msgstr "Organizador: {organizer}"
|
||||
#: pretix/presale/ical.py:139
|
||||
#, python-brace-format
|
||||
msgid "{event} - {item}"
|
||||
msgstr ""
|
||||
msgstr "{event} - {item}"
|
||||
|
||||
#: pretix/presale/ical.py:147
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Start date"
|
||||
#, python-brace-format
|
||||
msgid "Start: {datetime}"
|
||||
msgstr "Data inicial"
|
||||
msgstr "Início: {datetime}"
|
||||
|
||||
#: pretix/presale/ical.py:150
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Admission: {datetime}"
|
||||
#, python-brace-format
|
||||
msgid "End: {datetime}"
|
||||
msgstr "Admissão: {datetime}"
|
||||
msgstr "Término: {datetime}"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/base.html:44
|
||||
#, fuzzy
|
||||
@@ -35632,10 +35595,8 @@ msgid "The following gift cards are available in your customer account:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/giftcard/checkout.html:24
|
||||
#, fuzzy
|
||||
#| msgid "Issued gift cards"
|
||||
msgid "Use gift card"
|
||||
msgstr "Cartões-presente emitidos"
|
||||
msgstr "Usar cartão-presente"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/giftcard/checkout_confirm.html:4
|
||||
#, python-format
|
||||
@@ -35754,10 +35715,9 @@ msgid "Expired since %(date)s"
|
||||
msgstr "Expirado desde"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:46
|
||||
#, fuzzy, python-format
|
||||
#| msgid "Valid until %(datetime)s"
|
||||
#, python-format
|
||||
msgid "Valid until %(date)s"
|
||||
msgstr "Válido até %(datetime)s"
|
||||
msgstr "Válido até %(date)s"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:66
|
||||
#, fuzzy
|
||||
@@ -36287,10 +36247,8 @@ msgid "The selected date does not exist in this event series."
|
||||
msgstr "A data selecionada não existe nesta série 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 "O assento selecionado \"{seat}\" não está disponível."
|
||||
msgstr "A data selecionada não está disponível."
|
||||
|
||||
#: pretix/presale/views/widget.py:476
|
||||
#, python-format
|
||||
|
||||
@@ -8,8 +8,8 @@ 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-01 12:48+0000\n"
|
||||
"Last-Translator: José Manuel Silva <prof.jmas@gmail.com>\n"
|
||||
"PO-Revision-Date: 2025-12-02 16:47+0000\n"
|
||||
"Last-Translator: Ana Rute Pacheco Vivas <rute.vivas@om.org>\n"
|
||||
"Language-Team: Portuguese (Portugal) <https://translate.pretix.eu/projects/"
|
||||
"pretix/pretix/pt_PT/>\n"
|
||||
"Language: pt_PT\n"
|
||||
@@ -13318,13 +13318,13 @@ msgid "Contact:"
|
||||
msgstr "Contacto:"
|
||||
|
||||
#: pretix/base/templates/pretixbase/email/order_details.html:54
|
||||
#, fuzzy, python-format
|
||||
#| msgid ""
|
||||
#| "You are receiving this email because you placed an order for {event}."
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You are receiving this email because you placed an order for "
|
||||
"<strong>%(event)s</strong>."
|
||||
msgstr "Estás a receber este email porque fizeste um pedido para o {event}."
|
||||
msgstr ""
|
||||
"Estás a receber este email porque fizeste um pedido para o <strong>%(event)"
|
||||
"s</strong>."
|
||||
|
||||
#: pretix/base/templates/pretixbase/email/order_details.html:93
|
||||
#: pretix/control/templates/pretixcontrol/organizers/customer.html:23
|
||||
|
||||
@@ -41,15 +41,20 @@ for app in apps.get_app_configs():
|
||||
if hasattr(app, 'PretixPluginMeta'):
|
||||
if importlib.util.find_spec(app.name + '.urls'):
|
||||
urlmod = importlib.import_module(app.name + '.urls')
|
||||
single_plugin_patterns = []
|
||||
|
||||
if hasattr(urlmod, 'event_patterns'):
|
||||
patterns = plugin_event_urls(urlmod.event_patterns, plugin=app.name)
|
||||
raw_plugin_patterns.append(
|
||||
re_path(r'^(?P<event>[^/]+)/', include((patterns, app.label)))
|
||||
single_plugin_patterns.append(
|
||||
re_path(r'^(?P<event>[^/]+)/', include(patterns))
|
||||
)
|
||||
|
||||
if hasattr(urlmod, 'organizer_patterns'):
|
||||
patterns = plugin_event_urls(urlmod.organizer_patterns, plugin=app.name)
|
||||
single_plugin_patterns += plugin_event_urls(urlmod.organizer_patterns, plugin=app.name)
|
||||
|
||||
if single_plugin_patterns:
|
||||
raw_plugin_patterns.append(
|
||||
re_path(r'', include((patterns, app.label)))
|
||||
re_path(r'', include((single_plugin_patterns, app.label)))
|
||||
)
|
||||
|
||||
plugin_patterns = [
|
||||
|
||||
@@ -42,15 +42,20 @@ for app in apps.get_app_configs():
|
||||
if hasattr(app, 'PretixPluginMeta'):
|
||||
if importlib.util.find_spec(app.name + '.urls'):
|
||||
urlmod = importlib.import_module(app.name + '.urls')
|
||||
if hasattr(urlmod, 'event_patterns'):
|
||||
patterns = plugin_event_urls(urlmod.event_patterns, plugin=app.name)
|
||||
raw_plugin_patterns.append(
|
||||
re_path(r'^(?P<event>[^/]+)/', include((patterns, app.label)))
|
||||
)
|
||||
single_plugin_patterns = []
|
||||
|
||||
if hasattr(urlmod, 'organizer_patterns'):
|
||||
patterns = plugin_event_urls(urlmod.organizer_patterns, plugin=app.name)
|
||||
single_plugin_patterns += plugin_event_urls(urlmod.organizer_patterns, plugin=app.name)
|
||||
|
||||
if hasattr(urlmod, 'event_patterns'):
|
||||
plugin_event_patterns = plugin_event_urls(urlmod.event_patterns, plugin=app.name)
|
||||
single_plugin_patterns.append(
|
||||
re_path(r'^(?P<event>[^/]+)/', include(plugin_event_patterns))
|
||||
)
|
||||
|
||||
if single_plugin_patterns:
|
||||
raw_plugin_patterns.append(
|
||||
re_path(r'', include((patterns, app.label)))
|
||||
re_path(r'', include((single_plugin_patterns, app.label)))
|
||||
)
|
||||
|
||||
plugin_patterns = [
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -34,8 +34,10 @@
|
||||
|
||||
import json
|
||||
import logging
|
||||
import operator
|
||||
import re
|
||||
from decimal import Decimal
|
||||
from functools import reduce
|
||||
|
||||
import dateutil.parser
|
||||
from celery.exceptions import MaxRetriesExceededError
|
||||
@@ -117,20 +119,26 @@ def _find_order_for_code(base_qs, code):
|
||||
pass
|
||||
|
||||
|
||||
def _find_order_for_invoice_id(base_qs, prefix, number):
|
||||
def _find_order_for_invoice_id(base_qs, prefixes, number):
|
||||
try:
|
||||
# Working with __iregex here is an experiment, if this turns out to be too slow in production
|
||||
# we might need to switch to a different approach.
|
||||
r = [
|
||||
Q(
|
||||
prefix__istartswith=prefix, # redundant, but hopefully makes it a little faster
|
||||
full_invoice_no__iregex=prefix + r'[\- ]*0*' + number
|
||||
)
|
||||
for prefix in set(prefixes)
|
||||
]
|
||||
return base_qs.select_related('order').get(
|
||||
prefix__istartswith=prefix, # redundant, but hopefully makes it a little faster
|
||||
full_invoice_no__iregex=prefix + r'[\- ]*0*' + number
|
||||
reduce(operator.or_, r)
|
||||
).order
|
||||
except (Invoice.DoesNotExist, Invoice.MultipleObjectsReturned):
|
||||
pass
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
def _handle_transaction(trans: BankTransaction, matches: tuple, event: Event = None, organizer: Organizer = None):
|
||||
def _handle_transaction(trans: BankTransaction, matches: tuple, regex_match_to_slug, event: Event = None, organizer: Organizer = None):
|
||||
orders = []
|
||||
if event:
|
||||
for slug, code in matches:
|
||||
@@ -139,18 +147,19 @@ def _handle_transaction(trans: BankTransaction, matches: tuple, event: Event = N
|
||||
if order.code not in {o.code for o in orders}:
|
||||
orders.append(order)
|
||||
else:
|
||||
order = _find_order_for_invoice_id(Invoice.objects.filter(event=event), slug, code)
|
||||
order = _find_order_for_invoice_id(Invoice.objects.filter(event=event), (slug, regex_match_to_slug.get(slug, slug)), code)
|
||||
if order and order.code not in {o.code for o in orders}:
|
||||
orders.append(order)
|
||||
else:
|
||||
qs = Order.objects.filter(event__organizer=organizer)
|
||||
for slug, code in matches:
|
||||
order = _find_order_for_code(qs.filter(event__slug__iexact=slug), code)
|
||||
original_slug = regex_match_to_slug.get(slug, slug)
|
||||
order = _find_order_for_code(qs.filter(Q(event__slug__iexact=slug) | Q(event__slug__iexact=original_slug)), code)
|
||||
if order:
|
||||
if order.code not in {o.code for o in orders}:
|
||||
orders.append(order)
|
||||
else:
|
||||
order = _find_order_for_invoice_id(Invoice.objects.filter(event__organizer=organizer), slug, code)
|
||||
order = _find_order_for_invoice_id(Invoice.objects.filter(event__organizer=organizer), (slug, original_slug), code)
|
||||
if order and order.code not in {o.code for o in orders}:
|
||||
orders.append(order)
|
||||
|
||||
@@ -366,22 +375,37 @@ def process_banktransfers(self, job: int, data: list) -> None:
|
||||
transactions = _get_unknown_transactions(job, data, **job.owner_kwargs)
|
||||
|
||||
# Match order codes
|
||||
regex_match_to_slug = {}
|
||||
code_len_agg = Order.objects.filter(event__organizer=job.organizer).annotate(
|
||||
clen=Length('code')
|
||||
).aggregate(min=Min('clen'), max=Max('clen'))
|
||||
if job.event:
|
||||
prefixes = {job.event.slug.upper()}
|
||||
prefixes = {job.event.slug.upper(), job.event.slug.upper().replace("-", "")}
|
||||
if "-" in job.event.slug:
|
||||
regex_match_to_slug[job.event.slug.upper().replace("-", "")] = job.event.slug
|
||||
else:
|
||||
prefixes = {e.slug.upper() for e in job.organizer.events.all()}
|
||||
prefixes = set()
|
||||
for e in job.organizer.events.all():
|
||||
prefixes.add(e.slug.upper())
|
||||
if "-" in e.slug:
|
||||
prefixes.add(e.slug.upper().replace("-", ""))
|
||||
regex_match_to_slug[e.slug.upper().replace("-", "")] = e.slug
|
||||
|
||||
# Match invoice numbers
|
||||
inr_len_agg = Invoice.objects.filter(event__organizer=job.organizer).annotate(
|
||||
clen=Length('invoice_no')
|
||||
).aggregate(min=Min('clen'), max=Max('clen'))
|
||||
if job.event:
|
||||
prefixes |= {p.rstrip(' -') for p in Invoice.objects.filter(event=job.event).distinct().values_list('prefix', flat=True)}
|
||||
invoice_prefixes = Invoice.objects.filter(event=job.event)
|
||||
else:
|
||||
prefixes |= {p.rstrip(' -') for p in Invoice.objects.filter(event__organizer=job.organizer).distinct().values_list('prefix', flat=True)}
|
||||
invoice_prefixes = Invoice.objects.filter(event__organizer=job.organizer)
|
||||
for p in invoice_prefixes.order_by().distinct().values_list('prefix', flat=True):
|
||||
prefix = p.rstrip(" -")
|
||||
prefixes.add(prefix)
|
||||
if "-" in prefix:
|
||||
prefix_nodash = prefix.replace("-", "")
|
||||
prefixes.add(prefix_nodash)
|
||||
regex_match_to_slug[prefix_nodash] = prefix
|
||||
|
||||
pattern = re.compile(
|
||||
"(%s)[ \\-_]*([A-Z0-9]{%s,%s})" % (
|
||||
@@ -409,9 +433,9 @@ def process_banktransfers(self, job: int, data: list) -> None:
|
||||
|
||||
if matches:
|
||||
if job.event:
|
||||
_handle_transaction(trans, matches, event=job.event)
|
||||
_handle_transaction(trans, matches, regex_match_to_slug, event=job.event)
|
||||
else:
|
||||
_handle_transaction(trans, matches, organizer=job.organizer)
|
||||
_handle_transaction(trans, matches, regex_match_to_slug, organizer=job.organizer)
|
||||
else:
|
||||
trans.state = BankTransaction.STATE_NOMATCH
|
||||
trans.save()
|
||||
|
||||
@@ -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 %}
|
||||
@@ -82,6 +82,9 @@ $(function () {
|
||||
if ('label' in options) {
|
||||
dependent.closest(".form-group").find(".control-label").text(options.label);
|
||||
}
|
||||
if ('helptext_visible' in options) {
|
||||
dependent.closest(".form-group").find(".help-block").toggle(options.helptext_visible);
|
||||
}
|
||||
|
||||
const required = 'required' in options && visible && (
|
||||
(options.required === 'if_any' && isAnyRequired) ||
|
||||
|
||||
@@ -34,7 +34,7 @@ def test_no_invoice_address(client):
|
||||
'data': [],
|
||||
'state': {'label': 'State', 'required': False, 'visible': False},
|
||||
'street': {'required': 'if_any'},
|
||||
'vat_id': {'required': False, 'visible': True},
|
||||
'vat_id': {'helptext_visible': True, 'label': 'VAT ID', 'required': False, 'visible': True},
|
||||
'zipcode': {'required': 'if_any'}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ def test_no_invoice_address(client):
|
||||
'data': [],
|
||||
'state': {'label': 'State', 'required': False, 'visible': False},
|
||||
'street': {'required': 'if_any'},
|
||||
'vat_id': {'required': False, 'visible': False},
|
||||
'vat_id': {'helptext_visible': True, 'label': 'VAT ID', 'required': False, 'visible': False},
|
||||
'zipcode': {'required': False}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ def test_provider_only_email_available(client, event):
|
||||
'transmission_peppol_participant_id': {'required': False, 'visible': False},
|
||||
'transmission_type': {'visible': False},
|
||||
'transmission_types': [{'code': 'email', 'name': 'Email'}],
|
||||
'vat_id': {'required': False, 'visible': True},
|
||||
'vat_id': {'helptext_visible': True, 'label': 'VAT ID', 'required': False, 'visible': True},
|
||||
'zipcode': {'required': 'if_any'}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ def test_provider_italy_sdi_not_enforced_when_optional(client, event):
|
||||
'transmission_peppol_participant_id': {'required': False, 'visible': False},
|
||||
'transmission_type': {'visible': True},
|
||||
'transmission_types': [{'code': 'it_sdi', 'name': 'Exchange System (SdI)'}],
|
||||
'vat_id': {'required': False, 'visible': True},
|
||||
'vat_id': {'helptext_visible': True, 'label': 'VAT ID / P.IVA', 'required': False, 'visible': True},
|
||||
'zipcode': {'required': 'if_any'}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ def test_provider_italy_sdi_enforced_individual(client, event):
|
||||
'transmission_peppol_participant_id': {'required': False, 'visible': False},
|
||||
'transmission_type': {'visible': True},
|
||||
'transmission_types': [{'code': 'it_sdi', 'name': 'Exchange System (SdI)'}],
|
||||
'vat_id': {'required': False, 'visible': True},
|
||||
'vat_id': {'helptext_visible': True, 'label': 'VAT ID / P.IVA', 'required': False, 'visible': True},
|
||||
'zipcode': {'required': True}
|
||||
}
|
||||
|
||||
@@ -174,11 +174,37 @@ def test_provider_italy_sdi_enforced_business(client, event):
|
||||
'transmission_peppol_participant_id': {'required': False, 'visible': False},
|
||||
'transmission_type': {'visible': True},
|
||||
'transmission_types': [{'code': 'it_sdi', 'name': 'Exchange System (SdI)'}],
|
||||
'vat_id': {'required': True, 'visible': True},
|
||||
'vat_id': {'helptext_visible': False, 'label': 'VAT ID / P.IVA', 'required': True, 'visible': True},
|
||||
'zipcode': {'required': True}
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_vat_id_enforced(client, event):
|
||||
response = client.get(
|
||||
'/js_helpers/address_form/?country=GR&invoice=true&organizer=org&event=ev'
|
||||
'&is_business=business'
|
||||
)
|
||||
assert response.status_code == 200
|
||||
d = response.json()
|
||||
del d['data']
|
||||
assert d == {
|
||||
'city': {'required': 'if_any'},
|
||||
'state': {'label': 'State', 'required': False, 'visible': False},
|
||||
'street': {'required': 'if_any'},
|
||||
'transmission_email_address': {'required': False, 'visible': False},
|
||||
'transmission_email_other': {'required': False, 'visible': False},
|
||||
'transmission_it_sdi_codice_fiscale': {'required': False, 'visible': False},
|
||||
'transmission_it_sdi_pec': {'required': False, 'visible': False},
|
||||
'transmission_it_sdi_recipient_code': {'required': False, 'visible': False},
|
||||
'transmission_peppol_participant_id': {'required': False, 'visible': False},
|
||||
'transmission_type': {'visible': True},
|
||||
'transmission_types': [{'code': 'email', 'name': 'Email'}, {'code': 'peppol', 'name': 'Peppol'}],
|
||||
'vat_id': {'helptext_visible': False, 'label': 'VAT ID / TIN', 'required': True, 'visible': True},
|
||||
'zipcode': {'required': 'if_any'}
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_email_peppol_choice(client, event):
|
||||
response = client.get(
|
||||
@@ -203,7 +229,7 @@ def test_email_peppol_choice(client, event):
|
||||
{'code': 'email', 'name': 'Email'},
|
||||
{'code': 'peppol', 'name': 'Peppol'},
|
||||
],
|
||||
'vat_id': {'required': False, 'visible': True},
|
||||
'vat_id': {'helptext_visible': True, 'label': 'VAT ID', 'required': False, 'visible': True},
|
||||
'zipcode': {'required': 'if_any'}
|
||||
}
|
||||
|
||||
@@ -229,6 +255,6 @@ def test_email_peppol_choice(client, event):
|
||||
{'code': 'email', 'name': 'Email'},
|
||||
{'code': 'peppol', 'name': 'Peppol'},
|
||||
],
|
||||
'vat_id': {'required': False, 'visible': True},
|
||||
'vat_id': {'helptext_visible': True, 'label': 'VAT ID', 'required': False, 'visible': True},
|
||||
'zipcode': {'required': True}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import responses
|
||||
from requests import Timeout
|
||||
|
||||
from pretix.base.services.tax import (
|
||||
VATIDFinalError, VATIDTemporaryError, validate_vat_id,
|
||||
VATIDFinalError, VATIDTemporaryError, normalize_vat_id, validate_vat_id,
|
||||
)
|
||||
|
||||
|
||||
@@ -51,6 +51,18 @@ def test_eu_country_mismatch():
|
||||
validate_vat_id('AT12345', 'DE')
|
||||
|
||||
|
||||
@responses.activate
|
||||
def test_normalize():
|
||||
assert normalize_vat_id('AT U 12345678', 'AT') == 'ATU12345678'
|
||||
assert normalize_vat_id('U12345678', 'AT') == 'ATU12345678'
|
||||
assert normalize_vat_id('IT.123.456.789.00', 'IT') == 'IT12345678900'
|
||||
assert normalize_vat_id('12345678900', 'IT') == 'IT12345678900'
|
||||
assert normalize_vat_id('123456789MVA', 'NO') == "NO123456789MVA"
|
||||
assert normalize_vat_id('CHE 123456789 MWST', 'CH') == "CHE123456789"
|
||||
# Bad combination is left for validation
|
||||
assert normalize_vat_id('ATU12345678', 'IT') == 'ATU12345678'
|
||||
|
||||
|
||||
@responses.activate
|
||||
def test_eu_server_down():
|
||||
def _callback(request):
|
||||
|
||||
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'
|
||||
@@ -45,6 +45,8 @@ def env():
|
||||
def test_event_main_domain_front_page(env):
|
||||
assert eventreverse(env[1], 'presale:event.index') == '/mrmcd/2015/'
|
||||
assert eventreverse(env[0], 'presale:organizer.index') == '/mrmcd/'
|
||||
assert eventreverse(env[1], 'plugins:testdummy:view') == '/mrmcd/2015/testdummy'
|
||||
assert eventreverse(env[0], 'plugins:testdummy:view') == '/mrmcd/testdummy'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -52,12 +54,16 @@ def test_event_custom_domain_kwargs(env):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
|
||||
KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1])
|
||||
assert eventreverse(env[1], 'presale:event.checkout', {'step': 'payment'}) == 'http://barfoo/checkout/payment/'
|
||||
assert eventreverse(env[0], 'plugins:testdummy:view') == 'http://foobar/testdummy'
|
||||
assert eventreverse(env[1], 'plugins:testdummy:view') == 'http://barfoo/testdummy'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_org_domain_kwargs(env):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
|
||||
assert eventreverse(env[1], 'presale:event.checkout', {'step': 'payment'}) == 'http://foobar/2015/checkout/payment/'
|
||||
assert eventreverse(env[0], 'plugins:testdummy:view') == 'http://foobar/testdummy'
|
||||
assert eventreverse(env[1], 'plugins:testdummy:view') == 'http://foobar/2015/testdummy'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -65,9 +71,13 @@ def test_event_org_alt_domain_kwargs(env):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
|
||||
d = KnownDomain.objects.create(domainname='altfoo', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
assert eventreverse(env[1], 'presale:event.checkout', {'step': 'payment'}) == 'http://foobar/2015/checkout/payment/'
|
||||
assert eventreverse(env[1], 'plugins:testdummy:view') == 'http://foobar/2015/testdummy'
|
||||
d.event_assignments.create(event=env[1])
|
||||
with scopes_disabled():
|
||||
assert eventreverse(Event.objects.get(pk=env[1].pk), 'presale:event.checkout', {'step': 'payment'}) == 'http://altfoo/2015/checkout/payment/'
|
||||
event = Event.objects.get(pk=env[1].pk)
|
||||
assert eventreverse(event, 'presale:event.checkout', {'step': 'payment'}) == 'http://altfoo/2015/checkout/payment/'
|
||||
assert eventreverse(env[0], 'plugins:testdummy:view') == 'http://foobar/testdummy'
|
||||
assert eventreverse(event, 'plugins:testdummy:view') == 'http://altfoo/2015/testdummy'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
||||
@@ -385,6 +385,20 @@ def test_mark_paid_organizer_dash_in_slug(env, orga_job):
|
||||
assert env[2].status == Order.STATUS_PAID
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_mark_paid_organizer_dash_in_slug_missing(env, orga_job):
|
||||
env[0].slug = "foo-bar"
|
||||
env[0].save()
|
||||
process_banktransfers(orga_job, [{
|
||||
'payer': 'Karla Kundin',
|
||||
'reference': 'Bestellung FOOBAR1234S',
|
||||
'date': '2016-01-26',
|
||||
'amount': '23.00'
|
||||
}])
|
||||
env[2].refresh_from_db()
|
||||
assert env[2].status == Order.STATUS_PAID
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_mark_paid_organizer_varying_order_code_length(env, orga_job):
|
||||
env[2].code = "123412341234"
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -411,6 +411,69 @@ class CheckoutTestCase(BaseCheckoutTestCase, TimemachineTestMixin, TestCase):
|
||||
|
||||
with scopes_disabled():
|
||||
ia = InvoiceAddress.objects.get(pk=self.client.session['carts'][self.session_key].get('invoice_address'))
|
||||
assert ia.vat_id == "AT123456"
|
||||
assert not ia.vat_id_validated
|
||||
|
||||
def test_reverse_charge_vatid_required(self):
|
||||
self.event.settings.invoice_address_vatid = True
|
||||
self.event.settings.invoice_address_vatid_required_countries = ["AT"]
|
||||
|
||||
with scopes_disabled():
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
price=23, expires=now() + timedelta(minutes=10)
|
||||
)
|
||||
|
||||
resp = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
||||
'is_business': 'business',
|
||||
'company': 'Foo',
|
||||
'name': 'Bar',
|
||||
'street': 'Baz',
|
||||
'zipcode': '1234',
|
||||
'city': 'Here',
|
||||
'country': 'AT',
|
||||
'email': 'admin@localhost',
|
||||
'transmission_type': 'email',
|
||||
}, follow=True)
|
||||
assert 'has-error' in resp.content.decode()
|
||||
|
||||
def test_reverse_charge_vatid_check_unavailable_but_required(self):
|
||||
self.tr19.eu_reverse_charge = True
|
||||
self.tr19.home_country = Country('DE')
|
||||
self.tr19.save()
|
||||
self.event.settings.invoice_address_vatid = True
|
||||
self.event.settings.invoice_address_vatid_required_countries = ["AT"]
|
||||
|
||||
with scopes_disabled():
|
||||
cr1 = CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
price=23, expires=now() + timedelta(minutes=10)
|
||||
)
|
||||
|
||||
with mock.patch('pretix.base.services.tax._validate_vat_id_EU') as mock_validate:
|
||||
def raiser(*args, **kwargs):
|
||||
raise VATIDTemporaryError('temp')
|
||||
|
||||
mock_validate.side_effect = raiser
|
||||
self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
||||
'is_business': 'business',
|
||||
'company': 'Foo',
|
||||
'name': 'Bar',
|
||||
'street': 'Baz',
|
||||
'zipcode': '1234',
|
||||
'city': 'Here',
|
||||
'country': 'AT',
|
||||
'vat_id': 'AT123456',
|
||||
'email': 'admin@localhost',
|
||||
'transmission_type': 'email',
|
||||
}, follow=True)
|
||||
|
||||
cr1.refresh_from_db()
|
||||
assert cr1.price == Decimal('23.00')
|
||||
|
||||
with scopes_disabled():
|
||||
ia = InvoiceAddress.objects.get(pk=self.client.session['carts'][self.session_key].get('invoice_address'))
|
||||
assert ia.vat_id == "AT123456"
|
||||
assert not ia.vat_id_validated
|
||||
|
||||
def test_reverse_charge_keep_gross(self):
|
||||
@@ -448,6 +511,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TimemachineTestMixin, TestCase):
|
||||
|
||||
with scopes_disabled():
|
||||
ia = InvoiceAddress.objects.get(pk=self.client.session['carts'][self.session_key].get('invoice_address'))
|
||||
assert ia.vat_id == "AT123456"
|
||||
assert ia.vat_id_validated
|
||||
|
||||
def test_custom_tax_rules(self):
|
||||
@@ -1452,7 +1516,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TimemachineTestMixin, TestCase):
|
||||
'transmission_type': 'it_sdi',
|
||||
'vat_id': '',
|
||||
}, follow=True)
|
||||
assert "This field is required for the selected type" in response.content.decode()
|
||||
assert "This field is required" in response.content.decode()
|
||||
|
||||
response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
||||
'is_business': 'business',
|
||||
@@ -1468,6 +1532,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TimemachineTestMixin, TestCase):
|
||||
'state': 'MI',
|
||||
'email': 'admin@localhost',
|
||||
'transmission_type': 'email',
|
||||
'vat_id': 'IT01234567890',
|
||||
}, follow=True)
|
||||
assert "must be used for this country" in response.content.decode()
|
||||
|
||||
|
||||
52
src/tests/testdummy/urls.py
Normal file
52
src/tests/testdummy/urls.py
Normal file
@@ -0,0 +1,52 @@
|
||||
#
|
||||
# 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 django.http import HttpResponse
|
||||
from django.urls import path
|
||||
|
||||
|
||||
def view(request):
|
||||
return HttpResponse("")
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path(
|
||||
"testdummy",
|
||||
view,
|
||||
name="view",
|
||||
),
|
||||
]
|
||||
|
||||
organizer_patterns = [
|
||||
path(
|
||||
"testdummy",
|
||||
view,
|
||||
name="view",
|
||||
),
|
||||
]
|
||||
|
||||
event_patterns = [
|
||||
path(
|
||||
"testdummy",
|
||||
view,
|
||||
name="view",
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user