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
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())
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)
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)
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}"
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))
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)
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)
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)
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
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, })
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)
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)
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
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)
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, )
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)
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
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
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
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)
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))
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
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)
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
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)
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
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}'
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)