Example #1
0
class Config:
    remap: Dict[str, str] = dc_field(default_factory=dict)
    prefixed: Dict[str, str] = dc_field(default_factory=dict)
    cast: List[str] = dc_field(default_factory=list)
    transform: Dict[str, Callable[[Any], Any]] = dc_field(default_factory=dict)
    flattened: List[str] = dc_field(default_factory=list)
    forward_references: Optional[Dict[str, Any]] = None
Example #2
0
class CompilerOptions(GlobalCompilerOptions):

    #: Module name aliases.
    modaliases: Mapping[Optional[str], str] = dc_field(default_factory=dict)

    #: External symbol table.
    anchors: Mapping[str, Any] = dc_field(default_factory=dict)

    #: The symbol to assume as the prefix for abbreviated paths.
    path_prefix_anchor: Optional[str] = None

    #: Module to put derived schema objects to.
    derived_target_module: Optional[str] = None

    #: The name to use for the top-level type variant.
    result_view_name: Optional[s_name.QualName] = None

    #: If > 0, Inject implicit LIMIT to every SELECT query.
    implicit_limit: int = 0

    #: Include id property in every shape implicitly.
    implicit_id_in_shapes: bool = False

    #: Include __tid__ computable (.__type__.id) in every shape implicitly.
    implicit_tid_in_shapes: bool = False

    #: Include __tname__ computable (.__type__.name) in every shape implicitly.
    implicit_tname_in_shapes: bool = False

    #: A set of schema types and links that should be treated
    #: as singletons in the context of this compilation.
    singletons: FrozenSet[Union[s_types.Type,
                                s_pointers.Pointer]] = (frozenset())
Example #3
0
class RvDetalleEntry(RcvDetalleEntry):

    """
    Entry of the "detalle" of an RV ("Registro de Ventas").
    """

    RCV_KIND = RcvKind.VENTAS
    RC_ESTADO_CONTABLE = None

    # TODO: docstring
    # note: must be timezone-aware.
    fecha_acuse_dt: Optional[datetime] = dc_field()

    # TODO: docstring
    # note: must be timezone-aware.
    fecha_reclamo_dt: Optional[datetime] = dc_field()

    def __post_init__(self) -> None:
        super().__post_init__()

        if self.fecha_acuse_dt is not None:
            if not isinstance(self.fecha_acuse_dt, datetime):
                raise TypeError("Inappropriate type of 'fecha_acuse_dt'.")
            tz_utils.validate_dt_tz(self.fecha_acuse_dt, SII_OFFICIAL_TZ)

        if self.fecha_reclamo_dt is not None:
            if not isinstance(self.fecha_reclamo_dt, datetime):
                raise TypeError("Inappropriate type of 'fecha_reclamo_dt'.")
            tz_utils.validate_dt_tz(self.fecha_reclamo_dt, SII_OFFICIAL_TZ)
Example #4
0
class RcReclamadoDetalleEntry(RcvDetalleEntry):
    """
    Entry of the "detalle" of an RC ("Registro de Compras") / "reclamado".
    """

    RCV_KIND = RcvKind.COMPRAS
    RC_ESTADO_CONTABLE = RcEstadoContable.RECLAMADO

    emisor_razon_social: str = dc_field()
    """
    "Razón social" (legal name) of the "emisor" of the "documento".
    """

    # TODO: docstring
    # note: must be timezone-aware.
    fecha_reclamo_dt: Optional[datetime] = dc_field()

    def __post_init__(self) -> None:
        super().__post_init__()

        if not isinstance(self.emisor_razon_social, str):
            raise TypeError("Inappropriate type of 'emisor_razon_social'.")
        cl_sii.dte.data_models.validate_contribuyente_razon_social(
            self.emisor_razon_social)

        if self.fecha_reclamo_dt is not None:
            if not isinstance(self.fecha_reclamo_dt, datetime):
                raise TypeError("Inappropriate type of 'fecha_reclamo_dt'.")
            tz_utils.validate_dt_tz(self.fecha_reclamo_dt, SII_OFFICIAL_TZ)
Example #5
0
class PeriodoTributario:

    year: int = dc_field()
    month: int = dc_field()

    def __post_init__(self) -> None:
        if not isinstance(self.year, int):
            raise TypeError("Inappropriate type of 'year'.")
        if self.year < 1900:  # arbitrary number but it more useful than checking not < 1.
            raise ValueError("Value is out of the valid range for 'year'.")

        if not isinstance(self.month, int):
            raise TypeError("Inappropriate type of 'month'.")
        if self.month < 1 or self.month > 12:
            raise ValueError("Value is out of the valid range for 'month'.")

    ###########################################################################
    # dunder/magic methods
    ###########################################################################

    def __str__(self) -> str:
        # 'YYYY-MM' e.g. '2018-03'
        return f"{self.year}-{self.month:02d}"

    def __lt__(self, other: PeriodoTributario) -> bool:
        return self.as_date() < other.as_date()

    def __le__(self, other: PeriodoTributario) -> bool:
        return self.as_date() <= other.as_date()

    ###########################################################################
    # custom methods
    ###########################################################################

    @property
    def is_in_the_future(self) -> bool:
        return self.as_datetime() > tz_utils.get_now_tz_aware()

    @classmethod
    def from_date(cls, value: date) -> PeriodoTributario:
        return PeriodoTributario(year=value.year, month=value.month)

    @classmethod
    def from_datetime(cls, value: datetime) -> PeriodoTributario:
        value_naive = tz_utils.convert_tz_aware_dt_to_naive(
            value, SII_OFFICIAL_TZ)
        return cls.from_date(value_naive.date())

    def as_date(self) -> date:
        return date(self.year, self.month, day=1)

    def as_datetime(self) -> datetime:
        # note: timezone-aware
        return tz_utils.convert_naive_dt_to_tz_aware(
            datetime(self.year, self.month, day=1, hour=0, minute=0, second=0),
            SII_OFFICIAL_TZ)
class Ticket:
    """A class represeting an oauth2_proxy ticket"""

    ticket_id: str = dc_field(default_factory=new_id)
    secret: bytes = dc_field(default_factory=new_secret)

    def as_handle(self, prefix: str) -> str:
        return f"{prefix}-{self.ticket_id}"

    def encode(self, prefix: str) -> str:
        secret_b64 = base64.urlsafe_b64encode(self.secret).decode().rstrip("=")
        return f"{prefix}-{self.ticket_id}.{secret_b64}"
Example #7
0
class _SchemaGenSettings:
    data_class: Any
    base: Type[Schema]
    type_handlers: "Dict[Type[Any], Type[HandlerType]]"
    field_docstrings: Dict[str, str]
    type_var_index: "Dict[TypeVar, Type]" = (  # type: ignore
        dc_field(default_factory=dict))
