cuenca-validations 2.1.14.dev105__tar.gz → 2.1.16__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.
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/PKG-INFO +1 -1
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/__init__.py +18 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/enums.py +44 -13
- cuenca_validations-2.1.16/cuenca_validations/types/general.py +194 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/requests.py +29 -3
- cuenca_validations-2.1.16/cuenca_validations/version.py +1 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations.egg-info/PKG-INFO +1 -1
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/tests/test_types.py +63 -2
- cuenca_validations-2.1.14.dev105/cuenca_validations/types/general.py +0 -107
- cuenca_validations-2.1.14.dev105/cuenca_validations/version.py +0 -1
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/LICENSE +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/README.md +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/__init__.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/card_bins.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/errors.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/py.typed +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/card.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/files.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/helpers.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/identities.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/morals.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/queries.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/typing.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/validators.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations.egg-info/SOURCES.txt +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations.egg-info/dependency_links.txt +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations.egg-info/requires.txt +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations.egg-info/top_level.txt +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/setup.cfg +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/setup.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/tests/__init__.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/tests/test_card.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/tests/test_errors.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/tests/test_helpers.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/tests/test_requests.py +0 -0
- {cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/tests/test_statement.py +0 -0
{cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/__init__.py
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
__all__ = [
|
|
2
|
+
'AccountUseType',
|
|
2
3
|
'AccountQuery',
|
|
3
4
|
'Address',
|
|
4
5
|
'ApiKeyQuery',
|
|
@@ -43,6 +44,7 @@ __all__ = [
|
|
|
43
44
|
'FileRequest',
|
|
44
45
|
'FileUploadRequest',
|
|
45
46
|
'Gender',
|
|
47
|
+
'IncomeType',
|
|
46
48
|
'IssuerNetwork',
|
|
47
49
|
'IdentityQuery',
|
|
48
50
|
'JSONEncoder',
|
|
@@ -52,6 +54,8 @@ __all__ = [
|
|
|
52
54
|
'KYCValidationSource',
|
|
53
55
|
'Language',
|
|
54
56
|
'LimitedWalletRequest',
|
|
57
|
+
'MonthlyMovementsType',
|
|
58
|
+
'MonthlySpendingType',
|
|
55
59
|
'PartnerRequest',
|
|
56
60
|
'PartnerUpdateRequest',
|
|
57
61
|
'PhoneNumber',
|
|
@@ -107,12 +111,18 @@ __all__ = [
|
|
|
107
111
|
'WebhookEvent',
|
|
108
112
|
'digits',
|
|
109
113
|
'get_state_name',
|
|
114
|
+
'get_profession_name',
|
|
115
|
+
'get_income_type_name',
|
|
116
|
+
'get_account_use_type_name',
|
|
117
|
+
'get_monthly_movements_type_name',
|
|
118
|
+
'get_monthly_spending_type_name',
|
|
110
119
|
'uuid_field',
|
|
111
120
|
'LogConfig',
|
|
112
121
|
]
|
|
113
122
|
|
|
114
123
|
from .card import StrictPaymentCardNumber
|
|
115
124
|
from .enums import (
|
|
125
|
+
AccountUseType,
|
|
116
126
|
AuthorizerTransaction,
|
|
117
127
|
BankAccountStatus,
|
|
118
128
|
CardErrorType,
|
|
@@ -132,10 +142,13 @@ from .enums import (
|
|
|
132
142
|
FileExtension,
|
|
133
143
|
FileFormat,
|
|
134
144
|
Gender,
|
|
145
|
+
IncomeType,
|
|
135
146
|
IssuerNetwork,
|
|
136
147
|
KYCFileType,
|
|
137
148
|
KYCValidationSource,
|
|
138
149
|
Language,
|
|
150
|
+
MonthlyMovementsType,
|
|
151
|
+
MonthlySpendingType,
|
|
139
152
|
PlatformType,
|
|
140
153
|
PosCapability,
|
|
141
154
|
Profession,
|
|
@@ -163,6 +176,11 @@ from .general import (
|
|
|
163
176
|
SantizedDict,
|
|
164
177
|
StrictPositiveInt,
|
|
165
178
|
digits,
|
|
179
|
+
get_account_use_type_name,
|
|
180
|
+
get_income_type_name,
|
|
181
|
+
get_monthly_movements_type_name,
|
|
182
|
+
get_monthly_spending_type_name,
|
|
183
|
+
get_profession_name,
|
|
166
184
|
get_state_name,
|
|
167
185
|
)
|
|
168
186
|
from .helpers import uuid_field
|
{cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/enums.py
RENAMED
|
@@ -691,16 +691,47 @@ class SATRegimeCode(str, Enum):
|
|
|
691
691
|
|
|
692
692
|
|
|
693
693
|
class Profession(str, Enum):
|
|
694
|
-
artisticas = '
|
|
695
|
-
agropecuario = '
|
|
696
|
-
comercio = '
|
|
697
|
-
estudiante = '
|
|
698
|
-
empleado = '
|
|
699
|
-
emprendimiento = '
|
|
700
|
-
hogar = '
|
|
701
|
-
profesor = '
|
|
702
|
-
profesionista = '
|
|
703
|
-
servidor_publico = '
|
|
704
|
-
sistemas = '
|
|
705
|
-
independiente = '
|
|
706
|
-
oficios = '
|
|
694
|
+
artisticas = 'artisticas'
|
|
695
|
+
agropecuario = 'agropecuario'
|
|
696
|
+
comercio = 'comercio'
|
|
697
|
+
estudiante = 'estudiante'
|
|
698
|
+
empleado = 'empleado'
|
|
699
|
+
emprendimiento = 'emprendimiento'
|
|
700
|
+
hogar = 'hogar'
|
|
701
|
+
profesor = 'profesor'
|
|
702
|
+
profesionista = 'profesionista'
|
|
703
|
+
servidor_publico = 'servidor_publico'
|
|
704
|
+
sistemas = 'sistemas'
|
|
705
|
+
independiente = 'independiente'
|
|
706
|
+
oficios = 'oficios'
|
|
707
|
+
otro = 'otro'
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
class AccountUseType(str, Enum):
|
|
711
|
+
personal_expenses = 'personal_expenses'
|
|
712
|
+
business_expenses = 'business_expenses'
|
|
713
|
+
payment_of_goods_or_services = 'payment_of_goods_or_services'
|
|
714
|
+
send_or_receive_transfers = 'send_or_receive_transfers'
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
class MonthlyMovementsType(str, Enum):
|
|
718
|
+
between_1_and_20 = 'between_1_and_20'
|
|
719
|
+
between_20_and_40 = 'between_20_and_40'
|
|
720
|
+
between_40_and_60 = 'between_40_and_60'
|
|
721
|
+
more_than_60 = 'more_than_60'
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
class MonthlySpendingType(str, Enum):
|
|
725
|
+
less_than_1k = 'less_than_1k'
|
|
726
|
+
between_1k_and_10k = 'between_1k_and_10k'
|
|
727
|
+
between_10k_and_20k = 'between_10k_and_20k'
|
|
728
|
+
between_20k_and_50k = 'between_20k_and_50k'
|
|
729
|
+
between_50k_and_100k = 'between_50k_and_100k'
|
|
730
|
+
more_than_100k = 'more_than_100k'
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
class IncomeType(str, Enum):
|
|
734
|
+
salary = 'salary'
|
|
735
|
+
freelance = 'freelance'
|
|
736
|
+
support_from_third_party = 'support_from_third_party'
|
|
737
|
+
variable = 'variable'
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Annotated, Any, Optional
|
|
4
|
+
|
|
5
|
+
from pydantic import (
|
|
6
|
+
AnyUrl,
|
|
7
|
+
Field,
|
|
8
|
+
HttpUrl,
|
|
9
|
+
IPvAnyAddress,
|
|
10
|
+
PlainSerializer,
|
|
11
|
+
StringConstraints,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from ..validators import sanitize_dict, sanitize_item
|
|
15
|
+
from .enums import (
|
|
16
|
+
AccountUseType,
|
|
17
|
+
IncomeType,
|
|
18
|
+
MonthlyMovementsType,
|
|
19
|
+
MonthlySpendingType,
|
|
20
|
+
Profession,
|
|
21
|
+
State,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# We use custom serializers for IP addresses and URLs because
|
|
25
|
+
# Pydantic's IPvAnyAddress, AnyUrl, HttpUrl types are not JSON serializable.
|
|
26
|
+
SerializableHttpUrl = Annotated[HttpUrl, PlainSerializer(str, return_type=str)]
|
|
27
|
+
SerializableAnyUrl = Annotated[AnyUrl, PlainSerializer(str, return_type=str)]
|
|
28
|
+
SerializableIPvAnyAddress = Annotated[
|
|
29
|
+
IPvAnyAddress, PlainSerializer(str, return_type=str)
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
NonEmptyStr = Annotated[
|
|
33
|
+
str, StringConstraints(strip_whitespace=True, min_length=1)
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SantizedDict(dict):
|
|
38
|
+
def __init__(self, *args, **kwargs):
|
|
39
|
+
super().__init__(*args, **kwargs)
|
|
40
|
+
sanitize_dict(self)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class JSONEncoder(json.JSONEncoder):
|
|
44
|
+
def default(self, o):
|
|
45
|
+
return sanitize_item(o, default=super().default)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
MAX_VALUE_IN_DB = 21_474_836_47
|
|
49
|
+
|
|
50
|
+
StrictPositiveInt = Annotated[
|
|
51
|
+
int, Field(strict=True, gt=0, le=MAX_VALUE_IN_DB)
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def digits(
|
|
56
|
+
min_length: Optional[int] = None, max_length: Optional[int] = None
|
|
57
|
+
) -> Annotated[Any, StringConstraints]:
|
|
58
|
+
return Annotated[
|
|
59
|
+
str,
|
|
60
|
+
StringConstraints(
|
|
61
|
+
strip_whitespace=True,
|
|
62
|
+
min_length=min_length,
|
|
63
|
+
max_length=max_length,
|
|
64
|
+
pattern=r'^\d+$',
|
|
65
|
+
),
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
names_state = {
|
|
70
|
+
State.NE: 'Nacido en el Extranjero',
|
|
71
|
+
State.AS: 'Aguascalientes',
|
|
72
|
+
State.BC: 'Baja California',
|
|
73
|
+
State.BS: 'Baja California Sur',
|
|
74
|
+
State.CC: 'Campeche',
|
|
75
|
+
State.CS: 'Chiapas',
|
|
76
|
+
State.CH: 'Chihuahua',
|
|
77
|
+
State.CL: 'Coahuila',
|
|
78
|
+
State.CM: 'Colima',
|
|
79
|
+
State.DF: 'Ciudad de México',
|
|
80
|
+
State.DG: 'Durango',
|
|
81
|
+
State.GT: 'Guanajuato',
|
|
82
|
+
State.GR: 'Guerrero',
|
|
83
|
+
State.HG: 'Hidalgo',
|
|
84
|
+
State.JC: 'Jalisco',
|
|
85
|
+
State.MC: 'México',
|
|
86
|
+
State.MN: 'Michoacán',
|
|
87
|
+
State.MS: 'Morelos',
|
|
88
|
+
State.NT: 'Nayarit',
|
|
89
|
+
State.NL: 'Nuevo León',
|
|
90
|
+
State.OC: 'Oaxaca',
|
|
91
|
+
State.PL: 'Puebla',
|
|
92
|
+
State.QT: 'Querétaro',
|
|
93
|
+
State.QR: 'Quintana Roo',
|
|
94
|
+
State.SP: 'San Luis Potosí',
|
|
95
|
+
State.SL: 'Sinaloa',
|
|
96
|
+
State.SR: 'Sonora',
|
|
97
|
+
State.TC: 'Tabasco',
|
|
98
|
+
State.TL: 'Tlaxcala',
|
|
99
|
+
State.TS: 'Tamaulipas',
|
|
100
|
+
State.VZ: 'Veracruz',
|
|
101
|
+
State.YN: 'Yucatán',
|
|
102
|
+
State.ZS: 'Zacatecas',
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def get_state_name(state: State) -> str:
|
|
107
|
+
return names_state[state]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
names_professions = {
|
|
111
|
+
Profession.artisticas: 'Actividades Artísticas',
|
|
112
|
+
Profession.agropecuario: 'Agricultura, Ganadería o Pesca',
|
|
113
|
+
Profession.comercio: 'Comercio',
|
|
114
|
+
Profession.estudiante: 'Estudiante',
|
|
115
|
+
Profession.empleado: 'Empleado(a/e)',
|
|
116
|
+
Profession.emprendimiento: 'Emprendimiento',
|
|
117
|
+
Profession.hogar: 'Hogar',
|
|
118
|
+
Profession.profesor: 'Profesor(a/e)',
|
|
119
|
+
Profession.profesionista: 'Profesionista',
|
|
120
|
+
Profession.servidor_publico: 'Servidor(a/e) Público',
|
|
121
|
+
Profession.sistemas: 'Sistemas y Comunicaciones',
|
|
122
|
+
Profession.independiente: 'Trabajador(a/e) Independiente',
|
|
123
|
+
Profession.oficios: 'Oficios Varios',
|
|
124
|
+
Profession.otro: 'Otro',
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def get_profession_name(profession: Profession) -> str:
|
|
129
|
+
return names_professions[profession]
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
names_account_use_types = {
|
|
133
|
+
AccountUseType.personal_expenses: 'Gastos personales o familiares',
|
|
134
|
+
AccountUseType.business_expenses: (
|
|
135
|
+
'Gastos relacionados con tu actividad económica'
|
|
136
|
+
),
|
|
137
|
+
AccountUseType.payment_of_goods_or_services: 'Pago de bienes o servicios',
|
|
138
|
+
AccountUseType.send_or_receive_transfers: (
|
|
139
|
+
'Enviar o recibir transferencias'
|
|
140
|
+
),
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_account_use_type_name(account_use_type: AccountUseType) -> str:
|
|
145
|
+
return names_account_use_types[account_use_type]
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
names_monthly_movements_types = {
|
|
149
|
+
MonthlyMovementsType.between_1_and_20: 'Entre 1 y 20 movimientos',
|
|
150
|
+
MonthlyMovementsType.between_20_and_40: 'Entre 20 y 40 movimientos',
|
|
151
|
+
MonthlyMovementsType.between_40_and_60: 'Entre 40 y 60 movimientos',
|
|
152
|
+
MonthlyMovementsType.more_than_60: 'Más de 60 movimientos',
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def get_monthly_movements_type_name(
|
|
157
|
+
monthly_movements_type: MonthlyMovementsType,
|
|
158
|
+
) -> str:
|
|
159
|
+
return names_monthly_movements_types[monthly_movements_type]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
names_monthly_spending_types = {
|
|
163
|
+
MonthlySpendingType.less_than_1k: 'Menos de $1,000',
|
|
164
|
+
MonthlySpendingType.between_1k_and_10k: 'Entre $1,000 y $10,000',
|
|
165
|
+
MonthlySpendingType.between_10k_and_20k: 'Entre $10,000 y $20,000',
|
|
166
|
+
MonthlySpendingType.between_20k_and_50k: 'Entre $20,000 y $50,000',
|
|
167
|
+
MonthlySpendingType.between_50k_and_100k: 'Entre $50,000 y $100,000',
|
|
168
|
+
MonthlySpendingType.more_than_100k: 'Más de $100,000',
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def get_monthly_spending_type_name(
|
|
173
|
+
monthly_spending_type: MonthlySpendingType,
|
|
174
|
+
) -> str:
|
|
175
|
+
return names_monthly_spending_types[monthly_spending_type]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
names_income_types = {
|
|
179
|
+
IncomeType.salary: 'Sueldo o salario fijo',
|
|
180
|
+
IncomeType.freelance: 'Independiente',
|
|
181
|
+
IncomeType.support_from_third_party: 'Apoyo de terceros o familiares',
|
|
182
|
+
IncomeType.variable: 'Variable',
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def get_income_type_name(income_type: IncomeType) -> str:
|
|
187
|
+
return names_income_types[income_type]
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@dataclass
|
|
191
|
+
class LogConfig:
|
|
192
|
+
masked: bool = False
|
|
193
|
+
unmasked_chars_length: int = 0
|
|
194
|
+
excluded: bool = False
|
{cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/requests.py
RENAMED
|
@@ -17,6 +17,7 @@ from pydantic_core import core_schema
|
|
|
17
17
|
from pydantic_extra_types.coordinate import Coordinate
|
|
18
18
|
|
|
19
19
|
from ..types.enums import (
|
|
20
|
+
AccountUseType,
|
|
20
21
|
AuthorizerTransaction,
|
|
21
22
|
CardDesign,
|
|
22
23
|
CardFundingType,
|
|
@@ -29,9 +30,12 @@ from ..types.enums import (
|
|
|
29
30
|
EcommerceIndicator,
|
|
30
31
|
FileExtension,
|
|
31
32
|
Gender,
|
|
33
|
+
IncomeType,
|
|
32
34
|
IssuerNetwork,
|
|
33
35
|
KYCFileType,
|
|
34
36
|
KYCValidationSource,
|
|
37
|
+
MonthlyMovementsType,
|
|
38
|
+
MonthlySpendingType,
|
|
35
39
|
PlatformType,
|
|
36
40
|
PosCapability,
|
|
37
41
|
Profession,
|
|
@@ -447,7 +451,6 @@ class UserRequest(BaseModel):
|
|
|
447
451
|
'Mexican government ID (18 characters). ' 'Must be pre-validated.'
|
|
448
452
|
)
|
|
449
453
|
)
|
|
450
|
-
|
|
451
454
|
profession: Profession = Field(description='User profession or occupation')
|
|
452
455
|
address: AddressRequest = Field(
|
|
453
456
|
description='User residential address information'
|
|
@@ -460,13 +463,17 @@ class UserRequest(BaseModel):
|
|
|
460
463
|
...,
|
|
461
464
|
description='ID of previously validated email verification',
|
|
462
465
|
)
|
|
466
|
+
account_use_type: Optional[AccountUseType] = None
|
|
467
|
+
monthly_movements_type: Optional[MonthlyMovementsType] = None
|
|
468
|
+
monthly_spending_type: Optional[MonthlySpendingType] = None
|
|
469
|
+
income_type: Optional[IncomeType] = None
|
|
463
470
|
|
|
464
471
|
model_config = ConfigDict(
|
|
465
472
|
json_schema_extra={
|
|
466
473
|
'example': {
|
|
467
474
|
'curp': 'GOCG650418HVZNML08',
|
|
468
|
-
'
|
|
469
|
-
'
|
|
475
|
+
'phone_verification_id': 'VEKp662Yrf6lMztl0-9qzk7Q',
|
|
476
|
+
'email_verification_id': 'VEwjDEcCMWZIk3JJ3p7P6T_A',
|
|
470
477
|
'profession': 'engineer',
|
|
471
478
|
'address': AddressRequest.model_json_schema().get('example'),
|
|
472
479
|
}
|
|
@@ -480,6 +487,13 @@ class UserRequest(BaseModel):
|
|
|
480
487
|
validate_age_requirement(curp)
|
|
481
488
|
return curp
|
|
482
489
|
|
|
490
|
+
@field_validator('profession')
|
|
491
|
+
@classmethod
|
|
492
|
+
def validate_profession(cls, profession: Profession) -> Profession:
|
|
493
|
+
if profession == Profession.otro:
|
|
494
|
+
raise ValueError('Profession "otro" is not allowed')
|
|
495
|
+
return profession
|
|
496
|
+
|
|
483
497
|
|
|
484
498
|
class UserUpdateRequest(BaseModel):
|
|
485
499
|
profession: Optional[Profession] = None
|
|
@@ -492,9 +506,14 @@ class UserUpdateRequest(BaseModel):
|
|
|
492
506
|
proof_of_life: Optional[KYCFile] = None
|
|
493
507
|
curp_document_uri: Optional[SerializableHttpUrl] = None
|
|
494
508
|
fiscal_regime_code: Optional[SATRegimeCode] = None
|
|
509
|
+
fiscal_address: Optional[AddressRequest] = None
|
|
495
510
|
pronouns: Optional[str] = None
|
|
496
511
|
status: Optional[UserStatus] = None
|
|
497
512
|
required_level: Optional[int] = None
|
|
513
|
+
account_use_type: Optional[AccountUseType] = None
|
|
514
|
+
monthly_movements_type: Optional[MonthlyMovementsType] = None
|
|
515
|
+
monthly_spending_type: Optional[MonthlySpendingType] = None
|
|
516
|
+
income_type: Optional[IncomeType] = None
|
|
498
517
|
|
|
499
518
|
@field_validator('beneficiaries')
|
|
500
519
|
@classmethod
|
|
@@ -505,6 +524,13 @@ class UserUpdateRequest(BaseModel):
|
|
|
505
524
|
raise ValueError('The total percentage should be 100%')
|
|
506
525
|
return beneficiaries
|
|
507
526
|
|
|
527
|
+
@field_validator('profession')
|
|
528
|
+
@classmethod
|
|
529
|
+
def validate_profession(cls, profession: Profession) -> Profession:
|
|
530
|
+
if profession == Profession.otro:
|
|
531
|
+
raise ValueError('Profession "otro" is not allowed')
|
|
532
|
+
return profession
|
|
533
|
+
|
|
508
534
|
|
|
509
535
|
class UserLoginRequest(BaseRequest):
|
|
510
536
|
password: Annotated[
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.1.16'
|
|
@@ -17,10 +17,19 @@ from cuenca_validations.types import (
|
|
|
17
17
|
SessionRequest,
|
|
18
18
|
TransactionStatus,
|
|
19
19
|
digits,
|
|
20
|
+
get_account_use_type_name,
|
|
21
|
+
get_income_type_name,
|
|
22
|
+
get_monthly_movements_type_name,
|
|
23
|
+
get_monthly_spending_type_name,
|
|
24
|
+
get_profession_name,
|
|
20
25
|
get_state_name,
|
|
21
26
|
)
|
|
22
27
|
from cuenca_validations.types.enums import (
|
|
28
|
+
AccountUseType,
|
|
23
29
|
EcommerceIndicator,
|
|
30
|
+
IncomeType,
|
|
31
|
+
MonthlyMovementsType,
|
|
32
|
+
MonthlySpendingType,
|
|
24
33
|
Profession,
|
|
25
34
|
SessionType,
|
|
26
35
|
State,
|
|
@@ -316,7 +325,24 @@ def test_user_request():
|
|
|
316
325
|
phone_verification_id='VE12345678',
|
|
317
326
|
email_verification_id='VE0987654321',
|
|
318
327
|
)
|
|
319
|
-
assert UserRequest(**request).model_dump() == request
|
|
328
|
+
assert UserRequest(**request).model_dump(exclude_none=True) == request
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def test_user_request_invalid_profession():
|
|
332
|
+
request = dict(
|
|
333
|
+
curp='ABCD920604HDFSRN03',
|
|
334
|
+
profession=Profession.otro,
|
|
335
|
+
address=dict(
|
|
336
|
+
street='calle 1',
|
|
337
|
+
ext_number='2',
|
|
338
|
+
int_number='3',
|
|
339
|
+
postal_code_id='PC2ygq9j2bS9-9tsuVawzErA',
|
|
340
|
+
),
|
|
341
|
+
phone_verification_id='VE12345678',
|
|
342
|
+
email_verification_id='VE0987654321',
|
|
343
|
+
)
|
|
344
|
+
with pytest.raises(ValidationError):
|
|
345
|
+
UserRequest(**request)
|
|
320
346
|
|
|
321
347
|
|
|
322
348
|
@freeze_time('2022-01-01')
|
|
@@ -410,6 +436,7 @@ def test_user_update_request():
|
|
|
410
436
|
),
|
|
411
437
|
],
|
|
412
438
|
curp_document_uri='https://sandbox.cuenca.com/files/EF123',
|
|
439
|
+
profession=Profession.empleado,
|
|
413
440
|
)
|
|
414
441
|
update_req = UserUpdateRequest(**request)
|
|
415
442
|
beneficiaries = [b.model_dump() for b in update_req.beneficiaries]
|
|
@@ -418,6 +445,7 @@ def test_user_update_request():
|
|
|
418
445
|
update_req.curp_document_uri.unicode_string()
|
|
419
446
|
== request['curp_document_uri']
|
|
420
447
|
)
|
|
448
|
+
assert update_req.profession == Profession.empleado
|
|
421
449
|
|
|
422
450
|
request['beneficiaries'] = [
|
|
423
451
|
dict(
|
|
@@ -481,6 +509,10 @@ def test_user_update_request():
|
|
|
481
509
|
)
|
|
482
510
|
UserUpdateRequest(**kyc_request)
|
|
483
511
|
|
|
512
|
+
request['profession'] = Profession.otro
|
|
513
|
+
with pytest.raises(ValidationError) as v:
|
|
514
|
+
UserUpdateRequest(**request)
|
|
515
|
+
|
|
484
516
|
|
|
485
517
|
def test_session_request():
|
|
486
518
|
data = dict(
|
|
@@ -544,10 +576,39 @@ def test_limited_wallet_request():
|
|
|
544
576
|
assert LimitedWalletRequest(allowed_curp=curp, allowed_rfc=rfc)
|
|
545
577
|
|
|
546
578
|
|
|
547
|
-
def test_get_state_name():
|
|
579
|
+
def test_get_state_name() -> None:
|
|
548
580
|
assert get_state_name(State.VZ) == 'Veracruz'
|
|
549
581
|
|
|
550
582
|
|
|
583
|
+
def test_get_profession_name() -> None:
|
|
584
|
+
assert get_profession_name(Profession.empleado) == 'Empleado(a/e)'
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def test_get_income_type_name() -> None:
|
|
588
|
+
assert get_income_type_name(IncomeType.salary) == 'Sueldo o salario fijo'
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
def test_get_account_use_type_name() -> None:
|
|
592
|
+
assert (
|
|
593
|
+
get_account_use_type_name(AccountUseType.personal_expenses)
|
|
594
|
+
== 'Gastos personales o familiares'
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
def test_get_monthly_spending_type_name() -> None:
|
|
599
|
+
assert (
|
|
600
|
+
get_monthly_spending_type_name(MonthlySpendingType.less_than_1k)
|
|
601
|
+
== 'Menos de $1,000'
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def test_get_monthly_movements_type_name() -> None:
|
|
606
|
+
assert (
|
|
607
|
+
get_monthly_movements_type_name(MonthlyMovementsType.between_1_and_20)
|
|
608
|
+
== 'Entre 1 y 20 movimientos'
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
|
|
551
612
|
def test_bank_account_validation_clabe_request():
|
|
552
613
|
assert BankAccountValidationRequest(account_number='646180157098510917')
|
|
553
614
|
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
from typing import Annotated, Any, Optional
|
|
4
|
-
|
|
5
|
-
from pydantic import (
|
|
6
|
-
AnyUrl,
|
|
7
|
-
Field,
|
|
8
|
-
HttpUrl,
|
|
9
|
-
IPvAnyAddress,
|
|
10
|
-
PlainSerializer,
|
|
11
|
-
StringConstraints,
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
from ..validators import sanitize_dict, sanitize_item
|
|
15
|
-
from .enums import State
|
|
16
|
-
|
|
17
|
-
# We use custom serializers for IP addresses and URLs because
|
|
18
|
-
# Pydantic's IPvAnyAddress, AnyUrl, HttpUrl types are not JSON serializable.
|
|
19
|
-
SerializableHttpUrl = Annotated[HttpUrl, PlainSerializer(str, return_type=str)]
|
|
20
|
-
SerializableAnyUrl = Annotated[AnyUrl, PlainSerializer(str, return_type=str)]
|
|
21
|
-
SerializableIPvAnyAddress = Annotated[
|
|
22
|
-
IPvAnyAddress, PlainSerializer(str, return_type=str)
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
NonEmptyStr = Annotated[
|
|
26
|
-
str, StringConstraints(strip_whitespace=True, min_length=1)
|
|
27
|
-
]
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class SantizedDict(dict):
|
|
31
|
-
def __init__(self, *args, **kwargs):
|
|
32
|
-
super().__init__(*args, **kwargs)
|
|
33
|
-
sanitize_dict(self)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class JSONEncoder(json.JSONEncoder):
|
|
37
|
-
def default(self, o):
|
|
38
|
-
return sanitize_item(o, default=super().default)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
MAX_VALUE_IN_DB = 21_474_836_47
|
|
42
|
-
|
|
43
|
-
StrictPositiveInt = Annotated[
|
|
44
|
-
int, Field(strict=True, gt=0, le=MAX_VALUE_IN_DB)
|
|
45
|
-
]
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def digits(
|
|
49
|
-
min_length: Optional[int] = None, max_length: Optional[int] = None
|
|
50
|
-
) -> Annotated[Any, StringConstraints]:
|
|
51
|
-
return Annotated[
|
|
52
|
-
str,
|
|
53
|
-
StringConstraints(
|
|
54
|
-
strip_whitespace=True,
|
|
55
|
-
min_length=min_length,
|
|
56
|
-
max_length=max_length,
|
|
57
|
-
pattern=r'^\d+$',
|
|
58
|
-
),
|
|
59
|
-
]
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
names_state = {
|
|
63
|
-
State.NE: 'Nacido en el Extranjero',
|
|
64
|
-
State.AS: 'Aguascalientes',
|
|
65
|
-
State.BC: 'Baja California',
|
|
66
|
-
State.BS: 'Baja California Sur',
|
|
67
|
-
State.CC: 'Campeche',
|
|
68
|
-
State.CS: 'Chiapas',
|
|
69
|
-
State.CH: 'Chihuahua',
|
|
70
|
-
State.CL: 'Coahuila',
|
|
71
|
-
State.CM: 'Colima',
|
|
72
|
-
State.DF: 'Ciudad de México',
|
|
73
|
-
State.DG: 'Durango',
|
|
74
|
-
State.GT: 'Guanajuato',
|
|
75
|
-
State.GR: 'Guerrero',
|
|
76
|
-
State.HG: 'Hidalgo',
|
|
77
|
-
State.JC: 'Jalisco',
|
|
78
|
-
State.MC: 'México',
|
|
79
|
-
State.MN: 'Michoacan',
|
|
80
|
-
State.MS: 'Morelos',
|
|
81
|
-
State.NT: 'Nayarit',
|
|
82
|
-
State.NL: 'Nuevo León',
|
|
83
|
-
State.OC: 'Oaxaca',
|
|
84
|
-
State.PL: 'Puebla',
|
|
85
|
-
State.QT: 'Querétaro',
|
|
86
|
-
State.QR: 'Quintana Roo',
|
|
87
|
-
State.SP: 'San Luis Potosí',
|
|
88
|
-
State.SL: 'Sinaloa',
|
|
89
|
-
State.SR: 'Sonora',
|
|
90
|
-
State.TC: 'Tabasco',
|
|
91
|
-
State.TL: 'Tlaxcala',
|
|
92
|
-
State.TS: 'Tamaulipas',
|
|
93
|
-
State.VZ: 'Veracruz',
|
|
94
|
-
State.YN: 'Yucatán',
|
|
95
|
-
State.ZS: 'Zacatecas',
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def get_state_name(state: State):
|
|
100
|
-
return names_state[state]
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
@dataclass
|
|
104
|
-
class LogConfig:
|
|
105
|
-
masked: bool = False
|
|
106
|
-
unmasked_chars_length: int = 0
|
|
107
|
-
excluded: bool = False
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.1.14.dev105'
|
|
File without changes
|
|
File without changes
|
{cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/__init__.py
RENAMED
|
File without changes
|
{cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/card_bins.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/card.py
RENAMED
|
File without changes
|
{cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/files.py
RENAMED
|
File without changes
|
{cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/morals.py
RENAMED
|
File without changes
|
{cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/types/queries.py
RENAMED
|
File without changes
|
|
File without changes
|
{cuenca_validations-2.1.14.dev105 → cuenca_validations-2.1.16}/cuenca_validations/validators.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|