from decimal import Decimal
from functools import total_ordering
from onegov.core.orm import Base
from typing import NamedTuple, TYPE_CHECKING
if TYPE_CHECKING:
from onegov.pay.types import PriceDict
from sqlalchemy import Table
from typing import Self
[docs]
class _PriceBase(NamedTuple):
[docs]
credit_card_payment: bool
@total_ordering
[docs]
class Price(_PriceBase):
""" A single price.
The amount includes the fee. To get the net amount use the net_amount
property.
"""
def __new__(
cls,
amount: Decimal | float,
currency: str | None,
fee: Decimal | float = 0,
credit_card_payment: bool = False
) -> 'Self':
return super().__new__(
cls,
Decimal(amount),
currency,
Decimal(fee),
credit_card_payment
)
[docs]
def __bool__(self) -> bool:
return self.amount and True or False
[docs]
def __lt__(self, other: 'Price') -> bool: # type:ignore[override]
# FIXME: This probably should assert equal currency as well
return self.amount < other.amount
[docs]
def __add__(self, other: 'Price') -> 'Self': # type:ignore[override]
assert self.currency is None or self.currency == other.currency
cc_payment = self.credit_card_payment or other.credit_card_payment
return self.__class__(
self.amount + other.amount,
self.currency or other.currency,
credit_card_payment=cc_payment
)
[docs]
def __sub__(self, other: 'Price') -> 'Self':
assert self.currency == other.currency
cc_payment = self.credit_card_payment or other.credit_card_payment
return self.__class__(
self.amount - other.amount,
self.currency,
credit_card_payment=cc_payment
)
[docs]
def __str__(self) -> str:
return f'{self.amount:.2f} {self.currency}'
[docs]
def __repr__(self) -> str:
return f'Price({self.amount!r}, {self.currency!r})'
@classmethod
[docs]
def zero(cls) -> 'Self':
return cls(0, None)
[docs]
def as_dict(self) -> 'PriceDict':
return {
'amount': float(self.amount),
'currency': self.currency,
'fee': float(self.fee),
'credit_card_payment': self.credit_card_payment
}
@property
[docs]
def net_amount(self) -> Decimal:
return self.amount - self.fee
[docs]
def payments_association_table_for(cls: type[Base]) -> 'Table':
return Base.metadata.tables[f'payments_for_{cls.__tablename__}_payments']