@pisell/pisellos 0.0.493 → 0.0.494

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 (32) hide show
  1. package/dist/modules/Cart/utils/cartProduct.js +1 -0
  2. package/dist/modules/Order/index.d.ts +5 -0
  3. package/dist/modules/Order/index.js +70 -5
  4. package/dist/modules/Order/utils.js +12 -2
  5. package/dist/modules/Product/index.d.ts +1 -1
  6. package/dist/modules/Quotation/index.d.ts +8 -0
  7. package/dist/modules/Quotation/index.js +46 -13
  8. package/dist/modules/Schedule/getDateIsInSchedule.js +11 -18
  9. package/dist/solution/BookingByStep/index.d.ts +2 -2
  10. package/dist/solution/ScanOrder/utils.js +22 -3
  11. package/dist/solution/VenueBooking/index.d.ts +8 -9
  12. package/dist/solution/VenueBooking/index.js +612 -565
  13. package/dist/solution/VenueBooking/types.d.ts +7 -0
  14. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +13 -2
  15. package/dist/solution/VenueBooking/utils/slotMerge.js +92 -15
  16. package/lib/model/strategy/adapter/promotion/index.js +49 -0
  17. package/lib/modules/Cart/utils/cartProduct.js +1 -0
  18. package/lib/modules/Order/index.d.ts +5 -0
  19. package/lib/modules/Order/index.js +37 -1
  20. package/lib/modules/Order/utils.js +13 -0
  21. package/lib/modules/Product/index.d.ts +1 -1
  22. package/lib/modules/Quotation/index.d.ts +8 -0
  23. package/lib/modules/Quotation/index.js +27 -6
  24. package/lib/modules/Schedule/getDateIsInSchedule.js +9 -11
  25. package/lib/solution/BookingByStep/index.d.ts +2 -2
  26. package/lib/solution/ScanOrder/utils.js +20 -3
  27. package/lib/solution/VenueBooking/index.d.ts +8 -9
  28. package/lib/solution/VenueBooking/index.js +79 -69
  29. package/lib/solution/VenueBooking/types.d.ts +7 -0
  30. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +13 -2
  31. package/lib/solution/VenueBooking/utils/slotMerge.js +67 -13
  32. package/package.json +1 -1
@@ -50,6 +50,7 @@ var import_timeSlot = require("./utils/timeSlot");
50
50
  var import_dateSummary = require("./utils/dateSummary");
51
51
  var import_slotMerge = require("./utils/slotMerge");
52
52
  var import_utils2 = require("../../modules/Order/utils");
53
+ var import_Order = require("../../modules/Order");
53
54
  var import_types4 = require("../RegisterAndLogin/types");
54
55
  __reExport(VenueBooking_exports, require("./types"), module.exports);
