Compare commits

..

72 Commits

Author SHA1 Message Date
Phin Wolkwitz
49b1c61dd1 Move checks to describe function 2025-12-19 13:13:01 +01:00
Phin Wolkwitz
db47f5638e Improve helper function 2025-12-19 12:12:17 +01:00
Phin Wolkwitz
a2dc467d0b Remove iteration over unnecessary transmission types 2025-12-18 16:56:19 +01:00
Phin Wolkwitz
dc1a115863 Hide info from invisible fields in confirmation step 2025-12-17 16:49:46 +01:00
Raphael Michel
d0d7670ca5 Data sync: Allow more flexibility on list separators (#5718) 2025-12-17 16:23:07 +01:00
Richard Schreiber
a17a098b15 Exclude data-dir from code style checks (#5725) 2025-12-17 16:22:42 +01:00
sandra r
40516ab8e0 Translations: Update Galician
Currently translated at 15.9% (983 of 6172 strings)

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

powered by weblate
2025-12-17 16:21:25 +01:00
sandra r
3ca343fabc Translations: Update Galician
Currently translated at 15.9% (982 of 6172 strings)

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

powered by weblate
2025-12-17 16:21:25 +01:00
Lachlan Struthers
7304b7f24b Translations: Update Albanian
Currently translated at 91.3% (232 of 254 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/sq/

powered by weblate
2025-12-17 16:21:25 +01:00
Lachlan Struthers
abaf968103 Translations: Update Albanian
Currently translated at 1.1% (71 of 6172 strings)

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

powered by weblate
2025-12-17 16:21:25 +01:00
Lachlan Struthers
86e2f5a155 Translations: Update Albanian
Currently translated at 69.6% (177 of 254 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/sq/

powered by weblate
2025-12-17 16:21:25 +01:00
Lachlan Struthers
4c64af02c1 Translations: Update Albanian
Currently translated at 0.8% (52 of 6172 strings)

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

powered by weblate
2025-12-17 16:21:25 +01:00
Phin Wolkwitz
11df4398e1 Fix presale date display in calendar (Z#23216645) (#5710)
Fix presale date display in calendar and introduce a template tag
2025-12-17 16:18:59 +01:00
Lukas Bockstaller
2e89fc0a94 Questions: filter answers by dateFrame (Z#23216406) (#5706)
* replace manual form with QuestionFilterForm

* move form to form/item.py

* filter using a dateFrameField

* rename QuestionFilterForm to QuestionAnswerFilterForm

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

* pass existing `opqs` into `filter_qs`

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

* clean up filters

* fix view errors

* add labels

* display validation failures on field/label

* fix linting issues

* adjust datetime comparisons from lte to lt & gte to gt

* Change filter-form layout similar to order-filter-form

* improve label texts

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

* use order constants

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

* use Order Constants in Form where possible

* Change phrasing from Subevent to Date

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

* include product variations in products filter

* repair time zone comparisons

* fix linting

* move filter form to form/filter.py

* remove references to timezone.utc

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

* remove manual class statements

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

* removes unnecessary check

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

* fix datetime comparison

* Add full stop to error message to match style

* unify var-names and code-indent

---------

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2025-12-15 12:46:06 +01:00
Raphael Michel
510c4850a5 Merge branch 'Add-Promptpay-for-stripe' (#5670) 2025-12-12 09:08:12 +01:00
Raphael Michel
b13368d614 Event creation: Do not declare tax rate as optional (fixes #4794) (#5619) 2025-12-12 08:59:07 +01:00
Ana Rute Pacheco Vivas
b5cc8b368b Translations: Update Portuguese (Portugal)
Currently translated at 83.2% (5140 of 6172 strings)

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

powered by weblate
2025-12-12 08:59:04 +01:00
Renne Rocha
87c30d0acb Translations: Update Portuguese (Brazil)
Currently translated at 90.4% (5585 of 6172 strings)

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

powered by weblate
2025-12-12 08:59:04 +01:00
Raphael Michel
ffed8b29b1 Bank transfer: Allow CAMT import (#5601) 2025-12-12 08:58:52 +01:00
Ana Rute Pacheco Vivas
53fbb64225 Translations: Update Portuguese (Portugal)
Currently translated at 50.3% (128 of 254 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/pt_PT/

powered by weblate
2025-12-10 17:02:20 +01:00
Ana Rute Pacheco Vivas
e10ec4074b Translations: Update Portuguese (Portugal)
Currently translated at 83.1% (5135 of 6172 strings)

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

powered by weblate
2025-12-10 17:02:20 +01:00
Lachlan Struthers
7f2dc77aca Translations: Update Albanian
Currently translated at 41.3% (105 of 254 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/sq/

powered by weblate
2025-12-10 17:02:20 +01:00
Lachlan Struthers
199a3bf1e7 Translations: Update Albanian
Currently translated at 0.6% (39 of 6172 strings)

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

powered by weblate
2025-12-10 17:02:20 +01:00
Raphael Michel
904aa807a3 Footer link form: Add placeholder (Z#23217115) 2025-12-10 16:49:09 +01:00
Praveen Kathirvasan
0e41353a0e Add "Pay by bank" option for UK customers via Stripe (#5648)
* Add support for 'Pay by bank (UK)' payment method via Stripe

* Add 'Pay by bank' payment provider to Stripe integration

* Enhance Stripe integration: Allow UK bank payments and update imports

* Remove UK-specific payment method options from StripePayByBank integration

* Remove some UK references

---------

Co-authored-by: Raphael Michel <michel@rami.io>
2025-12-09 13:25:52 +01:00
Raphael Michel
82ca50c7ff Fix templates 2025-12-09 12:42:47 +01:00
Daniel
3437b64947 Add PromptPay support (#5)
* Handle PromptPay QR flow

* Send billing email for PromptPay

* fix isort

* Update payment.py

* Update signals.py

---------

Co-authored-by: Chondaen <chondaen12@1000WA>
2025-12-09 12:28:28 +01:00
Raphael Michel
b895d9bbca Import large package lazily to speed up startup (#5636)
* Import large package lazily to speed up startup

* Make all jsonschema imports lazy
2025-12-09 09:52:53 +01:00
Raphael Michel
f214edaf34 Timeline: Fix incorrect string formatting (fixes #5614) (#5617) 2025-12-09 08:52:09 +01:00
Raphael Michel
165a47b593 Bank transfer: Auto-ignore all 0-valued transactions (fixes #5168) (#5620)
* Bank transfer: Auto-ignore all 0-valued transactions (fixes #5168)

* Fix failing test
2025-12-09 08:50:04 +01:00
Renne Rocha
e06f281f1e Translations: Update Portuguese (Brazil)
Currently translated at 90.3% (5575 of 6172 strings)

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

powered by weblate
2025-12-09 08:49:57 +01:00
Renne Rocha
203c7e660d Translations: Update Portuguese (Brazil)
Currently translated at 100.0% (254 of 254 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/pt_BR/

powered by weblate
2025-12-09 08:49:57 +01:00
Renne Rocha
8c360b8754 Translations: Update Portuguese (Brazil)
Currently translated at 90.2% (5572 of 6172 strings)

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

powered by weblate
2025-12-09 08:49:57 +01:00
Ruud Hendrickx
90b6511d11 Translations: Update Dutch (informal) (nl_Informal)
Currently translated at 64.0% (3951 of 6172 strings)

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

powered by weblate
2025-12-09 08:49:57 +01:00
Ruud Hendrickx
bb356257cb Translations: Update Dutch
Currently translated at 96.3% (5945 of 6172 strings)

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

powered by weblate
2025-12-09 08:49:57 +01:00
sandra r
e1950e408e Translations: Update Galician
Currently translated at 15.5% (958 of 6172 strings)

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

powered by weblate
2025-12-09 08:49:57 +01:00
Yasunobu YesNo Kawaguchi
99d5722ce1 Translations: Update Japanese
Currently translated at 99.9% (6166 of 6172 strings)

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

powered by weblate
2025-12-09 08:49:57 +01:00
luelista
324eeb8d40 Fix crash when imported CSV has invalid syntax (#5702) 2025-12-09 08:09:34 +01:00
Raphael Michel
449e8dc905 Event cancel form: Add missing rich=True flag 2025-12-08 09:58:54 +01:00
Raphael Michel
c491c8232e Bank transfer: Allow dashes in event slug to be missing (Z#23216859) (#5682)
* Bank transfer: Allow dashes in event slug to be missing (Z#23216859)

* Update src/pretix/plugins/banktransfer/tasks.py

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

* Update src/pretix/plugins/banktransfer/tasks.py

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

* Apply suggestions from code review

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

---------

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
2025-12-05 10:54:03 +01:00
sandra r
aa02cc7968 Translations: Update Galician
Currently translated at 15.5% (961 of 6172 strings)

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

powered by weblate
2025-12-05 10:36:32 +01:00
Renne Rocha
cfa13d6b9d Translations: Update Portuguese (Brazil)
Currently translated at 90.2% (5572 of 6172 strings)

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

powered by weblate
2025-12-05 10:36:32 +01:00
Raphael Michel
af4eabc800 URL generation: Fix bug if plugins declare both event_urls and organizer_urls (#5688)
* URL generation: Fix bug if plugins declare both event_urls and organizer_urls

* Add missing file

* Add license header
2025-12-05 10:22:28 +01:00
luelista
e1f5678d7c Refactor payment QR code generation code and add SPAYD format (#5680)
Move generation of QR code contents out of the HTML template and into Python code, so it can
be reused in plugins and tested with unit tests. Add the SPAYD QR code format which is used in
Czech Republic and Slovakia [1]. Display BezahlCode QR codes only for German IBANs.

[1] https://en.wikipedia.org/wiki/Short_Payment_Descriptor
2025-12-04 14:15:29 +01:00
luelista
609b7c82ee Handle duplicate column names in CSV import (#5681)
- display a warning message to the user
- automatically rename columns by adding "__1", "__2", ... suffixes
2025-12-04 14:03:27 +01:00
Raphael Michel
8d66e1e732 Cart extension: Fix bundled product being removed from cart when sold out (#5690)
Instead, the entire bundle must be removed as it may not be sold
individually.
2025-12-04 11:48:40 +01:00
Richard Schreiber
c925f094f2 Reduce item event queries in waitinglist assign 2025-12-04 11:01:30 +01:00
Richard Schreiber
5caaa8586d Fix accounting report pending payment timezone (#5698) 2025-12-04 10:59:57 +01:00
SJang1
1b1cf1557d Translations: Update Korean
Currently translated at 50.8% (3139 of 6172 strings)

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

powered by weblate
2025-12-04 10:40:16 +01:00
sandra r
35d8a7eec5 Translations: Update Galician
Currently translated at 100.0% (254 of 254 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/gl/

powered by weblate
2025-12-04 10:40:16 +01:00
sandra r
d428c3e1a4 Translations: Update Galician
Currently translated at 14.0% (869 of 6172 strings)

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

powered by weblate
2025-12-04 10:40:16 +01:00
dependabot[bot]
63850f3139 Update sentry-sdk requirement from ==2.46.* to ==2.47.*
Updates the requirements on [sentry-sdk](https://github.com/getsentry/sentry-python) to permit the latest version.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.46.0...2.47.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-version: 2.47.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-04 10:40:05 +01:00
Felix Rindt
04c8270d43 Update pricing.rst to fix number typo (#5691)
I think you meant to point out the difference to the values in the table above...
2025-12-04 07:27:36 +01:00
dependabot[bot]
74a960e239 Update celery requirement from ==5.5.* to ==5.6.* (#5676) 2025-12-03 17:00:53 +01:00
Raphael Michel
5a1bcae085 Invoice address: Improve VAT ID input (#5647)
* Remove unmaintained depdendency vat_moss

* VAT ID normalization: Auto-add country codes

* VAT ID: County-specific labels

* Invoice address: Allow to set VAT ID as required per country

* Fix failing tests

* Update src/pretix/base/settings.py

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

* Review fixes

---------

Co-authored-by: luelista <weller@rami.io>
2025-12-03 16:48:19 +01:00
SJang1
051eb78312 Translations: Update Korean
Currently translated at 50.8% (3140 of 6172 strings)

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

powered by weblate
2025-12-03 16:29:20 +01:00
Ana Rute Pacheco Vivas
15808e55fd Translations: Update Portuguese (Portugal)
Currently translated at 83.1% (5134 of 6172 strings)

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

powered by weblate
2025-12-03 16:29:20 +01:00
David Ibáñez Cerdeira
c886c0b415 Translations: Update Galician
Currently translated at 9.2% (569 of 6172 strings)

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

powered by weblate
2025-12-03 16:29:20 +01:00
David Ibáñez Cerdeira
47472447eb Translations: Update Galician
Currently translated at 9.1% (567 of 6172 strings)

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

powered by weblate
2025-12-03 16:29:20 +01:00
Richard Schreiber
1a40215e91 Fix N+1 queries in API (#5684)
* Fix N+1 query in API quotas list

* fix membership N+1

* fix vouchers N+1 budget_used

* rename and reuse Voucher.annotate_budget_used_orders to budget_used

* fix flake8
2025-12-03 15:37:40 +01:00
Raphael Michel
d3fde85c39 Fix typo in CSS variable 2025-12-02 17:47:45 +01:00
Richard Schreiber
40bd66cb86 Fix PayPal2 payment patch request (#5678) 2025-12-02 13:14:12 +01:00
Raphael Michel
bdd94b1f8a Add prioritization to webhook/notifications queue (#5513)
* Add prioritization to webhook/notifications queue

* Add missing code

* Missing license header

* Fix argument

* Use redis pipeline

* Update license header
2025-12-02 09:13:01 +01:00
José Manuel Silva
1c907f6a6f Translations: Update Portuguese (Portugal)
Currently translated at 83.1% (5133 of 6172 strings)

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

powered by weblate
2025-12-01 13:49:40 +01:00
José Manuel Silva
39e3ed9c25 Translations: Update Portuguese (Portugal)
Currently translated at 83.2% (5136 of 6172 strings)

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

powered by weblate
2025-12-01 13:49:40 +01:00
Richard Schreiber
4b5711253e Fix display_add_to_cart for variations 2025-12-01 13:48:02 +01:00
Raphael Michel
bd554c7c29 Update remaining icon files 2025-12-01 13:41:06 +01:00
Raphael Michel
2261951b15 Peppol: Live ID validation (#5602)
* Peppol: Live ID validation

* Always check both systems

* Simplify logic
2025-11-27 19:50:53 +01:00
Raphael Michel
0f82e1cae6 Update pretix logo to new version (#5651)
* Update pretix logo to new version

* Make favicon transparent

* Update src/pretix/static/pretixcontrol/scss/main.scss

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Update src/pretix/static/pretixcontrol/scss/main.scss

Co-authored-by: Richard Schreiber <schreiber@rami.io>

---------

Co-authored-by: Richard Schreiber <schreiber@rami.io>
2025-11-27 16:05:30 +01:00
dependabot[bot]
b0760157ce Update sentry-sdk requirement from ==2.45.* to ==2.46.*
Updates the requirements on [sentry-sdk](https://github.com/getsentry/sentry-python) to permit the latest version.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.45.0...2.46.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-version: 2.46.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-27 16:05:18 +01:00
dependabot[bot]
de2dec9089 Update pypdf requirement from ==6.3.* to ==6.4.* (#5659)
Updates the requirements on [pypdf](https://github.com/py-pdf/pypdf) to permit the latest version.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.3.0...6.4.0)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.4.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-27 16:05:04 +01:00
Raphael Michel
446c8e622b Bump version to 2025.11.0.dev0 2025-11-27 15:34:32 +01:00
129 changed files with 5089 additions and 2413 deletions

View File

@@ -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) 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 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 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. does not allow the computation as created by pretix.

View File

@@ -30,11 +30,12 @@ dependencies = [
"babel", "babel",
"BeautifulSoup4==4.14.*", "BeautifulSoup4==4.14.*",
"bleach==6.2.*", "bleach==6.2.*",
"celery==5.5.*", "celery==5.6.*",
"chardet==5.2.*", "chardet==5.2.*",
"cryptography>=44.0.0", "cryptography>=44.0.0",
"css-inline==0.18.*", "css-inline==0.18.*",
"defusedcsv>=1.1.0", "defusedcsv>=1.1.0",
"dnspython==2.*",
"Django[argon2]==4.2.*,>=4.2.26", "Django[argon2]==4.2.*,>=4.2.26",
"django-bootstrap3==25.2", "django-bootstrap3==25.2",
"django-compressor==4.5.1", "django-compressor==4.5.1",
@@ -61,7 +62,7 @@ dependencies = [
"importlib_metadata==8.*", # Polyfill, we can probably drop this once we require Python 3.10+ "importlib_metadata==8.*", # Polyfill, we can probably drop this once we require Python 3.10+
"isoweek", "isoweek",
"jsonschema", "jsonschema",
"kombu==5.5.*", "kombu==5.6.*",
"libsass==0.23.*", "libsass==0.23.*",
"lxml", "lxml",
"markdown==3.9", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3. "markdown==3.9", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
@@ -81,7 +82,7 @@ dependencies = [
"pycountry", "pycountry",
"pycparser==2.23", "pycparser==2.23",
"pycryptodome==3.23.*", "pycryptodome==3.23.*",
"pypdf==6.3.*", "pypdf==6.4.*",
"python-bidi==0.6.*", # Support for Arabic in reportlab "python-bidi==0.6.*", # Support for Arabic in reportlab
"python-dateutil==2.9.*", "python-dateutil==2.9.*",
"pytz", "pytz",
@@ -91,14 +92,13 @@ dependencies = [
"redis==6.4.*", "redis==6.4.*",
"reportlab==4.4.*", "reportlab==4.4.*",
"requests==2.32.*", "requests==2.32.*",
"sentry-sdk==2.45.*", "sentry-sdk==2.47.*",
"sepaxml==2.7.*", "sepaxml==2.7.*",
"stripe==7.9.*", "stripe==7.9.*",
"text-unidecode==1.*", "text-unidecode==1.*",
"tlds>=2020041600", "tlds>=2020041600",
"tqdm==4.*", "tqdm==4.*",
"ua-parser==1.0.*", "ua-parser==1.0.*",
"vat_moss_forked==2020.3.20.0.11.0",
"vobject==0.9.*", "vobject==0.9.*",
"webauthn==2.7.*", "webauthn==2.7.*",
"zeep==4.3.*" "zeep==4.3.*"

View File

@@ -19,4 +19,4 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see # 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/>. # <https://www.gnu.org/licenses/>.
# #
__version__ = "2025.10.0" __version__ = "2025.11.0.dev0"

View File

@@ -795,6 +795,7 @@ class EventSettingsSerializer(SettingsSerializer):
'invoice_address_asked', 'invoice_address_asked',
'invoice_address_required', 'invoice_address_required',
'invoice_address_vatid', 'invoice_address_vatid',
'invoice_address_vatid_required_countries',
'invoice_address_company_required', 'invoice_address_company_required',
'invoice_address_beneficiary', 'invoice_address_beneficiary',
'invoice_address_custom_field', 'invoice_address_custom_field',
@@ -943,6 +944,7 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
'invoice_address_asked', 'invoice_address_asked',
'invoice_address_required', 'invoice_address_required',
'invoice_address_vatid', 'invoice_address_vatid',
'invoice_address_vatid_required_countries',
'invoice_address_company_required', 'invoice_address_company_required',
'invoice_address_beneficiary', 'invoice_address_beneficiary',
'invoice_address_custom_field', 'invoice_address_custom_field',

View File

@@ -567,7 +567,7 @@ class QuotaViewSet(ConditionalListView, viewsets.ModelViewSet):
write_permission = 'can_change_items' write_permission = 'can_change_items'
def get_queryset(self): 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): def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset()).distinct() queryset = self.filter_queryset(self.get_queryset()).distinct()

View File

@@ -721,7 +721,7 @@ class MembershipViewSet(viewsets.ModelViewSet):
def get_queryset(self): def get_queryset(self):
return Membership.objects.filter( return Membership.objects.filter(
customer__organizer=self.request.organizer customer__organizer=self.request.organizer
) ).select_related('customer')
def get_serializer_context(self): def get_serializer_context(self):
ctx = super().get_serializer_context() ctx = super().get_serializer_context()

View File

@@ -19,6 +19,7 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see # 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/>. # <https://www.gnu.org/licenses/>.
# #
from django.db import transaction from django.db import transaction
from django.db.models import F, Q from django.db.models import F, Q
from django.utils.timezone import now from django.utils.timezone import now
@@ -64,8 +65,13 @@ class VoucherViewSet(viewsets.ModelViewSet):
permission = 'can_view_vouchers' permission = 'can_view_vouchers'
write_permission = 'can_change_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): 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() @transaction.atomic()
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):

View File

@@ -43,6 +43,7 @@ from pretix.base.services.tasks import ProfiledTask, TransactionAwareTask
from pretix.base.signals import periodic_task from pretix.base.signals import periodic_task
from pretix.celery_app import app from pretix.celery_app import app
from pretix.helpers import OF_SELF from pretix.helpers import OF_SELF
from pretix.helpers.celery import get_task_priority
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_ALL_EVENTS = None _ALL_EVENTS = None
@@ -474,7 +475,10 @@ def notify_webhooks(logentry_ids: list):
) )
for wh in webhooks: for wh in webhooks:
send_webhook.apply_async(args=(logentry.id, notification_type.action_type, wh.pk)) send_webhook.apply_async(
args=(logentry.id, notification_type.action_type, wh.pk),
priority=get_task_priority("notifications", logentry.organizer_id),
)
@app.task(base=ProfiledTask, bind=True, max_retries=5, default_retry_delay=60, acks_late=True, autoretry_for=(DatabaseError,),) @app.task(base=ProfiledTask, bind=True, max_retries=5, default_retry_delay=60, acks_late=True, autoretry_for=(DatabaseError,),)

View File

@@ -90,6 +90,7 @@ StaticMapping = namedtuple('StaticMapping', ('id', 'pretix_model', 'external_obj
class OutboundSyncProvider: class OutboundSyncProvider:
max_attempts = 5 max_attempts = 5
list_field_joiner = "," # set to None to keep native lists in properties
def __init__(self, event): def __init__(self, event):
self.event = event self.event = event
@@ -281,7 +282,8 @@ class OutboundSyncProvider:
'Please update value mapping for field "{field_name}" - option "{val}" not assigned' 'Please update value mapping for field "{field_name}" - option "{val}" not assigned'
).format(field_name=key, val=val)]) ).format(field_name=key, val=val)])
val = ",".join(val) if self.list_field_joiner:
val = self.list_field_joiner.join(val)
return val return val
def get_properties(self, inputs: dict, property_mappings: List[dict]): def get_properties(self, inputs: dict, property_mappings: List[dict]):

View File

@@ -71,15 +71,20 @@ def assign_properties(
return out return out
def _add_to_list(out, field_name, current_value, new_item, list_sep): def _add_to_list(out, field_name, current_value, new_item_input, list_sep):
new_item = str(new_item)
if list_sep is not None: if list_sep is not None:
new_item = new_item.replace(list_sep, "") new_items = str(new_item_input).split(list_sep)
current_value = current_value.split(list_sep) if current_value else [] current_value = current_value.split(list_sep) if current_value else []
elif not isinstance(current_value, (list, tuple)): else:
current_value = [str(current_value)] new_items = [str(new_item_input)]
if new_item not in current_value: if not isinstance(current_value, (list, tuple)):
new_list = current_value + [new_item] current_value = [str(current_value)]
new_list = list(current_value)
for new_item in new_items:
if new_item not in current_value:
new_list.append(new_item)
if new_list != current_value:
if list_sep is not None: if list_sep is not None:
new_list = list_sep.join(new_list) new_list = list_sep.join(new_list)
out[field_name] = new_list out[field_name] = new_list

View File

@@ -83,7 +83,7 @@ from pretix.base.invoicing.transmission import (
from pretix.base.models import InvoiceAddress, Item, Question, QuestionOption from pretix.base.models import InvoiceAddress, Item, Question, QuestionOption
from pretix.base.models.tax import ask_for_vat_id from pretix.base.models.tax import ask_for_vat_id
from pretix.base.services.tax import ( from pretix.base.services.tax import (
VATIDFinalError, VATIDTemporaryError, validate_vat_id, VATIDFinalError, VATIDTemporaryError, normalize_vat_id, validate_vat_id,
) )
from pretix.base.settings import ( from pretix.base.settings import (
COUNTRIES_WITH_STATE_IN_ADDRESS, COUNTRY_STATE_LABEL, COUNTRIES_WITH_STATE_IN_ADDRESS, COUNTRY_STATE_LABEL,
@@ -1165,13 +1165,11 @@ class BaseInvoiceAddressForm(forms.ModelForm):
self.fields['vat_id'].help_text = '<br/>'.join([ self.fields['vat_id'].help_text = '<br/>'.join([
str(_('Optional, but depending on the country you reside in we might need to charge you ' str(_('Optional, but depending on the country you reside in we might need to charge you '
'additional taxes if you do not enter it.')), 'additional taxes if you do not enter it.')),
str(_('If you are registered in Switzerland, you can enter your UID instead.')),
]) ])
else: else:
self.fields['vat_id'].help_text = '<br/>'.join([ self.fields['vat_id'].help_text = '<br/>'.join([
str(_('Optional, but it might be required for you to claim tax benefits on your invoice ' str(_('Optional, but it might be required for you to claim tax benefits on your invoice '
'depending on your and the sellers country of residence.')), 'depending on your and the sellers country of residence.')),
str(_('If you are registered in Switzerland, you can enter your UID instead.')),
]) ])
transmission_type_choices = [ transmission_type_choices = [
@@ -1358,13 +1356,24 @@ class BaseInvoiceAddressForm(forms.ModelForm):
"transmission method.")} "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: if self.validate_vat_id and self.instance.vat_id_validated and 'vat_id' not in self.changed_data:
pass pass # Skip re-validation if it is validated
elif self.validate_vat_id and data.get('is_business') and ask_for_vat_id(data.get('country')) and data.get('vat_id'): elif self.validate_vat_id and vat_id_applicable:
try: try:
normalized_id = validate_vat_id(data.get('vat_id'), str(data.get('country'))) normalized_id = validate_vat_id(data.get('vat_id'), str(data.get('country')))
self.instance.vat_id_validated = True 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: except VATIDFinalError as e:
if self.all_optional: if self.all_optional:
self.instance.vat_id_validated = False self.instance.vat_id_validated = False
@@ -1372,6 +1381,9 @@ class BaseInvoiceAddressForm(forms.ModelForm):
else: else:
raise ValidationError({"vat_id": e.message}) raise ValidationError({"vat_id": e.message})
except VATIDTemporaryError as e: 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 self.instance.vat_id_validated = False
if self.request and self.vat_warning: if self.request and self.vat_warning:
messages.warning(self.request, e.message) messages.warning(self.request, e.message)

View File

@@ -32,7 +32,6 @@ from itertools import groupby
from typing import Tuple from typing import Tuple
import bleach import bleach
import vat_moss.exchange_rates
from bidi import get_display from bidi import get_display
from django.contrib.staticfiles import finders from django.contrib.staticfiles import finders
from django.db.models import Sum from django.db.models import Sum
@@ -1059,7 +1058,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
def fmt(val): def fmt(val):
try: try:
return vat_moss.exchange_rates.format(val, self.invoice.foreign_currency_display) return money_filter(val, self.invoice.foreign_currency_display)
except ValueError: except ValueError:
return localize(val) + ' ' + self.invoice.foreign_currency_display return localize(val) + ' ' + self.invoice.foreign_currency_display

View File

@@ -19,8 +19,11 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see # 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/>. # <https://www.gnu.org/licenses/>.
# #
import base64
import hashlib
import re import re
import dns.resolver
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _, pgettext from django.utils.translation import gettext_lazy as _, pgettext
@@ -123,6 +126,9 @@ class PeppolIdValidator:
"9959": ".*", "9959": ".*",
} }
def __init__(self, validate_online=False):
self.validate_online = validate_online
def __call__(self, value): def __call__(self, value):
if ":" not in value: if ":" not in value:
raise ValidationError(_("A Peppol participant ID always starts with a prefix, followed by a colon (:).")) raise ValidationError(_("A Peppol participant ID always starts with a prefix, followed by a colon (:)."))
@@ -136,6 +142,28 @@ class PeppolIdValidator:
raise ValidationError(_("The Peppol participant ID does not match the validation rules for the prefix " raise ValidationError(_("The Peppol participant ID does not match the validation rules for the prefix "
"%(number)s. Please reach out to us if you are sure this ID is correct."), "%(number)s. Please reach out to us if you are sure this ID is correct."),
params={"number": prefix}) params={"number": prefix})
if self.validate_online:
base_hostnames = ['edelivery.tech.ec.europa.eu', 'acc.edelivery.tech.ec.europa.eu']
smp_id = base64.b32encode(hashlib.sha256(value.lower().encode()).digest()).decode().rstrip("=")
for base_hostname in base_hostnames:
smp_domain = f'{smp_id}.iso6523-actorid-upis.{base_hostname}'
resolver = dns.resolver.Resolver()
try:
answers = resolver.resolve(smp_domain, 'NAPTR', lifetime=1.0)
if answers:
return value
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
# ID not registered, do not set found=True
pass
except Exception: # noqa
# Error likely on our end or infrastructure is down, allow user to proceed
return value
raise ValidationError(
_("The Peppol participant ID is not registered on the Peppol network."),
)
return value return value
@@ -155,7 +183,9 @@ class PeppolTransmissionType(TransmissionType):
"transmission_peppol_participant_id": forms.CharField( "transmission_peppol_participant_id": forms.CharField(
label=_("Peppol participant ID"), label=_("Peppol participant ID"),
validators=[ validators=[
PeppolIdValidator(), PeppolIdValidator(
validate_online=True,
),
] ]
), ),
} }

View File

@@ -47,6 +47,19 @@ class DataImportError(LazyLocaleException):
super().__init__(msg) 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): def parse_csv(file, length=None, mode="strict", charset=None):
file.seek(0) file.seek(0)
data = file.read(length) data = file.read(length)
@@ -70,6 +83,7 @@ def parse_csv(file, length=None, mode="strict", charset=None):
return None return None
reader = csv.DictReader(io.StringIO(data), dialect=dialect) reader = csv.DictReader(io.StringIO(data), dialect=dialect)
reader._had_duplicates = rename_duplicates(reader.fieldnames)
return reader return reader

View File

@@ -53,7 +53,6 @@ from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_otp.models import Device from django_otp.models import Device
from django_scopes import scopes_disabled from django_scopes import scopes_disabled
from webauthn.helpers.structs import PublicKeyCredentialDescriptor
from pretix.base.i18n import language from pretix.base.i18n import language
from pretix.helpers.urls import build_absolute_uri from pretix.helpers.urls import build_absolute_uri
@@ -708,6 +707,8 @@ class U2FDevice(Device):
@property @property
def webauthndevice(self): def webauthndevice(self):
from webauthn.helpers.structs import PublicKeyCredentialDescriptor
d = json.loads(self.json_data) d = json.loads(self.json_data)
return PublicKeyCredentialDescriptor(websafe_decode(d['keyHandle'])) return PublicKeyCredentialDescriptor(websafe_decode(d['keyHandle']))
@@ -737,6 +738,8 @@ class WebAuthnDevice(Device):
@property @property
def webauthndevice(self): def webauthndevice(self):
from webauthn.helpers.structs import PublicKeyCredentialDescriptor
return PublicKeyCredentialDescriptor(websafe_decode(self.credential_id)) return PublicKeyCredentialDescriptor(websafe_decode(self.credential_id))
@property @property

View File

@@ -31,6 +31,7 @@ from django.urls import reverse
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.functional import cached_property from django.utils.functional import cached_property
from pretix.helpers.celery import get_task_priority
from pretix.helpers.json import CustomJSONEncoder from pretix.helpers.json import CustomJSONEncoder
@@ -131,9 +132,15 @@ class LoggingMixin:
logentry.save() logentry.save()
if logentry.notification_type: if logentry.notification_type:
notify.apply_async(args=(logentry.pk,)) notify.apply_async(
args=(logentry.pk,),
priority=get_task_priority("notifications", logentry.organizer_id),
)
if logentry.webhook_type: if logentry.webhook_type:
notify_webhooks.apply_async(args=(logentry.pk,)) notify_webhooks.apply_async(
args=(logentry.pk,),
priority=get_task_priority("notifications", logentry.organizer_id),
)
return logentry return logentry

View File

@@ -35,11 +35,14 @@
import json import json
import logging import logging
from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db import connections, models from django.db import connections, models
from django.utils.functional import cached_property from django.utils.functional import cached_property
from pretix.helpers.celery import get_task_priority
class VisibleOnlyManager(models.Manager): class VisibleOnlyManager(models.Manager):
def get_queryset(self): def get_queryset(self):
@@ -186,7 +189,19 @@ class LogEntry(models.Model):
to_notify = [o.id for o in objects if o.notification_type] to_notify = [o.id for o in objects if o.notification_type]
if to_notify: if to_notify:
notify.apply_async(args=(to_notify,)) organizer_ids = set(o.organizer_id for o in objects if o.notification_type)
notify.apply_async(
args=(to_notify,),
priority=settings.PRIORITY_CELERY_HIGHEST_FUNC(
get_task_priority("notifications", oid) for oid in organizer_ids
),
)
to_wh = [o.id for o in objects if o.webhook_type] to_wh = [o.id for o in objects if o.webhook_type]
if to_wh: if to_wh:
notify_webhooks.apply_async(args=(to_wh,)) organizer_ids = set(o.organizer_id for o in objects if o.webhook_type)
notify_webhooks.apply_async(
args=(to_wh,),
priority=settings.PRIORITY_CELERY_HIGHEST_FUNC(
get_task_priority("notifications", oid) for oid in organizer_ids
),
)

View File

@@ -3526,11 +3526,30 @@ class InvoiceAddress(models.Model):
}) })
return d return d
def describe_transmission(self): def describe_transmission(self, event=None):
# we only need an explicit event if the order is not yet created and we do not want to show unnecessary data to customers
from pretix.base.invoicing.transmission import transmission_types from pretix.base.invoicing.transmission import transmission_types
data = [] data = []
t, __ = transmission_types.get(identifier=self.transmission_type) t, m = transmission_types.get(identifier=self.transmission_type)
d_event = self.order.event if self.order else event
# reusing hack from questions.py for default transmission -- this should at least be fast
if d_event:
if (
t.identifier == "email" and
m in ("transmission_email_other", "transmission_email_address") and
(
d_event.settings.invoice_generate == "False" or
not d_event.settings.invoice_email_attachment
)
):
return data
# we also don't show transmission data if we never showed the corresponding form fields
if not t.invoice_address_form_fields_visible(country=self.country, is_business=self.is_business):
return data
data.append((_("Transmission type"), t.public_name)) data.append((_("Transmission type"), t.public_name))
form_data = t.transmission_info_to_form_data(self.transmission_info or {}) form_data = t.transmission_info_to_form_data(self.transmission_info or {})
for k, f in t.invoice_address_form_fields.items(): for k, f in t.invoice_address_form_fields.items():

View File

@@ -22,7 +22,6 @@
import json import json
from collections import namedtuple from collections import namedtuple
import jsonschema
from django.contrib.staticfiles import finders from django.contrib.staticfiles import finders
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
@@ -38,6 +37,8 @@ from pretix.base.models import Event, Item, LoggedModel, Organizer, SubEvent
@deconstructible @deconstructible
class SeatingPlanLayoutValidator: class SeatingPlanLayoutValidator:
def __call__(self, value): def __call__(self, value):
import jsonschema
if not isinstance(value, dict): if not isinstance(value, dict):
try: try:
val = json.loads(value) val = json.loads(value)

View File

@@ -23,7 +23,6 @@ import json
from decimal import Decimal from decimal import Decimal
from typing import Optional from typing import Optional
import jsonschema
from django.contrib.staticfiles import finders from django.contrib.staticfiles import finders
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
@@ -298,6 +297,8 @@ def cc_to_vat_prefix(country_code):
@deconstructible @deconstructible
class CustomRulesValidator: class CustomRulesValidator:
def __call__(self, value): def __call__(self, value):
import jsonschema
if not isinstance(value, dict): if not isinstance(value, dict):
try: try:
val = json.loads(value) val = json.loads(value)

View File

@@ -623,7 +623,7 @@ class Voucher(LoggedModel):
return max(1, self.min_usages - self.redeemed) return max(1, self.min_usages - self.redeemed)
@classmethod @classmethod
def annotate_budget_used_orders(cls, qs): def annotate_budget_used(cls, qs):
opq = OrderPosition.objects.filter( opq = OrderPosition.objects.filter(
voucher_id=OuterRef('pk'), voucher_id=OuterRef('pk'),
voucher_budget_use__isnull=False, voucher_budget_use__isnull=False,
@@ -632,7 +632,7 @@ class Voucher(LoggedModel):
Order.STATUS_PENDING Order.STATUS_PENDING
] ]
).order_by().values('voucher_id').annotate(s=Sum('voucher_budget_use')).values('s') ).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): def budget_used(self):
ops = OrderPosition.objects.filter( ops = OrderPosition.objects.filter(

View File

@@ -47,7 +47,6 @@ from collections import OrderedDict, defaultdict
from functools import partial from functools import partial
from io import BytesIO from io import BytesIO
import jsonschema
import pypdf import pypdf
import pypdf.generic import pypdf.generic
import reportlab.rl_config import reportlab.rl_config
@@ -1311,6 +1310,8 @@ def _correct_page_media_box(page: pypdf.PageObject):
@deconstructible @deconstructible
class PdfLayoutValidator: class PdfLayoutValidator:
def __call__(self, value): def __call__(self, value):
import jsonschema
if not isinstance(value, dict): if not isinstance(value, dict):
try: try:
val = json.loads(value) val = json.loads(value)

View File

@@ -1361,6 +1361,11 @@ class CartManager:
deleted_positions.add(op.position.pk) deleted_positions.add(op.position.pk)
addons.delete() addons.delete()
op.position.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: else:
raise AssertionError("ExtendOperation cannot affect more than one item") raise AssertionError("ExtendOperation cannot affect more than one item")
elif isinstance(op, self.VoucherOperation): elif isinstance(op, self.VoucherOperation):

View File

@@ -47,7 +47,6 @@ from urllib.parse import urljoin, urlparse
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
import requests import requests
from bs4 import BeautifulSoup
from celery import chain from celery import chain
from celery.exceptions import MaxRetriesExceededError from celery.exceptions import MaxRetriesExceededError
from django.conf import settings from django.conf import settings
@@ -764,6 +763,8 @@ def render_mail(template, context, placeholder_mode=SafeFormatter.MODE_RICH_TO_P
def replace_images_with_cid_paths(body_html): def replace_images_with_cid_paths(body_html):
from bs4 import BeautifulSoup
if body_html: if body_html:
email = BeautifulSoup(body_html, "lxml") email = BeautifulSoup(body_html, "lxml")
cid_images = [] cid_images = []

View File

@@ -32,6 +32,7 @@ from pretix.base.services.mail import mail_send_task
from pretix.base.services.tasks import ProfiledTask, TransactionAwareTask from pretix.base.services.tasks import ProfiledTask, TransactionAwareTask
from pretix.base.signals import notification from pretix.base.signals import notification
from pretix.celery_app import app from pretix.celery_app import app
from pretix.helpers.celery import get_task_priority
from pretix.helpers.urls import build_absolute_uri from pretix.helpers.urls import build_absolute_uri
@@ -88,12 +89,18 @@ def notify(logentry_ids: list):
for um, enabled in notify_specific.items(): for um, enabled in notify_specific.items():
user, method = um user, method = um
if enabled: if enabled:
send_notification.apply_async(args=(logentry.id, notification_type.action_type, user.pk, method)) send_notification.apply_async(
args=(logentry.id, notification_type.action_type, user.pk, method),
priority=get_task_priority("notifications", logentry.organizer_id),
)
for um, enabled in notify_global.items(): for um, enabled in notify_global.items():
user, method = um user, method = um
if enabled and um not in notify_specific: if enabled and um not in notify_specific:
send_notification.apply_async(args=(logentry.id, notification_type.action_type, user.pk, method)) send_notification.apply_async(
args=(logentry.id, notification_type.action_type, user.pk, method),
priority=get_task_priority("notifications", logentry.organizer_id),
)
notification.send(logentry.event, logentry_id=logentry.id, notification_type=notification_type.action_type) notification.send(logentry.event, logentry_id=logentry.id, notification_type=notification_type.action_type)

View File

@@ -27,7 +27,6 @@ from decimal import Decimal
from xml.etree import ElementTree from xml.etree import ElementTree
import requests import requests
import vat_moss.id
from django.conf import settings from django.conf import settings
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from zeep import Client, Transport from zeep import Client, Transport
@@ -42,14 +41,142 @@ logger = logging.getLogger(__name__)
error_messages = { error_messages = {
'unavailable': _( 'unavailable': _(
'Your VAT ID could not be checked, as the VAT checking service of ' 'Your VAT ID could not be checked, as the VAT checking service of '
'your country is currently not available. We will therefore ' 'your country is currently not available. We will therefore need to '
'need to charge VAT on your invoice. You can get the tax amount ' 'charge you the same tax rate as if you did not enter a VAT ID.'
'back via the VAT reimbursement process.'
), ),
'invalid': _('This VAT ID is not valid. Please re-check your input.'), 'invalid': _('This VAT ID is not valid. Please re-check your input.'),
'country_mismatch': _('Your VAT ID does not match the selected country.'), '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): class VATIDError(Exception):
def __init__(self, message): def __init__(self, message):
@@ -64,13 +191,57 @@ class VATIDTemporaryError(VATIDError):
pass 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): def _validate_vat_id_NO(vat_id, country_code):
# Inspired by vat_moss library # 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: try:
vat_id = vat_moss.id.normalize(vat_id) vat_id = normalize_vat_id(vat_id, country_code)
except ValueError: except ValueError:
raise VATIDFinalError(error_messages['invalid']) 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): def _validate_vat_id_EU(vat_id, country_code):
# Inspired by vat_moss library # Inspired by vat_moss library
try: try:
vat_id = vat_moss.id.normalize(vat_id) vat_id = normalize_vat_id(vat_id, country_code)
except ValueError: except ValueError:
raise VATIDFinalError(error_messages['invalid']) raise VATIDFinalError(error_messages['invalid'])
@@ -112,11 +283,10 @@ def _validate_vat_id_EU(vat_id, country_code):
raise VATIDFinalError(error_messages['invalid']) raise VATIDFinalError(error_messages['invalid'])
number = vat_id[2:] number = vat_id[2:]
if vat_id[:2] != cc_to_vat_prefix(country_code): if vat_id[:2] != cc_to_vat_prefix(country_code):
raise VATIDFinalError(error_messages['country_mismatch']) 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']) raise VATIDFinalError(error_messages['invalid'])
# We are relying on the country code of the normalized VAT-ID and not the user/InvoiceAddress-provided # 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): def _validate_vat_id_CH(vat_id, country_code):
if vat_id[:3] != 'CHE': 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: try:
transport = Transport( transport = Transport(
cache=SqliteCache(os.path.join(settings.CACHE_DIR, "validate_vat_id_ch_zeep_cache.db")), cache=SqliteCache(os.path.join(settings.CACHE_DIR, "validate_vat_id_ch_zeep_cache.db")),

View File

@@ -113,6 +113,11 @@ def assign_automatically(event: Event, user_id: int=None, subevent_id: int=None)
lock_objects(quotas, shared_lock_objects=[event]) lock_objects(quotas, shared_lock_objects=[event])
for wle in qs: 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: if (wle.item_id, wle.variation_id, wle.subevent_id) in gone:
continue continue
ev = (wle.subevent or event) ev = (wle.subevent or event)

View File

@@ -629,13 +629,40 @@ DEFAULTS = {
'form_kwargs': dict( 'form_kwargs': dict(
label=_("Ask for VAT ID"), label=_("Ask for VAT ID"),
help_text=format_lazy( help_text=format_lazy(
_("Only works if an invoice address is asked for. VAT ID is never required and only requested from " _("Only works if an invoice address is asked for. VAT ID is only requested from business customers "
"business customers in the following countries: {countries}"), "in the following countries: {countries}."),
countries=lazy(lambda *args: ', '.join(sorted(gettext(Country(cc).name) for cc in VAT_ID_COUNTRIES)), str)() 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'}), 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': { 'invoice_address_explanation_text': {
'default': '', 'default': '',
'type': LazyI18nString, 'type': LazyI18nString,

View File

@@ -0,0 +1,65 @@
#
# 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 datetime
from django import template
from django.utils.html import format_html
from django.utils.timezone import get_current_timezone
from pretix.base.i18n import LazyExpiresDate
from pretix.helpers.templatetags.date_fast import date_fast
register = template.Library()
@register.simple_tag
def html_time(value: datetime, dt_format: str = "SHORT_DATE_FORMAT", **kwargs):
"""
Building a <time datetime='{html-datetime}'>{human-readable datetime}</time> html string,
where the html-datetime as well as the human-readable datetime can be set
to a value from django's FORMAT_SETTINGS or "format_expires".
If attr_fmt isnt provided, it will be set to isoformat.
Usage example:
{% html_time event_start "SHORT_DATETIME_FORMAT" %}
or
{% html_time event_start "TIME_FORMAT" attr_fmt="H:i" %}
"""
if value in (None, ''):
return ''
value = value.astimezone(get_current_timezone())
attr_fmt = kwargs["attr_fmt"] if kwargs else None
try:
if not attr_fmt:
date_html = value.isoformat()
else:
date_html = date_fast(value, attr_fmt)
if dt_format == "format_expires":
date_human = LazyExpiresDate(value)
else:
date_human = date_fast(value, dt_format)
return format_html("<time datetime='{}'>{}</time>", date_html, date_human)
except AttributeError:
return ''

View File

@@ -93,7 +93,9 @@ def timeline_for_event(event, subevent=None):
description=format_lazy( description=format_lazy(
'{} ({})', '{} ({})',
pgettext_lazy('timeline', 'End of ticket sales'), pgettext_lazy('timeline', 'End of ticket sales'),
pgettext_lazy('timeline', 'automatically because the event is over and no end of presale has been configured') if not ev.presale_end else "" pgettext_lazy('timeline', 'automatically because the event is over and no end of presale has been configured')
) if not ev.presale_end else (
pgettext_lazy('timeline', 'End of ticket sales')
), ),
edit_url=ev_edit_url + '#id_presale_end_0' edit_url=ev_edit_url + '#id_presale_end_0'
)) ))

View File

@@ -22,7 +22,7 @@
import pycountry import pycountry
from django.http import JsonResponse from django.http import JsonResponse
from django.shortcuts import get_object_or_404 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_countries.fields import Country
from django_scopes import scope from django_scopes import scope
@@ -36,6 +36,22 @@ from pretix.base.settings import (
COUNTRIES_WITH_STATE_IN_ADDRESS, COUNTRY_STATE_LABEL, 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): def _info(cc):
info = { info = {
@@ -47,7 +63,12 @@ def _info(cc):
'required': 'if_any' if cc in COUNTRIES_WITH_STATE_IN_ADDRESS else False, 'required': 'if_any' if cc in COUNTRIES_WITH_STATE_IN_ADDRESS else False,
'label': COUNTRY_STATE_LABEL.get(cc, pgettext('address', 'State')), '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: if cc not in COUNTRIES_WITH_STATE_IN_ADDRESS:
return {'data': [], **info} return {'data': [], **info}
@@ -124,4 +145,10 @@ def address_form(request):
"required": transmission_type.identifier == selected_transmission_type and k in required "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) return JsonResponse(info)

View File

@@ -207,6 +207,7 @@ class EventWizardBasicsForm(I18nModelForm):
'Sample Conference Center\nHeidelberg, Germany' 'Sample Conference Center\nHeidelberg, Germany'
) )
self.fields['slug'].widget.prefix = build_absolute_uri(self.organizer, 'presale:organizer.index') self.fields['slug'].widget.prefix = build_absolute_uri(self.organizer, 'presale:organizer.index')
self.fields['tax_rate']._required = True # Do not render as optional because it is conditionally required
if self.has_subevents: if self.has_subevents:
del self.fields['presale_start'] del self.fields['presale_start']
del self.fields['presale_end'] del self.fields['presale_end']
@@ -927,6 +928,7 @@ class InvoiceSettingsForm(EventSettingsValidationMixin, SettingsForm):
'invoice_address_asked', 'invoice_address_asked',
'invoice_address_required', 'invoice_address_required',
'invoice_address_vatid', 'invoice_address_vatid',
'invoice_address_vatid_required_countries',
'invoice_address_company_required', 'invoice_address_company_required',
'invoice_address_beneficiary', 'invoice_address_beneficiary',
'invoice_address_custom_field', 'invoice_address_custom_field',
@@ -1957,6 +1959,13 @@ class EventFooterLinkForm(I18nModelForm):
class Meta: class Meta:
model = EventFooterLink model = EventFooterLink
fields = ('label', 'url') fields = ('label', 'url')
widgets = {
"url": forms.URLInput(
attrs={
"placeholder": "https://..."
}
)
}
class BaseEventFooterLinkFormSet(I18nFormSetMixin, forms.BaseInlineFormSet): class BaseEventFooterLinkFormSet(I18nFormSetMixin, forms.BaseInlineFormSet):

View File

@@ -61,6 +61,10 @@ from pretix.base.models import (
SubEvent, SubEventMetaValue, Team, TeamAPIToken, TeamInvite, Voucher, SubEvent, SubEventMetaValue, Team, TeamAPIToken, TeamInvite, Voucher,
) )
from pretix.base.signals import register_payment_providers from pretix.base.signals import register_payment_providers
from pretix.base.timeframes import (
DateFrameField,
resolve_timeframe_to_datetime_start_inclusive_end_exclusive,
)
from pretix.control.forms import SplitDateTimeField from pretix.control.forms import SplitDateTimeField
from pretix.control.forms.widgets import Select2, Select2ItemVarQuota from pretix.control.forms.widgets import Select2, Select2ItemVarQuota
from pretix.control.signals import order_search_filter_q from pretix.control.signals import order_search_filter_q
@@ -1219,6 +1223,129 @@ class OrderPaymentSearchFilterForm(forms.Form):
return qs return qs
class QuestionAnswerFilterForm(forms.Form):
STATUS_VARIANTS = [
("", _("All orders")),
(Order.STATUS_PAID, _("Paid")),
(Order.STATUS_PAID + 'v', _("Paid or confirmed")),
(Order.STATUS_PENDING, _("Pending")),
(Order.STATUS_PENDING + Order.STATUS_PAID, _("Pending or paid")),
("o", _("Pending (overdue)")),
(Order.STATUS_EXPIRED, _("Expired")),
(Order.STATUS_PENDING + Order.STATUS_EXPIRED, _("Pending or expired")),
(Order.STATUS_CANCELED, _("Canceled"))
]
status = forms.ChoiceField(
choices=STATUS_VARIANTS,
required=False,
label=_("Order status"),
)
item = forms.ChoiceField(
choices=[],
required=False,
label=_("Products"),
)
subevent = forms.ModelChoiceField(
queryset=SubEvent.objects.none(),
required=False,
empty_label=pgettext_lazy('subevent', 'All dates'),
label=pgettext_lazy("subevent", "Date"),
)
date_range = DateFrameField(
required=False,
include_future_frames=True,
label=_('Event date'),
)
def __init__(self, *args, **kwargs):
self.event = kwargs.pop('event')
super().__init__(*args, **kwargs)
self.initial['status'] = Order.STATUS_PENDING + Order.STATUS_PAID
choices = [('', _('All products'))]
for i in self.event.items.prefetch_related('variations').all():
variations = list(i.variations.all())
if variations:
choices.append((str(i.pk), _('{product} Any variation').format(product=str(i))))
for v in variations:
choices.append(('%d-%d' % (i.pk, v.pk), '%s %s' % (str(i), v.value)))
else:
choices.append((str(i.pk), str(i)))
self.fields['item'].choices = choices
if self.event.has_subevents:
self.fields["subevent"].queryset = self.event.subevents.all()
self.fields['subevent'].widget = Select2(
attrs={
'data-model-select2': 'event',
'data-select2-url': reverse('control:event.subevents.select2', kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
}),
'data-placeholder': pgettext_lazy('subevent', 'All dates')
}
)
self.fields['subevent'].widget.choices = self.fields['subevent'].choices
else:
del self.fields['subevent']
def clean(self):
cleaned_data = super().clean()
subevent = cleaned_data.get('subevent')
date_range = cleaned_data.get('date_range')
if subevent is not None and date_range is not None:
d_start, d_end = resolve_timeframe_to_datetime_start_inclusive_end_exclusive(now(), date_range, self.event.timezone)
if (
(d_start and not (d_start <= subevent.date_from)) or
(d_end and not (subevent.date_from < d_end))
):
self.add_error('subevent', pgettext_lazy('subevent', "Date doesn't start in selected date range."))
return cleaned_data
def filter_qs(self, opqs):
fdata = self.cleaned_data
subevent = fdata.get('subevent', None)
date_range = fdata.get('date_range', None)
if subevent is not None:
opqs = opqs.filter(subevent=subevent)
if date_range is not None:
d_start, d_end = resolve_timeframe_to_datetime_start_inclusive_end_exclusive(now(), date_range, self.event.timezone)
opqs = opqs.filter(
subevent__date_from__gte=d_start,
subevent__date_from__lt=d_end
)
s = fdata.get("status", Order.STATUS_PENDING + Order.STATUS_PAID)
if s != "":
if s == Order.STATUS_PENDING:
opqs = opqs.filter(order__status=Order.STATUS_PENDING,
order__expires__lt=now().replace(hour=0, minute=0, second=0))
elif s == Order.STATUS_PENDING + Order.STATUS_PAID:
opqs = opqs.filter(order__status__in=[Order.STATUS_PENDING, Order.STATUS_PAID])
elif s == Order.STATUS_PAID + 'v':
opqs = opqs.filter(
Q(order__status=Order.STATUS_PAID) |
Q(order__status=Order.STATUS_PENDING, order__valid_if_pending=True)
)
elif s == Order.STATUS_PENDING + Order.STATUS_EXPIRED:
opqs = opqs.filter(order__status__in=[Order.STATUS_PENDING, Order.STATUS_EXPIRED])
else:
opqs = opqs.filter(order__status=s)
if s not in (Order.STATUS_CANCELED, ""):
opqs = opqs.filter(canceled=False)
if fdata.get("item", "") != "":
i = fdata.get("item", "")
opqs = opqs.filter(item_id__in=(i,))
return opqs
class SubEventFilterForm(FilterForm): class SubEventFilterForm(FilterForm):
orders = { orders = {
'date_from': 'date_from', 'date_from': 'date_from',

View File

@@ -974,7 +974,7 @@ class EventCancelForm(FormPlaceholderMixin, forms.Form):
self._set_field_placeholders('send_subject', ['event_or_subevent', 'refund_amount', 'position_or_address', self._set_field_placeholders('send_subject', ['event_or_subevent', 'refund_amount', 'position_or_address',
'order', 'event']) 'order', 'event'])
self._set_field_placeholders('send_message', ['event_or_subevent', 'refund_amount', 'position_or_address', self._set_field_placeholders('send_message', ['event_or_subevent', 'refund_amount', 'position_or_address',
'order', 'event']) 'order', 'event'], rich=True)
self.fields['send_waitinglist_subject'] = I18nFormField( self.fields['send_waitinglist_subject'] = I18nFormField(
label=_("Subject"), label=_("Subject"),
required=True, required=True,
@@ -998,7 +998,7 @@ class EventCancelForm(FormPlaceholderMixin, forms.Form):
)) ))
) )
self._set_field_placeholders('send_waitinglist_subject', ['event_or_subevent', 'event']) self._set_field_placeholders('send_waitinglist_subject', ['event_or_subevent', 'event'])
self._set_field_placeholders('send_waitinglist_message', ['event_or_subevent', 'event']) self._set_field_placeholders('send_waitinglist_message', ['event_or_subevent', 'event'], rich=True)
if self.event.has_subevents: if self.event.has_subevents:
self.fields['subevent'].queryset = self.event.subevents.all() self.fields['subevent'].queryset = self.event.subevents.all()

View File

@@ -1024,6 +1024,13 @@ class OrganizerFooterLinkForm(I18nModelForm):
class Meta: class Meta:
model = OrganizerFooterLink model = OrganizerFooterLink
fields = ('label', 'url') fields = ('label', 'url')
widgets = {
"url": forms.URLInput(
attrs={
"placeholder": "https://..."
}
)
}
class BaseOrganizerFooterLinkFormSet(I18nFormSetMixin, forms.BaseInlineFormSet): class BaseOrganizerFooterLinkFormSet(I18nFormSetMixin, forms.BaseInlineFormSet):

View File

@@ -126,7 +126,9 @@
{% endif %} {% endif %}
<a class="navbar-brand" href="{% url "control:index" %}"> <a class="navbar-brand" href="{% url "control:index" %}">
<img src="{% static "pretixbase/img/pretix-icon-white-mini.svg" %}" /> <img src="{% static "pretixbase/img/pretix-icon-white-mini.svg" %}" />
{{ settings.PRETIX_INSTANCE_NAME }} <span>
{{ settings.PRETIX_INSTANCE_NAME }}
</span>
</a> </a>
</div> </div>
<ul class="nav navbar-nav navbar-top-links navbar-left flip hidden-xs"> <ul class="nav navbar-nav navbar-top-links navbar-left flip hidden-xs">

View File

@@ -43,6 +43,7 @@
{% bootstrap_field form.invoice_name_required layout="control" %} {% bootstrap_field form.invoice_name_required layout="control" %}
{% bootstrap_field form.invoice_address_company_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 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_beneficiary layout="control" %}
{% bootstrap_field form.invoice_address_not_asked_free layout="control" %} {% bootstrap_field form.invoice_address_not_asked_free layout="control" %}
{% bootstrap_field form.invoice_address_custom_field layout="control" %} {% bootstrap_field form.invoice_address_custom_field layout="control" %}

View File

@@ -20,35 +20,20 @@
</div> </div>
<form class="panel-body filter-form" action="" method="get"> <form class="panel-body filter-form" action="" method="get">
<div class="row"> <div class="row">
<div class="col-lg-2 col-sm-6 col-xs-6"> <div class="col-md-2 col-xs-6">
<select name="status" class="form-control"> {% bootstrap_field form.status %}
<option value="" {% if request.GET.status == "" %}selected="selected"{% endif %}>{% trans "All orders" %}</option> </div>
<option value="p" {% if request.GET.status == "p" %}selected="selected"{% endif %}>{% trans "Paid" %}</option> <div class="col-md-3 col-xs-6">
<option value="pv" {% if request.GET.status == "pv" %}selected="selected"{% endif %}>{% trans "Paid or confirmed" %}</option> {% bootstrap_field form.item %}
<option value="n" {% if request.GET.status == "n" %}selected="selected"{% endif %}>{% trans "Pending" %}</option> </div>
<option value="np" {% if request.GET.status == "np" or "status" not in request.GET %}selected="selected"{% endif %}>{% trans "Pending or paid" %}</option> {% if has_subevents %}
<option value="o" {% if request.GET.status == "o" %}selected="selected"{% endif %}>{% trans "Pending (overdue)" %}</option> <div class="col-md-3 col-xs-6">
<option value="e" {% if request.GET.status == "e" %}selected="selected"{% endif %}>{% trans "Expired" %}</option> {% bootstrap_field form.subevent %}
<option value="ne" {% if request.GET.status == "ne" %}selected="selected"{% endif %}>{% trans "Pending or expired" %}</option>
<option value="c" {% if request.GET.status == "c" %}selected="selected"{% endif %}>{% trans "Canceled" %}</option>
</select>
</div> </div>
<div class="col-lg-5 col-sm-6 col-xs-6"> <div class="col-md-4 col-xs-6">
<select name="item" class="form-control"> {% bootstrap_field form.date_range %}
<option value="">{% trans "All products" %}</option>
{% for item in items %}
<option value="{{ item.id }}"
{% if request.GET.item|add:0 == item.id %}selected="selected"{% endif %}>
{{ item.name }}
</option>
{% endfor %}
</select>
</div> </div>
{% if request.event.has_subevents %} {% endif %}
<div class="col-lg-5 col-sm-6 col-xs-6">
{% include "pretixcontrol/event/fragment_subevent_choice_simple.html" %}
</div>
{% endif %}
</div> </div>
<div class="text-right"> <div class="text-right">
<button class="btn btn-primary btn-lg" type="submit"> <button class="btn btn-primary btn-lg" type="submit">

View File

@@ -165,7 +165,7 @@
{% if v.budget|default_if_none:"NONE" != "NONE" %} {% if v.budget|default_if_none:"NONE" != "NONE" %}
<br> <br>
<small class="text-muted"> <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> </small>
{% endif %} {% endif %}
</td> </td>

View File

@@ -60,7 +60,6 @@ from pretix.base.models import (
) )
from pretix.base.services.quotas import QuotaAvailability from pretix.base.services.quotas import QuotaAvailability
from pretix.base.timeline import timeline_for_event from pretix.base.timeline import timeline_for_event
from pretix.control.forms.event import CommentForm
from pretix.control.signals import ( from pretix.control.signals import (
event_dashboard_widgets, user_dashboard_widgets, event_dashboard_widgets, user_dashboard_widgets,
) )
@@ -341,6 +340,8 @@ def welcome_wizard_widget(sender, **kwargs):
def event_index(request, organizer, event): def event_index(request, organizer, event):
from pretix.control.forms.event import CommentForm
subevent = None subevent = None
if request.GET.get("subevent", "") != "" and request.event.has_subevents: if request.GET.get("subevent", "") != "" and request.event.has_subevents:
i = request.GET.get("subevent", "") i = request.GET.get("subevent", "")

View File

@@ -98,7 +98,6 @@ from pretix.control.views.mailsetup import MailSettingsSetupView
from pretix.control.views.user import RecentAuthenticationRequiredMixin from pretix.control.views.user import RecentAuthenticationRequiredMixin
from pretix.helpers.database import rolledback_transaction from pretix.helpers.database import rolledback_transaction
from pretix.multidomain.urlreverse import build_absolute_uri, get_event_domain from pretix.multidomain.urlreverse import build_absolute_uri, get_event_domain
from pretix.plugins.stripe.payment import StripeSettingsHolder
from pretix.presale.views.widget import ( from pretix.presale.views.widget import (
version_default as widget_version_default, version_default as widget_version_default,
) )
@@ -1666,6 +1665,8 @@ class QuickSetupView(FormView):
'or take your event live to start selling!')) 'or take your event live to start selling!'))
if form.cleaned_data.get('payment_stripe__enabled', False): if form.cleaned_data.get('payment_stripe__enabled', False):
from pretix.plugins.stripe.payment import StripeSettingsHolder
self.request.session['payment_stripe_oauth_enable'] = True self.request.session['payment_stripe_oauth_enable'] = True
return redirect(StripeSettingsHolder(self.request.event).get_connect_url(self.request)) return redirect(StripeSettingsHolder(self.request.event).get_connect_url(self.request))

View File

@@ -65,7 +65,7 @@ from pretix.api.serializers.item import (
) )
from pretix.base.forms import I18nFormSet from pretix.base.forms import I18nFormSet
from pretix.base.models import ( from pretix.base.models import (
CartPosition, Item, ItemCategory, ItemProgramTime, ItemVariation, Order, CartPosition, Item, ItemCategory, ItemProgramTime, ItemVariation,
OrderPosition, Question, QuestionAnswer, QuestionOption, Quota, OrderPosition, Question, QuestionAnswer, QuestionOption, Quota,
SeatCategoryMapping, Voucher, SeatCategoryMapping, Voucher,
) )
@@ -74,6 +74,7 @@ from pretix.base.models.items import ItemAddOn, ItemBundle, ItemMetaValue
from pretix.base.services.quotas import QuotaAvailability from pretix.base.services.quotas import QuotaAvailability
from pretix.base.services.tickets import invalidate_cache from pretix.base.services.tickets import invalidate_cache
from pretix.base.signals import quota_availability from pretix.base.signals import quota_availability
from pretix.control.forms.filter import QuestionAnswerFilterForm
from pretix.control.forms.item import ( from pretix.control.forms.item import (
CategoryForm, ItemAddOnForm, ItemAddOnsFormSet, ItemBundleForm, CategoryForm, ItemAddOnForm, ItemAddOnsFormSet, ItemBundleForm,
ItemBundleFormSet, ItemCreateForm, ItemMetaValueForm, ItemProgramTimeForm, ItemBundleFormSet, ItemCreateForm, ItemMetaValueForm, ItemProgramTimeForm,
@@ -660,46 +661,26 @@ class QuestionMixin:
return ctx return ctx
class QuestionView(EventPermissionRequiredMixin, QuestionMixin, ChartContainingView, DetailView): class QuestionView(EventPermissionRequiredMixin, ChartContainingView, DetailView):
model = Question model = Question
template_name = 'pretixcontrol/items/question.html' template_name = 'pretixcontrol/items/question.html'
permission = 'can_change_items' permission = 'can_change_items'
template_name_field = 'question' template_name_field = 'question'
@cached_property
def filter_form(self):
return QuestionAnswerFilterForm(event=self.request.event, data=self.request.GET)
def get_answer_statistics(self): def get_answer_statistics(self):
opqs = OrderPosition.objects.filter( opqs = OrderPosition.objects.filter(
order__event=self.request.event, order__event=self.request.event,
) )
if self.filter_form.is_valid():
opqs = self.filter_form.filter_qs(opqs)
qs = QuestionAnswer.objects.filter( qs = QuestionAnswer.objects.filter(
question=self.object, orderposition__isnull=False, question=self.object, orderposition__isnull=False,
) )
if self.request.GET.get("subevent", "") != "":
opqs = opqs.filter(subevent=self.request.GET["subevent"])
s = self.request.GET.get("status", "np")
if s != "":
if s == 'o':
opqs = opqs.filter(order__status=Order.STATUS_PENDING,
order__expires__lt=now().replace(hour=0, minute=0, second=0))
elif s == 'np':
opqs = opqs.filter(order__status__in=[Order.STATUS_PENDING, Order.STATUS_PAID])
elif s == 'pv':
opqs = opqs.filter(
Q(order__status=Order.STATUS_PAID) |
Q(order__status=Order.STATUS_PENDING, order__valid_if_pending=True)
)
elif s == 'ne':
opqs = opqs.filter(order__status__in=[Order.STATUS_PENDING, Order.STATUS_EXPIRED])
else:
opqs = opqs.filter(order__status=s)
if s not in (Order.STATUS_CANCELED, ""):
opqs = opqs.filter(canceled=False)
if self.request.GET.get("item", "") != "":
i = self.request.GET.get("item", "")
opqs = opqs.filter(item_id__in=(i,))
qs = qs.filter(orderposition__in=opqs) qs = qs.filter(orderposition__in=opqs)
op_cnt = opqs.filter(item__in=self.object.items.all()).count() op_cnt = opqs.filter(item__in=self.object.items.all()).count()
@@ -746,9 +727,11 @@ class QuestionView(EventPermissionRequiredMixin, QuestionMixin, ChartContainingV
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
ctx = super().get_context_data() ctx = super().get_context_data()
ctx['items'] = self.object.items.all() ctx['items'] = self.object.items.exists()
ctx['has_subevents'] = self.request.event.has_subevents
stats = self.get_answer_statistics() stats = self.get_answer_statistics()
ctx['stats'], ctx['total'] = stats ctx['stats'], ctx['total'] = stats
ctx['form'] = self.filter_form
return ctx return ctx
def get_object(self, queryset=None) -> Question: def get_object(self, queryset=None) -> Question:

View File

@@ -146,7 +146,7 @@ class BaseProcessView(AsyncAction, FormView):
else: else:
charset = None charset = None
try: try:
return parse_csv(self.file.file, 1024 * 1024, charset=charset) reader = parse_csv(self.file.file, 1024 * 1024, charset=charset)
except UnicodeDecodeError: except UnicodeDecodeError:
messages.warning( messages.warning(
self.request, self.request,
@@ -155,7 +155,16 @@ class BaseProcessView(AsyncAction, FormView):
"Some characters were replaced with a placeholder." "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 and 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 @cached_property
def parsed_list(self): def parsed_list(self):

View File

@@ -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 @scopes_disabled() # we have an event check here, and we can save some performance on subqueries
def get_queryset(self): 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'))) Exists(WaitingListEntry.objects.filter(voucher_id=OuterRef('pk')))
).select_related( ).select_related(
'item', 'variation', 'seat' 'item', 'variation', 'seat'

View File

@@ -0,0 +1,62 @@
#
# 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.conf import settings
THRESHOLD_DOWNGRADE_TO_MID = 50
THRESHOLD_DOWNGRADE_TO_LOW = 250
def get_task_priority(shard, organizer_id):
"""
This is an attempt to build a simple "fair-use" policy for webhooks and notifications. The problem is that when
one organizer creates e.g. 20,000 orders through the API, that might schedule 20,000 webhooks and every other
organizer will need to wait for these webhooks to go through.
We try to fix that by building three queues: high-prio, mid-prio, and low-prio. Every organizer starts in the
high-prio queue, and all their tasks are routed immediately. Once an organizer submits more than X jobs of a
certain type per minute, they get downgraded to the mid-prio queue, and then if they submit even more to the
low-prio queue. That way, if another organizer has "regular usage", they are prioritized over the organizer with
high load.
"""
from django_redis import get_redis_connection
if not settings.HAS_REDIS:
return settings.PRIORITY_CELERY_HIGH
# We use redis directly instead of the Django cache API since the Django cache API does not support INCR for
# nonexistant keys
rc = get_redis_connection("redis")
cache_key = f"pretix:task_priority:{shard}:{organizer_id}"
# Make sure counters expire after a while when not used
p = rc.pipeline()
p.incr(cache_key)
p.expire(cache_key, 60)
new_counter = p.execute()[0]
if new_counter >= THRESHOLD_DOWNGRADE_TO_LOW:
return settings.PRIORITY_CELERY_LOW
elif new_counter >= THRESHOLD_DOWNGRADE_TO_MID:
return settings.PRIORITY_CELERY_MID
else:
return settings.PRIORITY_CELERY_HIGH

View 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

View File

@@ -8,16 +8,16 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 10:37+0000\n" "POT-Creation-Date: 2025-11-20 10:37+0000\n"
"PO-Revision-Date: 2022-02-22 22:00+0000\n" "PO-Revision-Date: 2025-12-03 23:00+0000\n"
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n" "Last-Translator: sandra r <sandrarial@gestiontickets.online>\n"
"Language-Team: Galician <https://translate.pretix.eu/projects/pretix/pretix-" "Language-Team: Galician <https://translate.pretix.eu/projects/pretix/"
"js/gl/>\n" "pretix-js/gl/>\n"
"Language: gl\n" "Language: gl\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\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:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62 #: 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 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:34
msgid "PayPal" msgid "PayPal"
msgstr "" msgstr "PayPal"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:35 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:35
msgid "Venmo" msgid "Venmo"
msgstr "" msgstr "Venmo"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
#: pretix/static/pretixpresale/js/walletdetection.js:38 #: pretix/static/pretixpresale/js/walletdetection.js:38
msgid "Apple Pay" msgid "Apple Pay"
msgstr "" msgstr "Apple Pay"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:37 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:37
msgid "Itaú" msgid "Itaú"
msgstr "" msgstr "Itaú"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:38 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:38
msgid "PayPal Credit" msgid "PayPal Credit"
msgstr "" msgstr "Crédito PayPal"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:39 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:39
msgid "Credit Card" msgid "Credit Card"
msgstr "" msgstr "Tarxeta de crédito"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:40 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:40
msgid "PayPal Pay Later" msgid "PayPal Pay Later"
msgstr "" msgstr "PayPal Pagar Máis Tarde"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:41 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:41
msgid "iDEAL" msgid "iDEAL"
msgstr "" msgstr "iDEAL"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:42 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:42
msgid "SEPA Direct Debit" msgid "SEPA Direct Debit"
msgstr "" msgstr "Débito directo SEPA"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:43 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:43
msgid "Bancontact" msgid "Bancontact"
msgstr "" msgstr "Bancontact"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:44 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:44
msgid "giropay" msgid "giropay"
msgstr "" msgstr "giropay"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:45 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:45
msgid "SOFORT" msgid "SOFORT"
msgstr "" msgstr "SOFORT"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:46 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:46
#, fuzzy
#| msgid "Yes"
msgid "eps" msgid "eps"
msgstr "Si" msgstr "Si"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:47 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:47
msgid "MyBank" msgid "MyBank"
msgstr "" msgstr "MyBank"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:48 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:48
msgid "Przelewy24" msgid "Przelewy24"
msgstr "" msgstr "Przelewy24"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:49 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:49
msgid "Verkkopankki" msgid "Verkkopankki"
msgstr "" msgstr "Verkkopankki"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:50 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:50
msgid "PayU" msgid "PayU"
msgstr "" msgstr "PayU"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:51 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:51
msgid "BLIK" msgid "BLIK"
msgstr "" msgstr "BLIK"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:52 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:52
msgid "Trustly" msgid "Trustly"
msgstr "" msgstr "De confianza"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:53 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:53
msgid "Zimpler" msgid "Zimpler"
msgstr "" msgstr "Zimpler"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:54 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:54
msgid "Maxima" msgid "Maxima"
msgstr "" msgstr "Máxima"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:55 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:55
msgid "OXXO" msgid "OXXO"
msgstr "" msgstr "OXXO"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:56 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:56
msgid "Boleto" msgid "Boleto"
msgstr "" msgstr "Ticket"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:57 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:57
msgid "WeChat Pay" msgid "WeChat Pay"
msgstr "" msgstr "Pagar con WeChat"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:58 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:58
msgid "Mercado Pago" msgid "Mercado Pago"
msgstr "" msgstr "Mercado Pago"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:50 #: 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 #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:254
msgid "Payment method unavailable" 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:15
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39 #: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
@@ -240,11 +238,11 @@ msgstr "Cancelado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:46 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:46
msgid "Confirmed" msgid "Confirmed"
msgstr "" msgstr "Confirmado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:47 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:47
msgid "Approval pending" msgid "Approval pending"
msgstr "" msgstr "Aprobación pendente"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Redeemed" msgid "Redeemed"
@@ -300,16 +298,12 @@ msgid "Ticket code revoked/changed"
msgstr "Código de tícket revogado/cambiado" msgstr "Código de tícket revogado/cambiado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
#, fuzzy
#| msgid "Ticket not paid"
msgid "Ticket blocked" msgid "Ticket blocked"
msgstr "Tícket pendente de pago" msgstr "Ticket bloqueado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
#, fuzzy
#| msgid "Ticket not paid"
msgid "Ticket not valid at this time" msgid "Ticket not valid at this time"
msgstr "cket pendente de pago" msgstr "O ticket non é válido neste momento"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order canceled" msgid "Order canceled"
@@ -317,11 +311,11 @@ msgstr "Pedido cancelado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Ticket code is ambiguous on list" 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 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
msgid "Order not approved" msgid "Order not approved"
msgstr "" msgstr "Orde non aprobada"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
msgid "Checked-in Tickets" msgid "Checked-in Tickets"
@@ -422,7 +416,7 @@ msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:276 #: pretix/static/pretixbase/js/asynctask.js:276
msgid "If this takes longer than a few minutes, please contact us." 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 #: pretix/static/pretixbase/js/asynctask.js:331
msgid "Close message" msgid "Close message"
@@ -452,7 +446,7 @@ msgstr "está despois"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:40 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:40
msgid "=" msgid "="
msgstr "" msgstr "="
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:99 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:99
msgid "Product" msgid "Product"
@@ -464,7 +458,7 @@ msgstr "Ver variacións do produto"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:107 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:107
msgid "Gate" msgid "Gate"
msgstr "" msgstr "Porta"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:111 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:111
msgid "Current date and time" msgid "Current date and time"
@@ -472,11 +466,11 @@ msgstr "Data e hora actual"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:115 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:115
msgid "Current day of the week (1 = Monday, 7 = Sunday)" 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 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:119
msgid "Current entry status" msgid "Current entry status"
msgstr "" msgstr "Estado de entrada actual"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:123 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:123
msgid "Number of previous entries" 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" msgstr "Número de entradas previas desde a medianoite"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:131 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:131
#, fuzzy
#| msgid "Number of previous entries"
msgid "Number of previous entries since" 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 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:135
#, fuzzy
#| msgid "Number of previous entries"
msgid "Number of previous entries before" 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 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:139
msgid "Number of days with a previous entry" msgid "Number of days with a previous entry"
msgstr "Número de días cunha entrada previa" msgstr "Número de días cunha entrada previa"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:143 #: 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" 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 #: 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" msgid "Number of days with a previous entry before"
msgstr "Número de días cunha entrada previa" msgstr "Número de días cunha entrada previa"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:151 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:151
msgid "Minutes since last entry (-1 on first entry)" 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 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:155
msgid "Minutes since first entry (-1 on first entry)" 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 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:182
msgid "All of the conditions below (AND)" msgid "All of the conditions below (AND)"
@@ -564,25 +550,25 @@ msgstr "minutos"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:192 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:192
msgid "Duplicate" msgid "Duplicate"
msgstr "" msgstr "Duplicar"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:193 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:193
msgctxt "entry_status" msgctxt "entry_status"
msgid "present" msgid "present"
msgstr "" msgstr "presente"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:194 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:194
msgctxt "entry_status" msgctxt "entry_status"
msgid "absent" msgid "absent"
msgstr "" msgstr "ausente"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:289 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:289
msgid "Error: Product not found!" msgid "Error: Product not found!"
msgstr "" msgstr "Erro: Non se atopou o produto!"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:296 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:296
msgid "Error: Variation not found!" msgid "Error: Variation not found!"
msgstr "" msgstr "Erro: Variación non atopada!"
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:171
msgid "Check-in QR" msgid "Check-in QR"
@@ -597,16 +583,12 @@ msgid "Group of objects"
msgstr "Grupo de obxectos" msgstr "Grupo de obxectos"
#: pretix/static/pretixcontrol/js/ui/editor.js:909 #: pretix/static/pretixcontrol/js/ui/editor.js:909
#, fuzzy
#| msgid "Text object"
msgid "Text object (deprecated)" msgid "Text object (deprecated)"
msgstr "Obxecto de texto" msgstr "Obxecto de texto (obsoleto)"
#: pretix/static/pretixcontrol/js/ui/editor.js:911 #: pretix/static/pretixcontrol/js/ui/editor.js:911
#, fuzzy
#| msgid "Text object"
msgid "Text box" msgid "Text box"
msgstr "Obxecto de texto" msgstr "Caixa de texto"
#: pretix/static/pretixcontrol/js/ui/editor.js:913 #: pretix/static/pretixcontrol/js/ui/editor.js:913
msgid "Barcode area" msgid "Barcode area"
@@ -655,26 +637,26 @@ msgid "Unknown error."
msgstr "Erro descoñecido." msgstr "Erro descoñecido."
#: pretix/static/pretixcontrol/js/ui/main.js:309 #: 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." 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 #: pretix/static/pretixcontrol/js/ui/main.js:313
#, fuzzy
#| msgid "Your color has decent contrast and is probably good-enough to read!"
msgid "" msgid ""
"Your color has decent contrast and is sufficient for minimum accessibility " "Your color has decent contrast and is sufficient for minimum accessibility "
"requirements." "requirements."
msgstr "" msgstr ""
"A túa cor ten un contraste axeitado e probablemente sexa suficientemente " "A túa cor ten un contraste decente e é suficiente para os requisitos mínimos "
"lexible!" "de accesibilidade."
#: pretix/static/pretixcontrol/js/ui/main.js:317 #: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "" msgid ""
"Your color has insufficient contrast to white. Accessibility of your site " "Your color has insufficient contrast to white. Accessibility of your site "
"will be impacted." "will be impacted."
msgstr "" 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:443
#: pretix/static/pretixcontrol/js/ui/main.js:463 #: pretix/static/pretixcontrol/js/ui/main.js:463
@@ -695,11 +677,11 @@ msgstr "Soamente seleccionados"
#: pretix/static/pretixcontrol/js/ui/main.js:839 #: pretix/static/pretixcontrol/js/ui/main.js:839
msgid "Enter page number between 1 and %(max)s." 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 #: pretix/static/pretixcontrol/js/ui/main.js:842
msgid "Invalid page number." msgid "Invalid page number."
msgstr "" msgstr "Número de páxina non válido."
#: pretix/static/pretixcontrol/js/ui/main.js:1000 #: pretix/static/pretixcontrol/js/ui/main.js:1000
msgid "Use a different name internally" msgid "Use a different name internally"
@@ -718,10 +700,8 @@ msgid "Calculating default price…"
msgstr "Calculando o prezo por defecto…" msgstr "Calculando o prezo por defecto…"
#: pretix/static/pretixcontrol/js/ui/plugins.js:69 #: pretix/static/pretixcontrol/js/ui/plugins.js:69
#, fuzzy
#| msgid "Search results"
msgid "No results" msgid "No results"
msgstr "Resultados da procura" msgstr "Sen resultados"
#: pretix/static/pretixcontrol/js/ui/question.js:41 #: pretix/static/pretixcontrol/js/ui/question.js:41
msgid "Others" 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:58
#: pretix/static/pretixpresale/js/ui/cart.js:84 #: pretix/static/pretixpresale/js/ui/cart.js:84
msgid "Your cart is about to expire." 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 #: pretix/static/pretixpresale/js/ui/cart.js:62
msgid "The items in your cart are reserved for you for one minute." 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." "Os artigos da túa cesta están reservados para ti durante {num} minutos."
#: pretix/static/pretixpresale/js/ui/cart.js:83 #: pretix/static/pretixpresale/js/ui/cart.js:83
#, fuzzy
#| msgid "Cart expired"
msgid "Your cart has 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 #: 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 theyre available."
msgid "" msgid ""
"The items in your cart are no longer reserved for you. You can still " "The items in your cart are no longer reserved for you. You can still "
"complete your order as long as they're available." "complete your order as long as they're available."
@@ -781,11 +755,11 @@ msgstr ""
#: pretix/static/pretixpresale/js/ui/cart.js:87 #: pretix/static/pretixpresale/js/ui/cart.js:87
msgid "Do you want to renew the reservation period?" 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 #: pretix/static/pretixpresale/js/ui/cart.js:90
msgid "Renew reservation" msgid "Renew reservation"
msgstr "" msgstr "Renovar reserva"
#: pretix/static/pretixpresale/js/ui/main.js:194 #: pretix/static/pretixpresale/js/ui/main.js:194
msgid "The organizer keeps %(currency)s %(amount)s" 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 #: pretix/static/pretixpresale/js/walletdetection.js:39
msgid "Google Pay" msgid "Google Pay"
msgstr "" msgstr "Google Pay"
#: pretix/static/pretixpresale/js/widget/widget.js:16 #: pretix/static/pretixpresale/js/widget/widget.js:16
msgctxt "widget" msgctxt "widget"
msgid "Quantity" msgid "Quantity"
msgstr "" msgstr "Cantidade"
#: pretix/static/pretixpresale/js/widget/widget.js:17 #: pretix/static/pretixpresale/js/widget/widget.js:17
msgctxt "widget" msgctxt "widget"
msgid "Decrease quantity" msgid "Decrease quantity"
msgstr "" msgstr "Diminuír a cantidade"
#: pretix/static/pretixpresale/js/widget/widget.js:18 #: pretix/static/pretixpresale/js/widget/widget.js:18
msgctxt "widget" msgctxt "widget"
msgid "Increase quantity" msgid "Increase quantity"
msgstr "" msgstr "Aumentar a cantidade"
#: pretix/static/pretixpresale/js/widget/widget.js:19 #: pretix/static/pretixpresale/js/widget/widget.js:19
msgctxt "widget" msgctxt "widget"
msgid "Filter events by" msgid "Filter events by"
msgstr "" msgstr "Filtrar eventos por"
#: pretix/static/pretixpresale/js/widget/widget.js:20 #: pretix/static/pretixpresale/js/widget/widget.js:20
msgctxt "widget" msgctxt "widget"
msgid "Filter" msgid "Filter"
msgstr "" msgstr "Filtro"
#: pretix/static/pretixpresale/js/widget/widget.js:21 #: pretix/static/pretixpresale/js/widget/widget.js:21
msgctxt "widget" msgctxt "widget"
msgid "Price" msgid "Price"
msgstr "" msgstr "Prezo"
#: pretix/static/pretixpresale/js/widget/widget.js:22 #: pretix/static/pretixpresale/js/widget/widget.js:22
#, javascript-format #, javascript-format
msgctxt "widget" msgctxt "widget"
msgid "Original price: %s" msgid "Original price: %s"
msgstr "" msgstr "Prezo orixinal: %s"
#: pretix/static/pretixpresale/js/widget/widget.js:23 #: pretix/static/pretixpresale/js/widget/widget.js:23
#, javascript-format #, javascript-format
msgctxt "widget" msgctxt "widget"
msgid "New price: %s" msgid "New price: %s"
msgstr "" msgstr "Novo prezo: %s"
#: pretix/static/pretixpresale/js/widget/widget.js:24 #: pretix/static/pretixpresale/js/widget/widget.js:24
#, fuzzy
#| msgid "Selected only"
msgctxt "widget" msgctxt "widget"
msgid "Select" msgid "Select"
msgstr "Soamente seleccionados" msgstr "Seleccione"
#: pretix/static/pretixpresale/js/widget/widget.js:25 #: pretix/static/pretixpresale/js/widget/widget.js:25
#, fuzzy, javascript-format #, javascript-format
#| msgid "Selected only"
msgctxt "widget" msgctxt "widget"
msgid "Select %s" msgid "Select %s"
msgstr "Soamente seleccionados" msgstr "Seleccione %s"
#: pretix/static/pretixpresale/js/widget/widget.js:26 #: pretix/static/pretixpresale/js/widget/widget.js:26
#, fuzzy, javascript-format #, javascript-format
#| msgctxt "widget"
#| msgid "See variations"
msgctxt "widget" msgctxt "widget"
msgid "Select variant %s" msgid "Select variant %s"
msgstr "Ver variacións" msgstr "Seleccione a variante %s"
#: pretix/static/pretixpresale/js/widget/widget.js:27 #: pretix/static/pretixpresale/js/widget/widget.js:27
msgctxt "widget" msgctxt "widget"
@@ -905,7 +874,7 @@ msgstr "dende %(currency)s %(price)s"
#, javascript-format #, javascript-format
msgctxt "widget" msgctxt "widget"
msgid "Image of %s" msgid "Image of %s"
msgstr "" msgstr "Imaxe de %s"
#: pretix/static/pretixpresale/js/widget/widget.js:34 #: pretix/static/pretixpresale/js/widget/widget.js:34
msgctxt "widget" 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:40
#: pretix/static/pretixpresale/js/widget/widget.js:43 #: pretix/static/pretixpresale/js/widget/widget.js:43
#, fuzzy
#| msgctxt "widget"
#| msgid "currently available: %s"
msgctxt "widget" msgctxt "widget"
msgid "Not yet available" msgid "Not yet available"
msgstr "dispoñible actualmente: %s" msgstr "Aínda non dispoñible"
#: pretix/static/pretixpresale/js/widget/widget.js:41 #: pretix/static/pretixpresale/js/widget/widget.js:41
msgctxt "widget" msgctxt "widget"
msgid "Not available anymore" msgid "Not available anymore"
msgstr "" msgstr "Xa non está dispoñible"
#: pretix/static/pretixpresale/js/widget/widget.js:42 #: pretix/static/pretixpresale/js/widget/widget.js:42
#, fuzzy
#| msgctxt "widget"
#| msgid "currently available: %s"
msgctxt "widget" msgctxt "widget"
msgid "Currently not available" msgid "Currently not available"
msgstr "dispoñible actualmente: %s" msgstr "Non dispoñible actualmente"
#: pretix/static/pretixpresale/js/widget/widget.js:44 #: pretix/static/pretixpresale/js/widget/widget.js:44
#, javascript-format #, javascript-format
@@ -991,9 +954,6 @@ msgid "Open ticket shop"
msgstr "Abrir a tenda de tíckets" msgstr "Abrir a tenda de tíckets"
#: pretix/static/pretixpresale/js/widget/widget.js:50 #: pretix/static/pretixpresale/js/widget/widget.js:50
#, fuzzy
#| msgctxt "widget"
#| msgid "Resume checkout"
msgctxt "widget" msgctxt "widget"
msgid "Checkout" msgid "Checkout"
msgstr "Continuar co pagamento" msgstr "Continuar co pagamento"
@@ -1053,17 +1013,14 @@ msgid "Close"
msgstr "Cerrar" msgstr "Cerrar"
#: pretix/static/pretixpresale/js/widget/widget.js:62 #: pretix/static/pretixpresale/js/widget/widget.js:62
#, fuzzy
#| msgctxt "widget"
#| msgid "Resume checkout"
msgctxt "widget" msgctxt "widget"
msgid "Close checkout" msgid "Close checkout"
msgstr "Continuar co pagamento" msgstr "Pagamento pechado"
#: pretix/static/pretixpresale/js/widget/widget.js:63 #: pretix/static/pretixpresale/js/widget/widget.js:63
msgctxt "widget" msgctxt "widget"
msgid "You cannot cancel this operation. Please wait for loading to finish." 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 #: pretix/static/pretixpresale/js/widget/widget.js:64
msgctxt "widget" msgctxt "widget"
@@ -1071,20 +1028,14 @@ msgid "Continue"
msgstr "Continuar" msgstr "Continuar"
#: pretix/static/pretixpresale/js/widget/widget.js:65 #: pretix/static/pretixpresale/js/widget/widget.js:65
#, fuzzy
#| msgctxt "widget"
#| msgid "See variations"
msgctxt "widget" msgctxt "widget"
msgid "Show variants" msgid "Show variants"
msgstr "Ver variacións" msgstr "Ver variacións"
#: pretix/static/pretixpresale/js/widget/widget.js:66 #: pretix/static/pretixpresale/js/widget/widget.js:66
#, fuzzy
#| msgctxt "widget"
#| msgid "See variations"
msgctxt "widget" msgctxt "widget"
msgid "Hide variants" msgid "Hide variants"
msgstr "Ver variacións" msgstr "Ocultar variantes"
#: pretix/static/pretixpresale/js/widget/widget.js:67 #: pretix/static/pretixpresale/js/widget/widget.js:67
msgctxt "widget" msgctxt "widget"
@@ -1133,6 +1084,9 @@ msgid ""
"add yourself to the waiting list. We will then notify if seats are available " "add yourself to the waiting list. We will then notify if seats are available "
"again." "again."
msgstr "" 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 #: pretix/static/pretixpresale/js/widget/widget.js:76
msgctxt "widget" msgctxt "widget"
@@ -1169,31 +1123,31 @@ msgstr "Dom"
#: pretix/static/pretixpresale/js/widget/widget.js:85 #: pretix/static/pretixpresale/js/widget/widget.js:85
msgid "Monday" msgid "Monday"
msgstr "" msgstr "Luns"
#: pretix/static/pretixpresale/js/widget/widget.js:86 #: pretix/static/pretixpresale/js/widget/widget.js:86
msgid "Tuesday" msgid "Tuesday"
msgstr "" msgstr "Martes"
#: pretix/static/pretixpresale/js/widget/widget.js:87 #: pretix/static/pretixpresale/js/widget/widget.js:87
msgid "Wednesday" msgid "Wednesday"
msgstr "" msgstr "Mércores"
#: pretix/static/pretixpresale/js/widget/widget.js:88 #: pretix/static/pretixpresale/js/widget/widget.js:88
msgid "Thursday" msgid "Thursday"
msgstr "" msgstr "Xoves"
#: pretix/static/pretixpresale/js/widget/widget.js:89 #: pretix/static/pretixpresale/js/widget/widget.js:89
msgid "Friday" msgid "Friday"
msgstr "" msgstr "Venres"
#: pretix/static/pretixpresale/js/widget/widget.js:90 #: pretix/static/pretixpresale/js/widget/widget.js:90
msgid "Saturday" msgid "Saturday"
msgstr "" msgstr "Sábado"
#: pretix/static/pretixpresale/js/widget/widget.js:91 #: pretix/static/pretixpresale/js/widget/widget.js:91
msgid "Sunday" msgid "Sunday"
msgstr "" msgstr "Domingo"
#: pretix/static/pretixpresale/js/widget/widget.js:94 #: pretix/static/pretixpresale/js/widget/widget.js:94
msgid "January" msgid "January"

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 13:57+0000\n" "POT-Creation-Date: 2025-11-27 13:57+0000\n"
"PO-Revision-Date: 2025-11-26 17:00+0000\n" "PO-Revision-Date: 2025-12-05 18:00+0000\n"
"Last-Translator: Yasunobu YesNo Kawaguchi <kawaguti@gmail.com>\n" "Last-Translator: Yasunobu YesNo Kawaguchi <kawaguti@gmail.com>\n"
"Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/pretix/" "Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/pretix/"
"ja/>\n" "ja/>\n"
@@ -2149,7 +2149,7 @@ msgstr "クライアントID"
#: pretix/base/exporters/items.py:91 pretix/base/models/items.py:667 #: pretix/base/exporters/items.py:91 pretix/base/models/items.py:667
#: pretix/base/models/items.py:1168 #: pretix/base/models/items.py:1168
msgid "Original price" msgid "Original price"
msgstr "元の価格" msgstr "通常価格"
#: pretix/base/exporters/items.py:92 pretix/base/models/items.py:684 #: pretix/base/exporters/items.py:92 pretix/base/models/items.py:684
msgid "This product is a gift card" msgid "This product is a gift card"
@@ -22741,9 +22741,8 @@ msgid ""
"This position has been created with a voucher with a limited budget. If you " "This position has been created with a voucher with a limited budget. If you "
"change the price or item, the discount will still be calculated from the " "change the price or item, the discount will still be calculated from the "
"original price at the time of purchase." "original price at the time of purchase."
msgstr "" msgstr "このポジションは限られた予算のバウチャーで作成されました。価格やアイテムを変"
"このポジションは限られた予算のバウチャーで作成されました。価格やアイテムを変" "更しても、割引は購入時の通常価格から計算されます。"
"更しても、割引は購入時の元の価格から計算されます。"
#: pretix/control/templates/pretixcontrol/order/change.html:101 #: pretix/control/templates/pretixcontrol/order/change.html:101
#: pretix/control/templates/pretixcontrol/order/change.html:413 #: pretix/control/templates/pretixcontrol/order/change.html:413
@@ -33770,7 +33769,7 @@ msgstr "バリエーションを表示する"
#: pretix/presale/templates/pretixpresale/event/voucher.html:147 #: pretix/presale/templates/pretixpresale/event/voucher.html:147
#: pretix/presale/templates/pretixpresale/event/voucher.html:304 #: pretix/presale/templates/pretixpresale/event/voucher.html:304
msgid "Original price:" msgid "Original price:"
msgstr "元の価格:" msgstr "通常価格:"
#: pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html:136 #: pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html:136
#: pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html:278 #: pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html:278

View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 13:57+0000\n" "POT-Creation-Date: 2025-11-27 13:57+0000\n"
"PO-Revision-Date: 2025-11-18 17:00+0000\n" "PO-Revision-Date: 2025-12-03 23:00+0000\n"
"Last-Translator: Hijiri Umemoto <hijiri@umemoto.org>\n" "Last-Translator: SJang1 <git@sjang.dev>\n"
"Language-Team: Korean <https://translate.pretix.eu/projects/pretix/pretix/ko/" "Language-Team: Korean <https://translate.pretix.eu/projects/pretix/pretix/ko/"
">\n" ">\n"
"Language: ko\n" "Language: ko\n"
@@ -290,33 +290,27 @@ msgstr "묶음 상품은 그 자체로 또 다른 묶음을 포함 할 수 없
#: pretix/api/serializers/item.py:235 #: pretix/api/serializers/item.py:235
msgid "The program start must not be empty." msgid "The program start must not be empty."
msgstr "" msgstr "프로그램 시작일정은 비어 있어서는 안 됩니다."
#: pretix/api/serializers/item.py:239 #: pretix/api/serializers/item.py:239
msgid "The program end must not be empty." msgid "The program end must not be empty."
msgstr "" msgstr "프로그램 종료일정은 비어 있어서는 안 됩니다."
#: pretix/api/serializers/item.py:242 pretix/base/models/items.py:2321 #: 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." 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 #: 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." msgid "You cannot use program times on an event series."
msgstr "" msgstr "이벤트 시리즈에 있는 시간은 사용하실 수 없습니다."
"당신의이벤트가 이벤트 시리즈가 아닌 경우 하위 이벤트를 선택할 수 없습니다."
#: pretix/api/serializers/item.py:337 #: pretix/api/serializers/item.py:337
#, fuzzy
msgid "" msgid ""
"Updating add-ons, bundles, program times or variations via PATCH/PUT is not " "Updating add-ons, bundles, program times or variations via PATCH/PUT is not "
"supported. Please use the dedicated nested endpoint." "supported. Please use the dedicated nested endpoint."
msgstr "" msgstr ""
"추가 기능, 묶음 상품들, 또는 변형은 PATCH/PUT를 통해 업데이트 할 수 없습니" "추가 기능, 묶음 상품들, 또는 변형은 PATCH/PUT를 통해 업데이트 할 수 없습니다."
"다. 전용 중첩은 마지막 지점에서 사용하세요" " 전용 중첩은 마지막 지점에서 사용하세요."
#: pretix/api/serializers/item.py:345 #: pretix/api/serializers/item.py:345
msgid "Only admission products can currently be personalized." msgid "Only admission products can currently be personalized."
@@ -573,22 +567,15 @@ msgid "Event series date deleted"
msgstr "이벤트 시리즈 날짜 삭제" msgstr "이벤트 시리즈 날짜 삭제"
#: pretix/api/webhooks.py:374 #: pretix/api/webhooks.py:374
#, fuzzy
#| msgid "Product name"
msgid "Product changed" msgid "Product changed"
msgstr "제품" msgstr "제품 변경됨"
#: pretix/api/webhooks.py:375 #: 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 "" msgid ""
"This includes product added or deleted and changes to nested objects like " "This includes product added or deleted and changes to nested objects like "
"variations or bundles." "variations or bundles."
msgstr "" msgstr "여기에는 추가되거나 삭제되거나 변경된 중첩오브젝트나 번들과 같은 사항이 포함"
"제품 변경(제품 추가 또는 삭제, 변형 또는 번들과 같은 중첩된 객체에 대한 변경 " "됩니다."
"포함)"
#: pretix/api/webhooks.py:380 #: pretix/api/webhooks.py:380
msgid "Shop taken live" msgid "Shop taken live"
@@ -623,16 +610,12 @@ msgid "Waiting list entry received voucher"
msgstr "대기자 명단 항목이 바우처를 받았습니다" msgstr "대기자 명단 항목이 바우처를 받았습니다"
#: pretix/api/webhooks.py:412 #: pretix/api/webhooks.py:412
#, fuzzy
#| msgid "Voucher code"
msgid "Voucher added" msgid "Voucher added"
msgstr "바우처 코드" msgstr "바우처 추가됨"
#: pretix/api/webhooks.py:416 #: pretix/api/webhooks.py:416
#, fuzzy
#| msgid "Voucher assigned"
msgid "Voucher changed" msgid "Voucher changed"
msgstr "바우처 할당" msgstr "바우처 변경됨"
#: pretix/api/webhooks.py:417 #: pretix/api/webhooks.py:417
msgid "" msgid ""
@@ -643,10 +626,8 @@ msgstr ""
"하지 않습니다." "하지 않습니다."
#: pretix/api/webhooks.py:421 #: pretix/api/webhooks.py:421
#, fuzzy
#| msgid "Voucher redeemed"
msgid "Voucher deleted" msgid "Voucher deleted"
msgstr "바우처 상환" msgstr "바우처 제거됨"
#: pretix/api/webhooks.py:425 #: pretix/api/webhooks.py:425
msgid "Customer account created" msgid "Customer account created"
@@ -671,7 +652,7 @@ msgstr "고객 계정 익명화되었습니다"
#: pretix/plugins/banktransfer/payment.py:513 #: pretix/plugins/banktransfer/payment.py:513
#: pretix/presale/forms/customer.py:152 #: pretix/presale/forms/customer.py:152
msgid "This field is required." msgid "This field is required."
msgstr "이 필드는 필수입니다" msgstr "이 필드는 필수입니다."
#: pretix/base/addressvalidation.py:213 #: pretix/base/addressvalidation.py:213
msgid "Enter a postal code in the format XXX." msgid "Enter a postal code in the format XXX."
@@ -721,7 +702,7 @@ msgstr "비밀번호"
#: pretix/base/auth.py:176 pretix/base/auth.py:183 #: pretix/base/auth.py:176 pretix/base/auth.py:183
msgid "Your password must contain both numeric and alphabetic characters." msgid "Your password must contain both numeric and alphabetic characters."
msgstr "비밀번호는 숫자와 알파벳 문자가 모두 포함되어야 합니다" msgstr "비밀번호는 숫자와 알파벳 문자가 모두 포함되어야 합니다."
#: pretix/base/auth.py:202 pretix/base/auth.py:212 #: pretix/base/auth.py:202 pretix/base/auth.py:212
#, python-format #, python-format
@@ -815,28 +796,21 @@ msgstr ""
"소를 확인해 주십시요." "소를 확인해 주십시요."
#: pretix/base/datasync/datasync.py:263 #: pretix/base/datasync/datasync.py:263
#, fuzzy, python-brace-format #, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
msgid "" msgid ""
"Field \"{field_name}\" does not exist. Please check your {provider_name} " "Field \"{field_name}\" does not exist. Please check your {provider_name} "
"settings." "settings."
msgstr "" msgstr "필드 \"{field_name}\"은 존재하지 않습니다. 당신의 {provider_name} 설정을 확인"
"필드 \"{field_name}\"은 {available_inputs}에 유효하지 않습니다. " "해 주세요."
"{provider_name} 설정을 확인해 주세요."
#: pretix/base/datasync/datasync.py:270 #: pretix/base/datasync/datasync.py:270
#, fuzzy, python-brace-format #, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
msgid "" msgid ""
"Field \"{field_name}\" requires {required_input}, but only got " "Field \"{field_name}\" requires {required_input}, but only got "
"{available_inputs}. Please check your {provider_name} settings." "{available_inputs}. Please check your {provider_name} settings."
msgstr "" msgstr ""
"필드 \"{field_name}\" {available_inputs}에 유효하지 않습니다. " "필드 \"{field_name}\"는 {required_input}을 필요로 하지만, {available_inputs}"
"{provider_name} 설정을 확인해 주세요." "만 받았습니다. 당신의 {provider_name} 설정을 확인해 주세요."
#: pretix/base/datasync/datasync.py:281 #: pretix/base/datasync/datasync.py:281
#, python-brace-format #, python-brace-format
@@ -848,16 +822,12 @@ msgstr ""
"지 않았습니다" "지 않았습니다"
#: pretix/base/datasync/sourcefields.py:128 #: pretix/base/datasync/sourcefields.py:128
#, fuzzy
#| msgid "Order positions"
msgid "Order position details" msgid "Order position details"
msgstr "주문 위치" msgstr "주문 위치 세부 정보"
#: pretix/base/datasync/sourcefields.py:129 #: pretix/base/datasync/sourcefields.py:129
#, fuzzy
#| msgid "Attendee email"
msgid "Attendee details" msgid "Attendee details"
msgstr "참석자 이메일" msgstr "참석자 정보들"
#: pretix/base/datasync/sourcefields.py:130 pretix/base/exporters/answers.py:66 #: pretix/base/datasync/sourcefields.py:130 pretix/base/exporters/answers.py:66
#: pretix/base/models/items.py:1766 pretix/control/navigation.py:172 #: pretix/base/models/items.py:1766 pretix/control/navigation.py:172
@@ -867,10 +837,8 @@ msgid "Questions"
msgstr "질문들" msgstr "질문들"
#: pretix/base/datasync/sourcefields.py:131 #: pretix/base/datasync/sourcefields.py:131
#, fuzzy
#| msgid "Product data"
msgid "Product details" msgid "Product details"
msgstr "상품 데이터" msgstr "상품 정보"
#: pretix/base/datasync/sourcefields.py:132 #: pretix/base/datasync/sourcefields.py:132
#: pretix/control/templates/pretixcontrol/event/settings.html:279 #: pretix/control/templates/pretixcontrol/event/settings.html:279
@@ -895,17 +863,13 @@ msgid "Invoice address"
msgstr "송장 주소" msgstr "송장 주소"
#: pretix/base/datasync/sourcefields.py:134 #: pretix/base/datasync/sourcefields.py:134
#, fuzzy
#| msgid "Meta information"
msgid "Event information" msgid "Event information"
msgstr "메타 정보(데이타에 대한 정보)" msgstr "이벤트 정보"
#: pretix/base/datasync/sourcefields.py:135 #: pretix/base/datasync/sourcefields.py:135
#, fuzzy
#| msgid "Send recovery information"
msgctxt "subevent" msgctxt "subevent"
msgid "Event or date information" msgid "Event or date information"
msgstr "복구 정보를 전송하다" msgstr "이벤트 또는 날짜 정보"
#: pretix/base/datasync/sourcefields.py:175 #: pretix/base/datasync/sourcefields.py:175
#: pretix/base/exporters/orderlist.py:605 #: pretix/base/exporters/orderlist.py:605
@@ -930,10 +894,8 @@ msgstr "참석자 이름"
#: pretix/base/datasync/sourcefields.py:187 #: pretix/base/datasync/sourcefields.py:187
#: pretix/base/datasync/sourcefields.py:604 #: pretix/base/datasync/sourcefields.py:604
#: pretix/base/datasync/sourcefields.py:628 #: pretix/base/datasync/sourcefields.py:628
#, fuzzy
#| msgid "Attendee name"
msgid "Attendee" msgid "Attendee"
msgstr "참석자 이름" msgstr "참석자"
#: pretix/base/datasync/sourcefields.py:207 #: pretix/base/datasync/sourcefields.py:207
#: pretix/base/exporters/orderlist.py:612 pretix/base/forms/questions.py:687 #: pretix/base/exporters/orderlist.py:612 pretix/base/forms/questions.py:687
@@ -947,10 +909,8 @@ msgid "Attendee email"
msgstr "참석자 이메일" msgstr "참석자 이메일"
#: pretix/base/datasync/sourcefields.py:219 #: pretix/base/datasync/sourcefields.py:219
#, fuzzy
#| msgid "Attendee email"
msgid "Attendee or order email" msgid "Attendee or order email"
msgstr "참석자 이메일" msgstr "참석자 또는 구매 이메일"
#: pretix/base/datasync/sourcefields.py:232 #: pretix/base/datasync/sourcefields.py:232
#: pretix/base/exporters/orderlist.py:613 pretix/base/pdf.py:189 #: pretix/base/exporters/orderlist.py:613 pretix/base/pdf.py:189
@@ -963,28 +923,20 @@ msgid "Attendee company"
msgstr "참석자 회사" msgstr "참석자 회사"
#: pretix/base/datasync/sourcefields.py:241 #: pretix/base/datasync/sourcefields.py:241
#, fuzzy
#| msgid "Attendee address"
msgid "Attendee address street" msgid "Attendee address street"
msgstr "참석자 주소" msgstr "참석자 주소 도로명"
#: pretix/base/datasync/sourcefields.py:250 #: pretix/base/datasync/sourcefields.py:250
#, fuzzy
#| msgid "Attendee ZIP code"
msgid "Attendee address ZIP code" msgid "Attendee address ZIP code"
msgstr "참석자 우편번호" msgstr "참석자 주소 우편번호"
#: pretix/base/datasync/sourcefields.py:259 #: pretix/base/datasync/sourcefields.py:259
#, fuzzy
#| msgid "Attendee address"
msgid "Attendee address city" msgid "Attendee address city"
msgstr "참석자 주소" msgstr "참석자 주소 도시"
#: pretix/base/datasync/sourcefields.py:268 #: pretix/base/datasync/sourcefields.py:268
#, fuzzy
#| msgid "Attendee address"
msgid "Attendee address country" msgid "Attendee address country"
msgstr "참석자 주소" msgstr "참석자 주소 국가"
#: pretix/base/datasync/sourcefields.py:279 #: pretix/base/datasync/sourcefields.py:279
#: pretix/base/exporters/orderlist.py:653 pretix/base/pdf.py:347 #: pretix/base/exporters/orderlist.py:653 pretix/base/pdf.py:347
@@ -1020,16 +972,12 @@ msgid "Invoice address country"
msgstr "송장 주소 국가" msgstr "송장 주소 국가"
#: pretix/base/datasync/sourcefields.py:353 #: pretix/base/datasync/sourcefields.py:353
#, fuzzy
#| msgid "Order total"
msgid "Order email" msgid "Order email"
msgstr "주문 합계" msgstr "주문 이메일"
#: pretix/base/datasync/sourcefields.py:362 #: pretix/base/datasync/sourcefields.py:362
#, fuzzy
#| msgid "Order time"
msgid "Order email domain" msgid "Order email domain"
msgstr "주문 시간" msgstr "주문 이메일 도메인"
#: pretix/base/datasync/sourcefields.py:371 #: pretix/base/datasync/sourcefields.py:371
#: pretix/base/exporters/invoices.py:203 pretix/base/exporters/invoices.py:332 #: pretix/base/exporters/invoices.py:203 pretix/base/exporters/invoices.py:332
@@ -1061,10 +1009,8 @@ msgid "Order code"
msgstr "주문 코드" msgstr "주문 코드"
#: pretix/base/datasync/sourcefields.py:380 #: pretix/base/datasync/sourcefields.py:380
#, fuzzy
#| msgid "Event end date and time"
msgid "Event and order code" msgid "Event and order code"
msgstr "이벤트 종료 날짜 및 시간" msgstr "이벤트와 주문 번호"
#: pretix/base/datasync/sourcefields.py:389 #: pretix/base/datasync/sourcefields.py:389
#: pretix/base/exporters/orderlist.py:263 pretix/base/notifications.py:201 #: pretix/base/exporters/orderlist.py:263 pretix/base/notifications.py:201
@@ -1076,10 +1022,8 @@ msgid "Order total"
msgstr "주문 합계" msgstr "주문 합계"
#: pretix/base/datasync/sourcefields.py:398 #: pretix/base/datasync/sourcefields.py:398
#, fuzzy
#| msgid "Product name and variation"
msgid "Product and variation name" msgid "Product and variation name"
msgstr "제품명 및 변형" msgstr "제품명 및 변형 이름"
#: pretix/base/datasync/sourcefields.py:410 pretix/base/exporters/items.py:57 #: pretix/base/datasync/sourcefields.py:410 pretix/base/exporters/items.py:57
#: pretix/base/exporters/orderlist.py:598 #: pretix/base/exporters/orderlist.py:598
@@ -1089,16 +1033,12 @@ msgid "Product ID"
msgstr "상품 식별 아이디" msgstr "상품 식별 아이디"
#: pretix/base/datasync/sourcefields.py:419 #: pretix/base/datasync/sourcefields.py:419
#, fuzzy
#| msgid "Count add-on products"
msgid "Product is admission product" msgid "Product is admission product"
msgstr "추가된 제품을 포함합니다" msgstr "상품은 입장 상품입니다"
#: pretix/base/datasync/sourcefields.py:428 #: pretix/base/datasync/sourcefields.py:428
#, fuzzy
#| msgid "Short form"
msgid "Event short form" msgid "Event short form"
msgstr "짧은 형식" msgstr "이벤트 짧은 형식"
#: pretix/base/datasync/sourcefields.py:437 pretix/base/exporters/events.py:57 #: pretix/base/datasync/sourcefields.py:437 pretix/base/exporters/events.py:57
#: pretix/base/exporters/orderlist.py:263 #: pretix/base/exporters/orderlist.py:263
@@ -1141,10 +1081,8 @@ msgid "Order code and position number"
msgstr "주문 코드 및 위치 번호" msgstr "주문 코드 및 위치 번호"
#: pretix/base/datasync/sourcefields.py:482 #: pretix/base/datasync/sourcefields.py:482
#, fuzzy
#| msgid "Ticket code"
msgid "Ticket price" msgid "Ticket price"
msgstr "티켓 코드" msgstr "티켓 가격"
#: pretix/base/datasync/sourcefields.py:491 pretix/base/notifications.py:204 #: pretix/base/datasync/sourcefields.py:491 pretix/base/notifications.py:204
#: pretix/control/forms/filter.py:216 pretix/control/forms/modelimport.py:90 #: pretix/control/forms/filter.py:216 pretix/control/forms/modelimport.py:90
@@ -1152,22 +1090,16 @@ msgid "Order status"
msgstr "주문 상태" msgstr "주문 상태"
#: pretix/base/datasync/sourcefields.py:500 #: pretix/base/datasync/sourcefields.py:500
#, fuzzy
#| msgid "Device status"
msgid "Ticket status" msgid "Ticket status"
msgstr "기기 상태" msgstr "티켓 상태"
#: pretix/base/datasync/sourcefields.py:509 #: pretix/base/datasync/sourcefields.py:509
#, fuzzy
#| msgid "Purchase date and time"
msgid "Order date and time" msgid "Order date and time"
msgstr "구매 날짜 및 시간" msgstr "구매 날짜 및 시간"
#: pretix/base/datasync/sourcefields.py:518 #: pretix/base/datasync/sourcefields.py:518
#, fuzzy
#| msgid "Printing date and time"
msgid "Payment date and time" msgid "Payment date and time"
msgstr "인쇄 날짜 및 시간" msgstr "결제 날짜 및 시간"
#: pretix/base/datasync/sourcefields.py:527 #: pretix/base/datasync/sourcefields.py:527
#: pretix/base/exporters/orderlist.py:272 #: pretix/base/exporters/orderlist.py:272
@@ -1178,35 +1110,27 @@ msgid "Order locale"
msgstr "주문 지역 설정" msgstr "주문 지역 설정"
#: pretix/base/datasync/sourcefields.py:536 #: pretix/base/datasync/sourcefields.py:536
#, fuzzy
#| msgid "Order position"
msgid "Order position ID" msgid "Order position ID"
msgstr "주문 위치" msgstr "주문 위치 ID"
#: pretix/base/datasync/sourcefields.py:545 #: pretix/base/datasync/sourcefields.py:545
#: pretix/base/exporters/orderlist.py:292 #: pretix/base/exporters/orderlist.py:292
#, fuzzy
#| msgid "Order time"
msgid "Order link" msgid "Order link"
msgstr "주문 시간" msgstr "주문 링크"
#: pretix/base/datasync/sourcefields.py:560 #: pretix/base/datasync/sourcefields.py:560
#, fuzzy
#| msgid "Ticket design"
msgid "Ticket link" msgid "Ticket link"
msgstr "티켓 디자인" msgstr "티켓 링크"
#: pretix/base/datasync/sourcefields.py:578 #: pretix/base/datasync/sourcefields.py:578
#, fuzzy, python-brace-format #, python-brace-format
#| msgid "Check-in list {val}"
msgid "Check-in datetime on list {}" msgid "Check-in datetime on list {}"
msgstr "체크인 목록 {val}" msgstr "{} 리스트에 있는 체크인 날짜와 시간"
#: pretix/base/datasync/sourcefields.py:590 #: pretix/base/datasync/sourcefields.py:590
#, fuzzy, python-brace-format #, python-brace-format
#| msgid "Question {val}"
msgid "Question: {name}" msgid "Question: {name}"
msgstr "질문 {val}" msgstr "질문: {name}"
#: pretix/base/datasync/sourcefields.py:604 #: pretix/base/datasync/sourcefields.py:604
#: pretix/base/datasync/sourcefields.py:614 pretix/base/settings.py:3642 #: pretix/base/datasync/sourcefields.py:614 pretix/base/settings.py:3642
@@ -2422,9 +2346,9 @@ msgid "Fees"
msgstr "수수료" msgstr "수수료"
#: pretix/base/exporters/orderlist.py:277 #: pretix/base/exporters/orderlist.py:277
#, fuzzy, python-brace-format #, python-brace-format
msgid "Gross at {rate} % tax" msgid "Gross at {rate} % tax"
msgstr "세율{%}의 세금으로 총합" msgstr "세율 {rate}%의 세금으로 총합"
#: pretix/base/exporters/orderlist.py:278 #: pretix/base/exporters/orderlist.py:278
#, python-brace-format #, python-brace-format
@@ -2472,9 +2396,9 @@ msgid "External customer ID"
msgstr "외부고객 아이디" msgstr "외부고객 아이디"
#: pretix/base/exporters/orderlist.py:296 #: pretix/base/exporters/orderlist.py:296
#, fuzzy, python-brace-format #, python-brace-format
msgid "Paid by {method}" msgid "Paid by {method}"
msgstr "{방법}에 의해 결제됨" msgstr "{method}에 의해 결제됨"
#: pretix/base/exporters/orderlist.py:458 #: pretix/base/exporters/orderlist.py:458
#: pretix/base/exporters/orderlist.py:914 #: pretix/base/exporters/orderlist.py:914
@@ -2677,10 +2601,8 @@ msgid "Check-in lists"
msgstr "체크인 목록" msgstr "체크인 목록"
#: pretix/base/exporters/orderlist.py:672 #: pretix/base/exporters/orderlist.py:672
#, fuzzy
#| msgid "Additional footer link"
msgid "Position order link" msgid "Position order link"
msgstr "추가 하단 링크" msgstr "주문 링크 위치"
#: pretix/base/exporters/orderlist.py:841 #: pretix/base/exporters/orderlist.py:841
msgid "Order transaction data" msgid "Order transaction data"
@@ -3320,15 +3242,13 @@ msgid "Repeat password"
msgstr "반복 비밀번호" msgstr "반복 비밀번호"
#: pretix/base/forms/auth.py:220 pretix/base/forms/user.py:99 #: pretix/base/forms/auth.py:220 pretix/base/forms/user.py:99
#, fuzzy
#| msgid "Email address"
msgid "Your email address" msgid "Your email address"
msgstr "이메일 주소" msgstr "당신의 이메일 주소"
#: pretix/base/forms/auth.py:327 pretix/control/forms/orders.py:1041 #: pretix/base/forms/auth.py:327 pretix/control/forms/orders.py:1041
#: pretix/control/templates/pretixcontrol/shredder/download.html:53 #: pretix/control/templates/pretixcontrol/shredder/download.html:53
msgid "Confirmation code" msgid "Confirmation code"
msgstr "" msgstr "확인 코드"
#: pretix/base/forms/questions.py:137 pretix/base/forms/questions.py:264 #: pretix/base/forms/questions.py:137 pretix/base/forms/questions.py:264
msgctxt "name_salutation" msgctxt "name_salutation"
@@ -3411,14 +3331,12 @@ msgstr ""
"야 할 수도 있습니다." "야 할 수도 있습니다."
#: pretix/base/forms/questions.py:1181 #: pretix/base/forms/questions.py:1181
#, fuzzy
#| msgid "Cancellation requested"
msgid "No invoice requested" msgid "No invoice requested"
msgstr "취소 요청" msgstr "청구서 요청되지 않음"
#: pretix/base/forms/questions.py:1183 #: pretix/base/forms/questions.py:1183
msgid "Invoice transmission method" msgid "Invoice transmission method"
msgstr "" msgstr "청구서 전송 방식"
#: pretix/base/forms/questions.py:1329 #: pretix/base/forms/questions.py:1329
msgid "You need to provide a company name." msgid "You need to provide a company name."
@@ -3432,21 +3350,20 @@ msgstr "이름을 입력해야 합니다."
msgid "" msgid ""
"If you enter an invoice address, you also need to select an invoice " "If you enter an invoice address, you also need to select an invoice "
"transmission method." "transmission method."
msgstr "" msgstr "당신이 청구서 주소를 입력하신다면, 청구서 수신 방법도 선택하셔야 합니다."
#: pretix/base/forms/questions.py:1385 #: pretix/base/forms/questions.py:1385
#, fuzzy
#| msgid "The selected media type is not enabled in your organizer settings."
msgid "" msgid ""
"The selected transmission type is not available in your country or for your " "The selected transmission type is not available in your country or for your "
"type of address." "type of address."
msgstr "선택한 미디어 유형이 정리함 설정에서 활성화되지 않았습니다." msgstr "선택한 전송 유형은 당신의 국가 또는 지역에서 이용하실 수 없습니다."
#: pretix/base/forms/questions.py:1394 #: pretix/base/forms/questions.py:1394
msgid "" msgid ""
"The selected type of invoice transmission requires a field that is currently " "The selected type of invoice transmission requires a field that is currently "
"not available, please reach out to the organizer." "not available, please reach out to the organizer."
msgstr "" msgstr "선택하신 청구서 전송 유형은 현재 사용할 수 없는 필드의 입력을 필요로 하니, 주"
"최자에게 문의 해 주세요."
#: pretix/base/forms/questions.py:1398 #: pretix/base/forms/questions.py:1398
msgid "This field is required for the selected type of invoice transmission." msgid "This field is required for the selected type of invoice transmission."
@@ -3466,10 +3383,8 @@ msgstr ""
"대가 대신 사용됩니다." "대가 대신 사용됩니다."
#: pretix/base/forms/user.py:77 #: pretix/base/forms/user.py:77
#, fuzzy
#| msgid "Attendee email address"
msgid "Change email address" msgid "Change email address"
msgstr "참석자 이메일 주소" msgstr "이메일 주소 변경"
#: pretix/base/forms/user.py:83 #: pretix/base/forms/user.py:83
msgid "Device name" msgid "Device name"
@@ -3518,16 +3433,12 @@ msgstr ""
"이 이메일 주소와 관련된 계정이 이미 있습니다. 다른 계정을 선택해 주세요." "이 이메일 주소와 관련된 계정이 이미 있습니다. 다른 계정을 선택해 주세요."
#: pretix/base/forms/user.py:179 #: pretix/base/forms/user.py:179
#, fuzzy
#| msgid "Email address"
msgid "Old email address" msgid "Old email address"
msgstr "이메일 주소" msgstr "이전 이메일 주소"
#: pretix/base/forms/user.py:180 #: pretix/base/forms/user.py:180
#, fuzzy
#| msgid "Email address"
msgid "New email address" msgid "New email address"
msgstr "이메일 주소" msgstr "이메일 주소"
#: pretix/base/forms/validators.py:51 #: pretix/base/forms/validators.py:51
msgid "" msgid ""
@@ -3572,23 +3483,22 @@ msgstr "개별 고객"
#: pretix/base/invoicing/email.py:50 #: pretix/base/invoicing/email.py:50
msgid "Email invoice directly to accounting department" msgid "Email invoice directly to accounting department"
msgstr "" msgstr "청구서를 회계 부서로 이메일로 바로 보내기"
#: pretix/base/invoicing/email.py:51 #: pretix/base/invoicing/email.py:51
msgid "" msgid ""
"If not selected, the invoice will be sent to you using the email address " "If not selected, the invoice will be sent to you using the email address "
"listed above." "listed above."
msgstr "" msgstr "선택되지 않은 경우, 청구서는 위의 이메일 주소를 통해 당신에게 보내질 것 입니"
"다."
#: pretix/base/invoicing/email.py:55 #: pretix/base/invoicing/email.py:55
#, fuzzy
#| msgid "Email address verified"
msgid "Email address for invoice" msgid "Email address for invoice"
msgstr "이메일 주소 확인" msgstr "청구서용 이메일 주소"
#: pretix/base/invoicing/email.py:91 #: pretix/base/invoicing/email.py:91
msgid "PDF via email" msgid "PDF via email"
msgstr "" msgstr "이메일로 PDF"
#: pretix/base/invoicing/national.py:37 #: pretix/base/invoicing/national.py:37
msgctxt "italian_invoice" msgctxt "italian_invoice"
@@ -3613,11 +3523,9 @@ msgid "Address for certified electronic mail"
msgstr "" msgstr ""
#: pretix/base/invoicing/national.py:57 #: pretix/base/invoicing/national.py:57
#, fuzzy
#| msgid "Recipient"
msgctxt "italian_invoice" msgctxt "italian_invoice"
msgid "Recipient code" msgid "Recipient code"
msgstr "영수증" msgstr "영수증 코드"
#: pretix/base/invoicing/national.py:81 #: pretix/base/invoicing/national.py:81
msgctxt "italian_invoice" msgctxt "italian_invoice"
@@ -3697,7 +3605,6 @@ msgid ""
"until {to_date}" "until {to_date}"
msgstr "" msgstr ""
"{from_date}\n" "{from_date}\n"
"\n"
"{to_date}까지" "{to_date}까지"
#: pretix/base/invoicing/pdf.py:609 pretix/base/services/mail.py:512 #: 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} 총합" msgstr "단일 가격: {net_price} 순 / {gross_price} 총합"
#: pretix/base/invoicing/pdf.py:901 #: pretix/base/invoicing/pdf.py:901
#, fuzzy, python-brace-format #, python-brace-format
msgctxt "invoice" msgctxt "invoice"
msgid "Single price: {price}" msgid "Single price: {price}"
msgstr "단일 가격: {가격}" msgstr "단일 가격: {price}"
#: pretix/base/invoicing/pdf.py:944 pretix/base/invoicing/pdf.py:949 #: pretix/base/invoicing/pdf.py:944 pretix/base/invoicing/pdf.py:949
msgctxt "invoice" msgctxt "invoice"
@@ -3808,12 +3715,10 @@ msgid "Remaining amount"
msgstr "잔액" msgstr "잔액"
#: pretix/base/invoicing/pdf.py:1009 #: pretix/base/invoicing/pdf.py:1009
#, fuzzy, python-brace-format #, python-brace-format
#| msgctxt "invoice"
#| msgid "Event date: {date_range}"
msgctxt "invoice" msgctxt "invoice"
msgid "Invoice period: {daterange}" msgid "Invoice period: {daterange}"
msgstr "이벤트 날짜: {date_range}" msgstr "청구서 기간: {daterange}"
#: pretix/base/invoicing/pdf.py:1040 #: pretix/base/invoicing/pdf.py:1040
msgctxt "invoice" msgctxt "invoice"
@@ -3836,13 +3741,12 @@ msgid "Included taxes"
msgstr "세금 포함" msgstr "세금 포함"
#: pretix/base/invoicing/pdf.py:1100 #: pretix/base/invoicing/pdf.py:1100
#, fuzzy, python-brace-format #, python-brace-format
msgctxt "invoice" msgctxt "invoice"
msgid "" msgid ""
"Using the conversion rate of 1:{rate} as published by the {authority} on " "Using the conversion rate of 1:{rate} as published by the {authority} on "
"{date}, this corresponds to:" "{date}, this corresponds to:"
msgstr "" msgstr "{date}에 {authority}에서 발표한 1:{rate}의 변환율을 사용하면 다음과 같습니다:"
"{날짜}에 {당국}에서 발표한 1:{세율}의 변환율을 사용하면 다음과 같습니다:"
#: pretix/base/invoicing/pdf.py:1115 #: pretix/base/invoicing/pdf.py:1115
#, fuzzy, python-brace-format #, fuzzy, python-brace-format
@@ -3850,9 +3754,8 @@ msgctxt "invoice"
msgid "" msgid ""
"Using the conversion rate of 1:{rate} as published by the {authority} on " "Using the conversion rate of 1:{rate} as published by the {authority} on "
"{date}, the invoice total corresponds to {total}." "{date}, the invoice total corresponds to {total}."
msgstr "" msgstr "{date}에 {authority}에서 게시한 1:{rate}의 변환율을 사용하면 송장 총액이 "
"{날짜}에 {당국}에서 게시한 1:{세율}의 변환율을 사용하면 송장 총액이 {총합}에 " "{total}에 해당합니다."
"해당합니다."
#: pretix/base/invoicing/pdf.py:1129 #: pretix/base/invoicing/pdf.py:1129
msgid "Default invoice renderer (European-style letter)" msgid "Default invoice renderer (European-style letter)"

View File

@@ -7,16 +7,16 @@ msgstr ""
"Project-Id-Version: 1\n" "Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 13:57+0000\n" "POT-Creation-Date: 2025-11-27 13:57+0000\n"
"PO-Revision-Date: 2025-10-26 18:00+0000\n" "PO-Revision-Date: 2025-12-08 07:00+0000\n"
"Last-Translator: Jan Van Haver <jan.van.haver@gmail.com>\n" "Last-Translator: Ruud Hendrickx <ruud@leckxicon.eu>\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/" "Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/>"
">\n" "\n"
"Language: nl\n" "Language: nl\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.14\n" "X-Generator: Weblate 5.14.3\n"
#: pretix/_base_settings.py:87 #: pretix/_base_settings.py:87
msgid "English" msgid "English"
@@ -44,7 +44,7 @@ msgstr "Catalaans"
#: pretix/_base_settings.py:93 #: pretix/_base_settings.py:93
msgid "Chinese (simplified)" msgid "Chinese (simplified)"
msgstr "Chinees (versimpeld)" msgstr "Chinees (vereenvoudigd)"
#: pretix/_base_settings.py:94 #: pretix/_base_settings.py:94
msgid "Chinese (traditional)" msgid "Chinese (traditional)"

View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 13:57+0000\n" "POT-Creation-Date: 2025-11-27 13:57+0000\n"
"PO-Revision-Date: 2024-02-13 16:00+0000\n" "PO-Revision-Date: 2025-12-08 07:00+0000\n"
"Last-Translator: Wessel Stam <info@wesselstam.nl>\n" "Last-Translator: Ruud Hendrickx <ruud@leckxicon.eu>\n"
"Language-Team: Dutch (informal) <https://translate.pretix.eu/projects/pretix/" "Language-Team: Dutch (informal) <https://translate.pretix.eu/projects/pretix/"
"pretix/nl_Informal/>\n" "pretix/nl_Informal/>\n"
"Language: nl_Informal\n" "Language: nl_Informal\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.3.1\n" "X-Generator: Weblate 5.14.3\n"
#: pretix/_base_settings.py:87 #: pretix/_base_settings.py:87
msgid "English" msgid "English"
@@ -37,15 +37,15 @@ msgstr "Arabisch"
#: pretix/_base_settings.py:91 #: pretix/_base_settings.py:91
msgid "Basque" msgid "Basque"
msgstr "" msgstr "Baskisch"
#: pretix/_base_settings.py:92 #: pretix/_base_settings.py:92
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr "Catalaans"
#: pretix/_base_settings.py:93 #: pretix/_base_settings.py:93
msgid "Chinese (simplified)" msgid "Chinese (simplified)"
msgstr "Chinees (versimpeld)" msgstr "Chinees (vereenvoudigd)"
#: pretix/_base_settings.py:94 #: pretix/_base_settings.py:94
msgid "Chinese (traditional)" msgid "Chinese (traditional)"
@@ -57,7 +57,7 @@ msgstr "Tsjechisch"
#: pretix/_base_settings.py:96 #: pretix/_base_settings.py:96
msgid "Croatian" msgid "Croatian"
msgstr "" msgstr "Kroatisch"
#: pretix/_base_settings.py:97 #: pretix/_base_settings.py:97
msgid "Danish" msgid "Danish"
@@ -89,7 +89,7 @@ msgstr "Grieks"
#: pretix/_base_settings.py:104 #: pretix/_base_settings.py:104
msgid "Hebrew" msgid "Hebrew"
msgstr "" msgstr "Hebreeuws"
#: pretix/_base_settings.py:105 #: pretix/_base_settings.py:105
msgid "Indonesian" msgid "Indonesian"
@@ -101,7 +101,7 @@ msgstr "Italiaans"
#: pretix/_base_settings.py:107 #: pretix/_base_settings.py:107
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr "Japans"
#: pretix/_base_settings.py:108 #: pretix/_base_settings.py:108
msgid "Latvian" msgid "Latvian"
@@ -133,11 +133,11 @@ msgstr "Russisch"
#: pretix/_base_settings.py:115 #: pretix/_base_settings.py:115
msgid "Slovak" msgid "Slovak"
msgstr "" msgstr "Slowaaks"
#: pretix/_base_settings.py:116 #: pretix/_base_settings.py:116
msgid "Swedish" msgid "Swedish"
msgstr "" msgstr "Zweeds"
#: pretix/_base_settings.py:117 #: pretix/_base_settings.py:117
msgid "Spanish" msgid "Spanish"
@@ -145,7 +145,7 @@ msgstr "Spaans"
#: pretix/_base_settings.py:118 #: pretix/_base_settings.py:118
msgid "Spanish (Latin America)" msgid "Spanish (Latin America)"
msgstr "" msgstr "Spaans (Latijns-Amerika)"
#: pretix/_base_settings.py:119 #: pretix/_base_settings.py:119
msgid "Turkish" msgid "Turkish"
@@ -261,7 +261,7 @@ msgstr ""
#: pretix/api/serializers/event.py:234 pretix/api/serializers/event.py:554 #: pretix/api/serializers/event.py:234 pretix/api/serializers/event.py:554
#, python-brace-format #, python-brace-format
msgid "Meta data property '{name}' does not exist." msgid "Meta data property '{name}' does not exist."
msgstr "Metadataeigenschap '{name}' bestaat niet." msgstr "Metadata-eigenschap '{name}' bestaat niet."
#: pretix/api/serializers/event.py:237 pretix/api/serializers/event.py:557 #: pretix/api/serializers/event.py:237 pretix/api/serializers/event.py:557
#, python-brace-format #, python-brace-format
@@ -32970,7 +32970,7 @@ msgstr ""
#: pretix/plugins/reports/exporters.py:257 #: pretix/plugins/reports/exporters.py:257
msgctxt "export_category" msgctxt "export_category"
msgid "Analysis" msgid "Analysis"
msgstr "" msgstr "Analyse"
#: pretix/plugins/reports/accountingreport.py:83 #: pretix/plugins/reports/accountingreport.py:83
#, fuzzy #, fuzzy

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 13:57+0000\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-11 01:00+0000\n"
"Last-Translator: Renne Rocha <renne@rocha.dev.br>\n" "Last-Translator: Renne Rocha <renne@rocha.dev.br>\n"
"Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/" "Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/"
"pretix/pretix/pt_BR/>\n" "pretix/pretix/pt_BR/>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\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 #: pretix/_base_settings.py:87
msgid "English" msgid "English"
@@ -253,9 +253,8 @@ msgid ""
"Events cannot be created as 'live'. Quotas and payment must be added to the " "Events cannot be created as 'live'. Quotas and payment must be added to the "
"event before sales can go live." "event before sales can go live."
msgstr "" msgstr ""
"Os eventos não podem ser criados como 'ativo'. As cotas e métodos de " "Eventos não podem ser criados como 'ativo'. Cotas e métodos de pagamento "
"pagamento devem ser adicionados ao evento antes que as vendas possam ser " "devem ser adicionados ao evento antes que as vendas se iniciem."
"iniciadas."
#: pretix/api/serializers/event.py:234 pretix/api/serializers/event.py:554 #: pretix/api/serializers/event.py:234 pretix/api/serializers/event.py:554
#, python-brace-format #, python-brace-format
@@ -358,7 +357,8 @@ msgstr ""
#: pretix/api/serializers/item.py:587 pretix/control/forms/item.py:177 #: pretix/api/serializers/item.py:587 pretix/control/forms/item.py:177
msgid "Question cannot depend on a question asked during check-in." msgid "Question cannot depend on a question asked during check-in."
msgstr "A pergunta não pode depender de uma pergunta feita durante o check-in." msgstr ""
"A pergunta não pode depender de outra pergunta feita durante o check-in."
#: pretix/api/serializers/item.py:592 pretix/control/forms/item.py:182 #: pretix/api/serializers/item.py:592 pretix/control/forms/item.py:182
msgid "Circular dependency between questions detected." msgid "Circular dependency between questions detected."
@@ -816,28 +816,22 @@ msgstr ""
"confirme primeiro o endereço de email na sua conta." "confirme primeiro o endereço de email na sua conta."
#: pretix/base/datasync/datasync.py:263 #: pretix/base/datasync/datasync.py:263
#, fuzzy, python-brace-format #, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
msgid "" msgid ""
"Field \"{field_name}\" does not exist. Please check your {provider_name} " "Field \"{field_name}\" does not exist. Please check your {provider_name} "
"settings." "settings."
msgstr "" msgstr ""
"O campo \"{field_name}\" não é válido para {available_inputs}. Verifique as " "O campo \"{field_name}\" não existe. Por favor, verifique as configurações "
"configurações de {provider_name}." "de {provider_name}."
#: pretix/base/datasync/datasync.py:270 #: pretix/base/datasync/datasync.py:270
#, fuzzy, python-brace-format #, python-brace-format
#| msgid ""
#| "Field \"{field_name}\" is not valid for {available_inputs}. Please check "
#| "your {provider_name} settings."
msgid "" msgid ""
"Field \"{field_name}\" requires {required_input}, but only got " "Field \"{field_name}\" requires {required_input}, but only got "
"{available_inputs}. Please check your {provider_name} settings." "{available_inputs}. Please check your {provider_name} settings."
msgstr "" msgstr ""
"O campo \"{field_name}\" não é válido para {available_inputs}. Verifique as " "Campo \"{field_name}\" exige {required_input}, mas apenas {available_inputs} "
"configurações de {provider_name}." "foram fornecidas. Por favor, verifique as configurações de {provider_name}."
#: pretix/base/datasync/datasync.py:281 #: pretix/base/datasync/datasync.py:281
#, python-brace-format #, python-brace-format
@@ -2137,7 +2131,7 @@ msgstr "Comprar este produto requer aprovação"
#: pretix/base/exporters/items.py:85 pretix/base/models/items.py:627 #: pretix/base/exporters/items.py:85 pretix/base/models/items.py:627
msgid "Only sell this product as part of a bundle" msgid "Only sell this product as part of a bundle"
msgstr "Disponível apenas como parte de um pacote" msgstr "Venda este produto apenas como parte de um pacote"
#: pretix/base/exporters/items.py:86 pretix/base/models/items.py:634 #: pretix/base/exporters/items.py:86 pretix/base/models/items.py:634
msgid "Allow product to be canceled or changed" msgid "Allow product to be canceled or changed"
@@ -2839,7 +2833,7 @@ msgstr "Código de Status"
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_assign.html:25 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_assign.html:25
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/transaction_list.html:13 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/transaction_list.html:13
msgid "Amount" msgid "Amount"
msgstr "Valor" msgstr "Quantidade"
#: pretix/base/exporters/orderlist.py:1098 #: pretix/base/exporters/orderlist.py:1098
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:102 #: pretix/control/templates/pretixcontrol/boxoffice/payment.html:102
@@ -3286,10 +3280,8 @@ msgid "Repeat password"
msgstr "Repita a senha" msgstr "Repita a senha"
#: pretix/base/forms/auth.py:220 pretix/base/forms/user.py:99 #: pretix/base/forms/auth.py:220 pretix/base/forms/user.py:99
#, fuzzy
#| msgid "Email address"
msgid "Your 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/base/forms/auth.py:327 pretix/control/forms/orders.py:1041
#: pretix/control/templates/pretixcontrol/shredder/download.html:53 #: pretix/control/templates/pretixcontrol/shredder/download.html:53
@@ -3400,6 +3392,8 @@ msgid ""
"If you enter an invoice address, you also need to select an invoice " "If you enter an invoice address, you also need to select an invoice "
"transmission method." "transmission method."
msgstr "" msgstr ""
"Se você informar um endereço para a fatura, você também deverá selecionar um "
"método para transmissão da fatura."
#: pretix/base/forms/questions.py:1385 #: pretix/base/forms/questions.py:1385
msgid "" msgid ""
@@ -3414,6 +3408,8 @@ msgid ""
"The selected type of invoice transmission requires a field that is currently " "The selected type of invoice transmission requires a field that is currently "
"not available, please reach out to the organizer." "not available, please reach out to the organizer."
msgstr "" msgstr ""
"O tipo de transmissão de fatura selecionado exige um campo que não está "
"disponível atualmente. Por favor, entre em contato com a organização."
#: pretix/base/forms/questions.py:1398 #: pretix/base/forms/questions.py:1398
msgid "This field is required for the selected type of invoice transmission." msgid "This field is required for the selected type of invoice transmission."
@@ -3433,10 +3429,8 @@ msgstr ""
"as exibições de eventos, o fuso horário do evento é usado." "as exibições de eventos, o fuso horário do evento é usado."
#: pretix/base/forms/user.py:77 #: pretix/base/forms/user.py:77
#, fuzzy
#| msgid "Attendee email address"
msgid "Change email address" msgid "Change email address"
msgstr "Email do participante" msgstr "Alterar endereço de e-mail"
#: pretix/base/forms/user.py:83 #: pretix/base/forms/user.py:83
msgid "Device name" msgid "Device name"
@@ -3486,16 +3480,12 @@ msgstr ""
"um diferente." "um diferente."
#: pretix/base/forms/user.py:179 #: pretix/base/forms/user.py:179
#, fuzzy
#| msgid "Email address"
msgid "Old email address" msgid "Old email address"
msgstr "Endereço de email" msgstr "Endereço de e-mail antigo"
#: pretix/base/forms/user.py:180 #: pretix/base/forms/user.py:180
#, fuzzy
#| msgid "Email address"
msgid "New email address" msgid "New email address"
msgstr "Endereço de email" msgstr "Endereço de e-mail novo"
#: pretix/base/forms/validators.py:51 #: pretix/base/forms/validators.py:51
msgid "" msgid ""
@@ -3540,22 +3530,17 @@ msgid "Individual customer"
msgstr "Cliente pessoa física" msgstr "Cliente pessoa física"
#: pretix/base/invoicing/email.py:50 #: 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" msgid "Email invoice directly to accounting department"
msgstr "" msgstr ""
"Para enviar a fatura diretamente ao seu departamento de contabilidade, " "Enviar e-mail com a fatura diretamente para departamento de contabilidade"
"insira o endereço de e-mail:"
#: pretix/base/invoicing/email.py:51 #: pretix/base/invoicing/email.py:51
#, fuzzy
#| msgid "The invoice was sent to the designated email address."
msgid "" msgid ""
"If not selected, the invoice will be sent to you using the email address " "If not selected, the invoice will be sent to you using the email address "
"listed above." "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 #: pretix/base/invoicing/email.py:55
msgid "Email address for invoice" msgid "Email address for invoice"
@@ -3778,12 +3763,10 @@ msgid "Remaining amount"
msgstr "Valor restante" msgstr "Valor restante"
#: pretix/base/invoicing/pdf.py:1009 #: pretix/base/invoicing/pdf.py:1009
#, fuzzy, python-brace-format #, python-brace-format
#| msgctxt "invoice"
#| msgid "Event date: {date_range}"
msgctxt "invoice" msgctxt "invoice"
msgid "Invoice period: {daterange}" msgid "Invoice period: {daterange}"
msgstr "Data do evento: {date_range}" msgstr "Período da fatura: {daterange}"
#: pretix/base/invoicing/pdf.py:1040 #: pretix/base/invoicing/pdf.py:1040
msgctxt "invoice" msgctxt "invoice"
@@ -3868,12 +3851,9 @@ msgid "Peppol participant ID"
msgstr "" msgstr ""
#: pretix/base/invoicing/peppol.py:170 #: pretix/base/invoicing/peppol.py:170
#, fuzzy
#| msgctxt "italian_invoice"
#| msgid "Fiscal code"
msgctxt "peppol_invoice" msgctxt "peppol_invoice"
msgid "Visual copy" msgid "Visual copy"
msgstr "Código fiscal" msgstr "Cópia visual"
#: pretix/base/invoicing/peppol.py:175 #: pretix/base/invoicing/peppol.py:175
msgctxt "peppol_invoice" msgctxt "peppol_invoice"
@@ -4357,10 +4337,8 @@ msgid ""
msgstr "" msgstr ""
#: pretix/base/models/auth.py:392 #: pretix/base/models/auth.py:392
#, fuzzy
#| msgid "Confirmation code"
msgid "pretix 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/base/models/auth.py:435
#: pretix/control/templates/pretixcontrol/auth/forgot.html:7 #: pretix/control/templates/pretixcontrol/auth/forgot.html:7
@@ -4862,11 +4840,11 @@ msgid ""
"you can also choose to use a random value. This will be used in URLs, order " "you can also choose to use a random value. This will be used in URLs, order "
"codes, invoice numbers, and bank transfer references." "codes, invoice numbers, and bank transfer references."
msgstr "" msgstr ""
"Deve ser curts, conter apenas letras minúsculas, números, pontos e traços, e " "Deve conter apenas letras minúsculas, números, pontos e traços, sendo "
"ser exclusiva entre seus eventos. Recomendamos algum tipo de abreviação ou " "exclusiva para suas eventos. Recomendamos uma abreviação ou data com menos "
"uma data com menos de 10 caracteres que sejam facilmente lembradas, mas você " "de 10 caracteres e que seja facilmente lembrada, mas você também pode usar "
"também pode usar um valor aleatório. Esta informação será usada em URLs, " "um valor aleatório. Esta informação será usada em URLs, códigos de pedido, "
"códigos de pedido, números de fatura e referências de transferência bancária." "número de faturas e referências de transações bancárias."
#: pretix/base/models/event.py:607 pretix/base/models/organizer.py:89 #: pretix/base/models/event.py:607 pretix/base/models/organizer.py:89
msgid "The slug may only contain letters, numbers, dots and dashes." msgid "The slug may only contain letters, numbers, dots and dashes."
@@ -5394,24 +5372,16 @@ msgstr ""
"não tiver variações, este preço será usado." "não tiver variações, este preço será usado."
#: pretix/base/models/items.py:506 #: pretix/base/models/items.py:506
#, fuzzy
#| msgid ""
#| "If this option is active, your users can choose the price themselves. The "
#| "price configured above is then interpreted as the minimum price a user "
#| "has to enter. You could use this e.g. to collect additional donations for "
#| "your event. This is currently not supported for products that are bought "
#| "as an add-on to other products."
msgid "" msgid ""
"If this option is active, your users can choose the price themselves. The " "If this option is active, your users can choose the price themselves. The "
"price configured above is then interpreted as the minimum price a user has " "price configured above is then interpreted as the minimum price a user has "
"to enter. You could use this e.g. to collect additional donations for your " "to enter. You could use this e.g. to collect additional donations for your "
"event." "event."
msgstr "" msgstr ""
"Se esta opção estiver ativa, seus usuários podem escolher o próprio preço. O " "Se esta opção está selecionada, os usuários poderão pagar o preço que "
"preço configurado acima é então interpretado como o preço mínimo que um " "desejarem. O preço padrão acima será interpretado como o preço mínimo que o "
"usuário deve inserir. Você pode usar isso, por exemplo, para coletar doações " "usuário deverá pagar. Você pode usar esta opção, por exemplo, para coletar "
"adicionais para o seu evento. No momento, isto não é suportado por produtos " "doações adicionais para o seu evento."
"comprados como um complemento de outros produtos."
#: pretix/base/models/items.py:511 pretix/base/models/items.py:1175 #: pretix/base/models/items.py:511 pretix/base/models/items.py:1175
msgid "Suggested price" msgid "Suggested price"
@@ -6298,9 +6268,6 @@ msgstr ""
"insira um valor possível por linha." "insira um valor possível por linha."
#: pretix/base/models/items.py:2310 #: pretix/base/models/items.py:2310
#, fuzzy
#| msgctxt "timeframe"
#| msgid "Start"
msgid "Start" msgid "Start"
msgstr "Início" msgstr "Início"
@@ -7912,10 +7879,8 @@ msgid "123.45 EUR"
msgstr "123.45 BRL" msgstr "123.45 BRL"
#: pretix/base/pdf.py:166 #: pretix/base/pdf.py:166
#, fuzzy
#| msgid "Price including add-ons"
msgid "Price including bundled products" msgid "Price including bundled products"
msgstr "Preço incluindo complementos" msgstr "Preços incluindo produtos empacotados"
#: pretix/base/pdf.py:175 #: pretix/base/pdf.py:175
#, fuzzy #, fuzzy
@@ -9878,10 +9843,8 @@ msgid "Require a phone number per order"
msgstr "Exigir um número de telefone por pedido" msgstr "Exigir um número de telefone por pedido"
#: pretix/base/settings.py:482 #: pretix/base/settings.py:482
#, fuzzy
#| msgid "including all taxes"
msgid "Rounding of taxes" msgid "Rounding of taxes"
msgstr "incluindo todos os impostos" msgstr "Arredondamento dos impostos"
#: pretix/base/settings.py:486 #: pretix/base/settings.py:486
msgid "" msgid ""
@@ -10424,18 +10387,12 @@ msgid "Automatic, but prefer invoice date over event date"
msgstr "" msgstr ""
#: pretix/base/settings.py:1142 pretix/base/settings.py:1153 #: pretix/base/settings.py:1142 pretix/base/settings.py:1153
#, fuzzy
#| msgctxt "invoice"
#| msgid "Invoice date"
msgid "Invoice date" msgid "Invoice date"
msgstr "Data da fatura" msgstr "Data da fatura"
#: pretix/base/settings.py:1146 #: pretix/base/settings.py:1146
#, fuzzy
#| msgctxt "subevent"
#| msgid "Date ordering"
msgid "Date of service" msgid "Date of service"
msgstr "Ordenação de datas" msgstr "Data do serviço"
#: pretix/base/settings.py:1155 #: pretix/base/settings.py:1155
msgid "" msgid ""
@@ -11299,8 +11256,8 @@ msgstr "Endereço de contato"
#: pretix/base/settings.py:2161 pretix/control/forms/event.py:1824 #: pretix/base/settings.py:2161 pretix/control/forms/event.py:1824
msgid "We'll show this publicly to allow attendees to contact you." msgid "We'll show this publicly to allow attendees to contact you."
msgstr "" msgstr ""
"Mostraremos isso publicamente para permitir que os participantes entrem em " "Será exibido publicamente para que participantes possam entrar em contato "
"contato com você." "com você."
#: pretix/base/settings.py:2169 pretix/control/forms/event.py:1816 #: pretix/base/settings.py:2169 pretix/control/forms/event.py:1816
msgid "Imprint URL" msgid "Imprint URL"
@@ -12166,17 +12123,7 @@ msgid "Invoice {invoice_number}"
msgstr "Fatura {invoice_number}" msgstr "Fatura {invoice_number}"
#: pretix/base/settings.py:2789 #: pretix/base/settings.py:2789
#, fuzzy, python-brace-format #, 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"
msgid "" msgid ""
"Hello,\n" "Hello,\n"
"\n" "\n"
@@ -12189,12 +12136,12 @@ msgid ""
msgstr "" msgstr ""
"Olá,\n" "Olá,\n"
"\n" "\n"
"Segue anexo uma nova fatura para o pedido {code} para {evento} . Este pedido " "Em anexo você encontrará uma nova fatura para o pedido {code} para {event}. "
"foi feito por {order_email}.\n" "Este pedido foi feito por {order_email}.\n"
"\n" "\n"
"Atenciosamente, \n" "Atenciosamente, \n"
"\n" "\n"
"Equipe organizadora de {event}" "Organização {event}"
#: pretix/base/settings.py:2807 pretix/base/settings.py:2823 #: pretix/base/settings.py:2807 pretix/base/settings.py:2823
#, python-brace-format #, python-brace-format
@@ -13256,13 +13203,13 @@ msgid "Contact:"
msgstr "Contato:" msgstr "Contato:"
#: pretix/base/templates/pretixbase/email/order_details.html:54 #: pretix/base/templates/pretixbase/email/order_details.html:54
#, fuzzy, python-format #, python-format
#| msgid ""
#| "You are receiving this email because you placed an order for {event}."
msgid "" msgid ""
"You are receiving this email because you placed an order for " "You are receiving this email because you placed an order for "
"<strong>%(event)s</strong>." "<strong>%(event)s</strong>."
msgstr "Você está recebendo este email porque fez um pedido para {event}." msgstr ""
"Você está recebendo este email por ter realizado um pedido para <strong>%"
"(event)s</strong>."
#: pretix/base/templates/pretixbase/email/order_details.html:93 #: pretix/base/templates/pretixbase/email/order_details.html:93
#: pretix/control/templates/pretixcontrol/organizers/customer.html:23 #: pretix/control/templates/pretixcontrol/organizers/customer.html:23
@@ -13937,7 +13884,7 @@ msgstr "Padrão ({value})"
#: pretix/control/forms/event.py:380 #: pretix/control/forms/event.py:380
msgid "The currency cannot be changed because orders already exist." msgid "The currency cannot be changed because orders already exist."
msgstr "" msgstr "A moeda não pode ser alterada pois já foram realizados pedidos."
#: pretix/control/forms/event.py:391 pretix/control/forms/event.py:404 #: pretix/control/forms/event.py:391 pretix/control/forms/event.py:404
msgid "Domain" msgid "Domain"
@@ -14023,21 +13970,16 @@ msgstr ""
"que o cartão-presente é emitido." "que o cartão-presente é emitido."
#: pretix/control/forms/event.py:813 #: pretix/control/forms/event.py:813
#, fuzzy
#| msgid "including all taxes"
msgid "Prices including tax" msgid "Prices including tax"
msgstr "incluindo todos os impostos" msgstr "Preços incluindo impostos"
#: pretix/control/forms/event.py:814 #: pretix/control/forms/event.py:814
msgid "Recommended if you sell tickets at least partly to consumers." msgid "Recommended if you sell tickets at least partly to consumers."
msgstr "" msgstr ""
#: pretix/control/forms/event.py:818 #: pretix/control/forms/event.py:818
#, fuzzy
#| msgctxt "reporting_timeframe"
#| msgid "All future (excluding today)"
msgid "Prices excluding tax" msgid "Prices excluding tax"
msgstr "Todos os futuros (excluindo hoje)" msgstr "Preços excluindo impostos"
#: pretix/control/forms/event.py:819 #: pretix/control/forms/event.py:819
msgid "Recommended only if you sell tickets primarily to business customers." msgid "Recommended only if you sell tickets primarily to business customers."
@@ -17401,19 +17343,14 @@ msgid "Your account has been disabled."
msgstr "Sua conta foi desativada." msgstr "Sua conta foi desativada."
#: pretix/control/logdisplay.py:672 #: pretix/control/logdisplay.py:672
#, fuzzy, python-brace-format #, python-brace-format
#| msgid ""
#| "The email address has been changed from \"{old_email}\" to \"{new_email}"
#| "\"."
msgid "Your email address has been changed from {old_email} to {email}." msgid "Your email address has been changed from {old_email} to {email}."
msgstr "" msgstr "Seu endereço de email foi modificado de {old_email} para {email}."
"O endereço de e-mail foi alterado de \"{old_email}\" para \"{new_email}\"."
#: pretix/control/logdisplay.py:673 #: pretix/control/logdisplay.py:673
#, fuzzy, python-brace-format #, python-brace-format
#| msgid "Your email address has been updated."
msgid "Your email address {email} has been confirmed." 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 #: pretix/control/logdisplay.py:685
#, python-brace-format #, python-brace-format
@@ -19690,6 +19627,20 @@ msgid ""
"Best regards,\n" "Best regards,\n"
"Your pretix team\n" "Your pretix team\n"
msgstr "" 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 #: pretix/control/templates/pretixcontrol/email/email_setup.txt:1
#, python-format #, python-format
@@ -21314,10 +21265,8 @@ msgid "with custom rules"
msgstr "Regras personalizadas" msgstr "Regras personalizadas"
#: pretix/control/templates/pretixcontrol/event/tax.html:110 #: pretix/control/templates/pretixcontrol/event/tax.html:110
#, fuzzy
#| msgid "Base settings"
msgid "Tax 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:4
#: pretix/control/templates/pretixcontrol/event/tax_delete.html:6 #: pretix/control/templates/pretixcontrol/event/tax_delete.html:6
@@ -21980,6 +21929,9 @@ msgid ""
"your event. By default, we will only offer ticket downloads for these " "your event. By default, we will only offer ticket downloads for these "
"products." "products."
msgstr "" msgstr ""
"Cada compra deste produto representa uma pessoa que terá permissão de entrar "
"no seu evento. Por padrão, só oferecemos o download de ingressos para estes "
"produtos."
#: pretix/control/templates/pretixcontrol/item/create.html:33 #: pretix/control/templates/pretixcontrol/item/create.html:33
#: pretix/control/templates/pretixcontrol/item/index.html:41 #: pretix/control/templates/pretixcontrol/item/index.html:41
@@ -26144,7 +26096,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/shredder/download.html:29 #: pretix/control/templates/pretixcontrol/shredder/download.html:29
msgid "Download data" msgid "Download data"
msgstr "" msgstr "Baixar dados"
#: pretix/control/templates/pretixcontrol/shredder/download.html:34 #: pretix/control/templates/pretixcontrol/shredder/download.html:34
msgid "Step 2: Confirm deletion" msgid "Step 2: Confirm deletion"
@@ -26475,7 +26427,7 @@ msgstr "Adicionar um dispositivo de autenticação de dois fatores"
#: pretix/control/templates/pretixcontrol/user/2fa_confirm_totp.html:8 #: pretix/control/templates/pretixcontrol/user/2fa_confirm_totp.html:8
msgid "To set up this device, please follow the following steps:" 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 #: pretix/control/templates/pretixcontrol/user/2fa_confirm_totp.html:12
msgid "Download the Google Authenticator application to your phone:" msgid "Download the Google Authenticator application to your phone:"
@@ -26901,11 +26853,11 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/user/staff_session_edit.html:6 #: pretix/control/templates/pretixcontrol/user/staff_session_edit.html:6
msgid "Session notes" msgid "Session notes"
msgstr "" msgstr "Notas de sessão"
#: pretix/control/templates/pretixcontrol/user/staff_session_edit.html:17 #: pretix/control/templates/pretixcontrol/user/staff_session_edit.html:17
msgid "Audit log" msgid "Audit log"
msgstr "" msgstr "Log de auditoria"
#: pretix/control/templates/pretixcontrol/user/staff_session_edit.html:30 #: pretix/control/templates/pretixcontrol/user/staff_session_edit.html:30
msgid "Method" msgid "Method"
@@ -27949,7 +27901,7 @@ msgstr ""
#: pretix/control/views/item.py:237 #: pretix/control/views/item.py:237
msgid "The selected category has been deleted." msgid "The selected category has been deleted."
msgstr "" msgstr "A categoria selecionada foi excluída"
#: pretix/control/views/item.py:322 #: pretix/control/views/item.py:322
msgid "The new category has been created." msgid "The new category has been created."
@@ -29874,7 +29826,7 @@ msgstr ""
#: pretix/plugins/banktransfer/payment.py:239 #: pretix/plugins/banktransfer/payment.py:239
msgid "Please fill out your bank account details." 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 #: pretix/plugins/banktransfer/payment.py:243
msgid "Please enter your bank account details." msgid "Please enter your bank account details."
@@ -30175,7 +30127,7 @@ msgstr ""
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/pending.html:122 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/pending.html:122
msgid "Scan the QR code with your banking app" 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:5
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/refund_export.html:7 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/refund_export.html:7
@@ -30746,6 +30698,8 @@ msgid ""
"We're waiting for an answer from PayPal regarding your payment. Please " "We're waiting for an answer from PayPal regarding your payment. Please "
"contact us, if this takes more than a few hours." "contact us, if this takes more than a few hours."
msgstr "" 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/paypal/templates/pretixplugins/paypal/redirect.html:17
#: pretix/plugins/paypal2/templates/pretixplugins/paypal2/redirect.html:17 #: pretix/plugins/paypal2/templates/pretixplugins/paypal2/redirect.html:17
@@ -33370,19 +33324,17 @@ msgstr "Organizador: {organizer}"
#: pretix/presale/ical.py:139 #: pretix/presale/ical.py:139
#, python-brace-format #, python-brace-format
msgid "{event} - {item}" msgid "{event} - {item}"
msgstr "" msgstr "{event} - {item}"
#: pretix/presale/ical.py:147 #: pretix/presale/ical.py:147
#, fuzzy, python-brace-format #, python-brace-format
#| msgid "Start date"
msgid "Start: {datetime}" msgid "Start: {datetime}"
msgstr "Data inicial" msgstr "Início: {datetime}"
#: pretix/presale/ical.py:150 #: pretix/presale/ical.py:150
#, fuzzy, python-brace-format #, python-brace-format
#| msgid "Admission: {datetime}"
msgid "End: {datetime}" msgid "End: {datetime}"
msgstr "Admissão: {datetime}" msgstr "Término: {datetime}"
#: pretix/presale/templates/pretixpresale/base.html:44 #: pretix/presale/templates/pretixpresale/base.html:44
#, fuzzy #, fuzzy
@@ -33774,12 +33726,11 @@ msgstr "Selecione como deseja pagar o saldo restante:"
#: pretix/presale/templates/pretixpresale/event/checkout_payment.html:82 #: pretix/presale/templates/pretixpresale/event/checkout_payment.html:82
#: pretix/presale/templates/pretixpresale/event/order_pay_change.html:45 #: pretix/presale/templates/pretixpresale/event/order_pay_change.html:45
#, fuzzy, python-format #, python-format
#| msgid "%(num)s available"
msgid "(%(count)s available)" msgid "(%(count)s available)"
msgid_plural "(%(count)s available)" msgid_plural "(%(count)s available)"
msgstr[0] "%(num)s disponíveis" msgstr[0] "(%(count)s disponível)"
msgstr[1] "%(num)s disponíveis" msgstr[1] "(%(count)s disponíveis)"
#: pretix/presale/templates/pretixpresale/event/checkout_payment.html:101 #: pretix/presale/templates/pretixpresale/event/checkout_payment.html:101
msgid "This sales channel does not provide support for test mode." msgid "This sales channel does not provide support for test mode."
@@ -34558,13 +34509,11 @@ msgstr "Mostrar imagem em tamanho real de %(item)s"
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:131 #: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:131
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:288 #: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:288
#, fuzzy, python-format #, python-format
#| msgid "%(count)s event"
#| msgid_plural "%(count)s events"
msgid "%(amount)s× in your cart" msgid "%(amount)s× in your cart"
msgid_plural "%(amount)s× in your cart" msgid_plural "%(amount)s× in your cart"
msgstr[0] "%(count)s evento" msgstr[0] "%(amount)s× no seu carrinho"
msgstr[1] "%(count)s events" msgstr[1] "%(amount)s× no seu carrinho"
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:209 #: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:209
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:374 #: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:374
@@ -35632,10 +35581,8 @@ msgid "The following gift cards are available in your customer account:"
msgstr "" msgstr ""
#: pretix/presale/templates/pretixpresale/giftcard/checkout.html:24 #: pretix/presale/templates/pretixpresale/giftcard/checkout.html:24
#, fuzzy
#| msgid "Issued gift cards"
msgid "Use gift card" msgid "Use gift card"
msgstr "Cartões-presente emitidos" msgstr "Usar cartão-presente"
#: pretix/presale/templates/pretixpresale/giftcard/checkout_confirm.html:4 #: pretix/presale/templates/pretixpresale/giftcard/checkout_confirm.html:4
#, python-format #, python-format
@@ -35652,7 +35599,7 @@ msgid ""
"This is a self-hosted installation of <a %(a_attr)s>pretix, your free and " "This is a self-hosted installation of <a %(a_attr)s>pretix, your free and "
"open source ticket sales software</a>." "open source ticket sales software</a>."
msgstr "" msgstr ""
"Está é uma instalação self-hosted do <a %(a_attr)s>pretix, seu aplicativo " "Esta é uma instalação auto-hospedada do <a %(a_attr)s>pretix, seu aplicativo "
"livre e de código aberto para venda de ingressos.</a>." "livre e de código aberto para venda de ingressos.</a>."
#: pretix/presale/templates/pretixpresale/index.html:15 #: pretix/presale/templates/pretixpresale/index.html:15
@@ -35748,22 +35695,18 @@ msgid "Issued on %(date)s"
msgstr "Leitura negada: %(date)s" msgstr "Leitura negada: %(date)s"
#: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:38 #: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:38
#, fuzzy, python-format #, python-format
#| msgid "Expired since"
msgid "Expired since %(date)s" msgid "Expired since %(date)s"
msgstr "Expirado desde" msgstr "Expirado desde %(date)s"
#: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:46 #: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:46
#, fuzzy, python-format #, python-format
#| msgid "Valid until %(datetime)s"
msgid "Valid until %(date)s" 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 #: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:66
#, fuzzy
#| msgid "Remaining balance"
msgid "Remaining value:" msgid "Remaining value:"
msgstr "Saldo restante" msgstr "Valor restante:"
#: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:76 #: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:76
#, fuzzy #, fuzzy
@@ -36287,10 +36230,8 @@ msgid "The selected date does not exist in this event series."
msgstr "A data selecionada não existe nesta série de eventos." msgstr "A data selecionada não existe nesta série de eventos."
#: pretix/presale/views/widget.py:412 #: pretix/presale/views/widget.py:412
#, fuzzy
#| msgid "The selected seat \"{seat}\" is not available."
msgid "The selected date 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 #: pretix/presale/views/widget.py:476
#, python-format #, python-format

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 10:37+0000\n" "POT-Creation-Date: 2025-11-20 10:37+0000\n"
"PO-Revision-Date: 2025-08-28 13:43+0000\n" "PO-Revision-Date: 2025-12-09 00:47+0000\n"
"Last-Translator: Renne Rocha <renne@rocha.dev.br>\n" "Last-Translator: Renne Rocha <renne@rocha.dev.br>\n"
"Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/" "Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/"
"pretix/pretix-js/pt_BR/>\n" "pretix/pretix-js/pt_BR/>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n" "Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 5.13\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:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62 #: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -566,11 +566,11 @@ msgstr "ausente"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:289 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:289
msgid "Error: Product not found!" msgid "Error: Product not found!"
msgstr "" msgstr "Erro: Produto não encontrado!"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:296 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:296
msgid "Error: Variation not found!" msgid "Error: Variation not found!"
msgstr "" msgstr "Erro: Variação não encontrada!"
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:171
msgid "Check-in QR" msgid "Check-in QR"

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-20 10:37+0000\n" "POT-Creation-Date: 2025-11-20 10:37+0000\n"
"PO-Revision-Date: 2025-11-11 21:00+0000\n" "PO-Revision-Date: 2025-12-10 15:49+0000\n"
"Last-Translator: Ana Rute Pacheco Vivas <rute.vivas@om.org>\n" "Last-Translator: Ana Rute Pacheco Vivas <rute.vivas@om.org>\n"
"Language-Team: Portuguese (Portugal) <https://translate.pretix.eu/projects/" "Language-Team: Portuguese (Portugal) <https://translate.pretix.eu/projects/"
"pretix/pretix-js/pt_PT/>\n" "pretix/pretix-js/pt_PT/>\n"
@@ -798,7 +798,7 @@ msgstr ""
#: pretix/static/pretixpresale/js/ui/cart.js:90 #: pretix/static/pretixpresale/js/ui/cart.js:90
msgid "Renew reservation" msgid "Renew reservation"
msgstr "" msgstr "Renovar a reserva"
#: pretix/static/pretixpresale/js/ui/main.js:194 #: pretix/static/pretixpresale/js/ui/main.js:194
msgid "The organizer keeps %(currency)s %(amount)s" msgid "The organizer keeps %(currency)s %(amount)s"

View File

@@ -8,34 +8,36 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 13:57+0000\n" "POT-Creation-Date: 2025-11-27 13:57+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2025-12-14 00:00+0000\n"
"Last-Translator: Automatically generated\n" "Last-Translator: Lachlan Struthers <lachlan.struthers@om.org>\n"
"Language-Team: none\n" "Language-Team: Albanian <https://translate.pretix.eu/projects/pretix/pretix/"
"sq/>\n"
"Language: sq\n" "Language: sq\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.14.3\n"
#: pretix/_base_settings.py:87 #: pretix/_base_settings.py:87
msgid "English" msgid "English"
msgstr "" msgstr "Anglisht"
#: pretix/_base_settings.py:88 #: pretix/_base_settings.py:88
msgid "German" msgid "German"
msgstr "" msgstr "Gjermanisht"
#: pretix/_base_settings.py:89 #: pretix/_base_settings.py:89
msgid "German (informal)" msgid "German (informal)"
msgstr "" msgstr "Gjermanisht (joformale)"
#: pretix/_base_settings.py:90 #: pretix/_base_settings.py:90
msgid "Arabic" msgid "Arabic"
msgstr "" msgstr "Arabisht"
#: pretix/_base_settings.py:91 #: pretix/_base_settings.py:91
msgid "Basque" msgid "Basque"
msgstr "" msgstr "Baskisht"
#: pretix/_base_settings.py:92 #: pretix/_base_settings.py:92
msgid "Catalan" msgid "Catalan"
@@ -450,7 +452,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/event/mail.html:114 #: pretix/control/templates/pretixcontrol/event/mail.html:114
#: pretix/control/views/orders.py:1569 #: pretix/control/views/orders.py:1569
msgid "Order canceled" msgid "Order canceled"
msgstr "" msgstr "Porositja ësthë anuluar"
#: pretix/api/webhooks.py:278 pretix/base/notifications.py:257 #: pretix/api/webhooks.py:278 pretix/base/notifications.py:257
msgid "Order reactivated" msgid "Order reactivated"
@@ -1407,7 +1409,7 @@ msgstr ""
#: pretix/plugins/checkinlists/exporters.py:827 #: pretix/plugins/checkinlists/exporters.py:827
#: pretix/plugins/checkinlists/exporters.py:828 #: pretix/plugins/checkinlists/exporters.py:828
msgid "Yes" msgid "Yes"
msgstr "" msgstr "Po"
#: pretix/base/exporters/customers.py:100 #: pretix/base/exporters/customers.py:100
#: pretix/base/exporters/customers.py:101 pretix/base/exporters/events.py:83 #: pretix/base/exporters/customers.py:101 pretix/base/exporters/events.py:83
@@ -1431,7 +1433,7 @@ msgstr ""
#: pretix/plugins/checkinlists/exporters.py:827 #: pretix/plugins/checkinlists/exporters.py:827
#: pretix/plugins/checkinlists/exporters.py:828 #: pretix/plugins/checkinlists/exporters.py:828
msgid "No" msgid "No"
msgstr "" msgstr "Jo"
#: pretix/base/exporters/dekodi.py:42 pretix/base/exporters/invoices.py:66 #: pretix/base/exporters/dekodi.py:42 pretix/base/exporters/invoices.py:66
msgctxt "export_category" msgctxt "export_category"
@@ -2445,7 +2447,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:12 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:12
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:91 #: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:91
msgid "Product" msgid "Product"
msgstr "" msgstr "Produkti"
#: pretix/base/exporters/orderlist.py:619 pretix/base/models/vouchers.py:315 #: pretix/base/exporters/orderlist.py:619 pretix/base/models/vouchers.py:315
#: pretix/control/templates/pretixcontrol/vouchers/bulk.html:5 #: pretix/control/templates/pretixcontrol/vouchers/bulk.html:5
@@ -2783,7 +2785,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/event/cancel.html:20 #: pretix/control/templates/pretixcontrol/event/cancel.html:20
#: pretix/control/views/item.py:971 #: pretix/control/views/item.py:971
msgid "Paid orders" msgid "Paid orders"
msgstr "" msgstr "Porositje të paguara"
#: pretix/base/exporters/orderlist.py:1155 pretix/control/views/item.py:976 #: pretix/base/exporters/orderlist.py:1155 pretix/control/views/item.py:976
msgid "Pending orders" msgid "Pending orders"
@@ -2952,7 +2954,7 @@ msgstr ""
#: pretix/plugins/reports/accountingreport.py:105 #: pretix/plugins/reports/accountingreport.py:105
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:67 #: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:67
msgid "All" msgid "All"
msgstr "" msgstr "Të gjitha"
#: pretix/base/exporters/orderlist.py:1329 pretix/control/forms/filter.py:1450 #: pretix/base/exporters/orderlist.py:1329 pretix/control/forms/filter.py:1450
msgid "Live" msgid "Live"
@@ -3879,7 +3881,7 @@ msgstr ""
#: pretix/base/modelimport_vouchers.py:205 pretix/base/models/items.py:1256 #: pretix/base/modelimport_vouchers.py:205 pretix/base/models/items.py:1256
#: pretix/base/models/vouchers.py:266 pretix/base/models/waitinglist.py:99 #: pretix/base/models/vouchers.py:266 pretix/base/models/waitinglist.py:99
msgid "Product variation" msgid "Product variation"
msgstr "" msgstr "Varianti i produktit"
#: pretix/base/modelimport_orders.py:161 #: pretix/base/modelimport_orders.py:161
msgid "The variation can be specified by its internal ID or full name." msgid "The variation can be specified by its internal ID or full name."
@@ -4278,19 +4280,19 @@ msgstr ""
#: pretix/base/models/checkin.py:336 #: pretix/base/models/checkin.py:336
msgid "Entry" msgid "Entry"
msgstr "" msgstr "Hyrje"
#: pretix/base/models/checkin.py:337 #: pretix/base/models/checkin.py:337
msgid "Exit" msgid "Exit"
msgstr "" msgstr "Dalje"
#: pretix/base/models/checkin.py:356 #: pretix/base/models/checkin.py:356
msgid "Unknown ticket" msgid "Unknown ticket"
msgstr "" msgstr "Biletë e panjohur"
#: pretix/base/models/checkin.py:357 #: pretix/base/models/checkin.py:357
msgid "Ticket not paid" msgid "Ticket not paid"
msgstr "" msgstr "Bileta nuk është paguar"
#: pretix/base/models/checkin.py:358 #: pretix/base/models/checkin.py:358
msgid "Forbidden by custom rule" msgid "Forbidden by custom rule"
@@ -4298,23 +4300,23 @@ msgstr ""
#: pretix/base/models/checkin.py:359 #: pretix/base/models/checkin.py:359
msgid "Ticket code revoked/changed" msgid "Ticket code revoked/changed"
msgstr "" msgstr "Kodi i biletës është anuluar/ndryshuar"
#: pretix/base/models/checkin.py:360 #: pretix/base/models/checkin.py:360
msgid "Information required" msgid "Information required"
msgstr "" msgstr "Informacion i domosdoshëm"
#: pretix/base/models/checkin.py:361 #: pretix/base/models/checkin.py:361
msgid "Ticket already used" msgid "Ticket already used"
msgstr "" msgstr "Bileta tashmë është përdorur"
#: pretix/base/models/checkin.py:362 #: pretix/base/models/checkin.py:362
msgid "Ticket type not allowed here" msgid "Ticket type not allowed here"
msgstr "" msgstr "Ky lloj bilete nuk lejohet këtu"
#: pretix/base/models/checkin.py:363 #: pretix/base/models/checkin.py:363
msgid "Ticket code is ambiguous on list" msgid "Ticket code is ambiguous on list"
msgstr "" msgstr "Kodi i biletës është e paqartë në listë"
#: pretix/base/models/checkin.py:364 #: pretix/base/models/checkin.py:364
msgid "Server error" msgid "Server error"
@@ -4322,15 +4324,15 @@ msgstr ""
#: pretix/base/models/checkin.py:365 #: pretix/base/models/checkin.py:365
msgid "Ticket blocked" msgid "Ticket blocked"
msgstr "" msgstr "Bileta u bllokua"
#: pretix/base/models/checkin.py:366 #: pretix/base/models/checkin.py:366
msgid "Order not approved" msgid "Order not approved"
msgstr "" msgstr "Porositje nuk është aprovuar"
#: pretix/base/models/checkin.py:367 #: pretix/base/models/checkin.py:367
msgid "Ticket not valid at this time" msgid "Ticket not valid at this time"
msgstr "" msgstr "Bileta nuk është e vlefshme për momentin"
#: pretix/base/models/checkin.py:368 #: pretix/base/models/checkin.py:368
msgid "Check-in annulled" msgid "Check-in annulled"
@@ -4460,7 +4462,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/organizers/gates.html:16 #: pretix/control/templates/pretixcontrol/organizers/gates.html:16
#: pretix/plugins/checkinlists/exporters.py:769 #: pretix/plugins/checkinlists/exporters.py:769
msgid "Gate" msgid "Gate"
msgstr "" msgstr "Porta"
#: pretix/base/models/devices.py:131 #: pretix/base/models/devices.py:131
#: pretix/control/templates/pretixcontrol/organizers/devices.html:83 #: pretix/control/templates/pretixcontrol/organizers/devices.html:83
@@ -5928,7 +5930,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:34 #: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:34
#: pretix/presale/templates/pretixpresale/organizers/customer_memberships.html:44 #: pretix/presale/templates/pretixpresale/organizers/customer_memberships.html:44
msgid "Canceled" msgid "Canceled"
msgstr "" msgstr "e anuluar"
#: pretix/base/models/memberships.py:134 #: pretix/base/models/memberships.py:134
#: pretix/control/templates/pretixcontrol/organizers/customer.html:117 #: pretix/control/templates/pretixcontrol/organizers/customer.html:117
@@ -6671,7 +6673,7 @@ msgstr ""
#: pretix/base/models/vouchers.py:204 pretix/control/views/vouchers.py:120 #: pretix/base/models/vouchers.py:204 pretix/control/views/vouchers.py:120
msgid "Redeemed" msgid "Redeemed"
msgstr "" msgstr "e përdorur"
#: pretix/base/models/vouchers.py:209 #: pretix/base/models/vouchers.py:209
msgid "" msgid ""
@@ -7432,7 +7434,7 @@ msgstr ""
#: pretix/base/pdf.py:278 pretix/base/pdf.py:307 #: pretix/base/pdf.py:278 pretix/base/pdf.py:307
#: pretix/base/services/checkin.py:362 pretix/control/forms/filter.py:1271 #: pretix/base/services/checkin.py:362 pretix/control/forms/filter.py:1271
msgid "Friday" msgid "Friday"
msgstr "" msgstr "E Premte"
#: pretix/base/pdf.py:282 #: pretix/base/pdf.py:282
msgid "Event end date and time" msgid "Event end date and time"
@@ -7704,15 +7706,15 @@ msgstr ""
#: pretix/base/reldate.py:38 #: pretix/base/reldate.py:38
msgid "Event start" msgid "Event start"
msgstr "" msgstr "Fillimi i eventit"
#: pretix/base/reldate.py:39 #: pretix/base/reldate.py:39
msgid "Event end" msgid "Event end"
msgstr "" msgstr "Mbarimi i eventit"
#: pretix/base/reldate.py:40 #: pretix/base/reldate.py:40
msgid "Event admission" msgid "Event admission"
msgstr "" msgstr "Hyrja në event"
#: pretix/base/reldate.py:41 #: pretix/base/reldate.py:41
msgid "Presale start" msgid "Presale start"
@@ -8130,27 +8132,27 @@ msgstr ""
#: pretix/base/services/checkin.py:358 pretix/control/forms/filter.py:1267 #: pretix/base/services/checkin.py:358 pretix/control/forms/filter.py:1267
msgid "Monday" msgid "Monday"
msgstr "" msgstr "E Hënë"
#: pretix/base/services/checkin.py:359 pretix/control/forms/filter.py:1268 #: pretix/base/services/checkin.py:359 pretix/control/forms/filter.py:1268
msgid "Tuesday" msgid "Tuesday"
msgstr "" msgstr "E Martë"
#: pretix/base/services/checkin.py:360 pretix/control/forms/filter.py:1269 #: pretix/base/services/checkin.py:360 pretix/control/forms/filter.py:1269
msgid "Wednesday" msgid "Wednesday"
msgstr "" msgstr "E Mërkurë"
#: pretix/base/services/checkin.py:361 pretix/control/forms/filter.py:1270 #: pretix/base/services/checkin.py:361 pretix/control/forms/filter.py:1270
msgid "Thursday" msgid "Thursday"
msgstr "" msgstr "E Enjte"
#: pretix/base/services/checkin.py:363 pretix/control/forms/filter.py:1272 #: pretix/base/services/checkin.py:363 pretix/control/forms/filter.py:1272
msgid "Saturday" msgid "Saturday"
msgstr "" msgstr "E Shtunë"
#: pretix/base/services/checkin.py:364 pretix/control/forms/filter.py:1273 #: pretix/base/services/checkin.py:364 pretix/control/forms/filter.py:1273
msgid "Sunday" msgid "Sunday"
msgstr "" msgstr "E Diel"
#: pretix/base/services/checkin.py:368 #: pretix/base/services/checkin.py:368
#, python-brace-format #, python-brace-format
@@ -12889,7 +12891,7 @@ msgstr ""
#: pretix/plugins/reports/exporters.py:391 #: pretix/plugins/reports/exporters.py:391
#: pretix/presale/templates/pretixpresale/event/fragment_order_status.html:7 #: pretix/presale/templates/pretixpresale/event/fragment_order_status.html:7
msgid "Approval pending" msgid "Approval pending"
msgstr "" msgstr "Duke pritur aprovimin"
#: pretix/control/forms/filter.py:247 #: pretix/control/forms/filter.py:247
msgid "Follow-up configured" msgid "Follow-up configured"
@@ -13042,7 +13044,7 @@ msgstr ""
#: pretix/control/forms/filter.py:2052 pretix/control/forms/filter.py:2054 #: pretix/control/forms/filter.py:2052 pretix/control/forms/filter.py:2054
#: pretix/control/forms/filter.py:2620 pretix/control/forms/filter.py:2622 #: pretix/control/forms/filter.py:2620 pretix/control/forms/filter.py:2622
msgid "Search query" msgid "Search query"
msgstr "" msgstr "Kërkim"
#: pretix/control/forms/filter.py:1528 pretix/control/forms/filter.py:1600 #: pretix/control/forms/filter.py:1528 pretix/control/forms/filter.py:1600
#: pretix/control/templates/pretixcontrol/organizers/customer.html:47 #: pretix/control/templates/pretixcontrol/organizers/customer.html:47
@@ -13157,7 +13159,7 @@ msgstr ""
#: pretix/control/forms/filter.py:2117 #: pretix/control/forms/filter.py:2117
#: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:51 #: pretix/presale/templates/pretixpresale/organizers/customer_giftcards.html:51
msgid "Valid" msgid "Valid"
msgstr "" msgstr "e vlefshme"
#: pretix/control/forms/filter.py:2118 #: pretix/control/forms/filter.py:2118
msgid "Unredeemed" msgid "Unredeemed"
@@ -16427,7 +16429,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/order_pay_change.html:75 #: pretix/presale/templates/pretixpresale/event/order_pay_change.html:75
#: pretix/presale/templates/pretixpresale/event/position_change.html:29 #: pretix/presale/templates/pretixpresale/event/position_change.html:29
msgid "Continue" msgid "Continue"
msgstr "" msgstr "Vazhdoni"
#: pretix/control/templates/pretixcontrol/auth/oauth_authorization.html:8 #: pretix/control/templates/pretixcontrol/auth/oauth_authorization.html:8
msgid "Authorize an application" msgid "Authorize an application"
@@ -16675,7 +16677,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/postmessage.html:27 #: pretix/presale/templates/pretixpresale/postmessage.html:27
#: pretix/presale/templates/pretixpresale/waiting.html:42 #: pretix/presale/templates/pretixpresale/waiting.html:42
msgid "If this takes longer than a few minutes, please contact us." msgid "If this takes longer than a few minutes, please contact us."
msgstr "" msgstr "Nëse kalon më shumë se disa minuta, ju lutemi t'na kontaktoni."
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:4 #: pretix/control/templates/pretixcontrol/boxoffice/payment.html:4
#: pretix/control/templates/pretixcontrol/organizers/devices.html:71 #: pretix/control/templates/pretixcontrol/organizers/devices.html:71
@@ -16905,7 +16907,7 @@ msgstr[1] ""
#: pretix/presale/templates/pretixpresale/event/position_change.html:24 #: pretix/presale/templates/pretixpresale/event/position_change.html:24
#: pretix/presale/templates/pretixpresale/event/position_modify.html:44 #: pretix/presale/templates/pretixpresale/event/position_modify.html:44
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr "Anuloni"
#: pretix/control/templates/pretixcontrol/checkin/bulk_revert_confirm.html:27 #: pretix/control/templates/pretixcontrol/checkin/bulk_revert_confirm.html:27
#: pretix/control/templates/pretixcontrol/checkin/list_delete.html:24 #: pretix/control/templates/pretixcontrol/checkin/list_delete.html:24
@@ -17019,7 +17021,7 @@ msgstr ""
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/transaction_list.html:14 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/transaction_list.html:14
#: pretix/plugins/checkinlists/exporters.py:770 #: pretix/plugins/checkinlists/exporters.py:770
msgid "Result" msgid "Result"
msgstr "" msgstr "Rezultati"
#: pretix/control/templates/pretixcontrol/checkin/checkins.html:78 #: pretix/control/templates/pretixcontrol/checkin/checkins.html:78
#: pretix/control/templates/pretixcontrol/order/index.html:437 #: pretix/control/templates/pretixcontrol/order/index.html:437
@@ -17363,7 +17365,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/checkin/simulator.html:68 #: pretix/control/templates/pretixcontrol/checkin/simulator.html:68
msgid "Additional information required" msgid "Additional information required"
msgstr "" msgstr "Më shumë informacione kërkohen"
#: pretix/control/templates/pretixcontrol/checkin/simulator.html:70 #: pretix/control/templates/pretixcontrol/checkin/simulator.html:70
msgid "" msgid ""
@@ -18272,7 +18274,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/organizers/device_logs.html:50 #: pretix/control/templates/pretixcontrol/organizers/device_logs.html:50
#: pretix/control/templates/pretixcontrol/organizers/logs.html:80 #: pretix/control/templates/pretixcontrol/organizers/logs.html:80
msgid "No results" msgid "No results"
msgstr "" msgstr "S'ka rezultate"
#: pretix/control/templates/pretixcontrol/event/mail.html:7 #: pretix/control/templates/pretixcontrol/event/mail.html:7
#: pretix/control/templates/pretixcontrol/organizers/mail.html:11 #: pretix/control/templates/pretixcontrol/organizers/mail.html:11
@@ -18477,7 +18479,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/event/plugins.html:34 #: pretix/control/templates/pretixcontrol/event/plugins.html:34
#: pretix/control/templates/pretixcontrol/organizers/plugins.html:34 #: pretix/control/templates/pretixcontrol/organizers/plugins.html:34
msgid "Search results" msgid "Search results"
msgstr "" msgstr "Rezultatet e kërkimit"
#: pretix/control/templates/pretixcontrol/event/plugins.html:56 #: pretix/control/templates/pretixcontrol/event/plugins.html:56
#: pretix/control/templates/pretixcontrol/organizers/plugins.html:56 #: pretix/control/templates/pretixcontrol/organizers/plugins.html:56
@@ -19324,51 +19326,51 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/global_sysreport.html:16 #: pretix/control/templates/pretixcontrol/global_sysreport.html:16
msgid "January" msgid "January"
msgstr "" msgstr "Janar"
#: pretix/control/templates/pretixcontrol/global_sysreport.html:17 #: pretix/control/templates/pretixcontrol/global_sysreport.html:17
msgid "February" msgid "February"
msgstr "" msgstr "Shkurt"
#: pretix/control/templates/pretixcontrol/global_sysreport.html:18 #: pretix/control/templates/pretixcontrol/global_sysreport.html:18
msgid "March" msgid "March"
msgstr "" msgstr "Mars"
#: pretix/control/templates/pretixcontrol/global_sysreport.html:19 #: pretix/control/templates/pretixcontrol/global_sysreport.html:19
msgid "April" msgid "April"
msgstr "" msgstr "Prill"
#: pretix/control/templates/pretixcontrol/global_sysreport.html:20 #: pretix/control/templates/pretixcontrol/global_sysreport.html:20
msgid "May" msgid "May"
msgstr "" msgstr "Maj"
#: pretix/control/templates/pretixcontrol/global_sysreport.html:21 #: pretix/control/templates/pretixcontrol/global_sysreport.html:21
msgid "June" msgid "June"
msgstr "" msgstr "Qershor"
#: pretix/control/templates/pretixcontrol/global_sysreport.html:22 #: pretix/control/templates/pretixcontrol/global_sysreport.html:22
msgid "July" msgid "July"
msgstr "" msgstr "Korrik"
#: pretix/control/templates/pretixcontrol/global_sysreport.html:23 #: pretix/control/templates/pretixcontrol/global_sysreport.html:23
msgid "August" msgid "August"
msgstr "" msgstr "Gusht"
#: pretix/control/templates/pretixcontrol/global_sysreport.html:24 #: pretix/control/templates/pretixcontrol/global_sysreport.html:24
msgid "September" msgid "September"
msgstr "" msgstr "Shtator"
#: pretix/control/templates/pretixcontrol/global_sysreport.html:25 #: pretix/control/templates/pretixcontrol/global_sysreport.html:25
msgid "October" msgid "October"
msgstr "" msgstr "Tetor"
#: pretix/control/templates/pretixcontrol/global_sysreport.html:26 #: pretix/control/templates/pretixcontrol/global_sysreport.html:26
msgid "November" msgid "November"
msgstr "" msgstr "Nëntor"
#: pretix/control/templates/pretixcontrol/global_sysreport.html:27 #: pretix/control/templates/pretixcontrol/global_sysreport.html:27
msgid "December" msgid "December"
msgstr "" msgstr "Dhjetor"
#: pretix/control/templates/pretixcontrol/global_sysreport.html:32 #: pretix/control/templates/pretixcontrol/global_sysreport.html:32
msgid "Generate report" msgid "Generate report"
@@ -19743,7 +19745,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/subevents/bulk.html:355 #: pretix/control/templates/pretixcontrol/subevents/bulk.html:355
#: pretix/control/templates/pretixcontrol/subevents/bulk.html:364 #: pretix/control/templates/pretixcontrol/subevents/bulk.html:364
msgid "minutes" msgid "minutes"
msgstr "" msgstr "minuta"
#: pretix/control/templates/pretixcontrol/item/index.html:229 #: pretix/control/templates/pretixcontrol/item/index.html:229
msgid "hours" msgid "hours"
@@ -20096,7 +20098,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/items/question.html:91 #: pretix/control/templates/pretixcontrol/items/question.html:91
msgid "Count" msgid "Count"
msgstr "" msgstr "Sasia"
#: pretix/control/templates/pretixcontrol/items/question.html:92 #: pretix/control/templates/pretixcontrol/items/question.html:92
#, python-format #, python-format
@@ -21014,7 +21016,7 @@ msgstr ""
#: pretix/plugins/reports/exporters.py:969 #: pretix/plugins/reports/exporters.py:969
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:469 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:469
msgid "Total" msgid "Total"
msgstr "" msgstr "Shuma totale"
#: pretix/control/templates/pretixcontrol/order/index.html:789 #: pretix/control/templates/pretixcontrol/order/index.html:789
#: pretix/presale/templates/pretixpresale/event/order.html:210 #: pretix/presale/templates/pretixpresale/event/order.html:210
@@ -23096,7 +23098,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/pdf/index.html:52 #: pretix/control/templates/pretixcontrol/pdf/index.html:52
msgid "Text box" msgid "Text box"
msgstr "" msgstr "Kutia teksti"
#: pretix/control/templates/pretixcontrol/pdf/index.html:59 #: pretix/control/templates/pretixcontrol/pdf/index.html:59
msgid "QR Code" msgid "QR Code"
@@ -23135,7 +23137,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/pdf/index.html:107 #: pretix/control/templates/pretixcontrol/pdf/index.html:107
msgid "Duplicate" msgid "Duplicate"
msgstr "" msgstr "Kopjoni"
#: pretix/control/templates/pretixcontrol/pdf/index.html:117 #: pretix/control/templates/pretixcontrol/pdf/index.html:117
msgid "Undo" msgid "Undo"
@@ -27329,7 +27331,7 @@ msgstr ""
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/transaction_list.html:80 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/transaction_list.html:80
msgid "Comment:" msgid "Comment:"
msgstr "" msgstr "Komente:"
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/transaction_list.html:98 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/transaction_list.html:98
msgid "No order code detected" msgid "No order code detected"
@@ -27572,7 +27574,7 @@ msgstr ""
#: pretix/plugins/paypal2/payment.py:1097 #: pretix/plugins/paypal2/payment.py:1097
#: pretix/plugins/paypal2/payment.py:1098 pretix/plugins/stripe/payment.py:1816 #: pretix/plugins/paypal2/payment.py:1098 pretix/plugins/stripe/payment.py:1816
msgid "PayPal" msgid "PayPal"
msgstr "" msgstr "PayPal"
#: pretix/plugins/paypal/apps.py:53 #: pretix/plugins/paypal/apps.py:53
msgid "" msgid ""
@@ -29012,8 +29014,9 @@ msgid "Credit card payments"
msgstr "" msgstr ""
#: pretix/plugins/stripe/payment.py:342 pretix/plugins/stripe/payment.py:1527 #: pretix/plugins/stripe/payment.py:342 pretix/plugins/stripe/payment.py:1527
#, fuzzy
msgid "iDEAL" msgid "iDEAL"
msgstr "" msgstr "iDEAL"
#: pretix/plugins/stripe/payment.py:344 pretix/plugins/stripe/payment.py:352 #: pretix/plugins/stripe/payment.py:344 pretix/plugins/stripe/payment.py:352
#: pretix/plugins/stripe/payment.py:360 pretix/plugins/stripe/payment.py:395 #: pretix/plugins/stripe/payment.py:360 pretix/plugins/stripe/payment.py:395
@@ -29032,12 +29035,14 @@ msgid "Alipay"
msgstr "" msgstr ""
#: pretix/plugins/stripe/payment.py:358 pretix/plugins/stripe/payment.py:1564 #: pretix/plugins/stripe/payment.py:358 pretix/plugins/stripe/payment.py:1564
#, fuzzy
msgid "Bancontact" msgid "Bancontact"
msgstr "" msgstr "Bancontact"
#: pretix/plugins/stripe/payment.py:366 #: pretix/plugins/stripe/payment.py:366
#, fuzzy
msgid "SEPA Direct Debit" msgid "SEPA Direct Debit"
msgstr "" msgstr "Debit direkt me SEPA"
#: pretix/plugins/stripe/payment.py:369 #: pretix/plugins/stripe/payment.py:369
msgid "" msgid ""
@@ -29072,12 +29077,14 @@ msgid "Multibanco"
msgstr "" msgstr ""
#: pretix/plugins/stripe/payment.py:409 pretix/plugins/stripe/payment.py:1730 #: pretix/plugins/stripe/payment.py:409 pretix/plugins/stripe/payment.py:1730
#, fuzzy
msgid "Przelewy24" msgid "Przelewy24"
msgstr "" msgstr "Przelewy24"
#: pretix/plugins/stripe/payment.py:417 pretix/plugins/stripe/payment.py:1769 #: pretix/plugins/stripe/payment.py:417 pretix/plugins/stripe/payment.py:1769
#, fuzzy
msgid "WeChat Pay" msgid "WeChat Pay"
msgstr "" msgstr "WeChat Pay"
#: pretix/plugins/stripe/payment.py:433 pretix/plugins/stripe/payment.py:1824 #: pretix/plugins/stripe/payment.py:433 pretix/plugins/stripe/payment.py:1824
msgid "Swish" msgid "Swish"
@@ -29221,8 +29228,9 @@ msgid "giropay via Stripe"
msgstr "" msgstr ""
#: pretix/plugins/stripe/payment.py:1480 #: pretix/plugins/stripe/payment.py:1480
#, fuzzy
msgid "giropay" msgid "giropay"
msgstr "" msgstr "giropay"
#: pretix/plugins/stripe/payment.py:1483 #: pretix/plugins/stripe/payment.py:1483
msgid "" msgid ""
@@ -29757,7 +29765,7 @@ msgstr ""
#: pretix/plugins/ticketoutputpdf/templates/pretixplugins/ticketoutputpdf/edit.html:23 #: pretix/plugins/ticketoutputpdf/templates/pretixplugins/ticketoutputpdf/edit.html:23
msgid "Ticket design" msgid "Ticket design"
msgstr "" msgstr "Dizajni i biletës"
#: pretix/plugins/ticketoutputpdf/templates/pretixplugins/ticketoutputpdf/edit.html:27 #: pretix/plugins/ticketoutputpdf/templates/pretixplugins/ticketoutputpdf/edit.html:27
msgid "You can modify the design after you saved this page." msgid "You can modify the design after you saved this page."
@@ -30320,7 +30328,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:27 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:27
#: pretix/presale/templates/pretixpresale/event/fragment_cart_box.html:18 #: pretix/presale/templates/pretixpresale/event/fragment_cart_box.html:18
msgid "Cart expired" msgid "Cart expired"
msgstr "" msgstr "Shporta juaj u skadua"
#: pretix/presale/templates/pretixpresale/event/checkout_base.html:36 #: pretix/presale/templates/pretixpresale/event/checkout_base.html:36
msgid "Show full cart" msgid "Show full cart"
@@ -30928,11 +30936,13 @@ msgid ""
"The items in your cart are no longer reserved for you. You can still " "The items in your cart are no longer reserved for you. You can still "
"complete your order as long as theyre available." "complete your order as long as theyre available."
msgstr "" msgstr ""
"Sendet në shportën tuaj nuk janë të rezervuar më për ju. Ju mund t'a "
"përmbushni porosinë tuaj derisa janë ende të disponueshëm."
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:514 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:514
#: pretix/presale/templates/pretixpresale/fragment_modals.html:48 #: pretix/presale/templates/pretixpresale/fragment_modals.html:48
msgid "Renew reservation" msgid "Renew reservation"
msgstr "" msgstr "Rivendosni rezervimin"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:526 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:526
msgid "Reservation renewed" msgid "Reservation renewed"
@@ -32444,7 +32454,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/postmessage.html:21 #: pretix/presale/templates/pretixpresale/postmessage.html:21
#: pretix/presale/templates/pretixpresale/waiting.html:22 #: pretix/presale/templates/pretixpresale/waiting.html:22
msgid "We are processing your request …" msgid "We are processing your request …"
msgstr "" msgstr "Ne po e proçesojmë kërkesën tuaj …"
#: pretix/presale/utils.py:271 pretix/presale/utils.py:417 #: pretix/presale/utils.py:271 pretix/presale/utils.py:417
#: pretix/presale/utils.py:418 #: pretix/presale/utils.py:418

File diff suppressed because it is too large Load Diff

View File

@@ -41,15 +41,20 @@ for app in apps.get_app_configs():
if hasattr(app, 'PretixPluginMeta'): if hasattr(app, 'PretixPluginMeta'):
if importlib.util.find_spec(app.name + '.urls'): if importlib.util.find_spec(app.name + '.urls'):
urlmod = importlib.import_module(app.name + '.urls') urlmod = importlib.import_module(app.name + '.urls')
single_plugin_patterns = []
if hasattr(urlmod, 'event_patterns'): if hasattr(urlmod, 'event_patterns'):
patterns = plugin_event_urls(urlmod.event_patterns, plugin=app.name) patterns = plugin_event_urls(urlmod.event_patterns, plugin=app.name)
raw_plugin_patterns.append( single_plugin_patterns.append(
re_path(r'^(?P<event>[^/]+)/', include((patterns, app.label))) re_path(r'^(?P<event>[^/]+)/', include(patterns))
) )
if hasattr(urlmod, 'organizer_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( raw_plugin_patterns.append(
re_path(r'', include((patterns, app.label))) re_path(r'', include((single_plugin_patterns, app.label)))
) )
plugin_patterns = [ plugin_patterns = [

View File

@@ -42,15 +42,20 @@ for app in apps.get_app_configs():
if hasattr(app, 'PretixPluginMeta'): if hasattr(app, 'PretixPluginMeta'):
if importlib.util.find_spec(app.name + '.urls'): if importlib.util.find_spec(app.name + '.urls'):
urlmod = importlib.import_module(app.name + '.urls') urlmod = importlib.import_module(app.name + '.urls')
if hasattr(urlmod, 'event_patterns'): single_plugin_patterns = []
patterns = plugin_event_urls(urlmod.event_patterns, plugin=app.name)
raw_plugin_patterns.append(
re_path(r'^(?P<event>[^/]+)/', include((patterns, app.label)))
)
if hasattr(urlmod, 'organizer_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( raw_plugin_patterns.append(
re_path(r'', include((patterns, app.label))) re_path(r'', include((single_plugin_patterns, app.label)))
) )
plugin_patterns = [ plugin_patterns = [

View File

@@ -0,0 +1,71 @@
#
# 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.utils.translation import gettext_lazy as _
from lxml import etree
def parse(file):
# Spec: https://www.ebics.de/de/datenformate
data = file.read()
root = etree.fromstring(data)
statements = root.findall("{*}BkToCstmrStmt/{*}Stmt")
if not statements:
raise ValueError(_("Empty file or unknown format."))
def get_text(findall_result):
if len(findall_result) == 1:
return findall_result[0].text
return ""
rows = []
for stmt in statements:
for ntry in stmt.findall("{*}Ntry"):
minus = ""
otherparty = "Dbtr"
if ntry.findall("{*}CdtDbtInd")[0].text == "DBIT":
otherparty = "Cdtr"
minus = "-"
reference_parts = [
get_text(ntry.findall("{*}NtryDtls/{*}TxDtls/{*}RmtInf/{*}Ustrd")),
get_text(ntry.findall("{*}NtryDtls/{*}TxDtls/{*}Refs/{*}EndToEndId")),
get_text(ntry.findall("{*}NtryDtls/{*}TxDtls/{*}Refs/{*}InstructionIdentification")),
]
if ntry.findall("{*}NtryDtls/{*}Btch"):
# Batch booking, we do not support splitting yet
reference_parts.insert(0, get_text(ntry.findall("{*}NtryDtls/{*}Btch/{*}PmtInfId")))
row = {
'amount': minus + ntry.findall("{*}Amt")[0].text,
'date': get_text(ntry.findall("{*}BookgDt/{*}Dt")),
'reference': "\n".join(filter(lambda a: bool(a) and a != "NOTPROVIDED", reference_parts))
}
if ext_id := get_text(ntry.findall("{*}AcctSvcrRef")):
row['external_id'] = ext_id
if iban := get_text(ntry.findall(f"{{*}}NtryDtls/{{*}}TxDtls/{{*}}RltdPties/{{*}}{otherparty}Acct/{{*}}Id/{{*}}IBAN")):
row['iban'] = iban
if bic := get_text(ntry.findall(f"{{*}}NtryDtls/{{*}}TxDtls/{{*}}RltdAgts/{{*}}{otherparty}Agt/{{*}}FinInstnId/{{*}}BICFI")):
row['bic'] = bic
if payer := get_text(ntry.findall(f"{{*}}NtryDtls/{{*}}TxDtls/{{*}}RltdPties/{{*}}{otherparty}/{{*}}Nm")):
row['payer'] = payer
rows.append(row)
return rows

View File

@@ -46,12 +46,12 @@ from i18nfield.forms import I18nTextInput
from i18nfield.strings import LazyI18nString from i18nfield.strings import LazyI18nString
from localflavor.generic.forms import BICFormField, IBANFormField from localflavor.generic.forms import BICFormField, IBANFormField
from localflavor.generic.validators import IBANValidator from localflavor.generic.validators import IBANValidator
from text_unidecode import unidecode
from pretix.base.forms import I18nMarkdownTextarea from pretix.base.forms import I18nMarkdownTextarea
from pretix.base.models import InvoiceAddress, Order, OrderPayment, OrderRefund from pretix.base.models import InvoiceAddress, Order, OrderPayment, OrderRefund
from pretix.base.payment import BasePaymentProvider from pretix.base.payment import BasePaymentProvider
from pretix.base.templatetags.money import money_filter 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.plugins.banktransfer.templatetags.ibanformat import ibanformat
from pretix.presale.views.cart import cart_session 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)) t += str(self.settings.get('bank_details', as_type=LazyI18nString))
return t 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): def payment_pending_render(self, request: HttpRequest, payment: OrderPayment):
template = get_template('pretixplugins/banktransfer/pending.html') template = get_template('pretixplugins/banktransfer/pending.html')
ctx = { ctx = {
@@ -367,13 +322,18 @@ class BankTransfer(BasePaymentProvider):
'amount': payment.amount, 'amount': payment.amount,
'payment_info': payment.info_data, 'payment_info': payment.info_data,
'settings': self.settings, 'settings': self.settings,
'swiss_qrbill': self.swiss_qrbill(payment), 'payment_qr_codes': generate_payment_qr_codes(
'eu_barcodes': self.event.currency == 'EUR', 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), 'pending_description': self.settings.get('pending_description', as_type=LazyI18nString),
'details': self.settings.get('bank_details', as_type=LazyI18nString), 'details': self.settings.get('bank_details', as_type=LazyI18nString),
'has_invoices': payment.order.invoices.exists(), 'has_invoices': payment.order.invoices.exists(),
} }
ctx['any_barcodes'] = ctx['swiss_qrbill'] or ctx['eu_barcodes']
return template.render(ctx, request=request) return template.render(ctx, request=request)
def payment_control_render(self, request: HttpRequest, payment: OrderPayment) -> str: def payment_control_render(self, request: HttpRequest, payment: OrderPayment) -> str:

View File

@@ -34,8 +34,10 @@
import json import json
import logging import logging
import operator
import re import re
from decimal import Decimal from decimal import Decimal
from functools import reduce
import dateutil.parser import dateutil.parser
from celery.exceptions import MaxRetriesExceededError from celery.exceptions import MaxRetriesExceededError
@@ -117,20 +119,26 @@ def _find_order_for_code(base_qs, code):
pass pass
def _find_order_for_invoice_id(base_qs, prefix, number): def _find_order_for_invoice_id(base_qs, prefixes, number):
try: try:
# Working with __iregex here is an experiment, if this turns out to be too slow in production # 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. # 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( return base_qs.select_related('order').get(
prefix__istartswith=prefix, # redundant, but hopefully makes it a little faster reduce(operator.or_, r)
full_invoice_no__iregex=prefix + r'[\- ]*0*' + number
).order ).order
except (Invoice.DoesNotExist, Invoice.MultipleObjectsReturned): except (Invoice.DoesNotExist, Invoice.MultipleObjectsReturned):
pass pass
@transaction.atomic @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 = [] orders = []
if event: if event:
for slug, code in matches: 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}: if order.code not in {o.code for o in orders}:
orders.append(order) orders.append(order)
else: 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}: if order and order.code not in {o.code for o in orders}:
orders.append(order) orders.append(order)
else: else:
qs = Order.objects.filter(event__organizer=organizer) qs = Order.objects.filter(event__organizer=organizer)
for slug, code in matches: 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:
if order.code not in {o.code for o in orders}: if order.code not in {o.code for o in orders}:
orders.append(order) orders.append(order)
else: 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}: if order and order.code not in {o.code for o in orders}:
orders.append(order) 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) transactions = _get_unknown_transactions(job, data, **job.owner_kwargs)
# Match order codes # Match order codes
regex_match_to_slug = {}
code_len_agg = Order.objects.filter(event__organizer=job.organizer).annotate( code_len_agg = Order.objects.filter(event__organizer=job.organizer).annotate(
clen=Length('code') clen=Length('code')
).aggregate(min=Min('clen'), max=Max('clen')) ).aggregate(min=Min('clen'), max=Max('clen'))
if job.event: 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: 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 # Match invoice numbers
inr_len_agg = Invoice.objects.filter(event__organizer=job.organizer).annotate( inr_len_agg = Invoice.objects.filter(event__organizer=job.organizer).annotate(
clen=Length('invoice_no') clen=Length('invoice_no')
).aggregate(min=Min('clen'), max=Max('clen')) ).aggregate(min=Min('clen'), max=Max('clen'))
if job.event: 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: 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( pattern = re.compile(
"(%s)[ \\-_]*([A-Z0-9]{%s,%s})" % ( "(%s)[ \\-_]*([A-Z0-9]{%s,%s})" % (
@@ -395,6 +419,11 @@ def process_banktransfers(self, job: int, data: list) -> None:
) )
for trans in transactions: for trans in transactions:
if trans.amount == Decimal("0.00"):
# Ignore all zero-valued transactions
trans.state = BankTransaction.STATE_DISCARDED
trans.save()
continue
# Whitespace in references is unreliable since linebreaks and spaces can occur almost anywhere, e.g. # Whitespace in references is unreliable since linebreaks and spaces can occur almost anywhere, e.g.
# DEMOCON-123\n45 should be matched to DEMOCON-12345. However, sometimes whitespace is important, # DEMOCON-123\n45 should be matched to DEMOCON-12345. However, sometimes whitespace is important,
# e.g. when there are two references. "DEMOCON-12345 DEMOCON-45678" would otherwise be parsed as # e.g. when there are two references. "DEMOCON-12345 DEMOCON-45678" would otherwise be parsed as
@@ -409,9 +438,9 @@ def process_banktransfers(self, job: int, data: list) -> None:
if matches: if matches:
if job.event: if job.event:
_handle_transaction(trans, matches, event=job.event) _handle_transaction(trans, matches, regex_match_to_slug, event=job.event)
else: else:
_handle_transaction(trans, matches, organizer=job.organizer) _handle_transaction(trans, matches, regex_match_to_slug, organizer=job.organizer)
else: else:
trans.state = BankTransaction.STATE_NOMATCH trans.state = BankTransaction.STATE_NOMATCH
trans.save() trans.save()

View File

@@ -1,7 +1,6 @@
{% load i18n %} {% load i18n %}
{% load l10n %} {% load l10n %}
{% load commadecimal %} {% load commadecimal %}
{% load static %}
{% load dotdecimal %} {% load dotdecimal %}
{% load ibanformat %} {% load ibanformat %}
{% load money %} {% load money %}
@@ -17,7 +16,7 @@
{% endblocktrans %}</p> {% endblocktrans %}</p>
<div class="row"> <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"> <dl class="dl-horizontal">
<dt>{% trans "Reference code (important):" %}</dt><dd><b>{{ code }}</b></dd> <dt>{% trans "Reference code (important):" %}</dt><dd><b>{{ code }}</b></dd>
<dt>{% trans "Amount:" %}</dt><dd>{{ amount|money:event.currency }}</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." %} {% trans "We will send you an email as soon as we received your payment." %}
</p> </p>
</div> </div>
{% if settings.bank_details_type == "sepa" and any_barcodes %} {% if payment_qr_codes %}
<div class="tabcontainer col-md-6 col-sm-6 hidden-xs text-center js-only blank-after"> {% include "pretixpresale/event/payment_qr_codes.html" %}
<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 apps 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 apps 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 }}&currency={{ 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 apps 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 }}&currency={{ 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>
{% endif %} {% endif %}
</div> </div>
{% if swiss_qrbill %}
<link rel="stylesheet" href="{% static "pretixplugins/banktransfer/swisscross.css" %}">
{% endif %}

View File

@@ -66,7 +66,7 @@ from pretix.control.permissions import (
) )
from pretix.control.views.organizer import OrganizerDetailViewMixin from pretix.control.views.organizer import OrganizerDetailViewMixin
from pretix.helpers.json import CustomJSONEncoder from pretix.helpers.json import CustomJSONEncoder
from pretix.plugins.banktransfer import csvimport, mt940import from pretix.plugins.banktransfer import camtimport, csvimport, mt940import
from pretix.plugins.banktransfer.models import ( from pretix.plugins.banktransfer.models import (
BankImportJob, BankTransaction, RefundExport, BankImportJob, BankTransaction, RefundExport,
) )
@@ -419,6 +419,9 @@ class ImportView(ListView):
): ):
return self.process_mt940() return self.process_mt940()
elif 'file' in self.request.FILES and '.xml' in self.request.FILES.get('file').name.lower():
return self.process_camt()
elif self.request.FILES.get('file') is None: elif self.request.FILES.get('file') is None:
messages.error(self.request, _('You must choose a file to import.')) messages.error(self.request, _('You must choose a file to import.'))
return self.redirect_back() return self.redirect_back()
@@ -432,6 +435,14 @@ class ImportView(ListView):
def settings(self): def settings(self):
return SettingsSandbox('payment', 'banktransfer', getattr(self.request, 'event', self.request.organizer)) return SettingsSandbox('payment', 'banktransfer', getattr(self.request, 'event', self.request.organizer))
def process_camt(self):
try:
return self.start_processing(camtimport.parse(self.request.FILES.get('file')))
except:
logger.exception('Failed to import CAMT file')
messages.error(self.request, _('We were unable to process your input.'))
return self.redirect_back()
def process_mt940(self): def process_mt940(self):
try: try:
return self.start_processing(mt940import.parse(self.request.FILES.get('file'))) return self.start_processing(mt940import.parse(self.request.FILES.get('file')))

View File

@@ -711,7 +711,7 @@ class PaypalMethod(BasePaymentProvider):
description = '{prefix}{orderstring}{postfix}'.format( description = '{prefix}{orderstring}{postfix}'.format(
prefix='{} '.format(self.settings.prefix) if self.settings.prefix else '', prefix='{} '.format(self.settings.prefix) if self.settings.prefix else '',
orderstring=__('Order {order} for {event}').format( orderstring=__('Order {order} for {event}').format(
event=request.event.name, event=self.event.name,
order=payment.order.code order=payment.order.code
), ),
postfix=' {}'.format(self.settings.postfix) if self.settings.postfix else '' postfix=' {}'.format(self.settings.postfix) if self.settings.postfix else ''

View File

@@ -644,7 +644,9 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
FontFallbackParagraph( FontFallbackParagraph(
_("Pending payments at {datetime}").format( _("Pending payments at {datetime}").format(
datetime=date_format( datetime=date_format(
df_start - datetime.timedelta.resolution, (df_start - datetime.timedelta.resolution).astimezone(
self.timezone
),
"SHORT_DATETIME_FORMAT", "SHORT_DATETIME_FORMAT",
) )
), ),
@@ -694,7 +696,9 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
Paragraph( Paragraph(
_("Pending payments at {datetime}").format( _("Pending payments at {datetime}").format(
datetime=date_format( datetime=date_format(
(df_end or now()) - datetime.timedelta.resolution, ((df_end or now()) - datetime.timedelta.resolution).astimezone(
self.timezone
),
"SHORT_DATETIME_FORMAT", "SHORT_DATETIME_FORMAT",
) )
), ),
@@ -751,7 +755,9 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
Paragraph( Paragraph(
_("Total gift card value at {datetime}").format( _("Total gift card value at {datetime}").format(
datetime=date_format( datetime=date_format(
df_start - datetime.timedelta.resolution, (df_start - datetime.timedelta.resolution).astimezone(
self.timezone
),
"SHORT_DATETIME_FORMAT", "SHORT_DATETIME_FORMAT",
) )
), ),
@@ -789,7 +795,9 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
Paragraph( Paragraph(
_("Total gift card value at {datetime}").format( _("Total gift card value at {datetime}").format(
datetime=date_format( datetime=date_format(
(df_end or now()) - datetime.timedelta.resolution, ((df_end or now()) - datetime.timedelta.resolution).astimezone(
self.timezone
),
"SHORT_DATETIME_FORMAT", "SHORT_DATETIME_FORMAT",
) )
), ),

View File

@@ -137,7 +137,7 @@ logger = logging.getLogger('pretix.plugins.stripe')
# Real-time payments # Real-time payments
# - Swish: ✓ # - Swish: ✓
# - PayNow: ✗ # - PayNow: ✗
# - PromptPay: # - PromptPay:
# - Pix: ✗ # - Pix: ✗
# #
# Vouchers # Vouchers
@@ -412,6 +412,18 @@ class StripeSettingsHolder(BasePaymentProvider):
'before they work properly.'), 'before they work properly.'),
required=False, required=False,
)), )),
('method_pay_by_bank',
forms.BooleanField(
label=_('Pay by bank'),
disabled=self.event.currency not in ['EUR', 'GBP'],
help_text=' '.join([
str(_('Some payment methods might need to be enabled in the settings of your Stripe account '
'before they work properly.')),
str(_('Currently only available for charges in GBP and customers with UK bank accounts, and '
'in private preview for France and Germany.'))
]),
required=False,
)),
('method_wechatpay', ('method_wechatpay',
forms.BooleanField( forms.BooleanField(
label=_('WeChat Pay'), label=_('WeChat Pay'),
@@ -428,6 +440,14 @@ class StripeSettingsHolder(BasePaymentProvider):
'before they work properly.'), 'before they work properly.'),
required=False, required=False,
)), )),
('method_promptpay',
forms.BooleanField(
label='PromptPay',
disabled=self.event.currency != 'THB',
help_text=_('Some payment methods might need to be enabled in the settings of your Stripe account '
'before they work properly.'),
required=False,
)),
('method_swish', ('method_swish',
forms.BooleanField( forms.BooleanField(
label=_('Swish'), label=_('Swish'),
@@ -1810,6 +1830,32 @@ class StripeRevolutPay(StripeRedirectMethod):
} }
class StripePayByBank(StripeRedirectMethod):
identifier = 'stripe_pay_by_bank'
verbose_name = _('Pay by bank via Stripe')
public_name = _('Pay by bank')
method = 'pay_by_bank'
redirect_in_widget_allowed = False
confirmation_method = 'automatic'
explanation = _(
'Pay by bank allows you to authorize a secure Open Banking payment from your banking app. Currently available '
'only with a UK bank account.'
)
def is_allowed(self, request: HttpRequest, total: Decimal=None) -> bool:
return super().is_allowed(request, total) and self.event.currency == 'GBP'
def _payment_intent_kwargs(self, request, payment):
return {
"payment_method_data": {
"type": "pay_by_bank",
"billing_details": {
"email": payment.order.email,
},
},
}
class StripePayPal(StripeRedirectMethod): class StripePayPal(StripeRedirectMethod):
identifier = 'stripe_paypal' identifier = 'stripe_paypal'
verbose_name = _('PayPal via Stripe') verbose_name = _('PayPal via Stripe')
@@ -1842,6 +1888,30 @@ class StripeSwish(StripeRedirectMethod):
} }
class StripePromptPay(StripeRedirectMethod):
identifier = 'stripe_promptpay'
verbose_name = _('PromptPay via Stripe')
public_name = 'PromptPay'
method = 'promptpay'
confirmation_method = 'automatic'
explanation = _(
'This payment method is available to PromptPay users in Thailand. Please have your app ready.'
)
def is_allowed(self, request: HttpRequest, total: Decimal=None) -> bool:
return super().is_allowed(request, total) and request.event.currency == "THB"
def _payment_intent_kwargs(self, request, payment):
return {
"payment_method_data": {
"type": "promptpay",
"billing_details": {
"email": payment.order.email,
},
},
}
class StripeTwint(StripeRedirectMethod): class StripeTwint(StripeRedirectMethod):
identifier = 'stripe_twint' identifier = 'stripe_twint'
verbose_name = _('TWINT via Stripe') verbose_name = _('TWINT via Stripe')

View File

@@ -38,7 +38,6 @@ from pretix.base.signals import (
) )
from pretix.control.signals import nav_organizer from pretix.control.signals import nav_organizer
from pretix.plugins.stripe.forms import StripeKeyValidator from pretix.plugins.stripe.forms import StripeKeyValidator
from pretix.plugins.stripe.payment import StripeMethod
from pretix.presale.signals import html_head, process_response from pretix.presale.signals import html_head, process_response
@@ -47,15 +46,17 @@ def register_payment_provider(sender, **kwargs):
from .payment import ( from .payment import (
StripeAffirm, StripeAlipay, StripeBancontact, StripeCC, StripeEPS, StripeAffirm, StripeAlipay, StripeBancontact, StripeCC, StripeEPS,
StripeGiropay, StripeIdeal, StripeKlarna, StripeMobilePay, StripeGiropay, StripeIdeal, StripeKlarna, StripeMobilePay,
StripeMultibanco, StripePayPal, StripePrzelewy24, StripeRevolutPay, StripeMultibanco, StripePayByBank, StripePayPal, StripePromptPay,
StripeSEPADirectDebit, StripeSettingsHolder, StripeSofort, StripeSwish, StripePrzelewy24, StripeRevolutPay, StripeSEPADirectDebit,
StripeTwint, StripeWeChatPay, StripeSettingsHolder, StripeSofort, StripeSwish, StripeTwint,
StripeWeChatPay,
) )
return [ return [
StripeSettingsHolder, StripeCC, StripeGiropay, StripeIdeal, StripeAlipay, StripeBancontact, StripeSettingsHolder, StripeCC, StripeGiropay, StripeIdeal, StripeAlipay, StripeBancontact,
StripeSofort, StripeEPS, StripeMultibanco, StripePrzelewy24, StripeRevolutPay, StripeWeChatPay, StripeSofort, StripeEPS, StripeMultibanco, StripePayByBank, StripePrzelewy24, StripePromptPay, StripeRevolutPay,
StripeSEPADirectDebit, StripeAffirm, StripeKlarna, StripePayPal, StripeSwish, StripeTwint, StripeMobilePay StripeWeChatPay, StripeSEPADirectDebit, StripeAffirm, StripeKlarna, StripePayPal, StripeSwish,
StripeTwint, StripeMobilePay
] ]
@@ -189,6 +190,8 @@ def nav_o(sender, request, organizer, **kwargs):
@receiver(signal=process_response, dispatch_uid="stripe_middleware_resp") @receiver(signal=process_response, dispatch_uid="stripe_middleware_resp")
def signal_process_response(sender, request: HttpRequest, response: HttpResponse, **kwargs): def signal_process_response(sender, request: HttpRequest, response: HttpResponse, **kwargs):
from pretix.plugins.stripe.payment import StripeMethod
provider = StripeMethod(sender) provider = StripeMethod(sender)
url = resolve(request.path_info) url = resolve(request.path_info)

View File

@@ -79,3 +79,9 @@
.vcenter { .vcenter {
margin: auto; margin: auto;
} }
.stripe-qr-code {
max-width: 80%;
width: 200px;
height: auto;
}

View File

@@ -325,6 +325,8 @@ $(function () {
} else if ($("#stripe_payment_intent_next_action_redirect_url").length) { } else if ($("#stripe_payment_intent_next_action_redirect_url").length) {
let payment_intent_next_action_redirect_url = $.trim($("#stripe_payment_intent_next_action_redirect_url").html()); let payment_intent_next_action_redirect_url = $.trim($("#stripe_payment_intent_next_action_redirect_url").html());
pretixstripe.handlePaymentRedirectAction(payment_intent_next_action_redirect_url); pretixstripe.handlePaymentRedirectAction(payment_intent_next_action_redirect_url);
} else if ($.trim($("#stripe_payment_intent_action_type").html()) === "promptpay_display_qr_code") {
waitingDialog.hide();
} else if ($.trim($("#stripe_payment_intent_action_type").html()) === "wechat_pay_display_qr_code") { } else if ($.trim($("#stripe_payment_intent_action_type").html()) === "wechat_pay_display_qr_code") {
let payment_intent_client_secret = $.trim($("#stripe_payment_intent_client_secret").html()); let payment_intent_client_secret = $.trim($("#stripe_payment_intent_client_secret").html());
pretixstripe.handleWechatAction(payment_intent_client_secret); pretixstripe.handleWechatAction(payment_intent_client_secret);

View File

@@ -22,7 +22,6 @@
import logging import logging
from urllib.parse import urlsplit from urllib.parse import urlsplit
import stripe
from django.conf import settings from django.conf import settings
from pretix.base.services.tasks import EventTask from pretix.base.services.tasks import EventTask
@@ -50,7 +49,10 @@ def get_stripe_account_key(prov):
@app.task(base=EventTask, max_retries=5, default_retry_delay=1) @app.task(base=EventTask, max_retries=5, default_retry_delay=1)
def stripe_verify_domain(event, domain): def stripe_verify_domain(event, domain):
import stripe
from pretix.plugins.stripe.payment import StripeCC from pretix.plugins.stripe.payment import StripeCC
prov = StripeCC(event) prov = StripeCC(event)
account = get_stripe_account_key(prov) account = get_stripe_account_key(prov)

View File

@@ -27,9 +27,21 @@
<div class="stripe-errors sr-only panel-body"> <div class="stripe-errors sr-only panel-body">
</div> </div>
<div class="panel-body embed-responsive embed-responsive-sca" id="scacontainer"> {% if payment_intent_promptpay_image_url %}
<div class="panel-body">
</div> <p>{% blocktrans trimmed %}
Please scan the QR code below to complete your PromptPay payment.
Once you have completed your payment, you can refresh this page.
{% endblocktrans %}</p>
<div class="text-center">
<img src="{{ payment_intent_promptpay_image_url }}" alt="{% trans 'PromptPay QR code' %}"
class="stripe-qr-code" />
</div>
</div>
{% else %}
<div class="panel-body embed-responsive embed-responsive-sca" id="scacontainer">
</div>
{% endif %}
</div> </div>
<div class="row checkout-button-row"> <div class="row checkout-button-row">
<div class="col-md-4"> <div class="col-md-4">

View File

@@ -37,7 +37,6 @@ import logging
import urllib.parse import urllib.parse
import requests import requests
import stripe
from django.contrib import messages from django.contrib import messages
from django.core import signing from django.core import signing
from django.db import transaction from django.db import transaction
@@ -68,7 +67,6 @@ from pretix.helpers.http import redirect_to_url
from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse
from pretix.plugins.stripe.forms import OrganizerStripeSettingsForm from pretix.plugins.stripe.forms import OrganizerStripeSettingsForm
from pretix.plugins.stripe.models import ReferencedStripeObject from pretix.plugins.stripe.models import ReferencedStripeObject
from pretix.plugins.stripe.payment import StripeCC, StripeSettingsHolder
from pretix.plugins.stripe.tasks import ( from pretix.plugins.stripe.tasks import (
get_domain_for_event, stripe_verify_domain, get_domain_for_event, stripe_verify_domain,
) )
@@ -100,6 +98,8 @@ def redirect_view(request, *args, **kwargs):
@scopes_disabled() @scopes_disabled()
def oauth_return(request, *args, **kwargs): def oauth_return(request, *args, **kwargs):
import stripe
if 'payment_stripe_oauth_event' not in request.session: if 'payment_stripe_oauth_event' not in request.session:
messages.error(request, _('An error occurred during connecting with Stripe, please try again.')) messages.error(request, _('An error occurred during connecting with Stripe, please try again.'))
return redirect('control:index') return redirect('control:index')
@@ -268,6 +268,10 @@ SOURCE_TYPES = {
def charge_webhook(event, event_json, charge_id, rso): def charge_webhook(event, event_json, charge_id, rso):
import stripe
from pretix.plugins.stripe.payment import StripeCC
prov = StripeCC(event) prov = StripeCC(event)
prov._init_api() prov._init_api()
@@ -371,6 +375,10 @@ def charge_webhook(event, event_json, charge_id, rso):
def source_webhook(event, event_json, source_id, rso): def source_webhook(event, event_json, source_id, rso):
import stripe
from pretix.plugins.stripe.payment import StripeCC
prov = StripeCC(event) prov = StripeCC(event)
prov._init_api() prov._init_api()
try: try:
@@ -440,6 +448,10 @@ def source_webhook(event, event_json, source_id, rso):
def paymentintent_webhook(event, event_json, paymentintent_id, rso): def paymentintent_webhook(event, event_json, paymentintent_id, rso):
import stripe
from pretix.plugins.stripe.payment import StripeCC
prov = StripeCC(event) prov = StripeCC(event)
prov._init_api() prov._init_api()
@@ -516,6 +528,8 @@ class StripeOrderView:
@method_decorator(xframe_options_exempt, 'dispatch') @method_decorator(xframe_options_exempt, 'dispatch')
class ReturnView(StripeOrderView, View): class ReturnView(StripeOrderView, View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
import stripe
prov = self.pprov prov = self.pprov
prov._init_api() prov._init_api()
try: try:
@@ -568,6 +582,10 @@ class ReturnView(StripeOrderView, View):
class ScaView(StripeOrderView, View): class ScaView(StripeOrderView, View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
import stripe
from pretix.plugins.stripe.payment import StripeSettingsHolder
prov = self.pprov prov = self.pprov
prov._init_api() prov._init_api()
@@ -595,7 +613,7 @@ class ScaView(StripeOrderView, View):
if intent.status == 'requires_action' and intent.next_action.type in [ if intent.status == 'requires_action' and intent.next_action.type in [
'use_stripe_sdk', 'redirect_to_url', 'alipay_handle_redirect', 'wechat_pay_display_qr_code', 'use_stripe_sdk', 'redirect_to_url', 'alipay_handle_redirect', 'wechat_pay_display_qr_code',
'swish_handle_redirect_or_display_qr_code', 'multibanco_display_details', 'swish_handle_redirect_or_display_qr_code', 'multibanco_display_details', 'promptpay_display_qr_code',
]: ]:
ctx = { ctx = {
'order': self.order, 'order': self.order,
@@ -613,6 +631,8 @@ class ScaView(StripeOrderView, View):
elif intent.next_action.type == 'multibanco_display_details': elif intent.next_action.type == 'multibanco_display_details':
ctx['payment_intent_next_action_redirect_url'] = intent.next_action.multibanco_display_details['hosted_voucher_url'] ctx['payment_intent_next_action_redirect_url'] = intent.next_action.multibanco_display_details['hosted_voucher_url']
ctx['payment_intent_redirect_action_handling'] = 'iframe' ctx['payment_intent_redirect_action_handling'] = 'iframe'
elif intent.next_action.type == 'promptpay_display_qr_code':
ctx['payment_intent_promptpay_image_url'] = intent.next_action.promptpay_display_qr_code['image_url_svg']
r = render(request, 'pretixplugins/stripe/sca.html', ctx) r = render(request, 'pretixplugins/stripe/sca.html', ctx)
r._csp_ignore = True r._csp_ignore = True

View File

@@ -1545,6 +1545,8 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
ctx['invoice_address_asked'] = self.address_asked ctx['invoice_address_asked'] = self.address_asked
ctx['customer'] = self.cart_customer ctx['customer'] = self.cart_customer
ctx['transmission_fields'] = self.invoice_address.describe_transmission(self.event)
self.cart_session['shown_total'] = str(ctx['cart']['total']) self.cart_session['shown_total'] = str(ctx['cart']['total'])
email = self.cart_session.get('contact_form_data', {}).get('email') email = self.cart_session.get('contact_form_data', {}).get('email')

View File

@@ -111,7 +111,7 @@
<dt>{% trans "Internal reference" %}</dt> <dt>{% trans "Internal reference" %}</dt>
<dd>{{ addr.internal_reference }}</dd> <dd>{{ addr.internal_reference }}</dd>
{% endif %} {% endif %}
{% for k, v in addr.describe_transmission %} {% for k, v in transmission_fields %}
<dt>{{ k }}</dt> <dt>{{ k }}</dt>
<dd>{{ v }}</dd> <dd>{{ v }}</dd>
{% endfor %} {% endfor %}

View File

@@ -1,3 +1,4 @@
{% load html_time %}
{% load i18n %} {% load i18n %}
{% load icon %} {% load icon %}
{% load eventurl %} {% load eventurl %}
@@ -21,20 +22,18 @@
{% if event.settings.show_times %} {% if event.settings.show_times %}
<br> <br>
<span data-time="{{ ev.date_from.isoformat }}" data-timezone="{{ request.event.timezone }}"> <span data-time="{{ ev.date_from.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% with time_human=ev.date_from|date:"TIME_FORMAT" time_24=ev.date_from|time:"H:i" %} {% html_time ev.date_from "TIME_FORMAT" attr_fmt="H:i" as time%}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %} {% blocktrans with time=time %}
Begin: {{ time }} Begin: {{ time }}
{% endblocktrans %} {% endblocktrans %}
{% endwith %}
</span> </span>
{% if event.settings.show_date_to and ev.date_to %} {% if event.settings.show_date_to and ev.date_to %}
<br> <br>
<span data-time="{{ ev.date_to.isoformat }}" data-timezone="{{ request.event.timezone }}"> <span data-time="{{ ev.date_to.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% with time_human=ev.date_to|date:"TIME_FORMAT" time_24=ev.date_to|time:"H:i" %} {% html_time ev.date_to "TIME_FORMAT" attr_fmt="H:i" as time%}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %} {% blocktrans with time=time %}
End: {{ time }} End: {{ time }}
{% endblocktrans %} {% endblocktrans %}
{% endwith %}
</span> </span>
{% endif %} {% endif %}
{% endif %} {% endif %}
@@ -42,19 +41,17 @@
<br> <br>
{% if ev.date_admission|date:"SHORT_DATE_FORMAT" == ev.date_from|date:"SHORT_DATE_FORMAT" %} {% if ev.date_admission|date:"SHORT_DATE_FORMAT" == ev.date_from|date:"SHORT_DATE_FORMAT" %}
<span data-time="{{ ev.date_admission.isoformat }}" data-timezone="{{ request.event.timezone }}"> <span data-time="{{ ev.date_admission.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% with time_human=ev.date_admission|date:"TIME_FORMAT" time_24=ev.date_admission|time:"H:i" %} {% html_time ev.date_admission "TIME_FORMAT" attr_fmt="H:i" as time%}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %} {% blocktrans trimmed with time=time %}
Admission: {{ time }} Admission: {{ time }}
{% endblocktrans %} {% endblocktrans %}
{% endwith %}
</span> </span>
{% else %} {% else %}
<span data-time="{{ ev.date_admission.isoformat }}" data-timezone="{{ request.event.timezone }}"> <span data-time="{{ ev.date_admission.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% with datetime_human=ev.date_admission|date:"SHORT_DATETIME_FORMAT" datetime_iso=ev.date_admission|time:"Y-m-d H:i" %} {% html_time ev.date_admission "SHORT_DATETIME_FORMAT" attr_fmt="Y-m-d H:i" as datetime%}
{% blocktrans trimmed with datetime='<time datetime="'|add:datetime_iso|add:'">'|add:datetime_human|add:"</time>"|safe %} {% blocktrans trimmed with datetime=datetime %}
Admission: {{ datetime }} Admission: {{ datetime }}
{% endblocktrans %} {% endblocktrans %}
{% endwith %}
</span> </span>
{% endif %} {% endif %}
{% endif %} {% endif %}

View File

@@ -1,4 +1,5 @@
{% extends "pretixpresale/event/base.html" %} {% extends "pretixpresale/event/base.html" %}
{% load html_time %}
{% load i18n %} {% load i18n %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load eventsignal %} {% load eventsignal %}
@@ -92,11 +93,10 @@
A payment of {{ total }} is still pending for this order. A payment of {{ total }} is still pending for this order.
{% endblocktrans %}</strong> {% endblocktrans %}</strong>
<strong> <strong>
{% with date_human=order|format_expires|safe date_iso=order.expires|date:"c" %} {% html_time order.expires "format_expires" as date %}
{% blocktrans trimmed with date='<time datetime="'|add:date_iso|add:'">'|add:date_human|add:"</time>"|safe %} {% blocktrans trimmed with date=date %}
Please complete your payment before {{ date }} Please complete your payment before {{ date }}
{% endblocktrans %} {% endblocktrans %}
{% endwith %}
</strong> </strong>
</p> </p>
{% if last_payment %} {% if last_payment %}

View File

@@ -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 apps 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 %}

View File

@@ -1,3 +1,4 @@
{% load html_time %}
{% load i18n %} {% load i18n %}
{% load date_fast %} {% load date_fast %}
{% load calendarhead %} {% load calendarhead %}
@@ -55,7 +56,7 @@
running running
{% elif event.event.presale_has_ended %} {% elif event.event.presale_has_ended %}
over over
{% elif event.event.settings.presale_start_show_date and event.event.presale_start %} {% elif event.event.settings.presale_start_show_date and event.event.effective_presale_start %}
soon soon
{% else %} {% else %}
soon soon
@@ -108,13 +109,12 @@
<span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Book now" %} <span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Book now" %}
{% elif event.event.presale_has_ended %} {% elif event.event.presale_has_ended %}
{% trans "Sale over" %} {% trans "Sale over" %}
{% elif event.event.settings.presale_start_show_date and event.event.presale_start %} {% elif event.event.settings.presale_start_show_date and event.event.effective_presale_start %}
<span class="fa fa-ticket" aria-hidden="true"></span> <span class="fa fa-ticket" aria-hidden="true"></span>
{% with date_human=event.event.presale_start|date_fast:"SHORT_DATE_FORMAT" date_iso=event.event.presale_start|date_fast:"c" %} {% html_time event.event.effective_presale_start "SHORT_DATE_FORMAT" as start_date %}
{% blocktrans with start_date="<time datetime='"|add:date_iso|add:"'>"|add:date_human|add:"</time>"|safe %} {% blocktrans with start_date=start_date %}
from {{ start_date }} from {{ start_date }}
{% endblocktrans %} {% endblocktrans %}
{% endwith %}
{% else %} {% else %}
<span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Soon" %} <span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Soon" %}
{% endif %} {% endif %}

View File

@@ -1,3 +1,4 @@
{% load html_time %}
{% load i18n %} {% load i18n %}
{% load eventurl %} {% load eventurl %}
<div class="day-calendar cal-size-{{ raster_to_shortest_ratio }}{% if no_headlines %} no-headlines{% endif %}" <div class="day-calendar cal-size-{{ raster_to_shortest_ratio }}{% if no_headlines %} no-headlines{% endif %}"
@@ -52,7 +53,7 @@
running running
{% elif event.event.presale_has_ended %} {% elif event.event.presale_has_ended %}
over over
{% elif event.event.settings.presale_start_show_date and event.event.presale_start %} {% elif event.event.settings.presale_start_show_date and event.event.effective_presale_start %}
soon soon
{% else %} {% else %}
soon soon
@@ -114,9 +115,10 @@
<span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Book now" %} <span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Book now" %}
{% elif event.event.presale_has_ended %} {% elif event.event.presale_has_ended %}
<span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Sale over" %} <span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Sale over" %}
{% elif event.event.settings.presale_start_show_date and event.event.presale_start %} {% elif event.event.settings.presale_start_show_date and event.event.effective_presale_start %}
<span class="fa fa-ticket" aria-hidden="true"></span> <span class="fa fa-ticket" aria-hidden="true"></span>
{% blocktrans with start_date=event.event.presale_start|date:"SHORT_DATE_FORMAT" %} {% html_time event.event.effective_presale_start "SHORT_DATE_FORMAT" as start_date %}
{% blocktrans with start_date=start_date %}
from {{ start_date }} from {{ start_date }}
{% endblocktrans %} {% endblocktrans %}
{% else %} {% else %}

View File

@@ -1,3 +1,4 @@
{% load html_time %}
{% load i18n %} {% load i18n %}
{% load icon %} {% load icon %}
{% load textbubble %} {% load textbubble %}
@@ -52,11 +53,10 @@
{% endtextbubble %} {% endtextbubble %}
{% if event.settings.presale_start_show_date %} {% if event.settings.presale_start_show_date %}
<br><span class="text-muted"> <br><span class="text-muted">
{% with date_iso=event.effective_presale_start.isoformat date_human=event.effective_presale_start|date:"SHORT_DATE_FORMAT" %} {% html_time event.event.effective_presale_start "SHORT_DATE_FORMAT" as date %}
{% blocktrans trimmed with date='<time datetime="'|add:date_iso|add:'">'|add:date_human|add:"</time>"|safe %} {% blocktrans with date=date %}
Sale starts {{ date }} Sale starts {{ date }}
{% endblocktrans %} {% endblocktrans %}
{% endwith %}
</span> </span>
{% endif %} {% endif %}
{% endif %} {% endif %}

View File

@@ -1,3 +1,4 @@
{% load html_time %}
{% load i18n %} {% load i18n %}
{% load date_fast %} {% load date_fast %}
<div class="week-calendar"> <div class="week-calendar">
@@ -24,7 +25,7 @@
running running
{% elif event.event.presale_has_ended %} {% elif event.event.presale_has_ended %}
over over
{% elif event.event.settings.presale_start_show_date and event.event.presale_start %} {% elif event.event.settings.presale_start_show_date and event.event.effective_presale_start %}
soon soon
{% else %} {% else %}
soon soon
@@ -77,9 +78,10 @@
<span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Book now" %} <span class="fa fa-ticket" aria-hidden="true"></span> {% trans "Book now" %}
{% elif event.event.presale_has_ended %} {% elif event.event.presale_has_ended %}
{% trans "Sale over" %} {% trans "Sale over" %}
{% elif event.event.settings.presale_start_show_date and event.event.presale_start %} {% elif event.event.settings.presale_start_show_date and event.event.effective_presale_start %}
<span class="fa fa-ticket" aria-hidden="true"></span> <span class="fa fa-ticket" aria-hidden="true"></span>
{% blocktrans with start_date=event.event.presale_start|date_fast:"SHORT_DATE_FORMAT" %} {% html_time event.event.effective_presale_start "SHORT_DATE_FORMAT" as start_date %}
{% blocktrans with start_date=start_date %}
from {{ start_date }} from {{ start_date }}
{% endblocktrans %} {% endblocktrans %}
{% else %} {% else %}

View File

@@ -438,9 +438,6 @@ def get_grouped_items(event, *, channel: SalesChannel, subevent=None, voucher=No
base_price_is='net' if event.settings.display_net_prices else 'gross') # backwards-compat base_price_is='net' if event.settings.display_net_prices else 'gross') # backwards-compat
) if var.original_price or item.original_price else None ) if var.original_price or item.original_price else None
if not display_add_to_cart:
display_add_to_cart = not item.requires_seat and var.order_max > 0
var.current_unavailability_reason = var.unavailability_reason(has_voucher=voucher, subevent=subevent) var.current_unavailability_reason = var.unavailability_reason(has_voucher=voucher, subevent=subevent)
item.original_price = ( item.original_price = (
@@ -471,6 +468,8 @@ def get_grouped_items(event, *, channel: SalesChannel, subevent=None, voucher=No
item.best_variation_availability = max([v.cached_availability[0] for v in item.available_variations]) item.best_variation_availability = max([v.cached_availability[0] for v in item.available_variations])
item._remove = not bool(item.available_variations) item._remove = not bool(item.available_variations)
if not item._remove and not display_add_to_cart:
display_add_to_cart = not item.requires_seat and any(v.order_max > 0 for v in item.available_variations)
if not quota_cache_existed and not voucher and not allow_addons and not base_qs_set and not filter_items and not filter_categories: if not quota_cache_existed and not voucher and not allow_addons and not base_qs_set and not filter_items and not filter_categories:
event.cache.set(quota_cache_key, quota_cache, 5) event.cache.set(quota_cache_key, quota_cache, 5)

View File

@@ -471,10 +471,11 @@ class WidgetAPIProductList(EventListMixin, View):
availability['color'] = 'red' availability['color'] = 'red'
availability['text'] = gettext('Sale over') availability['text'] = gettext('Sale over')
availability['reason'] = 'over' availability['reason'] = 'over'
elif event.settings.presale_start_show_date and ev.presale_start: elif event.settings.presale_start_show_date and ev.effective_presale_start:
availability['color'] = 'orange' availability['color'] = 'orange'
availability['text'] = gettext('from %(start_date)s') % { availability['text'] = gettext('from %(start_date)s') % {
'start_date': date_format(ev.presale_start.astimezone(tz or event.timezone), "SHORT_DATE_FORMAT") 'start_date': date_format(ev.effective_presale_start.astimezone(tz or event.timezone),
"SHORT_DATE_FORMAT")
} }
availability['reason'] = 'soon' availability['reason'] = 'soon'
else: else:

View File

@@ -347,11 +347,53 @@ if HAS_CELERY:
CELERY_RESULT_BACKEND = config.get('celery', 'backend') CELERY_RESULT_BACKEND = config.get('celery', 'backend')
if HAS_CELERY_BROKER_TRANSPORT_OPTS: if HAS_CELERY_BROKER_TRANSPORT_OPTS:
CELERY_BROKER_TRANSPORT_OPTIONS = loads(config.get('celery', 'broker_transport_options')) CELERY_BROKER_TRANSPORT_OPTIONS = loads(config.get('celery', 'broker_transport_options'))
else:
CELERY_BROKER_TRANSPORT_OPTIONS = {}
if HAS_CELERY_BACKEND_TRANSPORT_OPTS: if HAS_CELERY_BACKEND_TRANSPORT_OPTS:
CELERY_RESULT_BACKEND_TRANSPORT_OPTIONS = loads(config.get('celery', 'backend_transport_options')) CELERY_RESULT_BACKEND_TRANSPORT_OPTIONS = loads(config.get('celery', 'backend_transport_options'))
CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True
if CELERY_BROKER_URL.startswith("amqp://"):
# https://docs.celeryq.dev/en/latest/userguide/routing.html#routing-options-rabbitmq-priorities
# Enable priorities for all queues
CELERY_TASK_QUEUE_MAX_PRIORITY = 3
# On RabbitMQ, higher number is higher priority, and having less levels makes rabbitmq use less CPU and RAM
PRIORITY_CELERY_LOW = 1
PRIORITY_CELERY_MID = 2
PRIORITY_CELERY_HIGH = 3
PRIORITY_CELERY_LOWEST_FUNC = min
PRIORITY_CELERY_HIGHEST_FUNC = max
# Set default
CELERY_TASK_DEFAULT_PRIORITY = PRIORITY_CELERY_MID
elif CELERY_BROKER_URL.startswith("redis://"):
# https://docs.celeryq.dev/en/latest/userguide/routing.html#redis-message-priorities
CELERY_BROKER_TRANSPORT_OPTIONS.update({
"queue_order_strategy": "priority",
"sep": ":",
"priority_steps": [0, 4, 8]
})
# On redis, lower number is higher priority, and it appears that there are always levels 0-9 even though it
# is only really executed based on the 3 steps listed above.
PRIORITY_CELERY_LOW = 9
PRIORITY_CELERY_MID = 5
PRIORITY_CELERY_HIGH = 0
PRIORITY_CELERY_LOWEST_FUNC = max
PRIORITY_CELERY_HIGHEST_FUNC = min
CELERY_TASK_DEFAULT_PRIORITY = PRIORITY_CELERY_MID
else:
# No priority support assumed
PRIORITY_CELERY_LOW = 0
PRIORITY_CELERY_MID = 0
PRIORITY_CELERY_HIGH = 0
PRIORITY_CELERY_LOWEST_FUNC = min
PRIORITY_CELERY_HIGHEST_FUNC = max
else: else:
CELERY_TASK_ALWAYS_EAGER = True CELERY_TASK_ALWAYS_EAGER = True
PRIORITY_CELERY_LOW = 0
PRIORITY_CELERY_MID = 0
PRIORITY_CELERY_HIGH = 0
PRIORITY_CELERY_LOWEST_FUNC = min
PRIORITY_CELERY_HIGHEST_FUNC = max
CACHE_TICKETS_HOURS = config.getint('cache', 'tickets', fallback=24 * 3) CACHE_TICKETS_HOURS = config.getint('cache', 'tickets', fallback=24 * 3)

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 842 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 B

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Some files were not shown because too many files have changed in this diff Show More