@pisell/pisellos 2.2.299 → 2.2.300
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 +151 -41
- package/dist/modules/ResourcePlanner/types.d.ts +10 -0
- package/dist/solution/UnifiedBookingSales/index.js +88 -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 +125 -30
- package/lib/modules/ResourcePlanner/types.d.ts +10 -0
- package/lib/solution/UnifiedBookingSales/index.js +80 -20
- package/package.json +1 -1
|
@@ -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 {
|
|
@@ -252,6 +257,11 @@ export interface GetProductTimeRangesRuntimeOptions {
|
|
|
252
257
|
* disabled.
|
|
253
258
|
*/
|
|
254
259
|
allowSessionCustomRanges?: boolean;
|
|
260
|
+
/**
|
|
261
|
+
* Internal POS capability: materialize candidates whose schedule date was
|
|
262
|
+
* explicitly excluded, while availability evaluation keeps them unavailable.
|
|
263
|
+
*/
|
|
264
|
+
allowExcludedScheduleDateOverride?: boolean;
|
|
255
265
|
}
|
|
256
266
|
export interface QueryAvailabilityBaseRequest {
|
|
257
267
|
productIds?: AvailabilityId[];
|
|
@@ -638,6 +638,22 @@ function normalizeResource(raw, fixedOffsetMinutes = (0, _localClock.captureRunt
|
|
|
638
638
|
const id = raw.resourceId ?? raw.resource_id ?? raw.id;
|
|
639
639
|
if (id === undefined || id === null || id === '') return null;
|
|
640
640
|
const rawWindows = raw.windows || raw.times || [];
|
|
641
|
+
const rawExcludedWindows = raw.excludedWindows || raw.excluded_windows || [];
|
|
642
|
+
const normalizeWindows = windows => (Array.isArray(windows) ? windows : []).flatMap(window => {
|
|
643
|
+
const range = normalizeAvailabilityLocalDateTimeRange({
|
|
644
|
+
startAt: window.startAt || window.start_at || window.start,
|
|
645
|
+
endAt: window.endAt || window.end_at || window.end,
|
|
646
|
+
fixedOffsetMinutes,
|
|
647
|
+
rollOverEnd: true
|
|
648
|
+
});
|
|
649
|
+
if (!range) return [];
|
|
650
|
+
return [{
|
|
651
|
+
startAt: range.startAt,
|
|
652
|
+
endAt: range.endAt,
|
|
653
|
+
source: window.source,
|
|
654
|
+
raw: window
|
|
655
|
+
}];
|
|
656
|
+
});
|
|
641
657
|
return {
|
|
642
658
|
id,
|
|
643
659
|
formId: raw.formId ?? raw.form_id ?? raw.resource_form_id,
|
|
@@ -645,21 +661,8 @@ function normalizeResource(raw, fixedOffsetMinutes = (0, _localClock.captureRunt
|
|
|
645
661
|
code: raw.code,
|
|
646
662
|
resourceType: normalizeResourceType(raw.type || raw.resourceType),
|
|
647
663
|
capacity: Math.max(1, Number(raw.capacity || raw.max_capacity || 1) || 1),
|
|
648
|
-
windows: (
|
|
649
|
-
|
|
650
|
-
startAt: window.startAt || window.start_at || window.start,
|
|
651
|
-
endAt: window.endAt || window.end_at || window.end,
|
|
652
|
-
fixedOffsetMinutes,
|
|
653
|
-
rollOverEnd: true
|
|
654
|
-
});
|
|
655
|
-
if (!range) return [];
|
|
656
|
-
return [{
|
|
657
|
-
startAt: range.startAt,
|
|
658
|
-
endAt: range.endAt,
|
|
659
|
-
source: window.source,
|
|
660
|
-
raw: window
|
|
661
|
-
}];
|
|
662
|
-
}),
|
|
664
|
+
windows: normalizeWindows(rawWindows),
|
|
665
|
+
excludedWindows: normalizeWindows(rawExcludedWindows),
|
|
663
666
|
raw
|
|
664
667
|
};
|
|
665
668
|
}
|
|
@@ -678,6 +681,17 @@ function dateRangeDays(dateRange) {
|
|
|
678
681
|
}
|
|
679
682
|
return dates;
|
|
680
683
|
}
|
|
684
|
+
function getExcludedScheduleDateOverrideTimePoints(date, schedule) {
|
|
685
|
+
if (!schedule.repeat_rule?.excluded_date?.length) return [];
|
|
686
|
+
if ((0, _getDateIsInSchedule.getScheduleStartEndTimePoints)(date, [schedule]).length > 0) return [];
|
|
687
|
+
return (0, _getDateIsInSchedule.getScheduleStartEndTimePoints)(date, [{
|
|
688
|
+
...schedule,
|
|
689
|
+
repeat_rule: {
|
|
690
|
+
...schedule.repeat_rule,
|
|
691
|
+
excluded_date: []
|
|
692
|
+
}
|
|
693
|
+
}]);
|
|
694
|
+
}
|
|
681
695
|
function normalizeResourceOccupancy(rawEvent, resourceId, source = 'remote', fixedOffsetMinutes = (0, _localClock.captureRuntimeOffsetMinutes)()) {
|
|
682
696
|
const range = normalizeAvailabilityLocalDateTimeRange({
|
|
683
697
|
startAt: rawEvent.start_at ?? rawEvent.startAt ?? rawEvent.start_time ?? rawEvent.startTime,
|
|
@@ -770,7 +784,26 @@ function buildAvailabilityResourcesFromV2(params) {
|
|
|
770
784
|
}
|
|
771
785
|
}];
|
|
772
786
|
})));
|
|
787
|
+
const excludedWindows = dates.flatMap(date => schedules.flatMap(schedule => getExcludedScheduleDateOverrideTimePoints(date, schedule).flatMap(slot => {
|
|
788
|
+
const range = normalizeAvailabilityLocalDateTimeRange({
|
|
789
|
+
startAt: slot.start_at,
|
|
790
|
+
endAt: slot.end_at,
|
|
791
|
+
fixedOffsetMinutes,
|
|
792
|
+
rollOverEnd: true
|
|
793
|
+
});
|
|
794
|
+
if (!range) return [];
|
|
795
|
+
return [{
|
|
796
|
+
startAt: range.startAt,
|
|
797
|
+
endAt: range.endAt,
|
|
798
|
+
source: `resource_schedule_excluded:${schedule.id}`,
|
|
799
|
+
raw: {
|
|
800
|
+
scheduleId: schedule.id,
|
|
801
|
+
excludedDate: date
|
|
802
|
+
}
|
|
803
|
+
}];
|
|
804
|
+
})));
|
|
773
805
|
resource.windows = materializedWindows.length > 0 ? materializedWindows : explicitWindows;
|
|
806
|
+
resource.excludedWindows = [...(resource.excludedWindows || []), ...excludedWindows];
|
|
774
807
|
if (resource.windows.length === 0 && missingScheduleIds.length > 0) {
|
|
775
808
|
resource.windows = buildResourceWindowFallbackTimes({
|
|
776
809
|
raw,
|
|
@@ -1011,7 +1044,7 @@ function buildBookingFromAvailabilitySelection(params) {
|
|
|
1011
1044
|
}
|
|
1012
1045
|
};
|
|
1013
1046
|
}
|
|
1014
|
-
const POS_OVERRIDABLE_AVAILABILITY_CONFLICT_CODES = new Set(['past', 'booking_cutoff', 'resource_full', 'resource_unavailable', 'resource_capacity_exceeded',
|
|
1047
|
+
const POS_OVERRIDABLE_AVAILABILITY_CONFLICT_CODES = new Set(['past', 'booking_cutoff', 'schedule_excluded_date', 'resource_full', 'resource_unavailable', 'resource_capacity_exceeded',
|
|
1015
1048
|
// A required group whose selected resource is outside its window is surfaced
|
|
1016
1049
|
// as missing_resource at group level. Structural validation below still
|
|
1017
1050
|
// rejects a genuinely missing selection.
|
|
@@ -2118,11 +2151,31 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
2118
2151
|
source: `operating_schedule:${descriptor.scheduleIds.map(String).join(',')}`
|
|
2119
2152
|
}] : [];
|
|
2120
2153
|
}));
|
|
2154
|
+
const excludedWindows = dateRangeDays(params.dateRange).flatMap(date => schedules.flatMap(schedule => getExcludedScheduleDateOverrideTimePoints(date, schedule).flatMap(slot => {
|
|
2155
|
+
const range = normalizeAvailabilityLocalDateTimeRange({
|
|
2156
|
+
startAt: slot.start_at,
|
|
2157
|
+
endAt: slot.end_at,
|
|
2158
|
+
fixedOffsetMinutes: this.getAvailabilityRuntimeOffsetMinutes(),
|
|
2159
|
+
rollOverEnd: true
|
|
2160
|
+
});
|
|
2161
|
+
return range ? [{
|
|
2162
|
+
startAt: range.startAt,
|
|
2163
|
+
endAt: range.endAt,
|
|
2164
|
+
source: `operating_schedule_excluded:${schedule.id}`,
|
|
2165
|
+
raw: {
|
|
2166
|
+
scheduleId: schedule.id,
|
|
2167
|
+
excludedDate: date
|
|
2168
|
+
}
|
|
2169
|
+
}] : [];
|
|
2170
|
+
})));
|
|
2121
2171
|
return {
|
|
2122
2172
|
source: descriptor.source,
|
|
2123
2173
|
scheduleIds: [...descriptor.scheduleIds],
|
|
2124
2174
|
missingScheduleIds,
|
|
2125
|
-
windows
|
|
2175
|
+
windows,
|
|
2176
|
+
...(excludedWindows.length > 0 ? {
|
|
2177
|
+
excludedWindows
|
|
2178
|
+
} : {})
|
|
2126
2179
|
};
|
|
2127
2180
|
}
|
|
2128
2181
|
async loadAvailabilityProducts(params) {
|
|
@@ -2820,7 +2873,8 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
2820
2873
|
} : {})
|
|
2821
2874
|
}, {
|
|
2822
2875
|
evaluatedAt: this.getAvailabilityRuntimeDateTime(),
|
|
2823
|
-
allowSessionCustomRanges: isPosAvailabilityRequest
|
|
2876
|
+
allowSessionCustomRanges: isPosAvailabilityRequest,
|
|
2877
|
+
allowExcludedScheduleDateOverride: isPosAvailabilityRequest
|
|
2824
2878
|
}).map(group => ({
|
|
2825
2879
|
...group,
|
|
2826
2880
|
ranges: group.ranges.map(candidate => this.decorateAvailabilityCandidate(context.contextId, contextVersion, candidate))
|
|
@@ -2835,7 +2889,10 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
2835
2889
|
refreshed
|
|
2836
2890
|
} = await this.ensureAvailabilityContextProjection(contextId, 'query_booking_availability');
|
|
2837
2891
|
this.assertContextualQueryFreshness(context, request);
|
|
2838
|
-
const
|
|
2892
|
+
const isPosAvailabilityRequest = String(this.otherParams?.platform ?? '').trim().toLowerCase() === 'pos';
|
|
2893
|
+
const result = (0, _ResourcePlanner.queryAvailabilityProjection)(projectionEntry.projection, request, {
|
|
2894
|
+
allowExcludedScheduleDateOverride: isPosAvailabilityRequest
|
|
2895
|
+
});
|
|
2839
2896
|
const contextVersion = context.activeVersion;
|
|
2840
2897
|
const contextualResult = result.target === 'time' ? {
|
|
2841
2898
|
...result,
|
|
@@ -3092,7 +3149,8 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
3092
3149
|
} : {})
|
|
3093
3150
|
} : {})
|
|
3094
3151
|
}, {
|
|
3095
|
-
allowSessionCustomRanges: allowPosAvailabilityOverride
|
|
3152
|
+
allowSessionCustomRanges: allowPosAvailabilityOverride,
|
|
3153
|
+
allowExcludedScheduleDateOverride: allowPosAvailabilityOverride
|
|
3096
3154
|
}).flatMap(group => group.ranges);
|
|
3097
3155
|
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);
|
|
3098
3156
|
if (!refreshedCandidateBase) {
|
|
@@ -3113,6 +3171,8 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
3113
3171
|
requirementSelections: params.requirementSelections,
|
|
3114
3172
|
partySize,
|
|
3115
3173
|
bookingCount
|
|
3174
|
+
}, {
|
|
3175
|
+
allowExcludedScheduleDateOverride: allowPosAvailabilityOverride
|
|
3116
3176
|
});
|
|
3117
3177
|
if (allowPosAvailabilityOverride && !validation.ok && validation.conflicts.every(conflict => POS_OVERRIDABLE_AVAILABILITY_CONFLICT_CODES.has(conflict.code))) {
|
|
3118
3178
|
const forced = buildPosForcedAvailabilityAssignments({
|