cuenca-validations 0.11.32.dev5__tar.gz → 2.0.0__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 (37) hide show
  1. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/PKG-INFO +22 -10
  2. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations/__init__.py +4 -0
  3. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations/errors.py +0 -22
  4. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations/types/__init__.py +3 -8
  5. cuenca_validations-2.0.0/cuenca_validations/types/card.py +41 -0
  6. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations/types/enums.py +1 -5
  7. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations/types/files.py +1 -1
  8. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations/types/general.py +17 -38
  9. cuenca_validations-2.0.0/cuenca_validations/types/identities.py +173 -0
  10. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations/types/morals.py +5 -5
  11. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations/types/queries.py +35 -40
  12. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations/types/requests.py +234 -283
  13. cuenca_validations-2.0.0/cuenca_validations/typing.py +5 -0
  14. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations/validators.py +5 -11
  15. cuenca_validations-2.0.0/cuenca_validations/version.py +1 -0
  16. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations.egg-info/PKG-INFO +22 -10
  17. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations.egg-info/SOURCES.txt +1 -0
  18. cuenca_validations-2.0.0/cuenca_validations.egg-info/requires.txt +5 -0
  19. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/setup.py +11 -8
  20. cuenca_validations-2.0.0/tests/test_card.py +35 -0
  21. cuenca_validations-2.0.0/tests/test_errors.py +32 -0
  22. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/tests/test_statement.py +16 -13
  23. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/tests/test_types.py +70 -45
  24. cuenca_validations-0.11.32.dev5/cuenca_validations/types/card.py +0 -53
  25. cuenca_validations-0.11.32.dev5/cuenca_validations/types/identities.py +0 -180
  26. cuenca_validations-0.11.32.dev5/cuenca_validations/typing.py +0 -5
  27. cuenca_validations-0.11.32.dev5/cuenca_validations/version.py +0 -1
  28. cuenca_validations-0.11.32.dev5/cuenca_validations.egg-info/requires.txt +0 -6
  29. cuenca_validations-0.11.32.dev5/tests/test_card.py +0 -35
  30. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/LICENSE +0 -0
  31. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/README.md +0 -0
  32. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations/card_bins.py +0 -0
  33. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations/py.typed +0 -0
  34. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations.egg-info/dependency_links.txt +0 -0
  35. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/cuenca_validations.egg-info/top_level.txt +0 -0
  36. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/setup.cfg +0 -0
  37. {cuenca_validations-0.11.32.dev5 → cuenca_validations-2.0.0}/tests/__init__.py +0 -0
@@ -1,23 +1,35 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: cuenca_validations
3
- Version: 0.11.32.dev5
3
+ Version: 2.0.0
4
4
  Summary: Cuenca common validations
5
5
  Home-page: https://github.com/cuenca-mx/cuenca-validations
6
6
  Author: Cuenca
7
7
  Author-email: dev@cuenca.com
8
8
  Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.6
10
- Classifier: Programming Language :: Python :: 3.7
11
- Classifier: Programming Language :: Python :: 3.8
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
12
14
  Classifier: License :: OSI Approved :: MIT License
13
15
  Classifier: Operating System :: OS Independent
14
- Requires-Python: >=3.6
16
+ Requires-Python: >=3.9
15
17
  Description-Content-Type: text/markdown
16
18
  License-File: LICENSE
17
- Requires-Dist: clabe<1.3,>=1.2
18
- Requires-Dist: pydantic[email]<1.9,>=1.6
19
- Requires-Dist: dataclasses>=0.6; python_version < "3.7"
20
- Requires-Dist: python-dateutil>=2.7.0
19
+ Requires-Dist: clabe>=2.0.0
20
+ Requires-Dist: pydantic[email]>=2.10.0
21
+ Requires-Dist: pydantic-extra-types>=2.10.0
22
+ Requires-Dist: python-dateutil>=2.9.0
23
+ Requires-Dist: phonenumbers>=8.13.0
24
+ Dynamic: author
25
+ Dynamic: author-email
26
+ Dynamic: classifier
27
+ Dynamic: description
28
+ Dynamic: description-content-type
29
+ Dynamic: home-page
30
+ Dynamic: requires-dist
31
+ Dynamic: requires-python
32
+ Dynamic: summary
21
33
 
22
34
  # Cuenca - validations
23
35
 