Example #8
0
class BatchFieldMetadata(Dictable):
    """Data that describes a field mapping in a batch object.

    """
    field: FieldFeatureMapping = dc_field()
    """The field mapping."""

    vectorizer: FeatureVectorizer = dc_field(repr=False)
    """The vectorizer used to map the field."""
    @property
    def shape(self):
        return self.vectorizer.shape

    def write(self, depth: int = 0, writer: TextIOBase = sys.stdout):
        self._write_line(self.field.attr, depth, writer)
        self._write_line('field:', depth + 1, writer)
        self.field.write(depth + 2, writer)
        self._write_line('vectorizer:', depth + 1, writer)
        self.vectorizer.write(depth + 2, writer)
Example #9
0
class BatchMetadata(Dictable):
    """Describes metadata about a :class:`.Batch` instance.

    """
    data_point_class: Type[DataPoint] = dc_field()
    """The :class:`.DataPoint` class, which are created at encoding time."""

    batch_class: Type[Batch] = dc_field()
    """The :class:`.Batch` class, which are created at encoding time."""

    mapping: BatchFeatureMapping = dc_field()
    """The mapping used for encoding and decoding the batch."""

    fields_by_attribute: Dict[str, BatchFieldMetadata] = dc_field(repr=False)
    """Mapping by field name to attribute."""
    def write(self, depth: int = 0, writer: TextIOBase = sys.stdout):
        self._write_line(f'data point: {self.data_point_class}', depth, writer)
        self._write_line(f'batch: {self.batch_class}', depth, writer)
        self._write_line('mapping:', depth, writer)
        self.mapping.write(depth + 1, writer)
        self._write_line('attributes:', depth, writer)
        for attr, field in self.fields_by_attribute.items():
            field.write(depth + 1, writer)
Example #10
0
class RvDetalleEntry(RcvDetalleEntry):
    """
    Entry of the "detalle" of an RV ("Registro de Ventas").
    """

    RCV_KIND = RcvKind.VENTAS
    RC_ESTADO_CONTABLE = None

    # TODO: docstring
    # TODO: can it be None? What happens for those "tipo docto" that do not have a receptor?
    receptor_razon_social: str = dc_field()

    # TODO: docstring
    # note: must be timezone-aware.
    fecha_acuse_dt: Optional[datetime] = dc_field()

    # TODO: docstring
    # note: must be timezone-aware.
    fecha_reclamo_dt: Optional[datetime] = dc_field()

    def __post_init__(self) -> None:
        super().__post_init__()

        if not isinstance(self.receptor_razon_social, str):
            raise TypeError("Inappropriate type of 'receptor_razon_social'.")
        cl_sii.dte.data_models.validate_contribuyente_razon_social(
            self.receptor_razon_social)

        if self.fecha_acuse_dt is not None:
            if not isinstance(self.fecha_acuse_dt, datetime):
                raise TypeError("Inappropriate type of 'fecha_acuse_dt'.")
            tz_utils.validate_dt_tz(self.fecha_acuse_dt, SII_OFFICIAL_TZ)

        if self.fecha_reclamo_dt is not None:
            if not isinstance(self.fecha_reclamo_dt, datetime):
                raise TypeError("Inappropriate type of 'fecha_reclamo_dt'.")
            tz_utils.validate_dt_tz(self.fecha_reclamo_dt, SII_OFFICIAL_TZ)
Example #11
0
 def wrap_field_in_optional_and_provide_default_none_value(
         field_name: str = None,
         field_type: type = None,
         field: Field = None) -> NewField:
     new_field = dc_field()
     if field is not None:
         if field_name is None:
             field_name = field.name
         if field_type is None:
             field_type = field.type
     new_field.name = field_name
     new_field.type = field_type if _is_optional(
         field_type) else Optional[field_type]
     if field is None or (field.default is MISSING
                          and field.default_factory is MISSING):
         new_field.default = None
     return new_field.name, new_field.type, new_field
Example #12
0
def field(*,
          db_name=None,
          default=MISSING,
          default_factory=MISSING,
          init=True,
          repr=True,
          hash=None,
          compare=True,
          metadata=None):
    return dc_field(default=default,
                    default_factory=default_factory,
                    init=init,
                    repr=repr,
                    hash=hash,
                    compare=compare,
                    metadata={
                        'db_name': db_name,
                    })
Example #13
0
class RcPendienteDetalleEntry(RcvDetalleEntry):
    """
    Entry of the "detalle" of an RC ("Registro de Compras") / "pendiente".
    """

    RCV_KIND = RcvKind.COMPRAS
    RC_ESTADO_CONTABLE = RcEstadoContable.PENDIENTE

    emisor_razon_social: str = dc_field()
    """
    "Razón social" (legal name) of the "emisor" of the "documento".
    """
    def __post_init__(self) -> None:
        super().__post_init__()

        if not isinstance(self.emisor_razon_social, str):
            raise TypeError("Inappropriate type of 'emisor_razon_social'.")
        cl_sii.dte.data_models.validate_contribuyente_razon_social(
            self.emisor_razon_social)
Example #14
0
class RcReclamadoDetalleEntry(RcvDetalleEntry):

    """
    Entry of the "detalle" of an RC ("Registro de Compras") / "reclamado".
    """

    RCV_KIND = RcvKind.COMPRAS
    RC_ESTADO_CONTABLE = RcEstadoContable.RECLAMADO

    # TODO: docstring
    # note: must be timezone-aware.
    fecha_reclamo_dt: Optional[datetime] = dc_field()

    def __post_init__(self) -> None:
        super().__post_init__()

        if self.fecha_reclamo_dt is not None:
            if not isinstance(self.fecha_reclamo_dt, datetime):
                raise TypeError("Inappropriate type of 'fecha_reclamo_dt'.")
            tz_utils.validate_dt_tz(self.fecha_reclamo_dt, SII_OFFICIAL_TZ)
Example #15
0
class MetadataNetworkSettings(NetworkSettings):
    """A network settings container that has metadata about batches it recieves for
    its model.

    """
    _PERSITABLE_TRANSIENT_ATTRIBUTES = {'batch_stash'}

    batch_stash: 'BatchStash' = dc_field(repr=False)
    """The batch stash that created the batches and has the batch metdata.

    """
    @property
    def batch_metadata(self) -> BatchMetadata:
        """Return the batch metadata used by this model.

        """
        return self.batch_stash.batch_metadata

    def _from_dictable(self, *args, **kwargs):
        dct = super()._from_dictable(*args, **kwargs)
        del dct['batch_stash']
        return dct
