@pisell/pisellos 2.2.175 → 2.2.177

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 (176) 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 +86 -17
  38. package/dist/modules/Order/index.js +1303 -497
  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 +18 -5
  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/index.d.ts +23 -0
  56. package/dist/server/modules/order/index.js +123 -46
  57. package/dist/server/modules/order/utils/filterOrders.js +20 -6
  58. package/dist/server/modules/products/index.d.ts +1 -1
  59. package/dist/server/modules/products/index.js +130 -113
  60. package/dist/server/modules/schedule/index.js +13 -6
  61. package/dist/solution/BaseSales/index.d.ts +89 -7
  62. package/dist/solution/BaseSales/index.js +1484 -365
  63. package/dist/solution/BaseSales/types.d.ts +67 -1
  64. package/dist/solution/BaseSales/types.js +3 -1
  65. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +275 -0
  66. package/dist/solution/BaseSales/utils/cartPromotion.js +1258 -0
  67. package/dist/solution/BaseSales/utils/quotationPrice.d.ts +7 -0
  68. package/dist/solution/BaseSales/utils/quotationPrice.js +237 -0
  69. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +68 -16
  70. package/dist/solution/BaseSales/utils.js +46 -8
  71. package/dist/solution/BookingByStep/index.d.ts +1 -1
  72. package/dist/solution/BookingTicket/index.d.ts +133 -1
  73. package/dist/solution/BookingTicket/index.js +780 -175
  74. package/dist/solution/BookingTicket/types.d.ts +2 -0
  75. package/dist/solution/BookingTicket/types.js +3 -0
  76. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +113 -0
  77. package/dist/solution/BookingTicket/utils/addProductDecision.js +346 -0
  78. package/dist/solution/BookingTicket/utils/cartView.js +4 -2
  79. package/dist/solution/ScanOrder/index.d.ts +9 -3
  80. package/dist/solution/ScanOrder/index.js +231 -128
  81. package/dist/solution/ScanOrder/utils.js +46 -8
  82. package/dist/solution/ShopDiscount/index.d.ts +0 -1
  83. package/dist/solution/ShopDiscount/index.js +18 -23
  84. package/dist/solution/VenueBooking/index.d.ts +5 -9
  85. package/dist/solution/VenueBooking/index.js +103 -55
  86. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +4 -0
  87. package/dist/solution/VenueBooking/utils/slotMerge.js +10 -0
  88. package/lib/model/strategy/adapter/promotion/index.js +0 -49
  89. package/lib/modules/BookingContext/index.d.ts +37 -0
  90. package/lib/modules/BookingContext/index.js +297 -0
  91. package/lib/modules/BookingContext/types.d.ts +102 -0
  92. package/lib/modules/BookingContext/types.js +34 -0
  93. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
  94. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +207 -0
  95. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
  96. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +141 -0
  97. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +75 -0
  98. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +198 -0
  99. package/lib/modules/BookingContext/utils/flexible.d.ts +28 -0
  100. package/lib/modules/BookingContext/utils/flexible.js +80 -0
  101. package/lib/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
  102. package/lib/modules/BookingContext/utils/formatResourceList.js +50 -0
  103. package/lib/modules/BookingContext/utils/index.d.ts +11 -0
  104. package/lib/modules/BookingContext/utils/index.js +43 -0
  105. package/lib/modules/BookingContext/utils/noResource.d.ts +13 -0
  106. package/lib/modules/BookingContext/utils/noResource.js +90 -0
  107. package/lib/modules/BookingContext/utils/productExtend.d.ts +72 -0
  108. package/lib/modules/BookingContext/utils/productExtend.js +283 -0
  109. package/lib/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
  110. package/lib/modules/BookingContext/utils/resourceErrors.js +80 -0
  111. package/lib/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
  112. package/lib/modules/BookingContext/utils/resourceSelection.js +46 -0
  113. package/lib/modules/BookingContext/utils/resources.d.ts +80 -0
  114. package/lib/modules/BookingContext/utils/resources.js +377 -0
  115. package/lib/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
  116. package/lib/modules/BookingContext/utils/serviceTimes.js +162 -0
  117. package/lib/modules/BookingContext/utils/timeSlices.d.ts +42 -0
  118. package/lib/modules/BookingContext/utils/timeSlices.js +124 -0
  119. package/lib/modules/Customer/index.d.ts +6 -0
  120. package/lib/modules/Customer/index.js +26 -11
  121. package/lib/modules/Customer/types.d.ts +2 -0
  122. package/lib/modules/Discount/index.d.ts +2 -4
  123. package/lib/modules/Discount/index.js +8 -63
  124. package/lib/modules/Discount/types.d.ts +1 -7
  125. package/lib/modules/Order/index.d.ts +86 -17
  126. package/lib/modules/Order/index.js +633 -146
  127. package/lib/modules/Order/types.d.ts +198 -18
  128. package/lib/modules/Order/types.js +1 -0
  129. package/lib/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  130. package/lib/modules/Order/utils/manualProductDiscount.js +207 -0
  131. package/lib/modules/Order/utils.d.ts +50 -1
  132. package/lib/modules/Order/utils.js +229 -21
  133. package/lib/modules/Quotation/index.d.ts +0 -1
  134. package/lib/modules/Quotation/index.js +18 -15
  135. package/lib/modules/Rules/index.d.ts +4 -0
  136. package/lib/modules/Rules/index.js +22 -14
  137. package/lib/modules/SalesSummary/index.js +4 -2
  138. package/lib/modules/SalesSummary/utils.js +18 -5
  139. package/lib/modules/Schedule/index.js +3 -1
  140. package/lib/modules/index.d.ts +1 -0
  141. package/lib/modules/index.js +3 -1
  142. package/lib/server/index.js +41 -4
  143. package/lib/server/modules/order/index.d.ts +23 -0
  144. package/lib/server/modules/order/index.js +46 -14
  145. package/lib/server/modules/order/utils/filterOrders.js +12 -4
  146. package/lib/server/modules/products/index.d.ts +1 -1
  147. package/lib/server/modules/products/index.js +30 -20
  148. package/lib/server/modules/schedule/index.js +2 -1
  149. package/lib/solution/BaseSales/index.d.ts +89 -7
  150. package/lib/solution/BaseSales/index.js +736 -22
  151. package/lib/solution/BaseSales/types.d.ts +67 -1
  152. package/lib/solution/BaseSales/types.js +3 -1
  153. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +275 -0
  154. package/lib/solution/BaseSales/utils/cartPromotion.js +836 -0
  155. package/lib/solution/BaseSales/utils/quotationPrice.d.ts +7 -0
  156. package/lib/solution/BaseSales/utils/quotationPrice.js +277 -0
  157. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +71 -12
  158. package/lib/solution/BaseSales/utils.js +46 -6
  159. package/lib/solution/BookingByStep/index.d.ts +1 -1
  160. package/lib/solution/BookingTicket/index.d.ts +133 -1
  161. package/lib/solution/BookingTicket/index.js +352 -8
  162. package/lib/solution/BookingTicket/types.d.ts +2 -0
  163. package/lib/solution/BookingTicket/types.js +6 -0
  164. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +113 -0
  165. package/lib/solution/BookingTicket/utils/addProductDecision.js +318 -0
  166. package/lib/solution/BookingTicket/utils/cartView.js +4 -2
  167. package/lib/solution/ScanOrder/index.d.ts +9 -3
  168. package/lib/solution/ScanOrder/index.js +147 -66
  169. package/lib/solution/ScanOrder/utils.js +46 -6
  170. package/lib/solution/ShopDiscount/index.d.ts +0 -1
  171. package/lib/solution/ShopDiscount/index.js +2 -4
  172. package/lib/solution/VenueBooking/index.d.ts +5 -9
  173. package/lib/solution/VenueBooking/index.js +50 -14
  174. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +4 -0
  175. package/lib/solution/VenueBooking/utils/slotMerge.js +10 -0
  176. 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
  }
