@pisell/pisellos 2.1.165 → 2.1.166

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 (78) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +3 -0
  3. package/dist/model/strategy/adapter/dataVariant/adapter.d.ts +50 -0
  4. package/dist/model/strategy/adapter/dataVariant/adapter.js +167 -0
  5. package/dist/model/strategy/adapter/dataVariant/evaluator.d.ts +89 -0
  6. package/dist/model/strategy/adapter/dataVariant/evaluator.js +780 -0
  7. package/dist/model/strategy/adapter/dataVariant/examples.d.ts +39 -0
  8. package/dist/model/strategy/adapter/dataVariant/examples.js +277 -0
  9. package/dist/model/strategy/adapter/dataVariant/index.d.ts +4 -0
  10. package/dist/model/strategy/adapter/dataVariant/index.js +4 -0
  11. package/dist/model/strategy/adapter/dataVariant/type.d.ts +148 -0
  12. package/dist/model/strategy/adapter/dataVariant/type.js +54 -0
  13. package/dist/model/strategy/adapter/index.d.ts +4 -0
  14. package/dist/model/strategy/adapter/index.js +5 -1
  15. package/dist/model/strategy/adapter/promotion/index.js +0 -9
  16. package/dist/modules/Discount/index.d.ts +5 -2
  17. package/dist/modules/Discount/index.js +21 -1
  18. package/dist/modules/Discount/types.d.ts +4 -0
  19. package/dist/modules/Order/index.d.ts +1 -0
  20. package/dist/modules/Order/index.js +18 -6
  21. package/dist/modules/Order/types.d.ts +2 -0
  22. package/dist/modules/ProductList/index.d.ts +3 -1
  23. package/dist/modules/ProductList/index.js +22 -11
  24. package/dist/modules/Schedule/getDateIsInSchedule.js +5 -0
  25. package/dist/solution/BookingByStep/index.d.ts +2 -2
  26. package/dist/solution/ScanOrder/index.js +1 -1
  27. package/dist/solution/ShopDiscount/index.js +16 -15
  28. package/dist/solution/VenueBooking/index.d.ts +40 -3
  29. package/dist/solution/VenueBooking/index.js +394 -261
  30. package/dist/solution/VenueBooking/types.d.ts +3 -1
  31. package/dist/solution/VenueBooking/types.js +1 -0
  32. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +2 -2
  33. package/dist/solution/VenueBooking/utils/dateSummary.js +12 -25
  34. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +0 -6
  35. package/dist/solution/VenueBooking/utils/slotMerge.js +3 -9
  36. package/dist/solution/VenueBooking/utils/smartPricing.d.ts +70 -0
  37. package/dist/solution/VenueBooking/utils/smartPricing.js +233 -0
  38. package/dist/solution/VenueBooking/utils/timeSlot.d.ts +2 -0
  39. package/dist/solution/VenueBooking/utils/timeSlot.js +9 -4
  40. package/lib/index.d.ts +1 -0
  41. package/lib/index.js +3 -0
  42. package/lib/model/strategy/adapter/dataVariant/adapter.d.ts +50 -0
  43. package/lib/model/strategy/adapter/dataVariant/adapter.js +149 -0
  44. package/lib/model/strategy/adapter/dataVariant/evaluator.d.ts +89 -0
  45. package/lib/model/strategy/adapter/dataVariant/evaluator.js +583 -0
  46. package/lib/model/strategy/adapter/dataVariant/examples.d.ts +39 -0
  47. package/lib/model/strategy/adapter/dataVariant/examples.js +293 -0
  48. package/lib/model/strategy/adapter/dataVariant/index.d.ts +4 -0
  49. package/lib/model/strategy/adapter/dataVariant/index.js +38 -0
  50. package/lib/model/strategy/adapter/dataVariant/type.d.ts +148 -0
  51. package/lib/model/strategy/adapter/dataVariant/type.js +31 -0
  52. package/lib/model/strategy/adapter/index.d.ts +4 -0
  53. package/lib/model/strategy/adapter/index.js +13 -2
  54. package/lib/modules/Discount/index.d.ts +5 -2
  55. package/lib/modules/Discount/index.js +11 -1
  56. package/lib/modules/Discount/types.d.ts +4 -0
  57. package/lib/modules/Order/index.d.ts +1 -0
  58. package/lib/modules/Order/index.js +11 -1
  59. package/lib/modules/Order/types.d.ts +2 -0
  60. package/lib/modules/ProductList/index.d.ts +3 -1
  61. package/lib/modules/ProductList/index.js +45 -39
  62. package/lib/modules/Schedule/getDateIsInSchedule.js +4 -0
  63. package/lib/solution/BookingByStep/index.d.ts +2 -2
  64. package/lib/solution/ScanOrder/index.js +1 -1
  65. package/lib/solution/ShopDiscount/index.js +3 -2
  66. package/lib/solution/VenueBooking/index.d.ts +40 -3
  67. package/lib/solution/VenueBooking/index.js +257 -111
  68. package/lib/solution/VenueBooking/types.d.ts +3 -1
  69. package/lib/solution/VenueBooking/types.js +1 -0
  70. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +2 -2
  71. package/lib/solution/VenueBooking/utils/dateSummary.js +5 -10
  72. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +0 -6
  73. package/lib/solution/VenueBooking/utils/slotMerge.js +3 -4
  74. package/lib/solution/VenueBooking/utils/smartPricing.d.ts +70 -0
  75. package/lib/solution/VenueBooking/utils/smartPricing.js +180 -0
  76. package/lib/solution/VenueBooking/utils/timeSlot.d.ts +2 -0
  77. package/lib/solution/VenueBooking/utils/timeSlot.js +7 -3
  78. package/package.json +1 -1