Example #16
0
class SpdxLicense(Dictionizer):
    """An individual license descriptor."""

    name: str
    reference: str
    id: str = cast(str, desert.field(fields.String(data_key="licenseId")))
    is_deprecated: bool = cast(
        bool, desert.field(fields.Boolean(data_key="isDeprecatedLicenseId")))
    details_url: str = cast(str,
                            desert.field(fields.String(data_key="detailsUrl")))
    reference_number: str = cast(
        str, desert.field(fields.String(data_key="referenceNumber")))
    see_also: List[str] = cast(
        List[str], desert.field(fields.List(fields.String,
                                            data_key="seeAlso")))
    is_osi_approved: bool = cast(
        bool, desert.field(fields.Boolean(data_key="isOsiApproved")))
    is_fsf_libre: Optional[bool] = dc_field(
        default=None,
        metadata=desert.metadata(fields.Boolean(data_key="isFsfLibre")))

    def to_dict(self) -> Dict:  # noqa:D102
        return asdict(self)
Example #17
0
def gfield(*,
           default: Any = MISSING,
           default_factory: Union[_MISSING_TYPE, Callable[[], Any]] = MISSING,
           init: bool = True,
           repr: bool = True,
           hash: Optional[bool] = None,
           compare: bool = True,
           metadata: Optional[MutableMapping[str, Any]] = None,
           garams: Optional[Garams] = None) -> DCField:
    """As ``dataclasses.field``, but garams can be passed directly"""
    if garams is not None:
        if metadata is None:
            metadata = dict()
        metadata["garams"] = garams

    return dc_field(  # type: ignore
        default=default,
        default_factory=default_factory,
        init=init,
        repr=repr,
        hash=hash,
        compare=compare,
        metadata=metadata,
    )
Example #18
0
class State:
    contract_begin = datetime.date(day=1, month=1, year=1970)

    contract_hours_per_week = timedelta(hours=0, minutes=0)
    contract_hours_per_workday = timedelta(
        hours=0, minutes=0)  # workday = Mon,Tue,Wed,Thu,Fri

    vacation_days_per_year = 0.0
    vacation_days_left = 0.0

    week_target_hours = timedelta(hours=0, minutes=0)
    week_total_hours = timedelta(hours=0, minutes=0)
    week_over_hours = timedelta(hours=0, minutes=0)

    month_total_hours = timedelta(hours=0, minutes=0)
    month_over_hours = timedelta(hours=0, minutes=0)

    year_total_hours = timedelta(hours=0, minutes=0)
    year_over_hours = timedelta(hours=0, minutes=0)

    contract_total_hours = timedelta(hours=0, minutes=0)
    contract_over_hours = timedelta(hours=0, minutes=0)

    all_counted_dates: List[datetime.date] = dc_field(default_factory=list)
Example #19
0
class _FieldGenSettings:
    # passed settings
    _data_field: InitVar[Union[DCField, Type]]
    schema_settings: _SchemaGenSettings

    # calculated settings
    type: Type = dc_field(init=False)
    data_field: Optional[DCField] = dc_field(init=False, default=None)

    # data holders
    args: Iterable[Any] = dc_field(init=False, default_factory=tuple)
    kwargs: Dict[str, Any] = dc_field(init=False, default_factory=dict)
    data_handler: Type[HandlerType] = dc_field(init=False)
    optional: bool = dc_field(init=False)

    def __post_init__(self, _data_field: Union[DCField, Type]) -> None:
        if isinstance(_data_field, DCField):
            self.data_field = _data_field
            self.type = _data_field.type
        else:
            self.type = _data_field
Example #20
0
class RcvDetalleEntry:
    """
    Entry of the "detalle" of an RCV.
    """

    ###########################################################################
    # constants
    ###########################################################################

    # note: as of Python 3.7.3 we can not do something like `RCV_KIND: Optional[RcvKind] = None`
    #   because 'dataclasses' gets confused and assumes that that class attribute is a dataclass
    #   field (it is not), and this error is triggered:
    #   > TypeError: non-default argument 'my_dc_field' follows default argument

    RCV_KIND = None  # type: Optional[RcvKind]
    RC_ESTADO_CONTABLE = None  # type: Optional[RcEstadoContable]

    ###########################################################################
    # fields
    ###########################################################################

    emisor_rut: Rut = dc_field()
    """
    RUT of the "emisor" of the "documento".
    """

    tipo_docto: RcvTipoDocto = dc_field()
    """
    The kind of "documento".
    """

    folio: int = dc_field()
    """
    The sequential number of a "documento".
    """

    # TODO: docstring
    fecha_emision_date: date = dc_field()

    # TODO: docstring
    # TODO: can it be None? What happens for those "tipo docto" that do not have a receptor?
    receptor_rut: Rut = dc_field()

    monto_total: int = dc_field()
    """
    Total amount of the "documento".
    """

    # TODO: docstring
    # note: must be timezone-aware.
    fecha_recepcion_dt: datetime = dc_field()

    def __post_init__(self) -> None:
        """
        Run validation automatically after setting the fields values.

        :raises TypeError, ValueError:

        """
        if self.RCV_KIND == RcvKind.COMPRAS:
            if self.RC_ESTADO_CONTABLE is None:
                raise ValueError(
                    "'RC_ESTADO_CONTABLE' must not be None when 'RCV_KIND' is 'COMPRAS'."
                )
        elif self.RCV_KIND == RcvKind.VENTAS:
            if self.RC_ESTADO_CONTABLE is not None:
                raise ValueError(
                    "'RC_ESTADO_CONTABLE' must be None when 'RCV_KIND' is 'VENTAS'."
                )

        if not isinstance(self.emisor_rut, Rut):
            raise TypeError("Inappropriate type of 'emisor_rut'.")

        if not isinstance(self.tipo_docto, RcvTipoDocto):
            raise TypeError("Inappropriate type of 'tipo_docto'.")

        if not isinstance(self.folio, int):
            raise TypeError("Inappropriate type of 'folio'.")
        if not self.folio > 0:
            raise ValueError("Inappropriate value of 'folio'.")

        if not isinstance(self.fecha_emision_date, date):
            raise TypeError("Inappropriate type of 'fecha_emision_date'.")

        if not isinstance(self.receptor_rut, Rut):
            raise TypeError("Inappropriate type of 'receptor_rut'.")

        # TODO: figure out validation rules of 'monto_total'
        if not isinstance(self.monto_total, int):
            raise TypeError("Inappropriate type of 'monto_total'.")

        if not isinstance(self.fecha_recepcion_dt, datetime):
            raise TypeError("Inappropriate type of 'fecha_recepcion_dt'.")
        tz_utils.validate_dt_tz(self.fecha_recepcion_dt, SII_OFFICIAL_TZ)

    @property
    def is_dte(self) -> bool:
        try:
            self.tipo_docto.as_tipo_dte()
        except ValueError:
            return False
        return True

    def as_dte_data_l2(self) -> cl_sii.dte.data_models.DteDataL2:
        try:
            tipo_dte = self.tipo_docto.as_tipo_dte()

            emisor_razon_social = getattr(self, 'emisor_razon_social', None)
            receptor_razon_social = getattr(self, 'receptor_razon_social',
                                            None)

            dte_data = cl_sii.dte.data_models.DteDataL2(
                emisor_rut=self.emisor_rut,
                tipo_dte=tipo_dte,
                folio=self.folio,
                fecha_emision_date=self.fecha_emision_date,
                receptor_rut=self.receptor_rut,
                monto_total=self.monto_total,
                emisor_razon_social=emisor_razon_social,
                receptor_razon_social=receptor_razon_social,
                # fecha_vencimiento_date='',
                # firma_documento_dt='',
                # signature_value='',
                # signature_x509_cert_der='',
                # emisor_giro='',
                # emisor_email='',
                # receptor_email='',
            )
        except (TypeError, ValueError):
            raise

        return dte_data
