Skip to content

Commit

Permalink
Merge pull request #105 from fyntex/develop
Browse files Browse the repository at this point in the history
Release v0.9.1
  • Loading branch information
jtrh committed Mar 20, 2020
2 parents 4484b94 + c0981de commit 328fb51
Show file tree
Hide file tree
Showing 8 changed files with 392 additions and 15 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.8.4
current_version = 0.9.1
commit = True
tag = True

Expand Down
12 changes: 12 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
History
-------

0.9.1 (2020-03-20)
+++++++++++++++++++++++

* Fix incorrect version used in the previous release's changelog.

0.9.0 (2020-03-20)
+++++++++++++++++++++++

* (PR #104, 2020-02-27) cte.f29.parser: Rename custom validator and deserializer parameters
* (PR #97, 2020-02-25) cte: Allow four digit Form 29 codes
* (PR #103, 2020-02-24) cte: Add custom validators and deserializers to parser

0.8.4 (2020-02-06)
+++++++++++++++++++++++

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.8.4'
__version__ = '0.9.1'
2 changes: 2 additions & 0 deletions cl_sii/cte/f29/data_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ class CteForm29:
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"
Expand All @@ -167,6 +168,7 @@ class CteForm29:
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:
Expand Down
59 changes: 51 additions & 8 deletions cl_sii/cte/f29/parse_datos_obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from datetime import datetime
from decimal import Decimal
from pathlib import Path
from typing import Mapping, MutableMapping
from typing import Callable, Mapping, MutableMapping, Optional

import jsonschema

Expand All @@ -22,27 +22,50 @@
CTE_F29_DATOS_OBJ_SCHEMA = read_json_schema(_CTE_F29_DATOS_OBJ_SCHEMA_PATH)


def parse_sii_cte_f29_datos_obj(datos_obj: SiiCteF29DatosObjType) -> CteForm29:
def parse_sii_cte_f29_datos_obj(
datos_obj: SiiCteF29DatosObjType,
schema_validator: Optional[Callable[[SiiCteF29DatosObjType], None]] = None,
campo_deserializer: Optional[Callable[[object, str], object]] = None,
) -> CteForm29:
"""
Parse the ``datos`` JavaScript object embedded in the IFrames of the
HTML version of the CTE's 'Declaraciones de IVA (F29)'.
:param schema_validator: Schema validator. For the signature of the callable, see the docstring
of ``cte_f29_datos_schema_default_validator``.
:param campo_deserializer: 'Campo' deserializer. For the signature of the callable, see the
docstring of ``cte_f29_datos_obj_campo_default_deserializer``.
:raises JsonSchemaValidationError: If schema validation fails.
"""
obj_params = _parse_sii_cte_f29_datos_obj_to_dict(datos_obj=datos_obj)
if schema_validator is None:
schema_validator = cte_f29_datos_schema_default_validator
if campo_deserializer is None:
campo_deserializer = cte_f29_datos_obj_campo_default_deserializer

obj_params = _parse_sii_cte_f29_datos_obj_to_dict(
datos_obj=datos_obj,
schema_validator=schema_validator,
campo_deserializer=campo_deserializer,
)
obj = CteForm29(**obj_params)
return obj


def _parse_sii_cte_f29_datos_obj_to_dict(datos_obj: SiiCteF29DatosObjType) -> Mapping[str, object]:
def _parse_sii_cte_f29_datos_obj_to_dict(
datos_obj: SiiCteF29DatosObjType,
schema_validator: Callable[[SiiCteF29DatosObjType], None],
campo_deserializer: Callable[[object, str], object],
) -> Mapping[str, object]:
"""
Parse the ``datos`` JavaScript object embedded in the IFrames of the
HTML version of the CTE's 'Declaraciones de IVA (F29)' and return a
dictionary.
:param schema_validator:
:param campo_deserializer:
:raises JsonSchemaValidationError: If schema validation fails.
"""
_validate_cte_f29_datos_schema(datos_obj=datos_obj)
schema_validator(datos_obj)

datos_obj_campos: Mapping[int, str] = {
int(code): str(value) for code, value in datos_obj['campos'].items()
Expand All @@ -56,7 +79,7 @@ def _parse_sii_cte_f29_datos_obj_to_dict(datos_obj: SiiCteF29DatosObjType) -> Ma
}

deserialized_datos_obj_campos = {
code: _deserialize_cte_f29_datos_obj_campo(campo_value=value, tipo=datos_obj_tipos[code])
code: campo_deserializer(value, datos_obj_tipos[code])
for code, value in datos_obj_campos.items()
}

Expand All @@ -82,13 +105,14 @@ def _parse_sii_cte_f29_datos_obj_to_dict(datos_obj: SiiCteF29DatosObjType) -> Ma
return obj_dict


def _deserialize_cte_f29_datos_obj_campo(campo_value: object, tipo: str) -> object:
def cte_f29_datos_obj_campo_default_deserializer(campo_value: object, tipo: str) -> object:
"""
Convert raw values from the ``datos`` object to the corresponding Python
data type.
:param campo_value: Raw value from 'campos'.
:param tipo: Data type code from 'tipos'.
:raises ValueError: If value conversion fails.
"""
if not isinstance(campo_value, str):
return campo_value
Expand All @@ -111,7 +135,26 @@ def _deserialize_cte_f29_datos_obj_campo(campo_value: object, tipo: str) -> obje
return deserialized_value


def _validate_cte_f29_datos_schema(datos_obj: SiiCteF29DatosObjType) -> None:
def cte_f29_datos_obj_campo_best_effort_deserializer(campo_value: object, tipo: str) -> object:
"""
Convert raw values from the ``datos`` object to the corresponding Python
data type. Values that cannot be converted will be returned without modification.
:param campo_value: Raw value from 'campos'.
:param tipo: Data type code from 'tipos'.
"""
try:
deserialized_value = cte_f29_datos_obj_campo_default_deserializer(
campo_value=campo_value,
tipo=tipo,
)
except Exception:
deserialized_value = campo_value

return deserialized_value


def cte_f29_datos_schema_default_validator(datos_obj: SiiCteF29DatosObjType) -> None:
"""
Validate the ``datos`` object against the schema.
Expand Down
10 changes: 5 additions & 5 deletions cl_sii/data/cte/schemas-json/f29_datos_obj.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"campos": {
"type": "object",
"patternProperties": {
"^\\d{3}$": {
"^\\d{3,4}$": {
"type": "string"
}
},
Expand Down Expand Up @@ -108,7 +108,7 @@
"glosa": {
"type": "object",
"patternProperties": {
"^\\d{3}$": {
"^\\d{3,4}$": {
"type": "string"
}
},
Expand All @@ -117,7 +117,7 @@
"justif": {
"type": "object",
"patternProperties": {
"^\\d{3}$": {
"^\\d{3,4}$": {
"type": "string",
"enum": [
"I",
Expand All @@ -131,7 +131,7 @@
"linea": {
"type": "object",
"patternProperties": {
"^\\d{3}$": {
"^\\d{3,4}$": {
"type": "string",
"pattern": "^\\d+$"
}
Expand All @@ -141,7 +141,7 @@
"tipos": {
"type": "object",
"patternProperties": {
"^\\d{3}$": {
"^\\d{3,4}$": {
"type": "string",
"enum": [
"C",
Expand Down
104 changes: 104 additions & 0 deletions tests/test_cte_f29_parse_datos_obj.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import datetime
from decimal import Decimal
from typing import Any, Mapping
from unittest import TestCase

Expand Down Expand Up @@ -114,3 +115,106 @@ def test_full(self) -> None:
"fecha_presentacion": datetime.date(2017, 8, 28),
}
self.assertEqual(dict(obj), expected_dict)

def test_full_2(self) -> None:
datos_obj: Mapping[str, Any] = read_test_file_json_dict(
'test_data/sii-cte/f29/cte--6286736-1--f29-6700000056-datos-obj-fake.json',
)
obj = parse_datos_obj.parse_sii_cte_f29_datos_obj(
datos_obj=datos_obj,
campo_deserializer=parse_datos_obj.cte_f29_datos_obj_campo_best_effort_deserializer,
)

self.assertIsInstance(obj, data_models.CteForm29)
self.assertEqual(obj.contribuyente_rut, Rut('6286736-1'))
self.assertEqual(obj.periodo_tributario, PeriodoTributario(year=2018, month=12))
self.assertEqual(obj.folio, 6700000056)

expected_codes_dict = {
1: "BONVALLET",
2: "GODOY",
3: Rut('6286736-1'),
5: "EDWARD GUILLERMO",
6: "GRECIA 2001",
7: 6700000056,
8: "MELIPILLA",
9: '0',
15: PeriodoTributario(year=2018, month=12),
48: 22633,
55: "E.BONVALLET@EXAMPLE.COM",
62: 32875,
77: 584976,
91: 503286,
115: Decimal('0.25'),
142: 13000000,
151: 447778,
315: datetime.date(2019, 1, 22),
502: 28500,
503: 1,
511: 613476,
519: 37,
520: 613476,
521: 2365382,
537: 613476,
538: 28500,
547: 503286,
562: 518660,
563: 13150000,
564: 27,
584: 11,
586: 1,
595: 503286,
596: 0,
9906: "22/01/2019",
}
self.assertEqual(obj.as_codes_dict(include_none=False), expected_codes_dict)

expected_dict = {
"contribuyente_rut": Rut('6286736-1'),
"periodo_tributario": PeriodoTributario(year=2018, month=12),
"folio": 6700000056,
"apellido_paterno_o_razon_social": "BONVALLET",
"apellido_materno": "GODOY",
"nombres": "EDWARD GUILLERMO",
"calle_direccion": "GRECIA 2001",
"numero_direccion": None,
"comuna_direccion": "MELIPILLA",
"telefono": "0",
"correo_electronico": "E.BONVALLET@EXAMPLE.COM",
"representante_legal_rut": None,
"extra": {
48: 22633,
62: 32875,
77: 584976,
115: Decimal('0.25'),
142: 13000000,
151: 447778,
502: 28500,
503: 1,
511: 613476,
519: 37,
520: 613476,
521: 2365382,
537: 613476,
538: 28500,
547: 503286,
562: 518660,
563: 13150000,
564: 27,
584: 11,
586: 1,
595: 503286,
596: 0,
9906: "22/01/2019",
},
"total_a_pagar_en_plazo_legal": 503286,
"total_a_pagar_con_recargo": None,
"pct_condonacion": None,
"num_res_condonacion": None,
"fecha_condonacion": None,
"tipo_declaracion": "Rectificatoria con Giro",
"banco": "",
"medio_pago": "",
"fecha_presentacion": datetime.date(2019, 1, 22),
}
self.assertEqual(dict(obj), expected_dict)

0 comments on commit 328fb51

Please sign in to comment.