@fiado/api-invoker 5.40.0 → 5.42.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/bin/loanCredit/api/LoanCreditBusinessApi.d.ts +2 -0
- package/bin/loanCredit/api/LoanCreditBusinessApi.js +4 -0
- package/bin/loanCredit/api/dtos/LoanCreditAnalyticsBatch.d.ts +31 -0
- package/bin/loanCredit/api/dtos/LoanCreditAnalyticsBatch.js +1 -0
- package/bin/loanCredit/api/interfaces/ILoanCreditBusinessApi.d.ts +11 -0
- package/bin/loanCredit/index.d.ts +1 -0
- package/bin/loanCredit/index.js +1 -0
- package/bin/retailOrg/api/RetailOrgBusinessApi.d.ts +9 -2
- package/bin/retailOrg/api/RetailOrgBusinessApi.js +15 -1
- package/bin/retailOrg/api/interfaces/IRetailOrgBusinessApi.d.ts +41 -2
- package/package.json +1 -1
- package/src/loanCredit/api/LoanCreditBusinessApi.ts +12 -0
- package/src/loanCredit/api/dtos/LoanCreditAnalyticsBatch.ts +34 -0
- package/src/loanCredit/api/interfaces/ILoanCreditBusinessApi.ts +18 -0
- package/src/loanCredit/index.ts +1 -0
- package/src/retailOrg/api/RetailOrgBusinessApi.ts +19 -1
- package/src/retailOrg/api/interfaces/IRetailOrgBusinessApi.ts +46 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { IHttpRequest } from "@fiado/http-client";
|
|
2
2
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
3
3
|
import { ActivationCheckRequest, ActivationCheckResponse, LoanBorrowerResponse, LoanCreditResponse, OriginateLoanCreditRequest, OriginateLoanCreditResponse, QuoteLoanCreditRequest, QuoteLoanCreditResponse, SignLoanCreditRequest, SignLoanCreditResponse, UpsertLoanBorrowerRequest } from "@fiado/type-kit/bin/loanCredit/index.js";
|
|
4
|
+
import { LoanCreditAnalyticsBatchRequest, LoanCreditAnalyticsBatchResponse } from "./dtos/LoanCreditAnalyticsBatch.js";
|
|
4
5
|
import { ILoanCreditBusinessApi } from "./interfaces/ILoanCreditBusinessApi.js";
|
|
5
6
|
/**
|
|
6
7
|
* Publisher HTTP del lambda `loan-credit-business` (componente 09 SureKeep F2 — motor de crédito).
|
|
@@ -25,5 +26,6 @@ export default class LoanCreditBusinessApi implements ILoanCreditBusinessApi {
|
|
|
25
26
|
discard(creditId: string, tenantId: string): Promise<StandardResponse<LoanCreditResponse>>;
|
|
26
27
|
getCredit(creditId: string, tenantId: string): Promise<StandardResponse<LoanCreditResponse>>;
|
|
27
28
|
getBorrowerByRetailCustomer(retailCustomerId: string, tenantId: string): Promise<StandardResponse<LoanBorrowerResponse>>;
|
|
29
|
+
batchGetCreditAnalytics(input: LoanCreditAnalyticsBatchRequest, tenantId: string): Promise<StandardResponse<LoanCreditAnalyticsBatchResponse>>;
|
|
28
30
|
upsertBorrower(input: UpsertLoanBorrowerRequest, tenantId: string): Promise<StandardResponse<LoanBorrowerResponse>>;
|
|
29
31
|
}
|
|
@@ -58,6 +58,10 @@ let LoanCreditBusinessApi = class LoanCreditBusinessApi {
|
|
|
58
58
|
const url = `${this.baseUrl}/private/borrowers/by-retail-customer/${encodeURIComponent(retailCustomerId)}?tenantId=${encodeURIComponent(tenantId)}`;
|
|
59
59
|
return await this.httpRequest.get(url);
|
|
60
60
|
}
|
|
61
|
+
async batchGetCreditAnalytics(input, tenantId) {
|
|
62
|
+
const url = `${this.baseUrl}/private/credits/analytics?tenantId=${encodeURIComponent(tenantId)}`;
|
|
63
|
+
return await this.httpRequest.post(url, input);
|
|
64
|
+
}
|
|
61
65
|
async upsertBorrower(input, tenantId) {
|
|
62
66
|
const url = `${this.baseUrl}/private/borrowers?tenantId=${encodeURIComponent(tenantId)}`;
|
|
63
67
|
return await this.httpRequest.post(url, input);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { LoanCreditStatusEnum } from "@fiado/type-kit/bin/loanCredit/index.js";
|
|
2
|
+
import { ClientLevelEnum } from "@fiado/type-kit/bin/loanScoring/index.js";
|
|
3
|
+
/** Body de `POST /private/credits/analytics`. Hasta 500 ids por request; el caller pagina. */
|
|
4
|
+
export interface LoanCreditAnalyticsBatchRequest {
|
|
5
|
+
creditIds: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Lo mínimo de un crédito para contar: eje de riesgo, plan, cifras del enganche, estado y fecha.
|
|
9
|
+
* CERO PII — ni acreditado, ni nombre, ni teléfono.
|
|
10
|
+
*/
|
|
11
|
+
export interface LoanCreditAnalyticsItem {
|
|
12
|
+
creditId: string;
|
|
13
|
+
/** Nivel CONGELADO al originar; ausente en los créditos que se originaron sin SCI. */
|
|
14
|
+
clientLevelAtOrigination?: ClientLevelEnum;
|
|
15
|
+
/** Score CONGELADO al originar; ausente en los créditos que se originaron sin SCI. */
|
|
16
|
+
sciAtOrigination?: number;
|
|
17
|
+
/** Plan comercial con el que se originó. Es lo que distingue una venta a empleado. */
|
|
18
|
+
planId: string;
|
|
19
|
+
equipmentPriceCents: number;
|
|
20
|
+
downPaymentCents: number;
|
|
21
|
+
status: LoanCreditStatusEnum;
|
|
22
|
+
createdAt: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Los ids que no existen simplemente no vuelven; los que DynamoDB no alcanzó a leer salen
|
|
26
|
+
* NOMBRADOS para que el caller no los cuente como cero.
|
|
27
|
+
*/
|
|
28
|
+
export interface LoanCreditAnalyticsBatchResponse {
|
|
29
|
+
items: LoanCreditAnalyticsItem[];
|
|
30
|
+
unresolvedCreditIds: string[];
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
2
2
|
import { ActivationCheckRequest, ActivationCheckResponse, LoanBorrowerResponse, LoanCreditResponse, OriginateLoanCreditRequest, OriginateLoanCreditResponse, QuoteLoanCreditRequest, QuoteLoanCreditResponse, SignLoanCreditRequest, SignLoanCreditResponse, UpsertLoanBorrowerRequest } from "@fiado/type-kit/bin/loanCredit/index.js";
|
|
3
|
+
import { LoanCreditAnalyticsBatchRequest, LoanCreditAnalyticsBatchResponse } from "../dtos/LoanCreditAnalyticsBatch.js";
|
|
3
4
|
/**
|
|
4
5
|
* Contrato del publisher de `loan-credit-business` (componente 09 SureKeep F2 — motor de crédito
|
|
5
6
|
* SOFOM, pista Loan). Endpoints PRIVADOS (VPC) consumidos por `retail-wizard-business` (pasos
|
|
@@ -56,6 +57,16 @@ export interface ILoanCreditBusinessApi {
|
|
|
56
57
|
* `404 BORROWER_NOT_FOUND` si el evento KYC aún no lo hidrató.
|
|
57
58
|
*/
|
|
58
59
|
getBorrowerByRetailCustomer(retailCustomerId: string, tenantId: string): Promise<StandardResponse<LoanBorrowerResponse>>;
|
|
60
|
+
/**
|
|
61
|
+
* Lee créditos EN LOTE por su id y devuelve solo lo que se cuenta: el eje de riesgo congelado
|
|
62
|
+
* al originar, el plan, las cifras del enganche, el estado y la fecha. **Cero PII.**
|
|
63
|
+
*
|
|
64
|
+
* Se resuelve por BatchGetItem sobre la clave primaria, así que el orden de salida NO es el de
|
|
65
|
+
* entrada. Un id que no existe no vuelve; uno que DynamoDB no alcanzó a leer vuelve nombrado en
|
|
66
|
+
* `unresolvedCreditIds` — nunca se confunde «no pude leerlo» con «no existe».
|
|
67
|
+
* `400 VALIDATION_ERROR` si el lote pasa de 500 ids o trae uno que no es ULID.
|
|
68
|
+
*/
|
|
69
|
+
batchGetCreditAnalytics(input: LoanCreditAnalyticsBatchRequest, tenantId: string): Promise<StandardResponse<LoanCreditAnalyticsBatchResponse>>;
|
|
59
70
|
/**
|
|
60
71
|
* Upsert idempotente del acreditado por `retailCustomerId` (mismo contrato que el consumidor
|
|
61
72
|
* del evento `CustomerKycVerifiedV1` — fallback operativo mientras la cola no exista).
|
package/bin/loanCredit/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { IHttpRequest } from "@fiado/http-client";
|
|
2
2
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
3
3
|
import { RetailerValidationDto, StoreValidationDto, RetailUserValidationDto, CollectorValidationDto, PrivateSellerListResponse, RetailUserStatusEnum } from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
4
|
-
import { IRetailOrgBusinessApi, StoreRosterStatusFilter } from "./interfaces/IRetailOrgBusinessApi.js";
|
|
4
|
+
import { IRetailOrgBusinessApi, RetailerRosterStatusFilter, StoreRosterStatusFilter } from "./interfaces/IRetailOrgBusinessApi.js";
|
|
5
5
|
/**
|
|
6
|
-
* Publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1) para sus
|
|
6
|
+
* Publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1) para sus 9
|
|
7
7
|
* endpoints privados de validación. Contrato y semántica completos → `IRetailOrgBusinessApi`.
|
|
8
8
|
*
|
|
9
9
|
* Los paths y el `?tenantId=` fueron verificados contra dev (200 + negativos 400/404) el
|
|
@@ -20,6 +20,13 @@ export default class RetailOrgBusinessApi implements IRetailOrgBusinessApi {
|
|
|
20
20
|
private httpRequest;
|
|
21
21
|
private readonly baseUrl;
|
|
22
22
|
constructor(httpRequest: IHttpRequest);
|
|
23
|
+
/**
|
|
24
|
+
* El padrón de retailers del SILO — lo consume la pantalla de analítica del Portal Admin, que
|
|
25
|
+
* agrega ventas de todo el silo y no de una cadena. Contrato y semántica completos (incluido por
|
|
26
|
+
* qué `status` SÍ tiene default y por qué acá el estado es el CRUDO, sin cascada del padre)
|
|
27
|
+
* → `IRetailOrgBusinessApi.listRetailers`.
|
|
28
|
+
*/
|
|
29
|
+
listRetailers(tenantId: string, status?: RetailerRosterStatusFilter): Promise<StandardResponse<RetailerValidationDto[]>>;
|
|
23
30
|
getRetailer(retailerId: string, tenantId: string): Promise<StandardResponse<RetailerValidationDto>>;
|
|
24
31
|
getRetailerByStore(storeId: string, tenantId: string): Promise<StandardResponse<RetailerValidationDto>>;
|
|
25
32
|
getStore(retailerId: string, storeId: string, tenantId: string): Promise<StandardResponse<StoreValidationDto>>;
|
|
@@ -12,7 +12,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
12
12
|
};
|
|
13
13
|
import { inject, injectable } from "inversify";
|
|
14
14
|
/**
|
|
15
|
-
* Publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1) para sus
|
|
15
|
+
* Publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1) para sus 9
|
|
16
16
|
* endpoints privados de validación. Contrato y semántica completos → `IRetailOrgBusinessApi`.
|
|
17
17
|
*
|
|
18
18
|
* Los paths y el `?tenantId=` fueron verificados contra dev (200 + negativos 400/404) el
|
|
@@ -31,6 +31,20 @@ let RetailOrgBusinessApi = class RetailOrgBusinessApi {
|
|
|
31
31
|
constructor(httpRequest) {
|
|
32
32
|
this.httpRequest = httpRequest;
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* El padrón de retailers del SILO — lo consume la pantalla de analítica del Portal Admin, que
|
|
36
|
+
* agrega ventas de todo el silo y no de una cadena. Contrato y semántica completos (incluido por
|
|
37
|
+
* qué `status` SÍ tiene default y por qué acá el estado es el CRUDO, sin cascada del padre)
|
|
38
|
+
* → `IRetailOrgBusinessApi.listRetailers`.
|
|
39
|
+
*/
|
|
40
|
+
async listRetailers(tenantId, status) {
|
|
41
|
+
// `URLSearchParams` en vez de concatenar: `status` es opcional y armar el `&status=` a mano
|
|
42
|
+
// deja un `&status=undefined` en cuanto alguien se distrae.
|
|
43
|
+
const query = new URLSearchParams({ tenantId });
|
|
44
|
+
if (status)
|
|
45
|
+
query.set("status", status);
|
|
46
|
+
return await this.httpRequest.get(`${this.baseUrl}/private/retailers?${query.toString()}`);
|
|
47
|
+
}
|
|
34
48
|
async getRetailer(retailerId, tenantId) {
|
|
35
49
|
const url = `${this.baseUrl}/private/retailers/${encodeURIComponent(retailerId)}?tenantId=${encodeURIComponent(tenantId)}`;
|
|
36
50
|
return await this.httpRequest.get(url);
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
2
|
-
import { RetailerValidationDto, StoreValidationDto, RetailUserValidationDto, CollectorValidationDto, PrivateSellerListResponse, RetailUserStatusEnum, StoreStatusEnum } from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
2
|
+
import { RetailerValidationDto, StoreValidationDto, RetailUserValidationDto, CollectorValidationDto, PrivateSellerListResponse, RetailUserStatusEnum, RetailerStatusEnum, StoreStatusEnum } from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
3
3
|
/**
|
|
4
4
|
* Filtro de `listStoresByRetailer`. `"ALL"` no es un estado de tienda: es la ausencia de filtro, y
|
|
5
5
|
* va en el MISMO parámetro que los estados porque los dos hablan del mismo eje — un segundo flag
|
|
6
6
|
* tipo `includeInactive` admitiría combinaciones sin sentido que alguien tendría que resolver.
|
|
7
7
|
*/
|
|
8
8
|
export type StoreRosterStatusFilter = StoreStatusEnum | "ALL";
|
|
9
|
+
/** Filtro de `listRetailers`. Mismo criterio que `StoreRosterStatusFilter`, un nivel más arriba. */
|
|
10
|
+
export type RetailerRosterStatusFilter = RetailerStatusEnum | "ALL";
|
|
9
11
|
/**
|
|
10
12
|
* Contrato del publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1)
|
|
11
|
-
* para sus
|
|
13
|
+
* para sus 9 endpoints privados de VALIDACIÓN (service-to-service, VPC-only).
|
|
12
14
|
*
|
|
13
15
|
* Consumidores previstos (regla R5 — cruces entre lambdas son SOLO lecturas de validación;
|
|
14
16
|
* ningún lambda escribe en tabla ajena):
|
|
@@ -51,6 +53,43 @@ export type StoreRosterStatusFilter = StoreStatusEnum | "ALL";
|
|
|
51
53
|
* Convención CLAUDE.md global: SSM key = nombre del lambda owner de la URL.
|
|
52
54
|
*/
|
|
53
55
|
export interface IRetailOrgBusinessApi {
|
|
56
|
+
/**
|
|
57
|
+
* `GET /private/retailers` — el **padrón de retailers del SILO**.
|
|
58
|
+
*
|
|
59
|
+
* ── 🔴 Por qué este método existe ────────────────────────────────────────────────────────────
|
|
60
|
+
* Lo consume la pantalla de analítica del Portal Admin, que agrega ventas de TODO el silo y no
|
|
61
|
+
* de una cadena. Para eso necesita saber **qué retailers existen**, y ningún otro privado lo
|
|
62
|
+
* dice: `getRetailer` resuelve UNO por id, `getRetailerByStore` el dueño de una tienda, y el
|
|
63
|
+
* resto baja un nivel. El único que lista cadenas, `publicListRetailers`, va detrás del
|
|
64
|
+
* `W4Authorizer` (token de usuario, no invocable entre lambdas), pagina, y devuelve el registro
|
|
65
|
+
* completo con RFC y datos de contacto.
|
|
66
|
+
*
|
|
67
|
+
* Es el hermano de `listStoresByRetailer` un nivel más arriba: aquel da las tiendas de UNA
|
|
68
|
+
* cadena, este da las cadenas. Sin `retailerId` en la firma: el recurso es el silo entero, que
|
|
69
|
+
* ya lo determina el `tenantId`.
|
|
70
|
+
*
|
|
71
|
+
* Devuelve el mismo `RetailerValidationDto` que `getRetailer` — `retailerId`, `name`, `type`,
|
|
72
|
+
* `status`, `parentId` — **sin ningún campo PII**. Trae el árbol COMPLETO (cadenas raíz +
|
|
73
|
+
* subdistribuidores + franquicias, a cualquier profundidad): una venta cuelga de una tienda que
|
|
74
|
+
* pertenece a un retailer de cualquier nivel, así que quedarse en las raíces perdería ventas.
|
|
75
|
+
*
|
|
76
|
+
* ⚠️ El `status` es el CRUDO de la fila, NO uno efectivo — a diferencia de `getStore` y
|
|
77
|
+
* `listStoresByRetailer`, donde sí viene la cascada del padre aplicada. En retail-org un
|
|
78
|
+
* retailer no hereda el estado de su padre en ninguna lectura: un subdistribuidor `ACTIVE` de
|
|
79
|
+
* una cadena `SUSPENDED` sale como `ACTIVE`. Si eso importa, cruza el `parentId`.
|
|
80
|
+
*
|
|
81
|
+
* ── ⚠️ `status` SÍ tiene default, igual que `listStoresByRetailer` ───────────────────────────
|
|
82
|
+
* Omitirlo devuelve **solo los `ACTIVE`**. Pasa `"ALL"` para el padrón completo. El default vive
|
|
83
|
+
* en el lambda, no acá: mandarlo implícito desde el publisher partiría la decisión en dos
|
|
84
|
+
* lugares que alguien tendría que mantener sincronizados.
|
|
85
|
+
*
|
|
86
|
+
* Devuelve `[]` si ningún retailer pasa el filtro. **No hay 404**: el silo siempre existe — lo
|
|
87
|
+
* garantiza el `tenantId`, que es lo que resolvió el contexto del tenant.
|
|
88
|
+
*
|
|
89
|
+
* @throws `400 RETAILER_STATUS_FILTER_INVALID` si `status` no es del catálogo ·
|
|
90
|
+
* `400 UNKNOWN_TENANT` sin `tenantId`.
|
|
91
|
+
*/
|
|
92
|
+
listRetailers(tenantId: string, status?: RetailerRosterStatusFilter): Promise<StandardResponse<RetailerValidationDto[]>>;
|
|
54
93
|
/**
|
|
55
94
|
* GET /private/retailers/{retailerId} — valida que el retailer exista y devuelve su shape mínimo
|
|
56
95
|
* (id, name, type, status, parentId). `status` ya viene con la cascada aplicada (estado efectivo).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fiado/api-invoker",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.42.0",
|
|
4
4
|
"description": "Sirve como un puente entre diferentes funciones lambda, facilitando la comunicación entre ellas a través de invocaciones http",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/index.js",
|
|
@@ -14,6 +14,10 @@ import {
|
|
|
14
14
|
SignLoanCreditResponse,
|
|
15
15
|
UpsertLoanBorrowerRequest,
|
|
16
16
|
} from "@fiado/type-kit/bin/loanCredit/index.js";
|
|
17
|
+
import {
|
|
18
|
+
LoanCreditAnalyticsBatchRequest,
|
|
19
|
+
LoanCreditAnalyticsBatchResponse,
|
|
20
|
+
} from "./dtos/LoanCreditAnalyticsBatch.js";
|
|
17
21
|
import { ILoanCreditBusinessApi } from "./interfaces/ILoanCreditBusinessApi.js";
|
|
18
22
|
|
|
19
23
|
/**
|
|
@@ -94,6 +98,14 @@ export default class LoanCreditBusinessApi implements ILoanCreditBusinessApi {
|
|
|
94
98
|
return await this.httpRequest.get(url);
|
|
95
99
|
}
|
|
96
100
|
|
|
101
|
+
async batchGetCreditAnalytics(
|
|
102
|
+
input: LoanCreditAnalyticsBatchRequest,
|
|
103
|
+
tenantId: string,
|
|
104
|
+
): Promise<StandardResponse<LoanCreditAnalyticsBatchResponse>> {
|
|
105
|
+
const url = `${this.baseUrl}/private/credits/analytics?tenantId=${encodeURIComponent(tenantId)}`;
|
|
106
|
+
return await this.httpRequest.post(url, input);
|
|
107
|
+
}
|
|
108
|
+
|
|
97
109
|
async upsertBorrower(
|
|
98
110
|
input: UpsertLoanBorrowerRequest,
|
|
99
111
|
tenantId: string,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { LoanCreditStatusEnum } from "@fiado/type-kit/bin/loanCredit/index.js";
|
|
2
|
+
import { ClientLevelEnum } from "@fiado/type-kit/bin/loanScoring/index.js";
|
|
3
|
+
|
|
4
|
+
/** Body de `POST /private/credits/analytics`. Hasta 500 ids por request; el caller pagina. */
|
|
5
|
+
export interface LoanCreditAnalyticsBatchRequest {
|
|
6
|
+
creditIds: string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Lo mínimo de un crédito para contar: eje de riesgo, plan, cifras del enganche, estado y fecha.
|
|
11
|
+
* CERO PII — ni acreditado, ni nombre, ni teléfono.
|
|
12
|
+
*/
|
|
13
|
+
export interface LoanCreditAnalyticsItem {
|
|
14
|
+
creditId: string;
|
|
15
|
+
/** Nivel CONGELADO al originar; ausente en los créditos que se originaron sin SCI. */
|
|
16
|
+
clientLevelAtOrigination?: ClientLevelEnum;
|
|
17
|
+
/** Score CONGELADO al originar; ausente en los créditos que se originaron sin SCI. */
|
|
18
|
+
sciAtOrigination?: number;
|
|
19
|
+
/** Plan comercial con el que se originó. Es lo que distingue una venta a empleado. */
|
|
20
|
+
planId: string;
|
|
21
|
+
equipmentPriceCents: number;
|
|
22
|
+
downPaymentCents: number;
|
|
23
|
+
status: LoanCreditStatusEnum;
|
|
24
|
+
createdAt: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Los ids que no existen simplemente no vuelven; los que DynamoDB no alcanzó a leer salen
|
|
29
|
+
* NOMBRADOS para que el caller no los cuente como cero.
|
|
30
|
+
*/
|
|
31
|
+
export interface LoanCreditAnalyticsBatchResponse {
|
|
32
|
+
items: LoanCreditAnalyticsItem[];
|
|
33
|
+
unresolvedCreditIds: string[];
|
|
34
|
+
}
|
|
@@ -12,6 +12,10 @@ import {
|
|
|
12
12
|
SignLoanCreditResponse,
|
|
13
13
|
UpsertLoanBorrowerRequest,
|
|
14
14
|
} from "@fiado/type-kit/bin/loanCredit/index.js";
|
|
15
|
+
import {
|
|
16
|
+
LoanCreditAnalyticsBatchRequest,
|
|
17
|
+
LoanCreditAnalyticsBatchResponse,
|
|
18
|
+
} from "../dtos/LoanCreditAnalyticsBatch.js";
|
|
15
19
|
|
|
16
20
|
/**
|
|
17
21
|
* Contrato del publisher de `loan-credit-business` (componente 09 SureKeep F2 — motor de crédito
|
|
@@ -100,6 +104,20 @@ export interface ILoanCreditBusinessApi {
|
|
|
100
104
|
tenantId: string,
|
|
101
105
|
): Promise<StandardResponse<LoanBorrowerResponse>>;
|
|
102
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Lee créditos EN LOTE por su id y devuelve solo lo que se cuenta: el eje de riesgo congelado
|
|
109
|
+
* al originar, el plan, las cifras del enganche, el estado y la fecha. **Cero PII.**
|
|
110
|
+
*
|
|
111
|
+
* Se resuelve por BatchGetItem sobre la clave primaria, así que el orden de salida NO es el de
|
|
112
|
+
* entrada. Un id que no existe no vuelve; uno que DynamoDB no alcanzó a leer vuelve nombrado en
|
|
113
|
+
* `unresolvedCreditIds` — nunca se confunde «no pude leerlo» con «no existe».
|
|
114
|
+
* `400 VALIDATION_ERROR` si el lote pasa de 500 ids o trae uno que no es ULID.
|
|
115
|
+
*/
|
|
116
|
+
batchGetCreditAnalytics(
|
|
117
|
+
input: LoanCreditAnalyticsBatchRequest,
|
|
118
|
+
tenantId: string,
|
|
119
|
+
): Promise<StandardResponse<LoanCreditAnalyticsBatchResponse>>;
|
|
120
|
+
|
|
103
121
|
/**
|
|
104
122
|
* Upsert idempotente del acreditado por `retailCustomerId` (mismo contrato que el consumidor
|
|
105
123
|
* del evento `CustomerKycVerifiedV1` — fallback operativo mientras la cola no exista).
|
package/src/loanCredit/index.ts
CHANGED
|
@@ -11,11 +11,12 @@ import {
|
|
|
11
11
|
} from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
12
12
|
import {
|
|
13
13
|
IRetailOrgBusinessApi,
|
|
14
|
+
RetailerRosterStatusFilter,
|
|
14
15
|
StoreRosterStatusFilter,
|
|
15
16
|
} from "./interfaces/IRetailOrgBusinessApi.js";
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
|
-
* Publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1) para sus
|
|
19
|
+
* Publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1) para sus 9
|
|
19
20
|
* endpoints privados de validación. Contrato y semántica completos → `IRetailOrgBusinessApi`.
|
|
20
21
|
*
|
|
21
22
|
* Los paths y el `?tenantId=` fueron verificados contra dev (200 + negativos 400/404) el
|
|
@@ -34,6 +35,23 @@ export default class RetailOrgBusinessApi implements IRetailOrgBusinessApi {
|
|
|
34
35
|
|
|
35
36
|
constructor(@inject("IHttpRequest") private httpRequest: IHttpRequest) {}
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* El padrón de retailers del SILO — lo consume la pantalla de analítica del Portal Admin, que
|
|
40
|
+
* agrega ventas de todo el silo y no de una cadena. Contrato y semántica completos (incluido por
|
|
41
|
+
* qué `status` SÍ tiene default y por qué acá el estado es el CRUDO, sin cascada del padre)
|
|
42
|
+
* → `IRetailOrgBusinessApi.listRetailers`.
|
|
43
|
+
*/
|
|
44
|
+
async listRetailers(
|
|
45
|
+
tenantId: string,
|
|
46
|
+
status?: RetailerRosterStatusFilter,
|
|
47
|
+
): Promise<StandardResponse<RetailerValidationDto[]>> {
|
|
48
|
+
// `URLSearchParams` en vez de concatenar: `status` es opcional y armar el `&status=` a mano
|
|
49
|
+
// deja un `&status=undefined` en cuanto alguien se distrae.
|
|
50
|
+
const query = new URLSearchParams({ tenantId });
|
|
51
|
+
if (status) query.set("status", status);
|
|
52
|
+
return await this.httpRequest.get(`${this.baseUrl}/private/retailers?${query.toString()}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
37
55
|
async getRetailer(
|
|
38
56
|
retailerId: string,
|
|
39
57
|
tenantId: string,
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
CollectorValidationDto,
|
|
7
7
|
PrivateSellerListResponse,
|
|
8
8
|
RetailUserStatusEnum,
|
|
9
|
+
RetailerStatusEnum,
|
|
9
10
|
StoreStatusEnum,
|
|
10
11
|
} from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
11
12
|
|
|
@@ -16,9 +17,12 @@ import {
|
|
|
16
17
|
*/
|
|
17
18
|
export type StoreRosterStatusFilter = StoreStatusEnum | "ALL";
|
|
18
19
|
|
|
20
|
+
/** Filtro de `listRetailers`. Mismo criterio que `StoreRosterStatusFilter`, un nivel más arriba. */
|
|
21
|
+
export type RetailerRosterStatusFilter = RetailerStatusEnum | "ALL";
|
|
22
|
+
|
|
19
23
|
/**
|
|
20
24
|
* Contrato del publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1)
|
|
21
|
-
* para sus
|
|
25
|
+
* para sus 9 endpoints privados de VALIDACIÓN (service-to-service, VPC-only).
|
|
22
26
|
*
|
|
23
27
|
* Consumidores previstos (regla R5 — cruces entre lambdas son SOLO lecturas de validación;
|
|
24
28
|
* ningún lambda escribe en tabla ajena):
|
|
@@ -61,6 +65,47 @@ export type StoreRosterStatusFilter = StoreStatusEnum | "ALL";
|
|
|
61
65
|
* Convención CLAUDE.md global: SSM key = nombre del lambda owner de la URL.
|
|
62
66
|
*/
|
|
63
67
|
export interface IRetailOrgBusinessApi {
|
|
68
|
+
/**
|
|
69
|
+
* `GET /private/retailers` — el **padrón de retailers del SILO**.
|
|
70
|
+
*
|
|
71
|
+
* ── 🔴 Por qué este método existe ────────────────────────────────────────────────────────────
|
|
72
|
+
* Lo consume la pantalla de analítica del Portal Admin, que agrega ventas de TODO el silo y no
|
|
73
|
+
* de una cadena. Para eso necesita saber **qué retailers existen**, y ningún otro privado lo
|
|
74
|
+
* dice: `getRetailer` resuelve UNO por id, `getRetailerByStore` el dueño de una tienda, y el
|
|
75
|
+
* resto baja un nivel. El único que lista cadenas, `publicListRetailers`, va detrás del
|
|
76
|
+
* `W4Authorizer` (token de usuario, no invocable entre lambdas), pagina, y devuelve el registro
|
|
77
|
+
* completo con RFC y datos de contacto.
|
|
78
|
+
*
|
|
79
|
+
* Es el hermano de `listStoresByRetailer` un nivel más arriba: aquel da las tiendas de UNA
|
|
80
|
+
* cadena, este da las cadenas. Sin `retailerId` en la firma: el recurso es el silo entero, que
|
|
81
|
+
* ya lo determina el `tenantId`.
|
|
82
|
+
*
|
|
83
|
+
* Devuelve el mismo `RetailerValidationDto` que `getRetailer` — `retailerId`, `name`, `type`,
|
|
84
|
+
* `status`, `parentId` — **sin ningún campo PII**. Trae el árbol COMPLETO (cadenas raíz +
|
|
85
|
+
* subdistribuidores + franquicias, a cualquier profundidad): una venta cuelga de una tienda que
|
|
86
|
+
* pertenece a un retailer de cualquier nivel, así que quedarse en las raíces perdería ventas.
|
|
87
|
+
*
|
|
88
|
+
* ⚠️ El `status` es el CRUDO de la fila, NO uno efectivo — a diferencia de `getStore` y
|
|
89
|
+
* `listStoresByRetailer`, donde sí viene la cascada del padre aplicada. En retail-org un
|
|
90
|
+
* retailer no hereda el estado de su padre en ninguna lectura: un subdistribuidor `ACTIVE` de
|
|
91
|
+
* una cadena `SUSPENDED` sale como `ACTIVE`. Si eso importa, cruza el `parentId`.
|
|
92
|
+
*
|
|
93
|
+
* ── ⚠️ `status` SÍ tiene default, igual que `listStoresByRetailer` ───────────────────────────
|
|
94
|
+
* Omitirlo devuelve **solo los `ACTIVE`**. Pasa `"ALL"` para el padrón completo. El default vive
|
|
95
|
+
* en el lambda, no acá: mandarlo implícito desde el publisher partiría la decisión en dos
|
|
96
|
+
* lugares que alguien tendría que mantener sincronizados.
|
|
97
|
+
*
|
|
98
|
+
* Devuelve `[]` si ningún retailer pasa el filtro. **No hay 404**: el silo siempre existe — lo
|
|
99
|
+
* garantiza el `tenantId`, que es lo que resolvió el contexto del tenant.
|
|
100
|
+
*
|
|
101
|
+
* @throws `400 RETAILER_STATUS_FILTER_INVALID` si `status` no es del catálogo ·
|
|
102
|
+
* `400 UNKNOWN_TENANT` sin `tenantId`.
|
|
103
|
+
*/
|
|
104
|
+
listRetailers(
|
|
105
|
+
tenantId: string,
|
|
106
|
+
status?: RetailerRosterStatusFilter,
|
|
107
|
+
): Promise<StandardResponse<RetailerValidationDto[]>>;
|
|
108
|
+
|
|
64
109
|
/**
|
|
65
110
|
* GET /private/retailers/{retailerId} — valida que el retailer exista y devuelve su shape mínimo
|
|
66
111
|
* (id, name, type, status, parentId). `status` ya viene con la cascada aplicada (estado efectivo).
|