@pisell/pisellos 2.2.175 → 2.2.176

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 (172) hide show
  1. package/dist/modules/BookingContext/index.d.ts +37 -0
  2. package/dist/modules/BookingContext/index.js +436 -0
  3. package/dist/modules/BookingContext/types.d.ts +102 -0
  4. package/dist/modules/BookingContext/types.js +51 -0
  5. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
  6. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +193 -0
  7. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
  8. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +137 -0
  9. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +75 -0
  10. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.js +215 -0
  11. package/dist/modules/BookingContext/utils/flexible.d.ts +28 -0
  12. package/dist/modules/BookingContext/utils/flexible.js +56 -0
  13. package/dist/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
  14. package/dist/modules/BookingContext/utils/formatResourceList.js +38 -0
  15. package/dist/modules/BookingContext/utils/index.d.ts +11 -0
  16. package/dist/modules/BookingContext/utils/index.js +11 -0
  17. package/dist/modules/BookingContext/utils/noResource.d.ts +13 -0
  18. package/dist/modules/BookingContext/utils/noResource.js +77 -0
  19. package/dist/modules/BookingContext/utils/productExtend.d.ts +72 -0
  20. package/dist/modules/BookingContext/utils/productExtend.js +339 -0
  21. package/dist/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
  22. package/dist/modules/BookingContext/utils/resourceErrors.js +74 -0
  23. package/dist/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
  24. package/dist/modules/BookingContext/utils/resourceSelection.js +24 -0
  25. package/dist/modules/BookingContext/utils/resources.d.ts +80 -0
  26. package/dist/modules/BookingContext/utils/resources.js +486 -0
  27. package/dist/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
  28. package/dist/modules/BookingContext/utils/serviceTimes.js +151 -0
  29. package/dist/modules/BookingContext/utils/timeSlices.d.ts +42 -0
  30. package/dist/modules/BookingContext/utils/timeSlices.js +100 -0
  31. package/dist/modules/Customer/index.d.ts +6 -0
  32. package/dist/modules/Customer/index.js +129 -83
  33. package/dist/modules/Customer/types.d.ts +2 -0
  34. package/dist/modules/Discount/index.d.ts +2 -4
  35. package/dist/modules/Discount/index.js +8 -93
  36. package/dist/modules/Discount/types.d.ts +1 -7
  37. package/dist/modules/Order/index.d.ts +84 -17
  38. package/dist/modules/Order/index.js +1239 -495
  39. package/dist/modules/Order/types.d.ts +198 -18
  40. package/dist/modules/Order/types.js +31 -0
  41. package/dist/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  42. package/dist/modules/Order/utils/manualProductDiscount.js +212 -0
  43. package/dist/modules/Order/utils.d.ts +50 -1
  44. package/dist/modules/Order/utils.js +261 -31
  45. package/dist/modules/Quotation/index.d.ts +0 -1
  46. package/dist/modules/Quotation/index.js +23 -21
  47. package/dist/modules/Rules/index.d.ts +4 -0
  48. package/dist/modules/Rules/index.js +26 -10
  49. package/dist/modules/SalesSummary/index.js +4 -2
  50. package/dist/modules/SalesSummary/utils.js +3 -3
  51. package/dist/modules/Schedule/index.js +6 -2
  52. package/dist/modules/index.d.ts +1 -0
  53. package/dist/modules/index.js +2 -1
  54. package/dist/server/index.js +87 -29
  55. package/dist/server/modules/order/utils/filterOrders.js +20 -6
  56. package/dist/server/modules/products/index.d.ts +1 -1
  57. package/dist/server/modules/products/index.js +130 -113
  58. package/dist/server/modules/schedule/index.js +13 -6
  59. package/dist/solution/BaseSales/index.d.ts +89 -7
  60. package/dist/solution/BaseSales/index.js +1484 -365
  61. package/dist/solution/BaseSales/types.d.ts +67 -1
  62. package/dist/solution/BaseSales/types.js +3 -1
  63. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +275 -0
  64. package/dist/solution/BaseSales/utils/cartPromotion.js +1258 -0
  65. package/dist/solution/BaseSales/utils/quotationPrice.d.ts +7 -0
  66. package/dist/solution/BaseSales/utils/quotationPrice.js +237 -0
  67. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +68 -16
  68. package/dist/solution/BaseSales/utils.js +46 -8
  69. package/dist/solution/BookingByStep/index.d.ts +1 -1
  70. package/dist/solution/BookingTicket/index.d.ts +133 -1
  71. package/dist/solution/BookingTicket/index.js +780 -175
  72. package/dist/solution/BookingTicket/types.d.ts +2 -0
  73. package/dist/solution/BookingTicket/types.js +3 -0
  74. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +113 -0
  75. package/dist/solution/BookingTicket/utils/addProductDecision.js +346 -0
  76. package/dist/solution/BookingTicket/utils/cartView.js +4 -2
  77. package/dist/solution/ScanOrder/index.d.ts +9 -3
  78. package/dist/solution/ScanOrder/index.js +231 -128
  79. package/dist/solution/ScanOrder/utils.js +46 -8
  80. package/dist/solution/ShopDiscount/index.d.ts +0 -1
  81. package/dist/solution/ShopDiscount/index.js +18 -23
  82. package/dist/solution/VenueBooking/index.d.ts +5 -9
  83. package/dist/solution/VenueBooking/index.js +103 -55
  84. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +4 -0
  85. package/dist/solution/VenueBooking/utils/slotMerge.js +10 -0
  86. package/lib/model/strategy/adapter/promotion/index.js +0 -49
  87. package/lib/modules/BookingContext/index.d.ts +37 -0
  88. package/lib/modules/BookingContext/index.js +297 -0
  89. package/lib/modules/BookingContext/types.d.ts +102 -0
  90. package/lib/modules/BookingContext/types.js +34 -0
  91. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
  92. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +207 -0
  93. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
  94. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +141 -0
  95. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +75 -0
  96. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +198 -0
  97. package/lib/modules/BookingContext/utils/flexible.d.ts +28 -0
  98. package/lib/modules/BookingContext/utils/flexible.js +80 -0
  99. package/lib/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
  100. package/lib/modules/BookingContext/utils/formatResourceList.js +50 -0
  101. package/lib/modules/BookingContext/utils/index.d.ts +11 -0
  102. package/lib/modules/BookingContext/utils/index.js +43 -0
  103. package/lib/modules/BookingContext/utils/noResource.d.ts +13 -0
  104. package/lib/modules/BookingContext/utils/noResource.js +90 -0
  105. package/lib/modules/BookingContext/utils/productExtend.d.ts +72 -0
  106. package/lib/modules/BookingContext/utils/productExtend.js +283 -0
  107. package/lib/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
  108. package/lib/modules/BookingContext/utils/resourceErrors.js +80 -0
  109. package/lib/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
  110. package/lib/modules/BookingContext/utils/resourceSelection.js +46 -0
  111. package/lib/modules/BookingContext/utils/resources.d.ts +80 -0
  112. package/lib/modules/BookingContext/utils/resources.js +377 -0
  113. package/lib/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
  114. package/lib/modules/BookingContext/utils/serviceTimes.js +162 -0
  115. package/lib/modules/BookingContext/utils/timeSlices.d.ts +42 -0
  116. package/lib/modules/BookingContext/utils/timeSlices.js +124 -0
  117. package/lib/modules/Customer/index.d.ts +6 -0
  118. package/lib/modules/Customer/index.js +26 -11
  119. package/lib/modules/Customer/types.d.ts +2 -0
  120. package/lib/modules/Discount/index.d.ts +2 -4
  121. package/lib/modules/Discount/index.js +8 -63
  122. package/lib/modules/Discount/types.d.ts +1 -7
  123. package/lib/modules/Order/index.d.ts +84 -17
  124. package/lib/modules/Order/index.js +588 -145
  125. package/lib/modules/Order/types.d.ts +198 -18
  126. package/lib/modules/Order/types.js +1 -0
  127. package/lib/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  128. package/lib/modules/Order/utils/manualProductDiscount.js +207 -0
  129. package/lib/modules/Order/utils.d.ts +50 -1
  130. package/lib/modules/Order/utils.js +229 -21
  131. package/lib/modules/Quotation/index.d.ts +0 -1
  132. package/lib/modules/Quotation/index.js +18 -15
  133. package/lib/modules/Rules/index.d.ts +4 -0
  134. package/lib/modules/Rules/index.js +22 -14
  135. package/lib/modules/SalesSummary/index.js +4 -2
  136. package/lib/modules/SalesSummary/utils.js +3 -3
  137. package/lib/modules/Schedule/index.js +3 -1
  138. package/lib/modules/index.d.ts +1 -0
  139. package/lib/modules/index.js +3 -1
  140. package/lib/server/index.js +41 -4
  141. package/lib/server/modules/order/utils/filterOrders.js +12 -4
  142. package/lib/server/modules/products/index.d.ts +1 -1
  143. package/lib/server/modules/products/index.js +30 -20
  144. package/lib/server/modules/schedule/index.js +2 -1
  145. package/lib/solution/BaseSales/index.d.ts +89 -7
  146. package/lib/solution/BaseSales/index.js +736 -22
  147. package/lib/solution/BaseSales/types.d.ts +67 -1
  148. package/lib/solution/BaseSales/types.js +3 -1
  149. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +275 -0
  150. package/lib/solution/BaseSales/utils/cartPromotion.js +836 -0
  151. package/lib/solution/BaseSales/utils/quotationPrice.d.ts +7 -0
  152. package/lib/solution/BaseSales/utils/quotationPrice.js +277 -0
  153. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +71 -12
  154. package/lib/solution/BaseSales/utils.js +46 -6
  155. package/lib/solution/BookingByStep/index.d.ts +1 -1
  156. package/lib/solution/BookingTicket/index.d.ts +133 -1
  157. package/lib/solution/BookingTicket/index.js +352 -8
  158. package/lib/solution/BookingTicket/types.d.ts +2 -0
  159. package/lib/solution/BookingTicket/types.js +6 -0
  160. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +113 -0
  161. package/lib/solution/BookingTicket/utils/addProductDecision.js +318 -0
  162. package/lib/solution/BookingTicket/utils/cartView.js +4 -2
  163. package/lib/solution/ScanOrder/index.d.ts +9 -3
  164. package/lib/solution/ScanOrder/index.js +147 -66
  165. package/lib/solution/ScanOrder/utils.js +46 -6
  166. package/lib/solution/ShopDiscount/index.d.ts +0 -1
  167. package/lib/solution/ShopDiscount/index.js +2 -4
  168. package/lib/solution/VenueBooking/index.d.ts +5 -9
  169. package/lib/solution/VenueBooking/index.js +50 -14
  170. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +4 -0
  171. package/lib/solution/VenueBooking/utils/slotMerge.js +10 -0
  172. package/package.json +2 -2
