@pisell/pisellos 0.0.500 → 0.0.502

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 (68) hide show
  1. package/dist/model/strategy/adapter/itemRule/adapter.d.ts +8 -0
  2. package/dist/model/strategy/adapter/itemRule/adapter.js +52 -8
  3. package/dist/model/strategy/adapter/itemRule/examples.d.ts +15 -0
  4. package/dist/model/strategy/adapter/itemRule/examples.js +68 -1
  5. package/dist/model/strategy/adapter/itemRule/index.d.ts +1 -1
  6. package/dist/model/strategy/adapter/itemRule/index.js +1 -1
  7. package/dist/model/strategy/adapter/itemRule/type.d.ts +20 -1
  8. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  9. package/dist/modules/Order/index.d.ts +10 -0
  10. package/dist/modules/Order/index.js +64 -40
  11. package/dist/modules/Order/types.d.ts +13 -1
  12. package/dist/modules/Order/utils.d.ts +36 -1
  13. package/dist/modules/Order/utils.js +120 -24
  14. package/dist/modules/ProductList/index.js +2 -2
  15. package/dist/modules/Quotation/index.js +6 -3
  16. package/dist/modules/SalesSummary/types.d.ts +2 -1
  17. package/dist/modules/SalesSummary/utils.js +10 -10
  18. package/dist/modules/Schedule/utils.d.ts +1 -1
  19. package/dist/solution/ScanOrder/index.d.ts +22 -0
  20. package/dist/solution/ScanOrder/index.js +863 -369
  21. package/dist/solution/ScanOrder/types.d.ts +31 -8
  22. package/dist/solution/ScanOrder/utils.d.ts +26 -0
  23. package/dist/solution/ScanOrder/utils.js +191 -44
  24. package/dist/solution/VenueBooking/index.d.ts +42 -5
  25. package/dist/solution/VenueBooking/index.js +692 -280
  26. package/dist/solution/VenueBooking/types.d.ts +23 -0
  27. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +2 -1
  28. package/dist/solution/VenueBooking/utils/dateSummary.js +7 -5
  29. package/dist/solution/VenueBooking/utils/resource.d.ts +11 -1
  30. package/dist/solution/VenueBooking/utils/resource.js +57 -21
  31. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +5 -0
  32. package/dist/solution/VenueBooking/utils/slotMerge.js +33 -12
  33. package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -1
  34. package/dist/solution/VenueBooking/utils/timeSlot.js +259 -62
  35. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +8 -0
  36. package/lib/model/strategy/adapter/itemRule/adapter.js +41 -2
  37. package/lib/model/strategy/adapter/itemRule/examples.d.ts +15 -0
  38. package/lib/model/strategy/adapter/itemRule/examples.js +47 -0
  39. package/lib/model/strategy/adapter/itemRule/index.d.ts +1 -1
  40. package/lib/model/strategy/adapter/itemRule/index.js +2 -0
  41. package/lib/model/strategy/adapter/itemRule/type.d.ts +20 -1
  42. package/lib/modules/Order/index.d.ts +10 -0
  43. package/lib/modules/Order/index.js +40 -43
  44. package/lib/modules/Order/types.d.ts +13 -1
  45. package/lib/modules/Order/utils.d.ts +36 -1
  46. package/lib/modules/Order/utils.js +100 -21
  47. package/lib/modules/ProductList/index.js +3 -2
  48. package/lib/modules/Quotation/index.js +3 -0
  49. package/lib/modules/SalesSummary/types.d.ts +2 -1
  50. package/lib/modules/SalesSummary/utils.js +2 -2
  51. package/lib/modules/Schedule/utils.d.ts +1 -1
  52. package/lib/solution/ScanOrder/index.d.ts +22 -0
  53. package/lib/solution/ScanOrder/index.js +376 -21
  54. package/lib/solution/ScanOrder/types.d.ts +31 -8
  55. package/lib/solution/ScanOrder/utils.d.ts +26 -0
  56. package/lib/solution/ScanOrder/utils.js +128 -19
  57. package/lib/solution/VenueBooking/index.d.ts +42 -5
  58. package/lib/solution/VenueBooking/index.js +356 -84
  59. package/lib/solution/VenueBooking/types.d.ts +23 -0
  60. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +2 -1
  61. package/lib/solution/VenueBooking/utils/dateSummary.js +14 -5
  62. package/lib/solution/VenueBooking/utils/resource.d.ts +11 -1
  63. package/lib/solution/VenueBooking/utils/resource.js +15 -4
  64. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +5 -0
  65. package/lib/solution/VenueBooking/utils/slotMerge.js +29 -12
  66. package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -1
  67. package/lib/solution/VenueBooking/utils/timeSlot.js +182 -43
  68. package/package.json +1 -1
@@ -40,6 +40,7 @@ var import_types2 = require("../BookingByStep/types");
40
40
  var import_ProductList = require("../../modules/ProductList");
41
41
  var import_Date = require("../../modules/Date");
42
42
  var import_Schedule = require("../../modules/Schedule");
43
+ var import_utils2 = require("../../modules/Schedule/utils");
43
44
  var import_Quotation = require("../../modules/Quotation");
44
45
  var import_OpenData = require("../../modules/OpenData");
45
46
  var import_types3 = require("../../modules/Account/types");
