@yoryoboy/bi-mcp 1.19.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/mcp-use.json +6 -3
  3. package/dist/scripts/profile-mercadolibre-billing.js +634 -0
  4. package/dist/scripts/profile-mercadolibre-billing.js.map +7 -0
  5. package/dist/src/config/__tests__/tiendanube-profile-store.test.js +50 -0
  6. package/dist/src/config/__tests__/tiendanube-profile-store.test.js.map +7 -0
  7. package/dist/src/config/tiendanube-profile-store.js +31 -0
  8. package/dist/src/config/tiendanube-profile-store.js.map +7 -0
  9. package/dist/src/google-ads/report-utils.js +78 -0
  10. package/dist/src/google-ads/report-utils.js.map +7 -0
  11. package/dist/src/search-console/search-console-utils.js +275 -0
  12. package/dist/src/search-console/search-console-utils.js.map +7 -0
  13. package/dist/src/services/mercadolibre/__tests__/mercadolibre-sales-report-ventas.test.js +358 -0
  14. package/dist/src/services/mercadolibre/__tests__/mercadolibre-sales-report-ventas.test.js.map +7 -0
  15. package/dist/src/services/mercadolibre/__tests__/mercadolibre-sales-report.test.js +96 -0
  16. package/dist/src/services/mercadolibre/__tests__/mercadolibre-sales-report.test.js.map +7 -0
  17. package/dist/src/services/mercadolibre/mercadolibre-claim-return-lookup.js +15 -0
  18. package/dist/src/services/mercadolibre/mercadolibre-claim-return-lookup.js.map +7 -0
  19. package/dist/src/services/mercadolibre/mercadolibre-observability.js +12 -0
  20. package/dist/src/services/mercadolibre/mercadolibre-observability.js.map +7 -0
  21. package/dist/src/services/mercadolibre/mercadolibre-sale-reference.js +7 -0
  22. package/dist/src/services/mercadolibre/mercadolibre-sale-reference.js.map +7 -0
  23. package/dist/src/services/mercadolibre/mercadolibre-sales-report.js +553 -0
  24. package/dist/src/services/mercadolibre/mercadolibre-sales-report.js.map +7 -0
  25. package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js +165 -0
  26. package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js.map +7 -0
  27. package/dist/src/tools/config/__tests__/list-profiles.test.js +144 -1
  28. package/dist/src/tools/config/__tests__/list-profiles.test.js.map +2 -2
  29. package/dist/src/tools/config/list-profiles.js +50 -6
  30. package/dist/src/tools/config/list-profiles.js.map +2 -2
  31. package/dist/src/tools/mercadolibre/__tests__/get-sales-report-foundation.test.js +417 -0
  32. package/dist/src/tools/mercadolibre/__tests__/get-sales-report-foundation.test.js.map +7 -0
  33. package/dist/src/tools/mercadolibre/get-sales-report.js +474 -0
  34. package/dist/src/tools/mercadolibre/get-sales-report.js.map +7 -0
  35. package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js +233 -0
  36. package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js.map +7 -0
  37. package/dist/src/tools/vtex/catalog-admin-batch.js +298 -0
  38. package/dist/src/tools/vtex/catalog-admin-batch.js.map +7 -0
  39. package/dist/tests/meli/meli-sales-report-buyers-mcp-e2e.test.js +137 -0
  40. package/dist/tests/meli/meli-sales-report-buyers-mcp-e2e.test.js.map +7 -0
  41. package/dist/tests/meli/meli-sales-report-mcp-e2e-server.js +24 -0
  42. package/dist/tests/meli/meli-sales-report-mcp-e2e-server.js.map +7 -0
  43. package/dist/tests/meli/meli-sales-report-mcp-e2e.test.js +207 -0
  44. package/dist/tests/meli/meli-sales-report-mcp-e2e.test.js.map +7 -0
  45. package/dist/tests/meli/meli-sales-report-public-mcp-e2e-server.js +10 -0
  46. package/dist/tests/meli/meli-sales-report-public-mcp-e2e-server.js.map +7 -0
  47. package/dist/tests/meli/meli-sales-report-public-registration.test.js +98 -0
  48. package/dist/tests/meli/meli-sales-report-public-registration.test.js.map +7 -0
  49. package/dist/tests/smoke/process-harness.js +89 -0
  50. package/dist/tests/smoke/process-harness.js.map +7 -0
  51. package/dist/tests/smoke/process-harness.test.js +39 -0
  52. package/dist/tests/smoke/process-harness.test.js.map +7 -0
  53. package/package.json +1 -1
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/services/mercadolibre/mercadolibre-observability.ts"],
4
+ "sourcesContent": ["export type MercadoLibreBuyerReconciliationTelemetry = {\n event: \"buyer_reconciliation_completed\";\n outcome: \"complete\" | \"partial\" | \"unavailable\" | \"deadline_exceeded\";\n latency_ms: number;\n references_with_partial_coverage: number;\n references_unavailable: number;\n deadline_exceeded: number;\n retry_exhausted: number;\n privacy: {\n pii_included: false;\n identifiers_included: false;\n raw_payloads_included: false;\n };\n};\n\nexport type MercadoLibreBuyerReconciliationTelemetrySink = {\n emit: (event: MercadoLibreBuyerReconciliationTelemetry) => void;\n};\n\nconst defaultTelemetrySink: MercadoLibreBuyerReconciliationTelemetrySink = {\n emit(event) {\n console.info(\"[meli_reconcile_buyer_data]\", JSON.stringify(event));\n },\n};\n\nexport function getDefaultMercadoLibreBuyerReconciliationTelemetrySink(): MercadoLibreBuyerReconciliationTelemetrySink {\n return defaultTelemetrySink;\n}\n"],
5
+ "mappings": "AAmBA,MAAM,uBAAqE;AAAA,EACzE,KAAK,OAAO;AACV,YAAQ,KAAK,+BAA+B,KAAK,UAAU,KAAK,CAAC;AAAA,EACnE;AACF;AAEO,SAAS,yDAAuG;AACrH,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,7 @@
1
+ function normalizeMercadoLibreSaleReference(input) {
2
+ return typeof input === "string" ? { reference: input } : input;
3
+ }
4
+ export {
5
+ normalizeMercadoLibreSaleReference
6
+ };
7
+ //# sourceMappingURL=mercadolibre-sale-reference.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/services/mercadolibre/mercadolibre-sale-reference.ts"],
4
+ "sourcesContent": ["export type MercadoLibreSaleReference =\n | string\n | { reference: string; kind: \"order\" | \"pack\" };\n\nexport function normalizeMercadoLibreSaleReference(input: MercadoLibreSaleReference): {\n reference: string;\n kind?: \"order\" | \"pack\";\n} {\n return typeof input === \"string\" ? { reference: input } : input;\n}\n"],
5
+ "mappings": "AAIO,SAAS,mCAAmC,OAGjD;AACA,SAAO,OAAO,UAAU,WAAW,EAAE,WAAW,MAAM,IAAI;AAC5D;",
6
+ "names": []
7
+ }
@@ -0,0 +1,553 @@
1
+ import {
2
+ normalizeMercadoLibrePaging,
3
+ runMercadoLibreBatch
4
+ } from "./mercadolibre-api.js";
5
+ import {
6
+ searchMercadoLibreOrders,
7
+ searchMercadoLibreOrdersBatch,
8
+ getMercadoLibreOrderDetailsBatch,
9
+ getMercadoLibreOrderShipments
10
+ } from "./mercadolibre-orders.js";
11
+ import { getMercadoLibrePackBatch } from "./mercadolibre-packs.js";
12
+ import {
13
+ getMercadoLibreSaleBillingInfo
14
+ } from "./mercadolibre-order-billing-info.js";
15
+ const VENTAS_CODES = /* @__PURE__ */ new Set(["CVFV", "CVFN", "CVFF", "CDSD"]);
16
+ const record = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2) ? value2 : {};
17
+ const records = (value2) => Array.isArray(value2) ? value2.filter((item) => item && typeof item === "object" && !Array.isArray(item)) : value2 !== null && typeof value2 === "object" ? [value2] : [];
18
+ const numberValue = (value2) => typeof value2 === "number" && Number.isFinite(value2) ? value2 : null;
19
+ const text = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
20
+ function orderSellerId(order) {
21
+ const seller = record(order.seller);
22
+ const id = seller.id;
23
+ return typeof id === "string" && id.trim() ? id.trim() : typeof id === "number" && Number.isFinite(id) ? String(id) : null;
24
+ }
25
+ function packMemberIds(pack) {
26
+ const members = Array.isArray(pack.orders) ? pack.orders : [];
27
+ return [...new Set(members.map((member) => {
28
+ const value2 = record(member).id ?? member;
29
+ return typeof value2 === "string" && value2.trim() ? value2.trim() : typeof value2 === "number" && Number.isFinite(value2) ? String(value2) : null;
30
+ }).filter((id) => id !== null))];
31
+ }
32
+ async function selectMercadoLibreOrdersById(profileId, sellerId, orderIds) {
33
+ const direct = await getMercadoLibreOrderDetailsBatch(profileId, orderIds, { maxConcurrency: 10 });
34
+ const ownedDirect = direct.successful.filter(({ document }) => orderSellerId(document) === sellerId);
35
+ const ownedDirectIds = new Set(ownedDirect.map(({ id }) => id));
36
+ const directFailures = new Map(direct.failed.map((failure) => [failure.id, failure]));
37
+ const unavailableRequestedIds = orderIds.filter((id) => !ownedDirectIds.has(id));
38
+ const packIds = [...new Set(ownedDirect.map(({ document }) => text(document.pack_id)).filter((id) => id !== null))];
39
+ const packs = packIds.length > 0 ? await getMercadoLibrePackBatch(profileId, packIds, { maxConcurrency: 10 }) : { successful: [], failed: [] };
40
+ const selected = new Map(ownedDirect.map(({ id, document }) => [id, document]));
41
+ const missingMemberIds = [...new Set(packs.successful.flatMap(({ document }) => packMemberIds(document)))].filter((id) => !selected.has(id));
42
+ const expanded = missingMemberIds.length > 0 ? await getMercadoLibreOrderDetailsBatch(profileId, missingMemberIds, { maxConcurrency: 10 }) : { successful: [], failed: [] };
43
+ for (const { id, document } of expanded.successful) {
44
+ if (orderSellerId(document) === sellerId) selected.set(id, document);
45
+ }
46
+ return {
47
+ orders: [...selected.values()],
48
+ requestedIds: orderIds,
49
+ unavailableRequestedIds,
50
+ incidents: [
51
+ ...unavailableRequestedIds.map((id) => {
52
+ const failure = directFailures.get(id);
53
+ return {
54
+ fuente: "orders",
55
+ alcance: "order_selection",
56
+ identificadores: [id],
57
+ mensaje: failure ? "The requested order could not be retrieved." : "The requested order is not available for this seller profile.",
58
+ reintentable: failure?.retryable ?? false,
59
+ estado_http: failure?.statusCode ?? null
60
+ };
61
+ }),
62
+ ...packs.failed.map((failure) => ({
63
+ fuente: "packs",
64
+ alcance: "pack_expansion",
65
+ identificadores: [failure.id],
66
+ mensaje: "The complete pack could not be retrieved.",
67
+ reintentable: failure.retryable,
68
+ estado_http: failure.statusCode ?? null
69
+ })),
70
+ ...expanded.failed.map(() => ({
71
+ fuente: "orders",
72
+ alcance: "pack_expansion",
73
+ identificadores: [],
74
+ mensaje: "A pack member could not be retrieved and was excluded.",
75
+ reintentable: true
76
+ })),
77
+ ...expanded.successful.filter(({ document }) => orderSellerId(document) !== sellerId).map(() => ({
78
+ fuente: "orders",
79
+ alcance: "pack_expansion",
80
+ identificadores: [],
81
+ mensaje: "A pack member is not available for this seller profile and was excluded.",
82
+ reintentable: false
83
+ }))
84
+ ]
85
+ };
86
+ }
87
+ const SALES_REPORT_BUYER_EVIDENCE = /* @__PURE__ */ Symbol("sales-report-buyer-evidence");
88
+ const emptyReturns = () => ({ unidades: null, forma_de_entrega: null, fecha_en_camino: null, fecha_entregado: null, transportista: null, numero_de_seguimiento: null, url_de_seguimiento: null, revisado_por_mercado_libre: null, fecha_de_revision: null, dinero_a_favor: null, resultado: null, destino: null, motivo_del_resultado: null });
89
+ const emptyClaims = () => ({ unidades: null, reclamo_abierto: null, reclamo_cerrado: null, con_mediacion: null });
90
+ function memberContext(result, reference, orderId, key) {
91
+ const entry = result?.results?.find((item) => item.sale_reference === reference);
92
+ if (!entry) return void 0;
93
+ const member = entry.members?.find((item) => item.order_id === orderId || record(item[key]).order_id === orderId);
94
+ const context = member?.[key] ?? (entry.members ? void 0 : entry[key]);
95
+ const issues = member?.issues ?? entry.issues ?? [];
96
+ return context || issues.length ? { ...record(context), order_id: record(context).order_id ?? member?.order_id ?? entry.order_id ?? orderId, __memberIssues: issues } : void 0;
97
+ }
98
+ function buildSalesReportPostPurchaseIncident(source, error) {
99
+ const rawStatus = record(error).status;
100
+ const status = typeof rawStatus === "number" ? rawStatus : null;
101
+ const retryable = status === null ? true : status === 408 || status === 429 || status >= 500 && status <= 599;
102
+ return { fuente: source, alcance: "source", identificadores: [], mensaje: `Mercado Libre ${source} evidence was unavailable${status === null ? " (transport)" : ` (HTTP ${status})`}.`, reintentable: retryable, estado_http: status };
103
+ }
104
+ function enrichSalesReportDevolucionesReclamos(operations, evidence) {
105
+ const incidents = [];
106
+ const returnFields = ["unidades", "revisado_por_mercado_libre", "fecha_de_revision", "resultado", "destino", "motivo_del_resultado"];
107
+ const claimFields = ["unidades", "reclamo_abierto", "reclamo_cerrado", "con_mediacion"];
108
+ const coverage = Object.fromEntries([...returnFields.map((field) => [`devoluciones.${field}`, { solicitados: 0, obtenidos: 0 }]), ...claimFields.map((field) => [`reclamos.${field}`, { solicitados: 0, obtenidos: 0 }])]);
109
+ let returnContexts = 0;
110
+ let claimContexts = 0;
111
+ const mapped = operations.map((operation2) => ({ ...operation2, ordenes: operation2.ordenes.map((order, memberIndex) => {
112
+ const returns = memberContext(evidence.returns, operation2.pack_id ?? order.order_id, order.order_id, "return_context");
113
+ const claims = memberContext(evidence.claims, operation2.pack_id ?? order.order_id, order.order_id, "claim_context");
114
+ if (returns) returnContexts++;
115
+ if (claims) claimContexts++;
116
+ const returnRows = returns?.return_status === "with_return" && returns.return ? [returns] : [];
117
+ const review = record(returnRows[0]?.review);
118
+ const destination = record(review.product_destination);
119
+ const reason = record(review.reason);
120
+ const devoluciones = { ...emptyReturns(), ...returnRows.length ? { unidades: numberValue(record(returnRows[0].return).quantities), revisado_por_mercado_libre: typeof review.status === "string" ? review.status === "success" : null, fecha_de_revision: text(review.date_created), resultado: text(review.status), destino: text(destination.code ?? destination.meaning), motivo_del_resultado: text(reason.code ?? reason.description) } : {} };
121
+ const claimRows = Array.isArray(claims?.claims) ? claims.claims.map(record) : [];
122
+ const quantities = claimRows.map((claim) => numberValue(claim.claimed_quantity)).filter((value2) => value2 !== null);
123
+ const reclamos = { ...emptyClaims(), ...claimRows.length ? { unidades: quantities.length ? quantities.reduce((sum, value2) => sum + value2, 0) : null, reclamo_abierto: claimRows.some((claim) => claim.status === "opened"), reclamo_cerrado: claimRows.some((claim) => claim.status === "closed"), con_mediacion: claimRows.some((claim) => claim.type === "mediations") } : {} };
124
+ for (const field of returnFields) {
125
+ const key = `devoluciones.${field}`;
126
+ coverage[key].solicitados++;
127
+ if (devoluciones[field] !== null) coverage[key].obtenidos++;
128
+ }
129
+ for (const field of claimFields) {
130
+ const key = `reclamos.${field}`;
131
+ coverage[key].solicitados++;
132
+ if (reclamos[field] !== null) coverage[key].obtenidos++;
133
+ }
134
+ if (returns?.__memberIssues?.length || returns?.issues?.length) incidents.push({ fuente: "returns", alcance: "order", identificadores: [order.order_id], mensaje: "Return evidence is partial; member-level reconciliation issues were preserved.", reintentable: true });
135
+ if (claims?.__memberIssues?.length || claims?.issues?.length) incidents.push({ fuente: "claims", alcance: "order", identificadores: [order.order_id], mensaje: "Claim evidence is partial; member-level reconciliation issues were preserved.", reintentable: true });
136
+ return { ...order, devoluciones, reclamos };
137
+ }) }));
138
+ if (evidence.returnFailure) incidents.push(buildSalesReportPostPurchaseIncident("returns", evidence.returnFailure));
139
+ if (evidence.claimFailure) incidents.push(buildSalesReportPostPurchaseIncident("claims", evidence.claimFailure));
140
+ return { operations: mapped, incidents, coverage, returnContexts, claimContexts };
141
+ }
142
+ const SALES_REPORT_ENVIOS_FIELDS = [
143
+ "forma_de_entrega",
144
+ "fecha_en_camino",
145
+ "fecha_entregado",
146
+ "transportista",
147
+ "numero_de_seguimiento",
148
+ "url_de_seguimiento"
149
+ ];
150
+ const SALES_REPORT_FACTURACION_FIELDS = [
151
+ "factura_adjunta",
152
+ "datos_personales_o_de_empresa",
153
+ "tipo_y_numero_de_documento",
154
+ "direccion",
155
+ "condicion_fiscal_iva",
156
+ "numero_iibb"
157
+ ];
158
+ function fiscalResultEntries(orderId, result) {
159
+ if (result.resolutionKind === "order") return [{ orderId, result: result.result }];
160
+ return result.members.flatMap((member) => {
161
+ if (!("orderId" in member)) return [];
162
+ return [member.accessible ? { orderId: member.orderId, result: member.result } : { orderId: member.orderId, unavailableReason: member.failureReason, statusCode: member.statusCode, retryable: member.retryable }];
163
+ });
164
+ }
165
+ function buildSalesReportFiscalIncident(orderId, statusCode, retryable, reason = "Fiscal billing-info request failed.") {
166
+ const statusText = statusCode === void 0 ? "transport" : `HTTP ${statusCode}`;
167
+ const statusRetryable = statusCode !== void 0 && [401, 403, 404].includes(statusCode) ? false : retryable;
168
+ return {
169
+ fuente: "billing_info",
170
+ alcance: "order",
171
+ identificadores: [orderId],
172
+ mensaje: `${reason} (${statusText}).`,
173
+ reintentable: statusRetryable,
174
+ estado_http: statusCode ?? null
175
+ };
176
+ }
177
+ async function fetchMercadoLibreFiscalEvidence(profileId, orderIds) {
178
+ const batch = await runMercadoLibreBatch(
179
+ [...new Set(orderIds)],
180
+ (orderId) => getMercadoLibreSaleBillingInfo(profileId, orderId)
181
+ );
182
+ return {
183
+ successful: batch.successful.flatMap(({ id, document }) => fiscalResultEntries(id, document)),
184
+ failed: batch.failed.map((failure) => ({ orderId: failure.id, statusCode: failure.statusCode, retryable: failure.retryable, reason: "Fiscal billing-info request failed." }))
185
+ };
186
+ }
187
+ function fiscalField(value2) {
188
+ if (typeof value2 === "string" && value2.trim()) return value2.trim();
189
+ if (typeof value2 === "number" && Number.isFinite(value2)) return String(value2);
190
+ return null;
191
+ }
192
+ function fiscalAddress(value2) {
193
+ const address = record(value2);
194
+ const street = [fiscalField(address.street_name), fiscalField(address.street_number)].filter((part) => part !== null).join(" ");
195
+ const parts = [street || null, fiscalField(address.city_name), fiscalField(address.state), fiscalField(address.zip_code), fiscalField(address.country_id)].filter((part) => part !== null);
196
+ return parts.length ? parts.join(", ") : null;
197
+ }
198
+ function enrichSalesReportFacturacion(operations, evidence, failedOrderIds = []) {
199
+ const byOrder = new Map(evidence.map((entry) => [entry.orderId, entry]));
200
+ const incidents = failedOrderIds.map(({ orderId, statusCode, retryable, reason }) => buildSalesReportFiscalIncident(orderId, statusCode, retryable, reason));
201
+ const fieldCoverage = Object.fromEntries(SALES_REPORT_FACTURACION_FIELDS.map((field) => [field, { solicitados: 0, obtenidos: 0 }]));
202
+ const mapped = operations.map((operation2) => ({
203
+ ...operation2,
204
+ ordenes: operation2.ordenes.map((order) => {
205
+ const entry = byOrder.get(order.order_id);
206
+ const billing = entry?.result?.available ? record(record(entry.result.billingInfo).buyer).billing_info : null;
207
+ const info = record(billing);
208
+ const identification = record(info.identification);
209
+ const taxpayer = record(record(info.taxes).taxpayer_type);
210
+ const fields = {
211
+ factura_adjunta: null,
212
+ datos_personales_o_de_empresa: [fiscalField(info.name), fiscalField(info.last_name)].filter((part) => part !== null).join(" ") || null,
213
+ tipo_y_numero_de_documento: [fiscalField(identification.type), fiscalField(identification.number)].filter((part) => part !== null).join(" ") || null,
214
+ direccion: fiscalAddress(info.address),
215
+ condicion_fiscal_iva: fiscalField(taxpayer.description),
216
+ numero_iibb: null
217
+ };
218
+ for (const field of SALES_REPORT_FACTURACION_FIELDS) {
219
+ fieldCoverage[field].solicitados++;
220
+ if (fields[field] !== null) fieldCoverage[field].obtenidos++;
221
+ if (fields[field] === null && field !== "factura_adjunta" && field !== "numero_iibb") {
222
+ incidents.push({ ...buildSalesReportFiscalIncident(order.order_id, entry?.statusCode, entry?.retryable ?? false, entry?.unavailableReason ?? "Fiscal field evidence was unavailable for this order."), alcance: `order.${field}` });
223
+ }
224
+ }
225
+ return { ...order, facturacion_al_comprador: fields };
226
+ })
227
+ }));
228
+ return { operations: mapped, incidents, fieldCoverage };
229
+ }
230
+ function enrichSalesReportCompradores(operations, expectedSellerId) {
231
+ const incidents = [];
232
+ const empty = { comprador: null, negocio: null, dni: null, domicilio: null, ciudad: null, estado: null, codigo_postal: null, pais: null };
233
+ const mapped = operations.map((operation2) => ({
234
+ ...operation2,
235
+ ordenes: operation2.ordenes.map((order) => {
236
+ const evidence = order[SALES_REPORT_BUYER_EVIDENCE];
237
+ if (!evidence || evidence.sellerId !== expectedSellerId) {
238
+ incidents.push({ fuente: "orders", alcance: "order", identificadores: [order.order_id], mensaje: "Buyer disclosure was blocked because seller ownership could not be verified.", reintentable: false });
239
+ return { ...order, compradores: empty };
240
+ }
241
+ if (!evidence.nickname) {
242
+ incidents.push({ fuente: "orders", alcance: "order", identificadores: [order.order_id], mensaje: "Buyer nickname was missing or malformed; buyer disclosure was blocked.", reintentable: false });
243
+ return { ...order, compradores: empty };
244
+ }
245
+ return { ...order, compradores: { ...empty, comprador: evidence.nickname } };
246
+ })
247
+ }));
248
+ return { operations: mapped, incidents };
249
+ }
250
+ function enrichSalesReportEnvios(operations, evidence) {
251
+ const byOrder = new Map(evidence.map((entry) => [entry.order_id, entry.shipment]));
252
+ const incidents = [];
253
+ const fieldCoverage = Object.fromEntries(SALES_REPORT_ENVIOS_FIELDS.map((field) => [field, { solicitados: operations.reduce((total, operation2) => total + operation2.ordenes.length, 0), obtenidos: 0 }]));
254
+ const fields = (orderId) => {
255
+ const shipment = record(byOrder.get(orderId));
256
+ const shipmentOrderId = text(shipment.order_id);
257
+ if (!Object.keys(shipment).length || shipmentOrderId !== orderId) {
258
+ incidents.push({ fuente: "shipments", alcance: "order", identificadores: [orderId], mensaje: shipmentOrderId ? "Shipment evidence belongs to a different order." : "No shipment evidence was returned for this order.", reintentable: false });
259
+ return { forma_de_entrega: null, fecha_en_camino: null, fecha_entregado: null, transportista: null, numero_de_seguimiento: null, url_de_seguimiento: null };
260
+ }
261
+ const statusHistory = record(shipment.status_history);
262
+ const shippingOption = record(shipment.shipping_option);
263
+ const carrier = record(shipment.carrier_info);
264
+ const mapped = {
265
+ forma_de_entrega: text(shippingOption.name) ?? text(shipment.mode),
266
+ fecha_en_camino: text(statusHistory.date_shipped),
267
+ fecha_entregado: text(statusHistory.date_delivered),
268
+ transportista: text(carrier.name),
269
+ numero_de_seguimiento: text(shipment.tracking_number),
270
+ url_de_seguimiento: text(carrier.url)
271
+ };
272
+ for (const field of SALES_REPORT_ENVIOS_FIELDS) {
273
+ if (mapped[field] !== null) fieldCoverage[field].obtenidos++;
274
+ else incidents.push({ fuente: "shipments", alcance: `order.${field}`, identificadores: [orderId], mensaje: "Shipment field evidence was unavailable for this order.", reintentable: false });
275
+ }
276
+ return mapped;
277
+ };
278
+ return {
279
+ operations: operations.map((operation2) => ({
280
+ ...operation2,
281
+ ordenes: operation2.ordenes.map((order) => ({ ...order, envios: fields(order.order_id) }))
282
+ })),
283
+ incidents,
284
+ fieldCoverage
285
+ };
286
+ }
287
+ async function fetchMercadoLibreShipmentEvidence(profileId, orderIds) {
288
+ return runMercadoLibreBatch(
289
+ [...new Set(orderIds)],
290
+ (orderId) => getMercadoLibreOrderShipments(profileId, orderId)
291
+ );
292
+ }
293
+ function formatVariation(item) {
294
+ const attributes = records(item.variation_attributes);
295
+ const values = attributes.map((attribute) => {
296
+ const name = text(attribute.name);
297
+ const value2 = text(attribute.value_name);
298
+ return name && value2 ? `${name}: ${value2}` : null;
299
+ }).filter((value2) => value2 !== null);
300
+ return values.length ? values.join(", ") : null;
301
+ }
302
+ function enrichSalesReportVentas(operations, billing) {
303
+ const byOrder = /* @__PURE__ */ new Map();
304
+ const unknownConcepts = /* @__PURE__ */ new Set();
305
+ const currencies = /* @__PURE__ */ new Set();
306
+ for (const row of billing.results) {
307
+ const fallback = String(row.order_id ?? "");
308
+ for (const detail of records(row.details)) {
309
+ const charge = record(detail.charge_info);
310
+ const code = String(charge.detail_sub_type ?? detail.detail_sub_type ?? "");
311
+ const currency = String(record(detail.currency_info).currency_id ?? row.currency_id ?? "");
312
+ if (currency) currencies.add(currency);
313
+ if (!VENTAS_CODES.has(code)) {
314
+ if (code) unknownConcepts.add(code);
315
+ continue;
316
+ }
317
+ const amount = numberValue(charge.detail_amount ?? detail.detail_amount);
318
+ if (currency !== "ARS" || amount === null || String(charge.detail_type ?? detail.detail_type ?? "") !== "CHARGE") continue;
319
+ const sales = records(detail.sales_info ?? detail.details_sales);
320
+ const orderIds = sales.map((sale) => String(sale.order_id ?? "")).filter(Boolean);
321
+ for (const orderId of orderIds.length ? orderIds : [fallback]) {
322
+ const list = byOrder.get(orderId) ?? [];
323
+ list.push({ code, amount, date: String(charge.creation_date_time ?? detail.creation_date_time ?? "") });
324
+ byOrder.set(orderId, list);
325
+ }
326
+ }
327
+ }
328
+ const result = operations.map((operation2) => {
329
+ const entries = operation2.ordenes.flatMap((order) => byOrder.get(order.order_id) ?? []);
330
+ const sum = (code) => entries.filter((entry) => entry.code === code).reduce((total, entry) => total - Number(entry.amount), 0);
331
+ const value2 = (code) => entries.some((entry) => entry.code === code) ? sum(code) : null;
332
+ const periods = [...new Set(entries.map((entry) => String(entry.date).slice(0, 7)).filter((period) => /^\d{4}-\d{2}$/.test(period)))].sort();
333
+ const known = entries.reduce((total, entry) => total - Number(entry.amount), 0);
334
+ return { ...operation2, ventas: { ...operation2.ventas, cargo_por_venta: value2("CVFV"), costo_fijo: value2("CVFF"), costo_por_ofrecer_cuotas: value2("CVFN"), anulaciones_y_reembolsos_ars: value2("CDSD"), total_ars: entries.length ? known : null, cargo_por_diferencias_en_medidas_y_peso_del_paquete: null, periodos_de_facturacion: periods } };
335
+ });
336
+ return {
337
+ operations: result,
338
+ unknownConcepts: [...unknownConcepts].sort(),
339
+ currencies: [...currencies].sort(),
340
+ incidents: billing.order_ids_not_found.map((orderId) => ({ fuente: "billing", alcance: "order", identificadores: [orderId], mensaje: "Billing-by-order returned no result for this order.", reintentable: false })),
341
+ failures: billing.failures
342
+ };
343
+ }
344
+ function buildOrderUniversePagePlan(paging, target, maxPages = 20) {
345
+ const effectiveLimit = paging.limit > 0 ? paging.limit : target;
346
+ const end = Math.min(
347
+ paging.total,
348
+ paging.offset + Math.max(0, target) + effectiveLimit
349
+ );
350
+ const offsets = [];
351
+ for (let offset = paging.offset + effectiveLimit; offset < end && offsets.length < maxPages; offset += effectiveLimit) {
352
+ offsets.push(offset);
353
+ }
354
+ return offsets;
355
+ }
356
+ function value(record2, key) {
357
+ return Object.prototype.hasOwnProperty.call(record2, key) ? record2[key] ?? null : null;
358
+ }
359
+ function normalizeMercadoLibreOrder(input) {
360
+ const id = String(input.id ?? "");
361
+ const date = String(input.date_created ?? input.date_closed ?? "");
362
+ const items = Array.isArray(input.order_items) ? input.order_items : [];
363
+ const firstItem = items[0] ?? {};
364
+ const item = firstItem.item ?? {};
365
+ const total = typeof input.total_amount === "number" ? input.total_amount : null;
366
+ const normalized = {
367
+ order_id: id,
368
+ ventas: {
369
+ fecha_de_venta: date,
370
+ estado: value(input, "status"),
371
+ descripcion_del_estado: value(input, "status_detail"),
372
+ pertenece_a_un_kit: null,
373
+ unidades: typeof firstItem.quantity === "number" ? firstItem.quantity : null,
374
+ ingresos_por_productos_ars: null,
375
+ orden_de_compra: null
376
+ },
377
+ publicidad: { venta_por_publicidad: null },
378
+ publicaciones: {
379
+ sku: value(item, "seller_sku"),
380
+ numero_de_publicacion: value(item, "id"),
381
+ canal_de_venta: null,
382
+ titulo_de_la_publicacion: value(item, "title"),
383
+ variante: formatVariation(item),
384
+ precio_unitario_de_venta_de_la_publicacion_ars: typeof firstItem.unit_price === "number" ? firstItem.unit_price : null,
385
+ tiene_cuotas_agregadas: null
386
+ },
387
+ facturacion_al_comprador: {
388
+ factura_adjunta: null,
389
+ datos_personales_o_de_empresa: null,
390
+ tipo_y_numero_de_documento: null,
391
+ direccion: null,
392
+ condicion_fiscal_iva: null,
393
+ numero_iibb: null
394
+ },
395
+ compradores: {
396
+ comprador: null,
397
+ negocio: null,
398
+ dni: null,
399
+ domicilio: null,
400
+ ciudad: null,
401
+ estado: null,
402
+ codigo_postal: null,
403
+ pais: null
404
+ },
405
+ envios: {
406
+ forma_de_entrega: null,
407
+ fecha_en_camino: null,
408
+ fecha_entregado: null,
409
+ transportista: null,
410
+ numero_de_seguimiento: null,
411
+ url_de_seguimiento: null
412
+ },
413
+ devoluciones: {
414
+ unidades: null,
415
+ forma_de_entrega: null,
416
+ fecha_en_camino: null,
417
+ fecha_entregado: null,
418
+ transportista: null,
419
+ numero_de_seguimiento: null,
420
+ url_de_seguimiento: null,
421
+ revisado_por_mercado_libre: null,
422
+ fecha_de_revision: null,
423
+ dinero_a_favor: null,
424
+ resultado: null,
425
+ destino: null,
426
+ motivo_del_resultado: null
427
+ },
428
+ reclamos: { unidades: null, reclamo_abierto: null, reclamo_cerrado: null, con_mediacion: null }
429
+ };
430
+ const seller = record(input.seller);
431
+ const buyer = record(input.buyer);
432
+ normalized[SALES_REPORT_BUYER_EVIDENCE] = { sellerId: text(seller.id), nickname: text(buyer.nickname) };
433
+ return normalized;
434
+ }
435
+ function enrichSalesReportPublicaciones(operations, billing) {
436
+ const byOrder = /* @__PURE__ */ new Map();
437
+ for (const row of billing.results) {
438
+ const fallback = String(row.order_id ?? "");
439
+ for (const detail of records(row.details)) {
440
+ const sales = records(detail.sales_info ?? detail.details_sales);
441
+ const items = records(detail.items_info ?? detail.details_items);
442
+ const orderIds = sales.map((sale) => text(sale.order_id)).filter((id) => id !== null);
443
+ for (const orderId of (orderIds.length ? orderIds : [fallback]).filter(Boolean)) {
444
+ const channel = text(sales.find((sale) => text(sale.order_id) === orderId)?.sales_channel);
445
+ const list = byOrder.get(orderId) ?? [];
446
+ list.push(...items.map((item) => ({ channel, item })));
447
+ byOrder.set(orderId, list);
448
+ }
449
+ }
450
+ }
451
+ const result = operations.map((operation2) => ({
452
+ ...operation2,
453
+ ordenes: operation2.ordenes.map((order) => {
454
+ const item = record(order.publicaciones);
455
+ const orderEvidence = byOrder.get(order.order_id) ?? [];
456
+ const evidence = orderEvidence.find(({ item: candidate }) => text(candidate.item_id) === item.numero_de_publicacion) ?? orderEvidence[0];
457
+ const billingItem = evidence?.item;
458
+ const hasFees = billingItem && Object.prototype.hasOwnProperty.call(billingItem, "fees_added_in_publication");
459
+ const feeValue = hasFees && (billingItem.fees_added_in_publication === "Si" || billingItem.fees_added_in_publication === "No") ? billingItem.fees_added_in_publication === "Si" : null;
460
+ const kitId = billingItem && Object.prototype.hasOwnProperty.call(billingItem, "item_kit_id") ? text(billingItem.item_kit_id) : null;
461
+ return {
462
+ ...order,
463
+ ventas: { ...order.ventas, pertenece_a_un_kit: billingItem && Object.prototype.hasOwnProperty.call(billingItem, "item_kit_id") ? kitId !== null : null },
464
+ publicaciones: {
465
+ ...order.publicaciones,
466
+ canal_de_venta: evidence?.channel ?? null,
467
+ titulo_de_la_publicacion: order.publicaciones.titulo_de_la_publicacion ?? text(billingItem?.item_title),
468
+ precio_unitario_de_venta_de_la_publicacion_ars: order.publicaciones.precio_unitario_de_venta_de_la_publicacion_ars ?? numberValue(billingItem?.item_price),
469
+ tiene_cuotas_agregadas: feeValue
470
+ }
471
+ };
472
+ })
473
+ }));
474
+ return { operations: result, incidents: billing.order_ids_not_found.map((orderId) => ({ fuente: "billing", alcance: "order", identificadores: [orderId], mensaje: "Billing-by-order returned no result for this order.", reintentable: false })) };
475
+ }
476
+ function operation(packId, orders) {
477
+ return {
478
+ pack_id: packId,
479
+ ventas: {
480
+ paquete_de_varios_productos: packId !== null,
481
+ cargo_por_venta: null,
482
+ costo_fijo: null,
483
+ costo_por_ofrecer_cuotas: null,
484
+ ingresos_por_envio_ars: null,
485
+ costos_de_envio_ars: null,
486
+ costo_de_envio_por_cambio_de_producto: null,
487
+ costo_de_envio_basado_en_medidas_y_peso_declarados: null,
488
+ cargo_por_diferencias_en_medidas_y_peso_del_paquete: null,
489
+ impuestos: null,
490
+ descuentos_y_bonificaciones: null,
491
+ anulaciones_y_reembolsos_ars: null,
492
+ total_ars: null,
493
+ periodos_de_facturacion: []
494
+ },
495
+ ordenes: orders
496
+ };
497
+ }
498
+ function groupMercadoLibreOrders(records2) {
499
+ const groups = /* @__PURE__ */ new Map();
500
+ for (const record2 of records2) {
501
+ const normalized = normalizeMercadoLibreOrder(record2);
502
+ const packId = record2.pack_id == null ? null : String(record2.pack_id);
503
+ const key = packId ?? `order:${normalized.order_id}`;
504
+ const group = groups.get(key) ?? [];
505
+ group.push(normalized);
506
+ groups.set(key, group);
507
+ }
508
+ return [...groups.entries()].map(
509
+ ([key, orders]) => operation(
510
+ key.startsWith("order:") ? null : key,
511
+ orders
512
+ )
513
+ ).sort((left, right) => {
514
+ const leftDate = left.ordenes.reduce((latest, order) => order.ventas.fecha_de_venta > latest ? order.ventas.fecha_de_venta : latest, left.ordenes[0].ventas.fecha_de_venta);
515
+ const rightDate = right.ordenes.reduce((latest, order) => order.ventas.fecha_de_venta > latest ? order.ventas.fecha_de_venta : latest, right.ordenes[0].ventas.fecha_de_venta);
516
+ return Date.parse(rightDate) - Date.parse(leftDate) || (right.pack_id ?? right.ordenes[0].order_id).localeCompare(left.pack_id ?? left.ordenes[0].order_id);
517
+ });
518
+ }
519
+ async function fetchMercadoLibreOrderUniverse(profileId, from, to, target) {
520
+ const seed = await searchMercadoLibreOrders(profileId, { seller: "", from, to, sort: "date_desc", limit: 50, offset: 0 });
521
+ const paging = normalizeMercadoLibrePaging(seed.paging);
522
+ const seedOrders = Array.isArray(seed.results) ? seed.results : Array.isArray(seed.orders) ? seed.orders : [];
523
+ const offsets = buildOrderUniversePagePlan(paging, target);
524
+ const remaining = offsets.length ? await searchMercadoLibreOrdersBatch(profileId, { seller: "", from, to, sort: "date_desc", limit: paging.limit }, offsets) : { successful: [], failed: [] };
525
+ return {
526
+ orders: [...seedOrders, ...remaining.successful.flatMap((page) => {
527
+ const data = page.document;
528
+ return Array.isArray(data.results) ? data.results : Array.isArray(data.orders) ? data.orders : [];
529
+ })],
530
+ paging,
531
+ failedPages: remaining.failed.map((failure) => failure.id)
532
+ };
533
+ }
534
+ export {
535
+ SALES_REPORT_ENVIOS_FIELDS,
536
+ SALES_REPORT_FACTURACION_FIELDS,
537
+ buildOrderUniversePagePlan,
538
+ buildSalesReportFiscalIncident,
539
+ buildSalesReportPostPurchaseIncident,
540
+ enrichSalesReportCompradores,
541
+ enrichSalesReportDevolucionesReclamos,
542
+ enrichSalesReportEnvios,
543
+ enrichSalesReportFacturacion,
544
+ enrichSalesReportPublicaciones,
545
+ enrichSalesReportVentas,
546
+ fetchMercadoLibreFiscalEvidence,
547
+ fetchMercadoLibreOrderUniverse,
548
+ fetchMercadoLibreShipmentEvidence,
549
+ groupMercadoLibreOrders,
550
+ normalizeMercadoLibreOrder,
551
+ selectMercadoLibreOrdersById
552
+ };
553
+ //# sourceMappingURL=mercadolibre-sales-report.js.map