@@ -50,6 +50,7 @@ var import_resource = require("./utils/resource");
50
50
  var import_timeSlot = require("./utils/timeSlot");
51
51
  var import_dateSummary = require("./utils/dateSummary");
52
52
  var import_slotMerge = require("./utils/slotMerge");
53
+ var import_smartPricing = require("./utils/smartPricing");
53
54
  var import_utils3 = require("../../modules/Order/utils");
54
55
  var import_Order = require("../../modules/Order");
55
56
  var import_types4 = require("../RegisterAndLogin/types");
@@ -110,6 +111,10 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
110
111
  this.loadAllProductsInFlight = null;
111
112
  this.productsLoaded = false;
112
113
  this.loadOpenDataConfigInFlight = null;
114
+ /** 按日期缓存 slot 智能定价 Map,避免同周重复 resolveProducts */
115
+ this.slotPriceMapCache = /* @__PURE__ */ new Map();
116
+ /** loadProducts 后缓存的全量商品(venue + addon),供分段算价复用 */
117
+ this.smartPricingProductCatalog = [];
113
118
  }
114
119
  getLoggerContext() {
115
120
  return {
@@ -272,19 +277,16 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
272
277
  });
273
278
  }
274
279
  registerCustomerLoginListeners() {
275
- var _a, _b, _c, _d;
276
280
  this.clearLoginEffectListeners();
277
- const accountModule = (_a = this.core) == null ? void 0 : _a.getModule("account");
278
- const registerAndLoginModule = (_b = this.core) == null ? void 0 : _b.getModule("registerAndLogin");
279
- const currentAccount = ((_c = accountModule == null ? void 0 : accountModule.getCurrentAccount) == null ? void 0 : _c.call(accountModule)) || ((_d = accountModule == null ? void 0 : accountModule.getAccount) == null ? void 0 : _d.call(accountModule)) || null;
280
- const createHandleLogin = (eventName) => async (payload) => {
281
+ const createHandleLogin = () => async (payload) => {
281
282
  const customerId = this.resolveCustomerIdFromLoginPayload(payload);
282
283
  if (!customerId)
283
284
  return;
284
285
  await this.refreshOrderMarketingAfterLogin({ customerId });
285
286
  };
286
- this.registerLoginEffect(import_types3.AccountHooks.OnLogin, createHandleLogin(import_types3.AccountHooks.OnLogin));
287
- this.registerLoginEffect(import_types4.RegisterAndLoginHooks.onLoginSuccess, createHandleLogin(import_types4.RegisterAndLoginHooks.onLoginSuccess));
287
+ this.registerLoginEffect(_VenueBookingImpl.PISELL1_LOGIN_SUCCESS, createHandleLogin());
288
+ this.registerLoginEffect(import_types3.AccountHooks.OnLogin, createHandleLogin());
289
+ this.registerLoginEffect(import_types4.RegisterAndLoginHooks.onLoginSuccess, createHandleLogin());
288
290
  }
289
291
  async refreshOrderMarketingAfterLogin(params) {
290
292
  if (!this.store.order)
@@ -298,20 +300,24 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
298
300
  }
299
301
  this.customerLoginRefreshIdInFlight = params.customerId;
300
302
  const refreshTask = (async () => {
301
- var _a;
302
- if (this.store.quotation) {
303
- await this.store.quotation.loadQuotations({
304
- channel: (_a = this.otherParams) == null ? void 0 : _a.channel
305
- });
306
- }
307
- this.recalculateOrderPricesFromQuotation();
303
+ var _a, _b, _c;
304
+ this.smartPricingCustomerId = params.customerId;
308
305
  await this.store.order.loadDiscountConfig({
309
306
  customerId: params.customerId
310
307
  });
308
+ this.clearSlotPriceMapCache();
309
+ this.recalculateOrderPricesFromSmartPricing();
310
+ this.store.order.applyDiscount();
311
+ this.normalizeDiscountedOrderPrices(
312
+ this.store.order.getDiscountList()
313
+ );
311
314
  await this.store.order.recalculateSummary({ createIfMissing: true });
312
315
  this.store.order.persistTempOrder();
313
316
  await this.refreshItemRuleQuantityLimits();
314
317
  await this.refreshCartValidationPassed();
318
+ await ((_c = (_b = (_a = this.core) == null ? void 0 : _a.effects) == null ? void 0 : _b.emit) == null ? void 0 : _c.call(_b, import_types.VenueBookingHooks.onSmartPricingUpdated, {
319
+ customerId: params.customerId
320
+ }));
315
321
  })();
316
322
  this.customerLoginRefreshInFlight = refreshTask;
317
323
  try {
@@ -326,11 +332,12 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
326
332
  }
327
333
  }
