@yoryoboy/bi-mcp 1.5.2 → 1.6.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 (182) hide show
  1. package/README.md +87 -87
  2. package/bin/bi-mcp.js +9 -9
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/mcp-use.json +2 -2
  7. package/dist/public/icon.svg +6 -6
  8. package/dist/scripts/_helpers.js.map +1 -1
  9. package/dist/scripts/admin-profile-delete.js.map +1 -1
  10. package/dist/scripts/admin-profile-list.js.map +1 -1
  11. package/dist/scripts/admin-profile-upsert.js.map +1 -1
  12. package/dist/scripts/admin-vtex-list.js.map +1 -1
  13. package/dist/scripts/admin-vtex-upsert.js.map +1 -1
  14. package/dist/scripts/admin-vtex-validate.js.map +1 -1
  15. package/dist/scripts/decrypt-secret.js +36 -0
  16. package/dist/scripts/decrypt-secret.js.map +7 -0
  17. package/dist/scripts/run-migrations.js.map +1 -1
  18. package/dist/scripts/test-db-connection.js.map +1 -1
  19. package/dist/src/analytics/ga4-channel-groups.js.map +1 -1
  20. package/dist/src/analytics/ga4-report-utils.js.map +1 -1
  21. package/dist/src/config/benchmarks.js.map +1 -1
  22. package/dist/src/config/google-store.js.map +1 -1
  23. package/dist/src/config/google.js.map +1 -1
  24. package/dist/src/config/mercadolibre-profile-store.js.map +1 -1
  25. package/dist/src/config/mercadolibre.js.map +1 -1
  26. package/dist/src/config/meta.js.map +1 -1
  27. package/dist/src/config/profile-store.js.map +1 -1
  28. package/dist/src/config/vtex-crypto.js.map +1 -1
  29. package/dist/src/config/vtex-profile-store.js.map +1 -1
  30. package/dist/src/config/vtex.js.map +1 -1
  31. package/dist/src/db/client.js.map +1 -1
  32. package/dist/src/meta/meta-utils.js.map +1 -1
  33. package/dist/src/prompts/reporte-ventas.js.map +1 -1
  34. package/dist/src/services/analytics/ga4-client.js.map +1 -1
  35. package/dist/src/services/analytics/oauth.js.map +1 -1
  36. package/dist/src/services/google-ads/google-ads-client.js.map +1 -1
  37. package/dist/src/services/mercadolibre/mercadolibre-api.js.map +1 -1
  38. package/dist/src/services/mercadolibre/mercadolibre-items.js.map +1 -1
  39. package/dist/src/services/mercadolibre/mercadolibre-orders.js +19 -5
  40. package/dist/src/services/mercadolibre/mercadolibre-orders.js.map +2 -2
  41. package/dist/src/services/mercadolibre/mercadolibre-questions.js.map +1 -1
  42. package/dist/src/services/meta/meta-ads.js.map +1 -1
  43. package/dist/src/services/meta/meta-api.js.map +1 -1
  44. package/dist/src/services/search-console/search-console-client.js.map +1 -1
  45. package/dist/src/services/search-console/search-console-utils.js.map +1 -1
  46. package/dist/src/services/vtex/vtex-api.js.map +1 -1
  47. package/dist/src/services/vtex/vtex-catalog-write.js.map +1 -1
  48. package/dist/src/services/vtex/vtex-catalog.js.map +1 -1
  49. package/dist/src/services/vtex/vtex-logistics.js.map +1 -1
  50. package/dist/src/services/vtex/vtex-orders-write.js.map +1 -1
  51. package/dist/src/services/vtex/vtex-orders.js.map +1 -1
  52. package/dist/src/services/vtex/vtex-pricing-write.js.map +1 -1
  53. package/dist/src/services/vtex/vtex-pricing.js.map +1 -1
  54. package/dist/src/services/vtex/vtex-write.js.map +1 -1
  55. package/dist/src/tools/analytics/attribution-gaps.js.map +1 -1
  56. package/dist/src/tools/analytics/channel-mix.js.map +1 -1
  57. package/dist/src/tools/analytics/ecommerce-tracking-health.js.map +1 -1
  58. package/dist/src/tools/analytics/engagement-overview.js.map +1 -1
  59. package/dist/src/tools/analytics/index.js.map +1 -1
  60. package/dist/src/tools/analytics/list-accessible-properties.js.map +1 -1
  61. package/dist/src/tools/analytics/property-info.js.map +1 -1
  62. package/dist/src/tools/analytics/revenue-by-channel.js.map +1 -1
  63. package/dist/src/tools/analytics/revenue-overview.js.map +1 -1
  64. package/dist/src/tools/analytics/revenue-trend.js.map +1 -1
  65. package/dist/src/tools/analytics/source-medium-breakdown.js.map +1 -1
  66. package/dist/src/tools/analytics/top-landing-pages.js.map +1 -1
  67. package/dist/src/tools/config/check-database-connection.js.map +1 -1
  68. package/dist/src/tools/config/index.js.map +1 -1
  69. package/dist/src/tools/config/list-profiles.js.map +1 -1
  70. package/dist/src/tools/google-ads/account-overview.js.map +1 -1
  71. package/dist/src/tools/google-ads/account-risks.js.map +1 -1
  72. package/dist/src/tools/google-ads/break-even-analysis.js.map +1 -1
  73. package/dist/src/tools/google-ads/campaign-performance.js.map +1 -1
  74. package/dist/src/tools/google-ads/channel-mix.js.map +1 -1
  75. package/dist/src/tools/google-ads/compare-accounts.js.map +1 -1
  76. package/dist/src/tools/google-ads/customer-clients.js.map +1 -1
  77. package/dist/src/tools/google-ads/customer-info.js.map +1 -1
  78. package/dist/src/tools/google-ads/index.js.map +1 -1
  79. package/dist/src/tools/google-ads/scaling-health.js.map +1 -1
  80. package/dist/src/tools/google-ads/search-terms-summary.js.map +1 -1
  81. package/dist/src/tools/google-ads/time-series.js.map +1 -1
  82. package/dist/src/tools/index.js.map +1 -1
  83. package/dist/src/tools/mercadolibre/answer-question.js.map +1 -1
  84. package/dist/src/tools/mercadolibre/create-item.js.map +1 -1
  85. package/dist/src/tools/mercadolibre/estimate-listing-fee.js.map +1 -1
  86. package/dist/src/tools/mercadolibre/get-account-context.js.map +1 -1
  87. package/dist/src/tools/mercadolibre/get-category-requirements.js.map +1 -1
  88. package/dist/src/tools/mercadolibre/get-item-details.js.map +1 -1
  89. package/dist/src/tools/mercadolibre/get-item-visits.js.map +1 -1
  90. package/dist/src/tools/mercadolibre/get-listing-quality.js.map +1 -1
  91. package/dist/src/tools/mercadolibre/get-order-details.js.map +1 -1
  92. package/dist/src/tools/mercadolibre/get-orders-summary.js +670 -38
  93. package/dist/src/tools/mercadolibre/get-orders-summary.js.map +2 -2
  94. package/dist/src/tools/mercadolibre/get-sales-by-item.js.map +1 -1
  95. package/dist/src/tools/mercadolibre/get-sales-trend.js.map +1 -1
  96. package/dist/src/tools/mercadolibre/get-shipping-summary.js.map +1 -1
  97. package/dist/src/tools/mercadolibre/get-store-performance.js.map +1 -1
  98. package/dist/src/tools/mercadolibre/helpers.js +13 -0
  99. package/dist/src/tools/mercadolibre/helpers.js.map +2 -2
  100. package/dist/src/tools/mercadolibre/index.js.map +1 -1
  101. package/dist/src/tools/mercadolibre/pause-or-reactivate-item.js.map +1 -1
  102. package/dist/src/tools/mercadolibre/predict-category.js.map +1 -1
  103. package/dist/src/tools/mercadolibre/profile-resolution.js.map +1 -1
  104. package/dist/src/tools/mercadolibre/search-items.js.map +1 -1
  105. package/dist/src/tools/mercadolibre/search-questions.js.map +1 -1
  106. package/dist/src/tools/mercadolibre/update-item-basic-fields.js.map +1 -1
  107. package/dist/src/tools/mercadolibre/update-item-description.js.map +1 -1
  108. package/dist/src/tools/mercadolibre/update-item-pictures.js.map +1 -1
  109. package/dist/src/tools/mercadolibre/validate-connection.js.map +1 -1
  110. package/dist/src/tools/mercadolibre/write-helpers.js.map +1 -1
  111. package/dist/src/tools/meta/account-overview.js.map +1 -1
  112. package/dist/src/tools/meta/ad-account-info.js.map +1 -1
  113. package/dist/src/tools/meta/ads-performance.js.map +1 -1
  114. package/dist/src/tools/meta/campaign-performance.js.map +1 -1
  115. package/dist/src/tools/meta/index.js.map +1 -1
  116. package/dist/src/tools/meta/list-accessible-ad-accounts.js.map +1 -1
  117. package/dist/src/tools/meta/list-accessible-businesses.js.map +1 -1
  118. package/dist/src/tools/meta/placement-mix.js.map +1 -1
  119. package/dist/src/tools/meta/time-series.js.map +1 -1
  120. package/dist/src/tools/search-console/country-breakdown.js.map +1 -1
  121. package/dist/src/tools/search-console/device-breakdown.js.map +1 -1
  122. package/dist/src/tools/search-console/high-impression-low-click-queries.js.map +1 -1
  123. package/dist/src/tools/search-console/index.js.map +1 -1
  124. package/dist/src/tools/search-console/list-accessible-sites.js.map +1 -1
  125. package/dist/src/tools/search-console/low-ctr-opportunities.js.map +1 -1
  126. package/dist/src/tools/search-console/page-performance.js.map +1 -1
  127. package/dist/src/tools/search-console/product-demand-low-capture-queries.js.map +1 -1
  128. package/dist/src/tools/search-console/query-page-matrix.js.map +1 -1
  129. package/dist/src/tools/search-console/query-performance.js.map +1 -1
  130. package/dist/src/tools/search-console/quick-win-opportunities.js.map +1 -1
  131. package/dist/src/tools/search-console/rising-non-brand-queries.js.map +1 -1
  132. package/dist/src/tools/search-console/search-performance.js.map +1 -1
  133. package/dist/src/tools/search-console/site-context.js.map +1 -1
  134. package/dist/src/tools/search-console/visibility-declines.js.map +1 -1
  135. package/dist/src/tools/vtex/activate-sku.js.map +1 -1
  136. package/dist/src/tools/vtex/add-order-tracking.js.map +1 -1
  137. package/dist/src/tools/vtex/associate-specification.js.map +1 -1
  138. package/dist/src/tools/vtex/attach-catalog-image.js.map +1 -1
  139. package/dist/src/tools/vtex/cancel-order.js.map +1 -1
  140. package/dist/src/tools/vtex/computed-price.js.map +1 -1
  141. package/dist/src/tools/vtex/create-brand.js.map +1 -1
  142. package/dist/src/tools/vtex/create-category.js.map +1 -1
  143. package/dist/src/tools/vtex/create-product-with-sku.js.map +1 -1
  144. package/dist/src/tools/vtex/create-product.js.map +1 -1
  145. package/dist/src/tools/vtex/create-sku.js.map +1 -1
  146. package/dist/src/tools/vtex/create-specification-value.js.map +1 -1
  147. package/dist/src/tools/vtex/create-specification.js.map +1 -1
  148. package/dist/src/tools/vtex/deactivate-sku.js.map +1 -1
  149. package/dist/src/tools/vtex/delete-fixed-price.js.map +1 -1
  150. package/dist/src/tools/vtex/index.js.map +1 -1
  151. package/dist/src/tools/vtex/inventory-check.js.map +1 -1
  152. package/dist/src/tools/vtex/invoice-order.js.map +1 -1
  153. package/dist/src/tools/vtex/order-details.js.map +1 -1
  154. package/dist/src/tools/vtex/orders-summary.js.map +1 -1
  155. package/dist/src/tools/vtex/product-offers.js.map +1 -1
  156. package/dist/src/tools/vtex/profile-resolution.js.map +1 -1
  157. package/dist/src/tools/vtex/sku-offers.js.map +1 -1
  158. package/dist/src/tools/vtex/sku-price.js.map +1 -1
  159. package/dist/src/tools/vtex/toggle-unlimited-quantity.js.map +1 -1
  160. package/dist/src/tools/vtex/update-inventory.js.map +1 -1
  161. package/dist/src/tools/vtex/update-lead-time.js.map +1 -1
  162. package/dist/src/tools/vtex/update-product-basic-fields.js.map +1 -1
  163. package/dist/src/tools/vtex/update-sku-basic-fields.js.map +1 -1
  164. package/dist/src/tools/vtex/update-sku-price.js.map +1 -1
  165. package/dist/src/tools/vtex/upsert-fixed-price.js.map +1 -1
  166. package/dist/src/tools/vtex/warehouse-inventory.js.map +1 -1
  167. package/dist/src/tools/vtex/write-helpers.js.map +1 -1
  168. package/dist/src/utils/case-conversion.js.map +1 -1
  169. package/dist/src/utils/currency.js.map +1 -1
  170. package/dist/src/utils/format-order-details.js.map +1 -1
  171. package/dist/src/utils/google-ads.js.map +1 -1
  172. package/dist/src/utils/money.js.map +1 -1
  173. package/dist/src/utils/order-status.js.map +1 -1
  174. package/dist/src/utils/pagination.js.map +1 -1
  175. package/dist/src/utils/strip-payload.js.map +1 -1
  176. package/dist/src/utils/type-guards.js.map +1 -1
  177. package/package.json +4 -3
  178. package/public/icon.svg +6 -6
  179. package/dist/src/google-ads/report-utils.js +0 -78
  180. package/dist/src/google-ads/report-utils.js.map +0 -7
  181. package/dist/src/search-console/search-console-utils.js +0 -275
  182. package/dist/src/search-console/search-console-utils.js.map +0 -7