@@ -37,7 +37,9 @@ var import_types = require("./types");
37
37
  var import_utils = require("./utils");
38
38
  var import_utils2 = require("../Product/utils");
39
39
  var import_dayjs = __toESM(require("dayjs"));
40
+ var import_cartPromotion = require("../../solution/BaseSales/utils/cartPromotion");
40
41
  var import_utils3 = require("../../solution/ScanOrder/utils");
42
+ var import_manualProductDiscount = require("./utils/manualProductDiscount");
41
43
  var import_Discount = require("../Discount");
42
44
  var import_Rules = require("../Rules");
43
45
  var import_types2 = require("../Rules/types");
@@ -49,6 +51,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
49
51
  super(name || "order", version);
50
52
  this.defaultName = "order";
51
53
  this.defaultVersion = "1.0.0";
54
+ // ─── 促销/赠品 ────────────────────────────────────
55
+ /** 评估器引用;由 SalesSdkProvider 注入,未注入时 applyPromotion 静默跳过 */
56
+ this.promotionEvaluator = null;
57
+ /** 赠品选择 resolver;由 SDK host bridge 注入 */
58
+ this.giftSelectResolver = null;
59
+ /** applyPromotion 递归保护 flag(写路径同步调 applyPromotion,禁止重入) */
60
+ this.isApplyingPromotion = false;
61
+ /** 最近一次 applyPromotion 的未满足促销提示 */
62
+ this.lastUnfulfilledPromotions = [];
63
+ /** 最近一次 applyPromotion 的赠品操作 diff(debug / SDK 读取使用) */
64
+ this.lastGiftActions = null;
52
65
  }
