Uecko_ERP_FactuGES_sync/app/utils/importes_helper.py

82 lines
2.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from decimal import ROUND_HALF_UP, Decimal
from typing import Any, Optional, Tuple
def cents4(value: Optional[Decimal | float | int]) -> int:
"""Convierte a centésimas (valor * 10000). Soporta None."""
v = Decimal(str(value or 0))
return int((v * 10000).to_integral_value(rounding=ROUND_HALF_UP))
def cents(value: Optional[Decimal | float | int]) -> int:
"""Convierte a centésimas (valor * 100). Soporta None."""
v = Decimal(str(value or 0))
return int((v * 100).to_integral_value(rounding=ROUND_HALF_UP))
def money_round(value: Decimal, ndigits: int = 2) -> Decimal:
"""Redondeo bancario a n decimales (por defecto 2)."""
q = Decimal((0, (1,), -ndigits)) # 10^-ndigits
return value.quantize(q, rounding=ROUND_HALF_UP)
def unscale_to_decimal(value: Any, scale: Any) -> Decimal:
"""
Convierte un valor escalado (p. ej. 24200 con scale=2) a su valor en unidades (242).
No redondea; sólo mueve el punto decimal.
"""
if value is None:
return Decimal("0")
d = Decimal(str(value))
s = int(scale or 0)
return d.scaleb(-s) # divide por 10**s
def unscale_to_str(
value: Any,
scale: Any,
*,
decimals: Optional[int] = None,
strip_trailing_zeros: bool = True
) -> str:
"""
Igual que unscale_to_decimal, pero devuelve str.
- decimals: fija nº de decimales (p. ej. 2). Si None, no fuerza decimales.
- strip_trailing_zeros: si True, quita ceros y el punto sobrantes.
"""
d = unscale_to_decimal(value, scale)
if decimals is not None:
q = Decimal("1").scaleb(-decimals) # p.ej. 2 -> Decimal('0.01')
d = d.quantize(q, rounding=ROUND_HALF_UP)
s = format(d, "f")
if strip_trailing_zeros and "." in s:
s = s.rstrip("0").rstrip(".")
return s
def calc_discount_cents4(subtotal_cents4: int, disc_pct: Optional[Decimal | float | int]) -> int:
"""
Calcula el importe de descuento en escala 4 (×10000) como ENTERO.
- subtotal_cents4: subtotal ya en escala 4
- disc_pct: porcentaje de descuento (p.ej. 10 -> 10%)
Devuelve un entero NEGATIVO (como en Delphi):
ImporteDto := (-1) * ((Subtotal * Descuento) / 100);
"""
pct = Decimal(str(disc_pct or 0))
# descuento = round(subtotal * pct / 100) en la MISMA escala (×10000)
disc = (Decimal(subtotal_cents4) * pct / Decimal(100)).to_integral_value(rounding=ROUND_HALF_UP)
return disc
def apply_discount_cents4(subtotal_cents4: int, disc_pct: Optional[Decimal | float | int]) -> Tuple[int, int]:
"""
Devuelve (discount_cents4, total_cents4) ambos en escala 4.
- discount_cents4 NEGATIVO
- total_cents4 = subtotal_cents4 + discount_cents4
"""
discount = calc_discount_cents4(subtotal_cents4, disc_pct)
total = subtotal_cents4 - discount
return discount, total