cuenca-validations 2.1.7.dev6__py3-none-any.whl → 2.1.8.dev2__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.
@@ -7,6 +7,7 @@ __all__ = [
7
7
  'BalanceEntryQuery',
8
8
  'BankAccountValidationQuery',
9
9
  'BankAccountValidationRequest',
10
+ 'PostalCodeQuery',
10
11
  'BankAccountStatus',
11
12
  'BatchFileMetadata',
12
13
  'Beneficiary',
@@ -55,7 +56,6 @@ __all__ = [
55
56
  'PlatformRequest',
56
57
  'PlatformType',
57
58
  'PosCapability',
58
- 'Profession',
59
59
  'QueryParams',
60
60
  'Rfc',
61
61
  'QuestionnairesRequest',
@@ -135,7 +135,6 @@ from .enums import (
135
135
  Language,
136
136
  PlatformType,
137
137
  PosCapability,
138
- Profession,
139
138
  SATRegimeCode,
140
139
  SavingCategory,
141
140
  ServiceProviderCategory,
@@ -185,6 +184,7 @@ from .queries import (
185
184
  EventQuery,
186
185
  FileQuery,
187
186
  IdentityQuery,
187
+ PostalCodeQuery,
188
188
  QueryParams,
189
189
  SessionQuery,
190
190
  StatementQuery,
@@ -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,25 +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
+ )
422
+ email_address: Optional[EmailStr] = Field(
423
+ None, description='Only if you validated previously on your side'
421
424
  )
422
- phone_verification_id: str = Field(
423
- ...,
424
- description='ID of previously validated phone 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',
425
431
  )
426
- email_verification_id: str = Field(
427
- ...,
428
- description='ID of previously validated email verification',
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',
429
437
  )
430
-
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
431
450
  model_config = ConfigDict(
432
451
  json_schema_extra={
433
452
  'example': {
@@ -435,7 +454,7 @@ class UserRequest(BaseModel):
435
454
  'phone_number': '+525511223344',
436
455
  'email_address': 'user@example.com',
437
456
  'profession': 'engineer',
438
- 'address': Address.model_json_schema().get('example'),
457
+ 'address': Address.schema().get('example'),
439
458
  }
440
459
  },
441
460
  )
@@ -460,7 +479,9 @@ class UserRequest(BaseModel):
460
479
 
461
480
 
462
481
  class UserUpdateRequest(BaseModel):
463
- profession: Optional[Profession] = None
482
+ phone_number: Optional[PhoneNumber] = None
483
+ email_address: Optional[EmailStr] = None
484
+ profession: Optional[str] = None
464
485
  verification_id: Optional[str] = None
465
486
  email_verification_id: Optional[str] = None
466
487
  phone_verification_id: Optional[str] = None
@@ -470,6 +491,9 @@ class UserUpdateRequest(BaseModel):
470
491
  proof_of_address: Optional[KYCFile] = None
471
492
  proof_of_life: Optional[KYCFile] = None
472
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
473
497
  curp_document_uri: Optional[SerializableHttpUrl] = None
474
498
 
475
499
  @field_validator('beneficiaries')
@@ -477,8 +501,8 @@ class UserUpdateRequest(BaseModel):
477
501
  def beneficiary_percentage(
478
502
  cls, beneficiaries: Optional[list[Beneficiary]] = None
479
503
  ):
480
- if beneficiaries and sum(b.percentage for b in beneficiaries) != 100:
481
- 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.')
482
506
  return beneficiaries
483
507
 
484
508
 
@@ -1 +1 @@
1
- __version__ = '2.1.7.dev6'
1
+ __version__ = '2.1.8.dev2'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuenca_validations
3
- Version: 2.1.7.dev6
3
+ Version: 2.1.8.dev2
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=xNYuWD95tiTllFVfWM7FmDp_XLbRZ6ABQ89u9YqAhXw,27
8
- cuenca_validations/types/__init__.py,sha256=1xGfD2tlFsPlhIy-T7BZPs-vvEgDoc6ZW2iXFLbyQpM,4799
7
+ cuenca_validations/version.py,sha256=oVQ93g6Jkbs7y5hpmfGYVhe60FLyj4UUZEOwspYR52E,27
8
+ cuenca_validations/types/__init__.py,sha256=jSc37T7oFYbaJFQhES7AQSWnMsgJtc21oPXb39V9SNg,4809
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=SdLE0N-3bRNVUQRQ2IWtShjR8Iok_Ev818o9UbcraTU,20370
18
- cuenca_validations-2.1.7.dev6.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.dev2.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.dev6.dist-info/METADATA,sha256=bN1iprUBHlSHDDvjNOOo9I-l8_fZN207JIm9Jkwvp-w,1599
26
- cuenca_validations-2.1.7.dev6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
- cuenca_validations-2.1.7.dev6.dist-info/top_level.txt,sha256=4233xdOs2HtuT-GFRjcDcwK0IwdwvWdczOtk0fPB6Gw,25
28
- cuenca_validations-2.1.7.dev6.dist-info/RECORD,,
24
+ tests/test_types.py,sha256=K_YNFz0Kac3k8fJNeXuYGkL1hjXDQu084jCX6xEMEE4,19348
25
+ cuenca_validations-2.1.8.dev2.dist-info/METADATA,sha256=pS4SwCIG4fjA8nrih50MBurrHtHqtPTLhLcMNJ2rD-A,1599
26
+ cuenca_validations-2.1.8.dev2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
+ cuenca_validations-2.1.8.dev2.dist-info/top_level.txt,sha256=4233xdOs2HtuT-GFRjcDcwK0IwdwvWdczOtk0fPB6Gw,25
28
+ cuenca_validations-2.1.8.dev2.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(