Example #21
0
class EnvoyStats:
    max_live_age: int = 120
    max_ready_age: int = 120
    created: float = 0.0
    last_update: Optional[float] = None
    last_attempt: Optional[float] = None
    update_errors: int = 0

    # Yes yes yes I know -- the contents of these dicts are not immutable.
    # That's OK for now, but realize that you mustn't go munging around altering
    # things in here once they're assigned!
    requests: Dict[str, Any] = dc_field(default_factory=dict)
    clusters: Dict[str, Any] = dc_field(default_factory=dict)
    envoy: Dict[str, Any] = dc_field(default_factory=dict)

    def is_alive(self) -> bool:
        """
        Make sure we've heard from Envoy within max_live_age seconds.

        If we haven't yet heard from Envoy at all (we've just booted),
        consider Envoy alive if we haven't yet been running for max_live_age
        seconds -- basically, Envoy gets a grace period to start running at
        boot time.
        """

        epoch = self.last_update

        if not epoch:
            epoch = self.created

        return (time.time() - epoch) <= self.max_live_age

    def is_ready(self) -> bool:
        """
        Make sure we've heard from Envoy within max_ready_age seconds.

        If we haven't yet heard from Envoy at all (we've just booted),
        then Envoy is not yet ready, and is_ready() returns False.
        """

        epoch = self.last_update

        if not epoch:
            return False

        return (time.time() - epoch) <= self.max_ready_age

    def time_since_boot(self) -> float:
        """ Return the number of seconds since Envoy booted. """
        return time.time() - self.created

    def time_since_update(self) -> Optional[float]:
        """
        Return the number of seconds since we last heard from Envoy, or None if
        we've never heard from Envoy.
        """

        if not self.last_update:
            return None
        else:
            return time.time() - self.last_update

    def cluster_stats(self, name: str) -> Dict[str, Union[str, bool]]:
        if not self.last_update:
            # No updates.
            return {
                'valid': False,
                'reason': "No stats updates have succeeded",
                'health': "no stats yet",
                'hmetric': 'startup',
                'hcolor': 'grey'
            }

        # OK, we should be OK.
        when = self.last_update
        cstat = self.clusters

        if name not in cstat:
            return {
                'valid': False,
                'reason': "Cluster %s is not defined" % name,
                'health': "undefined cluster",
                'hmetric': 'undefined cluster',
                'hcolor': 'orange',
            }

        cstat = dict(**cstat[name])
        cstat.update({
            'valid': True,
            'reason': "Cluster %s updated at %d" % (name, when)
        })

        pct = cstat.get('healthy_percent', None)

        if pct != None:
            color = 'green'

            if pct < 70:
                color = 'red'
            elif pct < 90:
                color = 'yellow'

            cstat.update({
                'health': "%d%% healthy" % pct,
                'hmetric': int(pct),
                'hcolor': color
            })
        else:
            cstat.update({
                'health': "no requests yet",
                'hmetric': 'waiting',
                'hcolor': 'grey'
            })

        return cstat
Example #22
0
 class NullApriori:
     renamed_modules: dict = dc_field(default_factory=dict)
     merged_modules: dict = dc_field(default_factory=dict)
     renamed_models: dict = dc_field(default_factory=dict)
     merged_models: dict = dc_field(default_factory=dict)
