@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.
- package/_test_/unit/common/IsStringValueRecord.test.ts +25 -0
- package/_test_/unit/moneyTransfer/accountDtos.test.ts +206 -0
- package/_test_/unit/moneyTransfer/dtos.test.ts +18 -8
- package/_test_/unit/platformRbac/dtos/tenantUserCustomFields.test.ts +55 -0
- package/bin/common/index.d.ts +1 -0
- package/bin/common/index.js +2 -0
- package/bin/common/validators/IsStringValueRecord.d.ts +5 -0
- package/bin/common/validators/IsStringValueRecord.js +25 -0
- package/bin/messagesConnector/validators/IsStringValueRecord.d.ts +1 -5
- package/bin/messagesConnector/validators/IsStringValueRecord.js +3 -21
- package/bin/moneyTransfer/dtos/AccountImpactResponse.d.ts +18 -0
- package/bin/moneyTransfer/dtos/AccountImpactResponse.js +14 -0
- package/bin/moneyTransfer/dtos/AccountListResponse.d.ts +6 -0
- package/bin/moneyTransfer/dtos/AccountListResponse.js +7 -0
- package/bin/moneyTransfer/dtos/AccountResponse.d.ts +23 -0
- package/bin/moneyTransfer/dtos/AccountResponse.js +13 -0
- package/bin/moneyTransfer/dtos/AccountWithImpactResponse.d.ts +11 -0
- package/bin/moneyTransfer/dtos/AccountWithImpactResponse.js +11 -0
- package/bin/moneyTransfer/dtos/CreateAccountRequest.d.ts +27 -0
- package/bin/moneyTransfer/dtos/CreateAccountRequest.js +75 -0
- package/bin/moneyTransfer/dtos/MoneyTransferProductRequest.d.ts +6 -5
- package/bin/moneyTransfer/dtos/MoneyTransferProductRequest.js +3 -14
- package/bin/moneyTransfer/dtos/SettlementResponse.d.ts +6 -0
- package/bin/moneyTransfer/dtos/TransactionDetailResponse.d.ts +7 -0
- package/bin/moneyTransfer/dtos/UpdateAccountRequest.d.ts +19 -0
- package/bin/moneyTransfer/dtos/UpdateAccountRequest.js +67 -0
- package/bin/moneyTransfer/index.d.ts +6 -0
- package/bin/moneyTransfer/index.js +6 -0
- package/bin/platformRbac/dtos/CreateTenantUserRequest.d.ts +4 -0
- package/bin/platformRbac/dtos/CreateTenantUserRequest.js +9 -0
- package/bin/platformRbac/dtos/UpdateTenantUserRequest.d.ts +5 -2
- package/bin/platformRbac/dtos/UpdateTenantUserRequest.js +10 -2
- package/package.json +1 -1
- package/src/common/index.ts +3 -0
- package/src/common/validators/IsStringValueRecord.ts +13 -0
- package/src/messagesConnector/validators/IsStringValueRecord.ts +2 -12
- package/src/moneyTransfer/dtos/AccountImpactResponse.ts +18 -0
- package/src/moneyTransfer/dtos/AccountListResponse.ts +7 -0
- package/src/moneyTransfer/dtos/AccountResponse.ts +24 -0
- package/src/moneyTransfer/dtos/AccountWithImpactResponse.ts +12 -0
- package/src/moneyTransfer/dtos/CreateAccountRequest.ts +60 -0
- package/src/moneyTransfer/dtos/MoneyTransferProductRequest.ts +10 -14
- package/src/moneyTransfer/dtos/SettlementResponse.ts +6 -0
- package/src/moneyTransfer/dtos/TransactionDetailResponse.ts +7 -0
- package/src/moneyTransfer/dtos/UpdateAccountRequest.ts +49 -0
- package/src/moneyTransfer/index.ts +6 -0
- package/src/platformRbac/dtos/CreateTenantUserRequest.ts +8 -1
- package/src/platformRbac/dtos/UpdateTenantUserRequest.ts +9 -3
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { IsStringValueRecord } from '../../../src/common';
|
|
3
|
+
import { IsStringValueRecord as FromMessagesConnector } from '../../../src/messagesConnector/validators/IsStringValueRecord';
|
|
4
|
+
|
|
5
|
+
describe('IsStringValueRecord (common)', () => {
|
|
6
|
+
const validator = new IsStringValueRecord();
|
|
7
|
+
const args = {} as never;
|
|
8
|
+
|
|
9
|
+
it('acepta un mapa de strings', () => {
|
|
10
|
+
expect(validator.validate({ a: 'x', b: '' }, args)).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('rechaza un valor que no es string', () => {
|
|
14
|
+
expect(validator.validate({ a: 1 }, args)).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('rechaza null y valores que no son objeto', () => {
|
|
18
|
+
expect(validator.validate(null, args)).toBe(false);
|
|
19
|
+
expect(validator.validate('x', args)).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('messagesConnector reexporta la misma clase', () => {
|
|
23
|
+
expect(FromMessagesConnector).toBe(IsStringValueRecord);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { plainToInstance } from "class-transformer";
|
|
3
|
+
import { validateSync } from "class-validator";
|
|
4
|
+
import {
|
|
5
|
+
AccountImpactResponse,
|
|
6
|
+
AccountListResponse,
|
|
7
|
+
AccountResponse,
|
|
8
|
+
AccountWithImpactResponse,
|
|
9
|
+
CreateAccountRequest,
|
|
10
|
+
PendingDetailsResponse,
|
|
11
|
+
SettlementResponse,
|
|
12
|
+
TransactionDetailResponse,
|
|
13
|
+
TransferChannelEnum,
|
|
14
|
+
UpdateAccountRequest,
|
|
15
|
+
} from "../../../src/moneyTransfer";
|
|
16
|
+
|
|
17
|
+
const propertiesWithErrors = (dto: object) => validateSync(dto).map((error) => error.property);
|
|
18
|
+
|
|
19
|
+
/** Cuenta destino válida (DEC-041). */
|
|
20
|
+
const CUENTA_COMPLETA = {
|
|
21
|
+
accountId: "ACC-BANORTE-01",
|
|
22
|
+
tenantId: "SPEI",
|
|
23
|
+
channel: TransferChannelEnum.SPEI,
|
|
24
|
+
account: "072180000123456789",
|
|
25
|
+
beneficiaryName: "ACME SA DE CV",
|
|
26
|
+
beneficiaryRfc: "ACM010101AB1",
|
|
27
|
+
bankName: "Banorte",
|
|
28
|
+
currency: "MXN",
|
|
29
|
+
enabled: true,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
describe("CreateAccountRequest", () => {
|
|
33
|
+
it("una cuenta completa pasa la validación", () => {
|
|
34
|
+
expect(validateSync(plainToInstance(CreateAccountRequest, CUENTA_COMPLETA))).toHaveLength(0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("beneficiaryRfc es opcional", () => {
|
|
38
|
+
const { beneficiaryRfc: _omit, ...sinRfc } = CUENTA_COMPLETA;
|
|
39
|
+
expect(validateSync(plainToInstance(CreateAccountRequest, sinRfc))).toHaveLength(0);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("exige todos los campos obligatorios cuando llega vacía", () => {
|
|
43
|
+
const props = propertiesWithErrors(plainToInstance(CreateAccountRequest, {}));
|
|
44
|
+
for (const campo of [
|
|
45
|
+
"accountId",
|
|
46
|
+
"tenantId",
|
|
47
|
+
"channel",
|
|
48
|
+
"account",
|
|
49
|
+
"beneficiaryName",
|
|
50
|
+
"bankName",
|
|
51
|
+
"currency",
|
|
52
|
+
"enabled",
|
|
53
|
+
]) {
|
|
54
|
+
expect(props).toContain(campo);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("rechaza un accountId en minúsculas o con caracteres fuera de [A-Z0-9-]", () => {
|
|
59
|
+
for (const accountId of ["acc-banorte-01", "ACC BANORTE", "-ACC", "ACC_01"]) {
|
|
60
|
+
expect(propertiesWithErrors(plainToInstance(CreateAccountRequest, { ...CUENTA_COMPLETA, accountId })))
|
|
61
|
+
.toContain("accountId");
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("acepta un accountId de 64 chars y rechaza 65", () => {
|
|
66
|
+
const enElLimite = { ...CUENTA_COMPLETA, accountId: "A".repeat(64) };
|
|
67
|
+
expect(validateSync(plainToInstance(CreateAccountRequest, enElLimite))).toHaveLength(0);
|
|
68
|
+
|
|
69
|
+
const pasado = { ...CUENTA_COMPLETA, accountId: "A".repeat(65) };
|
|
70
|
+
expect(propertiesWithErrors(plainToInstance(CreateAccountRequest, pasado))).toContain("accountId");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("rechaza un canal fuera de TransferChannelEnum", () => {
|
|
74
|
+
expect(propertiesWithErrors(plainToInstance(CreateAccountRequest, { ...CUENTA_COMPLETA, channel: "SWIFT" })))
|
|
75
|
+
.toContain("channel");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("rechaza beneficiaryName > 40, bankName > 60, account > 34 y currency != 3", () => {
|
|
79
|
+
const dto = plainToInstance(CreateAccountRequest, {
|
|
80
|
+
...CUENTA_COMPLETA,
|
|
81
|
+
beneficiaryName: "N".repeat(41),
|
|
82
|
+
bankName: "B".repeat(61),
|
|
83
|
+
account: "9".repeat(35),
|
|
84
|
+
currency: "MX",
|
|
85
|
+
});
|
|
86
|
+
const props = propertiesWithErrors(dto);
|
|
87
|
+
expect(props).toContain("beneficiaryName");
|
|
88
|
+
expect(props).toContain("bankName");
|
|
89
|
+
expect(props).toContain("account");
|
|
90
|
+
expect(props).toContain("currency");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("rechaza beneficiaryRfc fuera del rango 12-13 chars", () => {
|
|
94
|
+
for (const beneficiaryRfc of ["CORTO", "R".repeat(14)]) {
|
|
95
|
+
expect(propertiesWithErrors(plainToInstance(CreateAccountRequest, { ...CUENTA_COMPLETA, beneficiaryRfc })))
|
|
96
|
+
.toContain("beneficiaryRfc");
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("UpdateAccountRequest", () => {
|
|
102
|
+
it("un body vacío es válido: todos los campos son opcionales", () => {
|
|
103
|
+
expect(validateSync(plainToInstance(UpdateAccountRequest, {}))).toHaveLength(0);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("DEC-045: permite mandar solo el número de cuenta corregido", () => {
|
|
107
|
+
expect(validateSync(plainToInstance(UpdateAccountRequest, { account: "072180000987654321" }))).toHaveLength(0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("DEC-049: permite deshabilitar mandando solo enabled", () => {
|
|
111
|
+
expect(validateSync(plainToInstance(UpdateAccountRequest, { enabled: false }))).toHaveLength(0);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("accountId no es una propiedad del DTO: la PK viaja en el path, no en el body", () => {
|
|
115
|
+
// `accountId` no está declarado ni validado; si alguien lo manda, el DTO no lo reconoce
|
|
116
|
+
// como campo propio y el manager no lo puede usar para reasignar la PK.
|
|
117
|
+
const dto = plainToInstance(UpdateAccountRequest, { accountId: "ACC-OTRA-01" });
|
|
118
|
+
expect(validateSync(dto)).toHaveLength(0);
|
|
119
|
+
expect(Object.keys(new UpdateAccountRequest())).not.toContain("accountId");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("cuando un campo opcional viene, se valida igual que en el alta", () => {
|
|
123
|
+
const dto = plainToInstance(UpdateAccountRequest, {
|
|
124
|
+
channel: "SWIFT",
|
|
125
|
+
account: "",
|
|
126
|
+
beneficiaryName: "N".repeat(41),
|
|
127
|
+
beneficiaryRfc: "CORTO",
|
|
128
|
+
bankName: "B".repeat(61),
|
|
129
|
+
currency: "MX",
|
|
130
|
+
enabled: "si",
|
|
131
|
+
});
|
|
132
|
+
const props = propertiesWithErrors(dto);
|
|
133
|
+
for (const campo of ["channel", "account", "beneficiaryName", "beneficiaryRfc", "bankName", "currency", "enabled"]) {
|
|
134
|
+
expect(props).toContain(campo);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("DTOs de respuesta de cuentas", () => {
|
|
140
|
+
it("AccountResponse es instanciable y lleva la CLABE sin enmascarar", () => {
|
|
141
|
+
const response = plainToInstance(AccountResponse, {
|
|
142
|
+
...CUENTA_COMPLETA,
|
|
143
|
+
createdBy: "system",
|
|
144
|
+
updatedBy: "system",
|
|
145
|
+
createdAt: 1_758_000_000_000,
|
|
146
|
+
updatedAt: 1_758_000_000_000,
|
|
147
|
+
});
|
|
148
|
+
expect(response).toBeInstanceOf(AccountResponse);
|
|
149
|
+
expect(response.account).toBe(CUENTA_COMPLETA.account);
|
|
150
|
+
expect(response.channel).toBe(TransferChannelEnum.SPEI);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("AccountListResponse anida AccountResponse y nextToken es opcional", () => {
|
|
154
|
+
const page = plainToInstance(AccountListResponse, { items: [] });
|
|
155
|
+
expect(page).toBeInstanceOf(AccountListResponse);
|
|
156
|
+
expect(page.items).toEqual([]);
|
|
157
|
+
expect(page.nextToken).toBeUndefined();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("AccountImpactResponse (DEC-050) lleva los 4 contadores del impacto", () => {
|
|
161
|
+
const impact = plainToInstance(AccountImpactResponse, {
|
|
162
|
+
products: 2,
|
|
163
|
+
pendingDetails: 7,
|
|
164
|
+
pendingAmount: 1500,
|
|
165
|
+
pendingSettlements: 1,
|
|
166
|
+
});
|
|
167
|
+
expect(impact).toBeInstanceOf(AccountImpactResponse);
|
|
168
|
+
expect(impact.products).toBe(2);
|
|
169
|
+
expect(impact.pendingDetails).toBe(7);
|
|
170
|
+
expect(impact.pendingAmount).toBe(1500);
|
|
171
|
+
expect(impact.pendingSettlements).toBe(1);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("AccountWithImpactResponse compone cuenta + impacto", () => {
|
|
175
|
+
const dto = plainToInstance(AccountWithImpactResponse, {
|
|
176
|
+
account: { ...CUENTA_COMPLETA, createdBy: "system", updatedBy: "system", createdAt: 1, updatedAt: 2 },
|
|
177
|
+
impact: { products: 0, pendingDetails: 0, pendingAmount: 0, pendingSettlements: 0 },
|
|
178
|
+
});
|
|
179
|
+
expect(dto).toBeInstanceOf(AccountWithImpactResponse);
|
|
180
|
+
expect(dto.account.accountId).toBe("ACC-BANORTE-01");
|
|
181
|
+
expect(dto.impact.products).toBe(0);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
describe("Campos nuevos en los DTOs de respuesta existentes", () => {
|
|
186
|
+
it("DEC-044: SettlementResponse lleva sentToAccount, opcional", () => {
|
|
187
|
+
const conCuenta = plainToInstance(SettlementResponse, { sentToAccount: "072180000123456789" });
|
|
188
|
+
expect(conCuenta.sentToAccount).toBe("072180000123456789");
|
|
189
|
+
expect(plainToInstance(SettlementResponse, {}).sentToAccount).toBeUndefined();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("DEC-051: TransactionDetailResponse lleva blockedByAccount, opcional", () => {
|
|
193
|
+
expect(plainToInstance(TransactionDetailResponse, { blockedByAccount: true }).blockedByAccount).toBe(true);
|
|
194
|
+
expect(plainToInstance(TransactionDetailResponse, {}).blockedByAccount).toBeUndefined();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("PendingDetailsResponse propaga blockedByAccount vía sus items (F9)", () => {
|
|
198
|
+
const page = plainToInstance(PendingDetailsResponse, {
|
|
199
|
+
items: [{ transactionNumber: "T-1", blockedByAccount: true }],
|
|
200
|
+
count: 1,
|
|
201
|
+
totalAmount: 100,
|
|
202
|
+
});
|
|
203
|
+
expect(page).toBeInstanceOf(PendingDetailsResponse);
|
|
204
|
+
expect(page.items[0].blockedByAccount).toBe(true);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
@@ -22,7 +22,7 @@ const propertiesWithErrors = (dto: object) => validateSync(dto).map((error) => e
|
|
|
22
22
|
const VALID_UUID = "3f6c2c5e-8b1a-4f3e-9d2a-1c4b5e6f7a8b";
|
|
23
23
|
|
|
24
24
|
const PRODUCTO_COMPLETO = {
|
|
25
|
-
idProducto: "
|
|
25
|
+
idProducto: "ACC-BANORTE-01",
|
|
26
26
|
producto: "Dispersión SPEI en lote",
|
|
27
27
|
precio: 0,
|
|
28
28
|
amountType: AmountTypeEnum.VARIABLE,
|
|
@@ -39,8 +39,6 @@ const PRODUCTO_COMPLETO = {
|
|
|
39
39
|
enabled: true,
|
|
40
40
|
channel: TransferChannelEnum.SPEI,
|
|
41
41
|
settlementMode: SettlementModeEnum.BATCH,
|
|
42
|
-
beneficiaryName: "ACME SA DE CV",
|
|
43
|
-
beneficiaryRfc: "ACM010101AB1",
|
|
44
42
|
minAmount: 1,
|
|
45
43
|
maxAmount: 50000,
|
|
46
44
|
batchSchedule: { cron: "0 18 * * 1-5", timezone: "America/Mexico_City" },
|
|
@@ -131,15 +129,27 @@ describe("MoneyTransferProductRequest", () => {
|
|
|
131
129
|
expect(propertiesWithErrors(dto)).toContain("batchSchedule");
|
|
132
130
|
});
|
|
133
131
|
|
|
134
|
-
it("
|
|
132
|
+
it("DEC-046: ignora beneficiaryName/beneficiaryRfc, que ya no son del producto", () => {
|
|
135
133
|
const dto = plainToInstance(MoneyTransferProductRequest, {
|
|
136
134
|
...PRODUCTO_COMPLETO,
|
|
137
135
|
beneficiaryRfc: "CORTO",
|
|
138
136
|
beneficiaryName: "N".repeat(41),
|
|
139
137
|
});
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
expect(validateSync(dto)).toHaveLength(0);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("DEC-041: idProducto acepta un accountId de hasta 64 chars y rechaza 65", () => {
|
|
142
|
+
const enElLimite = plainToInstance(MoneyTransferProductRequest, {
|
|
143
|
+
...PRODUCTO_COMPLETO,
|
|
144
|
+
idProducto: "A".repeat(64),
|
|
145
|
+
});
|
|
146
|
+
expect(validateSync(enElLimite)).toHaveLength(0);
|
|
147
|
+
|
|
148
|
+
const pasado = plainToInstance(MoneyTransferProductRequest, {
|
|
149
|
+
...PRODUCTO_COMPLETO,
|
|
150
|
+
idProducto: "A".repeat(65),
|
|
151
|
+
});
|
|
152
|
+
expect(propertiesWithErrors(pasado)).toContain("idProducto");
|
|
143
153
|
});
|
|
144
154
|
|
|
145
155
|
it("rechaza channel y settlementMode fuera de sus enums", () => {
|
|
@@ -175,7 +185,7 @@ describe("MoneyTransferServiceCreateRequest", () => {
|
|
|
175
185
|
const nested = listError!.children?.[0]?.children?.map((child) => child.property) ?? [];
|
|
176
186
|
expect(nested).toContain("producto");
|
|
177
187
|
expect(nested).toContain("channel");
|
|
178
|
-
expect(nested).toContain("
|
|
188
|
+
expect(nested).toContain("settlementMode");
|
|
179
189
|
});
|
|
180
190
|
|
|
181
191
|
it("exige al menos un producto", () => {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validate } from 'class-validator';
|
|
4
|
+
import { CreateTenantUserRequest } from '../../../../src/platformRbac/dtos/CreateTenantUserRequest';
|
|
5
|
+
import { UpdateTenantUserRequest } from '../../../../src/platformRbac/dtos/UpdateTenantUserRequest';
|
|
6
|
+
import { PermissionScope } from '../../../../src/platformRbac/enums/PermissionScope';
|
|
7
|
+
|
|
8
|
+
const createBase = {
|
|
9
|
+
email: 'agente@example.com',
|
|
10
|
+
roleId: 'role-agente',
|
|
11
|
+
scope: PermissionScope.LEVEL_1,
|
|
12
|
+
scopeRef: 'grp-1',
|
|
13
|
+
createdBy: 'admin-sub-1',
|
|
14
|
+
};
|
|
15
|
+
const updateBase = { actedBy: 'admin-sub-1' };
|
|
16
|
+
|
|
17
|
+
describe.each([
|
|
18
|
+
['CreateTenantUserRequest', CreateTenantUserRequest, createBase],
|
|
19
|
+
['UpdateTenantUserRequest', UpdateTenantUserRequest, updateBase],
|
|
20
|
+
] as const)('%s.customFields', (_name, Dto, base) => {
|
|
21
|
+
const validateWith = async (customFields: unknown) =>
|
|
22
|
+
validate(plainToInstance(Dto as new () => object, { ...base, customFields }));
|
|
23
|
+
|
|
24
|
+
it('es opcional', async () => {
|
|
25
|
+
expect(await validate(plainToInstance(Dto as new () => object, base))).toEqual([]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('acepta un mapa de strings y lo conserva', async () => {
|
|
29
|
+
const dto = plainToInstance(Dto as new () => object, {
|
|
30
|
+
...base,
|
|
31
|
+
customFields: { directory_id: 'dir-1', home_group_id: 'grp-1' },
|
|
32
|
+
}) as { customFields?: Record<string, string> };
|
|
33
|
+
expect(await validate(dto)).toEqual([]);
|
|
34
|
+
expect(dto.customFields).toEqual({ directory_id: 'dir-1', home_group_id: 'grp-1' });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('acepta la cadena vacía (marca de borrado del rbac)', async () => {
|
|
38
|
+
expect(await validateWith({ directory_id: '' })).toEqual([]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('rechaza un valor que no es string', async () => {
|
|
42
|
+
const errors = await validateWith({ directory_id: 123 });
|
|
43
|
+
expect(errors.some((e) => e.property === 'customFields')).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('rechaza un arreglo', async () => {
|
|
47
|
+
const errors = await validateWith(['dir-1']);
|
|
48
|
+
expect(errors.some((e) => e.property === 'customFields')).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('rechaza un string suelto', async () => {
|
|
52
|
+
const errors = await validateWith('dir-1');
|
|
53
|
+
expect(errors.some((e) => e.property === 'customFields')).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
});
|
package/bin/common/index.d.ts
CHANGED
package/bin/common/index.js
CHANGED
|
@@ -16,6 +16,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
//decorators
|
|
18
18
|
__exportStar(require("./decorators/Trim"), exports);
|
|
19
|
+
//validators
|
|
20
|
+
__exportStar(require("./validators/IsStringValueRecord"), exports);
|
|
19
21
|
//dtos
|
|
20
22
|
__exportStar(require("./dtos/ProcessValidationResultDto"), exports);
|
|
21
23
|
//Enums
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ValidatorConstraintInterface, ValidationArguments } from 'class-validator';
|
|
2
|
+
export declare class IsStringValueRecord implements ValidatorConstraintInterface {
|
|
3
|
+
validate(value: unknown, _args: ValidationArguments): boolean;
|
|
4
|
+
defaultMessage(_args: ValidationArguments): string;
|
|
5
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.IsStringValueRecord = void 0;
|
|
10
|
+
const class_validator_1 = require("class-validator");
|
|
11
|
+
// Valida que un objeto sea un mapa clave → string.
|
|
12
|
+
let IsStringValueRecord = class IsStringValueRecord {
|
|
13
|
+
validate(value, _args) {
|
|
14
|
+
if (!value || typeof value !== 'object')
|
|
15
|
+
return false;
|
|
16
|
+
return Object.values(value).every(v => typeof v === 'string');
|
|
17
|
+
}
|
|
18
|
+
defaultMessage(_args) {
|
|
19
|
+
return 'All values in variables must be strings';
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
exports.IsStringValueRecord = IsStringValueRecord;
|
|
23
|
+
exports.IsStringValueRecord = IsStringValueRecord = __decorate([
|
|
24
|
+
(0, class_validator_1.ValidatorConstraint)({ name: 'IsStringValueRecord', async: false })
|
|
25
|
+
], IsStringValueRecord);
|
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export declare class IsStringValueRecord implements ValidatorConstraintInterface {
|
|
3
|
-
validate(value: unknown, _args: ValidationArguments): boolean;
|
|
4
|
-
defaultMessage(_args: ValidationArguments): string;
|
|
5
|
-
}
|
|
1
|
+
export { IsStringValueRecord } from '../../common/validators/IsStringValueRecord';
|
|
@@ -1,24 +1,6 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
3
|
exports.IsStringValueRecord = void 0;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (!value || typeof value !== 'object')
|
|
14
|
-
return false;
|
|
15
|
-
return Object.values(value).every(v => typeof v === 'string');
|
|
16
|
-
}
|
|
17
|
-
defaultMessage(_args) {
|
|
18
|
-
return 'All values in variables must be strings';
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
exports.IsStringValueRecord = IsStringValueRecord;
|
|
22
|
-
exports.IsStringValueRecord = IsStringValueRecord = __decorate([
|
|
23
|
-
(0, class_validator_1.ValidatorConstraint)({ name: 'IsStringValueRecord', async: false })
|
|
24
|
-
], IsStringValueRecord);
|
|
4
|
+
// Movido a common: se reexporta para no romper a quien importa desde messagesConnector.
|
|
5
|
+
var IsStringValueRecord_1 = require("../../common/validators/IsStringValueRecord");
|
|
6
|
+
Object.defineProperty(exports, "IsStringValueRecord", { enumerable: true, get: function () { return IsStringValueRecord_1.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 declare 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,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AccountImpactResponse = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* DEC-050: qué se rompe antes de romperlo. Deshabilitar o editar una cuenta NO bloquea aunque haya
|
|
6
|
+
* folios pendientes o liquidaciones en vuelo — pero el operador tiene que ver el número en el momento
|
|
7
|
+
* de apretar el botón, no descubrirlo después.
|
|
8
|
+
*
|
|
9
|
+
* `pendingSettlements` cuenta liquidaciones en estado NO terminal (CREATED, SCHEDULED, SENDING, FAILED):
|
|
10
|
+
* esas son las que todavía pueden salir al riel y por lo tanto resolverían la cuenta editada (DEC-042).
|
|
11
|
+
*/
|
|
12
|
+
class AccountImpactResponse {
|
|
13
|
+
}
|
|
14
|
+
exports.AccountImpactResponse = AccountImpactResponse;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AccountListResponse = void 0;
|
|
4
|
+
/** Página de cuentas de un tenant (GSI `tenantId-createdAt-index`). Paginada por cursor opaco. */
|
|
5
|
+
class AccountListResponse {
|
|
6
|
+
}
|
|
7
|
+
exports.AccountListResponse = AccountListResponse;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { TransferChannelEnum } from "../enums/TransferChannelEnum";
|
|
2
|
+
/**
|
|
3
|
+
* Cuenta destino en la respuesta de backoffice. Sin decoradores `class-validator`: el consumidor la
|
|
4
|
+
* lee tal cual viene por la red. Timestamps en epoch ms.
|
|
5
|
+
*
|
|
6
|
+
* `account` (la CLABE) SÍ sale: el operador de backoffice necesita verla para conciliar contra el
|
|
7
|
+
* estado de cuenta del banco. El enmascarado aplica a los LOGS, no a esta respuesta autenticada.
|
|
8
|
+
*/
|
|
9
|
+
export declare class AccountResponse {
|
|
10
|
+
accountId: string;
|
|
11
|
+
tenantId: string;
|
|
12
|
+
channel: TransferChannelEnum;
|
|
13
|
+
account: string;
|
|
14
|
+
beneficiaryName: string;
|
|
15
|
+
beneficiaryRfc?: string;
|
|
16
|
+
bankName: string;
|
|
17
|
+
currency: string;
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
createdBy: string;
|
|
20
|
+
updatedBy: string;
|
|
21
|
+
createdAt: number;
|
|
22
|
+
updatedAt: number;
|
|
23
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AccountResponse = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Cuenta destino en la respuesta de backoffice. Sin decoradores `class-validator`: el consumidor la
|
|
6
|
+
* lee tal cual viene por la red. Timestamps en epoch ms.
|
|
7
|
+
*
|
|
8
|
+
* `account` (la CLABE) SÍ sale: el operador de backoffice necesita verla para conciliar contra el
|
|
9
|
+
* estado de cuenta del banco. El enmascarado aplica a los LOGS, no a esta respuesta autenticada.
|
|
10
|
+
*/
|
|
11
|
+
class AccountResponse {
|
|
12
|
+
}
|
|
13
|
+
exports.AccountResponse = AccountResponse;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AccountImpactResponse } from "./AccountImpactResponse";
|
|
2
|
+
import { AccountResponse } from "./AccountResponse";
|
|
3
|
+
/**
|
|
4
|
+
* Respuesta del `PUT /backoffice/accounts/{accountId}` (DEC-050): la cuenta ya editada, más el impacto
|
|
5
|
+
* que esa edición tiene. Se devuelve el impacto incluso cuando el operador no consultó
|
|
6
|
+
* `/impact` antes, que es el caso que hay que cubrir.
|
|
7
|
+
*/
|
|
8
|
+
export declare class AccountWithImpactResponse {
|
|
9
|
+
account: AccountResponse;
|
|
10
|
+
impact: AccountImpactResponse;
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AccountWithImpactResponse = void 0;
|
|
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
|
+
class AccountWithImpactResponse {
|
|
10
|
+
}
|
|
11
|
+
exports.AccountWithImpactResponse = AccountWithImpactResponse;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { TransferChannelEnum } from "../enums/TransferChannelEnum";
|
|
2
|
+
/**
|
|
3
|
+
* Alta de una cuenta destino (DEC-041). La cuenta vive en `MoneyTransferAccount_GT` y el producto
|
|
4
|
+
* del catálogo la referencia por `accountId`; el número de cuenta (CLABE en SPEI) es el ÚNICO campo
|
|
5
|
+
* que cambia cuando el tercero cambia de banco.
|
|
6
|
+
*
|
|
7
|
+
* Sin `idempotencyKey` a propósito: el alta ya tiene llave natural (`accountId` repetido → 409) y no
|
|
8
|
+
* mueve dinero ni lotes, así que el doble click no puede duplicar nada.
|
|
9
|
+
*/
|
|
10
|
+
export declare class CreateAccountRequest {
|
|
11
|
+
/** Identificador estable y legible, p. ej. `ACC-BANORTE-01`. Es la PK de la tabla. */
|
|
12
|
+
accountId: string;
|
|
13
|
+
/** DEC-034: tenant (partner) dueño de la cuenta; = subcategoría del servicio. */
|
|
14
|
+
tenantId: string;
|
|
15
|
+
channel: TransferChannelEnum;
|
|
16
|
+
/** Número de cuenta del riel: CLABE de 18 dígitos en SPEI. El conector lo valida por canal (DEC-053). */
|
|
17
|
+
account: string;
|
|
18
|
+
/** Nombre del titular tal como viaja al riel (SPEI acepta máx. 40 chars). */
|
|
19
|
+
beneficiaryName: string;
|
|
20
|
+
/** RFC del titular: 12 chars persona moral, 13 persona física. */
|
|
21
|
+
beneficiaryRfc?: string;
|
|
22
|
+
/** Nombre del banco, para que el operador reconozca la cuenta en backoffice. */
|
|
23
|
+
bankName: string;
|
|
24
|
+
/** ISO 4217 (`MXN`, `USD`). */
|
|
25
|
+
currency: string;
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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.CreateAccountRequest = void 0;
|
|
13
|
+
const class_validator_1 = require("class-validator");
|
|
14
|
+
const TransferChannelEnum_1 = require("../enums/TransferChannelEnum");
|
|
15
|
+
/**
|
|
16
|
+
* Alta de una cuenta destino (DEC-041). La cuenta vive en `MoneyTransferAccount_GT` y el producto
|
|
17
|
+
* del catálogo la referencia por `accountId`; el número de cuenta (CLABE en SPEI) es el ÚNICO campo
|
|
18
|
+
* que cambia cuando el tercero cambia de banco.
|
|
19
|
+
*
|
|
20
|
+
* Sin `idempotencyKey` a propósito: el alta ya tiene llave natural (`accountId` repetido → 409) y no
|
|
21
|
+
* mueve dinero ni lotes, así que el doble click no puede duplicar nada.
|
|
22
|
+
*/
|
|
23
|
+
class CreateAccountRequest {
|
|
24
|
+
}
|
|
25
|
+
exports.CreateAccountRequest = CreateAccountRequest;
|
|
26
|
+
__decorate([
|
|
27
|
+
(0, class_validator_1.IsString)(),
|
|
28
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
29
|
+
(0, class_validator_1.MaxLength)(64),
|
|
30
|
+
(0, class_validator_1.Matches)(/^[A-Z0-9][A-Z0-9-]*$/, { message: "accountId debe ser mayúsculas, dígitos y guiones" }),
|
|
31
|
+
__metadata("design:type", String)
|
|
32
|
+
], CreateAccountRequest.prototype, "accountId", void 0);
|
|
33
|
+
__decorate([
|
|
34
|
+
(0, class_validator_1.IsString)(),
|
|
35
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
36
|
+
(0, class_validator_1.MaxLength)(64),
|
|
37
|
+
__metadata("design:type", String)
|
|
38
|
+
], CreateAccountRequest.prototype, "tenantId", void 0);
|
|
39
|
+
__decorate([
|
|
40
|
+
(0, class_validator_1.IsEnum)(TransferChannelEnum_1.TransferChannelEnum),
|
|
41
|
+
__metadata("design:type", String)
|
|
42
|
+
], CreateAccountRequest.prototype, "channel", void 0);
|
|
43
|
+
__decorate([
|
|
44
|
+
(0, class_validator_1.IsString)(),
|
|
45
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
46
|
+
(0, class_validator_1.MaxLength)(34),
|
|
47
|
+
__metadata("design:type", String)
|
|
48
|
+
], CreateAccountRequest.prototype, "account", void 0);
|
|
49
|
+
__decorate([
|
|
50
|
+
(0, class_validator_1.IsString)(),
|
|
51
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
52
|
+
(0, class_validator_1.MaxLength)(40),
|
|
53
|
+
__metadata("design:type", String)
|
|
54
|
+
], CreateAccountRequest.prototype, "beneficiaryName", void 0);
|
|
55
|
+
__decorate([
|
|
56
|
+
(0, class_validator_1.IsOptional)(),
|
|
57
|
+
(0, class_validator_1.IsString)(),
|
|
58
|
+
(0, class_validator_1.Length)(12, 13),
|
|
59
|
+
__metadata("design:type", String)
|
|
60
|
+
], CreateAccountRequest.prototype, "beneficiaryRfc", void 0);
|
|
61
|
+
__decorate([
|
|
62
|
+
(0, class_validator_1.IsString)(),
|
|
63
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
64
|
+
(0, class_validator_1.MaxLength)(60),
|
|
65
|
+
__metadata("design:type", String)
|
|
66
|
+
], CreateAccountRequest.prototype, "bankName", void 0);
|
|
67
|
+
__decorate([
|
|
68
|
+
(0, class_validator_1.IsString)(),
|
|
69
|
+
(0, class_validator_1.Length)(3, 3),
|
|
70
|
+
__metadata("design:type", String)
|
|
71
|
+
], CreateAccountRequest.prototype, "currency", void 0);
|
|
72
|
+
__decorate([
|
|
73
|
+
(0, class_validator_1.IsBoolean)(),
|
|
74
|
+
__metadata("design:type", Boolean)
|
|
75
|
+
], CreateAccountRequest.prototype, "enabled", void 0);
|