cuenca-validations 2.1.8.dev3__tar.gz → 2.1.8.dev6__tar.gz

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.
Files changed (34) hide show
  1. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/PKG-INFO +1 -1
  2. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/types/__init__.py +2 -0
  3. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/types/enums.py +16 -0
  4. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/types/identities.py +36 -2
  5. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/types/queries.py +2 -1
  6. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/types/requests.py +23 -47
  7. cuenca_validations-2.1.8.dev6/cuenca_validations/version.py +1 -0
  8. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations.egg-info/PKG-INFO +1 -1
  9. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/tests/test_types.py +33 -21
  10. cuenca_validations-2.1.8.dev3/cuenca_validations/version.py +0 -1
  11. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/LICENSE +0 -0
  12. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/README.md +0 -0
  13. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/__init__.py +0 -0
  14. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/card_bins.py +0 -0
  15. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/errors.py +0 -0
  16. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/py.typed +0 -0
  17. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/types/card.py +0 -0
  18. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/types/files.py +0 -0
  19. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/types/general.py +0 -0
  20. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/types/helpers.py +0 -0
  21. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/types/morals.py +0 -0
  22. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/typing.py +0 -0
  23. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations/validators.py +0 -0
  24. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations.egg-info/SOURCES.txt +0 -0
  25. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations.egg-info/dependency_links.txt +0 -0
  26. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations.egg-info/requires.txt +0 -0
  27. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/cuenca_validations.egg-info/top_level.txt +0 -0
  28. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/setup.cfg +0 -0
  29. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/setup.py +0 -0
  30. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/tests/__init__.py +0 -0
  31. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/tests/test_card.py +0 -0
  32. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/tests/test_errors.py +0 -0
  33. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/tests/test_helpers.py +0 -0
  34. {cuenca_validations-2.1.8.dev3 → cuenca_validations-2.1.8.dev6}/tests/test_statement.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuenca_validations
3
- Version: 2.1.8.dev3
3
+ Version: 2.1.8.dev6
4
4
  Summary: Cuenca common validations
5
5
  Home-page: https://github.com/cuenca-mx/cuenca-validations
6
6
  Author: Cuenca