Example #23
0
class CteForm29:
    contribuyente_rut: Rut
    periodo_tributario: PeriodoTributario
    folio: int

    apellido_paterno_o_razon_social: Optional[str] = dc_field(default=None,
                                                              repr=False)
    apellido_materno: Optional[str] = dc_field(default=None, repr=False)
    nombres: Optional[str] = dc_field(default=None, repr=False)
    calle_direccion: Optional[str] = dc_field(default=None, repr=False)
    numero_direccion: Optional[str] = dc_field(default=None, repr=False)
    comuna_direccion: Optional[str] = dc_field(default=None, repr=False)
    telefono: Optional[str] = dc_field(default=None, repr=False)
    correo_electronico: Optional[str] = dc_field(default=None, repr=False)
    representante_legal_rut: Optional[Rut] = dc_field(default=None, repr=False)

    total_a_pagar_en_plazo_legal: Optional[int] = dc_field(default=None,
                                                           repr=False)
    total_a_pagar_con_recargo: Optional[int] = dc_field(default=None,
                                                        repr=False)

    pct_condonacion: Optional[int] = dc_field(default=None, repr=False)
    num_res_condonacion: Optional[str] = dc_field(default=None, repr=False)
    fecha_condonacion: Optional[date] = dc_field(default=None, repr=False)

    tipo_declaracion: Optional[str] = dc_field(default=None, repr=False)
    banco: Optional[str] = dc_field(default=None, repr=False)
    medio_pago: Optional[str] = dc_field(default=None, repr=False)
    fecha_presentacion: Optional[date] = dc_field(default=None, repr=False)

    extra: Mapping[int, object] = dc_field(default_factory=dict, repr=False)
    """
    Any SII Form 29 codes that do not have their own field go in `extra`.
    """

    _strict_codes: bool = dc_field(default=False, repr=False)
    """
    Consider unknown codes as invalid and reject them.

    The default is `False` because the SII Form 29 has a large number of codes and not all of them
    are known to this class. Also, the SII may add new codes at any time in the future.
    """

    CODE_FIELD_MAPPING: ClassVar[Mapping[int, Optional[str]]]
    """
    Map SII Form 29 numeric codes to their respective field names.

    If a numeric code is valid, but no field has been created for it, use `None` so that this class
    considers it as "known". Numeric codes that are not included here are considered unknown or
    invalid, even if they appear in the SII Form 29.
    """

    CODE_FIELD_MAPPING = {
        1:
        'apellido_paterno_o_razon_social',  # "APELLIDO PATERNO O RAZÓN SOCIAL"
        2: 'apellido_materno',  # Apellido Materno
        3: 'contribuyente_rut',  # "N DE RUT"
        5: 'nombres',  # Nombres
        6: 'calle_direccion',  # "DIRECCION"
        7: 'folio',  # "FOLIO"
        8: 'comuna_direccion',  # "COMUNA"
        9: 'telefono',  # Teléfono
        15: 'periodo_tributario',  # "PERIODO TRIBUTARIO"
        20: None,  # Exportaciones | Monto Neto
        30: None,  # "PPM ART. 84, A) PERD. ART. 90"
        48: None,  # "RET. IMP. ÚNICO TRAB. ART. 74 N 1 LIR"
        55: 'correo_electronico',  # Correo Electrónico
        60: 'pct_condonacion',  # "PORCENTAJE CONDONACION TGR"
        62: None,  # "PPM NETO DET."
        77: None,  # "REMANENTE DE CRÉDITO FISC."
        89: None,  # "IMP. DETERM. IVA DETERM."
        91: 'total_a_pagar_en_plazo_legal',  # "TOTAL A PAGAR DENTRO DEL PLAZO"
        92: None,  # "REAJUSTES"
        93: None,  # "Intereses y multas"
        94: 'total_a_pagar_con_recargo',  # "Total a pagar con recargo"
        110: None,  # Boletas | Cantidad
        111: None,  # Boletas | Débitos
        115: None,  # "TASA PPM 1ra. CATEGORIA"
        142: None,  # "VENTAS Y/O SERV. EXENTOS O NO GRAVADOS"
        151: None,  # "RET, TASAS DE 10 % SOBRE LAS RENT."
        153: None,  # "RET, TASAS DE 10% o 20% SOBRE LAS RENT."
        314: 'representante_legal_rut',  # Rut Representante Legal
        315: 'fecha_presentacion',  # "FECHA TIMBRE CAJA"
        500: None,  # "CANTIDAD FACTURAS"
        501: None,  # "LIQUIDACION DE FACTURAS"
        502: None,  # "DÉBITOS FACTURAS EMITIDAS"
        503: None,  # "CANTIDAD FACTURAS EMITIDAS"
        504: None,  # "REMANENTE CREDITO MES ANTERIOR"
        509: None,  # "CANT. DCTOS. NOTAS DE CRÉDITOS EMITIDAS"
        510: None,  # "DÉBITOS  NOTAS DE CRÉDITOS EMITIDAS"
        511: None,  # "CRÉD. IVA POR DCTOS. ELECTRONICOS"
        512: None,  # "CANT. DE DCTOS. NOTAS DE DÉBITO EMIT."
        513: None,  # "NOTAS DE DÉBITOS EMITIDAS"
        514:
        None,  # IVA por documentos electrónicos recibidos | Sin derecho a Crédito
        515:
        None,  # Facturas de Compra recibidas c/ret. total y Fact. de Inicio emitida | Cantidad
        516:
        None,  # Facturas de Compra recibidas con retención parcial | Cantidad
        517:
        None,  # Facturas de Compra recibidas con retención parcial | Débitos
        519: None,  # "CANT. DE DCTOS. FACT. RECIB. DEL GIRO"
        520: None,  # "CRÉDITO REC. Y REINT./FACT. DEL GIRO"
        521: None,  # "MONTO NETO / INTERNAS AFECTAS"
        524: None,  # "CANT. FACT. ACTIVO FIJO"
        525: None,  # "CRÉD.  RECUP. Y REINT. FACT. ACTIVO FIJO"
        527: None,  # "CANT. NOTAS DE CRÉDITO RECIBIDAS"
        528: None,  # "CRÉDITO RECUP. Y REINT NOTAS DE CRÉD"
        531: None,  # "CANT. NOTAS DE DÉBITO RECIBIDAS"
        532: None,  # "NOTAS DE DÉBITO CRÉD, RECUP. Y REINT."
        534:
        None,  # Declaraciones de Ingreso (DIN) importaciones del giro | Cantidad
        535:
        None,  # Declaraciones de Ingreso (DIN) importaciones del giro | Créd., Recup. y Reint.
        536:
        None,  # Declaraciones de Ingreso (DIN) import. activo fijo | Cantidad
        537: None,  # "TOTAL CRÉDITOS"
        538: None,  # "TOTAL DÉBITOS"
        547: None,  # "TOTAL DETERMINADO"
        553:
        None,  # Declaraciones de Ingreso (DIN) import. activo fijo | Créd., Recup. y Reint.
        562: None,  # "MONTO SIN DER. A CRED. FISCAL"
        563: None,  # "BASE IMPONIBLE"
        564: None,  # "CANT. DOC. SIN DER. A CRED. FISCAL"
        584: None,  # "CANT.INT.EX.NO GRAV.SIN DER. CRED.FISCAL"
        585: None,  # Exportaciones | Cantidad
        586: None,  # "CANT. VTAS. Y/O SERV. PREST. INT. EXENT."
        587:
        None,  # Facturas de Compra recibidas c/ret. total y Fact. de Inicio emitida | Monto
        595: None,  # "SUB TOTAL IMP. DETERMINADO ANVERSO"
        596: None,  # "RETENCION CAMBIO DE SUJETO"
        601: None,  # Fax
        610: 'numero_direccion',  # Nº Dirección
        708: None,  # "CANT. NOTAS CRED. EMIT. VALES MAQ. IVA"
        709: None,  # "MONTO NOTAS CRED. EMIT. VALES MAQ. IVA."
        755: None,  # IVA Postergado
        756: None,  # Casillero (checkbox) "Postergación pago de IVA"
        761: None,  # "CANT. FACT. SUPERMERCADOS Y SIMILARES"
        762: None,  # "CRÉD. FACT. SUPERMERCADOS Y SIMILARES"
        763: None,  # "CANT. FACT. POR VENTA BIENES INMUEBLES"
        764: None,  # "DÉB. FACT. POR VENTA BIENES INMUEBLES"
        765: None,  # "CANT. FACT. ADQ. O CONSTR. B. INMUEBLES"
        766: None,  # "DÉB. FACT. ADQ. O CONSTR. B. INMUEBLES"
        795: None,  # "MONTO DE CONDONACION SII"
        915: 'fecha_condonacion',  # "Fecha de Vigencia de Condonacion"
        922:
        'num_res_condonacion',  # "NUMERO RESOLUCION SII AUTO. CONDONACION"
        9906: None,  # "FECHA PRESENTACION DECL. PRIMITIVA"
    }

    def __post_init__(self) -> None:
        # -----Set Fields from Extra-----

        new_extra: MutableMapping[int, object] = {}

        for code, value in self.extra.items():
            field_name = self.get_field_name(code, strict=self._strict_codes)

            if field_name is None:
                # If there's no field for the code; leave it in `extra`.

                new_extra[code] = value
            else:
                # There's a field for the code; remove it from `extra`.

                current_field_value = getattr(self, field_name)
                if current_field_value is None:
                    # The current field's value is empty, so we set it.
                    object.__setattr__(self, field_name, value)
                else:
                    # The current field's value is not empty and we do not overwrite it. We give
                    # precedence to the current field's value because it may have been previously
                    # converted to a different data type (e.g. `periodo_tributario` has a code, but
                    # the code's value must be parsed and converted to an instance of
                    # `PeriodoTributario`).
                    pass

        object.__setattr__(self, 'extra', new_extra)

        # -----Validations-----

        validate_field_type(self, 'contribuyente_rut', Rut)
        validate_field_type(self, 'periodo_tributario', PeriodoTributario)
        validate_field_type(self, 'folio', int)
        validate_field_type(self, 'representante_legal_rut', (Rut, type(None)))
        validate_field_type(self, 'fecha_presentacion', (date, type(None)))

        if not all(isinstance(code, int) for code in self.extra):
            raise TypeError("All codes in 'extra' must be integers")

        # TODO: Validate the type of the other fields.

        # -----Warn About Unknown Codes-----

        if not self._strict_codes:
            unknown_codes = self.get_all_codes(
                strict=False) - self.get_all_codes(strict=True)
            if unknown_codes:
                logger.warning(
                    "%s(contribuyente_rut=%r, periodo_tributario=%r, folio=%r)"
                    " contains invalid or unknown SII Form 29 codes: %s.",
                    self.__class__.__name__,
                    self.contribuyente_rut,
                    self.periodo_tributario,
                    self.folio,
                    ', '.join(str(code) for code in sorted(unknown_codes)),
                )

    @classmethod
    def get_field_name(cls, code: int, strict: bool = True) -> Optional[str]:
        """
        Return the field name for the SII Form 29 code if a field name has been defined for the
        code. Return ``None`` if the code is known, but no field name is associated with it.

        :param code: SII Form 29 code.
        :param strict: Whether to consider unknown codes as invalid and raise an exception.

        :raises KeyError: If ``code`` is invalid and ``strict`` is ``True``.
        """
        if not isinstance(code, int):
            raise TypeError(
                f"An integer is required (got type '{code.__class__.__name__}')"
            )

        try:
            return cls.CODE_FIELD_MAPPING[code]
        except KeyError as e:
            if strict:
                raise KeyError(
                    f"Invalid or unknown SII Form 29 code: {code}") from e
            else:
                return None

    @property
    def natural_key(self) -> CteForm29NaturalKey:
        return CteForm29NaturalKey(
            contribuyente_rut=self.contribuyente_rut,
            periodo_tributario=self.periodo_tributario,
            folio=self.folio,
        )

    def get_all_codes(self, strict: Optional[bool] = None) -> Set[int]:
        """
        Return a set with all codes.

        :param strict: Whether to include unknown codes.
        """
        strict = self._strict_codes if strict is None else strict

        if strict:
            return set(self.CODE_FIELD_MAPPING)
        else:
            return {*self.CODE_FIELD_MAPPING, *self.extra}

    def as_codes_dict(
        self,
        include_none: bool = True,
        strict: Optional[bool] = None,
    ) -> Mapping[int, object]:
        """
        Return a dictionary of SII Form 29 codes and values. Fields that do not have a code are not
        included.

        :param include_none: Include codes that have an empty value (i.e. ``None``).
        :param strict: Whether to include unknown codes.
        """
        strict = self._strict_codes if strict is None else strict

        obj_dict = {
            code: self[code]
            for code in self.get_all_codes(strict=strict)
        }

        if not include_none:
            obj_dict = {
                code: value
                for code, value in obj_dict.items() if value is not None
            }

        return obj_dict

    def __getitem__(self, key: int) -> Any:
        """
        Return the value of the SII Form 29 code ``key``.
        """
        field_name = self.get_field_name(key, strict=self._strict_codes)

        if field_name is None:
            # Field is valid, but no field name has been associated with it.
            return self.extra.get(key)
        else:
            # Field is valid and it has a field name.
            return getattr(self, field_name)

    def __iter__(self) -> Iterator:
        for dc_field_obj in dataclasses.fields(self):
            if not dc_field_obj.name.startswith(
                    '_'):  # Exclude private fields.
                yield (dc_field_obj.name, getattr(self, dc_field_obj.name))
