@asteby/storefront 0.13.0 → 0.15.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 +27 -1
- package/dist/chunk-IPUAZ3LQ.js +646 -0
- package/dist/chunk-IPUAZ3LQ.js.map +1 -0
- package/dist/cli.js +46 -1
- package/dist/fiscal-document.d.cts +2 -2
- package/dist/fiscal-document.d.ts +2 -2
- package/dist/index.cjs +202 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -3
- package/dist/index.d.ts +73 -3
- package/dist/index.js +79 -316
- package/dist/index.js.map +1 -1
- package/dist/mock.cjs +417 -39
- package/dist/mock.cjs.map +1 -1
- package/dist/mock.d.cts +3 -1
- package/dist/mock.d.ts +3 -1
- package/dist/mock.js +272 -23
- package/dist/mock.js.map +1 -1
- package/dist/mockup.d.cts +1 -1
- package/dist/mockup.d.ts +1 -1
- package/dist/node.cjs +1 -1
- package/dist/node.js +1 -1
- package/dist/{types-C_rsJRCl.d.ts → types-CuamKhDe.d.ts} +218 -1
- package/dist/{types-BQ3S6VjF.d.cts → types-cwXbVCwe.d.cts} +218 -1
- package/package.json +1 -1
- package/dist/chunk-HBSB7XS2.js +0 -220
- package/dist/chunk-HBSB7XS2.js.map +0 -1
package/dist/mock.cjs
CHANGED
|
@@ -1,5 +1,154 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// src/availability.ts
|
|
4
|
+
function resolveAvailability(source, options = {}) {
|
|
5
|
+
const a = source.availability;
|
|
6
|
+
if (a && typeof a === "object" && typeof a.status === "string") return a;
|
|
7
|
+
const qty = typeof source.quantity_available === "number" ? source.quantity_available : null;
|
|
8
|
+
const sellable = source.available_for_sale && (qty === null || qty > 0);
|
|
9
|
+
const threshold = options.lowStockThreshold;
|
|
10
|
+
const low = sellable && qty !== null && typeof threshold === "number" && threshold > 0 && qty <= threshold;
|
|
11
|
+
const status = !sellable ? "out_of_stock" : low ? "low_stock" : "in_stock";
|
|
12
|
+
return {
|
|
13
|
+
status,
|
|
14
|
+
quantity: sellable ? qty : null,
|
|
15
|
+
max_per_order: sellable ? qty : null,
|
|
16
|
+
restock_eta: null,
|
|
17
|
+
preorder_release_at: null,
|
|
18
|
+
// Old servers have no subscription route: never offer a form that cannot work.
|
|
19
|
+
notify_available: false
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/payments.ts
|
|
24
|
+
function safeHttpsUrl(value) {
|
|
25
|
+
if (typeof value !== "string" || value.length > 2048) return null;
|
|
26
|
+
try {
|
|
27
|
+
const u = new URL(value);
|
|
28
|
+
if (u.protocol !== "https:" || u.username || u.password) return null;
|
|
29
|
+
return u.toString();
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/order-progress.ts
|
|
36
|
+
var SHIPPING_STEPS = ["paid", "preparing", "shipped", "in_transit", "delivered"];
|
|
37
|
+
var PICKUP_STEPS = ["paid", "preparing", "ready_for_pickup", "delivered"];
|
|
38
|
+
var SHIPMENT_PROBLEMS = { exception: "delivery_exception", returned: "returned", lost: "lost" };
|
|
39
|
+
var RANK = { pending: 0, label_created: 1, in_transit: 2, out_for_delivery: 3, delivered: 4 };
|
|
40
|
+
var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
41
|
+
function isTestShipment(s) {
|
|
42
|
+
return !!s && (s.test_mode === true || s.environment === "sandbox");
|
|
43
|
+
}
|
|
44
|
+
function isTestInvoice(d) {
|
|
45
|
+
return !!d && (d.test_mode === true || d.environment === "sandbox");
|
|
46
|
+
}
|
|
47
|
+
function safeTrackingUrl(value) {
|
|
48
|
+
return safeHttpsUrl(value);
|
|
49
|
+
}
|
|
50
|
+
function time(iso) {
|
|
51
|
+
const t = iso ? Date.parse(iso) : NaN;
|
|
52
|
+
return Number.isNaN(t) ? -Infinity : t;
|
|
53
|
+
}
|
|
54
|
+
function sortTrackingEvents(events, limit = 50) {
|
|
55
|
+
if (!Array.isArray(events)) return [];
|
|
56
|
+
return events.filter(isRecord).map((e, i) => ({ e, i })).sort((a, b) => time(b.e.occurred_at) - time(a.e.occurred_at) || a.i - b.i).slice(0, Math.max(0, limit)).map((x) => x.e);
|
|
57
|
+
}
|
|
58
|
+
function orderShipments(order) {
|
|
59
|
+
const list = Array.isArray(order?.shipments) ? order.shipments.filter(isRecord) : [];
|
|
60
|
+
return [...list.filter((s) => s.kind !== "return"), ...list.filter((s) => s.kind === "return")];
|
|
61
|
+
}
|
|
62
|
+
function validProgress(p) {
|
|
63
|
+
return isRecord(p) && typeof p.stage === "string" && Array.isArray(p.steps);
|
|
64
|
+
}
|
|
65
|
+
var validStep = (s) => isRecord(s) && typeof s.key === "string" && typeof s.state === "string";
|
|
66
|
+
function cleanProgress(p) {
|
|
67
|
+
if (p.steps.every(validStep) && (p.problem === void 0 || p.problem === null || isRecord(p.problem) && typeof p.problem.code === "string")) return p;
|
|
68
|
+
const problem = isRecord(p.problem) && typeof p.problem.code === "string" ? p.problem : null;
|
|
69
|
+
return { stage: p.stage, problem, steps: p.steps.filter(validStep) };
|
|
70
|
+
}
|
|
71
|
+
var liveShipments = (shipments) => shipments.filter((s) => s.kind !== "return" && !isTestShipment(s));
|
|
72
|
+
function legacyStage(order, shipments) {
|
|
73
|
+
const status = typeof order.status === "string" ? order.status.toLowerCase() : "";
|
|
74
|
+
const pickup = order.delivery_method === "pickup";
|
|
75
|
+
if (status === "cancelled" || status === "canceled") return "cancelled";
|
|
76
|
+
if (status === "refunded") return "refunded";
|
|
77
|
+
if (status === "pending_payment" || order.payment_status === "pending") return "pending_payment";
|
|
78
|
+
if (status === "delivered" || status === "fulfilled") return "delivered";
|
|
79
|
+
const live = liveShipments(shipments);
|
|
80
|
+
const ranked = live.filter((s) => RANK[s.status] !== void 0);
|
|
81
|
+
const least = ranked.reduce((acc, s) => acc === null || RANK[s.status] < RANK[acc] ? s.status : acc, null);
|
|
82
|
+
if (least === "delivered") return "delivered";
|
|
83
|
+
if (least === "out_for_delivery") return "out_for_delivery";
|
|
84
|
+
if (least === "in_transit") return "in_transit";
|
|
85
|
+
if (status === "ready_for_pickup" || status === "ready" && pickup) return "ready_for_pickup";
|
|
86
|
+
const onlySandbox = shipments.some((s) => s.kind !== "return") && live.length === 0;
|
|
87
|
+
if (status === "shipped" && !onlySandbox || least === "label_created") return "shipped";
|
|
88
|
+
if (status === "fulfilling" || status === "accepted" || status === "ready" || status === "shipped") return "preparing";
|
|
89
|
+
return "paid";
|
|
90
|
+
}
|
|
91
|
+
var STAGE_TO_STEP = {
|
|
92
|
+
paid: "paid",
|
|
93
|
+
preparing: "preparing",
|
|
94
|
+
ready_for_pickup: "ready_for_pickup",
|
|
95
|
+
shipped: "shipped",
|
|
96
|
+
in_transit: "in_transit",
|
|
97
|
+
out_for_delivery: "in_transit",
|
|
98
|
+
delivered: "delivered"
|
|
99
|
+
};
|
|
100
|
+
function paidAt(order) {
|
|
101
|
+
const paid = Array.isArray(order.payments) ? order.payments.find((p) => isRecord(p) && typeof p.paid_at === "string" && p.paid_at) : void 0;
|
|
102
|
+
return paid?.paid_at ?? null;
|
|
103
|
+
}
|
|
104
|
+
function orderProgress(order) {
|
|
105
|
+
if (validProgress(order.progress)) return cleanProgress(order.progress);
|
|
106
|
+
const shipments = orderShipments(order);
|
|
107
|
+
const stage = legacyStage(order, shipments);
|
|
108
|
+
const keys = order.delivery_method === "pickup" ? PICKUP_STEPS : SHIPPING_STEPS;
|
|
109
|
+
const dead = stage === "cancelled" || stage === "refunded";
|
|
110
|
+
const awaiting = stage === "pending_payment";
|
|
111
|
+
const current = STAGE_TO_STEP[stage] ?? "paid";
|
|
112
|
+
const at = keys.indexOf(current);
|
|
113
|
+
const failed = liveShipments(shipments).find((s) => SHIPMENT_PROBLEMS[s.status]);
|
|
114
|
+
const problem = failed ? { code: SHIPMENT_PROBLEMS[failed.status], at: sortTrackingEvents(failed.events)[0]?.occurred_at ?? null } : order.payment_status === "failed" ? { code: "payment_failed" } : null;
|
|
115
|
+
const steps = dead ? [] : keys.map((key, i) => {
|
|
116
|
+
if (awaiting) return { key, state: "pending", at: null };
|
|
117
|
+
const state = i < at || stage === "delivered" ? "done" : i === at ? problem && i >= keys.indexOf("shipped") ? "problem" : "current" : "pending";
|
|
118
|
+
return { key, state, at: key === "paid" && state === "done" ? paidAt(order) : null };
|
|
119
|
+
});
|
|
120
|
+
return { stage, problem, steps };
|
|
121
|
+
}
|
|
122
|
+
function nextAction(order) {
|
|
123
|
+
if (order.next_action === null) return { kind: "none", href: null, ref: null };
|
|
124
|
+
if (isRecord(order.next_action) && typeof order.next_action.kind === "string") {
|
|
125
|
+
const href = safeTrackingUrl(order.next_action.href);
|
|
126
|
+
return { kind: order.next_action.kind, href, ref: typeof order.next_action.ref === "string" ? order.next_action.ref : null };
|
|
127
|
+
}
|
|
128
|
+
const progress = orderProgress(order);
|
|
129
|
+
if (progress.stage === "pending_payment") return { kind: "pay", href: null, ref: null };
|
|
130
|
+
if (progress.stage === "cancelled" || progress.stage === "refunded") return { kind: "none", href: null, ref: null };
|
|
131
|
+
const live = orderShipments(order).find((s) => s.kind !== "return" && !isTestShipment(s) && s.status !== "delivered" && s.status !== "cancelled");
|
|
132
|
+
const trackUrl = safeTrackingUrl(live?.tracking_url);
|
|
133
|
+
if (live && trackUrl) return { kind: "track", href: trackUrl, ref: live.id };
|
|
134
|
+
if (progress.stage === "ready_for_pickup") return { kind: "pickup", href: null, ref: null };
|
|
135
|
+
const inv = isRecord(order.invoice) ? order.invoice : void 0;
|
|
136
|
+
if (inv?.action === "download" || inv?.action === void 0 && inv?.stage === "issued") {
|
|
137
|
+
const docs = Array.isArray(order.invoices) ? order.invoices.filter((d) => isRecord(d) && d.kind === "invoice" && d.status !== "cancelled") : [];
|
|
138
|
+
const real = docs.find((d) => !isTestInvoice(d));
|
|
139
|
+
if (docs.length === 0 || real) return { kind: "download_invoice", href: null, ref: real?.id ?? null };
|
|
140
|
+
}
|
|
141
|
+
if (inv?.action === "correct_data") return { kind: "correct_invoice_data", href: null, ref: null };
|
|
142
|
+
if (inv?.action === "contact_store") return { kind: "contact_store", href: null, ref: null };
|
|
143
|
+
if (inv?.can_request === true && inv.stage !== "requested") return { kind: "request_invoice", href: null, ref: null };
|
|
144
|
+
return { kind: "none", href: null, ref: null };
|
|
145
|
+
}
|
|
146
|
+
function shipmentsStatus(order) {
|
|
147
|
+
if (order.shipments_summary && typeof order.shipments_summary.status === "string") return order.shipments_summary.status;
|
|
148
|
+
const live = orderShipments(order).filter((s) => s.kind !== "return" && !isTestShipment(s));
|
|
149
|
+
return live.length ? live[0].status : null;
|
|
150
|
+
}
|
|
151
|
+
|
|
3
152
|
// src/geo.ts
|
|
4
153
|
var RAD = Math.PI / 180;
|
|
5
154
|
function distanceKm(a, b) {
|
|
@@ -24,24 +173,208 @@ function rankByDistance(items, origin, { radiusKm, limit, getPoint = (i) => i }
|
|
|
24
173
|
return limit === void 0 ? out : out.slice(0, Math.max(0, limit));
|
|
25
174
|
}
|
|
26
175
|
|
|
27
|
-
// src/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
176
|
+
// src/mock/account-seed.ts
|
|
177
|
+
var money = (cents, currency) => ({ amount: (cents / 100).toFixed(2), currency_code: currency });
|
|
178
|
+
var line = (title, variant, quantity, unitCents, currency) => ({
|
|
179
|
+
title,
|
|
180
|
+
variant_title: variant,
|
|
181
|
+
sku: null,
|
|
182
|
+
quantity,
|
|
183
|
+
unit: money(unitCents, currency),
|
|
184
|
+
total: money(unitCents * quantity, currency),
|
|
185
|
+
image: null
|
|
186
|
+
});
|
|
187
|
+
var address = { first_name: "Ana", last_name: "Prueba", city: "Quer\xE9taro", province: "Qro.", zip: "76000", country_code: "MX" };
|
|
188
|
+
function buildAccountOrders(currency) {
|
|
189
|
+
const inTransit = {
|
|
190
|
+
id: "shp_seed_1",
|
|
191
|
+
number: "ENV-00001",
|
|
192
|
+
kind: "outbound",
|
|
193
|
+
status: "in_transit",
|
|
194
|
+
environment: "production",
|
|
195
|
+
test_mode: false,
|
|
196
|
+
carrier: "Estafeta",
|
|
197
|
+
carrier_logo_key: "estafeta",
|
|
198
|
+
service: "Terrestre",
|
|
199
|
+
tracking_number: "8012345678",
|
|
200
|
+
tracking_url: "https://rastreo.example.com/estafeta/8012345678",
|
|
201
|
+
shipped_at: "2026-08-12T15:00:00.000Z",
|
|
202
|
+
delivered_at: null,
|
|
203
|
+
promised_by: "2026-08-16",
|
|
204
|
+
label_url: null,
|
|
205
|
+
events: [
|
|
206
|
+
{ id: "evt_3", status: "in_transit", description: "En tr\xE1nsito hacia tu ciudad", location: "Quer\xE9taro, Qro.", occurred_at: "2026-08-14T09:30:00.000Z", source: "carrier" },
|
|
207
|
+
{ id: "evt_2", status: "in_transit", description: "Sali\xF3 del centro de distribuci\xF3n", location: "Ciudad de M\xE9xico, CDMX", occurred_at: "2026-08-13T07:10:00.000Z", source: "carrier" },
|
|
208
|
+
{ id: "evt_1", status: "label_created", description: "Gu\xEDa generada", location: null, occurred_at: "2026-08-12T15:00:00.000Z", source: "store" }
|
|
209
|
+
],
|
|
210
|
+
map: {
|
|
211
|
+
origin: { label: "Ciudad de M\xE9xico, CDMX", lat: 19.43, lng: -99.13, precision: "city" },
|
|
212
|
+
destination: { label: "Quer\xE9taro, Qro.", lat: 20.6, lng: -100.4, precision: "city" },
|
|
213
|
+
path: [{ label: "Quer\xE9taro, Qro.", lat: 20.59, lng: -100.39, precision: "city" }]
|
|
214
|
+
}
|
|
44
215
|
};
|
|
216
|
+
const invoice = {
|
|
217
|
+
id: "inv_seed_1",
|
|
218
|
+
status: "issued",
|
|
219
|
+
kind: "invoice",
|
|
220
|
+
stage: "issued",
|
|
221
|
+
action: "download",
|
|
222
|
+
can_request: false,
|
|
223
|
+
folio: "8F3C1A52-6E0B-4B58-9C0D-2B7D6B4D7A11",
|
|
224
|
+
series: "A",
|
|
225
|
+
folio_number: "1042",
|
|
226
|
+
total: money(129900, currency),
|
|
227
|
+
issued_at: "2026-08-13T10:00:00.000Z",
|
|
228
|
+
test_mode: false,
|
|
229
|
+
environment: "production"
|
|
230
|
+
};
|
|
231
|
+
const creditNote = {
|
|
232
|
+
id: "inv_seed_1_nc",
|
|
233
|
+
status: "issued",
|
|
234
|
+
kind: "credit_note",
|
|
235
|
+
stage: "issued",
|
|
236
|
+
action: "download",
|
|
237
|
+
can_request: false,
|
|
238
|
+
folio: "1D9B7E20-4A5C-4C4B-8F11-6A0E3C9D5B02",
|
|
239
|
+
series: "NC",
|
|
240
|
+
folio_number: "7",
|
|
241
|
+
total: money(2e4, currency),
|
|
242
|
+
issued_at: "2026-08-15T10:00:00.000Z",
|
|
243
|
+
related_to: "inv_seed_1",
|
|
244
|
+
test_mode: false,
|
|
245
|
+
environment: "production"
|
|
246
|
+
};
|
|
247
|
+
return [
|
|
248
|
+
{
|
|
249
|
+
id: "ord_seed_shipped",
|
|
250
|
+
number: "0999",
|
|
251
|
+
status: "shipped",
|
|
252
|
+
created_at: "2026-08-12T12:00:00.000Z",
|
|
253
|
+
total: money(129900, currency),
|
|
254
|
+
customer_id: "cus_1",
|
|
255
|
+
lines: [line("Kit de frenos", "Delantero", 1, 109900, currency), line("L\xEDquido de frenos", "500 ml", 1, 2e4, currency)],
|
|
256
|
+
delivery_method: "shipping",
|
|
257
|
+
payment_status: "paid",
|
|
258
|
+
subtotal: money(111983, currency),
|
|
259
|
+
tax: money(17917, currency),
|
|
260
|
+
shipping: money(0, currency),
|
|
261
|
+
discount: money(0, currency),
|
|
262
|
+
tax_included: true,
|
|
263
|
+
shipping_method: "Est\xE1ndar",
|
|
264
|
+
shipping_address: address,
|
|
265
|
+
returns: [],
|
|
266
|
+
shipments: [inTransit],
|
|
267
|
+
invoices: [invoice, creditNote],
|
|
268
|
+
payments: [{ method_label: "Tarjeta Visa terminada en 4242", family: "card", status: "partially_refunded", amount: money(129900, currency), paid_at: "2026-08-12T12:01:00.000Z" }],
|
|
269
|
+
refunds: [{ id: "rfd_seed_1", amount: money(2e4, currency), status: "completed", method_label: "Tarjeta Visa terminada en 4242", created_at: "2026-08-15T09:00:00.000Z", completed_at: "2026-08-15T10:00:00.000Z", return_id: null }]
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
id: "ord_seed_preparing",
|
|
273
|
+
number: "0998",
|
|
274
|
+
status: "fulfilling",
|
|
275
|
+
created_at: "2026-08-11T12:00:00.000Z",
|
|
276
|
+
total: money(54900, currency),
|
|
277
|
+
customer_id: "cus_1",
|
|
278
|
+
lines: [line("Filtro de aceite", null, 3, 18300, currency)],
|
|
279
|
+
delivery_method: "shipping",
|
|
280
|
+
payment_status: "paid",
|
|
281
|
+
shipping_address: address,
|
|
282
|
+
shipments: [],
|
|
283
|
+
invoices: [],
|
|
284
|
+
payments: [{ method_label: "OXXO", family: "cash_voucher", status: "paid", amount: money(54900, currency), paid_at: "2026-08-11T18:00:00.000Z" }],
|
|
285
|
+
refunds: []
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
id: "ord_seed_sandbox",
|
|
289
|
+
number: "0997",
|
|
290
|
+
status: "shipped",
|
|
291
|
+
created_at: "2026-08-10T12:00:00.000Z",
|
|
292
|
+
total: money(31e3, currency),
|
|
293
|
+
customer_id: "cus_1",
|
|
294
|
+
lines: [line("Buj\xEDa iridio", null, 4, 7750, currency)],
|
|
295
|
+
delivery_method: "shipping",
|
|
296
|
+
payment_status: "paid",
|
|
297
|
+
shipments: [
|
|
298
|
+
{
|
|
299
|
+
id: "shp_seed_test",
|
|
300
|
+
kind: "outbound",
|
|
301
|
+
status: "label_created",
|
|
302
|
+
environment: "sandbox",
|
|
303
|
+
test_mode: true,
|
|
304
|
+
carrier: "Estafeta",
|
|
305
|
+
carrier_logo_key: "estafeta",
|
|
306
|
+
tracking_number: null,
|
|
307
|
+
tracking_url: null,
|
|
308
|
+
events: [],
|
|
309
|
+
map: null
|
|
310
|
+
}
|
|
311
|
+
],
|
|
312
|
+
invoices: [
|
|
313
|
+
{ id: "inv_seed_test", status: "issued", kind: "invoice", stage: "issued", action: "download", can_request: false, folio: "00000000-0000-4000-8000-000000000000", test_mode: true, environment: "sandbox" }
|
|
314
|
+
],
|
|
315
|
+
payments: [],
|
|
316
|
+
refunds: []
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
id: "ord_seed_pickup",
|
|
320
|
+
number: "0996",
|
|
321
|
+
status: "ready_for_pickup",
|
|
322
|
+
created_at: "2026-08-09T12:00:00.000Z",
|
|
323
|
+
total: money(88e3, currency),
|
|
324
|
+
customer_id: "cus_1",
|
|
325
|
+
lines: [line("Bater\xEDa 12V", null, 1, 88e3, currency)],
|
|
326
|
+
delivery_method: "pickup",
|
|
327
|
+
pickup: { location_id: null, name: "Sucursal Centro", address: "Av. Reforma 100", city: "Ciudad de M\xE9xico", phone: null, ready_at: "2026-08-10T10:00:00.000Z", delivered_at: null },
|
|
328
|
+
payment_status: "paid",
|
|
329
|
+
shipments: [],
|
|
330
|
+
invoices: [],
|
|
331
|
+
payments: [],
|
|
332
|
+
refunds: [],
|
|
333
|
+
degraded: ["invoices"]
|
|
334
|
+
}
|
|
335
|
+
];
|
|
336
|
+
}
|
|
337
|
+
function buildPendingPayments(currency, nowMs) {
|
|
338
|
+
return [
|
|
339
|
+
{
|
|
340
|
+
id: "chk_seed_oxxo",
|
|
341
|
+
number: null,
|
|
342
|
+
status: "pending_payment",
|
|
343
|
+
created_at: new Date(nowMs - 36e5).toISOString(),
|
|
344
|
+
total: money(45e3, currency),
|
|
345
|
+
expires_at: new Date(nowMs + 47 * 36e5).toISOString(),
|
|
346
|
+
payment: {
|
|
347
|
+
provider: "mercadopago",
|
|
348
|
+
method: "oxxo",
|
|
349
|
+
reference: "12345678901234",
|
|
350
|
+
barcode: "12345678901234",
|
|
351
|
+
clabe: null,
|
|
352
|
+
voucher_url: "https://pagos.example.com/ficha/abc",
|
|
353
|
+
instructions: "Paga en cualquier OXXO antes de la fecha de vencimiento.",
|
|
354
|
+
expires_at: new Date(nowMs + 47 * 36e5).toISOString(),
|
|
355
|
+
amount: money(45e3, currency)
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
id: "chk_seed_spei",
|
|
360
|
+
number: null,
|
|
361
|
+
status: "expired",
|
|
362
|
+
created_at: new Date(nowMs - 9 * 864e5).toISOString(),
|
|
363
|
+
total: money(12e4, currency),
|
|
364
|
+
expires_at: new Date(nowMs - 2 * 864e5).toISOString(),
|
|
365
|
+
payment: {
|
|
366
|
+
provider: "mercadopago",
|
|
367
|
+
method: "spei",
|
|
368
|
+
reference: null,
|
|
369
|
+
barcode: null,
|
|
370
|
+
clabe: "646180000000000001",
|
|
371
|
+
voucher_url: null,
|
|
372
|
+
instructions: null,
|
|
373
|
+
expires_at: new Date(nowMs - 2 * 864e5).toISOString(),
|
|
374
|
+
amount: money(12e4, currency)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
];
|
|
45
378
|
}
|
|
46
379
|
|
|
47
380
|
// src/mock/region-mx.seed.ts
|
|
@@ -1037,7 +1370,7 @@ var PRODUCT_SPECS = [
|
|
|
1037
1370
|
{ handle: "llanta-265-70-r16-at", title: "Llanta 265/70 R16 All Terrain", collection: "llantas", type: "Llanta", vendor: "Marca Sur", price: 3599, attributes: { ...tire(265, 70, 16), load_index: 112, speed_rating: "T", runflat: false }, tags: ["camioneta"], description: "Para camioneta, asfalto y terracer\xEDa." },
|
|
1038
1371
|
{ handle: "llanta-175-70-r13-city", title: "Llanta 175/70 R13 City", collection: "llantas", type: "Llanta", vendor: "Marca Centro", price: 999, attributes: { ...tire(175, 70, 13), load_index: 82, speed_rating: "T", runflat: false }, tags: ["auto"], stock: 0, description: "Econ\xF3mica y durable para ciudad." }
|
|
1039
1372
|
];
|
|
1040
|
-
var
|
|
1373
|
+
var money2 = (n, currency) => ({ amount: n.toFixed(2), currency_code: currency });
|
|
1041
1374
|
function cartesian(options) {
|
|
1042
1375
|
let combos = [{}];
|
|
1043
1376
|
for (const opt of options) {
|
|
@@ -1067,8 +1400,8 @@ function buildProducts(currency, imageUrl = MOCK_IMAGE_URL) {
|
|
|
1067
1400
|
sku: `${spec.handle.toUpperCase().replace(/-/g, "")}-${vi + 1}`.slice(0, 32),
|
|
1068
1401
|
title,
|
|
1069
1402
|
selected_options: Object.entries(opts).map(([name, value]) => ({ name, value })),
|
|
1070
|
-
price:
|
|
1071
|
-
compare_at_price: spec.compareAt ?
|
|
1403
|
+
price: money2(price, currency),
|
|
1404
|
+
compare_at_price: spec.compareAt ? money2(spec.compareAt + delta, currency) : null,
|
|
1072
1405
|
available_for_sale: stock > 0,
|
|
1073
1406
|
quantity_available: stock,
|
|
1074
1407
|
image: firstValue ? image(imageUrl, `${spec.handle}-${firstValue}`, `${spec.title} ${firstValue}`) : null,
|
|
@@ -1093,8 +1426,8 @@ function buildProducts(currency, imageUrl = MOCK_IMAGE_URL) {
|
|
|
1093
1426
|
images,
|
|
1094
1427
|
options,
|
|
1095
1428
|
variants,
|
|
1096
|
-
price_range: { min:
|
|
1097
|
-
compare_at_price_range: compares.length ? { min:
|
|
1429
|
+
price_range: { min: money2(Math.min(...prices), currency), max: money2(Math.max(...prices), currency) },
|
|
1430
|
+
compare_at_price_range: compares.length ? { min: money2(Math.min(...compares), currency), max: money2(Math.max(...compares), currency) } : null,
|
|
1098
1431
|
available_for_sale: variants.some((v) => v.available_for_sale),
|
|
1099
1432
|
seo: { title: `${spec.title} | Mi tienda`, description: spec.description },
|
|
1100
1433
|
updated_at: new Date(BASE_DATE - pi * 864e5).toISOString()
|
|
@@ -1156,7 +1489,7 @@ function buildLocations(currency) {
|
|
|
1156
1489
|
opening_hours: { weekly: weekdays("10:00", "18:00", [1, 2, 3, 4, 5, 6]) },
|
|
1157
1490
|
services: ["pickup"],
|
|
1158
1491
|
contacts: contacts(null),
|
|
1159
|
-
pickup: { enabled: true, fee:
|
|
1492
|
+
pickup: { enabled: true, fee: money2(30, currency), prep_days: 1 }
|
|
1160
1493
|
},
|
|
1161
1494
|
{
|
|
1162
1495
|
id: "br_occidente",
|
|
@@ -1196,7 +1529,7 @@ function buildShop(currency, imageUrl = MOCK_IMAGE_URL) {
|
|
|
1196
1529
|
logo: { url: imageUrl("logo", 256, 256), alt: "Mi tienda", width: 256, height: 256 },
|
|
1197
1530
|
contact: { email: "hola@mi-tienda.example", phone: "+52 55 0000 0000", whatsapp: "+5215500000000" },
|
|
1198
1531
|
branches: buildLocations(currency),
|
|
1199
|
-
policies: { free_shipping_min:
|
|
1532
|
+
policies: { free_shipping_min: money2(999, currency), returns_days: 30 }
|
|
1200
1533
|
};
|
|
1201
1534
|
}
|
|
1202
1535
|
function buildTheme() {
|
|
@@ -1437,8 +1770,10 @@ function createMockStorefront(options = {}) {
|
|
|
1437
1770
|
lines: [
|
|
1438
1771
|
{ title: "Playera b\xE1sica", variant_title: "M / Blanco", sku: null, quantity: 1, unit: fromCents(24900, currency), total: fromCents(24900, currency), image: null }
|
|
1439
1772
|
]
|
|
1440
|
-
}
|
|
1773
|
+
},
|
|
1774
|
+
...buildAccountOrders(currency)
|
|
1441
1775
|
],
|
|
1776
|
+
pendingPayments: buildPendingPayments(currency, Date.now()),
|
|
1442
1777
|
otps: /* @__PURE__ */ new Map(),
|
|
1443
1778
|
passwords: /* @__PURE__ */ new Map(),
|
|
1444
1779
|
pendingRegistrations: /* @__PURE__ */ new Map(),
|
|
@@ -1736,6 +2071,11 @@ function createMockStorefront(options = {}) {
|
|
|
1736
2071
|
return inv;
|
|
1737
2072
|
}
|
|
1738
2073
|
const orderInvoice = (order) => {
|
|
2074
|
+
const doc = order.invoices?.find((d) => d.kind === "invoice");
|
|
2075
|
+
if (doc) {
|
|
2076
|
+
const { id: _id, kind: _k, total: _t, issued_at: _i, cancelled_at: _c, series: _s, folio_number: _f, related_to: _r, links_expire_at: _l, test_mode: _tm, environment: _e, ...summary } = doc;
|
|
2077
|
+
return { ...summary, url: null, xml_url: null };
|
|
2078
|
+
}
|
|
1739
2079
|
const entry = state.invoices.get(order.id);
|
|
1740
2080
|
const paid = order.status !== "pending_payment";
|
|
1741
2081
|
if (entry) {
|
|
@@ -1896,21 +2236,21 @@ function createMockStorefront(options = {}) {
|
|
|
1896
2236
|
if (!Array.isArray(body.lines)) throw new HttpError(400, "validation", "`lines` must be an array", "lines");
|
|
1897
2237
|
const updates = body.lines;
|
|
1898
2238
|
updates.forEach((u, i) => {
|
|
1899
|
-
const
|
|
1900
|
-
if (!
|
|
2239
|
+
const line2 = cart.lines.find((l) => l.id === u.id);
|
|
2240
|
+
if (!line2) throw new HttpError(400, "validation", `Unknown line ${String(u.id)}`, `lines[${i}].id`);
|
|
1901
2241
|
if (typeof u.quantity !== "number" || !Number.isInteger(u.quantity) || u.quantity < 0) {
|
|
1902
2242
|
throw new HttpError(400, "validation", "quantity must be a non-negative integer", `lines[${i}].quantity`);
|
|
1903
2243
|
}
|
|
1904
2244
|
});
|
|
1905
2245
|
checkStock(
|
|
1906
2246
|
updates.flatMap((u, i) => {
|
|
1907
|
-
const
|
|
1908
|
-
return u.quantity > 0 ? [{ variant: findVariant(
|
|
2247
|
+
const line2 = cart.lines.find((l) => l.id === u.id);
|
|
2248
|
+
return u.quantity > 0 ? [{ variant: findVariant(line2.variant_id).variant, quantity: u.quantity, lineId: line2.id, field: `lines[${i}].quantity` }] : [];
|
|
1909
2249
|
})
|
|
1910
2250
|
);
|
|
1911
2251
|
for (const u of updates) {
|
|
1912
|
-
const
|
|
1913
|
-
|
|
2252
|
+
const line2 = cart.lines.find((l) => l.id === u.id);
|
|
2253
|
+
line2.quantity = u.quantity;
|
|
1914
2254
|
}
|
|
1915
2255
|
cart.lines = cart.lines.filter((l) => l.quantity > 0);
|
|
1916
2256
|
cart.updated_at = now();
|
|
@@ -2042,7 +2382,7 @@ function createMockStorefront(options = {}) {
|
|
|
2042
2382
|
}
|
|
2043
2383
|
syncAvailability(state.products);
|
|
2044
2384
|
const orderId = nextId("ord");
|
|
2045
|
-
const number = String(
|
|
2385
|
+
const number = String(Math.max(1e3, ...state.orders.map((o) => Number(o.number) || 0)) + 1);
|
|
2046
2386
|
const customerId = cart.buyer.customer_id ?? state.customers.find((c) => c.email && c.email === cart.buyer.email)?.id ?? null;
|
|
2047
2387
|
state.orders.push({
|
|
2048
2388
|
id: orderId,
|
|
@@ -2323,17 +2663,34 @@ function createMockStorefront(options = {}) {
|
|
|
2323
2663
|
}
|
|
2324
2664
|
return { body: publicCustomer(c), cache: "private" };
|
|
2325
2665
|
});
|
|
2326
|
-
const
|
|
2666
|
+
const orderView = ({ customer_id: _c, ...o }, detail) => {
|
|
2667
|
+
const order = { ...o, invoice: orderInvoice(o) };
|
|
2668
|
+
if (order.shipments) order.shipments = order.shipments.map((s) => isTestShipment(s) ? { ...s, tracking_number: null, tracking_url: null } : { ...s, events: sortTrackingEvents(s.events) });
|
|
2669
|
+
order.progress = orderProgress(order);
|
|
2670
|
+
order.next_action = nextAction(order);
|
|
2671
|
+
const live = orderShipments(order).filter((s) => !isTestShipment(s));
|
|
2672
|
+
if (order.shipments?.length) {
|
|
2673
|
+
order.shipments_summary = { count: order.shipments.length, status: shipmentsStatus(order) ?? "pending", carrier: live[0]?.carrier ?? null, ...live[0]?.tracking_url ? { tracking_url: live[0].tracking_url } : {} };
|
|
2674
|
+
}
|
|
2675
|
+
if (!detail) {
|
|
2676
|
+
delete order.shipments;
|
|
2677
|
+
delete order.invoices;
|
|
2678
|
+
delete order.payments;
|
|
2679
|
+
delete order.refunds;
|
|
2680
|
+
delete order.shipping_address;
|
|
2681
|
+
}
|
|
2682
|
+
return order;
|
|
2683
|
+
};
|
|
2327
2684
|
route("GET", "/customer/orders", "orders:read", ({ headers, search }) => {
|
|
2328
2685
|
const c = requireCustomer(headers);
|
|
2329
|
-
const orders = state.orders.filter((o) => o.customer_id === c.id).sort((a, b) => b.created_at.localeCompare(a.created_at)).map(
|
|
2686
|
+
const orders = state.orders.filter((o) => o.customer_id === c.id).sort((a, b) => b.created_at.localeCompare(a.created_at)).map((o) => orderView(o, false));
|
|
2330
2687
|
return { body: paginate(orders, search, 20), cache: "private" };
|
|
2331
2688
|
});
|
|
2332
2689
|
const findOrder = (headers, id) => {
|
|
2333
2690
|
const c = requireCustomer(headers);
|
|
2334
2691
|
return requireNode(state.orders.find((o) => o.id === id && o.customer_id === c.id), `Order ${id}`, "id");
|
|
2335
2692
|
};
|
|
2336
|
-
route("GET", "/customer/orders/:id", "orders:read", ({ headers, params }) => ({ body:
|
|
2693
|
+
route("GET", "/customer/orders/:id", "orders:read", ({ headers, params }) => ({ body: orderView(findOrder(headers, params[0]), true), cache: "private" }));
|
|
2337
2694
|
route("POST", "/customer/orders/:id/returns", "customer:write", ({ headers, params, body }) => {
|
|
2338
2695
|
const order = findOrder(headers, params[0]);
|
|
2339
2696
|
if (!Array.isArray(body.lines) || body.lines.length === 0) throw new HttpError(400, "validation", "lines are required", "lines");
|
|
@@ -2350,16 +2707,37 @@ function createMockStorefront(options = {}) {
|
|
|
2350
2707
|
state.invoices.set(order.id, { stage: "queued", request });
|
|
2351
2708
|
return { status: 202, body: { status: "requested", url: null, stage: "queued", invoice: orderInvoice(order) }, cache: "private" };
|
|
2352
2709
|
});
|
|
2710
|
+
route("GET", "/customer/pending-payments", "orders:read", ({ headers, search }) => {
|
|
2711
|
+
const c = requireCustomer(headers);
|
|
2712
|
+
const nodes = c.id === "cus_1" ? state.pendingPayments : [];
|
|
2713
|
+
return { body: paginate(nodes, search, 20), cache: "private" };
|
|
2714
|
+
});
|
|
2715
|
+
route("GET", "/customer/orders/:id/invoices/:invoice_id/:kind", "orders:read", ({ headers, params }) => {
|
|
2716
|
+
const order = findOrder(headers, params[0]);
|
|
2717
|
+
const doc = order.invoices?.find((d) => d.id === params[1]);
|
|
2718
|
+
if (!doc || params[2] !== "pdf" && params[2] !== "xml") throw new HttpError(404, "not_found", "Invoice file not found", "invoice_id");
|
|
2719
|
+
const pdf = params[2] === "pdf";
|
|
2720
|
+
const text = pdf ? `%PDF-1.4
|
|
2721
|
+
% mock invoice ${doc.id}
|
|
2722
|
+
%%EOF
|
|
2723
|
+
` : `<?xml version="1.0"?><cfdi:Comprobante mock="true" id="${doc.id}"/>`;
|
|
2724
|
+
const file = {
|
|
2725
|
+
filename: `${doc.kind === "credit_note" ? "nota-de-credito" : "factura"}-${doc.id}.${params[2]}`,
|
|
2726
|
+
content_type: pdf ? "application/pdf" : "application/xml",
|
|
2727
|
+
content_base64: btoa(text)
|
|
2728
|
+
};
|
|
2729
|
+
return { body: file, headers: { "Cache-Control": "no-store" } };
|
|
2730
|
+
});
|
|
2353
2731
|
route("GET", "/customer/addresses", "customer:read", ({ headers }) => {
|
|
2354
2732
|
const c = requireCustomer(headers);
|
|
2355
2733
|
return { body: { nodes: state.addresses.get(c.id) ?? [] }, cache: "private" };
|
|
2356
2734
|
});
|
|
2357
2735
|
route("POST", "/customer/addresses", "customer:write", ({ headers, body }) => {
|
|
2358
2736
|
const c = requireCustomer(headers);
|
|
2359
|
-
const
|
|
2360
|
-
state.addresses.set(c.id, [...state.addresses.get(c.id) ?? [],
|
|
2361
|
-
if (!c.default_address_id) c.default_address_id =
|
|
2362
|
-
return { status: 201, body:
|
|
2737
|
+
const address2 = { ...validateAddress(body), id: nextId("addr") };
|
|
2738
|
+
state.addresses.set(c.id, [...state.addresses.get(c.id) ?? [], address2]);
|
|
2739
|
+
if (!c.default_address_id) c.default_address_id = address2.id;
|
|
2740
|
+
return { status: 201, body: address2, cache: "private" };
|
|
2363
2741
|
});
|
|
2364
2742
|
route("PATCH", "/customer/addresses/:id", "customer:write", ({ headers, params, body }) => {
|
|
2365
2743
|
const c = requireCustomer(headers);
|