Invoices: Allow to use currency rates from National Bank of Poland (#6100)

This commit is contained in:
Raphael Michel
2026-04-21 15:14:10 +02:00
committed by GitHub
parent ba75de7e7d
commit b000dff134
4 changed files with 62 additions and 0 deletions

View File

@@ -38,6 +38,7 @@ SOURCE_NAMES = {
None: _('European Central Bank'), # backwards-compatibility None: _('European Central Bank'), # backwards-compatibility
'eu:ecb:eurofxref-daily': _('European Central Bank'), 'eu:ecb:eurofxref-daily': _('European Central Bank'),
'cz:cnb:rate-fixing-daily': _('Czech National 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 = { source_tasks = {
'eu:ecb:eurofxref-daily': fetch_ecb_rates, 'eu:ecb:eurofxref-daily': fetch_ecb_rates,
'cz:cnb:rate-fixing-daily': fetch_cnb_cz_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(): for source_name, task in source_tasks.items():
@@ -144,3 +146,29 @@ def fetch_cnb_cz_rates():
rate=rate, 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,
)
)

View File

@@ -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 = rate.rate.quantize(Decimal('0.0001'), ROUND_HALF_UP)
invoice.foreign_currency_rate_date = rate.source_date invoice.foreign_currency_rate_date = rate.source_date
invoice.foreign_currency_source = 'cz:cnb:rate-fixing-daily' 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: except InvoiceAddress.DoesNotExist:
ia = None ia = None

View File

@@ -574,6 +574,7 @@ DEFAULTS = {
('True', _('Based on European Central Bank daily rates, whenever the invoice recipient is in an EU ' ('True', _('Based on European Central Bank daily rates, whenever the invoice recipient is in an EU '
'country that uses a different currency.')), 'country that uses a different currency.')),
('CZK', _('Based on Czech National Bank daily rates, whenever the invoice amount is not in CZK.')), ('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( 'serializer_kwargs': dict(
@@ -582,6 +583,7 @@ DEFAULTS = {
('True', _('Based on European Central Bank daily rates, whenever the invoice recipient is in an EU ' ('True', _('Based on European Central Bank daily rates, whenever the invoice recipient is in an EU '
'country that uses a different currency.')), 'country that uses a different currency.')),
('CZK', _('Based on Czech National Bank daily rates, whenever the invoice amount is not in CZK.')), ('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.')),
), ),
), ),
}, },

View File

@@ -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='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', ExchangeRate.objects.create(source_date=date.today(), source='cz:cnb:rate-fixing-daily', source_currency='EUR',
other_currency='CZK', rate=Decimal('25.0000')) 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 yield event, o
@@ -347,6 +349,23 @@ def test_invoice_indirect_currency_conversion(env):
assert inv.foreign_currency_source == 'eu:ecb:eurofxref-daily' 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 @pytest.mark.django_db
def test_invoice_czk_currency_conversion(env): def test_invoice_czk_currency_conversion(env):
event, order = env event, order = env