53
66
  /**
54
67
  * Populate `savedAmount` on each discount based on product-level discount details.
@@ -75,6 +88,27 @@ var OrderModule = class extends import_BaseModule.BaseModule {
75
88
  d.savedAmount = d.isSelected && key != null && savedMap.has(key) ? savedMap.get(key).toNumber() : 0;
76
89
  }
77
90
  }
91
+ applyProductDiscountPrices(products) {
92
+ return (products || []).map((product) => {
93
+ var _a, _b, _c;
94
+ if ((_a = product.metadata) == null ? void 0 : _a.is_manual_discount)
95
+ return product;
96
+ const totalPerUnitDiscount = (0, import_utils.resolveEffectivePerUnitDiscount)(product);
97
+ const optionSum = (0, import_utils.sumOptionUnitPrice)(product.product_option_item);
98
+ const sourcePrice = ((_b = product.metadata) == null ? void 0 : _b.source_product_price) ?? (((_c = product.metadata) == null ? void 0 : _c.main_product_original_price) != null ? new import_decimal.default(Number(product.metadata.main_product_original_price) || 0).minus(optionSum).toFixed(2) : product.original_price ?? "0");
99
+ const newSourceSellingPrice = new import_decimal.default(Number(sourcePrice) || 0).minus(totalPerUnitDiscount).toDecimalPlaces(2).toString();
100
+ const newMainSellingPrice = new import_decimal.default(Number(newSourceSellingPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
101
+ if (product.metadata) {
102
+ product.metadata.main_product_selling_price = newMainSellingPrice;
103
+ product.metadata.price_schema_version = 2;
104
+ }
105
+ product.selling_price = (0, import_utils.composeLinePrice)({
106
+ mainPrice: newMainSellingPrice,
107
+ bundle: product.product_bundle
108
+ });
109
+ return product;
110
+ });
111
+ }
78
112
  async initialize(core, options) {
79
113
  var _a, _b, _c;
80
114
  this.core = core;
@@ -101,7 +135,6 @@ var OrderModule = class extends import_BaseModule.BaseModule {
101
135
  this.orderHooks = options.hooks || ((_b = otherParams.order) == null ? void 0 : _b.hooks) || ((_c = otherParams.hooks) == null ? void 0 : _c.order);
102
136
  this.otherParams = otherParams;
103
137
  this.registerDiscountModules(options);
104
- this.restoreTempOrderFromStorage();
105
138
  this.logInfo("OrderModule initialized successfully");
106
139
  }
107
140
  /**
@@ -174,21 +207,25 @@ var OrderModule = class extends import_BaseModule.BaseModule {
174
207
  }
175
208
  // ─── Discount: 公共 API ───
176
209
  async loadDiscountConfig(params) {
177
- var _a, _b, _c, _d;
178
- const discountList = await ((_a = this.store.discount) == null ? void 0 : _a.loadPrepareConfig({
210
+ var _a, _b, _c, _d, _e, _f;
211
+ const orderId = params.orderId || ((_a = this.store.tempOrder) == null ? void 0 : _a.order_id) || void 0;
212
+ const discountList = await ((_b = this.store.discount) == null ? void 0 : _b.loadPrepareConfig({
179
213
  customer_id: params.customerId,
180
- action: params.action || "create",
214
+ action: params.action || (orderId ? "edit" : "create"),
181
215
  with_good_pass: 1,
182
216
  with_discount_card: 1,
183
217
  with_wallet_pass_holder: 1,
184
- request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
218
+ request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
219
+ ...orderId ? { order_id: orderId } : {}
185
220
  }));
186
221
  if (discountList) {
187
- (_b = this.store.discount) == null ? void 0 : _b.setDiscountList(discountList);
222
+ await ((_c = this.store.discount) == null ? void 0 : _c.setOriginalDiscountList(discountList));
223
+ await ((_d = this.store.discount) == null ? void 0 : _d.setDiscountList(discountList));
188
224
  }
189
- if ((_d = (_c = this.store.tempOrder) == null ? void 0 : _c.products) == null ? void 0 : _d.length) {
225
+ if (params.apply !== false && ((_f = (_e = this.store.tempOrder) == null ? void 0 : _e.products) == null ? void 0 : _f.length)) {
190
226
  this.applyDiscount();
191
227
  }
228
+ return this.getDiscountList();
192
229
  }
193
230
  getDiscountList() {
194
231
  var _a;
@@ -196,14 +233,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
196
233
  }
197
234
  async scanCode(code, customerId) {
198
235
  var _a, _b, _c, _d, _e;
199
- const resultDiscountWithReason = await ((_a = this.store.discount) == null ? void 0 : _a.batchSearch(code, { customerId })) || { discountList: [], unavailableReasonKey: null };
200
- const { discountList: resultDiscountList, unavailableReasonKey } = resultDiscountWithReason;
236
+ const resultDiscountList = await ((_a = this.store.discount) == null ? void 0 : _a.batchSearch(code, { customerId })) || [];
201
237
  const rulesModule = this.store.rules;
202
238
  if (!rulesModule) {
203
239
  return {
204
240
  type: "clientCalc",
205
241
  isAvailable: false,
206
242
  discountList: this.getDiscountList(),
243
+ scannedDiscountList: resultDiscountList,
207
244
  unavailableReason: import_types2.UnavailableReason.Unknown
208
245
  };
209
246
  }
@@ -212,7 +249,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
212
249
  type: "server",
213
250
  isAvailable: false,
214
251
  discountList: this.getDiscountList(),
215
- unavailableReasonKey
252
+ scannedDiscountList: resultDiscountList
216
253
  };
217
254
  }
218
255
  const withScanList = resultDiscountList.map((item) => ({
@@ -224,7 +261,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
224
261
  return {
225
262
  type: "clientCalc",
226
263
  isAvailable: true,
227
- discountList: this.getDiscountList()
264
+ discountList: this.getDiscountList(),
265
+ scannedDiscountList: resultDiscountList
228
266
  };
229
267
  }
230
268
  const tempOrder = this.store.tempOrder;
@@ -245,6 +283,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
245
283
  type: "clientCalc",
246
284
  isAvailable: isAvailable || false,
247
285
  discountList: newDiscountList || this.getDiscountList(),
286
+ scannedDiscountList: resultDiscountList,
248
287
  unavailableReason
249
288
  };
250
289
  }
@@ -253,6 +292,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
253
292
  const tempOrder = this.store.tempOrder;
254
293
  if (!tempOrder)
255
294
  return;
295
+ delete tempOrder.discount_list;
256
296
  const rulesModule = this.store.rules;
257
297
  if (!rulesModule)
258
298
  return;
@@ -266,7 +306,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
266
306
  orderTotalAmount: Number(((_d = this.store.summary) == null ? void 0 : _d.total_amount) || 0)
267
307
  });
268
308
  if (result == null ? void 0 : result.productList) {
269
- tempOrder.products = result.productList;
309
+ tempOrder.products = this.applyProductDiscountPrices(
310
+ (result.productList || []).map((product) => ({
311
+ ...product,
312
+ discount_list: (0, import_utils.normalizeOrderProductDiscountList)(
313
+ (0, import_manualProductDiscount.syncManualProductDiscountProductNum)(
314
+ product.discount_list,
315
+ product.num
316
+ )
317
+ )
318
+ }))
319
+ );
270
320
  }
271
321
  if (result == null ? void 0 : result.discountList) {
272
322
  OrderModule.populateSavedAmounts(
@@ -274,80 +324,178 @@ var OrderModule = class extends import_BaseModule.BaseModule {
274
324
  result.discountList
275
325
  );
276
326
  (_e = this.store.discount) == null ? void 0 : _e.setDiscountList(result.discountList);
277
- tempOrder.discount_list = result.discountList.filter(
278
- (d) => d.isSelected
279
- );
280
327
  }
281
328
  }
282
- // ─── TempOrder: 初始化入口 ───
283
- initTempOrder(params) {
284
- if (params.cacheId !== void 0)
285
- this.cacheId = params.cacheId;
286
- if (params.salesSummaryModuleName !== void 0)
287
- this.salesSummaryModuleName = params.salesSummaryModuleName;
288
- this.restoreTempOrderFromStorage();
329
+ // ─── 促销/赠品 ──────────────────────────────────────
330
+ /**
331
+ * 注入促销评估器。
332
+ *
333
+ * @example
334
+ * order.setPromotionEvaluator(appHelper.utils.promotionEvaluator);
335
+ */
336
+ setPromotionEvaluator(evaluator) {
337
+ this.promotionEvaluator = evaluator || null;
289
338
  }
