cuenca-validations 2.1.48.dev1__py3-none-any.whl → 2.1.49.dev0__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.
@@ -83,7 +83,6 @@ __all__ = [
83
83
  'Profession',
84
84
  'QueryParams',
85
85
  'ReferencedClabeRequest',
86
- 'RejectTransferOrderRequest',
87
86
  'LegalRfc',
88
87
  'Rfc',
89
88
  'QuestionnairesRequest',
@@ -114,6 +113,7 @@ __all__ = [
114
113
  'TransferOrderStatus',
115
114
  'TransferQuery',
116
115
  'TransferRequest',
116
+ 'UpdateTransferOrderRequest',
117
117
  'UpdateTransferRequest',
118
118
  'UserCardNotification',
119
119
  'UserCredentialRequest',
@@ -280,7 +280,6 @@ from .requests import (
280
280
  PlatformRequest,
281
281
  QuestionnairesRequest,
282
282
  ReferencedClabeRequest,
283
- RejectTransferOrderRequest,
284
283
  SavingRequest,
285
284
  SavingUpdateRequest,
286
285
  SessionRequest,
@@ -289,6 +288,7 @@ from .requests import (
289
288
  TransferOrderLineRequest,
290
289
  TransferOrderRequest,
291
290
  TransferRequest,
291
+ UpdateTransferOrderRequest,
292
292
  UpdateTransferRequest,
293
293
  UserCredentialRequest,
294
294
  UserCredentialUpdateRequest,
@@ -1,5 +1,5 @@
1
1
  import datetime as dt
2
- from typing import Annotated, Any, Optional, Union
2
+ from typing import Annotated, Any, Literal, Optional, Union
3
3
 
4
4
  from clabe import BANK_NAMES, Clabe
5
5
  from pydantic import (
@@ -50,6 +50,7 @@ from ..types.enums import (
50
50
  TrackDataMethod,
51
51
  TransactionStatus,
52
52
  TransactionTokenValidationStatus,
53
+ TransferOrderStatus,
53
54
  UserCardNotification,
54
55
  UserStatus,
55
56
  VerificationStatus,
@@ -1021,10 +1022,6 @@ class PhoneVerificationAssociationRequest(BaseRequest):
1021
1022
  verification_id: str
1022
1023
 
1023
1024
 
1024
- MAX_TRANSFER_ORDER_BATCH_LINES = 5000
1025
- MAX_TRANSFER_ORDER_EXPIRATION_HOURS = 24 * 7
1026
-
1027
-
1028
1025
  class TransferOrderLineRequest(BaseRequest):
1029
1026
  account_number: str
1030
1027
  recipient_name: str
@@ -1044,65 +1041,62 @@ class TransferOrderLineRequest(BaseRequest):
1044
1041
 
1045
1042
 
1046
1043
  class TransferOrderRequest(BaseRequest):
1047
- account_number: Optional[str] = None
1048
- recipient_name: Optional[str] = None
1049
- amount: Optional[int] = None
1050
- descriptor: Optional[str] = None
1051
1044
  idempotency_key: str
1052
1045
  legal_person_id: Optional[str] = None
1053
- items: Optional[list[TransferOrderLineRequest]] = None
1054
- expires_in_hours: Optional[int] = Field(
1055
- default=None,
1056
- gt=0,
1057
- le=MAX_TRANSFER_ORDER_EXPIRATION_HOURS,
1058
- )
1046
+ items: list[TransferOrderLineRequest]
1059
1047
 
1060
1048
  model_config = ConfigDict(
1061
1049
  json_schema_extra={
1062
1050
  'example': {
1063
- 'account_number': '646180157034181180',
1064
- 'recipient_name': 'Doroteo Arango',
1065
- 'amount': 100_00,
1066
- 'descriptor': 'Mezcal, pulque y tequila',
1067
1051
  'idempotency_key': 'UNIQUE-KEY-003',
1068
1052
  'legal_person_id': 'USWqY5cvkISJOxHyEKjAKf8w',
1053
+ 'items': [
1054
+ {
1055
+ 'account_number': '646180157034181180',
1056
+ 'recipient_name': 'Doroteo Arango',
1057
+ 'amount': 100_00,
1058
+ 'descriptor': 'Mezcal, pulque y tequila',
1059
+ }
1060
+ ],
1069
1061
  }
1070
1062
  },
1071
1063
  )
1072
1064
 
1073
1065
  @model_validator(mode='after')
1074
- def require_unitario_or_batch(self) -> 'TransferOrderRequest':
1075
- has_items = bool(self.items)
1076
- has_flat = all(
1077
- (
1078
- self.account_number is not None,
1079
- self.recipient_name is not None,
1080
- self.amount is not None,
1081
- self.descriptor is not None,
1082
- )
1083
- )
1084
- if has_items == has_flat:
1085
- raise ValueError(
1086
- 'Provide either a single transfer (account_number, '
1087
- 'recipient_name, amount, descriptor) or items[] for a batch, '
1088
- 'not both'
1089
- )
1090
- if self.items is not None:
1091
- if len(self.items) > MAX_TRANSFER_ORDER_BATCH_LINES:
1092
- raise ValueError(
1093
- 'Batch exceeds maximum of '
1094
- f'{MAX_TRANSFER_ORDER_BATCH_LINES} lines'
1095
- )
1096
- if len(self.items) == 0:
1097
- raise ValueError('items must not be empty')
1066
+ def validate_items(self) -> 'TransferOrderRequest':
1067
+ if len(self.items) == 0:
1068
+ raise ValueError('items must not be empty')
1098
1069
  return self
1099
1070
 
1100
1071
 
1101
- class RejectTransferOrderRequest(BaseRequest):
1102
- rejection_reason: str
1072
+ class UpdateTransferOrderRequest(BaseRequest):
1073
+ status: Literal[
1074
+ TransferOrderStatus.authorized, TransferOrderStatus.rejected
1075
+ ]
1076
+ rejection_reason: Optional[str] = None
1103
1077
 
1104
1078
  model_config = ConfigDict(
1105
1079
  json_schema_extra={
1106
- 'example': {'rejection_reason': 'Monto incorrecto'},
1080
+ 'examples': [
1081
+ {'status': 'authorized'},
1082
+ {
1083
+ 'status': 'rejected',
1084
+ 'rejection_reason': 'Monto incorrecto',
1085
+ },
1086
+ ]
1107
1087
  },
1108
1088
  )
1089
+
1090
+ @model_validator(mode='after')
1091
+ def validate_rejection_reason(self) -> 'UpdateTransferOrderRequest':
1092
+ if self.status is TransferOrderStatus.rejected:
1093
+ if (
1094
+ self.rejection_reason is None
1095
+ or not self.rejection_reason.strip()
1096
+ ):
1097
+ raise ValueError('rejection_reason is required')
1098
+ elif self.rejection_reason is not None:
1099
+ raise ValueError(
1100
+ 'rejection_reason is only allowed when status is rejected'
1101
+ )
1102
+ return self
@@ -1 +1 @@
1
- __version__ = '2.1.48.dev1'
1
+ __version__ = '2.1.49.dev0'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuenca_validations
3
- Version: 2.1.48.dev1
3
+ Version: 2.1.49.dev0
4
4
  Summary: Cuenca common validations
5
5
  Home-page: https://github.com/cuenca-mx/cuenca-validations
6
6
  Author: Cuenca
@@ -4,8 +4,8 @@ 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=zXSnU5-EMbQZBD-PfFXgP4Z6G7cm7KFDWXB7Nie6WUk,2682
7
- cuenca_validations/version.py,sha256=umPOhd32a7pnK7Db2pIyLe4_2kcNXGaPq1FCO0MRMS4,28
8
- cuenca_validations/types/__init__.py,sha256=VZu01odevifIR7SR9-lpJC2Ysee8QLwVnIOj7cjEUTU,6711
7
+ cuenca_validations/version.py,sha256=E1OKARtICq_n0Q4ukZ9yaw-H6GS0VhkKcaXf2C4kIGg,28
8
+ cuenca_validations/types/__init__.py,sha256=M657giDGa3sejskWMPIAfCXJHDgbh4p9J9BnheKJR-A,6711
9
9
  cuenca_validations/types/card.py,sha256=UGzz8NTFAverUmdUKAK1oGHnOnjSNTpIRUm93vKSSGY,1295
10
10
  cuenca_validations/types/enums.py,sha256=If8N4-AbM9daHmnH6txmBb9AqN0Ey7gi3jmkOZOIDAU,21104
11
11
  cuenca_validations/types/files.py,sha256=2CszbwF9ytXV9suFFwyDjYG4XxY8UhCjRw3HttVXXNw,269
@@ -14,17 +14,17 @@ cuenca_validations/types/helpers.py,sha256=4veeLZbugHHqZk0ezSim978VhH6Vq8XTrEhe1
14
14
  cuenca_validations/types/identities.py,sha256=j2xxh3UYHJe6IbAwr9yNXJkebTth_-g3SUmHeiPez8M,5513
15
15
  cuenca_validations/types/morals.py,sha256=davabh5hAnFVQyM7-yCyDaT5ewXnm0cr1BtqDIwzkX8,1833
16
16
  cuenca_validations/types/queries.py,sha256=yJZGW3p6uWGaBm6mjDp7KBgg7oLh0CUlAJeeVLRzowk,6134
17
- cuenca_validations/types/requests.py,sha256=6c9EUQMLCThX9RcU8VFpAY0Yuyc6erZpTbjosEmbD2s,32569
18
- cuenca_validations-2.1.48.dev1.dist-info/licenses/LICENSE,sha256=wR76FmxBbfnQpwELkkE5iMF8sFIafEMgXLTE4N4WPTc,1063
17
+ cuenca_validations/types/requests.py,sha256=26YyfGHh1egdjAHUWTDjTtelXARWKNPB6npPHkW1Tcg,32336
18
+ cuenca_validations-2.1.49.dev0.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=fx2R7y6oQO8XeQzevJe826NuHkBgvtznZicomchB3Gg,899
23
- tests/test_requests.py,sha256=mjH-VbU_jCdgjQPAalTcJo76iL6tSBnmyengiAY-M20,8671
23
+ tests/test_requests.py,sha256=NlXAvKDLyVy0tYDUHy3oNkxeX1KcA-dfyqwftddHWBo,10627
24
24
  tests/test_statement.py,sha256=IOE0rRRBgBZSJv_FLaETEyn5NzzXKMNTqgjv99GX-68,1436
25
25
  tests/test_types.py,sha256=rPjqPGfPU73MSdbeJpoW_ai5TAO25hWSnQAt_H7ZIeI,23055
26
26
  tests/test_validators.py,sha256=Jjr9gWTT4cRntGiKvQK4fncqx3JkEuTWkKm1VqpRHTs,1829
27
- cuenca_validations-2.1.48.dev1.dist-info/METADATA,sha256=Dv9rhKncVEqcSZk1z8OPXIJDU9reQEh76kaAHBHMe_U,1600
28
- cuenca_validations-2.1.48.dev1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
29
- cuenca_validations-2.1.48.dev1.dist-info/top_level.txt,sha256=4233xdOs2HtuT-GFRjcDcwK0IwdwvWdczOtk0fPB6Gw,25
30
- cuenca_validations-2.1.48.dev1.dist-info/RECORD,,
27
+ cuenca_validations-2.1.49.dev0.dist-info/METADATA,sha256=7CzNDGl4cHPMNcq-p0u36AMzFYj2tFFmM34sc-pfn_E,1600
28
+ cuenca_validations-2.1.49.dev0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
29
+ cuenca_validations-2.1.49.dev0.dist-info/top_level.txt,sha256=4233xdOs2HtuT-GFRjcDcwK0IwdwvWdczOtk0fPB6Gw,25
30
+ cuenca_validations-2.1.49.dev0.dist-info/RECORD,,
tests/test_requests.py CHANGED
@@ -5,17 +5,18 @@ from pydantic_extra_types.phone_numbers import PhoneNumber
5
5
  from cuenca_validations.types.enums import (
6
6
  Country,
7
7
  OperatorRole,
8
+ TransferOrderStatus,
8
9
  VerificationType,
9
10
  )
10
11
  from cuenca_validations.types.queries import OperatorQuery
11
12
  from cuenca_validations.types.requests import (
12
- MAX_TRANSFER_ORDER_BATCH_LINES,
13
13
  OperatorLoginRequest,
14
14
  OperatorLoginUpdateRequest,
15
15
  OperatorRequest,
16
16
  OperatorUpdateRequest,
17
17
  PasswordResetRequest,
18
18
  TransferOrderRequest,
19
+ UpdateTransferOrderRequest,
19
20
  UpdateTransferRequest,
20
21
  UserTOSAgreementRequest,
21
22
  UserUpdateRequest,
@@ -244,26 +245,73 @@ def test_transfer_order_request_shape() -> None:
244
245
  'descriptor': 'Mezcal',
245
246
  }
246
247
  single = TransferOrderRequest.model_validate(
247
- {**line, 'idempotency_key': 'single'}
248
+ {'idempotency_key': 'single', 'items': [line]}
248
249
  )
249
- assert single.items is None
250
+ assert single.items is not None
251
+ assert len(single.items) == 1
250
252
  batch = TransferOrderRequest.model_validate(
251
- {'idempotency_key': 'batch', 'items': [line]}
253
+ {'idempotency_key': 'batch', 'items': [line, line]}
252
254
  )
253
255
  assert batch.items is not None
256
+ assert len(batch.items) == 2
254
257
 
255
- with pytest.raises(ValidationError, match='not both'):
256
- TransferOrderRequest.model_validate(
257
- {**line, 'idempotency_key': 'both', 'items': [line]}
258
- )
259
258
  with pytest.raises(ValidationError, match='items must not be empty'):
260
259
  TransferOrderRequest.model_validate(
261
- {**line, 'idempotency_key': 'empty', 'items': []}
260
+ {'idempotency_key': 'empty', 'items': []}
262
261
  )
263
- with pytest.raises(ValidationError, match='Batch exceeds maximum'):
264
- TransferOrderRequest.model_validate(
265
- {
266
- 'idempotency_key': 'too-many',
267
- 'items': [line] * (MAX_TRANSFER_ORDER_BATCH_LINES + 1),
268
- }
262
+ with pytest.raises(ValidationError):
263
+ TransferOrderRequest.model_validate({'idempotency_key': 'missing'})
264
+
265
+
266
+ def test_update_transfer_order_request_authorized() -> None:
267
+ req = UpdateTransferOrderRequest.model_validate({'status': 'authorized'})
268
+ assert req.status is TransferOrderStatus.authorized
269
+ assert req.rejection_reason is None
270
+
271
+
272
+ def test_update_transfer_order_request_rejected() -> None:
273
+ req = UpdateTransferOrderRequest.model_validate(
274
+ {'status': 'rejected', 'rejection_reason': 'Monto incorrecto'}
275
+ )
276
+ assert req.status is TransferOrderStatus.rejected
277
+ assert req.rejection_reason == 'Monto incorrecto'
278
+
279
+
280
+ def test_update_transfer_order_request_rejected_requires_reason() -> None:
281
+ with pytest.raises(ValidationError, match='rejection_reason is required'):
282
+ UpdateTransferOrderRequest.model_validate({'status': 'rejected'})
283
+ with pytest.raises(ValidationError, match='rejection_reason is required'):
284
+ UpdateTransferOrderRequest.model_validate(
285
+ {'status': 'rejected', 'rejection_reason': ''}
286
+ )
287
+ with pytest.raises(ValidationError, match='rejection_reason is required'):
288
+ UpdateTransferOrderRequest.model_validate(
289
+ {'status': 'rejected', 'rejection_reason': ' '}
269
290
  )
291
+
292
+
293
+ def test_update_transfer_order_request_authorized_forbids_reason() -> None:
294
+ with pytest.raises(
295
+ ValidationError,
296
+ match='rejection_reason is only allowed when status is rejected',
297
+ ):
298
+ UpdateTransferOrderRequest.model_validate(
299
+ {'status': 'authorized', 'rejection_reason': 'no'}
300
+ )
301
+
302
+
303
+ def test_update_transfer_order_request_rejects_invalid_status() -> None:
304
+ with pytest.raises(ValidationError):
305
+ UpdateTransferOrderRequest.model_validate({'status': 'created'})
306
+ with pytest.raises(ValidationError):
307
+ UpdateTransferOrderRequest.model_validate({'status': 'expired'})
308
+ with pytest.raises(ValidationError):
309
+ UpdateTransferOrderRequest.model_validate({})
310
+
311
+
312
+ def test_update_transfer_order_request_forbids_extra() -> None:
313
+ with pytest.raises(ValidationError) as ex:
314
+ UpdateTransferOrderRequest.model_validate(
315
+ {'status': 'authorized', 'operator_id': 'OP123'}
316
+ )
317
+ assert 'Extra inputs are not permitted' in str(ex.value)