@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
@@ -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[];
@@ -1,3 +1,4 @@
1
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
1
2
  function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
2
3
  function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3
4
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
@@ -49,8 +50,47 @@ export function mergeConsecutiveSlots(slots) {
49
50
  });
50
51
  return groups;
51
52
  }
53
+ export function buildPriceBreakdown(params) {
54
+ var group = params.group,
55
+ productId = params.productId,
56
+ quotation = params.quotation;
57
+ if (!group.slots.length) return [];
58
+ var entries = [];
59
+ var _iterator = _createForOfIteratorHelper(group.slots),
60
+ _step;
61
+ try {
62
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
63
+ var slot = _step.value;
64
+ var unitPrice = Number(slot.price || '0');
65
+ var shelfId = quotation ? quotation.getQuotationShelfId({
66
+ productId: productId,
67
+ datetime: slot.startTime
68
+ }) : 0;
69
+ var startHm = dayjs(slot.startTime, 'YYYY-MM-DD HH:mm').format('HH:mm');
70
+ var endHm = dayjs(slot.endTime, 'YYYY-MM-DD HH:mm').format('HH:mm');
71
+ var last = entries[entries.length - 1];
72
+ if (last && last.unit_price === unitPrice && last.quotation_shelf_id === shelfId && last.end_time === startHm) {
73
+ last.end_time = endHm;
74
+ last.quantity += 1;
75
+ } else {
76
+ entries.push({
77
+ start_time: startHm,
78
+ end_time: endHm,
79
+ unit_price: unitPrice,
80
+ quotation_shelf_id: shelfId,
81
+ quantity: 1
82
+ });
83
+ }
84
+ }
85
+ } catch (err) {
86
+ _iterator.e(err);
87
+ } finally {
88
+ _iterator.f();
89
+ }
90
+ return entries;
91
+ }
52
92
  export function buildVenueBookingEntry(params) {
53
- var _rawResource$form_id, _rawResource$capacity;
93
+ var _rawResource$form_id;
54
94
  var group = params.group,
55
95
  resourceId = params.resourceId,
56
96
  mapping = params.mapping,
@@ -82,7 +122,7 @@ export function buildVenueBookingEntry(params) {
82
122
  main_field: mapping.resourceName,
83
123
  form_id: (_rawResource$form_id = rawResource === null || rawResource === void 0 ? void 0 : rawResource.form_id) !== null && _rawResource$form_id !== void 0 ? _rawResource$form_id : mapping.formId,
84
124
  relation_id: resourceId,
85
- capacity: (_rawResource$capacity = rawResource === null || rawResource === void 0 ? void 0 : rawResource.capacity) !== null && _rawResource$capacity !== void 0 ? _rawResource$capacity : 0,
125
+ capacity: 1,
86
126
  metadata: {}
87
127
  }],
88
128
  relation_products: [],