Example #24
0
class Config:
    remap: Dict[str, str] = dc_field(default_factory=dict)
    prefixed: Dict[str, str] = dc_field(default_factory=dict)
    cast: List[str] = dc_field(default_factory=list)
    transform: Dict[str, Callable[[Any], Any]] = dc_field(default_factory=dict)
    flattened: List[str] = dc_field(default_factory=list)
    forward_references: Optional[Dict[str, Any]] = None
    check_types: bool = True

    def validate(self, data_class: Type, data: Data) -> None:
        self._validate_field_name(data_class, "remap")
        self._validate_data_key(data_class, data, "remap")
        self._validate_field_name(data_class, "prefixed")
        self._validate_data_key(data_class, data, "prefixed",
                                lambda v, c: any(n.startswith(v) for n in c))
        self._validate_field_name(data_class, "cast")
        self._validate_field_name(data_class, "transform")
        self._validate_field_name(data_class, "flattened")

    def make_inner(self, field: Field) -> "Config":
        return Config(
            remap=self._extract_nested_dict(field, self.remap),
            prefixed=self._extract_nested_dict(field, self.prefixed),
            cast=self._extract_nested_list(field, self.cast),
            transform=self._extract_nested_dict(field, self.transform),
            flattened=self._extract_nested_list(field, self.flattened),
            check_types=self.check_types,
        )

    # pylint: disable=unsupported-membership-test,unsubscriptable-object,no-member
    def get_value(self, field: Field, data: Data) -> Any:
        if field.name in self.flattened or field.name in self.prefixed:
            if field.name in self.flattened:
                value = data
            else:
                value = self._extract_nested_dict_for_prefix(
                    self.prefixed[field.name], data)
        else:
            try:
                key_name = self.remap.get(field.name, field.name)
                value = data[key_name]
            except KeyError:
                raise ValueNotFoundError()
            if field.name in self.transform:
                value = self.transform[field.name](value)
            if field.name in self.cast:
                value = cast_value(field.type, value)
        return value

    def _validate_field_name(self, data_class: Type, parameter: str) -> None:
        data_class_fields = {field.name for field in fields(data_class)}
        for data_class_field in getattr(self, parameter):
            if "." not in data_class_field:
                if data_class_field not in data_class_fields:
                    raise InvalidConfigurationError(
                        parameter=parameter,
                        available_choices=data_class_fields,
                        value=data_class_field)

    def _validate_data_key(self,
                           data_class: Type,
                           data: Data,
                           parameter: str,
                           validator=lambda v, c: v in c) -> None:
        input_data_keys = set(data.keys())
        data_class_fields = {field.name: field for field in fields(data_class)}
        for field_name, input_data_field in getattr(self, parameter).items():
            if "." not in field_name:
                field = data_class_fields[field_name]
                if not validator(input_data_field, input_data_keys
                                 ) and not has_field_default_value(field):
                    raise InvalidConfigurationError(
                        parameter=parameter,
                        available_choices=input_data_keys,
                        value=input_data_field)

    def _extract_nested_dict(self, field: Field,
                             params: Dict[str, Any]) -> Dict[str, Any]:
        prefix = field.name + "."
        return self._extract_nested_dict_for_prefix(prefix=prefix, data=params)

    def _extract_nested_list(self, field: Field,
                             params: List[str]) -> List[str]:
        result = []
        prefix = field.name + "."
        prefix_len = len(prefix)
        for name in params:
            if name.startswith(prefix):
                result.append(name[prefix_len:])
        return result

    def _extract_nested_dict_for_prefix(
            self, prefix: str, data: Dict[str, Any]) -> Dict[str, Any]:
        result = {}
        prefix_len = len(prefix)
        for key, val in data.items():
            if key.startswith(prefix):
                result[key[prefix_len:]] = val
        return result
