@thorprovider/adapters 4.1.0 → 4.2.1

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.
@@ -0,0 +1,685 @@
1
+ import type {
2
+ BackendCapabilities,
3
+ Cart,
4
+ CartLineInput,
5
+ CartLineUpdate,
6
+ Collection,
7
+ CollectionProductsOptions,
8
+ Country,
9
+ Customer,
10
+ FulfillmentOption,
11
+ GetCategoriesOptions,
12
+ GetCollectionsOptions,
13
+ GetCustomersOptions,
14
+ GetOrdersOptions,
15
+ GetProductsOptions,
16
+ InventoryLevel,
17
+ Order,
18
+ PaymentMethod,
19
+ PaymentMethodsOptions,
20
+ Product,
21
+ ProductCategory,
22
+ ShippingMethod,
23
+ StockLocation,
24
+ StockValidation,
25
+ StorefrontContext,
26
+ } from '@thorprovider/types';
27
+ import type { CommerceProvider } from '../../types/provider';
28
+ import { STELORDER_CAPABILITIES } from '../../types/capabilities.types';
29
+ import { ProviderAPIError } from '../../core/errors';
30
+ import { createLogger } from '../../core/logger';
31
+ import { StelOrderClient, type StelOrderProviderConfig } from './client';
32
+ import {
33
+ buildStelHandle,
34
+ transformStelCategory,
35
+ transformStelCategoryToCollection,
36
+ transformStelClient,
37
+ transformStelDeliveryOption,
38
+ transformStelPaymentOption,
39
+ transformStelProduct,
40
+ transformStelProductWarehouseToInventoryLevel,
41
+ transformStelSalesOrder,
42
+ transformStelWarehouseToStockLocation,
43
+ type StelClient,
44
+ type StelDeliveryOption,
45
+ type StelPaymentOption,
46
+ type StelProduct,
47
+ type StelProductCategory,
48
+ type StelSalesOrder,
49
+ type StelWarehouse,
50
+ } from './transforms';
51
+
52
+ export type { StelOrderProviderConfig } from './client';
53
+ export { StelOrderClient } from './client';
54
+ export * from './transforms';
55
+
56
+ export class StelOrderProvider implements CommerceProvider {
57
+ readonly name = 'stelorder';
58
+ readonly version = '1.0.0';
59
+ readonly capabilities: BackendCapabilities = STELORDER_CAPABILITIES;
60
+
61
+ private readonly client: StelOrderClient;
62
+ private readonly logger: ReturnType<typeof createLogger>;
63
+ private readonly currencyCode: string;
64
+ private readonly storefrontId: string;
65
+ private readonly companyName: string;
66
+
67
+ constructor(config: StelOrderProviderConfig) {
68
+ this.client = new StelOrderClient(config);
69
+ this.logger = createLogger('StelOrderProvider', config.debug);
70
+ this.currencyCode = config.currencyCode ?? 'USD';
71
+ this.storefrontId = config.storefrontId ?? 'stelorder-primary';
72
+ this.companyName = config.companyName ?? 'Stel Order';
73
+
74
+ this.logger.info('Initializing Stel Order provider', {
75
+ storefrontId: this.storefrontId,
76
+ companyName: this.companyName,
77
+ currencyCode: this.currencyCode,
78
+ });
79
+ }
80
+
81
+ // ============================================
82
+ // Product Operations
83
+ // ============================================
84
+
85
+ async getProduct(handle: string, _options?: { regionId?: string }): Promise<Product | undefined> {
86
+ this.logger.debug('getProduct', { handle });
87
+
88
+ try {
89
+ // Attempt direct ID lookup if handle looks numeric
90
+ if (/^\d+$/.test(handle)) {
91
+ const raw = await this.client.getOne<StelProduct>('products', handle);
92
+ return raw ? transformStelProduct(raw, this.currencyCode) : undefined;
93
+ }
94
+
95
+ // Otherwise scan products to match by handle (slugified full-reference)
96
+ const rawProducts = await this.client.list<StelProduct>('products', { limit: 100 });
97
+
98
+ for (const raw of rawProducts) {
99
+ const productHandle = buildStelHandle(raw['full-reference'], raw.reference, raw.name, raw.id);
100
+ if (productHandle === handle) {
101
+ return transformStelProduct(raw, this.currencyCode);
102
+ }
103
+ }
104
+
105
+ return undefined;
106
+ } catch (error) {
107
+ this.logger.error('getProduct failed', { handle, error });
108
+ throw this.wrapError('getProduct', error);
109
+ }
110
+ }
111
+
112
+ async getProducts(options?: GetProductsOptions): Promise<Product[]> {
113
+ this.logger.debug('getProducts', { options });
114
+
115
+ try {
116
+ const query: Record<string, string | number | boolean> = {};
117
+
118
+ if (options?.first) {
119
+ query.limit = options.first;
120
+ }
121
+ if (options?.skip) {
122
+ query.start = options.skip;
123
+ }
124
+ if (typeof options?.after === 'number') {
125
+ query.start = options.after;
126
+ }
127
+ if (options?.sortKey) {
128
+ query.sort = options.reverse ? `-${options.sortKey}` : options.sortKey;
129
+ }
130
+ if (options?.categoryIds?.length) {
131
+ query['product-category-id'] = `in:${options.categoryIds.join(',')}`;
132
+ }
133
+
134
+ const rawProducts = await this.client.list<StelProduct>('products', query);
135
+
136
+ let products = rawProducts
137
+ .filter((p) => !p.deleted && !p.inactive)
138
+ .map((p) => transformStelProduct(p, this.currencyCode));
139
+
140
+ // Client-side text search (STEL has no fulltext search)
141
+ if (options?.query) {
142
+ const needle = options.query.toLowerCase();
143
+ products = products.filter(
144
+ (p) =>
145
+ p.title.toLowerCase().includes(needle) ||
146
+ p.description.toLowerCase().includes(needle) ||
147
+ p.handle.includes(needle),
148
+ );
149
+ }
150
+
151
+ return products;
152
+ } catch (error) {
153
+ this.logger.error('getProducts failed', { options, error });
154
+ throw this.wrapError('getProducts', error);
155
+ }
156
+ }
157
+
158
+ async getProductRecommendations(_productId: string): Promise<Product[]> {
159
+ // STEL has no recommendation engine — return empty array
160
+ return [];
161
+ }
162
+
163
+ // ============================================
164
+ // Category Operations
165
+ // ============================================
166
+
167
+ async getCategory(idOrHandle: string): Promise<ProductCategory | undefined> {
168
+ this.logger.debug('getCategory', { idOrHandle });
169
+
170
+ try {
171
+ // Direct ID lookup
172
+ if (/^\d+$/.test(idOrHandle)) {
173
+ const raw = await this.client.getOne<StelProductCategory>('productCategories', idOrHandle);
174
+ return raw ? transformStelCategory(raw) : undefined;
175
+ }
176
+
177
+ // Scan by handle
178
+ const rawCategories = await this.client.list<StelProductCategory>('productCategories');
179
+
180
+ for (const raw of rawCategories) {
181
+ const catHandle = buildStelHandle(raw.slug, raw.name, raw.id);
182
+ if (catHandle === idOrHandle) {
183
+ return transformStelCategory(raw);
184
+ }
185
+ }
186
+
187
+ return undefined;
188
+ } catch (error) {
189
+ this.logger.error('getCategory failed', { idOrHandle, error });
190
+ throw this.wrapError('getCategory', error);
191
+ }
192
+ }
193
+
194
+ async getCategories(options?: GetCategoriesOptions): Promise<ProductCategory[]> {
195
+ this.logger.debug('getCategories', { options });
196
+
197
+ try {
198
+ if (options?.customFetcher) {
199
+ return options.customFetcher(options);
200
+ }
201
+
202
+ const query: Record<string, string | number> = {};
203
+
204
+ if (options?.limit) {
205
+ query.limit = options.limit;
206
+ }
207
+ if (options?.parentCategoryId) {
208
+ query['parent-category-id'] = options.parentCategoryId;
209
+ }
210
+
211
+ const rawCategories = await this.client.list<StelProductCategory>('productCategories', query);
212
+ let categories = rawCategories
213
+ .filter((c) => !c.deleted)
214
+ .map(transformStelCategory);
215
+
216
+ if (options?.query) {
217
+ const needle = options.query.toLowerCase();
218
+ categories = categories.filter(
219
+ (c) =>
220
+ c.name.toLowerCase().includes(needle) ||
221
+ c.handle.includes(needle),
222
+ );
223
+ }
224
+
225
+ return categories;
226
+ } catch (error) {
227
+ this.logger.error('getCategories failed', { options, error });
228
+ throw this.wrapError('getCategories', error);
229
+ }
230
+ }
231
+
232
+ async getCategoryProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]> {
233
+ this.logger.debug('getCategoryProducts', { handle, options });
234
+
235
+ try {
236
+ const category = await this.getCategory(handle);
237
+
238
+ if (!category) {
239
+ return [];
240
+ }
241
+
242
+ return this.getProducts({
243
+ categoryIds: [category.id],
244
+ first: options?.first,
245
+ sortKey: options?.sortKey,
246
+ reverse: options?.reverse,
247
+ after: options?.after,
248
+ });
249
+ } catch (error) {
250
+ this.logger.error('getCategoryProducts failed', { handle, error });
251
+ throw this.wrapError('getCategoryProducts', error);
252
+ }
253
+ }
254
+
255
+ // ============================================
256
+ // Collection Operations (alias categories)
257
+ // ============================================
258
+
259
+ async getCollection(handle: string): Promise<Collection | undefined> {
260
+ this.logger.debug('getCollection', { handle });
261
+
262
+ try {
263
+ if (/^\d+$/.test(handle)) {
264
+ const raw = await this.client.getOne<StelProductCategory>('productCategories', handle);
265
+ return raw ? transformStelCategoryToCollection(raw) : undefined;
266
+ }
267
+
268
+ const rawCategories = await this.client.list<StelProductCategory>('productCategories');
269
+
270
+ for (const raw of rawCategories) {
271
+ const catHandle = buildStelHandle(raw.slug, raw.name, raw.id);
272
+ if (catHandle === handle) {
273
+ return transformStelCategoryToCollection(raw);
274
+ }
275
+ }
276
+
277
+ return undefined;
278
+ } catch (error) {
279
+ this.logger.error('getCollection failed', { handle, error });
280
+ throw this.wrapError('getCollection', error);
281
+ }
282
+ }
283
+
284
+ async getCollections(options?: GetCollectionsOptions): Promise<Collection[]> {
285
+ this.logger.debug('getCollections', { options });
286
+
287
+ try {
288
+ const query: Record<string, string | number> = {};
289
+
290
+ if (options?.limit) {
291
+ query.limit = options.limit;
292
+ }
293
+
294
+ const rawCategories = await this.client.list<StelProductCategory>('productCategories', query);
295
+ let collections = rawCategories
296
+ .filter((c) => !c.deleted)
297
+ .map(transformStelCategoryToCollection);
298
+
299
+ if (options?.query) {
300
+ const needle = options.query.toLowerCase();
301
+ collections = collections.filter(
302
+ (c) =>
303
+ c.title.toLowerCase().includes(needle) ||
304
+ c.handle.includes(needle),
305
+ );
306
+ }
307
+
308
+ return collections;
309
+ } catch (error) {
310
+ this.logger.error('getCollections failed', { options, error });
311
+ throw this.wrapError('getCollections', error);
312
+ }
313
+ }
314
+
315
+ async getCollectionProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]> {
316
+ return this.getCategoryProducts(handle, options);
317
+ }
318
+
319
+ // ============================================
320
+ // Inventory Operations
321
+ // ============================================
322
+
323
+ async getStockLocations(_params?: { salesChannelId?: string }): Promise<StockLocation[]> {
324
+ this.logger.debug('getStockLocations');
325
+
326
+ try {
327
+ const rawWarehouses = await this.client.list<StelWarehouse>('warehouses');
328
+ return rawWarehouses
329
+ .filter((w) => !w.deleted)
330
+ .map(transformStelWarehouseToStockLocation);
331
+ } catch (error) {
332
+ this.logger.error('getStockLocations failed', { error });
333
+ throw this.wrapError('getStockLocations', error);
334
+ }
335
+ }
336
+
337
+ async checkInventoryLevels(variantId: string, _salesChannelId?: string): Promise<InventoryLevel[]> {
338
+ this.logger.debug('checkInventoryLevels', { variantId });
339
+
340
+ try {
341
+ // variantId is the product ID (single synthetic variant per product)
342
+ const raw = await this.client.getOne<StelProduct>('products', variantId);
343
+
344
+ if (!raw?.['product-warehouses']?.length) {
345
+ return [];
346
+ }
347
+
348
+ return raw['product-warehouses'].map(transformStelProductWarehouseToInventoryLevel);
349
+ } catch (error) {
350
+ this.logger.error('checkInventoryLevels failed', { variantId, error });
351
+ throw this.wrapError('checkInventoryLevels', error);
352
+ }
353
+ }
354
+
355
+ validateCartStock(_cartId: string): Promise<StockValidation> {
356
+ return this.notSupported('validateCartStock');
357
+ }
358
+
359
+ getFulfillmentOptions(_cartId: string, _salesChannelId: string): Promise<FulfillmentOption[]> {
360
+ return this.notSupported('getFulfillmentOptions');
361
+ }
362
+
363
+ // ============================================
364
+ // Cart Operations (unsupported — STEL is ERP, not storefront)
365
+ // ============================================
366
+
367
+ createCart(_options?: { regionId?: string }): Promise<Cart> {
368
+ return this.notSupported('createCart');
369
+ }
370
+
371
+ getCart(_cartId: string): Promise<Cart | undefined> {
372
+ return this.notSupported('getCart');
373
+ }
374
+
375
+ addToCart(_cartId: string, _lines: CartLineInput[]): Promise<Cart> {
376
+ return this.notSupported('addToCart');
377
+ }
378
+
379
+ removeFromCart(_cartId: string, _lineIds: string[]): Promise<Cart> {
380
+ return this.notSupported('removeFromCart');
381
+ }
382
+
383
+ updateCart(_cartId: string, _lines: CartLineUpdate[]): Promise<Cart> {
384
+ return this.notSupported('updateCart');
385
+ }
386
+
387
+ // ============================================
388
+ // Checkout Operations (unsupported)
389
+ // ============================================
390
+
391
+ completeCheckout(_cartId: string): Promise<Order> {
392
+ return this.notSupported('completeCheckout');
393
+ }
394
+
395
+ getRegionCountries(_regionId: string): Promise<Country[]> {
396
+ return this.notSupported('getRegionCountries');
397
+ }
398
+
399
+ addShippingMethod(_cartId: string, _methodId: string): Promise<Cart> {
400
+ return this.notSupported('addShippingMethod');
401
+ }
402
+
403
+ initiatePaymentSession(_cartId: string, _providerId: string): Promise<void> {
404
+ return this.notSupported('initiatePaymentSession');
405
+ }
406
+
407
+ applyDiscountCode(_cartId: string, _code: string): Promise<Cart> {
408
+ return this.notSupported('applyDiscountCode');
409
+ }
410
+
411
+ removeDiscountCode(_cartId: string, _code: string): Promise<Cart> {
412
+ return this.notSupported('removeDiscountCode');
413
+ }
414
+
415
+ // ============================================
416
+ // Payment Methods
417
+ // ============================================
418
+
419
+ async getPaymentMethods(_options: PaymentMethodsOptions): Promise<PaymentMethod[]> {
420
+ this.logger.debug('getPaymentMethods');
421
+
422
+ try {
423
+ const rawOptions = await this.client.list<StelPaymentOption>('paymentOptions');
424
+ return rawOptions
425
+ .filter((o) => !o.deleted)
426
+ .map(transformStelPaymentOption);
427
+ } catch (error) {
428
+ this.logger.error('getPaymentMethods failed', { error });
429
+ throw this.wrapError('getPaymentMethods', error);
430
+ }
431
+ }
432
+
433
+ // ============================================
434
+ // Shipping / Delivery Methods
435
+ // ============================================
436
+
437
+ async getShippingMethods(_cartId: string): Promise<ShippingMethod[]> {
438
+ this.logger.debug('getShippingMethods (delivery options listing)');
439
+
440
+ try {
441
+ const rawOptions = await this.client.list<StelDeliveryOption>('deliveryOptions');
442
+ return rawOptions
443
+ .filter((o) => !o.deleted)
444
+ .map((o) => transformStelDeliveryOption(o, this.currencyCode));
445
+ } catch (error) {
446
+ this.logger.error('getShippingMethods failed', { error });
447
+ throw this.wrapError('getShippingMethods', error);
448
+ }
449
+ }
450
+
451
+ // ============================================
452
+ // Customer Operations (optional)
453
+ // ============================================
454
+
455
+ async getCustomer(customerId: string): Promise<Customer | undefined> {
456
+ this.logger.debug('getCustomer', { customerId });
457
+
458
+ try {
459
+ const raw = await this.client.getOne<StelClient>('clients', customerId);
460
+ return raw ? transformStelClient(raw) : undefined;
461
+ } catch (error) {
462
+ this.logger.error('getCustomer failed', { customerId, error });
463
+ throw this.wrapError('getCustomer', error);
464
+ }
465
+ }
466
+
467
+ async getCustomers(options?: GetCustomersOptions): Promise<Customer[]> {
468
+ this.logger.debug('getCustomers', { options });
469
+
470
+ try {
471
+ if (options?.customFetcher) {
472
+ return options.customFetcher(options);
473
+ }
474
+
475
+ const query: Record<string, string | number> = {};
476
+
477
+ if (options?.limit) {
478
+ query.limit = options.limit;
479
+ }
480
+ if (options?.offset) {
481
+ query.start = options.offset;
482
+ }
483
+
484
+ const rawClients = await this.client.list<StelClient>('clients', query);
485
+ let customers = rawClients
486
+ .filter((c) => !c.deleted)
487
+ .map(transformStelClient);
488
+
489
+ // Client-side search (STEL has no fulltext search for clients)
490
+ if (options?.search) {
491
+ const needle = options.search.toLowerCase();
492
+ customers = customers.filter(
493
+ (c) =>
494
+ c.email.toLowerCase().includes(needle) ||
495
+ (c.firstName ?? '').toLowerCase().includes(needle) ||
496
+ (c.lastName ?? '').toLowerCase().includes(needle),
497
+ );
498
+ }
499
+
500
+ return customers;
501
+ } catch (error) {
502
+ this.logger.error('getCustomers failed', { options, error });
503
+ throw this.wrapError('getCustomers', error);
504
+ }
505
+ }
506
+
507
+ async updateCustomer(
508
+ customerId: string,
509
+ data: Partial<Omit<Customer, 'id' | 'createdAt'>>,
510
+ ): Promise<Customer> {
511
+ this.logger.debug('updateCustomer', { customerId, data });
512
+
513
+ try {
514
+ const stelPayload: Record<string, unknown> = {};
515
+
516
+ if (data.firstName || data.lastName) {
517
+ stelPayload.name = [data.firstName, data.lastName].filter(Boolean).join(' ');
518
+ }
519
+ if (data.email) {
520
+ stelPayload.email = data.email;
521
+ }
522
+ if (data.phone) {
523
+ stelPayload.phone = data.phone;
524
+ }
525
+
526
+ const raw = await this.client.update<StelClient>('clients', customerId, stelPayload);
527
+ return transformStelClient(raw);
528
+ } catch (error) {
529
+ this.logger.error('updateCustomer failed', { customerId, error });
530
+ throw this.wrapError('updateCustomer', error);
531
+ }
532
+ }
533
+
534
+ // ============================================
535
+ // Order Operations (optional)
536
+ // ============================================
537
+
538
+ async getOrder(orderId: string): Promise<Order | undefined> {
539
+ this.logger.debug('getOrder', { orderId });
540
+
541
+ try {
542
+ const raw = await this.client.getOne<StelSalesOrder>('salesOrders', orderId);
543
+ return raw ? transformStelSalesOrder(raw, this.currencyCode) : undefined;
544
+ } catch (error) {
545
+ this.logger.error('getOrder failed', { orderId, error });
546
+ throw this.wrapError('getOrder', error);
547
+ }
548
+ }
549
+
550
+ async getOrders(options: GetOrdersOptions): Promise<Order[]> {
551
+ this.logger.debug('getOrders', { options });
552
+
553
+ try {
554
+ if (options?.customFetcher) {
555
+ return options.customFetcher(options);
556
+ }
557
+
558
+ const query: Record<string, string | number> = {};
559
+
560
+ if (options?.limit) {
561
+ query.limit = options.limit;
562
+ }
563
+ if (options?.offset) {
564
+ query.start = options.offset;
565
+ }
566
+ if (options?.customerId) {
567
+ query['client-id'] = options.customerId;
568
+ }
569
+
570
+ const rawOrders = await this.client.list<StelSalesOrder>('salesOrders', query);
571
+ return rawOrders
572
+ .filter((o) => !o.deleted)
573
+ .map((o) => transformStelSalesOrder(o, this.currencyCode));
574
+ } catch (error) {
575
+ this.logger.error('getOrders failed', { options, error });
576
+ throw this.wrapError('getOrders', error);
577
+ }
578
+ }
579
+
580
+ async getAdminOrders(options?: {
581
+ search?: string;
582
+ status?: string | string[];
583
+ limit?: number;
584
+ offset?: number;
585
+ }): Promise<{ orders: Order[]; count: number }> {
586
+ this.logger.debug('getAdminOrders', { options });
587
+
588
+ try {
589
+ const query: Record<string, string | number> = {};
590
+
591
+ if (options?.limit) {
592
+ query.limit = options.limit;
593
+ }
594
+ if (options?.offset) {
595
+ query.start = options.offset;
596
+ }
597
+
598
+ const rawOrders = await this.client.list<StelSalesOrder>('salesOrders', query);
599
+ let orders = rawOrders
600
+ .filter((o) => !o.deleted)
601
+ .map((o) => transformStelSalesOrder(o, this.currencyCode));
602
+
603
+ if (options?.search) {
604
+ const needle = options.search.toLowerCase();
605
+ orders = orders.filter(
606
+ (o) =>
607
+ o.orderNumber.toLowerCase().includes(needle) ||
608
+ o.email.toLowerCase().includes(needle),
609
+ );
610
+ }
611
+
612
+ if (options?.status) {
613
+ const statuses = Array.isArray(options.status) ? options.status : [options.status];
614
+ orders = orders.filter((o) => statuses.includes(o.status));
615
+ }
616
+
617
+ return { orders, count: orders.length };
618
+ } catch (error) {
619
+ this.logger.error('getAdminOrders failed', { options, error });
620
+ throw this.wrapError('getAdminOrders', error);
621
+ }
622
+ }
623
+
624
+ cancelOrder(_orderId: string): Promise<Order> {
625
+ return this.notSupported('cancelOrder');
626
+ }
627
+
628
+ createFulfillment(_orderId: string, _items?: Array<{ id: string; quantity: number }>): Promise<void> {
629
+ return this.notSupported('createFulfillment');
630
+ }
631
+
632
+ createRefund(_orderId: string, _amount?: number, _reason?: string): Promise<void> {
633
+ return this.notSupported('createRefund');
634
+ }
635
+
636
+ // ============================================
637
+ // Storefront Context
638
+ // ============================================
639
+
640
+ async getStorefrontContext(): Promise<StorefrontContext> {
641
+ return {
642
+ id: this.storefrontId,
643
+ name: this.companyName,
644
+ platformType: 'stelorder',
645
+ requiresProductScoping: false,
646
+ currencyCode: this.currencyCode,
647
+ metadata: {
648
+ apiBaseUrl: this.client.apiBaseUrl,
649
+ singleCompany: true,
650
+ providerVersion: this.version,
651
+ },
652
+ };
653
+ }
654
+
655
+ isStorefrontRequired(): boolean {
656
+ return false;
657
+ }
658
+
659
+ // ============================================
660
+ // Internal helpers
661
+ // ============================================
662
+
663
+ private notSupported<T>(operation: string): Promise<T> {
664
+ return Promise.reject(
665
+ new ProviderAPIError(
666
+ `Stel Order does not support \`${operation}\`. STEL is an ERP adapter without cart/checkout/auth capabilities.`,
667
+ 'stelorder',
668
+ 501,
669
+ ),
670
+ );
671
+ }
672
+
673
+ private wrapError(operation: string, error: unknown): Error {
674
+ if (error instanceof ProviderAPIError) {
675
+ return error;
676
+ }
677
+
678
+ const message = error instanceof Error ? error.message : String(error);
679
+ return new ProviderAPIError(
680
+ `Stel Order \`${operation}\` failed: ${message}`,
681
+ 'stelorder',
682
+ 500,
683
+ );
684
+ }
685
+ }