@fiado/type-kit 3.627.0 → 3.629.0

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 (212) hide show
  1. package/_test_/unit/collection/collectionCallbackRequests.test.ts +96 -0
  2. package/_test_/unit/gsfConnector/gsfMessages.test.ts +20 -0
  3. package/_test_/unit/loanCollectorAssignment/collectorAssignmentRequests.test.ts +65 -0
  4. package/_test_/unit/loanConfig/parameterWriteRequests.test.ts +62 -0
  5. package/_test_/unit/loanCredit/dtos/loanCreditInputLimits.test.ts +168 -0
  6. package/_test_/unit/loanCredit/signLoanCreditRequest.test.ts +61 -2
  7. package/_test_/unit/loanOfferings/offeringsInputRules.test.ts +254 -0
  8. package/_test_/unit/loanOfferings/promotionRequests.test.ts +1 -1
  9. package/_test_/unit/loanRetailAudit/auditInputLimits.test.ts +114 -0
  10. package/_test_/unit/loanScoring/evaluateSciRequest.test.ts +49 -0
  11. package/bin/collection/constants/CollectionResultLimits.d.ts +16 -0
  12. package/bin/collection/constants/CollectionResultLimits.js +19 -0
  13. package/bin/collection/dtos/ApplyCollectionResultRequest.d.ts +5 -0
  14. package/bin/collection/dtos/ApplyCollectionResultRequest.js +22 -0
  15. package/bin/collection/dtos/ResolvePendingChargesRequest.d.ts +2 -0
  16. package/bin/collection/dtos/ResolvePendingChargesRequest.js +9 -0
  17. package/bin/collection/index.d.ts +2 -0
  18. package/bin/collection/index.js +3 -0
  19. package/bin/collection/validators/IsCentPrecision.d.ts +11 -0
  20. package/bin/collection/validators/IsCentPrecision.js +32 -0
  21. package/bin/gsfConnector/dtos/GsfCreditTerms.d.ts +7 -2
  22. package/bin/gsfConnector/dtos/GsfCreditTerms.js +9 -0
  23. package/bin/loanCollectorAssignment/constants/CollectorAssignmentLimits.d.ts +12 -0
  24. package/bin/loanCollectorAssignment/constants/CollectorAssignmentLimits.js +15 -0
  25. package/bin/loanCollectorAssignment/dtos/requests/AutoAssignRequest.d.ts +1 -0
  26. package/bin/loanCollectorAssignment/dtos/requests/AutoAssignRequest.js +8 -0
  27. package/bin/loanCollectorAssignment/dtos/requests/ListCollectorAssignmentsRequest.d.ts +14 -0
  28. package/bin/loanCollectorAssignment/dtos/requests/ListCollectorAssignmentsRequest.js +62 -0
  29. package/bin/loanCollectorAssignment/dtos/requests/ReassignRequest.d.ts +1 -0
  30. package/bin/loanCollectorAssignment/dtos/requests/ReassignRequest.js +4 -0
  31. package/bin/loanCollectorAssignment/dtos/requests/RedistributeRequest.d.ts +1 -0
  32. package/bin/loanCollectorAssignment/dtos/requests/RedistributeRequest.js +4 -0
  33. package/bin/loanCollectorAssignment/dtos/responses/ListCollectorAssignmentsResponse.d.ts +9 -0
  34. package/bin/loanCollectorAssignment/dtos/responses/ListCollectorAssignmentsResponse.js +2 -0
  35. package/bin/loanCollectorAssignment/index.d.ts +3 -0
  36. package/bin/loanCollectorAssignment/index.js +4 -0
  37. package/bin/loanConfig/constants/ParameterRequestLimits.d.ts +8 -0
  38. package/bin/loanConfig/constants/ParameterRequestLimits.js +11 -0
  39. package/bin/loanConfig/dtos/requests/BulkGetParametersRequest.d.ts +1 -1
  40. package/bin/loanConfig/dtos/requests/BulkGetParametersRequest.js +3 -1
  41. package/bin/loanConfig/dtos/requests/BulkUpdateParametersRequest.d.ts +5 -2
  42. package/bin/loanConfig/dtos/requests/BulkUpdateParametersRequest.js +8 -4
  43. package/bin/loanConfig/dtos/requests/UpdateParameterRequest.d.ts +6 -3
  44. package/bin/loanConfig/dtos/requests/UpdateParameterRequest.js +10 -5
  45. package/bin/loanConfig/index.d.ts +1 -0
  46. package/bin/loanConfig/index.js +1 -0
  47. package/bin/loanCredit/constants/LoanInputLimits.d.ts +24 -0
  48. package/bin/loanCredit/constants/LoanInputLimits.js +27 -0
  49. package/bin/loanCredit/dtos/LoanBeneficiary.d.ts +1 -0
  50. package/bin/loanCredit/dtos/LoanBeneficiary.js +9 -0
  51. package/bin/loanCredit/dtos/LoanContractDetails.d.ts +18 -1
  52. package/bin/loanCredit/dtos/LoanContractDetails.js +2 -1
  53. package/bin/loanCredit/dtos/LoanDeviceNotifyItem.d.ts +5 -0
  54. package/bin/loanCredit/dtos/LoanDeviceNotifyItem.js +12 -3
  55. package/bin/loanCredit/dtos/RestructureTerms.d.ts +1 -1
  56. package/bin/loanCredit/dtos/RestructureTerms.js +2 -0
  57. package/bin/loanCredit/dtos/SignatureContext.d.ts +5 -1
  58. package/bin/loanCredit/dtos/requests/ActivationCheckRequest.d.ts +1 -1
  59. package/bin/loanCredit/dtos/requests/ActivationCheckRequest.js +4 -0
  60. package/bin/loanCredit/dtos/requests/BackofficeApproveRestructureRequest.js +2 -0
  61. package/bin/loanCredit/dtos/requests/BackofficeProposeRestructureRequest.js +2 -0
  62. package/bin/loanCredit/dtos/requests/BackofficeRejectRestructureRequest.js +2 -0
  63. package/bin/loanCredit/dtos/requests/BackofficeWriteOffCreditRequest.js +2 -0
  64. package/bin/loanCredit/dtos/requests/CreateCustomerPaymentRequest.d.ts +5 -0
  65. package/bin/loanCredit/dtos/requests/CreateCustomerPaymentRequest.js +6 -0
  66. package/bin/loanCredit/dtos/requests/CreateSellerPaymentRequest.d.ts +5 -0
  67. package/bin/loanCredit/dtos/requests/CreateSellerPaymentRequest.js +6 -0
  68. package/bin/loanCredit/dtos/requests/OriginateLoanCreditRequest.d.ts +1 -0
  69. package/bin/loanCredit/dtos/requests/OriginateLoanCreditRequest.js +7 -1
  70. package/bin/loanCredit/dtos/requests/SegmentFactsBatchRequest.d.ts +1 -1
  71. package/bin/loanCredit/dtos/requests/SegmentFactsBatchRequest.js +3 -1
  72. package/bin/loanCredit/dtos/requests/SignLoanCreditRequest.d.ts +2 -0
  73. package/bin/loanCredit/dtos/requests/SignLoanCreditRequest.js +3 -0
  74. package/bin/loanCredit/dtos/requests/UpsertLoanBorrowerRequest.d.ts +1 -0
  75. package/bin/loanCredit/dtos/requests/UpsertLoanBorrowerRequest.js +2 -0
  76. package/bin/loanCredit/index.d.ts +2 -0
  77. package/bin/loanCredit/index.js +2 -0
  78. package/bin/loanCredit/validators/IsContractBirthDate.d.ts +11 -0
  79. package/bin/loanCredit/validators/IsContractBirthDate.js +41 -0
  80. package/bin/loanOfferings/constants/offeringsInputLimits.d.ts +35 -0
  81. package/bin/loanOfferings/constants/offeringsInputLimits.js +38 -0
  82. package/bin/loanOfferings/dtos/requests/ChangeCreditPlanStatusRequest.d.ts +2 -0
  83. package/bin/loanOfferings/dtos/requests/ChangeCreditPlanStatusRequest.js +8 -0
  84. package/bin/loanOfferings/dtos/requests/ChangePromotionStatusRequest.js +2 -0
  85. package/bin/loanOfferings/dtos/requests/ComputeAmortizationScheduleRequest.d.ts +4 -1
  86. package/bin/loanOfferings/dtos/requests/ComputeAmortizationScheduleRequest.js +3 -1
  87. package/bin/loanOfferings/dtos/requests/ConsumePromotionRequest.d.ts +1 -0
  88. package/bin/loanOfferings/dtos/requests/ConsumePromotionRequest.js +3 -0
  89. package/bin/loanOfferings/dtos/requests/CreateCreditPlanRequest.d.ts +3 -0
  90. package/bin/loanOfferings/dtos/requests/CreateCreditPlanRequest.js +11 -2
  91. package/bin/loanOfferings/dtos/requests/CreateFinancierRequest.d.ts +5 -0
  92. package/bin/loanOfferings/dtos/requests/CreateFinancierRequest.js +11 -0
  93. package/bin/loanOfferings/dtos/requests/CreatePromotionRequest.d.ts +2 -2
  94. package/bin/loanOfferings/dtos/requests/CreatePromotionRequest.js +9 -0
  95. package/bin/loanOfferings/dtos/requests/CreateSegmentRequest.d.ts +1 -1
  96. package/bin/loanOfferings/dtos/requests/CreateSegmentRequest.js +3 -0
  97. package/bin/loanOfferings/dtos/requests/CreateStorePolicyRequest.d.ts +1 -1
  98. package/bin/loanOfferings/dtos/requests/CreateStorePolicyRequest.js +3 -0
  99. package/bin/loanOfferings/dtos/requests/ReleasePromotionRequest.d.ts +8 -0
  100. package/bin/loanOfferings/dtos/requests/ReleasePromotionRequest.js +30 -0
  101. package/bin/loanOfferings/dtos/requests/ReservePromotionBonusRequest.d.ts +1 -0
  102. package/bin/loanOfferings/dtos/requests/ReservePromotionBonusRequest.js +3 -0
  103. package/bin/loanOfferings/dtos/requests/SettlePromotionBonusRequest.d.ts +1 -0
  104. package/bin/loanOfferings/dtos/requests/SettlePromotionBonusRequest.js +3 -0
  105. package/bin/loanOfferings/dtos/requests/SimulateCreditPlanRequest.d.ts +1 -0
  106. package/bin/loanOfferings/dtos/requests/SimulateCreditPlanRequest.js +3 -0
  107. package/bin/loanOfferings/dtos/requests/UpdateCohortRequest.d.ts +3 -1
  108. package/bin/loanOfferings/dtos/requests/UpdateCohortRequest.js +5 -2
  109. package/bin/loanOfferings/dtos/requests/UpdateCreditPlanRequest.d.ts +8 -0
  110. package/bin/loanOfferings/dtos/requests/UpdateCreditPlanRequest.js +30 -12
  111. package/bin/loanOfferings/dtos/requests/UpdateFinancierRequest.d.ts +6 -0
  112. package/bin/loanOfferings/dtos/requests/UpdateFinancierRequest.js +13 -2
  113. package/bin/loanOfferings/dtos/requests/UpdatePromotionRequest.d.ts +7 -1
  114. package/bin/loanOfferings/dtos/requests/UpdatePromotionRequest.js +25 -5
  115. package/bin/loanOfferings/dtos/requests/UpdateSegmentRequest.d.ts +5 -2
  116. package/bin/loanOfferings/dtos/requests/UpdateSegmentRequest.js +12 -5
  117. package/bin/loanOfferings/dtos/requests/UpdateStorePolicyRequest.d.ts +18 -9
  118. package/bin/loanOfferings/dtos/requests/UpdateStorePolicyRequest.js +12 -4
  119. package/bin/loanOfferings/dtos/responses/ReleasePromotionResponse.d.ts +11 -0
  120. package/bin/loanOfferings/dtos/responses/ReleasePromotionResponse.js +2 -0
  121. package/bin/loanOfferings/index.d.ts +5 -0
  122. package/bin/loanOfferings/index.js +6 -0
  123. package/bin/loanOfferings/validators/IsOptionalNonNull.d.ts +9 -0
  124. package/bin/loanOfferings/validators/IsOptionalNonNull.js +14 -0
  125. package/bin/loanOfferings/validators/MaxJsonBytes.d.ts +5 -0
  126. package/bin/loanOfferings/validators/MaxJsonBytes.js +28 -0
  127. package/bin/loanRetailAudit/constants/auditInputLimits.d.ts +16 -0
  128. package/bin/loanRetailAudit/constants/auditInputLimits.js +19 -0
  129. package/bin/loanRetailAudit/dtos/AuditTrailChange.js +4 -0
  130. package/bin/loanRetailAudit/dtos/AuditTrailContextEntry.js +3 -0
  131. package/bin/loanRetailAudit/dtos/requests/CreatePiiAccessLogRequest.d.ts +7 -1
  132. package/bin/loanRetailAudit/dtos/requests/CreatePiiAccessLogRequest.js +8 -1
  133. package/bin/loanRetailAudit/events/AuditTrailRecordedV1.d.ts +1 -0
  134. package/bin/loanRetailAudit/events/AuditTrailRecordedV1.js +5 -1
  135. package/bin/loanRetailAudit/index.d.ts +2 -0
  136. package/bin/loanRetailAudit/index.js +3 -0
  137. package/bin/loanRetailAudit/validators/IsIsoDateTime.d.ts +13 -0
  138. package/bin/loanRetailAudit/validators/IsIsoDateTime.js +29 -0
  139. package/bin/loanScoring/dtos/ScoringOffer.d.ts +8 -6
  140. package/bin/loanScoring/dtos/requests/EvaluateSciRequest.d.ts +14 -3
  141. package/bin/loanScoring/dtos/requests/EvaluateSciRequest.js +15 -0
  142. package/package.json +1 -1
  143. package/src/collection/constants/CollectionResultLimits.ts +18 -0
  144. package/src/collection/dtos/ApplyCollectionResultRequest.ts +15 -8
  145. package/src/collection/dtos/ResolvePendingChargesRequest.ts +7 -4
  146. package/src/collection/index.ts +4 -0
  147. package/src/collection/validators/IsCentPrecision.ts +33 -0
  148. package/src/gsfConnector/dtos/GsfCreditTerms.ts +15 -3
  149. package/src/loanCollectorAssignment/constants/CollectorAssignmentLimits.ts +16 -0
  150. package/src/loanCollectorAssignment/dtos/requests/AutoAssignRequest.ts +13 -1
  151. package/src/loanCollectorAssignment/dtos/requests/ListCollectorAssignmentsRequest.ts +52 -0
  152. package/src/loanCollectorAssignment/dtos/requests/ReassignRequest.ts +8 -1
  153. package/src/loanCollectorAssignment/dtos/requests/RedistributeRequest.ts +8 -1
  154. package/src/loanCollectorAssignment/dtos/responses/ListCollectorAssignmentsResponse.ts +10 -0
  155. package/src/loanCollectorAssignment/index.ts +5 -0
  156. package/src/loanConfig/constants/ParameterRequestLimits.ts +11 -0
  157. package/src/loanConfig/dtos/requests/BulkGetParametersRequest.ts +4 -2
  158. package/src/loanConfig/dtos/requests/BulkUpdateParametersRequest.ts +26 -6
  159. package/src/loanConfig/dtos/requests/UpdateParameterRequest.ts +15 -7
  160. package/src/loanConfig/index.ts +1 -0
  161. package/src/loanCredit/constants/LoanInputLimits.ts +29 -0
  162. package/src/loanCredit/dtos/LoanBeneficiary.ts +11 -1
  163. package/src/loanCredit/dtos/LoanContractDetails.ts +26 -3
  164. package/src/loanCredit/dtos/LoanDeviceNotifyItem.ts +17 -3
  165. package/src/loanCredit/dtos/RestructureTerms.ts +4 -2
  166. package/src/loanCredit/dtos/SignatureContext.ts +5 -1
  167. package/src/loanCredit/dtos/requests/ActivationCheckRequest.ts +12 -2
  168. package/src/loanCredit/dtos/requests/BackofficeApproveRestructureRequest.ts +3 -1
  169. package/src/loanCredit/dtos/requests/BackofficeProposeRestructureRequest.ts +3 -1
  170. package/src/loanCredit/dtos/requests/BackofficeRejectRestructureRequest.ts +3 -1
  171. package/src/loanCredit/dtos/requests/BackofficeWriteOffCreditRequest.ts +3 -1
  172. package/src/loanCredit/dtos/requests/CreateCustomerPaymentRequest.ts +10 -1
  173. package/src/loanCredit/dtos/requests/CreateSellerPaymentRequest.ts +10 -1
  174. package/src/loanCredit/dtos/requests/OriginateLoanCreditRequest.ts +10 -1
  175. package/src/loanCredit/dtos/requests/SegmentFactsBatchRequest.ts +4 -2
  176. package/src/loanCredit/dtos/requests/SignLoanCreditRequest.ts +6 -1
  177. package/src/loanCredit/dtos/requests/UpsertLoanBorrowerRequest.ts +4 -1
  178. package/src/loanCredit/index.ts +2 -0
  179. package/src/loanCredit/validators/IsContractBirthDate.ts +41 -0
  180. package/src/loanOfferings/constants/offeringsInputLimits.ts +47 -0
  181. package/src/loanOfferings/dtos/requests/ChangeCreditPlanStatusRequest.ts +9 -1
  182. package/src/loanOfferings/dtos/requests/ChangePromotionStatusRequest.ts +3 -1
  183. package/src/loanOfferings/dtos/requests/ComputeAmortizationScheduleRequest.ts +7 -2
  184. package/src/loanOfferings/dtos/requests/ConsumePromotionRequest.ts +5 -1
  185. package/src/loanOfferings/dtos/requests/CreateCreditPlanRequest.ts +24 -2
  186. package/src/loanOfferings/dtos/requests/CreateFinancierRequest.ts +25 -0
  187. package/src/loanOfferings/dtos/requests/CreatePromotionRequest.ts +17 -2
  188. package/src/loanOfferings/dtos/requests/CreateSegmentRequest.ts +4 -1
  189. package/src/loanOfferings/dtos/requests/CreateStorePolicyRequest.ts +6 -1
  190. package/src/loanOfferings/dtos/requests/ReleasePromotionRequest.ts +16 -0
  191. package/src/loanOfferings/dtos/requests/ReservePromotionBonusRequest.ts +5 -1
  192. package/src/loanOfferings/dtos/requests/SettlePromotionBonusRequest.ts +5 -1
  193. package/src/loanOfferings/dtos/requests/SimulateCreditPlanRequest.ts +5 -1
  194. package/src/loanOfferings/dtos/requests/UpdateCohortRequest.ts +6 -3
  195. package/src/loanOfferings/dtos/requests/UpdateCreditPlanRequest.ts +43 -12
  196. package/src/loanOfferings/dtos/requests/UpdateFinancierRequest.ts +26 -2
  197. package/src/loanOfferings/dtos/requests/UpdatePromotionRequest.ts +34 -6
  198. package/src/loanOfferings/dtos/requests/UpdateSegmentRequest.ts +14 -7
  199. package/src/loanOfferings/dtos/requests/UpdateStorePolicyRequest.ts +28 -13
  200. package/src/loanOfferings/dtos/responses/ReleasePromotionResponse.ts +11 -0
  201. package/src/loanOfferings/index.ts +7 -0
  202. package/src/loanOfferings/validators/IsOptionalNonNull.ts +12 -0
  203. package/src/loanOfferings/validators/MaxJsonBytes.ts +28 -0
  204. package/src/loanRetailAudit/constants/auditInputLimits.ts +20 -0
  205. package/src/loanRetailAudit/dtos/AuditTrailChange.ts +5 -1
  206. package/src/loanRetailAudit/dtos/AuditTrailContextEntry.ts +4 -1
  207. package/src/loanRetailAudit/dtos/requests/CreatePiiAccessLogRequest.ts +20 -3
  208. package/src/loanRetailAudit/events/AuditTrailRecordedV1.ts +7 -2
  209. package/src/loanRetailAudit/index.ts +4 -0
  210. package/src/loanRetailAudit/validators/IsIsoDateTime.ts +33 -0
  211. package/src/loanScoring/dtos/ScoringOffer.ts +8 -6
  212. package/src/loanScoring/dtos/requests/EvaluateSciRequest.ts +26 -4
