Skip to content

Commit

Permalink
Merge pull request #231 from fyntex/release/v0.13.0
Browse files Browse the repository at this point in the history
Release v0.13.0
  • Loading branch information
ycouce-cdd committed Jul 15, 2021
2 parents a9eca10 + 559c8f5 commit 8e390e0
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 118 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.12.5
current_version = 0.13.0
commit = True
tag = True

Expand Down
6 changes: 3 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:

dist:
docker:
- image: docker.io/library/python:3.8.9
- image: docker.io/library/python:3.8.10

working_directory: ~/repo

Expand Down Expand Up @@ -119,7 +119,7 @@ jobs:

deploy:
docker:
- image: docker.io/library/python:3.8.9
- image: docker.io/library/python:3.8.10
environment:
<<: *x-deploy-environment

Expand Down Expand Up @@ -149,7 +149,7 @@ workflows:
parameters:
python_version:
- "3.7.9"
- "3.8.9"
- "3.8.10"
- "3.9.1"
- dist:
requires:
Expand Down
7 changes: 7 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
History
-------

0.13.0 (2021-07-14)
+++++++++++++++++++++++

* (PR #228, 2021-06-29) config: Update Python version used in CI jobs to 3.8.10
* (PR #230, 2021-07-09) dte: Convert stdlib dataclass `DteXmlData` into pydantic dataclass


0.12.5 (2021-06-15)
+++++++++++++++++++++++

Expand Down
2 changes: 1 addition & 1 deletion cl_sii/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
"""


__version__ = '0.12.5'
__version__ = '0.13.0'
119 changes: 51 additions & 68 deletions cl_sii/dte/data_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from datetime import date, datetime
from typing import Mapping, Optional

import pydantic

import cl_sii.contribuyente.constants
import cl_sii.rut.constants
from cl_sii.base.constants import SII_OFFICIAL_TZ
Expand Down Expand Up @@ -466,7 +468,12 @@ def as_dte_data_l1(self) -> DteDataL1:
monto_total=self.monto_total)


@dataclasses.dataclass(frozen=True)
@pydantic.dataclasses.dataclass(
frozen=True,
config=type('Config', (), dict(
arbitrary_types_allowed=True,
))
)
class DteXmlData(DteDataL1):

"""
Expand Down Expand Up @@ -495,32 +502,32 @@ class DteXmlData(DteDataL1):
# fields
###########################################################################

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

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

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

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

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

signature_x509_cert_der: Optional[bytes] = dc_field(default=None)
signature_x509_cert_der: Optional[bytes] = None
"""
DTE's digital signature's DER-encoded X.509 cert.
Expand All @@ -529,79 +536,21 @@ class DteXmlData(DteDataL1):
and :func:`cl_sii.libs.crypto_utils.x509_cert_der_to_pem`.
"""

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

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

receptor_email: Optional[str] = dc_field(default=None)
receptor_email: Optional[str] = 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'.")
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 as_dte_data_l1(self) -> DteDataL1:
return DteDataL1(
emisor_rut=self.emisor_rut,
Expand Down Expand Up @@ -629,3 +578,37 @@ def as_dte_data_l2(self) -> DteDataL2:
emisor_email=self.emisor_email,
receptor_email=self.receptor_email,
)

###########################################################################
# Validators
###########################################################################

@pydantic.validator('emisor_razon_social', 'receptor_razon_social')
def validate_contribuyente_razon_social(cls, v: object) -> object:
if isinstance(v, str):
validate_contribuyente_razon_social(v)
return v

@pydantic.validator('firma_documento_dt')
def validate_datetime_tz(cls, v: object) -> object:
if isinstance(v, datetime):
tz_utils.validate_dt_tz(v, cls.DATETIME_FIELDS_TZ)
return v

@pydantic.validator('signature_value', 'signature_x509_cert_der')
def validate_non_empty_bytes(cls, v: object) -> object:
if isinstance(v, bytes):
validate_non_empty_bytes(v)
return v

@pydantic.validator('emisor_giro', 'emisor_email', 'receptor_email')
def validate_no_leading_or_trailing_whitespace_characters(cls, v: object) -> object:
if isinstance(v, str):
validate_clean_str(v)
return v

@pydantic.validator('emisor_giro', 'emisor_email', 'receptor_email')
def validate_non_empty_stripped_str(cls, v: object) -> object:
if isinstance(v, str):
validate_non_empty_str(v)
return v

0 comments on commit 8e390e0

Please sign in to comment.