@@ -56,6 +56,7 @@ __all__ = [
56
56
  'PlatformRequest',
57
57
  'PlatformType',
58
58
  'PosCapability',
59
+ 'Profession',
59
60
  'QueryParams',
60
61
  'Rfc',
61
62
  'QuestionnairesRequest',
@@ -135,6 +136,7 @@ from .enums import (
135
136
  Language,
136
137
  PlatformType,
137
138
  PosCapability,
139
+ Profession,
138
140
  SATRegimeCode,
139
141
  SavingCategory,
140
142
  ServiceProviderCategory,
@@ -688,3 +688,19 @@ class SATRegimeCode(str, Enum):
688
688
  ING_PREM = "615" # Régimen de los ingresos por obtención de premios
689
689
  AE_PLAT_TEC = "625" # Régimen de las Actividades Empresariales con ingresos a través de Plataformas Tecnológicas # noqa: E501
690
690
  RS_CONF = "626" # Régimen Simplificado de Confianza
691
+
692
+
693
+ class Profession(str, Enum):
694
+ artisticas = 'Actividades Artísticas'
695
+ agropecuario = 'Agricultura, Ganadería o Pesca'
696
+ comercio = 'Comercio'
697
+ estudiante = 'Estudiante'
698
+ empleado = 'Empleado(a/e)'
699
+ emprendimiento = 'Emprendimiento'
700
+ hogar = 'Hogar'
701
+ profesor = 'Profesor(a/e)'
702
+ profesionista = 'Profesionista'
703
+ servidor_publico = 'Servidor(a/e) Público'
704
+ sistemas = 'Sistemas y Comunicaciones'
705
+ independiente = 'Trabajador(a/e) Independiente'
706
+ oficios = 'Oficios Varios'
@@ -4,7 +4,7 @@ from typing import Annotated, Optional
4
4
  from pydantic import BaseModel, ConfigDict, Field, SecretStr, StringConstraints
5
5
  from pydantic_extra_types.phone_numbers import PhoneNumber
6
6
 
7
- from .enums import KYCFileType, VerificationStatus
7
+ from .enums import Country, KYCFileType, State, VerificationStatus
8
8
  from .general import NonEmptyStr, SerializableIPvAnyAddress
9
9
 
10
10
  Password = Annotated[
@@ -36,8 +36,42 @@ Rfc = Annotated[
36
36
  ),
37
37
  ]
38
38
 
39
+ # NOTE:
40
+ # The Address model is kept for compatibility with legacy models and data
41
+ # that expect all address fields to be optional. This allows older systems
42
+ # or stored data using Address to continue working without breaking changes.
43
+ # For new request validation, use AddressRequest, which enforces required
44
+ # fields and is intended for validating incoming data.
45
+
39
46
 
40
47
  class Address(BaseModel):
48
+ street: Optional[str] = None
49
+ ext_number: Optional[str] = None
50
+ int_number: Optional[str] = None
51
+ colonia: Optional[str] = None
52
+ postal_code: Optional[str] = None
53
+ state: Optional[State] = None
54
+ country: Optional[Country] = None
55
+ city: Optional[str] = None
56
+ full_name: Optional[str] = None
57
+ model_config = ConfigDict(
58
+ json_schema_extra={
59
+ "example": {
60
+ "street": "Reforma",
61
+ "ext_number": "265",
62
+ "int_number": "5",
63
+ "colonia": "Cuauhtémoc",
64
+ "postal_code": "06500",
65
+ "state": "DF",
66
+ "country": "MX",
67
+ "city": "Cuauhtémoc",
68
+ }
69
+ }
70
+ )
71
+
72
+
73
+ class AddressRequest(BaseModel):
74
+ # This model is mainly for request validation, enforcing required fields.
41
75
  street: NonEmptyStr
42
76
  ext_number: NonEmptyStr
43
77
  int_number: Optional[NonEmptyStr] = None
@@ -60,7 +94,7 @@ class Beneficiary(BaseModel):
60
94
  birth_date: dt.date
61
95
  phone_number: PhoneNumber
62
96
  user_relationship: str
63
- percentage: int
97
+ percentage: Annotated[int, Field(ge=1, le=100)]
64
98
  model_config = ConfigDict(
65
99
  json_schema_extra={
66
100
  "example": {
@@ -24,6 +24,7 @@ from .enums import (
24
24
  TransferNetwork,
25
25
  UserStatus,
26
26
  )
27
+ from .general import digits
27
28
  from .identities import Curp
28
29
 
29
30
  MAX_PAGE_SIZE = 100
@@ -182,4 +183,4 @@ class BankAccountValidationQuery(QueryParams):
182
183
 
183
184
 
184
185
  class PostalCodeQuery(QueryParams):
185
- postal_code: str
186
+ postal_code: digits(5, 5) # type: ignore
@@ -32,6 +32,7 @@ from ..types.enums import (
32
32
  KYCValidationSource,
33
33
  PlatformType,
34
34
  PosCapability,
35
+ Profession,
35
36
  SavingCategory,
36
37
  SessionType,
37
38
  State,
@@ -39,7 +40,6 @@ from ..types.enums import (
39
40
  TrackDataMethod,
40
41
  TransactionTokenValidationStatus,
41
42
  UserCardNotification,
42
- UserStatus,
43
43
  VerificationType,
44
44
  WalletTransactionType,
45
45
  WebhookEvent,
@@ -64,14 +64,13 @@ from .general import (
64
64
  StrictPositiveInt,
65
65
  )
66
66
  from .identities import (
67
- Address,
67
+ AddressRequest,
68
68
  Beneficiary,
69
69
  Curp,
70
70
  KYCFile,
71
71
  Password,
72
72
  PhoneNumber,
73
73
  Rfc,
74
- TOSAgreement,
75
74
  )
76
75
  from .morals import (
77
76
  AuditDetails,
@@ -410,43 +409,25 @@ class UserTOSAgreementRequest(BaseModel):
410
409
 
411
410
 
412
411
  class UserRequest(BaseModel):
413
- id: Optional[str] = Field(
414
- None, description='if you want to create with specific `id`'
415
- )
416
412
  curp: Curp = Field(
417
- description='Previously validated in `curp_validations`'
418
- )
419
- phone_number: Optional[PhoneNumber] = Field(
420
- None, description='Only if you validated previously on your side'
421
- )
422
- email_address: Optional[EmailStr] = Field(
423
- None, description='Only if you validated previously on your side'
424
- )
425
- profession: Optional[str] = None
426
- address: Address
427
- status: Optional[UserStatus] = Field(
428
- None,
429
- description='Status that the user will have when created. '
430
- 'Defined by platform',
413
+ description=(
414
+ 'Mexican government ID (18 characters). ' 'Must be pre-validated.'
415
+ )
431
416
  )
432
- required_level: Optional[int] = Field(
433
- None,
434
- ge=1,
435
- le=3,
436
- description='Maximum level a User can reach. Defined by platform',
417
+
418
+ profession: Profession = Field(description='User profession or occupation')
419
+ address: AddressRequest = Field(
420
+ description='User residential address information'
437
421
  )
438
- phone_verification_id: Optional[str] = Field(
439
- None,
440
- description='Only if you validated it previously with the '
441
- 'resource `verifications`',
422
+ phone_verification_id: str = Field(
423
+ ...,
424
+ description='ID of previously validated phone verification',
442
425
  )
443
- email_verification_id: Optional[str] = Field(
444
- None,
445
- description='Only if you validated it previously with the '
446
- 'resource `verifications`',
426
+ email_verification_id: str = Field(
427
+ ...,
428
+ description='ID of previously validated email verification',
447
429
  )
448
- terms_of_service: Optional[TOSRequest] = None
449
- signature: Optional[KYCFile] = None
430
+
450
431
  model_config = ConfigDict(
451
432
  json_schema_extra={
452
433
  'example': {
@@ -454,7 +435,7 @@ class UserRequest(BaseModel):
454
435
  'phone_number': '+525511223344',
455
436
  'email_address': 'user@example.com',
456
437
  'profession': 'engineer',
457
- 'address': Address.schema().get('example'),
438
+ 'address': AddressRequest.model_json_schema().get('example'),
458
439
  }
459
440
  },
460
441
  )
@@ -479,21 +460,16 @@ class UserRequest(BaseModel):
479
460
 
480
461
 
481
462
  class UserUpdateRequest(BaseModel):
482
- phone_number: Optional[PhoneNumber] = None
483
- email_address: Optional[EmailStr] = None
484
- profession: Optional[str] = None
463
+ profession: Optional[Profession] = None
485
464
  verification_id: Optional[str] = None
486
465
  email_verification_id: Optional[str] = None
487
466
  phone_verification_id: Optional[str] = None
488
- address: Optional[Address] = None
467
+ address: Optional[AddressRequest] = None
489
468
  beneficiaries: Optional[list[Beneficiary]] = None
490
469
  govt_id: Optional[KYCFile] = None
491
470
  proof_of_address: Optional[KYCFile] = None
492
471
  proof_of_life: Optional[KYCFile] = None
493
472
  signature: Optional[KYCFile] = None
494
- status: Optional[UserStatus] = None
495
- terms_of_service: Optional[TOSRequest] = None
496
- platform_terms_of_service: Optional[TOSAgreement] = None
497
473
  curp_document_uri: Optional[SerializableHttpUrl] = None
498
474
 
499
475
  @field_validator('beneficiaries')
@@ -501,8 +477,8 @@ class UserUpdateRequest(BaseModel):
501
477
  def beneficiary_percentage(
502
478
  cls, beneficiaries: Optional[list[Beneficiary]] = None
503
479
  ):
504
- if beneficiaries and sum(b.percentage for b in beneficiaries) > 100:
505
- raise ValueError('The total percentage is more than 100.')
480
+ if beneficiaries and sum(b.percentage for b in beneficiaries) != 100:
481
+ raise ValueError('The total percentage should be 100%')
506
482
  return beneficiaries
507
483
 
508
484
 
@@ -709,7 +685,7 @@ class PartnerRequest(BaseRequest):
709
685
  web_site: str
710
686
  phone_number: PhoneNumber
711
687
  email_address: EmailStr
712
- address: Address
688
+ address: AddressRequest
713
689
 
714
690
 
715
691
  class PartnerUpdateRequest(BaseRequest):
@@ -723,7 +699,7 @@ class PartnerUpdateRequest(BaseRequest):
723
699
  web_site: Optional[str] = None
724
700
  phone_number: Optional[PhoneNumber] = None
725
701
  email_address: Optional[EmailStr] = None
726
- address: Optional[Address] = None
702
+ address: Optional[AddressRequest] = None
727
703
  business_details: Optional[BusinessDetails] = None
728
704
  transactional_profile: Optional[TransactionalProfile] = None
729
705
  external_account: Optional[Clabe] = None
@@ -0,0 +1 @@
1
+ __version__ = '2.1.8.dev6'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuenca_validations
3
- Version: 2.1.8.dev3
3
+ Version: 2.1.8.dev6
4
4
  Summary: Cuenca common validations
5
5
  Home-page: https://github.com/cuenca-mx/cuenca-validations
6
6
  Author: Cuenca
@@ -21,6 +21,7 @@ from cuenca_validations.types import (
21
21
  )
22
22
  from cuenca_validations.types.enums import (
23
23
  EcommerceIndicator,
24
+ Profession,
24
25
  SessionType,
25
26
  State,
26
27
  )
@@ -304,12 +305,8 @@ def test_saving_update_request():
304
305
  @freeze_time('2022-01-01')
305
306
  def test_user_request():
306
307
  request = dict(
307
- id=None,
308
308
  curp='ABCD920604HDFSRN03',
309
- phone_number='+525555555555',
310
- email_address='email@email.com',
311
- profession='worker',
312
- status='active',
309
+ profession=Profession.empleado,
313
310
  address=dict(
314
311
  street='calle 1',
315
312
  ext_number='2',
@@ -318,17 +315,29 @@ def test_user_request():
318
315
  ),
319
316
  phone_verification_id='VE12345678',
320
317
  email_verification_id='VE0987654321',
321
- required_level=3,
322
- terms_of_service=None,
323
- signature=None,
324
318
  )
325
319
  assert UserRequest(**request).model_dump() == request
326
320
 
327
- # changing curp so user is underage
328
- request['curp'] = 'ABCD060604HDFSRN03'
321
+
322
+ @freeze_time('2022-01-01')
323
+ def test_user_request_underage():
324
+ request = dict(
325
+ curp='ABCD060604HDFSRN03', # underage CURP
326
+ profession='worker',
327
+ address=dict(
328
+ street='calle 1',
329
+ ext_number='2',
330
+ int_number='3',
331
+ postal_code_id='PC2ygq9j2bS9-9tsuVawzErA',
332
+ ),
333
+ phone_verification_id='VE12345678',
334
+ email_verification_id='VE0987654321',
335
+ )
336
+
329
337
  with pytest.raises(ValueError) as v:
330
338
  UserRequest(**request)
331
- assert 'User does not meet age requirement.' in str(v)
339
+
340
+ assert 'User does not meet age requirement.' in str(v)
332
341
 
333
342
 
334
343
  @freeze_time('2022-01-01')
@@ -410,7 +419,7 @@ def test_user_update_request():
410
419
  birth_date=dt.date(2020, 1, 1).isoformat(),
411
420
  phone_number='+525555555555',
412
421
  user_relationship='brother',
413
- percentage=50,
422
+ percentage=100,
414
423
  ),
415
424
  ]
416
425
  assert UserUpdateRequest(**request)
@@ -421,12 +430,21 @@ def test_user_update_request():
421
430
  birth_date=dt.date(2020, 1, 1).isoformat(),
422
431
  phone_number='+525555555555',
423
432
  user_relationship='brother',
424
- percentage=101,
433
+ percentage=50,
434
+ ),
435
+ dict(
436
+ name='José Pérez',
437
+ birth_date=dt.date(2020, 1, 2).isoformat(),
438
+ phone_number='+525544444444',
439
+ user_relationship='brother',
440
+ percentage=51,
425
441
  ),
426
442
  ]
427
- with pytest.raises(ValueError) as v:
443
+ with pytest.raises(ValidationError) as v:
428
444
  UserUpdateRequest(**request)
429
- assert 'The total percentage is more than 100.' in str(v)
445
+
446
+ assert 'The total percentage should be 100%' in str(v)
447
+ request.pop('beneficiaries')
430
448
 
431
449
  tos_request = dict(
432
450
  terms_of_service=dict(
@@ -457,12 +475,6 @@ def test_user_update_request():
457
475
  )
458
476
  UserUpdateRequest(**kyc_request)
459
477
 
460
- # chagning to invalid request
461
- tos_request['terms_of_service']['ip'] = 'not valid ip'
462
- with pytest.raises(ValueError) as v:
463
- UserUpdateRequest(**tos_request)
464
- assert 'not valid ip' in str(v.value)
465
-
466
478
 
467
479
  def test_session_request():
468
480
  data = dict(
@@ -1 +0,0 @@
1
- __version__ = '2.1.8.dev3'