@@ -537,6 +696,39 @@ var OrderModule = class extends import_BaseModule.BaseModule {
537
696
  const fixedAmount = new import_decimal.default(serviceCharge.amount || 0);
538
697
  return new import_decimal.default(payment.amount || 0).times(percentage).plus(fixedAmount);
539
698
  }
699
+ formatSummaryMetadataMoney(value) {
700
+ if (value === void 0 || value === null || value === "")
701
+ return void 0;
702
+ if (value instanceof import_decimal.default)
703
+ return value.toFixed(2);
704
+ return new import_decimal.default(String(value || 0)).toFixed(2);
705
+ }
706
+ syncSummaryProductMetadata(products) {
707
+ const moneyKeys = [
708
+ "main_product_attached_bundle_surcharge_fee",
709
+ "main_product_attached_bundle_tax_fee",
710
+ "surcharge_rounding_remainder",
711
+ "tax_fee_rounding_remainder"
712
+ ];
713
+ for (const product of products || []) {
714
+ const productRecord = product;
715
+ const metadata = productRecord.metadata && typeof productRecord.metadata === "object" ? { ...productRecord.metadata } : {};
716
+ let hasMetadataPatch = false;
717
+ for (const key of moneyKeys) {
718
+ const value = this.formatSummaryMetadataMoney(productRecord[key]);
719
+ if (value === void 0)
720
+ continue;
721
+ metadata[key] = value;
722
+ hasMetadataPatch = true;
723
+ }
724
+ if (Array.isArray(productRecord.relation_surcharge_ids)) {
725
+ metadata.relation_surcharge_ids = productRecord.relation_surcharge_ids;
726
+ hasMetadataPatch = true;
727
+ }
728
+ if (hasMetadataPatch)
729
+ productRecord.metadata = metadata;
730
+ }
731
+ }
540
732
  getPaymentTargetAmount() {
541
733
  const tempOrder = this.ensureTempOrder();
542
734
  const summary = this.store.summary || (0, import_utils.createEmptySummary)();
@@ -559,17 +751,43 @@ var OrderModule = class extends import_BaseModule.BaseModule {
559
751
  return newOrder;
560
752
  }
561
753
  restoreOrder() {
754
+ var _a, _b;
562
755
  const freshTempOrder = this.createDefaultTempOrderInstance();
563
756
  this.ensureExternalSaleNumber(freshTempOrder);
564
757
  this.store.tempOrder = freshTempOrder;
565
758
  this.store.summary = (0, import_utils.createEmptySummary)();
566
759
  this.store.lastOrderInfo = void 0;
760
+ this.store.syncState = void 0;
761
+ void ((_a = this.store.discount) == null ? void 0 : _a.setOriginalDiscountList([]));
762
+ void ((_b = this.store.discount) == null ? void 0 : _b.setDiscountList([]));
567
763
  this.persistTempOrder();
568
764
  return freshTempOrder;
569
765
  }
570
766
  getTempOrder() {
571
767
  return this.store.tempOrder;
572
768
  }
769
+ async replaceTempOrder(tempOrder, options) {
770
+ if (!(0, import_utils.isTempOrder)(tempOrder)) {
771
+ throw new Error("无效的 tempOrder 数据");
772
+ }
773
+ const nextTempOrder = this.normalizeTempOrderForRuntime(
774
+ (0, import_lodash_es.cloneDeep)(tempOrder),
775
+ { ensureIdentity: false }
776
+ );
777
+ this.store.tempOrder = nextTempOrder;
778
+ if ((options == null ? void 0 : options.recalculateSummary) !== false) {
779
+ await this.recalculateSummary({ createIfMissing: false });
780
+ } else {
781
+ this.store.summary = (0, import_utils.createEmptySummary)();
782
+ }
783
+ if ((options == null ? void 0 : options.persist) !== false) {
784
+ this.persistTempOrder();
785
+ }
786
+ if (options == null ? void 0 : options.saveDraft) {
787
+ await this.saveDraft();
788
+ }
789
+ return nextTempOrder;
790
+ }
573
791
  getOrderIdentity() {
574
792
  var _a;
575
793
  const tempOrder = this.store.tempOrder;
@@ -718,6 +936,33 @@ var OrderModule = class extends import_BaseModule.BaseModule {
718
936
  }
719
937
  };
720
938
  }
