@thorprovider/adapters 4.2.0 → 4.2.2
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/.turbo/turbo-build.log +18 -18
- package/.turbo/turbo-test.log +1 -0
- package/.turbo/turbo-type-check.log +0 -0
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +12 -0
- package/PROVIDER_IMPLEMENTATION_ANALYSIS.md +659 -0
- package/README.md +22 -3
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.mts +140 -2
- package/dist/index.d.ts +140 -2
- package/dist/index.js +1075 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1070 -0
- package/dist/index.mjs.map +1 -1
- package/docs/RETRY_SOLUTION_SUMMARY.md +26 -11
- package/docs/authentication.md +89 -62
- package/docs/capabilities-system.md +100 -38
- package/docs/category-customization.md +37 -29
- package/docs/fetch-reliability-solution.md +127 -278
- package/docs/getting-started.md +43 -29
- package/docs/profile-management.md +68 -38
- package/docs/providers-overview.md +14 -0
- package/docs/services-overview.md +20 -2
- package/docs/update-documentation.md +10 -8
- package/package.json +11 -6
- package/publish.log +93 -0
- package/quick-reference.md +1 -0
- package/src/factory/index.ts +4 -0
- package/src/index.ts +7 -0
- package/src/providers/index.ts +3 -0
- package/src/providers/medusa/admin.test.ts +404 -0
- package/src/providers/medusa/admin.ts +442 -0
- package/src/providers/medusa/auth.test.ts +95 -0
- package/src/providers/medusa/index.test.ts +392 -0
- package/src/providers/medusa/live-smoke.test.ts +412 -0
- package/src/providers/stelorder/client.test-helpers.ts +3 -0
- package/src/providers/stelorder/client.test.ts +58 -0
- package/src/providers/stelorder/client.ts +326 -0
- package/src/providers/stelorder/index.test.ts +48 -0
- package/src/providers/stelorder/index.ts +685 -0
- package/src/providers/stelorder/live-smoke.test.ts +54 -0
- package/src/providers/stelorder/transforms.test.ts +103 -0
- package/src/providers/stelorder/transforms.ts +631 -0
- package/src/resilience/README.md +70 -48
- package/src/types/capabilities.types.ts +51 -0
- package/vitest.config.ts +13 -0
- package/vitest.live.config.ts +18 -0
- package/vitest.setup.ts +6 -0
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Collection,
|
|
3
|
+
Customer,
|
|
4
|
+
Address,
|
|
5
|
+
FulfillmentStatus,
|
|
6
|
+
Image,
|
|
7
|
+
InventoryLevel,
|
|
8
|
+
Money,
|
|
9
|
+
Order,
|
|
10
|
+
OrderItem,
|
|
11
|
+
OrderStatus,
|
|
12
|
+
PaymentMethod,
|
|
13
|
+
PaymentMethodType,
|
|
14
|
+
PaymentStatus,
|
|
15
|
+
Product,
|
|
16
|
+
ProductCategory,
|
|
17
|
+
ProductVariant,
|
|
18
|
+
ShippingMethod,
|
|
19
|
+
StockLocation,
|
|
20
|
+
} from '@thorprovider/types';
|
|
21
|
+
|
|
22
|
+
export interface StelResource {
|
|
23
|
+
id?: number;
|
|
24
|
+
path?: string;
|
|
25
|
+
deleted?: boolean;
|
|
26
|
+
'utc-last-modification-date'?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface StelItemImage {
|
|
30
|
+
'item-image-path'?: string;
|
|
31
|
+
order?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface StelItemRate {
|
|
35
|
+
'rate-id'?: number;
|
|
36
|
+
'rate-path'?: string;
|
|
37
|
+
price?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface StelProductWarehouse {
|
|
41
|
+
'warehouse-id'?: number;
|
|
42
|
+
'warehouse-path'?: string;
|
|
43
|
+
'minimum-stock'?: number;
|
|
44
|
+
'real-stock'?: number;
|
|
45
|
+
'virtual-stock'?: number;
|
|
46
|
+
location?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface StelProduct extends StelResource {
|
|
50
|
+
name?: string;
|
|
51
|
+
description?: string;
|
|
52
|
+
reference?: string;
|
|
53
|
+
'full-reference'?: string;
|
|
54
|
+
barcode?: string;
|
|
55
|
+
'sales-price'?: number;
|
|
56
|
+
'purchase-price'?: number;
|
|
57
|
+
'product-category-id'?: number;
|
|
58
|
+
'product-category-path'?: string;
|
|
59
|
+
'real-stock'?: number;
|
|
60
|
+
'virtual-stock'?: number;
|
|
61
|
+
'minimum-stock'?: number;
|
|
62
|
+
'stock-enabled'?: boolean;
|
|
63
|
+
inactive?: boolean;
|
|
64
|
+
promotional?: boolean;
|
|
65
|
+
'creation-date'?: string;
|
|
66
|
+
'item-images'?: StelItemImage[];
|
|
67
|
+
'item-rates'?: StelItemRate[];
|
|
68
|
+
'product-warehouses'?: StelProductWarehouse[];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface StelProductCategory extends StelResource {
|
|
72
|
+
name?: string;
|
|
73
|
+
description?: string;
|
|
74
|
+
slug?: string;
|
|
75
|
+
'parent-category-id'?: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface StelClient extends StelResource {
|
|
79
|
+
name?: string;
|
|
80
|
+
'business-name'?: string;
|
|
81
|
+
email?: string;
|
|
82
|
+
phone?: string;
|
|
83
|
+
mobile?: string;
|
|
84
|
+
'rate-id'?: number;
|
|
85
|
+
'payment-option-id'?: number;
|
|
86
|
+
'payment-term-id'?: number;
|
|
87
|
+
'delivery-option-id'?: number;
|
|
88
|
+
'main-address'?: StelAddress;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface StelAddress extends StelResource {
|
|
92
|
+
name?: string;
|
|
93
|
+
'first-name'?: string;
|
|
94
|
+
'last-name'?: string;
|
|
95
|
+
company?: string;
|
|
96
|
+
'address-data'?: string;
|
|
97
|
+
'postal-code'?: string;
|
|
98
|
+
'city-town'?: string;
|
|
99
|
+
province?: string;
|
|
100
|
+
'country-code'?: string;
|
|
101
|
+
phone?: string;
|
|
102
|
+
email?: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface StelDocumentLine {
|
|
106
|
+
'item-id'?: number;
|
|
107
|
+
'item-name'?: string;
|
|
108
|
+
'item-description'?: string;
|
|
109
|
+
'item-base-price'?: number;
|
|
110
|
+
'primary-tax-percentage'?: number;
|
|
111
|
+
'discount-percentage'?: number;
|
|
112
|
+
units?: number;
|
|
113
|
+
'line-type'?: 'ITEM' | 'DOCUMENT' | 'SECTION' | 'COMPONENT';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface StelSalesOrder extends StelResource {
|
|
117
|
+
reference?: string;
|
|
118
|
+
status?: string;
|
|
119
|
+
email?: string;
|
|
120
|
+
'client-id'?: number;
|
|
121
|
+
'payment-option-id'?: number;
|
|
122
|
+
'payment-term-id'?: number;
|
|
123
|
+
'delivery-option-id'?: number;
|
|
124
|
+
'creation-date'?: string;
|
|
125
|
+
'total-base'?: number;
|
|
126
|
+
'total-tax'?: number;
|
|
127
|
+
total?: number;
|
|
128
|
+
'document-lines'?: StelDocumentLine[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface StelPaymentOption extends StelResource {
|
|
132
|
+
name?: string;
|
|
133
|
+
description?: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface StelPaymentTerm extends StelResource {
|
|
137
|
+
name?: string;
|
|
138
|
+
description?: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface StelDeliveryOption extends StelResource {
|
|
142
|
+
name?: string;
|
|
143
|
+
description?: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface StelWarehouse extends StelResource {
|
|
147
|
+
name?: string;
|
|
148
|
+
description?: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function normalizeStelCollection<T>(payload: T | T[] | null | undefined): T[] {
|
|
152
|
+
if (!payload) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return Array.isArray(payload) ? payload : [payload];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function normalizeStelEntity<T>(payload: T | T[] | null | undefined): T | undefined {
|
|
160
|
+
if (!payload) {
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return Array.isArray(payload) ? payload[0] : payload;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function transformStelMoney(
|
|
168
|
+
amount: number | string | null | undefined,
|
|
169
|
+
currencyCode: string = 'USD',
|
|
170
|
+
): Money {
|
|
171
|
+
const normalizedAmount = typeof amount === 'string' ? Number.parseFloat(amount) : amount ?? 0;
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
amount: Number.isFinite(normalizedAmount) ? normalizedAmount.toFixed(2) : '0.00',
|
|
175
|
+
currencyCode: currencyCode.toUpperCase(),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function transformStelImage(url: string | null | undefined, altText: string = ''): Image | undefined {
|
|
180
|
+
const normalizedUrl = url?.trim();
|
|
181
|
+
|
|
182
|
+
if (!normalizedUrl) {
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
url: normalizedUrl,
|
|
188
|
+
altText,
|
|
189
|
+
width: 800,
|
|
190
|
+
height: 800,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function normalizeStelDate(value: string | null | undefined): string {
|
|
195
|
+
if (!value) {
|
|
196
|
+
return new Date().toISOString();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const parsed = new Date(value);
|
|
200
|
+
return Number.isNaN(parsed.getTime()) ? new Date().toISOString() : parsed.toISOString();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function extractStelIdFromPath(path: string | null | undefined): string | undefined {
|
|
204
|
+
if (!path) {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const match = path.match(/\/([^/]+)$/);
|
|
209
|
+
return match?.[1];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function toStelStringId(value: string | number | null | undefined): string {
|
|
213
|
+
if (value === null || value === undefined) {
|
|
214
|
+
return '';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return String(value);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function slugify(value: string): string {
|
|
221
|
+
return value
|
|
222
|
+
.normalize('NFKD')
|
|
223
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
224
|
+
.toLowerCase()
|
|
225
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
226
|
+
.replace(/^-+|-+$/g, '');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function buildStelHandle(...candidates: Array<string | number | null | undefined>): string {
|
|
230
|
+
for (const candidate of candidates) {
|
|
231
|
+
if (candidate === null || candidate === undefined) {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const normalized = slugify(String(candidate).trim());
|
|
236
|
+
if (normalized) {
|
|
237
|
+
return normalized;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return 'stel-item';
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// Product transforms
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
function transformStelProductImages(product: StelProduct): Image[] {
|
|
249
|
+
const images: Image[] = [];
|
|
250
|
+
|
|
251
|
+
if (!product['item-images']) {
|
|
252
|
+
return images;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const sorted = [...product['item-images']].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
256
|
+
|
|
257
|
+
for (const img of sorted) {
|
|
258
|
+
const transformed = transformStelImage(img['item-image-path'], product.name ?? '');
|
|
259
|
+
if (transformed) {
|
|
260
|
+
images.push(transformed);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return images;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function deriveStelStockStatus(product: StelProduct): 'in_stock' | 'out_of_stock' | 'low_stock' {
|
|
268
|
+
if (!product['stock-enabled']) {
|
|
269
|
+
return 'in_stock';
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const realStock = product['real-stock'] ?? 0;
|
|
273
|
+
const minimumStock = product['minimum-stock'] ?? 0;
|
|
274
|
+
|
|
275
|
+
if (realStock <= 0) {
|
|
276
|
+
return 'out_of_stock';
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (realStock <= minimumStock) {
|
|
280
|
+
return 'low_stock';
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return 'in_stock';
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Build a single synthetic variant from a STEL product.
|
|
288
|
+
* STEL products have no variant concept — every product is a single SKU.
|
|
289
|
+
*/
|
|
290
|
+
function buildSyntheticVariant(product: StelProduct, currencyCode: string): ProductVariant {
|
|
291
|
+
const price = transformStelMoney(product['sales-price'], currencyCode);
|
|
292
|
+
const stockStatus = deriveStelStockStatus(product);
|
|
293
|
+
const quantityAvailable = product['stock-enabled'] ? (product['real-stock'] ?? 0) : undefined;
|
|
294
|
+
const image = product['item-images']?.[0]
|
|
295
|
+
? transformStelImage(product['item-images'][0]['item-image-path'], product.name ?? '')
|
|
296
|
+
: undefined;
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
id: toStelStringId(product.id),
|
|
300
|
+
title: 'Default',
|
|
301
|
+
sku: product.barcode ?? product['full-reference'] ?? undefined,
|
|
302
|
+
availableForSale: stockStatus !== 'out_of_stock',
|
|
303
|
+
selectedOptions: [],
|
|
304
|
+
price,
|
|
305
|
+
compareAtPrice: undefined,
|
|
306
|
+
quantityAvailable,
|
|
307
|
+
stockStatus,
|
|
308
|
+
image,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function transformStelProduct(raw: StelProduct, currencyCode: string): Product {
|
|
313
|
+
const id = toStelStringId(raw.id);
|
|
314
|
+
const handle = buildStelHandle(raw['full-reference'], raw.reference, raw.name, raw.id);
|
|
315
|
+
const price = transformStelMoney(raw['sales-price'], currencyCode);
|
|
316
|
+
const images = transformStelProductImages(raw);
|
|
317
|
+
const variant = buildSyntheticVariant(raw, currencyCode);
|
|
318
|
+
const now = new Date().toISOString();
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
id,
|
|
322
|
+
handle,
|
|
323
|
+
title: raw.name ?? '',
|
|
324
|
+
description: raw.description ?? '',
|
|
325
|
+
availableForSale: variant.availableForSale,
|
|
326
|
+
options: [],
|
|
327
|
+
variants: [variant],
|
|
328
|
+
featuredImage: images[0],
|
|
329
|
+
images,
|
|
330
|
+
priceRange: {
|
|
331
|
+
minVariantPrice: price,
|
|
332
|
+
maxVariantPrice: price,
|
|
333
|
+
},
|
|
334
|
+
productType: undefined,
|
|
335
|
+
vendor: undefined,
|
|
336
|
+
tags: raw.promotional ? ['promotional'] : [],
|
|
337
|
+
createdAt: normalizeStelDate(raw['creation-date']),
|
|
338
|
+
updatedAt: normalizeStelDate(raw['utc-last-modification-date']) || now,
|
|
339
|
+
metadata: {
|
|
340
|
+
stelFullReference: raw['full-reference'],
|
|
341
|
+
stelReference: raw.reference,
|
|
342
|
+
stelBarcode: raw.barcode,
|
|
343
|
+
stelCategoryId: raw['product-category-id'],
|
|
344
|
+
stelInactive: raw.inactive,
|
|
345
|
+
stelPromotional: raw.promotional,
|
|
346
|
+
stelPurchasePrice: raw['purchase-price'],
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ---------------------------------------------------------------------------
|
|
352
|
+
// Category transforms
|
|
353
|
+
// ---------------------------------------------------------------------------
|
|
354
|
+
|
|
355
|
+
export function transformStelCategory(raw: StelProductCategory): ProductCategory {
|
|
356
|
+
const id = toStelStringId(raw.id);
|
|
357
|
+
const handle = buildStelHandle(raw.slug, raw.name, raw.id);
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
id,
|
|
361
|
+
name: raw.name ?? '',
|
|
362
|
+
handle,
|
|
363
|
+
description: raw.description ?? undefined,
|
|
364
|
+
parentCategoryId: raw['parent-category-id']
|
|
365
|
+
? toStelStringId(raw['parent-category-id'])
|
|
366
|
+
: undefined,
|
|
367
|
+
metadata: {
|
|
368
|
+
stelSlug: raw.slug,
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ---------------------------------------------------------------------------
|
|
374
|
+
// Collection transforms (alias categories as collections)
|
|
375
|
+
// ---------------------------------------------------------------------------
|
|
376
|
+
|
|
377
|
+
export function transformStelCategoryToCollection(raw: StelProductCategory): Collection {
|
|
378
|
+
const id = toStelStringId(raw.id);
|
|
379
|
+
const handle = buildStelHandle(raw.slug, raw.name, raw.id);
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
id,
|
|
383
|
+
handle,
|
|
384
|
+
title: raw.name ?? '',
|
|
385
|
+
description: raw.description ?? '',
|
|
386
|
+
path: `/collections/${handle}`,
|
|
387
|
+
updatedAt: normalizeStelDate(raw['utc-last-modification-date']),
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// ---------------------------------------------------------------------------
|
|
392
|
+
// Inventory / warehouse transforms
|
|
393
|
+
// ---------------------------------------------------------------------------
|
|
394
|
+
|
|
395
|
+
export function transformStelWarehouseToStockLocation(raw: StelWarehouse): StockLocation {
|
|
396
|
+
return {
|
|
397
|
+
id: toStelStringId(raw.id),
|
|
398
|
+
name: raw.name ?? 'Warehouse',
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export function transformStelProductWarehouseToInventoryLevel(
|
|
403
|
+
pw: StelProductWarehouse,
|
|
404
|
+
): InventoryLevel {
|
|
405
|
+
return {
|
|
406
|
+
locationId: toStelStringId(pw['warehouse-id']),
|
|
407
|
+
locationName: pw['warehouse-path'] ?? `Warehouse ${pw['warehouse-id'] ?? 'unknown'}`,
|
|
408
|
+
availableQuantity: pw['real-stock'] ?? 0,
|
|
409
|
+
reservedQuantity: Math.max(0, (pw['virtual-stock'] ?? 0) - (pw['real-stock'] ?? 0)),
|
|
410
|
+
stockedQuantity: pw['virtual-stock'] ?? pw['real-stock'] ?? 0,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// ---------------------------------------------------------------------------
|
|
415
|
+
// Customer / Address transforms
|
|
416
|
+
// ---------------------------------------------------------------------------
|
|
417
|
+
|
|
418
|
+
function splitStelName(fullName: string | null | undefined): { firstName: string; lastName: string } {
|
|
419
|
+
if (!fullName?.trim()) {
|
|
420
|
+
return { firstName: '', lastName: '' };
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const parts = fullName.trim().split(/\s+/);
|
|
424
|
+
const firstName = parts[0] ?? '';
|
|
425
|
+
const lastName = parts.length > 1 ? parts.slice(1).join(' ') : '';
|
|
426
|
+
return { firstName, lastName };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export function transformStelAddress(raw: StelAddress): Address {
|
|
430
|
+
const firstName = raw['first-name'] ?? '';
|
|
431
|
+
const lastName = raw['last-name'] ?? '';
|
|
432
|
+
|
|
433
|
+
// If first/last not present, attempt to split the 'name' field
|
|
434
|
+
const nameParts = !firstName && !lastName ? splitStelName(raw.name) : { firstName, lastName };
|
|
435
|
+
|
|
436
|
+
return {
|
|
437
|
+
id: raw.id ? toStelStringId(raw.id) : undefined,
|
|
438
|
+
firstName: nameParts.firstName,
|
|
439
|
+
lastName: nameParts.lastName,
|
|
440
|
+
company: raw.company ?? undefined,
|
|
441
|
+
address1: raw['address-data'] ?? '',
|
|
442
|
+
city: raw['city-town'] ?? '',
|
|
443
|
+
province: raw.province ?? undefined,
|
|
444
|
+
postalCode: raw['postal-code'] ?? '',
|
|
445
|
+
countryCode: raw['country-code'] ?? '',
|
|
446
|
+
phone: raw.phone ?? undefined,
|
|
447
|
+
isDefault: true,
|
|
448
|
+
metadata: {
|
|
449
|
+
stelEmail: raw.email,
|
|
450
|
+
},
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export function transformStelClient(raw: StelClient): Customer {
|
|
455
|
+
const { firstName, lastName } = splitStelName(raw.name ?? raw['business-name']);
|
|
456
|
+
|
|
457
|
+
return {
|
|
458
|
+
id: toStelStringId(raw.id),
|
|
459
|
+
email: raw.email ?? '',
|
|
460
|
+
firstName: firstName || null,
|
|
461
|
+
lastName: lastName || null,
|
|
462
|
+
phone: raw.phone ?? raw.mobile ?? null,
|
|
463
|
+
hasAccount: true,
|
|
464
|
+
createdAt: normalizeStelDate(raw['utc-last-modification-date']),
|
|
465
|
+
updatedAt: normalizeStelDate(raw['utc-last-modification-date']),
|
|
466
|
+
metadata: {
|
|
467
|
+
stelBusinessName: raw['business-name'],
|
|
468
|
+
stelRateId: raw['rate-id'],
|
|
469
|
+
stelPaymentOptionId: raw['payment-option-id'],
|
|
470
|
+
stelPaymentTermId: raw['payment-term-id'],
|
|
471
|
+
stelDeliveryOptionId: raw['delivery-option-id'],
|
|
472
|
+
},
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// ---------------------------------------------------------------------------
|
|
477
|
+
// Order transforms
|
|
478
|
+
// ---------------------------------------------------------------------------
|
|
479
|
+
|
|
480
|
+
function deriveStelOrderStatus(raw: StelSalesOrder): OrderStatus {
|
|
481
|
+
if (raw.deleted) {
|
|
482
|
+
return 'cancelled';
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const status = (raw.status ?? '').toLowerCase();
|
|
486
|
+
|
|
487
|
+
if (status.includes('cancel') || status.includes('anulad')) {
|
|
488
|
+
return 'cancelled';
|
|
489
|
+
}
|
|
490
|
+
if (status.includes('complet') || status.includes('cerrad') || status.includes('settled')) {
|
|
491
|
+
return 'completed';
|
|
492
|
+
}
|
|
493
|
+
if (status.includes('process') || status.includes('confirm')) {
|
|
494
|
+
return 'processing';
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return 'pending';
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function deriveStelPaymentStatus(raw: StelSalesOrder): PaymentStatus {
|
|
501
|
+
const orderStatus = deriveStelOrderStatus(raw);
|
|
502
|
+
|
|
503
|
+
if (orderStatus === 'completed') {
|
|
504
|
+
return 'paid';
|
|
505
|
+
}
|
|
506
|
+
if (orderStatus === 'cancelled') {
|
|
507
|
+
return 'pending';
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return 'pending';
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function deriveStelFulfillmentStatus(raw: StelSalesOrder): FulfillmentStatus {
|
|
514
|
+
const orderStatus = deriveStelOrderStatus(raw);
|
|
515
|
+
|
|
516
|
+
if (orderStatus === 'completed') {
|
|
517
|
+
return 'fulfilled';
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return 'unfulfilled';
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function transformStelDocumentLine(
|
|
524
|
+
line: StelDocumentLine,
|
|
525
|
+
index: number,
|
|
526
|
+
currencyCode: string,
|
|
527
|
+
): OrderItem {
|
|
528
|
+
const unitPrice = line['item-base-price'] ?? 0;
|
|
529
|
+
const quantity = line.units ?? 1;
|
|
530
|
+
const lineTotal = unitPrice * quantity;
|
|
531
|
+
|
|
532
|
+
return {
|
|
533
|
+
id: toStelStringId(line['item-id'] ?? index),
|
|
534
|
+
title: line['item-name'] ?? '',
|
|
535
|
+
variantTitle: undefined,
|
|
536
|
+
quantity,
|
|
537
|
+
price: transformStelMoney(unitPrice, currencyCode),
|
|
538
|
+
total: transformStelMoney(lineTotal, currencyCode),
|
|
539
|
+
sku: undefined,
|
|
540
|
+
productId: line['item-id'] ? toStelStringId(line['item-id']) : undefined,
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
export function transformStelSalesOrder(raw: StelSalesOrder, currencyCode: string): Order {
|
|
545
|
+
const id = toStelStringId(raw.id);
|
|
546
|
+
const now = new Date().toISOString();
|
|
547
|
+
const items = (raw['document-lines'] ?? [])
|
|
548
|
+
.filter((line) => line['line-type'] === 'ITEM' || !line['line-type'])
|
|
549
|
+
.map((line, i) => transformStelDocumentLine(line, i, currencyCode));
|
|
550
|
+
|
|
551
|
+
const status = deriveStelOrderStatus(raw);
|
|
552
|
+
|
|
553
|
+
return {
|
|
554
|
+
id,
|
|
555
|
+
orderNumber: raw.reference ?? id,
|
|
556
|
+
email: raw.email ?? '',
|
|
557
|
+
status,
|
|
558
|
+
paymentStatus: deriveStelPaymentStatus(raw),
|
|
559
|
+
fulfillmentStatus: deriveStelFulfillmentStatus(raw),
|
|
560
|
+
customerId: raw['client-id'] ? toStelStringId(raw['client-id']) : undefined,
|
|
561
|
+
items,
|
|
562
|
+
subtotal: transformStelMoney(raw['total-base'], currencyCode),
|
|
563
|
+
total: transformStelMoney(raw.total, currencyCode),
|
|
564
|
+
tax: raw['total-tax'] != null ? transformStelMoney(raw['total-tax'], currencyCode) : undefined,
|
|
565
|
+
createdAt: normalizeStelDate(raw['creation-date']),
|
|
566
|
+
updatedAt: normalizeStelDate(raw['utc-last-modification-date']) || now,
|
|
567
|
+
cancelledAt: status === 'cancelled' ? normalizeStelDate(raw['utc-last-modification-date']) : undefined,
|
|
568
|
+
completedAt: status === 'completed' ? normalizeStelDate(raw['utc-last-modification-date']) : undefined,
|
|
569
|
+
metadata: {
|
|
570
|
+
stelReference: raw.reference,
|
|
571
|
+
stelStatus: raw.status,
|
|
572
|
+
stelPaymentOptionId: raw['payment-option-id'],
|
|
573
|
+
stelPaymentTermId: raw['payment-term-id'],
|
|
574
|
+
stelDeliveryOptionId: raw['delivery-option-id'],
|
|
575
|
+
},
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// ---------------------------------------------------------------------------
|
|
580
|
+
// Payment / delivery transforms
|
|
581
|
+
// ---------------------------------------------------------------------------
|
|
582
|
+
|
|
583
|
+
function inferPaymentMethodType(name: string | null | undefined): PaymentMethodType {
|
|
584
|
+
if (!name) {
|
|
585
|
+
return 'other';
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const lower = name.toLowerCase();
|
|
589
|
+
|
|
590
|
+
if (lower.includes('transfer') || lower.includes('bank') || lower.includes('sepa') || lower.includes('iban')) {
|
|
591
|
+
return 'bank_transfer';
|
|
592
|
+
}
|
|
593
|
+
if (lower.includes('card') || lower.includes('tarjeta') || lower.includes('visa') || lower.includes('master')) {
|
|
594
|
+
return 'card';
|
|
595
|
+
}
|
|
596
|
+
if (lower.includes('paypal') || lower.includes('wallet') || lower.includes('apple') || lower.includes('google')) {
|
|
597
|
+
return 'wallet';
|
|
598
|
+
}
|
|
599
|
+
if (lower.includes('cash') || lower.includes('contrar') || lower.includes('efectivo') || lower.includes('cod')) {
|
|
600
|
+
return 'cash_on_delivery';
|
|
601
|
+
}
|
|
602
|
+
if (lower.includes('crypto') || lower.includes('bitcoin')) {
|
|
603
|
+
return 'crypto';
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
return 'other';
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
export function transformStelPaymentOption(raw: StelPaymentOption): PaymentMethod {
|
|
610
|
+
return {
|
|
611
|
+
id: toStelStringId(raw.id),
|
|
612
|
+
name: raw.name ?? 'Payment Option',
|
|
613
|
+
type: inferPaymentMethodType(raw.name),
|
|
614
|
+
isEnabled: !raw.deleted,
|
|
615
|
+
requiresSetup: false,
|
|
616
|
+
description: raw.description ?? undefined,
|
|
617
|
+
metadata: {
|
|
618
|
+
stelPath: raw.path,
|
|
619
|
+
source: 'paymentOption',
|
|
620
|
+
},
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
export function transformStelDeliveryOption(raw: StelDeliveryOption, currencyCode: string): ShippingMethod {
|
|
625
|
+
return {
|
|
626
|
+
id: toStelStringId(raw.id),
|
|
627
|
+
name: raw.name ?? 'Delivery Option',
|
|
628
|
+
price: transformStelMoney(0, currencyCode),
|
|
629
|
+
description: raw.description ?? undefined,
|
|
630
|
+
};
|
|
631
|
+
}
|