Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions cl_sii/dte/data_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import cl_sii.contribuyente.constants
import cl_sii.rut.constants
from cl_sii.libs import encoding_utils
from cl_sii.libs import tz_utils
from cl_sii.rut import Rut

from . import constants
Expand Down Expand Up @@ -95,6 +96,13 @@ def validate_non_empty_bytes(value: bytes) -> None:
raise ValueError("Bytes value length (stripped) is 0.")


def validate_correct_tz(value: datetime, tz: tz_utils.PytzTimezone) -> None:
if not tz_utils.dt_is_aware(value):
raise ValueError("Value must be a timezone-aware datetime.", value)
if value.tzinfo.zone != tz.zone: # type: ignore
raise ValueError(f"Timezone of datetime value must be '{tz.zone!s}'.", value)


@dataclasses.dataclass(frozen=True)
class DteNaturalKey:

Expand Down Expand Up @@ -318,6 +326,16 @@ class DteDataL2(DteDataL1):

"""

###########################################################################
# 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.
Expand All @@ -333,7 +351,7 @@ class DteDataL2(DteDataL1):
"Fecha de vencimiento (pago)" of the DTE.
"""

firma_documento_dt_naive: Optional[datetime] = dc_field(default=None)
firma_documento_dt: Optional[datetime] = dc_field(default=None)
"""
Datetime on which the "documento" was digitally signed.
"""
Expand Down Expand Up @@ -386,9 +404,10 @@ def __post_init__(self) -> None:
if not isinstance(self.fecha_vencimiento_date, date):
raise TypeError("Inappropriate type of 'fecha_vencimiento_date'.")

if self.firma_documento_dt_naive is not None:
if not isinstance(self.firma_documento_dt_naive, datetime):
raise TypeError("Inappropriate type of 'firma_documento_dt_naive'.")
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):
Expand Down
7 changes: 5 additions & 2 deletions cl_sii/dte/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from typing import Tuple

from cl_sii.libs import encoding_utils
from cl_sii.libs import tz_utils
from cl_sii.libs import xml_utils
from cl_sii.libs.xml_utils import XmlElement, XmlElementTree
from cl_sii.rut import Rut
Expand Down Expand Up @@ -447,7 +448,9 @@ def parse_dte_xml(xml_doc: XmlElement) -> data_models.DteDataL2:

monto_total_value = int(monto_total_em.text.strip())

tmst_firma_value = datetime.fromisoformat(tmst_firma_em.text)
tmst_firma_value = tz_utils.convert_naive_dt_to_tz_aware(
dt=datetime.fromisoformat(tmst_firma_em.text),
tz=data_models.DteDataL2.DATETIME_FIELDS_TZ)

signature_signature_value = encoding_utils.decode_base64_strict(
signature_signature_value_em.text.strip())
Expand All @@ -464,7 +467,7 @@ def parse_dte_xml(xml_doc: XmlElement) -> data_models.DteDataL2:
emisor_razon_social=emisor_razon_social_value,
receptor_razon_social=receptor_razon_social_value,
fecha_vencimiento_date=fecha_vencimiento_value,
firma_documento_dt_naive=tmst_firma_value,
firma_documento_dt=tmst_firma_value,
signature_value=signature_signature_value,
signature_x509_cert_pem=signature_key_info_x509_cert_pem,
emisor_giro=emisor_giro_value,
Expand Down
29 changes: 27 additions & 2 deletions tests/test_dte_data_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import unittest
from datetime import date, datetime

from cl_sii.libs import tz_utils
from cl_sii.rut import Rut # noqa: F401