55
56
  var OPEN_DATA_SECTION_CODES = [
@@ -416,6 +417,10 @@ var VenueBookingImpl = class extends import_BaseModule.BaseModule {
416
417
  await ((_h = this.store.order) == null ? void 0 : _h.recalculateSummary({ createIfMissing: false }));
417
418
  (_i = this.store.order) == null ? void 0 : _i.persistTempOrder();
418
419
  await this.loadRuntimeConfigs();
420
+ if (this.store.schedule) {
421
+ await this.store.schedule.loadAllSchedule();
422
+ this.injectScheduleResolverToQuotation();
423
+ }
419
424
  await this.refreshItemRuleQuantityLimits();
420
425
  this.store.status = "ready";
421
426
  console.log("[VenueBooking] 初始化完成");
@@ -491,52 +496,54 @@ var VenueBookingImpl = class extends import_BaseModule.BaseModule {
491
496
  return this.store.cartValidation || { passed: null, failures: [] };
492
497
  }
493
498
  // ─── 场地商品 & 附加商品 ───
494
- async loadVenueProducts(params) {
495
- this.logMethodStart("loadVenueProducts", { productIds: params.productIds });
499
+ async loadAllProducts() {
500
+ var _a, _b, _c;
501
+ this.logMethodStart("loadAllProducts");
496
502
  try {
497
503
  if (!this.store.venueProducts)
498
504
  throw new Error("venueProducts 模块未初始化");
499
- const res = await this.store.venueProducts.loadProducts({
500
- product_ids: params.productIds,
501
- cacheId: this.cacheId,
502
- schedule_date: (0, import_dayjs.default)().format("YYYY-MM-DD"),
503
- schedule_datetime: (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
504
- });
505
- const products = Array.isArray(res) ? res : [];
506
- this.resourceProductMap = (0, import_resource.buildResourceProductMap)(products);
507
- this.logMethodSuccess("loadVenueProducts", {
508
- productCount: products.length,
509
- resourceCount: this.resourceProductMap.size
510
- });
511
- return products;
512
- } catch (error) {
513
- this.logMethodError("loadVenueProducts", error);
514
- throw error;
515
- }
516
- }
517
- async loadAddonProducts(params) {
518
- this.logMethodStart("loadAddonProducts");
519
- try {
520
505
  if (!this.store.addonProducts)
521
506
  throw new Error("addonProducts 模块未初始化");
522
- const res = await this.store.addonProducts.loadProducts({
523
- category_ids: params.categoryIds || [],
524
- product_ids: params.productIds || [],
525
- collection: (params.collectionIds || []).map(String),
507
+ const associatedMenus = ((_b = (_a = this.otherParams) == null ? void 0 : _a.dineInConfig) == null ? void 0 : _b["menu.associated_menus"]) || [];
508
+ if (!associatedMenus.length) {
509
+ throw new Error("未获取到餐牌配置(menu.associated_menus),请检查 OpenData 配置");
510
+ }
511
+ const menuListIds = associatedMenus.map((n) => Number(n.value));
512
+ const allProducts = await this.store.venueProducts.loadProducts({
513
+ menu_list_ids: menuListIds,
526
514
  cacheId: this.cacheId,
527
515
  schedule_date: (0, import_dayjs.default)().format("YYYY-MM-DD"),
528
516
  schedule_datetime: (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
529
517
  });
530
- const products = Array.isArray(res) ? res : [];
531
- this.logMethodSuccess("loadAddonProducts", {
532
- productCount: products.length
518
+ const list = Array.isArray(allProducts) ? allProducts : [];
519
+ const venueList = list.filter((p) => p.duration != null);
520
+ const addonList = list.filter((p) => p.duration == null);
521
+ const venueStore = (_c = this.store.venueProducts) == null ? void 0 : _c.store;
522
+ if (venueStore) {
523
+ venueStore.list = venueList.slice().sort((a, b) => Number(b.sort) - Number(a.sort));
524
+ }
525
+ this.store.addonProducts.addProduct(addonList);
526
+ this.resourceProductMap = (0, import_resource.buildResourceProductMap)(venueList);
527
+ this.logMethodSuccess("loadAllProducts", {
528
+ total: list.length,
529
+ venueCount: venueList.length,
530
+ addonCount: addonList.length,
531
+ resourceCount: this.resourceProductMap.size
533
532
  });
534
- return products;
533
+ return { venueProducts: venueList, addonProducts: addonList };
535
534
  } catch (error) {
536
- this.logMethodError("loadAddonProducts", error);
535
+ this.logMethodError("loadAllProducts", error);
537
536
  throw error;
538
537
  }
539
538
  }
539
+ async loadVenueProducts() {
540
+ const result = await this.loadAllProducts();
541
+ return result.venueProducts;
542
+ }
543
+ async loadAddonProducts() {
544
+ const result = await this.loadAllProducts();
545
+ return result.addonProducts;
546
+ }
540
547
  getVenueProducts() {
541
548
  var _a, _b;
542
549
  return ((_b = (_a = this.store.venueProducts) == null ? void 0 : _a.store) == null ? void 0 : _b.list) || [];
@@ -799,7 +806,12 @@ var VenueBookingImpl = class extends import_BaseModule.BaseModule {
799
806
  start_time: group.startTime,
800
807
  end_time: group.endTime,
801
808
  slot_count: group.slotCount,
802
- booking_uid: bookingUuid
809
+ booking_uid: bookingUuid,
810
+ price_breakdown: (0, import_slotMerge.buildPriceBreakdown)({
811
+ group,
812
+ productId: mapping.productId,
813
+ quotation: this.store.quotation
814
+ })
803
815
  },
804
816
  _origin: {
805
817
  name: mapping.productTitle,
@@ -830,7 +842,7 @@ var VenueBookingImpl = class extends import_BaseModule.BaseModule {
830
842
  main_field: mapping.resourceName,
831
843
  form_id: (rawResource == null ? void 0 : rawResource.form_id) ?? mapping.formId,
832
844
  relation_id: resourceId,
833
- capacity: (rawResource == null ? void 0 : rawResource.capacity) ?? 0,
845
+ capacity: 1,
834
846
  metadata: {}
835
847
  }],
836
848
  schedule_id: 0,
@@ -878,12 +890,20 @@ var VenueBookingImpl = class extends import_BaseModule.BaseModule {
878
890
  if (!this.store.schedule)
879
891
  throw new Error("schedule 模块未初始化");
880
892
  await this.store.schedule.loadAllSchedule();
893
+ this.injectScheduleResolverToQuotation();
881
894
  this.logMethodSuccess("loadSchedules");
882
895
  } catch (error) {
883
896
  this.logMethodError("loadSchedules", error);
884
897
  throw error;
885
898
  }
886
899
  }
900
+ injectScheduleResolverToQuotation() {
901
+ if (this.store.quotation && this.store.schedule) {
902
+ this.store.quotation.setScheduleResolver(
903
+ (id) => this.store.schedule.getScheduleListByIds([id])[0]
904
+ );
905
+ }
906
+ }
887
907
  getScheduleListByIds(ids) {
888
908
  if (!this.store.schedule)
889
909
  return [];
@@ -949,8 +969,12 @@ var VenueBookingImpl = class extends import_BaseModule.BaseModule {
949
969
  const merged = (0, import_slotMerge.mergeConsecutiveSlots)(updatedSlots);
950
970
  if (merged.length === 1) {
951
971
  product.selling_price = merged[0].totalPrice;
952
- product.original_price = merged[0].totalPrice;
953
972
  product.payment_price = merged[0].totalPrice;
973
+ product.metadata.price_breakdown = (0, import_slotMerge.buildPriceBreakdown)({
974
+ group: merged[0],
975
+ productId: mapping.productId,
976
+ quotation: this.store.quotation
977
+ });
954
978
  }
955
979
  } else if (product.product_id != null) {
956
980
  const quotationPrice = this.store.quotation.getPriceForProduct({
@@ -960,7 +984,6 @@ var VenueBookingImpl = class extends import_BaseModule.BaseModule {
960
984
  });
961
985
  if (quotationPrice !== null) {
962
986
  product.selling_price = quotationPrice;
963
- product.original_price = quotationPrice;
964
987
  product.payment_price = quotationPrice;
965
988
  }
966
989
  }
@@ -1031,9 +1054,20 @@ var VenueBookingImpl = class extends import_BaseModule.BaseModule {
1031
1054
  }
1032
1055
  if (result == null ? void 0 : result.discountList) {
1033
1056
  nextDiscountList = result.discountList;
1034
- await (discountModule == null ? void 0 : discountModule.setDiscountList(result.discountList));
1057
+ if (!params.isSelected) {
1058
+ const beforeSelectedIds = new Set(
1059
+ updated.filter((d) => d.isSelected).map((d) => d.id)
1060
+ );
1061
+ for (const d of nextDiscountList) {
1062
+ if (d.isSelected && !beforeSelectedIds.has(d.id)) {
1063
+ d.isSelected = false;
1064
+ }
1065
+ }
1066
+ }
1035
1067
  }
1036
1068
  }
1069
+ import_Order.OrderModule.populateSavedAmounts(tempOrder.products, nextDiscountList);
1070
+ await (discountModule == null ? void 0 : discountModule.setDiscountList(nextDiscountList));
1037
1071
  tempOrder.discount_list = (nextDiscountList || []).filter((d) => d.isSelected);
1038
1072
  const afterApplyTarget = this.store.order.getDiscountList().find((d) => d.id === params.discountId) || null;
1039
1073
  await this.store.order.recalculateSummary({ createIfMissing: true });
@@ -1121,7 +1155,9 @@ var VenueBookingImpl = class extends import_BaseModule.BaseModule {
1121
1155
  });
1122
1156
  if (quotationPrice !== null) {
1123
1157
  product.selling_price = quotationPrice;
1124
- product.original_price = quotationPrice;
1158
+ if (product.original_price == null) {
1159
+ product.original_price = quotationPrice;
1160
+ }
1125
1161
  product.payment_price = quotationPrice;
1126
1162
  }
1127
1163
  }
@@ -1178,37 +1214,11 @@ var VenueBookingImpl = class extends import_BaseModule.BaseModule {
1178
1214
  }
1179
1215
  }
1180
1216
  async getProductList() {
1181
- var _a, _b, _c, _d, _e, _f, _g;
1182
- this.logMethodStart("getProductList");
1183
- const cachedList = (_b = (_a = this.store.products) == null ? void 0 : _a.store) == null ? void 0 : _b.list;
1184
- if (Array.isArray(cachedList) && cachedList.length > 0) {
1185
- const formattedRes = (0, import_utils.attachItemRuleLimitsToTopLevelProducts)(
1186
- cachedList,
1187
- this.store.itemRuleQuantityLimits || []
1188
- );
1189
- this.logMethodSuccess("getProductList", { fromCache: true });
1190
- return formattedRes;
1191
- }
1192
- const menuListIds = ((_c = this.otherParams) == null ? void 0 : _c.menuListIds) || ((_f = (_e = (_d = this.otherParams) == null ? void 0 : _d.dineInConfig) == null ? void 0 : _e["menu.associated_menus"]) == null ? void 0 : _f.map((n) => Number(n.value))) || [];
1193
- try {
1194
- const res = await ((_g = this.store.products) == null ? void 0 : _g.loadProducts({
1195
- menu_list_ids: menuListIds,
1196
- cacheId: this.cacheId,
1197
- schedule_date: (0, import_dayjs.default)().format("YYYY-MM-DD"),
1198
- schedule_datetime: (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
1199
- }));
1200
- const formattedRes = (0, import_utils.attachItemRuleLimitsToTopLevelProducts)(
1201
- res,
1202
- this.store.itemRuleQuantityLimits || []
1203
- );
1204
- this.logMethodSuccess("getProductList", {
1205
- menuCount: menuListIds.length
1206
- });
1207
- return formattedRes;
1208
- } catch (error) {
1209
- this.logMethodError("getProductList", error);
1210
- throw error;
1211
- }
1217
+ const result = await this.loadAllProducts();
1218
+ return (0, import_utils.attachItemRuleLimitsToTopLevelProducts)(
1219
+ result.addonProducts,
1220
+ this.store.itemRuleQuantityLimits || []
1221
+ );
1212
1222
  }
1213
1223
  // ─── ItemRule 引擎 ───
1214
1224
  async loadOpenDataConfig() {
@@ -92,6 +92,13 @@ export interface MergedSlotGroup {
92
92
  slotCount: number;
93
93
  slots: VenueSlotSelection[];
94
94
  }
95
+ export interface PriceBreakdownEntry {
96
+ start_time: string;
97
+ end_time: string;
98
+ unit_price: number;
99
+ quotation_shelf_id: number;
100
+ quantity: number;
101
+ }
95
102
  export interface VenueBookingState {
96
103
  entryContext: VenueBookingEntryContext | null;
97
104
  status: VenueBookingStatus;
@@ -1,7 +1,17 @@
1
- import type { VenueSlotSelection, MergedSlotGroup, ResourceProductMapping, VenueResourceRawData } from '../types';
1
+ import type { VenueSlotSelection, MergedSlotGroup, PriceBreakdownEntry, ResourceProductMapping, VenueResourceRawData } from '../types';
2
2
  import type { ScanOrderOrderProduct } from '../../ScanOrder/types';
3
3
  export declare function buildVenueIdentityKey(resourceId: number | string, groupIndex: number): string;
4
4
  export declare function mergeConsecutiveSlots(slots: VenueSlotSelection[]): MergedSlotGroup[];
5
+ export declare function buildPriceBreakdown(params: {
6
+ group: MergedSlotGroup;
7
+ productId: number;
8
+ quotation?: {
9
+ getQuotationShelfId: (p: {
10
+ productId: number;
11
+ datetime: string;
12
+ }) => number;
13
+ };
14
+ }): PriceBreakdownEntry[];
5
15
  export interface BuildVenueBookingParams {
6
16
  group: MergedSlotGroup;
7
17
  resourceId: number | string;
@@ -13,6 +23,7 @@ export interface BuildVenueBookingParams {
13
23
  export declare function buildVenueBookingEntry(params: BuildVenueBookingParams): Record<string, any>;
14
24
  /**
15
25
  * 从一条已合并的订单商品还原出独立的时段列表。
16
- * 通过 metadata 中的 start_time/end_time 和 slotDurationMinutes 进行拆分。
26
+ * 优先使用 metadata.price_breakdown 还原每个时段的精确价格,
27
+ * 回退到按 selling_price / slot_count 均分。
17
28
  */
18
29
  export declare function expandMergedSlotToIndividual(product: ScanOrderOrderProduct, slotDurationMinutes: number): VenueSlotSelection[];
@@ -29,6 +29,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  // src/solution/VenueBooking/utils/slotMerge.ts
30
30
  var slotMerge_exports = {};
31
31
  __export(slotMerge_exports, {
32
+ buildPriceBreakdown: () => buildPriceBreakdown,
32
33
  buildVenueBookingEntry: () => buildVenueBookingEntry,
33
34
  buildVenueIdentityKey: () => buildVenueIdentityKey,
34
35
  expandMergedSlotToIndividual: () => expandMergedSlotToIndividual,
@@ -79,6 +80,32 @@ function mergeConsecutiveSlots(slots) {
79
80
  });
80
81
  return groups;
81
82
  }
83
+ function buildPriceBreakdown(params) {
84
+ const { group, productId, quotation } = params;
85
+ if (!group.slots.length)
86
+ return [];
87
+ const entries = [];
88
+ for (const slot of group.slots) {
89
+ const unitPrice = Number(slot.price || "0");
90
+ const shelfId = quotation ? quotation.getQuotationShelfId({ productId, datetime: slot.startTime }) : 0;
91
+ const startHm = (0, import_dayjs.default)(slot.startTime, "YYYY-MM-DD HH:mm").format("HH:mm");
92
+ const endHm = (0, import_dayjs.default)(slot.endTime, "YYYY-MM-DD HH:mm").format("HH:mm");
93
+ const last = entries[entries.length - 1];
94
+ if (last && last.unit_price === unitPrice && last.quotation_shelf_id === shelfId && last.end_time === startHm) {
95
+ last.end_time = endHm;
96
+ last.quantity += 1;
97
+ } else {
98
+ entries.push({
99
+ start_time: startHm,
100
+ end_time: endHm,
101
+ unit_price: unitPrice,
102
+ quotation_shelf_id: shelfId,
103
+ quantity: 1
104
+ });
105
+ }
106
+ }
107
+ return entries;
108
+ }
82
109
  function buildVenueBookingEntry(params) {
83
110
  const { group, resourceId, mapping, rawResource, bookingUuid, productUid } = params;
84
111
  const startMoment = (0, import_dayjs.default)(group.startTime, "YYYY-MM-DD HH:mm");
@@ -106,7 +133,7 @@ function buildVenueBookingEntry(params) {
106
133
  main_field: mapping.resourceName,
107
134
  form_id: (rawResource == null ? void 0 : rawResource.form_id) ?? mapping.formId,
108
135
  relation_id: resourceId,
109
- capacity: (rawResource == null ? void 0 : rawResource.capacity) ?? 0,
136
+ capacity: 1,
110
137
  metadata: {}
111
138
  }],
112
139
  relation_products: [],
@@ -129,26 +156,53 @@ function expandMergedSlotToIndividual(product, slotDurationMinutes) {
129
156
  const endTime = meta.end_time;
130
157
  if (!startTime || !endTime)
131
158
  return [];
132
- const slotCount = meta.slot_count || 1;
133
- const totalPrice = new import_decimal.default(product.selling_price || "0");
134
- const perSlotPrice = slotCount > 0 ? totalPrice.div(slotCount).toFixed(2) : totalPrice.toFixed(2);
159
+ const breakdown = meta.price_breakdown;
160
+ const datePrefix = (0, import_dayjs.default)(startTime, "YYYY-MM-DD HH:mm").format("YYYY-MM-DD");
135
161
  const result = [];
136
162
  let cursor = (0, import_dayjs.default)(startTime, "YYYY-MM-DD HH:mm");
137
163
  const end = (0, import_dayjs.default)(endTime, "YYYY-MM-DD HH:mm");
138
- while (cursor.isBefore(end)) {
139
- const slotEnd = cursor.add(slotDurationMinutes, "minute");
140
- result.push({
141
- resourceId,
142
- startTime: cursor.format("YYYY-MM-DD HH:mm"),
143
- endTime: slotEnd.format("YYYY-MM-DD HH:mm"),
144
- price: perSlotPrice
145
- });
146
- cursor = slotEnd;
164
+ if (breakdown == null ? void 0 : breakdown.length) {
165
+ const priceMap = /* @__PURE__ */ new Map();
166
+ for (const entry of breakdown) {
167
+ let entryCursor = (0, import_dayjs.default)(`${datePrefix} ${entry.start_time}`, "YYYY-MM-DD HH:mm");
168
+ const entryEnd = (0, import_dayjs.default)(`${datePrefix} ${entry.end_time}`, "YYYY-MM-DD HH:mm");
169
+ while (entryCursor.isBefore(entryEnd)) {
170
+ priceMap.set(entryCursor.format("HH:mm"), entry.unit_price);
171
+ entryCursor = entryCursor.add(slotDurationMinutes, "minute");
172
+ }
173
+ }
174
+ while (cursor.isBefore(end)) {
175
+ const slotEnd = cursor.add(slotDurationMinutes, "minute");
176
+ const hm = cursor.format("HH:mm");
177
+ const price = priceMap.get(hm) ?? 0;
178
+ result.push({
179
+ resourceId,
180
+ startTime: cursor.format("YYYY-MM-DD HH:mm"),
181
+ endTime: slotEnd.format("YYYY-MM-DD HH:mm"),
182
+ price: new import_decimal.default(price).toFixed(2)
183
+ });
184
+ cursor = slotEnd;
185
+ }
186
+ } else {
187
+ const slotCount = meta.slot_count || 1;
188
+ const totalPrice = new import_decimal.default(product.selling_price || "0");
189
+ const perSlotPrice = slotCount > 0 ? totalPrice.div(slotCount).toFixed(2) : totalPrice.toFixed(2);
190
+ while (cursor.isBefore(end)) {
191
+ const slotEnd = cursor.add(slotDurationMinutes, "minute");
192
+ result.push({
193
+ resourceId,
194
+ startTime: cursor.format("YYYY-MM-DD HH:mm"),
195
+ endTime: slotEnd.format("YYYY-MM-DD HH:mm"),
196
+ price: perSlotPrice
197
+ });
198
+ cursor = slotEnd;
199
+ }
147
200
  }
148
201
  return result;
149
202
  }
150
203
  // Annotate the CommonJS export names for ESM import in node:
151
204
  0 && (module.exports = {
205
+ buildPriceBreakdown,
152
206
  buildVenueBookingEntry,
153
207
  buildVenueIdentityKey,
154
208
  expandMergedSlotToIndividual,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "0.0.493",
4
+ "version": "0.0.494",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",