@@ -5,7 +5,10 @@ import {
5
5
  formatMercadoLibreError,
6
6
  normalizeMercadoLibrePaging
7
7
  } from "../../services/mercadolibre/mercadolibre-api.js";
8
- import { searchMercadoLibreOrders } from "../../services/mercadolibre/mercadolibre-orders.js";
8
+ import {
9
+ searchMercadoLibreOrders,
10
+ searchMercadoLibreOrdersBatch
11
+ } from "../../services/mercadolibre/mercadolibre-orders.js";
9
12
  import { stripNulls } from "../../utils/strip-payload.js";
10
13
  import {
11
14
  asArray,
@@ -13,13 +16,600 @@ import {
13
16
  compactDateTime,
14
17
  currencyBucket,
15
18
  mercadoLibreDateRegex,
19
+ normalizeScalarString,
16
20
  normalizeString,
17
21
  roundMoney,
18
22
  toNumber
19
23
  } from "./helpers.js";
20
24
  import { resolveMercadoLibreProfileOrSelection } from "./profile-resolution.js";
21
25
  import { mercadolibreProfileIdSchemaField } from "./write-helpers.js";
22
- const ordersSchema = ["id", "date", "status", "total", "currency", "buyer", "shipping", "items"];
26
+ const PAGE_FETCH_CONCURRENCY = 4;
27
+ const PAGE_FETCH_MAX_RETRIES = 2;
28
+ const TOP_LIMIT = 10;
29
+ const ordersSchema = [
30
+ "id",
31
+ "date_created",
32
+ "status",
33
+ "total_amount",
34
+ "currency",
35
+ "buyer",
36
+ "shipping_status",
37
+ "items",
38
+ "date_closed",
39
+ "last_updated",
40
+ "paid_amount",
41
+ "buyer_id",
42
+ "seller",
43
+ "seller_id",
44
+ "shipping_id",
45
+ "pack_id",
46
+ "fulfilled",
47
+ "tags",
48
+ "channel",
49
+ "site",
50
+ "payment_id",
51
+ "payment_status",
52
+ "payment_status_detail",
53
+ "payment_method",
54
+ "payment_type",
55
+ "payment_total_paid",
56
+ "payment_transaction_amount",
57
+ "installments",
58
+ "coupon_amount",
59
+ "available_actions",
60
+ "feedback_buyer",
61
+ "feedback_seller",
62
+ "order_request_change",
63
+ "order_request_return",
64
+ "first_item_id",
65
+ "first_item_title",
66
+ "first_item_sku",
67
+ "first_item_quantity",
68
+ "first_item_unit_price",
69
+ "first_item_sale_fee",
70
+ "first_item_listing_type",
71
+ "variation_attributes",
72
+ "stock_node_id"
73
+ ];
74
+ function compactStringList(values, separator = " | ") {
75
+ return asArray(values).map((value) => normalizeScalarString(value).trim()).filter(Boolean).join(separator);
76
+ }
77
+ function compactNestedValue(value) {
78
+ if (value == null) {
79
+ return "";
80
+ }
81
+ const scalar = normalizeScalarString(value);
82
+ if (scalar) {
83
+ return scalar;
84
+ }
85
+ if (Array.isArray(value)) {
86
+ return value.map((entry) => compactNestedValue(entry)).filter(Boolean).join(" | ");
87
+ }
88
+ const record = asRecord(value);
89
+ return Object.entries(record).map(([key, entryValue]) => {
90
+ const normalized = compactNestedValue(entryValue);
91
+ return normalized ? `${key}:${normalized}` : "";
92
+ }).filter(Boolean).join(" | ");
93
+ }
94
+ function compactVariationAttributes(value) {
95
+ return asArray(value).map((attribute) => {
96
+ const name = normalizeString(attribute.name) || normalizeString(attribute.id);
97
+ const normalizedValue = normalizeString(attribute.value_name) || normalizeString(attribute.value_id) || normalizeScalarString(attribute.value);
98
+ if (!name || !normalizedValue) {
99
+ return "";
100
+ }
101
+ return `${name}: ${normalizedValue}`;
102
+ }).filter(Boolean).join(" | ");
103
+ }
104
+ function createMetricsBucket() {
105
+ return {
106
+ orders: 0,
107
+ revenue: 0,
108
+ avg_order_value: 0
109
+ };
110
+ }
111
+ function safeRate(numerator, denominator) {
112
+ if (!Number.isFinite(denominator) || denominator <= 0) {
113
+ return 0;
114
+ }
115
+ return Number((numerator / denominator).toFixed(4));
116
+ }
117
+ function topBreakdownFromMap(counts, totalOrders, limit = TOP_LIMIT) {
118
+ const sorted = Array.from(counts.entries()).filter(([, count]) => count > 0).sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]));
119
+ const entries = sorted.slice(0, limit).map(([key, orders]) => ({
120
+ key,
121
+ orders,
122
+ rate: safeRate(orders, totalOrders)
123
+ }));
124
+ const otherOrders = sorted.slice(limit).reduce((sum, [, count]) => sum + count, 0);
125
+ return stripNulls({
126
+ entries,
127
+ other_orders: otherOrders > 0 ? otherOrders : void 0,
128
+ other_rate: otherOrders > 0 ? safeRate(otherOrders, totalOrders) : void 0
129
+ });
130
+ }
131
+ function incrementCount(map, key) {
132
+ if (!key) {
133
+ return;
134
+ }
135
+ map.set(key, (map.get(key) ?? 0) + 1);
136
+ }
137
+ function incrementCurrency(map, currencyId, amount) {
138
+ map[currencyId] = roundMoney((map[currencyId] ?? 0) + amount);
139
+ }
140
+ function sortedCurrencyTotals(input) {
141
+ return Object.fromEntries(
142
+ Object.entries(input).filter(([, amount]) => amount !== 0).sort(([left], [right]) => left.localeCompare(right)).map(([currency, amount]) => [currency, roundMoney(amount)])
143
+ );
144
+ }
145
+ function compactFeedbackValue(value) {
146
+ const compact = compactNestedValue(value);
147
+ return compact.trim();
148
+ }
149
+ function getFirstPayment(order) {
150
+ return asRecord(asArray(order.payments)[0]);
151
+ }
152
+ function getOrderCurrency(order) {
153
+ return normalizeString(order.currency_id, "UNKNOWN");
154
+ }
155
+ function buildCalculations(orders, metadata, failedPages) {
156
+ const ordersTotal = orders.length;
157
+ const statusCounts = /* @__PURE__ */ new Map();
158
+ const tagCounts = /* @__PURE__ */ new Map();
159
+ const channelCounts = /* @__PURE__ */ new Map();
160
+ const siteCounts = /* @__PURE__ */ new Map();
161
+ const paymentStatusCounts = /* @__PURE__ */ new Map();
162
+ const paymentStatusDetailCounts = /* @__PURE__ */ new Map();
163
+ const paymentMethodCounts = /* @__PURE__ */ new Map();
164
+ const paymentTypeCounts = /* @__PURE__ */ new Map();
165
+ const installmentsCounts = /* @__PURE__ */ new Map();
166
+ const stockNodeCounts = /* @__PURE__ */ new Map();
167
+ const listingTypeCounts = /* @__PURE__ */ new Map();
168
+ const variationValueCounts = /* @__PURE__ */ new Map();
169
+ const buyers = /* @__PURE__ */ new Map();
170
+ const items = /* @__PURE__ */ new Map();
171
+ const variations = /* @__PURE__ */ new Map();
172
+ const skus = /* @__PURE__ */ new Map();
173
+ let unitsTotal = 0;
174
+ let fulfilledOrders = 0;
175
+ let deliveredOrders = 0;
176
+ let packOrders = 0;
177
+ let discountedOrders = 0;
178
+ let catalogOrders = 0;
179
+ let ordersWithFeedback = 0;
180
+ let ordersWithChangeRequest = 0;
181
+ let ordersWithReturnRequest = 0;
182
+ let feedbackBuyerCount = 0;
183
+ let feedbackSellerCount = 0;
184
+ let ordersWithShipping = 0;
185
+ let singleItemOrders = 0;
186
+ let multiItemOrders = 0;
187
+ let ordersWithoutPaymentData = 0;
188
+ let paymentApprovedOrders = 0;
189
+ let itemLineCount = 0;
190
+ let ordersWithAnyPostSaleIssue = 0;
191
+ const itemsSeen = /* @__PURE__ */ new Set();
192
+ const buyerSeen = /* @__PURE__ */ new Set();
193
+ const financialRaw = {};
194
+ for (const order of orders) {
195
+ const currencyId = getOrderCurrency(order);
196
+ const totalAmount = toNumber(order.total_amount);
197
+ const paidAmount = toNumber(order.paid_amount);
198
+ const couponAmount = toNumber(asRecord(order.coupon).amount);
199
+ const payment = getFirstPayment(order);
200
+ const orderItems = asArray(order.order_items);
201
+ const buyer = asRecord(order.buyer);
202
+ const seller = asRecord(order.seller);
203
+ const feedback = asRecord(order.feedback);
204
+ const orderRequest = asRecord(order.order_request);
205
+ const shipping = asRecord(order.shipping);
206
+ const context = asRecord(order.context);
207
+ const paymentStatus = normalizeString(payment.status);
208
+ const paymentStatusDetail = normalizeString(payment.status_detail);
209
+ const paymentMethod = normalizeString(payment.payment_method_id);
210
+ const paymentType = normalizeString(payment.payment_type);
211
+ const installments = normalizeScalarString(payment.installments);
212
+ const tags = asArray(order.tags).map((tag) => normalizeScalarString(tag)).filter(Boolean);
213
+ const orderItemDistinctIds = /* @__PURE__ */ new Set();
214
+ financialRaw[currencyId] = financialRaw[currencyId] ?? {
215
+ orders: 0,
216
+ units: 0,
217
+ distinctItems: 0,
218
+ revenue: 0,
219
+ paidAmount: 0,
220
+ paymentTotalPaidAmount: 0,
221
+ transactionAmount: 0,
222
+ couponAmount: 0,
223
+ saleFee: 0
224
+ };
225
+ financialRaw[currencyId].orders += 1;
226
+ financialRaw[currencyId].revenue += totalAmount;
227
+ financialRaw[currencyId].paidAmount += paidAmount;
228
+ financialRaw[currencyId].paymentTotalPaidAmount += toNumber(payment.total_paid_amount);
229
+ financialRaw[currencyId].transactionAmount += toNumber(payment.transaction_amount);
230
+ financialRaw[currencyId].couponAmount += couponAmount;
231
+ incrementCount(statusCounts, normalizeString(order.status));
232
+ incrementCount(channelCounts, normalizeString(context.channel));
233
+ incrementCount(siteCounts, normalizeString(context.site));
234
+ if (paymentStatus) {
235
+ incrementCount(paymentStatusCounts, paymentStatus);
236
+ } else {
237
+ ordersWithoutPaymentData += 1;
238
+ }
239
+ incrementCount(paymentStatusDetailCounts, paymentStatusDetail);
240
+ incrementCount(paymentMethodCounts, paymentMethod);
241
+ incrementCount(paymentTypeCounts, paymentType);
242
+ incrementCount(installmentsCounts, installments);
243
+ if (paymentStatus === "approved") {
244
+ paymentApprovedOrders += 1;
245
+ }
246
+ if (order.fulfilled === true) {
247
+ fulfilledOrders += 1;
248
+ }
249
+ if (normalizeScalarString(shipping.id)) {
250
+ ordersWithShipping += 1;
251
+ }
252
+ if (orderItems.length === 1) {
253
+ singleItemOrders += 1;
254
+ } else if (orderItems.length > 1) {
255
+ multiItemOrders += 1;
256
+ }
257
+ for (const tag of tags) {
258
+ incrementCount(tagCounts, tag);
259
+ }
260
+ if (tags.includes("delivered")) {
261
+ deliveredOrders += 1;
262
+ }
263
+ if (tags.includes("pack_order")) {
264
+ packOrders += 1;
265
+ }
266
+ if (tags.includes("order_has_discount")) {
267
+ discountedOrders += 1;
268
+ }
269
+ if (tags.includes("catalog")) {
270
+ catalogOrders += 1;
271
+ }
272
+ const feedbackBuyer = compactFeedbackValue(feedback.buyer);
273
+ const feedbackSeller = compactFeedbackValue(feedback.seller);
274
+ const hasFeedback = Boolean(feedbackBuyer || feedbackSeller);
275
+ if (hasFeedback) {
276
+ ordersWithFeedback += 1;
277
+ }
278
+ if (feedbackBuyer) {
279
+ feedbackBuyerCount += 1;
280
+ }
281
+ if (feedbackSeller) {
282
+ feedbackSellerCount += 1;
283
+ }
284
+ const hasChangeRequest = Boolean(compactFeedbackValue(orderRequest.change));
285
+ const hasReturnRequest = Boolean(compactFeedbackValue(orderRequest.return));
286
+ if (hasChangeRequest) {
287
+ ordersWithChangeRequest += 1;
288
+ }
289
+ if (hasReturnRequest) {
290
+ ordersWithReturnRequest += 1;
291
+ }
292
+ if (hasFeedback || hasChangeRequest || hasReturnRequest) {
293
+ ordersWithAnyPostSaleIssue += 1;
294
+ }
295
+ const buyerId = normalizeScalarString(buyer.id);
296
+ const buyerNickname = normalizeString(buyer.nickname);
297
+ if (buyerId || buyerNickname) {
298
+ const buyerKey = buyerId || buyerNickname;
299
+ buyerSeen.add(buyerKey);
300
+ const currentBuyer = buyers.get(buyerKey) ?? {
301
+ buyer_id: buyerId,
302
+ nickname: buyerNickname,
303
+ orders: 0,
304
+ revenue_by_currency: {}
305
+ };
306
+ currentBuyer.orders += 1;
307
+ incrementCurrency(currentBuyer.revenue_by_currency, currencyId, totalAmount);
308
+ buyers.set(buyerKey, currentBuyer);
309
+ }
310
+ for (const orderItem of orderItems) {
311
+ itemLineCount += 1;
312
+ const item = asRecord(orderItem.item);
313
+ const stock = asRecord(orderItem.stock);
314
+ const itemId = normalizeString(item.id);
315
+ const title = normalizeString(item.title);
316
+ const sellerSku = normalizeString(item.seller_sku);
317
+ const quantity = toNumber(orderItem.quantity);
318
+ const unitPrice = toNumber(orderItem.unit_price);
319
+ const saleFee = toNumber(orderItem.sale_fee);
320
+ const listingType = normalizeString(orderItem.listing_type_id);
321
+ const stockNode = normalizeString(stock.node_id);
322
+ const variationSummary = compactVariationAttributes(item.variation_attributes);
323
+ const itemKey = `${itemId}::${variationSummary}::${sellerSku}`;
324
+ const skuKey = sellerSku || itemId || title;
325
+ const variationKey = variationSummary || "NO_VARIATION";
326
+ unitsTotal += quantity;
327
+ financialRaw[currencyId].units += quantity;
328
+ financialRaw[currencyId].saleFee += saleFee;
329
+ if (itemId) {
330
+ itemsSeen.add(itemId);
331
+ orderItemDistinctIds.add(itemId);
332
+ }
333
+ incrementCount(listingTypeCounts, listingType);
334
+ incrementCount(stockNodeCounts, stockNode);
335
+ incrementCount(variationValueCounts, variationKey);
336
+ const itemAggregate = items.get(itemKey) ?? {
337
+ item_id: itemId,
338
+ title,
339
+ seller_sku: sellerSku,
340
+ variation_summary: variationSummary,
341
+ orders: 0,
342
+ units: 0,
343
+ revenue_by_currency: {},
344
+ sale_fee_by_currency: {}
345
+ };
346
+ itemAggregate.orders += 1;
347
+ itemAggregate.units += quantity;
348
+ incrementCurrency(itemAggregate.revenue_by_currency, currencyId, unitPrice * quantity);
349
+ incrementCurrency(itemAggregate.sale_fee_by_currency, currencyId, saleFee);
350
+ items.set(itemKey, itemAggregate);
351
+ const variationAggregate = variations.get(variationKey) ?? {
352
+ variation_summary: variationSummary,
353
+ orders: 0,
354
+ units: 0,
355
+ revenue_by_currency: {}
356
+ };
357
+ variationAggregate.orders += 1;
358
+ variationAggregate.units += quantity;
359
+ incrementCurrency(variationAggregate.revenue_by_currency, currencyId, unitPrice * quantity);
360
+ variations.set(variationKey, variationAggregate);
361
+ const skuAggregate = skus.get(skuKey) ?? {
362
+ seller_sku: sellerSku,
363
+ orders: 0,
364
+ units: 0,
365
+ revenue_by_currency: {}
366
+ };
367
+ skuAggregate.orders += 1;
368
+ skuAggregate.units += quantity;
369
+ incrementCurrency(skuAggregate.revenue_by_currency, currencyId, unitPrice * quantity);
370
+ skus.set(skuKey, skuAggregate);
371
+ }
372
+ financialRaw[currencyId].distinctItems += orderItemDistinctIds.size;
373
+ }
374
+ const financialByCurrency = {};
375
+ for (const [currencyId, raw] of Object.entries(financialRaw)) {
376
+ financialByCurrency[currencyId] = {
377
+ revenue_total: roundMoney(raw.revenue),
378
+ paid_amount_total: roundMoney(raw.paidAmount),
379
+ payment_total_paid_amount: roundMoney(raw.paymentTotalPaidAmount),
380
+ transaction_amount_total: roundMoney(raw.transactionAmount),
381
+ coupon_amount_total: roundMoney(raw.couponAmount),
382
+ sale_fee_total: roundMoney(raw.saleFee),
383
+ avg_order_value: roundMoney(raw.revenue / Math.max(1, raw.orders)),
384
+ avg_paid_amount_per_order: roundMoney(raw.paidAmount / Math.max(1, raw.orders)),
385
+ avg_units_per_order: roundMoney(raw.units / Math.max(1, raw.orders)),
386
+ avg_distinct_items_per_order: roundMoney(raw.distinctItems / Math.max(1, raw.orders)),
387
+ avg_sale_fee_per_order: roundMoney(raw.saleFee / Math.max(1, raw.orders)),
388
+ sale_fee_rate: safeRate(raw.saleFee, raw.revenue)
389
+ };
390
+ }
391
+ const topItemsByRevenue = Array.from(items.values()).sort((left, right) => {
392
+ const leftRevenue = Object.values(left.revenue_by_currency).reduce((sum, value) => sum + value, 0);
393
+ const rightRevenue = Object.values(right.revenue_by_currency).reduce((sum, value) => sum + value, 0);
394
+ return rightRevenue - leftRevenue || left.item_id.localeCompare(right.item_id);
395
+ }).slice(0, TOP_LIMIT).map((item) => ({
396
+ ...item,
397
+ revenue_by_currency: sortedCurrencyTotals(item.revenue_by_currency),
398
+ sale_fee_by_currency: sortedCurrencyTotals(item.sale_fee_by_currency)
399
+ }));
400
+ const topItemsByUnits = Array.from(items.values()).sort((left, right) => right.units - left.units || left.item_id.localeCompare(right.item_id)).slice(0, TOP_LIMIT).map((item) => ({
401
+ ...item,
402
+ revenue_by_currency: sortedCurrencyTotals(item.revenue_by_currency),
403
+ sale_fee_by_currency: sortedCurrencyTotals(item.sale_fee_by_currency)
404
+ }));
405
+ const topItemsByOrders = Array.from(items.values()).sort((left, right) => right.orders - left.orders || left.item_id.localeCompare(right.item_id)).slice(0, TOP_LIMIT).map((item) => ({
406
+ ...item,
407
+ revenue_by_currency: sortedCurrencyTotals(item.revenue_by_currency),
408
+ sale_fee_by_currency: sortedCurrencyTotals(item.sale_fee_by_currency)
409
+ }));
410
+ const topVariations = Array.from(variations.values()).filter((variation) => variation.variation_summary).sort((left, right) => right.units - left.units || left.variation_summary.localeCompare(right.variation_summary)).slice(0, TOP_LIMIT).map((variation) => ({
411
+ ...variation,
412
+ revenue_by_currency: sortedCurrencyTotals(variation.revenue_by_currency)
413
+ }));
414
+ const topSkus = Array.from(skus.values()).filter((sku) => sku.seller_sku).sort((left, right) => right.units - left.units || left.seller_sku.localeCompare(right.seller_sku)).slice(0, TOP_LIMIT).map((sku) => ({
415
+ ...sku,
416
+ revenue_by_currency: sortedCurrencyTotals(sku.revenue_by_currency)
417
+ }));
418
+ const topBuyersByRevenue = Array.from(buyers.values()).sort((left, right) => {
419
+ const leftRevenue = Object.values(left.revenue_by_currency).reduce((sum, value) => sum + value, 0);
420
+ const rightRevenue = Object.values(right.revenue_by_currency).reduce((sum, value) => sum + value, 0);
421
+ return rightRevenue - leftRevenue || left.buyer_id.localeCompare(right.buyer_id);
422
+ }).slice(0, TOP_LIMIT).map((buyer) => ({
423
+ ...buyer,
424
+ revenue_by_currency: sortedCurrencyTotals(buyer.revenue_by_currency)
425
+ }));
426
+ const topBuyersByOrders = Array.from(buyers.values()).sort((left, right) => right.orders - left.orders || left.buyer_id.localeCompare(right.buyer_id)).slice(0, TOP_LIMIT).map((buyer) => ({
427
+ ...buyer,
428
+ revenue_by_currency: sortedCurrencyTotals(buyer.revenue_by_currency)
429
+ }));
430
+ const buyersWithMultipleOrders = Array.from(buyers.values()).filter((buyer) => buyer.orders > 1).length;
431
+ 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
+ const topSingleValue = (counts) => Array.from(counts.entries()).filter(([key]) => key).sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]))[0]?.[0] ?? "";
433
+ return {
434
+ overview: {
435
+ orders_total: ordersTotal,
436
+ units_total: unitsTotal,
437
+ distinct_items_total: itemsSeen.size,
438
+ distinct_buyers_total: buyerSeen.size,
439
+ fulfilled_orders: fulfilledOrders,
440
+ delivered_orders: deliveredOrders,
441
+ pack_orders: packOrders,
442
+ orders_with_discount: discountedOrders,
443
+ orders_with_feedback: ordersWithFeedback,
444
+ orders_with_change_request: ordersWithChangeRequest,
445
+ orders_with_return_request: ordersWithReturnRequest
446
+ },
447
+ financial_by_currency: financialByCurrency,
448
+ order_mix: {
449
+ status_breakdown: topBreakdownFromMap(statusCounts, ordersTotal),
450
+ tags_breakdown: topBreakdownFromMap(tagCounts, ordersTotal),
451
+ channel_breakdown: topBreakdownFromMap(channelCounts, ordersTotal),
452
+ site_breakdown: topBreakdownFromMap(siteCounts, ordersTotal),
453
+ single_item_order_rate: safeRate(singleItemOrders, ordersTotal),
454
+ multi_item_order_rate: safeRate(multiItemOrders, ordersTotal),
455
+ fulfilled_rate: safeRate(fulfilledOrders, ordersTotal),
456
+ delivered_rate: safeRate(deliveredOrders, ordersTotal),
457
+ pack_order_rate: safeRate(packOrders, ordersTotal),
458
+ discounted_order_rate: safeRate(discountedOrders, ordersTotal),
459
+ catalog_order_rate: safeRate(catalogOrders, ordersTotal)
460
+ },
461
+ payment_mix: {
462
+ payment_status_breakdown: topBreakdownFromMap(paymentStatusCounts, ordersTotal),
463
+ payment_status_detail_breakdown: topBreakdownFromMap(paymentStatusDetailCounts, ordersTotal),
464
+ payment_method_breakdown: topBreakdownFromMap(paymentMethodCounts, ordersTotal),
465
+ payment_type_breakdown: topBreakdownFromMap(paymentTypeCounts, ordersTotal),
466
+ installments_breakdown: topBreakdownFromMap(installmentsCounts, ordersTotal),
467
+ payment_approved_rate: safeRate(paymentApprovedOrders, ordersTotal),
468
+ orders_without_payment_data: ordersWithoutPaymentData
469
+ },
470
+ fulfillment: {
471
+ orders_with_shipping: ordersWithShipping,
472
+ shipping_attached_rate: safeRate(ordersWithShipping, ordersTotal),
473
+ shipping_id_coverage: ordersWithShipping,
474
+ stock_node_breakdown: topBreakdownFromMap(stockNodeCounts, Math.max(1, itemLineCount)),
475
+ listing_type_breakdown: topBreakdownFromMap(listingTypeCounts, Math.max(1, itemLineCount))
476
+ },
477
+ post_sale_friction: {
478
+ orders_with_feedback_rate: safeRate(ordersWithFeedback, ordersTotal),
479
+ feedback_buyer_count: feedbackBuyerCount,
480
+ feedback_seller_count: feedbackSellerCount,
481
+ change_request_rate: safeRate(ordersWithChangeRequest, ordersTotal),
482
+ return_request_rate: safeRate(ordersWithReturnRequest, ordersTotal),
483
+ orders_with_any_post_sale_issue_rate: safeRate(ordersWithAnyPostSaleIssue, ordersTotal)
484
+ },
485
+ product_insights: {
486
+ top_items_by_revenue: topItemsByRevenue,
487
+ top_items_by_units: topItemsByUnits,
488
+ top_items_by_orders: topItemsByOrders,
489
+ top_variations: topVariations,
490
+ top_skus: topSkus,
491
+ top_listing_types: topBreakdownFromMap(listingTypeCounts, Math.max(1, itemLineCount)),
492
+ top_stock_nodes: topBreakdownFromMap(stockNodeCounts, Math.max(1, itemLineCount))
493
+ },
494
+ customer_insights: {
495
+ top_buyers_by_revenue: topBuyersByRevenue,
496
+ top_buyers_by_orders: topBuyersByOrders,
497
+ repeat_buyer_rate: safeRate(buyersWithMultipleOrders, buyerSeen.size),
498
+ buyers_with_multiple_orders: buyersWithMultipleOrders
499
+ },
500
+ operational_context: {
501
+ most_used_payment_method: topSingleValue(paymentMethodCounts),
502
+ most_used_listing_type: topSingleValue(listingTypeCounts),
503
+ most_used_stock_node: topSingleValue(stockNodeCounts),
504
+ most_used_channel: topSingleValue(channelCounts),
505
+ most_common_tag: topSingleValue(tagCounts),
506
+ most_common_variation_values: topVariationValues
507
+ },
508
+ coverage: {
509
+ fetch_mode: metadata.fetch_mode,
510
+ universe_fully_fetched: metadata.universe_fully_fetched,
511
+ orders_returned: metadata.returned,
512
+ pages_requested: metadata.pages_requested,
513
+ pages_succeeded: metadata.pages_succeeded,
514
+ pages_failed: metadata.pages_failed,
515
+ failed_pages_count: failedPages.length,
516
+ failed_pages: failedPages
517
+ }
518
+ };
519
+ }
520
+ function buildOrdersSummaryMetadata(params) {
521
+ const hasMoreForPage = params.effectiveOffset + params.returned < params.total;
522
+ const universeFullyFetched = !params.paginated && params.pagesFailed === 0 && params.returned >= params.total;
523
+ let continuation = "No more pages for this query.";
524
+ if (params.paginated && hasMoreForPage) {
525
+ continuation = `Reenviar la misma tool con offset=${params.effectiveOffset + params.effectiveLimit} para continuar.`;
526
+ } else if (params.pagesFailed > 0) {
527
+ const offsets = params.failedOffsets.slice(0, 5).join(", ");
528
+ const moreOffsets = params.failedOffsets.length > 5 ? ` y ${params.failedOffsets.length - 5} offsets adicionales` : "";
529
+ continuation = `Se recuperaron paginas parciales. Reintentar la misma tool con los offsets fallidos (${offsets}${moreOffsets}) y limit=${params.effectiveLimit}.`;
530
+ } else if (!params.paginated) {
531
+ continuation = "Universe fetched successfully.";
532
+ }
533
+ return {
534
+ total: params.total,
535
+ limit: params.effectiveLimit,
536
+ offset: params.effectiveOffset,
537
+ returned: params.returned,
538
+ has_more: params.paginated ? hasMoreForPage : params.pagesFailed > 0,
539
+ continuation,
540
+ fetch_mode: params.paginated ? "page" : "full",
541
+ pages_requested: params.pagesRequested,
542
+ pages_succeeded: params.pagesSucceeded,
543
+ pages_failed: params.pagesFailed,
544
+ universe_fully_fetched: universeFullyFetched
545
+ };
546
+ }
547
+ function formatCompactOrder(order, metricsByCurrency) {
548
+ const currencyId = normalizeString(order.currency_id, "UNKNOWN");
549
+ const totalAmount = toNumber(order.total_amount);
550
+ const paidAmount = toNumber(order.paid_amount);
551
+ const bucket = currencyBucket(metricsByCurrency, currencyId, createMetricsBucket);
552
+ bucket.orders += 1;
553
+ bucket.revenue += totalAmount;
554
+ bucket.avg_order_value = bucket.orders > 0 ? bucket.revenue / bucket.orders : 0;
555
+ const orderItems = asArray(order.order_items);
556
+ const firstOrderItem = asRecord(orderItems[0]);
557
+ const firstItem = asRecord(firstOrderItem.item);
558
+ const firstPayment = asRecord(asArray(order.payments)[0]);
559
+ const shipping = asRecord(order.shipping);
560
+ const buyer = asRecord(order.buyer);
561
+ const seller = asRecord(order.seller);
562
+ const context = asRecord(order.context);
563
+ const feedback = asRecord(order.feedback);
564
+ const orderRequest = asRecord(order.order_request);
565
+ const coupon = asRecord(order.coupon);
566
+ const stock = asRecord(firstOrderItem.stock);
567
+ return [
568
+ normalizeScalarString(order.id),
569
+ compactDateTime(order.date_created),
570
+ normalizeString(order.status),
571
+ totalAmount,
572
+ currencyId,
573
+ normalizeString(buyer.nickname) || normalizeScalarString(buyer.id),
574
+ normalizeString(shipping.status),
575
+ orderItems.length,
576
+ compactDateTime(order.date_closed),
577
+ compactDateTime(order.date_last_updated) || compactDateTime(order.last_updated),
578
+ paidAmount,
579
+ normalizeScalarString(buyer.id),
580
+ normalizeString(seller.nickname) || normalizeScalarString(seller.id),
581
+ normalizeScalarString(seller.id),
582
+ normalizeScalarString(shipping.id),
583
+ normalizeScalarString(order.pack_id),
584
+ Boolean(order.fulfilled),
585
+ compactStringList(order.tags),
586
+ normalizeString(context.channel),
587
+ normalizeString(context.site),
588
+ normalizeScalarString(firstPayment.id),
589
+ normalizeString(firstPayment.status),
590
+ normalizeString(firstPayment.status_detail),
591
+ normalizeString(firstPayment.payment_method_id),
592
+ normalizeString(firstPayment.payment_type),
593
+ toNumber(firstPayment.total_paid_amount),
594
+ toNumber(firstPayment.transaction_amount),
595
+ toNumber(firstPayment.installments),
596
+ toNumber(coupon.amount),
597
+ compactStringList(firstPayment.available_actions),
598
+ compactNestedValue(feedback.buyer),
599
+ compactNestedValue(feedback.seller),
600
+ compactNestedValue(orderRequest.change),
601
+ compactNestedValue(orderRequest.return),
602
+ normalizeString(firstItem.id),
603
+ normalizeString(firstItem.title),
604
+ normalizeString(firstItem.seller_sku),
605
+ toNumber(firstOrderItem.quantity),
606
+ toNumber(firstOrderItem.unit_price),
607
+ toNumber(firstOrderItem.sale_fee),
608
+ normalizeString(firstOrderItem.listing_type_id),
609
+ compactVariationAttributes(firstItem.variation_attributes),
610
+ normalizeString(stock.node_id)
611
+ ];
612
+ }
23
613
  const meliGetOrdersSummarySchema = z.object({
24
614
  profileId: mercadolibreProfileIdSchemaField,
25
615
  startDate: z.string().regex(mercadoLibreDateRegex).describe("Start date in YYYY-MM-DD format."),
@@ -27,8 +617,8 @@ const meliGetOrdersSummarySchema = z.object({
27
617
  status: z.string().trim().min(1).optional().describe("Optional MercadoLibre order status filter."),
28
618
  q: z.string().trim().min(1).optional().describe("Optional free-text search query supported by MercadoLibre orders search."),
29
619
  sort: z.string().trim().min(1).optional().describe("Optional MercadoLibre sort expression."),
30
- offset: z.number().int().min(0).max(5e3).optional().describe("Pagination offset."),
31
- limit: z.number().int().min(1).max(50).optional().describe("Results per page (1-50).")
620
+ offset: z.number().int().min(0).max(5e3).optional().describe("Optional pagination offset. If provided, the tool returns only that page instead of trying to fetch the full universe."),
621
+ limit: z.number().int().min(1).max(50).optional().describe("Optional page size (1-50). If provided, the tool returns only that page instead of trying to fetch the full universe.")
32
622
  });
33
623
  async function meliGetOrdersSummaryHandler(params) {
34
624
  try {
@@ -47,51 +637,93 @@ async function meliGetOrdersSummaryHandler(params) {
47
637
  offset: params.offset,
48
638
  limit: params.limit
49
639
  });
50
- const orders = asArray(response.results);
51
640
  const paging = normalizeMercadoLibrePaging(asRecord(response.paging));
641
+ const baseOrders = asArray(response.results);
52
642
  const metricsByCurrency = {};
53
- const compactOrders = orders.map((order) => {
54
- const currencyId = normalizeString(order.currency_id, "UNKNOWN");
55
- const totalAmount = toNumber(order.total_amount);
56
- const bucket = currencyBucket(metricsByCurrency, currencyId, () => ({
57
- orders: 0,
58
- revenue: 0,
59
- avg_order_value: 0
60
- }));
61
- bucket.orders += 1;
62
- bucket.revenue += totalAmount;
63
- bucket.avg_order_value = bucket.orders > 0 ? bucket.revenue / bucket.orders : 0;
64
- const orderItems = asArray(order.order_items);
65
- const shipping = asRecord(order.shipping);
66
- const buyer = asRecord(order.buyer);
67
- return [
68
- normalizeString(order.id),
69
- compactDateTime(order.date_created),
70
- normalizeString(order.status),
71
- totalAmount,
72
- currencyId,
73
- normalizeString(buyer.nickname) || normalizeString(buyer.id),
74
- normalizeString(shipping.status),
75
- orderItems.length
76
- ];
77
- });
643
+ const paginatedMode = params.offset !== void 0 || params.limit !== void 0;
644
+ const effectiveLimit = paging.limit || params.limit || baseOrders.length;
645
+ const effectiveOffset = paging.offset || params.offset || 0;
646
+ const allOrders = [...baseOrders];
647
+ const failedPages = [];
648
+ let pagesRequested = 1;
649
+ let pagesSucceeded = 1;
650
+ if (!paginatedMode && effectiveLimit > 0 && paging.total > baseOrders.length) {
651
+ const nextOffsets = [];
652
+ for (let nextOffset = effectiveOffset + effectiveLimit; nextOffset < paging.total; nextOffset += effectiveLimit) {
653
+ nextOffsets.push(nextOffset);
654
+ }
655
+ pagesRequested += nextOffsets.length;
656
+ if (nextOffsets.length > 0) {
657
+ const batch = await searchMercadoLibreOrdersBatch(
658
+ profileId,
659
+ {
660
+ seller: "",
661
+ from: params.startDate,
662
+ to: params.endDate,
663
+ status: params.status,
664
+ q: params.q,
665
+ sort: params.sort,
666
+ limit: effectiveLimit
667
+ },
668
+ nextOffsets,
669
+ {
670
+ maxConcurrency: PAGE_FETCH_CONCURRENCY,
671
+ maxRetries: PAGE_FETCH_MAX_RETRIES
672
+ }
673
+ );
674
+ pagesSucceeded += batch.successful.length;
675
+ batch.successful.forEach((entry) => {
676
+ allOrders.push(...asArray(entry.document.results));
677
+ });
678
+ batch.failed.forEach((failure) => {
679
+ const failedOffset = Number(failure.id);
680
+ failedPages.push({
681
+ offset: failedOffset,
682
+ limit: effectiveLimit,
683
+ page_number: Math.floor(failedOffset / effectiveLimit) + 1,
684
+ message: failure.message,
685
+ status_code: failure.statusCode,
686
+ attempts: failure.attempts,
687
+ retryable: failure.retryable
688
+ });
689
+ });
690
+ }
691
+ }
692
+ const compactOrders = allOrders.map((order) => formatCompactOrder(order, metricsByCurrency));
78
693
  for (const metrics of Object.values(metricsByCurrency)) {
79
694
  metrics.revenue = roundMoney(metrics.revenue);
80
695
  metrics.avg_order_value = roundMoney(metrics.avg_order_value);
81
696
  }
697
+ const metadata = buildOrdersSummaryMetadata({
698
+ total: paging.total,
699
+ effectiveLimit,
700
+ effectiveOffset,
701
+ returned: compactOrders.length,
702
+ paginated: paginatedMode,
703
+ pagesRequested,
704
+ pagesSucceeded,
705
+ pagesFailed: failedPages.length,
706
+ failedOffsets: failedPages.map((failure) => failure.offset)
707
+ });
708
+ const calculations = buildCalculations(allOrders, metadata, failedPages);
82
709
  return object(
83
710
  stripNulls({
84
711
  profile_id: profileId,
85
- metadata: buildMercadoLibrePaginationMetadata({
86
- total: paging.total,
87
- limit: paging.limit || params.limit || compactOrders.length,
88
- offset: paging.offset || params.offset || 0,
89
- returned: compactOrders.length,
90
- nextField: "offset"
91
- }),
712
+ metadata: paginatedMode ? {
713
+ ...buildMercadoLibrePaginationMetadata({
714
+ total: paging.total,
715
+ limit: effectiveLimit,
716
+ offset: effectiveOffset,
717
+ returned: compactOrders.length,
718
+ nextField: "offset"
719
+ }),
720
+ ...metadata
721
+ } : metadata,
92
722
  metrics_by_currency: metricsByCurrency,
93
723
  orders_schema: ordersSchema,
94
- orders: compactOrders
724
+ orders: compactOrders,
725
+ calculations,
726
+ failed_pages: failedPages
95
727
  })
96
728
  );
97
729
  } catch (err) {