@munchi_oy/cart-engine 0.1.1 → 0.1.4

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.cjs CHANGED
@@ -36,7 +36,9 @@ __export(index_exports, {
36
36
  allocateProportionalMinorUnits: () => allocateProportionalMinorUnits,
37
37
  calculateCartPriceSnapshot: () => calculateCartPriceSnapshot,
38
38
  calculateDiscountAmount: () => calculateDiscountAmount,
39
+ calculateEqualSplitPartPricing: () => calculateEqualSplitPartPricing,
39
40
  calculateItemPrice: () => calculateItemPrice,
41
+ calculateItemSplitSeatPricing: () => calculateItemSplitSeatPricing,
40
42
  calculateOrderTaxSummary: () => calculateOrderTaxSummary,
41
43
  calculateSequentialDiscountTotal: () => calculateSequentialDiscountTotal,
42
44
  splitTaxInclusiveAmount: () => splitTaxInclusiveAmount
@@ -111,7 +113,49 @@ function calculateSequentialDiscountTotal(discounts, initialAmount) {
111
113
  };
112
114
  }
113
115
 
116
+ // src/discount/modulo.ts
117
+ var allocateProportionalMinorUnits = withSafeIntegers(
118
+ (weights, totalAmount) => {
119
+ for (let i = 0; i < weights.length; i++) {
120
+ if (!Number.isInteger(weights[i])) {
121
+ throw new Error(
122
+ `[CartEngine SafeLock] Weight at index ${i} MUST be an integer. Received: ${weights[i]}`
123
+ );
124
+ }
125
+ }
126
+ if (weights.length === 0 || totalAmount <= 0) {
127
+ return new Array(weights.length).fill(0);
128
+ }
129
+ const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
130
+ if (totalWeight <= 0) {
131
+ return new Array(weights.length).fill(0);
132
+ }
133
+ const baseAllocations = weights.map(
134
+ (weight) => Math.floor(weight * totalAmount / totalWeight)
135
+ );
136
+ const remainderOrder = weights.map((weight, index) => ({
137
+ index,
138
+ remainder: weight * totalAmount % totalWeight
139
+ })).sort(
140
+ (left, right) => right.remainder - left.remainder || left.index - right.index
141
+ );
142
+ let remainingAmount = totalAmount - baseAllocations.reduce((sum, amount) => sum + amount, 0);
143
+ for (const { index } of remainderOrder) {
144
+ if (remainingAmount <= 0) break;
145
+ baseAllocations[index] = (baseAllocations[index] ?? 0) + 1;
146
+ remainingAmount -= 1;
147
+ }
148
+ return baseAllocations;
149
+ }
150
+ );
151
+
114
152
  // src/pricing/calculator.ts
153
+ var EMPTY_SPLIT_PRICING_RESULT = {
154
+ targets: [],
155
+ totalGrossAmount: 0,
156
+ totalDiscountAmount: 0,
157
+ totalNetAmount: 0
158
+ };
115
159
  var assertMinorUnit = (value, fieldName) => {
116
160
  if (!Number.isInteger(value)) {
117
161
  throw new Error(
@@ -119,6 +163,93 @@ var assertMinorUnit = (value, fieldName) => {
119
163
  );
120
164
  }
121
165
  };
166
+ var sortDiscountsByCreatedAt = (discounts) => {
167
+ return [...discounts].sort(
168
+ (left, right) => new Date(left.createdAt).getTime() - new Date(right.createdAt).getTime()
169
+ );
170
+ };
171
+ var calculateTargetPricing = (id, grossAmount, discounts) => {
172
+ assertMinorUnit(grossAmount, `target gross amount (${id})`);
173
+ const { totalDiscountAmount, remainingAmount } = calculateSequentialDiscountTotal(discounts, grossAmount);
174
+ return {
175
+ id,
176
+ grossAmount,
177
+ discountAmount: totalDiscountAmount,
178
+ netAmount: remainingAmount
179
+ };
180
+ };
181
+ var sumAmounts = (targets, field) => {
182
+ return targets.reduce((sum, target) => sum + target[field], 0);
183
+ };
184
+ var buildSplitPricingResult = (targets) => {
185
+ return {
186
+ targets,
187
+ totalGrossAmount: sumAmounts(targets, "grossAmount"),
188
+ totalDiscountAmount: sumAmounts(targets, "discountAmount"),
189
+ totalNetAmount: sumAmounts(targets, "netAmount")
190
+ };
191
+ };
192
+ var filterTargetedDiscounts = (discounts, targetType, targetId) => {
193
+ return sortDiscountsByCreatedAt(
194
+ discounts.filter(
195
+ (discount) => discount.targetType === targetType && discount.targetId === targetId
196
+ )
197
+ );
198
+ };
199
+ var getSeatGrossAmount = (seat) => {
200
+ return seat.assignments.reduce((sum, assignment) => {
201
+ assertMinorUnit(assignment.amount, `seat assignment amount (${seat.id})`);
202
+ return sum + assignment.amount;
203
+ }, 0);
204
+ };
205
+ var getEqualSplitPartIds = (equalSplitState) => {
206
+ const partCount = Math.max(equalSplitState.partCount, equalSplitState.parts.length);
207
+ const ids = equalSplitState.parts.map((part) => part.id);
208
+ for (let index = ids.length; index < partCount; index += 1) {
209
+ ids.push(`__generated_equal_split_part_${index + 1}`);
210
+ }
211
+ return ids;
212
+ };
213
+ function calculateItemSplitSeatPricing(details) {
214
+ if (!details.tableService) {
215
+ return EMPTY_SPLIT_PRICING_RESULT;
216
+ }
217
+ const targets = details.tableService.itemSplitSeats.map(
218
+ (seat) => calculateTargetPricing(
219
+ seat.id,
220
+ getSeatGrossAmount(seat),
221
+ filterTargetedDiscounts(
222
+ details.discounts,
223
+ import_core2.PosDiscountTargetType.ItemSplitSeat,
224
+ seat.id
225
+ )
226
+ )
227
+ );
228
+ return buildSplitPricingResult(targets);
229
+ }
230
+ function calculateEqualSplitPartPricing(details) {
231
+ assertMinorUnit(details.totalAmount, "details.totalAmount");
232
+ if (!details.equalSplitState || details.equalSplitState.partCount <= 0) {
233
+ return EMPTY_SPLIT_PRICING_RESULT;
234
+ }
235
+ const partIds = getEqualSplitPartIds(details.equalSplitState);
236
+ const grossAmounts = allocateProportionalMinorUnits(
237
+ new Array(partIds.length).fill(1),
238
+ details.totalAmount
239
+ );
240
+ const targets = partIds.map(
241
+ (partId, index) => calculateTargetPricing(
242
+ partId,
243
+ grossAmounts[index] ?? 0,
244
+ filterTargetedDiscounts(
245
+ details.discounts,
246
+ import_core2.PosDiscountTargetType.EqualSplitPart,
247
+ partId
248
+ )
249
+ )
250
+ );
251
+ return buildSplitPricingResult(targets);
252
+ }
122
253
  function calculateItemPrice(details) {
123
254
  assertMinorUnit(details.basePriceInMinorUnit, "details.basePriceInMinorUnit");
124
255
  assertMinorUnit(details.quantity, "details.quantity");
@@ -181,7 +312,9 @@ function calculateCartPriceSnapshot(details) {
181
312
  (sum, item) => sum + (item.itemPrice.priceBreakdown.totalDiscounts.amount ?? 0),
182
313
  0
183
314
  );
184
- const cartDiscounts = details.discounts.filter((discount) => discount.scope === import_core2.DiscountScope.Cart).sort(
315
+ const cartDiscounts = details.discounts.filter(
316
+ (discount) => discount.scope === import_core2.DiscountScope.Cart && (discount.targetType === null || discount.targetType === void 0 || discount.targetType === import_core2.PosDiscountTargetType.Order)
317
+ ).sort(
185
318
  (left, right) => new Date(left.createdAt).getTime() - new Date(right.createdAt).getTime()
186
319
  );
187
320
  const grossAfterItemDiscounts = Math.max(
@@ -192,7 +325,21 @@ function calculateCartPriceSnapshot(details) {
192
325
  cartDiscounts,
193
326
  grossAfterItemDiscounts
194
327
  );
195
- const totalDiscountsAmount = subtotalItemDiscountsAmount + cartDiscountCalculation.totalDiscountAmount;
328
+ const itemSplitSeatPricing = details.checkoutModel === import_core2.CheckoutModelEnum.ItemSplit ? calculateItemSplitSeatPricing({
329
+ tableService: details.tableService,
330
+ discounts: details.discounts
331
+ }) : null;
332
+ const equalSplitPartPricing = details.checkoutModel === import_core2.CheckoutModelEnum.EqualSplit ? calculateEqualSplitPartPricing({
333
+ totalAmount: cartDiscountCalculation.remainingAmount,
334
+ equalSplitState: details.equalSplitState,
335
+ discounts: details.discounts
336
+ }) : null;
337
+ const subtotalTargetedDiscountsAmount = itemSplitSeatPricing?.totalDiscountAmount ?? equalSplitPartPricing?.totalDiscountAmount ?? 0;
338
+ const totalAmount = Math.max(
339
+ 0,
340
+ cartDiscountCalculation.remainingAmount - subtotalTargetedDiscountsAmount
341
+ );
342
+ const totalDiscountsAmount = subtotalItemDiscountsAmount + cartDiscountCalculation.totalDiscountAmount + subtotalTargetedDiscountsAmount;
196
343
  const price = {
197
344
  priceBreakdown: {
198
345
  totalBeforeDiscounts: {
@@ -213,7 +360,7 @@ function calculateCartPriceSnapshot(details) {
213
360
  }
214
361
  },
215
362
  total: {
216
- amount: cartDiscountCalculation.remainingAmount,
363
+ amount: totalAmount,
217
364
  currency: details.currency
218
365
  }
219
366
  };
@@ -222,52 +369,17 @@ function calculateCartPriceSnapshot(details) {
222
369
  totalBeforeDiscountsAmount,
223
370
  subtotalItemDiscountsAmount,
224
371
  subtotalBasketDiscountsAmount: cartDiscountCalculation.totalDiscountAmount,
372
+ subtotalTargetedDiscountsAmount,
225
373
  totalDiscountsAmount,
226
- totalAmount: cartDiscountCalculation.remainingAmount,
374
+ totalAmount,
375
+ itemSplitSeatPricing,
376
+ equalSplitPartPricing,
227
377
  price
228
378
  };
229
379
  }
230
380
 
231
381
  // src/tax/index.ts
232
382
  var import_core3 = require("@munchi_oy/core");
233
-
234
- // src/discount/modulo.ts
235
- var allocateProportionalMinorUnits = withSafeIntegers(
236
- (weights, totalAmount) => {
237
- for (let i = 0; i < weights.length; i++) {
238
- if (!Number.isInteger(weights[i])) {
239
- throw new Error(
240
- `[CartEngine SafeLock] Weight at index ${i} MUST be an integer. Received: ${weights[i]}`
241
- );
242
- }
243
- }
244
- if (weights.length === 0 || totalAmount <= 0) {
245
- return new Array(weights.length).fill(0);
246
- }
247
- const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
248
- if (totalWeight <= 0) {
249
- return new Array(weights.length).fill(0);
250
- }
251
- const baseAllocations = weights.map(
252
- (weight) => Math.floor(weight * totalAmount / totalWeight)
253
- );
254
- const remainderOrder = weights.map((weight, index) => ({
255
- index,
256
- remainder: weight * totalAmount % totalWeight
257
- })).sort(
258
- (left, right) => right.remainder - left.remainder || left.index - right.index
259
- );
260
- let remainingAmount = totalAmount - baseAllocations.reduce((sum, amount) => sum + amount, 0);
261
- for (const { index } of remainderOrder) {
262
- if (remainingAmount <= 0) break;
263
- baseAllocations[index] = (baseAllocations[index] ?? 0) + 1;
264
- remainingAmount -= 1;
265
- }
266
- return baseAllocations;
267
- }
268
- );
269
-
270
- // src/tax/index.ts
271
383
  function assertMinorUnit2(value, fieldName) {
272
384
  if (!Number.isInteger(value)) {
273
385
  throw new Error(
@@ -375,6 +487,76 @@ function calculateOrderTaxSummary(details) {
375
487
 
376
488
  // src/cart/Cart.ts
377
489
  var { isEqual } = import_lodash.default;
490
+ function cloneEqualSplitPart(part) {
491
+ return {
492
+ ...part,
493
+ name: part.name ?? null,
494
+ loyaltyId: part.loyaltyId ?? null,
495
+ loyaltyUserName: part.loyaltyUserName ?? null,
496
+ appliedDiscountIds: [...part.appliedDiscountIds ?? []]
497
+ };
498
+ }
499
+ function cloneEqualSplitState(equalSplitState) {
500
+ if (!equalSplitState) {
501
+ return null;
502
+ }
503
+ return {
504
+ partCount: equalSplitState.partCount,
505
+ parts: (equalSplitState.parts ?? []).map(cloneEqualSplitPart)
506
+ };
507
+ }
508
+ function createEmptyEqualSplitState() {
509
+ return {
510
+ partCount: 0,
511
+ parts: []
512
+ };
513
+ }
514
+ function cloneItemSplitSeatAssignment(assignment) {
515
+ return {
516
+ ...assignment,
517
+ appliedDiscountIds: [...assignment.appliedDiscountIds ?? []]
518
+ };
519
+ }
520
+ function cloneItemSplitSeat(seat) {
521
+ return {
522
+ ...seat,
523
+ name: seat.name ?? null,
524
+ loyaltyId: seat.loyaltyId ?? null,
525
+ loyaltyUserName: seat.loyaltyUserName ?? null,
526
+ appliedDiscountIds: [...seat.appliedDiscountIds ?? []],
527
+ assignments: (seat.assignments ?? []).map(cloneItemSplitSeatAssignment)
528
+ };
529
+ }
530
+ function cloneTableService(tableService) {
531
+ if (!tableService) {
532
+ return null;
533
+ }
534
+ return {
535
+ name: tableService.name ?? null,
536
+ seats: tableService.seats,
537
+ itemSplitSeats: (tableService.itemSplitSeats ?? []).map(cloneItemSplitSeat)
538
+ };
539
+ }
540
+ function createEmptyTableService() {
541
+ return {
542
+ name: null,
543
+ seats: 0,
544
+ itemSplitSeats: []
545
+ };
546
+ }
547
+ function validateEqualSplitState(equalSplitState) {
548
+ if (!equalSplitState) {
549
+ return;
550
+ }
551
+ if (!Number.isInteger(equalSplitState.partCount) || equalSplitState.partCount < 0) {
552
+ throw new Error("Equal split part count must be a non-negative integer");
553
+ }
554
+ if (equalSplitState.parts.length > equalSplitState.partCount) {
555
+ throw new Error(
556
+ "Equal split state cannot contain more parts than partCount"
557
+ );
558
+ }
559
+ }
378
560
  var Cart = class _Cart {
379
561
  id;
380
562
  businessId;
@@ -399,6 +581,7 @@ var Cart = class _Cart {
399
581
  _customer = null;
400
582
  _refunds = [];
401
583
  _tableService;
584
+ _equalSplitState;
402
585
  _taxSummary;
403
586
  _newlyCancelledItemIds;
404
587
  _invoiceCompany;
@@ -424,7 +607,8 @@ var Cart = class _Cart {
424
607
  this.status = import_core4.OrderStatusEnum.Draft;
425
608
  this.orderFormat = import_core4.OrderFormat.Instant;
426
609
  this._locatorType = import_core4.LocatorType.Table;
427
- this._tableService = options.tableService ?? null;
610
+ this._tableService = cloneTableService(options.tableService);
611
+ this._equalSplitState = cloneEqualSplitState(options.equalSplitState);
428
612
  this._newlyCancelledItemIds = /* @__PURE__ */ new Set();
429
613
  this._staffId = options.staffId ?? null;
430
614
  this._shiftId = options.shiftId ?? null;
@@ -438,6 +622,7 @@ var Cart = class _Cart {
438
622
  orderNumber: options.orderNumber,
439
623
  checkoutModel: options.checkoutModel ?? import_core4.CheckoutModelEnum.Full,
440
624
  tableService: options.tableService ?? null,
625
+ equalSplitState: options.equalSplitState ?? null,
441
626
  currency: options.currency,
442
627
  business: options.business,
443
628
  staffId: options.staffId,
@@ -452,6 +637,7 @@ var Cart = class _Cart {
452
637
  orderNumber: orderDto.orderNumber,
453
638
  checkoutModel: orderDto.checkoutModel,
454
639
  tableService: orderDto.tableService ?? null,
640
+ equalSplitState: orderDto.equalSplitState ?? null,
455
641
  currency: orderDto.currency,
456
642
  business: orderDto.business,
457
643
  staffId: orderDto.staffId ?? null,
@@ -473,7 +659,8 @@ var Cart = class _Cart {
473
659
  cart._refunds = orderDto.refunds ? [...orderDto.refunds] : [];
474
660
  cart._loyaltyTransactionIds = [...orderDto.loyaltyTransactionIds];
475
661
  cart._loyaltyProgramId = orderDto.loyaltyProgramId ?? null;
476
- cart._tableService = orderDto.tableService ? { ...orderDto.tableService } : null;
662
+ cart._tableService = cloneTableService(orderDto.tableService);
663
+ cart._equalSplitState = cloneEqualSplitState(orderDto.equalSplitState);
477
664
  cart._newlyCancelledItemIds = /* @__PURE__ */ new Set();
478
665
  cart._taxSummary = orderDto.taxSummary;
479
666
  cart._invoiceCompany = orderDto.invoiceCompany ?? null;
@@ -513,13 +700,22 @@ var Cart = class _Cart {
513
700
  return this._tableService?.seats ?? null;
514
701
  }
515
702
  get tableService() {
516
- return this._tableService;
703
+ return this.getTableService();
704
+ }
705
+ get equalSplitState() {
706
+ return this.getEqualSplitState();
707
+ }
708
+ get equalSplitPartCount() {
709
+ return this._equalSplitState?.partCount ?? null;
517
710
  }
518
711
  get price() {
519
712
  return calculateCartPriceSnapshot({
520
713
  items: this._items,
521
714
  discounts: this._discounts,
522
- currency: this.currency
715
+ currency: this.currency,
716
+ checkoutModel: this.checkoutModel,
717
+ tableService: this._tableService,
718
+ equalSplitState: this._equalSplitState
523
719
  }).price;
524
720
  }
525
721
  setInvoiceCompany(company) {
@@ -622,21 +818,25 @@ var Cart = class _Cart {
622
818
  this._newlyCancelledItemIds.clear();
623
819
  }
624
820
  applyDiscountToCart(discountData) {
625
- this._discounts.push({
626
- ...discountData,
627
- scope: import_core4.DiscountScope.Cart,
628
- lineItemId: null,
629
- createdAt: (0, import_dayjs.default)().toDate().toISOString()
630
- });
821
+ this._discounts.push(
822
+ this.createDiscountRecord(
823
+ discountData,
824
+ import_core4.DiscountScope.Cart,
825
+ null,
826
+ import_core4.PosDiscountTargetType.Order,
827
+ null
828
+ )
829
+ );
631
830
  this.touch();
632
831
  }
633
832
  applyDiscountToItem(lineItemId, discountData) {
634
- const newDiscount = {
635
- ...discountData,
636
- scope: import_core4.DiscountScope.Item,
833
+ const newDiscount = this.createDiscountRecord(
834
+ discountData,
835
+ import_core4.DiscountScope.Item,
637
836
  lineItemId,
638
- createdAt: (0, import_dayjs.default)().toDate().toISOString()
639
- };
837
+ import_core4.PosDiscountTargetType.LineItem,
838
+ lineItemId
839
+ );
640
840
  const existingDiscountIndex = this._discounts.findIndex(
641
841
  (discount) => discount.lineItemId === lineItemId && discount.scope === import_core4.DiscountScope.Item
642
842
  );
@@ -667,6 +867,42 @@ var Cart = class _Cart {
667
867
  });
668
868
  this.touch();
669
869
  }
870
+ applyDiscountToItemSplitSeat(seatId, discountData) {
871
+ this._discounts.push(
872
+ this.createDiscountRecord(
873
+ discountData,
874
+ import_core4.DiscountScope.Cart,
875
+ null,
876
+ import_core4.PosDiscountTargetType.ItemSplitSeat,
877
+ seatId
878
+ )
879
+ );
880
+ this.touch();
881
+ }
882
+ applyDiscountToItemSplitAssignment(assignmentId, discountData) {
883
+ this._discounts.push(
884
+ this.createDiscountRecord(
885
+ discountData,
886
+ import_core4.DiscountScope.Cart,
887
+ null,
888
+ import_core4.PosDiscountTargetType.ItemSplitAssignment,
889
+ assignmentId
890
+ )
891
+ );
892
+ this.touch();
893
+ }
894
+ applyDiscountToEqualSplitPart(partId, discountData) {
895
+ this._discounts.push(
896
+ this.createDiscountRecord(
897
+ discountData,
898
+ import_core4.DiscountScope.Cart,
899
+ null,
900
+ import_core4.PosDiscountTargetType.EqualSplitPart,
901
+ partId
902
+ )
903
+ );
904
+ this.touch();
905
+ }
670
906
  removeItemDiscount(lineItemId) {
671
907
  this._items = this._items.map((item) => {
672
908
  if (item.lineItemId !== lineItemId) {
@@ -779,15 +1015,277 @@ var Cart = class _Cart {
779
1015
  this.setCheckoutModel(checkoutModel);
780
1016
  }
781
1017
  setGuestCount(guestCount) {
782
- if (this._tableService?.seats ?? null === guestCount) {
1018
+ if (guestCount === null) {
1019
+ if (this._tableService === null) {
1020
+ return;
1021
+ }
1022
+ this._tableService = null;
1023
+ this.touch();
783
1024
  return;
784
1025
  }
785
- this._tableService = guestCount === null ? null : { seats: guestCount };
786
- this.touch();
1026
+ this.setSeatsCount(guestCount);
787
1027
  }
788
1028
  setSeatNumber(guestCount) {
789
1029
  this.setGuestCount(guestCount);
790
1030
  }
1031
+ getTableService() {
1032
+ return cloneTableService(this._tableService);
1033
+ }
1034
+ setTableService(tableService) {
1035
+ const nextTableService = cloneTableService(tableService);
1036
+ if (isEqual(this._tableService, nextTableService)) {
1037
+ return this;
1038
+ }
1039
+ this._tableService = nextTableService;
1040
+ this.touch();
1041
+ return this;
1042
+ }
1043
+ clearTableService() {
1044
+ return this.setTableService(null);
1045
+ }
1046
+ getTableName() {
1047
+ return this._tableService?.name ?? null;
1048
+ }
1049
+ setTableName(name) {
1050
+ const tableService = this.getTableService() ?? createEmptyTableService();
1051
+ if (tableService.name === name) {
1052
+ return this;
1053
+ }
1054
+ tableService.name = name;
1055
+ return this.setTableService(tableService);
1056
+ }
1057
+ getSeatsCount() {
1058
+ return this._tableService?.seats ?? null;
1059
+ }
1060
+ setSeatsCount(seats) {
1061
+ if (!Number.isInteger(seats) || seats < 0) {
1062
+ throw new Error("Seats count must be a non-negative integer");
1063
+ }
1064
+ const tableService = this.getTableService() ?? createEmptyTableService();
1065
+ if (tableService.seats === seats) {
1066
+ return this;
1067
+ }
1068
+ tableService.seats = seats;
1069
+ return this.setTableService(tableService);
1070
+ }
1071
+ getItemSplitSeats() {
1072
+ return this._tableService ? this._tableService.itemSplitSeats.map(cloneItemSplitSeat) : [];
1073
+ }
1074
+ setItemSplitSeats(seats) {
1075
+ const tableService = this.getTableService() ?? createEmptyTableService();
1076
+ tableService.itemSplitSeats = seats.map(cloneItemSplitSeat);
1077
+ return this.setTableService(tableService);
1078
+ }
1079
+ clearItemSplitSeats() {
1080
+ return this.setItemSplitSeats([]);
1081
+ }
1082
+ addItemSplitSeat(seat) {
1083
+ if (this.getItemSplitSeatById(seat.id)) {
1084
+ return this;
1085
+ }
1086
+ const seats = this.getItemSplitSeats();
1087
+ seats.push(cloneItemSplitSeat(seat));
1088
+ return this.setItemSplitSeats(seats);
1089
+ }
1090
+ updateItemSplitSeat(seatId, updater) {
1091
+ const seats = this.getItemSplitSeats();
1092
+ const seatIndex = seats.findIndex((seat) => seat.id === seatId);
1093
+ if (seatIndex === -1 || !seats[seatIndex]) {
1094
+ return this;
1095
+ }
1096
+ seats[seatIndex] = cloneItemSplitSeat(updater(cloneItemSplitSeat(seats[seatIndex])));
1097
+ return this.setItemSplitSeats(seats);
1098
+ }
1099
+ removeItemSplitSeat(seatId) {
1100
+ const seats = this.getItemSplitSeats();
1101
+ const nextSeats = seats.filter((seat) => seat.id !== seatId);
1102
+ if (nextSeats.length === seats.length) {
1103
+ return this;
1104
+ }
1105
+ return this.setItemSplitSeats(nextSeats);
1106
+ }
1107
+ getItemSplitSeatById(seatId) {
1108
+ const seat = this._tableService?.itemSplitSeats.find(
1109
+ (itemSplitSeat) => itemSplitSeat.id === seatId
1110
+ );
1111
+ return seat ? cloneItemSplitSeat(seat) : null;
1112
+ }
1113
+ setItemSplitSeatAssignments(seatId, assignments) {
1114
+ return this.updateItemSplitSeat(seatId, (seat) => ({
1115
+ ...seat,
1116
+ assignments: assignments.map(cloneItemSplitSeatAssignment)
1117
+ }));
1118
+ }
1119
+ addItemSplitSeatAssignment(seatId, assignment) {
1120
+ return this.updateItemSplitSeat(seatId, (seat) => ({
1121
+ ...seat,
1122
+ assignments: [...seat.assignments, cloneItemSplitSeatAssignment(assignment)]
1123
+ }));
1124
+ }
1125
+ removeItemSplitSeatAssignmentsByLineItem(seatId, lineItemId) {
1126
+ return this.updateItemSplitSeat(seatId, (seat) => ({
1127
+ ...seat,
1128
+ assignments: seat.assignments.filter(
1129
+ (assignment) => assignment.lineItemId !== lineItemId
1130
+ )
1131
+ }));
1132
+ }
1133
+ clearItemSplitSeatAssignments(seatId) {
1134
+ return this.setItemSplitSeatAssignments(seatId, []);
1135
+ }
1136
+ setItemSplitSeatLoyalty(seatId, loyaltyId, loyaltyUserName) {
1137
+ return this.updateItemSplitSeat(seatId, (seat) => ({
1138
+ ...seat,
1139
+ loyaltyId,
1140
+ loyaltyUserName
1141
+ }));
1142
+ }
1143
+ clearItemSplitSeatLoyalty(seatId) {
1144
+ return this.setItemSplitSeatLoyalty(seatId, null, null);
1145
+ }
1146
+ setItemSplitSeatAppliedDiscountIds(seatId, appliedDiscountIds) {
1147
+ return this.updateItemSplitSeat(seatId, (seat) => ({
1148
+ ...seat,
1149
+ appliedDiscountIds: [...appliedDiscountIds]
1150
+ }));
1151
+ }
1152
+ addItemSplitSeatAppliedDiscountId(seatId, discountId) {
1153
+ return this.updateItemSplitSeat(seatId, (seat) => {
1154
+ if (seat.appliedDiscountIds.includes(discountId)) {
1155
+ return seat;
1156
+ }
1157
+ return {
1158
+ ...seat,
1159
+ appliedDiscountIds: [...seat.appliedDiscountIds, discountId]
1160
+ };
1161
+ });
1162
+ }
1163
+ removeItemSplitSeatAppliedDiscountId(seatId, discountId) {
1164
+ return this.updateItemSplitSeat(seatId, (seat) => ({
1165
+ ...seat,
1166
+ appliedDiscountIds: seat.appliedDiscountIds.filter(
1167
+ (appliedDiscountId) => appliedDiscountId !== discountId
1168
+ )
1169
+ }));
1170
+ }
1171
+ clearItemSplitSeatAppliedDiscountIds(seatId) {
1172
+ return this.setItemSplitSeatAppliedDiscountIds(seatId, []);
1173
+ }
1174
+ getEqualSplitState() {
1175
+ return cloneEqualSplitState(this._equalSplitState);
1176
+ }
1177
+ setEqualSplitState(equalSplitState) {
1178
+ const nextEqualSplitState = cloneEqualSplitState(equalSplitState);
1179
+ validateEqualSplitState(nextEqualSplitState);
1180
+ if (isEqual(this._equalSplitState, nextEqualSplitState)) {
1181
+ return this;
1182
+ }
1183
+ this._equalSplitState = nextEqualSplitState;
1184
+ this.touch();
1185
+ return this;
1186
+ }
1187
+ clearEqualSplitState() {
1188
+ return this.setEqualSplitState(null);
1189
+ }
1190
+ getEqualSplitParts() {
1191
+ return this._equalSplitState ? this._equalSplitState.parts.map(cloneEqualSplitPart) : [];
1192
+ }
1193
+ setEqualSplitParts(parts) {
1194
+ if (parts.length === 0) {
1195
+ return this.clearEqualSplitState();
1196
+ }
1197
+ const equalSplitState = this.getEqualSplitState() ?? createEmptyEqualSplitState();
1198
+ equalSplitState.parts = parts.map(cloneEqualSplitPart);
1199
+ equalSplitState.partCount = parts.length;
1200
+ return this.setEqualSplitState(equalSplitState);
1201
+ }
1202
+ clearEqualSplitParts() {
1203
+ return this.clearEqualSplitState();
1204
+ }
1205
+ addEqualSplitPart(part) {
1206
+ if (this.getEqualSplitPartById(part.id)) {
1207
+ return this;
1208
+ }
1209
+ const parts = this.getEqualSplitParts();
1210
+ parts.push(cloneEqualSplitPart(part));
1211
+ return this.setEqualSplitParts(parts);
1212
+ }
1213
+ updateEqualSplitPart(partId, updater) {
1214
+ const parts = this.getEqualSplitParts();
1215
+ const partIndex = parts.findIndex((part) => part.id === partId);
1216
+ if (partIndex === -1 || !parts[partIndex]) {
1217
+ return this;
1218
+ }
1219
+ parts[partIndex] = cloneEqualSplitPart(updater(cloneEqualSplitPart(parts[partIndex])));
1220
+ return this.setEqualSplitParts(parts);
1221
+ }
1222
+ removeEqualSplitPart(partId) {
1223
+ const parts = this.getEqualSplitParts();
1224
+ const nextParts = parts.filter((part) => part.id !== partId);
1225
+ if (nextParts.length === parts.length) {
1226
+ return this;
1227
+ }
1228
+ return nextParts.length === 0 ? this.clearEqualSplitState() : this.setEqualSplitParts(nextParts);
1229
+ }
1230
+ getEqualSplitPartById(partId) {
1231
+ const part = this._equalSplitState?.parts.find(
1232
+ (equalSplitPart) => equalSplitPart.id === partId
1233
+ );
1234
+ return part ? cloneEqualSplitPart(part) : null;
1235
+ }
1236
+ getEqualSplitPartCount() {
1237
+ return this._equalSplitState?.partCount ?? null;
1238
+ }
1239
+ setEqualSplitPartCount(partCount) {
1240
+ if (partCount === null) {
1241
+ return this.clearEqualSplitState();
1242
+ }
1243
+ if (!Number.isInteger(partCount) || partCount <= 0) {
1244
+ throw new Error("Equal split part count must be a positive integer or null");
1245
+ }
1246
+ const equalSplitState = this.getEqualSplitState() ?? createEmptyEqualSplitState();
1247
+ if (equalSplitState.parts.length > partCount) {
1248
+ throw new Error(
1249
+ "Equal split part count cannot be less than persisted parts length"
1250
+ );
1251
+ }
1252
+ if (equalSplitState.partCount === partCount) {
1253
+ return this;
1254
+ }
1255
+ equalSplitState.partCount = partCount;
1256
+ return this.setEqualSplitState(equalSplitState);
1257
+ }
1258
+ clearEqualSplitPartCount() {
1259
+ return this.clearEqualSplitState();
1260
+ }
1261
+ setEqualSplitPartAppliedDiscountIds(partId, appliedDiscountIds) {
1262
+ return this.updateEqualSplitPart(partId, (part) => ({
1263
+ ...part,
1264
+ appliedDiscountIds: [...appliedDiscountIds]
1265
+ }));
1266
+ }
1267
+ addEqualSplitPartAppliedDiscountId(partId, discountId) {
1268
+ return this.updateEqualSplitPart(partId, (part) => {
1269
+ if (part.appliedDiscountIds.includes(discountId)) {
1270
+ return part;
1271
+ }
1272
+ return {
1273
+ ...part,
1274
+ appliedDiscountIds: [...part.appliedDiscountIds, discountId]
1275
+ };
1276
+ });
1277
+ }
1278
+ removeEqualSplitPartAppliedDiscountId(partId, discountId) {
1279
+ return this.updateEqualSplitPart(partId, (part) => ({
1280
+ ...part,
1281
+ appliedDiscountIds: part.appliedDiscountIds.filter(
1282
+ (appliedDiscountId) => appliedDiscountId !== discountId
1283
+ )
1284
+ }));
1285
+ }
1286
+ clearEqualSplitPartAppliedDiscountIds(partId) {
1287
+ return this.setEqualSplitPartAppliedDiscountIds(partId, []);
1288
+ }
791
1289
  updateOrderStatus(newStatus) {
792
1290
  if (this.status === newStatus) {
793
1291
  return;
@@ -846,7 +1344,8 @@ var Cart = class _Cart {
846
1344
  locatorType: this._locatorType,
847
1345
  spotNumber: this.spotNumber,
848
1346
  comments: this.comments,
849
- tableService: this._tableService,
1347
+ tableService: cloneTableService(this._tableService),
1348
+ equalSplitState: cloneEqualSplitState(this._equalSplitState),
850
1349
  orderRefundStatus: this.orderRefundStatus,
851
1350
  items: this._items,
852
1351
  status: this.status,
@@ -875,6 +1374,7 @@ var Cart = class _Cart {
875
1374
  orderNumber: this.orderNumber,
876
1375
  checkoutModel: this.checkoutModel,
877
1376
  tableService: this._tableService,
1377
+ equalSplitState: this._equalSplitState,
878
1378
  currency: this.currency,
879
1379
  business: this._business,
880
1380
  staffId: this._staffId,
@@ -893,7 +1393,8 @@ var Cart = class _Cart {
893
1393
  newCart._refunds = [...this._refunds];
894
1394
  newCart._loyaltyProgramId = this._loyaltyProgramId;
895
1395
  newCart._loyaltyTransactionIds = [...this._loyaltyTransactionIds];
896
- newCart._tableService = this._tableService ? { ...this._tableService } : null;
1396
+ newCart._tableService = cloneTableService(this._tableService);
1397
+ newCart._equalSplitState = cloneEqualSplitState(this._equalSplitState);
897
1398
  newCart._taxSummary = this._taxSummary;
898
1399
  newCart._newlyCancelledItemIds = new Set(this._newlyCancelledItemIds);
899
1400
  newCart._invoiceCompany = this._invoiceCompany ? { ...this._invoiceCompany } : null;
@@ -906,6 +1407,16 @@ var Cart = class _Cart {
906
1407
  currency: this.currency
907
1408
  });
908
1409
  }
1410
+ createDiscountRecord(discountData, scope, lineItemId, targetType, targetId) {
1411
+ return {
1412
+ ...discountData,
1413
+ scope,
1414
+ lineItemId,
1415
+ targetType,
1416
+ targetId,
1417
+ createdAt: (0, import_dayjs.default)().toDate().toISOString()
1418
+ };
1419
+ }
909
1420
  touch() {
910
1421
  this.updatedAt = (0, import_dayjs.default)().toDate();
911
1422
  }
@@ -919,7 +1430,7 @@ var ApplyTo = /* @__PURE__ */ ((ApplyTo2) => {
919
1430
  })(ApplyTo || {});
920
1431
 
921
1432
  // src/version.ts
922
- var VERSION = "0.1.1";
1433
+ var VERSION = "0.1.4";
923
1434
  // Annotate the CommonJS export names for ESM import in node:
924
1435
  0 && (module.exports = {
925
1436
  ApplyTo,
@@ -928,7 +1439,9 @@ var VERSION = "0.1.1";
928
1439
  allocateProportionalMinorUnits,
929
1440
  calculateCartPriceSnapshot,
930
1441
  calculateDiscountAmount,
1442
+ calculateEqualSplitPartPricing,
931
1443
  calculateItemPrice,
1444
+ calculateItemSplitSeatPricing,
932
1445
  calculateOrderTaxSummary,
933
1446
  calculateSequentialDiscountTotal,
934
1447
  splitTaxInclusiveAmount