@@ -99,7 +139,8 @@ export function buildVenueBookingEntry(params) {
99
139
 
100
140
  /**
101
141
  * 从一条已合并的订单商品还原出独立的时段列表。
102
- * 通过 metadata 中的 start_time/end_time 和 slotDurationMinutes 进行拆分。
142
+ * 优先使用 metadata.price_breakdown 还原每个时段的精确价格,
143
+ * 回退到按 selling_price / slot_count 均分。
103
144
  */
104
145
  export function expandMergedSlotToIndividual(product, slotDurationMinutes) {
105
146
  var meta = product.metadata || {};
@@ -108,21 +149,57 @@ export function expandMergedSlotToIndividual(product, slotDurationMinutes) {
108
149
  var startTime = meta.start_time;
109
150
  var endTime = meta.end_time;
110
151
  if (!startTime || !endTime) return [];
111
- var slotCount = meta.slot_count || 1;
112
- var totalPrice = new Decimal(product.selling_price || '0');
113
- var perSlotPrice = slotCount > 0 ? totalPrice.div(slotCount).toFixed(2) : totalPrice.toFixed(2);
152
+ var breakdown = meta.price_breakdown;
153
+ var datePrefix = dayjs(startTime, 'YYYY-MM-DD HH:mm').format('YYYY-MM-DD');
114
154
  var result = [];
115
155
  var cursor = dayjs(startTime, 'YYYY-MM-DD HH:mm');
116
156
  var end = dayjs(endTime, 'YYYY-MM-DD HH:mm');
117
- while (cursor.isBefore(end)) {
118
- var slotEnd = cursor.add(slotDurationMinutes, 'minute');
119
- result.push({
120
- resourceId: resourceId,
121
- startTime: cursor.format('YYYY-MM-DD HH:mm'),
122
- endTime: slotEnd.format('YYYY-MM-DD HH:mm'),
123
- price: perSlotPrice
124
- });
125
- cursor = slotEnd;
157
+ if (breakdown !== null && breakdown !== void 0 && breakdown.length) {
158
+ var priceMap = new Map();
159
+ var _iterator2 = _createForOfIteratorHelper(breakdown),
160
+ _step2;
161
+ try {
162
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
163
+ var entry = _step2.value;
164
+ var entryCursor = dayjs("".concat(datePrefix, " ").concat(entry.start_time), 'YYYY-MM-DD HH:mm');
165
+ var entryEnd = dayjs("".concat(datePrefix, " ").concat(entry.end_time), 'YYYY-MM-DD HH:mm');
166
+ while (entryCursor.isBefore(entryEnd)) {
167
+ priceMap.set(entryCursor.format('HH:mm'), entry.unit_price);
168
+ entryCursor = entryCursor.add(slotDurationMinutes, 'minute');
169
+ }
170
+ }
171
+ } catch (err) {
172
+ _iterator2.e(err);
173
+ } finally {
174
+ _iterator2.f();
175
+ }
176
+ while (cursor.isBefore(end)) {
177
+ var _priceMap$get;
178
+ var slotEnd = cursor.add(slotDurationMinutes, 'minute');
179
+ var hm = cursor.format('HH:mm');
180
+ var price = (_priceMap$get = priceMap.get(hm)) !== null && _priceMap$get !== void 0 ? _priceMap$get : 0;
181
+ result.push({
182
+ resourceId: resourceId,
183
+ startTime: cursor.format('YYYY-MM-DD HH:mm'),
184
+ endTime: slotEnd.format('YYYY-MM-DD HH:mm'),
185
+ price: new Decimal(price).toFixed(2)
186
+ });
187
+ cursor = slotEnd;
188
+ }
189
+ } else {
190
+ var slotCount = meta.slot_count || 1;
191
+ var totalPrice = new Decimal(product.selling_price || '0');
192
+ var perSlotPrice = slotCount > 0 ? totalPrice.div(slotCount).toFixed(2) : totalPrice.toFixed(2);
193
+ while (cursor.isBefore(end)) {
194
+ var _slotEnd = cursor.add(slotDurationMinutes, 'minute');
195
+ result.push({
196
+ resourceId: resourceId,
197
+ startTime: cursor.format('YYYY-MM-DD HH:mm'),
198
+ endTime: _slotEnd.format('YYYY-MM-DD HH:mm'),
199
+ price: perSlotPrice
200
+ });
201
+ cursor = _slotEnd;
202
+ }
126
203
  }
127
204
  return result;
128
205
  }
@@ -0,0 +1,49 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/model/strategy/adapter/promotion/index.ts
30
+ var promotion_exports = {};
31
+ __export(promotion_exports, {
32
+ BUY_X_GET_Y_FREE_STRATEGY: () => import_examples.BUY_X_GET_Y_FREE_STRATEGY,
33
+ PromotionAdapter: () => import_adapter.PromotionAdapter,
34
+ PromotionEvaluator: () => import_evaluator.PromotionEvaluator,
35
+ X_ITEMS_FOR_Y_PRICE_STRATEGY: () => import_examples.X_ITEMS_FOR_Y_PRICE_STRATEGY,
36
+ default: () => import_adapter2.default
37
+ });
38
+ module.exports = __toCommonJS(promotion_exports);
39
+ var import_evaluator = require("./evaluator");
40
+ var import_adapter = require("./adapter");
41
+ var import_adapter2 = __toESM(require("./adapter"));
42
+ var import_examples = require("./examples");
43
+ // Annotate the CommonJS export names for ESM import in node:
44
+ 0 && (module.exports = {
45
+ BUY_X_GET_Y_FREE_STRATEGY,
46
+ PromotionAdapter,
47
+ PromotionEvaluator,
48
+ X_ITEMS_FOR_Y_PRICE_STRATEGY
49
+ });
@@ -341,6 +341,7 @@ var formatBundleToOrigin = (bundle) => {
341
341
  option: formatOptionsToOrigin(getBundleValueByKey("option")),
342
342
  discount_list: d.discount_list,
343
343
  "bundle_selling_price": d == null ? void 0 : d.price,
344
+ "custom_price": d == null ? void 0 : d.price,
344
345
  metadata: {
345
346
  custom_product_bundle_map_id: d._id,
346
347
  product_discount_difference
@@ -15,6 +15,11 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
15
15
  private cacheId;
16
16
  private salesSummaryModuleName;
17
17
  private rulesHooksOverride?;
18
+ /**
19
+ * Populate `savedAmount` on each discount based on product-level discount details.
20
+ * Only selected discounts get a non-zero value.
21
+ */
22
+ static populateSavedAmounts(productList: Array<Record<string, any>>, discountList: Array<Record<string, any>>): void;
18
23
  constructor(name?: string, version?: string);
19
24
  initialize(core: PisellCore, options: ModuleOptions): Promise<void>;
20
25
  /**
@@ -47,6 +47,28 @@ var OrderModule = class extends import_BaseModule.BaseModule {
47
47
  this.defaultName = "order";
48
48
  this.defaultVersion = "1.0.0";
49
49
  }
50
+ /**
51
+ * Populate `savedAmount` on each discount based on product-level discount details.
52
+ * Only selected discounts get a non-zero value.
53
+ */
54
+ static populateSavedAmounts(productList, discountList) {
55
+ var _a, _b;
56
+ const savedMap = /* @__PURE__ */ new Map();
57
+ for (const product of productList) {
58
+ const qty = product.num || 1;
59
+ for (const pd of product.discount_list || []) {
60
+ const discountKey = ((_a = pd.discount) == null ? void 0 : _a.resource_id) || pd.id;
61
+ if (discountKey == null)
62
+ continue;
63
+ const amount = new import_decimal.default(pd.amount || 0).times(qty).plus(((_b = pd.metadata) == null ? void 0 : _b.product_discount_difference) || 0);
64
+ savedMap.set(discountKey, (savedMap.get(discountKey) || new import_decimal.default(0)).plus(amount));
65
+ }
66
+ }
67
+ for (const d of discountList) {
68
+ const key = d.id;
69
+ d.savedAmount = d.isSelected && key != null && savedMap.has(key) ? savedMap.get(key).toNumber() : 0;
70
+ }
71
+ }
50
72
  async initialize(core, options) {
51
73
  var _a;
52
74
  this.core = core;
@@ -151,7 +173,11 @@ var OrderModule = class extends import_BaseModule.BaseModule {
151
173
  original_price: values.original_price !== void 0 ? String(values.original_price) : product.original_price,
152
174
  discount_list: values.discount_list ?? product.discount_list,
153
175
  num: values.quantity ?? product.num,
154
- product_bundle: values.bundle ?? product.product_bundle
176
+ product_bundle: values.bundle ?? product.product_bundle,
177
+ metadata: {
178
+ ...product.metadata || {},
179
+ ...values.main_product_selling_price !== void 0 ? { main_product_selling_price: String(values.main_product_selling_price) } : {}
180
+ }
155
181
  })
156
182
  };
157
183
  }
@@ -247,6 +273,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
247
273
  tempOrder.products = result.productList;
248
274
  }
249
275
  if (result == null ? void 0 : result.discountList) {
276
+ OrderModule.populateSavedAmounts(result.productList || tempOrder.products, result.discountList);
250
277
  (_f = this.store.discount) == null ? void 0 : _f.setDiscountList(result.discountList);
251
278
  tempOrder.discount_list = result.discountList.filter((d) => d.isSelected);
252
279
  }
@@ -425,9 +452,18 @@ var OrderModule = class extends import_BaseModule.BaseModule {
425
452
  }
426
453
  async removeProductFromOrder(identity) {
427
454
  const tempOrder = this.ensureTempOrder();
455
+ const beforeProducts = tempOrder.products;
428
456
  tempOrder.products = tempOrder.products.filter(
429
457
  (item) => !(0, import_utils3.isIdentityMatch)(item, identity)
430
458
  );
459
+ const removedByStrictIdentity = beforeProducts.length - tempOrder.products.length;
460
+ if (removedByStrictIdentity === 0 && identity.identity_key) {
461
+ tempOrder.products = tempOrder.products.filter((item) => {
462
+ const isSameProduct = String(item.product_id) === String(identity.product_id) && String(item.product_variant_id) === String(identity.product_variant_id);
463
+ const hasNoIdentityKey = !item.identity_key;
464
+ return !(isSameProduct && hasNoIdentityKey);
465
+ });
466
+ }
431
467
  this.applyDiscount();
432
468
  await this.recalculateSummary({ createIfMissing: true });
433
469
  this.persistTempOrder();
@@ -110,6 +110,19 @@ function normalizeSubmitProduct(product) {
110
110
  if (rawMetadata.unique_identification_number) {
111
111
  cleanMetadata.unique_identification_number = rawMetadata.unique_identification_number;
112
112
  }
113
+ const priceMetaKeys = [
114
+ "main_product_original_price",
115
+ "main_product_selling_price",
116
+ "source_product_price"
117
+ ];
118
+ for (const key of priceMetaKeys) {
119
+ if (rawMetadata[key] !== void 0) {
120
+ cleanMetadata[key] = rawMetadata[key];
121
+ }
122
+ }
123
+ if (rawMetadata.price_breakdown) {
124
+ cleanMetadata.price_breakdown = rawMetadata.price_breakdown;
125
+ }
113
126
  return {
114
127
  ...submitProduct,
115
128
  ...bookingUid ? { booking_uid: bookingUid } : {},
@@ -49,5 +49,5 @@ export declare class Product extends BaseModule implements Module {
49
49
  getCategories(): ProductCategory[];
50
50
  setOtherParams(key: string, value: any): void;
51
51
  getOtherParams(): any;
52
- getProductType(): "duration" | "session" | "normal";
52
+ getProductType(): "normal" | "duration" | "session";
53
53
  }
@@ -1,5 +1,6 @@
1
1
  import { Module, PisellCore, ModuleOptions } from '../../types';
2
2
  import { BaseModule } from '../BaseModule';
3
+ import type { ScheduleItem } from '../Schedule/types';
3
4
  import type { QuotationItem } from './types';
4
5
  export type { QuotationItem, QuotationProductData, QuotationSchedule, QuotationState } from './types';
5
6
  export declare class QuotationModule extends BaseModule implements Module {
@@ -7,6 +8,7 @@ export declare class QuotationModule extends BaseModule implements Module {
7
8
  protected defaultVersion: string;
8
9
  private request;
9
10
  private store;
11
+ private scheduleResolver?;
10
12
  constructor(name?: string, version?: string);
11
13
  initialize(core: PisellCore, options: ModuleOptions): Promise<void>;
12
14
  loadQuotations(params?: {
@@ -26,6 +28,11 @@ export declare class QuotationModule extends BaseModule implements Module {
26
28
  variantId?: number;
27
29
  datetime: string;
28
30
  }): string | null;
31
+ getQuotationShelfId(params: {
32
+ productId: number;
33
+ variantId?: number;
34
+ datetime: string;
35
+ }): number;
29
36
  /**
30
37
  * Batch pre-compute quotation prices for a set of products across multiple time points.
31
38
  * Key format: `${productId}:${timePoint}`
@@ -35,6 +42,7 @@ export declare class QuotationModule extends BaseModule implements Module {
35
42
  productIds: number[];
36
43
  timePoints: string[];
37
44
  }): Map<string, string | null>;
45
+ setScheduleResolver(resolver: (id: number) => ScheduleItem | undefined): void;
38
46
  private isQuotationActiveAt;
39
47
  private findProductData;
40
48
  }
@@ -77,6 +77,18 @@ var QuotationModule = class extends import_BaseModule.BaseModule {
77
77
  }
78
78
  return null;
79
79
  }
80
+ getQuotationShelfId(params) {
81
+ const { productId, variantId, datetime } = params;
82
+ for (const quotation of this.store.list) {
83
+ if (!this.isQuotationActiveAt(quotation, datetime))
84
+ continue;
85
+ const match = this.findProductData(quotation.product_data, productId, variantId);
86
+ if (!match || match.value === 0)
87
+ continue;
88
+ return quotation.id;
89
+ }
90
+ return 0;
91
+ }
80
92
  /**
81
93
  * Batch pre-compute quotation prices for a set of products across multiple time points.
82
94
  * Key format: `${productId}:${timePoint}`
@@ -97,16 +109,25 @@ var QuotationModule = class extends import_BaseModule.BaseModule {
97
109
  }
98
110
  return map;
99
111
  }
112
+ setScheduleResolver(resolver) {
113
+ this.scheduleResolver = resolver;
114
+ }
100
115
  isQuotationActiveAt(quotation, datetime) {
101
116
  var _a;
102
117
  if (!((_a = quotation.schedule) == null ? void 0 : _a.length))
103
118
  return false;
104
- const scheduleItems = quotation.schedule.map((s) => ({
105
- ...s,
106
- repeat_type: s.repeat_type || "none",
107
- repeat_rule: s.repeat_rule || null,
108
- time_slot: s.time_slot || []
109
- }));
119
+ const scheduleItems = quotation.schedule.map((s) => {
120
+ var _a2;
121
+ const full = (_a2 = this.scheduleResolver) == null ? void 0 : _a2.call(this, s.id);
122
+ if (full)
123
+ return full;
124
+ return {
125
+ ...s,
126
+ repeat_type: s.repeat_type || "none",
127
+ repeat_rule: s.repeat_rule || null,
128
+ time_slot: s.time_slot || []
129
+ };
130
+ });
110
131
  return (0, import_getDateIsInSchedule.getDateIsInSchedule)(datetime, scheduleItems);
111
132
  }
112
133
  findProductData(productData, productId, variantId) {
@@ -70,7 +70,7 @@ var isInStandardSchedule = (targetDate, targetDateString, targetTimeString, sche
70
70
  }
71
71
  const startDate = (0, import_dayjs.default)(schedule.start_time);
72
72
  const endDate = (0, import_dayjs.default)(schedule.end_time);
73
- const isInBasicRange = targetDate.isSameOrAfter(startDate) && targetDate.isSameOrBefore(endDate);
73
+ const isInBasicRange = targetDate.isSameOrAfter(startDate) && targetDate.isBefore(endDate);
74
74
  if (schedule.repeat_type === "none") {
75
75
  return isInBasicRange;
76
76
  }
@@ -80,16 +80,14 @@ var isInTimeSlotsSchedule = (targetDate, targetDateString, targetTimeString, sch
80
80
  if (!schedule.start_time) {
81
81
  return false;
82
82
  }
83
- const scheduleDate = schedule.start_time.split(" ")[0];
84
- if (targetDateString !== scheduleDate) {
83
+ const scheduleStartDate = (0, import_dayjs.default)(schedule.start_time.split(" ")[0]);
84
+ if (!isDateInTimeSlotsSchedule(targetDate, schedule, scheduleStartDate)) {
85
85
  return false;
86
86
  }
87
87
  for (const timeSlot of schedule.time_slot) {
88
- const slotStart = `${scheduleDate} ${timeSlot.start_time}:00`;
89
- const slotEnd = `${scheduleDate} ${timeSlot.end_time}:00`;
90
- const slotStartDate = (0, import_dayjs.default)(slotStart);
91
- const slotEndDate = (0, import_dayjs.default)(slotEnd);
92
- if (targetDate.isSameOrAfter(slotStartDate) && targetDate.isSameOrBefore(slotEndDate)) {
88
+ const slotStart = (0, import_dayjs.default)(`${targetDateString} ${timeSlot.start_time}:00`);
89
+ const slotEnd = (0, import_dayjs.default)(`${targetDateString} ${timeSlot.end_time}:00`);
90
+ if (targetDate.isSameOrAfter(slotStart) && targetDate.isBefore(slotEnd)) {
93
91
  return true;
94
92
  }
95
93
  }
@@ -104,7 +102,7 @@ var isInDesignationSchedule = (targetDate, targetDateString, targetTimeString, s
104
102
  const endDateTime = `${designation.end_date} ${designation.end_time}:00`;
105
103
  const startDate = (0, import_dayjs.default)(startDateTime);
106
104
  const endDate = (0, import_dayjs.default)(endDateTime);
107
- if (targetDate.isSameOrAfter(startDate) && targetDate.isSameOrBefore(endDate)) {
105
+ if (targetDate.isSameOrAfter(startDate) && targetDate.isBefore(endDate)) {
108
106
  return true;
109
107
  }
110
108
  }
@@ -158,7 +156,7 @@ var isInDailyRepeat = (targetDate, startDate, endDate, repeatRule) => {
158
156
  const targetTimeOfDay = targetDate.hour() * 3600 + targetDate.minute() * 60 + targetDate.second();
159
157
  const startTimeOfDay = startDate.hour() * 3600 + startDate.minute() * 60 + startDate.second();
160
158
  const endTimeOfDay = endDate.hour() * 3600 + endDate.minute() * 60 + endDate.second();
161
- return targetTimeOfDay >= startTimeOfDay && targetTimeOfDay <= endTimeOfDay;
159
+ return targetTimeOfDay >= startTimeOfDay && targetTimeOfDay < endTimeOfDay;
162
160
  };
163
161
  var isInWeeklyRepeat = (targetDate, startDate, endDate, repeatRule) => {
164
162
  const targetDayOfWeek = targetDate.day();
@@ -175,7 +173,7 @@ var isInWeeklyRepeat = (targetDate, startDate, endDate, repeatRule) => {
175
173
  const targetTimeOfDay = targetDate.hour() * 3600 + targetDate.minute() * 60 + targetDate.second();
176
174
  const startTimeOfDay = startDate.hour() * 3600 + startDate.minute() * 60 + startDate.second();
177
175
  const endTimeOfDay = endDate.hour() * 3600 + endDate.minute() * 60 + endDate.second();
178
- return targetTimeOfDay >= startTimeOfDay && targetTimeOfDay <= endTimeOfDay;
176
+ return targetTimeOfDay >= startTimeOfDay && targetTimeOfDay < endTimeOfDay;
179
177
  };
180
178
  var getScheduleStartEndTimePoints = (date, scheduleList) => {
181
179
  if (!date || !scheduleList || scheduleList.length === 0) {
@@ -311,7 +311,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
311
311
  date: string;
312
312
  status: string;
313
313
  week: string;
314
- weekNum: 0 | 2 | 1 | 3 | 4 | 5 | 6;
314
+ weekNum: 0 | 1 | 2 | 3 | 4 | 5 | 6;
315
315
  }[]>;
316
316
  submitTimeSlot(timeSlots: TimeSliceItem): void;
317
317
  private getScheduleDataByIds;
@@ -358,7 +358,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
358
358
  };
359
359
  setOtherData(key: string, value: any): void;
360
360
  getOtherData(key: string): any;
361
- getProductTypeById(id: number): Promise<"duration" | "session" | "normal">;
361
+ getProductTypeById(id: number): Promise<"normal" | "duration" | "session">;
362
362
  /**
363
363
  * 提供给 UI 的方法,减轻 UI 层的计算压力,UI 层只需要传递 cartItemId 和 resourceCode 即返回对应的 renderList
364
364
  *
@@ -294,10 +294,27 @@ function getProductIdentityIndex(products, identity) {
294
294
  return products.findIndex((item) => isIdentityMatch(item, identity));
295
295
  }
296
296
  function normalizeOrderProduct(product) {
297
+ var _a;
297
298
  const metadata = { ...product.metadata || {} };
298
299
  if (product.identity_key && !metadata.unique_identification_number) {
299
300
  metadata.unique_identification_number = product.identity_key;
300
301
  }
302
+ const resolvedOriginalPrice = product.original_price || "0.00";
303
+ const resolvedSellingPrice = product.selling_price || "0.00";
304
+ if (metadata.main_product_original_price === void 0) {
305
+ metadata.main_product_original_price = resolvedOriginalPrice;
306
+ }
307
+ if (metadata.main_product_selling_price === void 0) {
308
+ metadata.main_product_selling_price = resolvedSellingPrice;
309
+ }
310
+ if (metadata.source_product_price === void 0) {
311
+ metadata.source_product_price = ((_a = product._origin) == null ? void 0 : _a.original_price) ?? resolvedOriginalPrice;
312
+ }
313
+ const normalizedBundle = (product.product_bundle || []).map((item) => ({
314
+ ...item,
315
+ bundle_selling_price: item.bundle_selling_price ?? item.price ?? "0.00",
316
+ custom_price: item.custom_price ?? item.bundle_selling_price ?? item.price ?? "0.00"
317
+ }));
301
318
  return {
302
319
  order_detail_id: product.order_detail_id || null,
303
320
  product_id: product.product_id,
@@ -305,13 +322,13 @@ function normalizeOrderProduct(product) {
305
322
  product_variant_id: product.product_variant_id,
306
323
  identity_key: product.identity_key,
307
324
  product_option_item: product.product_option_item || [],
308
- selling_price: product.selling_price || "0.00",
309
- original_price: product.original_price || "0.00",
325
+ selling_price: resolvedSellingPrice,
326
+ original_price: resolvedOriginalPrice,
310
327
  payment_price: product.payment_price || "0.00",
311
328
  tax_fee: product.tax_fee || "0.00",
312
329
  is_charge_tax: product.is_charge_tax ?? 0,
313
330
  discount_list: product.discount_list || [],
314
- product_bundle: product.product_bundle || [],
331
+ product_bundle: normalizedBundle,
315
332
  metadata,
316
333
  _origin: product._origin
317
334
  };
@@ -60,14 +60,12 @@ export declare class VenueBookingImpl extends BaseModule implements Module {
60
60
  passed: boolean | null;
61
61
  failures: QuantityCheckResult[];
62
62
  };
63
- loadVenueProducts(params: {
64
- productIds: number[];
65
- }): Promise<ProductData[]>;
66
- loadAddonProducts(params: {
67
- categoryIds?: number[];
68
- productIds?: number[];
69
- collectionIds?: number[];
70
- }): Promise<ProductData[]>;
63
+ loadAllProducts(): Promise<{
64
+ venueProducts: ProductData[];
65
+ addonProducts: ProductData[];
66
+ }>;
67
+ loadVenueProducts(): Promise<ProductData[]>;
68
+ loadAddonProducts(): Promise<ProductData[]>;
71
69
  getVenueProducts(): ProductData[];
72
70
  getAddonProductsList(): ProductData[];
73
71
  loadQuotations(params?: {
@@ -107,6 +105,7 @@ export declare class VenueBookingImpl extends BaseModule implements Module {
107
105
  setSlotConfig(config: Partial<VenueBookingSlotConfig>): void;
108
106
  getSlotConfig(): VenueBookingSlotConfig;
109
107
  loadSchedules(): Promise<void>;
108
+ private injectScheduleResolverToQuotation;
110
109
  getScheduleListByIds(ids: number[]): import("../../server").ScheduleItem[];
111
110
  getTempOrder(): import("./types").ScanOrderTempOrder | null;
112
111
  updateTempOrderNote(note: string): string;
@@ -140,7 +139,7 @@ export declare class VenueBookingImpl extends BaseModule implements Module {
140
139
  updates: Partial<ScanOrderOrderProduct>;
141
140
  }): Promise<ScanOrderOrderProduct[]>;
142
141
  removeProductFromOrder(identity: ScanOrderOrderProductIdentity): Promise<ScanOrderOrderProduct[]>;
143
- getProductList(): Promise<any>;
142
+ getProductList(): Promise<ProductData[]>;
144
143
  private loadOpenDataConfig;
145
144
  private loadRuntimeConfigs;
146
145
  private fetchItemRuleConfigsByModelIds;