@@ -49,7 +50,7 @@ var import_resource = require("./utils/resource");
49
50
  var import_timeSlot = require("./utils/timeSlot");
50
51
  var import_dateSummary = require("./utils/dateSummary");
51
52
  var import_slotMerge = require("./utils/slotMerge");
52
- var import_utils2 = require("../../modules/Order/utils");
53
+ var import_utils3 = require("../../modules/Order/utils");
53
54
  var import_Order = require("../../modules/Order");
54
55
  var import_types4 = require("../RegisterAndLogin/types");
55
56
  var import_decimal = __toESM(require("decimal.js"));
@@ -96,6 +97,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
96
97
  }
97
98
  };
98
99
  this.otherParams = {};
100
+ this.baseSlotConfig = { ...import_types.DEFAULT_SLOT_CONFIG };
99
101
  this.itemRuleEvaluator = new import_itemRule.ItemRuleEvaluator();
100
102
  this.itemRuleConfigs = [];
101
103
  this.itemRuleConfigsPromise = null;
@@ -292,7 +294,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
292
294
  }
293
295
  }
294
296
  async initialize(core, options = {}) {
295
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
297
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
296
298
  this.logMethodStart("initialize");
297
299
  this.core = core;
298
300
  this.initializeOptions = options || {};
@@ -301,10 +303,11 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
301
303
  this.store.status = "initializing";
302
304
  this.store.error = null;
303
305
  this.store.rawResourceData = [];
304
- this.store.slotConfig = {
306
+ this.baseSlotConfig = {
305
307
  ...import_types.DEFAULT_SLOT_CONFIG,
306
308
  ...((_b = options.otherParams) == null ? void 0 : _b.slotConfig) || {}
307
309
  };
310
+ this.store.slotConfig = { ...this.baseSlotConfig };
308
311
  this.otherParams = options.otherParams || {};
309
312
  this.cacheId = (_c = this.otherParams) == null ? void 0 : _c.cacheId;
310
313
  this.itemRuleRuntimeConfig = ((_d = this.otherParams) == null ? void 0 : _d.venueBookingItemRule) || ((_e = this.otherParams) == null ? void 0 : _e.itemRule) || {};
@@ -425,8 +428,10 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
425
428
  if (this.store.schedule) {
426
429
  await this.store.schedule.loadAllSchedule();
427
430
  this.injectScheduleResolverToQuotation();
431
+ (_k = this.store.quotation) == null ? void 0 : _k.loadQuotations({
432
+ channel: (_j = this.otherParams) == null ? void 0 : _j.channel
433
+ });
428
434
  }
429
- this.loadQuotations();
430
435
  await this.refreshItemRuleQuantityLimits();
431
436
  this.store.status = "ready";
432
437
  console.log("[VenueBooking] 初始化完成");
@@ -624,12 +629,14 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
624
629
  }
625
630
  this.store.rawResourceData = rawData;
626
631
  for (const item of rawData) {
627
- const mapping = this.resourceProductMap.get(item.resourceId);
628
- if (mapping && item.main_field) {
629
- mapping.resourceName = item.main_field;
630
- }
631
- if (mapping && item.resource_form_id) {
632
- mapping.formId = item.resource_form_id;
632
+ const mappings = this.resourceProductMap.get(item.resourceId);
633
+ if (!mappings)
634
+ continue;
635
+ for (const mapping of mappings) {
636
+ if (item.main_field)
637
+ mapping.resourceName = item.main_field;
638
+ if (item.resource_form_id)
639
+ mapping.formId = item.resource_form_id;
633
640
  }
634
641
  }
635
642
  this.logMethodSuccess("fetchResourceAvailability", {
@@ -643,27 +650,91 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
643
650
  }
644
651
  }
645
652
  // ─── 日期概览 & 时间槽矩阵(纯计算,不发请求) ───
653
+ getOperatingHoursScheduleIds() {
654
+ var _a, _b;
655
+ const openData = this.otherParams.openData || ((_b = (_a = this.store.openData) == null ? void 0 : _a.getOpenData) == null ? void 0 : _b.call(_a)) || null;
656
+ const rawIds = openData == null ? void 0 : openData["availability.operating_hours"];
657
+ if (!Array.isArray(rawIds))
658
+ return [];
659
+ return [...new Set(
660
+ rawIds.map((id) => Number(id)).filter((id) => Number.isFinite(id) && id > 0).map((id) => Math.floor(id))
661
+ )];
662
+ }
663
+ resolveSlotConfigForDate(date) {
664
+ var _a;
665
+ const nextSlotConfig = { ...this.baseSlotConfig };
666
+ if (!date || !this.store.schedule)
667
+ return nextSlotConfig;
668
+ const operatingHoursScheduleIds = this.getOperatingHoursScheduleIds();
669
+ if (!operatingHoursScheduleIds.length)
670
+ return nextSlotConfig;
671
+ const scheduleList = this.store.schedule.getScheduleListByIds(
672
+ operatingHoursScheduleIds
673
+ );
674
+ if (!scheduleList.length)
675
+ return nextSlotConfig;
676
+ const timeRangeMap = (0, import_utils2.calcMinTimeMaxTimeBySchedules)(
677
+ scheduleList,
678
+ void 0,
679
+ date
680
+ );
681
+ let earliestStart = null;
682
+ let latestEnd = null;
683
+ for (const schedule of scheduleList) {
684
+ const dateRangeList = ((_a = timeRangeMap == null ? void 0 : timeRangeMap[schedule.id]) == null ? void 0 : _a.dateRangeFormat) || [];
685
+ for (const range of dateRangeList) {
686
+ const start = (0, import_dayjs.default)(range == null ? void 0 : range.start);
687
+ const end = (0, import_dayjs.default)(range == null ? void 0 : range.end);
688
+ if (!start.isValid() || !end.isValid())
689
+ continue;
690
+ if (!earliestStart || start.isBefore(earliestStart)) {
691
+ earliestStart = start;
692
+ }
693
+ if (!latestEnd || end.isAfter(latestEnd)) {
694
+ latestEnd = end;
695
+ }
696
+ }
697
+ }
698
+ if (!earliestStart || !latestEnd)
699
+ return nextSlotConfig;
700
+ return {
701
+ ...nextSlotConfig,
702
+ businessStartTime: earliestStart.format("HH:mm"),
703
+ businessEndTime: latestEnd.format("HH:mm")
704
+ };
705
+ }
706
+ syncOperatingHoursToSlotConfig(date) {
707
+ const resolvedSlotConfig = this.resolveSlotConfigForDate(date);
708
+ this.store.slotConfig = resolvedSlotConfig;
709
+ return resolvedSlotConfig;
710
+ }
646
711
  getDateRangeSummary(params) {
647
712
  return (0, import_dateSummary.buildDateRangeSummary)({
648
713
  startDate: params.startDate,
649
714
  endDate: params.endDate,
650
- config: this.store.slotConfig,
715
+ config: this.baseSlotConfig,
651
716
  rawResources: this.store.rawResourceData,
652
717
  resourceProductMap: this.resourceProductMap,
653
- quotationModule: this.store.quotation
718
+ quotationModule: this.store.quotation,
719
+ resolveConfig: (date) => this.resolveSlotConfigForDate(date)
654
720
  });
655
721
  }
656
722
  getTimeSlotGrid(date) {
723
+ const resolvedSlotConfig = this.syncOperatingHoursToSlotConfig(date);
657
724
  let quotationPriceMap;
658
725
  if (this.store.quotation) {
659
- const productIds = [...new Set([...this.resourceProductMap.values()].map((m) => m.productId))];
660
- const timeLabels = (0, import_timeSlot.generateTimeLabels)(this.store.slotConfig);
726
+ const productIds = [
727
+ ...new Set(
728
+ [...this.resourceProductMap.values()].flat().map((m) => m.productId)
729
+ )
730
+ ];
731
+ const timeLabels = (0, import_timeSlot.generateTimeLabels)(resolvedSlotConfig);
661
732
  const timePoints = timeLabels.map((label) => `${date} ${label}`);
662
733
  quotationPriceMap = this.store.quotation.buildProductPriceMap({ productIds, timePoints });
663
734
  }
664
735
  return (0, import_timeSlot.buildTimeSlotGrid)({
665
736
  date,
666
- config: this.store.slotConfig,
737
+ config: resolvedSlotConfig,
667
738
  rawResources: this.store.rawResourceData,
668
739
  resourceProductMap: this.resourceProductMap,
669
740
  quotationPriceMap
@@ -673,19 +744,31 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
673
744
  /**
674
745
  * 切换单个时段的选中状态(选中/取消)。
675
746
  * 内部自动处理连续时段的合并与拆分,订单是唯一真相源。
747
+ *
748
+ * slot.productId 指定当前操作针对的是该 resourceId 下的哪一个商品。
749
+ * 同一资源下不同 productId 之间互相隔离,不会相互合并。
676
750
  */
677
751
  async toggleSlot(slot) {
678
752
  this.logMethodStart("toggleSlot", {
679
753
  resourceId: slot.resourceId,
754
+ productId: slot.productId,
680
755
  startTime: slot.startTime
681
756
  });
682
757
  try {
683
- const mapping = this.resourceProductMap.get(slot.resourceId);
684
- if (!mapping)
758
+ const mappings = this.resourceProductMap.get(slot.resourceId);
759
+ if (!mappings || !mappings.length) {
685
760
  throw new Error(`未找到资源 ${slot.resourceId} 的商品映射`);
761
+ }
762
+ const mapping = mappings.find((m) => m.productId === slot.productId);
763
+ if (!mapping) {
764
+ throw new Error(`资源 ${slot.resourceId} 未关联商品 ${slot.productId}`);
765
+ }
686
766
  if (!this.store.order)
687
767
  throw new Error("order 模块未初始化");
688
- const currentSlots = this.getSelectedSlotsForResource(slot.resourceId);
768
+ const currentSlots = this.getSelectedSlotsForResourceProduct(
769
+ slot.resourceId,
770
+ slot.productId
771
+ );
689
772
  const existIndex = currentSlots.findIndex((s) => s.startTime === slot.startTime);
690
773
  let nextSlots;
691
774
  if (existIndex !== -1) {
@@ -693,13 +776,18 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
693
776
  } else {
694
777
  nextSlots = [...currentSlots, slot];
695
778
  }
696
- await this.reconcileOrderForResource(slot.resourceId, nextSlots);
779
+ await this.reconcileOrderForResourceProduct(
780
+ slot.resourceId,
781
+ slot.productId,
782
+ nextSlots
783
+ );
697
784
  const products = this.store.order.getOrderProducts();
698
785
  await this.refreshItemRuleQuantityLimits();
699
786
  await this.refreshCartValidationPassed();
700
787
  this.logMethodSuccess("toggleSlot", {
701
788
  action: existIndex !== -1 ? "remove" : "add",
702
789
  resourceId: slot.resourceId,
790
+ productId: slot.productId,
703
791
  slotCount: nextSlots.length
704
792
  });
705
793
  return products;
@@ -710,33 +798,106 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
710
798
  }
711
799
  /**
712
800
  * 获取某资源当前选中的所有独立时段(从订单中解析)。
801
+ * 不传 productId 时返回该资源下所有商品的选中时段;传了则精确匹配。
713
802
  */
714
- getSelectedSlotsForResource(resourceId) {
803
+ getSelectedSlotsForResource(resourceId, productId) {
715
804
  var _a;
716
805
  const tempOrder = (_a = this.store.order) == null ? void 0 : _a.getTempOrder();
717
806
  if (!tempOrder)
718
807
  return [];
719
- const venueProducts = tempOrder.products.filter(
720
- (p) => {
721
- var _a2, _b;
722
- return ((_a2 = p.metadata) == null ? void 0 : _a2.venue_booking) && String((_b = p.metadata) == null ? void 0 : _b.resource_id) === String(resourceId);
723
- }
724
- );
808
+ const venueProducts = tempOrder.products.filter((p) => {
809
+ var _a2, _b;
810
+ if (!((_a2 = p.metadata) == null ? void 0 : _a2.venue_booking))
811
+ return false;
812
+ if (String((_b = p.metadata) == null ? void 0 : _b.resource_id) !== String(resourceId))
813
+ return false;
814
+ if (productId != null && Number(p.product_id) !== Number(productId))
815
+ return false;
816
+ return true;
817
+ });
725
818
  const slotDuration = this.store.slotConfig.slotDurationMinutes;
726
819
  const slots = [];
727
820
  for (const product of venueProducts) {
728
- slots.push(...(0, import_slotMerge.expandMergedSlotToIndividual)(product, slotDuration));
821
+ slots.push(
822
+ ...(0, import_slotMerge.expandMergedSlotToIndividual)(product, slotDuration).map((s) => ({
823
+ ...s,
824
+ productId: Number(product.product_id)
825
+ }))
826
+ );
729
827
  }
730
828
  return slots.sort((a, b) => a.startTime.localeCompare(b.startTime));
731
829
  }
830
+ /** getSelectedSlotsForResource 的 (resourceId, productId) 精确版,内部使用。 */
831
+ getSelectedSlotsForResourceProduct(resourceId, productId) {
832
+ return this.getSelectedSlotsForResource(resourceId, productId);
833
+ }
732
834
  /**
733
835
  * 判断某个时段是否已选中。
836
+ * 不传 productId 时:只要该资源下任一商品在 startTime 被选中即返回 true;传了则精确匹配。
837
+ */
838
+ isSlotSelected(resourceId, startTime, productId) {
839
+ return this.getSelectedSlotsForResource(resourceId, productId).some((s) => s.startTime === startTime);
840
+ }
841
+ /**
842
+ * 判断指定 (resourceId, productId, startTime) 格子是否应因其它已选项而被禁用。
843
+ * 规则:
844
+ * 1) 同一 resourceId 下若已选了另一个 productId 的同 startTime → 禁用
845
+ * 2) 当前 resource 是组合资源:若其任一 child resource 在 startTime 被选中 → 禁用
846
+ * 3) 当前 resource 是某些组合资源的 child:若该组合资源在 startTime 被选中 → 禁用
847
+ * 4) 两个组合资源的 child 集合有交集,且对方在该 startTime 被选中 → 禁用
734
848
  */
735
- isSlotSelected(resourceId, startTime) {
736
- return this.getSelectedSlotsForResource(resourceId).some((s) => s.startTime === startTime);
849
+ isSlotDisabledBySelection(params) {
850
+ const { resourceId, productId, startTime } = params;
851
+ const allSelected = this.getAllSelectedSlots();
852
+ const getSelectedAt = (resId) => {
853
+ const list = allSelected.get(resId) || [];
854
+ return list.filter((s) => s.startTime === startTime);
855
+ };
856
+ const sameResourceSelections = getSelectedAt(resourceId);
857
+ if (sameResourceSelections.some((s) => Number(s.productId) !== Number(productId))) {
858
+ return true;
859
+ }
860
+ const getCombinedChildIds = (resId) => {
861
+ const raw = this.store.rawResourceData.find(
862
+ (r) => String(r.resourceId) === String(resId)
863
+ );
864
+ const combined = raw == null ? void 0 : raw.combined_resource;
865
+ if (combined && combined.status === 1 && Array.isArray(combined.resource_ids)) {
866
+ return combined.resource_ids;
867
+ }
868
+ return [];
869
+ };
870
+ const currentChildIds = getCombinedChildIds(resourceId);
871
+ const currentIsCombined = currentChildIds.length > 0;
872
+ if (currentIsCombined) {
873
+ for (const childId of currentChildIds) {
874
+ if (getSelectedAt(childId).length > 0)
875
+ return true;
876
+ }
877
+ }
878
+ for (const [selectedResId, slots] of allSelected) {
879
+ if (!slots.some((s) => s.startTime === startTime))
880
+ continue;
881
+ if (String(selectedResId) === String(resourceId))
882
+ continue;
883
+ const selectedChildIds = getCombinedChildIds(selectedResId);
884
+ if (selectedChildIds.length === 0)
885
+ continue;
886
+ if (selectedChildIds.some((id) => String(id) === String(resourceId))) {
887
+ return true;
888
+ }
889
+ if (currentIsCombined) {
890
+ const intersect = selectedChildIds.some(
891
+ (id) => currentChildIds.some((cid) => String(cid) === String(id))
892
+ );
893
+ if (intersect)
894
+ return true;
895
+ }
896
+ }
897
+ return false;
737
898
  }
738
899
  /**
739
- * 获取所有已选时段(按资源分组)。
900
+ * 获取所有已选时段(按资源分组)。每个 slot 上带 productId,便于 UI 做 per-product 判定。
740
901
  */
741
902
  getAllSelectedSlots() {
742
903
  var _a, _b;
@@ -753,8 +914,14 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
753
914
  const resourceId = (_b = product.metadata) == null ? void 0 : _b.resource_id;
754
915
  if (resourceId == null)
755
916
  continue;
917
+ const productId = Number(product.product_id);
756
918
  const existing = result.get(resourceId) || [];
757
- existing.push(...(0, import_slotMerge.expandMergedSlotToIndividual)(product, slotDuration));
919
+ existing.push(
920
+ ...(0, import_slotMerge.expandMergedSlotToIndividual)(product, slotDuration).map((s) => ({
921
+ ...s,
922
+ productId
923
+ }))
924
+ );
758
925
  result.set(resourceId, existing);
759
926
  }
760
927
  for (const [key, slots] of result) {
@@ -763,24 +930,33 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
763
930
  return result;
764
931
  }
765
932
  /**
766
- * 对指定资源的订单商品进行 reconcile:
933
+ * 对指定 (resourceId, productId) 的订单商品进行 reconcile:
767
934
  * 清除旧商品 → 合并连续时段 → 重新写入。
935
+ * 同一场地下不同商品互不干扰,各自单独 reconcile。
768
936
  */
769
- async reconcileOrderForResource(resourceId, slots) {
770
- const mapping = this.resourceProductMap.get(resourceId);
771
- if (!mapping || !this.store.order)
937
+ async reconcileOrderForResourceProduct(resourceId, productId, slots) {
938
+ const mappings = this.resourceProductMap.get(resourceId);
939
+ if (!mappings || !mappings.length || !this.store.order)
940
+ return;
941
+ const mapping = mappings.find((m) => m.productId === productId);
942
+ if (!mapping)
772
943
  return;
773
944
  const tempOrder = this.store.order.ensureTempOrder();
945
+ const matchesCurrent = (meta, pid) => !!(meta == null ? void 0 : meta.venue_booking) && String(meta == null ? void 0 : meta.resource_id) === String(resourceId) && Number(pid) === Number(productId);
774
946
  tempOrder.products = tempOrder.products.filter(
775
- (p) => {
776
- var _a, _b;
777
- return !(((_a = p.metadata) == null ? void 0 : _a.venue_booking) && String((_b = p.metadata) == null ? void 0 : _b.resource_id) === String(resourceId));
778
- }
947
+ (p) => !matchesCurrent(p.metadata, p.product_id)
779
948
  );
780
949
  tempOrder.bookings = (tempOrder.bookings || []).filter(
781
950
  (b) => {
782
- var _a, _b;
783
- return !(((_a = b.metadata) == null ? void 0 : _a.venue_booking) && String((_b = b.metadata) == null ? void 0 : _b.resource_id) === String(resourceId));
951
+ var _a, _b, _c, _d;
952
+ if (!((_a = b.metadata) == null ? void 0 : _a.venue_booking))
953
+ return true;
954
+ if (String((_b = b.metadata) == null ? void 0 : _b.resource_id) !== String(resourceId))
955
+ return true;
956
+ const bookingProductId = ((_c = b.metadata) == null ? void 0 : _c.product_id) ?? ((_d = b == null ? void 0 : b.product) == null ? void 0 : _d.product_id);
957
+ if (bookingProductId == null)
958
+ return false;
959
+ return Number(bookingProductId) !== Number(productId);
784
960
  }
785
961
  );
786
962
  if (!slots.length) {
@@ -793,16 +969,39 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
793
969
  const rawResource = this.store.rawResourceData.find(
794
970
  (r) => String(r.resourceId) === String(resourceId)
795
971
  );
972
+ const childRawResources = this.getCombinedChildRawResources(rawResource);
796
973
  const venueProduct = this.getVenueProducts().find((p) => p.id === mapping.productId);
797
974
  tempOrder.bookings = tempOrder.bookings || [];
798
975
  for (let i = 0; i < merged.length; i++) {
799
976
  const group = merged[i];
800
977
  const identityKey = (0, import_slotMerge.buildVenueIdentityKey)(resourceId, i);
801
- const bookingUuid = (0, import_utils2.createUuidV4)();
978
+ const bookingUuid = (0, import_utils3.createUuidV4)();
802
979
  const startMoment = (0, import_dayjs.default)(group.startTime, "YYYY-MM-DD HH:mm");
803
980
  const endMoment = (0, import_dayjs.default)(group.endTime, "YYYY-MM-DD HH:mm");
804
981
  const duration = endMoment.diff(startMoment, "minute");
805
982
  const customDepositData = cloneCustomDepositData(venueProduct == null ? void 0 : venueProduct.custom_deposit_data);
983
+ const resourceEntry = {
984
+ relation_type: "form",
985
+ like_status: "common",
986
+ id: resourceId,
987
+ main_field: mapping.resourceName,
988
+ form_id: (rawResource == null ? void 0 : rawResource.form_id) ?? mapping.formId,
989
+ relation_id: resourceId,
990
+ capacity: 1,
991
+ metadata: {}
992
+ };
993
+ if (childRawResources && childRawResources.length) {
994
+ resourceEntry.children = childRawResources.map((child) => ({
995
+ relation_type: "form",
996
+ like_status: "common",
997
+ id: child.resourceId,
998
+ main_field: child.main_field || "",
999
+ form_id: child.form_id ?? child.formId,
1000
+ relation_id: child.resourceId,
1001
+ capacity: child.capacity ?? 1,
1002
+ metadata: {}
1003
+ }));
1004
+ }
806
1005
  tempOrder.products.push(
807
1006
  (0, import_utils.normalizeOrderProduct)({
808
1007
  product_id: mapping.productId,
@@ -811,7 +1010,6 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
811
1010
  num: 1,
812
1011
  selling_price: group.totalPrice,
813
1012
  original_price: group.totalPrice,
814
- payment_price: group.totalPrice,
815
1013
  is_charge_tax: (venueProduct == null ? void 0 : venueProduct.is_charge_tax) ?? 0,
816
1014
  metadata: {
817
1015
  venue_booking: true,
@@ -850,16 +1048,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
850
1048
  sub_type: "minutes",
851
1049
  duration,
852
1050
  like_status: "common",
853
- resources: [{
854
- relation_type: "form",
855
- like_status: "common",
856
- id: resourceId,
857
- main_field: mapping.resourceName,
858
- form_id: (rawResource == null ? void 0 : rawResource.form_id) ?? mapping.formId,
859
- relation_id: resourceId,
860
- capacity: 1,
861
- metadata: {}
862
- }],
1051
+ resources: [resourceEntry],
863
1052
  schedule_id: 0,
864
1053
  select_date: startMoment.format("YYYY-MM-DD"),
865
1054
  start_date: startMoment.format("YYYY-MM-DD"),
@@ -880,7 +1069,8 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
880
1069
  mapping,
881
1070
  rawResource,
882
1071
  bookingUuid,
883
- productUid: identityKey
1072
+ productUid: identityKey,
1073
+ childResources: childRawResources
884
1074
  });
885
1075
  tempOrder.bookings.push(booking);
886
1076
  }
@@ -888,12 +1078,33 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
888
1078
  await this.store.order.recalculateSummary({ createIfMissing: true });
889
1079
  this.store.order.persistTempOrder();
890
1080
  }
1081
+ /** 给定一个父 rawResource,返回其 combined_resource.resource_ids 对应的子 rawResource 列表。 */
1082
+ getCombinedChildRawResources(rawResource) {
1083
+ if (!rawResource)
1084
+ return void 0;
1085
+ const combined = rawResource.combined_resource;
1086
+ if (!combined || combined.status !== 1 || !Array.isArray(combined.resource_ids) || !combined.resource_ids.length) {
1087
+ return void 0;
1088
+ }
1089
+ const children = [];
1090
+ for (const id of combined.resource_ids) {
1091
+ const child = this.store.rawResourceData.find(
1092
+ (r) => String(r.resourceId) === String(id)
1093
+ );
1094
+ if (child)
1095
+ children.push(child);
1096
+ }
1097
+ return children.length ? children : void 0;
1098
+ }
891
1099
  // ─── 配置 ───
892
1100
  setSlotConfig(config) {
893
- this.store.slotConfig = {
894
- ...this.store.slotConfig,
1101
+ this.baseSlotConfig = {
1102
+ ...this.baseSlotConfig,
895
1103
  ...config
896
1104
  };
1105
+ this.store.slotConfig = {
1106
+ ...this.baseSlotConfig
1107
+ };
897
1108
  }
898
1109
  getSlotConfig() {
899
1110
  return { ...this.store.slotConfig };
@@ -967,7 +1178,10 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
967
1178
  const now = (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss");
968
1179
  for (const product of tempOrder.products) {
969
1180
  if ((_b = product.metadata) == null ? void 0 : _b.venue_booking) {
970
- const mapping = this.resourceProductMap.get(product.metadata.resource_id);
1181
+ const mappings = this.resourceProductMap.get(product.metadata.resource_id);
1182
+ if (!mappings || !mappings.length)
1183
+ continue;
1184
+ const mapping = mappings.find((m) => Number(m.productId) === Number(product.product_id)) || mappings[0];
971
1185
  if (!mapping)
972
1186
  continue;
973
1187
  const slots = (0, import_slotMerge.expandMergedSlotToIndividual)(
@@ -976,6 +1190,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
976
1190
  );
977
1191
  const updatedSlots = slots.map((slot) => ({
978
1192
  ...slot,
1193
+ productId: mapping.productId,
979
1194
  price: this.store.quotation.getPriceForProduct({
980
1195
  productId: mapping.productId,
981
1196
  datetime: slot.startTime
@@ -984,7 +1199,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
984
1199
  const merged = (0, import_slotMerge.mergeConsecutiveSlots)(updatedSlots);
985
1200
  if (merged.length === 1) {
986
1201
  product.selling_price = merged[0].totalPrice;
987
- product.payment_price = merged[0].totalPrice;
1202
+ product.original_price = merged[0].totalPrice;
988
1203
  product.metadata.price_breakdown = (0, import_slotMerge.buildPriceBreakdown)({
989
1204
  group: merged[0],
990
1205
  productId: mapping.productId,
@@ -999,7 +1214,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
999
1214
  });
1000
1215
  if (quotationPrice !== null) {
1001
1216
  product.selling_price = quotationPrice;
1002
- product.payment_price = quotationPrice;
1217
+ product.original_price = quotationPrice;
1003
1218
  }
1004
1219
  }
1005
1220
  }
@@ -1085,25 +1300,21 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1085
1300
  nextDiscountList.filter((d) => d.isSelected).map((d) => d.id)
1086
1301
  );
1087
1302
  for (const product of tempOrder.products) {
1088
- if (!((_c = product.discount_list) == null ? void 0 : _c.length))
1303
+ if ((_c = product._origin) == null ? void 0 : _c.isManualDiscount)
1089
1304
  continue;
1090
- const before = product.discount_list.length;
1091
- product.discount_list = product.discount_list.filter((pd) => {
1305
+ product.discount_list = (product.discount_list || []).filter((pd) => {
1092
1306
  var _a2;
1093
1307
  const rid = ((_a2 = pd.discount) == null ? void 0 : _a2.resource_id) ?? pd.id;
1094
1308
  return rid != null && selectedResourceIds.has(rid);
1095
1309
  });
1096
- if (product.discount_list.length < before) {
1097
- const totalDiscountAmount = product.discount_list.reduce(
1098
- (sum, pd) => sum + (pd.amount || 0),
1099
- 0
1100
- );
1101
- const newPaymentPrice = new import_decimal.default(product.selling_price || 0).minus(totalDiscountAmount).toNumber();
1102
- const paymentStr = (0, import_utils.toPriceString)(newPaymentPrice);
1103
- product.payment_price = paymentStr;
1104
- if (product.metadata) {
1105
- product.metadata.main_product_selling_price = paymentStr;
1106
- }
1310
+ const totalPerUnitDiscount = (product.discount_list || []).reduce(
1311
+ (sum, pd) => sum + (pd.amount || 0),
1312
+ 0
1313
+ );
1314
+ const newSellingPrice = new import_decimal.default(product.original_price || 0).minus(totalPerUnitDiscount).toDecimalPlaces(2).toString();
1315
+ product.selling_price = newSellingPrice;
1316
+ if (product.metadata) {
1317
+ product.metadata.main_product_selling_price = newSellingPrice;
1107
1318
  }
1108
1319
  }
1109
1320
  import_Order.OrderModule.populateSavedAmounts(tempOrder.products, nextDiscountList);
@@ -1158,7 +1369,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1158
1369
  }
1159
1370
  }
1160
1371
  async submitOrder() {
1161
- var _a;
1372
+ var _a, _b, _c, _d, _e;
1162
1373
  this.logMethodStart("submitOrder");
1163
1374
  try {
1164
1375
  await this.validateBeforeSubmitByItemRule();
@@ -1166,11 +1377,15 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1166
1377
  throw new Error("venueBooking解决方案需要 order 模块支持");
1167
1378
  }
1168
1379
  const result = await this.store.order.submitTempOrder({
1169
- cacheId: this.cacheId
1380
+ cacheId: this.cacheId,
1381
+ platform: (_a = this.otherParams) == null ? void 0 : _a.platform,
1382
+ businessCode: (_b = this.otherParams) == null ? void 0 : _b.businessCode,
1383
+ channel: (_c = this.otherParams) == null ? void 0 : _c.channel,
1384
+ type: (_d = this.otherParams) == null ? void 0 : _d.type
1170
1385
  });
1171
1386
  const tempOrder = this.store.order.getTempOrder();
1172
1387
  this.logMethodSuccess("submitOrder", {
1173
- productCount: ((_a = tempOrder == null ? void 0 : tempOrder.products) == null ? void 0 : _a.length) || 0
1388
+ productCount: ((_e = tempOrder == null ? void 0 : tempOrder.products) == null ? void 0 : _e.length) || 0
1174
1389
  });
1175
1390
  return result;
1176
1391
  } catch (error) {
@@ -1195,10 +1410,9 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1195
1410
  });
1196
1411
  if (quotationPrice !== null) {
1197
1412
  product.selling_price = quotationPrice;
1198
- if (product.original_price == null) {
1199
- product.original_price = quotationPrice;
1200
- }
1201
- product.payment_price = quotationPrice;
1413
+ product.original_price = quotationPrice;
1414
+ } else if (product.selling_price != null) {
1415
+ product.original_price = product.selling_price;
1202
1416
  }
1203
1417
  }
1204
1418
  const products = await this.store.order.addProductToOrder(product);
@@ -1443,16 +1657,13 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1443
1657
  });
1444
1658
  if (productIndex === -1) {
1445
1659
  const sellingPrice = (0, import_utils.toPriceString)((sourceItem == null ? void 0 : sourceItem.price) ?? (sourceItem == null ? void 0 : sourceItem.selling_price), "0.00");
1446
- const originalPrice = (0, import_utils.toPriceString)((sourceItem == null ? void 0 : sourceItem.original_price) ?? (sourceItem == null ? void 0 : sourceItem.price) ?? sellingPrice, sellingPrice);
1447
- const paymentPrice = (0, import_utils.toPriceString)((sourceItem == null ? void 0 : sourceItem.payment_price) ?? (sourceItem == null ? void 0 : sourceItem.price) ?? sellingPrice, sellingPrice);
1448
1660
  tempOrder.products.push(
1449
1661
  (0, import_utils.normalizeOrderProduct)({
1450
1662
  product_id: productId,
1451
1663
  product_variant_id: productVariantId,
1452
1664
  num: targetQuantity,
1453
1665
  selling_price: sellingPrice,
1454
- original_price: originalPrice,
1455
- payment_price: paymentPrice,
1666
+ original_price: sellingPrice,
1456
1667
  metadata: { item_rule_prefill: true, item_rule_id: prefillItem.ruleId },
1457
1668
  _origin: { ...sourceItem || {}, item_rule_prefill: true, item_rule_id: prefillItem.ruleId }
1458
1669
  })
@@ -1475,6 +1686,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1475
1686
  }
1476
1687
  }
1477
1688
  if (hasChanges) {
1689
+ this.store.order.applyDiscount();
1478
1690
  await this.store.order.recalculateSummary({ createIfMissing: true });
1479
1691
  this.store.order.persistTempOrder();
1480
1692
  }
@@ -1561,6 +1773,53 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1561
1773
  getOtherParams() {
1562
1774
  return this.otherParams;
1563
1775
  }
1776
+ getUIStateBucketKey() {
1777
+ if (!this.cacheId)
1778
+ return null;
1779
+ return `${_VenueBookingImpl.UI_STATE_KEY_PREFIX}${this.cacheId}`;
1780
+ }
1781
+ readUIStateBucket() {
1782
+ var _a;
1783
+ const key = this.getUIStateBucketKey();
1784
+ if (!key || !((_a = this.window) == null ? void 0 : _a.sessionStorage))
1785
+ return {};
1786
+ try {
1787
+ const raw = this.window.sessionStorage.getItem(key) || "{}";
1788
+ const parsed = JSON.parse(raw);
1789
+ return parsed && typeof parsed === "object" ? parsed : {};
1790
+ } catch {
1791
+ return {};
1792
+ }
1793
+ }
1794
+ writeUIStateBucket(bucket) {
1795
+ var _a;
1796
+ const key = this.getUIStateBucketKey();
1797
+ if (!key || !((_a = this.window) == null ? void 0 : _a.sessionStorage))
1798
+ return;
1799
+ this.window.sessionStorage.setItem(key, JSON.stringify(bucket));
1800
+ }
1801
+ setUIState(key, value) {
1802
+ if (!this.getUIStateBucketKey())
1803
+ return;
1804
+ const bucket = this.readUIStateBucket();
1805
+ bucket[key] = value;
1806
+ this.writeUIStateBucket(bucket);
1807
+ }
1808
+ getUIState(key) {
1809
+ if (!this.getUIStateBucketKey())
1810
+ return void 0;
1811
+ const bucket = this.readUIStateBucket();
1812
+ return bucket[key];
1813
+ }
1814
+ deleteUIState(key) {
1815
+ if (!this.getUIStateBucketKey())
1816
+ return;
1817
+ const bucket = this.readUIStateBucket();
1818
+ if (key in bucket) {
1819
+ delete bucket[key];
1820
+ this.writeUIStateBucket(bucket);
1821
+ }
1822
+ }
1564
1823
  // ─── OpenData 可用性 ───
1565
1824
  async checkOpenDataAvailability() {
1566
1825
  if (!this.store.openData)
@@ -1568,9 +1827,22 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1568
1827
  await this.loadOpenDataConfig();
1569
1828
  return this.store.openData.checkAvailability(this.store.schedule);
1570
1829
  }
1830
+ async setOtherParams(params, { cover = false } = {}) {
1831
+ if (cover) {
1832
+ this.otherParams = params;
1833
+ } else {
1834
+ this.otherParams = { ...this.otherParams, ...params };
1835
+ }
1836
+ }
1571
1837
  };
1572
1838
  var VenueBookingImpl = _VenueBookingImpl;
1573
1839
  VenueBookingImpl.OPEN_DATA_CACHE_TTL = 5 * 60 * 1e3;
1840
+ // ─── UI 状态缓存(按 cacheId 分桶,sessionStorage) ───
1841
+ //
1842
+ // 用于物料层持久化 UI 层面的轻量状态(如当前步骤、登录回跳意图等)。
1843
+ // 桶键:pisell.venueBooking.uiState:<cacheId>;内部以 JSON object 形式存储多个字段。
1844
+ // 无 cacheId 时所有方法自动降级为 no-op,上层不用判空。
1845
+ VenueBookingImpl.UI_STATE_KEY_PREFIX = "pisell.venueBooking.uiState:";
1574
1846
  // Annotate the CommonJS export names for ESM import in node:
1575
1847
  0 && (module.exports = {
1576
1848
  VenueBookingImpl,