diff --git a/src/cl_sii/extras/mm_fields.py b/src/cl_sii/extras/mm_fields.py index a101e2fc..2af05dd0 100644 --- a/src/cl_sii/extras/mm_fields.py +++ b/src/cl_sii/extras/mm_fields.py @@ -14,7 +14,7 @@ raise ImportError("Package 'marshmallow' is required to use this module.") from exc import datetime -from typing import Any, Mapping, Optional +from typing import Any, ClassVar, Mapping, Optional import marshmallow.fields @@ -47,11 +47,25 @@ class RutField(marshmallow.fields.Field): """ + validate_dv: bool + validate_dv_by_default: ClassVar[bool] = False + default_error_messages = { 'invalid': 'Not a syntactically valid RUT.', + 'invalid_dv': """RUT's "digito verificador" is incorrect.""", 'type': 'Invalid type.', } + def __init__( + self, + *, + validate_dv: bool = validate_dv_by_default, + **kwargs: Any, + ): + super().__init__(**kwargs) + + self.validate_dv = validate_dv + def _serialize( self, value: Optional[object], attr: str | None, obj: object, **kwargs: Any ) -> Optional[str]: @@ -73,6 +87,13 @@ def _validated(self, value: Optional[object]) -> Optional[Rut]: raise self.make_error('type') from exc except ValueError as exc: raise self.make_error('invalid') from exc + + if self.validate_dv and validated is not None: + try: + validated.validate_dv(raise_exception=True) + except ValueError as exc: + raise self.make_error('invalid_dv') from exc + return validated diff --git a/src/tests/test_extras_mm_fields.py b/src/tests/test_extras_mm_fields.py index 52a9463b..57cb4a02 100644 --- a/src/tests/test_extras_mm_fields.py +++ b/src/tests/test_extras_mm_fields.py @@ -47,6 +47,7 @@ class MyMmSchemaStrict(marshmallow.Schema): emisor_rut = RutField( required=True, data_key='RUT of Emisor', + validate_dv=True, ) other_field = marshmallow.fields.Integer( required=False, @@ -61,27 +62,27 @@ class MyMmSchemaStrict(marshmallow.Schema): def test_load_ok_valid(self) -> None: schema = self.LoadMyMmSchema() - data_valid_1 = {'RUT of Emisor': '1-1'} - data_valid_2 = {'RUT of Emisor': Rut('1-1')} - data_valid_3 = {'RUT of Emisor': ' 1.111.111-k \t '} + data_valid_1 = {'RUT of Emisor': '1-9'} + data_valid_2 = {'RUT of Emisor': Rut('1-9')} + data_valid_3 = {'RUT of Emisor': ' 1.111.119-k \t '} result = schema.load(data_valid_1) - self.assertDictEqual(dict(result), {'emisor_rut': Rut('1-1')}) + self.assertDictEqual(dict(result), {'emisor_rut': Rut('1-9')}) result = schema.load(data_valid_2) - self.assertDictEqual(dict(result), {'emisor_rut': Rut('1-1')}) + self.assertDictEqual(dict(result), {'emisor_rut': Rut('1-9')}) result = schema.load(data_valid_3) - self.assertDictEqual(dict(result), {'emisor_rut': Rut('1111111-K')}) + self.assertDictEqual(dict(result), {'emisor_rut': Rut('1111119-K')}) def test_dump_ok_valid(self) -> None: schema = self.DumpMyMmSchema() - obj_valid_1 = self.MyObj(emisor_rut=Rut('1-1')) + obj_valid_1 = self.MyObj(emisor_rut=Rut('1-9')) obj_valid_2 = self.MyObj(emisor_rut=None) data = schema.dump(obj_valid_1) - self.assertDictEqual(data, {'emisor_rut': '1-1', 'other_field': None}) + self.assertDictEqual(data, {'emisor_rut': '1-9', 'other_field': None}) data = schema.dump(obj_valid_2) self.assertDictEqual(data, {'emisor_rut': None, 'other_field': None}) @@ -110,11 +111,13 @@ def test_dump_ok_strange(self) -> None: def test_load_fail(self) -> None: schema = self.LoadMyMmSchema() + schema_strict = self.MyMmSchemaStrict() data_invalid_1 = {'RUT of Emisor': '123123123123'} data_invalid_2 = {'RUT of Emisor': 123} data_invalid_3 = {'RUT of Emisor': None} data_invalid_4 = {} + data_invalid_5 = {'RUT of Emisor': '1-1'} with self.assertRaises(marshmallow.ValidationError) as cm: schema.load(data_invalid_1) @@ -136,6 +139,18 @@ def test_load_fail(self) -> None: cm.exception.messages, {'RUT of Emisor': ['Missing data for required field.']} ) + try: + schema.load(data_invalid_5) + except marshmallow.ValidationError as exc: + self.fail(f'{exc.__class__.__name__} raised') + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema_strict.load(data_invalid_5) + self.assertEqual( + cm.exception.messages, + {'RUT of Emisor': ["""RUT's "digito verificador" is incorrect."""]}, + ) + def test_dump_fail(self) -> None: schema = self.DumpMyMmSchema()