File money.py of Package fint-bot
from __future__ import annotations
import math
from dataclasses import dataclass
from tinkoff.invest import MoneyValue, Quotation
@dataclass(init=False, order=True)
class Money:
units: int
nano: int
MOD: int = 10 ** 9
def __init__(self, value: int | float | Quotation | MoneyValue, nano: int = None):
if nano is not None:
# Если передан nano, value должен быть int
self.units = int(value)
self.nano = int(nano)
self._normalize()
else:
match value:
case int() as value:
self.units = value
self.nano = 0
case float() as value:
# Более точное преобразование float
self.units = int(math.trunc(value))
fraction = abs(value - self.units)
self.nano = int(round(fraction * self.MOD))
if value < 0 and self.nano > 0:
# Для отрицательных чисел корректируем nano
self.nano = -self.nano
self._normalize()
case Quotation() | MoneyValue() as value:
self.units = value.units
self.nano = value.nano
self._normalize()
case _:
raise ValueError(f'{type(value)} is not supported as initial value for Money')
def _normalize(self):
"""Нормализация nano в диапазон [0, MOD-1] или [-MOD+1, 0] и корректировка units"""
if self.nano >= self.MOD or self.nano <= -self.MOD:
self.units += self.nano // self.MOD
self.nano %= self.MOD
# Если units и nano имеют разные знаки, корректируем
if self.units == 0 and self.nano < 0:
# Отрицательное значение с units = 0
self.units = -1
self.nano += self.MOD
elif self.units < 0 and self.nano > 0:
# units отрицательный, nano положительный
self.units += 1
self.nano -= self.MOD
elif self.units > 0 and self.nano < 0:
# units положительный, nano отрицательный
self.units -= 1
self.nano += self.MOD
def __float__(self):
return self.units + self.nano / self.MOD
def to_float(self):
return float(self)
def to_quotation(self):
return Quotation(self.units, self.nano)
def to_money_value(self, currency: str):
return MoneyValue(currency, self.units, self.nano)
def __add__(self, other: Money) -> Money:
result = Money(
self.units + other.units,
self.nano + other.nano
)
result._normalize()
return result
def __neg__(self) -> Money:
return Money(-self.units, -self.nano)
def __sub__(self, other: Money) -> Money:
return self + (-other)
def __mul__(self, other: int) -> Money:
if not isinstance(other, int):
raise TypeError("Multiplication is only supported by integers")
result = Money(
self.units * other,
self.nano * other
)
result._normalize()
return result
def __str__(self) -> str:
return f'<Money units={self.units} nano={self.nano} value={float(self):.2f}>'