328
334
  async initialize(core, options = {}) {
329
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
335
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
330
336
  this.logMethodStart("initialize");
331
337
  this.core = core;
332
338
  this.initializeOptions = options || {};
333
339
  this.store = { ...this.store, ...options.store };
340
+ this.smartPricingCustomerId = void 0;
334
341
  this.store.entryContext = ((_a = options.otherParams) == null ? void 0 : _a.entryContext) || this.store.entryContext;
335
342
  this.store.status = "initializing";
336
343
  this.store.error = null;
@@ -462,10 +469,6 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
462
469
  await this.loadRuntimeConfigs();
463
470
  if (this.store.schedule) {
464
471
  await this.store.schedule.loadAllSchedule();
465
- this.injectScheduleResolverToQuotation();
466
- (_k = this.store.quotation) == null ? void 0 : _k.loadQuotations({
467
- channel: (_j = this.otherParams) == null ? void 0 : _j.channel
468
- });
469
472
  }
470
473
  await this.refreshItemRuleQuantityLimits();
471
474
  this.store.status = "ready";
@@ -541,6 +544,95 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
541
544
  getCartValidation() {
542
545
  return this.store.cartValidation || { passed: null, failures: [] };
543
546
  }
547
+ /** 清除 slot 价格分段缓存(商品重载 / 登录后 customerId 变化时调用) */
548
+ clearSlotPriceMapCache() {
549
+ this.slotPriceMapCache.clear();
550
+ }
551
+ /** 读取当前登录 customerId */
552
+ resolveCustomerIdForSmartPricing() {
553
+ var _a, _b, _c;
554
+ if (this.smartPricingCustomerId)
555
+ return this.smartPricingCustomerId;
556
+ try {
557
+ const userPlugin = (_a = this.core) == null ? void 0 : _a.getPlugin("user");
558
+ const customerId = Number((_c = (_b = userPlugin == null ? void 0 : userPlugin.get) == null ? void 0 : _b.call(userPlugin)) == null ? void 0 : _c.id);
559
+ return Number.isFinite(customerId) && customerId > 0 ? customerId : void 0;
560
+ } catch {
561
+ return void 0;
562
+ }
563
+ }
564
+ resolveAvailableWalletIdsForSmartPricing() {
565
+ var _a, _b, _c, _d, _e, _f, _g, _h;
566
+ const orderWalletIds = (_b = (_a = this.store.order) == null ? void 0 : _a.getAvailableWalletIds) == null ? void 0 : _b.call(_a);
567
+ const fallbackWalletIds = ((_c = this.otherParams) == null ? void 0 : _c.available_wallet_ids) || ((_d = this.otherParams) == null ? void 0 : _d.availableWalletIds) || ((_f = (_e = this.otherParams) == null ? void 0 : _e.strategy_context) == null ? void 0 : _f.available_wallet_ids) || ((_h = (_g = this.otherParams) == null ? void 0 : _g.strategy_context) == null ? void 0 : _h.availableWalletIds);
568
+ const source = Array.isArray(orderWalletIds) && orderWalletIds.length ? orderWalletIds : fallbackWalletIds;
569
+ if (!Array.isArray(source))
570
+ return [];
571
+ const ids = source.map((item) => Number(item)).filter((item) => Number.isFinite(item) && item > 0);
572
+ return Array.from(new Set(ids));
573
+ }
574
+ /** 读取 schedule 全量列表,供智能定价 schedule_match 使用 */
575
+ getScheduleListForSmartPricing() {
576
+ var _a;
577
+ const scheduleModule = this.store.schedule;
578
+ const list = (_a = scheduleModule == null ? void 0 : scheduleModule.store) == null ? void 0 : _a.scheduleList;
579
+ return Array.isArray(list) ? list : [];
580
+ }
581
+ /** 构建智能定价上下文,对齐 OS Server buildProductFormatterContext */
582
+ buildSmartPricingContext(customerId) {
583
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
584
+ const evaluator = (_b = (_a = this.core) == null ? void 0 : _a.context) == null ? void 0 : _b.dataVariantEvaluator;
585
+ return {
586
+ evaluator,
587
+ scheduleList: this.getScheduleListForSmartPricing(),
588
+ menuList: ((_c = this.otherParams) == null ? void 0 : _c.menuList) || ((_e = (_d = this.otherParams) == null ? void 0 : _d.openData) == null ? void 0 : _e.menuList) || [],
589
+ customerId: customerId ?? this.resolveCustomerIdForSmartPricing(),
590
+ availableWalletIds: this.resolveAvailableWalletIdsForSmartPricing(),
591
+ channel: (_f = this.otherParams) == null ? void 0 : _f.channel,
592
+ businessCode: (_g = this.otherParams) == null ? void 0 : _g.businessCode,
593
+ orderType: ((_h = this.otherParams) == null ? void 0 : _h.orderType) || ((_i = this.otherParams) == null ? void 0 : _i.type),
594
+ strategy_context: (_j = this.otherParams) == null ? void 0 : _j.strategy_context
595
+ };
596
+ }
597
+ /** venue + addon 全量商品,供智能定价评估 */
598
+ getAllProductsForSmartPricing() {
599
+ if (this.smartPricingProductCatalog.length) {
600
+ return this.smartPricingProductCatalog;
601
+ }
602
+ const venueList = this.getVenueProducts();
603
+ const addonList = this.getAddonProductsList();
604
+ return [...venueList, ...addonList];
605
+ }
606
+ getSlotPriceMapCacheKey(date, context) {
607
+ return [
608
+ date,
609
+ context.customerId ?? "",
610
+ (context.availableWalletIds || []).join(","),
611
+ context.channel ?? "",
612
+ context.businessCode ?? ""
613
+ ].join("|");
614
+ }
615
+ /**
616
+ * 按 schedule 分段构建 slot 价格 Map,带实例级缓存。
617
+ */
618
+ buildSlotPriceMapForDate(date, config, productIds) {
619
+ const context = this.buildSmartPricingContext();
620
+ const cacheKey = this.getSlotPriceMapCacheKey(date, context);
621
+ const cached = this.slotPriceMapCache.get(cacheKey);
622
+ if (cached)
623
+ return cached;
624
+ const timeLabels = (0, import_timeSlot.generateTimeLabels)(config);
625
+ const slotPriceMap = (0, import_smartPricing.buildSlotPriceMapByScheduleSegments)({
626
+ products: this.getAllProductsForSmartPricing(),
627
+ productIds,
628
+ date,
629
+ timeLabels,
630
+ config,
631
+ context
632
+ });
633
+ this.slotPriceMapCache.set(cacheKey, slotPriceMap);
634
+ return slotPriceMap;
635
+ }
544
636
  // ─── 场地商品 & 附加商品 ───
545
637
  async loadAllProducts() {
546
638
  if (this.loadAllProductsInFlight)
@@ -569,9 +661,18 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
569
661
  menu_list_ids: menuListIds,
570
662
  cacheId: this.cacheId,
571
663
  schedule_date: (0, import_dayjs.default)().format("YYYY-MM-DD"),
572
- schedule_datetime: (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
664
+ schedule_datetime: (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss"),
665
+ with_extra: ["dataVariants"]
573
666
  });
574
- const list = Array.isArray(allProducts) ? allProducts : [];
667
+ const rawList = Array.isArray(allProducts) ? allProducts : [];
668
+ let list = rawList;
669
+ this.smartPricingProductCatalog = rawList.slice();
670
+ list = (0, import_smartPricing.formatProductsWithDataVariant)(
671
+ list,
672
+ this.buildSmartPricingContext(),
673
+ (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
674
+ );
675
+ this.clearSlotPriceMapCache();
575
676
  const venueList = list.filter((p) => p.duration != null);
576
677
  const addonList = list.filter((p) => p.duration == null);
577
678
  const venueStore = (_c = this.store.venueProducts) == null ? void 0 : _c.store;
@@ -752,29 +853,24 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
752
853
  config: this.baseSlotConfig,
753
854
  rawResources: this.store.rawResourceData,
754
855
  resourceProductMap: this.resourceProductMap,
755
- quotationModule: this.store.quotation,
756
- resolveConfig: (date) => this.resolveSlotConfigForDate(date)
856
+ resolveConfig: (date) => this.resolveSlotConfigForDate(date),
857
+ buildSlotPriceMap: (date, config, productIds) => this.buildSlotPriceMapForDate(date, config, productIds)
757
858
  });
758
859
  }