290
- // ─── TempOrder: localStorage 持久化 ───
291
- getTempOrderStorageKey() {
292
- if (!this.cacheId)
293
- return null;
294
- return `scanOrder:tempOrder:${this.cacheId}`;
339
+ /**
340
+ * 注入赠品选择 resolver。OS 需要补赠品时会 await 调用 resolver;缺省时跳过新增并 console.warn。
341
+ *
342
+ * @example
343
+ * order.setGiftSelectResolver(async (ctx) => {
344
+ * // SDK 内部决定弹窗 or 自动选第一项,返回完整 OrderProduct[]
345
+ * return giftSelectBridge.request(ctx);
346
+ * });
347
+ */
348
+ setGiftSelectResolver(resolver) {
349
+ this.giftSelectResolver = resolver || null;
295
350
  }
296
- restoreTempOrderFromStorage() {
297
- var _a, _b;
298
- const key = this.getTempOrderStorageKey();
299
- if (!key)
351
+ /**
352
+ * 为商品目录追加促销标签,供上层 Sales 门面复用。
353
+ *
354
+ * @example
355
+ * const products = order.appendPromotionTags(catalogProducts);
356
+ */
357
+ appendPromotionTags(products) {
358
+ return (0, import_cartPromotion.appendPromotionTags)(products, this.promotionEvaluator);
359
+ }
360
+ /**
361
+ * 应用购物车促销:计算 → 差异化赠品 → 写回 tempOrder.products → emit onPromotionApplied。
362
+ *
363
+ * 调用时机:写路径(add/update/remove/clear/setCustomer)末尾自动触发;外部一般无需手动调。
364
+ *
365
+ * 关键约束:
366
+ * - 不调用任何公开 CRUD API(如 removeProductFromOrder),全部内部 mutate tempOrder.products,避免事件风暴;
367
+ * - 用 `isApplyingPromotion` 防递归;
368
+ * - 多选项赠品依赖 giftSelectResolver,未注入则跳过且 console.warn;
369
+ * - 不主动调用 applyDiscount / recalculateSummary,调用方负责跟进。
370
+ */
371
+ async applyPromotion() {
372
+ if (this.isApplyingPromotion)
300
373
  return;
301
- if (!this.window)
374
+ if (!this.promotionEvaluator)
302
375
  return;
303
- const cachedData = this.window.localStorage.getItem(key);
304
- if (!cachedData)
376
+ const tempOrder = this.store.tempOrder;
377
+ if (!tempOrder)
305
378
  return;
379
+ this.isApplyingPromotion = true;
306
380
  try {
307
- const parsedData = JSON.parse(cachedData);
308
- if (!(0, import_utils.isTempOrder)(parsedData)) {
309
- this.window.localStorage.removeItem(key);
310
- return;
381
+ const result = (0, import_cartPromotion.processCartPromotion)(
382
+ tempOrder.products,
383
+ this.promotionEvaluator
384
+ );
385
+ if (result.giftActions.toRemove.length > 0) {
386
+ const removeSet = new Set(result.giftActions.toRemove.map(String));
387
+ result.products = result.products.filter((p) => {
388
+ const uid = (0, import_cartPromotion.getOrderProductUid)(p);
389
+ return uid ? !removeSet.has(String(uid)) : true;
390
+ });
311
391
  }
312
- if (Array.isArray(parsedData.products)) {
313
- for (let i = 0; i < parsedData.products.length; i++) {
314
- const p = parsedData.products[i];
315
- if (!p || typeof p !== "object")
316
- continue;
317
- if (!Array.isArray(p.product_option_item)) {
318
- p.product_option_item = [];
319
- }
320
- if (!Array.isArray(p.product_bundle)) {
321
- p.product_bundle = [];
392
+ for (const reduce of result.giftActions.toReduce) {
393
+ for (const item of reduce.items) {
394
+ const product = result.products.find(
395
+ (p) => (0, import_cartPromotion.getOrderProductUid)(p) === item.uid
396
+ );
397
+ if (product) {
398
+ product.num = item.newQuantity;
322
399
  }
323
- const row = p;
324
- parsedData.products[i] = (0, import_utils3.normalizeOrderProduct)(row);
325
400
  }
326
401
  }
327
- const parsedRecord = parsedData;
328
- this.store.summary = parsedRecord.summary || (0, import_utils.createEmptySummary)();
329
- delete parsedData.summary;
330
- if (parsedRecord.lastOrderInfo) {
331
- this.store.lastOrderInfo = parsedRecord.lastOrderInfo;
332
- delete parsedData.lastOrderInfo;
333
- }
334
- if (!parsedData._extend || typeof parsedData._extend !== "object") {
335
- parsedData._extend = { productsByUid: {} };
336
- } else if (!parsedData._extend.productsByUid) {
337
- parsedData._extend.productsByUid = {};
402
+ if (result.giftActions.toAdd.length > 0) {
403
+ if (!this.giftSelectResolver) {
404
+ if (result.giftActions.toAdd.some((g) => g.giftOptions.length > 1)) {
405
+ console.warn(
406
+ "[OrderModule] giftSelectResolver not set, skip applying gifts",
407
+ result.giftActions.toAdd
408
+ );
409
+ } else {
410
+ console.warn(
411
+ "[OrderModule] giftSelectResolver not set, skip applying single-option gifts",
412
+ result.giftActions.toAdd
413
+ );
414
+ }
415
+ } else {
416
+ for (const addAction of result.giftActions.toAdd) {
417
+ if (addAction.giftOptions.length === 1) {
418
+ const sameStrategy = result.products.find(
419
+ (p) => {
420
+ var _a, _b;
421
+ return ((_b = (_a = p.metadata) == null ? void 0 : _a._giftInfo) == null ? void 0 : _b.strategyId) === addAction.strategyId;
422
+ }
423
+ );
424
+ if (sameStrategy) {
425
+ sameStrategy.num = (Number(sameStrategy.num) || 0) + (addAction.giftCount || 1);
426
+ continue;
427
+ }
428
+ }
429
+ try {
430
+ const giftProducts = await this.giftSelectResolver({
431
+ strategyId: addAction.strategyId,
432
+ strategyName: addAction.strategyName,
433
+ giftCount: addAction.giftCount,
434
+ giftOptions: addAction.giftOptions,
435
+ sourceProductIds: addAction.sourceProductIds
436
+ });
437
+ if (Array.isArray(giftProducts) && giftProducts.length > 0) {
438
+ for (const gp of giftProducts) {
439
+ if (!gp)
440
+ continue;
441
+ result.products.push(gp);
442
+ }
443
+ }
444
+ } catch (error) {
445
+ console.warn(
446
+ "[OrderModule] giftSelectResolver rejected, skip add gift",
447
+ addAction.strategyId,
448
+ error
449
+ );
450
+ }
451
+ }
452
+ }
338
453
  }
339
- this.store.tempOrder = parsedData;
340
- } catch {
341
- (_b = (_a = this.window) == null ? void 0 : _a.localStorage) == null ? void 0 : _b.removeItem(key);
454
+ tempOrder.products = result.products;
455
+ this.lastUnfulfilledPromotions = result.unfulfilledPromotions;
456
+ this.lastGiftActions = result.giftActions;
457
+ const payload = {
458
+ unfulfilledPromotions: result.unfulfilledPromotions,
459
+ giftActions: result.giftActions,
460
+ gifts: result.gifts.map((g) => ({
461
+ strategyId: g.strategyId,
462
+ strategyName: g.strategyName,
463
+ giftCount: g.giftCount,
464
+ giftOptions: g.giftOptions
465
+ })),
466
+ products: tempOrder.products
467
+ };
468
+ this.effectsEmit("onPromotionApplied", payload);
469
+ } catch (error) {
470
+ console.error("[OrderModule] applyPromotion failed", error);
471
+ } finally {
472
+ this.isApplyingPromotion = false;
342
473
  }
343
474
  }
475
+ /** 最近一次 applyPromotion 输出的未满足促销列表 */
476
+ getUnfulfilledPromotions() {
477
+ return this.lastUnfulfilledPromotions;
478
+ }
479
+ /** 最近一次 applyPromotion 输出的赠品操作 diff */
480
+ getLastGiftActions() {
481
+ return this.lastGiftActions;
482
+ }
483
+ // ─── TempOrder: 初始化入口 ───
484
+ initTempOrder(params) {
485
+ if (params.cacheId !== void 0)
486
+ this.cacheId = params.cacheId;
487
+ if (params.salesSummaryModuleName !== void 0)
488
+ this.salesSummaryModuleName = params.salesSummaryModuleName;
489
+ }
490
+ /** @deprecated OrderModule 不再负责 tempOrder localStorage 持久化。 */
491
+ setEnableTempOrderPersist(_enabled) {
492
+ }
493
+ /** @deprecated OrderModule 不再负责 tempOrder localStorage 持久化。 */
494
+ isTempOrderPersistEnabled() {
495
+ return false;
496
+ }
497
+ /** @deprecated OrderModule 不再负责 tempOrder localStorage 持久化;草稿保存请使用 IndexedDB saveDraft。 */
344
498
  persistTempOrder() {
345
- const key = this.getTempOrderStorageKey();
346
- if (!key || !this.store.tempOrder)
347
- return;
348
- if (!this.window)
349
- return;
350
- this.window.localStorage.setItem(key, JSON.stringify(this.store.tempOrder));
351
499
  }
352
500
  // ─── TempOrder: 创建 & 获取 ───
353
501
  createDefaultTempOrderInstance() {
@@ -391,6 +539,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
391
539
  }
392
540
  return tempOrder.request_unique_idempotency_token;
393
541
  }
542
+ buildPaymentSyncIdempotencyToken(tempOrder, payments) {
543
+ const baseToken = this.ensureRequestUniqueIdempotencyToken(tempOrder);
544
+ const paidPaymentsCount = (payments || []).filter((payment) => {
545
+ return String((payment == null ? void 0 : payment.status) || "").toLowerCase() === "paid";
546
+ }).length;
547
+ const paidCount = paidPaymentsCount > 0 ? paidPaymentsCount : payments.length;
548
+ return `${baseToken}_payment_${paidCount}`;
549
+ }
394
550
  hasLocalDatabase() {
395
551
  return !!this.dbManager;
396
552
  }
@@ -493,6 +649,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
493
649
  });
494
650
  }
495
651
  }
652
+ saveDraftInBackground() {
653
+ void this.saveDraft();
654
+ }
496
655
  async hydrateTempOrderFromLocalRecord(record) {
497
656
  return this.hydrateTempOrderFromRecord(record);
498
657
  }
@@ -559,17 +718,43 @@ var OrderModule = class extends import_BaseModule.BaseModule {
559
718
  return newOrder;
560
719
  }
561
720
  restoreOrder() {
721
+ var _a, _b;
562
722
  const freshTempOrder = this.createDefaultTempOrderInstance();
563
723
  this.ensureExternalSaleNumber(freshTempOrder);
564
724
  this.store.tempOrder = freshTempOrder;
565
725
  this.store.summary = (0, import_utils.createEmptySummary)();
566
726
  this.store.lastOrderInfo = void 0;
727
+ this.store.syncState = void 0;
728
+ void ((_a = this.store.discount) == null ? void 0 : _a.setOriginalDiscountList([]));
729
+ void ((_b = this.store.discount) == null ? void 0 : _b.setDiscountList([]));
567
730
  this.persistTempOrder();
568
731
  return freshTempOrder;
569
732
  }
570
733
  getTempOrder() {
571
734
  return this.store.tempOrder;
572
735
  }
736
+ async replaceTempOrder(tempOrder, options) {
737
+ if (!(0, import_utils.isTempOrder)(tempOrder)) {
738
+ throw new Error("无效的 tempOrder 数据");
739
+ }
740
+ const nextTempOrder = this.normalizeTempOrderForRuntime(
741
+ (0, import_lodash_es.cloneDeep)(tempOrder),
742
+ { ensureIdentity: false }
743
+ );
744
+ this.store.tempOrder = nextTempOrder;
745
+ if ((options == null ? void 0 : options.recalculateSummary) !== false) {
746
+ await this.recalculateSummary({ createIfMissing: false });
747
+ } else {
748
+ this.store.summary = (0, import_utils.createEmptySummary)();
749
+ }
750
+ if ((options == null ? void 0 : options.persist) !== false) {
751
+ this.persistTempOrder();
752
+ }
753
+ if (options == null ? void 0 : options.saveDraft) {
754
+ await this.saveDraft();
755
+ }
756
+ return nextTempOrder;
757
+ }
573
758
  getOrderIdentity() {
574
759
  var _a;
575
760
  const tempOrder = this.store.tempOrder;
@@ -718,6 +903,33 @@ var OrderModule = class extends import_BaseModule.BaseModule {
718
903
  }
719
904
  };
720
905
  }
906
+ getBookingUniqueIdentificationNumber(booking) {
907
+ var _a;
908
+ const uid = (_a = booking == null ? void 0 : booking.metadata) == null ? void 0 : _a.unique_identification_number;
909
+ if (uid === void 0 || uid === null || uid === "")
910
+ return null;
911
+ return String(uid);
912
+ }
913
+ findBookingIndexByUniqueIdentificationNumber(tempOrder, uniqueIdentificationNumber) {
914
+ return (tempOrder.bookings || []).findIndex((booking) => {
915
+ return this.getBookingUniqueIdentificationNumber(booking) === uniqueIdentificationNumber;
916
+ });
917
+ }
918
+ removeLinkedBookingsForProduct(tempOrder, product) {
919
+ var _a, _b;
920
+ const productUid = ((_a = product == null ? void 0 : product.metadata) == null ? void 0 : _a.unique_identification_number) || (product == null ? void 0 : product.unique_identification_number);
921
+ const bookingUid = (product == null ? void 0 : product.booking_uid) || ((_b = product == null ? void 0 : product.metadata) == null ? void 0 : _b.booking_uid);
922
+ if (!productUid && !bookingUid)
923
+ return;
924
+ tempOrder.bookings = (tempOrder.bookings || []).filter((booking) => {
925
+ const currentBookingUid = this.getBookingUniqueIdentificationNumber(booking);
926
+ if (bookingUid && currentBookingUid === String(bookingUid))
927
+ return false;
928
+ if (productUid && String((booking == null ? void 0 : booking.product_uid) || "") === String(productUid))
929
+ return false;
930
+ return true;
931
+ });
932
+ }
721
933
  // ─── TempOrder: 金额汇总 ───
722
934
  getSalesSummary() {
723
935
  if (!this.salesSummaryModuleName)
@@ -777,13 +989,22 @@ var OrderModule = class extends import_BaseModule.BaseModule {
777
989
  this.persistTempOrder();
778
990
  return summary;
779
991
  }
780
- // ─── TempOrder: 备注 ───
781
- updateTempOrderNote(note) {
992
+ updateTempOrderNote(note, sync = false) {
782
993
  const tempOrder = this.ensureTempOrder();
783
994
  tempOrder.note = String(note || "");
784
995
  this.persistTempOrder();
996
+ if (sync) {
997
+ const orderId = tempOrder.order_id;
998
+ if (!orderId)
999
+ return tempOrder.note;
1000
+ return this.syncTempOrderNoteToRemote(orderId, tempOrder.note);
1001
+ }
785
1002
  return tempOrder.note;
786
1003
  }
1004
+ async syncTempOrderNoteToRemote(orderId, note) {
1005
+ await this.request.put(`/order/order/${orderId}/note`, { note });
1006
+ return note;
1007
+ }
787
1008
  getTempOrderNote() {
788
1009
  var _a;
789
1010
  return ((_a = this.store.tempOrder) == null ? void 0 : _a.note) || "";
@@ -807,10 +1028,29 @@ var OrderModule = class extends import_BaseModule.BaseModule {
807
1028
  this.persistTempOrder();
808
1029
  return tempOrder.contacts_info;
809
1030
  }
1031
+ buildTempOrderCustomer(params) {
1032
+ if (!params.customerId)
1033
+ return null;
1034
+ const source = params.source ? (0, import_lodash_es.cloneDeep)(params.source) : {};
1035
+ const name = source.name ?? source.display_name ?? source.customerName ?? source.customer_name ?? params.customerName;
1036
+ const customerName = source.customer_name ?? source.customerName ?? source.display_name ?? source.name ?? params.customerName;
1037
+ return {
1038
+ ...source,
1039
+ id: source.id ?? params.customerId,
1040
+ customer_id: source.customer_id ?? params.customerId,
1041
+ name: String(name || ""),
1042
+ customer_name: String(customerName || ""),
1043
+ country_calling_code: String(
1044
+ source.country_calling_code ?? source.countryCallingCode ?? params.countryCallingCode
1045
+ ),
1046
+ phone: String(source.phone ?? params.phone),
1047
+ email: String(source.email ?? params.email)
1048
+ };
1049
+ }
810
1050
  /**
811
1051
  * 更新当前 tempOrder 的客户协议字段。
812
1052
  *
813
- * PaymentModal 只需要修改订单里的客户字段;如果外部还有客户模块需要同步,
1053
+ * PaymentModal 只需要修改订单里的客户事实;如果外部还有客户模块需要同步,
814
1054
  * 应由调用方通过回调处理,不能把外部 UI 状态写进 tempOrder。
815
1055
  */
816
1056
  updateTempOrderCustomer(customer) {
@@ -824,6 +1064,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
824
1064
  );
825
1065
  tempOrder.phone = String(customer.phone || "");
826
1066
  tempOrder.email = String(customer.email || "");
1067
+ tempOrder.customer = this.buildTempOrderCustomer({
1068
+ source: customer,
1069
+ customerId: tempOrder.customer_id,
1070
+ customerName: tempOrder.customer_name,
1071
+ countryCallingCode: tempOrder.country_calling_code,
1072
+ phone: tempOrder.phone,
1073
+ email: tempOrder.email
1074
+ });
827
1075
  this.persistTempOrder();
828
1076
  return {
829
1077
  customerId: tempOrder.customer_id,
@@ -838,11 +1086,20 @@ var OrderModule = class extends import_BaseModule.BaseModule {
838
1086
  return;
839
1087
  }
840
1088
  const tempOrder = this.ensureTempOrder();
1089
+ const previousCustomerId = tempOrder.customer_id ?? null;
841
1090
  tempOrder.customer_id = patch.customer_id || patch.id || null;
842
1091
  tempOrder.customer_name = String(patch.customer_name || "");
843
1092
  tempOrder.country_calling_code = String(patch.country_calling_code || "");
844
1093
  tempOrder.phone = String(patch.phone || "");
845
1094
  tempOrder.email = String(patch.email || "");
1095
+ tempOrder.customer = this.buildTempOrderCustomer({
1096
+ source: snapshot || null,
1097
+ customerId: tempOrder.customer_id,
1098
+ customerName: tempOrder.customer_name,
1099
+ countryCallingCode: tempOrder.country_calling_code,
1100
+ phone: tempOrder.phone,
1101
+ email: tempOrder.email
1102
+ });
846
1103
  if (snapshot !== void 0) {
847
1104
  const extend = this.ensureExtend(tempOrder);
848
1105
  if (snapshot === null)
@@ -850,12 +1107,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
850
1107
  else
851
1108
  extend.customerSnapshot = (0, import_lodash_es.cloneDeep)(snapshot);
852
1109
  }
1110
+ const nextCustomerId = tempOrder.customer_id ?? null;
1111
+ if (String(previousCustomerId ?? "") !== String(nextCustomerId ?? "")) {
1112
+ (0, import_utils.clearTempOrderHolderAssignments)(tempOrder);
1113
+ }
853
1114
  this.persistTempOrder();
1115
+ this.saveDraftInBackground();
854
1116
  this.emitOrderCustomerChange();
855
1117
  }
856
1118
  getOrderCustomer() {
857
1119
  const tempOrder = this.store.tempOrder;
858
- if (!tempOrder || tempOrder.customer_id === null || tempOrder.customer_id === void 0) {
1120
+ if (!tempOrder || !tempOrder.customer_id) {
859
1121
  return null;
860
1122
  }
861
1123
  return {
@@ -873,14 +1135,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
873
1135
  }
874
1136
  clearOrderCustomer() {
875
1137
  const tempOrder = this.ensureTempOrder();
1138
+ (0, import_utils.clearTempOrderHolderAssignments)(tempOrder);
876
1139
  tempOrder.customer_id = null;
877
1140
  tempOrder.customer_name = "";
878
1141
  tempOrder.country_calling_code = "";
879
1142
  tempOrder.phone = "";
880
1143
  tempOrder.email = "";
1144
+ tempOrder.customer = null;
881
1145
  if (tempOrder._extend)
882
1146
  delete tempOrder._extend.customerSnapshot;
883
1147
  this.persistTempOrder();
1148
+ this.saveDraftInBackground();
884
1149
  this.core.effects.emit(import_types.OrderHooks.OnOrderCustomerChange, null);
885
1150
  }
886
1151
  emitOrderCustomerChange() {
@@ -920,6 +1185,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
920
1185
  }
921
1186
  /** 追加单个支付项,并立即持久化当前 tempOrder。 */
922
1187
  addOrderPayment(payment) {
1188
+ this.logInfo("addOrderPayment", {
1189
+ payment
1190
+ });
923
1191
  const tempOrder = this.ensureTempOrder();
924
1192
  const mapped = (0, import_utils.mapPaymentItemsToOrderPayments)([payment]);
925
1193
  tempOrder.payments = [...tempOrder.payments || [], ...mapped];
@@ -1026,6 +1294,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1026
1294
  productToAdd.product_bundle
1027
1295
  );
1028
1296
  const normalizedIncoming = (0, import_utils3.normalizeOrderProduct)(productToAdd);
1297
+ normalizedIncoming.discount_list = (0, import_utils.normalizeOrderProductDiscountList)(
1298
+ normalizedIncoming.discount_list
1299
+ );
1029
1300
  const hasIncomingGoodPass = this.hasGoodPassDiscount(
1030
1301
  normalizedIncoming
1031
1302
  );
@@ -1082,6 +1353,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1082
1353
  unique_identification_number: targetUid
1083
1354
  }
1084
1355
  });
1356
+ normalizedProduct.discount_list = (0, import_utils.normalizeOrderProductDiscountList)(
1357
+ normalizedProduct.discount_list
1358
+ );
1085
1359
  tempOrder.products[matchedIndex] = {
1086
1360
  ...targetProduct,
1087
1361
  ...normalizedProduct,
@@ -1104,6 +1378,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1104
1378
  if (linked) {
1105
1379
  tempOrder.bookings = [...tempOrder.bookings || [], linked.booking];
1106
1380
  }
1381
+ await this.applyPromotion();
1107
1382
  this.applyDiscount();
1108
1383
  await this.recalculateSummary({ createIfMissing: true });
1109
1384
  this.persistTempOrder();
@@ -1127,15 +1402,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1127
1402
  (item) => (item == null ? void 0 : item.type) === "good_pass" || (item == null ? void 0 : item.tag) === "good_pass"
1128
1403
  );
1129
1404
  }
1130
- async updateProductInOrder(params) {
1131
- var _a, _b, _c, _d, _e;
1405
+ async updateOrderProduct(params) {
1406
+ var _a, _b, _c, _d, _e, _f, _g;
1132
1407
  const {
1133
1408
  product_id,
1134
1409
  product_variant_id,
1135
1410
  updates,
1136
1411
  unique_identification_number,
1412
+ identity_key,
1137
1413
  product_option_item,
1138
- product_bundle
1414
+ product_bundle,
1415
+ booking
1139
1416
  } = params;
1140
1417
  const tempOrder = this.ensureTempOrder();
1141
1418
  const identityLookup = {
@@ -1144,12 +1421,30 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1144
1421
  };
1145
1422
  if (unique_identification_number !== void 0) {
1146
1423
  identityLookup.unique_identification_number = unique_identification_number;
1424
+ if (identity_key === void 0) {
1425
+ identityLookup.identity_key = unique_identification_number;
1426
+ }
1427
+ }
1428
+ if (identity_key !== void 0) {
1429
+ identityLookup.identity_key = identity_key;
1147
1430
  }
1148
1431
  if (product_option_item !== void 0)
1149
1432
  identityLookup.product_option_item = product_option_item;
1150
1433
  if (product_bundle !== void 0)
1151
1434
  identityLookup.product_bundle = product_bundle;
1152
- const productIndex = (0, import_utils3.getProductIdentityIndex)(tempOrder.products, identityLookup);
1435
+ let productIndex = -1;
1436
+ if (unique_identification_number !== void 0) {
1437
+ const targetUid = String(unique_identification_number);
1438
+ productIndex = tempOrder.products.findIndex((item) => {
1439
+ var _a2;
1440
+ return String(
1441
+ ((_a2 = item.metadata) == null ? void 0 : _a2.unique_identification_number) || item.unique_identification_number || ""
1442
+ ) === targetUid;
1443
+ });
1444
+ }
1445
+ if (productIndex === -1) {
1446
+ productIndex = (0, import_utils3.getProductIdentityIndex)(tempOrder.products, identityLookup);
1447
+ }
1153
1448
  if (productIndex === -1) {
1154
1449
  throw new Error("[Order] 目标商品不存在,无法更新");
1155
1450
  }
@@ -1176,28 +1471,178 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1176
1471
  delete nextProduct.metadata.main_product_original_price;
1177
1472
  delete nextProduct.metadata.price_schema_version;
1178
1473
  }
1179
- tempOrder.products[productIndex] = (0, import_utils3.normalizeOrderProduct)(nextProduct);
1474
+ let normalizedProduct = (0, import_utils3.normalizeOrderProduct)(nextProduct);
1475
+ normalizedProduct.discount_list = (0, import_utils.normalizeOrderProductDiscountList)(
1476
+ normalizedProduct.discount_list
1477
+ );
1478
+ if (booking) {
1479
+ const productUid = String(
1480
+ ((_e = normalizedProduct.metadata) == null ? void 0 : _e.unique_identification_number) || ((_f = targetProduct.metadata) == null ? void 0 : _f.unique_identification_number) || unique_identification_number || (0, import_utils.createUuidV4)()
1481
+ );
1482
+ normalizedProduct.metadata = {
1483
+ ...normalizedProduct.metadata || {},
1484
+ unique_identification_number: productUid
1485
+ };
1486
+ const linked = this.createLinkedProductAndBooking({
1487
+ product: {
1488
+ ...normalizedProduct,
1489
+ unique_identification_number: productUid
1490
+ },
1491
+ booking
1492
+ });
1493
+ this.removeLinkedBookingsForProduct(tempOrder, targetProduct);
1494
+ normalizedProduct = {
1495
+ ...normalizedProduct,
1496
+ booking_uid: linked.bookingUid,
1497
+ metadata: {
1498
+ ...normalizedProduct.metadata || {},
1499
+ booking_uid: linked.bookingUid,
1500
+ unique_identification_number: productUid
1501
+ }
1502
+ };
1503
+ tempOrder.bookings = [...tempOrder.bookings || [], linked.booking];
1504
+ }
1505
+ tempOrder.products[productIndex] = normalizedProduct;
1180
1506
  this.captureProductRuntime(
1181
1507
  tempOrder,
1182
1508
  updates,
1183
1509
  tempOrder.products[productIndex],
1184
- (_e = tempOrder.products[productIndex].metadata) == null ? void 0 : _e.unique_identification_number
1510
+ (_g = tempOrder.products[productIndex].metadata) == null ? void 0 : _g.unique_identification_number
1185
1511
  );
1512
+ await this.applyPromotion();
1186
1513
  this.applyDiscount();
1187
1514
  await this.recalculateSummary({ createIfMissing: true });
1188
1515
  this.persistTempOrder();
1189
1516
  return tempOrder.products;
1190
1517
  }
1191
- async removeProductFromOrder(identity) {
1518
+ async updateOrderProductQuantity(params) {
1519
+ var _a;
1520
+ const {
1521
+ product_id,
1522
+ product_variant_id,
1523
+ num,
1524
+ unique_identification_number,
1525
+ identity_key,
1526
+ product_option_item,
1527
+ product_bundle
1528
+ } = params;
1529
+ const tempOrder = this.ensureTempOrder();
1530
+ const identityLookup = {
1531
+ product_id,
1532
+ product_variant_id
1533
+ };
1534
+ if (unique_identification_number !== void 0) {
1535
+ identityLookup.unique_identification_number = unique_identification_number;
1536
+ if (identity_key === void 0) {
1537
+ identityLookup.identity_key = unique_identification_number;
1538
+ }
1539
+ }
1540
+ if (identity_key !== void 0)
1541
+ identityLookup.identity_key = identity_key;
1542
+ if (product_option_item !== void 0)
1543
+ identityLookup.product_option_item = product_option_item;
1544
+ if (product_bundle !== void 0)
1545
+ identityLookup.product_bundle = product_bundle;
1546
+ let productIndex = -1;
1547
+ if (unique_identification_number !== void 0) {
1548
+ const targetUid = String(unique_identification_number);
1549
+ productIndex = tempOrder.products.findIndex((item) => {
1550
+ var _a2;
1551
+ return String(
1552
+ ((_a2 = item.metadata) == null ? void 0 : _a2.unique_identification_number) || item.unique_identification_number || ""
1553
+ ) === targetUid;
1554
+ });
1555
+ }
1556
+ if (productIndex === -1) {
1557
+ productIndex = (0, import_utils3.getProductIdentityIndex)(tempOrder.products, identityLookup);
1558
+ }
1559
+ if (productIndex === -1) {
1560
+ throw new Error("[Order] 目标商品不存在,无法更新数量");
1561
+ }
1562
+ const targetProduct = tempOrder.products[productIndex];
1563
+ const normalizedProduct = (0, import_utils3.normalizeOrderProduct)({
1564
+ ...targetProduct,
1565
+ num: (0, import_utils3.getSafeProductNum)(num),
1566
+ metadata: {
1567
+ ...targetProduct.metadata || {},
1568
+ unique_identification_number: ((_a = targetProduct.metadata) == null ? void 0 : _a.unique_identification_number) || unique_identification_number
1569
+ }
1570
+ });
1571
+ normalizedProduct.discount_list = (0, import_utils.normalizeOrderProductDiscountList)(
1572
+ normalizedProduct.discount_list
1573
+ );
1574
+ tempOrder.products[productIndex] = normalizedProduct;
1575
+ await this.applyPromotion();
1576
+ this.applyDiscount();
1577
+ await this.recalculateSummary({ createIfMissing: true });
1578
+ this.persistTempOrder();
1579
+ return tempOrder.products;
1580
+ }
1581
+ updateOrderBooking(params) {
1582
+ const { unique_identification_number, updates } = params;
1192
1583
  const tempOrder = this.ensureTempOrder();
1193
- tempOrder.products = tempOrder.products.filter(
1194
- (item) => !(0, import_utils3.isIdentityMatch)(item, identity)
1584
+ const bookingUid = String(unique_identification_number || "");
1585
+ const bookingIndex = this.findBookingIndexByUniqueIdentificationNumber(
1586
+ tempOrder,
1587
+ bookingUid
1195
1588
  );
1589
+ if (!bookingUid || bookingIndex === -1) {
1590
+ throw new Error("[Order] 目标 booking 不存在,无法更新");
1591
+ }
1592
+ const targetBooking = tempOrder.bookings[bookingIndex] || {};
1593
+ const nextBooking = {
1594
+ ...targetBooking,
1595
+ ...updates,
1596
+ metadata: {
1597
+ ...targetBooking.metadata || {},
1598
+ ...updates.metadata || {},
1599
+ unique_identification_number: bookingUid
1600
+ }
1601
+ };
1602
+ nextBooking.is_all = (0, import_utils.normalizeBookingIsAll)(nextBooking);
1603
+ nextBooking.sub_type = (0, import_utils.normalizeBookingSubType)(nextBooking);
1604
+ tempOrder.bookings[bookingIndex] = nextBooking;
1605
+ this.persistTempOrder();
1606
+ return tempOrder.bookings;
1607
+ }
1608
+ async removeProductFromOrder(identity) {
1609
+ const tempOrder = this.ensureTempOrder();
1610
+ const removedProducts = [];
1611
+ tempOrder.products = tempOrder.products.filter((item) => {
1612
+ const shouldRemove = (0, import_utils3.isIdentityMatch)(item, identity);
1613
+ if (shouldRemove)
1614
+ removedProducts.push(item);
1615
+ return !shouldRemove;
1616
+ });
1617
+ for (const product of removedProducts) {
1618
+ this.removeLinkedBookingsForProduct(tempOrder, product);
1619
+ }
1620
+ await this.applyPromotion();
1196
1621
  this.applyDiscount();
1197
1622
  await this.recalculateSummary({ createIfMissing: true });
1198
1623
  this.persistTempOrder();
1199
1624
  return tempOrder.products;
1200
1625
  }
1626
+ /**
1627
+ * 仅清空购物车行(products / bookings),不重置整单。
1628
+ * 用于「仅清空商品」;客户、备注、支付等协议字段保持不变。
1629
+ */
1630
+ async clearOrderCartLines() {
1631
+ const tempOrder = this.ensureTempOrder();
1632
+ tempOrder.products = [];
1633
+ tempOrder.bookings = [];
1634
+ if (tempOrder._extend && typeof tempOrder._extend === "object") {
1635
+ tempOrder._extend = {
1636
+ ...tempOrder._extend,
1637
+ productsByUid: {}
1638
+ };
1639
+ }
1640
+ await this.applyPromotion();
1641
+ this.applyDiscount();
1642
+ await this.recalculateSummary({ createIfMissing: true });
1643
+ this.persistTempOrder();
1644
+ return tempOrder;
1645
+ }
1201
1646
  // ─── TempOrder: 提交 ───
1202
1647
  /**
1203
1648
  * 提交当前 Sales tempOrder。
@@ -1224,7 +1669,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1224
1669
  ...hasPaymentStatusOverride ? { payment_status: params == null ? void 0 : params.paymentStatus } : {},
1225
1670
  ...hasSmallTicketDataFlagOverride ? { small_ticket_data_flag: params == null ? void 0 : params.smallTicketDataFlag } : {}
1226
1671
  };
1227
- return (params == null ? void 0 : params.enhancePayload) ? params.enhancePayload(nextPayload, ctx) : nextPayload;
1672
+ const enhancedPayload = (params == null ? void 0 : params.enhancePayload) ? params.enhancePayload(nextPayload, ctx) : nextPayload;
1673
+ return enhancedPayload;
1228
1674
  } : void 0;
1229
1675
  const payload = (0, import_utils.buildSubmitPayload)({
1230
1676
  tempOrder,
@@ -1241,6 +1687,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1241
1687
  this.logInfo("submitTempOrder calling sales checkout", {
1242
1688
  orderId: payload.order_id,
1243
1689
  externalSaleNumber: payload.external_sale_number,
1690
+ requestUniqueIdempotencyToken: payload.request_unique_idempotency_token,
1244
1691
  paymentStatus: payload.payment_status,
1245
1692
  paymentsCount: ((_b = payload.payments) == null ? void 0 : _b.length) || 0,
1246
1693
  smallTicketDataFlag: payload.small_ticket_data_flag
@@ -1327,21 +1774,37 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1327
1774
  const mappedPayments = (0, import_utils.mapPaymentItemsToOrderPayments)(
1328
1775
  params.payments || []
1329
1776
  );
1330
- tempOrder.payments = mappedPayments;
1331
- if (params.paymentStatus)
1332
- tempOrder.payment_status = params.paymentStatus;
1333
- this.persistTempOrder();
1334
- await this.saveDraft();
1335
1777
  const paymentTotal = this.calculatePaymentTotal(mappedPayments);
1336
1778
  const targetAmount = this.getPaymentTargetAmount();
1337
1779
  const isFullyPaid = targetAmount.lte(0) ? false : paymentTotal.gte(targetAmount);
1338
- if (!isFullyPaid || !params.submitWhenPaid) {
1780
+ const paymentStatus = isFullyPaid ? params.paymentStatus || "paid" : "partially_paid";
1781
+ tempOrder.payments = mappedPayments;
1782
+ tempOrder.payment_status = paymentStatus;
1783
+ this.persistTempOrder();
1784
+ await this.saveDraft();
1785
+ if (!params.submitWhenPaid) {
1786
+ return { isFullyPaid };
1787
+ }
1788
+ if (this.store.syncState === "submitting") {
1339
1789
  return { isFullyPaid };
1340
1790
  }
1791
+ this.store.syncState = "submitting";
1792
+ const paymentSyncIdempotencyToken = this.buildPaymentSyncIdempotencyToken(
1793
+ tempOrder,
1794
+ mappedPayments
1795
+ );
1341
1796
  const submitResult = await this.submitTempOrder({
1342
1797
  payments: mappedPayments,
1343
- paymentStatus: params.paymentStatus || "paid",
1344
- smallTicketDataFlag: params.smallTicketDataFlag
1798
+ paymentStatus,
1799
+ smallTicketDataFlag: params.smallTicketDataFlag,
1800
+ channel: params.channel,
1801
+ enhancePayload: (payload) => {
1802
+ const nextPayload = {
1803
+ ...payload,
1804
+ request_unique_idempotency_token: paymentSyncIdempotencyToken
1805
+ };
1806
+ return nextPayload;
1807
+ }
1345
1808
  });
1346
1809
  return { isFullyPaid, submitResult };
1347
1810
  }
@@ -1641,53 +2104,36 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1641
2104
  */
1642
2105
  async hydrateTempOrderFromRecord(record, options) {
1643
2106
  const raw = record && typeof record === "object" && record.data && record.order_id == null ? record.data : record || {};
1644
- const defaults = this.createDefaultTempOrderInstance();
2107
+ const nextTempOrder = this.normalizeTempOrderForRuntime(raw, {
2108
+ ensureIdentity: true
2109
+ });
2110
+ this.store.tempOrder = nextTempOrder;
2111
+ this.store.summary = raw.summary && typeof raw.summary === "object" ? { ...(0, import_utils.createEmptySummary)(), ...raw.summary } : (0, import_utils.createEmptySummary)();
2112
+ this.store.lastOrderInfo = raw.lastOrderInfo || raw;
2113
+ if (options == null ? void 0 : options.recalcOnHydrate) {
2114
+ await this.recalculateSummary({ createIfMissing: false });
2115
+ }
2116
+ this.persistTempOrder();
2117
+ return nextTempOrder;
2118
+ }
2119
+ normalizeTempOrderForRuntime(raw, options) {
1645
2120
  const nextTempOrder = {
1646
- ...defaults
2121
+ ...this.createDefaultTempOrderInstance(),
2122
+ ...raw || {}
1647
2123
  };
1648
- const TEMP_ORDER_KEYS = [
1649
- "order_id",
1650
- "external_sale_number",
1651
- "order_number",
1652
- "shop_order_number",
1653
- "shop_full_order_number",
1654
- "type",
1655
- "business_code",
1656
- "platform",
1657
- "sales_channel",
1658
- "order_sales_channel",
1659
- "status",
1660
- "payment_status",
1661
- "shipping_status",
1662
- "customer_id",
1663
- "customer_name",
1664
- "country_calling_code",
1665
- "phone",
1666
- "email",
1667
- "customer",
1668
- "is_price_include_tax",
1669
- "tax_title",
1670
- "tax_country_code",
1671
- "currency_code",
1672
- "currency_symbol",
1673
- "currency_format",
1674
- "is_deposit",
1675
- "deposit_amount",
1676
- "shop_discount",
1677
- "surcharge_fee",
1678
- "note",
1679
- "buzzer",
1680
- "delivery_type",
1681
- "table_number",
1682
- "schedule_date",
1683
- "created_at",
1684
- "request_unique_idempotency_token"
1685
- ];
1686
- TEMP_ORDER_KEYS.forEach((key) => {
1687
- if (raw[key] !== void 0) {
1688
- nextTempOrder[key] = raw[key];
1689
- }
1690
- });
2124
+ delete nextTempOrder.summary;
2125
+ delete nextTempOrder.lastOrderInfo;
2126
+ delete nextTempOrder.discount_list;
2127
+ delete nextTempOrder.shop_id;
2128
+ delete nextTempOrder.create_date;
2129
+ delete nextTempOrder.total_amount;
2130
+ delete nextTempOrder.expect_amount;
2131
+ delete nextTempOrder.paid_amount;
2132
+ delete nextTempOrder.shop_note;
2133
+ delete nextTempOrder.start_time;
2134
+ delete nextTempOrder.tax_fee;
2135
+ delete nextTempOrder.total_refund_amount;
2136
+ delete nextTempOrder.holder_id;
1691
2137
  nextTempOrder.metadata = raw.metadata && typeof raw.metadata === "object" ? { ...raw.metadata } : {};
1692
2138
  nextTempOrder.holder = raw.holder && typeof raw.holder === "object" ? { ...raw.holder } : null;
1693
2139
  nextTempOrder.products = Array.isArray(raw.products) ? raw.products.map((p) => this.normalizeHydratedOrderProduct(p)) : [];
@@ -1698,7 +2144,6 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1698
2144
  })) : [];
1699
2145
  nextTempOrder.payments = Array.isArray(raw.payments) ? raw.payments.map((p) => ({ ...p || {} })) : [];
1700
2146
  nextTempOrder.surcharges = Array.isArray(raw.surcharges) ? raw.surcharges.map((s) => ({ ...s || {} })) : [];
1701
- nextTempOrder.discount_list = Array.isArray(raw.discount_list) ? raw.discount_list.map((d) => ({ ...d || {} })) : [];
1702
2147
  nextTempOrder.relation_forms = Array.isArray(raw.relation_forms) ? raw.relation_forms.map((f) => ({ ...f || {} })) : [];
1703
2148
  nextTempOrder.contacts = Array.isArray(raw.contacts) ? raw.contacts.map((c) => ({ ...c || {} })) : [];
1704
2149
  nextTempOrder.contacts_info = raw.contacts_info && typeof raw.contacts_info === "object" && !Array.isArray(raw.contacts_info) ? { ...raw.contacts_info } : null;
@@ -1706,14 +2151,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1706
2151
  ...raw._extend,
1707
2152
  productsByUid: raw._extend.productsByUid || {}
1708
2153
  } : { productsByUid: {} };
1709
- this.ensureExternalSaleNumber(nextTempOrder);
1710
- this.store.tempOrder = nextTempOrder;
1711
- this.store.summary = raw.summary && typeof raw.summary === "object" ? { ...(0, import_utils.createEmptySummary)(), ...raw.summary } : (0, import_utils.createEmptySummary)();
1712
- this.store.lastOrderInfo = raw.lastOrderInfo || raw;
1713
- if (options == null ? void 0 : options.recalcOnHydrate) {
1714
- await this.recalculateSummary({ createIfMissing: false });
2154
+ if ((options == null ? void 0 : options.ensureIdentity) !== false) {
2155
+ this.ensureExternalSaleNumber(nextTempOrder);
1715
2156
  }
1716
- this.persistTempOrder();
1717
2157
  return nextTempOrder;
1718
2158
  }
1719
2159
  normalizeHydratedOrderProduct(product) {
@@ -1732,6 +2172,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1732
2172
  row.metadata.unique_identification_number = (0, import_utils.createUuidV4)();
1733
2173
  }
1734
2174
  const normalized = (0, import_utils3.normalizeOrderProduct)(row);
2175
+ normalized.discount_list = (0, import_utils.normalizeOrderProductDiscountList)(
2176
+ normalized.discount_list
2177
+ );
1735
2178
  const result = {
1736
2179
  ...row,
1737
2180
  ...normalized,