@pisell/pisellos 0.10.7 → 0.10.8

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 (57) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  2. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +12 -10
  3. package/dist/modules/Order/index.d.ts +12 -5
  4. package/dist/modules/Order/index.js +310 -210
  5. package/dist/modules/Order/types.d.ts +26 -3
  6. package/dist/modules/Order/types.js +11 -0
  7. package/dist/modules/Order/utils/discountProductLineIdentity.d.ts +45 -0
  8. package/dist/modules/Order/utils/discountProductLineIdentity.js +227 -0
  9. package/dist/modules/Rules/index.js +125 -52
  10. package/dist/modules/SalesSummary/utils.js +7 -1
  11. package/dist/server/index.d.ts +6 -0
  12. package/dist/server/index.js +810 -729
  13. package/dist/server/modules/order/index.d.ts +1 -0
  14. package/dist/server/modules/order/index.js +9 -2
  15. package/dist/server/modules/order/types.d.ts +1 -1
  16. package/dist/solution/BaseSales/index.d.ts +15 -5
  17. package/dist/solution/BaseSales/index.js +211 -159
  18. package/dist/solution/BaseSales/utils/cartPromotion.js +26 -3
  19. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +1 -1
  20. package/dist/solution/BaseSales/utils.js +31 -20
  21. package/dist/solution/BookingByStep/index.d.ts +1 -1
  22. package/dist/solution/BookingTicket/index.js +1 -4
  23. package/dist/solution/BookingTicket/types.d.ts +3 -0
  24. package/dist/solution/BookingTicket/utils/cartView.js +44 -32
  25. package/dist/solution/Sales/index.d.ts +1 -0
  26. package/dist/solution/Sales/index.js +16 -2
  27. package/dist/solution/ScanOrder/utils.js +31 -20
  28. package/dist/solution/UnifiedBookingSales/index.d.ts +5 -4
  29. package/dist/solution/UnifiedBookingSales/index.js +51 -39
  30. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +3 -1
  31. package/lib/modules/Order/index.d.ts +12 -5
  32. package/lib/modules/Order/index.js +148 -43
  33. package/lib/modules/Order/types.d.ts +26 -3
  34. package/lib/modules/Order/utils/discountProductLineIdentity.d.ts +45 -0
  35. package/lib/modules/Order/utils/discountProductLineIdentity.js +170 -0
  36. package/lib/modules/Rules/index.js +100 -20
  37. package/lib/modules/SalesSummary/utils.js +5 -1
  38. package/lib/server/index.d.ts +6 -0
  39. package/lib/server/index.js +51 -0
  40. package/lib/server/modules/order/index.d.ts +1 -0
  41. package/lib/server/modules/order/index.js +9 -3
  42. package/lib/server/modules/order/types.d.ts +1 -1
  43. package/lib/solution/BaseSales/index.d.ts +15 -5
  44. package/lib/solution/BaseSales/index.js +64 -16
  45. package/lib/solution/BaseSales/utils/cartPromotion.js +26 -2
  46. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +1 -1
  47. package/lib/solution/BaseSales/utils.js +29 -18
  48. package/lib/solution/BookingByStep/index.d.ts +1 -1
  49. package/lib/solution/BookingTicket/index.js +1 -4
  50. package/lib/solution/BookingTicket/types.d.ts +3 -0
  51. package/lib/solution/BookingTicket/utils/cartView.js +43 -30
  52. package/lib/solution/Sales/index.d.ts +1 -0
  53. package/lib/solution/Sales/index.js +15 -3
  54. package/lib/solution/ScanOrder/utils.js +29 -18
  55. package/lib/solution/UnifiedBookingSales/index.d.ts +5 -4
  56. package/lib/solution/UnifiedBookingSales/index.js +35 -26
  57. package/package.json +1 -1
