Source code for cfdibills.schemas.mixins

"""
Mixins to be used with CFDIs.
"""

from typing import List, Optional, Protocol, Type

from cfdibills.errors import ComplementoNotFoundError
from cfdibills.schemas.catalogs import Impuesto
from cfdibills.schemas.complementos import AnyComplementoType, ComplementoType
from cfdibills.schemas.fields import NonNegativeSixDecimals


class _SingleTaxProto(Protocol):
    impuesto: Impuesto
    importe: NonNegativeSixDecimals


class _ImpuestosProto(Protocol):
    traslados: List[_SingleTaxProto]
    retenciones: List[_SingleTaxProto]


[docs]class CFDIMixin: """ Behavior to be extended by a CFDI. This mixin is meant to be used with any version of CFDI. """ # Type stubs are used to tell mypy to expect these attrs to exist. # See: https://mypy.readthedocs.io/en/stable/protocols.html#defining-subprotocols-and-subclassing-protocols #: Type stub of CFDIx.impuestos impuestos: Optional[_ImpuestosProto] #: Type stub of CFDIx.complemento complemento: List[ComplementoType] = []
[docs] def get_total_transferred_tax(self, tax_type: Impuesto) -> float: """ Computes the total tax transferred (from ``impuestos.traslados``) of type ``tax_type``. Parameters ---------- tax_type: Impuesto Type of tax to sum. Returns ------- float Sum of all the transferred taxes of type ``tax_type``. """ taxes = self.impuestos.traslados if self.impuestos else [] return sum([tax.importe for tax in taxes if tax.impuesto == tax_type])
[docs] def get_total_withheld_tax(self, tax_type: Impuesto) -> float: """ Computes the total tax get_total_withheld_tax (from ``impuestos.retenciones``) of type ``tax_type``. Parameters ---------- tax_type: Impuesto Type of tax to sum. Returns ------- float Sum of all the withheld taxes of type ``tax_type``. """ taxes = self.impuestos.retenciones if self.impuestos else [] return sum([tax.importe for tax in taxes if tax.impuesto == tax_type])
[docs] def get_complemento(self, complemento_type: Type[AnyComplementoType]) -> AnyComplementoType: """ Retrieves the complemento of type ``complemento_type``. Parameters ---------- complemento_type: Type[AnyComplementoType] Type of complemento to find. Returns ------- AnyComplementoType Complemento found in this CFDI of type ``complemento_type`` Raises ------- ComplementoNotFoundError When the CFDI doesn't contain a complemento of type ``complemento_type`` """ for complemento in self.complemento: if isinstance(complemento, complemento_type): return complemento raise ComplementoNotFoundError(f"This CFDI has no {complemento_type.__name__}")