@fiado/type-kit 3.470.0 → 3.472.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 (48) hide show
  1. package/_test_/unit/common/IsStringValueRecord.test.ts +25 -0
  2. package/_test_/unit/moneyTransfer/accountDtos.test.ts +206 -0
  3. package/_test_/unit/moneyTransfer/dtos.test.ts +18 -8
  4. package/_test_/unit/platformRbac/dtos/tenantUserCustomFields.test.ts +55 -0
  5. package/bin/common/index.d.ts +1 -0
  6. package/bin/common/index.js +2 -0
  7. package/bin/common/validators/IsStringValueRecord.d.ts +5 -0
  8. package/bin/common/validators/IsStringValueRecord.js +25 -0
  9. package/bin/messagesConnector/validators/IsStringValueRecord.d.ts +1 -5
  10. package/bin/messagesConnector/validators/IsStringValueRecord.js +3 -21
  11. package/bin/moneyTransfer/dtos/AccountImpactResponse.d.ts +18 -0
  12. package/bin/moneyTransfer/dtos/AccountImpactResponse.js +14 -0
  13. package/bin/moneyTransfer/dtos/AccountListResponse.d.ts +6 -0
  14. package/bin/moneyTransfer/dtos/AccountListResponse.js +7 -0
  15. package/bin/moneyTransfer/dtos/AccountResponse.d.ts +23 -0
  16. package/bin/moneyTransfer/dtos/AccountResponse.js +13 -0
  17. package/bin/moneyTransfer/dtos/AccountWithImpactResponse.d.ts +11 -0
  18. package/bin/moneyTransfer/dtos/AccountWithImpactResponse.js +11 -0
  19. package/bin/moneyTransfer/dtos/CreateAccountRequest.d.ts +27 -0
  20. package/bin/moneyTransfer/dtos/CreateAccountRequest.js +75 -0
  21. package/bin/moneyTransfer/dtos/MoneyTransferProductRequest.d.ts +6 -5
  22. package/bin/moneyTransfer/dtos/MoneyTransferProductRequest.js +3 -14
  23. package/bin/moneyTransfer/dtos/SettlementResponse.d.ts +6 -0
  24. package/bin/moneyTransfer/dtos/TransactionDetailResponse.d.ts +7 -0
  25. package/bin/moneyTransfer/dtos/UpdateAccountRequest.d.ts +19 -0
  26. package/bin/moneyTransfer/dtos/UpdateAccountRequest.js +67 -0
  27. package/bin/moneyTransfer/index.d.ts +6 -0
  28. package/bin/moneyTransfer/index.js +6 -0
  29. package/bin/platformRbac/dtos/CreateTenantUserRequest.d.ts +4 -0
  30. package/bin/platformRbac/dtos/CreateTenantUserRequest.js +9 -0
  31. package/bin/platformRbac/dtos/UpdateTenantUserRequest.d.ts +5 -2
  32. package/bin/platformRbac/dtos/UpdateTenantUserRequest.js +10 -2
  33. package/package.json +1 -1
  34. package/src/common/index.ts +3 -0
  35. package/src/common/validators/IsStringValueRecord.ts +13 -0
  36. package/src/messagesConnector/validators/IsStringValueRecord.ts +2 -12
  37. package/src/moneyTransfer/dtos/AccountImpactResponse.ts +18 -0
  38. package/src/moneyTransfer/dtos/AccountListResponse.ts +7 -0
  39. package/src/moneyTransfer/dtos/AccountResponse.ts +24 -0
  40. package/src/moneyTransfer/dtos/AccountWithImpactResponse.ts +12 -0
  41. package/src/moneyTransfer/dtos/CreateAccountRequest.ts +60 -0
  42. package/src/moneyTransfer/dtos/MoneyTransferProductRequest.ts +10 -14
  43. package/src/moneyTransfer/dtos/SettlementResponse.ts +6 -0
  44. package/src/moneyTransfer/dtos/TransactionDetailResponse.ts +7 -0
  45. package/src/moneyTransfer/dtos/UpdateAccountRequest.ts +49 -0
  46. package/src/moneyTransfer/index.ts +6 -0
  47. package/src/platformRbac/dtos/CreateTenantUserRequest.ts +8 -1
  48. package/src/platformRbac/dtos/UpdateTenantUserRequest.ts +9 -3
@@ -6,9 +6,14 @@ import { BatchScheduleRequest } from "./BatchScheduleRequest";
6
6
  /**
7
7
  * Producto de un servicio de transferencia de dinero (Centro de Beneficios). Los campos en
8
8
  * español espejan el catálogo legacy de pago de servicios; los campos en inglés son la
9
- * configuración propia del riel (canal, modo de liquidación, beneficiario, lote).
9
+ * configuración propia del riel (canal, modo de liquidación, lote). El beneficiario ya no vive
10
+ * acá: se mudó a la cuenta destino (DEC-046).
10
11
  */
