cuenca-validations 2.1.7.dev5__py3-none-any.whl → 2.1.8.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.
@@ -55,7 +55,6 @@ __all__ = [
55
55
  'PlatformRequest',
56
56
  'PlatformType',
57
57
  'PosCapability',
58
- 'Profession',
59
58
  'QueryParams',
60
59
  'Rfc',
61
60
  'QuestionnairesRequest',
@@ -135,7 +134,6 @@ from .enums import (
135
134
  Language,
136
135
  PlatformType,
137
136
  PosCapability,
138
- Profession,
139
137
  SATRegimeCode,
140
138
  SavingCategory,
141
139
  ServiceProviderCategory,
@@ -688,19 +688,3 @@ 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
- arts = 'arts'
695
- agriculture = 'agriculture'
696
- commerce = 'commerce'
697
- student = 'student'
698
- employee = 'employee'
699
- entrepreneur = 'entrepreneur'
700
- homemaker = 'homemaker'
701
- teacher = 'teacher'
702
- professional = 'professional'
703
- public_servant = 'public_servant'
704
- it_communications = 'it_communications'
705
- freelancer = 'freelancer'
706
- skilled_trades = 'skilled_trades'
@@ -1,11 +1,18 @@
1
1
  import datetime as dt
2
- from typing import Annotated, Optional
2
+ from typing import Annotated, Any, Optional
3
3
 
4
- from pydantic import BaseModel, ConfigDict, Field, SecretStr, StringConstraints
4
+ from pydantic import (
5
+ BaseModel,
6
+ ConfigDict,
7
+ Field,
8
+ SecretStr,
9
+ StringConstraints,
10
+ model_validator,
11
+ )
5
12
  from pydantic_extra_types.phone_numbers import PhoneNumber
6
13
 
7
14
  from .enums import Country, KYCFileType, State, VerificationStatus
8
- from .general import NonEmptyStr, SerializableIPvAnyAddress
15
+ from .general import SerializableIPvAnyAddress
9
16
 
10
17
  Password = Annotated[
11
18
  SecretStr,
@@ -38,15 +45,15 @@ Rfc = Annotated[
38
45
 
39
46
 
40
47
  class Address(BaseModel):
41
- street: NonEmptyStr
42
- ext_number: NonEmptyStr
43
- int_number: Optional[NonEmptyStr] = None
44
- colonia: NonEmptyStr
45
- postal_code: NonEmptyStr
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
46
53
  state: Optional[State] = None
47
- country: Country
48
- city: NonEmptyStr
49
-
54
+ country: Optional[Country] = None
55
+ city: Optional[str] = None
56
+ full_name: Optional[str] = None
50
57
  model_config = ConfigDict(
51
58
  json_schema_extra={
52
59
  "example": {
@@ -62,13 +69,24 @@ class Address(BaseModel):
62
69
  }
63
70
  )
64
71
 
72
+ @model_validator(mode='before')
73
+ @classmethod
74
+ def full_name_complete(cls, values: dict[str, Any]) -> dict[str, Any]:
75
+ if values.get('full_name'):
76
+ return values
77
+ if not values.get('street'):
78
+ raise ValueError('required street')
79
+ if not values.get('ext_number'):
80
+ raise ValueError('required ext_number')
81
+ return values
82
+
65
83
 
66
84
  class Beneficiary(BaseModel):
67
85
  name: str
68
86
  birth_date: dt.date
69
87
  phone_number: PhoneNumber
70
88
  user_relationship: str
71
- percentage: Annotated[int, Field(ge=1, le=100)]
89
+ percentage: int
72
90
  model_config = ConfigDict(
73
91
  json_schema_extra={
74
92
  "example": {
@@ -179,3 +179,7 @@ class FileQuery(QueryParams):
179
179
  class BankAccountValidationQuery(QueryParams):
180
180
  account_number: Optional[str] = None
181
181
  status: Optional[BankAccountStatus] = None
182
+
183
+
184
+ class PostalCodeQuery(QueryParams):
185
+ postal_code: str
@@ -32,7 +32,6 @@ from ..types.enums import (
32
32
  KYCValidationSource,
33
33
  PlatformType,
34
34
  PosCapability,
35
- Profession,
36
35
  SavingCategory,
37
36
  SessionType,
38
37
  State,
@@ -40,6 +39,7 @@ from ..types.enums import (
40
39
  TrackDataMethod,
41
40
  TransactionTokenValidationStatus,
42
41
  UserCardNotification,
42
+ UserStatus,
43
43
  VerificationType,
44
44
  WalletTransactionType,
45
45
  WebhookEvent,
@@ -71,6 +71,7 @@ from .identities import (
71
71
  Password,
72
72
  PhoneNumber,
73
73
  Rfc,
74
+ TOSAgreement,
74
75
  )
75
76
  from .morals import (
76
77
  AuditDetails,
@@ -409,28 +410,43 @@ class UserTOSAgreementRequest(BaseModel):
409
410
 
410
411
 
411
412
  class UserRequest(BaseModel):
413
+ id: Optional[str] = Field(
414
+ None, description='if you want to create with specific `id`'
415
+ )
412
416
  curp: Curp = Field(
413
- description=(
414
- 'Mexican government ID (18 characters). ' 'Must be pre-validated.'
415
- )
417
+ description='Previously validated in `curp_validations`'
416
418
  )
417
-
418
- profession: Profession = Field(description='User profession or occupation')
419
- address: Address = Field(
420
- description='User residential address information'
419
+ phone_number: Optional[PhoneNumber] = Field(
420
+ None, description='Only if you validated previously on your side'
421
421
  )
422
- phone_verification_id: str = Field(
423
- ...,
424
- description='ID of previously validated phone verification',
422
+ email_address: Optional[EmailStr] = Field(
423
+ None, description='Only if you validated previously on your side'
425
424
  )
426
- email_verification_id: str = Field(
427
- ...,
428
- description='ID of previously validated email verification',
425
+ profession: Optional[str] = None
426
+ address: Optional[Address] = None
427
+ status: Optional[UserStatus] = Field(
428
+ None,
429
+ description='Status that the user will have when created. '
430
+ 'Defined by platform',
429
431
  )
430
- tos_agreement: UserTOSAgreementRequest = Field(
431
- ..., description='TOS agreement for the user'
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',
432
437
  )
433
-
438
+ phone_verification_id: Optional[str] = Field(
439
+ None,
440
+ description='Only if you validated it previously with the '
441
+ 'resource `verifications`',
442
+ )
443
+ email_verification_id: Optional[str] = Field(
444
+ None,
445
+ description='Only if you validated it previously with the '
446
+ 'resource `verifications`',
447
+ )
448
+ terms_of_service: Optional[TOSRequest] = None
449
+ signature: Optional[KYCFile] = None
434
450
  model_config = ConfigDict(
435
451
  json_schema_extra={
436
452
  'example': {
@@ -438,7 +454,7 @@ class UserRequest(BaseModel):
438
454
  'phone_number': '+525511223344',
439
455
  'email_address': 'user@example.com',
440
456
  'profession': 'engineer',
441
- 'address': Address.model_json_schema().get('example'),
457
+ 'address': Address.schema().get('example'),
442
458
  }
443
459
  },
444
460
  )
@@ -463,7 +479,9 @@ class UserRequest(BaseModel):
463
479
 
464
480
 
465
481
  class UserUpdateRequest(BaseModel):
466
- profession: Optional[Profession] = None
482
+ phone_number: Optional[PhoneNumber] = None
483
+ email_address: Optional[EmailStr] = None
484
+ profession: Optional[str] = None
467
485
  verification_id: Optional[str] = None
468
486
  email_verification_id: Optional[str] = None
469
487
  phone_verification_id: Optional[str] = None
@@ -473,6 +491,9 @@ class UserUpdateRequest(BaseModel):
473
491
  proof_of_address: Optional[KYCFile] = None
474
492
  proof_of_life: Optional[KYCFile] = None
475
493
  signature: Optional[KYCFile] = None
494
+ status: Optional[UserStatus] = None
495
+ terms_of_service: Optional[TOSRequest] = None
496
+ platform_terms_of_service: Optional[TOSAgreement] = None
476
497
  curp_document_uri: Optional[SerializableHttpUrl] = None
477
498
 
478
499
  @field_validator('beneficiaries')
@@ -480,8 +501,8 @@ class UserUpdateRequest(BaseModel):
480
501
  def beneficiary_percentage(
481
502
  cls, beneficiaries: Optional[list[Beneficiary]] = None
482
503
  ):
483
- if beneficiaries and sum(b.percentage for b in beneficiaries) != 100:
484
- raise ValueError('The total percentage should be 100%')
504
+ if beneficiaries and sum(b.percentage for b in beneficiaries) > 100:
505
+ raise ValueError('The total percentage is more than 100.')
485
506
  return beneficiaries
486
507
 
487
508
 
@@ -1 +1 @@
1
- __version__ = '2.1.7.dev5'
1
+ __version__ = '2.1.8.dev1'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuenca_validations
3
- Version: 2.1.7.dev5
3
+ Version: 2.1.8.dev1
4
4
  Summary: Cuenca common validations
5
5
  Home-page: https://github.com/cuenca-mx/cuenca-validations
6
6
  Author: Cuenca
@@ -4,25 +4,25 @@ 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=wzwLnJ4wHggZvqp3mearbFkzvDERGeTNvJkuofQnuMc,1484
7
- cuenca_validations/version.py,sha256=t_ZE4qAissTLU3mpMGxmvUi0gveTc5dOA6cEKwJL51c,27
8
- cuenca_validations/types/__init__.py,sha256=1xGfD2tlFsPlhIy-T7BZPs-vvEgDoc6ZW2iXFLbyQpM,4799
7
+ cuenca_validations/version.py,sha256=Y47Jxl-J4Lho25cNExZ2X-dcgS1p0plK6pTd8Tsj-Ik,27
8
+ cuenca_validations/types/__init__.py,sha256=UNb7auBcD0qp-x9TX7TNNj8IKo7uaI7xHaf9PYmaziY,4765
9
9
  cuenca_validations/types/card.py,sha256=UGzz8NTFAverUmdUKAK1oGHnOnjSNTpIRUm93vKSSGY,1295
10
- cuenca_validations/types/enums.py,sha256=o2XmufNIBhx88KDVARnpoXqLkyXVP89XMW0DDPDH9NI,19424
10
+ cuenca_validations/types/enums.py,sha256=Vm9HGf83jPkEp8iZ-KGtKGWQGx5q-cIUuQj32y_QcvU,18997
11
11
  cuenca_validations/types/files.py,sha256=2CszbwF9ytXV9suFFwyDjYG4XxY8UhCjRw3HttVXXNw,269
12
12
  cuenca_validations/types/general.py,sha256=vJmQBD_Iv_hsxD8x3_Bip-NlYAiE2rmXSPQKj4kTtto,2621
13
13
  cuenca_validations/types/helpers.py,sha256=6rHUhwoQ7jJZtGcW3LX-W5ZDl42PWE1RoBpGme7KCkk,610
14
- cuenca_validations/types/identities.py,sha256=FBpWUwRfy99jBr2Zhuv9gArnnUe12o3A2kbs25i9nwo,4470
14
+ cuenca_validations/types/identities.py,sha256=-TWRDRpmIaCI4nE4hVy1FQtQtlxuErtmEtzJW1FBzec,4956
15
15
  cuenca_validations/types/morals.py,sha256=m8kAedevmwfSPTA9GYe03l7pkgipynwYgKfejyVtnuI,1813
16
- cuenca_validations/types/queries.py,sha256=KCRx0sPzWDtDDbZysmFGVgANgfqil17EITWaG7tGQ-A,4700
17
- cuenca_validations/types/requests.py,sha256=-47_fJrsTpZwYVSYUpNsLhMwLhncyv6dxAeVpxAWqo8,20482
18
- cuenca_validations-2.1.7.dev5.dist-info/licenses/LICENSE,sha256=wR76FmxBbfnQpwELkkE5iMF8sFIafEMgXLTE4N4WPTc,1063
16
+ cuenca_validations/types/queries.py,sha256=fDXm2LIpM4IrkaANTDTr3_tqqAK6nvs5-CGCTjjK9Oo,4759
17
+ cuenca_validations/types/requests.py,sha256=6aEoMe7e-FPJs3_ouECV_Y6ZtyyJVyXfTed8wHtx_xw,21338
18
+ cuenca_validations-2.1.8.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=ubzpi1UXCryLQdgsT_Zm2IX-XE_4L0dnHnhLwH06xK8,748
23
23
  tests/test_statement.py,sha256=IOE0rRRBgBZSJv_FLaETEyn5NzzXKMNTqgjv99GX-68,1436
24
- tests/test_types.py,sha256=TimSUqe0YX83qIzo7p6XUy8YYuf19a7M3iPkVKw0iMg,19203
25
- cuenca_validations-2.1.7.dev5.dist-info/METADATA,sha256=uhhQmpUuuRbj77vU8ggcDAWZ6R0hrf61EDRXaW0pOjs,1599
26
- cuenca_validations-2.1.7.dev5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
- cuenca_validations-2.1.7.dev5.dist-info/top_level.txt,sha256=4233xdOs2HtuT-GFRjcDcwK0IwdwvWdczOtk0fPB6Gw,25
28
- cuenca_validations-2.1.7.dev5.dist-info/RECORD,,
24
+ tests/test_types.py,sha256=K_YNFz0Kac3k8fJNeXuYGkL1hjXDQu084jCX6xEMEE4,19348
25
+ cuenca_validations-2.1.8.dev1.dist-info/METADATA,sha256=5_-q-ovKXKdzB25IF-4aTgJyQunRQtpvE3eA7N_w_rc,1599
26
+ cuenca_validations-2.1.8.dev1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
+ cuenca_validations-2.1.8.dev1.dist-info/top_level.txt,sha256=4233xdOs2HtuT-GFRjcDcwK0IwdwvWdczOtk0fPB6Gw,25
28
+ cuenca_validations-2.1.8.dev1.dist-info/RECORD,,
tests/test_types.py CHANGED
@@ -10,6 +10,7 @@ from pydantic import AfterValidator, BaseModel, SecretStr, ValidationError
10
10
  from pydantic.fields import FieldInfo
11
11
 
12
12
  from cuenca_validations.types import (
13
+ Address,
13
14
  CardQuery,
14
15
  JSONEncoder,
15
16
  QueryParams,
@@ -22,7 +23,6 @@ from cuenca_validations.types import (
22
23
  from cuenca_validations.types.enums import (
23
24
  Country,
24
25
  EcommerceIndicator,
25
- Profession,
26
26
  SessionType,
27
27
  State,
28
28
  )
@@ -303,54 +303,60 @@ def test_saving_update_request():
303
303
  SavingUpdateRequest(**data)
304
304
 
305
305
 
306
- @freeze_time('2022-01-01')
307
- def test_user_request():
308
- request = dict(
309
- curp='ABCD920604HDFSRN03',
310
- profession=Profession.employee,
311
- address=dict(
312
- street='calle 1',
313
- ext_number='2',
314
- int_number='3',
315
- colonia='Juarez',
316
- postal_code='09900',
317
- state=State.DF,
318
- country=Country.MX,
319
- city='Obrera',
320
- ),
321
- phone_verification_id='VE12345678',
322
- email_verification_id='VE0987654321',
323
- tos_agreement=dict(
324
- tos_id='TOS123',
325
- location=dict(latitude=19.4326, longitude=-99.1332),
326
- ),
306
+ def test_address_validation():
307
+ data = dict(
308
+ full_name='Varsovia 36, Col Cuahutemoc',
327
309
  )
328
- assert UserRequest(**request).model_dump() == request
310
+ assert Address(**data)
311
+ with pytest.raises(ValueError) as v:
312
+ Address(**dict())
313
+ assert 'required street' in str(v)
314
+ data = dict(street='somestreet')
315
+ with pytest.raises(ValueError) as v:
316
+ Address(**data)
317
+ assert 'required ext_number' in str(v)
318
+ data = dict(
319
+ street='varsovia',
320
+ ext_number='36',
321
+ state=State.DF,
322
+ country=Country.MX,
323
+ )
324
+ assert Address(**data)
329
325
 
330
326
 
331
327
  @freeze_time('2022-01-01')
332
- def test_user_request_underage():
328
+ def test_user_request():
333
329
  request = dict(
334
- curp='ABCD060604HDFSRN03', # underage CURP
330
+ id=None,
331
+ curp='ABCD920604HDFSRN03',
332
+ phone_number='+525555555555',
333
+ email_address='email@email.com',
335
334
  profession='worker',
335
+ status='active',
336
336
  address=dict(
337
337
  street='calle 1',
338
338
  ext_number='2',
339
339
  int_number='3',
340
340
  colonia='Juarez',
341
341
  postal_code='09900',
342
- state=State.DF,
342
+ state=State.DF.value,
343
343
  country=Country.MX,
344
344
  city='Obrera',
345
+ full_name=None,
345
346
  ),
346
347
  phone_verification_id='VE12345678',
347
348
  email_verification_id='VE0987654321',
349
+ required_level=3,
350
+ terms_of_service=None,
351
+ signature=None,
348
352
  )
353
+ assert UserRequest(**request).model_dump() == request
349
354
 
355
+ # changing curp so user is underage
356
+ request['curp'] = 'ABCD060604HDFSRN03'
350
357
  with pytest.raises(ValueError) as v:
351
358
  UserRequest(**request)
352
-
353
- assert 'User does not meet age requirement.' in str(v)
359
+ assert 'User does not meet age requirement.' in str(v)
354
360
 
355
361
 
356
362
  @freeze_time('2022-01-01')
@@ -432,7 +438,7 @@ def test_user_update_request():
432
438
  birth_date=dt.date(2020, 1, 1).isoformat(),
433
439
  phone_number='+525555555555',
434
440
  user_relationship='brother',
435
- percentage=100,
441
+ percentage=50,
436
442
  ),
437
443
  ]
438
444
  assert UserUpdateRequest(**request)
@@ -443,21 +449,12 @@ def test_user_update_request():
443
449
  birth_date=dt.date(2020, 1, 1).isoformat(),
444
450
  phone_number='+525555555555',
445
451
  user_relationship='brother',
446
- percentage=50,
447
- ),
448
- dict(
449
- name='José Pérez',
450
- birth_date=dt.date(2020, 1, 2).isoformat(),
451
- phone_number='+525544444444',
452
- user_relationship='brother',
453
- percentage=51,
452
+ percentage=101,
454
453
  ),
455
454
  ]
456
- with pytest.raises(ValidationError) as v:
455
+ with pytest.raises(ValueError) as v:
457
456
  UserUpdateRequest(**request)
458
-
459
- assert 'The total percentage should be 100%' in str(v)
460
- request.pop('beneficiaries')
457
+ assert 'The total percentage is more than 100.' in str(v)
461
458
 
462
459
  tos_request = dict(
463
460
  terms_of_service=dict(
@@ -488,6 +485,12 @@ def test_user_update_request():
488
485
  )
489
486
  UserUpdateRequest(**kyc_request)
490
487
 
488
+ # chagning to invalid request
489
+ tos_request['terms_of_service']['ip'] = 'not valid ip'
490
+ with pytest.raises(ValueError) as v:
491
+ UserUpdateRequest(**tos_request)
492
+ assert 'not valid ip' in str(v.value)
493
+
491
494
 
492
495
  def test_session_request():
493
496
  data = dict(