939
+ getBookingUniqueIdentificationNumber(booking) {
940
+ var _a;
941
+ const uid = (_a = booking == null ? void 0 : booking.metadata) == null ? void 0 : _a.unique_identification_number;
942
+ if (uid === void 0 || uid === null || uid === "")
943
+ return null;
944
+ return String(uid);
945
+ }
946
+ findBookingIndexByUniqueIdentificationNumber(tempOrder, uniqueIdentificationNumber) {
947
+ return (tempOrder.bookings || []).findIndex((booking) => {
948
+ return this.getBookingUniqueIdentificationNumber(booking) === uniqueIdentificationNumber;
949
+ });
950
+ }
951
+ removeLinkedBookingsForProduct(tempOrder, product) {
952
+ var _a, _b;
953
+ const productUid = ((_a = product == null ? void 0 : product.metadata) == null ? void 0 : _a.unique_identification_number) || (product == null ? void 0 : product.unique_identification_number);
954
+ const bookingUid = (product == null ? void 0 : product.booking_uid) || ((_b = product == null ? void 0 : product.metadata) == null ? void 0 : _b.booking_uid);
955
+ if (!productUid && !bookingUid)
956
+ return;
957
+ tempOrder.bookings = (tempOrder.bookings || []).filter((booking) => {
958
+ const currentBookingUid = this.getBookingUniqueIdentificationNumber(booking);
959
+ if (bookingUid && currentBookingUid === String(bookingUid))
960
+ return false;
961
+ if (productUid && String((booking == null ? void 0 : booking.product_uid) || "") === String(productUid))
962
+ return false;
963
+ return true;
964
+ });
965
+ }
721
966
  // ─── TempOrder: 金额汇总 ───
722
967
  getSalesSummary() {
723
968
  if (!this.salesSummaryModuleName)
@@ -750,6 +995,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
750
995
  isPriceIncludeTax: tempOrder.is_price_include_tax,
751
996
  shopDiscount: tempOrder.shop_discount
752
997
  });
