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)
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'.") tz_utils.validate_dt_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)
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)
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.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 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 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)
def test_validate_dt_tz_tzinfo_zone_attribute_check(self) -> None: # Time zone: UTC. Source: Pytz: tzinfo_utc_pytz = TZ_UTC dt_with_tzinfo_utc_pytz = convert_naive_dt_to_tz_aware( datetime.datetime(2021, 1, 6, 15, 21), tzinfo_utc_pytz, ) # Time zone: UTC. Source: Python Standard Library: tzinfo_utc_stdlib = datetime.timezone.utc dt_with_tzinfo_utc_stdlib = datetime.datetime.fromisoformat( '2021-01-06T15:04+00:00') # Time zone: Not UTC. Source: Pytz: tzinfo_not_utc_pytz = _TZ_CL_SANTIAGO dt_with_tzinfo_not_utc_pytz = convert_naive_dt_to_tz_aware( datetime.datetime(2021, 1, 6, 15, 21), tzinfo_not_utc_pytz, ) # Time zone: Not UTC. Source: Python Standard Library: tzinfo_not_utc_stdlib = datetime.timezone( datetime.timedelta(days=-1, seconds=75600)) dt_with_tzinfo_not_utc_stdlib = datetime.datetime.fromisoformat( '2021-01-06T15:04-03:00') # Test datetimes with UTC time zone: expected_error_message = re.compile( r"^Object datetime.timezone.utc must have 'zone' attribute.$") with self.assertRaisesRegex(AssertionError, expected_error_message): validate_dt_tz(dt_with_tzinfo_utc_pytz, tzinfo_utc_stdlib) with self.assertRaisesRegex(AssertionError, expected_error_message): validate_dt_tz(dt_with_tzinfo_utc_stdlib, tzinfo_utc_pytz) # Test datetimes with non-UTC time zone: expected_error_message = re.compile( r"^Object" r" datetime.timezone\(datetime.timedelta\(days=-1, seconds=75600\)\)" r" must have 'zone' attribute.$") with self.assertRaisesRegex(AssertionError, expected_error_message): validate_dt_tz(dt_with_tzinfo_not_utc_pytz, tzinfo_not_utc_stdlib) # type: ignore with self.assertRaisesRegex(AssertionError, expected_error_message): validate_dt_tz(dt_with_tzinfo_not_utc_stdlib, tzinfo_not_utc_pytz)
def __post_init__(self) -> None: """ Run validation automatically after setting the fields values. :raises TypeError, ValueError: """ ####################################################################### # fields of DTE ####################################################################### if not isinstance(self.dte_vendedor_rut, Rut): raise TypeError("Inappropriate type of 'dte_vendedor_rut'.") if not isinstance(self.dte_deudor_rut, Rut): raise TypeError("Inappropriate type of 'dte_deudor_rut'.") if not isinstance(self.dte_tipo_dte, TipoDteEnum): raise TypeError("Inappropriate type of 'dte_tipo_dte'.") if self.dte_tipo_dte not in TIPO_DTE_CEDIBLES: raise ValueError( "The \"tipo DTE\" in 'dte_tipo_dte' is not \"cedible\".", self.dte_tipo_dte) if not isinstance(self.dte_folio, int): raise TypeError("Inappropriate type of 'dte_folio'.") if not self.dte_folio > 0: raise ValueError("Inappropriate value of 'dte_folio'.") if not isinstance(self.dte_fecha_emision, date): raise TypeError("Inappropriate type of 'dte_fecha_emision'.") # TODO: figure out validation rules of 'dte_monto_total' if not isinstance(self.dte_monto_total, int): raise TypeError("Inappropriate type of 'dte_monto_total'.") ####################################################################### # fields of "cesion" ####################################################################### if not isinstance(self.cedente_rut, Rut): raise TypeError("Inappropriate type of 'cedente_rut'.") if not isinstance(self.cedente_razon_social, str): raise TypeError("Inappropriate type of 'cedente_razon_social'.") cl_sii.dte.data_models.validate_contribuyente_razon_social( self.cedente_razon_social) if self.cedente_email is not None: if not isinstance(self.cedente_email, str): raise TypeError("Inappropriate type of 'cedente_email'.") cl_sii.dte.data_models.validate_clean_str(self.cedente_email) cl_sii.dte.data_models.validate_non_empty_str(self.cedente_email) if not isinstance(self.cesionario_rut, Rut): raise TypeError("Inappropriate type of 'cesionario_rut'.") if not isinstance(self.cesionario_razon_social, str): raise TypeError("Inappropriate type of 'cesionario_razon_social'.") cl_sii.dte.data_models.validate_contribuyente_razon_social( self.cesionario_razon_social) if self.cesionario_emails is not None: if not isinstance(self.cesionario_emails, str): raise TypeError("Inappropriate type of 'cesionario_emails'.") cl_sii.dte.data_models.validate_clean_str(self.cesionario_emails) cl_sii.dte.data_models.validate_non_empty_str( self.cesionario_emails) if self.deudor_email is not None: if not isinstance(self.deudor_email, str): raise TypeError("Inappropriate type of 'deudor_email'.") cl_sii.dte.data_models.validate_clean_str(self.deudor_email) cl_sii.dte.data_models.validate_non_empty_str(self.deudor_email) if not isinstance(self.fecha_cesion_dt, datetime): raise TypeError("Inappropriate type of 'fecha_cesion_dt'.") tz_utils.validate_dt_tz(self.fecha_cesion_dt, SII_OFFICIAL_TZ) if not isinstance(self.fecha_cesion, date): raise TypeError("Inappropriate type of 'fecha_cesion'.") if self.fecha_cesion_dt.date() != self.fecha_cesion: raise ValueError( "Date of 'fecha_cesion_dt' (considering timezone) does not match 'fecha_cesion'.", self.fecha_cesion_dt, self.fecha_cesion) if not isinstance(self.monto_cedido, int): raise TypeError("Inappropriate type of 'monto_cedido'.") if not self.monto_cedido >= CESION_MONTO_CEDIDO_FIELD_MIN_VALUE: raise ValueError( f"Amount 'monto_cedido' must be >= {CESION_MONTO_CEDIDO_FIELD_MIN_VALUE}.", self.monto_cedido) data_models.validate_cesion_and_dte_montos( cesion_value=self.monto_cedido, dte_value=self.dte_monto_total, ) if not isinstance(self.fecha_ultimo_vencimiento, date): raise TypeError( "Inappropriate type of 'fecha_ultimo_vencimiento'.") if not isinstance(self.estado, str): raise TypeError("Inappropriate type of 'estado'.") cl_sii.dte.data_models.validate_clean_str(self.estado) cl_sii.dte.data_models.validate_non_empty_str(self.estado)
def validate_datetime_tz(cls, v: object) -> object: if isinstance(v, datetime): tz_utils.validate_dt_tz(v, cls.DATETIME_FIELDS_TZ) return v