@pisell/pisellos 0.10.27 → 0.10.28
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.
- package/dist/model/strategy/adapter/promotion/index.js +9 -0
- package/dist/modules/Order/index.d.ts +1 -1
- package/dist/modules/ResourcePlanner/planner.js +21 -2
- package/dist/modules/ResourcePlanner/types.d.ts +7 -0
- package/dist/modules/Rules/index.js +1 -1
- package/dist/solution/BookingByStep/index.d.ts +2 -2
- package/dist/solution/BookingTicket/index.d.ts +13 -1
- package/dist/solution/UnifiedBookingSales/index.js +14 -8
- package/lib/model/strategy/adapter/promotion/index.js +1 -46
- package/lib/modules/Order/index.d.ts +1 -1
- package/lib/modules/ResourcePlanner/planner.js +20 -3
- package/lib/modules/ResourcePlanner/types.d.ts +7 -0
- package/lib/modules/Rules/index.js +1 -1
- package/lib/solution/BookingByStep/index.d.ts +2 -2
- package/lib/solution/BookingTicket/index.d.ts +13 -1
- package/lib/solution/UnifiedBookingSales/index.js +10 -4
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// 导出评估器
|
|
2
|
+
export { PromotionEvaluator } from "./evaluator";
|
|
3
|
+
|
|
4
|
+
// 导出适配器
|
|
5
|
+
export { PromotionAdapter } from "./adapter";
|
|
6
|
+
export { default } from "./adapter";
|
|
7
|
+
|
|
8
|
+
// 导出策略配置示例常量
|
|
9
|
+
export { X_ITEMS_FOR_Y_PRICE_STRATEGY, BUY_X_GET_Y_FREE_STRATEGY, ITEM_REWARD_STRATEGY } from "./examples";
|
|
@@ -533,7 +533,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
533
533
|
generateIdempotencyToken(): string;
|
|
534
534
|
syncPaymentsToOrder<T = any>(params: SyncPaymentsToOrderParams): Promise<SyncPaymentsToOrderResult<T>>;
|
|
535
535
|
createOrder(params: CommitOrderParams['query']): {
|
|
536
|
-
type: "
|
|
536
|
+
type: "appointment_booking" | "virtual";
|
|
537
537
|
platform: string;
|
|
538
538
|
sales_channel: string;
|
|
539
539
|
order_sales_channel: string;
|
|
@@ -62,13 +62,21 @@ function resolveRequestedDurationMinutes(product, request) {
|
|
|
62
62
|
}
|
|
63
63
|
function resolveRequestedDurationStartTimes(product, request) {
|
|
64
64
|
var _request$durationStar;
|
|
65
|
-
|
|
65
|
+
var allowSessionCustomRanges = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
66
|
+
if (product.kind !== 'duration' && !(product.kind === 'session' && allowSessionCustomRanges)) return [];
|
|
66
67
|
var values = (_request$durationStar = request.durationStartTimesByProductId) === null || _request$durationStar === void 0 ? void 0 : _request$durationStar[String(product.id)];
|
|
67
68
|
if (!Array.isArray(values)) return [];
|
|
68
69
|
return Array.from(new Set(values.filter(function (value) {
|
|
69
70
|
return typeof value === 'string' && /^([01]\d|2[0-3]):[0-5]\d$/.test(value);
|
|
70
71
|
})));
|
|
71
72
|
}
|
|
73
|
+
function resolveRequestedSessionDurationMinutes(product, request, allowSessionCustomRanges) {
|
|
74
|
+
var _request$durationMinu2;
|
|
75
|
+
if (product.kind !== 'session' || !allowSessionCustomRanges) return undefined;
|
|
76
|
+
var override = (_request$durationMinu2 = request.durationMinutesByProductId) === null || _request$durationMinu2 === void 0 ? void 0 : _request$durationMinu2[String(product.id)];
|
|
77
|
+
var normalized = Number(override);
|
|
78
|
+
return Number.isInteger(normalized) && normalized > 0 ? normalized : undefined;
|
|
79
|
+
}
|
|
72
80
|
function toDateTime(value, _timezoneName) {
|
|
73
81
|
try {
|
|
74
82
|
return localDateTimeToScalar(parseLocalDateTime(value));
|
|
@@ -1025,12 +1033,23 @@ export function getProductTimeRanges(projection) {
|
|
|
1025
1033
|
};
|
|
1026
1034
|
}
|
|
1027
1035
|
if (product.kind === 'session' || product.kind === 'venue_slot') {
|
|
1036
|
+
var fixedRanges = getSessionRangesForProduct(projection, product, dateRange);
|
|
1037
|
+
var customRanges = [];
|
|
1038
|
+
var customDurationMinutes = resolveRequestedSessionDurationMinutes(product, request, runtimeOptions.allowSessionCustomRanges === true);
|
|
1039
|
+
var _requestedStartTimes = resolveRequestedDurationStartTimes(product, request, runtimeOptions.allowSessionCustomRanges === true);
|
|
1040
|
+
if (product.kind === 'session' && customDurationMinutes && dateRange.startDate === dateRange.endDate) {
|
|
1041
|
+
_requestedStartTimes.forEach(function (startTime) {
|
|
1042
|
+
var start = toDateTimeInDate(dateRange.startDate, startTime, projection.meta.timezone);
|
|
1043
|
+
if (!Number.isFinite(start)) return;
|
|
1044
|
+
customRanges.push(createDurationTimeRange(product, start, customDurationMinutes, projection, fixedOffsetMinutes));
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1028
1047
|
return {
|
|
1029
1048
|
productId: product.id,
|
|
1030
1049
|
title: product.title,
|
|
1031
1050
|
kind: product.kind,
|
|
1032
1051
|
slotStepMinutes: slotStepMinutes,
|
|
1033
|
-
ranges:
|
|
1052
|
+
ranges: dedupeProductRanges([].concat(_toConsumableArray(fixedRanges), customRanges))
|
|
1034
1053
|
};
|
|
1035
1054
|
}
|
|
1036
1055
|
var requiredGroups = (product.requirementGroups || []).filter(function (group) {
|
|
@@ -243,6 +243,13 @@ export interface GetProductTimeRangesRequest {
|
|
|
243
243
|
}
|
|
244
244
|
export interface GetProductTimeRangesRuntimeOptions {
|
|
245
245
|
evaluatedAt?: string;
|
|
246
|
+
/**
|
|
247
|
+
* Internal POS capability: allow explicit duration/start overrides to append
|
|
248
|
+
* a custom duration candidate for Session products. Fixed Session ranges are
|
|
249
|
+
* still returned unchanged. Callers outside the POS facade must leave this
|
|
250
|
+
* disabled.
|
|
251
|
+
*/
|
|
252
|
+
allowSessionCustomRanges?: boolean;
|
|
246
253
|
}
|
|
247
254
|
export interface QueryAvailabilityBaseRequest {
|
|
248
255
|
productIds?: AvailabilityId[];
|
|
@@ -1388,7 +1388,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
|
|
|
1388
1388
|
// bundle子商品:保存到扁平化Map
|
|
1389
1389
|
processedFlatItemsMap.set(flatItem._id, [_objectSpread(_objectSpread({}, flatItem), {}, {
|
|
1390
1390
|
discount_list: isManualDiscount ? _this4.resolveDiscountListForManualOverride(((_flatItem$bundleItem8 = flatItem.bundleItem) === null || _flatItem$bundleItem8 === void 0 ? void 0 : _flatItem$bundleItem8.discount_list) || []) : _this4.filterDiscountListByType(((_flatItem$bundleItem9 = flatItem.bundleItem) === null || _flatItem$bundleItem9 === void 0 ? void 0 : _flatItem$bundleItem9.discount_list) || [], 'promotion'),
|
|
1391
|
-
price:
|
|
1391
|
+
price: flatItem.bundleItem.price,
|
|
1392
1392
|
bundle_selling_price: (_flatItem$bundleItem$ = flatItem.bundleItem.bundle_selling_price) !== null && _flatItem$bundleItem$ !== void 0 ? _flatItem$bundleItem$ : isManualDiscount ? flatItem.bundleItem.price : flatItem.bundleItem.original_price,
|
|
1393
1393
|
processed: true
|
|
1394
1394
|
})]);
|
|
@@ -326,7 +326,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
326
326
|
date: string;
|
|
327
327
|
status: string;
|
|
328
328
|
week: string;
|
|
329
|
-
weekNum: 0 |
|
|
329
|
+
weekNum: 0 | 5 | 3 | 1 | 2 | 4 | 6;
|
|
330
330
|
}[]>;
|
|
331
331
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
332
332
|
private getScheduleDataByIds;
|
|
@@ -345,7 +345,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
345
345
|
count: number;
|
|
346
346
|
left: number;
|
|
347
347
|
summaryCount: number;
|
|
348
|
-
status: "
|
|
348
|
+
status: "sold_out" | "lots_of_space" | "filling_up_fast";
|
|
349
349
|
}[];
|
|
350
350
|
/**
|
|
351
351
|
* 找到多个资源的公共可用时间段
|
|
@@ -458,7 +458,19 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
458
458
|
* 加车两阶段主决策(规格弹窗 / 资源编辑 / 直接加车)。
|
|
459
459
|
* 与 ticketBooking `handleSelectProduct` + `handleBooking4Service` 等价。
|
|
460
460
|
*/
|
|
461
|
-
decideAddProduct(item: any, options?: Partial<AddProductDecideContext>):
|
|
461
|
+
decideAddProduct(item: any, options?: Partial<AddProductDecideContext>): {
|
|
462
|
+
action: "reloadCatalog";
|
|
463
|
+
} | {
|
|
464
|
+
action: "add";
|
|
465
|
+
cacheItem: any;
|
|
466
|
+
} | {
|
|
467
|
+
action: "requiresDetail";
|
|
468
|
+
payload: AddProductRequiresDetailPayload;
|
|
469
|
+
} | {
|
|
470
|
+
action: "requiresBookingEdit";
|
|
471
|
+
cacheItem: any;
|
|
472
|
+
payload: import("./utils/addProductDecision").AddProductRequiresBookingEditPayload;
|
|
473
|
+
};
|
|
462
474
|
/** 规格弹窗 callback 后的第二段决策。 */
|
|
463
475
|
decideAfterDetail(cacheItem: any, options?: Partial<AddProductDecideContext>): import("./utils/addProductDecision").AddProductDecision;
|
|
464
476
|
/**
|
|
@@ -1030,7 +1030,7 @@ function buildBookingFromAvailabilitySelection(params) {
|
|
|
1030
1030
|
var timeSlotId = (_candidate$primarySch3 = (_candidate$primarySch4 = candidate.primaryScheduleRef) === null || _candidate$primarySch4 === void 0 ? void 0 : _candidate$primarySch4.timeSlotId) !== null && _candidate$primarySch3 !== void 0 ? _candidate$primarySch3 : (_assignments$find2 = assignments.find(function (assignment) {
|
|
1031
1031
|
return assignment.timeSlotId != null;
|
|
1032
1032
|
})) === null || _assignments$find2 === void 0 ? void 0 : _assignments$find2.timeSlotId;
|
|
1033
|
-
if (product.kind === 'session' && scheduleId == null) {
|
|
1033
|
+
if (product.kind === 'session' && candidate.source === 'session_schedule' && scheduleId == null) {
|
|
1034
1034
|
throw new AvailabilityError('REVALIDATION_FAILED', 'session 候选缺少真实 schedule identity', {
|
|
1035
1035
|
productId: product.id,
|
|
1036
1036
|
candidateKey: candidate.key,
|
|
@@ -1039,15 +1039,16 @@ function buildBookingFromAvailabilitySelection(params) {
|
|
|
1039
1039
|
}
|
|
1040
1040
|
var normalizedHolder = holder && _typeof(holder) === 'object' ? cloneDeep(holder) : undefined;
|
|
1041
1041
|
var normalizedHolderIds = Array.isArray(holderIds) ? cloneDeep(holderIds) : holderIds;
|
|
1042
|
-
return _objectSpread(_objectSpread({
|
|
1042
|
+
return _objectSpread(_objectSpread(_objectSpread({
|
|
1043
1043
|
start_date: bookingStartAt.slice(0, 10),
|
|
1044
1044
|
start_time: bookingStartAt.slice(11, 16),
|
|
1045
1045
|
end_date: bookingEndAt.slice(0, 10),
|
|
1046
1046
|
end_time: bookingEndAt.slice(11, 16),
|
|
1047
1047
|
duration: Math.max(1, Math.trunc((endScalar - startScalar) / (60 * 1000))),
|
|
1048
|
-
like_status: 'common'
|
|
1048
|
+
like_status: 'common'
|
|
1049
|
+
}, product.kind === 'session' && candidate.source === 'duration_window' ? {} : {
|
|
1049
1050
|
schedule_id: scheduleId !== null && scheduleId !== void 0 ? scheduleId : 0
|
|
1050
|
-
}, normalizedHolder ? {
|
|
1051
|
+
}), normalizedHolder ? {
|
|
1051
1052
|
holder: normalizedHolder
|
|
1052
1053
|
} : {}), {}, {
|
|
1053
1054
|
resources: assignments.map(function (assignment) {
|
|
@@ -4272,6 +4273,7 @@ export var UnifiedBookingSalesImpl = /*#__PURE__*/function (_BookingTicket) {
|
|
|
4272
4273
|
context,
|
|
4273
4274
|
projectionEntry,
|
|
4274
4275
|
contextVersion,
|
|
4276
|
+
isPosAvailabilityRequest,
|
|
4275
4277
|
allowOutsideOperatingHoursForRequestedStartTimes,
|
|
4276
4278
|
_args37 = arguments;
|
|
4277
4279
|
return _regeneratorRuntime().wrap(function _callee37$(_context37) {
|
|
@@ -4285,13 +4287,15 @@ export var UnifiedBookingSalesImpl = /*#__PURE__*/function (_BookingTicket) {
|
|
|
4285
4287
|
context = _yield$this$ensureAva.context;
|
|
4286
4288
|
projectionEntry = _yield$this$ensureAva.projectionEntry;
|
|
4287
4289
|
contextVersion = context.activeVersion;
|
|
4288
|
-
|
|
4290
|
+
isPosAvailabilityRequest = String((_this$otherParams$pla = (_this$otherParams19 = this.otherParams) === null || _this$otherParams19 === void 0 ? void 0 : _this$otherParams19.platform) !== null && _this$otherParams$pla !== void 0 ? _this$otherParams$pla : '').trim().toLowerCase() === 'pos';
|
|
4291
|
+
allowOutsideOperatingHoursForRequestedStartTimes = isPosAvailabilityRequest && Object.values(request.durationStartTimesByProductId || {}).some(function (startTimes) {
|
|
4289
4292
|
return Array.isArray(startTimes) && startTimes.length > 0;
|
|
4290
4293
|
});
|
|
4291
4294
|
return _context37.abrupt("return", getProjectionProductTimeRanges(projectionEntry.projection, _objectSpread(_objectSpread({}, request), allowOutsideOperatingHoursForRequestedStartTimes ? {
|
|
4292
4295
|
allowOutsideOperatingHoursForRequestedStartTimes: true
|
|
4293
4296
|
} : {}), {
|
|
4294
|
-
evaluatedAt: this.getAvailabilityRuntimeDateTime()
|
|
4297
|
+
evaluatedAt: this.getAvailabilityRuntimeDateTime(),
|
|
4298
|
+
allowSessionCustomRanges: isPosAvailabilityRequest
|
|
4295
4299
|
}).map(function (group) {
|
|
4296
4300
|
return _objectSpread(_objectSpread({}, group), {}, {
|
|
4297
4301
|
ranges: group.ranges.map(function (candidate) {
|
|
@@ -4299,7 +4303,7 @@ export var UnifiedBookingSalesImpl = /*#__PURE__*/function (_BookingTicket) {
|
|
|
4299
4303
|
})
|
|
4300
4304
|
});
|
|
4301
4305
|
}));
|
|
4302
|
-
case
|
|
4306
|
+
case 10:
|
|
4303
4307
|
case "end":
|
|
4304
4308
|
return _context37.stop();
|
|
4305
4309
|
}
|
|
@@ -4678,7 +4682,9 @@ export var UnifiedBookingSalesImpl = /*#__PURE__*/function (_BookingTicket) {
|
|
|
4678
4682
|
durationStartTimesByProductId: _defineProperty({}, String(params.candidate.productId), [candidateStartTime])
|
|
4679
4683
|
}, allowPosAvailabilityOverride ? {
|
|
4680
4684
|
allowOutsideOperatingHoursForRequestedStartTimes: true
|
|
4681
|
-
} : {}) : {}) : {})
|
|
4685
|
+
} : {}) : {}) : {}), {
|
|
4686
|
+
allowSessionCustomRanges: allowPosAvailabilityOverride
|
|
4687
|
+
}).flatMap(function (group) {
|
|
4682
4688
|
return group.ranges;
|
|
4683
4689
|
});
|
|
4684
4690
|
refreshedCandidateBase = refreshedRanges.find(function (candidate) {
|
|
@@ -1,46 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
Object.defineProperty(exports, "BUY_X_GET_Y_FREE_STRATEGY", {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: function () {
|
|
9
|
-
return _examples.BUY_X_GET_Y_FREE_STRATEGY;
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
Object.defineProperty(exports, "ITEM_REWARD_STRATEGY", {
|
|
13
|
-
enumerable: true,
|
|
14
|
-
get: function () {
|
|
15
|
-
return _examples.ITEM_REWARD_STRATEGY;
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
Object.defineProperty(exports, "PromotionAdapter", {
|
|
19
|
-
enumerable: true,
|
|
20
|
-
get: function () {
|
|
21
|
-
return _adapter.PromotionAdapter;
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
Object.defineProperty(exports, "PromotionEvaluator", {
|
|
25
|
-
enumerable: true,
|
|
26
|
-
get: function () {
|
|
27
|
-
return _evaluator.PromotionEvaluator;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
Object.defineProperty(exports, "X_ITEMS_FOR_Y_PRICE_STRATEGY", {
|
|
31
|
-
enumerable: true,
|
|
32
|
-
get: function () {
|
|
33
|
-
return _examples.X_ITEMS_FOR_Y_PRICE_STRATEGY;
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
Object.defineProperty(exports, "default", {
|
|
37
|
-
enumerable: true,
|
|
38
|
-
get: function () {
|
|
39
|
-
return _adapter.default;
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
var _evaluator = require("./evaluator");
|
|
43
|
-
var _adapter = _interopRequireWildcard(require("./adapter"));
|
|
44
|
-
var _examples = require("./examples");
|
|
45
|
-
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
46
|
-
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
1
|
+
"use strict";
|
|
@@ -533,7 +533,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
533
533
|
generateIdempotencyToken(): string;
|
|
534
534
|
syncPaymentsToOrder<T = any>(params: SyncPaymentsToOrderParams): Promise<SyncPaymentsToOrderResult<T>>;
|
|
535
535
|
createOrder(params: CommitOrderParams['query']): {
|
|
536
|
-
type: "
|
|
536
|
+
type: "appointment_booking" | "virtual";
|
|
537
537
|
platform: string;
|
|
538
538
|
sales_channel: string;
|
|
539
539
|
order_sales_channel: string;
|
|
@@ -55,12 +55,18 @@ function resolveRequestedDurationMinutes(product, request) {
|
|
|
55
55
|
const normalized = Number(override);
|
|
56
56
|
return Number.isInteger(normalized) && normalized > 0 ? normalized : configured;
|
|
57
57
|
}
|
|
58
|
-
function resolveRequestedDurationStartTimes(product, request) {
|
|
59
|
-
if (product.kind !== 'duration') return [];
|
|
58
|
+
function resolveRequestedDurationStartTimes(product, request, allowSessionCustomRanges = false) {
|
|
59
|
+
if (product.kind !== 'duration' && !(product.kind === 'session' && allowSessionCustomRanges)) return [];
|
|
60
60
|
const values = request.durationStartTimesByProductId?.[String(product.id)];
|
|
61
61
|
if (!Array.isArray(values)) return [];
|
|
62
62
|
return Array.from(new Set(values.filter(value => typeof value === 'string' && /^([01]\d|2[0-3]):[0-5]\d$/.test(value))));
|
|
63
63
|
}
|
|
64
|
+
function resolveRequestedSessionDurationMinutes(product, request, allowSessionCustomRanges) {
|
|
65
|
+
if (product.kind !== 'session' || !allowSessionCustomRanges) return undefined;
|
|
66
|
+
const override = request.durationMinutesByProductId?.[String(product.id)];
|
|
67
|
+
const normalized = Number(override);
|
|
68
|
+
return Number.isInteger(normalized) && normalized > 0 ? normalized : undefined;
|
|
69
|
+
}
|
|
64
70
|
function toDateTime(value, _timezoneName) {
|
|
65
71
|
try {
|
|
66
72
|
return (0, _localClock.localDateTimeToScalar)((0, _localClock.parseLocalDateTime)(value));
|
|
@@ -919,12 +925,23 @@ function getProductTimeRanges(projection, request = {}, runtimeOptions = {}) {
|
|
|
919
925
|
};
|
|
920
926
|
}
|
|
921
927
|
if (product.kind === 'session' || product.kind === 'venue_slot') {
|
|
928
|
+
const fixedRanges = getSessionRangesForProduct(projection, product, dateRange);
|
|
929
|
+
const customRanges = [];
|
|
930
|
+
const customDurationMinutes = resolveRequestedSessionDurationMinutes(product, request, runtimeOptions.allowSessionCustomRanges === true);
|
|
931
|
+
const requestedStartTimes = resolveRequestedDurationStartTimes(product, request, runtimeOptions.allowSessionCustomRanges === true);
|
|
932
|
+
if (product.kind === 'session' && customDurationMinutes && dateRange.startDate === dateRange.endDate) {
|
|
933
|
+
requestedStartTimes.forEach(startTime => {
|
|
934
|
+
const start = toDateTimeInDate(dateRange.startDate, startTime, projection.meta.timezone);
|
|
935
|
+
if (!Number.isFinite(start)) return;
|
|
936
|
+
customRanges.push(createDurationTimeRange(product, start, customDurationMinutes, projection, fixedOffsetMinutes));
|
|
937
|
+
});
|
|
938
|
+
}
|
|
922
939
|
return {
|
|
923
940
|
productId: product.id,
|
|
924
941
|
title: product.title,
|
|
925
942
|
kind: product.kind,
|
|
926
943
|
slotStepMinutes,
|
|
927
|
-
ranges:
|
|
944
|
+
ranges: dedupeProductRanges([...fixedRanges, ...customRanges])
|
|
928
945
|
};
|
|
929
946
|
}
|
|
930
947
|
const requiredGroups = (product.requirementGroups || []).filter(group => group.required !== false);
|
|
@@ -243,6 +243,13 @@ export interface GetProductTimeRangesRequest {
|
|
|
243
243
|
}
|
|
244
244
|
export interface GetProductTimeRangesRuntimeOptions {
|
|
245
245
|
evaluatedAt?: string;
|
|
246
|
+
/**
|
|
247
|
+
* Internal POS capability: allow explicit duration/start overrides to append
|
|
248
|
+
* a custom duration candidate for Session products. Fixed Session ranges are
|
|
249
|
+
* still returned unchanged. Callers outside the POS facade must leave this
|
|
250
|
+
* disabled.
|
|
251
|
+
*/
|
|
252
|
+
allowSessionCustomRanges?: boolean;
|
|
246
253
|
}
|
|
247
254
|
export interface QueryAvailabilityBaseRequest {
|
|
248
255
|
productIds?: AvailabilityId[];
|
|
@@ -1128,7 +1128,7 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
1128
1128
|
processedFlatItemsMap.set(flatItem._id, [{
|
|
1129
1129
|
...flatItem,
|
|
1130
1130
|
discount_list: isManualDiscount ? this.resolveDiscountListForManualOverride(flatItem.bundleItem?.discount_list || []) : this.filterDiscountListByType(flatItem.bundleItem?.discount_list || [], 'promotion'),
|
|
1131
|
-
price:
|
|
1131
|
+
price: flatItem.bundleItem.price,
|
|
1132
1132
|
bundle_selling_price: flatItem.bundleItem.bundle_selling_price ?? (isManualDiscount ? flatItem.bundleItem.price : flatItem.bundleItem.original_price),
|
|
1133
1133
|
processed: true
|
|
1134
1134
|
}]);
|
|
@@ -326,7 +326,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
326
326
|
date: string;
|
|
327
327
|
status: string;
|
|
328
328
|
week: string;
|
|
329
|
-
weekNum: 0 |
|
|
329
|
+
weekNum: 0 | 5 | 3 | 1 | 2 | 4 | 6;
|
|
330
330
|
}[]>;
|
|
331
331
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
332
332
|
private getScheduleDataByIds;
|
|
@@ -345,7 +345,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
345
345
|
count: number;
|
|
346
346
|
left: number;
|
|
347
347
|
summaryCount: number;
|
|
348
|
-
status: "
|
|
348
|
+
status: "sold_out" | "lots_of_space" | "filling_up_fast";
|
|
349
349
|
}[];
|
|
350
350
|
/**
|
|
351
351
|
* 找到多个资源的公共可用时间段
|
|
@@ -458,7 +458,19 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
458
458
|
* 加车两阶段主决策(规格弹窗 / 资源编辑 / 直接加车)。
|
|
459
459
|
* 与 ticketBooking `handleSelectProduct` + `handleBooking4Service` 等价。
|
|
460
460
|
*/
|
|
461
|
-
decideAddProduct(item: any, options?: Partial<AddProductDecideContext>):
|
|
461
|
+
decideAddProduct(item: any, options?: Partial<AddProductDecideContext>): {
|
|
462
|
+
action: "reloadCatalog";
|
|
463
|
+
} | {
|
|
464
|
+
action: "add";
|
|
465
|
+
cacheItem: any;
|
|
466
|
+
} | {
|
|
467
|
+
action: "requiresDetail";
|
|
468
|
+
payload: AddProductRequiresDetailPayload;
|
|
469
|
+
} | {
|
|
470
|
+
action: "requiresBookingEdit";
|
|
471
|
+
cacheItem: any;
|
|
472
|
+
payload: import("./utils/addProductDecision").AddProductRequiresBookingEditPayload;
|
|
473
|
+
};
|
|
462
474
|
/** 规格弹窗 callback 后的第二段决策。 */
|
|
463
475
|
decideAfterDetail(cacheItem: any, options?: Partial<AddProductDecideContext>): import("./utils/addProductDecision").AddProductDecision;
|
|
464
476
|
/**
|
|
@@ -946,7 +946,7 @@ function buildBookingFromAvailabilitySelection(params) {
|
|
|
946
946
|
const endScalar = (0, _localClock.localDateTimeToScalar)((0, _localClock.parseLocalDateTime)(bookingEndAt));
|
|
947
947
|
const scheduleId = candidate.primaryScheduleRef?.scheduleId ?? assignments.find(assignment => assignment.scheduleId != null)?.scheduleId;
|
|
948
948
|
const timeSlotId = candidate.primaryScheduleRef?.timeSlotId ?? assignments.find(assignment => assignment.timeSlotId != null)?.timeSlotId;
|
|
949
|
-
if (product.kind === 'session' && scheduleId == null) {
|
|
949
|
+
if (product.kind === 'session' && candidate.source === 'session_schedule' && scheduleId == null) {
|
|
950
950
|
throw new _ResourcePlanner.AvailabilityError('REVALIDATION_FAILED', 'session 候选缺少真实 schedule identity', {
|
|
951
951
|
productId: product.id,
|
|
952
952
|
candidateKey: candidate.key,
|
|
@@ -962,7 +962,9 @@ function buildBookingFromAvailabilitySelection(params) {
|
|
|
962
962
|
end_time: bookingEndAt.slice(11, 16),
|
|
963
963
|
duration: Math.max(1, Math.trunc((endScalar - startScalar) / (60 * 1000))),
|
|
964
964
|
like_status: 'common',
|
|
965
|
-
|
|
965
|
+
...(product.kind === 'session' && candidate.source === 'duration_window' ? {} : {
|
|
966
|
+
schedule_id: scheduleId ?? 0
|
|
967
|
+
}),
|
|
966
968
|
...(normalizedHolder ? {
|
|
967
969
|
holder: normalizedHolder
|
|
968
970
|
} : {}),
|
|
@@ -2822,14 +2824,16 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
2822
2824
|
projectionEntry
|
|
2823
2825
|
} = await this.ensureAvailabilityContextProjection(contextId, 'get_product_time_ranges');
|
|
2824
2826
|
const contextVersion = context.activeVersion;
|
|
2825
|
-
const
|
|
2827
|
+
const isPosAvailabilityRequest = String(this.otherParams?.platform ?? '').trim().toLowerCase() === 'pos';
|
|
2828
|
+
const allowOutsideOperatingHoursForRequestedStartTimes = isPosAvailabilityRequest && Object.values(request.durationStartTimesByProductId || {}).some(startTimes => Array.isArray(startTimes) && startTimes.length > 0);
|
|
2826
2829
|
return (0, _ResourcePlanner.getProductTimeRanges)(projectionEntry.projection, {
|
|
2827
2830
|
...request,
|
|
2828
2831
|
...(allowOutsideOperatingHoursForRequestedStartTimes ? {
|
|
2829
2832
|
allowOutsideOperatingHoursForRequestedStartTimes: true
|
|
2830
2833
|
} : {})
|
|
2831
2834
|
}, {
|
|
2832
|
-
evaluatedAt: this.getAvailabilityRuntimeDateTime()
|
|
2835
|
+
evaluatedAt: this.getAvailabilityRuntimeDateTime(),
|
|
2836
|
+
allowSessionCustomRanges: isPosAvailabilityRequest
|
|
2833
2837
|
}).map(group => ({
|
|
2834
2838
|
...group,
|
|
2835
2839
|
ranges: group.ranges.map(candidate => this.decorateAvailabilityCandidate(context.contextId, contextVersion, candidate))
|
|
@@ -3100,6 +3104,8 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
3100
3104
|
} : {})
|
|
3101
3105
|
} : {})
|
|
3102
3106
|
} : {})
|
|
3107
|
+
}, {
|
|
3108
|
+
allowSessionCustomRanges: allowPosAvailabilityOverride
|
|
3103
3109
|
}).flatMap(group => group.ranges);
|
|
3104
3110
|
const refreshedCandidateBase = refreshedRanges.find(candidate => candidate.startAt === params.candidate.startAt && candidate.endAt === params.candidate.endAt && candidate.bookingStartAt === params.candidate.bookingStartAt && candidate.bookingEndAt === params.candidate.bookingEndAt);
|
|
3105
3111
|
if (!refreshedCandidateBase) {
|