998
+ this.syncSummaryProductMetadata(tempOrder.products || []);
753
999
  const summary = {
754
1000
  ...salesSummaryResult,
755
1001
  total_refund_amount: existedSummary.total_refund_amount || "0.00",
@@ -777,13 +1023,22 @@ var OrderModule = class extends import_BaseModule.BaseModule {
777
1023
  this.persistTempOrder();
778
1024
  return summary;
779
1025
  }
780
- // ─── TempOrder: 备注 ───
781
- updateTempOrderNote(note) {
1026
+ updateTempOrderNote(note, sync = false) {
782
1027
  const tempOrder = this.ensureTempOrder();
783
1028
  tempOrder.note = String(note || "");
784
1029
  this.persistTempOrder();
1030
+ if (sync) {
1031
+ const orderId = tempOrder.order_id;
1032
+ if (!orderId)
1033
+ return tempOrder.note;
1034
+ return this.syncTempOrderNoteToRemote(orderId, tempOrder.note);
1035
+ }
785
1036
  return tempOrder.note;
786
1037
  }
1038
+ async syncTempOrderNoteToRemote(orderId, note) {
1039
+ await this.request.put(`/order/order/${orderId}/note`, { note });
1040
+ return note;
1041
+ }
787
1042
  getTempOrderNote() {
788
1043
  var _a;
789
1044
  return ((_a = this.store.tempOrder) == null ? void 0 : _a.note) || "";
@@ -807,10 +1062,29 @@ var OrderModule = class extends import_BaseModule.BaseModule {
807
1062
  this.persistTempOrder();
808
1063
  return tempOrder.contacts_info;
809
1064
  }
1065
+ buildTempOrderCustomer(params) {
1066
+ if (!params.customerId)
1067
+ return null;
1068
+ const source = params.source ? (0, import_lodash_es.cloneDeep)(params.source) : {};
1069
+ const name = source.name ?? source.display_name ?? source.customerName ?? source.customer_name ?? params.customerName;
1070
+ const customerName = source.customer_name ?? source.customerName ?? source.display_name ?? source.name ?? params.customerName;
1071
+ return {
1072
+ ...source,
1073
+ id: source.id ?? params.customerId,
1074
+ customer_id: source.customer_id ?? params.customerId,
1075
+ name: String(name || ""),
1076
+ customer_name: String(customerName || ""),
1077
+ country_calling_code: String(
1078
+ source.country_calling_code ?? source.countryCallingCode ?? params.countryCallingCode
1079
+ ),
1080
+ phone: String(source.phone ?? params.phone),
1081
+ email: String(source.email ?? params.email)
1082
+ };
1083
+ }
810
1084
  /**
811
1085
  * 更新当前 tempOrder 的客户协议字段。
812
1086
  *
813
- * PaymentModal 只需要修改订单里的客户字段;如果外部还有客户模块需要同步,
1087
+ * PaymentModal 只需要修改订单里的客户事实;如果外部还有客户模块需要同步,
814
1088
  * 应由调用方通过回调处理,不能把外部 UI 状态写进 tempOrder。
815
1089
  */
816
1090
  updateTempOrderCustomer(customer) {
@@ -824,6 +1098,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
824
1098
  );
825
1099
  tempOrder.phone = String(customer.phone || "");
826
1100
  tempOrder.email = String(customer.email || "");
1101
+ tempOrder.customer = this.buildTempOrderCustomer({
1102
+ source: customer,
1103
+ customerId: tempOrder.customer_id,
1104
+ customerName: tempOrder.customer_name,
1105
+ countryCallingCode: tempOrder.country_calling_code,
1106
+ phone: tempOrder.phone,
1107
+ email: tempOrder.email
1108
+ });
827
1109
  this.persistTempOrder();
828
1110
  return {
829
1111
  customerId: tempOrder.customer_id,
@@ -838,11 +1120,20 @@ var OrderModule = class extends import_BaseModule.BaseModule {
838
1120
  return;
839
1121
  }
840
1122
  const tempOrder = this.ensureTempOrder();
1123
+ const previousCustomerId = tempOrder.customer_id ?? null;
841
1124
  tempOrder.customer_id = patch.customer_id || patch.id || null;
842
1125
  tempOrder.customer_name = String(patch.customer_name || "");
843
1126
  tempOrder.country_calling_code = String(patch.country_calling_code || "");
844
1127
  tempOrder.phone = String(patch.phone || "");
845
1128
  tempOrder.email = String(patch.email || "");
1129
+ tempOrder.customer = this.buildTempOrderCustomer({
1130
+ source: snapshot || null,
1131
+ customerId: tempOrder.customer_id,
1132
+ customerName: tempOrder.customer_name,
1133
+ countryCallingCode: tempOrder.country_calling_code,
1134
+ phone: tempOrder.phone,
1135
+ email: tempOrder.email
1136
+ });
846
1137
  if (snapshot !== void 0) {
847
1138
  const extend = this.ensureExtend(tempOrder);
848
1139
  if (snapshot === null)
@@ -850,12 +1141,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
850
1141
  else
851
1142
  extend.customerSnapshot = (0, import_lodash_es.cloneDeep)(snapshot);
852
1143
  }
1144
+ const nextCustomerId = tempOrder.customer_id ?? null;
1145
+ if (String(previousCustomerId ?? "") !== String(nextCustomerId ?? "")) {
1146
+ (0, import_utils.clearTempOrderHolderAssignments)(tempOrder);
1147
+ }
853
1148
  this.persistTempOrder();
1149
+ this.saveDraftInBackground();
854
1150
  this.emitOrderCustomerChange();
855
1151
  }
856
1152
  getOrderCustomer() {
857
1153
  const tempOrder = this.store.tempOrder;
858
- if (!tempOrder || tempOrder.customer_id === null || tempOrder.customer_id === void 0) {
1154
+ if (!tempOrder || !tempOrder.customer_id) {
859
1155
  return null;
860
1156
  }
861
1157
  return {
@@ -873,14 +1169,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
873
1169
  }
874
1170
  clearOrderCustomer() {
875
1171
  const tempOrder = this.ensureTempOrder();
1172
+ (0, import_utils.clearTempOrderHolderAssignments)(tempOrder);
876
1173
  tempOrder.customer_id = null;
877
1174
  tempOrder.customer_name = "";
878
1175
  tempOrder.country_calling_code = "";
879
1176
  tempOrder.phone = "";
880
1177
  tempOrder.email = "";
1178
+ tempOrder.customer = null;
881
1179
  if (tempOrder._extend)
882
1180
  delete tempOrder._extend.customerSnapshot;
883
1181
  this.persistTempOrder();
1182
+ this.saveDraftInBackground();
884
1183
  this.core.effects.emit(import_types.OrderHooks.OnOrderCustomerChange, null);
885
1184
  }
886
1185
  emitOrderCustomerChange() {
@@ -920,10 +1219,23 @@ var OrderModule = class extends import_BaseModule.BaseModule {
920
1219
  }
921
1220
  /** 追加单个支付项,并立即持久化当前 tempOrder。 */
922
1221
  addOrderPayment(payment) {
1222
+ this.logInfo("addOrderPayment", {
1223
+ payment
1224
+ });
923
1225
  const tempOrder = this.ensureTempOrder();
924
- const mapped = (0, import_utils.mapPaymentItemsToOrderPayments)([payment]);
1226
+ const paymentRecord = payment;
1227
+ const mapped = (0, import_utils.mapPaymentItemsToOrderPayments)([{
1228
+ ...paymentRecord,
1229
+ status: paymentRecord.status ?? "paid",
1230
+ origin_amount: paymentRecord.origin_amount ?? paymentRecord.amount ?? "0.00"
1231
+ }]);
925
1232
  tempOrder.payments = [...tempOrder.payments || [], ...mapped];
926
1233
  this.persistTempOrder();
1234
+ void this.recalculateSummary({ createIfMissing: true }).catch((error) => {
1235
+ this.logError("recalculate summary after addOrderPayment failed", {
1236
+ error: error instanceof Error ? error.message : String(error)
1237
+ });
1238
+ });
927
1239
  return tempOrder.payments;
928
1240
  }
929
1241
  /**
@@ -1026,6 +1338,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1026
1338
  productToAdd.product_bundle
1027
1339
  );
1028
1340
  const normalizedIncoming = (0, import_utils3.normalizeOrderProduct)(productToAdd);
1341
+ normalizedIncoming.discount_list = (0, import_utils.normalizeOrderProductDiscountList)(
1342
+ normalizedIncoming.discount_list
1343
+ );
1029
1344
  const hasIncomingGoodPass = this.hasGoodPassDiscount(
1030
1345
  normalizedIncoming
1031
1346
  );
@@ -1082,6 +1397,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1082
1397
  unique_identification_number: targetUid
1083
1398
  }
1084
1399
  });
1400
+ normalizedProduct.discount_list = (0, import_utils.normalizeOrderProductDiscountList)(
1401
+ normalizedProduct.discount_list
1402
+ );
1085
1403
  tempOrder.products[matchedIndex] = {
1086
1404
  ...targetProduct,
1087
1405
  ...normalizedProduct,
@@ -1104,6 +1422,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1104
1422
  if (linked) {
1105
1423
  tempOrder.bookings = [...tempOrder.bookings || [], linked.booking];
1106
1424
  }
1425
+ await this.applyPromotion();
1107
1426
  this.applyDiscount();
1108
1427
  await this.recalculateSummary({ createIfMissing: true });
1109
1428
  this.persistTempOrder();
@@ -1127,15 +1446,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1127
1446
  (item) => (item == null ? void 0 : item.type) === "good_pass" || (item == null ? void 0 : item.tag) === "good_pass"
1128
1447
  );
1129
1448
  }
1130
- async updateProductInOrder(params) {
1131
- var _a, _b, _c, _d, _e;
1449
+ async updateOrderProduct(params) {
1450
+ var _a, _b, _c, _d, _e, _f, _g;
1132
1451
  const {
1133
1452
  product_id,
1134
1453
  product_variant_id,
1135
1454
  updates,
1136
1455
  unique_identification_number,
1456
+ identity_key,
1137
1457
  product_option_item,
1138
- product_bundle
1458
+ product_bundle,
1459
+ booking
1139
1460
  } = params;
1140
1461
  const tempOrder = this.ensureTempOrder();
1141
1462
  const identityLookup = {
@@ -1144,12 +1465,30 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1144
1465
  };
1145
1466
  if (unique_identification_number !== void 0) {
1146
1467
  identityLookup.unique_identification_number = unique_identification_number;
1468
+ if (identity_key === void 0) {
1469
+ identityLookup.identity_key = unique_identification_number;
1470
+ }
1471
+ }
1472
+ if (identity_key !== void 0) {
1473
+ identityLookup.identity_key = identity_key;
1147
1474
  }
1148
1475
  if (product_option_item !== void 0)
1149
1476
  identityLookup.product_option_item = product_option_item;
1150
1477
  if (product_bundle !== void 0)
1151
1478
  identityLookup.product_bundle = product_bundle;
1152
- const productIndex = (0, import_utils3.getProductIdentityIndex)(tempOrder.products, identityLookup);
1479
+ let productIndex = -1;
1480
+ if (unique_identification_number !== void 0) {
1481
+ const targetUid = String(unique_identification_number);
1482
+ productIndex = tempOrder.products.findIndex((item) => {
1483
+ var _a2;
1484
+ return String(
1485
+ ((_a2 = item.metadata) == null ? void 0 : _a2.unique_identification_number) || item.unique_identification_number || ""
1486
+ ) === targetUid;
1487
+ });
1488
+ }
1489
+ if (productIndex === -1) {
1490
+ productIndex = (0, import_utils3.getProductIdentityIndex)(tempOrder.products, identityLookup);
1491
+ }
1153
1492
  if (productIndex === -1) {
1154
1493
  throw new Error("[Order] 目标商品不存在,无法更新");
1155
1494
  }
@@ -1176,28 +1515,178 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1176
1515
  delete nextProduct.metadata.main_product_original_price;
1177
1516
  delete nextProduct.metadata.price_schema_version;
1178
1517
  }
1179
- tempOrder.products[productIndex] = (0, import_utils3.normalizeOrderProduct)(nextProduct);
1518
+ let normalizedProduct = (0, import_utils3.normalizeOrderProduct)(nextProduct);
1519
+ normalizedProduct.discount_list = (0, import_utils.normalizeOrderProductDiscountList)(
1520
+ normalizedProduct.discount_list
1521
+ );
1522
+ if (booking) {
1523
+ const productUid = String(
1524
+ ((_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)()
1525
+ );
1526
+ normalizedProduct.metadata = {
1527
+ ...normalizedProduct.metadata || {},
1528
+ unique_identification_number: productUid
1529
+ };
1530
+ const linked = this.createLinkedProductAndBooking({
1531
+ product: {
1532
+ ...normalizedProduct,
1533
+ unique_identification_number: productUid
1534
+ },
1535
+ booking
1536
+ });
1537
+ this.removeLinkedBookingsForProduct(tempOrder, targetProduct);
1538
+ normalizedProduct = {
1539
+ ...normalizedProduct,
1540
+ booking_uid: linked.bookingUid,
1541
+ metadata: {
1542
+ ...normalizedProduct.metadata || {},
1543
+ booking_uid: linked.bookingUid,
1544
+ unique_identification_number: productUid
1545
+ }
1546
+ };
1547
+ tempOrder.bookings = [...tempOrder.bookings || [], linked.booking];
1548
+ }
1549
+ tempOrder.products[productIndex] = normalizedProduct;
1180
1550
  this.captureProductRuntime(
1181
1551
  tempOrder,
1182
1552
  updates,
1183
1553
  tempOrder.products[productIndex],
1184
- (_e = tempOrder.products[productIndex].metadata) == null ? void 0 : _e.unique_identification_number
1554
+ (_g = tempOrder.products[productIndex].metadata) == null ? void 0 : _g.unique_identification_number
1185
1555
  );
1556
+ await this.applyPromotion();
1186
1557
  this.applyDiscount();
1187
1558
  await this.recalculateSummary({ createIfMissing: true });
1188
1559
  this.persistTempOrder();
1189
1560
  return tempOrder.products;
1190
1561
  }
1191
- async removeProductFromOrder(identity) {
1562
+ async updateOrderProductQuantity(params) {
1563
+ var _a;
1564
+ const {
1565
+ product_id,
1566
+ product_variant_id,
1567
+ num,
1568
+ unique_identification_number,
1569
+ identity_key,
1570
+ product_option_item,
1571
+ product_bundle
1572
+ } = params;
1192
1573
  const tempOrder = this.ensureTempOrder();
1193
- tempOrder.products = tempOrder.products.filter(
1194
- (item) => !(0, import_utils3.isIdentityMatch)(item, identity)
1574
+ const identityLookup = {
1575
+ product_id,
1576
+ product_variant_id
1577
+ };
1578
+ if (unique_identification_number !== void 0) {
1579
+ identityLookup.unique_identification_number = unique_identification_number;
1580
+ if (identity_key === void 0) {
1581
+ identityLookup.identity_key = unique_identification_number;
1582
+ }
1583
+ }
1584
+ if (identity_key !== void 0)
1585
+ identityLookup.identity_key = identity_key;
1586
+ if (product_option_item !== void 0)
1587
+ identityLookup.product_option_item = product_option_item;
1588
+ if (product_bundle !== void 0)
1589
+ identityLookup.product_bundle = product_bundle;
1590
+ let productIndex = -1;
1591
+ if (unique_identification_number !== void 0) {
1592
+ const targetUid = String(unique_identification_number);
1593
+ productIndex = tempOrder.products.findIndex((item) => {
1594
+ var _a2;
1595
+ return String(
1596
+ ((_a2 = item.metadata) == null ? void 0 : _a2.unique_identification_number) || item.unique_identification_number || ""
1597
+ ) === targetUid;
1598
+ });
1599
+ }
1600
+ if (productIndex === -1) {
1601
+ productIndex = (0, import_utils3.getProductIdentityIndex)(tempOrder.products, identityLookup);
1602
+ }
1603
+ if (productIndex === -1) {
1604
+ throw new Error("[Order] 目标商品不存在,无法更新数量");
1605
+ }
1606
+ const targetProduct = tempOrder.products[productIndex];
1607
+ const normalizedProduct = (0, import_utils3.normalizeOrderProduct)({
1608
+ ...targetProduct,
1609
+ num: (0, import_utils3.getSafeProductNum)(num),
1610
+ metadata: {
1611
+ ...targetProduct.metadata || {},
1612
+ unique_identification_number: ((_a = targetProduct.metadata) == null ? void 0 : _a.unique_identification_number) || unique_identification_number
1613
+ }
1614
+ });
1615
+ normalizedProduct.discount_list = (0, import_utils.normalizeOrderProductDiscountList)(
1616
+ normalizedProduct.discount_list
1195
1617
  );
1618
+ tempOrder.products[productIndex] = normalizedProduct;
1619
+ await this.applyPromotion();
1196
1620
  this.applyDiscount();
1197
1621
  await this.recalculateSummary({ createIfMissing: true });
1198
1622
  this.persistTempOrder();
1199
1623
  return tempOrder.products;
1200
1624
  }
1625
+ updateOrderBooking(params) {
1626
+ const { unique_identification_number, updates } = params;
1627
+ const tempOrder = this.ensureTempOrder();
1628
+ const bookingUid = String(unique_identification_number || "");
1629
+ const bookingIndex = this.findBookingIndexByUniqueIdentificationNumber(
1630
+ tempOrder,
1631
+ bookingUid
1632
+ );
1633
+ if (!bookingUid || bookingIndex === -1) {
1634
+ throw new Error("[Order] 目标 booking 不存在,无法更新");
1635
+ }
1636
+ const targetBooking = tempOrder.bookings[bookingIndex] || {};
1637
+ const nextBooking = {
1638
+ ...targetBooking,
1639
+ ...updates,
1640
+ metadata: {
1641
+ ...targetBooking.metadata || {},
1642
+ ...updates.metadata || {},
1643
+ unique_identification_number: bookingUid
1644
+ }
1645
+ };
1646
+ nextBooking.is_all = (0, import_utils.normalizeBookingIsAll)(nextBooking);
1647
+ nextBooking.sub_type = (0, import_utils.normalizeBookingSubType)(nextBooking);
1648
+ tempOrder.bookings[bookingIndex] = nextBooking;
1649
+ this.persistTempOrder();
1650
+ return tempOrder.bookings;
1651
+ }
1652
+ async removeProductFromOrder(identity) {
1653
+ const tempOrder = this.ensureTempOrder();
1654
+ const removedProducts = [];
1655
+ tempOrder.products = tempOrder.products.filter((item) => {
1656
+ const shouldRemove = (0, import_utils3.isIdentityMatch)(item, identity);
1657
+ if (shouldRemove)
1658
+ removedProducts.push(item);
1659
+ return !shouldRemove;
1660
+ });
1661
+ for (const product of removedProducts) {
1662
+ this.removeLinkedBookingsForProduct(tempOrder, product);
1663
+ }
1664
+ await this.applyPromotion();
1665
+ this.applyDiscount();
1666
+ await this.recalculateSummary({ createIfMissing: true });
1667
+ this.persistTempOrder();
1668
+ return tempOrder.products;
1669
+ }
1670
+ /**
1671
+ * 仅清空购物车行(products / bookings),不重置整单。
1672
+ * 用于「仅清空商品」;客户、备注、支付等协议字段保持不变。
1673
+ */
1674
+ async clearOrderCartLines() {
1675
+ const tempOrder = this.ensureTempOrder();
1676
+ tempOrder.products = [];
1677
+ tempOrder.bookings = [];
1678
+ if (tempOrder._extend && typeof tempOrder._extend === "object") {
1679
+ tempOrder._extend = {
1680
+ ...tempOrder._extend,
1681
+ productsByUid: {}
1682
+ };
1683
+ }
1684
+ await this.applyPromotion();
1685
+ this.applyDiscount();
1686
+ await this.recalculateSummary({ createIfMissing: true });
1687
+ this.persistTempOrder();
1688
+ return tempOrder;
1689
+ }
1201
1690
  // ─── TempOrder: 提交 ───
1202
1691
  /**
1203
1692
  * 提交当前 Sales tempOrder。
@@ -1224,7 +1713,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1224
1713
  ...hasPaymentStatusOverride ? { payment_status: params == null ? void 0 : params.paymentStatus } : {},
1225
1714
  ...hasSmallTicketDataFlagOverride ? { small_ticket_data_flag: params == null ? void 0 : params.smallTicketDataFlag } : {}
1226
1715
  };
1227
- return (params == null ? void 0 : params.enhancePayload) ? params.enhancePayload(nextPayload, ctx) : nextPayload;
1716
+ const enhancedPayload = (params == null ? void 0 : params.enhancePayload) ? params.enhancePayload(nextPayload, ctx) : nextPayload;
1717
+ return enhancedPayload;
1228
1718
  } : void 0;
1229
1719
  const payload = (0, import_utils.buildSubmitPayload)({
1230
1720
  tempOrder,
@@ -1241,6 +1731,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1241
1731
  this.logInfo("submitTempOrder calling sales checkout", {
1242
1732
  orderId: payload.order_id,
1243
1733
  externalSaleNumber: payload.external_sale_number,
1734
+ requestUniqueIdempotencyToken: payload.request_unique_idempotency_token,
1244
1735
  paymentStatus: payload.payment_status,
1245
1736
  paymentsCount: ((_b = payload.payments) == null ? void 0 : _b.length) || 0,
1246
1737
  smallTicketDataFlag: payload.small_ticket_data_flag
@@ -1327,21 +1818,37 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1327
1818
  const mappedPayments = (0, import_utils.mapPaymentItemsToOrderPayments)(
1328
1819
  params.payments || []
1329
1820
  );
1330
- tempOrder.payments = mappedPayments;
1331
- if (params.paymentStatus)
1332
- tempOrder.payment_status = params.paymentStatus;
1333
- this.persistTempOrder();
1334
- await this.saveDraft();
1335
1821
  const paymentTotal = this.calculatePaymentTotal(mappedPayments);
1336
1822
  const targetAmount = this.getPaymentTargetAmount();
1337
1823
  const isFullyPaid = targetAmount.lte(0) ? false : paymentTotal.gte(targetAmount);
1338
- if (!isFullyPaid || !params.submitWhenPaid) {
1824
+ const paymentStatus = isFullyPaid ? params.paymentStatus || "paid" : "partially_paid";
1825
+ tempOrder.payments = mappedPayments;
1826
+ tempOrder.payment_status = paymentStatus;
1827
+ this.persistTempOrder();
1828
+ await this.saveDraft();
1829
+ if (!params.submitWhenPaid) {
1830
+ return { isFullyPaid };
1831
+ }
1832
+ if (this.store.syncState === "submitting") {
1339
1833
  return { isFullyPaid };
1340
1834
  }
1835
+ this.store.syncState = "submitting";
1836
+ const paymentSyncIdempotencyToken = this.buildPaymentSyncIdempotencyToken(
1837
+ tempOrder,
1838
+ mappedPayments
1839
+ );
1341
1840
  const submitResult = await this.submitTempOrder({
1342
1841
  payments: mappedPayments,
1343
- paymentStatus: params.paymentStatus || "paid",
1344
- smallTicketDataFlag: params.smallTicketDataFlag
1842
+ paymentStatus,
1843
+ smallTicketDataFlag: params.smallTicketDataFlag,
1844
+ channel: params.channel,
1845
+ enhancePayload: (payload) => {
1846
+ const nextPayload = {
1847
+ ...payload,
1848
+ request_unique_idempotency_token: paymentSyncIdempotencyToken
1849
+ };
1850
+ return nextPayload;
1851
+ }
1345
1852
  });
1346
1853
  return { isFullyPaid, submitResult };
1347
1854
  }
@@ -1641,53 +2148,36 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1641
2148
  */
1642
2149
  async hydrateTempOrderFromRecord(record, options) {
1643
2150
  const raw = record && typeof record === "object" && record.data && record.order_id == null ? record.data : record || {};
1644
- const defaults = this.createDefaultTempOrderInstance();
2151
+ const nextTempOrder = this.normalizeTempOrderForRuntime(raw, {
2152
+ ensureIdentity: true
2153
+ });
2154
+ this.store.tempOrder = nextTempOrder;
2155
+ this.store.summary = raw.summary && typeof raw.summary === "object" ? { ...(0, import_utils.createEmptySummary)(), ...raw.summary } : (0, import_utils.createEmptySummary)();
2156
+ this.store.lastOrderInfo = raw.lastOrderInfo || raw;
2157
+ if (options == null ? void 0 : options.recalcOnHydrate) {
2158
+ await this.recalculateSummary({ createIfMissing: false });
2159
+ }
2160
+ this.persistTempOrder();
2161
+ return nextTempOrder;
2162
+ }
2163
+ normalizeTempOrderForRuntime(raw, options) {
1645
2164
  const nextTempOrder = {
1646
- ...defaults
2165
+ ...this.createDefaultTempOrderInstance(),
2166
+ ...raw || {}
1647
2167
  };
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
- });
2168
+ delete nextTempOrder.summary;
2169
+ delete nextTempOrder.lastOrderInfo;
2170
+ delete nextTempOrder.discount_list;
2171
+ delete nextTempOrder.shop_id;
2172
+ delete nextTempOrder.create_date;
2173
+ delete nextTempOrder.total_amount;
2174
+ delete nextTempOrder.expect_amount;
2175
+ delete nextTempOrder.paid_amount;
2176
+ delete nextTempOrder.shop_note;
2177
+ delete nextTempOrder.start_time;
2178
+ delete nextTempOrder.tax_fee;
2179
+ delete nextTempOrder.total_refund_amount;
2180
+ delete nextTempOrder.holder_id;
1691
2181
  nextTempOrder.metadata = raw.metadata && typeof raw.metadata === "object" ? { ...raw.metadata } : {};
1692
2182
  nextTempOrder.holder = raw.holder && typeof raw.holder === "object" ? { ...raw.holder } : null;
1693
2183
  nextTempOrder.products = Array.isArray(raw.products) ? raw.products.map((p) => this.normalizeHydratedOrderProduct(p)) : [];
@@ -1698,7 +2188,6 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1698
2188
  })) : [];
1699
2189
  nextTempOrder.payments = Array.isArray(raw.payments) ? raw.payments.map((p) => ({ ...p || {} })) : [];
1700
2190
  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
2191
  nextTempOrder.relation_forms = Array.isArray(raw.relation_forms) ? raw.relation_forms.map((f) => ({ ...f || {} })) : [];
1703
2192
  nextTempOrder.contacts = Array.isArray(raw.contacts) ? raw.contacts.map((c) => ({ ...c || {} })) : [];
1704
2193
  nextTempOrder.contacts_info = raw.contacts_info && typeof raw.contacts_info === "object" && !Array.isArray(raw.contacts_info) ? { ...raw.contacts_info } : null;
@@ -1706,14 +2195,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1706
2195
  ...raw._extend,
1707
2196
  productsByUid: raw._extend.productsByUid || {}
1708
2197
  } : { 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 });
2198
+ if ((options == null ? void 0 : options.ensureIdentity) !== false) {
2199
+ this.ensureExternalSaleNumber(nextTempOrder);
1715
2200
  }
1716
- this.persistTempOrder();
1717
2201
  return nextTempOrder;
1718
2202
  }
1719
2203
  normalizeHydratedOrderProduct(product) {
@@ -1732,6 +2216,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1732
2216
  row.metadata.unique_identification_number = (0, import_utils.createUuidV4)();
1733
2217
  }
1734
2218
  const normalized = (0, import_utils3.normalizeOrderProduct)(row);
2219
+ normalized.discount_list = (0, import_utils.normalizeOrderProductDiscountList)(
2220
+ normalized.discount_list
2221
+ );
1735
2222
  const result = {
1736
2223
  ...row,
1737
2224
  ...normalized,