@delicity/client-cart 1.5.8 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +1 -0
  2. package/{classes → dist/cjs/classes}/Cart.d.ts +7 -6
  3. package/dist/cjs/classes/Cart.d.ts.map +1 -0
  4. package/{classes → dist/cjs/classes}/Cart.js +78 -51
  5. package/dist/cjs/classes/Cart.js.map +1 -0
  6. package/dist/cjs/classes/Checkout.d.ts.map +1 -0
  7. package/dist/cjs/classes/Checkout.js.map +1 -0
  8. package/dist/cjs/index.d.ts.map +1 -0
  9. package/dist/cjs/index.js.map +1 -0
  10. package/dist/cjs/service/CartLoaderService.d.ts.map +1 -0
  11. package/{service → dist/cjs/service}/CartLoaderService.js +3 -4
  12. package/dist/cjs/service/CartLoaderService.js.map +1 -0
  13. package/{service → dist/cjs/service}/CartService.d.ts +1 -0
  14. package/dist/cjs/service/CartService.d.ts.map +1 -0
  15. package/{service → dist/cjs/service}/CartService.js +31 -17
  16. package/dist/cjs/service/CartService.js.map +1 -0
  17. package/dist/cjs/service/CategoryAvailabilityService.d.ts.map +1 -0
  18. package/dist/cjs/service/CategoryAvailabilityService.js.map +1 -0
  19. package/dist/cjs/service/DeliveryService.d.ts.map +1 -0
  20. package/{service → dist/cjs/service}/DeliveryService.js +1 -2
  21. package/dist/cjs/service/DeliveryService.js.map +1 -0
  22. package/dist/cjs/service/HelperService.d.ts.map +1 -0
  23. package/dist/cjs/service/HelperService.js.map +1 -0
  24. package/{types → dist/cjs/types}/CartInterface.d.ts +10 -1
  25. package/dist/cjs/types/CartInterface.d.ts.map +1 -0
  26. package/dist/cjs/types/CartInterface.js.map +1 -0
  27. package/{types → dist/cjs/types}/MenuInterface.d.ts +2 -1
  28. package/dist/cjs/types/MenuInterface.d.ts.map +1 -0
  29. package/dist/cjs/types/MenuInterface.js.map +1 -0
  30. package/dist/cjs/types/MerchantInterface.d.ts.map +1 -0
  31. package/dist/cjs/types/MerchantInterface.js.map +1 -0
  32. package/dist/esm/classes/Cart.js +705 -0
  33. package/dist/esm/classes/Cart.js.map +1 -0
  34. package/dist/esm/classes/Checkout.js +342 -0
  35. package/dist/esm/classes/Checkout.js.map +1 -0
  36. package/dist/esm/index.js +9 -0
  37. package/dist/esm/index.js.map +1 -0
  38. package/dist/esm/service/CartLoaderService.js +113 -0
  39. package/dist/esm/service/CartLoaderService.js.map +1 -0
  40. package/dist/esm/service/CartService.js +355 -0
  41. package/dist/esm/service/CartService.js.map +1 -0
  42. package/dist/esm/service/CategoryAvailabilityService.js +95 -0
  43. package/dist/esm/service/CategoryAvailabilityService.js.map +1 -0
  44. package/dist/esm/service/DeliveryService.js +26 -0
  45. package/dist/esm/service/DeliveryService.js.map +1 -0
  46. package/dist/esm/service/HelperService.js +10 -0
  47. package/dist/esm/service/HelperService.js.map +1 -0
  48. package/dist/esm/types/CartInterface.js +7 -0
  49. package/dist/esm/types/CartInterface.js.map +1 -0
  50. package/dist/esm/types/MenuInterface.js +2 -0
  51. package/dist/esm/types/MenuInterface.js.map +1 -0
  52. package/dist/esm/types/MerchantInterface.js +2 -0
  53. package/dist/esm/types/MerchantInterface.js.map +1 -0
  54. package/package.json +21 -8
  55. package/classes/Cart.d.ts.map +0 -1
  56. package/classes/Cart.js.map +0 -1
  57. package/classes/Checkout.d.ts.map +0 -1
  58. package/classes/Checkout.js.map +0 -1
  59. package/index.d.ts.map +0 -1
  60. package/index.js.map +0 -1
  61. package/service/CartLoaderService.d.ts.map +0 -1
  62. package/service/CartLoaderService.js.map +0 -1
  63. package/service/CartService.d.ts.map +0 -1
  64. package/service/CartService.js.map +0 -1
  65. package/service/CategoryAvailabilityService.d.ts.map +0 -1
  66. package/service/CategoryAvailabilityService.js.map +0 -1
  67. package/service/DeliveryService.d.ts.map +0 -1
  68. package/service/DeliveryService.js.map +0 -1
  69. package/service/HelperService.d.ts.map +0 -1
  70. package/service/HelperService.js.map +0 -1
  71. package/service/UrlService.d.ts +0 -23
  72. package/service/UrlService.d.ts.map +0 -1
  73. package/service/UrlService.js +0 -78
  74. package/service/UrlService.js.map +0 -1
  75. package/types/CartInterface.d.ts.map +0 -1
  76. package/types/CartInterface.js.map +0 -1
  77. package/types/MenuInterface.d.ts.map +0 -1
  78. package/types/MenuInterface.js.map +0 -1
  79. package/types/MerchantInterface.d.ts.map +0 -1
  80. package/types/MerchantInterface.js.map +0 -1
  81. /package/{classes → dist/cjs/classes}/Checkout.d.ts +0 -0
  82. /package/{classes → dist/cjs/classes}/Checkout.js +0 -0
  83. /package/{index.d.ts → dist/cjs/index.d.ts} +0 -0
  84. /package/{index.js → dist/cjs/index.js} +0 -0
  85. /package/{service → dist/cjs/service}/CartLoaderService.d.ts +0 -0
  86. /package/{service → dist/cjs/service}/CategoryAvailabilityService.d.ts +0 -0
  87. /package/{service → dist/cjs/service}/CategoryAvailabilityService.js +0 -0
  88. /package/{service → dist/cjs/service}/DeliveryService.d.ts +0 -0
  89. /package/{service → dist/cjs/service}/HelperService.d.ts +0 -0
  90. /package/{service → dist/cjs/service}/HelperService.js +0 -0
  91. /package/{types → dist/cjs/types}/CartInterface.js +0 -0
  92. /package/{types → dist/cjs/types}/MenuInterface.js +0 -0
  93. /package/{types → dist/cjs/types}/MerchantInterface.d.ts +0 -0
  94. /package/{types → dist/cjs/types}/MerchantInterface.js +0 -0
