@yoryoboy/bi-mcp 1.6.0 → 1.8.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/README.md +87 -87
- package/bin/bi-mcp.js +9 -9
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.js +14 -3
- package/dist/index.js.map +2 -2
- package/dist/mcp-use.json +2 -2
- package/dist/public/icon.svg +6 -6
- package/dist/scripts/_helpers.js.map +1 -1
- package/dist/scripts/admin-profile-delete.js.map +1 -1
- package/dist/scripts/admin-profile-list.js.map +1 -1
- package/dist/scripts/admin-profile-upsert.js.map +1 -1
- package/dist/scripts/admin-vtex-list.js.map +1 -1
- package/dist/scripts/admin-vtex-upsert.js.map +1 -1
- package/dist/scripts/admin-vtex-validate.js.map +1 -1
- package/dist/scripts/run-migrations.js.map +1 -1
- package/dist/scripts/test-db-connection.js.map +1 -1
- package/dist/src/analytics/ga4-channel-groups.js.map +1 -1
- package/dist/src/analytics/ga4-report-utils.js.map +1 -1
- package/dist/src/config/benchmarks.js.map +1 -1
- package/dist/src/config/google-store.js.map +1 -1
- package/dist/src/config/google.js.map +1 -1
- package/dist/src/config/mercadolibre-profile-store.js.map +1 -1
- package/dist/src/config/mercadolibre.js.map +1 -1
- package/dist/src/config/meta.js.map +1 -1
- package/dist/src/config/profile-store.js.map +1 -1
- package/dist/src/config/vtex-crypto.js.map +1 -1
- package/dist/src/config/vtex-profile-store.js.map +1 -1
- package/dist/src/config/vtex.js.map +1 -1
- package/dist/src/db/client.js.map +1 -1
- package/dist/src/google-ads/report-utils.js +78 -0
- package/dist/src/google-ads/report-utils.js.map +7 -0
- package/dist/src/meta/meta-utils.js.map +1 -1
- package/dist/src/prompts/reporte-ventas.js.map +1 -1
- package/dist/src/search-console/search-console-utils.js +275 -0
- package/dist/src/search-console/search-console-utils.js.map +7 -0
- package/dist/src/services/analytics/ga4-client.js.map +1 -1
- package/dist/src/services/analytics/oauth.js.map +1 -1
- package/dist/src/services/google-ads/google-ads-client.js.map +1 -1
- package/dist/src/services/mercadolibre/mercadolibre-api.js.map +1 -1
- package/dist/src/services/mercadolibre/mercadolibre-items.js +29 -2
- package/dist/src/services/mercadolibre/mercadolibre-items.js.map +2 -2
- package/dist/src/services/mercadolibre/mercadolibre-orders.js.map +1 -1
- package/dist/src/services/mercadolibre/mercadolibre-questions.js.map +1 -1
- package/dist/src/services/meta/meta-ads.js.map +1 -1
- package/dist/src/services/meta/meta-api.js.map +1 -1
- package/dist/src/services/search-console/search-console-client.js.map +1 -1
- package/dist/src/services/search-console/search-console-utils.js.map +1 -1
- package/dist/src/services/vtex/vtex-api.js.map +1 -1
- package/dist/src/services/vtex/vtex-catalog-write.js.map +1 -1
- package/dist/src/services/vtex/vtex-catalog.js.map +1 -1
- package/dist/src/services/vtex/vtex-logistics.js.map +1 -1
- package/dist/src/services/vtex/vtex-orders-write.js.map +1 -1
- package/dist/src/services/vtex/vtex-orders.js.map +1 -1
- package/dist/src/services/vtex/vtex-pricing-write.js.map +1 -1
- package/dist/src/services/vtex/vtex-pricing.js.map +1 -1
- package/dist/src/services/vtex/vtex-write.js.map +1 -1
- package/dist/src/tools/analytics/attribution-gaps.js.map +1 -1
- package/dist/src/tools/analytics/channel-mix.js.map +1 -1
- package/dist/src/tools/analytics/ecommerce-tracking-health.js.map +1 -1
- package/dist/src/tools/analytics/engagement-overview.js.map +1 -1
- package/dist/src/tools/analytics/index.js.map +1 -1
- package/dist/src/tools/analytics/list-accessible-properties.js.map +1 -1
- package/dist/src/tools/analytics/property-info.js.map +1 -1
- package/dist/src/tools/analytics/revenue-by-channel.js.map +1 -1
- package/dist/src/tools/analytics/revenue-overview.js.map +1 -1
- package/dist/src/tools/analytics/revenue-trend.js.map +1 -1
- package/dist/src/tools/analytics/source-medium-breakdown.js.map +1 -1
- package/dist/src/tools/analytics/top-landing-pages.js.map +1 -1
- package/dist/src/tools/config/check-database-connection.js.map +1 -1
- package/dist/src/tools/config/index.js.map +1 -1
- package/dist/src/tools/config/list-profiles.js.map +1 -1
- package/dist/src/tools/google-ads/account-overview.js.map +1 -1
- package/dist/src/tools/google-ads/account-risks.js.map +1 -1
- package/dist/src/tools/google-ads/break-even-analysis.js.map +1 -1
- package/dist/src/tools/google-ads/campaign-performance.js.map +1 -1
- package/dist/src/tools/google-ads/channel-mix.js.map +1 -1
- package/dist/src/tools/google-ads/compare-accounts.js.map +1 -1
- package/dist/src/tools/google-ads/customer-clients.js.map +1 -1
- package/dist/src/tools/google-ads/customer-info.js.map +1 -1
- package/dist/src/tools/google-ads/index.js.map +1 -1
- package/dist/src/tools/google-ads/scaling-health.js.map +1 -1
- package/dist/src/tools/google-ads/search-terms-summary.js.map +1 -1
- package/dist/src/tools/google-ads/time-series.js.map +1 -1
- package/dist/src/tools/index.js.map +1 -1
- package/dist/src/tools/mercadolibre/answer-question.js.map +1 -1
- package/dist/src/tools/mercadolibre/create-item.js.map +1 -1
- package/dist/src/tools/mercadolibre/estimate-listing-fee.js +4 -17
- package/dist/src/tools/mercadolibre/estimate-listing-fee.js.map +2 -2
- package/dist/src/tools/mercadolibre/get-account-context.js.map +1 -1
- package/dist/src/tools/mercadolibre/get-category-requirements.js.map +1 -1
- package/dist/src/tools/mercadolibre/get-item-details.js.map +1 -1
- package/dist/src/tools/mercadolibre/get-item-visits.js.map +1 -1
- package/dist/src/tools/mercadolibre/get-listing-quality.js.map +1 -1
- package/dist/src/tools/mercadolibre/get-order-details.js.map +1 -1
- package/dist/src/tools/mercadolibre/get-orders-summary.js +366 -28
- package/dist/src/tools/mercadolibre/get-orders-summary.js.map +2 -2
- package/dist/src/tools/mercadolibre/get-sales-by-item.js.map +1 -1
- package/dist/src/tools/mercadolibre/get-sales-trend.js.map +1 -1
- package/dist/src/tools/mercadolibre/get-shipping-summary.js.map +1 -1
- package/dist/src/tools/mercadolibre/get-site-categories-and-listing-types.js +50 -0
- package/dist/src/tools/mercadolibre/get-site-categories-and-listing-types.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-store-performance.js.map +1 -1
- package/dist/src/tools/mercadolibre/helpers.js.map +1 -1
- package/dist/src/tools/mercadolibre/index.js +1 -0
- package/dist/src/tools/mercadolibre/index.js.map +2 -2
- package/dist/src/tools/mercadolibre/pause-or-reactivate-item.js.map +1 -1
- package/dist/src/tools/mercadolibre/predict-category.js.map +1 -1
- package/dist/src/tools/mercadolibre/profile-resolution.js.map +1 -1
- package/dist/src/tools/mercadolibre/search-items.js +207 -52
- package/dist/src/tools/mercadolibre/search-items.js.map +2 -2
- package/dist/src/tools/mercadolibre/search-questions.js.map +1 -1
- package/dist/src/tools/mercadolibre/update-item-basic-fields.js.map +1 -1
- package/dist/src/tools/mercadolibre/update-item-description.js.map +1 -1
- package/dist/src/tools/mercadolibre/update-item-pictures.js.map +1 -1
- package/dist/src/tools/mercadolibre/validate-connection.js.map +1 -1
- package/dist/src/tools/mercadolibre/write-helpers.js.map +1 -1
- package/dist/src/tools/meta/account-overview.js.map +1 -1
- package/dist/src/tools/meta/ad-account-info.js.map +1 -1
- package/dist/src/tools/meta/ads-performance.js.map +1 -1
- package/dist/src/tools/meta/campaign-performance.js.map +1 -1
- package/dist/src/tools/meta/index.js.map +1 -1
- package/dist/src/tools/meta/list-accessible-ad-accounts.js.map +1 -1
- package/dist/src/tools/meta/list-accessible-businesses.js.map +1 -1
- package/dist/src/tools/meta/placement-mix.js.map +1 -1
- package/dist/src/tools/meta/time-series.js.map +1 -1
- package/dist/src/tools/search-console/country-breakdown.js.map +1 -1
- package/dist/src/tools/search-console/device-breakdown.js.map +1 -1
- package/dist/src/tools/search-console/high-impression-low-click-queries.js.map +1 -1
- package/dist/src/tools/search-console/index.js.map +1 -1
- package/dist/src/tools/search-console/list-accessible-sites.js.map +1 -1
- package/dist/src/tools/search-console/low-ctr-opportunities.js.map +1 -1
- package/dist/src/tools/search-console/page-performance.js.map +1 -1
- package/dist/src/tools/search-console/product-demand-low-capture-queries.js.map +1 -1
- package/dist/src/tools/search-console/query-page-matrix.js.map +1 -1
- package/dist/src/tools/search-console/query-performance.js.map +1 -1
- package/dist/src/tools/search-console/quick-win-opportunities.js.map +1 -1
- package/dist/src/tools/search-console/rising-non-brand-queries.js.map +1 -1
- package/dist/src/tools/search-console/search-performance.js.map +1 -1
- package/dist/src/tools/search-console/site-context.js.map +1 -1
- package/dist/src/tools/search-console/visibility-declines.js.map +1 -1
- package/dist/src/tools/vtex/activate-sku.js.map +1 -1
- package/dist/src/tools/vtex/add-order-tracking.js.map +1 -1
- package/dist/src/tools/vtex/associate-specification.js.map +1 -1
- package/dist/src/tools/vtex/attach-catalog-image.js.map +1 -1
- package/dist/src/tools/vtex/cancel-order.js.map +1 -1
- package/dist/src/tools/vtex/computed-price.js.map +1 -1
- package/dist/src/tools/vtex/create-brand.js.map +1 -1
- package/dist/src/tools/vtex/create-category.js.map +1 -1
- package/dist/src/tools/vtex/create-product-with-sku.js.map +1 -1
- package/dist/src/tools/vtex/create-product.js.map +1 -1
- package/dist/src/tools/vtex/create-sku.js.map +1 -1
- package/dist/src/tools/vtex/create-specification-value.js.map +1 -1
- package/dist/src/tools/vtex/create-specification.js.map +1 -1
- package/dist/src/tools/vtex/deactivate-sku.js.map +1 -1
- package/dist/src/tools/vtex/delete-fixed-price.js.map +1 -1
- package/dist/src/tools/vtex/index.js.map +1 -1
- package/dist/src/tools/vtex/inventory-check.js.map +1 -1
- package/dist/src/tools/vtex/invoice-order.js.map +1 -1
- package/dist/src/tools/vtex/order-details.js.map +1 -1
- package/dist/src/tools/vtex/orders-summary.js.map +1 -1
- package/dist/src/tools/vtex/product-offers.js.map +1 -1
- package/dist/src/tools/vtex/profile-resolution.js.map +1 -1
- package/dist/src/tools/vtex/sku-offers.js.map +1 -1
- package/dist/src/tools/vtex/sku-price.js.map +1 -1
- package/dist/src/tools/vtex/toggle-unlimited-quantity.js.map +1 -1
- package/dist/src/tools/vtex/update-inventory.js.map +1 -1
- package/dist/src/tools/vtex/update-lead-time.js.map +1 -1
- package/dist/src/tools/vtex/update-product-basic-fields.js.map +1 -1
- package/dist/src/tools/vtex/update-sku-basic-fields.js.map +1 -1
- package/dist/src/tools/vtex/update-sku-price.js.map +1 -1
- package/dist/src/tools/vtex/upsert-fixed-price.js.map +1 -1
- package/dist/src/tools/vtex/warehouse-inventory.js.map +1 -1
- package/dist/src/tools/vtex/write-helpers.js.map +1 -1
- package/dist/src/utils/case-conversion.js.map +1 -1
- package/dist/src/utils/currency.js.map +1 -1
- package/dist/src/utils/format-order-details.js.map +1 -1
- package/dist/src/utils/google-ads.js.map +1 -1
- package/dist/src/utils/money.js.map +1 -1
- package/dist/src/utils/order-status.js.map +1 -1
- package/dist/src/utils/pagination.js.map +1 -1
- package/dist/src/utils/strip-payload.js.map +1 -1
- package/dist/src/utils/type-guards.js.map +1 -1
- package/package.json +1 -1
- package/public/icon.svg +6 -6
|
@@ -23,8 +23,10 @@ import {
|
|
|
23
23
|
} from "./helpers.js";
|
|
24
24
|
import { resolveMercadoLibreProfileOrSelection } from "./profile-resolution.js";
|
|
25
25
|
import { mercadolibreProfileIdSchemaField } from "./write-helpers.js";
|
|
26
|
-
const PAGE_FETCH_CONCURRENCY =
|
|
26
|
+
const PAGE_FETCH_CONCURRENCY = 15;
|
|
27
27
|
const PAGE_FETCH_MAX_RETRIES = 2;
|
|
28
|
+
const ORDER_DETAIL_MAX_PAGES_PER_CALL = 15;
|
|
29
|
+
const DEFAULT_ORDER_DETAIL_PAGE_LIMIT = 50;
|
|
28
30
|
const TOP_LIMIT = 10;
|
|
29
31
|
const ordersSchema = [
|
|
30
32
|
"id",
|
|
@@ -142,6 +144,57 @@ function sortedCurrencyTotals(input) {
|
|
|
142
144
|
Object.entries(input).filter(([, amount]) => amount !== 0).sort(([left], [right]) => left.localeCompare(right)).map(([currency, amount]) => [currency, roundMoney(amount)])
|
|
143
145
|
);
|
|
144
146
|
}
|
|
147
|
+
function createCurrencyMetricBucket() {
|
|
148
|
+
return {
|
|
149
|
+
orders: 0,
|
|
150
|
+
units: 0,
|
|
151
|
+
revenue: 0,
|
|
152
|
+
paidRevenue: 0,
|
|
153
|
+
paymentTotalPaid: 0,
|
|
154
|
+
transactionAmount: 0,
|
|
155
|
+
refundedAmount: 0,
|
|
156
|
+
cancelledGmv: 0,
|
|
157
|
+
saleFee: 0,
|
|
158
|
+
shippingCost: 0,
|
|
159
|
+
couponAmount: 0,
|
|
160
|
+
paymentCouponAmount: 0,
|
|
161
|
+
discountedGmv: 0,
|
|
162
|
+
notDeliveredGmv: 0,
|
|
163
|
+
cancelledOrders: 0,
|
|
164
|
+
refundedOrders: 0,
|
|
165
|
+
ordersWithShippingCost: 0,
|
|
166
|
+
ordersWithCoupon: 0,
|
|
167
|
+
discountedOrders: 0,
|
|
168
|
+
notDeliveredOrders: 0
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function incrementCurrencyMetric(map, currencyId, updater) {
|
|
172
|
+
map[currencyId] = map[currencyId] ?? createCurrencyMetricBucket();
|
|
173
|
+
updater(map[currencyId]);
|
|
174
|
+
}
|
|
175
|
+
function sortedCurrencyMetrics(input, mapper) {
|
|
176
|
+
return Object.fromEntries(
|
|
177
|
+
Object.entries(input).sort(([left], [right]) => left.localeCompare(right)).map(([currencyId, bucket]) => [currencyId, stripNulls(mapper(bucket))])
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
function topAmountBreakdownFromMap(counts, amounts, totalOrders, limit = TOP_LIMIT) {
|
|
181
|
+
const sorted = Array.from(counts.entries()).filter(([, count]) => count > 0).sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]));
|
|
182
|
+
const entries = sorted.slice(0, limit).map(([key, orders]) => ({
|
|
183
|
+
key,
|
|
184
|
+
orders,
|
|
185
|
+
rate: safeRate(orders, totalOrders),
|
|
186
|
+
amount_by_currency: sortedCurrencyTotals(amounts.get(key) ?? {})
|
|
187
|
+
}));
|
|
188
|
+
const otherOrders = sorted.slice(limit).reduce((sum, [, count]) => sum + count, 0);
|
|
189
|
+
return stripNulls({
|
|
190
|
+
entries,
|
|
191
|
+
other_orders: otherOrders > 0 ? otherOrders : void 0,
|
|
192
|
+
other_rate: otherOrders > 0 ? safeRate(otherOrders, totalOrders) : void 0
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
function revenueSum(input) {
|
|
196
|
+
return Object.values(input).reduce((sum, value) => sum + value, 0);
|
|
197
|
+
}
|
|
145
198
|
function compactFeedbackValue(value) {
|
|
146
199
|
const compact = compactNestedValue(value);
|
|
147
200
|
return compact.trim();
|
|
@@ -170,6 +223,14 @@ function buildCalculations(orders, metadata, failedPages) {
|
|
|
170
223
|
const items = /* @__PURE__ */ new Map();
|
|
171
224
|
const variations = /* @__PURE__ */ new Map();
|
|
172
225
|
const skus = /* @__PURE__ */ new Map();
|
|
226
|
+
const dailyMetrics = /* @__PURE__ */ new Map();
|
|
227
|
+
const flowMetrics = /* @__PURE__ */ new Map();
|
|
228
|
+
const skuEconomics = /* @__PURE__ */ new Map();
|
|
229
|
+
const cancelGroupCounts = /* @__PURE__ */ new Map();
|
|
230
|
+
const cancelCodeCounts = /* @__PURE__ */ new Map();
|
|
231
|
+
const cancelRequestedByCounts = /* @__PURE__ */ new Map();
|
|
232
|
+
const cancelReasonCounts = /* @__PURE__ */ new Map();
|
|
233
|
+
const cancelReasonAmounts = /* @__PURE__ */ new Map();
|
|
173
234
|
let unitsTotal = 0;
|
|
174
235
|
let fulfilledOrders = 0;
|
|
175
236
|
let deliveredOrders = 0;
|
|
@@ -188,9 +249,16 @@ function buildCalculations(orders, metadata, failedPages) {
|
|
|
188
249
|
let paymentApprovedOrders = 0;
|
|
189
250
|
let itemLineCount = 0;
|
|
190
251
|
let ordersWithAnyPostSaleIssue = 0;
|
|
252
|
+
let ordersWithRefundedAmount = 0;
|
|
253
|
+
let ordersWithShippingCostAmount = 0;
|
|
254
|
+
let ordersWithCouponAmount = 0;
|
|
255
|
+
let notDeliveredOrders = 0;
|
|
256
|
+
let multiUnitItemLines = 0;
|
|
257
|
+
let multiUnitSaleFeeObservations = 0;
|
|
191
258
|
const itemsSeen = /* @__PURE__ */ new Set();
|
|
192
259
|
const buyerSeen = /* @__PURE__ */ new Set();
|
|
193
260
|
const financialRaw = {};
|
|
261
|
+
const currencyMetrics = {};
|
|
194
262
|
for (const order of orders) {
|
|
195
263
|
const currencyId = getOrderCurrency(order);
|
|
196
264
|
const totalAmount = toNumber(order.total_amount);
|
|
@@ -210,6 +278,16 @@ function buildCalculations(orders, metadata, failedPages) {
|
|
|
210
278
|
const paymentType = normalizeString(payment.payment_type);
|
|
211
279
|
const installments = normalizeScalarString(payment.installments);
|
|
212
280
|
const tags = asArray(order.tags).map((tag) => normalizeScalarString(tag)).filter(Boolean);
|
|
281
|
+
const cancelDetail = asRecord(order.cancel_detail);
|
|
282
|
+
const isCancelled = normalizeString(order.status) === "cancelled";
|
|
283
|
+
const refundedAmount = toNumber(payment.transaction_amount_refunded);
|
|
284
|
+
const shippingCost = toNumber(payment.shipping_cost);
|
|
285
|
+
const paymentCouponAmount = toNumber(payment.coupon_amount);
|
|
286
|
+
const dayKey = compactDateTime(order.date_created)?.slice(0, 10) || "UNKNOWN_DATE";
|
|
287
|
+
const flows = asArray(context.flows).map((flow) => normalizeScalarString(flow)).filter(Boolean);
|
|
288
|
+
const effectiveFlows = flows.length > 0 ? flows : ["NO_FLOW"];
|
|
289
|
+
const isDiscounted = tags.includes("order_has_discount") || couponAmount > 0 || paymentCouponAmount > 0;
|
|
290
|
+
const isNotDelivered = tags.includes("not_delivered") || normalizeString(cancelDetail.group) === "shipment";
|
|
213
291
|
const orderItemDistinctIds = /* @__PURE__ */ new Set();
|
|
214
292
|
financialRaw[currencyId] = financialRaw[currencyId] ?? {
|
|
215
293
|
orders: 0,
|
|
@@ -228,6 +306,40 @@ function buildCalculations(orders, metadata, failedPages) {
|
|
|
228
306
|
financialRaw[currencyId].paymentTotalPaidAmount += toNumber(payment.total_paid_amount);
|
|
229
307
|
financialRaw[currencyId].transactionAmount += toNumber(payment.transaction_amount);
|
|
230
308
|
financialRaw[currencyId].couponAmount += couponAmount;
|
|
309
|
+
const dailyCurrencyMetrics = dailyMetrics.get(dayKey) ?? {};
|
|
310
|
+
dailyMetrics.set(dayKey, dailyCurrencyMetrics);
|
|
311
|
+
const updateOrderCurrencyMetrics = (bucket) => {
|
|
312
|
+
bucket.orders += 1;
|
|
313
|
+
bucket.revenue += totalAmount;
|
|
314
|
+
bucket.paidRevenue += paidAmount;
|
|
315
|
+
bucket.paymentTotalPaid += toNumber(payment.total_paid_amount);
|
|
316
|
+
bucket.transactionAmount += toNumber(payment.transaction_amount);
|
|
317
|
+
bucket.refundedAmount += refundedAmount;
|
|
318
|
+
bucket.shippingCost += shippingCost;
|
|
319
|
+
bucket.couponAmount += couponAmount;
|
|
320
|
+
bucket.paymentCouponAmount += paymentCouponAmount;
|
|
321
|
+
if (isCancelled) {
|
|
322
|
+
bucket.cancelledOrders += 1;
|
|
323
|
+
bucket.cancelledGmv += totalAmount;
|
|
324
|
+
}
|
|
325
|
+
if (refundedAmount > 0) {
|
|
326
|
+
bucket.refundedOrders += 1;
|
|
327
|
+
}
|
|
328
|
+
if (shippingCost > 0) {
|
|
329
|
+
bucket.ordersWithShippingCost += 1;
|
|
330
|
+
}
|
|
331
|
+
if (couponAmount > 0 || paymentCouponAmount > 0) {
|
|
332
|
+
bucket.ordersWithCoupon += 1;
|
|
333
|
+
}
|
|
334
|
+
if (isDiscounted) {
|
|
335
|
+
bucket.discountedOrders += 1;
|
|
336
|
+
}
|
|
337
|
+
if (isNotDelivered) {
|
|
338
|
+
bucket.notDeliveredOrders += 1;
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
incrementCurrencyMetric(currencyMetrics, currencyId, updateOrderCurrencyMetrics);
|
|
342
|
+
incrementCurrencyMetric(dailyCurrencyMetrics, currencyId, updateOrderCurrencyMetrics);
|
|
231
343
|
incrementCount(statusCounts, normalizeString(order.status));
|
|
232
344
|
incrementCount(channelCounts, normalizeString(context.channel));
|
|
233
345
|
incrementCount(siteCounts, normalizeString(context.site));
|
|
@@ -269,6 +381,49 @@ function buildCalculations(orders, metadata, failedPages) {
|
|
|
269
381
|
if (tags.includes("catalog")) {
|
|
270
382
|
catalogOrders += 1;
|
|
271
383
|
}
|
|
384
|
+
if (refundedAmount > 0) {
|
|
385
|
+
ordersWithRefundedAmount += 1;
|
|
386
|
+
}
|
|
387
|
+
if (shippingCost > 0) {
|
|
388
|
+
ordersWithShippingCostAmount += 1;
|
|
389
|
+
}
|
|
390
|
+
if (couponAmount > 0 || paymentCouponAmount > 0) {
|
|
391
|
+
ordersWithCouponAmount += 1;
|
|
392
|
+
}
|
|
393
|
+
if (isNotDelivered) {
|
|
394
|
+
notDeliveredOrders += 1;
|
|
395
|
+
}
|
|
396
|
+
if (isCancelled) {
|
|
397
|
+
const cancelGroup = normalizeString(cancelDetail.group, "UNKNOWN_GROUP");
|
|
398
|
+
const cancelCode = normalizeString(cancelDetail.code, "UNKNOWN_CODE");
|
|
399
|
+
const requestedBy = normalizeString(cancelDetail.requested_by, "UNKNOWN_REQUESTER");
|
|
400
|
+
const reasonKey = `${cancelGroup}:${cancelCode}:${requestedBy}`;
|
|
401
|
+
incrementCount(cancelGroupCounts, cancelGroup);
|
|
402
|
+
incrementCount(cancelCodeCounts, cancelCode);
|
|
403
|
+
incrementCount(cancelRequestedByCounts, requestedBy);
|
|
404
|
+
incrementCount(cancelReasonCounts, reasonKey);
|
|
405
|
+
const reasonAmount = cancelReasonAmounts.get(reasonKey) ?? {};
|
|
406
|
+
incrementCurrency(reasonAmount, currencyId, totalAmount);
|
|
407
|
+
cancelReasonAmounts.set(reasonKey, reasonAmount);
|
|
408
|
+
}
|
|
409
|
+
for (const flow of effectiveFlows) {
|
|
410
|
+
const flowAggregate = flowMetrics.get(flow) ?? {
|
|
411
|
+
flow,
|
|
412
|
+
orders: 0,
|
|
413
|
+
units: 0,
|
|
414
|
+
cancelled_orders: 0,
|
|
415
|
+
revenue_by_currency: {},
|
|
416
|
+
paid_revenue_by_currency: {},
|
|
417
|
+
sale_fee_by_currency: {}
|
|
418
|
+
};
|
|
419
|
+
flowAggregate.orders += 1;
|
|
420
|
+
if (isCancelled) {
|
|
421
|
+
flowAggregate.cancelled_orders += 1;
|
|
422
|
+
}
|
|
423
|
+
incrementCurrency(flowAggregate.revenue_by_currency, currencyId, totalAmount);
|
|
424
|
+
incrementCurrency(flowAggregate.paid_revenue_by_currency, currencyId, paidAmount);
|
|
425
|
+
flowMetrics.set(flow, flowAggregate);
|
|
426
|
+
}
|
|
272
427
|
const feedbackBuyer = compactFeedbackValue(feedback.buyer);
|
|
273
428
|
const feedbackSeller = compactFeedbackValue(feedback.seller);
|
|
274
429
|
const hasFeedback = Boolean(feedbackBuyer || feedbackSeller);
|
|
@@ -321,11 +476,39 @@ function buildCalculations(orders, metadata, failedPages) {
|
|
|
321
476
|
const stockNode = normalizeString(stock.node_id);
|
|
322
477
|
const variationSummary = compactVariationAttributes(item.variation_attributes);
|
|
323
478
|
const itemKey = `${itemId}::${variationSummary}::${sellerSku}`;
|
|
324
|
-
const skuKey = sellerSku || itemId || title;
|
|
479
|
+
const skuKey = sellerSku || itemId || title || "UNKNOWN_SKU";
|
|
325
480
|
const variationKey = variationSummary || "NO_VARIATION";
|
|
481
|
+
const itemRevenue = unitPrice * quantity;
|
|
482
|
+
const paidRevenueApprox = totalAmount > 0 ? paidAmount * (itemRevenue / totalAmount) : itemRevenue;
|
|
483
|
+
const refundedAmountApprox = totalAmount > 0 ? refundedAmount * (itemRevenue / totalAmount) : refundedAmount;
|
|
484
|
+
if (quantity > 1) {
|
|
485
|
+
multiUnitItemLines += 1;
|
|
486
|
+
if (saleFee > 0) {
|
|
487
|
+
multiUnitSaleFeeObservations += 1;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
326
490
|
unitsTotal += quantity;
|
|
327
491
|
financialRaw[currencyId].units += quantity;
|
|
328
492
|
financialRaw[currencyId].saleFee += saleFee;
|
|
493
|
+
const updateItemCurrencyMetrics = (bucket) => {
|
|
494
|
+
bucket.units += quantity;
|
|
495
|
+
bucket.saleFee += saleFee;
|
|
496
|
+
if (isDiscounted) {
|
|
497
|
+
bucket.discountedGmv += itemRevenue;
|
|
498
|
+
}
|
|
499
|
+
if (isNotDelivered) {
|
|
500
|
+
bucket.notDeliveredGmv += itemRevenue;
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
incrementCurrencyMetric(currencyMetrics, currencyId, updateItemCurrencyMetrics);
|
|
504
|
+
incrementCurrencyMetric(dailyCurrencyMetrics, currencyId, updateItemCurrencyMetrics);
|
|
505
|
+
for (const flow of effectiveFlows) {
|
|
506
|
+
const flowAggregate = flowMetrics.get(flow);
|
|
507
|
+
if (flowAggregate) {
|
|
508
|
+
flowAggregate.units += quantity;
|
|
509
|
+
incrementCurrency(flowAggregate.sale_fee_by_currency, currencyId, saleFee);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
329
512
|
if (itemId) {
|
|
330
513
|
itemsSeen.add(itemId);
|
|
331
514
|
orderItemDistinctIds.add(itemId);
|
|
@@ -368,6 +551,32 @@ function buildCalculations(orders, metadata, failedPages) {
|
|
|
368
551
|
skuAggregate.units += quantity;
|
|
369
552
|
incrementCurrency(skuAggregate.revenue_by_currency, currencyId, unitPrice * quantity);
|
|
370
553
|
skus.set(skuKey, skuAggregate);
|
|
554
|
+
const skuEconomicsAggregate = skuEconomics.get(skuKey) ?? {
|
|
555
|
+
seller_sku: sellerSku,
|
|
556
|
+
item_id: itemId,
|
|
557
|
+
title,
|
|
558
|
+
orders: 0,
|
|
559
|
+
units: 0,
|
|
560
|
+
cancelled_orders: 0,
|
|
561
|
+
refunded_orders: 0,
|
|
562
|
+
revenue_by_currency: {},
|
|
563
|
+
paid_revenue_by_currency: {},
|
|
564
|
+
sale_fee_by_currency: {},
|
|
565
|
+
refunded_amount_by_currency: {}
|
|
566
|
+
};
|
|
567
|
+
skuEconomicsAggregate.orders += 1;
|
|
568
|
+
skuEconomicsAggregate.units += quantity;
|
|
569
|
+
if (isCancelled) {
|
|
570
|
+
skuEconomicsAggregate.cancelled_orders += 1;
|
|
571
|
+
}
|
|
572
|
+
if (refundedAmount > 0) {
|
|
573
|
+
skuEconomicsAggregate.refunded_orders += 1;
|
|
574
|
+
}
|
|
575
|
+
incrementCurrency(skuEconomicsAggregate.revenue_by_currency, currencyId, itemRevenue);
|
|
576
|
+
incrementCurrency(skuEconomicsAggregate.paid_revenue_by_currency, currencyId, paidRevenueApprox);
|
|
577
|
+
incrementCurrency(skuEconomicsAggregate.sale_fee_by_currency, currencyId, saleFee);
|
|
578
|
+
incrementCurrency(skuEconomicsAggregate.refunded_amount_by_currency, currencyId, refundedAmountApprox);
|
|
579
|
+
skuEconomics.set(skuKey, skuEconomicsAggregate);
|
|
371
580
|
}
|
|
372
581
|
financialRaw[currencyId].distinctItems += orderItemDistinctIds.size;
|
|
373
582
|
}
|
|
@@ -430,6 +639,108 @@ function buildCalculations(orders, metadata, failedPages) {
|
|
|
430
639
|
const buyersWithMultipleOrders = Array.from(buyers.values()).filter((buyer) => buyer.orders > 1).length;
|
|
431
640
|
const topVariationValues = Array.from(variationValueCounts.entries()).filter(([key]) => key && key !== "NO_VARIATION").sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])).slice(0, TOP_LIMIT).map(([key, occurrences]) => ({ key, occurrences }));
|
|
432
641
|
const topSingleValue = (counts) => Array.from(counts.entries()).filter(([key]) => key).sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]))[0]?.[0] ?? "";
|
|
642
|
+
const revenueQuality = {
|
|
643
|
+
by_currency: sortedCurrencyMetrics(currencyMetrics, (bucket) => ({
|
|
644
|
+
gross_gmv: roundMoney(bucket.revenue),
|
|
645
|
+
paid_gmv: roundMoney(bucket.paidRevenue),
|
|
646
|
+
payment_total_paid_amount: roundMoney(bucket.paymentTotalPaid),
|
|
647
|
+
transaction_amount_total: roundMoney(bucket.transactionAmount),
|
|
648
|
+
refunded_amount_total: roundMoney(bucket.refundedAmount),
|
|
649
|
+
net_after_refunds: roundMoney(bucket.transactionAmount - bucket.refundedAmount),
|
|
650
|
+
cancelled_gmv: roundMoney(bucket.cancelledGmv),
|
|
651
|
+
cancelled_rate_by_orders: safeRate(bucket.cancelledOrders, bucket.orders),
|
|
652
|
+
cancelled_rate_by_gmv: safeRate(bucket.cancelledGmv, bucket.revenue),
|
|
653
|
+
paid_rate_by_gmv: safeRate(bucket.paidRevenue, bucket.revenue)
|
|
654
|
+
}))
|
|
655
|
+
};
|
|
656
|
+
const refundsCancellations = {
|
|
657
|
+
by_currency: sortedCurrencyMetrics(currencyMetrics, (bucket) => ({
|
|
658
|
+
refunded_amount_total: roundMoney(bucket.refundedAmount),
|
|
659
|
+
refunded_orders: bucket.refundedOrders,
|
|
660
|
+
refund_rate_by_orders: safeRate(bucket.refundedOrders, bucket.orders),
|
|
661
|
+
refund_rate_by_transaction_amount: safeRate(bucket.refundedAmount, bucket.transactionAmount),
|
|
662
|
+
cancelled_orders: bucket.cancelledOrders,
|
|
663
|
+
cancelled_gmv: roundMoney(bucket.cancelledGmv),
|
|
664
|
+
cancelled_rate_by_orders: safeRate(bucket.cancelledOrders, bucket.orders),
|
|
665
|
+
cancelled_rate_by_gmv: safeRate(bucket.cancelledGmv, bucket.revenue)
|
|
666
|
+
})),
|
|
667
|
+
cancel_group_breakdown: topBreakdownFromMap(cancelGroupCounts, ordersTotal),
|
|
668
|
+
cancel_code_breakdown: topBreakdownFromMap(cancelCodeCounts, ordersTotal),
|
|
669
|
+
cancel_requested_by_breakdown: topBreakdownFromMap(cancelRequestedByCounts, ordersTotal),
|
|
670
|
+
top_cancel_reasons: topAmountBreakdownFromMap(cancelReasonCounts, cancelReasonAmounts, ordersTotal)
|
|
671
|
+
};
|
|
672
|
+
const shippingAndDiscounts = {
|
|
673
|
+
by_currency: sortedCurrencyMetrics(currencyMetrics, (bucket) => ({
|
|
674
|
+
buyer_shipping_paid_total: roundMoney(bucket.shippingCost),
|
|
675
|
+
orders_with_shipping_cost: bucket.ordersWithShippingCost,
|
|
676
|
+
avg_shipping_paid_by_order_with_cost: roundMoney(bucket.shippingCost / Math.max(1, bucket.ordersWithShippingCost)),
|
|
677
|
+
shipping_cost_rate_on_paid_gmv: safeRate(bucket.shippingCost, bucket.paidRevenue),
|
|
678
|
+
coupon_amount_total: roundMoney(bucket.couponAmount),
|
|
679
|
+
payment_coupon_amount_total: roundMoney(bucket.paymentCouponAmount),
|
|
680
|
+
orders_with_coupon: bucket.ordersWithCoupon,
|
|
681
|
+
coupon_rate_by_orders: safeRate(bucket.ordersWithCoupon, bucket.orders),
|
|
682
|
+
coupon_rate_on_gmv: safeRate(bucket.couponAmount + bucket.paymentCouponAmount, bucket.revenue),
|
|
683
|
+
discounted_orders: bucket.discountedOrders,
|
|
684
|
+
discounted_order_rate: safeRate(bucket.discountedOrders, bucket.orders),
|
|
685
|
+
discounted_gmv: roundMoney(bucket.discountedGmv),
|
|
686
|
+
not_delivered_orders: bucket.notDeliveredOrders,
|
|
687
|
+
not_delivered_gmv: roundMoney(bucket.notDeliveredGmv),
|
|
688
|
+
not_delivered_rate_by_orders: safeRate(bucket.notDeliveredOrders, bucket.orders),
|
|
689
|
+
not_delivered_rate_by_gmv: safeRate(bucket.notDeliveredGmv, bucket.revenue)
|
|
690
|
+
}))
|
|
691
|
+
};
|
|
692
|
+
const dailyTrend = Array.from(dailyMetrics.entries()).sort(([left], [right]) => left.localeCompare(right)).map(([date, byCurrency]) => ({
|
|
693
|
+
date,
|
|
694
|
+
by_currency: sortedCurrencyMetrics(byCurrency, (bucket) => ({
|
|
695
|
+
orders: bucket.orders,
|
|
696
|
+
units: bucket.units,
|
|
697
|
+
revenue: roundMoney(bucket.revenue),
|
|
698
|
+
paid_revenue: roundMoney(bucket.paidRevenue),
|
|
699
|
+
cancelled_orders: bucket.cancelledOrders,
|
|
700
|
+
refunded_amount: roundMoney(bucket.refundedAmount),
|
|
701
|
+
avg_order_value: roundMoney(bucket.revenue / Math.max(1, bucket.orders))
|
|
702
|
+
}))
|
|
703
|
+
}));
|
|
704
|
+
const flowPerformance = Array.from(flowMetrics.values()).sort((left, right) => revenueSum(right.revenue_by_currency) - revenueSum(left.revenue_by_currency) || left.flow.localeCompare(right.flow)).slice(0, TOP_LIMIT).map((flow) => ({
|
|
705
|
+
flow: flow.flow,
|
|
706
|
+
orders: flow.orders,
|
|
707
|
+
units: flow.units,
|
|
708
|
+
revenue_by_currency: sortedCurrencyTotals(flow.revenue_by_currency),
|
|
709
|
+
paid_revenue_by_currency: sortedCurrencyTotals(flow.paid_revenue_by_currency),
|
|
710
|
+
sale_fee_by_currency: sortedCurrencyTotals(flow.sale_fee_by_currency),
|
|
711
|
+
cancelled_orders: flow.cancelled_orders,
|
|
712
|
+
cancelled_rate: safeRate(flow.cancelled_orders, flow.orders),
|
|
713
|
+
fee_rate_by_currency: Object.fromEntries(
|
|
714
|
+
Object.keys(flow.revenue_by_currency).sort((left, right) => left.localeCompare(right)).map((currencyId) => [currencyId, safeRate(flow.sale_fee_by_currency[currencyId] ?? 0, flow.revenue_by_currency[currencyId] ?? 0)])
|
|
715
|
+
)
|
|
716
|
+
}));
|
|
717
|
+
const skuUnitEconomics = Array.from(skuEconomics.values()).sort((left, right) => revenueSum(right.revenue_by_currency) - revenueSum(left.revenue_by_currency) || left.seller_sku.localeCompare(right.seller_sku)).slice(0, TOP_LIMIT).map((sku) => ({
|
|
718
|
+
seller_sku: sku.seller_sku,
|
|
719
|
+
item_id: sku.item_id,
|
|
720
|
+
title: sku.title,
|
|
721
|
+
orders: sku.orders,
|
|
722
|
+
units: sku.units,
|
|
723
|
+
revenue_by_currency: sortedCurrencyTotals(sku.revenue_by_currency),
|
|
724
|
+
paid_revenue_by_currency: sortedCurrencyTotals(sku.paid_revenue_by_currency),
|
|
725
|
+
sale_fee_by_currency: sortedCurrencyTotals(sku.sale_fee_by_currency),
|
|
726
|
+
refunded_amount_by_currency: sortedCurrencyTotals(sku.refunded_amount_by_currency),
|
|
727
|
+
avg_unit_price_by_currency: Object.fromEntries(
|
|
728
|
+
Object.keys(sku.revenue_by_currency).sort((left, right) => left.localeCompare(right)).map((currencyId) => [currencyId, roundMoney((sku.revenue_by_currency[currencyId] ?? 0) / Math.max(1, sku.units))])
|
|
729
|
+
),
|
|
730
|
+
fee_rate_by_currency: Object.fromEntries(
|
|
731
|
+
Object.keys(sku.revenue_by_currency).sort((left, right) => left.localeCompare(right)).map((currencyId) => [currencyId, safeRate(sku.sale_fee_by_currency[currencyId] ?? 0, sku.revenue_by_currency[currencyId] ?? 0)])
|
|
732
|
+
),
|
|
733
|
+
cancelled_orders: sku.cancelled_orders,
|
|
734
|
+
cancelled_rate: safeRate(sku.cancelled_orders, sku.orders),
|
|
735
|
+
refunded_orders: sku.refunded_orders,
|
|
736
|
+
refund_rate: safeRate(sku.refunded_orders, sku.orders)
|
|
737
|
+
}));
|
|
738
|
+
const feeDiagnostics = {
|
|
739
|
+
sale_fee_assumption: "order_items.sale_fee is used as returned by MercadoLibre and is not multiplied by quantity yet.",
|
|
740
|
+
multi_unit_item_lines: multiUnitItemLines,
|
|
741
|
+
multi_unit_sale_fee_observations: multiUnitSaleFeeObservations,
|
|
742
|
+
needs_sale_fee_granularity_validation: true
|
|
743
|
+
};
|
|
433
744
|
return {
|
|
434
745
|
overview: {
|
|
435
746
|
orders_total: ordersTotal,
|
|
@@ -445,6 +756,13 @@ function buildCalculations(orders, metadata, failedPages) {
|
|
|
445
756
|
orders_with_return_request: ordersWithReturnRequest
|
|
446
757
|
},
|
|
447
758
|
financial_by_currency: financialByCurrency,
|
|
759
|
+
revenue_quality: revenueQuality,
|
|
760
|
+
refunds_cancellations: refundsCancellations,
|
|
761
|
+
shipping_and_discounts: shippingAndDiscounts,
|
|
762
|
+
daily_trend: dailyTrend,
|
|
763
|
+
flow_performance: flowPerformance,
|
|
764
|
+
sku_unit_economics: skuUnitEconomics,
|
|
765
|
+
fee_diagnostics: feeDiagnostics,
|
|
448
766
|
order_mix: {
|
|
449
767
|
status_breakdown: topBreakdownFromMap(statusCounts, ordersTotal),
|
|
450
768
|
tags_breakdown: topBreakdownFromMap(tagCounts, ordersTotal),
|
|
@@ -518,26 +836,30 @@ function buildCalculations(orders, metadata, failedPages) {
|
|
|
518
836
|
};
|
|
519
837
|
}
|
|
520
838
|
function buildOrdersSummaryMetadata(params) {
|
|
521
|
-
const
|
|
522
|
-
const
|
|
839
|
+
const requestedWindowEndOffset = params.effectiveOffset + params.effectiveLimit * params.pagesRequested;
|
|
840
|
+
const nextOffset = params.responseMode === "orders_chunk" ? requestedWindowEndOffset : params.effectiveOffset + params.returned;
|
|
841
|
+
const hasMoreOrders = nextOffset < params.total;
|
|
842
|
+
const universeFullyFetched = params.responseMode === "calculations" && params.pagesFailed === 0 && params.returned >= params.total;
|
|
523
843
|
let continuation = "No more pages for this query.";
|
|
524
|
-
if (params.
|
|
525
|
-
continuation = `Reenviar la misma tool con offset=${params.effectiveOffset + params.effectiveLimit} para continuar.`;
|
|
526
|
-
} else if (params.pagesFailed > 0) {
|
|
844
|
+
if (params.pagesFailed > 0) {
|
|
527
845
|
const offsets = params.failedOffsets.slice(0, 5).join(", ");
|
|
528
846
|
const moreOffsets = params.failedOffsets.length > 5 ? ` y ${params.failedOffsets.length - 5} offsets adicionales` : "";
|
|
529
|
-
continuation = `Se recuperaron paginas parciales. Reintentar la misma tool con
|
|
530
|
-
} else if (
|
|
531
|
-
continuation = "
|
|
847
|
+
continuation = params.responseMode === "orders_chunk" ? `Se recuperaron paginas parciales. Reintentar primero la misma tool con responseMode="orders_chunk", offset=${params.failedOffsets[0]} y limit=${params.effectiveLimit}. Offsets fallidos: ${offsets}${moreOffsets}. Luego continuar con offset=${nextOffset} si hace falta.` : `Calculos parciales por paginas fallidas. Reintentar la misma tool con responseMode="calculations". Offsets fallidos: ${offsets}${moreOffsets}.`;
|
|
848
|
+
} else if (params.responseMode === "orders_chunk" && hasMoreOrders) {
|
|
849
|
+
continuation = `Para continuar trayendo ordenes, volver a invocar meli_get_orders_summary con los mismos filtros, responseMode="orders_chunk", offset=${nextOffset} y limit=${params.effectiveLimit}. Repetir hasta has_more=false.`;
|
|
850
|
+
} else if (params.responseMode === "calculations") {
|
|
851
|
+
continuation = 'Calculos completos. Si el usuario pide detalle de ordenes, invocar la misma tool con responseMode="orders_chunk", offset=0 y limit=50.';
|
|
532
852
|
}
|
|
533
853
|
return {
|
|
534
854
|
total: params.total,
|
|
535
855
|
limit: params.effectiveLimit,
|
|
536
856
|
offset: params.effectiveOffset,
|
|
537
857
|
returned: params.returned,
|
|
538
|
-
has_more: params.
|
|
858
|
+
has_more: params.responseMode === "orders_chunk" ? hasMoreOrders || params.pagesFailed > 0 : params.pagesFailed > 0,
|
|
859
|
+
next_offset: params.responseMode === "orders_chunk" && hasMoreOrders ? nextOffset : void 0,
|
|
539
860
|
continuation,
|
|
540
|
-
fetch_mode: params.
|
|
861
|
+
fetch_mode: params.responseMode === "orders_chunk" ? "orders_chunk" : "full_calculations",
|
|
862
|
+
pages_per_call_limit: params.responseMode === "orders_chunk" ? ORDER_DETAIL_MAX_PAGES_PER_CALL : void 0,
|
|
541
863
|
pages_requested: params.pagesRequested,
|
|
542
864
|
pages_succeeded: params.pagesSucceeded,
|
|
543
865
|
pages_failed: params.pagesFailed,
|
|
@@ -617,8 +939,9 @@ const meliGetOrdersSummarySchema = z.object({
|
|
|
617
939
|
status: z.string().trim().min(1).optional().describe("Optional MercadoLibre order status filter."),
|
|
618
940
|
q: z.string().trim().min(1).optional().describe("Optional free-text search query supported by MercadoLibre orders search."),
|
|
619
941
|
sort: z.string().trim().min(1).optional().describe("Optional MercadoLibre sort expression."),
|
|
620
|
-
|
|
621
|
-
|
|
942
|
+
responseMode: z.enum(["calculations", "orders_chunk"]).optional().default("calculations").describe("Response mode. calculations (default) fetches all matching orders internally and returns only aggregate KPIs. orders_chunk returns compact order rows for up to 15 MercadoLibre pages and includes continuation metadata."),
|
|
943
|
+
offset: z.number().int().min(0).max(5e3).optional().describe("Starting offset for responseMode=orders_chunk. Use metadata.next_offset to continue."),
|
|
944
|
+
limit: z.number().int().min(1).max(50).optional().describe("MercadoLibre page size for responseMode=orders_chunk (1-50). Defaults to 50.")
|
|
622
945
|
});
|
|
623
946
|
async function meliGetOrdersSummaryHandler(params) {
|
|
624
947
|
try {
|
|
@@ -627,6 +950,9 @@ async function meliGetOrdersSummaryHandler(params) {
|
|
|
627
950
|
return profileResolution.response;
|
|
628
951
|
}
|
|
629
952
|
const profileId = profileResolution.value.profileId;
|
|
953
|
+
const responseMode = params.responseMode ?? "calculations";
|
|
954
|
+
const requestedLimit = responseMode === "orders_chunk" ? params.limit ?? DEFAULT_ORDER_DETAIL_PAGE_LIMIT : DEFAULT_ORDER_DETAIL_PAGE_LIMIT;
|
|
955
|
+
const requestedOffset = responseMode === "orders_chunk" ? params.offset ?? 0 : 0;
|
|
630
956
|
const response = await searchMercadoLibreOrders(profileId, {
|
|
631
957
|
seller: "",
|
|
632
958
|
from: params.startDate,
|
|
@@ -634,23 +960,29 @@ async function meliGetOrdersSummaryHandler(params) {
|
|
|
634
960
|
status: params.status,
|
|
635
961
|
q: params.q,
|
|
636
962
|
sort: params.sort,
|
|
637
|
-
offset:
|
|
638
|
-
limit:
|
|
963
|
+
offset: requestedOffset,
|
|
964
|
+
limit: requestedLimit
|
|
639
965
|
});
|
|
640
966
|
const paging = normalizeMercadoLibrePaging(asRecord(response.paging));
|
|
641
967
|
const baseOrders = asArray(response.results);
|
|
642
968
|
const metricsByCurrency = {};
|
|
643
|
-
const
|
|
644
|
-
const
|
|
645
|
-
const
|
|
969
|
+
const effectiveLimit = paging.limit || requestedLimit || baseOrders.length;
|
|
970
|
+
const effectiveOffset = paging.offset || requestedOffset || 0;
|
|
971
|
+
const maxPagesForResponse = responseMode === "orders_chunk" ? ORDER_DETAIL_MAX_PAGES_PER_CALL : Infinity;
|
|
646
972
|
const allOrders = [...baseOrders];
|
|
647
973
|
const failedPages = [];
|
|
648
974
|
let pagesRequested = 1;
|
|
649
975
|
let pagesSucceeded = 1;
|
|
650
|
-
if (
|
|
976
|
+
if (effectiveLimit > 0 && paging.total > baseOrders.length) {
|
|
651
977
|
const nextOffsets = [];
|
|
978
|
+
const maxPagesRemaining = Math.max(0, maxPagesForResponse - 1);
|
|
979
|
+
let pagesAdded = 0;
|
|
652
980
|
for (let nextOffset = effectiveOffset + effectiveLimit; nextOffset < paging.total; nextOffset += effectiveLimit) {
|
|
981
|
+
if (responseMode === "orders_chunk" && pagesAdded >= maxPagesRemaining) {
|
|
982
|
+
break;
|
|
983
|
+
}
|
|
653
984
|
nextOffsets.push(nextOffset);
|
|
985
|
+
pagesAdded += 1;
|
|
654
986
|
}
|
|
655
987
|
pagesRequested += nextOffsets.length;
|
|
656
988
|
if (nextOffsets.length > 0) {
|
|
@@ -689,27 +1021,33 @@ async function meliGetOrdersSummaryHandler(params) {
|
|
|
689
1021
|
});
|
|
690
1022
|
}
|
|
691
1023
|
}
|
|
692
|
-
const compactOrders = allOrders.map((order) => formatCompactOrder(order, metricsByCurrency));
|
|
1024
|
+
const compactOrders = responseMode === "orders_chunk" ? allOrders.map((order) => formatCompactOrder(order, metricsByCurrency)) : [];
|
|
1025
|
+
if (responseMode === "calculations") {
|
|
1026
|
+
for (const order of allOrders) {
|
|
1027
|
+
formatCompactOrder(order, metricsByCurrency);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
693
1030
|
for (const metrics of Object.values(metricsByCurrency)) {
|
|
694
1031
|
metrics.revenue = roundMoney(metrics.revenue);
|
|
695
1032
|
metrics.avg_order_value = roundMoney(metrics.avg_order_value);
|
|
696
1033
|
}
|
|
1034
|
+
const returned = responseMode === "orders_chunk" ? compactOrders.length : allOrders.length;
|
|
697
1035
|
const metadata = buildOrdersSummaryMetadata({
|
|
698
1036
|
total: paging.total,
|
|
699
1037
|
effectiveLimit,
|
|
700
1038
|
effectiveOffset,
|
|
701
|
-
returned
|
|
702
|
-
|
|
1039
|
+
returned,
|
|
1040
|
+
responseMode,
|
|
703
1041
|
pagesRequested,
|
|
704
1042
|
pagesSucceeded,
|
|
705
1043
|
pagesFailed: failedPages.length,
|
|
706
1044
|
failedOffsets: failedPages.map((failure) => failure.offset)
|
|
707
1045
|
});
|
|
708
|
-
const calculations = buildCalculations(allOrders, metadata, failedPages);
|
|
1046
|
+
const calculations = responseMode === "calculations" ? buildCalculations(allOrders, metadata, failedPages) : void 0;
|
|
709
1047
|
return object(
|
|
710
1048
|
stripNulls({
|
|
711
1049
|
profile_id: profileId,
|
|
712
|
-
metadata:
|
|
1050
|
+
metadata: responseMode === "orders_chunk" ? {
|
|
713
1051
|
...buildMercadoLibrePaginationMetadata({
|
|
714
1052
|
total: paging.total,
|
|
715
1053
|
limit: effectiveLimit,
|
|
@@ -720,10 +1058,10 @@ async function meliGetOrdersSummaryHandler(params) {
|
|
|
720
1058
|
...metadata
|
|
721
1059
|
} : metadata,
|
|
722
1060
|
metrics_by_currency: metricsByCurrency,
|
|
723
|
-
orders_schema: ordersSchema,
|
|
724
|
-
orders: compactOrders,
|
|
1061
|
+
orders_schema: responseMode === "orders_chunk" ? ordersSchema : void 0,
|
|
1062
|
+
orders: responseMode === "orders_chunk" ? compactOrders : void 0,
|
|
725
1063
|
calculations,
|
|
726
|
-
failed_pages: failedPages
|
|
1064
|
+
failed_pages: failedPages.length > 0 ? failedPages : void 0
|
|
727
1065
|
})
|
|
728
1066
|
);
|
|
729
1067
|
} catch (err) {
|