@@ -2,30 +2,49 @@ import { Expose } from 'class-transformer';
2
2
  import {
3
3
  IsArray,
4
4
  IsEnum,
5
+ IsNotEmpty,
5
6
  IsObject,
6
7
  IsOptional,
7
8
  IsString,
9
+ IsUrl,
10
+ MaxLength,
8
11
  } from 'class-validator';
12
+ import {
13
+ FINANCIER_CONFIG_MAX_BYTES,
14
+ FINANCIER_REDIRECT_URL_MAX_LENGTH,
15
+ OFFERINGS_LABEL_MAX_LENGTH,
16
+ OFFERINGS_NAME_MAX_LENGTH,
17
+ } from '../../constants/offeringsInputLimits';
9
18
  import { ProductTierEnum } from '../../enums/ProductTierEnum';
19
+ import { IsOptionalNonNull } from '../../validators/IsOptionalNonNull';
20
+ import { MaxJsonBytes } from '../../validators/MaxJsonBytes';
10
21
 
11
22
  /**
12
23
  * Body de PUT /financiers/:financierId — edición parcial (todos opcionales).
13
24
  * `code`, `type` y `displayOrder` no se editan aquí (identidad / orden por endpoint dedicado).
25
+ * `null` se acepta en `externalRedirectUrl`, `eligibleTiers` y `config`; en `name` y `label` da 400.
14
26
  */