11
12
  export declare class MoneyTransferProductRequest {
13
+ /**
14
+ * DEC-041: `idProducto` es el `accountId` de la cuenta destino (`ACC-BANORTE-01`), NO la CLABE.
15
+ * La CLABE vive en `MoneyTransferAccount_GT` y se resuelve al pagar (F1) y al enviar (F4).
16
+ */
12
17
  idProducto: string;
13
18
  producto: string;
14
19
  precio: number;
@@ -26,10 +31,6 @@ export declare class MoneyTransferProductRequest {
26
31
  enabled: boolean;
27
32
  channel: TransferChannelEnum;
28
33
  settlementMode: SettlementModeEnum;
29
- /** Nombre del beneficiario tal como viaja al riel (SPEI acepta máx. 40 chars). */
30
- beneficiaryName: string;
31
- /** RFC del beneficiario: 12 chars persona moral, 13 persona física. */
32
- beneficiaryRfc?: string;
33
34
  minAmount?: number;
34
35
  maxAmount?: number;
35
36
  /** Requerido operativamente cuando `settlementMode = BATCH`; lo valida el conector. */
@@ -20,7 +20,8 @@ const BatchScheduleRequest_1 = require("./BatchScheduleRequest");
20
20
  /**
21
21
  * Producto de un servicio de transferencia de dinero (Centro de Beneficios). Los campos en
22
22
  * español espejan el catálogo legacy de pago de servicios; los campos en inglés son la
23
- * configuración propia del riel (canal, modo de liquidación, beneficiario, lote).
23
+ * configuración propia del riel (canal, modo de liquidación, lote). El beneficiario ya no vive
24
+ * acá: se mudó a la cuenta destino (DEC-046).
24
25
  */
25
26
  class MoneyTransferProductRequest {
26
27
  }
@@ -28,7 +29,7 @@ exports.MoneyTransferProductRequest = MoneyTransferProductRequest;
28
29
  __decorate([
29
30
  (0, class_validator_1.IsString)(),
30
31
  (0, class_validator_1.IsNotEmpty)(),
31
- (0, class_validator_1.MaxLength)(34),
32
+ (0, class_validator_1.MaxLength)(64),
32
33
  __metadata("design:type", String)
33
34
  ], MoneyTransferProductRequest.prototype, "idProducto", void 0);
34
35
  __decorate([
@@ -99,18 +100,6 @@ __decorate([
99
100
  (0, class_validator_1.IsEnum)(SettlementModeEnum_1.SettlementModeEnum),
100
101
  __metadata("design:type", String)
101
102
  ], MoneyTransferProductRequest.prototype, "settlementMode", void 0);
102
- __decorate([
103
- (0, class_validator_1.IsString)(),
104
- (0, class_validator_1.IsNotEmpty)(),
105
- (0, class_validator_1.MaxLength)(40),
106
- __metadata("design:type", String)
107
- ], MoneyTransferProductRequest.prototype, "beneficiaryName", void 0);
108
- __decorate([
109
- (0, class_validator_1.IsOptional)(),
110
- (0, class_validator_1.IsString)(),
111
- (0, class_validator_1.Length)(12, 13),
112
- __metadata("design:type", String)
113
- ], MoneyTransferProductRequest.prototype, "beneficiaryRfc", void 0);
114
103
  __decorate([
115
104
  (0, class_validator_1.IsOptional)(),
116
105
  (0, class_validator_1.IsNumber)(),
@@ -19,6 +19,12 @@ export declare class SettlementResponse {
19
19
  scheduledAt?: number;
20
20
  /** Id de la transacción en el riel (ACH) o clave de rastreo (SPEI). */
21
21
  achTransactionId?: string;
22
+ /**
23
+ * DEC-044: cuenta (CLABE) a la que se INTENTÓ enviar este lote, escrita al tomar el lock `SENDING`.
24
+ * No significa "cuenta que recibió el dinero": la evidencia de dinero movido es este campo + un
25
+ * estado de la terna SENT/SETTLED/RETURNED + `achTransactionId`.
26
+ */
27
+ sentToAccount?: string;
22
28
  returnReason?: string;
23
29
  lastError?: string;
24
30
  /** `true` cuando el envío no obtuvo respuesta concluyente del riel (posible duplicado). */
@@ -19,6 +19,13 @@ export declare class TransactionDetailResponse {
19
19
  status: TransactionDetailStatusEnum;
20
20
  /** Presente cuando el folio ya fue agrupado en una liquidación. */
21
21
  settlementId?: string;
22
+ /**
23
+ * DEC-051: `true` cuando la cuenta de este folio no existe o está deshabilitada. El folio se queda
24
+ * en `PENDING_SETTLEMENT` (DEC-048) y no puede liquidarse hasta que operaciones arregle la cuenta;
25
+ * sin esta marca quedaría invisible en la cola y se descubriría cuando el usuario reclame.
26
+ * Solo lo calcula el listado de pendientes de backoffice (F9); en otros contextos viene ausente.
27
+ */
28
+ blockedByAccount?: boolean;
22
29
  /** Epoch ms. */
23
30
  createdAt: number;
24
31
  }
@@ -0,0 +1,19 @@
1
+ import { TransferChannelEnum } from "../enums/TransferChannelEnum";
2
+ /**
3
+ * Edición de una cuenta destino (DEC-045: actualización en el mismo registro, sin versionado).
4
+ * Todos los campos son opcionales: se envía solo lo que cambia. `accountId` no está porque es la PK
5
+ * y viaja en el path. `enabled` se cambia acá, sin endpoint propio (evita duplicar la lógica de impacto).
6
+ *
7
+ * Sin `idempotencyKey`: la edición es idempotente por naturaleza (escribe el estado deseado).
8
+ */
9
+ export declare class UpdateAccountRequest {
10
+ channel?: TransferChannelEnum;
11
+ /** El campo que hace útil todo este cambio: corregir el número no obliga a tocar ningún producto. */
12
+ account?: string;
13
+ beneficiaryName?: string;
14
+ beneficiaryRfc?: string;
15
+ bankName?: string;
16
+ currency?: string;
17
+ /** DEC-049: nunca se borra una cuenta, solo se deshabilita. */
18
+ enabled?: boolean;
19
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.UpdateAccountRequest = void 0;
13
+ const class_validator_1 = require("class-validator");
14
+ const TransferChannelEnum_1 = require("../enums/TransferChannelEnum");
15
+ /**
16
+ * Edición de una cuenta destino (DEC-045: actualización en el mismo registro, sin versionado).
17
+ * Todos los campos son opcionales: se envía solo lo que cambia. `accountId` no está porque es la PK
18
+ * y viaja en el path. `enabled` se cambia acá, sin endpoint propio (evita duplicar la lógica de impacto).
19
+ *
20
+ * Sin `idempotencyKey`: la edición es idempotente por naturaleza (escribe el estado deseado).
21
+ */
22
+ class UpdateAccountRequest {
23
+ }
24
+ exports.UpdateAccountRequest = UpdateAccountRequest;
25
+ __decorate([
26
+ (0, class_validator_1.IsOptional)(),
27
+ (0, class_validator_1.IsEnum)(TransferChannelEnum_1.TransferChannelEnum),
28
+ __metadata("design:type", String)
29
+ ], UpdateAccountRequest.prototype, "channel", void 0);
30
+ __decorate([
31
+ (0, class_validator_1.IsOptional)(),
32
+ (0, class_validator_1.IsString)(),
33
+ (0, class_validator_1.IsNotEmpty)(),
34
+ (0, class_validator_1.MaxLength)(34),
35
+ __metadata("design:type", String)
36
+ ], UpdateAccountRequest.prototype, "account", void 0);
37
+ __decorate([
38
+ (0, class_validator_1.IsOptional)(),
39
+ (0, class_validator_1.IsString)(),
40
+ (0, class_validator_1.IsNotEmpty)(),
41
+ (0, class_validator_1.MaxLength)(40),
42
+ __metadata("design:type", String)
43
+ ], UpdateAccountRequest.prototype, "beneficiaryName", void 0);
44
+ __decorate([
45
+ (0, class_validator_1.IsOptional)(),
46
+ (0, class_validator_1.IsString)(),
47
+ (0, class_validator_1.Length)(12, 13),
48
+ __metadata("design:type", String)
49
+ ], UpdateAccountRequest.prototype, "beneficiaryRfc", void 0);
50
+ __decorate([
51
+ (0, class_validator_1.IsOptional)(),
52
+ (0, class_validator_1.IsString)(),
53
+ (0, class_validator_1.IsNotEmpty)(),
54
+ (0, class_validator_1.MaxLength)(60),
55
+ __metadata("design:type", String)
56
+ ], UpdateAccountRequest.prototype, "bankName", void 0);
57
+ __decorate([
58
+ (0, class_validator_1.IsOptional)(),
59
+ (0, class_validator_1.IsString)(),
60
+ (0, class_validator_1.Length)(3, 3),
61
+ __metadata("design:type", String)
62
+ ], UpdateAccountRequest.prototype, "currency", void 0);
63
+ __decorate([
64
+ (0, class_validator_1.IsOptional)(),
65
+ (0, class_validator_1.IsBoolean)(),
66
+ __metadata("design:type", Boolean)
67
+ ], UpdateAccountRequest.prototype, "enabled", void 0);
@@ -11,7 +11,13 @@ export * from './dtos/MoneyTransferServiceUpdateRequest';
11
11
  export * from './dtos/CreateSettlementRequest';
12
12
  export * from './dtos/SettlementActionRequest';
13
13
  export * from './dtos/RescheduleSettlementRequest';
14
+ export * from './dtos/CreateAccountRequest';
15
+ export * from './dtos/UpdateAccountRequest';
14
16
  export * from './dtos/TransactionDetailResponse';
15
17
  export * from './dtos/SettlementResponse';
16
18
  export * from './dtos/PendingDetailsResponse';
17
19
  export * from './dtos/SettlementListResponse';
20
+ export * from './dtos/AccountResponse';
21
+ export * from './dtos/AccountListResponse';
22
+ export * from './dtos/AccountImpactResponse';
23
+ export * from './dtos/AccountWithImpactResponse';
@@ -33,8 +33,14 @@ __exportStar(require("./dtos/MoneyTransferServiceUpdateRequest"), exports);
33
33
  __exportStar(require("./dtos/CreateSettlementRequest"), exports);
34
34
  __exportStar(require("./dtos/SettlementActionRequest"), exports);
35
35
  __exportStar(require("./dtos/RescheduleSettlementRequest"), exports);
36
+ __exportStar(require("./dtos/CreateAccountRequest"), exports);
37
+ __exportStar(require("./dtos/UpdateAccountRequest"), exports);
36
38
  //DTOs — responses
37
39
  __exportStar(require("./dtos/TransactionDetailResponse"), exports);
38
40
  __exportStar(require("./dtos/SettlementResponse"), exports);
39
41
  __exportStar(require("./dtos/PendingDetailsResponse"), exports);
40
42
  __exportStar(require("./dtos/SettlementListResponse"), exports);
43
+ __exportStar(require("./dtos/AccountResponse"), exports);
44
+ __exportStar(require("./dtos/AccountListResponse"), exports);
45
+ __exportStar(require("./dtos/AccountImpactResponse"), exports);
46
+ __exportStar(require("./dtos/AccountWithImpactResponse"), exports);
@@ -8,6 +8,7 @@ import { PermissionScope } from '../enums/PermissionScope';
8
8
  * - Idempotente por identidad Cognito.
9
9
  * - `scope`/`scopeRef` = grant del rol (ej. LEVEL_3 + storeId para un vendedor de tienda).
10
10
  * - `createdBy` = cognitoSub del admin real (audit + grantedBy del assignment).
11
+ * - `customFields` = valores de los campos personalizados del tenant; si el correo ya tiene usuario, se fusionan.
11
12
  */
12
13
  export declare class CreateTenantUserRequest {
13
14
  email: string;
@@ -16,4 +17,7 @@ export declare class CreateTenantUserRequest {
16
17
  scope: PermissionScope;
17
18
  scopeRef: string;
18
19
  createdBy: string;
20
+ customFields?: {
21
+ [key: string]: string;
22
+ };
19
23
  }
@@ -13,6 +13,7 @@ exports.CreateTenantUserRequest = void 0;
13
13
  const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
15
  const PermissionScope_1 = require("../enums/PermissionScope");
16
+ const IsStringValueRecord_1 = require("../../common/validators/IsStringValueRecord");
16
17
  /**
17
18
  * Body del POST /internal/tenants/{tenantId}/users (endpoint privado, VPC/ANONIMUS). `tenantId` viaja en el path.
18
19
  * Provisiona Cognito + SharedUser + RoleAssignment (rol + scopeRef) en RBAC — reusa UserProvisioningService.
@@ -22,6 +23,7 @@ const PermissionScope_1 = require("../enums/PermissionScope");
22
23
  * - Idempotente por identidad Cognito.
23
24
  * - `scope`/`scopeRef` = grant del rol (ej. LEVEL_3 + storeId para un vendedor de tienda).
24
25
  * - `createdBy` = cognitoSub del admin real (audit + grantedBy del assignment).
26
+ * - `customFields` = valores de los campos personalizados del tenant; si el correo ya tiene usuario, se fusionan.
25
27
  */
26
28
  class CreateTenantUserRequest {
27
29
  }
@@ -61,3 +63,10 @@ __decorate([
61
63
  (0, class_validator_1.IsNotEmpty)(),
62
64
  __metadata("design:type", String)
63
65
  ], CreateTenantUserRequest.prototype, "createdBy", void 0);
66
+ __decorate([
67
+ (0, class_transformer_1.Expose)(),
68
+ (0, class_validator_1.IsOptional)(),
69
+ (0, class_validator_1.IsObject)(),
70
+ (0, class_validator_1.Validate)(IsStringValueRecord_1.IsStringValueRecord, { message: 'customFields: todos los valores deben ser string' }),
71
+ __metadata("design:type", Object)
72
+ ], CreateTenantUserRequest.prototype, "customFields", void 0);
@@ -1,8 +1,11 @@
1
1
  /**
2
- * Body del PUT que renombra a un usuario. El correo es inmutable en el rbac y el grupo se mueve
3
- * reasignando el rol, así que aquí no viaja ninguno de los dos.
2
+ * Body del PUT que edita a un usuario: nombre y campos personalizados. `customFields` se fusiona con los
3
+ * actuales (solo cambian las claves enviadas; "" borra una). Correo y grupo no viajan aquí.
4
4
  */
5
5
  export declare class UpdateTenantUserRequest {
6
6
  displayName?: string;
7
+ customFields?: {
8
+ [key: string]: string;
9
+ };
7
10
  actedBy: string;
8
11
  }
@@ -12,9 +12,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.UpdateTenantUserRequest = void 0;
13
13
  const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
+ const IsStringValueRecord_1 = require("../../common/validators/IsStringValueRecord");
15
16
  /**
16
- * Body del PUT que renombra a un usuario. El correo es inmutable en el rbac y el grupo se mueve
17
- * reasignando el rol, así que aquí no viaja ninguno de los dos.
17
+ * Body del PUT que edita a un usuario: nombre y campos personalizados. `customFields` se fusiona con los
18
+ * actuales (solo cambian las claves enviadas; "" borra una). Correo y grupo no viajan aquí.
18
19
  */
19
20
  class UpdateTenantUserRequest {
20
21
  }
@@ -27,6 +28,13 @@ __decorate([
27
28
  (0, class_validator_1.MaxLength)(120),
28
29
  __metadata("design:type", String)
29
30
  ], UpdateTenantUserRequest.prototype, "displayName", void 0);
31
+ __decorate([
32
+ (0, class_transformer_1.Expose)(),
33
+ (0, class_validator_1.IsOptional)(),
34
+ (0, class_validator_1.IsObject)(),
35
+ (0, class_validator_1.Validate)(IsStringValueRecord_1.IsStringValueRecord, { message: 'customFields: todos los valores deben ser string' }),
36
+ __metadata("design:type", Object)
37
+ ], UpdateTenantUserRequest.prototype, "customFields", void 0);
30
38
  __decorate([
31
39
  (0, class_transformer_1.Expose)(),
32
40
  (0, class_validator_1.IsString)(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiado/type-kit",
3
- "version": "3.470.0",
3
+ "version": "3.472.0",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",
@@ -1,6 +1,9 @@
1
1
  //decorators
2
2
  export * from './decorators/Trim';
3
3
 
4
+ //validators
5
+ export * from './validators/IsStringValueRecord';
6
+
4
7
  //dtos
5
8
  export * from './dtos/ProcessValidationResultDto';
6
9
 
@@ -0,0 +1,13 @@
1
+ import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator';
2
+
3
+ // Valida que un objeto sea un mapa clave → string.
4
+ @ValidatorConstraint({ name: 'IsStringValueRecord', async: false })
5
+ export class IsStringValueRecord implements ValidatorConstraintInterface {
6
+ validate(value: unknown, _args: ValidationArguments): boolean {
7
+ if (!value || typeof value !== 'object') return false;
8
+ return Object.values(value as Record<string, unknown>).every(v => typeof v === 'string');
9
+ }
10
+ defaultMessage(_args: ValidationArguments): string {
11
+ return 'All values in variables must be strings';
12
+ }
13
+ }
@@ -1,12 +1,2 @@
1
- import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator';
2
-
3
- @ValidatorConstraint({ name: 'IsStringValueRecord', async: false })
4
- export class IsStringValueRecord implements ValidatorConstraintInterface {
5
- validate(value: unknown, _args: ValidationArguments): boolean {
6
- if (!value || typeof value !== 'object') return false;
7
- return Object.values(value as Record<string, unknown>).every(v => typeof v === 'string');
8
- }
9
- defaultMessage(_args: ValidationArguments): string {
10
- return 'All values in variables must be strings';
11
- }
12
- }
1
+ // Movido a common: se reexporta para no romper a quien importa desde messagesConnector.
2
+ export { IsStringValueRecord } from '../../common/validators/IsStringValueRecord';
@@ -0,0 +1,18 @@
1
+ /**
2
+ * DEC-050: qué se rompe antes de romperlo. Deshabilitar o editar una cuenta NO bloquea aunque haya
3
+ * folios pendientes o liquidaciones en vuelo — pero el operador tiene que ver el número en el momento
4
+ * de apretar el botón, no descubrirlo después.
5
+ *
6
+ * `pendingSettlements` cuenta liquidaciones en estado NO terminal (CREATED, SCHEDULED, SENDING, FAILED):
7
+ * esas son las que todavía pueden salir al riel y por lo tanto resolverían la cuenta editada (DEC-042).
8
+ */
9
+ export class AccountImpactResponse {
10
+ /** Productos del catálogo que referencian esta cuenta por `idProducto`. */
11
+ products!: number;
12
+ /** Folios en `PENDING_SETTLEMENT` de esta cuenta (GSI `pendingProductId-createdAt-index`). */
13
+ pendingDetails!: number;
14
+ /** Suma de los montos de esos folios. */
15
+ pendingAmount!: number;
16
+ /** Liquidaciones en estado no terminal que todavía pueden salir al riel. */
17
+ pendingSettlements!: number;
18
+ }
@@ -0,0 +1,7 @@
1
+ import { AccountResponse } from "./AccountResponse";
2
+
3
+ /** Página de cuentas de un tenant (GSI `tenantId-createdAt-index`). Paginada por cursor opaco. */
4
+ export class AccountListResponse {
5
+ items!: AccountResponse[];
6
+ nextToken?: string;
7
+ }
@@ -0,0 +1,24 @@
1
+ import { TransferChannelEnum } from "../enums/TransferChannelEnum";
2
+
3
+ /**
4
+ * Cuenta destino en la respuesta de backoffice. Sin decoradores `class-validator`: el consumidor la
5
+ * lee tal cual viene por la red. Timestamps en epoch ms.
6
+ *
7
+ * `account` (la CLABE) SÍ sale: el operador de backoffice necesita verla para conciliar contra el
8
+ * estado de cuenta del banco. El enmascarado aplica a los LOGS, no a esta respuesta autenticada.
9
+ */
10
+ export class AccountResponse {
11
+ accountId!: string;
12
+ tenantId!: string;
13
+ channel!: TransferChannelEnum;
14
+ account!: string;
15
+ beneficiaryName!: string;
16
+ beneficiaryRfc?: string;
17
+ bankName!: string;
18
+ currency!: string;
19
+ enabled!: boolean;
20
+ createdBy!: string;
21
+ updatedBy!: string;
22
+ createdAt!: number;
23
+ updatedAt!: number;
24
+ }
@@ -0,0 +1,12 @@
1
+ import { AccountImpactResponse } from "./AccountImpactResponse";
2
+ import { AccountResponse } from "./AccountResponse";
3
+
4
+ /**
5
+ * Respuesta del `PUT /backoffice/accounts/{accountId}` (DEC-050): la cuenta ya editada, más el impacto
6
+ * que esa edición tiene. Se devuelve el impacto incluso cuando el operador no consultó
7
+ * `/impact` antes, que es el caso que hay que cubrir.
8
+ */
9
+ export class AccountWithImpactResponse {
10
+ account!: AccountResponse;
11
+ impact!: AccountImpactResponse;
12
+ }
@@ -0,0 +1,60 @@
1
+ import { IsBoolean, IsEnum, IsNotEmpty, IsOptional, IsString, Length, Matches, MaxLength } from "class-validator";
2
+ import { TransferChannelEnum } from "../enums/TransferChannelEnum";
3
+
4
+ /**
5
+ * Alta de una cuenta destino (DEC-041). La cuenta vive en `MoneyTransferAccount_GT` y el producto
6
+ * del catálogo la referencia por `accountId`; el número de cuenta (CLABE en SPEI) es el ÚNICO campo
7
+ * que cambia cuando el tercero cambia de banco.
8
+ *
9
+ * Sin `idempotencyKey` a propósito: el alta ya tiene llave natural (`accountId` repetido → 409) y no
10
+ * mueve dinero ni lotes, así que el doble click no puede duplicar nada.
11
+ */
12
+ export class CreateAccountRequest {
13
+ /** Identificador estable y legible, p. ej. `ACC-BANORTE-01`. Es la PK de la tabla. */
14
+ @IsString()
15
+ @IsNotEmpty()
16
+ @MaxLength(64)
17
+ @Matches(/^[A-Z0-9][A-Z0-9-]*$/, { message: "accountId debe ser mayúsculas, dígitos y guiones" })
18
+ accountId!: string;
19
+
20
+ /** DEC-034: tenant (partner) dueño de la cuenta; = subcategoría del servicio. */
21
+ @IsString()
22
+ @IsNotEmpty()
23
+ @MaxLength(64)
24
+ tenantId!: string;
25
+
26
+ @IsEnum(TransferChannelEnum)
27
+ channel!: TransferChannelEnum;
28
+
29
+ /** Número de cuenta del riel: CLABE de 18 dígitos en SPEI. El conector lo valida por canal (DEC-053). */
30
+ @IsString()
31
+ @IsNotEmpty()
32
+ @MaxLength(34)
33
+ account!: string;
34
+
35
+ /** Nombre del titular tal como viaja al riel (SPEI acepta máx. 40 chars). */
36
+ @IsString()
37
+ @IsNotEmpty()
38
+ @MaxLength(40)
39
+ beneficiaryName!: string;
40
+
41
+ /** RFC del titular: 12 chars persona moral, 13 persona física. */
42
+ @IsOptional()
43
+ @IsString()
44
+ @Length(12, 13)
45
+ beneficiaryRfc?: string;
46
+
47
+ /** Nombre del banco, para que el operador reconozca la cuenta en backoffice. */
48
+ @IsString()
49
+ @IsNotEmpty()
50
+ @MaxLength(60)
51
+ bankName!: string;
52
+
53
+ /** ISO 4217 (`MXN`, `USD`). */
54
+ @IsString()
55
+ @Length(3, 3)
56
+ currency!: string;
57
+
58
+ @IsBoolean()
59
+ enabled!: boolean;
60
+ }
@@ -7,7 +7,6 @@ import {
7
7
  IsNumber,
8
8
  IsOptional,
9
9
  IsString,
10
- Length,
11
10
  MaxLength,
12
11
  Min,
13
12
  ValidateNested,
@@ -21,12 +20,17 @@ import { BatchScheduleRequest } from "./BatchScheduleRequest";
21
20
  /**
22
21
  * Producto de un servicio de transferencia de dinero (Centro de Beneficios). Los campos en
23
22
  * español espejan el catálogo legacy de pago de servicios; los campos en inglés son la
24
- * configuración propia del riel (canal, modo de liquidación, beneficiario, lote).
23
+ * configuración propia del riel (canal, modo de liquidación, lote). El beneficiario ya no vive
24
+ * acá: se mudó a la cuenta destino (DEC-046).
25
25
  */
26
26
  export class MoneyTransferProductRequest {
27
+ /**
28
+ * DEC-041: `idProducto` es el `accountId` de la cuenta destino (`ACC-BANORTE-01`), NO la CLABE.
29
+ * La CLABE vive en `MoneyTransferAccount_GT` y se resuelve al pagar (F1) y al enviar (F4).
30
+ */
27
31
  @IsString()
28
32
  @IsNotEmpty()
29
- @MaxLength(34)
33
+ @MaxLength(64)
30
34
  idProducto!: string;
31
35
 
32
36
  @IsString()
@@ -81,17 +85,9 @@ export class MoneyTransferProductRequest {
81
85
  @IsEnum(SettlementModeEnum)
82
86
  settlementMode!: SettlementModeEnum;
83
87
 
84
- /** Nombre del beneficiario tal como viaja al riel (SPEI acepta máx. 40 chars). */
85
- @IsString()
86
- @IsNotEmpty()
87
- @MaxLength(40)
88
- beneficiaryName!: string;
89
-
90
- /** RFC del beneficiario: 12 chars persona moral, 13 persona física. */
91
- @IsOptional()
92
- @IsString()
93
- @Length(12, 13)
94
- beneficiaryRfc?: string;
88
+ // DEC-046: `beneficiaryName` y `beneficiaryRfc` se mudaron a la cuenta (`MoneyTransferAccount_GT`).
89
+ // Son atributos del titular de la cuenta, no del producto: tenerlos acá permitía que dos productos
90
+ // a la misma cuenta divergieran en el titular sin que nada lo impidiera.
95
91
 
96
92
  @IsOptional()
97
93
  @IsNumber()
@@ -20,6 +20,12 @@ export class SettlementResponse {
20
20
  scheduledAt?: number;
21
21
  /** Id de la transacción en el riel (ACH) o clave de rastreo (SPEI). */
22
22
  achTransactionId?: string;
23
+ /**
24
+ * DEC-044: cuenta (CLABE) a la que se INTENTÓ enviar este lote, escrita al tomar el lock `SENDING`.
25
+ * No significa "cuenta que recibió el dinero": la evidencia de dinero movido es este campo + un
26
+ * estado de la terna SENT/SETTLED/RETURNED + `achTransactionId`.
27
+ */
28
+ sentToAccount?: string;
23
29
  returnReason?: string;
24
30
  lastError?: string;
25
31
  /** `true` cuando el envío no obtuvo respuesta concluyente del riel (posible duplicado). */
@@ -20,6 +20,13 @@ export class TransactionDetailResponse {
20
20
  status!: TransactionDetailStatusEnum;
21
21
  /** Presente cuando el folio ya fue agrupado en una liquidación. */
22
22
  settlementId?: string;
23
+ /**
24
+ * DEC-051: `true` cuando la cuenta de este folio no existe o está deshabilitada. El folio se queda
25
+ * en `PENDING_SETTLEMENT` (DEC-048) y no puede liquidarse hasta que operaciones arregle la cuenta;
26
+ * sin esta marca quedaría invisible en la cola y se descubriría cuando el usuario reclame.
27
+ * Solo lo calcula el listado de pendientes de backoffice (F9); en otros contextos viene ausente.
28
+ */
29
+ blockedByAccount?: boolean;
23
30
  /** Epoch ms. */
24
31
  createdAt!: number;
25
32
  }
@@ -0,0 +1,49 @@
1
+ import { IsBoolean, IsEnum, IsNotEmpty, IsOptional, IsString, Length, MaxLength } from "class-validator";
2
+ import { TransferChannelEnum } from "../enums/TransferChannelEnum";
3
+
4
+ /**
5
+ * Edición de una cuenta destino (DEC-045: actualización en el mismo registro, sin versionado).
6
+ * Todos los campos son opcionales: se envía solo lo que cambia. `accountId` no está porque es la PK
7
+ * y viaja en el path. `enabled` se cambia acá, sin endpoint propio (evita duplicar la lógica de impacto).
8
+ *
9
+ * Sin `idempotencyKey`: la edición es idempotente por naturaleza (escribe el estado deseado).
10
+ */
11
+ export class UpdateAccountRequest {
12
+ @IsOptional()
13
+ @IsEnum(TransferChannelEnum)
14
+ channel?: TransferChannelEnum;
15
+
16
+ /** El campo que hace útil todo este cambio: corregir el número no obliga a tocar ningún producto. */
17
+ @IsOptional()
18
+ @IsString()
19
+ @IsNotEmpty()
20
+ @MaxLength(34)
21
+ account?: string;
22
+
23
+ @IsOptional()
24
+ @IsString()
25
+ @IsNotEmpty()
26
+ @MaxLength(40)
27
+ beneficiaryName?: string;
28
+
29
+ @IsOptional()
30
+ @IsString()
31
+ @Length(12, 13)
32
+ beneficiaryRfc?: string;
33
+
34
+ @IsOptional()
35
+ @IsString()
36
+ @IsNotEmpty()
37
+ @MaxLength(60)
38
+ bankName?: string;
39
+
40
+ @IsOptional()
41
+ @IsString()
42
+ @Length(3, 3)
43
+ currency?: string;
44
+
45
+ /** DEC-049: nunca se borra una cuenta, solo se deshabilita. */
46
+ @IsOptional()
47
+ @IsBoolean()
48
+ enabled?: boolean;
49
+ }
@@ -19,9 +19,15 @@ export * from './dtos/MoneyTransferServiceUpdateRequest';
19
19
  export * from './dtos/CreateSettlementRequest';
20
20
  export * from './dtos/SettlementActionRequest';
21
21
  export * from './dtos/RescheduleSettlementRequest';
22
+ export * from './dtos/CreateAccountRequest';
23
+ export * from './dtos/UpdateAccountRequest';
22
24
 
23
25
  //DTOs — responses
24
26
  export * from './dtos/TransactionDetailResponse';
25
27
  export * from './dtos/SettlementResponse';
26
28
  export * from './dtos/PendingDetailsResponse';
27
29
  export * from './dtos/SettlementListResponse';
30
+ export * from './dtos/AccountResponse';
31
+ export * from './dtos/AccountListResponse';
32
+ export * from './dtos/AccountImpactResponse';
33
+ export * from './dtos/AccountWithImpactResponse';
@@ -1,6 +1,7 @@
1
1
  import { Expose } from 'class-transformer';
2
- import { IsEmail, IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator';
2
+ import { IsEmail, IsEnum, IsNotEmpty, IsObject, IsOptional, IsString, Validate } from 'class-validator';
3
3
  import { PermissionScope } from '../enums/PermissionScope';
4
+ import { IsStringValueRecord } from '../../common/validators/IsStringValueRecord';
4
5
 
5
6
  /**
6
7
  * Body del POST /internal/tenants/{tenantId}/users (endpoint privado, VPC/ANONIMUS). `tenantId` viaja en el path.
@@ -11,6 +12,7 @@ import { PermissionScope } from '../enums/PermissionScope';
11
12
  * - Idempotente por identidad Cognito.
12
13
  * - `scope`/`scopeRef` = grant del rol (ej. LEVEL_3 + storeId para un vendedor de tienda).
13
14
  * - `createdBy` = cognitoSub del admin real (audit + grantedBy del assignment).
15
+ * - `customFields` = valores de los campos personalizados del tenant; si el correo ya tiene usuario, se fusionan.
14
16
  */
15
17
  export class CreateTenantUserRequest {
16
18
  @Expose() @IsEmail() email!: string;
@@ -19,4 +21,9 @@ export class CreateTenantUserRequest {
19
21
  @Expose() @IsEnum(PermissionScope) scope!: PermissionScope;
20
22
  @Expose() @IsString() @IsNotEmpty() scopeRef!: string;
21
23
  @Expose() @IsString() @IsNotEmpty() createdBy!: string;
24
+ @Expose()
25
+ @IsOptional()
26
+ @IsObject()
27
+ @Validate(IsStringValueRecord, { message: 'customFields: todos los valores deben ser string' })
28
+ customFields?: { [key: string]: string };
22
29
  }