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
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.21.0
current_version = 0.22.0
commit = True
tag = False
message = chore: Bump version from {current_version} to {new_version}
Expand Down
8 changes: 3 additions & 5 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ jobs:
strategy:
matrix:
python_version:
- "3.7.15"
- "3.8.13"
- "3.9.16"
- "3.10.9"
Expand All @@ -46,7 +45,7 @@ jobs:
run: make python-virtualenv PYTHON_VIRTUALENV_DIR="venv"

- name: Restoring/Saving Cache
uses: actions/cache@v3.2.5
uses: actions/cache@v3.2.6
with:
path: "venv"
key: py-v1-deps-${{ runner.os }}-${{ matrix.python_version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements-dev.txt') }}-${{ hashFiles('Makefile', 'make/**.mk') }}
Expand All @@ -70,7 +69,6 @@ jobs:
strategy:
matrix:
python_version:
- "3.7.15"
- "3.8.13"
- "3.9.16"
- "3.10.9"
Expand All @@ -85,7 +83,7 @@ jobs:
python-version: "${{ matrix.python_version }}"

- name: Restoring/Saving Cache
uses: actions/cache@v3.2.5
uses: actions/cache@v3.2.6
with:
path: "venv"
key: py-v1-deps-${{ runner.os }}-${{ matrix.python_version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements-dev.txt') }}-${{ hashFiles('Makefile', 'make/**.mk') }}
Expand Down Expand Up @@ -126,7 +124,7 @@ jobs:

- name: Check that compiled Python dependency manifests are up-to-date with their sources
# FIXME: There are issues related to testing with multiple Python versions.
if: ${{ startsWith(matrix.python_version, '3.7.') }}
if: ${{ startsWith(matrix.python_version, '3.8.') }}
run: |
source "$PYTHON_VIRTUALENV_ACTIVATE"
make python-deps-sync-check
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
python-version: "3.10.9"

- name: Restoring/Saving Cache
uses: actions/cache@v3.2.5
uses: actions/cache@v3.2.6
with:
path: "venv"
key: py-v1-deps-${{ runner.os }}-${{ steps.set_up_python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements-dev.txt') }}-${{ hashFiles('Makefile', 'make/**.mk') }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
run: make python-virtualenv PYTHON_VIRTUALENV_DIR="venv"

- name: Restoring/Saving Cache
uses: actions/cache@v3.2.5
uses: actions/cache@v3.2.6
with:
path: "venv"
key: py-v1-deps-${{ runner.os }}-${{ steps.set_up_python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements-dev.txt') }}-${{ hashFiles('Makefile', 'make/**.mk') }}
Expand Down
7 changes: 7 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# History

## 0.22.0 (2023-03-09)

- (PR #446, 2023-03-07) chore(deps-dev): Bump mypy from 0.991 to 1.0.1
- (PR #449, 2023-03-08) Drop support for Python 3.7
- (PR #442, 2023-03-09) chore: Bump actions/cache from 3.2.5 to 3.2.6
- (PR #440, 2023-03-09) fix(cte): Add default values for known missing codes in SII CTE Form 29

## 0.21.0 (2023-02-28)

- (PR #441, 2023-03-01) Switch CI/CD from CircleCI to GitHub Actions
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ The full documentation is at <https://lib-cl-sii-python.readthedocs.io>.

## Supported Python versions

Only Python 3.7, 3.8, 3.9 and 3.10. Python 3.6 and below will not work because we use some features
introduced in Python 3.7.
Only Python 3.8, 3.9 and 3.10. Python 3.7 and below will not work because we use some features
introduced in Python 3.8.

## Quickstart

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.21.0'
__version__ = '0.22.0'
108 changes: 99 additions & 9 deletions cl_sii/cte/f29/parse_datos_obj.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import copy
import json
from datetime import datetime
from decimal import Decimal
from pathlib import Path
Expand All @@ -23,10 +25,17 @@
)
CTE_F29_DATOS_OBJ_SCHEMA = read_json_schema(_CTE_F29_DATOS_OBJ_SCHEMA_PATH)

_CTE_F29_DATOS_OBJ_MISSING_KEY_FIXES_PATH = (
Path(__file__).parent.parent.parent / 'data' / 'cte' / 'f29_datos_obj_missing_key_fixes.json'
)
CTE_F29_DATOS_OBJ_MISSING_KEY_FIXES: SiiCteF29DatosObjType = json.load(
open(_CTE_F29_DATOS_OBJ_MISSING_KEY_FIXES_PATH)
)


def parse_sii_cte_f29_datos_obj(
datos_obj: SiiCteF29DatosObjType,
schema_validator: Optional[Callable[[SiiCteF29DatosObjType], None]] = None,
schema_validator: Optional[Callable[[SiiCteF29DatosObjType], SiiCteF29DatosObjType]] = None,
campo_deserializer: Optional[Callable[[object, str], object]] = None,
) -> CteForm29:
"""
Expand Down Expand Up @@ -55,7 +64,7 @@ def parse_sii_cte_f29_datos_obj(

def _parse_sii_cte_f29_datos_obj_to_dict(
datos_obj: SiiCteF29DatosObjType,
schema_validator: Callable[[SiiCteF29DatosObjType], None],
schema_validator: Callable[[SiiCteF29DatosObjType], SiiCteF29DatosObjType],
campo_deserializer: Callable[[object, str], object],
) -> Mapping[str, object]:
"""
Expand All @@ -67,17 +76,17 @@ def _parse_sii_cte_f29_datos_obj_to_dict(
:param campo_deserializer:
:raises JsonSchemaValidationError: If schema validation fails.
"""
schema_validator(datos_obj)
validated_datos_obj = schema_validator(datos_obj)

datos_obj_campos: Mapping[int, str] = {
int(code): str(value) for code, value in datos_obj['campos'].items()
int(code): str(value) for code, value in validated_datos_obj['campos'].items()
}
datos_obj_extras: Mapping[str, object] = datos_obj['extras']
datos_obj_extras: Mapping[str, object] = validated_datos_obj['extras']
datos_obj_glosa: Mapping[int, str] = { # noqa: F841
int(code): str(value) for code, value in datos_obj['glosa'].items()
int(code): str(value) for code, value in validated_datos_obj['glosa'].items()
}
datos_obj_tipos: Mapping[int, str] = {
int(code): str(value) for code, value in datos_obj['tipos'].items()
int(code): str(value) for code, value in validated_datos_obj['tipos'].items()
}

deserialized_datos_obj_campos = {
Expand Down Expand Up @@ -156,12 +165,14 @@ def cte_f29_datos_obj_campo_best_effort_deserializer(campo_value: object, tipo:
return deserialized_value


def cte_f29_datos_schema_default_validator(datos_obj: SiiCteF29DatosObjType) -> None:
def cte_f29_datos_schema_default_validator(
datos_obj: SiiCteF29DatosObjType,
) -> SiiCteF29DatosObjType:
"""
Validate the ``datos`` object against the schema.

:raises JsonSchemaValidationError: If schema validation fails.
:returns: ``None`` if schema validation passed.
:returns: Validated ``datos`` object if schema validation passed.
"""
try:
jsonschema.validate(datos_obj, schema=CTE_F29_DATOS_OBJ_SCHEMA)
Expand All @@ -172,3 +183,82 @@ def cte_f29_datos_schema_default_validator(datos_obj: SiiCteF29DatosObjType) ->
raise JsonSchemaValidationError("The keys of 'campos' and 'tipos' are not exactly the same")
if datos_obj['campos'].keys() != datos_obj['glosa'].keys():
raise JsonSchemaValidationError("The keys of 'campos' and 'tipos' are not exactly the same")

return datos_obj


def cte_f29_datos_schema_best_effort_validator(
datos_obj: SiiCteF29DatosObjType,
) -> SiiCteF29DatosObjType:
"""
Validate the ``datos`` object against the schema.

If there are missing keys in the `tipos` or `glosa` dicts, it will try to get them
from a list of default values.

:raises JsonSchemaValidationError: If schema validation fails.
:returns: Validated ``datos`` object if schema validation passed.
"""
try:
validated_datos_obj = cte_f29_datos_schema_default_validator(datos_obj)
except JsonSchemaValidationError as exc:
if exc.__cause__ is jsonschema.exceptions.ValidationError:
# We will not try to fix this kind of error.
raise
elif exc.__cause__ is None:
# Let's try to fix this.
new_datos_obj = try_fix_cte_f29_datos(datos_obj)

# Let's try again.
cte_f29_datos_schema_default_validator(new_datos_obj)
return new_datos_obj
else:
raise
else:
return validated_datos_obj


def try_fix_cte_f29_datos(datos_obj: SiiCteF29DatosObjType) -> SiiCteF29DatosObjType:
"""
Try to fix the ``datos`` object.

If there are missing keys in the `tipos` or `glosa` dicts, it will try to get them
from a list of default values.

:raises JsonSchemaValidationError: If an unfixable issue is found.
:returns: A possibly fixed ``datos`` object.
"""
new_datos_obj: Mapping[str, MutableMapping[str, object]]
new_datos_obj = copy.deepcopy(datos_obj) # type: ignore[arg-type]

campos_tipos_keys_diff = datos_obj['campos'].keys() - datos_obj['tipos'].keys()
remaining_campos_tipos_diff = (
campos_tipos_keys_diff - CTE_F29_DATOS_OBJ_MISSING_KEY_FIXES['tipos'].keys()
)
if remaining_campos_tipos_diff:
raise JsonSchemaValidationError(
"The keys of 'campos' and 'tipos' differ for the following codes: "
f"{remaining_campos_tipos_diff}"
)
else:
for missing_key in campos_tipos_keys_diff:
new_datos_obj['tipos'][missing_key] = CTE_F29_DATOS_OBJ_MISSING_KEY_FIXES['tipos'][
missing_key
]

campos_glosa_keys_diff = datos_obj['campos'].keys() - datos_obj['glosa'].keys()
remaining_campos_glosa_diff = (
campos_glosa_keys_diff - CTE_F29_DATOS_OBJ_MISSING_KEY_FIXES['glosa'].keys()
)
if remaining_campos_glosa_diff:
raise JsonSchemaValidationError(
"The keys of 'campos' and 'glosa' differ for the following codes: "
f"{remaining_campos_glosa_diff}"
)
else:
for missing_key in campos_glosa_keys_diff:
new_datos_obj['glosa'][missing_key] = CTE_F29_DATOS_OBJ_MISSING_KEY_FIXES['glosa'][
missing_key
]

return new_datos_obj
8 changes: 8 additions & 0 deletions cl_sii/data/cte/f29_datos_obj_missing_key_fixes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"glosa": {
"049": "(Desconocido)"
},
"tipos": {
"049": "M"
}
}
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[mypy]
python_version = 3.7
python_version = 3.8
platform = linux
files =
cl_sii,
Expand Down
2 changes: 1 addition & 1 deletion requirements-dev.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ codecov==2.1.12
coverage==7.1.0
flake8==4.0.1
isort==5.10.1
mypy==0.991
mypy==1.0.1
tox==3.25.1
twine==3.1.1
types-jsonschema==4.17.0.3
Expand Down
22 changes: 2 additions & 20 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# This file is autogenerated by pip-compile with python 3.7
# This file is autogenerated by pip-compile with python 3.8
# To update, run:
#
# pip-compile --strip-extras requirements-dev.in
Expand Down Expand Up @@ -47,16 +47,6 @@ flake8==4.0.1
# via -r requirements-dev.in
idna==2.10
# via requests
importlib-metadata==1.6.0
# via
# -c requirements.txt
# click
# flake8
# keyring
# pluggy
# tox
# twine
# virtualenv
isort==5.10.1
# via -r requirements-dev.in
jeepney==0.8.0
Expand All @@ -67,7 +57,7 @@ keyring==21.4.0
# via twine
mccabe==0.6.1
# via flake8
mypy==0.991
mypy==1.0.1
# via -r requirements-dev.in
mypy-extensions==0.4.3
# via
Expand Down Expand Up @@ -125,10 +115,6 @@ tqdm==4.64.0
# via twine
twine==3.1.1
# via -r requirements-dev.in
typed-ast==1.5.4
# via
# black
# mypy
types-jsonschema==4.17.0.3
# via -r requirements-dev.in
types-pyopenssl==23.0.0.2
Expand All @@ -148,10 +134,6 @@ webencodings==0.5.1
# via bleach
wheel==0.38.4
# via -r requirements-dev.in
zipp==3.8.1
# via
# -c requirements.txt
# importlib-metadata

# The following packages are considered to be unsafe in a requirements file:
# setuptools
11 changes: 3 additions & 8 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# This file is autogenerated by pip-compile with python 3.7
# This file is autogenerated by pip-compile with python 3.8
# To update, run:
#
# pip-compile --strip-extras requirements.in
Expand All @@ -26,9 +26,7 @@ django==3.2.17
djangorestframework==3.14.0
# via -r requirements.in
importlib-metadata==1.6.0
# via
# -r requirements.in
# jsonschema
# via -r requirements.in
importlib-resources==5.10.2
# via jsonschema
jsonschema==4.17.3
Expand Down Expand Up @@ -63,10 +61,7 @@ signxml==2.10.1
sqlparse==0.4.2
# via django
typing-extensions==4.3.0
# via
# asgiref
# jsonschema
# pydantic
# via pydantic
zipp==3.8.1
# via
# importlib-metadata
Expand Down
3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ def get_version(*file_paths: Sequence[str]) -> str:
'License :: OSI Approved :: MIT License',
'Natural Language :: English',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
Expand All @@ -87,7 +86,7 @@ def get_version(*file_paths: Sequence[str]) -> str:
name='cl-sii',
package_data=_package_data,
packages=find_packages(exclude=['docs', 'tests*']),
python_requires='>=3.7, <3.11',
python_requires='>=3.8, <3.11',
setup_requires=setup_requirements,
test_suite='tests',
tests_require=test_requirements,
Expand Down
Loading