Example #25
0
class Config:
    remap: Dict[str, str] = dc_field(default_factory=dict)
    prefixed: Dict[str, str] = dc_field(default_factory=dict)
    cast: List[str] = dc_field(default_factory=list)
    transform: Dict[str, Callable[[Any], Any]] = dc_field(default_factory=dict)
    flattened: List[str] = dc_field(default_factory=list)
Example #26
0
class Config:
    type_hooks: Dict[Type, Callable[[Any], Any]] = dc_field(default_factory=dict)
    forward_references: Optional[Dict[str, Any]] = None
    check_types: bool = True
    strict: bool = False
Example #27
0
class DteDataL2(DteDataL1):
    """
    DTE data level 2.

    About fields
    - ``emisor_razon_social``: redundant but required by the DTE XML schema.
    - ``receptor_razon_social``: redundant but required by the DTE XML schema.
    - ``fecha_vencimiento`` (date): important for some business logic
      but it is not required by the DTE XML schema.

    The class instances are immutable.

    """

    ###########################################################################
    # constants
    ###########################################################################

    DATETIME_FIELDS_TZ = tz_utils.TZ_CL_SANTIAGO

    ###########################################################################
    # fields
    ###########################################################################

    emisor_razon_social: str = dc_field()
    """
    "Razón social" (legal name) of the "emisor" of the DTE.
    """

    receptor_razon_social: str = dc_field()
    """
    "Razón social" (legal name) of the "receptor" of the DTE.
    """

    fecha_vencimiento_date: Optional[date] = dc_field(default=None)
    """
    "Fecha de vencimiento (pago)" of the DTE.
    """

    firma_documento_dt: Optional[datetime] = dc_field(default=None)
    """
    Datetime on which the "documento" was digitally signed.
    """

    signature_value: Optional[bytes] = dc_field(default=None)
    """
    DTE's digital signature's value (raw bytes, without base64 encoding).
    """

    signature_x509_cert_der: Optional[bytes] = dc_field(default=None)
    """
    DTE's digital signature's DER-encoded X.509 cert.

    .. seealso::
        Functions :func:`cl_sii.libs.crypto_utils.load_der_x509_cert`
        and :func:`cl_sii.libs.crypto_utils.x509_cert_der_to_pem`.
    """

    emisor_giro: Optional[str] = dc_field(default=None)
    """
    "Giro" of the "emisor" of the DTE.
    """

    emisor_email: Optional[str] = dc_field(default=None)
    """
    Email address of the "emisor" of the DTE.
    """

    receptor_email: Optional[str] = dc_field(default=None)
    """
    Email address of the "receptor" of the DTE.
    """
    def __post_init__(self) -> None:
        """
        Run validation automatically after setting the fields values.

        :raises TypeError, ValueError:

        """
        super().__post_init__()

        if not isinstance(self.emisor_razon_social, str):
            raise TypeError("Inappropriate type of 'emisor_razon_social'.")
        validate_contribuyente_razon_social(self.emisor_razon_social)

        if not isinstance(self.receptor_razon_social, str):
            raise TypeError("Inappropriate type of 'receptor_razon_social'.")
        validate_contribuyente_razon_social(self.receptor_razon_social)

        if self.fecha_vencimiento_date is not None:
            if not isinstance(self.fecha_vencimiento_date, date):
                raise TypeError(
                    "Inappropriate type of 'fecha_vencimiento_date'.")

        if self.firma_documento_dt is not None:
            if not isinstance(self.firma_documento_dt, datetime):
                raise TypeError("Inappropriate type of 'firma_documento_dt'.")
            validate_correct_tz(self.firma_documento_dt,
                                self.DATETIME_FIELDS_TZ)

        if self.signature_value is not None:
            if not isinstance(self.signature_value, bytes):
                raise TypeError("Inappropriate type of 'signature_value'.")
            # warning: do NOT strip a bytes value because "strip" implies an ASCII-encoded text,
            #   which in this case it is not.
            validate_non_empty_bytes(self.signature_value)

        if self.signature_x509_cert_der is not None:
            if not isinstance(self.signature_x509_cert_der, bytes):
                raise TypeError(
                    "Inappropriate type of 'signature_x509_cert_der'.")
            # warning: do NOT strip a bytes value because "strip" implies an ASCII-encoded text,
            #   which in this case it is not.
            validate_non_empty_bytes(self.signature_x509_cert_der)

        if self.emisor_giro is not None:
            if not isinstance(self.emisor_giro, str):
                raise TypeError("Inappropriate type of 'emisor_giro'.")
            validate_clean_str(self.emisor_giro)
            validate_non_empty_str(self.emisor_giro)

        if self.emisor_email is not None:
            if not isinstance(self.emisor_email, str):
                raise TypeError("Inappropriate type of 'emisor_email'.")
            validate_clean_str(self.emisor_email)
            validate_non_empty_str(self.emisor_email)

        if self.receptor_email is not None:
            if not isinstance(self.receptor_email, str):
                raise TypeError("Inappropriate type of 'receptor_email'.")
            validate_clean_str(self.receptor_email)
            validate_non_empty_str(self.receptor_email)