@@ -73,8 +73,11 @@ function isRejectedSalesSubmitResult(result) {
73
73
  return candidates.some((candidate) => {
74
74
  if (!candidate || typeof candidate !== "object")
75
75
  return false;
76
- const responseCode = Number(candidate.code ?? candidate.statusCode);
77
- if (Number.isFinite(responseCode) && responseCode >= 400)
76
+ if (candidate.code !== void 0 && candidate.code !== 200 && candidate.code !== "200") {
77
+ return true;
78
+ }
79
+ const statusCode = Number(candidate.statusCode);
80
+ if (Number.isFinite(statusCode) && statusCode >= 400)
78
81
  return true;
79
82
  if (candidate.status === false || candidate.success === false || candidate.result === false) {
80
83
  return true;
@@ -84,9 +87,9 @@ function isRejectedSalesSubmitResult(result) {
84
87
  );
85
88
  });
86
89
  }
87
- function getSalesSubmitErrorMessage(result) {
90
+ function getSalesSubmitErrorMessage(result, fallbackMessage) {
88
91
  var _a, _b;
89
- return (result == null ? void 0 : result.message) || (result == null ? void 0 : result.msg) || ((_a = result == null ? void 0 : result.data) == null ? void 0 : _a.message) || ((_b = result == null ? void 0 : result.data) == null ? void 0 : _b.msg) || "Wallet 支付确认失败";
92
+ return (result == null ? void 0 : result.message) || (result == null ? void 0 : result.msg) || ((_a = result == null ? void 0 : result.data) == null ? void 0 : _a.message) || ((_b = result == null ? void 0 : result.data) == null ? void 0 : _b.msg) || fallbackMessage;
90
93
  }
91
94
  function toWalletFundValue(value) {
92
95
  try {
@@ -703,6 +706,36 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
703
706
  return null;
704
707
  return productId;
705
708
  }
709
+ /**
710
+ * Change Time / customer reprice 必须沿用商品列表的渠道口径。
711
+ * SalesSdk Provider 通常将渠道存为 platform,因此不能只读取 otherParams.channel。
712
+ */
713
+ getPriceQueryChannel(channel) {
714
+ var _a, _b, _c;
715
+ const strategyContext = ((_a = this.otherParams) == null ? void 0 : _a.strategy_context) && typeof this.otherParams.strategy_context === "object" ? this.otherParams.strategy_context : {};
716
+ const value = channel ?? strategyContext.channel ?? ((_b = this.otherParams) == null ? void 0 : _b.channel) ?? ((_c = this.otherParams) == null ? void 0 : _c.platform);
717
+ if (value === void 0 || value === null || String(value).trim() === "") {
718
+ return void 0;
719
+ }
720
+ return String(value).trim();
721
+ }
722
+ /**
723
+ * /product/query 的智能定价评估只读取 strategy_context;
724
+ * application_code 仅作为接口兼容字段,不能替代 channel / business_code。
725
+ */
726
+ getPriceQueryStrategyContext(channel) {
727
+ var _a, _b, _c;
728
+ const inherited = ((_a = this.otherParams) == null ? void 0 : _a.strategy_context) && typeof this.otherParams.strategy_context === "object" ? this.otherParams.strategy_context : {};
729
+ const strategyContext = { ...inherited };
730
+ const resolvedChannel = this.getPriceQueryChannel(channel);
731
+ const businessCode = strategyContext.business_code ?? ((_b = this.otherParams) == null ? void 0 : _b.businessCode) ?? ((_c = this.otherParams) == null ? void 0 : _c.business_code);
732
+ if (resolvedChannel)
733
+ strategyContext.channel = resolvedChannel;
734
+ if (businessCode !== void 0 && businessCode !== null && String(businessCode).trim() !== "") {
735
+ strategyContext.business_code = String(businessCode).trim();
736
+ }
737
+ return strategyContext;
738
+ }
706
739
  getPriceQuerySchedule(params) {
707
740
  const booking = params.booking || {};
708
741
  if (typeof booking.start_date === "string" && booking.start_date.trim()) {
@@ -730,13 +763,15 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
730
763
  };
731
764
  }
732
765
  async loadProductsForPriceQuery(params) {
733
- var _a, _b;
766
+ var _a;
734
767
  const ids = Array.from(new Set(
735
768
  params.ids.filter((id) => Number.isFinite(id))
736
769
  ));
737
770
  const productsById = /* @__PURE__ */ new Map();
738
771
  if (!ids.length || !this.request)
739
772
  return productsById;
773
+ const channel = this.getPriceQueryChannel(params.channel);
774
+ const strategyContext = this.getPriceQueryStrategyContext(channel);
740
775
  try {
741
776
  const response = await this.request.post("/product/query", {
742
777
  ids,
@@ -748,13 +783,14 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
748
783
  customer_id: params.customerId,
749
784
  schedule_date: params.schedule.schedule_date,
750
785
  schedule_datetime: params.schedule.schedule_datetime,
751
- application_code: params.channel || ((_a = this.otherParams) == null ? void 0 : _a.channel)
786
+ application_code: channel,
787
+ ...Object.keys(strategyContext).length ? { strategy_context: strategyContext } : {}
752
788
  }, {
753
789
  osServer: true,
754
790
  customToast: () => {
755
791
  }
756
792
  });
757
- const list = ((_b = response == null ? void 0 : response.data) == null ? void 0 : _b.list) || (response == null ? void 0 : response.list) || [];
793
+ const list = ((_a = response == null ? void 0 : response.data) == null ? void 0 : _a.list) || (response == null ? void 0 : response.list) || [];
758
794
  if (!Array.isArray(list))
759
795
  return productsById;
760
796
  list.forEach((item) => {
@@ -1203,10 +1239,10 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1203
1239
  const result = ((_a = this.store.order) == null ? void 0 : _a.getTempOrder()) || null;
1204
1240
  return result;
1205
1241
  }
1206
- async replaceTempOrder(tempOrder) {
1242
+ async replaceTempOrder(tempOrder, options) {
1207
1243
  if (!this.store.order)
1208
1244
  throw new Error("order 模块未初始化");
1209
- const nextTempOrder = await this.store.order.replaceTempOrder(tempOrder);
1245
+ const nextTempOrder = await this.store.order.replaceTempOrder(tempOrder, options);
1210
1246
  await this.effectsEmit("onTempOrderReplaced", { tempOrder: nextTempOrder });
1211
1247
  return nextTempOrder;
1212
1248
  }
@@ -2766,7 +2802,9 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
2766
2802
  confirmPendingVoucherPayments: true
2767
2803
  });
2768
2804
  if (isRejectedSalesSubmitResult(submitResult)) {
2769
- throw new Error(getSalesSubmitErrorMessage(submitResult));
2805
+ throw new Error(
2806
+ getSalesSubmitErrorMessage(submitResult, "Wallet 支付确认失败")
2807
+ );
2770
2808
  }
2771
2809
  } catch (error) {
2772
2810
  this.store.order.setOrderPayments(beforePayments);
@@ -2869,6 +2907,11 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
2869
2907
  smallTicketDataFlag: 1,
2870
2908
  confirmPendingVoucherPayments: true
2871
2909
  });
2910
+ if (isRejectedSalesSubmitResult(syncResult.submitResult)) {
2911
+ throw new Error(
2912
+ getSalesSubmitErrorMessage(syncResult.submitResult, "订单提交失败")
2913
+ );
2914
+ }
2872
2915
  if (this.store.payment) {
2873
2916
  const committed = this.getCommittedWalletAssets();
2874
2917
  const walletState = this.store.payment.wallet.setCommittedWalletAssets(
@@ -2979,6 +3022,11 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
2979
3022
  smallTicketDataFlag: 1,
2980
3023
  confirmPendingVoucherPayments: true
2981
3024
  });
3025
+ if (isRejectedSalesSubmitResult(syncResult.submitResult)) {
3026
+ throw new Error(
3027
+ getSalesSubmitErrorMessage(syncResult.submitResult, "订单提交失败")
3028
+ );
3029
+ }
2982
3030
  if (this.store.payment) {
2983
3031
  const committed = this.getCommittedWalletAssets();
2984
3032
  const walletState = this.store.payment.wallet.setCommittedWalletAssets(
@@ -3200,11 +3248,11 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
3200
3248
  });
3201
3249
  });
3202
3250
  }
