@pisell/pisellos 2.2.299 → 2.2.301
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/modules/ResourcePlanner/planner.d.ts +2 -2
- package/dist/modules/ResourcePlanner/planner.js +163 -46
- package/dist/modules/ResourcePlanner/types.d.ts +17 -0
- package/dist/solution/UnifiedBookingSales/index.js +92 -19
- package/lib/model/strategy/adapter/promotion/index.js +46 -1
- package/lib/modules/ResourcePlanner/planner.d.ts +2 -2
- package/lib/modules/ResourcePlanner/planner.js +137 -35
- package/lib/modules/ResourcePlanner/types.d.ts +17 -0
- package/lib/solution/UnifiedBookingSales/index.js +84 -20
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AvailabilityProjection, AvailabilityProjectionInput, AvailabilityQuery, AvailabilityQueryResult, AvailabilitySelectionValidationParams, AvailabilitySelectionValidationResult, GetProductTimeRangesRequest, GetProductTimeRangesRuntimeOptions, ProductTimeRangeGroup } from './types';
|
|
2
2
|
export declare function createAvailabilityProjection(input: AvailabilityProjectionInput): AvailabilityProjection;
|
|
3
3
|
export declare function getProductTimeRanges(projection: AvailabilityProjection, request?: GetProductTimeRangesRequest, runtimeOptions?: GetProductTimeRangesRuntimeOptions): ProductTimeRangeGroup[];
|
|
4
|
-
export declare function queryAvailabilityProjection(projection: AvailabilityProjection, request: AvailabilityQuery): AvailabilityQueryResult;
|
|
5
|
-
export declare function validateAvailabilitySelection(projection: AvailabilityProjection, params: AvailabilitySelectionValidationParams): AvailabilitySelectionValidationResult;
|
|
4
|
+
export declare function queryAvailabilityProjection(projection: AvailabilityProjection, request: AvailabilityQuery, runtimeOptions?: GetProductTimeRangesRuntimeOptions): AvailabilityQueryResult;
|
|
5
|
+
export declare function validateAvailabilitySelection(projection: AvailabilityProjection, params: AvailabilitySelectionValidationParams, runtimeOptions?: GetProductTimeRangesRuntimeOptions): AvailabilitySelectionValidationResult;
|
|
@@ -175,8 +175,70 @@ function addLeadWindow(base, unit, unitType, _timezoneName) {
|
|
|
175
175
|
if (unitType === 'hours') return addLocalHours(base, unit);
|
|
176
176
|
return addLocalMinutes(base, unit);
|
|
177
177
|
}
|
|
178
|
+
function normalizedWindowContainsRange(windows, candidateRange, timezoneName) {
|
|
179
|
+
return windows.some(function (window) {
|
|
180
|
+
var normalized = normalizeAbsoluteRange(window, timezoneName);
|
|
181
|
+
return Boolean(normalized && contains(normalized, candidateRange));
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function normalizedWindowsContainDate(windows, date, timezoneName) {
|
|
185
|
+
return windows.some(function (window) {
|
|
186
|
+
var normalized = normalizeAbsoluteRange(window, timezoneName);
|
|
187
|
+
return Boolean(normalized && formatDate(normalized.start, timezoneName) === date);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function isRangeFromExcludedWindows(params) {
|
|
191
|
+
var candidateDate = formatDate(params.candidateRange.start, params.timezoneName);
|
|
192
|
+
var hasNormalWindowOnDate = normalizedWindowsContainDate(params.normalWindows, candidateDate, params.timezoneName);
|
|
193
|
+
var hasExcludedWindowOnDate = normalizedWindowsContainDate(params.excludedWindows, candidateDate, params.timezoneName);
|
|
194
|
+
if (!hasNormalWindowOnDate && hasExcludedWindowOnDate) return true;
|
|
195
|
+
return !normalizedWindowContainsRange(params.normalWindows, params.candidateRange, params.timezoneName) && normalizedWindowContainsRange(params.excludedWindows, params.candidateRange, params.timezoneName);
|
|
196
|
+
}
|
|
197
|
+
function isCandidateFromExcludedScheduleDate(params) {
|
|
198
|
+
var projection = params.projection,
|
|
199
|
+
product = params.product,
|
|
200
|
+
candidate = params.candidate,
|
|
201
|
+
candidateRange = params.candidateRange;
|
|
202
|
+
var operatingHours = projection.meta.defaults.operatingHours;
|
|
203
|
+
if (isRangeFromExcludedWindows({
|
|
204
|
+
normalWindows: operatingHours.windows,
|
|
205
|
+
excludedWindows: operatingHours.excludedWindows || [],
|
|
206
|
+
candidateRange: candidateRange,
|
|
207
|
+
timezoneName: projection.meta.timezone
|
|
208
|
+
})) {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
if (product.kind !== 'session' && product.kind !== 'venue_slot') {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
if (candidate.source !== 'session_schedule') {
|
|
215
|
+
var hasNormalRule = (product.sessionRules || []).some(function (rule) {
|
|
216
|
+
return matchesScheduleRuleDate(rule, candidate.date, projection.meta.timezone);
|
|
217
|
+
});
|
|
218
|
+
var hasExcludedOverrideRule = (product.sessionRules || []).some(function (rule) {
|
|
219
|
+
return !matchesScheduleRuleDate(rule, candidate.date, projection.meta.timezone) && matchesScheduleRuleDate(rule, candidate.date, projection.meta.timezone, true);
|
|
220
|
+
});
|
|
221
|
+
return !hasNormalRule && hasExcludedOverrideRule;
|
|
222
|
+
}
|
|
223
|
+
var candidateDateRange = {
|
|
224
|
+
startDate: candidate.date,
|
|
225
|
+
endDate: candidate.date
|
|
226
|
+
};
|
|
227
|
+
var candidateIdentity = getProductTimeRangeIdentity(candidate);
|
|
228
|
+
var normalRanges = getSessionRangesForProduct(projection, product, candidateDateRange);
|
|
229
|
+
if (normalRanges.some(function (range) {
|
|
230
|
+
return getProductTimeRangeIdentity(range) === candidateIdentity;
|
|
231
|
+
})) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
return getSessionRangesForProduct(projection, product, candidateDateRange, true).some(function (range) {
|
|
235
|
+
return getProductTimeRangeIdentity(range) === candidateIdentity;
|
|
236
|
+
});
|
|
237
|
+
}
|
|
178
238
|
function evaluateCandidateTimeGate(params) {
|
|
179
|
-
var
|
|
239
|
+
var projection = params.projection,
|
|
240
|
+
product = params.product,
|
|
241
|
+
candidate = params.candidate,
|
|
180
242
|
candidateRange = params.candidateRange,
|
|
181
243
|
evaluationContext = params.evaluationContext,
|
|
182
244
|
timezoneName = params.timezoneName;
|
|
@@ -188,6 +250,18 @@ function evaluateCandidateTimeGate(params) {
|
|
|
188
250
|
conflictCode: 'past'
|
|
189
251
|
};
|
|
190
252
|
}
|
|
253
|
+
if (evaluationContext.allowExcludedScheduleDateOverride && isCandidateFromExcludedScheduleDate({
|
|
254
|
+
projection: projection,
|
|
255
|
+
product: product,
|
|
256
|
+
candidate: candidate,
|
|
257
|
+
candidateRange: candidateRange
|
|
258
|
+
})) {
|
|
259
|
+
return {
|
|
260
|
+
status: 'unavailable',
|
|
261
|
+
reasonCodes: ['schedule_excluded_date'],
|
|
262
|
+
conflictCode: 'schedule_excluded_date'
|
|
263
|
+
};
|
|
264
|
+
}
|
|
191
265
|
var policy = product.cutOffPolicy;
|
|
192
266
|
if (!policy) return null;
|
|
193
267
|
if (resolveCutOffPolicyIssue(policy)) {
|
|
@@ -220,10 +294,12 @@ function evaluateCandidateTimeGate(params) {
|
|
|
220
294
|
};
|
|
221
295
|
}
|
|
222
296
|
if (policy.type === 'before_start') {
|
|
223
|
-
// Flexible-duration candidates
|
|
224
|
-
// minute
|
|
225
|
-
// already started because the query
|
|
226
|
-
|
|
297
|
+
// Flexible-duration candidates and POS fixed-duration anchors start at
|
|
298
|
+
// minute precision. Keep that current minute selectable instead of treating
|
|
299
|
+
// the freshly generated candidate as already started because the query
|
|
300
|
+
// clock still contains seconds.
|
|
301
|
+
var usesMinutePrecision = product.isFlexibleDuration || evaluationContext.useExactDurationCandidateMinute && product.kind === 'duration';
|
|
302
|
+
var hasNotReachedStart = usesMinutePrecision ? floorToMinute(now) <= candidateRange.start : now < candidateRange.start;
|
|
227
303
|
if (hasNotReachedStart) return null;
|
|
228
304
|
return {
|
|
229
305
|
status: 'unavailable',
|
|
@@ -334,7 +410,8 @@ function normalizeCoverage(input) {
|
|
|
334
410
|
};
|
|
335
411
|
}
|
|
336
412
|
function normalizeResourceWindow(resource, coverage, timezoneName) {
|
|
337
|
-
|
|
413
|
+
var includeExcludedWindows = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
414
|
+
return mergeRanges([].concat(_toConsumableArray(resource.windows || []), _toConsumableArray(includeExcludedWindows ? resource.excludedWindows || [] : [])).map(function (window) {
|
|
338
415
|
return normalizeAbsoluteRange(window, timezoneName);
|
|
339
416
|
}).filter(function (window) {
|
|
340
417
|
return Boolean(window);
|
|
@@ -407,12 +484,15 @@ function normalizeResources(resources) {
|
|
|
407
484
|
capacity: Math.max(1, Number(resource.capacity || 1) || 1),
|
|
408
485
|
windows: (resource.windows || []).map(function (window) {
|
|
409
486
|
return _objectSpread({}, window);
|
|
487
|
+
}),
|
|
488
|
+
excludedWindows: (resource.excludedWindows || []).map(function (window) {
|
|
489
|
+
return _objectSpread({}, window);
|
|
410
490
|
})
|
|
411
491
|
});
|
|
412
492
|
});
|
|
413
493
|
}
|
|
414
494
|
function normalizeOperatingHours(params) {
|
|
415
|
-
var _params$input, _params$input2, _params$input3;
|
|
495
|
+
var _params$input, _params$input2, _params$input3, _params$input4;
|
|
416
496
|
var fallbackWindows = enumerateDates(params.dateRange.startDate, params.dateRange.endDate).map(function (date) {
|
|
417
497
|
var start = toDateTimeInDate(date, params.businessHours.startTime, params.timezoneName);
|
|
418
498
|
var end = toDateTimeInDate(date, params.businessHours.endTime, params.timezoneName);
|
|
@@ -430,26 +510,32 @@ function normalizeOperatingHours(params) {
|
|
|
430
510
|
endAt: formatDateTime(range.end, params.timezoneName, params.fixedOffsetMinutes)
|
|
431
511
|
};
|
|
432
512
|
});
|
|
433
|
-
var
|
|
434
|
-
return
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
source: ((_params$input = params.input) === null || _params$input === void 0 ? void 0 : _params$input.source) || params.fallbackSource,
|
|
449
|
-
scheduleIds: uniqById(((_params$input2 = params.input) === null || _params$input2 === void 0 ? void 0 : _params$input2.scheduleIds) || []),
|
|
450
|
-
missingScheduleIds: uniqById(((_params$input3 = params.input) === null || _params$input3 === void 0 ? void 0 : _params$input3.missingScheduleIds) || []),
|
|
451
|
-
windows: windows
|
|
513
|
+
var normalizeWindows = function normalizeWindows(source) {
|
|
514
|
+
return mergeRanges(source.map(function (window) {
|
|
515
|
+
return normalizeAbsoluteRange(window, params.timezoneName);
|
|
516
|
+
}).filter(function (window) {
|
|
517
|
+
return Boolean(window);
|
|
518
|
+
}).map(function (window) {
|
|
519
|
+
return clipRange(window, params.coverage);
|
|
520
|
+
}).filter(function (window) {
|
|
521
|
+
return Boolean(window);
|
|
522
|
+
})).map(function (range) {
|
|
523
|
+
return {
|
|
524
|
+
startAt: formatDateTime(range.start, params.timezoneName, params.fixedOffsetMinutes),
|
|
525
|
+
endAt: formatDateTime(range.end, params.timezoneName, params.fixedOffsetMinutes)
|
|
526
|
+
};
|
|
527
|
+
});
|
|
452
528
|
};
|
|
529
|
+
var windows = normalizeWindows(sourceWindows);
|
|
530
|
+
var excludedWindows = normalizeWindows(((_params$input = params.input) === null || _params$input === void 0 ? void 0 : _params$input.excludedWindows) || []);
|
|
531
|
+
return _objectSpread({
|
|
532
|
+
source: ((_params$input2 = params.input) === null || _params$input2 === void 0 ? void 0 : _params$input2.source) || params.fallbackSource,
|
|
533
|
+
scheduleIds: uniqById(((_params$input3 = params.input) === null || _params$input3 === void 0 ? void 0 : _params$input3.scheduleIds) || []),
|
|
534
|
+
missingScheduleIds: uniqById(((_params$input4 = params.input) === null || _params$input4 === void 0 ? void 0 : _params$input4.missingScheduleIds) || []),
|
|
535
|
+
windows: windows
|
|
536
|
+
}, excludedWindows.length > 0 ? {
|
|
537
|
+
excludedWindows: excludedWindows
|
|
538
|
+
} : {});
|
|
453
539
|
}
|
|
454
540
|
function matchesRequirementGroup(requirementGroup, resource, product) {
|
|
455
541
|
var _requirementGroup$res;
|
|
@@ -759,11 +845,12 @@ function resolveProjectionDateRange(projection, requestDateRange) {
|
|
|
759
845
|
};
|
|
760
846
|
}
|
|
761
847
|
function buildOperatingRanges(projection, dateRange) {
|
|
848
|
+
var includeExcludedWindows = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
762
849
|
var coverage = {
|
|
763
850
|
start: toDateStart(dateRange.startDate, projection.meta.timezone),
|
|
764
851
|
end: toDateEnd(nextDate(dateRange.endDate), projection.meta.timezone)
|
|
765
852
|
};
|
|
766
|
-
return mergeRanges(projection.meta.defaults.operatingHours.windows.map(function (window) {
|
|
853
|
+
return mergeRanges([].concat(_toConsumableArray(projection.meta.defaults.operatingHours.windows), _toConsumableArray(includeExcludedWindows ? projection.meta.defaults.operatingHours.excludedWindows || [] : [])).map(function (window) {
|
|
767
854
|
return normalizeAbsoluteRange(window, projection.meta.timezone);
|
|
768
855
|
}).filter(function (window) {
|
|
769
856
|
return Boolean(window);
|
|
@@ -774,12 +861,13 @@ function buildOperatingRanges(projection, dateRange) {
|
|
|
774
861
|
}));
|
|
775
862
|
}
|
|
776
863
|
function buildResourceWindowIndex(projection) {
|
|
864
|
+
var includeExcludedWindows = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
777
865
|
var coverage = {
|
|
778
866
|
start: toDateTime(projection.meta.coverage.startAt, projection.meta.timezone),
|
|
779
867
|
end: toDateTime(projection.meta.coverage.endAt, projection.meta.timezone)
|
|
780
868
|
};
|
|
781
869
|
return new Map(projection.resources.map(function (resource) {
|
|
782
|
-
return [String(resource.id), normalizeResourceWindow(resource, coverage, projection.meta.timezone)];
|
|
870
|
+
return [String(resource.id), normalizeResourceWindow(resource, coverage, projection.meta.timezone, includeExcludedWindows)];
|
|
783
871
|
}));
|
|
784
872
|
}
|
|
785
873
|
function buildGroupWorkingRanges(params) {
|
|
@@ -801,7 +889,7 @@ function buildGroupWorkingRanges(params) {
|
|
|
801
889
|
return !filteredResourceIdSet || filteredResourceIdSet.has(String(resource.id));
|
|
802
890
|
}).map(function (resource) {
|
|
803
891
|
var _params$resourceWindo;
|
|
804
|
-
var windows = ((_params$resourceWindo = params.resourceWindowIndex) === null || _params$resourceWindo === void 0 ? void 0 : _params$resourceWindo.get(String(resource.id))) || normalizeResourceWindow(resource, coverage, projection.meta.timezone);
|
|
892
|
+
var windows = ((_params$resourceWindo = params.resourceWindowIndex) === null || _params$resourceWindo === void 0 ? void 0 : _params$resourceWindo.get(String(resource.id))) || normalizeResourceWindow(resource, coverage, projection.meta.timezone, params.includeExcludedWindows === true);
|
|
805
893
|
return windows.map(function (window) {
|
|
806
894
|
return clipRange(window, coverage);
|
|
807
895
|
}).filter(function (window) {
|
|
@@ -848,11 +936,12 @@ function buildGroupWorkingRanges(params) {
|
|
|
848
936
|
return mergeRanges(satisfiedRanges);
|
|
849
937
|
}
|
|
850
938
|
function materializeScheduleRules(rules, dateRange, timezoneName, fixedOffsetMinutes) {
|
|
939
|
+
var allowExcludedScheduleDates = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
|
|
851
940
|
var dates = enumerateDates(dateRange.startDate, dateRange.endDate);
|
|
852
941
|
var seen = new Map();
|
|
853
942
|
rules.forEach(function (rule) {
|
|
854
943
|
dates.forEach(function (date) {
|
|
855
|
-
if (!matchesScheduleRuleDate(rule, date, timezoneName)) return;
|
|
944
|
+
if (!matchesScheduleRuleDate(rule, date, timezoneName, allowExcludedScheduleDates)) return;
|
|
856
945
|
var start = toDateTimeInDate(date, rule.startTime, timezoneName);
|
|
857
946
|
var end = toDateTimeInDate(date, rule.endTime, timezoneName);
|
|
858
947
|
if (end <= start) end = toDateTimeInDate(nextDate(date, timezoneName), rule.endTime, timezoneName);
|
|
@@ -885,8 +974,9 @@ function materializeScheduleRules(rules, dateRange, timezoneName, fixedOffsetMin
|
|
|
885
974
|
}
|
|
886
975
|
function matchesScheduleRuleDate(rule, date, _timezoneName) {
|
|
887
976
|
var _rule$includeDates, _rule$excludeDates;
|
|
977
|
+
var allowExcludedScheduleDate = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
888
978
|
if ((_rule$includeDates = rule.includeDates) !== null && _rule$includeDates !== void 0 && _rule$includeDates.includes(date)) return true;
|
|
889
|
-
if ((_rule$excludeDates = rule.excludeDates) !== null && _rule$excludeDates !== void 0 && _rule$excludeDates.includes(date)) return false;
|
|
979
|
+
if (!allowExcludedScheduleDate && (_rule$excludeDates = rule.excludeDates) !== null && _rule$excludeDates !== void 0 && _rule$excludeDates.includes(date)) return false;
|
|
890
980
|
if (rule.startDate && date < rule.startDate) return false;
|
|
891
981
|
if (rule.endDate && date > rule.endDate) return false;
|
|
892
982
|
if (rule.mode === 'designation') {
|
|
@@ -942,6 +1032,7 @@ function buildGroupResourceIndex(params) {
|
|
|
942
1032
|
}
|
|
943
1033
|
function getSessionRangesForProduct(projection, product, dateRange) {
|
|
944
1034
|
var _extractFixedOffsetMi2;
|
|
1035
|
+
var allowExcludedScheduleDates = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
945
1036
|
var fixedOffsetMinutes = (_extractFixedOffsetMi2 = extractFixedOffsetMinutes(projection.meta.now)) !== null && _extractFixedOffsetMi2 !== void 0 ? _extractFixedOffsetMi2 : captureRuntimeOffsetMinutes();
|
|
946
1037
|
var ranges = (product.sessionRanges || []).map(function (range) {
|
|
947
1038
|
var _range$scheduleRefs2;
|
|
@@ -967,7 +1058,7 @@ function getSessionRangesForProduct(projection, product, dateRange) {
|
|
|
967
1058
|
primaryScheduleRef: (_range$scheduleRefs2 = range.scheduleRefs) === null || _range$scheduleRefs2 === void 0 ? void 0 : _range$scheduleRefs2[0]
|
|
968
1059
|
};
|
|
969
1060
|
}).filter(Boolean);
|
|
970
|
-
var ruleRanges = materializeScheduleRules(product.sessionRules || [], dateRange, projection.meta.timezone, fixedOffsetMinutes);
|
|
1061
|
+
var ruleRanges = materializeScheduleRules(product.sessionRules || [], dateRange, projection.meta.timezone, fixedOffsetMinutes, allowExcludedScheduleDates);
|
|
971
1062
|
return dedupeProductRanges([].concat(_toConsumableArray(ranges), _toConsumableArray(ruleRanges.map(function (range) {
|
|
972
1063
|
return _objectSpread(_objectSpread({}, range), {}, {
|
|
973
1064
|
key: "".concat(String(product.id), ":").concat(range.startAt, ":").concat(range.endAt),
|
|
@@ -1022,7 +1113,8 @@ export function getProductTimeRanges(projection) {
|
|
|
1022
1113
|
var dateRange = resolveProjectionDateRange(projection, request.dateRange);
|
|
1023
1114
|
var slotStepMinutes = projection.meta.defaults.slotStepMinutes;
|
|
1024
1115
|
var fixedOffsetMinutes = (_extractFixedOffsetMi3 = extractFixedOffsetMinutes(projection.meta.now)) !== null && _extractFixedOffsetMi3 !== void 0 ? _extractFixedOffsetMi3 : captureRuntimeOffsetMinutes();
|
|
1025
|
-
var
|
|
1116
|
+
var allowExcludedScheduleDateOverride = runtimeOptions.allowExcludedScheduleDateOverride === true;
|
|
1117
|
+
var resourceWindowIndex = buildResourceWindowIndex(projection, allowExcludedScheduleDateOverride);
|
|
1026
1118
|
var filteredProductIdSet = (_request$productIds = request.productIds) !== null && _request$productIds !== void 0 && _request$productIds.length ? new Set(request.productIds.map(String)) : null;
|
|
1027
1119
|
return projection.products.filter(function (product) {
|
|
1028
1120
|
return !filteredProductIdSet || filteredProductIdSet.has(String(product.id));
|
|
@@ -1037,7 +1129,7 @@ export function getProductTimeRanges(projection) {
|
|
|
1037
1129
|
};
|
|
1038
1130
|
}
|
|
1039
1131
|
if (product.kind === 'session' || product.kind === 'venue_slot') {
|
|
1040
|
-
var fixedRanges = getSessionRangesForProduct(projection, product, dateRange);
|
|
1132
|
+
var fixedRanges = getSessionRangesForProduct(projection, product, dateRange, allowExcludedScheduleDateOverride);
|
|
1041
1133
|
var customRanges = [];
|
|
1042
1134
|
var customDurationMinutes = resolveRequestedSessionDurationMinutes(product, request, runtimeOptions.allowSessionCustomRanges === true);
|
|
1043
1135
|
var _requestedStartTimes = resolveRequestedDurationStartTimes(product, request, runtimeOptions.allowSessionCustomRanges === true);
|
|
@@ -1059,7 +1151,7 @@ export function getProductTimeRanges(projection) {
|
|
|
1059
1151
|
var requiredGroups = (product.requirementGroups || []).filter(function (group) {
|
|
1060
1152
|
return group.required !== false;
|
|
1061
1153
|
});
|
|
1062
|
-
var operatingRanges = buildOperatingRanges(projection, dateRange);
|
|
1154
|
+
var operatingRanges = buildOperatingRanges(projection, dateRange, allowExcludedScheduleDateOverride);
|
|
1063
1155
|
var operatingEndAt = dateRange.startDate === dateRange.endDate && operatingRanges.length > 0 ? formatDateTime(Math.max.apply(Math, _toConsumableArray(operatingRanges.map(function (range) {
|
|
1064
1156
|
return range.end;
|
|
1065
1157
|
}))), projection.meta.timezone, fixedOffsetMinutes) : undefined;
|
|
@@ -1071,7 +1163,8 @@ export function getProductTimeRanges(projection) {
|
|
|
1071
1163
|
dateRange: dateRange,
|
|
1072
1164
|
resourceIds: request.resourceIds,
|
|
1073
1165
|
partySize: normalizedPartySize,
|
|
1074
|
-
resourceWindowIndex: resourceWindowIndex
|
|
1166
|
+
resourceWindowIndex: resourceWindowIndex,
|
|
1167
|
+
includeExcludedWindows: allowExcludedScheduleDateOverride
|
|
1075
1168
|
});
|
|
1076
1169
|
}).reduce(function (current, ranges) {
|
|
1077
1170
|
return current === null ? ranges : intersectRangeLists(current, ranges);
|
|
@@ -1088,7 +1181,8 @@ export function getProductTimeRanges(projection) {
|
|
|
1088
1181
|
}
|
|
1089
1182
|
return floorToMinute(evaluatedAt);
|
|
1090
1183
|
}();
|
|
1091
|
-
var
|
|
1184
|
+
var durationCandidateAnchor = toDateTime(projection.meta.durationCandidateAnchorAt, projection.meta.timezone);
|
|
1185
|
+
var durationAnchor = runtimeOptions.useExactDurationCandidateMinute ? floorToMinute(durationCandidateAnchor) : ceilToTenMinutes(durationCandidateAnchor, projection.meta.timezone);
|
|
1092
1186
|
var requestedStartTimes = resolveRequestedDurationStartTimes(product, request);
|
|
1093
1187
|
var ranges = [];
|
|
1094
1188
|
baseRanges.forEach(function (range) {
|
|
@@ -1353,7 +1447,9 @@ function buildEvaluationContext(params) {
|
|
|
1353
1447
|
evaluatedAt: evaluatedAt,
|
|
1354
1448
|
evaluatedAtMs: evaluatedAt,
|
|
1355
1449
|
resourceIdSet: (_params$resourceIds2 = params.resourceIds) !== null && _params$resourceIds2 !== void 0 && _params$resourceIds2.length ? new Set(params.resourceIds.map(String)) : null,
|
|
1356
|
-
selectionsByGroupKey: selectionsByGroupKey
|
|
1450
|
+
selectionsByGroupKey: selectionsByGroupKey,
|
|
1451
|
+
useExactDurationCandidateMinute: params.useExactDurationCandidateMinute === true,
|
|
1452
|
+
allowExcludedScheduleDateOverride: params.allowExcludedScheduleDateOverride === true
|
|
1357
1453
|
};
|
|
1358
1454
|
}
|
|
1359
1455
|
function summarizeStatuses(statuses) {
|
|
@@ -1525,7 +1621,8 @@ function evaluateCandidateSummary(projection, candidate, request) {
|
|
|
1525
1621
|
var evaluationContext = request.evaluationContext || buildEvaluationContext({
|
|
1526
1622
|
projection: projection,
|
|
1527
1623
|
evaluatedAt: request.evaluatedAt,
|
|
1528
|
-
resourceIds: request.resourceIds
|
|
1624
|
+
resourceIds: request.resourceIds,
|
|
1625
|
+
allowExcludedScheduleDateOverride: request.allowExcludedScheduleDateOverride
|
|
1529
1626
|
});
|
|
1530
1627
|
var requestedPartySize = normalizePartySize(request.partySize);
|
|
1531
1628
|
var requiredGroups = (product.requirementGroups || []).filter(function (group) {
|
|
@@ -1543,7 +1640,9 @@ function evaluateCandidateSummary(projection, candidate, request) {
|
|
|
1543
1640
|
});
|
|
1544
1641
|
});
|
|
1545
1642
|
var gate = evaluateCandidateTimeGate({
|
|
1643
|
+
projection: projection,
|
|
1546
1644
|
product: product,
|
|
1645
|
+
candidate: candidate,
|
|
1547
1646
|
candidateRange: candidateRange,
|
|
1548
1647
|
evaluationContext: evaluationContext,
|
|
1549
1648
|
timezoneName: projection.meta.timezone
|
|
@@ -1598,7 +1697,8 @@ function evaluateCandidate(projection, candidate, request) {
|
|
|
1598
1697
|
projection: projection,
|
|
1599
1698
|
evaluatedAt: request.evaluatedAt,
|
|
1600
1699
|
resourceIds: request.resourceIds,
|
|
1601
|
-
requirementSelections: request.requirementSelections
|
|
1700
|
+
requirementSelections: request.requirementSelections,
|
|
1701
|
+
allowExcludedScheduleDateOverride: request.allowExcludedScheduleDateOverride
|
|
1602
1702
|
});
|
|
1603
1703
|
var requestedPartySize = normalizePartySize(request.partySize);
|
|
1604
1704
|
var partySize = requestedPartySize !== null && requestedPartySize !== void 0 ? requestedPartySize : 1;
|
|
@@ -1621,6 +1721,12 @@ function evaluateCandidate(projection, candidate, request) {
|
|
|
1621
1721
|
var options = matchedResources.map(function (resource) {
|
|
1622
1722
|
var _request$cellIndex, _resource$raw2;
|
|
1623
1723
|
var matchedCell = aggregateMatchedCells(product.id, requirementGroup.id, resource.id, candidateRange, ((_request$cellIndex = request.cellIndex) === null || _request$cellIndex === void 0 ? void 0 : _request$cellIndex.get([String(product.id), requirementGroup.id, String(resource.id)].join(':'))) || []);
|
|
1724
|
+
var isExcludedScheduleDate = evaluationContext.allowExcludedScheduleDateOverride && isRangeFromExcludedWindows({
|
|
1725
|
+
normalWindows: resource.windows || [],
|
|
1726
|
+
excludedWindows: resource.excludedWindows || [],
|
|
1727
|
+
candidateRange: candidateRange,
|
|
1728
|
+
timezoneName: projection.meta.timezone
|
|
1729
|
+
});
|
|
1624
1730
|
if (!matchedCell) {
|
|
1625
1731
|
var _resource$raw;
|
|
1626
1732
|
var _isPast = candidateRange.endMs <= evaluationContext.evaluatedAtMs;
|
|
@@ -1632,7 +1738,7 @@ function evaluateCandidate(projection, candidate, request) {
|
|
|
1632
1738
|
resourceType: resource.resourceType,
|
|
1633
1739
|
status: _isPast ? 'past' : 'unavailable',
|
|
1634
1740
|
maxPartySize: 0,
|
|
1635
|
-
reasonCodes: _isPast ? ['past'] : ['outside_resource_window']
|
|
1741
|
+
reasonCodes: _isPast ? ['past'] : [].concat(_toConsumableArray(isExcludedScheduleDate ? ['schedule_excluded_date'] : []), ['outside_resource_window'])
|
|
1636
1742
|
};
|
|
1637
1743
|
}
|
|
1638
1744
|
var remainingCapacity = matchedCell.remainingCapacity;
|
|
@@ -1640,6 +1746,7 @@ function evaluateCandidate(projection, candidate, request) {
|
|
|
1640
1746
|
var isPast = candidateRange.endMs <= evaluationContext.evaluatedAtMs;
|
|
1641
1747
|
var maxPartySize = isPast ? 0 : Math.max(0, remainingCapacity || 0);
|
|
1642
1748
|
var requiredCapacity = resolveRequiredCapacity(requirementGroup, product, requestedPartySize);
|
|
1749
|
+
var reasonCodes = Array.from(new Set([].concat(_toConsumableArray(matchedCell.reasonCodes), _toConsumableArray(isExcludedScheduleDate ? ['schedule_excluded_date'] : []))));
|
|
1643
1750
|
var status = function () {
|
|
1644
1751
|
if (isPast) return 'past';
|
|
1645
1752
|
if (matchedCell.status === 'unavailable') return 'unavailable';
|
|
@@ -1657,7 +1764,7 @@ function evaluateCandidate(projection, candidate, request) {
|
|
|
1657
1764
|
remainingCapacity: remainingCapacity,
|
|
1658
1765
|
maxCapacity: maxCapacity,
|
|
1659
1766
|
maxPartySize: maxPartySize,
|
|
1660
|
-
reasonCodes: isPast ? Array.from(new Set([].concat(
|
|
1767
|
+
reasonCodes: isPast ? Array.from(new Set([].concat(reasonCodes, ['past']))) : reasonCodes,
|
|
1661
1768
|
matchedCell: matchedCell
|
|
1662
1769
|
};
|
|
1663
1770
|
});
|
|
@@ -1715,7 +1822,9 @@ function evaluateCandidate(projection, candidate, request) {
|
|
|
1715
1822
|
return group.status;
|
|
1716
1823
|
});
|
|
1717
1824
|
var gate = evaluateCandidateTimeGate({
|
|
1825
|
+
projection: projection,
|
|
1718
1826
|
product: product,
|
|
1827
|
+
candidate: candidate,
|
|
1719
1828
|
candidateRange: candidateRange,
|
|
1720
1829
|
evaluationContext: evaluationContext,
|
|
1721
1830
|
timezoneName: projection.meta.timezone
|
|
@@ -1804,6 +1913,7 @@ function assertRangesWithinProjection(projection, ranges) {
|
|
|
1804
1913
|
}
|
|
1805
1914
|
export function queryAvailabilityProjection(projection, request) {
|
|
1806
1915
|
var _extractFixedOffsetMi6, _request$productIds2;
|
|
1916
|
+
var runtimeOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
1807
1917
|
var normalizedPartySize = normalizePartySize(request.partySize);
|
|
1808
1918
|
var evaluatedAt = formatDateTime(localDateToScalar(new Date()), projection.meta.timezone, (_extractFixedOffsetMi6 = extractFixedOffsetMinutes(projection.meta.now)) !== null && _extractFixedOffsetMi6 !== void 0 ? _extractFixedOffsetMi6 : captureRuntimeOffsetMinutes());
|
|
1809
1919
|
if (request.target === 'time') {
|
|
@@ -1826,7 +1936,9 @@ export function queryAvailabilityProjection(projection, request) {
|
|
|
1826
1936
|
var evaluationContext = buildEvaluationContext({
|
|
1827
1937
|
projection: projection,
|
|
1828
1938
|
evaluatedAt: evaluatedAt,
|
|
1829
|
-
resourceIds: request.resourceIds
|
|
1939
|
+
resourceIds: request.resourceIds,
|
|
1940
|
+
useExactDurationCandidateMinute: runtimeOptions.useExactDurationCandidateMinute,
|
|
1941
|
+
allowExcludedScheduleDateOverride: runtimeOptions.allowExcludedScheduleDateOverride
|
|
1830
1942
|
});
|
|
1831
1943
|
if (request.target === 'time') {
|
|
1832
1944
|
var candidates = dedupeProductRanges(request.candidates);
|
|
@@ -1914,7 +2026,9 @@ export function queryAvailabilityProjection(projection, request) {
|
|
|
1914
2026
|
resourceIds: request.resourceIds,
|
|
1915
2027
|
partySize: normalizedPartySize
|
|
1916
2028
|
}, {
|
|
1917
|
-
evaluatedAt: evaluatedAt
|
|
2029
|
+
evaluatedAt: evaluatedAt,
|
|
2030
|
+
useExactDurationCandidateMinute: runtimeOptions.useExactDurationCandidateMinute,
|
|
2031
|
+
allowExcludedScheduleDateOverride: runtimeOptions.allowExcludedScheduleDateOverride
|
|
1918
2032
|
});
|
|
1919
2033
|
var candidateGroupMap = new Map(candidateGroups.map(function (group) {
|
|
1920
2034
|
return [String(group.productId), group];
|
|
@@ -2092,6 +2206,7 @@ function summarizeProductStatus(statuses) {
|
|
|
2092
2206
|
}
|
|
2093
2207
|
export function validateAvailabilitySelection(projection, params) {
|
|
2094
2208
|
var _extractFixedOffsetMi7;
|
|
2209
|
+
var runtimeOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
2095
2210
|
var normalizedPartySize = normalizePartySize(params.partySize);
|
|
2096
2211
|
var normalizedBookingCount = normalizeBookingCount(params.bookingCount);
|
|
2097
2212
|
assertRangesWithinProjection(projection, [params.candidate]);
|
|
@@ -2112,14 +2227,16 @@ export function validateAvailabilitySelection(projection, params) {
|
|
|
2112
2227
|
evaluationContext: buildEvaluationContext({
|
|
2113
2228
|
projection: projection,
|
|
2114
2229
|
evaluatedAt: evaluatedAt,
|
|
2115
|
-
requirementSelections: params.requirementSelections
|
|
2230
|
+
requirementSelections: params.requirementSelections,
|
|
2231
|
+
useExactDurationCandidateMinute: runtimeOptions.useExactDurationCandidateMinute,
|
|
2232
|
+
allowExcludedScheduleDateOverride: runtimeOptions.allowExcludedScheduleDateOverride
|
|
2116
2233
|
})
|
|
2117
2234
|
});
|
|
2118
2235
|
var conflicts = [];
|
|
2119
2236
|
if (evaluated.gate) {
|
|
2120
2237
|
conflicts.push({
|
|
2121
2238
|
code: evaluated.gate.conflictCode,
|
|
2122
|
-
message: evaluated.gate.
|
|
2239
|
+
message: evaluated.gate.conflictCode === 'past' ? '候选时间已过期' : evaluated.gate.conflictCode === 'schedule_excluded_date' ? '候选日期已被日程排除' : '候选时间不满足预订时间门禁',
|
|
2123
2240
|
reasonCodes: evaluated.gate.reasonCodes,
|
|
2124
2241
|
productId: params.candidate.productId,
|
|
2125
2242
|
candidateKey: params.candidate.key
|
|
@@ -101,6 +101,9 @@ export interface AvailabilityOperatingHoursSnapshot {
|
|
|
101
101
|
scheduleIds: AvailabilityId[];
|
|
102
102
|
missingScheduleIds: AvailabilityId[];
|
|
103
103
|
windows: AvailabilityResourceWindow[];
|
|
104
|
+
/** Schedule windows omitted only because their date is excluded. POS may use
|
|
105
|
+
* these to surface an unavailable, manually selectable candidate. */
|
|
106
|
+
excludedWindows?: AvailabilityResourceWindow[];
|
|
104
107
|
}
|
|
105
108
|
export interface AvailabilityResource {
|
|
106
109
|
id: AvailabilityId;
|
|
@@ -110,6 +113,8 @@ export interface AvailabilityResource {
|
|
|
110
113
|
resourceType: AvailabilityResourceType;
|
|
111
114
|
capacity: number;
|
|
112
115
|
windows: AvailabilityResourceWindow[];
|
|
116
|
+
/** Resource work windows omitted only by an excluded schedule date. */
|
|
117
|
+
excludedWindows?: AvailabilityResourceWindow[];
|
|
113
118
|
raw?: Record<string, any>;
|
|
114
119
|
}
|
|
115
120
|
export interface AvailabilityOccupancy extends AvailabilityAbsoluteRange {
|
|
@@ -245,6 +250,13 @@ export interface GetProductTimeRangesRequest {
|
|
|
245
250
|
}
|
|
246
251
|
export interface GetProductTimeRangesRuntimeOptions {
|
|
247
252
|
evaluatedAt?: string;
|
|
253
|
+
/**
|
|
254
|
+
* Internal POS capability: fixed-duration candidates start from the stable
|
|
255
|
+
* context anchor's current minute instead of rounding up to ten minutes, and
|
|
256
|
+
* that minute remains selectable during before-start cutoff evaluation.
|
|
257
|
+
* Other callers keep the legacy alignment and strict second-level cutoff.
|
|
258
|
+
*/
|
|
259
|
+
useExactDurationCandidateMinute?: boolean;
|
|
248
260
|
/**
|
|
249
261
|
* Internal POS capability: allow explicit duration/start overrides to append
|
|
250
262
|
* a custom duration candidate for Session products. Fixed Session ranges are
|
|
@@ -252,6 +264,11 @@ export interface GetProductTimeRangesRuntimeOptions {
|
|
|
252
264
|
* disabled.
|
|
253
265
|
*/
|
|
254
266
|
allowSessionCustomRanges?: boolean;
|
|
267
|
+
/**
|
|
268
|
+
* Internal POS capability: materialize candidates whose schedule date was
|
|
269
|
+
* explicitly excluded, while availability evaluation keeps them unavailable.
|
|
270
|
+
*/
|
|
271
|
+
allowExcludedScheduleDateOverride?: boolean;
|
|
255
272
|
}
|
|
256
273
|
export interface QueryAvailabilityBaseRequest {
|
|
257
274
|
productIds?: AvailabilityId[];
|