Example #28
0
class DteDataL1(DteDataL0):
    """
    DTE data level 1.

    It is the minimal set of DTE data fields that are useful.
    For example, SII has an endpoint that confirms that a given DTE exists,
    and the data that it requires can be obtained from this struct.

    The class instances are immutable.

    >>> instance = DteDataL1(
    ...     Rut('60910000-1'), TipoDteEnum.FACTURA_ELECTRONICA, 2093465, date(2018, 5, 7),
    ...     Rut('60910000-1'), 10403)

    >>> str(instance)
    "DteDataL1(" \
    "emisor_rut=Rut('60910000-1'), tipo_dte=<TipoDteEnum.FACTURA_ELECTRONICA: 33>, " \
    "folio=2093465, fecha_emision_date=datetime.date(2018, 5, 7), " \
    "receptor_rut=Rut('60910000-1'), monto_total=10403)"
    >>> str(instance) == repr(instance)
    True

    """

    fecha_emision_date: date = dc_field()
    """
    Field 'fecha_emision' of the DTE.

    .. warning:: It may not match the **real date** on which the DTE was issued
        or received/processed by SII.

    """

    receptor_rut: Rut = dc_field()
    """
    RUT of the "receptor" of the DTE.
    """

    monto_total: int = dc_field()
    """
    Total amount of the DTE.
    """
    def __post_init__(self) -> None:
        """
        Run validation automatically after setting the fields values.

        :raises TypeError, ValueError:

        """
        super().__post_init__()

        if not isinstance(self.fecha_emision_date, date):
            raise TypeError("Inappropriate type of 'fecha_emision_date'.")

        if not isinstance(self.receptor_rut, Rut):
            raise TypeError("Inappropriate type of 'receptor_rut'.")

        if not isinstance(self.monto_total, int):
            raise TypeError("Inappropriate type of 'monto_total'.")

        validate_dte_monto_total(self.monto_total)

    @property
    def vendedor_rut(self) -> Rut:
        """
        Return the RUT of the "vendedor".

        :raises ValueError:
        """
        if self.tipo_dte.emisor_is_vendedor:
            result = self.emisor_rut
        elif self.tipo_dte.receptor_is_vendedor:
            result = self.receptor_rut
        else:
            raise ValueError(
                "Concept \"vendedor\" does not apply for this 'tipo_dte'.",
                self.tipo_dte)

        return result

    @property
    def deudor_rut(self) -> Rut:
        """
        Return the RUT of the "deudor".

        :raises ValueError:
        """
        if self.tipo_dte.emisor_is_vendedor:
            result = self.receptor_rut
        elif self.tipo_dte.receptor_is_vendedor:
            result = self.emisor_rut
        else:
            raise ValueError(
                "Concept \"deudor\" does not apply for this 'tipo_dte'.",
                self.tipo_dte)

        return result
Example #29
0
class DteNaturalKey:
    """
    Natural key of a DTE.

    The class instances are immutable.

    This group of fields uniquely identifies a DTE.

    >>> instance = DteNaturalKey(Rut('60910000-1'), TipoDteEnum.FACTURA_ELECTRONICA, 2093465)

    >>> str(instance)
    "DteNaturalKey(" \
    "emisor_rut=Rut('60910000-1'), tipo_dte=<TipoDteEnum.FACTURA_ELECTRONICA: 33>, folio=2093465)"
    >>> str(instance) == repr(instance)
    True
    >>> instance.slug
    '60910000-1--33--2093465'

    """

    emisor_rut: Rut = dc_field()
    """
    RUT of the "emisor" of the DTE.
    """

    tipo_dte: TipoDteEnum = dc_field()
    """
    The kind of DTE.
    """

    folio: int = dc_field()
    """
    The sequential number of a DTE of given kind issued by 'emisor_rut'.
    """
    def __post_init__(self) -> None:
        """
        Run validation automatically after setting the fields values.

        :raises TypeError, ValueError:

        """

        if not isinstance(self.emisor_rut, Rut):
            raise TypeError("Inappropriate type of 'emisor_rut'.")

        if not isinstance(self.tipo_dte, TipoDteEnum):
            raise TypeError("Inappropriate type of 'tipo_dte'.")

        if not isinstance(self.folio, int):
            raise TypeError("Inappropriate type of 'folio'.")

        validate_dte_folio(self.folio)

    def as_dict(self) -> Mapping[str, object]:
        return dataclasses.asdict(self)

    @property
    def slug(self) -> str:
        """
        Return an slug representation (that preserves uniquess) of the instance.
        """
        # note: many alternatives were considered and discarded such as:
        #   f'{self.emisor_rut}-{self.tipo_dte}-{self.folio}'
        #   f'{self.emisor_rut}.{self.tipo_dte}.{self.folio}'
        #   f'{self.emisor_rut}/{self.tipo_dte}/{self.folio}'
        #   f'R-{self.emisor_rut}-T-{self.tipo_dte}-F-{self.folio}'
        #   f'rut-{self.emisor_rut}-tipo-{self.tipo_dte}-folio-{self.folio}'

        return f'{self.emisor_rut}--{self.tipo_dte}--{self.folio}'
Example #30
0
class FgData:
    dhcp_server: List[FgDhcpServer] = dc_field(default_factory=list)
    net_alias: List[FgNetAlias] = dc_field(default_factory=list)
    net_alias_group: List[FgNetAliasGroup] = dc_field(default_factory=list)
    ip_alias: List[FgIPAlias] = dc_field(default_factory=list)
    policy: List[FgPolicy] = dc_field(default_factory=list)
    service: List[FgService] = dc_field(default_factory=list)
    service_group: List[FgServiceGroup] = dc_field(default_factory=list)
    service_category: List[FgServiceCategory] = dc_field(default_factory=list)
    interface: List[FgInterface] = dc_field(default_factory=list)
    vpn_cert_ca: List[FgVpnCertCa] = dc_field(default_factory=list)
    vpn_cert_local: List[FgVpnCertLocal] = dc_field(default_factory=list)
    vpn_ipsec_phase_1: List[FgVpnIpsecPhase1] = dc_field(default_factory=list)
    vpn_ipsec_phase_2: List[FgVpnIpsecPhase2] = dc_field(default_factory=list)