15
27
  export class UpdateFinancierRequest {
16
28
  @Expose()
17
- @IsOptional()
29
+ @IsOptionalNonNull()
18
30
  @IsString()
31
+ @IsNotEmpty()
32
+ @MaxLength(OFFERINGS_NAME_MAX_LENGTH)
19
33
  name?: string;
20
34
 
21
35
  @Expose()
22
- @IsOptional()
36
+ @IsOptionalNonNull()
23
37
  @IsString()
38
+ @IsNotEmpty()
39
+ @MaxLength(OFFERINGS_LABEL_MAX_LENGTH)
24
40
  label?: string;
25
41
 
42
+ /** URL https a la que se redirige al cliente; `null` = sin redirección. */
26
43
  @Expose()
27
44
  @IsOptional()
28
45
  @IsString()
46
+ @MaxLength(FINANCIER_REDIRECT_URL_MAX_LENGTH)
47
+ @IsUrl({ protocols: ['https'], require_protocol: true })
29
48
  externalRedirectUrl?: string | null;
30
49
 
31
50
  @Expose()
@@ -34,8 +53,13 @@ export class UpdateFinancierRequest {
34
53
  @IsEnum(ProductTierEnum, { each: true })
35
54
  eligibleTiers?: ProductTierEnum[] | null;
36
55
 
56
+ /**
57
+ * @deprecated Ningún consumidor lo lee (TD-019 de loan-offerings) y se va a eliminar (decisión
58
+ * D-9). Mientras tanto, a lo más 4 KB serializado.
59
+ */
37
60
  @Expose()
38
61
  @IsOptional()
39
62
  @IsObject()
63
+ @MaxJsonBytes(FINANCIER_CONFIG_MAX_BYTES)
40
64
  config?: Record<string, unknown> | null;
41
65
  }
@@ -9,52 +9,71 @@ import {
9
9
  IsNotEmpty,
10
10
  IsOptional,
11
11
  IsString,
12
+ MaxLength,
12
13
  ValidateNested,
13
14
  } from 'class-validator';
15
+ import {
16
+ OFFERINGS_AUDIT_REASON_MAX_LENGTH,
17
+ OFFERINGS_DESCRIPTION_MAX_LENGTH,
18
+ OFFERINGS_NAME_MAX_LENGTH,
19
+ PROMOTION_PRODUCT_SKUS_MAX_ITEMS,
20
+ SCOPE_STORE_IDS_MAX_ITEMS,
21
+ } from '../../constants/offeringsInputLimits';
14
22
  import { PROMOTION_RETAILER_IDS_MAX_ITEMS } from '../../constants/promotionRetailerScope';
15
23
  import { CreditPlanLevelEnum } from '../../enums/CreditPlanLevelEnum';
16
24
  import { ProductCategoryEnum } from '../../../retailCatalog/enums/ProductCategoryEnum';
17
25
  import { ProductTierEnum } from '../../../retailCatalog/enums/ProductTierEnum';
26
+ import { IsOptionalNonNull } from '../../validators/IsOptionalNonNull';
18
27
  import { PromotionEffectInput } from './PromotionEffectInput';
19
28
 
20
29
  /**
21
30
  * Body de PUT /promotions/:promotionId — edición parcial (todos los campos opcionales).
22
31
  * Solo se admite sobre `DRAFT` y `PUBLISHED`, y sube `version`. El cambio de estado va por su
23
32
  * endpoint dedicado. `effect` se reemplaza entero cuando viene, no se mergea clave a clave.
33
+ *
34
+ * Ausente deja el campo como está. `null` solo se acepta en los alcances, las fechas y el
35
+ * presupuesto, que lo declaran; en `name`, `description`, `effect`, `segmentId` y `targetLevels` da 400.
24
36
  */
25
37
  export class UpdatePromotionRequest {
26
38
  @Expose()
27
- @IsOptional()
39
+ @IsOptionalNonNull()
28
40
  @IsString()
41
+ @IsNotEmpty()
42
+ @MaxLength(OFFERINGS_NAME_MAX_LENGTH)
29
43
  name?: string;
30
44
 
31
45
  @Expose()
32
- @IsOptional()
46
+ @IsOptionalNonNull()
33
47
  @IsString()
48
+ @IsNotEmpty()
49
+ @MaxLength(OFFERINGS_DESCRIPTION_MAX_LENGTH)
34
50
  description?: string;
35
51
 
36
52
  @Expose()
37
- @IsOptional()
53
+ @IsOptionalNonNull()
38
54
  @ValidateNested()
39
55
  @Type(() => PromotionEffectInput)
40
56
  effect?: PromotionEffectInput;
41
57
 
42
58
  @Expose()
43
- @IsOptional()
59
+ @IsOptionalNonNull()
44
60
  @IsString()
45
61
  segmentId?: string;
46
62
 
47
63
  @Expose()
48
- @IsOptional()
64
+ @IsOptionalNonNull()
49
65
  @IsArray()
50
66
  @ArrayNotEmpty()
51
67
  @IsEnum(CreditPlanLevelEnum, { each: true })
52
68
  targetLevels?: CreditPlanLevelEnum[];
53
69
 
70
+ /** SKUs alcanzados; `null` = todos los productos. Hasta 2 000. */
54
71
  @Expose()
55
72
  @IsOptional()
56
73
  @IsArray()
74
+ @ArrayMaxSize(PROMOTION_PRODUCT_SKUS_MAX_ITEMS)
57
75
  @IsString({ each: true })
76
+ @IsNotEmpty({ each: true })
58
77
  productSkus?: string[] | null;
59
78
 
60
79
  /** Tipos de producto alcanzados; ausente o `null` = todos los tipos. */
@@ -80,11 +99,13 @@ export class UpdatePromotionRequest {
80
99
  @IsNotEmpty({ each: true })
81
100
  retailerIds?: string[] | null;
82
101
 
83
- /** Tiendas alcanzadas. `null` o ausente = todas. */
102
+ /** Tiendas alcanzadas. `null` o ausente = todas. Hasta 1 000. */
84
103
  @Expose()
85
104
  @IsOptional()
86
105
  @IsArray()
106
+ @ArrayMaxSize(SCOPE_STORE_IDS_MAX_ITEMS)
87
107
  @IsString({ each: true })
108
+ @IsNotEmpty({ each: true })
88
109
  storeIds?: string[] | null;
89
110
 
90
111
  @Expose()
@@ -101,4 +122,11 @@ export class UpdatePromotionRequest {
101
122
  @IsOptional()
102
123
  @IsInt()
103
124
  budgetMaxCents?: number | null;
125
+
126
+ /** Motivo del cambio; queda en la bitácora de la promoción. */
127
+ @Expose()
128
+ @IsOptional()
129
+ @IsString()
130
+ @MaxLength(OFFERINGS_AUDIT_REASON_MAX_LENGTH)
131
+ reason?: string;
104
132
  }
@@ -1,6 +1,8 @@
1
1
  import { Expose } from 'class-transformer';
2
2
  import { ArrayMaxSize, IsArray, IsEnum, IsNotEmpty, IsOptional, IsString, MaxLength } from 'class-validator';
3
+ import { SEGMENT_MEMBER_IDS_MAX_ITEMS } from '../../constants/offeringsInputLimits';
3
4
  import { SegmentColorSlotEnum } from '../../enums/SegmentColorSlotEnum';
5
+ import { IsOptionalNonNull } from '../../validators/IsOptionalNonNull';
4
6
 
5
7
  /**
6
8
  * Body de PUT /segments/:segmentId — edición parcial (todos los campos opcionales). Sube `version`.
@@ -8,10 +10,13 @@ import { SegmentColorSlotEnum } from '../../enums/SegmentColorSlotEnum';
8
10
  *
9
11
  * Un arreglo vacío VACÍA la lista; ausente la deja como está. El segmento por omisión
10
12
  * (`isDefault`) solo acepta el cambio de nombre.
13
+ *
14
+ * `null` solo se acepta en `description`, donde la borra (así la manda el backoffice al vaciarla).
15
+ * En el resto da 400: un `null` en las listas reventaba con 500.
11
16
  */
12
17
  export class UpdateSegmentRequest {
13
18
  @Expose()
14
- @IsOptional()
19
+ @IsOptionalNonNull()
15
20
  @IsString()
16
21
  @MaxLength(120)
17
22
  name?: string;
@@ -20,16 +25,16 @@ export class UpdateSegmentRequest {
20
25
  @IsOptional()
21
26
  @IsString()
22
27
  @MaxLength(500)
23
- description?: string;
28
+ description?: string | null;
24
29
 
25
30
  @Expose()
26
- @IsOptional()
31
+ @IsOptionalNonNull()
27
32
  @IsEnum(SegmentColorSlotEnum)
28
33
  colorSlot?: SegmentColorSlotEnum;
29
34
 
30
35
  /** Reemplaza la lista entera; `[]` deja el segmento sin cohorts. */
31
36
  @Expose()
32
- @IsOptional()
37
+ @IsOptionalNonNull()
33
38
  @IsArray()
34
39
  @IsString({ each: true })
35
40
  @IsNotEmpty({ each: true })
@@ -38,16 +43,18 @@ export class UpdateSegmentRequest {
38
43
 
39
44
  /** @deprecated Usar `cohortIds`; se conserva para los consumidores que aún no migran. */
40
45
  @Expose()
41
- @IsOptional()
46
+ @IsOptionalNonNull()
42
47
  @IsArray()
43
48
  @IsString({ each: true })
44
49
  filterIds?: string[];
45
50
 
46
- /** Reemplaza la lista entera; `[]` saca a todos los agregados a mano. */
51
+ /** Reemplaza la lista entera; `[]` saca a todos los agregados a mano. Hasta 5 000, sin ids vacíos. */
47
52
  @Expose()
48
- @IsOptional()
53
+ @IsOptionalNonNull()
49
54
  @IsArray()
55
+ @ArrayMaxSize(SEGMENT_MEMBER_IDS_MAX_ITEMS)
50
56
  @IsString({ each: true })
57
+ @IsNotEmpty({ each: true })
51
58
  memberIds?: string[];
52
59
 
53
60
  @Expose()
@@ -1,26 +1,34 @@
1
1
  import { Expose, Type } from 'class-transformer';
2
2
  import {
3
+ ArrayMaxSize,
3
4
  ArrayNotEmpty,
4
5
  IsArray,
5
6
  IsBoolean,
6
7
  IsEnum,
7
8
  IsISO8601,
9
+ IsNotEmpty,
8
10
  IsOptional,
9
11
  IsString,
10
12
  MaxLength,
11
13
  ValidateNested,
12
14
  } from 'class-validator';
15
+ import { SCOPE_STORE_IDS_MAX_ITEMS } from '../../constants/offeringsInputLimits';
13
16
  import { CreditPlanLevelEnum } from '../../enums/CreditPlanLevelEnum';
17
+ import { IsOptionalNonNull } from '../../validators/IsOptionalNonNull';
14
18
  import { StorePolicyEasingDto, StorePolicyHardeningDto } from './CreateStorePolicyRequest';
15
19
 
16
20
  /**
17
21
  * Body de PUT /store-policies/:policyId — edición parcial (todos los campos opcionales).
18
22
  * Sube `version`. El cambio de estado va por su endpoint dedicado. Cada grupo de palancas se
19
23
  * reemplaza entero cuando viene, no se mergea clave a clave.
24
+ *
25
+ * Ausente deja el campo como está. `null` BORRA el valor en la descripción, los alcances y las
26
+ * fechas: el alcance vuelve a «toda la red» y la vigencia a «permanente» (así lo usa el backoffice).
27
+ * En `name`, `hardening`, `easing` y `suspended` un `null` da 400.
20
28
  */
21
29
  export class UpdateStorePolicyRequest {
22
30
  @Expose()
23
- @IsOptional()
31
+ @IsOptionalNonNull()
24
32
  @IsString()
25
33
  @MaxLength(120)
26
34
  name?: string;
@@ -29,70 +37,77 @@ export class UpdateStorePolicyRequest {
29
37
  @IsOptional()
30
38
  @IsString()
31
39
  @MaxLength(500)
32
- description?: string;
40
+ description?: string | null;
33
41
 
34
42
  @Expose()
35
- @IsOptional()
43
+ @IsOptionalNonNull()
36
44
  @ValidateNested()
37
45
  @Type(() => StorePolicyHardeningDto)
38
46
  hardening?: StorePolicyHardeningDto;
39
47
 
40
48
  @Expose()
41
- @IsOptional()
49
+ @IsOptionalNonNull()
42
50
  @ValidateNested()
43
51
  @Type(() => StorePolicyEasingDto)
44
52
  easing?: StorePolicyEasingDto;
45
53
 
46
54
  @Expose()
47
- @IsOptional()
55
+ @IsOptionalNonNull()
48
56
  @IsBoolean()
49
57
  suspended?: boolean;
50
58
 
59
+ /** Compuerta: SKUs habilitados; `null` = sin restricción. */
51
60
  @Expose()
52
61
  @IsOptional()
53
62
  @IsArray()
54
63
  @ArrayNotEmpty()
55
64
  @IsString({ each: true })
56
- allowedSkus?: string[];
65
+ allowedSkus?: string[] | null;
57
66
 
67
+ /** Alcance: tiendas; `null` = toda la red. Hasta 1 000. */
58
68
  @Expose()
59
69
  @IsOptional()
60
70
  @IsArray()
61
71
  @ArrayNotEmpty()
72
+ @ArrayMaxSize(SCOPE_STORE_IDS_MAX_ITEMS)
62
73
  @IsString({ each: true })
63
- storeIds?: string[];
74
+ @IsNotEmpty({ each: true })
75
+ storeIds?: string[] | null;
64
76
 
77
+ /** Alcance: niveles; `null` = todos. */
65
78
  @Expose()
66
79
  @IsOptional()
67
80
  @IsArray()
68
81
  @ArrayNotEmpty()
69
82
  @IsEnum(CreditPlanLevelEnum, { each: true })
70
- targetLevels?: CreditPlanLevelEnum[];
83
+ targetLevels?: CreditPlanLevelEnum[] | null;
71
84
 
72
- /** Alcance: segmentos alcanzados. */
85
+ /** Alcance: segmentos alcanzados; `null` = todos. */
73
86
  @Expose()
74
87
  @IsOptional()
75
88
  @IsArray()
76
89
  @ArrayNotEmpty()
77
90
  @IsString({ each: true })
78
- segmentIds?: string[];
91
+ segmentIds?: string[] | null;
79
92
 
93
+ /** Alcance: SKUs; `null` = todos los productos. */
80
94
  @Expose()
81
95
  @IsOptional()
82
96
  @IsArray()
83
97
  @ArrayNotEmpty()
84
98
  @IsString({ each: true })
85
- targetSkus?: string[];
99
+ targetSkus?: string[] | null;
86
100
 
87
101
  @Expose()
88
102
  @IsOptional()
89
103
  @IsISO8601()
90
- validFrom?: string;
104
+ validFrom?: string | null;
91
105
 
106
+ /** `null` = permanente. */
92
107
  @Expose()
93
108
  @IsOptional()
94
109
  @IsISO8601()
95
- validUntil?: string;
110
+ validUntil?: string | null;
96
111
 
97
112
  @Expose()
98
113
  @IsOptional()
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Resultado de liberar el lugar de un crédito en una promoción. `released` en `false` no es error:
3
+ * es un reenvío del mismo crédito o un crédito que nunca se contó, y el contador no se movió.
4
+ */
5
+ export interface ReleasePromotionResponse {
6
+ promotionId: string;
7
+ /** `true` si esta llamada bajó `creditsGenerated`. */
8
+ released: boolean;
9
+ /** Créditos generados después de la llamada. */
10
+ creditsGenerated: number;
11
+ }
@@ -31,6 +31,11 @@ export * from './enums/SegmentationRecountStatusEnum';
31
31
  // Constantes
32
32
  export * from './constants/promotionSkuBatch';
33
33
  export * from './constants/promotionRetailerScope';
34
+ export * from './constants/offeringsInputLimits';
35
+
36
+ // Validadores
37
+ export * from './validators/IsOptionalNonNull';
38
+ export * from './validators/MaxJsonBytes';
34
39
 
35
40
  // Entity DTOs
36
41
  export * from './dtos/CreditPlan';
@@ -53,6 +58,7 @@ export * from './dtos/requests/CreatePromotionRequest';
53
58
  export * from './dtos/requests/UpdatePromotionRequest';
54
59
  export * from './dtos/requests/ChangePromotionStatusRequest';
55
60
  export * from './dtos/requests/ConsumePromotionRequest';
61
+ export * from './dtos/requests/ReleasePromotionRequest';
56
62
  export * from './dtos/requests/ReservePromotionBonusRequest';
57
63
  export * from './dtos/requests/SettlePromotionBonusRequest';
58
64
  export * from './dtos/requests/CreateStorePolicyRequest';
@@ -85,6 +91,7 @@ export * from './dtos/responses/SimulationResultResponse';
85
91
  export * from './dtos/responses/PromotionResponse';
86
92
  export * from './dtos/responses/PromotionLogEntryResponse';
87
93
  export * from './dtos/responses/ConsumePromotionResponse';
94
+ export * from './dtos/responses/ReleasePromotionResponse';
88
95
  export * from './dtos/responses/PromotionBonusResponse';
89
96
  export * from './dtos/responses/ReservePromotionBonusResponse';
90
97
  export * from './dtos/responses/PromotedSkusResponse';
@@ -0,0 +1,12 @@
1
+ import { ValidateIf } from 'class-validator';
2
+
3
+ /**
4
+ * Campo opcional de un PUT parcial que NO admite `null`: ausente deja el valor como está, y `null`
5
+ * corre los validadores del campo y falla con 400.
6
+ *
7
+ * `@IsOptional()` deja pasar `null`; en los PUT de offerings ese `null` se convierte en `$REMOVE` y
8
+ * revienta con 500 en los atributos requeridos (L-100). Los campos que sí se borran con `null`
9
+ * (alcances, fechas, descripción) siguen con `@IsOptional()`.
10
+ */
11
+ export const IsOptionalNonNull = (): PropertyDecorator =>
12
+ ValidateIf((_object: object, value: unknown) => value !== undefined);
@@ -0,0 +1,28 @@
1
+ import { ValidateBy, ValidationOptions } from 'class-validator';
2
+
3
+ /** Bytes UTF-8 del valor serializado en JSON; `undefined` si no se puede serializar. */
4
+ export const jsonByteLength = (value: unknown): number | undefined => {
5
+ try {
6
+ const json = JSON.stringify(value);
7
+ return json === undefined ? undefined : new TextEncoder().encode(json).length;
8
+ } catch {
9
+ return undefined;
10
+ }
11
+ };
12
+
13
+ /** El valor, serializado en JSON, ocupa a lo más `maxBytes` bytes UTF-8. */
14
+ export const MaxJsonBytes = (maxBytes: number, validationOptions?: ValidationOptions): PropertyDecorator =>
15
+ ValidateBy(
16
+ {
17
+ name: 'maxJsonBytes',
18
+ constraints: [maxBytes],
19
+ validator: {
20
+ validate: (value: unknown): boolean => {
21
+ const bytes = jsonByteLength(value);
22
+ return bytes !== undefined && bytes <= maxBytes;
23
+ },
24
+ defaultMessage: (): string => `$property no puede pasar de ${maxBytes} bytes serializado`,
25
+ },
26
+ },
27
+ validationOptions,
28
+ );
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Topes de forma de la auditoría y del registro de accesos a PII (L-183). Son de cordura, no de
3
+ * tamaño total: el ítem de DynamoDB (400 KB) lo cuida la guardia de tamaño de loan-retail-audit.
4
+ * Los productores de hoy mandan a lo más 7 `changes`, 4 `context`, 10 `fields` y valores de ~40
5
+ * caracteres.
6
+ */
7
+ export const AUDIT_TRAIL_CHANGES_MAX_ITEMS = 100;
8
+ export const AUDIT_TRAIL_CONTEXT_MAX_ITEMS = 50;
9
+
10
+ /** Nombre de un campo o clave de contexto. */
11
+ export const AUDIT_FIELD_NAME_MAX_LENGTH = 128;
12
+
13
+ /** Valor serializado de un cambio o de un dato de contexto (4 KB). */
14
+ export const AUDIT_VALUE_MAX_LENGTH = 4096;
15
+
16
+ /** Campos PII por acceso. */
17
+ export const PII_ACCESS_FIELDS_MAX_ITEMS = 50;
18
+
19
+ /** Un id de recurso en `resourceIds` (hoy ULID de 26 o `acc-` + 32 hex). */
20
+ export const PII_RESOURCE_ID_MAX_LENGTH = 128;
@@ -1,22 +1,26 @@
1
1
  import { Expose } from 'class-transformer';
2
- import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
2
+ import { IsNotEmpty, IsOptional, IsString, MaxLength } from 'class-validator';
3
+ import { AUDIT_FIELD_NAME_MAX_LENGTH, AUDIT_VALUE_MAX_LENGTH } from '../constants/auditInputLimits';
3
4
 
4
5
  /** Un campo que cambió; los valores van serializados a string y nunca llevan PII. */
5
6
  export class AuditTrailChange {
6
7
  @Expose()
7
8
  @IsString()
8
9
  @IsNotEmpty()
10
+ @MaxLength(AUDIT_FIELD_NAME_MAX_LENGTH)
9
11
  field!: string;
10
12
 
11
13
  /** Ausente en un alta. */
12
14
  @Expose()
13
15
  @IsOptional()
14
16
  @IsString()
17
+ @MaxLength(AUDIT_VALUE_MAX_LENGTH)
15
18
  oldValue?: string;
16
19
 
17
20
  /** Ausente en una baja. */
18
21
  @Expose()
19
22
  @IsOptional()
20
23
  @IsString()
24
+ @MaxLength(AUDIT_VALUE_MAX_LENGTH)
21
25
  newValue?: string;
22
26
  }
@@ -1,14 +1,17 @@
1
1
  import { Expose } from 'class-transformer';
2
- import { IsNotEmpty, IsString } from 'class-validator';
2
+ import { IsNotEmpty, IsString, MaxLength } from 'class-validator';
3
+ import { AUDIT_FIELD_NAME_MAX_LENGTH, AUDIT_VALUE_MAX_LENGTH } from '../constants/auditInputLimits';
3
4
 
4
5
  /** Dato que la acción no cambió y se guarda para leer la entrada sin abrir otra pantalla. */
5
6
  export class AuditTrailContextEntry {
6
7
  @Expose()
7
8
  @IsString()
8
9
  @IsNotEmpty()
10
+ @MaxLength(AUDIT_FIELD_NAME_MAX_LENGTH)
9
11
  key!: string;
10
12
 
11
13
  @Expose()
12
14
  @IsString()
15
+ @MaxLength(AUDIT_VALUE_MAX_LENGTH)
13
16
  value!: string;
14
17
  }
@@ -1,5 +1,11 @@
1
1
  import { Expose } from 'class-transformer';
2
- import { ArrayNotEmpty, IsArray, IsEnum, IsISO8601, IsNotEmpty, IsOptional, IsString } from 'class-validator';
2
+ import { ArrayMaxSize, ArrayNotEmpty, IsArray, IsEnum, IsNotEmpty, IsOptional, IsString, MaxLength } from 'class-validator';
3
+ import {
4
+ AUDIT_FIELD_NAME_MAX_LENGTH,
5
+ PII_ACCESS_FIELDS_MAX_ITEMS,
6
+ PII_RESOURCE_ID_MAX_LENGTH,
7
+ } from '../../constants/auditInputLimits';
8
+ import { IsIsoDateTime } from '../../validators/IsIsoDateTime';
3
9
  import { PiiAccessActionEnum } from '../../enums/PiiAccessActionEnum';
4
10
  import { PiiResourceTypeEnum } from '../../enums/PiiResourceTypeEnum';
5
11
 
@@ -11,8 +17,9 @@ export class CreatePiiAccessLogRequest {
11
17
  @IsNotEmpty()
12
18
  accessId!: string;
13
19
 
20
+ /** Fecha-hora ISO-8601 completa con zona, como `toISOString()`. */
14
21
  @Expose()
15
- @IsISO8601()
22
+ @IsIsoDateTime()
16
23
  occurredAt!: string;
17
24
 
18
25
  @Expose()
@@ -49,16 +56,26 @@ export class CreatePiiAccessLogRequest {
49
56
  @IsNotEmpty()
50
57
  resourceId!: string;
51
58
 
52
- /** Nombres de los campos PII que viajaron; nunca sus valores. */
59
+ /** Nombres de los campos PII que viajaron; nunca sus valores. Sin nombres vacíos. */
53
60
  @Expose()
54
61
  @IsArray()
55
62
  @ArrayNotEmpty()
63
+ @ArrayMaxSize(PII_ACCESS_FIELDS_MAX_ITEMS)
56
64
  @IsString({ each: true })
65
+ @IsNotEmpty({ each: true })
66
+ @MaxLength(AUDIT_FIELD_NAME_MAX_LENGTH, { each: true })
57
67
  fields!: string[];
58
68
 
69
+ /**
70
+ * Ids del listado, sin vacíos y de hasta 128 caracteres cada uno. Sin tope de cantidad todavía:
71
+ * `/backoffice/customers` de retail-wizard manda todos los clientes de la ventana (miles), y un
72
+ * rechazo tumba el listado. El tope llega con el fan-out por id del central (L-184).
73
+ */
59
74
  @Expose()
60
75
  @IsOptional()
61
76
  @IsArray()
62
77
  @IsString({ each: true })
78
+ @IsNotEmpty({ each: true })
79
+ @MaxLength(PII_RESOURCE_ID_MAX_LENGTH, { each: true })
63
80
  resourceIds?: string[];
64
81
  }
@@ -1,5 +1,7 @@
1
1
  import { Expose, Type } from 'class-transformer';
2
- import { IsArray, IsIn, IsEnum, IsISO8601, IsNotEmpty, IsOptional, IsString, ValidateNested } from 'class-validator';
2
+ import { ArrayMaxSize, IsArray, IsIn, IsEnum, IsNotEmpty, IsOptional, IsString, ValidateNested } from 'class-validator';
3
+ import { AUDIT_TRAIL_CHANGES_MAX_ITEMS, AUDIT_TRAIL_CONTEXT_MAX_ITEMS } from '../constants/auditInputLimits';
4
+ import { IsIsoDateTime } from '../validators/IsIsoDateTime';
3
5
  import { AUDIT_TRAIL_ENTITY_TYPES, AuditTrailEntityType } from '../constants/auditTrailEntityTypes';
4
6
  import { AuditTrailChange } from '../dtos/AuditTrailChange';
5
7
  import { AuditTrailContextEntry } from '../dtos/AuditTrailContextEntry';
@@ -27,12 +29,14 @@ export class AuditTrailRecordedV1 {
27
29
 
28
30
  @Expose()
29
31
  @IsArray()
32
+ @ArrayMaxSize(AUDIT_TRAIL_CHANGES_MAX_ITEMS)
30
33
  @ValidateNested({ each: true })
31
34
  @Type(() => AuditTrailChange)
32
35
  changes!: AuditTrailChange[];
33
36
 
34
37
  @Expose()
35
38
  @IsArray()
39
+ @ArrayMaxSize(AUDIT_TRAIL_CONTEXT_MAX_ITEMS)
36
40
  @ValidateNested({ each: true })
37
41
  @Type(() => AuditTrailContextEntry)
38
42
  context!: AuditTrailContextEntry[];
@@ -48,8 +52,9 @@ export class AuditTrailRecordedV1 {
48
52
  @IsNotEmpty()
49
53
  sourceLambda!: string;
50
54
 
55
+ /** Fecha-hora ISO-8601 completa con zona, como `toISOString()`. */
51
56
  @Expose()
52
- @IsISO8601()
57
+ @IsIsoDateTime()
53
58
  occurredAt!: string;
54
59
 
55
60
  @Expose()
@@ -7,6 +7,10 @@ export * from './enums/PiiAccessActionEnum';
7
7
 
8
8
  // Constantes
9
9
  export * from './constants/auditTrailEntityTypes';
10
+ export * from './constants/auditInputLimits';
11
+
12
+ // Validadores
13
+ export * from './validators/IsIsoDateTime';
10
14
 
11
15
  // DTOs
12
16
  export * from './dtos/AuditTrailChange';
@@ -0,0 +1,33 @@
1
+ import { isISO8601, ValidateBy, ValidationOptions } from 'class-validator';
2
+
3
+ /** Fecha-hora completa: fecha, hora con segundos y zona (`Z` u offset). Como `toISOString()`. */
4
+ const ISO_DATE_TIME_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})$/;
5
+
6
+ /**
7
+ * `true` si el valor es una fecha-hora ISO-8601 completa, con zona, que existe en el calendario y
8
+ * que `Date.parse` entiende.
9
+ */
10
+ export const isIsoDateTime = (value: unknown): boolean =>
11
+ typeof value === 'string' &&
12
+ ISO_DATE_TIME_PATTERN.test(value) &&
13
+ isISO8601(value, { strict: true, strictSeparator: true }) &&
14
+ Number.isFinite(Date.parse(value));
15
+
16
+ /**
17
+ * Fecha-hora ISO-8601 completa con zona (L-188).
18
+ *
19
+ * `@IsISO8601()` acepta `2026-W44-1`, `2026-305` o `2026`, que `new Date()` no entiende, y la fecha
20
+ * sin zona, que cada runtime lee distinto. El central calcula el vencimiento del registro con ella.
21
+ */
22
+ export const IsIsoDateTime = (validationOptions?: ValidationOptions): PropertyDecorator =>
23
+ ValidateBy(
24
+ {
25
+ name: 'isIsoDateTime',
26
+ validator: {
27
+ validate: (value: unknown): boolean => isIsoDateTime(value),
28
+ defaultMessage: (): string =>
29
+ '$property debe ser una fecha-hora ISO-8601 completa con zona (p. ej. 2026-11-03T10:00:00.000Z)',
30
+ },
31
+ },
32
+ validationOptions,
33
+ );
@@ -3,16 +3,18 @@
3
3
  * Modelias la devuelve completa; el camino Círculo+SCI no produce oferta, el plan lo resuelve
4
4
  * loan-offerings por nivel).
5
5
  *
6
- * ⚠️ Los valores numéricos se transportan TAL CUAL los entrega el proveedor (el contrato Modelias
7
- * no confirma unidades de `linea_credito`/`enganche` — ver TD del módulo modelias). NO asumir cents.
6
+ * Se guarda TAL CUAL la entrega el proveedor, sin convertir unidades (decisión D-4 de la auditoría,
7
+ * L-181). Unidades observadas contra Modelias DEV (A9 de loan-scoring, `docs/F2/ASSUMPTIONS.md`):
8
+ * `linea_credito` 10000/7500/4000 y `enganche` 0.1/0.2/0.35. Quien la use para originar convierte
9
+ * a centavos y a porcentaje; no la sumes a montos en centavos.
8
10
  */
9
11
  export interface ScoringOffer {
10
- /** Línea de crédito aprobada (unidad según proveedor). */
12
+ /** Línea de crédito aprobada, en PESOS MXN enteros (no centavos): `10000` = $10,000. */
11
13
  creditLine: number | null;
12
- /** Enganche requerido (unidad/base según proveedor). */
14
+ /** Enganche requerido como FRACCIÓN del precio (no monto ni porcentaje): `0.2` = 20%. */
13
15
  downPayment: number | null;
14
- /** Plazos ofrecidos, en semanas. */
16
+ /** Plazos ofrecidos, en semanas (Modelias usa múltiplos de 13). */
15
17
  termsWeeks: number[];
16
- /** Tasas por plazo (clave = plazo, valor = tasa decimal). */
18
+ /** Tasas por plazo: clave = plazo en semanas, valor = tasa en decimal. */
17
19
  rates: Record<string, number>;
18
20
  }