from cl_sii.dte.constants import TipoDteEnum # noqa: F401
Expand Down Expand Up @@ -150,7 +151,9 @@ def setUp(self) -> None:
emisor_razon_social='INGENIERIA ENACON SPA',
receptor_razon_social='MINERA LOS PELAMBRES',
fecha_vencimiento_date=None,
firma_documento_dt_naive=datetime(2019, 4, 1, 1, 36, 40),
firma_documento_dt=tz_utils.convert_naive_dt_to_tz_aware(
dt=datetime(2019, 4, 1, 1, 36, 40),
tz=DteDataL2.DATETIME_FIELDS_TZ),
signature_value=None,
signature_x509_cert_pem=None,
emisor_giro='Ingenieria y Construccion',
Expand All @@ -175,7 +178,9 @@ def test_as_dict(self) -> None:
emisor_razon_social='INGENIERIA ENACON SPA',
receptor_razon_social='MINERA LOS PELAMBRES',
fecha_vencimiento_date=None,
firma_documento_dt_naive=datetime(2019, 4, 1, 1, 36, 40),
firma_documento_dt=tz_utils.convert_naive_dt_to_tz_aware(
dt=datetime(2019, 4, 1, 1, 36, 40),
tz=DteDataL2.DATETIME_FIELDS_TZ),
signature_value=None,
signature_x509_cert_pem=None,
emisor_giro='Ingenieria y Construccion',
Expand All @@ -197,3 +202,23 @@ def test_validate_dte_folio(self) -> None:
def test_validate_dte_monto_total(self) -> None:
# TODO: implement for 'validate_dte_monto_total'
pass

def test_validate_clean_str(self) -> None:
# TODO: implement for 'validate_clean_str'
pass

def test_validate_clean_bytes(self) -> None:
# TODO: implement for 'validate_clean_bytes'
pass

def test_validate_non_empty_str(self) -> None:
# TODO: implement for 'validate_non_empty_str'
pass

def test_validate_non_empty_bytes(self) -> None:
# TODO: implement for 'validate_non_empty_bytes'
pass

def test_validate_correct_tz(self) -> None:
# TODO: implement for 'validate_correct_tz'
pass
10 changes: 8 additions & 2 deletions tests/test_dte_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
from datetime import date, datetime

import cl_sii.dte.constants
from cl_sii.dte.data_models import DteDataL2
from cl_sii.libs import crypto_utils
from cl_sii.libs import encoding_utils
from cl_sii.libs import tz_utils
from cl_sii.libs import xml_utils
from cl_sii.rut import Rut

Expand Down Expand Up @@ -341,7 +343,9 @@ def test_parse_dte_xml_ok_1(self) -> None:
emisor_razon_social='INGENIERIA ENACON SPA',
receptor_razon_social='MINERA LOS PELAMBRES',
fecha_vencimiento_date=None,
firma_documento_dt_naive=datetime(2019, 4, 1, 1, 36, 40),
firma_documento_dt=tz_utils.convert_naive_dt_to_tz_aware(
dt=datetime(2019, 4, 1, 1, 36, 40),
tz=DteDataL2.DATETIME_FIELDS_TZ),
signature_value=self._TEST_DTE_1_SIGNATURE_VALUE,
signature_x509_cert_pem=self.dte_clean_xml_1_cert_pem_bytes,
emisor_giro='Ingenieria y Construccion',
Expand All @@ -365,7 +369,9 @@ def test_parse_dte_xml_ok_2(self) -> None:
emisor_razon_social='COMERCIALIZADORA INNOVA MOBEL SPA',
receptor_razon_social='EMPRESAS LA POLAR S.A.',
fecha_vencimiento_date=None,
firma_documento_dt_naive=datetime(2019, 3, 28, 13, 59, 52),
firma_documento_dt=tz_utils.convert_naive_dt_to_tz_aware(
dt=datetime(2019, 3, 28, 13, 59, 52),
tz=DteDataL2.DATETIME_FIELDS_TZ),
signature_value=self._TEST_DTE_2_SIGNATURE_VALUE,
signature_x509_cert_pem=self.dte_clean_xml_2_cert_pem_bytes,
emisor_giro='COMERCIALIZACION DE PRODUCTOS PARA EL HOGAR',
Expand Down