"""IVA — Impuesto al Valor Agregado (Mexico). IVA is a flow-through tax. Each month you net: IVA a cargo (payable) = IVA trasladado (collected on sales) − IVA acreditable (paid on deductible purchases) − IVA retenido (withheld from you by clients) A negative result is a *saldo a favor* (credit) you can carry forward or request back. The model never computes this — it calls ``iva_monthly`` and explains the ``breakdown``. """ from __future__ import annotations from .money import D, money from .result import CalcResult from .tax_tables import IVA_BORDER_MX, IVA_STANDARD_MX, IVA_ZERO_MX def iva_on_amount(amount, rate=IVA_STANDARD_MX) -> CalcResult: """IVA charged on a single taxable amount.""" base = D(amount) iva = base * D(rate) return CalcResult( amount=money(iva), label="IVA sobre el monto", breakdown=[("Base", money(base)), ("Tasa", D(rate)), ("IVA", money(iva))], source="LIVA Art. 1", ) def iva_monthly( iva_trasladado=0, iva_acreditable=0, iva_retenido=0 ) -> CalcResult: """Monthly net IVA position. Positive amount → IVA a cargo (you pay the SAT). Negative amount → saldo a favor (credit carried forward). """ trasladado = D(iva_trasladado) acreditable = D(iva_acreditable) retenido = D(iva_retenido) net = trasladado - acreditable - retenido if net >= 0: label = "IVA a cargo (mensual)" notes = [] else: label = "Saldo a favor de IVA (mensual)" notes = ["Negative net → credit; carry forward or request a refund."] return CalcResult( amount=money(net), label=label, breakdown=[ ("IVA trasladado (cobrado en ventas)", money(trasladado)), ("IVA acreditable (pagado en compras)", money(acreditable)), ("IVA retenido por clientes", money(retenido)), ("Neto", money(net)), ], source="LIVA Art. 5-D", notes=notes, ) # Convenience rate lookups so callers don't import tax_tables directly. RATE_STANDARD = IVA_STANDARD_MX RATE_BORDER = IVA_BORDER_MX RATE_ZERO = IVA_ZERO_MX