759
860
  getTimeSlotGrid(date) {
760
861
  const resolvedSlotConfig = this.syncOperatingHoursToSlotConfig(date);
761
- let quotationPriceMap;
762
- if (this.store.quotation) {
763
- const productIds = [
764
- ...new Set(
765
- [...this.resourceProductMap.values()].flat().map((m) => m.productId)
766
- )
767
- ];
768
- const timeLabels = (0, import_timeSlot.generateTimeLabels)(resolvedSlotConfig);
769
- const timePoints = timeLabels.map((label) => `${date} ${label}`);
770
- quotationPriceMap = this.store.quotation.buildProductPriceMap({ productIds, timePoints });
771
- }
862
+ const productIds = [
863
+ ...new Set(
864
+ [...this.resourceProductMap.values()].flat().map((m) => m.productId)
865
+ )
866
+ ];
867
+ const slotPriceMap = productIds.length ? this.buildSlotPriceMapForDate(date, resolvedSlotConfig, productIds) : void 0;
772
868
  return (0, import_timeSlot.buildTimeSlotGrid)({
773
869
  date,
774
870
  config: resolvedSlotConfig,
775
871
  rawResources: this.store.rawResourceData,
776
872
  resourceProductMap: this.resourceProductMap,
777
- quotationPriceMap
873
+ slotPriceMap
778
874
  });
779
875
  }
780
876
  // ─── 时间槽订单操作 ───
@@ -1059,8 +1155,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1059
1155
  booking_uid: bookingUuid,
1060
1156
  price_breakdown: (0, import_slotMerge.buildPriceBreakdown)({
1061
1157
  group,
1062
- productId: mapping.productId,
1063
- quotation: this.store.quotation
1158
+ productId: mapping.productId
1064
1159
  })
1065
1160
  },
1066
1161
  _origin: {
@@ -1153,20 +1248,12 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1153
1248
  if (!this.store.schedule)
1154
1249
  throw new Error("schedule 模块未初始化");
1155
1250
  await this.store.schedule.loadAllSchedule();
1156
- this.injectScheduleResolverToQuotation();
1157
1251
  this.logMethodSuccess("loadSchedules");
1158
1252
  } catch (error) {
1159
1253
  this.logMethodError("loadSchedules", error);
1160
1254
  throw error;
1161
1255
  }
1162
1256
  }
1163
- injectScheduleResolverToQuotation() {
1164
- if (this.store.quotation && this.store.schedule) {
1165
- this.store.quotation.setScheduleResolver(
1166
- (id) => this.store.schedule.getScheduleListByIds([id])[0]
1167
- );
1168
- }
1169
- }
1170
1257
  getScheduleListByIds(ids) {
1171
1258
  if (!this.store.schedule)
1172
1259
  return [];
@@ -1205,14 +1292,16 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1205
1292
  throw error;
1206
1293
  }
