fix(reports): localize and group exported income values
This commit is contained in:
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import date
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from pathlib import Path
|
||||
from typing import Iterable
|
||||
|
||||
@@ -89,6 +90,16 @@ PERIOD_LABELS = {
|
||||
},
|
||||
}
|
||||
|
||||
CURRENCY_LABELS = {
|
||||
"USD": {"en": "USD", "fa": "دلار آمریکا"},
|
||||
"EUR": {"en": "EUR", "fa": "یورو"},
|
||||
"GBP": {"en": "GBP", "fa": "پوند"},
|
||||
"IRR": {"en": "IRR", "fa": "ریال"},
|
||||
"IRT": {"en": "IRT", "fa": "تومان"},
|
||||
"AED": {"en": "AED", "fa": "درهم"},
|
||||
"TRY": {"en": "TRY", "fa": "لیر"},
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ExportLocale:
|
||||
@@ -122,14 +133,40 @@ class ExportLocale:
|
||||
def format_duration(self, value: str, *, ascii_digits: bool = False) -> str:
|
||||
return self.format_number(value, ascii_digits=ascii_digits)
|
||||
|
||||
def format_amount(self, value: object, *, ascii_digits: bool = False) -> str:
|
||||
raw = str(value).strip()
|
||||
if not raw:
|
||||
return raw
|
||||
try:
|
||||
decimal_value = Decimal(raw)
|
||||
except InvalidOperation:
|
||||
return self.format_number(raw, ascii_digits=ascii_digits)
|
||||
|
||||
sign = "-" if decimal_value < 0 else ""
|
||||
unsigned = abs(decimal_value)
|
||||
normalized = format(unsigned, "f")
|
||||
integer_part, _, fractional_part = normalized.partition(".")
|
||||
grouped_integer = f"{int(integer_part):,}"
|
||||
formatted = f"{sign}{grouped_integer}"
|
||||
if fractional_part:
|
||||
trimmed_fraction = fractional_part.rstrip("0")
|
||||
if trimmed_fraction:
|
||||
formatted = f"{formatted}.{trimmed_fraction}"
|
||||
return self.format_number(formatted, ascii_digits=ascii_digits)
|
||||
|
||||
def format_money_label(self, income_totals: list[dict], *, ascii_digits: bool = False) -> str:
|
||||
if not income_totals:
|
||||
return "-"
|
||||
parts = []
|
||||
for item in income_totals:
|
||||
parts.append(f"{self.format_number(item['amount'], ascii_digits=ascii_digits)} {item['currency']}")
|
||||
currency = self.currency_label(item["currency"])
|
||||
parts.append(f"{self.format_amount(item['amount'], ascii_digits=ascii_digits)} {currency}")
|
||||
return " | ".join(parts)
|
||||
|
||||
def currency_label(self, code: str | None) -> str:
|
||||
raw = str(code or "").upper()
|
||||
return CURRENCY_LABELS.get(raw, {}).get(self.language, raw)
|
||||
|
||||
def shape(self, text: object) -> str:
|
||||
raw = str(text)
|
||||
if not any(start <= ord(char) <= end for char in raw for start, end in ARABIC_RANGES):
|
||||
|
||||
Reference in New Issue
Block a user