@fiado/api-invoker 5.61.0 → 5.63.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/loanOfferings/api/LoanOfferingsBusinessApi.d.ts +2 -0
- package/bin/loanOfferings/api/LoanOfferingsBusinessApi.js +8 -0
- package/bin/loanOfferings/api/dtos/SegmentMembers.d.ts +23 -0
- package/bin/loanOfferings/api/dtos/SegmentMembers.js +5 -0
- package/bin/loanOfferings/api/interfaces/ILoanOfferingsBusinessApi.d.ts +14 -0
- package/bin/loanOfferings/index.d.ts +1 -0
- package/bin/loanOfferings/index.js +1 -0
- package/bin/retailCatalog/api/RetailCatalogBusinessApi.d.ts +2 -1
- package/bin/retailCatalog/api/RetailCatalogBusinessApi.js +10 -1
- package/bin/retailCatalog/api/interfaces/IRetailCatalogBusinessApi.d.ts +50 -0
- package/bin/retailCatalog/api/interfaces/IRetailCatalogBusinessApi.js +2 -0
- package/bin/retailCustomer/api/RetailCustomerBusinessApi.d.ts +3 -0
- package/bin/retailCustomer/api/RetailCustomerBusinessApi.js +10 -0
- package/bin/retailCustomer/api/dtos/CustomersPage.d.ts +25 -0
- package/bin/retailCustomer/api/dtos/CustomersPage.js +6 -0
- package/bin/retailCustomer/api/interfaces/IRetailCustomerBusinessApi.d.ts +16 -0
- package/bin/retailCustomer/index.d.ts +1 -0
- package/bin/retailCustomer/index.js +1 -0
- package/package.json +1 -1
- package/src/loanOfferings/api/LoanOfferingsBusinessApi.ts +13 -0
- package/src/loanOfferings/api/dtos/SegmentMembers.ts +26 -0
- package/src/loanOfferings/api/interfaces/ILoanOfferingsBusinessApi.ts +18 -0
- package/src/loanOfferings/index.ts +1 -0
- package/src/retailCatalog/api/RetailCatalogBusinessApi.ts +18 -0
- package/src/retailCatalog/api/interfaces/IRetailCatalogBusinessApi.ts +58 -0
- package/src/retailCustomer/api/RetailCustomerBusinessApi.ts +14 -0
- package/src/retailCustomer/api/dtos/CustomersPage.ts +27 -0
- package/src/retailCustomer/api/interfaces/IRetailCustomerBusinessApi.ts +20 -0
- package/src/retailCustomer/index.ts +1 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { IHttpRequest } from "@fiado/http-client";
|
|
2
2
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
3
3
|
import { ConsumePromotionRequest, PromotedSkusResponse, ConsumePromotionResponse, CreditPlanLevelEnum, CreditPlanResponse, FinancierResponse, ReservePromotionBonusRequest, ReservePromotionBonusResponse, ResolveCustomerSegmentsRequest, ResolveCustomerSegmentsResponse, ScopeWidePromotionsResponse, SkuPromotionsResponse, SettlePromotionBonusRequest, PromotionBonusResponse, SimulateCreditPlanRequest, SimulationResultResponse } from "@fiado/type-kit/bin/loanOfferings/index.js";
|
|
4
|
+
import { SegmentMembersResponse } from "./dtos/SegmentMembers.js";
|
|
4
5
|
import { ILoanOfferingsBusinessApi } from "./interfaces/ILoanOfferingsBusinessApi.js";
|
|
5
6
|
/**
|
|
6
7
|
* Publisher HTTP del lambda `loan-offerings-business` (componente 07 SureKeep Fase 1) para sus
|
|
@@ -29,4 +30,5 @@ export default class LoanOfferingsBusinessApi implements ILoanOfferingsBusinessA
|
|
|
29
30
|
settlePromotionBonus(promotionId: string, input: SettlePromotionBonusRequest, tenantId: string): Promise<StandardResponse<PromotionBonusResponse>>;
|
|
30
31
|
listSkuPromotions(storeId: string, tenantId: string, level?: CreditPlanLevelEnum, segmentId?: string): Promise<StandardResponse<SkuPromotionsResponse>>;
|
|
31
32
|
listScopeWidePromotions(storeId: string, tenantId: string, level?: CreditPlanLevelEnum, segmentId?: string): Promise<StandardResponse<ScopeWidePromotionsResponse>>;
|
|
33
|
+
getSegmentMembers(retailCustomerIds: string[], tenantId: string): Promise<StandardResponse<SegmentMembersResponse>>;
|
|
32
34
|
}
|
|
@@ -86,6 +86,14 @@ let LoanOfferingsBusinessApi = class LoanOfferingsBusinessApi {
|
|
|
86
86
|
url += `&segmentId=${encodeURIComponent(segmentId)}`;
|
|
87
87
|
return await this.httpRequest.get(url);
|
|
88
88
|
}
|
|
89
|
+
async getSegmentMembers(retailCustomerIds, tenantId) {
|
|
90
|
+
const params = new URLSearchParams({
|
|
91
|
+
retailCustomerIds: retailCustomerIds.join(","),
|
|
92
|
+
tenantId,
|
|
93
|
+
});
|
|
94
|
+
const url = `${this.baseUrl}/private/segments/members?${params.toString()}`;
|
|
95
|
+
return await this.httpRequest.get(url);
|
|
96
|
+
}
|
|
89
97
|
};
|
|
90
98
|
LoanOfferingsBusinessApi = __decorate([
|
|
91
99
|
injectable(),
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tipos de la pertenencia a segmentos EN LOTE. Viven en esta lib (y no en `@fiado/type-kit`) porque
|
|
3
|
+
* el type-kit todavía no modela esta vista reducida — el lambda destino la tiene como DTO local.
|
|
4
|
+
*/
|
|
5
|
+
/** Un segmento al que pertenece el cliente, con lo mínimo para pintarlo. */
|
|
6
|
+
export interface CustomerSegmentMembership {
|
|
7
|
+
segmentId: string;
|
|
8
|
+
name: string;
|
|
9
|
+
/**
|
|
10
|
+
* Segmento por omisión del catálogo. Su semántica cambia por motor (alcanza a todos en
|
|
11
|
+
* promociones y a nadie en políticas), así que el consumidor decide si lo pinta.
|
|
12
|
+
*/
|
|
13
|
+
isDefault: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** Un cliente y los segmentos que le tocan. `segments: []` afirma que no está en ninguno. */
|
|
16
|
+
export interface SegmentMembersItem {
|
|
17
|
+
retailCustomerId: string;
|
|
18
|
+
segments: CustomerSegmentMembership[];
|
|
19
|
+
}
|
|
20
|
+
/** Una fila por cada id PEDIDO, no por cada id con segmentos. */
|
|
21
|
+
export interface SegmentMembersResponse {
|
|
22
|
+
items: SegmentMembersItem[];
|
|
23
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
2
|
+
import { SegmentMembersResponse } from "../dtos/SegmentMembers.js";
|
|
2
3
|
import { ConsumePromotionRequest, PromotedSkusResponse, ConsumePromotionResponse, CreditPlanLevelEnum, CreditPlanResponse, FinancierResponse, ReservePromotionBonusRequest, ReservePromotionBonusResponse, ResolveCustomerSegmentsRequest, ResolveCustomerSegmentsResponse, ScopeWidePromotionsResponse, SkuPromotionsResponse, SettlePromotionBonusRequest, PromotionBonusResponse, SimulateCreditPlanRequest, SimulationResultResponse } from "@fiado/type-kit/bin/loanOfferings/index.js";
|
|
3
4
|
/**
|
|
4
5
|
* Contrato del publisher HTTP del lambda `loan-offerings-business` (componente 07 SureKeep Fase 1)
|
|
@@ -133,4 +134,17 @@ export interface ILoanOfferingsBusinessApi {
|
|
|
133
134
|
* hay ninguna vigente; nunca 404.
|
|
134
135
|
*/
|
|
135
136
|
listScopeWidePromotions(storeId: string, tenantId: string, level?: CreditPlanLevelEnum, segmentId?: string): Promise<StandardResponse<ScopeWidePromotionsResponse>>;
|
|
137
|
+
/**
|
|
138
|
+
* `GET /private/segments/members?retailCustomerIds=<csv>` — pertenencia a segmentos EN LOTE.
|
|
139
|
+
* Es la alternativa a resolver segmento por cliente: una llamada por página de tabla.
|
|
140
|
+
*
|
|
141
|
+
* Devuelve una fila por cada id PEDIDO: los que no están en ningún segmento vuelven con
|
|
142
|
+
* `segments: []`, no se omiten. Aun así, indexa por `retailCustomerId` — nunca por posición.
|
|
143
|
+
*
|
|
144
|
+
* Tope de 100 ids por request, medido sobre lo que manda el caller.
|
|
145
|
+
*
|
|
146
|
+
* @throws `400 SEGMENT_MEMBERS_BATCH_TOO_LARGE` si el lote pasa de 100 ids.
|
|
147
|
+
* @throws `400 UNKNOWN_TENANT` si el `tenantId` viene ausente o desconocido.
|
|
148
|
+
*/
|
|
149
|
+
getSegmentMembers(retailCustomerIds: string[], tenantId: string): Promise<StandardResponse<SegmentMembersResponse>>;
|
|
136
150
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IHttpRequest } from "@fiado/http-client";
|
|
2
2
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
3
|
-
import { ImeiProductLookupResponse, IRetailCatalogBusinessApi, ReleaseReservationBody, ReserveInventoryBody, ReserveInventoryResult, SellInventoryBody, StoreCatalogItem } from "./interfaces/IRetailCatalogBusinessApi.js";
|
|
3
|
+
import { ImeiProductLookupResponse, IRetailCatalogBusinessApi, ProductsByRetailersResponse, ReleaseReservationBody, ReserveInventoryBody, ReserveInventoryResult, SellInventoryBody, StoreCatalogItem } from "./interfaces/IRetailCatalogBusinessApi.js";
|
|
4
4
|
/**
|
|
5
5
|
* Publisher HTTP del lambda `retail-catalog-business` (SureKeep Fase 1, pista Retail) para sus
|
|
6
6
|
* endpoints privados del ciclo de venta del IMEI. Contrato y semántica completos →
|
|
@@ -21,6 +21,7 @@ export default class RetailCatalogBusinessApi implements IRetailCatalogBusinessA
|
|
|
21
21
|
private tenantHeader;
|
|
22
22
|
getStoreCatalog(storeId: string, issuer: string): Promise<StandardResponse<StoreCatalogItem[]>>;
|
|
23
23
|
getProductsByImei(imeis: string[], tenantId: string): Promise<StandardResponse<ImeiProductLookupResponse>>;
|
|
24
|
+
getProductsByRetailers(retailerIds: string[], tenantId: string): Promise<StandardResponse<ProductsByRetailersResponse>>;
|
|
24
25
|
reserveInventory(imei: string, body: ReserveInventoryBody, issuer: string): Promise<StandardResponse<ReserveInventoryResult>>;
|
|
25
26
|
sellInventory(imei: string, body: SellInventoryBody, issuer: string): Promise<StandardResponse<void>>;
|
|
26
27
|
releaseInventory(imei: string, body: ReleaseReservationBody, issuer: string): Promise<StandardResponse<void>>;
|
|
@@ -11,7 +11,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
11
11
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
12
|
};
|
|
13
13
|
import { inject, injectable } from "inversify";
|
|
14
|
-
import { IMEI_PRODUCT_BATCH_MAX, } from "./interfaces/IRetailCatalogBusinessApi.js";
|
|
14
|
+
import { IMEI_PRODUCT_BATCH_MAX, RETAILER_PRODUCTS_BATCH_MAX, } from "./interfaces/IRetailCatalogBusinessApi.js";
|
|
15
15
|
/** Header por el que el destino resuelve el silo (issuer Cognito forwardeado por el caller). */
|
|
16
16
|
const TENANT_ISSUER_HEADER = "x-tenant-issuer";
|
|
17
17
|
/**
|
|
@@ -51,6 +51,15 @@ let RetailCatalogBusinessApi = class RetailCatalogBusinessApi {
|
|
|
51
51
|
const query = new URLSearchParams({ imeis: imeis.join(","), tenantId });
|
|
52
52
|
return await this.httpRequest.get(`${this.baseUrl}/private/products/by-imei?${query.toString()}`);
|
|
53
53
|
}
|
|
54
|
+
async getProductsByRetailers(retailerIds, tenantId) {
|
|
55
|
+
// Se corta aquí: mandar un lote que el destino va a rechazar solo gasta un round-trip.
|
|
56
|
+
if (retailerIds.length > RETAILER_PRODUCTS_BATCH_MAX) {
|
|
57
|
+
throw new Error(`El lote admite hasta ${RETAILER_PRODUCTS_BATCH_MAX} cadenas y llegaron ${retailerIds.length}`);
|
|
58
|
+
}
|
|
59
|
+
const query = new URLSearchParams({ tenantId });
|
|
60
|
+
const body = { retailerIds };
|
|
61
|
+
return await this.httpRequest.post(`${this.baseUrl}/private/products/by-retailers?${query.toString()}`, body);
|
|
62
|
+
}
|
|
54
63
|
async reserveInventory(imei, body, issuer) {
|
|
55
64
|
const url = `${this.baseUrl}/private/inventory/${encodeURIComponent(imei)}/reserve`;
|
|
56
65
|
return await this.httpRequest.put(url, body, this.tenantHeader(issuer));
|
|
@@ -80,6 +80,30 @@ export interface ImeiProductLookupEntry {
|
|
|
80
80
|
export interface ImeiProductLookupResponse {
|
|
81
81
|
items: ImeiProductLookupEntry[];
|
|
82
82
|
}
|
|
83
|
+
/** Techo de cadenas por request del lote cadena->catálogo. Lo declara y lo honra el destino. */
|
|
84
|
+
export declare const RETAILER_PRODUCTS_BATCH_MAX = 100;
|
|
85
|
+
/** Body de POST `/private/products/by-retailers`. */
|
|
86
|
+
export interface ProductsByRetailersBody {
|
|
87
|
+
retailerIds: string[];
|
|
88
|
+
}
|
|
89
|
+
/** Un producto vendible de una cadena (espeja `RetailerProductItem` del destino). */
|
|
90
|
+
export interface RetailerProductItem {
|
|
91
|
+
/** Cadena a la que pertenece el producto; es la llave con la que el caller agrupa. */
|
|
92
|
+
retailerId: string;
|
|
93
|
+
sku: string;
|
|
94
|
+
/** Marca comercial (p. ej. "Samsung"). */
|
|
95
|
+
brand: string;
|
|
96
|
+
/** Modelo (p. ej. "A54"). */
|
|
97
|
+
model: string;
|
|
98
|
+
}
|
|
99
|
+
/** Respuesta del lote cadena->catálogo: los productos de TODAS las cadenas pedidas, en una lista plana. */
|
|
100
|
+
export interface ProductsByRetailersResponse {
|
|
101
|
+
items: RetailerProductItem[];
|
|
102
|
+
/** Cuántos productos trae `items`. */
|
|
103
|
+
count: number;
|
|
104
|
+
/** `true` si el destino recortó el resultado; pide las cadenas en lotes más chicos. */
|
|
105
|
+
truncated: boolean;
|
|
106
|
+
}
|
|
83
107
|
/** Resultado de la reserva (espeja `ReserveInventoryResponse` del destino). */
|
|
84
108
|
export interface ReserveInventoryResult {
|
|
85
109
|
reservationExpiresAt: string;
|
|
@@ -125,6 +149,32 @@ export interface IRetailCatalogBusinessApi {
|
|
|
125
149
|
* `400 IMEI_BATCH_REQUIRED` si `imeis` viene vacío · `400 UNKNOWN_TENANT` sin `tenantId`.
|
|
126
150
|
*/
|
|
127
151
|
getProductsByImei(imeis: string[], tenantId: string): Promise<StandardResponse<ImeiProductLookupResponse>>;
|
|
152
|
+
/**
|
|
153
|
+
* POST `/private/products/by-retailers?tenantId=` — catálogo de productos de VARIAS cadenas en
|
|
154
|
+
* una sola llamada, para el consumidor que arma su vista con N cadenas y no quiere N requests.
|
|
155
|
+
*
|
|
156
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
157
|
+
* ⚠️ El tenant va por `?tenantId=`, NO por el header `x-tenant-issuer` del resto de este publisher.
|
|
158
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
159
|
+
* Es obligatorio. El destino entra por `TenantContextService.withContextByTenantId`, igual que
|
|
160
|
+
* `getProductsByImei`.
|
|
161
|
+
*
|
|
162
|
+
* ── El contrato del lote ────────────────────────────────────────────────────────────────────
|
|
163
|
+
* `retailerIds` no puede venir vacío, sin repetidos y con un techo de
|
|
164
|
+
* `RETAILER_PRODUCTS_BATCH_MAX` cadenas; el caller parte los lotes mayores. La respuesta es una
|
|
165
|
+
* lista PLANA de productos con su `retailerId` — el caller agrupa. Una cadena sin catálogo
|
|
166
|
+
* simplemente no aporta ítems.
|
|
167
|
+
*
|
|
168
|
+
* O el lote sale completo o no sale: si falla la lectura de al menos una cadena el destino
|
|
169
|
+
* responde `INTERNAL_ERROR`. Nunca recibes una lista parcial.
|
|
170
|
+
*
|
|
171
|
+
* @throws Error si el lote supera el techo (se corta aquí, sin salir a la red) ·
|
|
172
|
+
* `400 VALIDATION_ERROR` body vacío, con repetidos, con no-strings o de más de 100 ids ·
|
|
173
|
+
* `400 UNKNOWN_TENANT` falta o no se reconoce el `tenantId` ·
|
|
174
|
+
* `500 TENANT_ASSUME_ROLE_FAILED` fallo de infra del silo ·
|
|
175
|
+
* `500 INTERNAL_ERROR` no se pudo leer alguna cadena.
|
|
176
|
+
*/
|
|
177
|
+
getProductsByRetailers(retailerIds: string[], tenantId: string): Promise<StandardResponse<ProductsByRetailersResponse>>;
|
|
128
178
|
/** PUT `/private/inventory/{imei}/reserve` — reserva atómica del IMEI para una sesión (AVAILABLE → RESERVED). */
|
|
129
179
|
reserveInventory(imei: string, body: ReserveInventoryBody, issuer: string): Promise<StandardResponse<ReserveInventoryResult>>;
|
|
130
180
|
/** PUT `/private/inventory/{imei}/sell` — vende el IMEI (RESERVED → SOLD). */
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
/** Techo de IMEIs por request del lote IMEI->producto. Lo declara y lo honra el destino. */
|
|
2
2
|
export const IMEI_PRODUCT_BATCH_MAX = 100;
|
|
3
|
+
/** Techo de cadenas por request del lote cadena->catálogo. Lo declara y lo honra el destino. */
|
|
4
|
+
export const RETAILER_PRODUCTS_BATCH_MAX = 100;
|
|
@@ -2,6 +2,8 @@ import type { IHttpRequest } from "@fiado/http-client";
|
|
|
2
2
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
3
3
|
import { UpsertCustomerFromKycRequest, AccountResultRequest, CustomerResponse, WelcomeAccreditationRequest, WelcomeAccreditationResponse, WelcomeAccreditationResultRequest, WelcomeBonusResponse } from "@fiado/type-kit/bin/retailCustomer/index.js";
|
|
4
4
|
import { CustomersBatchRequest, CustomersBatchResponse } from "./dtos/CustomersBatch.js";
|
|
5
|
+
import { CustomersPageQuery, PrivateCustomerListItem } from "./dtos/CustomersPage.js";
|
|
6
|
+
import { PaginatedResult } from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
5
7
|
import { IRetailCustomerBusinessApi } from "./interfaces/IRetailCustomerBusinessApi.js";
|
|
6
8
|
/**
|
|
7
9
|
* Publisher HTTP del lambda `retail-customer-business` (SureKeep Fase 2, pista Retail) para sus 7
|
|
@@ -28,4 +30,5 @@ export default class RetailCustomerBusinessApi implements IRetailCustomerBusines
|
|
|
28
30
|
markWelcomeAccredited(customerId: string, tenantId: string, payload?: WelcomeAccreditationRequest): Promise<StandardResponse<WelcomeAccreditationResponse>>;
|
|
29
31
|
setWelcomeAccreditationResult(customerId: string, payload: WelcomeAccreditationResultRequest, tenantId: string): Promise<StandardResponse<WelcomeBonusResponse>>;
|
|
30
32
|
getCustomersBatch(payload: CustomersBatchRequest, tenantId: string): Promise<StandardResponse<CustomersBatchResponse>>;
|
|
33
|
+
listCustomersPage(query: CustomersPageQuery, tenantId: string): Promise<StandardResponse<PaginatedResult<PrivateCustomerListItem>>>;
|
|
31
34
|
}
|
|
@@ -60,6 +60,16 @@ let RetailCustomerBusinessApi = class RetailCustomerBusinessApi {
|
|
|
60
60
|
const url = `${this.baseUrl}/private/customers/batch?tenantId=${encodeURIComponent(tenantId)}`;
|
|
61
61
|
return await this.httpRequest.post(url, payload);
|
|
62
62
|
}
|
|
63
|
+
async listCustomersPage(query, tenantId) {
|
|
64
|
+
const params = new URLSearchParams({ tenantId });
|
|
65
|
+
if (query.limit !== undefined)
|
|
66
|
+
params.set("limit", String(query.limit));
|
|
67
|
+
// El cursor viaja tal cual lo entregó el destino: componerlo o re-codificarlo lo rompe.
|
|
68
|
+
if (query.nextToken)
|
|
69
|
+
params.set("nextToken", query.nextToken);
|
|
70
|
+
const url = `${this.baseUrl}/private/customers?${params.toString()}`;
|
|
71
|
+
return await this.httpRequest.get(url);
|
|
72
|
+
}
|
|
63
73
|
};
|
|
64
74
|
RetailCustomerBusinessApi = __decorate([
|
|
65
75
|
injectable(),
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tipos del listado paginado de clientes del tenant. Viven en esta lib (y no en `@fiado/type-kit`)
|
|
3
|
+
* porque el type-kit todavía no modela esta vista reducida — el lambda destino la tiene como DTO
|
|
4
|
+
* local, mismo criterio que `CustomersBatch`.
|
|
5
|
+
*/
|
|
6
|
+
/** Query de `GET /private/customers`. `limit` default 25, máximo 100 del lado del destino. */
|
|
7
|
+
export interface CustomersPageQuery {
|
|
8
|
+
limit?: number;
|
|
9
|
+
/** Cursor opaco de la página anterior. Ausente = primera página. */
|
|
10
|
+
nextToken?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Lo mínimo para pintar el renglón de un cliente. `fullName: null` significa que el cliente existe
|
|
14
|
+
* y no tiene ninguna parte del nombre resuelta — es distinto de que el id no vuelva.
|
|
15
|
+
*/
|
|
16
|
+
export interface PrivateCustomerListItem {
|
|
17
|
+
retailCustomerId: string;
|
|
18
|
+
/** `names + paternalLastName + maternalLastName` ya compuesto por el destino. */
|
|
19
|
+
fullName: string | null;
|
|
20
|
+
phone: string | null;
|
|
21
|
+
/** Tienda donde se dio de alta. La ficha del backoffice la nombra y resuelve su KYC con ella. */
|
|
22
|
+
originStoreId: string | null;
|
|
23
|
+
/** Alta del cliente en ISO-8601 UTC. Es el orden del listado (descendente). */
|
|
24
|
+
createdAt: string;
|
|
25
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
2
2
|
import { UpsertCustomerFromKycRequest, AccountResultRequest, CustomerResponse, WelcomeAccreditationRequest, WelcomeAccreditationResponse, WelcomeAccreditationResultRequest, WelcomeBonusResponse } from "@fiado/type-kit/bin/retailCustomer/index.js";
|
|
3
3
|
import { CustomersBatchRequest, CustomersBatchResponse } from "../dtos/CustomersBatch.js";
|
|
4
|
+
import { CustomersPageQuery, PrivateCustomerListItem } from "../dtos/CustomersPage.js";
|
|
5
|
+
import { PaginatedResult } from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
4
6
|
/**
|
|
5
7
|
* Contrato del publisher HTTP del lambda `retail-customer-business` (SureKeep Fase 2, pista Retail)
|
|
6
8
|
* para sus 7 endpoints privados de cliente (service-to-service, VPC-only).
|
|
@@ -134,4 +136,18 @@ export interface IRetailCustomerBusinessApi {
|
|
|
134
136
|
* @throws `400 UNKNOWN_TENANT` si el `tenantId` viene ausente o desconocido.
|
|
135
137
|
*/
|
|
136
138
|
getCustomersBatch(payload: CustomersBatchRequest, tenantId: string): Promise<StandardResponse<CustomersBatchResponse>>;
|
|
139
|
+
/**
|
|
140
|
+
* GET /private/customers?limit&nextToken&tenantId= — UNA página del padrón de clientes del
|
|
141
|
+
* tenant, del alta más reciente a la más vieja. Es el listado que alimenta las tablas de
|
|
142
|
+
* backoffice: una sola Query sobre el GSI de listado, sin fan-out por negocio.
|
|
143
|
+
*
|
|
144
|
+
* `nextToken` es OPACO: se pasea tal cual, no se compone ni se re-codifica. Ausente en la
|
|
145
|
+
* respuesta = última página. `count` son las filas de ESA página, nunca el total del padrón.
|
|
146
|
+
*
|
|
147
|
+
* `limit` default 25, máximo 100.
|
|
148
|
+
*
|
|
149
|
+
* @throws `400 INVALID_QUERY_PARAM` si `limit` no parsea o se pasa del tope.
|
|
150
|
+
* @throws `400 UNKNOWN_TENANT` si el `tenantId` viene ausente o desconocido.
|
|
151
|
+
*/
|
|
152
|
+
listCustomersPage(query: CustomersPageQuery, tenantId: string): Promise<StandardResponse<PaginatedResult<PrivateCustomerListItem>>>;
|
|
137
153
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fiado/api-invoker",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.63.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",
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
SimulateCreditPlanRequest,
|
|
20
20
|
SimulationResultResponse,
|
|
21
21
|
} from "@fiado/type-kit/bin/loanOfferings/index.js";
|
|
22
|
+
import { SegmentMembersResponse } from "./dtos/SegmentMembers.js";
|
|
22
23
|
import { ILoanOfferingsBusinessApi } from "./interfaces/ILoanOfferingsBusinessApi.js";
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -142,4 +143,16 @@ export default class LoanOfferingsBusinessApi implements ILoanOfferingsBusinessA
|
|
|
142
143
|
if (segmentId) url += `&segmentId=${encodeURIComponent(segmentId)}`;
|
|
143
144
|
return await this.httpRequest.get(url);
|
|
144
145
|
}
|
|
146
|
+
|
|
147
|
+
async getSegmentMembers(
|
|
148
|
+
retailCustomerIds: string[],
|
|
149
|
+
tenantId: string,
|
|
150
|
+
): Promise<StandardResponse<SegmentMembersResponse>> {
|
|
151
|
+
const params = new URLSearchParams({
|
|
152
|
+
retailCustomerIds: retailCustomerIds.join(","),
|
|
153
|
+
tenantId,
|
|
154
|
+
});
|
|
155
|
+
const url = `${this.baseUrl}/private/segments/members?${params.toString()}`;
|
|
156
|
+
return await this.httpRequest.get(url);
|
|
157
|
+
}
|
|
145
158
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tipos de la pertenencia a segmentos EN LOTE. Viven en esta lib (y no en `@fiado/type-kit`) porque
|
|
3
|
+
* el type-kit todavía no modela esta vista reducida — el lambda destino la tiene como DTO local.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Un segmento al que pertenece el cliente, con lo mínimo para pintarlo. */
|
|
7
|
+
export interface CustomerSegmentMembership {
|
|
8
|
+
segmentId: string;
|
|
9
|
+
name: string;
|
|
10
|
+
/**
|
|
11
|
+
* Segmento por omisión del catálogo. Su semántica cambia por motor (alcanza a todos en
|
|
12
|
+
* promociones y a nadie en políticas), así que el consumidor decide si lo pinta.
|
|
13
|
+
*/
|
|
14
|
+
isDefault: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Un cliente y los segmentos que le tocan. `segments: []` afirma que no está en ninguno. */
|
|
18
|
+
export interface SegmentMembersItem {
|
|
19
|
+
retailCustomerId: string;
|
|
20
|
+
segments: CustomerSegmentMembership[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Una fila por cada id PEDIDO, no por cada id con segmentos. */
|
|
24
|
+
export interface SegmentMembersResponse {
|
|
25
|
+
items: SegmentMembersItem[];
|
|
26
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
2
|
+
import { SegmentMembersResponse } from "../dtos/SegmentMembers.js";
|
|
2
3
|
import {
|
|
3
4
|
ConsumePromotionRequest,
|
|
4
5
|
PromotedSkusResponse,
|
|
@@ -203,4 +204,21 @@ export interface ILoanOfferingsBusinessApi {
|
|
|
203
204
|
level?: CreditPlanLevelEnum,
|
|
204
205
|
segmentId?: string,
|
|
205
206
|
): Promise<StandardResponse<ScopeWidePromotionsResponse>>;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* `GET /private/segments/members?retailCustomerIds=<csv>` — pertenencia a segmentos EN LOTE.
|
|
210
|
+
* Es la alternativa a resolver segmento por cliente: una llamada por página de tabla.
|
|
211
|
+
*
|
|
212
|
+
* Devuelve una fila por cada id PEDIDO: los que no están en ningún segmento vuelven con
|
|
213
|
+
* `segments: []`, no se omiten. Aun así, indexa por `retailCustomerId` — nunca por posición.
|
|
214
|
+
*
|
|
215
|
+
* Tope de 100 ids por request, medido sobre lo que manda el caller.
|
|
216
|
+
*
|
|
217
|
+
* @throws `400 SEGMENT_MEMBERS_BATCH_TOO_LARGE` si el lote pasa de 100 ids.
|
|
218
|
+
* @throws `400 UNKNOWN_TENANT` si el `tenantId` viene ausente o desconocido.
|
|
219
|
+
*/
|
|
220
|
+
getSegmentMembers(
|
|
221
|
+
retailCustomerIds: string[],
|
|
222
|
+
tenantId: string,
|
|
223
|
+
): Promise<StandardResponse<SegmentMembersResponse>>;
|
|
206
224
|
}
|
|
@@ -5,9 +5,12 @@ import {
|
|
|
5
5
|
IMEI_PRODUCT_BATCH_MAX,
|
|
6
6
|
ImeiProductLookupResponse,
|
|
7
7
|
IRetailCatalogBusinessApi,
|
|
8
|
+
ProductsByRetailersBody,
|
|
9
|
+
ProductsByRetailersResponse,
|
|
8
10
|
ReleaseReservationBody,
|
|
9
11
|
ReserveInventoryBody,
|
|
10
12
|
ReserveInventoryResult,
|
|
13
|
+
RETAILER_PRODUCTS_BATCH_MAX,
|
|
11
14
|
SellInventoryBody,
|
|
12
15
|
StoreCatalogItem,
|
|
13
16
|
} from "./interfaces/IRetailCatalogBusinessApi.js";
|
|
@@ -58,6 +61,21 @@ export default class RetailCatalogBusinessApi implements IRetailCatalogBusinessA
|
|
|
58
61
|
return await this.httpRequest.get(`${this.baseUrl}/private/products/by-imei?${query.toString()}`);
|
|
59
62
|
}
|
|
60
63
|
|
|
64
|
+
async getProductsByRetailers(
|
|
65
|
+
retailerIds: string[],
|
|
66
|
+
tenantId: string,
|
|
67
|
+
): Promise<StandardResponse<ProductsByRetailersResponse>> {
|
|
68
|
+
// Se corta aquí: mandar un lote que el destino va a rechazar solo gasta un round-trip.
|
|
69
|
+
if (retailerIds.length > RETAILER_PRODUCTS_BATCH_MAX) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`El lote admite hasta ${RETAILER_PRODUCTS_BATCH_MAX} cadenas y llegaron ${retailerIds.length}`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const query = new URLSearchParams({ tenantId });
|
|
75
|
+
const body: ProductsByRetailersBody = { retailerIds };
|
|
76
|
+
return await this.httpRequest.post(`${this.baseUrl}/private/products/by-retailers?${query.toString()}`, body);
|
|
77
|
+
}
|
|
78
|
+
|
|
61
79
|
async reserveInventory(
|
|
62
80
|
imei: string,
|
|
63
81
|
body: ReserveInventoryBody,
|
|
@@ -93,6 +93,34 @@ export interface ImeiProductLookupResponse {
|
|
|
93
93
|
items: ImeiProductLookupEntry[];
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/** Techo de cadenas por request del lote cadena->catálogo. Lo declara y lo honra el destino. */
|
|
97
|
+
export const RETAILER_PRODUCTS_BATCH_MAX = 100;
|
|
98
|
+
|
|
99
|
+
/** Body de POST `/private/products/by-retailers`. */
|
|
100
|
+
export interface ProductsByRetailersBody {
|
|
101
|
+
retailerIds: string[];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Un producto vendible de una cadena (espeja `RetailerProductItem` del destino). */
|
|
105
|
+
export interface RetailerProductItem {
|
|
106
|
+
/** Cadena a la que pertenece el producto; es la llave con la que el caller agrupa. */
|
|
107
|
+
retailerId: string;
|
|
108
|
+
sku: string;
|
|
109
|
+
/** Marca comercial (p. ej. "Samsung"). */
|
|
110
|
+
brand: string;
|
|
111
|
+
/** Modelo (p. ej. "A54"). */
|
|
112
|
+
model: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Respuesta del lote cadena->catálogo: los productos de TODAS las cadenas pedidas, en una lista plana. */
|
|
116
|
+
export interface ProductsByRetailersResponse {
|
|
117
|
+
items: RetailerProductItem[];
|
|
118
|
+
/** Cuántos productos trae `items`. */
|
|
119
|
+
count: number;
|
|
120
|
+
/** `true` si el destino recortó el resultado; pide las cadenas en lotes más chicos. */
|
|
121
|
+
truncated: boolean;
|
|
122
|
+
}
|
|
123
|
+
|
|
96
124
|
/** Resultado de la reserva (espeja `ReserveInventoryResponse` del destino). */
|
|
97
125
|
export interface ReserveInventoryResult {
|
|
98
126
|
reservationExpiresAt: string;
|
|
@@ -144,6 +172,36 @@ export interface IRetailCatalogBusinessApi {
|
|
|
144
172
|
*/
|
|
145
173
|
getProductsByImei(imeis: string[], tenantId: string): Promise<StandardResponse<ImeiProductLookupResponse>>;
|
|
146
174
|
|
|
175
|
+
/**
|
|
176
|
+
* POST `/private/products/by-retailers?tenantId=` — catálogo de productos de VARIAS cadenas en
|
|
177
|
+
* una sola llamada, para el consumidor que arma su vista con N cadenas y no quiere N requests.
|
|
178
|
+
*
|
|
179
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
180
|
+
* ⚠️ El tenant va por `?tenantId=`, NO por el header `x-tenant-issuer` del resto de este publisher.
|
|
181
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
182
|
+
* Es obligatorio. El destino entra por `TenantContextService.withContextByTenantId`, igual que
|
|
183
|
+
* `getProductsByImei`.
|
|
184
|
+
*
|
|
185
|
+
* ── El contrato del lote ────────────────────────────────────────────────────────────────────
|
|
186
|
+
* `retailerIds` no puede venir vacío, sin repetidos y con un techo de
|
|
187
|
+
* `RETAILER_PRODUCTS_BATCH_MAX` cadenas; el caller parte los lotes mayores. La respuesta es una
|
|
188
|
+
* lista PLANA de productos con su `retailerId` — el caller agrupa. Una cadena sin catálogo
|
|
189
|
+
* simplemente no aporta ítems.
|
|
190
|
+
*
|
|
191
|
+
* O el lote sale completo o no sale: si falla la lectura de al menos una cadena el destino
|
|
192
|
+
* responde `INTERNAL_ERROR`. Nunca recibes una lista parcial.
|
|
193
|
+
*
|
|
194
|
+
* @throws Error si el lote supera el techo (se corta aquí, sin salir a la red) ·
|
|
195
|
+
* `400 VALIDATION_ERROR` body vacío, con repetidos, con no-strings o de más de 100 ids ·
|
|
196
|
+
* `400 UNKNOWN_TENANT` falta o no se reconoce el `tenantId` ·
|
|
197
|
+
* `500 TENANT_ASSUME_ROLE_FAILED` fallo de infra del silo ·
|
|
198
|
+
* `500 INTERNAL_ERROR` no se pudo leer alguna cadena.
|
|
199
|
+
*/
|
|
200
|
+
getProductsByRetailers(
|
|
201
|
+
retailerIds: string[],
|
|
202
|
+
tenantId: string,
|
|
203
|
+
): Promise<StandardResponse<ProductsByRetailersResponse>>;
|
|
204
|
+
|
|
147
205
|
/** PUT `/private/inventory/{imei}/reserve` — reserva atómica del IMEI para una sesión (AVAILABLE → RESERVED). */
|
|
148
206
|
reserveInventory(imei: string, body: ReserveInventoryBody, issuer: string): Promise<StandardResponse<ReserveInventoryResult>>;
|
|
149
207
|
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
WelcomeBonusResponse,
|
|
12
12
|
} from "@fiado/type-kit/bin/retailCustomer/index.js";
|
|
13
13
|
import { CustomersBatchRequest, CustomersBatchResponse } from "./dtos/CustomersBatch.js";
|
|
14
|
+
import { CustomersPageQuery, PrivateCustomerListItem } from "./dtos/CustomersPage.js";
|
|
15
|
+
import { PaginatedResult } from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
14
16
|
import { IRetailCustomerBusinessApi } from "./interfaces/IRetailCustomerBusinessApi.js";
|
|
15
17
|
|
|
16
18
|
/**
|
|
@@ -92,4 +94,16 @@ export default class RetailCustomerBusinessApi implements IRetailCustomerBusines
|
|
|
92
94
|
const url = `${this.baseUrl}/private/customers/batch?tenantId=${encodeURIComponent(tenantId)}`;
|
|
93
95
|
return await this.httpRequest.post(url, payload);
|
|
94
96
|
}
|
|
97
|
+
|
|
98
|
+
async listCustomersPage(
|
|
99
|
+
query: CustomersPageQuery,
|
|
100
|
+
tenantId: string,
|
|
101
|
+
): Promise<StandardResponse<PaginatedResult<PrivateCustomerListItem>>> {
|
|
102
|
+
const params = new URLSearchParams({ tenantId });
|
|
103
|
+
if (query.limit !== undefined) params.set("limit", String(query.limit));
|
|
104
|
+
// El cursor viaja tal cual lo entregó el destino: componerlo o re-codificarlo lo rompe.
|
|
105
|
+
if (query.nextToken) params.set("nextToken", query.nextToken);
|
|
106
|
+
const url = `${this.baseUrl}/private/customers?${params.toString()}`;
|
|
107
|
+
return await this.httpRequest.get(url);
|
|
108
|
+
}
|
|
95
109
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tipos del listado paginado de clientes del tenant. Viven en esta lib (y no en `@fiado/type-kit`)
|
|
3
|
+
* porque el type-kit todavía no modela esta vista reducida — el lambda destino la tiene como DTO
|
|
4
|
+
* local, mismo criterio que `CustomersBatch`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Query de `GET /private/customers`. `limit` default 25, máximo 100 del lado del destino. */
|
|
8
|
+
export interface CustomersPageQuery {
|
|
9
|
+
limit?: number;
|
|
10
|
+
/** Cursor opaco de la página anterior. Ausente = primera página. */
|
|
11
|
+
nextToken?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Lo mínimo para pintar el renglón de un cliente. `fullName: null` significa que el cliente existe
|
|
16
|
+
* y no tiene ninguna parte del nombre resuelta — es distinto de que el id no vuelva.
|
|
17
|
+
*/
|
|
18
|
+
export interface PrivateCustomerListItem {
|
|
19
|
+
retailCustomerId: string;
|
|
20
|
+
/** `names + paternalLastName + maternalLastName` ya compuesto por el destino. */
|
|
21
|
+
fullName: string | null;
|
|
22
|
+
phone: string | null;
|
|
23
|
+
/** Tienda donde se dio de alta. La ficha del backoffice la nombra y resuelve su KYC con ella. */
|
|
24
|
+
originStoreId: string | null;
|
|
25
|
+
/** Alta del cliente en ISO-8601 UTC. Es el orden del listado (descendente). */
|
|
26
|
+
createdAt: string;
|
|
27
|
+
}
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
WelcomeBonusResponse,
|
|
10
10
|
} from "@fiado/type-kit/bin/retailCustomer/index.js";
|
|
11
11
|
import { CustomersBatchRequest, CustomersBatchResponse } from "../dtos/CustomersBatch.js";
|
|
12
|
+
import { CustomersPageQuery, PrivateCustomerListItem } from "../dtos/CustomersPage.js";
|
|
13
|
+
import { PaginatedResult } from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Contrato del publisher HTTP del lambda `retail-customer-business` (SureKeep Fase 2, pista Retail)
|
|
@@ -173,4 +175,22 @@ export interface IRetailCustomerBusinessApi {
|
|
|
173
175
|
payload: CustomersBatchRequest,
|
|
174
176
|
tenantId: string,
|
|
175
177
|
): Promise<StandardResponse<CustomersBatchResponse>>;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* GET /private/customers?limit&nextToken&tenantId= — UNA página del padrón de clientes del
|
|
181
|
+
* tenant, del alta más reciente a la más vieja. Es el listado que alimenta las tablas de
|
|
182
|
+
* backoffice: una sola Query sobre el GSI de listado, sin fan-out por negocio.
|
|
183
|
+
*
|
|
184
|
+
* `nextToken` es OPACO: se pasea tal cual, no se compone ni se re-codifica. Ausente en la
|
|
185
|
+
* respuesta = última página. `count` son las filas de ESA página, nunca el total del padrón.
|
|
186
|
+
*
|
|
187
|
+
* `limit` default 25, máximo 100.
|
|
188
|
+
*
|
|
189
|
+
* @throws `400 INVALID_QUERY_PARAM` si `limit` no parsea o se pasa del tope.
|
|
190
|
+
* @throws `400 UNKNOWN_TENANT` si el `tenantId` viene ausente o desconocido.
|
|
191
|
+
*/
|
|
192
|
+
listCustomersPage(
|
|
193
|
+
query: CustomersPageQuery,
|
|
194
|
+
tenantId: string,
|
|
195
|
+
): Promise<StandardResponse<PaginatedResult<PrivateCustomerListItem>>>;
|
|
176
196
|
}
|