1207
1294
  }
1208
- recalculateOrderPricesFromQuotation() {
1295
+ recalculateOrderPricesFromSmartPricing() {
1209
1296
  var _a, _b;
1210
- if (!this.store.order || !this.store.quotation)
1297
+ if (!this.store.order)
1211
1298
  return;
1212
1299
  const tempOrder = this.store.order.getTempOrder();
1213
1300
  if (!((_a = tempOrder == null ? void 0 : tempOrder.products) == null ? void 0 : _a.length))
1214
1301
  return;
1215
1302
  const now = (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss");
1303
+ const catalog = this.getAllProductsForSmartPricing();
1304
+ const context = this.buildSmartPricingContext();
1216
1305
  for (const product of tempOrder.products) {
1217
1306
  if ((_b = product.metadata) == null ? void 0 : _b.venue_booking) {
1218
1307
  const mappings = this.resourceProductMap.get(product.metadata.resource_id);
@@ -1225,37 +1314,75 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1225
1314
  product,
1226
1315
  this.store.slotConfig.slotDurationMinutes
1227
1316
  );
1228
- const updatedSlots = slots.map((slot) => ({
1229
- ...slot,
1230
- productId: mapping.productId,
1231
- price: this.store.quotation.getPriceForProduct({
1317
+ const uniqueDates = [
1318
+ ...new Set(slots.map((slot) => (0, import_dayjs.default)(slot.startTime).format("YYYY-MM-DD")))
1319
+ ];
1320
+ const slotPriceMaps = /* @__PURE__ */ new Map();
1321
+ for (const slotDate of uniqueDates) {
1322
+ slotPriceMaps.set(
1323
+ slotDate,
1324
+ this.buildSlotPriceMapForDate(slotDate, this.store.slotConfig, [mapping.productId])
1325
+ );
1326
+ }
1327
+ const updatedSlots = slots.map((slot) => {
1328
+ const slotDate = (0, import_dayjs.default)(slot.startTime).format("YYYY-MM-DD");
1329
+ const slotPriceMap = slotPriceMaps.get(slotDate);
1330
+ return {
1331
+ ...slot,
1232
1332
  productId: mapping.productId,
1233
- datetime: slot.startTime
1234
- }) ?? mapping.price
1235
- }));
1333
+ price: slotPriceMap ? (0, import_smartPricing.getSlotPriceFromMap)({
1334
+ slotPriceMap,
1335
+ productId: mapping.productId,
1336
+ slotStartTime: slot.startTime,
1337
+ fallbackPrice: mapping.price
1338
+ }) : mapping.price
1339
+ };
1340
+ });
1236
1341
  const merged = (0, import_slotMerge.mergeConsecutiveSlots)(updatedSlots);
1237
1342
  if (merged.length === 1) {
1238
- product.selling_price = merged[0].totalPrice;
1239
- product.original_price = merged[0].totalPrice;
1240
- product.metadata.price_breakdown = (0, import_slotMerge.buildPriceBreakdown)({
1241
- group: merged[0],
1242
- productId: mapping.productId,
1243
- quotation: this.store.quotation
1343
+ this.resetProductPriceForSmartPricing({
1344
+ product,
1345
+ price: merged[0].totalPrice,
1346
+ priceBreakdown: (0, import_slotMerge.buildPriceBreakdown)({
1347
+ group: merged[0],
1348
+ productId: mapping.productId
1349
+ })
1244
1350
  });
1245
1351
  }
1246
1352
  } else if (product.product_id != null) {
1247
- const quotationPrice = this.store.quotation.getPriceForProduct({
1248
- productId: product.product_id,
1249
- variantId: product.product_variant_id ?? void 0,
1250
- datetime: now
1353
+ const smartPrice = (0, import_smartPricing.getPriceForProductAtDatetime)({
1354
+ products: catalog,
1355
+ context,
1356
+ productId: Number(product.product_id),
1357
+ datetime: now,
1358
+ fallbackPrice: product.selling_price
1359
+ });
1360
+ this.resetProductPriceForSmartPricing({
1361
+ product,
1362
+ price: smartPrice
1251
1363
  });
1252
- if (quotationPrice !== null) {
1253
- product.selling_price = quotationPrice;
1254
- product.original_price = quotationPrice;
1255
- }
1256
1364
  }
1257
1365
  }
1258
1366
  }
1367
+ /**
1368
+ * DataVariant 重算价格后,同步刷新订单展示、Rules 与 Summary 共用的价格口径。
1369
+ * Rules 会在本轮结束后根据当前客户的 discountList 重新写入实际折扣。
1370
+ */
1371
+ resetProductPriceForSmartPricing(params) {
1372
+ const { product, price, priceBreakdown } = params;
1373
+ const metadata = product.metadata || {};
1374
+ product.selling_price = price;
1375
+ product.original_price = price;
1376
+ product.discount_list = [];
1377
+ product.metadata = {
1378
+ ...metadata,
1379
+ ...priceBreakdown ? { price_breakdown: priceBreakdown } : {},
1380
+ source_product_price: price,
1381
+ main_product_original_price: price,
1382
+ main_product_selling_price: price,
1383
+ price_schema_version: 2
1384
+ };
1385
+ }
1259
1386
  async scanCode(code, customerId) {
1260
1387
  this.logMethodStart("scanCode", { code });
1261
1388
  try {
@@ -1281,7 +1408,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1281
1408
  }
1282
1409
  /** 勾选/取消勾选某张折扣券,重新计算折扣并持久化 */
1283
1410
  async setDiscountSelected(params) {
1284
- var _a, _b, _c, _d, _e, _f, _g, _h;
1411
+ var _a, _b, _c, _d, _e;
1285
1412
  this.logMethodStart("setDiscountSelected", params);
1286
1413
  try {
1287
1414
  if (!this.store.order)
@@ -1333,42 +1460,15 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1333
1460
  }
1334
1461
  }
1335
1462
  }
1336
- const selectedResourceIds = new Set(
1337
- nextDiscountList.filter((d) => d.isSelected).map((d) => d.id)
1338
- );
1339
- for (const product of tempOrder.products) {
1340
- if ((_c = product._origin) == null ? void 0 : _c.isManualDiscount)
1341
- continue;
1342
- product.discount_list = (product.discount_list || []).filter((pd) => {
1343
- var _a2;
1344
- const rid = ((_a2 = pd.discount) == null ? void 0 : _a2.resource_id) ?? pd.id;
1345
- return rid != null && selectedResourceIds.has(rid);
1346
- });
1347
- const totalPerUnitDiscount = (product.discount_list || []).reduce(
1348
- (sum, pd) => sum + (pd.amount || 0),
1349
- 0
1350
- );
1351
- const optionSum = (0, import_utils3.sumOptionUnitPrice)(product.product_option_item);
1352
- const sourcePrice = ((_d = product.metadata) == null ? void 0 : _d.source_product_price) ?? (((_e = product.metadata) == null ? void 0 : _e.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");
1353
- const newSourceSellingPrice = new import_decimal.default(Number(sourcePrice) || 0).minus(totalPerUnitDiscount).toDecimalPlaces(2).toString();
1354
- const newMainSellingPrice = new import_decimal.default(Number(newSourceSellingPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
1355
- if (product.metadata) {
1356
- product.metadata.main_product_selling_price = newMainSellingPrice;
1357
- product.metadata.price_schema_version = 2;
1358
- }
1359
- product.selling_price = (0, import_utils3.composeLinePrice)({
1360
- mainPrice: newMainSellingPrice,
1361
- bundle: product.product_bundle
1362
- });
1363
- }
1463
+ this.normalizeDiscountedOrderPrices(nextDiscountList);
1364
1464
  import_Order.OrderModule.populateSavedAmounts(tempOrder.products, nextDiscountList);
1365
1465
  await (discountModule == null ? void 0 : discountModule.setDiscountList(nextDiscountList));
1366
1466
  tempOrder.discount_list = (nextDiscountList || []).filter((d) => d.isSelected);
1367
1467
  const afterApplyTarget = this.store.order.getDiscountList().find((d) => d.id === params.discountId) || null;
1368
1468
  await this.store.order.recalculateSummary({ createIfMissing: true });
1369
1469
  this.store.order.persistTempOrder();
1370
- const finalSummary = ((_f = this.store.order.getTempOrder()) == null ? void 0 : _f.summary) || null;
1371
- const finalProduct = ((_h = (_g = this.store.order.getTempOrder()) == null ? void 0 : _g.products) == null ? void 0 : _h[0]) || null;
1470
+ const finalSummary = ((_c = this.store.order.getTempOrder()) == null ? void 0 : _c.summary) || null;
1471
+ const finalProduct = ((_e = (_d = this.store.order.getTempOrder()) == null ? void 0 : _d.products) == null ? void 0 : _e[0]) || null;
1372
1472
  this.logMethodSuccess("setDiscountSelected", params);
1373
1473
  const finalTarget = this.store.order.getDiscountList().find((d) => d.id === params.discountId) || null;
1374
1474
  return this.store.order.getDiscountList();
@@ -1377,6 +1477,51 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1377
1477
  throw error;
1378
1478
  }
1379
1479
  }
1480
+ /**
1481
+ * 统一 VenueBooking 折扣后的订单行价。
1482
+ *
1483
+ * Rules 的 `discount_list.amount` 是已经按适用规则计算出的每件优惠额;它是这里唯一
1484
+ * 的折扣金额来源。使用智能定价写入的 source_product_price 减去该金额,再同步
1485
+ * selling_price 与 Summary 使用的 main_product_selling_price,避免 UI 的购物车行价、
1486
+ * 小计和提交订单各自读取到不同的价格字段。
1487
+ */
1488
+ normalizeDiscountedOrderPrices(discountList) {
1489
+ var _a;
1490
+ const order = this.store.order;
1491
+ if (!order)
1492
+ return;
1493
+ const tempOrder = order.ensureTempOrder();
1494
+ const selectedResourceIds = new Set(
1495
+ (discountList || []).filter((discount) => discount.isSelected).map((discount) => discount.id)
1496
+ );
1497
+ for (const product of tempOrder.products) {
1498
+ if ((_a = product._origin) == null ? void 0 : _a.isManualDiscount)
1499
+ continue;
1500
+ product.discount_list = (product.discount_list || []).filter((detail) => {
1501
+ var _a2;
1502
+ const resourceId = ((_a2 = detail.discount) == null ? void 0 : _a2.resource_id) ?? detail.id;
1503
+ return resourceId != null && selectedResourceIds.has(resourceId);
1504
+ });
1505
+ const totalPerUnitDiscount = (product.discount_list || []).reduce(
1506
+ (total, detail) => total.plus(detail.amount || 0),
1507
+ new import_decimal.default(0)
1508
+ );
1509
+ const metadata = product.metadata || (product.metadata = {});
1510
+ const optionSum = (0, import_utils3.sumOptionUnitPrice)(product.product_option_item);
1511
+ const sourcePrice = metadata.source_product_price ?? (metadata.main_product_original_price != null ? new import_decimal.default(Number(metadata.main_product_original_price) || 0).minus(optionSum).toFixed(2) : product.original_price ?? "0");
1512
+ const nextSourceSellingPrice = import_decimal.default.max(
1513
+ 0,
1514
+ new import_decimal.default(Number(sourcePrice) || 0).minus(totalPerUnitDiscount)
1515
+ ).toDecimalPlaces(2).toString();
1516
+ const nextMainSellingPrice = new import_decimal.default(Number(nextSourceSellingPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
1517
+ metadata.main_product_selling_price = nextMainSellingPrice;
1518
+ metadata.price_schema_version = 2;
1519
+ product.selling_price = (0, import_utils3.composeLinePrice)({
1520
+ mainPrice: nextMainSellingPrice,
1521
+ bundle: product.product_bundle
1522
+ });
1523
+ }
1524
+ }
1380
1525
  async addNewOrder() {
1381
1526
  this.logMethodStart("addNewOrder");
1382
1527
  try {
@@ -1456,7 +1601,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1456
1601
  }
1457
1602
  }
1458
1603
  async addProductToOrder(product) {
1459
- var _a;
1604
+ var _a, _b;
1460
1605
  this.logMethodStart("addProductToOrder", {
1461
1606
  product_id: product.product_id,
1462
1607
  product_variant_id: product.product_variant_id
@@ -1464,18 +1609,18 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1464
1609
  try {
1465
1610
  if (!this.store.order)
1466
1611
  throw new Error("order 模块未初始化");
1467
- if (!((_a = product.metadata) == null ? void 0 : _a.venue_booking) && this.store.quotation && product.product_id != null) {
1468
- const quotationPrice = this.store.quotation.getPriceForProduct({
1469
- productId: product.product_id,
1470
- variantId: product.product_variant_id ?? void 0,
1471
- datetime: (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
1612
+ if (!((_a = product.metadata) == null ? void 0 : _a.venue_booking) && product.product_id != null) {
1613
+ const smartPrice = (0, import_smartPricing.getPriceForProductAtDatetime)({
1614
+ products: this.getAllProductsForSmartPricing(),
1615
+ context: this.buildSmartPricingContext(),
1616
+ productId: Number(product.product_id),
1617
+ datetime: (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss"),
1618
+ fallbackPrice: product.selling_price
1472
1619
  });
1473
- if (quotationPrice !== null) {
1474
- product.selling_price = quotationPrice;
1475
- product.original_price = quotationPrice;
1476
- } else if (product.selling_price != null) {
1477
- product.original_price = product.selling_price;
1478
- }
1620
+ product.selling_price = smartPrice;
1621
+ product.original_price = smartPrice;
1622
+ } else if (product.selling_price != null && !((_b = product.metadata) == null ? void 0 : _b.venue_booking)) {
1623
+ product.original_price = product.selling_price;
1479
1624
  }
1480
1625
  const products = await this.store.order.addProductToOrder(product);
1481
1626
  await this.refreshItemRuleQuantityLimits();
@@ -1942,6 +2087,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1942
2087
  };
1943
2088
  var VenueBookingImpl = _VenueBookingImpl;
1944
2089
  VenueBookingImpl.OPEN_DATA_CACHE_TTL = 5 * 60 * 1e3;
2090
+ VenueBookingImpl.PISELL1_LOGIN_SUCCESS = "pisell1.login.success";
1945
2091
  // ─── UI 状态缓存(按 cacheId 分桶,sessionStorage) ───
1946
2092
  //
1947
2093
  // 用于物料层持久化 UI 层面的轻量状态(如当前步骤、登录回跳意图等)。
@@ -6,7 +6,9 @@ export declare enum VenueBookingHooks {
6
6
  onDestroy = "venueBooking:onDestroy",
7
7
  onRetryInit = "venueBooking:onRetryInit",
8
8
  onRefresh = "venueBooking:onRefresh",
9
- onCartValidationChanged = "venueBooking:onCartValidationChanged"
9
+ onCartValidationChanged = "venueBooking:onCartValidationChanged",
10
+ /** 登录客户的智能定价、营销配置与订单汇总已完成刷新 */
11
+ onSmartPricingUpdated = "venueBooking:onSmartPricingUpdated"
10
12
  }
11
13
  export type VenueBookingStatus = 'idle' | 'initializing' | 'ready' | 'error';
12
14
  export interface VenueBookingEntryContext {
@@ -29,6 +29,7 @@ var VenueBookingHooks = /* @__PURE__ */ ((VenueBookingHooks2) => {
29
29
  VenueBookingHooks2["onRetryInit"] = "venueBooking:onRetryInit";
30
30
  VenueBookingHooks2["onRefresh"] = "venueBooking:onRefresh";
31
31
  VenueBookingHooks2["onCartValidationChanged"] = "venueBooking:onCartValidationChanged";
32
+ VenueBookingHooks2["onSmartPricingUpdated"] = "venueBooking:onSmartPricingUpdated";
32
33
  return VenueBookingHooks2;
33
34
  })(VenueBookingHooks || {});
34
35
  var DEFAULT_SLOT_CONFIG = {
@@ -1,11 +1,11 @@
1
1
  import type { VenueBookingSlotConfig, VenueResourceRawData, VenueDateSummaryItem, ResourceProductMapping } from '../types';
2
- import type { QuotationModule } from '../../../modules/Quotation';
3
2
  export declare function buildDateRangeSummary(params: {
4
3
  startDate: string;
5
4
  endDate: string;
6
5
  config: VenueBookingSlotConfig;
7
6
  rawResources: VenueResourceRawData[];
8
7
  resourceProductMap: Map<number | string, ResourceProductMapping[]>;
9
- quotationModule?: QuotationModule;
10
8
  resolveConfig?: (date: string) => VenueBookingSlotConfig;
9
+ /** 按日期构建 slot 智能定价 Map */
10
+ buildSlotPriceMap?: (date: string, config: VenueBookingSlotConfig, productIds: number[]) => Map<string, string | null>;
11
11
  }): VenueDateSummaryItem[];
@@ -41,28 +41,23 @@ function buildDateRangeSummary(params) {
41
41
  config,
42
42
  rawResources,
43
43
  resourceProductMap,
44
- quotationModule,
45
- resolveConfig
44
+ resolveConfig,
45
+ buildSlotPriceMap
46
46
  } = params;
47
47
  const result = [];
48
- const productIds = quotationModule ? [...new Set([...resourceProductMap.values()].flat().map((m) => m.productId))] : [];
48
+ const productIds = buildSlotPriceMap ? [...new Set([...resourceProductMap.values()].flat().map((m) => m.productId))] : [];
49
49
  let cursor = (0, import_dayjs.default)(startDate);
50
50
  const end = (0, import_dayjs.default)(endDate);
51
51
  while (!cursor.isAfter(end)) {
52
52
  const date = cursor.format("YYYY-MM-DD");
53
53
  const effectiveConfig = (resolveConfig == null ? void 0 : resolveConfig(date)) || config;
54
- let quotationPriceMap;
55
- if (quotationModule && productIds.length) {
56
- const timeLabels = (0, import_timeSlot.generateTimeLabels)(effectiveConfig);
57
- const timePoints = timeLabels.map((label) => `${date} ${label}`);
58
- quotationPriceMap = quotationModule.buildProductPriceMap({ productIds, timePoints });
59
- }
54
+ const slotPriceMap = buildSlotPriceMap && productIds.length ? buildSlotPriceMap(date, effectiveConfig, productIds) : void 0;
60
55
  const grid = (0, import_timeSlot.buildTimeSlotGrid)({
61
56
  date,
62
57
  config: effectiveConfig,
63
58
  rawResources,
64
59
  resourceProductMap,
65
- quotationPriceMap
60
+ slotPriceMap
66
61
  });
67
62
  let totalSlots = 0;
68
63
  let availableSlots = 0;
@@ -5,12 +5,6 @@ export declare function mergeConsecutiveSlots(slots: VenueSlotSelection[]): Merg
5
5
  export declare function buildPriceBreakdown(params: {
6
6
  group: MergedSlotGroup;
7
7
  productId: number;
8
- quotation?: {
9
- getQuotationShelfId: (p: {
10
- productId: number;
11
- datetime: string;
12
- }) => number;
13
- };
14
8
  }): PriceBreakdownEntry[];
15
9
  export interface BuildVenueBookingParams {
16
10
  group: MergedSlotGroup;
@@ -82,17 +82,16 @@ function mergeConsecutiveSlots(slots) {
82
82
  return groups;
83
83
  }
84
84
  function buildPriceBreakdown(params) {
85
- const { group, productId, quotation } = params;
85
+ const { group } = params;
86
86
  if (!group.slots.length)
87
87
  return [];
88
88
  const entries = [];
89
89
  for (const slot of group.slots) {
90
90
  const unitPrice = Number(slot.price || "0");
91
- const shelfId = quotation ? quotation.getQuotationShelfId({ productId, datetime: slot.startTime }) : 0;
92
91
  const startHm = (0, import_dayjs.default)(slot.startTime, "YYYY-MM-DD HH:mm").format("YYYY-MM-DD HH:mm");
93
92
  const endHm = (0, import_dayjs.default)(slot.endTime, "YYYY-MM-DD HH:mm").format("YYYY-MM-DD HH:mm");
94
93
  const last = entries[entries.length - 1];
95
- if (last && last.unit_price === unitPrice && last.quotation_shelf_id === shelfId && last.end_time === startHm) {
94
+ if (last && last.unit_price === unitPrice && last.quotation_shelf_id === 0 && last.end_time === startHm) {
96
95
  last.end_time = endHm;
97
96
  last.quantity += 1;
98
97
  } else {
@@ -100,7 +99,7 @@ function buildPriceBreakdown(params) {
100
99
  start_time: startHm,
101
100
  end_time: endHm,
102
101
  unit_price: unitPrice,
103
- quotation_shelf_id: shelfId,
102
+ quotation_shelf_id: 0,
104
103
  quantity: 1
105
104
  });
106
105
  }