@fire-spark/core 0.0.1 → 0.0.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/dist/index.d.ts +416 -46
- package/dist/index.js +570 -49
- package/package.json +2 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AndConditions, CheckoutMode, CheckoutResolved, Condition, CustomerIdentifyInput, CustomerRegisterInput, CustomerUpdateInput, FulfillmentListInput, GroupDisplayType, KioskDeviceConfig, KioskDeviceOverrides, KioskHealthBatchInput, KioskHealthBatchResult, KioskHeartbeatInput, KioskHeartbeatResult, KioskRealtimeToken, MatchResult, MatchableRule, Menu, Menu as Menu$1, MenuLookup, MenuLookup as MenuLookup$1, ModifierControlType, ModifierGroup, OrConditions, Order as Order$1, Order as Order$2, OrderInsertInput, OrderLine, OrderListInput, OrderReorderInput, OrderTotals, OrderUpdateInput, OrderValidateInput, OrderValidateOutput, PaymentIntentInput, PaymentMethod, PaymentProvider, PricingModifier, Product, Product as Product$1, Recommendation, RecurringDateCronResult, RecurringDateCronValues, RulesEngineOrderLine, RulesEngineParams, RulesEngineRuntime, Schedule, StoreListInput, StoreNearbyInput, StorefrontAvailability, StorefrontMenuScope, StorefrontMenuStreamOptions, StorefrontPaymentsListInput } from "@fire-spark/types";
|
|
1
|
+
import { AndConditions, CheckoutMode, CheckoutResolved, Condition, CustomerIdentifyInput, CustomerRegisterInput, CustomerUpdateInput, FulfillmentListInput, GroupDisplayType, KioskDeviceConfig, KioskDeviceOverrides, KioskHealthBatchInput, KioskHealthBatchResult, KioskHeartbeatInput, KioskHeartbeatResult, KioskRealtimeToken, MatchResult, MatchableRule, Menu, Menu as Menu$1, MenuLookup, MenuLookup as MenuLookup$1, ModifierControlType, ModifierGroup, OrConditions, Order as Order$1, Order as Order$2, OrderInsertInput, OrderLine, OrderListInput, OrderRedeem, OrderRedemptionStep, OrderReorderInput, OrderTotals, OrderUpdateInput, OrderValidateInput, OrderValidateOutput, PaymentIntentInput, PaymentMethod, PaymentProvider, PricingModifier, Product, Product as Product$1, Recommendation, RecurringDateCronResult, RecurringDateCronValues, RulesEngineOrderLine, RulesEngineParams, RulesEngineRuntime, Schedule, StoreListInput, StoreNearbyInput, StorefrontAvailability, StorefrontMenuScope, StorefrontMenuStreamOptions, StorefrontPaymentsListInput } from "@fire-spark/types";
|
|
2
2
|
//#region src/productCustomization/types.d.ts
|
|
3
3
|
type SelectionStatus = "IDLE" | "INCOMPLETE" | "VALID" | "INVALID";
|
|
4
4
|
type CustomizationPath = string;
|
|
@@ -208,9 +208,11 @@ declare const listOrders: (input?: OrderListInput, options?: ServiceHttpOptions)
|
|
|
208
208
|
included_quantity: number;
|
|
209
209
|
}[];
|
|
210
210
|
redeem: {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
reward_id: string;
|
|
212
|
+
quantity: number;
|
|
213
|
+
discount_rate?: number | undefined;
|
|
214
|
+
discount_amount?: number | undefined;
|
|
215
|
+
expires_at?: string | undefined;
|
|
214
216
|
}[];
|
|
215
217
|
quantity: number;
|
|
216
218
|
tax_rate: number | null;
|
|
@@ -233,9 +235,44 @@ declare const listOrders: (input?: OrderListInput, options?: ServiceHttpOptions)
|
|
|
233
235
|
group_total: number;
|
|
234
236
|
}[];
|
|
235
237
|
redeem: {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
238
|
+
reward_id: string;
|
|
239
|
+
quantity: number;
|
|
240
|
+
discount_rate?: number | undefined;
|
|
241
|
+
discount_amount?: number | undefined;
|
|
242
|
+
expires_at?: string | undefined;
|
|
243
|
+
}[];
|
|
244
|
+
redemption_steps: {
|
|
245
|
+
reward_id: string;
|
|
246
|
+
reward_quantity: number;
|
|
247
|
+
target: "LINE" | "ORDER";
|
|
248
|
+
target_id?: string | undefined;
|
|
249
|
+
action: "CREATE" | "DELETE" | "READ" | "UPDATE";
|
|
250
|
+
discount_amount: number;
|
|
251
|
+
before: {
|
|
252
|
+
value: number;
|
|
253
|
+
quantity: number;
|
|
254
|
+
discount_amount: number;
|
|
255
|
+
discount_rate: number;
|
|
256
|
+
};
|
|
257
|
+
after: {
|
|
258
|
+
value: number;
|
|
259
|
+
quantity: number;
|
|
260
|
+
discount_amount: number;
|
|
261
|
+
discount_rate: number;
|
|
262
|
+
};
|
|
263
|
+
target_before?: {
|
|
264
|
+
value: number;
|
|
265
|
+
quantity: number;
|
|
266
|
+
discount_amount: number;
|
|
267
|
+
discount_rate: number;
|
|
268
|
+
} | undefined;
|
|
269
|
+
target_after?: {
|
|
270
|
+
value: number;
|
|
271
|
+
quantity: number;
|
|
272
|
+
discount_amount: number;
|
|
273
|
+
discount_rate: number;
|
|
274
|
+
} | undefined;
|
|
275
|
+
error?: string | undefined;
|
|
239
276
|
}[];
|
|
240
277
|
customer: {
|
|
241
278
|
customer_info: {
|
|
@@ -343,9 +380,11 @@ declare const createOrder: (body: OrderInsertInput, options?: ServiceHttpOptions
|
|
|
343
380
|
included_quantity: number;
|
|
344
381
|
}[];
|
|
345
382
|
redeem: {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
383
|
+
reward_id: string;
|
|
384
|
+
quantity: number;
|
|
385
|
+
discount_rate?: number | undefined;
|
|
386
|
+
discount_amount?: number | undefined;
|
|
387
|
+
expires_at?: string | undefined;
|
|
349
388
|
}[];
|
|
350
389
|
quantity: number;
|
|
351
390
|
tax_rate: number | null;
|
|
@@ -368,9 +407,44 @@ declare const createOrder: (body: OrderInsertInput, options?: ServiceHttpOptions
|
|
|
368
407
|
group_total: number;
|
|
369
408
|
}[];
|
|
370
409
|
redeem: {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
410
|
+
reward_id: string;
|
|
411
|
+
quantity: number;
|
|
412
|
+
discount_rate?: number | undefined;
|
|
413
|
+
discount_amount?: number | undefined;
|
|
414
|
+
expires_at?: string | undefined;
|
|
415
|
+
}[];
|
|
416
|
+
redemption_steps: {
|
|
417
|
+
reward_id: string;
|
|
418
|
+
reward_quantity: number;
|
|
419
|
+
target: "LINE" | "ORDER";
|
|
420
|
+
target_id?: string | undefined;
|
|
421
|
+
action: "CREATE" | "DELETE" | "READ" | "UPDATE";
|
|
422
|
+
discount_amount: number;
|
|
423
|
+
before: {
|
|
424
|
+
value: number;
|
|
425
|
+
quantity: number;
|
|
426
|
+
discount_amount: number;
|
|
427
|
+
discount_rate: number;
|
|
428
|
+
};
|
|
429
|
+
after: {
|
|
430
|
+
value: number;
|
|
431
|
+
quantity: number;
|
|
432
|
+
discount_amount: number;
|
|
433
|
+
discount_rate: number;
|
|
434
|
+
};
|
|
435
|
+
target_before?: {
|
|
436
|
+
value: number;
|
|
437
|
+
quantity: number;
|
|
438
|
+
discount_amount: number;
|
|
439
|
+
discount_rate: number;
|
|
440
|
+
} | undefined;
|
|
441
|
+
target_after?: {
|
|
442
|
+
value: number;
|
|
443
|
+
quantity: number;
|
|
444
|
+
discount_amount: number;
|
|
445
|
+
discount_rate: number;
|
|
446
|
+
} | undefined;
|
|
447
|
+
error?: string | undefined;
|
|
374
448
|
}[];
|
|
375
449
|
customer: {
|
|
376
450
|
customer_info: {
|
|
@@ -478,9 +552,11 @@ declare const getOrder: (id: string, options?: ServiceHttpOptions) => Promise<{
|
|
|
478
552
|
included_quantity: number;
|
|
479
553
|
}[];
|
|
480
554
|
redeem: {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
555
|
+
reward_id: string;
|
|
556
|
+
quantity: number;
|
|
557
|
+
discount_rate?: number | undefined;
|
|
558
|
+
discount_amount?: number | undefined;
|
|
559
|
+
expires_at?: string | undefined;
|
|
484
560
|
}[];
|
|
485
561
|
quantity: number;
|
|
486
562
|
tax_rate: number | null;
|
|
@@ -503,9 +579,44 @@ declare const getOrder: (id: string, options?: ServiceHttpOptions) => Promise<{
|
|
|
503
579
|
group_total: number;
|
|
504
580
|
}[];
|
|
505
581
|
redeem: {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
582
|
+
reward_id: string;
|
|
583
|
+
quantity: number;
|
|
584
|
+
discount_rate?: number | undefined;
|
|
585
|
+
discount_amount?: number | undefined;
|
|
586
|
+
expires_at?: string | undefined;
|
|
587
|
+
}[];
|
|
588
|
+
redemption_steps: {
|
|
589
|
+
reward_id: string;
|
|
590
|
+
reward_quantity: number;
|
|
591
|
+
target: "LINE" | "ORDER";
|
|
592
|
+
target_id?: string | undefined;
|
|
593
|
+
action: "CREATE" | "DELETE" | "READ" | "UPDATE";
|
|
594
|
+
discount_amount: number;
|
|
595
|
+
before: {
|
|
596
|
+
value: number;
|
|
597
|
+
quantity: number;
|
|
598
|
+
discount_amount: number;
|
|
599
|
+
discount_rate: number;
|
|
600
|
+
};
|
|
601
|
+
after: {
|
|
602
|
+
value: number;
|
|
603
|
+
quantity: number;
|
|
604
|
+
discount_amount: number;
|
|
605
|
+
discount_rate: number;
|
|
606
|
+
};
|
|
607
|
+
target_before?: {
|
|
608
|
+
value: number;
|
|
609
|
+
quantity: number;
|
|
610
|
+
discount_amount: number;
|
|
611
|
+
discount_rate: number;
|
|
612
|
+
} | undefined;
|
|
613
|
+
target_after?: {
|
|
614
|
+
value: number;
|
|
615
|
+
quantity: number;
|
|
616
|
+
discount_amount: number;
|
|
617
|
+
discount_rate: number;
|
|
618
|
+
} | undefined;
|
|
619
|
+
error?: string | undefined;
|
|
509
620
|
}[];
|
|
510
621
|
customer: {
|
|
511
622
|
customer_info: {
|
|
@@ -1051,9 +1162,11 @@ declare const updateOrder: (id: string, body: OrderUpdateInput, options?: Servic
|
|
|
1051
1162
|
included_quantity: number;
|
|
1052
1163
|
}[];
|
|
1053
1164
|
redeem: {
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1165
|
+
reward_id: string;
|
|
1166
|
+
quantity: number;
|
|
1167
|
+
discount_rate?: number | undefined;
|
|
1168
|
+
discount_amount?: number | undefined;
|
|
1169
|
+
expires_at?: string | undefined;
|
|
1057
1170
|
}[];
|
|
1058
1171
|
quantity: number;
|
|
1059
1172
|
tax_rate: number | null;
|
|
@@ -1076,9 +1189,44 @@ declare const updateOrder: (id: string, body: OrderUpdateInput, options?: Servic
|
|
|
1076
1189
|
group_total: number;
|
|
1077
1190
|
}[];
|
|
1078
1191
|
redeem: {
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1192
|
+
reward_id: string;
|
|
1193
|
+
quantity: number;
|
|
1194
|
+
discount_rate?: number | undefined;
|
|
1195
|
+
discount_amount?: number | undefined;
|
|
1196
|
+
expires_at?: string | undefined;
|
|
1197
|
+
}[];
|
|
1198
|
+
redemption_steps: {
|
|
1199
|
+
reward_id: string;
|
|
1200
|
+
reward_quantity: number;
|
|
1201
|
+
target: "LINE" | "ORDER";
|
|
1202
|
+
target_id?: string | undefined;
|
|
1203
|
+
action: "CREATE" | "DELETE" | "READ" | "UPDATE";
|
|
1204
|
+
discount_amount: number;
|
|
1205
|
+
before: {
|
|
1206
|
+
value: number;
|
|
1207
|
+
quantity: number;
|
|
1208
|
+
discount_amount: number;
|
|
1209
|
+
discount_rate: number;
|
|
1210
|
+
};
|
|
1211
|
+
after: {
|
|
1212
|
+
value: number;
|
|
1213
|
+
quantity: number;
|
|
1214
|
+
discount_amount: number;
|
|
1215
|
+
discount_rate: number;
|
|
1216
|
+
};
|
|
1217
|
+
target_before?: {
|
|
1218
|
+
value: number;
|
|
1219
|
+
quantity: number;
|
|
1220
|
+
discount_amount: number;
|
|
1221
|
+
discount_rate: number;
|
|
1222
|
+
} | undefined;
|
|
1223
|
+
target_after?: {
|
|
1224
|
+
value: number;
|
|
1225
|
+
quantity: number;
|
|
1226
|
+
discount_amount: number;
|
|
1227
|
+
discount_rate: number;
|
|
1228
|
+
} | undefined;
|
|
1229
|
+
error?: string | undefined;
|
|
1082
1230
|
}[];
|
|
1083
1231
|
customer: {
|
|
1084
1232
|
customer_info: {
|
|
@@ -1186,9 +1334,11 @@ declare const cancelOrder: (id: string, options?: ServiceHttpOptions) => Promise
|
|
|
1186
1334
|
included_quantity: number;
|
|
1187
1335
|
}[];
|
|
1188
1336
|
redeem: {
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1337
|
+
reward_id: string;
|
|
1338
|
+
quantity: number;
|
|
1339
|
+
discount_rate?: number | undefined;
|
|
1340
|
+
discount_amount?: number | undefined;
|
|
1341
|
+
expires_at?: string | undefined;
|
|
1192
1342
|
}[];
|
|
1193
1343
|
quantity: number;
|
|
1194
1344
|
tax_rate: number | null;
|
|
@@ -1211,9 +1361,44 @@ declare const cancelOrder: (id: string, options?: ServiceHttpOptions) => Promise
|
|
|
1211
1361
|
group_total: number;
|
|
1212
1362
|
}[];
|
|
1213
1363
|
redeem: {
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1364
|
+
reward_id: string;
|
|
1365
|
+
quantity: number;
|
|
1366
|
+
discount_rate?: number | undefined;
|
|
1367
|
+
discount_amount?: number | undefined;
|
|
1368
|
+
expires_at?: string | undefined;
|
|
1369
|
+
}[];
|
|
1370
|
+
redemption_steps: {
|
|
1371
|
+
reward_id: string;
|
|
1372
|
+
reward_quantity: number;
|
|
1373
|
+
target: "LINE" | "ORDER";
|
|
1374
|
+
target_id?: string | undefined;
|
|
1375
|
+
action: "CREATE" | "DELETE" | "READ" | "UPDATE";
|
|
1376
|
+
discount_amount: number;
|
|
1377
|
+
before: {
|
|
1378
|
+
value: number;
|
|
1379
|
+
quantity: number;
|
|
1380
|
+
discount_amount: number;
|
|
1381
|
+
discount_rate: number;
|
|
1382
|
+
};
|
|
1383
|
+
after: {
|
|
1384
|
+
value: number;
|
|
1385
|
+
quantity: number;
|
|
1386
|
+
discount_amount: number;
|
|
1387
|
+
discount_rate: number;
|
|
1388
|
+
};
|
|
1389
|
+
target_before?: {
|
|
1390
|
+
value: number;
|
|
1391
|
+
quantity: number;
|
|
1392
|
+
discount_amount: number;
|
|
1393
|
+
discount_rate: number;
|
|
1394
|
+
} | undefined;
|
|
1395
|
+
target_after?: {
|
|
1396
|
+
value: number;
|
|
1397
|
+
quantity: number;
|
|
1398
|
+
discount_amount: number;
|
|
1399
|
+
discount_rate: number;
|
|
1400
|
+
} | undefined;
|
|
1401
|
+
error?: string | undefined;
|
|
1217
1402
|
}[];
|
|
1218
1403
|
customer: {
|
|
1219
1404
|
customer_info: {
|
|
@@ -1321,9 +1506,11 @@ declare const refundOrder: (id: string, options?: ServiceHttpOptions) => Promise
|
|
|
1321
1506
|
included_quantity: number;
|
|
1322
1507
|
}[];
|
|
1323
1508
|
redeem: {
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1509
|
+
reward_id: string;
|
|
1510
|
+
quantity: number;
|
|
1511
|
+
discount_rate?: number | undefined;
|
|
1512
|
+
discount_amount?: number | undefined;
|
|
1513
|
+
expires_at?: string | undefined;
|
|
1327
1514
|
}[];
|
|
1328
1515
|
quantity: number;
|
|
1329
1516
|
tax_rate: number | null;
|
|
@@ -1346,9 +1533,44 @@ declare const refundOrder: (id: string, options?: ServiceHttpOptions) => Promise
|
|
|
1346
1533
|
group_total: number;
|
|
1347
1534
|
}[];
|
|
1348
1535
|
redeem: {
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1536
|
+
reward_id: string;
|
|
1537
|
+
quantity: number;
|
|
1538
|
+
discount_rate?: number | undefined;
|
|
1539
|
+
discount_amount?: number | undefined;
|
|
1540
|
+
expires_at?: string | undefined;
|
|
1541
|
+
}[];
|
|
1542
|
+
redemption_steps: {
|
|
1543
|
+
reward_id: string;
|
|
1544
|
+
reward_quantity: number;
|
|
1545
|
+
target: "LINE" | "ORDER";
|
|
1546
|
+
target_id?: string | undefined;
|
|
1547
|
+
action: "CREATE" | "DELETE" | "READ" | "UPDATE";
|
|
1548
|
+
discount_amount: number;
|
|
1549
|
+
before: {
|
|
1550
|
+
value: number;
|
|
1551
|
+
quantity: number;
|
|
1552
|
+
discount_amount: number;
|
|
1553
|
+
discount_rate: number;
|
|
1554
|
+
};
|
|
1555
|
+
after: {
|
|
1556
|
+
value: number;
|
|
1557
|
+
quantity: number;
|
|
1558
|
+
discount_amount: number;
|
|
1559
|
+
discount_rate: number;
|
|
1560
|
+
};
|
|
1561
|
+
target_before?: {
|
|
1562
|
+
value: number;
|
|
1563
|
+
quantity: number;
|
|
1564
|
+
discount_amount: number;
|
|
1565
|
+
discount_rate: number;
|
|
1566
|
+
} | undefined;
|
|
1567
|
+
target_after?: {
|
|
1568
|
+
value: number;
|
|
1569
|
+
quantity: number;
|
|
1570
|
+
discount_amount: number;
|
|
1571
|
+
discount_rate: number;
|
|
1572
|
+
} | undefined;
|
|
1573
|
+
error?: string | undefined;
|
|
1352
1574
|
}[];
|
|
1353
1575
|
customer: {
|
|
1354
1576
|
customer_info: {
|
|
@@ -1456,9 +1678,11 @@ declare const reorderOrder: (id: string, body?: OrderReorderInput, options?: Ser
|
|
|
1456
1678
|
included_quantity: number;
|
|
1457
1679
|
}[];
|
|
1458
1680
|
redeem: {
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1681
|
+
reward_id: string;
|
|
1682
|
+
quantity: number;
|
|
1683
|
+
discount_rate?: number | undefined;
|
|
1684
|
+
discount_amount?: number | undefined;
|
|
1685
|
+
expires_at?: string | undefined;
|
|
1462
1686
|
}[];
|
|
1463
1687
|
quantity: number;
|
|
1464
1688
|
tax_rate: number | null;
|
|
@@ -1481,9 +1705,44 @@ declare const reorderOrder: (id: string, body?: OrderReorderInput, options?: Ser
|
|
|
1481
1705
|
group_total: number;
|
|
1482
1706
|
}[];
|
|
1483
1707
|
redeem: {
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1708
|
+
reward_id: string;
|
|
1709
|
+
quantity: number;
|
|
1710
|
+
discount_rate?: number | undefined;
|
|
1711
|
+
discount_amount?: number | undefined;
|
|
1712
|
+
expires_at?: string | undefined;
|
|
1713
|
+
}[];
|
|
1714
|
+
redemption_steps: {
|
|
1715
|
+
reward_id: string;
|
|
1716
|
+
reward_quantity: number;
|
|
1717
|
+
target: "LINE" | "ORDER";
|
|
1718
|
+
target_id?: string | undefined;
|
|
1719
|
+
action: "CREATE" | "DELETE" | "READ" | "UPDATE";
|
|
1720
|
+
discount_amount: number;
|
|
1721
|
+
before: {
|
|
1722
|
+
value: number;
|
|
1723
|
+
quantity: number;
|
|
1724
|
+
discount_amount: number;
|
|
1725
|
+
discount_rate: number;
|
|
1726
|
+
};
|
|
1727
|
+
after: {
|
|
1728
|
+
value: number;
|
|
1729
|
+
quantity: number;
|
|
1730
|
+
discount_amount: number;
|
|
1731
|
+
discount_rate: number;
|
|
1732
|
+
};
|
|
1733
|
+
target_before?: {
|
|
1734
|
+
value: number;
|
|
1735
|
+
quantity: number;
|
|
1736
|
+
discount_amount: number;
|
|
1737
|
+
discount_rate: number;
|
|
1738
|
+
} | undefined;
|
|
1739
|
+
target_after?: {
|
|
1740
|
+
value: number;
|
|
1741
|
+
quantity: number;
|
|
1742
|
+
discount_amount: number;
|
|
1743
|
+
discount_rate: number;
|
|
1744
|
+
} | undefined;
|
|
1745
|
+
error?: string | undefined;
|
|
1487
1746
|
}[];
|
|
1488
1747
|
customer: {
|
|
1489
1748
|
customer_info: {
|
|
@@ -1594,6 +1853,26 @@ type ReorderOrderOptions = CreateOrderOptions & {
|
|
|
1594
1853
|
strategy?: "replace" | "merge";
|
|
1595
1854
|
};
|
|
1596
1855
|
type OrderDraft = Order$2;
|
|
1856
|
+
type ApplyRedeemInput = {
|
|
1857
|
+
reward_id: string;
|
|
1858
|
+
/** Reward units to redeem (integer ≥ 1). */
|
|
1859
|
+
quantity: number;
|
|
1860
|
+
/** 0–1, compounded per unit over the remaining value. */
|
|
1861
|
+
discount_rate?: number;
|
|
1862
|
+
/** > 0, per unit; caps the rate discount when both are set. */
|
|
1863
|
+
discount_amount?: number;
|
|
1864
|
+
/** Root line to discount; omit to discount the whole order. */
|
|
1865
|
+
line_uid?: string;
|
|
1866
|
+
/** ISO datetime with offset; validation fails once it has passed. */
|
|
1867
|
+
expires_at?: string;
|
|
1868
|
+
};
|
|
1869
|
+
type RemoveRedeemInput = {
|
|
1870
|
+
reward_id: string;
|
|
1871
|
+
/** Reward units to remove (integer ≥ 1), latest applied first. */
|
|
1872
|
+
quantity: number;
|
|
1873
|
+
/** Root line holding the redeem; omit for order-level redeems. */
|
|
1874
|
+
line_uid?: string;
|
|
1875
|
+
};
|
|
1597
1876
|
//#endregion
|
|
1598
1877
|
//#region src/order/Order.d.ts
|
|
1599
1878
|
declare class Order {
|
|
@@ -1650,6 +1929,16 @@ declare class Order {
|
|
|
1650
1929
|
removeLine(lineUid: string): void;
|
|
1651
1930
|
replaceLine(lineUid: string, lines: OrderLine | OrderLine[]): void;
|
|
1652
1931
|
emptyOrder(): void;
|
|
1932
|
+
/**
|
|
1933
|
+
* Appends a redeem to `line_uid` (root line) or to the order. Steps keep
|
|
1934
|
+
* the order in which redeems were applied.
|
|
1935
|
+
*/
|
|
1936
|
+
applyRedeem(input: ApplyRedeemInput): void;
|
|
1937
|
+
/**
|
|
1938
|
+
* Removes `quantity` units of `reward_id` from `line_uid` or the order,
|
|
1939
|
+
* latest applied first. Removing more units than applied clears them all.
|
|
1940
|
+
*/
|
|
1941
|
+
removeRedeem(input: RemoveRedeemInput): void;
|
|
1653
1942
|
setScope(scope: Partial<OrderScope>): void;
|
|
1654
1943
|
/** Shallow-merges into order metadata; `null` values are kept as-is. */
|
|
1655
1944
|
setMetadata(patch: Record<string, unknown>): void;
|
|
@@ -1661,6 +1950,8 @@ declare class Order {
|
|
|
1661
1950
|
refundOrder(): Promise<void>;
|
|
1662
1951
|
validateOrder(): Promise<void>;
|
|
1663
1952
|
placeOrder(input?: PlaceOrderInput): Promise<void>;
|
|
1953
|
+
/** Applies lines/redeem, replaying redemption steps and totals. */
|
|
1954
|
+
private commitLines;
|
|
1664
1955
|
private scheduleAfterMutate;
|
|
1665
1956
|
private notify;
|
|
1666
1957
|
}
|
|
@@ -1725,8 +2016,8 @@ declare const milestoneTrack: (workflow: TrackWorkflow) => MilestoneTrackItem[];
|
|
|
1725
2016
|
declare const milestoneSteps: (workflow: TrackWorkflow, currentNodeIds: string[]) => MilestoneSteps | null;
|
|
1726
2017
|
//#endregion
|
|
1727
2018
|
//#region src/order/validate.d.ts
|
|
1728
|
-
type OrderLineIssueCode = "PRODUCT_NOT_FOUND" | "HIDDEN" | "OUT_OF_STOCK" | "OUTSIDE_HOURS" | "MODIFIER_NOT_FOUND" | "MODIFIER_RULES_INVALID" | "PRICE_MISMATCH" | "TAX_MISMATCH" | "DISCOUNT_MISMATCH" | "QUANTITY_MISMATCH" | "QUANTITY_BELOW_MINIMUM" | "QUANTITY_ABOVE_MAXIMUM" | "STRUCTURE_MISMATCH" | "FEE_MISMATCH";
|
|
1729
|
-
type OrderIssueCode = "NO_MENU" | "MENU_CLOSED" | "SCOPE_INACTIVE" | "TOTALS_MISMATCH" | "EMPTY_ORDER" | "ITEMS_BELOW_MINIMUM" | "ITEMS_ABOVE_MAXIMUM" | "TOTAL_BELOW_MINIMUM" | "TOTAL_ABOVE_MAXIMUM" | "REDEEMS_ABOVE_MAXIMUM" | "REDEEMS_TOTAL_ABOVE_MAXIMUM" | "SHIPPING_REQUIRED" | "BILLING_REQUIRED" | "SHIPPING_FIELD_REQUIRED" | "BILLING_FIELD_REQUIRED";
|
|
2019
|
+
type OrderLineIssueCode = "PRODUCT_NOT_FOUND" | "HIDDEN" | "OUT_OF_STOCK" | "OUTSIDE_HOURS" | "MODIFIER_NOT_FOUND" | "MODIFIER_RULES_INVALID" | "PRICE_MISMATCH" | "TAX_MISMATCH" | "DISCOUNT_MISMATCH" | "QUANTITY_MISMATCH" | "QUANTITY_BELOW_MINIMUM" | "QUANTITY_ABOVE_MAXIMUM" | "STRUCTURE_MISMATCH" | "FEE_MISMATCH" | "REDEEM_EXPIRED" | "REDEMPTION_STEPS_MISMATCH";
|
|
2020
|
+
type OrderIssueCode = "NO_MENU" | "MENU_CLOSED" | "SCOPE_INACTIVE" | "TOTALS_MISMATCH" | "REDEEM_EXPIRED" | "REDEMPTION_STEPS_MISMATCH" | "REDEMPTION_MISMATCH" | "EMPTY_ORDER" | "ITEMS_BELOW_MINIMUM" | "ITEMS_ABOVE_MAXIMUM" | "TOTAL_BELOW_MINIMUM" | "TOTAL_ABOVE_MAXIMUM" | "REDEEMS_ABOVE_MAXIMUM" | "REDEEMS_TOTAL_ABOVE_MAXIMUM" | "SHIPPING_REQUIRED" | "BILLING_REQUIRED" | "SHIPPING_FIELD_REQUIRED" | "BILLING_FIELD_REQUIRED";
|
|
1730
2021
|
type OrderLineIssue = {
|
|
1731
2022
|
code: OrderLineIssueCode;
|
|
1732
2023
|
field?: string;
|
|
@@ -1753,6 +2044,10 @@ type ValidateOrderAgainstMenuInput = {
|
|
|
1753
2044
|
menuActive: boolean;
|
|
1754
2045
|
locale?: string;
|
|
1755
2046
|
expectedFeeLines?: OrderLine[];
|
|
2047
|
+
redeem?: OrderRedeem[];
|
|
2048
|
+
redemptionSteps?: OrderRedemptionStep[];
|
|
2049
|
+
/** Epoch ms used for `expires_at` checks. */
|
|
2050
|
+
now?: number;
|
|
1756
2051
|
};
|
|
1757
2052
|
/** Compare cart lines/totals against a fresh menu (exact Big.js equality). */
|
|
1758
2053
|
declare const validateOrderAgainstMenu: (input: ValidateOrderAgainstMenuInput) => OrderValidationResult;
|
|
@@ -1778,6 +2073,58 @@ declare class OrderValidationError extends Error {
|
|
|
1778
2073
|
}
|
|
1779
2074
|
declare const isOrderValidationError: (error: unknown) => error is OrderValidationError;
|
|
1780
2075
|
//#endregion
|
|
2076
|
+
//#region src/order/redemptions.d.ts
|
|
2077
|
+
/** Steps + totals for a set of lines and redeems, keeping step order. */
|
|
2078
|
+
declare const recomputeRedemptions: (input: {
|
|
2079
|
+
lines: OrderLine[];
|
|
2080
|
+
redeem: OrderRedeem[];
|
|
2081
|
+
redemption_steps: OrderRedemptionStep[];
|
|
2082
|
+
currency: string;
|
|
2083
|
+
}) => {
|
|
2084
|
+
redemption_steps: {
|
|
2085
|
+
reward_id: string;
|
|
2086
|
+
reward_quantity: number;
|
|
2087
|
+
target: "LINE" | "ORDER";
|
|
2088
|
+
target_id?: string | undefined;
|
|
2089
|
+
action: "CREATE" | "DELETE" | "READ" | "UPDATE";
|
|
2090
|
+
discount_amount: number;
|
|
2091
|
+
before: {
|
|
2092
|
+
value: number;
|
|
2093
|
+
quantity: number;
|
|
2094
|
+
discount_amount: number;
|
|
2095
|
+
discount_rate: number;
|
|
2096
|
+
};
|
|
2097
|
+
after: {
|
|
2098
|
+
value: number;
|
|
2099
|
+
quantity: number;
|
|
2100
|
+
discount_amount: number;
|
|
2101
|
+
discount_rate: number;
|
|
2102
|
+
};
|
|
2103
|
+
target_before?: {
|
|
2104
|
+
value: number;
|
|
2105
|
+
quantity: number;
|
|
2106
|
+
discount_amount: number;
|
|
2107
|
+
discount_rate: number;
|
|
2108
|
+
} | undefined;
|
|
2109
|
+
target_after?: {
|
|
2110
|
+
value: number;
|
|
2111
|
+
quantity: number;
|
|
2112
|
+
discount_amount: number;
|
|
2113
|
+
discount_rate: number;
|
|
2114
|
+
} | undefined;
|
|
2115
|
+
error?: string | undefined;
|
|
2116
|
+
}[];
|
|
2117
|
+
totals: {
|
|
2118
|
+
currency: string;
|
|
2119
|
+
subtotal: number;
|
|
2120
|
+
discount_rate: number;
|
|
2121
|
+
discount_total: number;
|
|
2122
|
+
tax_rate: number;
|
|
2123
|
+
tax_total: number;
|
|
2124
|
+
total: number;
|
|
2125
|
+
};
|
|
2126
|
+
};
|
|
2127
|
+
//#endregion
|
|
1781
2128
|
//#region src/order/totals.d.ts
|
|
1782
2129
|
/** Sum root line group totals into order totals (Big.js). */
|
|
1783
2130
|
declare const recomputeTotals: (lines: OrderLine[], currency?: string) => OrderTotals;
|
|
@@ -3276,6 +3623,29 @@ type ScheduleWindow = {
|
|
|
3276
3623
|
declare const getScheduleWindow: (schedule: Schedule | null | undefined, now?: Date) => ScheduleWindow;
|
|
3277
3624
|
declare const isOnSchedule: (schedule: Schedule | null | undefined, now?: Date) => boolean;
|
|
3278
3625
|
//#endregion
|
|
3626
|
+
//#region src/shared/genId.d.ts
|
|
3627
|
+
type IdBuilder = {
|
|
3628
|
+
/**
|
|
3629
|
+
* Keeps only `[A-Za-z0-9]`, cut to 32 characters. `fallback` is used only
|
|
3630
|
+
* when `value` is empty after trim; throws if nothing alphanumeric is left.
|
|
3631
|
+
*/
|
|
3632
|
+
literal: (value: string | null | undefined, fallback?: string) => IdBuilder;
|
|
3633
|
+
/** Appends `-`, the only separator allowed. */
|
|
3634
|
+
separator: () => IdBuilder;
|
|
3635
|
+
/** Cryptographically random `[0-9a-z]`; at least 12 characters. */
|
|
3636
|
+
hash: (length?: number) => IdBuilder;
|
|
3637
|
+
/** New id on every call; throws when the shape is unsafe. */
|
|
3638
|
+
build: () => string;
|
|
3639
|
+
};
|
|
3640
|
+
/**
|
|
3641
|
+
* Safe id builder: `genId().literal("ord").separator().hash(12).build()`.
|
|
3642
|
+
* Literals are cleaned to `[A-Za-z0-9]`; `-` is the only separator and a
|
|
3643
|
+
* random hash of at least 12 characters is required.
|
|
3644
|
+
*/
|
|
3645
|
+
declare const genId: () => IdBuilder;
|
|
3646
|
+
/** `ord-<store>-<hash>`; `nostore` when the store id is blank. */
|
|
3647
|
+
declare const genOrderId: (storeId?: string | null) => string;
|
|
3648
|
+
//#endregion
|
|
3279
3649
|
//#region src/rules/RulesEngine.d.ts
|
|
3280
3650
|
declare class RulesEngine {
|
|
3281
3651
|
order?: RulesEngineParams["order"];
|
|
@@ -3396,4 +3766,4 @@ declare const paymentMatchesClientConditions: (conditions: OrConditions | null |
|
|
|
3396
3766
|
now?: string;
|
|
3397
3767
|
}) => boolean;
|
|
3398
3768
|
//#endregion
|
|
3399
|
-
export { type AuthConfigureOptions, type CreateOrderOptions, type CreateSessionOptions, type CustomizationPath, type CustomizationSession, DEFAULT_RECOMMENDATION_TYPES, type ExchangeOptions, type FlatMenuCategory, type GroupDisplayType, type ListMenusStreamOptions, type Menu, type MenuCategoryNode, type MenuLookup, type MilestoneSteps, type MilestoneTrackItem, type ModifierControlType, type ModifierCtx, type ModifierGroupCtx, type NestedMenuCategory, Order, type OrderCustomerSource, type OrderDraft, type OrderIssue, type OrderIssueCode, type OrderLineDiff, type OrderLineIssue, type OrderLineIssueCode, type OrderScope, OrderSyncPendingError, OrderValidationError, type OrderValidationResult, type PricingModifier, type Product, type ProductCtx, type QuantitiesByPath, type RecommendationType, RecurringDateCron, type ReorderOrderOptions, type ResetOptions, RulesEngine, type ScheduleWindow, type SelectionStatus, type ServiceHttpOptions, type Snapshot, type StorefrontAccessToken, StorefrontHttpError, type TrackWorkflow, type ValidateOptions, type ValidateOrderAgainstMenuInput, type VisitedState, addLines, auth, buildCustomerSnapshot, collectOrderLineDiffs, countCartProducts, createSession, currentNodes, defaultConditionError, flattenMenuCategories, fromOrderLines, getScheduleWindow, indexMenu, isCheckoutFieldFilled, isDeviceNotLinked, isKioskUnauthorized, isMilestoneStepDone, isOnSchedule, isOrderValidationError, isStorefrontNotFound, listMenusStream, matchAbsoluteDateOperator, matchBooleanOperator, matchCurrencyOperator, matchEnumOperator, matchNumberOperator, matchRecurringDateOperator, matchRelativeDateOperator, matchStringOperator, milestoneSteps, milestoneTrack, nestMenuCategories, paymentMatchesClientConditions, recomputeTotals, resolveCheckout, services, storefrontJson, toOrderLines, validateOrderAgainstMenu, visitedStates };
|
|
3769
|
+
export { type ApplyRedeemInput, type AuthConfigureOptions, type CreateOrderOptions, type CreateSessionOptions, type CustomizationPath, type CustomizationSession, DEFAULT_RECOMMENDATION_TYPES, type ExchangeOptions, type FlatMenuCategory, type GroupDisplayType, type IdBuilder, type ListMenusStreamOptions, type Menu, type MenuCategoryNode, type MenuLookup, type MilestoneSteps, type MilestoneTrackItem, type ModifierControlType, type ModifierCtx, type ModifierGroupCtx, type NestedMenuCategory, Order, type OrderCustomerSource, type OrderDraft, type OrderIssue, type OrderIssueCode, type OrderLineDiff, type OrderLineIssue, type OrderLineIssueCode, type OrderScope, OrderSyncPendingError, OrderValidationError, type OrderValidationResult, type PricingModifier, type Product, type ProductCtx, type QuantitiesByPath, type RecommendationType, RecurringDateCron, type RemoveRedeemInput, type ReorderOrderOptions, type ResetOptions, RulesEngine, type ScheduleWindow, type SelectionStatus, type ServiceHttpOptions, type Snapshot, type StorefrontAccessToken, StorefrontHttpError, type TrackWorkflow, type ValidateOptions, type ValidateOrderAgainstMenuInput, type VisitedState, addLines, auth, buildCustomerSnapshot, collectOrderLineDiffs, countCartProducts, createSession, currentNodes, defaultConditionError, flattenMenuCategories, fromOrderLines, genId, genOrderId, getScheduleWindow, indexMenu, isCheckoutFieldFilled, isDeviceNotLinked, isKioskUnauthorized, isMilestoneStepDone, isOnSchedule, isOrderValidationError, isStorefrontNotFound, listMenusStream, matchAbsoluteDateOperator, matchBooleanOperator, matchCurrencyOperator, matchEnumOperator, matchNumberOperator, matchRecurringDateOperator, matchRelativeDateOperator, matchStringOperator, milestoneSteps, milestoneTrack, nestMenuCategories, paymentMatchesClientConditions, recomputeRedemptions, recomputeTotals, resolveCheckout, services, storefrontJson, toOrderLines, validateOrderAgainstMenu, visitedStates };
|
package/dist/index.js
CHANGED
|
@@ -1978,6 +1978,83 @@ const services = {
|
|
|
1978
1978
|
isKioskUnauthorized
|
|
1979
1979
|
}
|
|
1980
1980
|
};
|
|
1981
|
+
const DISALLOWED = /[^A-Za-z0-9]/g;
|
|
1982
|
+
const ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
1983
|
+
const BYTE_LIMIT = 252;
|
|
1984
|
+
const toLiteral = (value, fallback) => {
|
|
1985
|
+
const trimmed = (value ?? "").trim();
|
|
1986
|
+
const source = trimmed === "" ? fallback : trimmed;
|
|
1987
|
+
if (source === void 0) throw new Error("Id literal must not be empty");
|
|
1988
|
+
const cleaned = source.replace(DISALLOWED, "");
|
|
1989
|
+
if (cleaned === "") throw new Error(`Id literal has no alphanumeric characters: "${source}"`);
|
|
1990
|
+
return cleaned.slice(0, 32);
|
|
1991
|
+
};
|
|
1992
|
+
const randomChars = (length) => {
|
|
1993
|
+
const c = globalThis.crypto;
|
|
1994
|
+
if (typeof c?.getRandomValues !== "function") throw new Error("crypto.getRandomValues is required");
|
|
1995
|
+
let out = "";
|
|
1996
|
+
const bytes = new Uint8Array(length * 2);
|
|
1997
|
+
while (out.length < length) {
|
|
1998
|
+
c.getRandomValues(bytes);
|
|
1999
|
+
for (const byte of bytes) {
|
|
2000
|
+
if (byte >= BYTE_LIMIT) continue;
|
|
2001
|
+
out += ALPHABET[byte % 36];
|
|
2002
|
+
if (out.length === length) break;
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
return out;
|
|
2006
|
+
};
|
|
2007
|
+
const assertShape = (parts) => {
|
|
2008
|
+
if (!parts.some((part) => part.kind === "hash")) throw new Error("Id requires a random hash part");
|
|
2009
|
+
const first = parts[0];
|
|
2010
|
+
const last = parts[parts.length - 1];
|
|
2011
|
+
if (first?.kind === "separator" || last?.kind === "separator") throw new Error("Id must not start or end with a separator");
|
|
2012
|
+
parts.forEach((part, index) => {
|
|
2013
|
+
if (part.kind !== "separator") return;
|
|
2014
|
+
if (parts[index - 1]?.kind !== "separator") return;
|
|
2015
|
+
throw new Error("Id must not contain consecutive separators");
|
|
2016
|
+
});
|
|
2017
|
+
};
|
|
2018
|
+
const render = (part) => {
|
|
2019
|
+
if (part.kind === "literal") return part.value;
|
|
2020
|
+
if (part.kind === "separator") return "-";
|
|
2021
|
+
return randomChars(part.length);
|
|
2022
|
+
};
|
|
2023
|
+
const builder = (parts) => {
|
|
2024
|
+
return {
|
|
2025
|
+
literal: (value, fallback) => {
|
|
2026
|
+
const literal = {
|
|
2027
|
+
kind: "literal",
|
|
2028
|
+
value: toLiteral(value, fallback)
|
|
2029
|
+
};
|
|
2030
|
+
return builder([...parts, literal]);
|
|
2031
|
+
},
|
|
2032
|
+
separator: () => builder([...parts, { kind: "separator" }]),
|
|
2033
|
+
hash: (length = 12) => {
|
|
2034
|
+
if (!Number.isInteger(length) || length < 12) throw new Error(`Id hash must be an integer ≥ 12, got ${length}`);
|
|
2035
|
+
return builder([...parts, {
|
|
2036
|
+
kind: "hash",
|
|
2037
|
+
length
|
|
2038
|
+
}]);
|
|
2039
|
+
},
|
|
2040
|
+
build: () => {
|
|
2041
|
+
assertShape(parts);
|
|
2042
|
+
const id = parts.map(render).join("");
|
|
2043
|
+
if (id.length > 64) throw new Error(`Id must be ≤ 64 characters`);
|
|
2044
|
+
return id;
|
|
2045
|
+
}
|
|
2046
|
+
};
|
|
2047
|
+
};
|
|
2048
|
+
/**
|
|
2049
|
+
* Safe id builder: `genId().literal("ord").separator().hash(12).build()`.
|
|
2050
|
+
* Literals are cleaned to `[A-Za-z0-9]`; `-` is the only separator and a
|
|
2051
|
+
* random hash of at least 12 characters is required.
|
|
2052
|
+
*/
|
|
2053
|
+
const genId = () => builder([]);
|
|
2054
|
+
/** `ord-<store>-<hash>`; `nostore` when the store id is blank. */
|
|
2055
|
+
const genOrderId = (storeId) => {
|
|
2056
|
+
return genId().literal("ord").separator().literal(storeId, "nostore").separator().hash().build();
|
|
2057
|
+
};
|
|
1981
2058
|
//#endregion
|
|
1982
2059
|
//#region src/order/retry.ts
|
|
1983
2060
|
const defaultSleep = (ms) => {
|
|
@@ -2037,6 +2114,8 @@ const createFlushController = (runtime) => {
|
|
|
2037
2114
|
const b5 = { customer: order.customer };
|
|
2038
2115
|
const b6 = { payment_intents: order.payment_intents };
|
|
2039
2116
|
const b7 = { payment_status: order.payment_status };
|
|
2117
|
+
const b8 = { redeem: order.redeem };
|
|
2118
|
+
const b9 = { redemption_steps: order.redemption_steps };
|
|
2040
2119
|
return {
|
|
2041
2120
|
...b1,
|
|
2042
2121
|
...b2,
|
|
@@ -2044,7 +2123,9 @@ const createFlushController = (runtime) => {
|
|
|
2044
2123
|
...b4,
|
|
2045
2124
|
...b5,
|
|
2046
2125
|
...b6,
|
|
2047
|
-
...b7
|
|
2126
|
+
...b7,
|
|
2127
|
+
...b8,
|
|
2128
|
+
...b9
|
|
2048
2129
|
};
|
|
2049
2130
|
};
|
|
2050
2131
|
const insertBody = (order) => {
|
|
@@ -2068,6 +2149,8 @@ const createFlushController = (runtime) => {
|
|
|
2068
2149
|
};
|
|
2069
2150
|
const b7 = { payment_intents: order.payment_intents };
|
|
2070
2151
|
const b8 = { payment_status: order.payment_status };
|
|
2152
|
+
const b9 = { redeem: order.redeem };
|
|
2153
|
+
const b10 = { redemption_steps: order.redemption_steps };
|
|
2071
2154
|
return {
|
|
2072
2155
|
...b1,
|
|
2073
2156
|
...b2,
|
|
@@ -2076,7 +2159,9 @@ const createFlushController = (runtime) => {
|
|
|
2076
2159
|
...b5,
|
|
2077
2160
|
...b6,
|
|
2078
2161
|
...b7,
|
|
2079
|
-
...b8
|
|
2162
|
+
...b8,
|
|
2163
|
+
...b9,
|
|
2164
|
+
...b10
|
|
2080
2165
|
};
|
|
2081
2166
|
};
|
|
2082
2167
|
const flushNow = () => {
|
|
@@ -2564,6 +2649,227 @@ const emptyTotals = (currency = "USD") => {
|
|
|
2564
2649
|
};
|
|
2565
2650
|
};
|
|
2566
2651
|
//#endregion
|
|
2652
|
+
//#region src/order/redemptions.ts
|
|
2653
|
+
const ORDER_KEY = "ORDER";
|
|
2654
|
+
/** Same rules as `vOrders.redeem()`; throws on invalid input. */
|
|
2655
|
+
const buildRedeem = (input) => {
|
|
2656
|
+
const { reward_id, quantity, discount_rate, discount_amount } = input;
|
|
2657
|
+
const { expires_at } = input;
|
|
2658
|
+
if (!reward_id.trim()) throw new Error("Redeem requires reward_id");
|
|
2659
|
+
if (!Number.isInteger(quantity) || quantity < 1) throw new Error("Redeem quantity must be an integer ≥ 1");
|
|
2660
|
+
if (discount_rate === void 0 && discount_amount === void 0) throw new Error("Redeem requires discount_rate or discount_amount");
|
|
2661
|
+
const rate = discount_rate ?? 0;
|
|
2662
|
+
if (!(rate >= 0 && rate <= 1)) throw new Error("Redeem discount_rate must be between 0 and 1");
|
|
2663
|
+
if (discount_amount !== void 0 && !(discount_amount > 0)) throw new Error("Redeem discount_amount must be > 0");
|
|
2664
|
+
if (expires_at !== void 0 && Number.isNaN(Date.parse(expires_at))) throw new Error("Redeem expires_at must be an ISO datetime");
|
|
2665
|
+
const redeem = {
|
|
2666
|
+
reward_id,
|
|
2667
|
+
quantity
|
|
2668
|
+
};
|
|
2669
|
+
if (discount_rate !== void 0) redeem.discount_rate = discount_rate;
|
|
2670
|
+
if (discount_amount !== void 0) redeem.discount_amount = discount_amount;
|
|
2671
|
+
if (expires_at !== void 0) redeem.expires_at = expires_at;
|
|
2672
|
+
return redeem;
|
|
2673
|
+
};
|
|
2674
|
+
/**
|
|
2675
|
+
* Takes `quantity` units of `reward_id` off the latest entries first, so the
|
|
2676
|
+
* earlier redeems keep their step position. Entries that reach 0 are removed.
|
|
2677
|
+
*/
|
|
2678
|
+
const subtractRedeem = (items, reward_id, quantity) => {
|
|
2679
|
+
if (!Number.isInteger(quantity) || quantity < 1) throw new Error("Redeem quantity must be an integer ≥ 1");
|
|
2680
|
+
let pending = quantity;
|
|
2681
|
+
const kept = [];
|
|
2682
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
2683
|
+
const item = items[i];
|
|
2684
|
+
if (pending === 0 || item.reward_id !== reward_id) {
|
|
2685
|
+
kept.unshift(item);
|
|
2686
|
+
continue;
|
|
2687
|
+
}
|
|
2688
|
+
const taken = Math.min(item.quantity, pending);
|
|
2689
|
+
pending -= taken;
|
|
2690
|
+
const rest = item.quantity - taken;
|
|
2691
|
+
if (rest > 0) kept.unshift({
|
|
2692
|
+
...item,
|
|
2693
|
+
quantity: rest
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2696
|
+
return kept;
|
|
2697
|
+
};
|
|
2698
|
+
const redemptionTargetKey = (target) => {
|
|
2699
|
+
if (target.target === "ORDER") return ORDER_KEY;
|
|
2700
|
+
return `LINE:${target.target_id ?? ""}`;
|
|
2701
|
+
};
|
|
2702
|
+
/** Only root lines own redeems; child-line redeems are never applied. */
|
|
2703
|
+
const redeemPools = (lines, redeem) => {
|
|
2704
|
+
const pools = /* @__PURE__ */ new Map();
|
|
2705
|
+
for (const line of lines) {
|
|
2706
|
+
const items = line.redeem ?? [];
|
|
2707
|
+
if (line.parent_uid != null || items.length === 0) continue;
|
|
2708
|
+
const target = {
|
|
2709
|
+
target: "LINE",
|
|
2710
|
+
target_id: line.uid
|
|
2711
|
+
};
|
|
2712
|
+
const pool = items.map((item) => ({
|
|
2713
|
+
...target,
|
|
2714
|
+
redeem: item
|
|
2715
|
+
}));
|
|
2716
|
+
pools.set(redemptionTargetKey(target), pool);
|
|
2717
|
+
}
|
|
2718
|
+
const order = { target: "ORDER" };
|
|
2719
|
+
const pool = redeem.map((item) => ({
|
|
2720
|
+
...order,
|
|
2721
|
+
redeem: item
|
|
2722
|
+
}));
|
|
2723
|
+
pools.set(ORDER_KEY, pool);
|
|
2724
|
+
return pools;
|
|
2725
|
+
};
|
|
2726
|
+
/**
|
|
2727
|
+
* Pairs the k-th step of a target with the k-th redeem of that target, in
|
|
2728
|
+
* step order. Steps without a matching redeem are dropped; redeems without a
|
|
2729
|
+
* step are appended (lines first, then order).
|
|
2730
|
+
*/
|
|
2731
|
+
const sequenceRedeems = (lines, redeem, steps) => {
|
|
2732
|
+
const pools = redeemPools(lines, redeem);
|
|
2733
|
+
const cursors = /* @__PURE__ */ new Map();
|
|
2734
|
+
const sequence = [];
|
|
2735
|
+
for (const step of steps) {
|
|
2736
|
+
const key = redemptionTargetKey(step);
|
|
2737
|
+
const at = cursors.get(key) ?? 0;
|
|
2738
|
+
const next = pools.get(key)?.[at];
|
|
2739
|
+
if (!next || next.redeem.reward_id !== step.reward_id) continue;
|
|
2740
|
+
cursors.set(key, at + 1);
|
|
2741
|
+
sequence.push(next);
|
|
2742
|
+
}
|
|
2743
|
+
for (const [key, pool] of pools) sequence.push(...pool.slice(cursors.get(key) ?? 0));
|
|
2744
|
+
return sequence;
|
|
2745
|
+
};
|
|
2746
|
+
const minBig = (a, b) => {
|
|
2747
|
+
return a.lt(b) ? a : b;
|
|
2748
|
+
};
|
|
2749
|
+
const left = (run) => {
|
|
2750
|
+
return run.base.minus(run.discount);
|
|
2751
|
+
};
|
|
2752
|
+
const rateOf = (discount, base) => {
|
|
2753
|
+
if (base.lte(0)) return 0;
|
|
2754
|
+
return minBig(discount.div(base), new Big(1)).toNumber();
|
|
2755
|
+
};
|
|
2756
|
+
const snapshot = (run) => {
|
|
2757
|
+
const b1 = {
|
|
2758
|
+
value: left(run).toNumber(),
|
|
2759
|
+
quantity: run.quantity
|
|
2760
|
+
};
|
|
2761
|
+
const b2 = { discount_amount: run.discount.toNumber() };
|
|
2762
|
+
const b3 = { discount_rate: rateOf(run.discount, run.base) };
|
|
2763
|
+
return {
|
|
2764
|
+
...b1,
|
|
2765
|
+
...b2,
|
|
2766
|
+
...b3
|
|
2767
|
+
};
|
|
2768
|
+
};
|
|
2769
|
+
/**
|
|
2770
|
+
* Rate compounds once per reward unit over the remaining value; amount is
|
|
2771
|
+
* per unit. With both, the rate discount is capped at `amount × quantity`.
|
|
2772
|
+
*/
|
|
2773
|
+
const redeemDiscount = (redeem, remaining) => {
|
|
2774
|
+
const { quantity, discount_rate, discount_amount } = redeem;
|
|
2775
|
+
let cap = null;
|
|
2776
|
+
if (discount_amount !== void 0) cap = new Big(discount_amount).times(quantity);
|
|
2777
|
+
if (discount_rate === void 0) return cap ?? new Big(0);
|
|
2778
|
+
const kept = new Big(1).minus(discount_rate).pow(quantity);
|
|
2779
|
+
const byRate = remaining.times(new Big(1).minus(kept));
|
|
2780
|
+
return cap ? minBig(byRate, cap) : byRate;
|
|
2781
|
+
};
|
|
2782
|
+
const runningFor = (base, quantity) => {
|
|
2783
|
+
return {
|
|
2784
|
+
base: new Big(base),
|
|
2785
|
+
discount: new Big(0),
|
|
2786
|
+
quantity
|
|
2787
|
+
};
|
|
2788
|
+
};
|
|
2789
|
+
/**
|
|
2790
|
+
* Replays every redeem in persisted step order over the root lines' group
|
|
2791
|
+
* totals. A discount never exceeds what is left on its target or the order.
|
|
2792
|
+
*/
|
|
2793
|
+
const computeRedemptionSteps = (lines, redeem, steps) => {
|
|
2794
|
+
const sequence = sequenceRedeems(lines, redeem, steps);
|
|
2795
|
+
if (sequence.length === 0) return [];
|
|
2796
|
+
const roots = lines.filter((line) => line.parent_uid == null);
|
|
2797
|
+
let orderBase = new Big(0);
|
|
2798
|
+
const lineRuns = /* @__PURE__ */ new Map();
|
|
2799
|
+
for (const root of roots) {
|
|
2800
|
+
orderBase = orderBase.plus(root.group_total);
|
|
2801
|
+
lineRuns.set(root.uid, runningFor(root.group_total, root.quantity));
|
|
2802
|
+
}
|
|
2803
|
+
const order = runningFor(orderBase, 1);
|
|
2804
|
+
return sequence.map((item) => {
|
|
2805
|
+
const line = item.target_id ? lineRuns.get(item.target_id) : void 0;
|
|
2806
|
+
const before = snapshot(order);
|
|
2807
|
+
const targetBefore = line ? snapshot(line) : void 0;
|
|
2808
|
+
const orderLeft = left(order);
|
|
2809
|
+
const targetLeft = line ? left(line) : orderLeft;
|
|
2810
|
+
const raw = redeemDiscount(item.redeem, targetLeft);
|
|
2811
|
+
const discount = minBig(minBig(raw, targetLeft), orderLeft);
|
|
2812
|
+
order.discount = order.discount.plus(discount);
|
|
2813
|
+
if (line) line.discount = line.discount.plus(discount);
|
|
2814
|
+
const action = discount.gt(0) ? "UPDATE" : "READ";
|
|
2815
|
+
const { reward_id, quantity } = item.redeem;
|
|
2816
|
+
const b1 = {
|
|
2817
|
+
reward_id,
|
|
2818
|
+
reward_quantity: quantity,
|
|
2819
|
+
target: item.target
|
|
2820
|
+
};
|
|
2821
|
+
const b2 = item.target_id ? { target_id: item.target_id } : {};
|
|
2822
|
+
const b3 = {
|
|
2823
|
+
action,
|
|
2824
|
+
discount_amount: discount.toNumber()
|
|
2825
|
+
};
|
|
2826
|
+
const b4 = {
|
|
2827
|
+
before,
|
|
2828
|
+
after: snapshot(order)
|
|
2829
|
+
};
|
|
2830
|
+
let b5 = {};
|
|
2831
|
+
if (line) b5 = {
|
|
2832
|
+
target_before: targetBefore,
|
|
2833
|
+
target_after: snapshot(line)
|
|
2834
|
+
};
|
|
2835
|
+
return {
|
|
2836
|
+
...b1,
|
|
2837
|
+
...b2,
|
|
2838
|
+
...b3,
|
|
2839
|
+
...b4,
|
|
2840
|
+
...b5
|
|
2841
|
+
};
|
|
2842
|
+
});
|
|
2843
|
+
};
|
|
2844
|
+
/** Subtract redemption discounts from line-based order totals. */
|
|
2845
|
+
const applyRedemptionTotals = (totals, steps) => {
|
|
2846
|
+
if (steps.length === 0) return totals;
|
|
2847
|
+
let redeemed = new Big(0);
|
|
2848
|
+
for (const step of steps) redeemed = redeemed.plus(step.discount_amount);
|
|
2849
|
+
const discountTotal = new Big(totals.discount_total).plus(redeemed);
|
|
2850
|
+
const subtotal = new Big(totals.subtotal);
|
|
2851
|
+
const discount_rate = rateOf(discountTotal, subtotal);
|
|
2852
|
+
const discount_total = discountTotal.toNumber();
|
|
2853
|
+
const total = new Big(totals.total).minus(redeemed).toNumber();
|
|
2854
|
+
return {
|
|
2855
|
+
...totals,
|
|
2856
|
+
discount_rate,
|
|
2857
|
+
discount_total,
|
|
2858
|
+
total
|
|
2859
|
+
};
|
|
2860
|
+
};
|
|
2861
|
+
/** Steps + totals for a set of lines and redeems, keeping step order. */
|
|
2862
|
+
const recomputeRedemptions = (input) => {
|
|
2863
|
+
const { lines, redeem, currency } = input;
|
|
2864
|
+
const steps = input.redemption_steps;
|
|
2865
|
+
const redemption_steps = computeRedemptionSteps(lines, redeem, steps);
|
|
2866
|
+
const lineTotals = recomputeTotals(lines, currency);
|
|
2867
|
+
return {
|
|
2868
|
+
redemption_steps,
|
|
2869
|
+
totals: applyRedemptionTotals(lineTotals, redemption_steps)
|
|
2870
|
+
};
|
|
2871
|
+
};
|
|
2872
|
+
//#endregion
|
|
2567
2873
|
//#region src/order/Order.ts
|
|
2568
2874
|
const cloneOrder = (order) => {
|
|
2569
2875
|
return structuredClone(order);
|
|
@@ -2585,7 +2891,7 @@ const optionalBrandId = (value) => {
|
|
|
2585
2891
|
const buildInsertFromOptions = (options) => {
|
|
2586
2892
|
if (!options.scope) throw new Error("Order requires scope when fromOrderId is omitted");
|
|
2587
2893
|
if (!options.customer) throw new Error("Order requires customer when fromOrderId is omitted");
|
|
2588
|
-
const orderId = options.orderId ??
|
|
2894
|
+
const orderId = options.orderId ?? genOrderId(options.scope.store_id);
|
|
2589
2895
|
const brand_id = optionalBrandId(options.scope.brand_id);
|
|
2590
2896
|
const customer = buildCustomerSnapshot(options.customer);
|
|
2591
2897
|
const is_anonymous = options.isAnonymous ?? false;
|
|
@@ -2606,7 +2912,8 @@ const buildInsertFromOptions = (options) => {
|
|
|
2606
2912
|
};
|
|
2607
2913
|
const b5 = {
|
|
2608
2914
|
totals: emptyTotals(),
|
|
2609
|
-
metadata: {}
|
|
2915
|
+
metadata: {},
|
|
2916
|
+
redemption_steps: []
|
|
2610
2917
|
};
|
|
2611
2918
|
return {
|
|
2612
2919
|
...b1,
|
|
@@ -2617,8 +2924,8 @@ const buildInsertFromOptions = (options) => {
|
|
|
2617
2924
|
};
|
|
2618
2925
|
};
|
|
2619
2926
|
const buildInsertFromSource = (options, source) => {
|
|
2620
|
-
const orderId = options.orderId ?? `ord-${randomUUID().slice(0, 8)}`;
|
|
2621
2927
|
const store_id = options.scope?.store_id ?? source.store_id;
|
|
2928
|
+
const orderId = options.orderId ?? genOrderId(store_id);
|
|
2622
2929
|
const channel_id = options.scope?.channel_id ?? source.channel_id;
|
|
2623
2930
|
const fulfillment_id = options.scope?.fulfillment_id ?? source.fulfillment_id;
|
|
2624
2931
|
let brand_id = optionalBrandId(source.brand_id);
|
|
@@ -2627,7 +2934,13 @@ const buildInsertFromSource = (options, source) => {
|
|
|
2627
2934
|
if (options.customer) customer = buildCustomerSnapshot(options.customer);
|
|
2628
2935
|
const lines = structuredClone(source.lines);
|
|
2629
2936
|
const is_anonymous = options.isAnonymous ?? source.is_anonymous;
|
|
2630
|
-
const
|
|
2937
|
+
const redeem = structuredClone(source.redeem ?? []);
|
|
2938
|
+
const { redemption_steps, totals } = recomputeRedemptions({
|
|
2939
|
+
lines,
|
|
2940
|
+
redeem,
|
|
2941
|
+
redemption_steps: source.redemption_steps ?? [],
|
|
2942
|
+
currency: source.totals.currency
|
|
2943
|
+
});
|
|
2631
2944
|
const b1 = {
|
|
2632
2945
|
id: orderId,
|
|
2633
2946
|
store_id,
|
|
@@ -2638,7 +2951,6 @@ const buildInsertFromSource = (options, source) => {
|
|
|
2638
2951
|
brand_id,
|
|
2639
2952
|
is_anonymous
|
|
2640
2953
|
};
|
|
2641
|
-
const redeem = source.redeem ?? [];
|
|
2642
2954
|
const b3 = {
|
|
2643
2955
|
customer,
|
|
2644
2956
|
lines,
|
|
@@ -2649,13 +2961,14 @@ const buildInsertFromSource = (options, source) => {
|
|
|
2649
2961
|
return {
|
|
2650
2962
|
...b1,
|
|
2651
2963
|
...b2,
|
|
2652
|
-
...b3
|
|
2964
|
+
...b3,
|
|
2965
|
+
redemption_steps
|
|
2653
2966
|
};
|
|
2654
2967
|
};
|
|
2655
2968
|
const toRedeem = (items) => {
|
|
2656
2969
|
return (items ?? []).map((item) => ({
|
|
2657
2970
|
...item,
|
|
2658
|
-
|
|
2971
|
+
quantity: item.quantity ?? 1
|
|
2659
2972
|
}));
|
|
2660
2973
|
};
|
|
2661
2974
|
/** Local draft until the first mutating flush POSTs to the API. */
|
|
@@ -2708,7 +3021,10 @@ const buildLocalDraft = (insert) => {
|
|
|
2708
3021
|
totals,
|
|
2709
3022
|
metadata
|
|
2710
3023
|
};
|
|
2711
|
-
const b6 = {
|
|
3024
|
+
const b6 = {
|
|
3025
|
+
payment_status,
|
|
3026
|
+
redemption_steps: insert.redemption_steps ?? []
|
|
3027
|
+
};
|
|
2712
3028
|
const b7 = {
|
|
2713
3029
|
fulfillment_status: "IDLE",
|
|
2714
3030
|
status: "OPEN"
|
|
@@ -2924,54 +3240,89 @@ var Order = class Order {
|
|
|
2924
3240
|
}
|
|
2925
3241
|
addLine(lines) {
|
|
2926
3242
|
if (this.deleted) return;
|
|
2927
|
-
|
|
2928
|
-
this.draft = {
|
|
2929
|
-
...this.draft,
|
|
2930
|
-
lines: nextLines,
|
|
2931
|
-
totals: recomputeTotals(nextLines, this.draft.totals.currency)
|
|
2932
|
-
};
|
|
2933
|
-
this.scheduleAfterMutate();
|
|
3243
|
+
this.commitLines({ lines: addLines(this.draft.lines, lines) });
|
|
2934
3244
|
}
|
|
2935
3245
|
subtractLine(lineUid, quantity = 1) {
|
|
2936
3246
|
if (this.deleted) return;
|
|
2937
|
-
const
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
};
|
|
2943
|
-
this.scheduleAfterMutate();
|
|
3247
|
+
const p = [
|
|
3248
|
+
this.draft.lines,
|
|
3249
|
+
lineUid,
|
|
3250
|
+
quantity
|
|
3251
|
+
];
|
|
3252
|
+
this.commitLines({ lines: subtractLine(...p) });
|
|
2944
3253
|
}
|
|
2945
3254
|
removeLine(lineUid) {
|
|
2946
3255
|
if (this.deleted) return;
|
|
2947
|
-
|
|
2948
|
-
this.draft = {
|
|
2949
|
-
...this.draft,
|
|
2950
|
-
lines: nextLines,
|
|
2951
|
-
totals: recomputeTotals(nextLines, this.draft.totals.currency)
|
|
2952
|
-
};
|
|
2953
|
-
this.scheduleAfterMutate();
|
|
3256
|
+
this.commitLines({ lines: removeLine(this.draft.lines, lineUid) });
|
|
2954
3257
|
}
|
|
2955
3258
|
replaceLine(lineUid, lines) {
|
|
2956
3259
|
if (this.deleted) return;
|
|
2957
|
-
const
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
lines
|
|
2961
|
-
|
|
2962
|
-
};
|
|
2963
|
-
this.scheduleAfterMutate();
|
|
3260
|
+
const p = [
|
|
3261
|
+
this.draft.lines,
|
|
3262
|
+
lineUid,
|
|
3263
|
+
lines
|
|
3264
|
+
];
|
|
3265
|
+
this.commitLines({ lines: replaceLine(...p) });
|
|
2964
3266
|
}
|
|
2965
3267
|
emptyOrder() {
|
|
2966
3268
|
if (this.deleted) return;
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
3269
|
+
this.commitLines({
|
|
3270
|
+
lines: emptyLines(),
|
|
3271
|
+
redeem: []
|
|
3272
|
+
});
|
|
3273
|
+
}
|
|
3274
|
+
/**
|
|
3275
|
+
* Appends a redeem to `line_uid` (root line) or to the order. Steps keep
|
|
3276
|
+
* the order in which redeems were applied.
|
|
3277
|
+
*/
|
|
3278
|
+
applyRedeem(input) {
|
|
3279
|
+
if (this.deleted) return;
|
|
3280
|
+
const { line_uid, ...rest } = input;
|
|
3281
|
+
const redeem = buildRedeem(rest);
|
|
3282
|
+
if (line_uid === void 0) {
|
|
3283
|
+
this.commitLines({ redeem: [...this.draft.redeem ?? [], redeem] });
|
|
3284
|
+
return;
|
|
3285
|
+
}
|
|
3286
|
+
const line = this.draft.lines.find((item) => item.uid === line_uid);
|
|
3287
|
+
if (!line || line.parent_uid != null || line.type === "FEE") throw new Error(`Redeem requires a root product line, got ${line_uid}`);
|
|
3288
|
+
const lines = this.draft.lines.map((item) => {
|
|
3289
|
+
if (item.uid !== line_uid) return item;
|
|
3290
|
+
return {
|
|
3291
|
+
...item,
|
|
3292
|
+
redeem: [...item.redeem ?? [], redeem]
|
|
3293
|
+
};
|
|
3294
|
+
});
|
|
3295
|
+
this.commitLines({ lines });
|
|
3296
|
+
}
|
|
3297
|
+
/**
|
|
3298
|
+
* Removes `quantity` units of `reward_id` from `line_uid` or the order,
|
|
3299
|
+
* latest applied first. Removing more units than applied clears them all.
|
|
3300
|
+
*/
|
|
3301
|
+
removeRedeem(input) {
|
|
3302
|
+
if (this.deleted) return;
|
|
3303
|
+
const { reward_id, quantity, line_uid } = input;
|
|
3304
|
+
if (line_uid === void 0) {
|
|
3305
|
+
const p = [
|
|
3306
|
+
this.draft.redeem ?? [],
|
|
3307
|
+
reward_id,
|
|
3308
|
+
quantity
|
|
3309
|
+
];
|
|
3310
|
+
this.commitLines({ redeem: subtractRedeem(...p) });
|
|
3311
|
+
return;
|
|
3312
|
+
}
|
|
3313
|
+
const lines = this.draft.lines.map((item) => {
|
|
3314
|
+
if (item.uid !== line_uid) return item;
|
|
3315
|
+
const p = [
|
|
3316
|
+
item.redeem ?? [],
|
|
3317
|
+
reward_id,
|
|
3318
|
+
quantity
|
|
3319
|
+
];
|
|
3320
|
+
return {
|
|
3321
|
+
...item,
|
|
3322
|
+
redeem: subtractRedeem(...p)
|
|
3323
|
+
};
|
|
3324
|
+
});
|
|
3325
|
+
this.commitLines({ lines });
|
|
2975
3326
|
}
|
|
2976
3327
|
setScope(scope) {
|
|
2977
3328
|
if (this.deleted) return;
|
|
@@ -3068,7 +3419,11 @@ var Order = class Order {
|
|
|
3068
3419
|
lines: draft.lines,
|
|
3069
3420
|
redeem: draft.redeem ?? []
|
|
3070
3421
|
};
|
|
3071
|
-
const
|
|
3422
|
+
const steps = draft.redemption_steps ?? [];
|
|
3423
|
+
const b5 = {
|
|
3424
|
+
totals: draft.totals,
|
|
3425
|
+
redemption_steps: steps
|
|
3426
|
+
};
|
|
3072
3427
|
const body = {
|
|
3073
3428
|
...b1,
|
|
3074
3429
|
...b2,
|
|
@@ -3092,6 +3447,25 @@ var Order = class Order {
|
|
|
3092
3447
|
this.synced = cloneOrder(next);
|
|
3093
3448
|
this.notify();
|
|
3094
3449
|
}
|
|
3450
|
+
/** Applies lines/redeem, replaying redemption steps and totals. */
|
|
3451
|
+
commitLines(patch) {
|
|
3452
|
+
const lines = patch.lines ?? this.draft.lines;
|
|
3453
|
+
const redeem = patch.redeem ?? this.draft.redeem ?? [];
|
|
3454
|
+
const { redemption_steps, totals } = recomputeRedemptions({
|
|
3455
|
+
lines,
|
|
3456
|
+
redeem,
|
|
3457
|
+
redemption_steps: this.draft.redemption_steps ?? [],
|
|
3458
|
+
currency: this.draft.totals.currency
|
|
3459
|
+
});
|
|
3460
|
+
this.draft = {
|
|
3461
|
+
...this.draft,
|
|
3462
|
+
lines,
|
|
3463
|
+
redeem,
|
|
3464
|
+
redemption_steps,
|
|
3465
|
+
totals
|
|
3466
|
+
};
|
|
3467
|
+
this.scheduleAfterMutate();
|
|
3468
|
+
}
|
|
3095
3469
|
scheduleAfterMutate() {
|
|
3096
3470
|
this.flush.markDirtyDuringFlight();
|
|
3097
3471
|
this.notify();
|
|
@@ -3611,6 +3985,152 @@ const validateProductRoot = (root, lines, lookup, locale, out) => {
|
|
|
3611
3985
|
}
|
|
3612
3986
|
compareTrees(lines, expected, root.uid, expectedRoot.uid, out);
|
|
3613
3987
|
};
|
|
3988
|
+
const isExpired = (redeem, now) => {
|
|
3989
|
+
if (redeem.expires_at === void 0) return false;
|
|
3990
|
+
return Date.parse(redeem.expires_at) <= now;
|
|
3991
|
+
};
|
|
3992
|
+
const expiredIssues = (redeem, now) => {
|
|
3993
|
+
const issues = [];
|
|
3994
|
+
redeem.forEach((item, index) => {
|
|
3995
|
+
if (!isExpired(item, now)) return;
|
|
3996
|
+
const field = `redeem.${index}.expires_at`;
|
|
3997
|
+
issues.push({
|
|
3998
|
+
code: "REDEEM_EXPIRED",
|
|
3999
|
+
field,
|
|
4000
|
+
actual: item.expires_at
|
|
4001
|
+
});
|
|
4002
|
+
});
|
|
4003
|
+
return issues;
|
|
4004
|
+
};
|
|
4005
|
+
const redeemSignature = (items) => {
|
|
4006
|
+
return items.map((item) => `${item.reward_id}:${item.quantity}`).join(",");
|
|
4007
|
+
};
|
|
4008
|
+
const stepSignature = (steps) => {
|
|
4009
|
+
return steps.map((step) => `${step.reward_id}:${step.reward_quantity}`).join(",");
|
|
4010
|
+
};
|
|
4011
|
+
/** Every target's redeems must have one step each, in the same order. */
|
|
4012
|
+
const checkRedemptionTargets = (lines, redeem, steps, issues, lineIssues) => {
|
|
4013
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
4014
|
+
for (const step of steps) {
|
|
4015
|
+
const key = redemptionTargetKey(step);
|
|
4016
|
+
byTarget.set(key, [...byTarget.get(key) ?? [], step]);
|
|
4017
|
+
}
|
|
4018
|
+
const check = (key, items) => {
|
|
4019
|
+
const targetSteps = byTarget.get(key) ?? [];
|
|
4020
|
+
byTarget.delete(key);
|
|
4021
|
+
const expected = redeemSignature(items);
|
|
4022
|
+
const actual = stepSignature(targetSteps);
|
|
4023
|
+
if (expected === actual) return null;
|
|
4024
|
+
return {
|
|
4025
|
+
code: "REDEMPTION_STEPS_MISMATCH",
|
|
4026
|
+
field: "redemption_steps",
|
|
4027
|
+
expected,
|
|
4028
|
+
actual
|
|
4029
|
+
};
|
|
4030
|
+
};
|
|
4031
|
+
const orderIssue = check(redemptionTargetKey({ target: "ORDER" }), redeem);
|
|
4032
|
+
if (orderIssue) issues.push(orderIssue);
|
|
4033
|
+
for (const line of lines) {
|
|
4034
|
+
const lineIssue = check(redemptionTargetKey({
|
|
4035
|
+
target: "LINE",
|
|
4036
|
+
target_id: line.uid
|
|
4037
|
+
}), line.redeem ?? []);
|
|
4038
|
+
if (lineIssue) pushLineIssue(lineIssues, line.uid, lineIssue);
|
|
4039
|
+
}
|
|
4040
|
+
for (const orphan of byTarget.values()) {
|
|
4041
|
+
const b1 = { code: "REDEMPTION_STEPS_MISMATCH" };
|
|
4042
|
+
const b2 = {
|
|
4043
|
+
field: "redemption_steps.target_id",
|
|
4044
|
+
expected: ""
|
|
4045
|
+
};
|
|
4046
|
+
issues.push({
|
|
4047
|
+
...b1,
|
|
4048
|
+
...b2,
|
|
4049
|
+
actual: stepSignature(orphan)
|
|
4050
|
+
});
|
|
4051
|
+
}
|
|
4052
|
+
};
|
|
4053
|
+
const STATE_FIELDS = [
|
|
4054
|
+
"value",
|
|
4055
|
+
"quantity",
|
|
4056
|
+
"discount_amount",
|
|
4057
|
+
"discount_rate"
|
|
4058
|
+
];
|
|
4059
|
+
const STATE_KEYS = [
|
|
4060
|
+
"before",
|
|
4061
|
+
"after",
|
|
4062
|
+
"target_before",
|
|
4063
|
+
"target_after"
|
|
4064
|
+
];
|
|
4065
|
+
const flattenState = (prefix, state, out) => {
|
|
4066
|
+
for (const field of STATE_FIELDS) out[`${prefix}.${field}`] = state ? bigStr(state[field]) : "";
|
|
4067
|
+
};
|
|
4068
|
+
const flattenStep = (step) => {
|
|
4069
|
+
const b1 = { reward_id: step.reward_id };
|
|
4070
|
+
const b2 = { reward_quantity: String(step.reward_quantity) };
|
|
4071
|
+
const b3 = {
|
|
4072
|
+
target: step.target,
|
|
4073
|
+
target_id: step.target_id ?? ""
|
|
4074
|
+
};
|
|
4075
|
+
const b4 = { action: step.action };
|
|
4076
|
+
const b5 = { discount_amount: bigStr(step.discount_amount) };
|
|
4077
|
+
const out = {
|
|
4078
|
+
...b1,
|
|
4079
|
+
...b2,
|
|
4080
|
+
...b3,
|
|
4081
|
+
...b4,
|
|
4082
|
+
...b5
|
|
4083
|
+
};
|
|
4084
|
+
for (const key of STATE_KEYS) flattenState(key, step[key], out);
|
|
4085
|
+
return out;
|
|
4086
|
+
};
|
|
4087
|
+
/** Replayed steps must equal the submitted ones field by field. */
|
|
4088
|
+
const compareRedemptionSteps = (submitted, expected, issues) => {
|
|
4089
|
+
if (submitted.length !== expected.length) {
|
|
4090
|
+
const b1 = { code: "REDEMPTION_MISMATCH" };
|
|
4091
|
+
const b2 = { field: "redemption_steps" };
|
|
4092
|
+
const b3 = { expected: String(expected.length) };
|
|
4093
|
+
issues.push({
|
|
4094
|
+
...b1,
|
|
4095
|
+
...b2,
|
|
4096
|
+
...b3,
|
|
4097
|
+
actual: String(submitted.length)
|
|
4098
|
+
});
|
|
4099
|
+
return;
|
|
4100
|
+
}
|
|
4101
|
+
submitted.forEach((step, index) => {
|
|
4102
|
+
const actual = flattenStep(step);
|
|
4103
|
+
const wanted = flattenStep(expected[index]);
|
|
4104
|
+
for (const field of Object.keys(wanted)) {
|
|
4105
|
+
if (actual[field] === wanted[field]) continue;
|
|
4106
|
+
const b1 = { code: "REDEMPTION_MISMATCH" };
|
|
4107
|
+
const b2 = { field: `redemption_steps.${index}.${field}` };
|
|
4108
|
+
const b3 = {
|
|
4109
|
+
expected: wanted[field],
|
|
4110
|
+
actual: actual[field]
|
|
4111
|
+
};
|
|
4112
|
+
issues.push({
|
|
4113
|
+
...b1,
|
|
4114
|
+
...b2,
|
|
4115
|
+
...b3
|
|
4116
|
+
});
|
|
4117
|
+
}
|
|
4118
|
+
});
|
|
4119
|
+
};
|
|
4120
|
+
const validateRedemptions = (input, issues, lineIssues) => {
|
|
4121
|
+
const redeem = input.redeem ?? [];
|
|
4122
|
+
const steps = input.redemptionSteps ?? [];
|
|
4123
|
+
const now = input.now ?? Date.now();
|
|
4124
|
+
issues.push(...expiredIssues(redeem, now));
|
|
4125
|
+
for (const line of input.lines) {
|
|
4126
|
+
const expired = expiredIssues(line.redeem ?? [], now);
|
|
4127
|
+
for (const issue of expired) pushLineIssue(lineIssues, line.uid, issue);
|
|
4128
|
+
}
|
|
4129
|
+
checkRedemptionTargets(input.lines, redeem, steps, issues, lineIssues);
|
|
4130
|
+
const expected = computeRedemptionSteps(input.lines, redeem, steps);
|
|
4131
|
+
compareRedemptionSteps(steps, expected, issues);
|
|
4132
|
+
return expected;
|
|
4133
|
+
};
|
|
3614
4134
|
/** Compare cart lines/totals against a fresh menu (exact Big.js equality). */
|
|
3615
4135
|
const validateOrderAgainstMenu = (input) => {
|
|
3616
4136
|
const locale = input.locale ?? "en_us";
|
|
@@ -3641,7 +4161,8 @@ const validateOrderAgainstMenu = (input) => {
|
|
|
3641
4161
|
}
|
|
3642
4162
|
checkRootProductQuantities(roots, lookup, lineIssues);
|
|
3643
4163
|
if (input.expectedFeeLines) compareExpectedFeeLines(input.lines, input.expectedFeeLines, lineIssues);
|
|
3644
|
-
const
|
|
4164
|
+
const expectedSteps = validateRedemptions(input, issues, lineIssues);
|
|
4165
|
+
const expectedTotals = applyRedemptionTotals(recomputeTotals(input.lines, input.totals.currency), expectedSteps);
|
|
3645
4166
|
for (const field of TOTALS_FIELDS) {
|
|
3646
4167
|
if (bigEq(input.totals[field], expectedTotals[field])) continue;
|
|
3647
4168
|
issues.push({
|
|
@@ -5018,4 +5539,4 @@ const paymentMatchesClientConditions = (conditions, order, options) => {
|
|
|
5018
5539
|
return Boolean(engine.matchOrConditions(groups).match);
|
|
5019
5540
|
};
|
|
5020
5541
|
//#endregion
|
|
5021
|
-
export { DEFAULT_RECOMMENDATION_TYPES, Order, OrderSyncPendingError, OrderValidationError, RecurringDateCron, RulesEngine, StorefrontHttpError, addLines, auth, buildCustomerSnapshot, collectOrderLineDiffs, countCartProducts, createSession, currentNodes, defaultConditionError, flattenMenuCategories, fromOrderLines, getScheduleWindow, indexMenu, isCheckoutFieldFilled, isDeviceNotLinked, isKioskUnauthorized, isMilestoneStepDone, isOnSchedule, isOrderValidationError, isStorefrontNotFound, listMenusStream, matchAbsoluteDateOperator, matchBooleanOperator, matchCurrencyOperator, matchEnumOperator, matchNumberOperator, matchRecurringDateOperator, matchRelativeDateOperator, matchStringOperator, milestoneSteps, milestoneTrack, nestMenuCategories, paymentMatchesClientConditions, recomputeTotals, resolveCheckout, services, storefrontJson, toOrderLines, validateOrderAgainstMenu, visitedStates };
|
|
5542
|
+
export { DEFAULT_RECOMMENDATION_TYPES, Order, OrderSyncPendingError, OrderValidationError, RecurringDateCron, RulesEngine, StorefrontHttpError, addLines, auth, buildCustomerSnapshot, collectOrderLineDiffs, countCartProducts, createSession, currentNodes, defaultConditionError, flattenMenuCategories, fromOrderLines, genId, genOrderId, getScheduleWindow, indexMenu, isCheckoutFieldFilled, isDeviceNotLinked, isKioskUnauthorized, isMilestoneStepDone, isOnSchedule, isOrderValidationError, isStorefrontNotFound, listMenusStream, matchAbsoluteDateOperator, matchBooleanOperator, matchCurrencyOperator, matchEnumOperator, matchNumberOperator, matchRecurringDateOperator, matchRelativeDateOperator, matchStringOperator, milestoneSteps, milestoneTrack, nestMenuCategories, paymentMatchesClientConditions, recomputeRedemptions, recomputeTotals, resolveCheckout, services, storefrontJson, toOrderLines, validateOrderAgainstMenu, visitedStates };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fire-spark/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Fire Spark headless SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
14
|
-
"development": "./src/index.ts",
|
|
15
14
|
"types": "./dist/index.d.ts",
|
|
16
15
|
"import": "./dist/index.js",
|
|
17
16
|
"default": "./dist/index.js"
|
|
@@ -20,7 +19,7 @@
|
|
|
20
19
|
"dependencies": {
|
|
21
20
|
"big.js": "6.2.1",
|
|
22
21
|
"dayjs": "1.11.9",
|
|
23
|
-
"@fire-spark/types": "0.0.
|
|
22
|
+
"@fire-spark/types": "0.0.2"
|
|
24
23
|
},
|
|
25
24
|
"devDependencies": {
|
|
26
25
|
"@types/big.js": "6.2.2",
|