From b000dff1348fca836196914586a9a2f9d9201cb7 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Tue, 21 Apr 2026 15:14:10 +0200 Subject: [PATCH] Invoices: Allow to use currency rates from National Bank of Poland (#6100) --- src/pretix/base/services/currencies.py | 28 ++++++++++++++++++++++++++ src/pretix/base/services/invoices.py | 13 ++++++++++++ src/pretix/base/settings.py | 2 ++ src/tests/base/test_invoices.py | 19 +++++++++++++++++ 4 files changed, 62 insertions(+) diff --git a/src/pretix/base/services/currencies.py b/src/pretix/base/services/currencies.py index 620f88b121..c8de3c4891 100644 --- a/src/pretix/base/services/currencies.py +++ b/src/pretix/base/services/currencies.py @@ -38,6 +38,7 @@ SOURCE_NAMES = { None: _('European Central Bank'), # backwards-compatibility 'eu:ecb:eurofxref-daily': _('European Central Bank'), 'cz:cnb:rate-fixing-daily': _('Czech National Bank'), + 'pl:nbp:table-a': _('National Bank of Poland'), } @@ -49,6 +50,7 @@ def fetch_rates(sender, **kwargs): source_tasks = { 'eu:ecb:eurofxref-daily': fetch_ecb_rates, 'cz:cnb:rate-fixing-daily': fetch_cnb_cz_rates, + 'pl:nbp:table-a': fetch_nbp_pl_rates, } for source_name, task in source_tasks.items(): @@ -144,3 +146,29 @@ def fetch_cnb_cz_rates(): rate=rate, ) ) + + +@app.task() +def fetch_nbp_pl_rates(): + """ + Fetches currency rates from the Polish National Bank. + """ + r = requests.get("https://api.nbp.pl/api/exchangerates/tables/A/", headers={ + "Accept": "application/json", + }) + r.raise_for_status() + data = r.json()[0] + + source_date = datetime.strptime(data["effectiveDate"], "%Y-%m-%d").date() + + for r in data["rates"]: + rate = Decimal(r["mid"]).quantize(Decimal('0.000001')) + ExchangeRate.objects.update_or_create( + source='pl:nbp:table-a', + source_currency=r["code"], + other_currency='PLN', + defaults=dict( + source_date=source_date, + rate=rate, + ) + ) diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py index 5e4e9666b0..75653e9081 100644 --- a/src/pretix/base/services/invoices.py +++ b/src/pretix/base/services/invoices.py @@ -205,6 +205,19 @@ def build_invoice(invoice: Invoice) -> Invoice: invoice.foreign_currency_rate = rate.rate.quantize(Decimal('0.0001'), ROUND_HALF_UP) invoice.foreign_currency_rate_date = rate.source_date invoice.foreign_currency_source = 'cz:cnb:rate-fixing-daily' + elif invoice.event.settings.invoice_eu_currencies == 'PLN' and invoice.event.currency != 'PLN': + invoice.foreign_currency_display = 'PLN' + if settings.FETCH_ECB_RATES: + rate = ExchangeRate.objects.filter( + source='pl:nbp:table-a', + source_currency=invoice.event.currency, + other_currency=invoice.foreign_currency_display, + source_date__gt=now().date() - timedelta(days=7) + ).first() + if rate: + invoice.foreign_currency_rate = rate.rate.quantize(Decimal('0.0001'), ROUND_HALF_UP) + invoice.foreign_currency_rate_date = rate.source_date + invoice.foreign_currency_source = 'pl:nbp:table-a' except InvoiceAddress.DoesNotExist: ia = None diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index 940f621920..5e2be57132 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -574,6 +574,7 @@ DEFAULTS = { ('True', _('Based on European Central Bank daily rates, whenever the invoice recipient is in an EU ' 'country that uses a different currency.')), ('CZK', _('Based on Czech National Bank daily rates, whenever the invoice amount is not in CZK.')), + ('PLN', _('Based on National Bank of Poland daily rates, whenever the invoice amount is not in PLN.')), ), ), 'serializer_kwargs': dict( @@ -582,6 +583,7 @@ DEFAULTS = { ('True', _('Based on European Central Bank daily rates, whenever the invoice recipient is in an EU ' 'country that uses a different currency.')), ('CZK', _('Based on Czech National Bank daily rates, whenever the invoice amount is not in CZK.')), + ('PLN', _('Based on National Bank of Poland daily rates, whenever the invoice amount is not in PLN.')), ), ), }, diff --git a/src/tests/base/test_invoices.py b/src/tests/base/test_invoices.py index 6d8ee71f6b..790633df82 100644 --- a/src/tests/base/test_invoices.py +++ b/src/tests/base/test_invoices.py @@ -123,6 +123,8 @@ def env(): ExchangeRate.objects.create(source_date=date.today(), source='eu:ecb:eurofxref-daily', source_currency='EUR', other_currency=currency, rate=rate) ExchangeRate.objects.create(source_date=date.today(), source='cz:cnb:rate-fixing-daily', source_currency='EUR', other_currency='CZK', rate=Decimal('25.0000')) + ExchangeRate.objects.create(source_date=date.today(), source='pl:nbp:table-a', source_currency='EUR', + other_currency='PLN', rate=Decimal('4.2355')) yield event, o @@ -347,6 +349,23 @@ def test_invoice_indirect_currency_conversion(env): assert inv.foreign_currency_source == 'eu:ecb:eurofxref-daily' +@pytest.mark.django_db +def test_invoice_pln_currency_conversion(env): + event, order = env + event.settings.invoice_eu_currencies = 'PLN' + + event.settings.set('invoice_language', 'en') + InvoiceAddress.objects.create(company='Acme Company', street='221B Baker Street', zipcode='12345', city='Warsaw', + country=Country('PL'), vat_id='PL123456780', vat_id_validated=True, order=order, + is_business=True) + + inv = generate_invoice(order) + assert inv.foreign_currency_display == "PLN" + assert inv.foreign_currency_rate == Decimal("4.2355") + assert inv.foreign_currency_rate_date == date.today() + assert inv.foreign_currency_source == 'pl:nbp:table-a' + + @pytest.mark.django_db def test_invoice_czk_currency_conversion(env): event, order = env