From 8d244214ade550ebd29c5a7c116292e632c2620c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Thu, 14 Nov 2024 10:33:15 +0100 Subject: [PATCH] chore: consolidate bank account information --- weblate_web/invoices/models.py | 129 +++++++++++++++++- .../invoices/templates/invoice-template.html | 32 +---- weblate_web/payments/backends.py | 19 +-- 3 files changed, 136 insertions(+), 44 deletions(-) diff --git a/weblate_web/invoices/models.py b/weblate_web/invoices/models.py index 884ebf6575..f8b94990f6 100644 --- a/weblate_web/invoices/models.py +++ b/weblate_web/invoices/models.py @@ -23,7 +23,7 @@ from decimal import Decimal from pathlib import Path from shutil import copyfile -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Literal, cast import qrcode import qrcode.image.svg @@ -37,7 +37,7 @@ from django.template.loader import render_to_string from django.utils.functional import cached_property from django.utils.safestring import mark_safe -from django.utils.translation import override +from django.utils.translation import gettext, override from fakturace.rates import DecimalRates from lxml import etree @@ -45,6 +45,10 @@ from weblate_web.utils import get_site_url if TYPE_CHECKING: + from collections.abc import Generator + + from django_stubs_ext import StrOrPromise + from weblate_web.payments.models import Payment INVOICES_URL = "invoices:" @@ -101,6 +105,115 @@ class Currency(models.IntegerChoices): GBP = 3, "GBP" +InfoType = Literal["number", "short_number", "iban", "bic", "bank", "holder"] + + +class BankAccountInfo: + def __init__( # noqa: PLR0913 + self, + *, + number: str, + bank: str, + iban: str, + bic: str, + holder: str = "Weblate s.r.o.", + short_list: tuple[InfoType, ...], + ): + self._number = number + self._bank = bank + self._iban = iban + self._bic = bic + self._holder = holder + self._short_list = short_list + + @property + def number(self) -> str: + return self._number + + @property + def short_number(self) -> str: + return self._number.split("/")[0].strip() + + @property + def bank(self) -> str: + return self._bank + + @property + def iban(self) -> str: + return self._iban + + @property + def raw_iban(self) -> str: + return self._iban.replace(" ", "") + + @property + def bic(self) -> str: + return self._bic + + @property + def holder(self) -> str: + return self._holder + + def get_info(self, *items: InfoType) -> Generator[tuple[StrOrPromise, str]]: + for item in items: + if item == "iban": + yield (gettext("IBAN"), self._iban) + elif item == "number": + yield (gettext("Account number"), self._number) + elif item == "short_number": + yield (gettext("Account number"), self.short_number) + elif item == "bic": + yield (gettext("BIC/SWIFT"), self._bic) + elif item == "bank": + yield (gettext("Issuing bank"), self._bank) + elif item == "holder": + yield (gettext("Account holder"), self._holder) + else: + raise ValueError(f"Unknown info type: {item}") + + def get_full_info(self) -> list[tuple[StrOrPromise, str]]: + return list(self.get_info("iban", "number", "bic", "bank", "holder")) + + def get_short_info(self) -> list[tuple[StrOrPromise, str]]: + return list(self.get_info(*self._short_list)) + + +FIO_BANK: str = "Fio banka, a.s., Na Florenci 2139/2, 11000 Praha, Czechia" +FIO_BIC: str = "FIOBCZPPXXX" + + +BANK_ACCOUNTS: dict[Currency, BankAccountInfo] = { + Currency.EUR: BankAccountInfo( + number="2302907395 / 2010", + bank=FIO_BANK, + iban="CZ30 2010 0000 0023 0290 7395", + bic=FIO_BIC, + short_list=("iban",), + ), + Currency.CZK: BankAccountInfo( + number="2002907393 / 2010", + bank=FIO_BANK, + iban="CZ49 2010 0000 0020 0290 7393", + bic=FIO_BIC, + short_list=("number",), + ), + Currency.USD: BankAccountInfo( + number="2603015278 / 2010", + bank=FIO_BANK, + iban="CZ37 2010 0000 0026 0301 5278", + bic=FIO_BIC, + short_list=("short_number", "bic"), + ), + Currency.GBP: BankAccountInfo( + number="2803015280 / 2010", + bank=FIO_BANK, + iban="CZ71 2010 0000 0028 0301 5280", + bic=FIO_BIC, + short_list=("short_number", "bic"), + ), +} + + class InvoiceKind(models.IntegerChoices): DRAFT = 0, "Draft" INVOICE = 10, "Invoice" @@ -231,6 +344,10 @@ def exchange_rate_czk(self) -> Decimal: self.issue_date.isoformat(), self.get_currency_display() ) + @cached_property + def bank_account(self) -> BankAccountInfo: + return BANK_ACCOUNTS[cast(Currency, self.currency)] + @cached_property def exchange_rate_eur(self) -> Decimal: """Exchange rate from currency to EUR.""" @@ -531,9 +648,9 @@ def get_payment_qrcode(self) -> str: 001 1 SCT -FIOBCZPPXXX -Weblate s.r.o. -CZ3020100000002302907395 +{self.bank_account.bic} +{self.bank_account.holder} +{self.bank_account.raw_iban} EUR{self.total_amount} {self.number} @@ -541,7 +658,7 @@ def get_payment_qrcode(self) -> str: """ elif self.currency == Currency.CZK: - data = f"SPD*1.0*ACC:CZ3020100000002302907395*AM:{self.total_amount}*CC:CZK*RF:{self.number}*RN:Weblate s.r.o" + data = f"SPD*1.0*ACC:{self.bank_account.raw_iban}*AM:{self.total_amount}*CC:CZK*RF:{self.number}*RN:{self.bank_account.holder}" else: return "" diff --git a/weblate_web/invoices/templates/invoice-template.html b/weblate_web/invoices/templates/invoice-template.html index fb8bd05238..3a45c97b1a 100644 --- a/weblate_web/invoices/templates/invoice-template.html +++ b/weblate_web/invoices/templates/invoice-template.html @@ -167,18 +167,10 @@

Issued by

{% else %} Bank transfer
- {% if invoice.get_currency_display == "CZK" %} - Bank account: 2002907393 / 2010 - {% elif invoice.get_currency_display == "EUR" %} - IBAN: CZ30 2010 0000 0023 0290 7395 - {% elif invoice.get_currency_display == "USD" %} - Bank account: 2603015278 - BIC/SWIFT: FIOBCZPPXXX - {% elif invoice.get_currency_display == "GBP" %} - Bank account: 2803015280 - BIC/SWIFT: FIOBCZPPXXX - {% endif %} -
+ {% for item, value in invoice.bank_account.get_short_info %} + {{ item }}: {{ value }} +
+ {% endfor %} Reference: {{ invoice.number }}
{% endif %} @@ -235,19 +227,9 @@

Issued by

Weblate s.r.o. - - {% if invoice.get_currency_display == "CZK" %} - 2002907393 / 2010 - {% elif invoice.get_currency_display == "EUR" %} - 2302907395 / 2010 - {% elif invoice.get_currency_display == "USD" %} - 2603015278 / 2010 - {% elif invoice.get_currency_display == "GBP" %} - 2803015280 / 2010 - {% endif %} - - FIOBCZPPXXX - Fio banka, a.s., Na Florenci 2139/2, 11000 Praha, Czechia + {{ invoice.bank_account.number }} + {{ invoice.bank_account.bic }} + {{ invoice.bank_account.bank }} ♥ Thank you! diff --git a/weblate_web/payments/backends.py b/weblate_web/payments/backends.py index 8c13832982..b0cb9a495c 100644 --- a/weblate_web/payments/backends.py +++ b/weblate_web/payments/backends.py @@ -290,7 +290,7 @@ def collect(self, request: HttpRequest | None) -> bool | None: """Collect payment information.""" raise NotImplementedError - def get_instructions(self) -> list[tuple[StrOrPromise, StrOrPromise]]: + def get_instructions(self) -> list[tuple[StrOrPromise, str]]: """Payment instructions for manual methods.""" return [] @@ -544,20 +544,13 @@ def perform( self.send_notification("payment_pending") return redirect(complete_url) - def get_instructions(self) -> list[tuple[StrOrPromise, StrOrPromise]]: + def get_instructions(self) -> list[tuple[StrOrPromise, str]]: from weblate_web.invoices.models import Invoice # noqa: PLC0415 - return [ - ( - gettext("Issuing bank"), - "Fio banka, a.s., Na Florenci 2139/2, 11000 Praha, Czechia", - ), - (gettext("Account holder"), "Weblate s.r.o."), - (gettext("Account number"), "2302907395 / 2010"), - (gettext("SWIFT code"), "FIOBCZPPXXX"), - (gettext("IBAN"), "CZ30 2010 0000 0023 0290 7395"), - (gettext("Reference"), cast(Invoice, self.payment.draft_invoice).number), - ] + invoice = cast(Invoice, self.payment.draft_invoice) + instructions = invoice.bank_account.get_full_info() + instructions.append((gettext("Reference"), invoice.number)) + return instructions @classmethod def fetch_payments(cls, from_date: str | None = None) -> None: