@pisell/pisellos 2.2.181 → 2.2.183

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 (173) 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 +276 -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.js +4 -3
  35. package/dist/modules/Discount/types.d.ts +1 -0
  36. package/dist/modules/Order/index.d.ts +100 -9
  37. package/dist/modules/Order/index.js +1447 -499
  38. package/dist/modules/Order/types.d.ts +201 -18
  39. package/dist/modules/Order/types.js +31 -0
  40. package/dist/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  41. package/dist/modules/Order/utils/manualProductDiscount.js +210 -0
  42. package/dist/modules/Order/utils.d.ts +83 -1
  43. package/dist/modules/Order/utils.js +405 -61
  44. package/dist/modules/Product/index.d.ts +1 -1
  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 +43 -39
  49. package/dist/modules/Rules/types.d.ts +1 -0
  50. package/dist/modules/SalesSummary/index.js +4 -2
  51. package/dist/modules/SalesSummary/utils.js +21 -7
  52. package/dist/modules/Schedule/index.js +6 -2
  53. package/dist/modules/index.d.ts +1 -0
  54. package/dist/modules/index.js +2 -1
  55. package/dist/server/index.js +87 -29
  56. package/dist/server/modules/order/index.d.ts +23 -0
  57. package/dist/server/modules/order/index.js +123 -46
  58. package/dist/server/modules/order/utils/filterOrders.js +20 -6
  59. package/dist/server/modules/products/index.d.ts +1 -1
  60. package/dist/server/modules/products/index.js +130 -113
  61. package/dist/server/modules/schedule/index.js +13 -6
  62. package/dist/solution/BaseSales/index.d.ts +89 -7
  63. package/dist/solution/BaseSales/index.js +1504 -380
  64. package/dist/solution/BaseSales/types.d.ts +67 -1
  65. package/dist/solution/BaseSales/types.js +3 -1
  66. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +275 -0
  67. package/dist/solution/BaseSales/utils/cartPromotion.js +1258 -0
  68. package/dist/solution/BaseSales/utils/quotationPrice.d.ts +7 -0
  69. package/dist/solution/BaseSales/utils/quotationPrice.js +237 -0
  70. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +68 -16
  71. package/dist/solution/BaseSales/utils.js +46 -8
  72. package/dist/solution/BookingByStep/index.d.ts +1 -1
  73. package/dist/solution/BookingTicket/index.d.ts +151 -1
  74. package/dist/solution/BookingTicket/index.js +848 -184
  75. package/dist/solution/BookingTicket/types.d.ts +2 -0
  76. package/dist/solution/BookingTicket/types.js +3 -0
  77. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +113 -0
  78. package/dist/solution/BookingTicket/utils/addProductDecision.js +346 -0
  79. package/dist/solution/BookingTicket/utils/cartView.js +4 -2
  80. package/dist/solution/ScanOrder/index.d.ts +9 -3
  81. package/dist/solution/ScanOrder/index.js +231 -128
  82. package/dist/solution/ScanOrder/utils.js +46 -8
  83. package/dist/solution/VenueBooking/index.d.ts +5 -2
  84. package/dist/solution/VenueBooking/index.js +103 -55
  85. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +4 -0
  86. package/dist/solution/VenueBooking/utils/slotMerge.js +10 -0
  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 +271 -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.js +5 -1
  121. package/lib/modules/Discount/types.d.ts +1 -0
  122. package/lib/modules/Order/index.d.ts +100 -9
  123. package/lib/modules/Order/index.js +751 -160
  124. package/lib/modules/Order/types.d.ts +201 -18
  125. package/lib/modules/Order/types.js +1 -0
  126. package/lib/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  127. package/lib/modules/Order/utils/manualProductDiscount.js +206 -0
  128. package/lib/modules/Order/utils.d.ts +83 -1
  129. package/lib/modules/Order/utils.js +340 -22
  130. package/lib/modules/Product/index.d.ts +1 -1
  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 +26 -27
  135. package/lib/modules/Rules/types.d.ts +1 -0
  136. package/lib/modules/SalesSummary/index.js +4 -2
  137. package/lib/modules/SalesSummary/utils.js +21 -7
  138. package/lib/modules/Schedule/index.js +3 -1
  139. package/lib/modules/index.d.ts +1 -0
  140. package/lib/modules/index.js +3 -1
  141. package/lib/server/index.js +41 -4
  142. package/lib/server/modules/order/index.d.ts +23 -0
  143. package/lib/server/modules/order/index.js +46 -14
  144. package/lib/server/modules/order/utils/filterOrders.js +12 -4
  145. package/lib/server/modules/products/index.d.ts +1 -1
  146. package/lib/server/modules/products/index.js +30 -20
  147. package/lib/server/modules/schedule/index.js +2 -1
  148. package/lib/solution/BaseSales/index.d.ts +89 -7
  149. package/lib/solution/BaseSales/index.js +743 -23
  150. package/lib/solution/BaseSales/types.d.ts +67 -1
  151. package/lib/solution/BaseSales/types.js +3 -1
  152. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +275 -0
  153. package/lib/solution/BaseSales/utils/cartPromotion.js +836 -0
  154. package/lib/solution/BaseSales/utils/quotationPrice.d.ts +7 -0
  155. package/lib/solution/BaseSales/utils/quotationPrice.js +277 -0
  156. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +71 -12
  157. package/lib/solution/BaseSales/utils.js +46 -6
  158. package/lib/solution/BookingByStep/index.d.ts +1 -1
  159. package/lib/solution/BookingTicket/index.d.ts +151 -1
  160. package/lib/solution/BookingTicket/index.js +389 -9
  161. package/lib/solution/BookingTicket/types.d.ts +2 -0
  162. package/lib/solution/BookingTicket/types.js +6 -0
  163. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +113 -0
  164. package/lib/solution/BookingTicket/utils/addProductDecision.js +318 -0
  165. package/lib/solution/BookingTicket/utils/cartView.js +4 -2
  166. package/lib/solution/ScanOrder/index.d.ts +9 -3
  167. package/lib/solution/ScanOrder/index.js +147 -66
  168. package/lib/solution/ScanOrder/utils.js +46 -6
  169. package/lib/solution/VenueBooking/index.d.ts +5 -2
  170. package/lib/solution/VenueBooking/index.js +50 -14
  171. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +4 -0
  172. package/lib/solution/VenueBooking/utils/slotMerge.js +10 -0
  173. package/package.json +1 -1
@@ -35,12 +35,16 @@ __export(BookingTicket_exports, {
35
35
  });
36
36
  module.exports = __toCommonJS(BookingTicket_exports);
37
37
  var import_types = require("./types");
38
+ var import_BookingContext = require("../../modules/BookingContext");
38
39
  var import_BaseSales = require("../BaseSales");
39
40
  var import_scan = __toESM(require("./utils/scan"));
40
41
  var import_orderCustomer = require("./utils/orderCustomer");
41
42
  var import_handleScan = require("./utils/scan/handleScan");
42
43
  var import_scanCache = __toESM(require("./utils/scan/scanCache"));
43
44
  var import_cartView = require("./utils/cartView");
45
+ var import_addProductDecision = require("./utils/addProductDecision");
46
+ var import_buildCacheItemFromOrderLine = require("../../modules/BookingContext/utils/buildCacheItemFromOrderLine");
47
+ var import_buildNormalProductCacheItemFromOrderLine = require("../../modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine");
44
48
  var import_bookingStatus = require("./utils/bookingStatus");
45
49
  __reExport(BookingTicket_exports, require("./types"), module.exports);
46
50
  var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
@@ -54,12 +58,18 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
54
58
  */
55
59
  this.store = {};
56
60
  this.productCatalog = [];
61
+ this.orderCustomerDiscountRefreshInFlight = null;
62
+ this.orderCustomerDiscountRefreshCustomerId = null;
57
63
  }
58
64
  /**
59
- * 在 BaseSales 默认模块清单(products/order/salesSummary/schedule)基础上追加 customer。
65
+ * 在 BaseSales 默认模块清单(products/order/salesSummary/schedule)基础上追加 customer + bookingContext
66
+ * bookingContext 是 SalesSdk 加车下沉所需的「预约 UI 上下文」(bookingConfig / resourcesOrigin / date)。
60
67
  */
61
68
  getRegisteredModuleNames() {
62
- return [...super.getRegisteredModuleNames(), "customer"];
69
+ return [...super.getRegisteredModuleNames(), "customer", "bookingContext"];
70
+ }
71
+ getSubmitOrderSalesChannel() {
72
+ return "pos";
63
73
  }
64
74
  /**
65
75
  * 业务侧子模块工厂:BookingTicket 自带的 createBookingTicketModule 优先匹配;
@@ -93,25 +103,112 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
93
103
  */