@@ -5,5 +5,9 @@ __all__ = [
5
5
  'validators',
6
6
  ]
7
7
 
8
+ from pydantic_extra_types.phone_numbers import PhoneNumber
9
+
8
10
  from . import types, typing, validators
9
11
  from .version import __version__
12
+
13
+ PhoneNumber.phone_format = 'E164'
@@ -1,39 +1,17 @@
1
1
  __all__ = [
2
2
  'ApiError',
3
3
  'AuthMethodNotAllowedError',
4
- 'CardBinValidationError',
5
4
  'CuencaError',
6
5
  'ERROR_CODES',
7
6
  'InvalidOTPCodeError',
8
7
  'MissingAuthorizationHeaderError',
9
8
  'NoPasswordFoundError',
10
- 'NotDigitError',
11
9
  'TooManyAttemptsError',
12
10
  'UserLocationError',
13
11
  'UserNotLoggedInError',
14
12
  'WrongCredsError',
15
13
  ]
16
14
 
17
- from pydantic.errors import (
18
- NotDigitError as PydanticNotDigitError,
19
- PydanticValueError,
20
- )
21
-
22
-
23
- class CardBinValidationError(PydanticValueError):
24
- code = 'payment_card_number.bin'
25
- msg_template = (
26
- 'The card number contains a BIN (first six digits) that does not have'
27
- 'a known association with a Mexican bank. To add the association,'
28
- 'please file an issue:'
29
- 'https://github.com/cuenca-mx/cuenca-validations/issues'
30
- )
31
-
32
-
33
- class NotDigitError(PydanticNotDigitError):
34
- code = 'digits'
35
- msg_template = 'value is not all digits'
36
-
37
15
 
38
16
  class CuencaError(Exception):
39
17
  """Exceptions related to ApiKeys, Login, Password, etc"""