3203
- async addProductToOrder(product, booking) {
3251
+ async addProductToOrder(product, booking, options) {
3204
3252
  try {
3205
3253
  if (!this.store.order)
3206
3254
  throw new Error("order 模块未初始化");
3207
- const products = await this.store.order.addProductToOrder(product, booking);
3255
+ const products = await this.store.order.addProductToOrder(product, booking, options);
3208
3256
  return products;
3209
3257
  } catch (error) {
3210
3258
  throw error;
@@ -3280,17 +3328,17 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
3280
3328
  * @example
3281
3329
  * await baseSales.removeProductsFromOrder([identityA, identityB]);
3282
3330
  */
3283
- async removeProductsFromOrder(identities) {
3331
+ async removeProductsFromOrder(identities, options) {
3284
3332
  try {
3285
3333
  if (!this.store.order)
3286
3334
  throw new Error("order 模块未初始化");
3287
- return await this.store.order.removeProductsFromOrder(identities);
3335
+ return await this.store.order.removeProductsFromOrder(identities, options);
3288
3336
  } catch (error) {
3289
3337
  throw error;
3290
3338
  }
3291
3339
  }
3292
- async removeProductFromOrder(identity) {
3293
- return this.removeProductsFromOrder([identity]);
3340
+ async removeProductFromOrder(identity, options) {
3341
+ return this.removeProductsFromOrder([identity], options);
3294
3342
  }
3295
3343
  // ─── 促销/赠品 透传(迁移自 booking usePromotion) ────────────────
3296
3344
  /**
@@ -366,9 +366,33 @@ function getProductLineNoteSignature(product) {
366
366
  const uid = getOrderProductUid(product);
367
367
  return uid ? `note:${uid}` : `note:${normalizedNote}`;
368
368
  }
369
+ function hasGoodPassDiscount(product) {
370
+ var _a, _b;
371
+ const productBundle = Array.isArray(product == null ? void 0 : product.product_bundle) ? product.product_bundle : [];
372
+ const extendedBundle = Array.isArray((_b = (_a = product == null ? void 0 : product._extend) == null ? void 0 : _a.other) == null ? void 0 : _b.bundle) ? product._extend.other.bundle : [];
373
+ const discountLists = [
374
+ product == null ? void 0 : product.discount_list,
375
+ ...productBundle.map((item) => item == null ? void 0 : item.discount_list),
376
+ ...extendedBundle.map((item) => item == null ? void 0 : item.discount_list)
377
+ ];
378
+ return discountLists.some(
379
+ (discountList) => Array.isArray(discountList) && discountList.some(
380
+ (item) => (item == null ? void 0 : item.type) === "good_pass" || (item == null ? void 0 : item.tag) === "good_pass"
381
+ )
382
+ );
383
+ }
369
384
  function getPromotionLineMergeBoundary(product) {
370
- var _a;
371
- if (((_a = product == null ? void 0 : product.metadata) == null ? void 0 : _a.is_edit_for_runtime) !== true)
385
+ var _a, _b;
386
+ if ((product == null ? void 0 : product.product_id) === void 0 || (product == null ? void 0 : product.product_id) === null) {
387
+ return `custom:${getOrderProductUid(product)}`;
388
+ }
389
+ if (hasGoodPassDiscount(product)) {
390
+ return `good-pass:${getOrderProductUid(product)}`;
391
+ }
392
+ if (Number(((_a = product == null ? void 0 : product.product_sku) == null ? void 0 : _a.open_sold_weight) || 0) === 1) {
393
+ return `weighing:${getOrderProductUid(product)}`;
394
+ }
395
+ if (((_b = product == null ? void 0 : product.metadata) == null ? void 0 : _b.is_edit_for_runtime) !== true)
372
396
  return "";
373
397
  return `edit:${getOrderProductUid(product)}`;
374
398
  }
@@ -217,7 +217,7 @@ function transformBaseProductToOrderProduct(params) {
217
217
  }
218
218
  const productTitle = {
219
219
  ...(sourceProduct == null ? void 0 : sourceProduct.title_i18n) || {},
220
- original: (sourceProduct == null ? void 0 : sourceProduct.title) || ""
220
+ original: (sourceProduct == null ? void 0 : sourceProduct.original_title) || (sourceProduct == null ? void 0 : sourceProduct.title) || ""
221
221
  };
222
222
  return {
223
223
  product_id: productId,
@@ -60,33 +60,44 @@ function createEmptySummary() {
60
60
  };
61
61
  }
62
62
  function buildProductLineFingerprint(productOptionItem, productBundle) {
63
- const optArr = Array.isArray(productOptionItem) ? productOptionItem : [];
64
- const normalizedOpts = optArr.map((item) => ({
65
- option_group_item_id: Number(item == null ? void 0 : item.option_group_item_id) || 0,
66
- option_group_id: Number(item == null ? void 0 : item.option_group_id) || 0,
67
- num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
68
- price: String((item == null ? void 0 : item.add_price) ?? (item == null ? void 0 : item.price) ?? "")
69
- })).sort((p, q) => {
70
- if (p.option_group_item_id !== q.option_group_item_id) {
71
- return p.option_group_item_id - q.option_group_item_id;
72
- }
73
- if (p.option_group_id !== q.option_group_id)
74
- return p.option_group_id - q.option_group_id;
75
- if (p.num !== q.num)
76
- return p.num - q.num;
77
- return p.price.localeCompare(q.price);
78
- });
63
+ const normalizeOptions = (options) => {
64
+ const optionArr = Array.isArray(options) ? options : [];
65
+ return optionArr.map((item) => ({
66
+ option_group_item_id: Number(item == null ? void 0 : item.option_group_item_id) || 0,
67
+ option_group_id: Number(item == null ? void 0 : item.option_group_id) || 0,
68
+ num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
69
+ price: String((item == null ? void 0 : item.add_price) ?? (item == null ? void 0 : item.price) ?? "")
70
+ })).sort((p, q) => {
71
+ if (p.option_group_item_id !== q.option_group_item_id) {
72
+ return p.option_group_item_id - q.option_group_item_id;
73
+ }
74
+ if (p.option_group_id !== q.option_group_id) {
75
+ return p.option_group_id - q.option_group_id;
76
+ }
77
+ if (p.num !== q.num)
78
+ return p.num - q.num;
79
+ return p.price.localeCompare(q.price);
80
+ });
81
+ };
82
+ const normalizedOpts = normalizeOptions(productOptionItem);
79
83
  const bundleArr = Array.isArray(productBundle) ? productBundle : [];
80
84
  const normalizedBundles = bundleArr.map((item) => ({
81
85
  bundle_id: Number((item == null ? void 0 : item.bundle_id) ?? (item == null ? void 0 : item.id) ?? 0) || 0,
82
86
  product_id: Number(item == null ? void 0 : item.product_id) || 0,
83
- num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1
87
+ product_variant_id: Number((item == null ? void 0 : item.bundle_variant_id) ?? (item == null ? void 0 : item.product_variant_id)) || 0,
88
+ num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
89
+ option: normalizeOptions(item == null ? void 0 : item.option)
84
90
  })).sort((p, q) => {
85
91
  if (p.bundle_id !== q.bundle_id)
86
92
  return p.bundle_id - q.bundle_id;
87
93
  if (p.product_id !== q.product_id)
88
94
  return p.product_id - q.product_id;
89
- return p.num - q.num;
95
+ if (p.product_variant_id !== q.product_variant_id) {
96
+ return p.product_variant_id - q.product_variant_id;
97
+ }
98
+ if (p.num !== q.num)
99
+ return p.num - q.num;
100
+ return JSON.stringify(p.option).localeCompare(JSON.stringify(q.option));
90
101
  });
91
102
  return JSON.stringify([normalizedOpts, normalizedBundles]);
92
103
  }
@@ -326,7 +326,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
326
326
  date: string;
327
327
  status: string;
328
328
  week: string;
329
- weekNum: 0 | 5 | 2 | 1 | 6 | 4 | 3;
329
+ weekNum: 0 | 2 | 1 | 6 | 4 | 3 | 5;
330
330
  }[]>;
331
331
  submitTimeSlot(timeSlots: TimeSliceItem): void;
332
332
  private getScheduleDataByIds;
@@ -1599,10 +1599,7 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
1599
1599
  });
1600
1600
  }
1601
1601
  if (typeof this.store.order.addProductsToOrder === "function") {
1602
- return this.store.order.addProductsToOrder(orderLines, {
1603
- skipEditDiscountConfigRefresh: true,
1604
- skipDiscountRecalculation: true
1605
- });
1602
+ return this.store.order.addProductsToOrder(orderLines);
1606
1603
  }
1607
1604
  const result = [];
1608
1605
  for (const line of orderLines) {
@@ -118,12 +118,15 @@ export interface BookingTicketCartBookingView extends ISalesBooking {
118
118
  canAddTime: boolean;
119
119
  };
120
120
  }
121
+ export type BookingTicketCartLineView = BookingTicketCartBookingView | BookingTicketCartItemView;
121
122
  /**
122
123
  * BookingTicket 面向 UI 的购物车视图。
123
124
  */
124
125
  export interface BookingTicketCartView {
125
126
  bookings: BookingTicketCartBookingView[];
126
127
  items: BookingTicketCartItemView[];
128
+ /** 以 tempOrder.products 为唯一顺序真源的统一购物车行(旧到新)。 */
129
+ lines: BookingTicketCartLineView[];
127
130
  }
128
131
  /**
129
132
  * BookingTicket 面向 UI 的客户展示视图。
@@ -86,29 +86,27 @@ function isGiftProductLine(line) {
86
86
  function buildCartView(lines, bookings, options = {}) {
87
87
  const productLines = Array.isArray(lines) ? lines : [];
88
88
  const bookingList = Array.isArray(bookings) ? bookings : [];
89
+ const childBookings = bookingList.filter((booking) => booking.parent_id !== 0);
90
+ const bookingsByProductUid = indexBookingsByProductUid(childBookings);
89
91
  const linesByUid = indexProductsByUid(productLines);
90
92
  const addTimeLinesByBookingUid = indexAddTimeProductsByBookingUid(productLines);
91
- const claimedUids = /* @__PURE__ */ new Set();
92
93
  const claimedAddTimeLines = /* @__PURE__ */ new Set();
93
- const cartBookings = bookingList.filter((booking) => booking.parent_id !== 0).reduce((acc, booking) => {
94
- const uid = booking == null ? void 0 : booking.product_uid;
94
+ childBookings.forEach((booking) => {
95
+ const bookingUid = getBookingUid(booking);
96
+ if (!bookingUid)
97
+ return;
98
+ (addTimeLinesByBookingUid[bookingUid] || []).forEach((line) => {
99
+ claimedAddTimeLines.add(line);
100
+ });
101
+ });
102
+ const claimedBookings = /* @__PURE__ */ new Set();
103
+ const cartBookings = [];
104
+ const items = [];
105
+ const orderedLines = [];
106
+ const buildBookingView = (booking, product) => {
95
107
  const bookingUid = getBookingUid(booking);
96
- let product = null;
97
- if (uid !== void 0 && uid !== null && uid !== "") {
98
- const key = String(uid);
99
- if (!claimedUids.has(key)) {
100
- const line = linesByUid[key];
101
- if (isGiftProductLine(line))
102
- return acc;
103
- if (line) {
104
- claimedUids.add(key);
105
- product = line;
106
- }
107
- }
108
- }
109
108
  const addTimeProduct = bookingUid ? addTimeLinesByBookingUid[bookingUid] || [] : [];
110
- addTimeProduct.forEach((line) => claimedAddTimeLines.add(line));
111
- acc.push({
109
+ return {
112
110
  ...booking,
113
111
  _extend: {
114
112
  product,
@@ -122,21 +120,36 @@ function buildCartView(lines, bookings, options = {}) {
122
120
  shopOpeningHours: options.shopOpeningHours
123
121
  })
124
122
  }
125
- });
126
- return acc;
127
- }, []);
128
- const items = productLines.filter((line) => {
123
+ };
124
+ };
125
+ productLines.forEach((line) => {
129
126
  var _a;
130
- if (isGiftProductLine(line))
131
- return false;
132
- if (claimedAddTimeLines.has(line))
133
- return false;
127
+ if (isGiftProductLine(line) || claimedAddTimeLines.has(line))
128
+ return;
134
129
  const uid = (_a = line.metadata) == null ? void 0 : _a.unique_identification_number;
135
- if (uid === void 0 || uid === null || uid === "")
136
- return true;
137
- return !claimedUids.has(String(uid));
130
+ const matchedBooking = uid === void 0 || uid === null || uid === "" ? void 0 : (bookingsByProductUid[String(uid)] || []).find(
131
+ (booking) => !claimedBookings.has(booking)
132
+ );
133
+ if (matchedBooking) {
134
+ claimedBookings.add(matchedBooking);
135
+ const bookingView = buildBookingView(matchedBooking, line);
136
+ cartBookings.push(bookingView);
137
+ orderedLines.push(bookingView);
138
+ return;
139
+ }
140
+ items.push(line);
141
+ orderedLines.push(line);
142
+ });
143
+ childBookings.forEach((booking) => {
144
+ if (claimedBookings.has(booking))
145
+ return;
146
+ const uid = booking == null ? void 0 : booking.product_uid;
147
+ const linkedProduct = uid === void 0 || uid === null || uid === "" ? null : linesByUid[String(uid)] || null;
148
+ if (isGiftProductLine(linkedProduct))
149
+ return;
150
+ cartBookings.push(buildBookingView(booking, null));
138
151
  });
139
- return { bookings: cartBookings, items };
152
+ return { bookings: cartBookings, items, lines: orderedLines };
140
153
  }
141
154
  function isWalkInCustomer(customer) {
142
155
  if (!customer)
@@ -95,6 +95,7 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
95
95
  * 2) 若无 active,返回已超时且仍占用中的预约(允许并发多条)
96
96
  * 3) 若仍无,返回最近一组未来预约(同 start_time 的并发预约全部返回)
97
97
  * 4) 最后兜底返回一条(避免资源有历史预约时完全无反馈)
98
+ * - includeAllFuture 时,保留选中预约并返回当天全部后续预约;仅详情接口启用。
98
99
  */
99
100
  private pickBookingsForCurrentPoint;
100
101
  /**
@@ -316,8 +316,9 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
316
316
  * 2) 若无 active,返回已超时且仍占用中的预约(允许并发多条)
317
317
  * 3) 若仍无,返回最近一组未来预约(同 start_time 的并发预约全部返回)
318
318
  * 4) 最后兜底返回一条(避免资源有历史预约时完全无反馈)
319
+ * - includeAllFuture 时,保留选中预约并返回当天全部后续预约;仅详情接口启用。
319
320
  */
320
- pickBookingsForCurrentPoint(current, bookings) {
321
+ pickBookingsForCurrentPoint(current, bookings, options = {}) {
321
322
  if (bookings.length === 0)
322
323
  return [];
323
324
  const currentDayStart = current.startOf("day");
@@ -334,6 +335,13 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
334
335
  const appendNextStartGroupIfNeeded = (selectedBookings) => {
335
336
  if (selectedBookings.length === 0)
336
337
  return selectedBookings;
338
+ if (options.includeAllFuture) {
339
+ const allFutureBookings = dayScopedBookings.filter((booking) => {
340
+ const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
341
+ return startAt.isValid() && startAt.isAfter(current);
342
+ });
343
+ return [...selectedBookings, ...allFutureBookings];
344
+ }
337
345
  const shouldAppendNextGroup = selectedBookings.some(
338
346
  (booking) => booking.status === "reserved" || booking.status === "occupied"
339
347
  );
@@ -384,6 +392,8 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
384
392
  return startAt.isValid() && startAt.isAfter(current) && startAt.isSame(current, "day");
385
393
  });
386
394
  if (upcoming.length > 0) {
395
+ if (options.includeAllFuture)
396
+ return upcoming;
387
397
  const firstStartAt = this.toBookingDateTime(upcoming[0].start_date, upcoming[0].start_time).valueOf();
388
398
  const upcomingStartGroup = upcoming.filter((booking) => {
389
399
  const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
@@ -638,7 +648,8 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
638
648
  const unpaidBookingList = matchedBookingList.filter((booking) => this.isUnpaidBooking(booking));
639
649
  const bookings = this.pickBookingsForCurrentPoint(
640
650
  current,
641
- this.normalizeResourceBookings(current, deviceCurrent, matchedBookingList)
651
+ this.normalizeResourceBookings(current, deviceCurrent, matchedBookingList),
652
+ { includeAllFuture: true }
642
653
  );
643
654
  const completedBookings = completedBookingList.map((booking) => {
644
655
  return {
@@ -654,7 +665,8 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
654
665
  });
655
666
  const unpaidBookings = this.pickBookingsForCurrentPoint(
656
667
  current,
657
- this.normalizeResourceBookings(current, deviceCurrent, unpaidBookingList)
668
+ this.normalizeResourceBookings(current, deviceCurrent, unpaidBookingList),
669
+ { includeAllFuture: true }
658
670
  );
659
671
  const orderCount = this.countBookingOrders(statsBookingList);
660
672
  const bookingCount = statsBookingList.length;
@@ -357,33 +357,44 @@ function attachItemRuleLimitsToTopLevelProducts(productList, limits) {
357
357
  return productList;
358
358
  }
359
359
  function buildProductLineFingerprint(productOptionItem, productBundle) {
360
- const optArr = Array.isArray(productOptionItem) ? productOptionItem : [];
361
- const normalizedOpts = optArr.map((item) => ({
362
- option_group_item_id: Number(item == null ? void 0 : item.option_group_item_id) || 0,
363
- option_group_id: Number(item == null ? void 0 : item.option_group_id) || 0,
364
- num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
365
- price: String((item == null ? void 0 : item.add_price) ?? (item == null ? void 0 : item.price) ?? "")
366
- })).sort((p, q) => {
367
- if (p.option_group_item_id !== q.option_group_item_id) {
368
- return p.option_group_item_id - q.option_group_item_id;
369
- }
370
- if (p.option_group_id !== q.option_group_id)
371
- return p.option_group_id - q.option_group_id;
372
- if (p.num !== q.num)
373
- return p.num - q.num;
374
- return p.price.localeCompare(q.price);
375
- });
360
+ const normalizeOptions = (options) => {
361
+ const optionArr = Array.isArray(options) ? options : [];
362
+ return optionArr.map((item) => ({
363
+ option_group_item_id: Number(item == null ? void 0 : item.option_group_item_id) || 0,
364
+ option_group_id: Number(item == null ? void 0 : item.option_group_id) || 0,
365
+ num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
366
+ price: String((item == null ? void 0 : item.add_price) ?? (item == null ? void 0 : item.price) ?? "")
367
+ })).sort((p, q) => {
368
+ if (p.option_group_item_id !== q.option_group_item_id) {
369
+ return p.option_group_item_id - q.option_group_item_id;
370
+ }
371
+ if (p.option_group_id !== q.option_group_id) {
372
+ return p.option_group_id - q.option_group_id;
373
+ }
374
+ if (p.num !== q.num)
375
+ return p.num - q.num;
376
+ return p.price.localeCompare(q.price);
377
+ });
378
+ };
379
+ const normalizedOpts = normalizeOptions(productOptionItem);
376
380
  const bundleArr = Array.isArray(productBundle) ? productBundle : [];
377
381
  const normalizedBundles = bundleArr.map((item) => ({
378
382
  bundle_id: Number((item == null ? void 0 : item.bundle_id) ?? (item == null ? void 0 : item.id) ?? 0) || 0,
379
383
  product_id: Number(item == null ? void 0 : item.product_id) || 0,
380
- num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1
384
+ product_variant_id: Number((item == null ? void 0 : item.bundle_variant_id) ?? (item == null ? void 0 : item.product_variant_id)) || 0,
385
+ num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
386
+ option: normalizeOptions(item == null ? void 0 : item.option)
381
387
  })).sort((p, q) => {
382
388
  if (p.bundle_id !== q.bundle_id)
383
389
  return p.bundle_id - q.bundle_id;
384
390
  if (p.product_id !== q.product_id)
385
391
  return p.product_id - q.product_id;
386
- return p.num - q.num;
392
+ if (p.product_variant_id !== q.product_variant_id) {
393
+ return p.product_variant_id - q.product_variant_id;
394
+ }
395
+ if (p.num !== q.num)
396
+ return p.num - q.num;
397
+ return JSON.stringify(p.option).localeCompare(JSON.stringify(q.option));
387
398
  });
388
399
  return JSON.stringify([normalizedOpts, normalizedBundles]);
389
400
  }
@@ -76,6 +76,11 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
76
76
  * value 为 undefined 时删除该 key;存储失败不阻断业务流程。
77
77
  */
78
78
  setSessionStateValue(key: string, value: unknown): void;
79
+ /**
80
+ * 清除当前 cacheId 的可恢复订单快照,同时保留 Solution UI sessionState。
81
+ * 用于支付跳转等终态场景,避免浏览器返回时恢复已提交订单。
82
+ */
83
+ clearSessionStorageOrderSnapshot(): void;
79
84
  /**
80
85
  * Kiosk 不能把已收款订单留在本地等待重试:默认直接等待云端 checkout。
81
86
  * 兼容仍需要 WebPOS 待同步模式的入口可显式传 checkoutSyncTaskEnabled: true。
@@ -122,10 +127,6 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
122
127
  * Read-only compatibility view for salesSdk skins. It reconnects a booking to its product but
123
128
  * must not be used to mutate tempOrder; all writes go through BaseSales / planner APIs.
124
129
  */
125
- getCart(): {
126
- items: any[];
127
- bookings: any[];
128
- };
129
130
  /**
130
131
  * Loads the current customer's product coupons and discount cards after planner lines exist,
131
132
  * then optionally lets the existing Rules engine select the best valid combination. This is a