94
104
  async loadSalesDetail(orderIdOrParams) {
95
105
  const sales = await super.loadSalesDetail(orderIdOrParams);
96
- console.log(sales, "1234sales");
97
106
  const customerModule = this.store.customer;
98
107
  const detailCustomer = sales == null ? void 0 : sales.customer;
99
- if (customerModule && detailCustomer) {
100
- const { id: detailCustomerId, ...detailCustomerRest } = detailCustomer ?? {};
108
+ const orderCustomerSnapshot = this.store.order.getOrderCustomerSnapshot();
109
+ const protocolCustomerId = sales == null ? void 0 : sales.customer_id;
110
+ const protocolCustomer = protocolCustomerId !== void 0 && protocolCustomerId !== null ? {
111
+ id: protocolCustomerId,
112
+ name: String((sales == null ? void 0 : sales.customer_name) || ""),
113
+ phone: sales == null ? void 0 : sales.phone,
114
+ email: sales == null ? void 0 : sales.email,
115
+ country_calling_code: sales == null ? void 0 : sales.country_calling_code
116
+ } : null;
117
+ const selectedCustomer = detailCustomer ?? orderCustomerSnapshot ?? protocolCustomer;
118
+ if (customerModule && selectedCustomer) {
119
+ const { id: detailCustomerId, ...detailCustomerRest } = selectedCustomer ?? {};
101
120
  const rawId = detailCustomerId ?? "";
102
121
  const customer = {
103
122
  ...detailCustomerRest,
104
123
  id: rawId == null ? "" : rawId,
105
- name: (detailCustomer == null ? void 0 : detailCustomer.name) ?? "",
106
- phone: detailCustomer == null ? void 0 : detailCustomer.phone,
107
- email: detailCustomer == null ? void 0 : detailCustomer.email,
108
- country_calling_code: detailCustomer == null ? void 0 : detailCustomer.country_calling_code
124
+ name: (selectedCustomer == null ? void 0 : selectedCustomer.name) ?? "",
125
+ phone: selectedCustomer == null ? void 0 : selectedCustomer.phone,
126
+ email: selectedCustomer == null ? void 0 : selectedCustomer.email,
127
+ country_calling_code: selectedCustomer == null ? void 0 : selectedCustomer.country_calling_code
109
128
  };
110
129
  customerModule.setSelectedCustomer(customer);
111
130
  this.setOrderCustomer(customer);
131
+ await this.store.order.saveDraft();
112
132
  }
113
133
  return sales;
114
134
  }
135
+ async scanPromotionCode(code, customerId) {
136
+ if (!this.store.order)
137
+ throw new Error("order 模块未初始化");
138
+ const raw = await this.store.order.scanCode(code, customerId);
139
+ if (raw.isAvailable) {
140
+ await this.hydrateOrderCustomerFromScanDiscounts(raw.scannedDiscountList || []);
141
+ await this.store.order.recalculateSummary({ createIfMissing: true });
142
+ this.store.order.persistTempOrder();
143
+ }
144
+ return {
145
+ isAvailable: raw.isAvailable,
146
+ type: raw.type,
147
+ unavailableReason: raw.unavailableReason
148
+ };
149
+ }
150
+ async hydrateOrderCustomerFromScanDiscounts(discounts) {
151
+ const currentOrderCustomer = this.store.order.getOrderCustomer();
152
+ if (currentOrderCustomer)
153
+ return;
154
+ const customerId = this.getDiscountCustomerId(discounts);
155
+ if (!customerId)
156
+ return;
157
+ const customer = await this.resolveCustomerByDiscountCustomerId(customerId);
158
+ if (!customer)
159
+ return;
160
+ this.store.customer.setSelectedCustomer(customer);
161
+ this.setOrderCustomer(customer);
162
+ }
163
+ getDiscountCustomerId(discounts) {
164
+ const discount = discounts.find((item) => item.customer_id != null);
165
+ const customerId = Number(discount == null ? void 0 : discount.customer_id);
166
+ if (!Number.isFinite(customerId) || customerId <= 0)
167
+ return null;
168
+ return customerId;
169
+ }
170
+ async resolveCustomerByDiscountCustomerId(customerId) {
171
+ var _a;
172
+ const localCustomers = this.store.customer.getCustomers();
173
+ const localCustomer = this.findCustomerById(localCustomers, customerId);
174
+ const normalizedLocalCustomer = localCustomer ? this.normalizeCustomer(localCustomer) : null;
175
+ if (normalizedLocalCustomer && this.hasUsableCustomerDetails(normalizedLocalCustomer)) {
176
+ return normalizedLocalCustomer;
177
+ }
178
+ const result = await this.store.customer.queryCustomerList({
179
+ ids: [customerId],
180
+ skip: 1,
181
+ num: 1
182
+ });
183
+ const remoteCustomer = this.findCustomerById((result == null ? void 0 : result.list) || [], customerId) || ((_a = result == null ? void 0 : result.list) == null ? void 0 : _a[0]) || null;
184
+ return remoteCustomer ? this.normalizeCustomer(remoteCustomer) : null;
185
+ }
186
+ findCustomerById(customers = [], customerId) {
187
+ return customers.find((customer) => {
188
+ const rawId = this.getRawCustomerId(customer);
189
+ return rawId !== null && String(rawId) === String(customerId);
190
+ }) || null;
191
+ }
192
+ normalizeCustomer(customer) {
193
+ const raw = customer;
194
+ const rawId = this.getRawCustomerId(customer);
195
+ const name = customer.name || raw.display_name || raw.nickname || raw.customer_name || [raw.first_name, raw.last_name].filter(Boolean).join(" ") || "";
196
+ return {
197
+ ...customer,
198
+ id: rawId ?? customer.id,
199
+ name,
200
+ phone: customer.phone ?? raw.phone ?? raw.mobile ?? "",
201
+ email: customer.email ?? raw.email ?? "",
202
+ country_calling_code: customer.country_calling_code ?? raw.country_calling_code ?? ""
203
+ };
204
+ }
205
+ getRawCustomerId(customer) {
206
+ const raw = customer;
207
+ return customer.id ?? raw.customer_id ?? raw.customerId ?? null;
208
+ }
209
+ hasUsableCustomerDetails(customer) {
210
+ return !!(customer.name || customer.phone || customer.email || customer.display_name || customer.customer_name);
211
+ }
115
212
  /**
116
213
  * 更新当前预约的 appointment_status。
117
214
  *
@@ -192,6 +289,10 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
192
289
  */
193
290
  async loadProducts(params = {}, options) {
194
291
  const { schedule_date, customer_id, menu_list_ids, schedule_datetime } = params;
292
+ const bookingDate = schedule_datetime || schedule_date || "";
293
+ if (bookingDate) {
294
+ this.setBookingDate(bookingDate);
295
+ }
195
296
  try {
196
297
  const result = await this.store.products.loadProducts({
197
298
  with_count: ["bundleGroup", "optionGroup"],
@@ -207,6 +308,39 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
207
308
  throw error;
208
309
  }
209
310
  }
311
+ /**
312
+ * 只读查询单商品详情:走 ProductList 报价查询,但不写 productCatalog、
313
+ * 不 emit onProductsLoaded、不修改 BookingDate。
314
+ *
315
+ * 供「列表已 loadProducts、仅需补 product_resource / capacity」等场景使用,
316
+ * 避免 loadProductDetail 覆盖左侧商品列表。
317
+ *
318
+ * @example
319
+ * const detail = await bookingTicket.queryProductDetail({
320
+ * product_id: 2,
321
+ * schedule_date: '2030-01-15',
322
+ * schedule_datetime: '2030-01-15 10:00:00',
323
+ * });
324
+ */
325
+ async queryProductDetail(params, options) {
326
+ const { product_id, ...queryParams } = params;
327
+ try {
328
+ const result = await this.store.products.loadProducts({
329
+ with_count: ["bundleGroup", "optionGroup"],
330
+ with_schedule: 1,
331
+ ...queryParams,
332
+ product_ids: [product_id],
333
+ cacheId: this.cacheId
334
+ }, options);
335
+ const list = Array.isArray(result) ? result : [];
336
+ return list.find(
337
+ (product) => Number(product.id) === Number(product_id)
338
+ );
339
+ } catch (error) {
340
+ console.error("Failed to query product detail:", error);
341
+ throw error;
342
+ }
343
+ }
210
344
  /**
211
345
  * 按预约时间加载单个商品详情。
212
346
  * 价格仍由 server products 的报价单桥接统一计算,避免 solution 实例重复请求报价单。
@@ -380,14 +514,28 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
380
514
  * 在 BookingTicket 命名空间也 emit 一次,方便上层统一从 Solution 订阅。
381
515
  */
382
516
  setOrderCustomer(customer) {
517
+ var _a;
383
518
  if (!customer) {
384
519
  this.clearOrderCustomer();
385
520
  return;
386
521
  }
522
+ const previousCustomerId = (_a = this.store.order.getOrderCustomer()) == null ? void 0 : _a.customer_id;
387
523
  const patch = (0, import_orderCustomer.formatOrderCustomerPatch)(customer);
524
+ const customerUnchanged = Number(previousCustomerId || 0) === Number(patch.customer_id || 0);
525
+ if (!customerUnchanged) {
526
+ this.discountConfigCacheKey = null;
527
+ this.hasManualDiscountSelection = false;
528
+ }
388
529
  this.store.order.setOrderCustomer(patch, customer);
530
+ if (customerUnchanged) {
531
+ return;
532
+ }
389
533
  const next = this.buildOrderCustomerPayload();
390
534
  this.core.effects.emit(`${this.name}:onOrderCustomerChange`, next);
535
+ this.refreshDiscountConfigAfterOrderCustomerChange(patch.customer_id);
536
+ void this.store.order.applyPromotion().catch((error) => {
537
+ console.error("[BookingTicket] applyPromotion after customer change failed", error);
538
+ });
391
539
  }
392
540
  /**
393
541
  * 读取 tempOrder 上的下单客户协议字段;customer_id 缺失时返回 null。
@@ -405,9 +553,36 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
405
553
  * 清空下单客户(协议字段 + snapshot)。
406
554
  */
407
555
  clearOrderCustomer() {
556
+ var _a, _b;
408
557
  this.store.order.clearOrderCustomer();
558
+ this.discountConfigCacheKey = null;
559
+ this.hasManualDiscountSelection = false;
560
+ const orderStore = this.store.order.store || {};
561
+ const discountModule = orderStore.discount;
562
+ void ((_a = discountModule == null ? void 0 : discountModule.setOriginalDiscountList) == null ? void 0 : _a.call(discountModule, []));
563
+ void ((_b = discountModule == null ? void 0 : discountModule.setDiscountList) == null ? void 0 : _b.call(discountModule, []));
564
+ this.store.order.applyDiscount();
565
+ void this.store.order.recalculateSummary({ createIfMissing: true }).then(() => this.store.order.persistTempOrder()).catch((error) => {
566
+ console.error("Failed to recalculate summary after clearing customer:", error);
567
+ });
409
568
  this.core.effects.emit(`${this.name}:onOrderCustomerChange`, null);
410
569
  }
570
+ /**
571
+ * 重置 tempOrder,并同步清空下单客户运行态。
572
+ *
573
+ * OrderModule.restoreOrder 会重建空单(customer_id 已为 null),但不会:
574
+ * - emit BookingTicket `onOrderCustomerChange`
575
+ * - 重置 CustomerModule.selectedCustomer(老路 getActiveCustomer)
576
+ *
577
+ * 因此在 super.restoreOrder 之后显式清客户,保证 SalesSdk / ticketBooking 订阅方与 tempOrder 一致。
578
+ */
579
+ async restoreOrder() {
580
+ var _a;
581
+ const tempOrder = await super.restoreOrder();
582
+ this.clearOrderCustomer();
583
+ (_a = this.store.customer) == null ? void 0 : _a.setSelectedCustomer(null);
584
+ return tempOrder;
585
+ }
411
586
  /** 内部:基于 OrderModule 当前状态构造一份 OrderCustomerChangePayload。 */
412
587
  buildOrderCustomerPayload() {
413
588
  const patch = this.store.order.getOrderCustomer();
@@ -418,6 +593,34 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
418
593
  snapshot: this.store.order.getOrderCustomerSnapshot()
419
594
  };
420
595
  }
596
+ refreshDiscountConfigAfterOrderCustomerChange(customerId) {
597
+ if (!customerId || customerId === 1)
598
+ return;
599
+ if (this.orderCustomerDiscountRefreshInFlight && this.orderCustomerDiscountRefreshCustomerId === customerId) {
600
+ return;
601
+ }
602
+ const previousRefresh = this.orderCustomerDiscountRefreshInFlight;
603
+ const refreshTask = (async () => {
604
+ if (previousRefresh) {
605
+ await previousRefresh.catch(() => void 0);
606
+ }
607
+ const currentCustomer = this.store.order.getOrderCustomer();
608
+ if (!currentCustomer || Number(currentCustomer.customer_id) !== customerId) {
609
+ return;
610
+ }
611
+ await this.loadDiscountConfig({ customerId });
612
+ })();
613
+ this.orderCustomerDiscountRefreshInFlight = refreshTask;
614
+ this.orderCustomerDiscountRefreshCustomerId = customerId;
615
+ refreshTask.catch((error) => {
616
+ console.error("Failed to load discount config after customer change:", error);
617
+ }).finally(() => {
618
+ if (this.orderCustomerDiscountRefreshInFlight !== refreshTask)
619
+ return;
620
+ this.orderCustomerDiscountRefreshInFlight = null;
621
+ this.orderCustomerDiscountRefreshCustomerId = null;
622
+ });
623
+ }
421
624
  /**
422
625
  * 获取客户分页信息
423
626
  * @returns 分页信息
@@ -613,6 +816,183 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
613
816
  async getOtherParams() {
614
817
  return this.otherParams;
615
818
  }
819
+ // ─── BookingContext API(透传给 BookingContextModule) ──────────────────────
820
+ // SalesSdkProvider 挂载时默认调 initBookingContext;也可由 UI 显式 set* 灌入已有数据。
821
+ /** 拉取 board config 并写入 BookingContext。 */
822
+ loadBookingConfig(params) {
823
+ return this.store.bookingContext.loadBookingConfig(params);
824
+ }
825
+ /** 拉取资源全集并写入 BookingContext。 */
826
+ loadBookingResources(params) {
827
+ return this.store.bookingContext.loadResources(params);
828
+ }
829
+ /** 初始化 BookingContext(date + config + resources)。 */
830
+ initBookingContext(params) {
831
+ return this.store.bookingContext.initBookingContext(params);
832
+ }
833
+ /** 当前 store 是否已具备与入参等价的 config / resources(无需再请求接口)。 */
834
+ isBookingContextReady(params) {
835
+ return this.store.bookingContext.isBookingContextReady(params);
836
+ }
837
+ /** 写入 booking config(来源:`/core/board/management/config`)。 */
838
+ setBookingConfig(config) {
839
+ this.store.bookingContext.setBookingConfig(config);
840
+ }
841
+ /** 读取当前 booking config。 */
842
+ getBookingConfig() {
843
+ return this.store.bookingContext.getBookingConfig();
844
+ }
845
+ /** 写入资源全集(来源:`/form/schedule`),同步派生 resourcesOriginMap。 */
846
+ setResources(resources) {
847
+ this.store.bookingContext.setResources(resources);
848
+ }
849
+ /** 读取资源全集(浅克隆)。 */
850
+ getResourcesOrigin() {
851
+ return this.store.bookingContext.getResourcesOrigin();
852
+ }
853
+ /** 读取资源全集 map(id → resource)。 */
854
+ getResourcesOriginMap() {
855
+ return this.store.bookingContext.getResourcesOriginMap();
856
+ }
857
+ /** 写入当前选择的日期;支持 dayjs / Date / string。 */
858
+ setBookingDate(date) {
859
+ this.store.bookingContext.setDate(date);
860
+ }
861
+ /** 读取当前选择的日期(统一 string)。 */
862
+ getBookingDate() {
863
+ return this.store.bookingContext.getDate();
864
+ }
865
+ /** 读取 BookingContext 完整 store(调试 / 测试 / 透传给 SDK 使用)。 */
866
+ getBookingContextState() {
867
+ return this.store.bookingContext.getState();
868
+ }
869
+ /** 清空 BookingContext(切租户 / destroy 场景)。 */
870
+ clearBookingContext() {
871
+ this.store.bookingContext.clear();
872
+ }
873
+ /**
874
+ * 构造 BookingContext 计算上下文(供 getProductExtend / getResourceByIds 等读 API 复用)。
875
+ */
876
+ buildBookingCalcContext(extra) {
877
+ const state = this.store.bookingContext.getState();
878
+ const date = (extra == null ? void 0 : extra.date) ?? state.date;
879
+ return {
880
+ bookingConfig: state.bookingConfig,
881
+ resourcesOrigin: state.resourcesOrigin,
882
+ resourcesOriginMap: state.resourcesOriginMap,
883
+ date,
884
+ presetStartTime: (extra == null ? void 0 : extra.presetStartTime) ?? (0, import_BookingContext.derivePresetStartTimeFromDate)(date),
885
+ ...extra || {}
886
+ };
887
+ }
888
+ /**
889
+ * 取商品当前可选资源列表(透传 OS BookingContext utils#getResourceByIds)。
890
+ * `extraResources` 用于把拖入未绑定的资源也展示出来。
891
+ */
892
+ getResourcesForProduct(product, options) {
893
+ var _a, _b;
894
+ const ctx = this.buildBookingCalcContext();
895
+ return (0, import_BookingContext.getResourceByIds)(ctx.resourcesOriginMap || {}, product, {
896
+ resourceTab: (_b = (_a = ctx.bookingConfig) == null ? void 0 : _a.config) == null ? void 0 : _b.resource_tab,
897
+ resourcesOrigin: ctx.resourcesOrigin,
898
+ extraResources: (options == null ? void 0 : options.extraResources) || []
899
+ });
900
+ }
901
+ /**
902
+ * 拼装 cacheItem 的 _extend / _data(资源 / 容量 / timeObj),与 info2 `getProductExtend` 等价。
903
+ */
904
+ getProductExtend(cacheItem, extra) {
905
+ return (0, import_BookingContext.getProductExtend)({
906
+ cacheItem,
907
+ ctx: this.buildBookingCalcContext(extra)
908
+ });
909
+ }
910
+ /**
911
+ * 取资源不可用原因列表(结构化 enum + params,i18n 文案由 UI 渲染)。
912
+ */
913
+ getResourceErrors(resource, cacheItem) {
914
+ return (0, import_BookingContext.getResourceErrors)(resource, cacheItem);
915
+ }
916
+ // ─── addProduct 决策 / 转换 API(SDK 加车下沉所需) ─────────────────────────
917
+ // 这一组方法是 SalesSdkCartContext.addProduct / confirmDetail 的 OS 后端。
918
+ // 调用方传入 ProductData,OS 自取 bookingContext / customer / date 等上下文,
919
+ // 输出 (a) autoClose 决策结果或 (b) `{ product, booking }` 协议入参。
920
+ /**
921
+ * 拼装 addProductDecision 通用上下文(从 store 自取)。
922
+ */
923
+ buildAddProductCtx(extra) {
924
+ var _a, _b;
925
+ const bookingContextState = this.store.bookingContext.getState();
926
+ const date = (extra == null ? void 0 : extra.date) ?? bookingContextState.date;
927
+ const orderCustomerId = ((_a = this.getOrderCustomerSnapshot()) == null ? void 0 : _a.id) ?? ((_b = this.store.order.getTempOrder()) == null ? void 0 : _b.customer_id) ?? void 0;
928
+ return {
929
+ bookingConfig: bookingContextState.bookingConfig,
930
+ resourcesOrigin: bookingContextState.resourcesOrigin,
931
+ resourcesOriginMap: bookingContextState.resourcesOriginMap,
932
+ date,
933
+ presetStartTime: (extra == null ? void 0 : extra.presetStartTime) ?? (0, import_BookingContext.derivePresetStartTimeFromDate)(date),
934
+ customerId: orderCustomerId,
935
+ ...extra || {}
936
+ };
937
+ }
938
+ /**
939
+ * 加车两阶段主决策(规格弹窗 / 资源编辑 / 直接加车)。
940
+ * 与 ticketBooking `handleSelectProduct` + `handleBooking4Service` 等价。
941
+ */
942
+ decideAddProduct(item, options) {
943
+ return (0, import_addProductDecision.decideAddProduct)(item, this.buildAddProductCtx(options));
944
+ }
945
+ /** 规格弹窗 callback 后的第二段决策。 */
946
+ decideAfterDetail(cacheItem, options) {
947
+ return (0, import_addProductDecision.decideAfterDetail)(cacheItem, this.buildAddProductCtx(options));
948
+ }
949
+ /**
950
+ * @deprecated 请用 `decideAddProduct`;`autoClose=true` 仅当 action=add。
951
+ */
952
+ decideAutoClose(item, options) {
953
+ return (0, import_addProductDecision.decideAutoClose)(item, this.buildAddProductCtx(options));
954
+ }
955
+ /** 替代 info2 `getIsEject`:商品是否需要弹窗(0/1)。 */
956
+ getIsEject(item, type = "select") {
957
+ return (0, import_addProductDecision.getIsEject)(item, type);
958
+ }
959
+ /** 仅 session 商品(无 variant/option/package)→ true。 */
960
+ getIsOnlySession(item) {
961
+ return (0, import_addProductDecision.getIsOnlySession)(item);
962
+ }
963
+ /**
964
+ * 拼装 requiresDetail payload(给 SDK 直接 return 给 UI 用)。
965
+ */
966
+ buildRequiresDetailPayload(item, options) {
967
+ return (0, import_addProductDecision.buildRequiresDetailPayload)(item, this.buildAddProductCtx(options));
968
+ }
969
+ /**
970
+ * 把 detail 弹窗回调结果(e, extension_type, detail)转换为 `(product, booking)` 入参。
971
+ * 内部会先拼 cacheItem,再走 cacheItemToBookingInput 严格协议对齐。
972
+ */
973
+ transformDetailToProductAndBooking(input) {
974
+ const { item, cacheItem: preparedCacheItem, detailResult, options } = input;
975
+ const ctx = this.buildAddProductCtx(options);
976
+ const cacheItem = preparedCacheItem || (detailResult ? (0, import_addProductDecision.buildCacheItemFromDetail)(item, detailResult, ctx) : (0, import_addProductDecision.buildCacheItemFromCartDetail)(item, ctx));
977
+ return (0, import_addProductDecision.transformDetailToProductAndBooking)(cacheItem, ctx);
978
+ }
979
+ /**
980
+ * 从已加车的 order line + booking 反查资源编辑抽屉所需的 cacheItem。
981
+ * 供 SalesSdk「编辑资源」等场景使用;字段语义与 buildCacheItemFromDetail 产出对齐。
982
+ */
983
+ buildCacheItemFromOrderLine(input) {
984
+ return (0, import_buildCacheItemFromOrderLine.buildCacheItemFromOrderLine)(input);
985
+ }
986
+ /**
987
+ * 从已加车的普通商品 order line 反查 SkuDetailModal edit 所需的 cacheItem。
988
+ */
989
+ buildNormalProductCacheItemFromOrderLine(input) {
990
+ return (0, import_buildNormalProductCacheItemFromOrderLine.buildNormalProductCacheItemFromOrderLine)(input);
991
+ }
992
+ /** 文档别名:与 OrderModule.updateOrderProduct 一致。 */
993
+ async updateProductInOrder(params) {
994
+ return this.updateOrderProduct(params);
995
+ }
616
996
  /**
617
997
  * 销毁模块:先调用父类(销毁所有 store 内子模块 + emit destroy + super.destroy()),
618
998
  * 再清理 BookingTicket 自有资源(scan 监听 + scan 内存缓存)。
@@ -1,4 +1,5 @@
1
1
  import { ProductList, OrderModule } from '../../modules';
2
+ import { BookingContextModule } from '../../modules/BookingContext';
2
3
  import { CustomerModule } from '../../modules/Customer';
3
4
  import { SalesSummaryModule } from '../../modules/SalesSummary';
4
5
  import { ScheduleModule } from '../../modules/Schedule';
@@ -16,6 +17,7 @@ export interface BookingTicketState extends BaseSalesState {
16
17
  salesSummary: SalesSummaryModule;
17
18
  schedule: ScheduleModule;
18
19
  customer: CustomerModule;
20
+ bookingContext: BookingContextModule;
19
21
  }
20
22
  export declare function createBookingTicketModule<T extends keyof BookingTicketState>(moduleName: T, solutionName: string, name?: string, version?: string): BookingTicketState[T] | null;
21
23
  export declare enum BookingTicketHooks {
@@ -24,6 +24,7 @@ __export(types_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(types_exports);
26
26
  var import_modules = require("../../modules");
27
+ var import_BookingContext = require("../../modules/BookingContext");
27
28
  var import_Customer = require("../../modules/Customer");
28
29
  var import_SalesSummary = require("../../modules/SalesSummary");
29
30
  var import_Schedule = require("../../modules/Schedule");
@@ -54,6 +55,11 @@ function createBookingTicketModule(moduleName, solutionName, name, version) {
54
55
  `${solutionName}_${name || moduleName}`,
55
56
  version
56
57
  );
58
+ case "bookingContext":
59
+ return new import_BookingContext.BookingContextModule(
60
+ `${solutionName}_${name || moduleName}`,
61
+ version
62
+ );
57
63
  default:
58
64
  return null;
59
65
  }
@@ -0,0 +1,113 @@
1
+ import { BookingCalcContext } from '../../../modules/BookingContext';
2
+ import type { ProductData } from '../../../modules/Product/types';
3
+ import type { OrderProduct, OrderProductIdentity, AddProductBookingInput } from '../../../modules/Order/types';
4
+ /**
5
+ * cart.addProduct 两阶段决策(与 ticketBooking `handleSelectProduct` 对齐):
6
+ *
7
+ * 1. `requiresDetail`:规格 / SKU / 套餐 / Session / 称重弹窗(门禁:`isOpenDetailModal` / `cartDetailValue` / `open_sold_weight`)
8
+ * 2. `requiresBookingEdit`:资源 / 时间编辑抽屉(门禁:`getIsAutoClose` on prepared cacheItem)
9
+ * 3. `add`:两关均通过,直接 transform + 加车
10
+ */
11
+ export type AddProductDecision = {
12
+ action: 'add';
13
+ cacheItem: any;
14
+ } | {
15
+ action: 'requiresDetail';
16
+ payload: AddProductRequiresDetailPayload;
17
+ } | {
18
+ action: 'requiresBookingEdit';
19
+ cacheItem: any;
20
+ payload: AddProductRequiresBookingEditPayload;
21
+ };
22
+ /** 资源 / 时间编辑抽屉所需的最小 payload。 */
23
+ export interface AddProductRequiresBookingEditPayload {
24
+ cacheItem: any;
25
+ customerId?: number | string;
26
+ date: string | null;
27
+ }
28
+ /**
29
+ * 加车主决策:对齐 `handleSelectProduct` 两段分支。
30
+ */
31
+ export declare function decideAddProduct(item: any, ctx?: AddProductDecideContext): AddProductDecision;
32
+ /**
33
+ * 规格弹窗 callback 后的第二段决策(对齐 `handleBooking4Service` 内 `cacheItem.autoClose` 分支)。
34
+ */
35
+ export declare function decideAfterDetail(cacheItem: any, ctx?: AddProductDecideContext): AddProductDecision;
36
+ /**
37
+ * @deprecated 请用 `decideAddProduct`;保留兼容:autoClose=true 仅表示可直接加车(action=add)。
38
+ */
39
+ export interface AddProductRequiresDetailPayload {
40
+ item: ProductData | Record<string, any>;
41
+ /** UI 需展示的子弹窗开关。 */
42
+ showConfig: {
43
+ option: boolean;
44
+ session: boolean;
45
+ variant: boolean;
46
+ package: boolean;
47
+ number: boolean;
48
+ };
49
+ /** 仅 session 商品(且无 variant/option/package)→ true,UI 可只渲染 session 选择。 */
50
+ isOnlySession: boolean;
51
+ /** 0/1,与 info2 `getIsEject` 等价含义。 */
52
+ isEject: 0 | 1;
53
+ /** 当前选中客户 id(透传给 detail 弹窗用)。 */
54
+ customerId?: number | string;
55
+ /** 当前选择的日期。 */
56
+ date: string | null;
57
+ /** 透传完整商品数据,UI 渲染时直接用。 */
58
+ productData: ProductData | Record<string, any>;
59
+ }
60
+ export interface AddProductDecideContext extends BookingCalcContext {
61
+ type?: 'select' | 'detail';
62
+ quantity?: number;
63
+ customerId?: number | string;
64
+ }
65
+ /**
66
+ * 统一补齐预约商品的运行态字段。
67
+ *
68
+ * 新链路不再让决策和提交各自重建 cacheItem;这里作为唯一入口补齐
69
+ * `_extend.resource / capacity / startDate / endDate` 等后续 booking mapper 需要的字段。
70
+ */
71
+ export declare function prepareBookingCacheItem(cacheItem: any, ctx?: AddProductDecideContext): any;
72
+ export declare function decideAutoClose(item: any, ctx?: AddProductDecideContext): {
73
+ autoClose: boolean;
74
+ cacheItem?: any;
75
+ };
76
+ /**
77
+ * 替代 info2 `getIsEject`:商品是否需要弹窗(含 type='detail' / option_group / bundle_group / variant / session / sold_by_weight)。
78
+ */
79
+ export declare function getIsEject(item: any, type?: 'select' | 'detail'): 0 | 1;
80
+ /** 仅 session 商品(且没有 variant/option/package)。 */
81
+ export declare function getIsOnlySession(item: any): boolean;
82
+ /**
83
+ * 拼装 requiresDetail payload;showConfig 与 info2 弹窗内部判断一致。
84
+ */
85
+ export declare function buildRequiresDetailPayload(item: any, ctx?: AddProductDecideContext): AddProductRequiresDetailPayload;
86
+ /**
87
+ * 用 item.cartDetailValue 拼装一个 cacheItem 雏形(与 info2 `handleDirectAddService` 等价路径)。
88
+ * 这里只补 `_extend.start_date / quantity / other` 等运行态字段;后续走 getProductExtend 才会补齐资源 / 时间。
89
+ */
90
+ export declare function buildCacheItemFromCartDetail(item: any, ctx?: AddProductDecideContext): any;
91
+ /**
92
+ * 用 Info2 callback 的 (e, extension_type, detail) 拼装 cacheItem。
93
+ *
94
+ * 保留 detail._extend 中的 pricing / note(规格弹窗改价、备注等),
95
+ * 再叠加 session 时间、ctx.quantity 等运行态字段;other 以 callback `e` 为准。
96
+ */
97
+ export declare function buildCacheItemFromDetail(item: any, detailResult: {
98
+ e: any;
99
+ extension_type?: any;
100
+ detail?: any;
101
+ }, ctx?: AddProductDecideContext): any;
102
+ /**
103
+ * `transformDetailToProductAndBooking`:把 cacheItem 拆成 OS `addProductToOrder` 所需的 `(product, booking)`。
104
+ *
105
+ * - `product` 走 `transformBaseProductToOrderProduct`(沿用 BaseSales 现有 mapper)。
106
+ * - `booking` 走 `cacheItemToBookingInput`(严格按 TEMP_ORDER_FIELDS_SUMMARY.md 协议)。
107
+ * - 普通商品(无 duration / 无 schedules)不产 booking,与 info2 一致。
108
+ */
109
+ export declare function transformDetailToProductAndBooking(cacheItem: any, ctx?: AddProductDecideContext): {
110
+ product: Partial<OrderProduct> & OrderProductIdentity;
111
+ booking?: AddProductBookingInput;
112
+ cacheItem: any;
113
+ };