@@ -22,7 +22,7 @@ __all__ = [
22
22
  'CardTransactionType',
23
23
  'CardType',
24
24
  'Country',
25
- 'CurpField',
25
+ 'Curp',
26
26
  'CurpValidationRequest',
27
27
  'CommissionType',
28
28
  'DepositNetwork',
@@ -49,10 +49,8 @@ __all__ = [
49
49
  'KYCVerificationUpdateRequest',
50
50
  'Language',
51
51
  'LimitedWalletRequest',
52
- 'PageSize',
53
52
  'PartnerRequest',
54
53
  'PartnerUpdateRequest',
55
- 'PaymentCardNumber',
56
54
  'PhoneNumber',
57
55
  'PlatformRequest',
58
56
  'PlatformType',
@@ -73,7 +71,6 @@ __all__ = [
73
71
  'StatementQuery',
74
72
  'StrictPaymentCardNumber',
75
73
  'StrictPositiveInt',
76
- 'StrictPositiveFloat',
77
74
  'StrictTransferRequest',
78
75
  'TermsOfService',
79
76
  'TOSAgreement',
@@ -108,7 +105,7 @@ __all__ = [
108
105
  'get_state_name',
109
106
  ]
110
107
 
111
- from .card import PaymentCardNumber, StrictPaymentCardNumber
108
+ from .card import StrictPaymentCardNumber
112
109
  from .enums import (
113
110
  AuthorizerTransaction,
114
111
  BankAccountStatus,
@@ -155,7 +152,6 @@ from .files import BatchFileMetadata
155
152
  from .general import (
156
153
  JSONEncoder,
157
154
  SantizedDict,
158
- StrictPositiveFloat,
159
155
  StrictPositiveInt,
160
156
  digits,
161
157
  get_state_name,
@@ -163,7 +159,7 @@ from .general import (
163
159
  from .identities import (
164
160
  Address,
165
161
  Beneficiary,
166
- CurpField,
162
+ Curp,
167
163
  KYCFile,
168
164
  PhoneNumber,
169
165
  Rfc,
@@ -182,7 +178,6 @@ from .queries import (
182
178
  EventQuery,
183
179
  FileQuery,
184
180
  IdentityQuery,
185
- PageSize,
186
181
  QueryParams,
187
182
  SessionQuery,
188
183
  StatementQuery,
@@ -0,0 +1,41 @@
1
+ from typing import Annotated
2
+
3
+ from pydantic import Field, StringConstraints
4
+ from pydantic_core import PydanticCustomError, core_schema
5
+ from pydantic_extra_types.payment import PaymentCardNumber
6
+
7
+ from ..card_bins import CARD_BINS
8
+
9
+ ExpMonth = Annotated[int, Field(strict=True, ge=1, le=12)]
10
+ ExpYear = Annotated[int, Field(strict=True, ge=1, le=99)]
11
+ Cvv = Annotated[
12
+ str,
13
+ StringConstraints(
14
+ strip_whitespace=True,
15
+ min_length=3,
16
+ max_length=3,
17
+ pattern=r'\d{3}',
18
+ ),
19
+ ]
20
+
21
+
22
+ class StrictPaymentCardNumber(PaymentCardNumber):
23
+
24
+ @classmethod
25
+ def validate(
26
+ cls, card_number: str, validation_info: core_schema.ValidationInfo
27
+ ) -> 'StrictPaymentCardNumber':
28
+ card = super().validate(card_number, validation_info)
29
+ if card.bin not in CARD_BINS:
30
+ raise PydanticCustomError(
31
+ 'payment_card_number.bin',
32
+ 'The card number contains a BIN (first six digits) that '
33
+ 'does not have a known association with a Mexican bank. '
34
+ 'To add the association, please file an issue: '
35
+ 'https://github.com/cuenca-mx/cuenca-validations/issues',
36
+ )
37
+ return cls(card)
38
+
39
+ @property
40
+ def bank_code(self) -> str:
41
+ return CARD_BINS[self.bin]
@@ -570,6 +570,7 @@ class UserStatus(str, Enum):
570
570
  in_review = 'in_review'
571
571
  pld_blocked = 'pld_blocked'
572
572
  inactive = 'inactive'
573
+ user_blocked = 'user_blocked'
573
574
 
574
575
 
575
576
  class SessionType(str, Enum):
@@ -678,8 +679,3 @@ class SATRegimeCode(str, Enum):
678
679
  ING_PREM = "615" # Régimen de los ingresos por obtención de premios
679
680
  AE_PLAT_TEC = "625" # Régimen de las Actividades Empresariales con ingresos a través de Plataformas Tecnológicas # noqa: E501
680
681
  RS_CONF = "626" # Régimen Simplificado de Confianza
681
-
682
-
683
- class PasswordType(str, Enum):
684
- numeric = 'numeric'
685
- alphanumeric = 'alphanumeric'
@@ -6,7 +6,7 @@ from .enums import KYCFileType
6
6
 
7
7
 
8
8
  class BatchFileMetadata(BaseModel):
9
- id: Optional[str]
9
+ id: Optional[str] = None
10
10
  is_back: bool
11
11
  type: KYCFileType
12
12
  url: HttpUrl
@@ -1,14 +1,11 @@
1
1
  import json
2
- from typing import TYPE_CHECKING, Optional, Type
2
+ from typing import Annotated, Any, Optional
3
3
 
4
- from pydantic import ConstrainedFloat, ConstrainedInt, ConstrainedStr
4
+ from pydantic import Field, StringConstraints
5
5
 
6
- from ..validators import sanitize_dict, sanitize_item, validate_digits
6
+ from ..validators import sanitize_dict, sanitize_item
7
7
  from .enums import State
8
8
 
9
- if TYPE_CHECKING:
10
- from pydantic.typing import CallableGenerator
11
-
12
9
 
13
10
  class SantizedDict(dict):
14
11
  def __init__(self, *args, **kwargs):
@@ -21,43 +18,25 @@ class JSONEncoder(json.JSONEncoder):
21
18
  return sanitize_item(o, default=super().default)
22
19
 
23
20
 
24
- class StrictPositiveInt(ConstrainedInt):
25
- """
26
- - strict: ensures a float isn't passed in by accident
27
- - gt (greater than): ensures the value is strictly above 0
28
- """
29
-
30
- strict = True
31
- gt = 0
32
- le = 21_474_836_47 # Max value in DB
33
-
34
- ...
35
-
21
+ MAX_VALUE_IN_DB = 21_474_836_47
36
22
 
37
- class StrictPositiveFloat(ConstrainedFloat):
38
- """
39
- - strict: ensures an integer isn't passed in by accident
40
- - ge (greater than or equal): ensures the value is above 0
41
- """
42
-
43
- strict = True
44
- ge = 0
45
-
46
- ...
23
+ StrictPositiveInt = Annotated[
24
+ int, Field(strict=True, gt=0, le=MAX_VALUE_IN_DB)
25
+ ]
47
26
 
48
27
 
49
28
  def digits(
50
29
  min_length: Optional[int] = None, max_length: Optional[int] = None
51
- ) -> Type[str]:
52
- namespace = dict(min_length=min_length, max_length=max_length)
53
- return type('DigitsValue', (Digits,), namespace)
54
-
55
-
56
- class Digits(ConstrainedStr):
57
- @classmethod
58
- def __get_validators__(cls) -> 'CallableGenerator':
59
- yield from ConstrainedStr.__get_validators__()
60
- yield validate_digits
30
+ ) -> Annotated[Any, StringConstraints]:
31
+ return Annotated[
32
+ str,
33
+ StringConstraints(
34
+ strip_whitespace=True,
35
+ min_length=min_length,
36
+ max_length=max_length,
37
+ pattern=r'^\d+$',
38
+ ),
39
+ ]
61
40
 
62
41
 
63
42
  names_state = {
@@ -0,0 +1,173 @@
1
+ import datetime as dt
2
+ from typing import Annotated, Any, Optional
3
+
4
+ from pydantic import (
5
+ BaseModel,
6
+ ConfigDict,
7
+ Field,
8
+ IPvAnyAddress,
9
+ StringConstraints,
10
+ model_validator,
11
+ )
12
+ from pydantic_extra_types.phone_numbers import PhoneNumber
13
+
14
+ from .enums import Country, KYCFileType, State, VerificationStatus
15
+
16
+ Password = Annotated[
17
+ str,
18
+ Field(
19
+ min_length=6,
20
+ max_length=128,
21
+ description=(
22
+ 'Any str with at least 6 characters, maximum 128 characters'
23
+ ),
24
+ ),
25
+ ]
26
+
27
+ Curp = Annotated[
28
+ str,
29
+ StringConstraints(
30
+ min_length=18,
31
+ max_length=18,
32
+ pattern=r'^[A-Z]{4}[0-9]{6}[A-Z]{6}[A-Z|0-9][0-9]$',
33
+ ),
34
+ ]
35
+
36
+
37
+ Rfc = Annotated[
38
+ str,
39
+ StringConstraints(
40
+ min_length=12,
41
+ max_length=13,
42
+ ),
43
+ ]
44
+
45
+
46
+ class Address(BaseModel):
47
+ street: Optional[str] = None
48
+ ext_number: Optional[str] = None
49
+ int_number: Optional[str] = None
50
+ colonia: Optional[str] = None
51
+ postal_code: Optional[str] = None
52
+ state: Optional[State] = None
53
+ country: Optional[Country] = None
54
+ city: Optional[str] = None
55
+ full_name: Optional[str] = None
56
+ model_config = ConfigDict(
57
+ json_schema_extra={
58
+ "example": {
59
+ "street": "Reforma",
60
+ "ext_number": "265",
61
+ "int_number": "5",
62
+ "colonia": "Cuauhtémoc",
63
+ "postal_code": "06500",
64
+ "state": "DF",
65
+ "country": "MX",
66
+ "city": "Cuauhtémoc",
67
+ }
68
+ }
69
+ )
70
+
71
+ @model_validator(mode='before')
72
+ @classmethod
73
+ def full_name_complete(cls, values: dict[str, Any]) -> dict[str, Any]:
74
+ if values.get('full_name'):
75
+ return values
76
+ if not values.get('street'):
77
+ raise ValueError('required street')
78
+ if not values.get('ext_number'):
79
+ raise ValueError('required ext_number')
80
+ return values
81
+
82
+
83
+ class Beneficiary(BaseModel):
84
+ name: str
85
+ birth_date: dt.date
86
+ phone_number: PhoneNumber
87
+ user_relationship: str
88
+ percentage: int
89
+ model_config = ConfigDict(
90
+ json_schema_extra={
91
+ "example": {
92
+ "name": "Juan Perez",
93
+ "birth_date": "1907-07-06",
94
+ "phone_number": "+525500998877",
95
+ "user_relationship": "friend",
96
+ "percentage": 100,
97
+ }
98
+ }
99
+ )
100
+
101
+
102
+ class VerificationErrors(BaseModel):
103
+ identifier: str = Field(
104
+ description='Unique identifier for the step validation'
105
+ )
106
+ error: str = Field(
107
+ description='Error throwed on validation,'
108
+ ' can be StepError or SystemError in case of '
109
+ 'KYCProvider intermittence',
110
+ )
111
+ code: str = Field(description='Specific code of the failure in the step.')
112
+ message: Optional[str] = Field(None, description='Error description')
113
+ model_config = ConfigDict(
114
+ json_schema_extra={
115
+ "example": {
116
+ "identifier": "age-check",
117
+ "error": 'StepError',
118
+ "code": "underage.noDOB",
119
+ "message": "The date of birth could not be obtained",
120
+ }
121
+ },
122
+ )
123
+
124
+
125
+ class KYCFile(BaseModel):
126
+ type: KYCFileType
127
+ uri_front: str = Field(description='API uri to fetch the file')
128
+ uri_back: Optional[str] = Field(
129
+ None, description='API uri to fetch the file'
130
+ )
131
+ is_mx: bool = True
132
+ data: Optional[dict] = None
133
+ status: Optional[VerificationStatus] = Field(
134
+ None, description='The status of the file depends on KYCValidation'
135
+ )
136
+ errors: Optional[list[VerificationErrors]] = Field(
137
+ None, description='List of document errors found during kyc validation'
138
+ )
139
+ verification_id: Optional[str] = Field(
140
+ None, description='The provider identifier of the validation result'
141
+ )
142
+ attempt: Optional[int] = Field(
143
+ None,
144
+ description='The number of kyc_validation intents for this document',
145
+ )
146
+ model_config = ConfigDict(
147
+ json_schema_extra={
148
+ "example": {
149
+ "type": "ine",
150
+ "is_mx": True,
151
+ "uri_front": "/files/FILE-01",
152
+ "uri_back": "/files/FILE-02",
153
+ "data": {},
154
+ "status": "created",
155
+ "errors": [],
156
+ }
157
+ },
158
+ )
159
+
160
+
161
+ class TOSAgreement(BaseModel):
162
+ version: str
163
+ ip: IPvAnyAddress
164
+ location: Optional[str] = None
165
+ model_config = ConfigDict(
166
+ json_schema_extra={
167
+ "example": {
168
+ "version": "2022-01-01",
169
+ "ip": "192.168.0.1",
170
+ "location": "19.427224, -99.168082",
171
+ }
172
+ }
173
+ )
@@ -1,9 +1,9 @@
1
1
  import datetime as dt
2
- from typing import List, Optional
2
+ from typing import Optional
3
3
 
4
4
  from pydantic import BaseModel, EmailStr
5
5
 
6
- from cuenca_validations.types import Address, CurpField, PhoneNumber, Rfc
6
+ from cuenca_validations.types import Address, Curp, PhoneNumber, Rfc
7
7
 
8
8
 
9
9
  class BusinessDetails(BaseModel):
@@ -52,7 +52,7 @@ class PhysicalPerson(BaseModel):
52
52
  names: str
53
53
  first_surname: str
54
54
  second_surname: Optional[str] = None
55
- curp: Optional[CurpField] = None
55
+ curp: Optional[Curp] = None
56
56
  rfc: Optional[Rfc] = None
57
57
 
58
58
 
@@ -70,5 +70,5 @@ class ShareholderPhysical(PhysicalPerson):
70
70
  class Shareholder(BaseModel):
71
71
  name: str
72
72
  percentage: int
73
- shareholders: List[ShareholderPhysical]
74
- legal_representatives: List[LegalRepresentative]
73
+ shareholders: list[ShareholderPhysical]
74
+ legal_representatives: list[LegalRepresentative]
@@ -1,8 +1,14 @@
1
1
  import datetime as dt
2
- from typing import Optional
3
-
4
- from pydantic import BaseModel, EmailStr, Extra, validator
5
- from pydantic.types import ConstrainedInt, PositiveInt
2
+ from typing import Annotated, Optional
3
+
4
+ from pydantic import (
5
+ BaseModel,
6
+ ConfigDict,
7
+ EmailStr,
8
+ Field,
9
+ PositiveInt,
10
+ field_validator,
11
+ )
6
12
 
7
13
  from ..typing import DictStrAny
8
14
  from ..validators import sanitize_dict
@@ -18,19 +24,16 @@ from .enums import (
18
24
  TransferNetwork,
19
25
  UserStatus,
20
26
  )
21
- from .identities import CurpField
27
+ from .identities import Curp
22
28
 
23
29
  MAX_PAGE_SIZE = 100
24
30
 
25
31
 
26
- class PageSize(ConstrainedInt):
27
- gt = 0
28
- le = MAX_PAGE_SIZE
29
-
30
-
31
32
  class QueryParams(BaseModel):
32
33
  count: bool = False
33
- page_size: PageSize = PageSize(MAX_PAGE_SIZE)
34
+ page_size: Annotated[
35
+ int, Field(gt=0, le=MAX_PAGE_SIZE, default=MAX_PAGE_SIZE)
36
+ ]
34
37
  limit: Optional[PositiveInt] = None
35
38
  user_id: Optional[str] = None
36
39
  created_before: Optional[dt.datetime] = None
@@ -38,9 +41,9 @@ class QueryParams(BaseModel):
38
41
  related_transaction: Optional[str] = None
39
42
  platform_id: Optional[str] = None
40
43
 
41
- class Config:
42
- extra = Extra.forbid # raise ValidationError if there are extra fields
43
- fields = {
44
+ model_config = ConfigDict(
45
+ extra="forbid",
46
+ json_schema_extra={
44
47
  'count': {'description': 'Set `true` value to get only a counter'},
45
48
  'page_size': {'description': 'Number of items per page'},
46
49
  'limit': {'description': 'Limit of items to query'},
@@ -54,12 +57,13 @@ class QueryParams(BaseModel):
54
57
  'or greater than this value, this field represents the min '
55
58
  'creation date.'
56
59
  },
57
- }
60
+ },
61
+ )
58
62
 
59
- def dict(self, *args, **kwargs) -> DictStrAny:
63
+ def model_dump(self, *args, **kwargs) -> DictStrAny:
60
64
  kwargs.setdefault('exclude_none', True)
61
65
  kwargs.setdefault('exclude_unset', True)
62
- d = super().dict(*args, **kwargs)
66
+ d = super().model_dump(*args, **kwargs)
63
67
  if self.count:
64
68
  d['count'] = 1
65
69
  sanitize_dict(d)
@@ -93,43 +97,34 @@ class CardTransactionQuery(TransactionQuery):
93
97
  class ApiKeyQuery(QueryParams):
94
98
  active: Optional[bool] = None
95
99
 
96
- class Config(QueryParams.Config):
97
- fields = {
98
- **QueryParams.Config.fields,
99
- 'active': {
100
- 'description': 'Set `true` value to fetch active keys or '
101
- '`false` to fetch deactivated keys'
102
- },
103
- }
100
+ model_config = ConfigDict(
101
+ extra="forbid",
102
+ json_schema_extra={
103
+ 'properties': {
104
+ 'active': {
105
+ 'description': 'Set `true` value to fetch active keys or '
106
+ '`false` to fetch deactivated keys'
107
+ },
108
+ }
109
+ },
110
+ )
104
111
 
105
112
 
106
113
  class CardQuery(QueryParams):
107
114
  number: Optional[str] = None
108
- exp_month: Optional[int] = None
109
- exp_year: Optional[int] = None
110
- cvv: Optional[str] = None
111
- cvv2: Optional[str] = None
112
- icvv: Optional[str] = None
113
- pin_block: Optional[str] = None
114
115
  issuer: Optional[CardIssuer] = None
115
116
  funding_type: Optional[CardFundingType] = None
116
117
  status: Optional[CardStatus] = None
117
118
  type: Optional[CardType] = None
118
119
 
119
- @validator('exp_month', 'exp_year', 'cvv2', 'cvv')
120
- def query_by_exp_cvv_if_number_set(cls, v, values):
121
- if not values['number']:
122
- raise ValueError('Number must be set to query by exp or cvv')
123
- return v
124
-
125
120
 
126
121
  class StatementQuery(QueryParams):
127
122
  year: int
128
123
  month: int
129
124
 
130
- @validator('month')
125
+ @field_validator('month')
131
126
  def validate_year_month(cls, month, values):
132
- year = values['year']
127
+ year = values.data['year']
133
128
  month_now = dt.date.today().replace(day=1)
134
129
  month_set = dt.date(year, month, 1)
135
130
  if month_set >= month_now:
@@ -163,7 +158,7 @@ class UserQuery(QueryParams):
163
158
 
164
159
 
165
160
  class IdentityQuery(QueryParams):
166
- curp: Optional[CurpField] = None
161
+ curp: Optional[Curp] = None
167
162
  rfc: Optional[str] = None
168
163
  status: Optional[UserStatus] = None
169
164