@@ -0,0 +1,705 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { OrderModesEnum } from "../types/CartInterface";
11
+ import { DateTime } from 'luxon';
12
+ import { v4 as uuidv4 } from 'uuid';
13
+ import { addDiscountCode, addonIntegrity, fetchMerchantMenu, getProductCount, getProductPrice, getProductPriceSum, getTotalModifiersUnitPrice, isItemAlreadyInCart, isItemAvailable, mandatoryModifierCheck, removeDiscountCode, synchronizeCart, transformMenuAddonInCartAddon, isBundleAvailable, validateBundleRequirements, validateBundlePartQuantities, getBundlePrice, isBundleAlreadyInCart } from "../service/CartService";
14
+ import { getDeliveryDetails } from "../service/DeliveryService";
15
+ import { formatPrice } from "../service/HelperService";
16
+ // @ts-ignore
17
+ import ScheduleManagement from "@delicity/backend-schedule-management";
18
+ import { categoryAvailable } from "../service/CategoryAvailabilityService";
19
+ export class Cart {
20
+ constructor(config, merchant, menu, OrderMode, when) {
21
+ this.acceptCondition = false;
22
+ this.errors = [];
23
+ this.listeners = {};
24
+ this.isPriceLoading = false;
25
+ this.isDeliveryLoading = false;
26
+ this.syncPromise = null;
27
+ this.voucherBalance = {
28
+ edenred: 0
29
+ };
30
+ this.merchant = merchant;
31
+ this.menu = JSON.parse(JSON.stringify(menu));
32
+ this.config = config;
33
+ this.remoteErrors = {};
34
+ if (this.merchant.schedule) {
35
+ this.merchantSchedule = new ScheduleManagement(this.merchant.schedule.slots, this.merchant.schedule.closes, this.merchant.schedule.options);
36
+ }
37
+ this.cart = {
38
+ uuid: uuidv4(),
39
+ createdAt: DateTime.now().toISO(),
40
+ merchantId: merchant.id,
41
+ preparationDurationBetween: null,
42
+ items: [],
43
+ totalItems: 0,
44
+ totalItemPrice: 0,
45
+ paymentMethods: [],
46
+ when: when,
47
+ comment: null,
48
+ type: OrderMode,
49
+ addons: menu.addons.map(a => transformMenuAddonInCartAddon(a))
50
+ };
51
+ }
52
+ on(event, callback) {
53
+ if (!this.listeners[event])
54
+ this.listeners[event] = [];
55
+ this.listeners[event].push(callback);
56
+ }
57
+ off(event, callback) {
58
+ const list = this.listeners[event];
59
+ if (!list)
60
+ return;
61
+ if (!callback) {
62
+ this.listeners[event] = [];
63
+ return;
64
+ }
65
+ this.listeners[event] = list.filter(cb => cb !== callback);
66
+ }
67
+ trigger(event, ...args) {
68
+ const list = this.listeners[event];
69
+ if (!list)
70
+ return;
71
+ for (const cb of list) {
72
+ try {
73
+ cb(...args);
74
+ }
75
+ catch (e) {
76
+ console.error(`[client-cart] listener for "${event}" threw:`, e);
77
+ }
78
+ }
79
+ }
80
+ setMenu(menu) {
81
+ this.menu = JSON.parse(JSON.stringify(menu));
82
+ this.cartIntegrity();
83
+ this.updateCartProductPrices();
84
+ this.trigger('update-cart');
85
+ return this;
86
+ }
87
+ reloadMenu() {
88
+ return __awaiter(this, void 0, void 0, function* () {
89
+ const menu = yield fetchMerchantMenu(this.config, this.merchant.slug);
90
+ this.menu = JSON.parse(JSON.stringify(menu));
91
+ this.cartIntegrity(true);
92
+ this.updateCartProductPrices();
93
+ this.trigger('update-cart');
94
+ return this;
95
+ });
96
+ }
97
+ setCart(cart) {
98
+ if (!cart.paymentMethods)
99
+ cart.paymentMethods = [];
100
+ this.cart = cart;
101
+ this.cartIntegrity();
102
+ this.updateCartProductPrices();
103
+ if (cart.addons) {
104
+ cart.addons = addonIntegrity(cart.addons, this.menu.addons);
105
+ // Add missing addon if any
106
+ this.menu.addons.map(a => transformMenuAddonInCartAddon(a))
107
+ .filter(a => cart.addons && !cart.addons.map(e => e.name).includes(a.name))
108
+ .forEach(a => { var _a; return (_a = cart.addons) === null || _a === void 0 ? void 0 : _a.push(a); });
109
+ }
110
+ this.trigger('update-cart');
111
+ return this;
112
+ }
113
+ getPrices() {
114
+ return this.cart.prices || null;
115
+ }
116
+ getCartAddons() {
117
+ return this.cart.addons || [];
118
+ }
119
+ getAcceptCondition() {
120
+ return this.acceptCondition;
121
+ }
122
+ setAcceptCondition(acceptCondition) {
123
+ this.acceptCondition = acceptCondition;
124
+ return this;
125
+ }
126
+ getPreparationDurationBetween() {
127
+ return this.cart.preparationDurationBetween;
128
+ }
129
+ addonIncrement(identifier) {
130
+ if (!this.cart.addons)
131
+ return;
132
+ const addonIndex = this.cart.addons.findIndex(e => e.identifier === identifier);
133
+ if (addonIndex === -1)
134
+ return;
135
+ this.cart.addons[addonIndex].quantity++;
136
+ // if addon is checkbox, set quantity to 1
137
+ if (!this.cart.addons[addonIndex].quantitySelector)
138
+ this.cart.addons[addonIndex].quantity = 1;
139
+ this.cart.addons[addonIndex].totalPriceWt = this.cart.addons[addonIndex].quantity * this.cart.addons[addonIndex].unitPriceWt;
140
+ }
141
+ addonDecrement(identifier) {
142
+ if (!this.cart.addons)
143
+ return;
144
+ const addonIndex = this.cart.addons.findIndex(e => e.identifier === identifier);
145
+ if (addonIndex === -1)
146
+ return;
147
+ if (this.cart.addons[addonIndex].quantity === 0)
148
+ return;
149
+ this.cart.addons[addonIndex].quantity--;
150
+ // if addon is checkbox, reset quantity to 0
151
+ if (!this.cart.addons[addonIndex].quantitySelector)
152
+ this.cart.addons[addonIndex].quantity = 0;
153
+ this.cart.addons[addonIndex].totalPriceWt = this.cart.addons[addonIndex].quantity * this.cart.addons[addonIndex].unitPriceWt;
154
+ }
155
+ getIsDeliveryLoading() {
156
+ return this.isDeliveryLoading;
157
+ }
158
+ getIsPriceLoading() {
159
+ return this.isPriceLoading;
160
+ }
161
+ getRemoteErrors() {
162
+ return this.remoteErrors;
163
+ }
164
+ setAddress(address) {
165
+ return __awaiter(this, void 0, void 0, function* () {
166
+ if (this.cart.type !== OrderModesEnum.DELIVERY)
167
+ throw new Error('invalid_order_mode');
168
+ this.isDeliveryLoading = true;
169
+ const delivery = yield getDeliveryDetails(this.config, this.merchant.id, address.position);
170
+ this.cart.delivery = Object.assign({ address }, delivery);
171
+ this.isDeliveryLoading = false;
172
+ this.trigger('update-cart');
173
+ return this;
174
+ });
175
+ }
176
+ getDeliveryInfos() {
177
+ return this.cart.delivery;
178
+ }
179
+ setCartComment(comment) {
180
+ this.cart.comment = comment;
181
+ return this;
182
+ }
183
+ getCartComment() {
184
+ return this.cart.comment;
185
+ }
186
+ updateCartProductPrices() {
187
+ for (let index in this.cart.items) {
188
+ const cartItem = this.cart.items[index];
189
+ const menuItem = this.menu.items.find(m => m.id === cartItem.item.id);
190
+ if (!menuItem) {
191
+ this.cart.items.splice(parseInt(index), 1);
192
+ continue;
193
+ }
194
+ cartItem.item = menuItem;
195
+ if (cartItem.type === 'BUNDLE') {
196
+ // Handle bundle items
197
+ const bundleItems = cartItem.bundleItems || [];
198
+ const quantity = cartItem.quantity;
199
+ const unitPrice = getBundlePrice(menuItem, bundleItems, this.cart.type);
200
+ cartItem.unitPrice = unitPrice;
201
+ cartItem.totalPrice = unitPrice * quantity;
202
+ }
203
+ else {
204
+ // Handle regular products
205
+ const modifierGroups = cartItem.modifierGroups;
206
+ const quantity = cartItem.quantity;
207
+ cartItem.unitPrice = getProductPrice(menuItem, this.cart.type) + getTotalModifiersUnitPrice(modifierGroups || []);
208
+ cartItem.totalPrice = (getProductPrice(menuItem, this.cart.type) + getTotalModifiersUnitPrice(modifierGroups || [])) * quantity;
209
+ }
210
+ }
211
+ this.cart.totalItems = getProductCount(this.cart);
212
+ this.cart.totalItemPrice = getProductPriceSum(this.cart);
213
+ if (this.cart.paymentMethods && this.cart.paymentMethods.length > 0) {
214
+ this.updatePaymentMethodAmount();
215
+ }
216
+ this.validateCartIfError();
217
+ }
218
+ setOrderMode(mode) {
219
+ this.cart.type = mode;
220
+ if (!this.canBePayOnsite() && this.getPaymentMethods().some(e => e.type === 'cash')) {
221
+ this.cart.paymentMethods = [];
222
+ }
223
+ this.updateCartProductPrices();
224
+ this.trigger('update-cart');
225
+ return this;
226
+ }
227
+ setWhen(when) {
228
+ this.cart.when = when;
229
+ this.trigger('update-cart');
230
+ this.cartIntegrity(true);
231
+ this.validateCartIfError();
232
+ return this;
233
+ }
234
+ setAddressComment(comment) {
235
+ var _a;
236
+ if ((_a = this.cart.delivery) === null || _a === void 0 ? void 0 : _a.address) {
237
+ this.cart.delivery.address.additional_informations = comment;
238
+ }
239
+ return this;
240
+ }
241
+ getMerchantId() {
242
+ return this.merchant.id;
243
+ }
244
+ getMerchant() {
245
+ return this.merchant;
246
+ }
247
+ getMerchantMenu() {
248
+ return this.menu;
249
+ }
250
+ getCartUUID() {
251
+ return this.cart.uuid;
252
+ }
253
+ getOrderMode() {
254
+ return this.cart.type;
255
+ }
256
+ getWhen() {
257
+ return this.cart.when;
258
+ }
259
+ getPaymentMethods() {
260
+ return this.cart.paymentMethods || [];
261
+ }
262
+ setPaymentMethods(paymentMethods) {
263
+ this.cart.paymentMethods = paymentMethods;
264
+ return this;
265
+ }
266
+ getPaymentMethodTotalAmount() {
267
+ return this.cart.paymentMethods.reduce((a, value) => a + value.amount, 0);
268
+ }
269
+ isVoucherEligible() {
270
+ return this.merchant.features.edenredIntegrationEnabled;
271
+ }
272
+ isOrderFree() {
273
+ return this.getTotalPrice() === 0;
274
+ }
275
+ isVoucherPayingEverything() {
276
+ return this.cart.paymentMethods.some(p => p.type === 'meal-voucher' && p.amount === this.getTotalPrice());
277
+ }
278
+ enablePaymentMethodVoucher(type, availableVoucherAmount) {
279
+ if (!this.isVoucherEligible())
280
+ throw new Error("voucher_not_available_in_this_merchant");
281
+ // Set current voucher balance
282
+ this.setVoucherBalance(type, availableVoucherAmount);
283
+ // Keep Only Existing payment method
284
+ this.cart.paymentMethods = this.cart.paymentMethods.filter(p => p.type !== 'meal-voucher');
285
+ // Get Amount to pay with voucher (Only product price)
286
+ const totalToPayVoucher = Math.min(this.voucherBalance[type], this.getTotalItemPrice());
287
+ // Set Meal Voucher
288
+ this.cart.paymentMethods.push({
289
+ type: "meal-voucher",
290
+ voucher: { type },
291
+ amount: totalToPayVoucher
292
+ });
293
+ // Update current card amount if defined
294
+ const leftToPayWithCard = this.getTotalPrice() - totalToPayVoucher;
295
+ if (leftToPayWithCard > 0 && this.cart.paymentMethods.some(p => p.type !== 'meal-voucher')) {
296
+ const cardIndex = this.cart.paymentMethods.findIndex(p => p.type !== 'meal-voucher');
297
+ this.cart.paymentMethods[cardIndex].amount = leftToPayWithCard;
298
+ }
299
+ this.updatePaymentMethodAmount();
300
+ return this;
301
+ }
302
+ disablePaymentMethodVoucher() {
303
+ // Keep Only Existing payment method
304
+ this.cart.paymentMethods = this.cart.paymentMethods.filter(p => p.type !== 'meal-voucher');
305
+ this.updatePaymentMethodAmount();
306
+ return this;
307
+ }
308
+ paymentMethodCard(type, token, brand, last4, expiration) {
309
+ // Keep only meal-voucher
310
+ this.cart.paymentMethods = this.cart.paymentMethods.filter(p => p.type === 'meal-voucher');
311
+ // Get left to pay with Card
312
+ let leftToPay = Math.max(this.getTotalPrice() - this.getPaymentMethodTotalAmount(), 0);
313
+ // Add Card
314
+ this.cart.paymentMethods.push({
315
+ type: 'card',
316
+ amount: leftToPay,
317
+ card: { type, token, brand, last4, expiration }
318
+ });
319
+ this.updatePaymentMethodAmount();
320
+ return this;
321
+ }
322
+ paymentMethodWallet(type) {
323
+ // Keep only meal-voucher
324
+ this.cart.paymentMethods = this.cart.paymentMethods.filter(p => p.type === 'meal-voucher');
325
+ // Get left to pay with Card
326
+ let leftToPay = Math.max(this.getTotalPrice() - this.getPaymentMethodTotalAmount(), 0);
327
+ // Add Wallet
328
+ this.cart.paymentMethods.push({
329
+ type: "wallet",
330
+ wallet: { type },
331
+ amount: leftToPay,
332
+ });
333
+ return this;
334
+ }
335
+ // Set All order is paid onsite
336
+ paymentMethodOnsite() {
337
+ this.cart.paymentMethods = [];
338
+ this.cart.paymentMethods.push({
339
+ type: 'cash',
340
+ amount: this.getTotalPrice(),
341
+ });
342
+ return this;
343
+ }
344
+ paymentMethodDeliveryCash() {
345
+ this.cart.paymentMethods = [];
346
+ this.cart.paymentMethods.push({
347
+ type: 'delivery_cash',
348
+ amount: this.getTotalPrice(),
349
+ });
350
+ return this;
351
+ }
352
+ updatePaymentMethodAmount() {
353
+ var _a, _b, _c;
354
+ if (!this.isOrderFree()) {
355
+ this.cart.paymentMethods = this.cart.paymentMethods.filter(p => p.type !== 'free');
356
+ }
357
+ if (((_a = this.cart.paymentMethods) === null || _a === void 0 ? void 0 : _a.length) === 1) {
358
+ if (this.cart.paymentMethods[0].type !== 'meal-voucher') {
359
+ this.cart.paymentMethods[0].amount = this.getTotalPrice(); // Card Pay all
360
+ }
361
+ else if (this.cart.paymentMethods[0].voucher) {
362
+ // Voucher Can pay only product
363
+ this.cart.paymentMethods[0].amount = Math.min(this.voucherBalance[this.cart.paymentMethods[0].voucher.type], this.getTotalItemPrice());
364
+ }
365
+ }
366
+ else if (((_b = this.cart.paymentMethods) === null || _b === void 0 ? void 0 : _b.length) === 2 && ((_c = this.cart.paymentMethods) === null || _c === void 0 ? void 0 : _c.some(p => p.type === 'meal-voucher'))) {
367
+ const voucherIndex = this.cart.paymentMethods.findIndex(p => p.type === 'meal-voucher' && p.voucher);
368
+ const cardIndex = this.cart.paymentMethods.findIndex(p => p.type !== 'meal-voucher');
369
+ // Voucher Can pay only product
370
+ const voucherType = this.cart.paymentMethods[voucherIndex];
371
+ if (voucherType.voucher) {
372
+ this.cart.paymentMethods[voucherIndex].amount = Math.min(this.voucherBalance[voucherType.voucher.type], this.getTotalItemPrice());
373
+ }
374
+ // Card pay the rest : Delivery / Delicity Fees...
375
+ this.cart.paymentMethods[cardIndex].amount = this.getTotalPrice() - this.cart.paymentMethods[voucherIndex].amount;
376
+ }
377
+ else {
378
+ // If multiple payment methods, split amount
379
+ // Not dev yet...
380
+ }
381
+ return this;
382
+ }
383
+ addToCart(item, modifierGroups, quantity, comment, replace) {
384
+ // Check if item have mandatory modifiers
385
+ if (!mandatoryModifierCheck(item, modifierGroups, this.menu)) {
386
+ throw new Error('mandatory_field_missing');
387
+ }
388
+ // Check if item is available
389
+ if (!isItemAvailable(item, this.menu)) {
390
+ throw new Error('item_not_available');
391
+ }
392
+ const index = isItemAlreadyInCart(item, modifierGroups, this.cart, comment);
393
+ if (index !== -1 && !replace) {
394
+ this.cart.items[index].quantity += quantity;
395
+ this.cart.items[index].totalPrice = (getProductPrice(item, this.cart.type) + getTotalModifiersUnitPrice(modifierGroups)) * this.cart.items[index].quantity;
396
+ }
397
+ else {
398
+ const cartItem = {
399
+ uuid: uuidv4(),
400
+ item,
401
+ modifierGroups,
402
+ quantity,
403
+ comment: comment || null,
404
+ unitPrice: getProductPrice(item, this.cart.type) + getTotalModifiersUnitPrice(modifierGroups),
405
+ totalPrice: (getProductPrice(item, this.cart.type) + getTotalModifiersUnitPrice(modifierGroups)) * quantity,
406
+ type: 'PRODUCT'
407
+ };
408
+ if (replace) {
409
+ const index = this.cart.items.findIndex(i => i.uuid === replace.uuid);
410
+ this.cart.items.splice(index, 1, cartItem);
411
+ }
412
+ else {
413
+ this.cart.items.push(cartItem);
414
+ }
415
+ }
416
+ this.cart.totalItems = getProductCount(this.cart);
417
+ this.cart.totalItemPrice = getProductPriceSum(this.cart);
418
+ if (this.cart.paymentMethods && this.cart.paymentMethods.length > 0) {
419
+ this.updatePaymentMethodAmount();
420
+ }
421
+ this.validateCartIfError();
422
+ this.cartIntegrity(true);
423
+ this.trigger('update-cart');
424
+ return this;
425
+ }
426
+ addBundleToCart(bundle, bundleItems, quantity, comment, replace) {
427
+ // Check if bundle is available
428
+ if (!isBundleAvailable(bundle, this.menu)) {
429
+ throw new Error('bundle_not_available');
430
+ }
431
+ // Validate bundle requirements (required parts)
432
+ validateBundleRequirements(bundle, bundleItems, this.menu);
433
+ // Validate bundle part quantity constraints
434
+ validateBundlePartQuantities(bundleItems);
435
+ // Check if identical bundle already exists in cart
436
+ const index = isBundleAlreadyInCart(bundle, bundleItems, this.cart, comment);
437
+ if (index !== -1 && !replace) {
438
+ // Merge with existing bundle
439
+ this.cart.items[index].quantity += quantity;
440
+ const unitPrice = getBundlePrice(bundle, bundleItems, this.cart.type);
441
+ this.cart.items[index].totalPrice = unitPrice * this.cart.items[index].quantity;
442
+ }
443
+ else {
444
+ // Create new bundle cart item
445
+ const unitPrice = getBundlePrice(bundle, bundleItems, this.cart.type);
446
+ const cartItem = {
447
+ uuid: uuidv4(),
448
+ item: bundle,
449
+ modifierGroups: [],
450
+ bundleItems: bundleItems,
451
+ quantity,
452
+ comment: comment || null,
453
+ unitPrice,
454
+ totalPrice: unitPrice * quantity,
455
+ type: 'BUNDLE'
456
+ };
457
+ if (replace) {
458
+ // Replace existing item
459
+ const replaceIndex = this.cart.items.findIndex(i => i.uuid === replace.uuid);
460
+ this.cart.items.splice(replaceIndex, 1, cartItem);
461
+ }
462
+ else {
463
+ // Add new bundle to cart
464
+ this.cart.items.push(cartItem);
465
+ }
466
+ }
467
+ // Update cart totals
468
+ this.cart.totalItems = getProductCount(this.cart);
469
+ this.cart.totalItemPrice = getProductPriceSum(this.cart);
470
+ // Update payment methods if needed
471
+ if (this.cart.paymentMethods && this.cart.paymentMethods.length > 0) {
472
+ this.updatePaymentMethodAmount();
473
+ }
474
+ // Validate and trigger update
475
+ this.validateCartIfError();
476
+ this.cartIntegrity(true);
477
+ this.trigger('update-cart');
478
+ return this;
479
+ }
480
+ removeFromCart(cartItem) {
481
+ this.cart.items = this.cart.items.filter((item) => item.uuid !== cartItem.uuid);
482
+ this.cart.totalItems = getProductCount(this.cart);
483
+ this.cart.totalItemPrice = getProductPriceSum(this.cart);
484
+ if (this.cart.paymentMethods) {
485
+ this.updatePaymentMethodAmount();
486
+ }
487
+ this.cartIntegrity(true);
488
+ this.trigger('update-cart');
489
+ return this;
490
+ }
491
+ updateCart(cartItem, modifierGroup, quantity, comment) {
492
+ return this.addToCart(cartItem.item, modifierGroup, quantity, comment, cartItem);
493
+ }
494
+ getCart() {
495
+ return this.cart;
496
+ }
497
+ getItems() {
498
+ return this.cart.items;
499
+ }
500
+ getTotalItems() {
501
+ return this.cart.totalItems;
502
+ }
503
+ getTotalItemPrice() {
504
+ var _a, _b;
505
+ return this.cart.totalItemPrice - Math.abs(((_b = (_a = this.cart.discounts) === null || _a === void 0 ? void 0 : _a.filter(a => a.affect === 'products')) === null || _b === void 0 ? void 0 : _b.reduce((a, d) => a + d.value, 0))
506
+ || 0);
507
+ }
508
+ getTotalItemPriceRaw() {
509
+ return this.cart.totalItemPrice;
510
+ }
511
+ getTotalPrice() {
512
+ var _a;
513
+ return ((_a = this.cart.prices) === null || _a === void 0 ? void 0 : _a.total) || this.getTotalItemPrice();
514
+ }
515
+ canBePayOnsite() {
516
+ return [OrderModesEnum.PICKUP, OrderModesEnum.ON_SITE_COUNTER].includes(this.cart.type) && this.merchant.features.onsitePayment;
517
+ }
518
+ canBePayWithCashOnDelivery() {
519
+ return [OrderModesEnum.DELIVERY].includes(this.cart.type) && this.merchant.features.deliveryCashPayment;
520
+ }
521
+ synchronize() {
522
+ return __awaiter(this, void 0, void 0, function* () {
523
+ if (this.cart.type === OrderModesEnum.DELIVERY && !this.cart.delivery) {
524
+ throw new Error('no_delivery_address');
525
+ }
526
+ // Dedupe concurrent calls: return the in-flight promise to prevent server-side races
527
+ // when the user double-clicks "Commander" or another flow re-triggers a sync mid-flight.
528
+ if (this.syncPromise)
529
+ return this.syncPromise;
530
+ this.syncPromise = (() => __awaiter(this, void 0, void 0, function* () {
531
+ var _a;
532
+ this.isPriceLoading = true;
533
+ try {
534
+ const request = yield synchronizeCart(this.config, this.cart);
535
+ if (this.cart.delivery && request.delivery) {
536
+ this.cart.delivery = Object.assign({ address: (_a = this.cart.delivery) === null || _a === void 0 ? void 0 : _a.address }, request.delivery);
537
+ }
538
+ this.cart.preparationDurationBetween = request.preparationDurationBetween;
539
+ this.cart.discounts = request.discounts;
540
+ this.cart.prices = request.prices;
541
+ this.cart.addons = request.addons;
542
+ this.cart.paymentMethods = request.paymentMethods;
543
+ this.remoteErrors = request.errors;
544
+ if (this.cart.paymentMethods && this.cart.paymentMethods.length > 0) {
545
+ this.updatePaymentMethodAmount();
546
+ }
547
+ return this;
548
+ }
549
+ finally {
550
+ this.isPriceLoading = false;
551
+ this.syncPromise = null;
552
+ }
553
+ }))();
554
+ return this.syncPromise;
555
+ });
556
+ }
557
+ setCartDiscounts(discounts) {
558
+ this.cart.discounts = discounts;
559
+ this.cartIntegrity();
560
+ if (this.cart.paymentMethods && this.cart.paymentMethods.length > 0) {
561
+ this.updatePaymentMethodAmount();
562
+ }
563
+ this.trigger('update-cart');
564
+ return this;
565
+ }
566
+ setAddons(addons) {
567
+ this.cart.addons = addons;
568
+ if (this.cart.paymentMethods && this.cart.paymentMethods.length > 0) {
569
+ this.updatePaymentMethodAmount();
570
+ }
571
+ this.trigger('update-cart');
572
+ return this;
573
+ }
574
+ validateMerchantSchedule() {
575
+ this.errors = this.errors.filter(e => e.field !== 'schedule');
576
+ if (this.merchantSchedule) {
577
+ const when = this.getWhen() !== null ? DateTime.fromISO(this.getWhen()) : DateTime.now();
578
+ if (!this.merchantSchedule.isOpenAt(when))
579
+ this.errors.push({ field: 'schedule', code: 'merchant_currently_close', vars: {} });
580
+ }
581
+ return this;
582
+ }
583
+ validateCart() {
584
+ var _a;
585
+ this.errors = [];
586
+ if (!this.isOrderFree()) {
587
+ if (this.getPaymentMethods().reduce((a, b) => a + b.amount, 0) !== this.getTotalPrice() || !this.cart.paymentMethods || ((_a = this.cart.paymentMethods) === null || _a === void 0 ? void 0 : _a.length) === 0)
588
+ this.errors.push({ field: 'payment_method', code: 'missing_payment_methods', vars: {} });
589
+ if (this.cart.type === OrderModesEnum.DELIVERY && this.merchant.settings.minDeliveryOrderPrice > this.cart.totalItemPrice)
590
+ this.errors.push({ field: 'prices', code: 'minium_delivery_product_price', vars: { min: formatPrice(this.merchant.settings.minDeliveryOrderPrice) } });
591
+ }
592
+ if (this.cart.type === OrderModesEnum.DELIVERY && !this.cart.delivery)
593
+ this.errors.push({ field: 'address', code: 'no_address_defined', vars: {} });
594
+ if (this.cart.type === OrderModesEnum.DELIVERY && this.cart.delivery && !this.cart.delivery.available)
595
+ this.errors.push({ field: 'address', code: 'address_not_available', vars: {} });
596
+ if (!this.acceptCondition)
597
+ this.errors.push({ field: 'conditions', code: 'condition_not_accepted', vars: {} });
598
+ this.validateMerchantSchedule();
599
+ return this;
600
+ }
601
+ validateCartIfError() {
602
+ if (this.errors.length > 0)
603
+ this.validateCart();
604
+ return this;
605
+ }
606
+ resetErrors() {
607
+ this.errors = [];
608
+ return this;
609
+ }
610
+ getErrors() {
611
+ return this.errors;
612
+ }
613
+ pushError(field, code, vars) {
614
+ this.errors.push({ field, code, vars });
615
+ return this;
616
+ }
617
+ setErrors(errors) {
618
+ this.errors = errors;
619
+ return this;
620
+ }
621
+ setVoucherBalance(type, amount) {
622
+ this.voucherBalance[type] = amount;
623
+ }
624
+ addDiscountCoupon(code) {
625
+ return __awaiter(this, void 0, void 0, function* () {
626
+ yield addDiscountCode(this.config, this.cart, code);
627
+ this.trigger('update-cart');
628
+ return this;
629
+ });
630
+ }
631
+ getDiscounts() {
632
+ var _a;
633
+ return (_a = this.cart.discounts) !== null && _a !== void 0 ? _a : [];
634
+ }
635
+ getCoupons() {
636
+ var _a, _b;
637
+ return (_b = (_a = this.cart.discounts) === null || _a === void 0 ? void 0 : _a.filter(d => d.type === 'coupon')) !== null && _b !== void 0 ? _b : [];
638
+ }
639
+ removeDiscount(discount) {
640
+ return __awaiter(this, void 0, void 0, function* () {
641
+ yield removeDiscountCode(this.config, this.cart, discount);
642
+ this.trigger('update-cart');
643
+ return this;
644
+ });
645
+ }
646
+ cartIntegrity(event = false) {
647
+ var _a;
648
+ // Check if cart products are still available in menu
649
+ if (this.cart.items.length > 0) {
650
+ const merchantSchedule = new ScheduleManagement(this.merchant.schedule.slots, this.merchant.schedule.closes, this.merchant.schedule.options);
651
+ const toRemove = [];
652
+ for (let i = 0; i < this.cart.items.length; i++) {
653
+ const cartItem = this.cart.items[i];
654
+ // Skip validation for bundles - they have their own integrity logic
655
+ if (cartItem.type === 'BUNDLE') {
656
+ if (cartItem.item.unavailable)
657
+ toRemove.push({ item: cartItem, reason: 'unavailable' });
658
+ continue;
659
+ }
660
+ const menuItem = this.menu.items.find(m => m.id === cartItem.item.id);
661
+ const category = this.menu.categories.find(c => { var _a; return (_a = c.itemIds) === null || _a === void 0 ? void 0 : _a.includes(cartItem.item.uid); });
662
+ if (!menuItem) {
663
+ toRemove.push({ item: cartItem, reason: 'missing' });
664
+ continue;
665
+ }
666
+ if (menuItem.archived) {
667
+ toRemove.push({ item: cartItem, reason: 'archived' });
668
+ continue;
669
+ }
670
+ if (menuItem.unavailable) {
671
+ toRemove.push({ item: cartItem, reason: 'unavailable' });
672
+ continue;
673
+ }
674
+ if (category === null || category === void 0 ? void 0 : category.availability) {
675
+ const atDate = this.cart.when ? DateTime.fromISO(this.cart.when) : DateTime.now();
676
+ // ItemAvailability/CategoryAvailability share semantics but use different slot interfaces — bridge via cast.
677
+ const categoryAvailability = categoryAvailable(category.availability, atDate, merchantSchedule);
678
+ if (!categoryAvailability.isAvailable) {
679
+ toRemove.push({ item: cartItem, reason: 'not-available-at-date' });
680
+ continue;
681
+ }
682
+ }
683
+ cartItem.item = menuItem;
684
+ }
685
+ if (toRemove.length > 0) {
686
+ const removedUuids = new Set(toRemove.map(r => r.item.uuid));
687
+ this.cart.items = this.cart.items.filter(item => !removedUuids.has(item.uuid));
688
+ if (event) {
689
+ for (const r of toRemove) {
690
+ this.trigger('product-removed', { item: r.item, reason: r.reason });
691
+ }
692
+ }
693
+ }
694
+ // Update cart
695
+ this.cart.totalItems = getProductCount(this.cart);
696
+ this.cart.totalItemPrice = getProductPriceSum(this.cart);
697
+ if (this.cart.paymentMethods && this.cart.paymentMethods.length > 0) {
698
+ this.updatePaymentMethodAmount();
699
+ }
700
+ if (this.cart.addons && ((_a = this.cart.addons) === null || _a === void 0 ? void 0 : _a.length) > 0) {
701
+ }
702
+ }
703
+ }
704
+ }
705
+ //# sourceMappingURL=Cart.js.map