cuenca-validations 2.1.41.dev0__py3-none-any.whl → 2.1.41.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -63,8 +63,16 @@ __all__ = [
63
63
  'LimitedWalletRequest',
64
64
  'MonthlyMovementsType',
65
65
  'MonthlySpendingType',
66
+ 'MoralPersonRequest',
67
+ 'MoralPersonUpdateRequest',
66
68
  'OperationalEventAction',
67
69
  'OperationalEventQuery',
70
+ 'OperatorLoginRequest',
71
+ 'OperatorLoginResponse',
72
+ 'OperatorRequest',
73
+ 'OperatorRole',
74
+ 'OperatorStatus',
75
+ 'OperatorUpdateRequest',
68
76
  'PartnerRequest',
69
77
  'PartnerUpdateRequest',
70
78
  'PasswordResetRequest',
@@ -162,6 +170,8 @@ from .enums import (
162
170
  MonthlyMovementsType,
163
171
  MonthlySpendingType,
164
172
  OperationalEventAction,
173
+ OperatorRole,
174
+ OperatorStatus,
165
175
  PlatformType,
166
176
  PosCapability,
167
177
  Profession,
@@ -249,6 +259,12 @@ from .requests import (
249
259
  FraudFundsTransferRequest,
250
260
  KYCValidationRequest,
251
261
  LimitedWalletRequest,
262
+ MoralPersonRequest,
263
+ MoralPersonUpdateRequest,
264
+ OperatorLoginRequest,
265
+ OperatorLoginResponse,
266
+ OperatorRequest,
267
+ OperatorUpdateRequest,
252
268
  PartnerRequest,
253
269
  PartnerUpdateRequest,
254
270
  PasswordResetRequest,
@@ -756,3 +756,13 @@ class OperationalEventAction(str, Enum):
756
756
  account_updated = 'account_updated'
757
757
  transfer_created = 'transfer_created'
758
758
  statement_downloaded = 'statement_downloaded'
759
+
760
+
761
+ class OperatorRole(str, Enum):
762
+ operator = 'operator'
763
+ authorizer = 'authorizer'
764
+
765
+
766
+ class OperatorStatus(str, Enum):
767
+ active = 'active'
768
+ disabled = 'disabled'
@@ -37,6 +37,8 @@ from ..types.enums import (
37
37
  KYCValidationSource,
38
38
  MonthlyMovementsType,
39
39
  MonthlySpendingType,
40
+ OperatorRole,
41
+ OperatorStatus,
40
42
  PlatformType,
41
43
  PosCapability,
42
44
  Profession,
@@ -897,5 +899,130 @@ class PartnerUpdateRequest(BaseRequest):
897
899
  shareholders: Optional[list[Shareholder]] = None
898
900
 
899
901
 
902
+ class MoralPersonRequest(BaseRequest):
903
+ legal_name: str
904
+ rfc: Rfc
905
+ legal_representatives: list[LegalRepresentative]
906
+
907
+ model_config = ConfigDict(
908
+ json_schema_extra={
909
+ 'example': {
910
+ 'legal_name': 'Aceros del Norte SA de CV',
911
+ 'rfc': 'ADN850101ABC',
912
+ 'legal_representatives': [
913
+ {
914
+ 'names': 'Juan',
915
+ 'first_surname': 'Perez',
916
+ 'job': 'Director General',
917
+ 'phone_number': '+525512345678',
918
+ 'email_address': 'juan.perez@aceros.com',
919
+ 'address': AddressRequest.model_json_schema().get(
920
+ 'example'
921
+ ),
922
+ }
923
+ ],
924
+ }
925
+ },
926
+ )
927
+
928
+ @field_validator('rfc')
929
+ @classmethod
930
+ def validate_moral_rfc(cls, rfc: Rfc) -> Rfc:
931
+ if len(rfc) != 12:
932
+ raise ValueError('RFC must be 12 characters for moral persons')
933
+ return rfc
934
+
935
+
936
+ class MoralPersonUpdateRequest(BaseRequest):
937
+ legal_name: Optional[str] = None
938
+ rfc: Optional[Rfc] = None
939
+ legal_representatives: Optional[list[LegalRepresentative]] = None
940
+
941
+ @model_validator(mode="before")
942
+ @classmethod
943
+ def check_at_least_one_param(cls, values: DictStrAny) -> DictStrAny:
944
+ if not values:
945
+ raise ValueError('At least one parameter must be provided')
946
+ return values
947
+
948
+ @field_validator('rfc')
949
+ @classmethod
950
+ def validate_moral_rfc(cls, rfc: Optional[Rfc]) -> Optional[Rfc]:
951
+ if rfc is not None and len(rfc) != 12:
952
+ raise ValueError('RFC must be 12 characters for moral persons')
953
+ return rfc
954
+
955
+
956
+ class OperatorRequest(BaseRequest):
957
+ name: str
958
+ email: EmailStr
959
+ phone: PhoneNumber
960
+ company_user_id: str
961
+ role: OperatorRole
962
+ status: OperatorStatus = OperatorStatus.active
963
+
964
+ model_config = ConfigDict(
965
+ json_schema_extra={
966
+ 'example': {
967
+ 'name': 'Maria Lopez',
968
+ 'email': 'maria.lopez@aceros.com',
969
+ 'phone': '+525512345678',
970
+ 'company_user_id': 'USWqY5cvkISJOxHyEKjAKf8w',
971
+ 'role': 'operator',
972
+ }
973
+ },
974
+ )
975
+
976
+ @field_validator('email', mode='before')
977
+ @classmethod
978
+ def validate_email(cls, email: str) -> str:
979
+ return normalize_email(email)
980
+
981
+
982
+ class OperatorUpdateRequest(BaseRequest):
983
+ name: Optional[str] = None
984
+ phone: Optional[PhoneNumber] = None
985
+ role: Optional[OperatorRole] = None
986
+ status: Optional[OperatorStatus] = None
987
+
988
+ @model_validator(mode="before")
989
+ @classmethod
990
+ def check_at_least_one_param(cls, values: DictStrAny) -> DictStrAny:
991
+ if not values:
992
+ raise ValueError('At least one parameter must be provided')
993
+ return values
994
+
995
+
996
+ class OperatorLoginRequest(BaseRequest):
997
+ email: EmailStr
998
+ password: Annotated[Password, LogConfig(masked=True)]
999
+
1000
+ model_config = ConfigDict(
1001
+ json_schema_extra={
1002
+ 'example': {
1003
+ 'email': 'maria.lopez@aceros.com',
1004
+ 'password': 'supersecret',
1005
+ }
1006
+ },
1007
+ )
1008
+
1009
+ @field_validator('email', mode='before')
1010
+ @classmethod
1011
+ def validate_email(cls, email: str) -> str:
1012
+ return normalize_email(email)
1013
+
1014
+
1015
+ class OperatorLoginResponse(BaseModel):
1016
+ session_token: str
1017
+
1018
+ model_config = ConfigDict(
1019
+ json_schema_extra={
1020
+ 'example': {
1021
+ 'session_token': 'SEWqY5cvkISJOxHyEKjAKf8w',
1022
+ }
1023
+ },
1024
+ )
1025
+
1026
+
900
1027
  class PhoneVerificationAssociationRequest(BaseRequest):
901
1028
  verification_id: str
@@ -1 +1 @@
1
- __version__ = '2.1.41.dev0'
1
+ __version__ = '2.1.41.dev1'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuenca_validations
3
- Version: 2.1.41.dev0
3
+ Version: 2.1.41.dev1
4
4
  Summary: Cuenca common validations
5
5
  Home-page: https://github.com/cuenca-mx/cuenca-validations
6
6
  Author: Cuenca
@@ -4,27 +4,27 @@ cuenca_validations/errors.py,sha256=OtM8EgiKqYdz9Hn66AbBO96orL1or7efkyt0vh0Zxbs,
4
4
  cuenca_validations/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  cuenca_validations/typing.py,sha256=1QCu81IbVZZpyInjyeAuO-nF36gpT5Gi4o6V9PozuOU,204
6
6
  cuenca_validations/validators.py,sha256=zXSnU5-EMbQZBD-PfFXgP4Z6G7cm7KFDWXB7Nie6WUk,2682
7
- cuenca_validations/version.py,sha256=ubLWpnM5zm6k0bIqWBrnYr4Msp7aeytwO3zKPd4HR98,28
8
- cuenca_validations/types/__init__.py,sha256=n4mpy0wj_WIElwNXeT2cN4en1yKX5NtK59eoOeKU4WM,5981
7
+ cuenca_validations/version.py,sha256=8vBTEb00cimUGbORVDKZKFmy6McE_C2hEVctFO5ycwg,28
8
+ cuenca_validations/types/__init__.py,sha256=xqc0H9XGx1Qe5FCkTgg5sAB9E0nw82M232SeZeO7Jww,6383
9
9
  cuenca_validations/types/card.py,sha256=UGzz8NTFAverUmdUKAK1oGHnOnjSNTpIRUm93vKSSGY,1295
10
- cuenca_validations/types/enums.py,sha256=xl-J2uQYhhjioJWewySHODM692P3SLUNX3sabt78aw4,20923
10
+ cuenca_validations/types/enums.py,sha256=FNh-jMeRor-HHdlLYSkeOzEJWQt_rY7gcJ_xxnJ92BA,21095
11
11
  cuenca_validations/types/files.py,sha256=2CszbwF9ytXV9suFFwyDjYG4XxY8UhCjRw3HttVXXNw,269
12
12
  cuenca_validations/types/general.py,sha256=eYFYwyx_a4_J480GYpqW3DFbZabDFcUjvLRMQbShIUc,5622
13
13
  cuenca_validations/types/helpers.py,sha256=4veeLZbugHHqZk0ezSim978VhH6Vq8XTrEhe1eE_r3A,1531
14
14
  cuenca_validations/types/identities.py,sha256=j2xxh3UYHJe6IbAwr9yNXJkebTth_-g3SUmHeiPez8M,5513
15
15
  cuenca_validations/types/morals.py,sha256=davabh5hAnFVQyM7-yCyDaT5ewXnm0cr1BtqDIwzkX8,1833
16
16
  cuenca_validations/types/queries.py,sha256=mcliWtNb6QAst1phf3KYQ24vj90cxBAn6FDlC-WAhKo,6021
17
- cuenca_validations/types/requests.py,sha256=gJD0zI6ADvO_9FiWZ2zfSjtsHr-_B2ILhd4IFtpqLkE,26550
18
- cuenca_validations-2.1.41.dev0.dist-info/licenses/LICENSE,sha256=wR76FmxBbfnQpwELkkE5iMF8sFIafEMgXLTE4N4WPTc,1063
17
+ cuenca_validations/types/requests.py,sha256=tYNEwgxOfkSBwliStTyQbEQkw4AM3YyL_eTlMytn3nI,30199
18
+ cuenca_validations-2.1.41.dev1.dist-info/licenses/LICENSE,sha256=wR76FmxBbfnQpwELkkE5iMF8sFIafEMgXLTE4N4WPTc,1063
19
19
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  tests/test_card.py,sha256=QAfRz7e11gWICPnFJZ2tiYgUsFV3C9TwzJXrDnDNXFw,1202
21
21
  tests/test_errors.py,sha256=ixiIgEuBuzfsL5p4uCFdF32XqFRtTPF6EVhGJ0keOrI,930
22
22
  tests/test_helpers.py,sha256=fx2R7y6oQO8XeQzevJe826NuHkBgvtznZicomchB3Gg,899
23
- tests/test_requests.py,sha256=9kzuWBikfhrQqH7sJ-HQN9UCJ-Fph0MPL4j6q1RfI4o,4320
23
+ tests/test_requests.py,sha256=UmGQ00D3VgFH0FveJjvW5BMsg0b7l3W0KFDGNFqY7W8,8926
24
24
  tests/test_statement.py,sha256=IOE0rRRBgBZSJv_FLaETEyn5NzzXKMNTqgjv99GX-68,1436
25
25
  tests/test_types.py,sha256=rPjqPGfPU73MSdbeJpoW_ai5TAO25hWSnQAt_H7ZIeI,23055
26
26
  tests/test_validators.py,sha256=Jjr9gWTT4cRntGiKvQK4fncqx3JkEuTWkKm1VqpRHTs,1829
27
- cuenca_validations-2.1.41.dev0.dist-info/METADATA,sha256=sMBxw8wEViatzoxLp2Q3fdHYza0uh5Tgd9rqvGU_Nxs,1600
28
- cuenca_validations-2.1.41.dev0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
29
- cuenca_validations-2.1.41.dev0.dist-info/top_level.txt,sha256=4233xdOs2HtuT-GFRjcDcwK0IwdwvWdczOtk0fPB6Gw,25
30
- cuenca_validations-2.1.41.dev0.dist-info/RECORD,,
27
+ cuenca_validations-2.1.41.dev1.dist-info/METADATA,sha256=bjW6vUS0InzJUjWld5Ifuj7j4XFcyz_6aTZu7qvU4kA,1600
28
+ cuenca_validations-2.1.41.dev1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
29
+ cuenca_validations-2.1.41.dev1.dist-info/top_level.txt,sha256=4233xdOs2HtuT-GFRjcDcwK0IwdwvWdczOtk0fPB6Gw,25
30
+ cuenca_validations-2.1.41.dev1.dist-info/RECORD,,
tests/test_requests.py CHANGED
@@ -2,8 +2,19 @@ import pytest
2
2
  from pydantic import ValidationError
3
3
  from pydantic_extra_types.phone_numbers import PhoneNumber
4
4
 
5
- from cuenca_validations.types.enums import Country, VerificationType
5
+ from cuenca_validations.types.enums import (
6
+ Country,
7
+ OperatorRole,
8
+ OperatorStatus,
9
+ VerificationType,
10
+ )
6
11
  from cuenca_validations.types.requests import (
12
+ MoralPersonRequest,
13
+ MoralPersonUpdateRequest,
14
+ OperatorLoginRequest,
15
+ OperatorLoginResponse,
16
+ OperatorRequest,
17
+ OperatorUpdateRequest,
7
18
  PasswordResetRequest,
8
19
  UpdateTransferRequest,
9
20
  UserTOSAgreementRequest,
@@ -12,6 +23,144 @@ from cuenca_validations.types.requests import (
12
23
  )
13
24
  from cuenca_validations.typing import DictStrAny
14
25
 
26
+ LEGAL_REPRESENTATIVE: DictStrAny = {
27
+ 'names': 'Juan',
28
+ 'first_surname': 'Perez',
29
+ 'job': 'Director General',
30
+ 'phone_number': '+525512345678',
31
+ 'email_address': 'juan.perez@aceros.com',
32
+ 'address': {
33
+ 'street': 'Reforma',
34
+ 'ext_number': '265',
35
+ 'postal_code_id': 'PC2ygq9j2bS9-9tsuVawzErA',
36
+ },
37
+ }
38
+
39
+ MORAL_PERSON_REQUEST: DictStrAny = {
40
+ 'legal_name': 'Aceros del Norte SA de CV',
41
+ 'rfc': 'ADN850101ABC',
42
+ 'legal_representatives': [LEGAL_REPRESENTATIVE],
43
+ }
44
+
45
+
46
+ def test_moral_person_request_valid() -> None:
47
+ req = MoralPersonRequest.model_validate(MORAL_PERSON_REQUEST)
48
+ assert req.legal_name == 'Aceros del Norte SA de CV'
49
+ assert req.rfc == 'ADN850101ABC'
50
+
51
+
52
+ def test_moral_person_request_rejects_physical_rfc() -> None:
53
+ with pytest.raises(ValidationError) as ex:
54
+ MoralPersonRequest.model_validate(
55
+ {**MORAL_PERSON_REQUEST, 'rfc': 'GOCG650418TJ1'}
56
+ )
57
+ assert 'RFC must be 12 characters for moral persons' in str(ex.value)
58
+
59
+
60
+ def test_moral_person_request_forbids_extra() -> None:
61
+ with pytest.raises(ValidationError) as ex:
62
+ MoralPersonRequest.model_validate(
63
+ {**MORAL_PERSON_REQUEST, 'foo': 'bar'}
64
+ )
65
+ assert 'Extra inputs are not permitted' in str(ex.value)
66
+
67
+
68
+ def test_moral_person_update_requires_at_least_one_param() -> None:
69
+ with pytest.raises(ValueError) as ex:
70
+ MoralPersonUpdateRequest()
71
+ assert 'At least one parameter must be provided' in str(ex.value)
72
+
73
+
74
+ def test_moral_person_update_valid() -> None:
75
+ req = MoralPersonUpdateRequest.model_validate({'legal_name': 'New name'})
76
+ assert req.legal_name == 'New name'
77
+
78
+
79
+ def test_moral_person_update_accepts_moral_rfc() -> None:
80
+ req = MoralPersonUpdateRequest.model_validate({'rfc': 'ADN850101ABC'})
81
+ assert req.rfc == 'ADN850101ABC'
82
+
83
+
84
+ def test_moral_person_update_rejects_physical_rfc() -> None:
85
+ with pytest.raises(ValidationError) as ex:
86
+ MoralPersonUpdateRequest.model_validate({'rfc': 'GOCG650418TJ1'})
87
+ assert 'RFC must be 12 characters for moral persons' in str(ex.value)
88
+
89
+
90
+ def test_operator_request_valid() -> None:
91
+ req = OperatorRequest(
92
+ name='Maria Lopez',
93
+ email='Maria+Tag@Aceros.com',
94
+ phone=PhoneNumber('+525512345678'),
95
+ company_user_id='USWqY5cvkISJOxHyEKjAKf8w',
96
+ role=OperatorRole.operator,
97
+ )
98
+ assert req.email == 'maria@aceros.com'
99
+ assert req.status == OperatorStatus.active
100
+
101
+
102
+ def test_operator_request_rejects_invalid_role() -> None:
103
+ with pytest.raises(ValidationError) as ex:
104
+ OperatorRequest.model_validate(
105
+ {
106
+ 'name': 'Maria Lopez',
107
+ 'email': 'maria@aceros.com',
108
+ 'phone': '+525512345678',
109
+ 'company_user_id': 'USWqY5cvkISJOxHyEKjAKf8w',
110
+ 'role': 'admin',
111
+ }
112
+ )
113
+ assert 'role' in str(ex.value)
114
+
115
+
116
+ def test_operator_update_requires_at_least_one_param() -> None:
117
+ with pytest.raises(ValueError) as ex:
118
+ OperatorUpdateRequest()
119
+ assert 'At least one parameter must be provided' in str(ex.value)
120
+
121
+
122
+ def test_operator_update_valid() -> None:
123
+ req = OperatorUpdateRequest.model_validate({'name': 'New name'})
124
+ assert req.name == 'New name'
125
+
126
+
127
+ def test_operator_login_request_valid() -> None:
128
+ req = OperatorLoginRequest.model_validate(
129
+ {
130
+ 'email': 'Operator+Tag@Aceros.com',
131
+ 'password': 'supersecret',
132
+ }
133
+ )
134
+ assert req.email == 'operator@aceros.com'
135
+
136
+
137
+ def test_operator_login_request_rejects_short_password() -> None:
138
+ with pytest.raises(ValidationError) as ex:
139
+ OperatorLoginRequest.model_validate(
140
+ {
141
+ 'email': 'operator@aceros.com',
142
+ 'password': 'short',
143
+ }
144
+ )
145
+ assert 'password' in str(ex.value)
146
+
147
+
148
+ def test_operator_login_request_forbids_extra() -> None:
149
+ with pytest.raises(ValidationError) as ex:
150
+ OperatorLoginRequest.model_validate(
151
+ {
152
+ 'email': 'operator@aceros.com',
153
+ 'password': 'supersecret',
154
+ 'foo': 'bar',
155
+ }
156
+ )
157
+ assert 'Extra inputs are not permitted' in str(ex.value)
158
+
159
+
160
+ def test_operator_login_response_valid() -> None:
161
+ resp = OperatorLoginResponse(session_token='SEWqY5cvkISJOxHyEKjAKf8w')
162
+ assert resp.session_token == 'SEWqY5cvkISJOxHyEKjAKf8w'
163
+
15
164
 
16
165
  @pytest.mark.parametrize('environment', ['api.stage', 'api.sandbox', 'api'])
17
166
  def test_file_cuenca_url(environment: str) -> None: