@pisell/pisellos 2.3.72 → 2.3.73
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/OpenData/types.d.ts +1 -0
- package/dist/modules/Order/utils.js +6 -1
- package/dist/modules/ResourcePlanner/index.d.ts +0 -22
- package/dist/modules/ResourcePlanner/index.js +1 -180
- package/dist/modules/ResourcePlanner/localClock.d.ts +33 -0
- package/dist/modules/ResourcePlanner/localClock.js +183 -0
- package/dist/modules/ResourcePlanner/planner.d.ts +5 -14
- package/dist/modules/ResourcePlanner/planner.js +2019 -936
- package/dist/modules/ResourcePlanner/types.d.ts +346 -187
- package/dist/modules/ResourcePlanner/types.js +33 -1
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.js +4 -2
- package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +2 -1
- package/dist/solution/BookingTicket/utils/addProductDecision.js +3 -2
- package/dist/solution/UnifiedBookingSales/index.d.ts +110 -73
- package/dist/solution/UnifiedBookingSales/index.js +3349 -971
- package/dist/solution/UnifiedBookingSales/remoteOccupancyCache.d.ts +49 -0
- package/dist/solution/UnifiedBookingSales/remoteOccupancyCache.js +430 -0
- package/dist/solution/UnifiedBookingSales/types.d.ts +133 -76
- package/dist/solution/UnifiedBookingSales/types.js +4 -9
- package/lib/model/strategy/adapter/promotion/index.js +0 -51
- package/lib/modules/OpenData/types.d.ts +1 -0
- package/lib/modules/Order/utils.js +5 -1
- package/lib/modules/ResourcePlanner/index.d.ts +0 -22
- package/lib/modules/ResourcePlanner/index.js +0 -123
- package/lib/modules/ResourcePlanner/localClock.d.ts +33 -0
- package/lib/modules/ResourcePlanner/localClock.js +270 -0
- package/lib/modules/ResourcePlanner/planner.d.ts +5 -14
- package/lib/modules/ResourcePlanner/planner.js +1903 -798
- package/lib/modules/ResourcePlanner/types.d.ts +346 -187
- package/lib/modules/ResourcePlanner/types.js +19 -0
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.js +2 -1
- package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +2 -1
- package/lib/solution/BookingTicket/utils/addProductDecision.js +2 -0
- package/lib/solution/UnifiedBookingSales/index.d.ts +110 -73
- package/lib/solution/UnifiedBookingSales/index.js +2289 -374
- package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.d.ts +49 -0
- package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.js +376 -0
- package/lib/solution/UnifiedBookingSales/types.d.ts +133 -76
- package/lib/solution/UnifiedBookingSales/types.js +3 -2
- package/package.json +1 -1
|
@@ -1,1069 +1,2152 @@
|
|
|
1
|
-
function
|
|
2
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
3
|
-
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
4
|
-
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
5
|
-
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
6
2
|
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
7
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."); }
|
|
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); }
|
|
8
5
|
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
9
6
|
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
10
|
-
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; } } }; }
|
|
11
|
-
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); }
|
|
12
7
|
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
productIds: [],
|
|
25
|
-
resourceIds: [],
|
|
26
|
-
assignments: []
|
|
8
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
9
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
10
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
11
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
12
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
13
|
+
import { AvailabilityError } from "./types";
|
|
14
|
+
import { addLocalCalendarDays, addLocalHours, addLocalMinutes, captureRuntimeOffsetMinutes, ceilLocalScalarToTenMinutes, dateOrdinalFromLocalDate, extractFixedOffsetMinutes, formatLocalDate, formatLocalDateTime, formatLocalTime, localDateTimeToScalar, localDateToScalar, localDateToScalarStart, nextDate as getNextLocalDate, parseLocalDate, parseLocalDateTime, weekdayFromLocalDate } from "./localClock";
|
|
15
|
+
var DEFAULT_SLOT_STEP_MINUTES = 30;
|
|
16
|
+
var DEFAULT_BUSINESS_HOURS = {
|
|
17
|
+
startTime: '09:00',
|
|
18
|
+
endTime: '18:00'
|
|
27
19
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
products: Array.isArray(input.products) ? input.products : [],
|
|
32
|
-
resources: Array.isArray(input.resources) ? input.resources : [],
|
|
33
|
-
externalEvents: Array.isArray(input.externalEvents) ? input.externalEvents : [],
|
|
34
|
-
slotDurationMinutes: input.slotDurationMinutes || DEFAULT_RESOURCE_PLANNER_CONTEXT.slotDurationMinutes,
|
|
35
|
-
businessStartTime: input.businessStartTime || DEFAULT_RESOURCE_PLANNER_CONTEXT.businessStartTime,
|
|
36
|
-
businessEndTime: input.businessEndTime || DEFAULT_RESOURCE_PLANNER_CONTEXT.businessEndTime
|
|
37
|
-
};
|
|
20
|
+
function availabilityError(code, message, details) {
|
|
21
|
+
return new AvailabilityError(code, message, details);
|
|
38
22
|
}
|
|
39
23
|
function sameId(left, right) {
|
|
24
|
+
if (left === undefined || left === null || right === undefined || right === null) return false;
|
|
40
25
|
return String(left) === String(right);
|
|
41
26
|
}
|
|
42
|
-
function
|
|
43
|
-
|
|
27
|
+
function uniqById() {
|
|
28
|
+
var values = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
|
29
|
+
var map = new Map();
|
|
30
|
+
values.forEach(function (value) {
|
|
31
|
+
map.set(String(value), value);
|
|
32
|
+
});
|
|
33
|
+
return Array.from(map.values());
|
|
34
|
+
}
|
|
35
|
+
function normalizePartySize(partySize) {
|
|
36
|
+
if (partySize === undefined || partySize === null) return undefined;
|
|
37
|
+
var normalized = Number(partySize);
|
|
38
|
+
if (!Number.isInteger(normalized) || normalized <= 0) {
|
|
39
|
+
throw availabilityError('INVALID_TARGET_FILTER', 'partySize 必须是正整数', {
|
|
40
|
+
partySize: partySize
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return normalized;
|
|
44
|
+
}
|
|
45
|
+
function normalizeBookingCount(bookingCount) {
|
|
46
|
+
if (bookingCount === undefined || bookingCount === null) return 1;
|
|
47
|
+
var normalized = Number(bookingCount);
|
|
48
|
+
if (!Number.isInteger(normalized) || normalized <= 0) {
|
|
49
|
+
throw availabilityError('INVALID_TARGET_FILTER', 'bookingCount 必须是正整数', {
|
|
50
|
+
bookingCount: bookingCount
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return normalized;
|
|
54
|
+
}
|
|
55
|
+
function toDateTime(value, _timezoneName) {
|
|
56
|
+
try {
|
|
57
|
+
return localDateTimeToScalar(parseLocalDateTime(value));
|
|
58
|
+
} catch (_error) {
|
|
59
|
+
return Number.NaN;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function toDateStart(value, _timezoneName) {
|
|
63
|
+
try {
|
|
64
|
+
return localDateToScalarStart(value);
|
|
65
|
+
} catch (_error) {
|
|
66
|
+
return Number.NaN;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function toDateEnd(value, _timezoneName) {
|
|
70
|
+
var start = toDateStart(value);
|
|
71
|
+
return Number.isFinite(start) ? addLocalCalendarDays(start, 1) - 1 : Number.NaN;
|
|
72
|
+
}
|
|
73
|
+
function toDateTimeInDate(date, time, _timezoneName) {
|
|
74
|
+
return toDateTime("".concat(date, "T").concat(time.length === 5 ? "".concat(time, ":00") : time));
|
|
44
75
|
}
|
|
45
|
-
function
|
|
46
|
-
return
|
|
76
|
+
function addMinutesZoned(value, minutes, _timezoneName) {
|
|
77
|
+
return addLocalMinutes(value, minutes);
|
|
47
78
|
}
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
return dayjs("".concat(date, " ").concat(time.length === 5 ? "".concat(time, ":00") : time));
|
|
79
|
+
function ceilToTenMinutes(value, _timezoneName) {
|
|
80
|
+
return ceilLocalScalarToTenMinutes(value);
|
|
51
81
|
}
|
|
52
|
-
function
|
|
53
|
-
return
|
|
82
|
+
function nextDate(value, _timezoneName) {
|
|
83
|
+
return getNextLocalDate(value);
|
|
54
84
|
}
|
|
55
|
-
function
|
|
56
|
-
return
|
|
85
|
+
function formatDateTime(value, _timezoneName, fixedOffsetMinutes) {
|
|
86
|
+
return formatLocalDateTime(value, fixedOffsetMinutes !== null && fixedOffsetMinutes !== void 0 ? fixedOffsetMinutes : captureRuntimeOffsetMinutes());
|
|
57
87
|
}
|
|
58
|
-
function
|
|
59
|
-
return
|
|
88
|
+
function formatDate(value, _timezoneName) {
|
|
89
|
+
return formatLocalDate(value);
|
|
60
90
|
}
|
|
61
|
-
function
|
|
62
|
-
return
|
|
91
|
+
function formatTime(value, _timezoneName) {
|
|
92
|
+
return formatLocalTime(value, false);
|
|
63
93
|
}
|
|
64
|
-
function
|
|
65
|
-
var
|
|
66
|
-
var
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
94
|
+
function normalizeAbsoluteRange(range, _timezoneName) {
|
|
95
|
+
var start = toDateTime(range.startAt);
|
|
96
|
+
var end = toDateTime(range.endAt);
|
|
97
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
|
|
98
|
+
return {
|
|
99
|
+
start: start,
|
|
100
|
+
end: end
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function getCandidateBookingRange(candidate) {
|
|
104
|
+
return {
|
|
105
|
+
// Runtime fallback keeps older caller-created candidates fail-safe during migration.
|
|
106
|
+
startAt: candidate.bookingStartAt || candidate.startAt,
|
|
107
|
+
endAt: candidate.bookingEndAt || candidate.endAt
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function getCandidateRangeCacheKey(candidate) {
|
|
111
|
+
var bookingRange = getCandidateBookingRange(candidate);
|
|
112
|
+
return "".concat(candidate.key, ":").concat(bookingRange.startAt, ":").concat(bookingRange.endAt);
|
|
113
|
+
}
|
|
114
|
+
function getProductTimeRangeIdentity(range) {
|
|
115
|
+
var bookingRange = getCandidateBookingRange(range);
|
|
116
|
+
return [String(range.productId), range.startAt, range.endAt, bookingRange.startAt, bookingRange.endAt].join(':');
|
|
117
|
+
}
|
|
118
|
+
function toIndexedRange(range) {
|
|
119
|
+
return _objectSpread(_objectSpread({}, range), {}, {
|
|
120
|
+
startMs: range.start,
|
|
121
|
+
endMs: range.end
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function normalizeNonNegativeNumber(value) {
|
|
125
|
+
var normalized = Number(value);
|
|
126
|
+
if (!Number.isFinite(normalized) || normalized < 0) return null;
|
|
127
|
+
return normalized;
|
|
128
|
+
}
|
|
129
|
+
function resolveCutOffPolicyIssue(policy) {
|
|
130
|
+
if (policy.type === 'invalid') {
|
|
131
|
+
return policy.reason || 'invalid_cutoff_policy';
|
|
71
132
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
133
|
+
if ('futureDay' in policy && policy.futureDay !== undefined && normalizeNonNegativeNumber(policy.futureDay) === null) {
|
|
134
|
+
return 'invalid_future_day';
|
|
135
|
+
}
|
|
136
|
+
if (policy.type === 'ongoing' && policy.mode === 'after_start' && normalizeNonNegativeNumber(policy.graceMinutes) === null) {
|
|
137
|
+
return 'invalid_grace_minutes';
|
|
138
|
+
}
|
|
139
|
+
if (policy.type === 'advance') {
|
|
140
|
+
if (normalizeNonNegativeNumber(policy.unit) === null) return 'invalid_advance_unit';
|
|
141
|
+
if (!['days', 'hours', 'minutes'].includes(policy.unitType)) return 'invalid_advance_unit_type';
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
function addLeadWindow(base, unit, unitType, _timezoneName) {
|
|
146
|
+
if (unitType === 'days') return addLocalCalendarDays(base, unit);
|
|
147
|
+
if (unitType === 'hours') return addLocalHours(base, unit);
|
|
148
|
+
return addLocalMinutes(base, unit);
|
|
149
|
+
}
|
|
150
|
+
function evaluateCandidateTimeGate(params) {
|
|
151
|
+
var product = params.product,
|
|
152
|
+
candidateRange = params.candidateRange,
|
|
153
|
+
evaluationContext = params.evaluationContext,
|
|
154
|
+
timezoneName = params.timezoneName;
|
|
155
|
+
var now = evaluationContext.evaluatedAt;
|
|
156
|
+
if (candidateRange.endMs <= evaluationContext.evaluatedAtMs) {
|
|
157
|
+
return {
|
|
158
|
+
status: 'past',
|
|
159
|
+
reasonCodes: ['past'],
|
|
160
|
+
conflictCode: 'past'
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
var policy = product.cutOffPolicy;
|
|
164
|
+
if (!policy) return null;
|
|
165
|
+
if (resolveCutOffPolicyIssue(policy)) {
|
|
166
|
+
return {
|
|
167
|
+
status: 'unavailable',
|
|
168
|
+
reasonCodes: ['booking_cutoff_invalid'],
|
|
169
|
+
conflictCode: 'booking_cutoff'
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if ('futureDay' in policy && policy.futureDay !== undefined) {
|
|
173
|
+
var futureDay = normalizeNonNegativeNumber(policy.futureDay);
|
|
174
|
+
var lastAllowedDateOrdinal = dateOrdinalFromLocalDate(formatDate(now)) + futureDay;
|
|
175
|
+
if (dateOrdinalFromLocalDate(formatDate(candidateRange.start)) > lastAllowedDateOrdinal) {
|
|
176
|
+
return {
|
|
177
|
+
status: 'unavailable',
|
|
178
|
+
reasonCodes: ['booking_cutoff_future_day'],
|
|
179
|
+
conflictCode: 'booking_cutoff'
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (policy.type === 'ongoing') {
|
|
184
|
+
if (policy.mode === 'before_end') return null;
|
|
185
|
+
var graceMinutes = normalizeNonNegativeNumber(policy.graceMinutes);
|
|
186
|
+
var graceDeadline = addMinutesZoned(candidateRange.start, graceMinutes, timezoneName);
|
|
187
|
+
if (now < graceDeadline) return null;
|
|
188
|
+
return {
|
|
189
|
+
status: 'unavailable',
|
|
190
|
+
reasonCodes: ['booking_cutoff_after_start'],
|
|
191
|
+
conflictCode: 'booking_cutoff'
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (policy.type === 'before_start') {
|
|
195
|
+
if (now < candidateRange.start) return null;
|
|
196
|
+
return {
|
|
197
|
+
status: 'unavailable',
|
|
198
|
+
reasonCodes: ['booking_cutoff_before_start'],
|
|
199
|
+
conflictCode: 'booking_cutoff'
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (policy.type === 'advance') {
|
|
203
|
+
var leadWindow = addLeadWindow(now, normalizeNonNegativeNumber(policy.unit), policy.unitType, timezoneName);
|
|
204
|
+
if (leadWindow <= candidateRange.start) return null;
|
|
205
|
+
return {
|
|
206
|
+
status: 'unavailable',
|
|
207
|
+
reasonCodes: ['booking_cutoff_advance'],
|
|
208
|
+
conflictCode: 'booking_cutoff'
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
status: 'unavailable',
|
|
213
|
+
reasonCodes: ['booking_cutoff_invalid'],
|
|
214
|
+
conflictCode: 'booking_cutoff'
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function overlaps(left, right) {
|
|
218
|
+
return left.start < right.end && left.end > right.start;
|
|
219
|
+
}
|
|
220
|
+
function contains(outer, inner) {
|
|
221
|
+
return inner.start >= outer.start && inner.end <= outer.end;
|
|
222
|
+
}
|
|
223
|
+
function clipRange(base, clip) {
|
|
224
|
+
var start = Math.max(base.start, clip.start);
|
|
225
|
+
var end = Math.min(base.end, clip.end);
|
|
226
|
+
if (end <= start) return null;
|
|
227
|
+
return {
|
|
228
|
+
start: start,
|
|
229
|
+
end: end
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function sortRanges(ranges) {
|
|
233
|
+
return _toConsumableArray(ranges).sort(function (left, right) {
|
|
234
|
+
return left.start - right.start;
|
|
81
235
|
});
|
|
82
236
|
}
|
|
83
|
-
function
|
|
84
|
-
var
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
237
|
+
function mergeRanges(ranges) {
|
|
238
|
+
var sorted = sortRanges(ranges);
|
|
239
|
+
var merged = [];
|
|
240
|
+
sorted.forEach(function (range) {
|
|
241
|
+
var last = merged[merged.length - 1];
|
|
242
|
+
if (!last) {
|
|
243
|
+
merged.push(range);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (range.start <= last.end) {
|
|
247
|
+
last.end = Math.max(range.end, last.end);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
merged.push(range);
|
|
90
251
|
});
|
|
252
|
+
return merged;
|
|
91
253
|
}
|
|
92
|
-
function
|
|
93
|
-
var
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
254
|
+
function intersectRangeLists(left, right) {
|
|
255
|
+
var results = [];
|
|
256
|
+
left.forEach(function (leftRange) {
|
|
257
|
+
right.forEach(function (rightRange) {
|
|
258
|
+
var clipped = clipRange(leftRange, rightRange);
|
|
259
|
+
if (clipped) results.push(clipped);
|
|
260
|
+
});
|
|
98
261
|
});
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
function
|
|
102
|
-
var
|
|
103
|
-
var
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return rawTitle.en || rawTitle.zh || rawTitle.default || Object.values(rawTitle)[0];
|
|
262
|
+
return mergeRanges(results);
|
|
263
|
+
}
|
|
264
|
+
function enumerateDates(startDate, endDate) {
|
|
265
|
+
var dates = [];
|
|
266
|
+
var cursor = startDate;
|
|
267
|
+
while (cursor <= endDate) {
|
|
268
|
+
dates.push(cursor);
|
|
269
|
+
cursor = nextDate(cursor);
|
|
108
270
|
}
|
|
109
|
-
return
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
var
|
|
113
|
-
var
|
|
114
|
-
var
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
var product = _step.value;
|
|
120
|
-
var quantity = Math.max(1, Number(product.quantity || 1));
|
|
121
|
-
var capacityRequired = Math.max(1, Number(query.pax || product.capacityRequired || 1));
|
|
122
|
-
var requirements = product.resourceRequirements || [];
|
|
123
|
-
if (product.kind === 'session' || product.kind === 'venue_slot') {
|
|
124
|
-
var schedules = product.schedule || [];
|
|
125
|
-
var _iterator2 = _createForOfIteratorHelper(schedules),
|
|
126
|
-
_step2;
|
|
127
|
-
try {
|
|
128
|
-
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
129
|
-
var schedule = _step2.value;
|
|
130
|
-
var scheduleDate = dateOf(schedule.start_at);
|
|
131
|
-
if (dates.length > 0 && !dates.includes(scheduleDate)) continue;
|
|
132
|
-
var start = dayjs(schedule.start_at);
|
|
133
|
-
var end = dayjs(schedule.end_at);
|
|
134
|
-
intervals.push({
|
|
135
|
-
id: "".concat(product.id, ":").concat(start.toISOString(), ":").concat(end.toISOString()),
|
|
136
|
-
productId: product.id,
|
|
137
|
-
source: product.kind,
|
|
138
|
-
date: dateOf(schedule.start_at),
|
|
139
|
-
start_at: start.toISOString(),
|
|
140
|
-
end_at: end.toISOString(),
|
|
141
|
-
durationMinutes: Math.max(1, end.diff(start, 'minute')),
|
|
142
|
-
quantity: quantity,
|
|
143
|
-
capacityRequired: capacityRequired,
|
|
144
|
-
resourceRequirements: requirements
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
} catch (err) {
|
|
148
|
-
_iterator2.e(err);
|
|
149
|
-
} finally {
|
|
150
|
-
_iterator2.f();
|
|
151
|
-
}
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (product.kind === 'duration') {
|
|
155
|
-
var duration = Math.max(1, Number(product.durationMinutes || 30));
|
|
156
|
-
var _iterator3 = _createForOfIteratorHelper(dates),
|
|
157
|
-
_step3;
|
|
158
|
-
try {
|
|
159
|
-
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
160
|
-
var currentDate = _step3.value;
|
|
161
|
-
var _start = query.startTime ? toDateTime(currentDate, query.startTime) : toDateTime(currentDate, (context === null || context === void 0 ? void 0 : context.businessStartTime) || DEFAULT_RESOURCE_PLANNER_CONTEXT.businessStartTime);
|
|
162
|
-
var _end = query.startTime && query.endTime ? toDateTime(currentDate, query.endTime) : query.startTime ? _start.add(duration, 'minute') : toDateTime(currentDate, (context === null || context === void 0 ? void 0 : context.businessEndTime) || DEFAULT_RESOURCE_PLANNER_CONTEXT.businessEndTime);
|
|
163
|
-
if (!_end.isAfter(_start)) _end = _end.add(1, 'day');
|
|
164
|
-
intervals.push({
|
|
165
|
-
id: "".concat(product.id, ":").concat(currentDate, ":").concat(query.startTime || 'range'),
|
|
166
|
-
productId: product.id,
|
|
167
|
-
source: 'duration',
|
|
168
|
-
date: currentDate,
|
|
169
|
-
start_at: _start.toISOString(),
|
|
170
|
-
end_at: _end.toISOString(),
|
|
171
|
-
durationMinutes: duration,
|
|
172
|
-
quantity: quantity,
|
|
173
|
-
capacityRequired: capacityRequired,
|
|
174
|
-
resourceRequirements: requirements
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
} catch (err) {
|
|
178
|
-
_iterator3.e(err);
|
|
179
|
-
} finally {
|
|
180
|
-
_iterator3.f();
|
|
181
|
-
}
|
|
182
|
-
}
|
|
271
|
+
return dates;
|
|
272
|
+
}
|
|
273
|
+
function normalizeCoverage(input) {
|
|
274
|
+
var startDate = input.coverage.startDate;
|
|
275
|
+
var endDate = input.coverage.endDate;
|
|
276
|
+
var isCanonicalDate = function isCanonicalDate(value) {
|
|
277
|
+
try {
|
|
278
|
+
return formatLocalDate(parseLocalDate(value)) === value;
|
|
279
|
+
} catch (_error) {
|
|
280
|
+
return false;
|
|
183
281
|
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
282
|
+
};
|
|
283
|
+
if (!startDate || !endDate || !isCanonicalDate(startDate) || !isCanonicalDate(endDate)) {
|
|
284
|
+
throw availabilityError('INVALID_DATE_RANGE', 'coverage.startDate / coverage.endDate 非法', {
|
|
285
|
+
coverage: input.coverage
|
|
286
|
+
});
|
|
188
287
|
}
|
|
189
|
-
|
|
288
|
+
if (endDate < startDate) {
|
|
289
|
+
throw availabilityError('INVALID_DATE_RANGE', 'coverage.endDate 不能早于 startDate', {
|
|
290
|
+
coverage: input.coverage
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
dateRange: {
|
|
295
|
+
startDate: startDate,
|
|
296
|
+
endDate: endDate
|
|
297
|
+
},
|
|
298
|
+
absolute: {
|
|
299
|
+
start: toDateStart(startDate, input.timezone),
|
|
300
|
+
end: toDateEnd(nextDate(endDate, input.timezone), input.timezone)
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function normalizeResourceWindow(resource, coverage, timezoneName) {
|
|
305
|
+
return mergeRanges((resource.windows || []).map(function (window) {
|
|
306
|
+
return normalizeAbsoluteRange(window, timezoneName);
|
|
307
|
+
}).filter(function (window) {
|
|
308
|
+
return Boolean(window);
|
|
309
|
+
}).map(function (window) {
|
|
310
|
+
return clipRange(window, coverage);
|
|
311
|
+
}).filter(function (window) {
|
|
312
|
+
return Boolean(window);
|
|
313
|
+
}));
|
|
314
|
+
}
|
|
315
|
+
function normalizeOccupancy(occupancy, coverage, timezoneName, fixedOffsetMinutes) {
|
|
316
|
+
var normalized = normalizeAbsoluteRange(occupancy, timezoneName);
|
|
317
|
+
if (!normalized) return null;
|
|
318
|
+
var clipped = clipRange(normalized, coverage);
|
|
319
|
+
if (!clipped) return null;
|
|
320
|
+
return _objectSpread(_objectSpread({}, occupancy), {}, {
|
|
321
|
+
startAt: formatDateTime(clipped.start, timezoneName, fixedOffsetMinutes),
|
|
322
|
+
endAt: formatDateTime(clipped.end, timezoneName, fixedOffsetMinutes),
|
|
323
|
+
pax: Math.max(1, Number(occupancy.pax || 1) || 1)
|
|
324
|
+
});
|
|
190
325
|
}
|
|
191
|
-
function
|
|
192
|
-
var
|
|
193
|
-
|
|
326
|
+
function normalizeRequirementGroup(product, requirementGroup) {
|
|
327
|
+
var _requirementGroup$for;
|
|
328
|
+
var resourceType = 'single';
|
|
329
|
+
var required = requirementGroup.required !== false;
|
|
330
|
+
var min = required ? 1 : 0;
|
|
331
|
+
return _objectSpread(_objectSpread({}, requirementGroup), {}, {
|
|
332
|
+
id: requirementGroup.id || "group:".concat(String(product.id), ":").concat(String((_requirementGroup$for = requirementGroup.formId) !== null && _requirementGroup$for !== void 0 ? _requirementGroup$for : resourceType)),
|
|
333
|
+
resourceType: resourceType,
|
|
334
|
+
required: required,
|
|
335
|
+
min: min,
|
|
336
|
+
max: 1,
|
|
337
|
+
resourceIds: uniqById(requirementGroup.resourceIds || []),
|
|
338
|
+
capacityRequired: Math.max(1, Number(requirementGroup.capacityRequired || product.capacityRequired || 1) || 1)
|
|
194
339
|
});
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
340
|
+
}
|
|
341
|
+
function normalizeProducts(products) {
|
|
342
|
+
return products.map(function (product) {
|
|
343
|
+
return _objectSpread(_objectSpread({}, product), {}, {
|
|
344
|
+
quantity: Math.max(1, Number(product.quantity || 1) || 1),
|
|
345
|
+
capacityRequired: Math.max(1, Number(product.capacityRequired || 1) || 1),
|
|
346
|
+
durationMinutes: Math.max(1, Number(product.durationMinutes || 30) || 30),
|
|
347
|
+
cutOffPolicy: product.cutOffPolicy ? _objectSpread({}, product.cutOffPolicy) : undefined,
|
|
348
|
+
requirementGroups: (product.requirementGroups || []).map(function (group) {
|
|
349
|
+
return normalizeRequirementGroup(product, group);
|
|
350
|
+
}),
|
|
351
|
+
sessionRules: (product.sessionRules || []).map(function (rule) {
|
|
352
|
+
return _objectSpread(_objectSpread({}, rule), {}, {
|
|
353
|
+
weekdays: rule.weekdays ? _toConsumableArray(rule.weekdays) : undefined,
|
|
354
|
+
dates: rule.dates ? _toConsumableArray(rule.dates) : undefined,
|
|
355
|
+
includeDates: rule.includeDates ? _toConsumableArray(rule.includeDates) : undefined,
|
|
356
|
+
excludeDates: rule.excludeDates ? _toConsumableArray(rule.excludeDates) : undefined,
|
|
357
|
+
scheduleRef: rule.scheduleRef ? _objectSpread({}, rule.scheduleRef) : undefined
|
|
358
|
+
});
|
|
359
|
+
}),
|
|
360
|
+
sessionRanges: (product.sessionRanges || []).map(function (range) {
|
|
361
|
+
var _range$scheduleRefs;
|
|
362
|
+
return _objectSpread(_objectSpread({}, range), {}, {
|
|
363
|
+
scheduleRefs: (_range$scheduleRefs = range.scheduleRefs) === null || _range$scheduleRefs === void 0 ? void 0 : _range$scheduleRefs.map(function (scheduleRef) {
|
|
364
|
+
return _objectSpread({}, scheduleRef);
|
|
365
|
+
})
|
|
366
|
+
});
|
|
367
|
+
})
|
|
368
|
+
});
|
|
204
369
|
});
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
function
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
370
|
+
}
|
|
371
|
+
function normalizeResources(resources) {
|
|
372
|
+
return resources.map(function (resource) {
|
|
373
|
+
return _objectSpread(_objectSpread({}, resource), {}, {
|
|
374
|
+
resourceType: 'single',
|
|
375
|
+
capacity: Math.max(1, Number(resource.capacity || 1) || 1),
|
|
376
|
+
windows: (resource.windows || []).map(function (window) {
|
|
377
|
+
return _objectSpread({}, window);
|
|
378
|
+
})
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
function normalizeOperatingHours(params) {
|
|
383
|
+
var _params$input, _params$input2, _params$input3;
|
|
384
|
+
var fallbackWindows = enumerateDates(params.dateRange.startDate, params.dateRange.endDate).map(function (date) {
|
|
385
|
+
var start = toDateTimeInDate(date, params.businessHours.startTime, params.timezoneName);
|
|
386
|
+
var end = toDateTimeInDate(date, params.businessHours.endTime, params.timezoneName);
|
|
387
|
+
if (end <= start) {
|
|
388
|
+
end = toDateTimeInDate(nextDate(date), params.businessHours.endTime, params.timezoneName);
|
|
389
|
+
}
|
|
213
390
|
return {
|
|
214
391
|
start: start,
|
|
215
392
|
end: end
|
|
216
393
|
};
|
|
217
|
-
}).filter(function (segment) {
|
|
218
|
-
return segment.end.isAfter(segment.start);
|
|
219
394
|
});
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
return left.start.valueOf() - right.start.valueOf();
|
|
395
|
+
var sourceWindows = params.input ? params.input.windows : fallbackWindows.map(function (range) {
|
|
396
|
+
return {
|
|
397
|
+
startAt: formatDateTime(range.start, params.timezoneName, params.fixedOffsetMinutes),
|
|
398
|
+
endAt: formatDateTime(range.end, params.timezoneName, params.fixedOffsetMinutes)
|
|
399
|
+
};
|
|
226
400
|
});
|
|
227
|
-
var
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
401
|
+
var windows = mergeRanges(sourceWindows.map(function (window) {
|
|
402
|
+
return normalizeAbsoluteRange(window, params.timezoneName);
|
|
403
|
+
}).filter(function (window) {
|
|
404
|
+
return Boolean(window);
|
|
405
|
+
}).map(function (window) {
|
|
406
|
+
return clipRange(window, params.coverage);
|
|
407
|
+
}).filter(function (window) {
|
|
408
|
+
return Boolean(window);
|
|
409
|
+
})).map(function (range) {
|
|
410
|
+
return {
|
|
411
|
+
startAt: formatDateTime(range.start, params.timezoneName, params.fixedOffsetMinutes),
|
|
412
|
+
endAt: formatDateTime(range.end, params.timezoneName, params.fixedOffsetMinutes)
|
|
413
|
+
};
|
|
235
414
|
});
|
|
236
|
-
return
|
|
415
|
+
return {
|
|
416
|
+
source: ((_params$input = params.input) === null || _params$input === void 0 ? void 0 : _params$input.source) || params.fallbackSource,
|
|
417
|
+
scheduleIds: uniqById(((_params$input2 = params.input) === null || _params$input2 === void 0 ? void 0 : _params$input2.scheduleIds) || []),
|
|
418
|
+
missingScheduleIds: uniqById(((_params$input3 = params.input) === null || _params$input3 === void 0 ? void 0 : _params$input3.missingScheduleIds) || []),
|
|
419
|
+
windows: windows
|
|
420
|
+
};
|
|
237
421
|
}
|
|
238
|
-
function
|
|
239
|
-
var
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
422
|
+
function matchesRequirementGroup(requirementGroup, resource, product) {
|
|
423
|
+
var _requirementGroup$res;
|
|
424
|
+
var partySize = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
|
|
425
|
+
if (requirementGroup.formId !== undefined && resource.formId !== undefined && !sameId(requirementGroup.formId, resource.formId)) {
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
if ((_requirementGroup$res = requirementGroup.resourceIds) !== null && _requirementGroup$res !== void 0 && _requirementGroup$res.length && !requirementGroup.resourceIds.some(function (resourceId) {
|
|
429
|
+
return sameId(resourceId, resource.id);
|
|
430
|
+
})) {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
return resource.capacity >= resolveRequiredCapacity(requirementGroup, product, partySize);
|
|
434
|
+
}
|
|
435
|
+
function resolveRequiredCapacity(requirementGroup, product, partySize) {
|
|
436
|
+
var _ref, _ref2;
|
|
437
|
+
return Math.max(1, Number((_ref = (_ref2 = partySize !== null && partySize !== void 0 ? partySize : requirementGroup.capacityRequired) !== null && _ref2 !== void 0 ? _ref2 : product.capacityRequired) !== null && _ref !== void 0 ? _ref : 1) || 1);
|
|
438
|
+
}
|
|
439
|
+
function buildCellKey(params) {
|
|
440
|
+
return [String(params.productId), params.groupId, String(params.resourceId), params.start, params.end].join(':');
|
|
441
|
+
}
|
|
442
|
+
function dedupeOccupancyRefKey(occupancy) {
|
|
443
|
+
if (occupancy.scheduleEventId !== undefined && occupancy.scheduleEventId !== null) {
|
|
444
|
+
return "scheduleEvent:".concat(String(occupancy.resourceId), ":").concat(String(occupancy.scheduleEventId));
|
|
445
|
+
}
|
|
446
|
+
if (occupancy.bookingUid !== undefined && occupancy.bookingUid !== null) {
|
|
447
|
+
return "bookingUid:".concat(String(occupancy.resourceId), ":").concat(String(occupancy.bookingUid));
|
|
448
|
+
}
|
|
449
|
+
if (occupancy.productUid !== undefined && occupancy.productUid !== null) {
|
|
450
|
+
return "productUid:".concat(String(occupancy.resourceId), ":").concat(String(occupancy.productUid));
|
|
451
|
+
}
|
|
452
|
+
return "range:".concat(String(occupancy.resourceId), ":").concat(occupancy.startAt, ":").concat(occupancy.endAt, ":").concat(String(occupancy.pax || 1));
|
|
453
|
+
}
|
|
454
|
+
function buildCells(projection) {
|
|
455
|
+
var _extractFixedOffsetMi;
|
|
456
|
+
var coverage = {
|
|
457
|
+
start: toDateTime(projection.meta.coverage.startAt, projection.meta.timezone),
|
|
458
|
+
end: toDateTime(projection.meta.coverage.endAt, projection.meta.timezone)
|
|
459
|
+
};
|
|
460
|
+
var cells = [];
|
|
461
|
+
var timezoneName = projection.meta.timezone;
|
|
462
|
+
var fixedOffsetMinutes = (_extractFixedOffsetMi = extractFixedOffsetMinutes(projection.meta.now)) !== null && _extractFixedOffsetMi !== void 0 ? _extractFixedOffsetMi : captureRuntimeOffsetMinutes();
|
|
463
|
+
var projectionNow = toDateTime(projection.meta.now);
|
|
464
|
+
projection.products.forEach(function (product) {
|
|
465
|
+
(product.requirementGroups || []).forEach(function (requirementGroup) {
|
|
466
|
+
projection.resources.filter(function (resource) {
|
|
467
|
+
return matchesRequirementGroup(requirementGroup, resource, product);
|
|
468
|
+
}).forEach(function (resource) {
|
|
469
|
+
var windows = normalizeResourceWindow(resource, coverage, timezoneName);
|
|
470
|
+
var occupancies = projection.resourceOccupancies.filter(function (occupancy) {
|
|
471
|
+
return sameId(occupancy.resourceId, resource.id);
|
|
472
|
+
}).map(function (occupancy) {
|
|
473
|
+
return {
|
|
474
|
+
occupancy: occupancy,
|
|
475
|
+
range: normalizeAbsoluteRange(occupancy, timezoneName)
|
|
476
|
+
};
|
|
477
|
+
}).filter(function (item) {
|
|
478
|
+
return Boolean(item.range);
|
|
479
|
+
}).map(function (item) {
|
|
480
|
+
return _objectSpread(_objectSpread({}, item), {}, {
|
|
481
|
+
range: clipRange(item.range, coverage)
|
|
482
|
+
});
|
|
483
|
+
}).filter(function (item) {
|
|
484
|
+
return Boolean(item.range);
|
|
255
485
|
});
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
workingSegments.forEach(function (working) {
|
|
275
|
-
var boundaries = new Map([[working.start.valueOf(), working.start], [working.end.valueOf(), working.end]]);
|
|
276
|
-
events.forEach(function (event) {
|
|
277
|
-
var eventStart = dayjs(event.start_at);
|
|
278
|
-
var eventEnd = dayjs(event.end_at);
|
|
279
|
-
if (!overlaps(working.start, working.end, eventStart, eventEnd)) return;
|
|
280
|
-
var clippedStart = maxDayjs(eventStart, working.start);
|
|
281
|
-
var clippedEnd = minDayjs(eventEnd, working.end);
|
|
282
|
-
boundaries.set(clippedStart.valueOf(), clippedStart);
|
|
283
|
-
boundaries.set(clippedEnd.valueOf(), clippedEnd);
|
|
284
|
-
});
|
|
285
|
-
var points = Array.from(boundaries.values()).sort(function (left, right) {
|
|
286
|
-
return left.valueOf() - right.valueOf();
|
|
287
|
-
});
|
|
288
|
-
var _loop = function _loop() {
|
|
289
|
-
var start = points[index];
|
|
290
|
-
var end = points[index + 1];
|
|
291
|
-
if (!end.isAfter(start)) return 0; // continue
|
|
292
|
-
var usedCapacity = events.filter(function (event) {
|
|
293
|
-
return overlaps(start, end, dayjs(event.start_at), dayjs(event.end_at));
|
|
294
|
-
}).reduce(function (sum, event) {
|
|
295
|
-
return sum + Math.max(1, Number(event.pax || 1));
|
|
296
|
-
}, 0);
|
|
297
|
-
var remainingCapacity = Math.max(0, maxCapacity - usedCapacity);
|
|
298
|
-
if (remainingCapacity < requiredCapacity) return 0; // continue
|
|
299
|
-
var status = remainingCapacity < maxCapacity ? 'limited' : 'available';
|
|
300
|
-
var previous = ranges[ranges.length - 1];
|
|
301
|
-
if (previous && previous.end.isSame(start) && previous.remainingCapacity === remainingCapacity && previous.maxCapacity === maxCapacity && previous.status === status) {
|
|
302
|
-
previous.end = end;
|
|
303
|
-
} else {
|
|
304
|
-
ranges.push({
|
|
486
|
+
var boundaries = new Set([coverage.start, coverage.end]);
|
|
487
|
+
windows.forEach(function (window) {
|
|
488
|
+
boundaries.add(window.start);
|
|
489
|
+
boundaries.add(window.end);
|
|
490
|
+
});
|
|
491
|
+
occupancies.forEach(function (_ref3) {
|
|
492
|
+
var range = _ref3.range;
|
|
493
|
+
boundaries.add(range.start);
|
|
494
|
+
boundaries.add(range.end);
|
|
495
|
+
});
|
|
496
|
+
var sortedBoundaries = Array.from(boundaries.values()).sort(function (left, right) {
|
|
497
|
+
return left - right;
|
|
498
|
+
});
|
|
499
|
+
var _loop = function _loop() {
|
|
500
|
+
var start = sortedBoundaries[index];
|
|
501
|
+
var end = sortedBoundaries[index + 1];
|
|
502
|
+
if (end <= start) return 1; // continue
|
|
503
|
+
var segment = {
|
|
305
504
|
start: start,
|
|
306
|
-
end: end
|
|
505
|
+
end: end
|
|
506
|
+
};
|
|
507
|
+
var isInsideWorkingWindow = windows.some(function (window) {
|
|
508
|
+
return contains(window, segment);
|
|
509
|
+
});
|
|
510
|
+
var segmentOccupancies = isInsideWorkingWindow ? occupancies.filter(function (_ref4) {
|
|
511
|
+
var range = _ref4.range;
|
|
512
|
+
return overlaps(range, segment);
|
|
513
|
+
}).map(function (_ref5) {
|
|
514
|
+
var occupancy = _ref5.occupancy;
|
|
515
|
+
return occupancy;
|
|
516
|
+
}) : [];
|
|
517
|
+
var usedCapacity = segmentOccupancies.reduce(function (sum, occupancy) {
|
|
518
|
+
return sum + Math.max(1, Number(occupancy.pax || 1) || 1);
|
|
519
|
+
}, 0);
|
|
520
|
+
var remainingCapacity = Math.max(0, resource.capacity - usedCapacity);
|
|
521
|
+
var requiredCapacity = resolveRequiredCapacity(requirementGroup, product);
|
|
522
|
+
var reasonCodes = Array.from(new Set(segmentOccupancies.flatMap(function (occupancy) {
|
|
523
|
+
var source = occupancy.source || 'remote';
|
|
524
|
+
if (source === 'selection') return ['selection_occupied'];
|
|
525
|
+
if (source === 'cart') return ['cart_occupied'];
|
|
526
|
+
return ['resource_occupied'];
|
|
527
|
+
})));
|
|
528
|
+
var status = 'available';
|
|
529
|
+
if (end <= projectionNow) {
|
|
530
|
+
status = 'past';
|
|
531
|
+
reasonCodes.push('past');
|
|
532
|
+
} else if (!isInsideWorkingWindow) {
|
|
533
|
+
status = 'unavailable';
|
|
534
|
+
reasonCodes.push('outside_resource_window');
|
|
535
|
+
} else if (remainingCapacity < requiredCapacity) {
|
|
536
|
+
status = 'full';
|
|
537
|
+
reasonCodes.push('capacity_full');
|
|
538
|
+
} else if (remainingCapacity < resource.capacity) {
|
|
539
|
+
status = 'limited';
|
|
540
|
+
}
|
|
541
|
+
cells.push({
|
|
542
|
+
key: buildCellKey({
|
|
543
|
+
productId: product.id,
|
|
544
|
+
groupId: requirementGroup.id,
|
|
545
|
+
resourceId: resource.id,
|
|
546
|
+
start: start,
|
|
547
|
+
end: end
|
|
548
|
+
}),
|
|
549
|
+
productId: product.id,
|
|
550
|
+
requirementGroupId: requirementGroup.id,
|
|
551
|
+
requirementFormId: requirementGroup.formId,
|
|
552
|
+
resourceId: resource.id,
|
|
553
|
+
resourceName: resource.name,
|
|
554
|
+
resourceType: resource.resourceType,
|
|
555
|
+
startAt: formatDateTime(start, timezoneName, fixedOffsetMinutes),
|
|
556
|
+
endAt: formatDateTime(end, timezoneName, fixedOffsetMinutes),
|
|
557
|
+
date: formatDate(start, timezoneName),
|
|
558
|
+
startTime: formatTime(start, timezoneName),
|
|
559
|
+
endTime: formatTime(end, timezoneName),
|
|
560
|
+
timezone: projection.meta.timezone,
|
|
561
|
+
status: status,
|
|
562
|
+
// 资源组当前统一单选;具体资源仍用 capacity 表达可承载的预约数量。
|
|
307
563
|
remainingCapacity: remainingCapacity,
|
|
308
|
-
maxCapacity:
|
|
309
|
-
|
|
564
|
+
maxCapacity: resource.capacity,
|
|
565
|
+
reasonCodes: Array.from(new Set(reasonCodes)),
|
|
566
|
+
occupancyRefs: segmentOccupancies.map(function (occupancy) {
|
|
567
|
+
return {
|
|
568
|
+
id: occupancy.id,
|
|
569
|
+
resourceId: occupancy.resourceId,
|
|
570
|
+
source: occupancy.source,
|
|
571
|
+
serverEventId: occupancy.serverEventId,
|
|
572
|
+
bookingUid: occupancy.bookingUid,
|
|
573
|
+
productUid: occupancy.productUid,
|
|
574
|
+
scheduleEventId: occupancy.scheduleEventId
|
|
575
|
+
};
|
|
576
|
+
})
|
|
310
577
|
});
|
|
578
|
+
};
|
|
579
|
+
for (var index = 0; index < sortedBoundaries.length - 1; index += 1) {
|
|
580
|
+
if (_loop()) continue;
|
|
311
581
|
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
return compressCells(cells);
|
|
586
|
+
}
|
|
587
|
+
function compressCells(cells) {
|
|
588
|
+
var sorted = _toConsumableArray(cells).sort(function (left, right) {
|
|
589
|
+
var keyLeft = [String(left.productId), String(left.requirementGroupId), String(left.resourceId), left.startAt].join(':');
|
|
590
|
+
var keyRight = [String(right.productId), String(right.requirementGroupId), String(right.resourceId), right.startAt].join(':');
|
|
591
|
+
return keyLeft.localeCompare(keyRight);
|
|
318
592
|
});
|
|
319
|
-
return
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
var
|
|
593
|
+
return sorted.reduce(function (result, cell) {
|
|
594
|
+
var last = result[result.length - 1];
|
|
595
|
+
if (last && sameId(last.productId, cell.productId) && sameId(last.requirementGroupId, cell.requirementGroupId) && sameId(last.resourceId, cell.resourceId) && last.status === cell.status && last.remainingCapacity === cell.remainingCapacity && last.maxCapacity === cell.maxCapacity && JSON.stringify(last.reasonCodes) === JSON.stringify(cell.reasonCodes) && JSON.stringify(last.occupancyRefs) === JSON.stringify(cell.occupancyRefs) && last.endAt === cell.startAt) {
|
|
596
|
+
last.endAt = cell.endAt;
|
|
597
|
+
last.endTime = cell.endTime;
|
|
598
|
+
return result;
|
|
599
|
+
}
|
|
600
|
+
result.push(_objectSpread({}, cell));
|
|
601
|
+
return result;
|
|
602
|
+
}, []);
|
|
603
|
+
}
|
|
604
|
+
function makeCoverageAbsolute(dateRange, timezoneName, fixedOffsetMinutes) {
|
|
605
|
+
var guardEndDate = nextDate(dateRange.endDate, timezoneName);
|
|
332
606
|
return {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
resourceId: resource.id,
|
|
336
|
-
date: interval.date,
|
|
337
|
-
start_at: segment.start.toISOString(),
|
|
338
|
-
end_at: segment.end.toISOString(),
|
|
339
|
-
startTime: segment.start.format('HH:mm'),
|
|
340
|
-
endTime: segment.end.format('HH:mm'),
|
|
341
|
-
durationMinutes: interval.durationMinutes,
|
|
342
|
-
cellType: 'availability_range',
|
|
343
|
-
status: status,
|
|
344
|
-
remainingCapacity: remainingCapacity,
|
|
345
|
-
maxCapacity: maxCapacity,
|
|
346
|
-
reasonCodes: [],
|
|
347
|
-
conflicts: []
|
|
607
|
+
startAt: formatDateTime(toDateStart(dateRange.startDate, timezoneName), timezoneName, fixedOffsetMinutes),
|
|
608
|
+
endAt: formatDateTime(toDateEnd(guardEndDate, timezoneName), timezoneName, fixedOffsetMinutes)
|
|
348
609
|
};
|
|
349
610
|
}
|
|
350
|
-
function
|
|
351
|
-
var
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
var now = dayjs();
|
|
359
|
-
workingSegments = workingSegments.map(function (segment) {
|
|
360
|
-
return {
|
|
361
|
-
start: segment.end.isAfter(now) ? maxDayjs(segment.start, now) : segment.start,
|
|
362
|
-
end: segment.end
|
|
363
|
-
};
|
|
364
|
-
}).filter(function (segment) {
|
|
365
|
-
return segment.end.isAfter(segment.start);
|
|
366
|
-
});
|
|
367
|
-
var availableSegments = resource.resourceType === 'single' ? subtractBusyIntervals(workingSegments, events).map(function (segment) {
|
|
368
|
-
return _objectSpread(_objectSpread({}, segment), {}, {
|
|
369
|
-
remainingCapacity: 1,
|
|
370
|
-
maxCapacity: 1,
|
|
371
|
-
status: 'available'
|
|
611
|
+
function normalizeProjectionInstant(value, fallbackScalar, timezoneName, fixedOffsetMinutes) {
|
|
612
|
+
var scalar = value ? toDateTime(value) : fallbackScalar;
|
|
613
|
+
return formatDateTime(Number.isFinite(scalar) ? scalar : fallbackScalar, timezoneName, fixedOffsetMinutes);
|
|
614
|
+
}
|
|
615
|
+
function cloneSnapshotMetadataValue(value) {
|
|
616
|
+
if (Array.isArray(value)) {
|
|
617
|
+
return value.map(function (item) {
|
|
618
|
+
return cloneSnapshotMetadataValue(item);
|
|
372
619
|
});
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
620
|
+
}
|
|
621
|
+
if (!value || _typeof(value) !== 'object') return value;
|
|
622
|
+
return Object.keys(value).reduce(function (result, key) {
|
|
623
|
+
result[key] = cloneSnapshotMetadataValue(value[key]);
|
|
624
|
+
return result;
|
|
625
|
+
}, {});
|
|
626
|
+
}
|
|
627
|
+
function deepFreeze(value) {
|
|
628
|
+
if (!value || _typeof(value) !== 'object' || Object.isFrozen(value)) return value;
|
|
629
|
+
Object.freeze(value);
|
|
630
|
+
Object.getOwnPropertyNames(value).forEach(function (key) {
|
|
631
|
+
// `raw` is an explicit escape hatch to the backend/catalog record. It is not owned by the
|
|
632
|
+
// projection and must not freeze mutable ProductList / Order data shared elsewhere.
|
|
633
|
+
if (key === 'raw') return;
|
|
634
|
+
var child = value[key];
|
|
635
|
+
if (child && (_typeof(child) === 'object' || typeof child === 'function')) {
|
|
636
|
+
deepFreeze(child);
|
|
637
|
+
}
|
|
378
638
|
});
|
|
379
|
-
return
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
639
|
+
return value;
|
|
640
|
+
}
|
|
641
|
+
export function createAvailabilityProjection(input) {
|
|
642
|
+
var _input$businessHours, _input$businessHours2;
|
|
643
|
+
var coverage = normalizeCoverage(input);
|
|
644
|
+
var fixedOffsetMinutes = captureRuntimeOffsetMinutes();
|
|
645
|
+
var runtimeNow = localDateToScalar(new Date());
|
|
646
|
+
var products = normalizeProducts(input.products || []);
|
|
647
|
+
var resources = normalizeResources(input.resources || []);
|
|
648
|
+
var resourceOccupancies = uniqByOccupancy((input.resourceOccupancies || []).map(function (occupancy) {
|
|
649
|
+
return normalizeOccupancy(occupancy, coverage.absolute, input.timezone, fixedOffsetMinutes);
|
|
650
|
+
}).filter(function (occupancy) {
|
|
651
|
+
return Boolean(occupancy);
|
|
652
|
+
}));
|
|
653
|
+
var preparedAt = normalizeProjectionInstant(input.preparedAt, runtimeNow, input.timezone, fixedOffsetMinutes);
|
|
654
|
+
var now = normalizeProjectionInstant(input.now, runtimeNow, input.timezone, fixedOffsetMinutes);
|
|
655
|
+
var durationCandidateAnchorAt = normalizeProjectionInstant(input.durationCandidateAnchorAt || now, runtimeNow, input.timezone, fixedOffsetMinutes);
|
|
656
|
+
var businessHours = {
|
|
657
|
+
startTime: ((_input$businessHours = input.businessHours) === null || _input$businessHours === void 0 ? void 0 : _input$businessHours.startTime) || DEFAULT_BUSINESS_HOURS.startTime,
|
|
658
|
+
endTime: ((_input$businessHours2 = input.businessHours) === null || _input$businessHours2 === void 0 ? void 0 : _input$businessHours2.endTime) || DEFAULT_BUSINESS_HOURS.endTime
|
|
659
|
+
};
|
|
660
|
+
var operatingHours = normalizeOperatingHours({
|
|
661
|
+
input: input.operatingHours,
|
|
662
|
+
businessHours: businessHours,
|
|
663
|
+
coverage: coverage.absolute,
|
|
664
|
+
dateRange: coverage.dateRange,
|
|
665
|
+
timezoneName: input.timezone,
|
|
666
|
+
fixedOffsetMinutes: fixedOffsetMinutes,
|
|
667
|
+
fallbackSource: input.businessHours ? 'prepare_business_hours' : 'default_business_hours'
|
|
387
668
|
});
|
|
669
|
+
var meta = {
|
|
670
|
+
snapshotId: input.snapshotId || "availability-".concat(Date.now(), "-").concat(Math.random().toString(36).slice(2, 8)),
|
|
671
|
+
preparedAt: preparedAt,
|
|
672
|
+
timezone: input.timezone,
|
|
673
|
+
orderRevision: input.orderRevision,
|
|
674
|
+
mode: input.mode || 'appointment',
|
|
675
|
+
coverage: _objectSpread(_objectSpread({}, coverage.dateRange), makeCoverageAbsolute(coverage.dateRange, input.timezone, fixedOffsetMinutes)),
|
|
676
|
+
defaults: {
|
|
677
|
+
slotStepMinutes: Math.max(1, Number(input.slotStepMinutes || DEFAULT_SLOT_STEP_MINUTES) || DEFAULT_SLOT_STEP_MINUTES),
|
|
678
|
+
businessHours: _objectSpread({}, businessHours),
|
|
679
|
+
operatingHours: operatingHours
|
|
680
|
+
},
|
|
681
|
+
now: now,
|
|
682
|
+
durationCandidateAnchorAt: durationCandidateAnchorAt,
|
|
683
|
+
anchors: input.anchors ? cloneSnapshotMetadataValue(input.anchors) : undefined,
|
|
684
|
+
prepareParams: input.prepareParams ? cloneSnapshotMetadataValue(input.prepareParams) : undefined
|
|
685
|
+
};
|
|
686
|
+
var projectionBase = {
|
|
687
|
+
meta: meta,
|
|
688
|
+
products: products,
|
|
689
|
+
resources: resources,
|
|
690
|
+
resourceOccupancies: resourceOccupancies
|
|
691
|
+
};
|
|
692
|
+
return deepFreeze(_objectSpread(_objectSpread({}, projectionBase), {}, {
|
|
693
|
+
cells: buildCells(projectionBase)
|
|
694
|
+
}));
|
|
388
695
|
}
|
|
389
|
-
|
|
390
|
-
var
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
var
|
|
398
|
-
var
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
696
|
+
function uniqByOccupancy(occupancies) {
|
|
697
|
+
var map = new Map();
|
|
698
|
+
occupancies.forEach(function (occupancy) {
|
|
699
|
+
map.set(dedupeOccupancyRefKey(occupancy), occupancy);
|
|
700
|
+
});
|
|
701
|
+
return Array.from(map.values());
|
|
702
|
+
}
|
|
703
|
+
function resolveProjectionDateRange(projection, requestDateRange) {
|
|
704
|
+
var startDate = (requestDateRange === null || requestDateRange === void 0 ? void 0 : requestDateRange.startDate) || projection.meta.coverage.startDate;
|
|
705
|
+
var endDate = (requestDateRange === null || requestDateRange === void 0 ? void 0 : requestDateRange.endDate) || projection.meta.coverage.endDate;
|
|
706
|
+
var isCanonicalDate = function isCanonicalDate(value) {
|
|
707
|
+
try {
|
|
708
|
+
return formatLocalDate(parseLocalDate(value)) === value;
|
|
709
|
+
} catch (_error) {
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
if (!isCanonicalDate(startDate) || !isCanonicalDate(endDate) || endDate < startDate) {
|
|
714
|
+
throw availabilityError('INVALID_DATE_RANGE', '查询日期范围必须是有效的 YYYY-MM-DD,且结束日期不能早于开始日期', {
|
|
715
|
+
requested: requestDateRange
|
|
406
716
|
});
|
|
407
717
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
718
|
+
if (startDate < projection.meta.coverage.startDate || endDate > projection.meta.coverage.endDate) {
|
|
719
|
+
throw availabilityError('OUT_OF_PROJECTION_COVERAGE', '查询范围超出 projection.coverage', {
|
|
720
|
+
requested: requestDateRange,
|
|
721
|
+
coverage: projection.meta.coverage
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
return {
|
|
725
|
+
startDate: startDate,
|
|
726
|
+
endDate: endDate
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
function buildOperatingRanges(projection, dateRange) {
|
|
730
|
+
var coverage = {
|
|
731
|
+
start: toDateStart(dateRange.startDate, projection.meta.timezone),
|
|
732
|
+
end: toDateEnd(nextDate(dateRange.endDate), projection.meta.timezone)
|
|
733
|
+
};
|
|
734
|
+
return mergeRanges(projection.meta.defaults.operatingHours.windows.map(function (window) {
|
|
735
|
+
return normalizeAbsoluteRange(window, projection.meta.timezone);
|
|
736
|
+
}).filter(function (window) {
|
|
737
|
+
return Boolean(window);
|
|
738
|
+
}).map(function (window) {
|
|
739
|
+
return clipRange(window, coverage);
|
|
740
|
+
}).filter(function (window) {
|
|
741
|
+
return Boolean(window);
|
|
742
|
+
}));
|
|
743
|
+
}
|
|
744
|
+
function buildResourceWindowIndex(projection) {
|
|
745
|
+
var coverage = {
|
|
746
|
+
start: toDateTime(projection.meta.coverage.startAt, projection.meta.timezone),
|
|
747
|
+
end: toDateTime(projection.meta.coverage.endAt, projection.meta.timezone)
|
|
748
|
+
};
|
|
749
|
+
return new Map(projection.resources.map(function (resource) {
|
|
750
|
+
return [String(resource.id), normalizeResourceWindow(resource, coverage, projection.meta.timezone)];
|
|
751
|
+
}));
|
|
752
|
+
}
|
|
753
|
+
function buildGroupWorkingRanges(params) {
|
|
754
|
+
var _requirementGroup$min;
|
|
755
|
+
var projection = params.projection,
|
|
756
|
+
requirementGroup = params.requirementGroup,
|
|
757
|
+
dateRange = params.dateRange,
|
|
758
|
+
resourceIds = params.resourceIds,
|
|
759
|
+
partySize = params.partySize;
|
|
760
|
+
var guardEndDate = nextDate(dateRange.endDate, projection.meta.timezone);
|
|
761
|
+
var coverage = {
|
|
762
|
+
start: toDateStart(dateRange.startDate, projection.meta.timezone),
|
|
763
|
+
end: toDateEnd(guardEndDate, projection.meta.timezone)
|
|
764
|
+
};
|
|
765
|
+
var filteredResourceIdSet = resourceIds !== null && resourceIds !== void 0 && resourceIds.length ? new Set(resourceIds.map(String)) : null;
|
|
766
|
+
var resourceWindows = projection.resources.filter(function (resource) {
|
|
767
|
+
return matchesRequirementGroup(requirementGroup, resource, params.product, partySize);
|
|
768
|
+
}).filter(function (resource) {
|
|
769
|
+
return !filteredResourceIdSet || filteredResourceIdSet.has(String(resource.id));
|
|
770
|
+
}).map(function (resource) {
|
|
771
|
+
var _params$resourceWindo;
|
|
772
|
+
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);
|
|
773
|
+
return windows.map(function (window) {
|
|
774
|
+
return clipRange(window, coverage);
|
|
775
|
+
}).filter(function (window) {
|
|
776
|
+
return Boolean(window);
|
|
418
777
|
});
|
|
778
|
+
});
|
|
779
|
+
var requiredConcurrentResources = Math.max(requirementGroup.required === false ? 0 : 1, Number((_requirementGroup$min = requirementGroup.min) !== null && _requirementGroup$min !== void 0 ? _requirementGroup$min : requirementGroup.required === false ? 0 : 1) || 0);
|
|
780
|
+
if (requiredConcurrentResources <= 1) {
|
|
781
|
+
return mergeRanges(resourceWindows.flatMap(function (windows) {
|
|
782
|
+
return windows;
|
|
783
|
+
}));
|
|
419
784
|
}
|
|
420
|
-
var
|
|
421
|
-
|
|
422
|
-
|
|
785
|
+
var boundaries = new Set();
|
|
786
|
+
resourceWindows.forEach(function (windows) {
|
|
787
|
+
windows.forEach(function (window) {
|
|
788
|
+
boundaries.add(window.start);
|
|
789
|
+
boundaries.add(window.end);
|
|
790
|
+
});
|
|
423
791
|
});
|
|
424
|
-
var
|
|
425
|
-
return
|
|
426
|
-
}
|
|
427
|
-
var
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
792
|
+
var sortedBoundaries = Array.from(boundaries.values()).sort(function (left, right) {
|
|
793
|
+
return left - right;
|
|
794
|
+
});
|
|
795
|
+
var satisfiedRanges = [];
|
|
796
|
+
var _loop2 = function _loop2() {
|
|
797
|
+
var start = sortedBoundaries[index];
|
|
798
|
+
var end = sortedBoundaries[index + 1];
|
|
799
|
+
if (end <= start) return 1; // continue
|
|
800
|
+
var segment = {
|
|
801
|
+
start: start,
|
|
802
|
+
end: end
|
|
803
|
+
};
|
|
804
|
+
var activeResources = resourceWindows.reduce(function (count, windows) {
|
|
805
|
+
return windows.some(function (window) {
|
|
806
|
+
return contains(window, segment);
|
|
807
|
+
}) ? count + 1 : count;
|
|
808
|
+
}, 0);
|
|
809
|
+
if (activeResources >= requiredConcurrentResources) {
|
|
810
|
+
satisfiedRanges.push(segment);
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
for (var index = 0; index < sortedBoundaries.length - 1; index += 1) {
|
|
814
|
+
if (_loop2()) continue;
|
|
439
815
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
816
|
+
return mergeRanges(satisfiedRanges);
|
|
817
|
+
}
|
|
818
|
+
function materializeScheduleRules(rules, dateRange, timezoneName, fixedOffsetMinutes) {
|
|
819
|
+
var dates = enumerateDates(dateRange.startDate, dateRange.endDate);
|
|
820
|
+
var seen = new Map();
|
|
821
|
+
rules.forEach(function (rule) {
|
|
822
|
+
dates.forEach(function (date) {
|
|
823
|
+
if (!matchesScheduleRuleDate(rule, date, timezoneName)) return;
|
|
824
|
+
var start = toDateTimeInDate(date, rule.startTime, timezoneName);
|
|
825
|
+
var end = toDateTimeInDate(date, rule.endTime, timezoneName);
|
|
826
|
+
if (end <= start) end = toDateTimeInDate(nextDate(date, timezoneName), rule.endTime, timezoneName);
|
|
827
|
+
var key = "".concat(date, ":").concat(start, ":").concat(end);
|
|
828
|
+
if (!seen.has(key)) {
|
|
829
|
+
seen.set(key, {
|
|
830
|
+
key: key,
|
|
831
|
+
productId: '',
|
|
832
|
+
source: 'session_schedule',
|
|
833
|
+
startAt: formatDateTime(start, timezoneName, fixedOffsetMinutes),
|
|
834
|
+
endAt: formatDateTime(end, timezoneName, fixedOffsetMinutes),
|
|
835
|
+
bookingStartAt: formatDateTime(start, timezoneName, fixedOffsetMinutes),
|
|
836
|
+
bookingEndAt: formatDateTime(end, timezoneName, fixedOffsetMinutes),
|
|
837
|
+
date: formatDate(start, timezoneName),
|
|
838
|
+
startTime: formatTime(start, timezoneName),
|
|
839
|
+
endTime: formatTime(end, timezoneName),
|
|
840
|
+
timezone: timezoneName,
|
|
841
|
+
scheduleRefs: []
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
var current = seen.get(key);
|
|
845
|
+
var scheduleRef = _objectSpread(_objectSpread({}, rule.scheduleRef || {}), {}, {
|
|
846
|
+
ruleId: rule.id
|
|
847
|
+
});
|
|
848
|
+
current.scheduleRefs = mergeScheduleRefs(current.scheduleRefs, [scheduleRef]);
|
|
849
|
+
if (!current.primaryScheduleRef) current.primaryScheduleRef = scheduleRef;
|
|
447
850
|
});
|
|
851
|
+
});
|
|
852
|
+
return Array.from(seen.values());
|
|
853
|
+
}
|
|
854
|
+
function matchesScheduleRuleDate(rule, date, _timezoneName) {
|
|
855
|
+
var _rule$includeDates, _rule$excludeDates;
|
|
856
|
+
if ((_rule$includeDates = rule.includeDates) !== null && _rule$includeDates !== void 0 && _rule$includeDates.includes(date)) return true;
|
|
857
|
+
if ((_rule$excludeDates = rule.excludeDates) !== null && _rule$excludeDates !== void 0 && _rule$excludeDates.includes(date)) return false;
|
|
858
|
+
if (rule.startDate && date < rule.startDate) return false;
|
|
859
|
+
if (rule.endDate && date > rule.endDate) return false;
|
|
860
|
+
if (rule.mode === 'designation') {
|
|
861
|
+
var _rule$dates;
|
|
862
|
+
if ((_rule$dates = rule.dates) !== null && _rule$dates !== void 0 && _rule$dates.includes(date)) return true;
|
|
863
|
+
return false;
|
|
448
864
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
865
|
+
if (rule.mode === 'weekly') {
|
|
866
|
+
var _rule$weekdays;
|
|
867
|
+
if (!((_rule$weekdays = rule.weekdays) !== null && _rule$weekdays !== void 0 && _rule$weekdays.length)) return true;
|
|
868
|
+
var freq = Math.max(1, Number(rule.frequency || 1) || 1);
|
|
869
|
+
if (!rule.weekdays.includes(weekdayFromLocalDate(date))) return false;
|
|
870
|
+
if (!rule.startDate) return true;
|
|
871
|
+
var targetWeekStart = dateOrdinalFromLocalDate(date) - weekdayFromLocalDate(date);
|
|
872
|
+
var ruleWeekStart = dateOrdinalFromLocalDate(rule.startDate) - weekdayFromLocalDate(rule.startDate);
|
|
873
|
+
return Math.floor((targetWeekStart - ruleWeekStart) / 7) % freq === 0;
|
|
454
874
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
start_at: interval.start_at,
|
|
461
|
-
end_at: interval.end_at,
|
|
462
|
-
startTime: timeOf(interval.start_at),
|
|
463
|
-
endTime: timeOf(interval.end_at),
|
|
464
|
-
durationMinutes: interval.durationMinutes,
|
|
465
|
-
cellType: status === 'available' || status === 'limited' ? 'fixed_interval' : 'unavailable',
|
|
466
|
-
status: status,
|
|
467
|
-
remainingCapacity: remainingCapacity,
|
|
468
|
-
maxCapacity: maxCapacity,
|
|
469
|
-
reasonCodes: reasonCodes,
|
|
470
|
-
conflicts: conflicts
|
|
471
|
-
};
|
|
875
|
+
if (rule.mode === 'daily' && rule.startDate) {
|
|
876
|
+
var _freq = Math.max(1, Number(rule.frequency || 1) || 1);
|
|
877
|
+
return (dateOrdinalFromLocalDate(date) - dateOrdinalFromLocalDate(rule.startDate)) % _freq === 0;
|
|
878
|
+
}
|
|
879
|
+
return true;
|
|
472
880
|
}
|
|
473
|
-
function
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
if (cells.some(function (cell) {
|
|
482
|
-
return cell.status === 'past';
|
|
483
|
-
})) return 'past';
|
|
484
|
-
if (cells.some(function (cell) {
|
|
485
|
-
return cell.status === 'full';
|
|
486
|
-
})) return 'full';
|
|
487
|
-
if (cells.some(function (cell) {
|
|
488
|
-
return cell.status === 'conflict';
|
|
489
|
-
})) return 'conflict';
|
|
490
|
-
return 'unavailable';
|
|
881
|
+
function mergeScheduleRefs(base, next) {
|
|
882
|
+
var map = new Map();
|
|
883
|
+
[].concat(_toConsumableArray(base), _toConsumableArray(next)).forEach(function (ref) {
|
|
884
|
+
var _ref$scheduleId, _ref$timeSlotId, _ref$ruleId, _ref$source, _ref$label;
|
|
885
|
+
var key = [(_ref$scheduleId = ref.scheduleId) !== null && _ref$scheduleId !== void 0 ? _ref$scheduleId : '', (_ref$timeSlotId = ref.timeSlotId) !== null && _ref$timeSlotId !== void 0 ? _ref$timeSlotId : '', (_ref$ruleId = ref.ruleId) !== null && _ref$ruleId !== void 0 ? _ref$ruleId : '', (_ref$source = ref.source) !== null && _ref$source !== void 0 ? _ref$source : '', (_ref$label = ref.label) !== null && _ref$label !== void 0 ? _ref$label : ''].join(':');
|
|
886
|
+
map.set(key, ref);
|
|
887
|
+
});
|
|
888
|
+
return Array.from(map.values());
|
|
491
889
|
}
|
|
492
|
-
function
|
|
493
|
-
return
|
|
494
|
-
return
|
|
495
|
-
}))
|
|
890
|
+
function buildProductIndex(projection) {
|
|
891
|
+
return new Map(projection.products.map(function (product) {
|
|
892
|
+
return [String(product.id), product];
|
|
893
|
+
}));
|
|
496
894
|
}
|
|
497
|
-
function
|
|
498
|
-
var
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
895
|
+
function buildGroupResourceIndex(params) {
|
|
896
|
+
var _params$resourceIds;
|
|
897
|
+
var resourceIdSet = (_params$resourceIds = params.resourceIds) !== null && _params$resourceIds !== void 0 && _params$resourceIds.length ? new Set(params.resourceIds.map(String)) : null;
|
|
898
|
+
var index = new Map();
|
|
899
|
+
params.projection.products.forEach(function (product) {
|
|
900
|
+
(product.requirementGroups || []).forEach(function (requirementGroup) {
|
|
901
|
+
var key = [String(product.id), requirementGroup.id].join(':');
|
|
902
|
+
index.set(key, params.projection.resources.filter(function (resource) {
|
|
903
|
+
return matchesRequirementGroup(requirementGroup, resource, product, params.partySize);
|
|
904
|
+
}).filter(function (resource) {
|
|
905
|
+
return !resourceIdSet || resourceIdSet.has(String(resource.id));
|
|
906
|
+
}));
|
|
502
907
|
});
|
|
503
|
-
|
|
908
|
+
});
|
|
909
|
+
return index;
|
|
910
|
+
}
|
|
911
|
+
function getSessionRangesForProduct(projection, product, dateRange) {
|
|
912
|
+
var _extractFixedOffsetMi2;
|
|
913
|
+
var fixedOffsetMinutes = (_extractFixedOffsetMi2 = extractFixedOffsetMinutes(projection.meta.now)) !== null && _extractFixedOffsetMi2 !== void 0 ? _extractFixedOffsetMi2 : captureRuntimeOffsetMinutes();
|
|
914
|
+
var ranges = (product.sessionRanges || []).map(function (range) {
|
|
915
|
+
var _range$scheduleRefs2;
|
|
916
|
+
var normalized = normalizeAbsoluteRange(range, projection.meta.timezone);
|
|
917
|
+
if (!normalized) return null;
|
|
918
|
+
var startDate = formatDate(normalized.start, projection.meta.timezone);
|
|
919
|
+
if (startDate < dateRange.startDate || startDate > dateRange.endDate) {
|
|
920
|
+
return null;
|
|
921
|
+
}
|
|
504
922
|
return {
|
|
923
|
+
key: "".concat(String(product.id), ":").concat(normalized.start, ":").concat(normalized.end),
|
|
505
924
|
productId: product.id,
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
925
|
+
source: 'session_schedule',
|
|
926
|
+
startAt: formatDateTime(normalized.start, projection.meta.timezone, fixedOffsetMinutes),
|
|
927
|
+
endAt: formatDateTime(normalized.end, projection.meta.timezone, fixedOffsetMinutes),
|
|
928
|
+
bookingStartAt: formatDateTime(normalized.start, projection.meta.timezone, fixedOffsetMinutes),
|
|
929
|
+
bookingEndAt: formatDateTime(normalized.end, projection.meta.timezone, fixedOffsetMinutes),
|
|
930
|
+
date: formatDate(normalized.start, projection.meta.timezone),
|
|
931
|
+
startTime: formatTime(normalized.start, projection.meta.timezone),
|
|
932
|
+
endTime: formatTime(normalized.end, projection.meta.timezone),
|
|
933
|
+
timezone: projection.meta.timezone,
|
|
934
|
+
scheduleRefs: range.scheduleRefs || [],
|
|
935
|
+
primaryScheduleRef: (_range$scheduleRefs2 = range.scheduleRefs) === null || _range$scheduleRefs2 === void 0 ? void 0 : _range$scheduleRefs2[0]
|
|
509
936
|
};
|
|
937
|
+
}).filter(Boolean);
|
|
938
|
+
var ruleRanges = materializeScheduleRules(product.sessionRules || [], dateRange, projection.meta.timezone, fixedOffsetMinutes);
|
|
939
|
+
return dedupeProductRanges([].concat(_toConsumableArray(ranges), _toConsumableArray(ruleRanges.map(function (range) {
|
|
940
|
+
return _objectSpread(_objectSpread({}, range), {}, {
|
|
941
|
+
key: "".concat(String(product.id), ":").concat(range.startAt, ":").concat(range.endAt),
|
|
942
|
+
productId: product.id,
|
|
943
|
+
timezone: projection.meta.timezone
|
|
944
|
+
});
|
|
945
|
+
}))));
|
|
946
|
+
}
|
|
947
|
+
function dedupeProductRanges(ranges) {
|
|
948
|
+
var map = new Map();
|
|
949
|
+
ranges.forEach(function (range) {
|
|
950
|
+
var key = getProductTimeRangeIdentity(range);
|
|
951
|
+
if (!map.has(key)) {
|
|
952
|
+
map.set(key, _objectSpread(_objectSpread({}, range), {}, {
|
|
953
|
+
key: key
|
|
954
|
+
}));
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
var existing = map.get(key);
|
|
958
|
+
existing.scheduleRefs = mergeScheduleRefs(existing.scheduleRefs, range.scheduleRefs || []);
|
|
959
|
+
if (!existing.primaryScheduleRef) existing.primaryScheduleRef = range.primaryScheduleRef;
|
|
960
|
+
});
|
|
961
|
+
return Array.from(map.values()).sort(function (left, right) {
|
|
962
|
+
return left.startAt.localeCompare(right.startAt);
|
|
510
963
|
});
|
|
511
964
|
}
|
|
512
|
-
function
|
|
513
|
-
var
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
965
|
+
export function getProductTimeRanges(projection) {
|
|
966
|
+
var _extractFixedOffsetMi3, _request$productIds;
|
|
967
|
+
var request = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
968
|
+
var normalizedPartySize = normalizePartySize(request.partySize);
|
|
969
|
+
var dateRange = resolveProjectionDateRange(projection, request.dateRange);
|
|
970
|
+
var fixedOffsetMinutes = (_extractFixedOffsetMi3 = extractFixedOffsetMinutes(projection.meta.now)) !== null && _extractFixedOffsetMi3 !== void 0 ? _extractFixedOffsetMi3 : captureRuntimeOffsetMinutes();
|
|
971
|
+
var resourceWindowIndex = buildResourceWindowIndex(projection);
|
|
972
|
+
var filteredProductIdSet = (_request$productIds = request.productIds) !== null && _request$productIds !== void 0 && _request$productIds.length ? new Set(request.productIds.map(String)) : null;
|
|
973
|
+
return projection.products.filter(function (product) {
|
|
974
|
+
return !filteredProductIdSet || filteredProductIdSet.has(String(product.id));
|
|
975
|
+
}).map(function (product) {
|
|
976
|
+
if (product.kind === 'normal') {
|
|
977
|
+
return {
|
|
978
|
+
productId: product.id,
|
|
979
|
+
title: product.title,
|
|
980
|
+
kind: product.kind,
|
|
981
|
+
ranges: []
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
if (product.kind === 'session' || product.kind === 'venue_slot') {
|
|
985
|
+
return {
|
|
986
|
+
productId: product.id,
|
|
987
|
+
title: product.title,
|
|
988
|
+
kind: product.kind,
|
|
989
|
+
ranges: getSessionRangesForProduct(projection, product, dateRange)
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
var requiredGroups = (product.requirementGroups || []).filter(function (group) {
|
|
993
|
+
return group.required !== false;
|
|
994
|
+
});
|
|
995
|
+
var operatingRanges = buildOperatingRanges(projection, dateRange);
|
|
996
|
+
var resourceRanges = requiredGroups.length ? requiredGroups.map(function (group) {
|
|
997
|
+
return buildGroupWorkingRanges({
|
|
998
|
+
projection: projection,
|
|
999
|
+
product: product,
|
|
1000
|
+
requirementGroup: group,
|
|
1001
|
+
dateRange: dateRange,
|
|
1002
|
+
resourceIds: request.resourceIds,
|
|
1003
|
+
partySize: normalizedPartySize,
|
|
1004
|
+
resourceWindowIndex: resourceWindowIndex
|
|
1005
|
+
});
|
|
1006
|
+
}).reduce(function (current, ranges) {
|
|
1007
|
+
return current === null ? ranges : intersectRangeLists(current, ranges);
|
|
1008
|
+
}, null) || [] : null;
|
|
1009
|
+
var baseRanges = resourceRanges === null ? operatingRanges : intersectRangeLists(resourceRanges, operatingRanges);
|
|
1010
|
+
var durationMinutes = Math.max(1, Number(product.durationMinutes || 30) || 30);
|
|
1011
|
+
var timeSliceMinutes = projection.meta.defaults.slotStepMinutes;
|
|
1012
|
+
var durationAnchor = ceilToTenMinutes(toDateTime(projection.meta.durationCandidateAnchorAt, projection.meta.timezone), projection.meta.timezone);
|
|
1013
|
+
var ranges = [];
|
|
1014
|
+
baseRanges.forEach(function (range) {
|
|
1015
|
+
if (range.end <= durationAnchor) return;
|
|
1016
|
+
var cursor = range.start;
|
|
1017
|
+
if (cursor < durationAnchor) {
|
|
1018
|
+
cursor = durationAnchor;
|
|
1019
|
+
}
|
|
1020
|
+
while (true) {
|
|
1021
|
+
var bookingEnd = addMinutesZoned(cursor, durationMinutes, projection.meta.timezone);
|
|
1022
|
+
if (bookingEnd > range.end) break;
|
|
1023
|
+
var bookingDate = formatDate(cursor);
|
|
1024
|
+
if (bookingDate >= dateRange.startDate && bookingDate <= dateRange.endDate) {
|
|
1025
|
+
ranges.push({
|
|
1026
|
+
key: "".concat(String(product.id), ":").concat(cursor, ":").concat(bookingEnd),
|
|
1027
|
+
productId: product.id,
|
|
1028
|
+
source: 'duration_window',
|
|
1029
|
+
startAt: formatDateTime(cursor, projection.meta.timezone, fixedOffsetMinutes),
|
|
1030
|
+
endAt: formatDateTime(bookingEnd, projection.meta.timezone, fixedOffsetMinutes),
|
|
1031
|
+
bookingStartAt: formatDateTime(cursor, projection.meta.timezone, fixedOffsetMinutes),
|
|
1032
|
+
bookingEndAt: formatDateTime(bookingEnd, projection.meta.timezone, fixedOffsetMinutes),
|
|
1033
|
+
date: formatDate(cursor, projection.meta.timezone),
|
|
1034
|
+
startTime: formatTime(cursor, projection.meta.timezone),
|
|
1035
|
+
endTime: formatTime(bookingEnd, projection.meta.timezone),
|
|
1036
|
+
timezone: projection.meta.timezone,
|
|
1037
|
+
scheduleRefs: []
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
cursor = addMinutesZoned(cursor, timeSliceMinutes, projection.meta.timezone);
|
|
1041
|
+
}
|
|
517
1042
|
});
|
|
518
|
-
var bestCell = resourceCells.find(function (cell) {
|
|
519
|
-
return cell.status === 'available';
|
|
520
|
-
}) || resourceCells.find(function (cell) {
|
|
521
|
-
return cell.status === 'limited';
|
|
522
|
-
}) || resourceCells[0];
|
|
523
1043
|
return {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
status: resourceCells.length ? aggregateStatus(resourceCells) : 'unavailable',
|
|
529
|
-
remainingCapacity: bestCell === null || bestCell === void 0 ? void 0 : bestCell.remainingCapacity,
|
|
530
|
-
maxCapacity: (bestCell === null || bestCell === void 0 ? void 0 : bestCell.maxCapacity) || resource.capacity,
|
|
531
|
-
reasonCodes: uniqueReasonCodes(resourceCells)
|
|
1044
|
+
productId: product.id,
|
|
1045
|
+
title: product.title,
|
|
1046
|
+
kind: product.kind,
|
|
1047
|
+
ranges: dedupeProductRanges(ranges)
|
|
532
1048
|
};
|
|
533
1049
|
});
|
|
534
1050
|
}
|
|
535
|
-
function
|
|
536
|
-
var
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
1051
|
+
function aggregateMatchedCells(productId, requirementGroupId, resourceId, candidateRange, indexedCells) {
|
|
1052
|
+
var _extractFixedOffsetMi4, _extractFixedOffsetMi5;
|
|
1053
|
+
var candidateStartMs = candidateRange.start;
|
|
1054
|
+
var candidateEndMs = candidateRange.end;
|
|
1055
|
+
var left = 0;
|
|
1056
|
+
var right = indexedCells.length;
|
|
1057
|
+
while (left < right) {
|
|
1058
|
+
var middle = Math.floor((left + right) / 2);
|
|
1059
|
+
if (indexedCells[middle].range.endMs <= candidateStartMs) {
|
|
1060
|
+
left = middle + 1;
|
|
1061
|
+
} else {
|
|
1062
|
+
right = middle;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
var overlapped = [];
|
|
1066
|
+
for (var index = Math.max(0, left - 1); index < indexedCells.length; index += 1) {
|
|
1067
|
+
var _indexedCells$index = indexedCells[index],
|
|
1068
|
+
cell = _indexedCells$index.cell,
|
|
1069
|
+
range = _indexedCells$index.range;
|
|
1070
|
+
if (range.startMs >= candidateEndMs) break;
|
|
1071
|
+
if (range.endMs <= candidateStartMs) continue;
|
|
1072
|
+
var clipped = clipRange(range, candidateRange);
|
|
1073
|
+
if (clipped) overlapped.push({
|
|
1074
|
+
cell: cell,
|
|
1075
|
+
range: clipped
|
|
542
1076
|
});
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
}
|
|
1077
|
+
}
|
|
1078
|
+
if (!overlapped.length) return undefined;
|
|
1079
|
+
var cursor = candidateRange.start;
|
|
1080
|
+
for (var _i = 0, _overlapped = overlapped; _i < _overlapped.length; _i++) {
|
|
1081
|
+
var item = _overlapped[_i];
|
|
1082
|
+
if (item.range.start > cursor) return undefined;
|
|
1083
|
+
if (item.range.end > cursor) {
|
|
1084
|
+
cursor = item.range.end;
|
|
1085
|
+
}
|
|
1086
|
+
if (cursor >= candidateRange.end) break;
|
|
1087
|
+
}
|
|
1088
|
+
if (cursor < candidateRange.end) return undefined;
|
|
1089
|
+
var cells = overlapped.map(function (item) {
|
|
1090
|
+
return item.cell;
|
|
1091
|
+
});
|
|
1092
|
+
var statuses = cells.map(function (cell) {
|
|
1093
|
+
return cell.status;
|
|
1094
|
+
});
|
|
1095
|
+
var nonPastStatuses = statuses.filter(function (status) {
|
|
1096
|
+
return status !== 'past';
|
|
1097
|
+
});
|
|
1098
|
+
var aggregatedStatus = function () {
|
|
1099
|
+
if (nonPastStatuses.some(function (status) {
|
|
1100
|
+
return status === 'unavailable';
|
|
1101
|
+
})) return 'unavailable';
|
|
1102
|
+
if (nonPastStatuses.some(function (status) {
|
|
1103
|
+
return status === 'full';
|
|
1104
|
+
})) return 'full';
|
|
1105
|
+
if (nonPastStatuses.some(function (status) {
|
|
1106
|
+
return status === 'limited';
|
|
1107
|
+
})) return 'limited';
|
|
1108
|
+
if (nonPastStatuses.every(function (status) {
|
|
1109
|
+
return status === 'available';
|
|
1110
|
+
})) return 'available';
|
|
1111
|
+
if (statuses.every(function (status) {
|
|
1112
|
+
return status === 'past';
|
|
1113
|
+
})) return 'past';
|
|
1114
|
+
return 'available';
|
|
1115
|
+
}();
|
|
1116
|
+
var capacityCells = cells.filter(function (cell) {
|
|
1117
|
+
return cell.remainingCapacity !== undefined || cell.maxCapacity !== undefined;
|
|
1118
|
+
});
|
|
1119
|
+
var reasonCodes = Array.from(new Set(cells.flatMap(function (cell) {
|
|
1120
|
+
return cell.reasonCodes;
|
|
1121
|
+
})));
|
|
1122
|
+
var occupancyRefs = Array.from(new Map(cells.flatMap(function (cell) {
|
|
1123
|
+
return cell.occupancyRefs || [];
|
|
1124
|
+
}).map(function (ref) {
|
|
1125
|
+
var _ref$id, _ref$resourceId, _ref$serverEventId, _ref$bookingUid, _ref$productUid, _ref$scheduleEventId, _ref$source2;
|
|
1126
|
+
return [[(_ref$id = ref.id) !== null && _ref$id !== void 0 ? _ref$id : '', (_ref$resourceId = ref.resourceId) !== null && _ref$resourceId !== void 0 ? _ref$resourceId : '', (_ref$serverEventId = ref.serverEventId) !== null && _ref$serverEventId !== void 0 ? _ref$serverEventId : '', (_ref$bookingUid = ref.bookingUid) !== null && _ref$bookingUid !== void 0 ? _ref$bookingUid : '', (_ref$productUid = ref.productUid) !== null && _ref$productUid !== void 0 ? _ref$productUid : '', (_ref$scheduleEventId = ref.scheduleEventId) !== null && _ref$scheduleEventId !== void 0 ? _ref$scheduleEventId : '', (_ref$source2 = ref.source) !== null && _ref$source2 !== void 0 ? _ref$source2 : ''].join(':'), ref];
|
|
1127
|
+
})).values());
|
|
1128
|
+
var firstCell = cells[0];
|
|
1129
|
+
return _objectSpread(_objectSpread({}, firstCell), {}, {
|
|
1130
|
+
key: buildCellKey({
|
|
1131
|
+
productId: productId,
|
|
1132
|
+
groupId: requirementGroupId,
|
|
1133
|
+
resourceId: resourceId,
|
|
1134
|
+
start: candidateRange.start,
|
|
1135
|
+
end: candidateRange.end
|
|
1136
|
+
}),
|
|
1137
|
+
startAt: formatDateTime(candidateRange.start, firstCell.timezone, (_extractFixedOffsetMi4 = extractFixedOffsetMinutes(firstCell.startAt)) !== null && _extractFixedOffsetMi4 !== void 0 ? _extractFixedOffsetMi4 : captureRuntimeOffsetMinutes()),
|
|
1138
|
+
endAt: formatDateTime(candidateRange.end, firstCell.timezone, (_extractFixedOffsetMi5 = extractFixedOffsetMinutes(firstCell.startAt)) !== null && _extractFixedOffsetMi5 !== void 0 ? _extractFixedOffsetMi5 : captureRuntimeOffsetMinutes()),
|
|
1139
|
+
date: formatDate(candidateRange.start, firstCell.timezone),
|
|
1140
|
+
startTime: formatTime(candidateRange.start, firstCell.timezone),
|
|
1141
|
+
endTime: formatTime(candidateRange.end, firstCell.timezone),
|
|
1142
|
+
status: aggregatedStatus,
|
|
1143
|
+
remainingCapacity: capacityCells.length ? Math.min.apply(Math, _toConsumableArray(capacityCells.map(function (cell) {
|
|
1144
|
+
var _cell$remainingCapaci;
|
|
1145
|
+
return (_cell$remainingCapaci = cell.remainingCapacity) !== null && _cell$remainingCapaci !== void 0 ? _cell$remainingCapaci : Number.MAX_SAFE_INTEGER;
|
|
1146
|
+
}))) : undefined,
|
|
1147
|
+
maxCapacity: capacityCells.length ? Math.min.apply(Math, _toConsumableArray(capacityCells.map(function (cell) {
|
|
1148
|
+
var _cell$maxCapacity;
|
|
1149
|
+
return (_cell$maxCapacity = cell.maxCapacity) !== null && _cell$maxCapacity !== void 0 ? _cell$maxCapacity : Number.MAX_SAFE_INTEGER;
|
|
1150
|
+
}))) : undefined,
|
|
1151
|
+
reasonCodes: reasonCodes,
|
|
1152
|
+
occupancyRefs: occupancyRefs
|
|
552
1153
|
});
|
|
553
1154
|
}
|
|
554
|
-
function
|
|
555
|
-
|
|
556
|
-
var
|
|
557
|
-
var
|
|
558
|
-
var
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
1155
|
+
function aggregateMatchedCellSummary(candidateRange, indexedCells) {
|
|
1156
|
+
var candidateStartMs = candidateRange.startMs;
|
|
1157
|
+
var candidateEndMs = candidateRange.endMs;
|
|
1158
|
+
var left = 0;
|
|
1159
|
+
var right = indexedCells.length;
|
|
1160
|
+
while (left < right) {
|
|
1161
|
+
var middle = Math.floor((left + right) / 2);
|
|
1162
|
+
if (indexedCells[middle].range.endMs <= candidateStartMs) {
|
|
1163
|
+
left = middle + 1;
|
|
1164
|
+
} else {
|
|
1165
|
+
right = middle;
|
|
1166
|
+
}
|
|
564
1167
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
1168
|
+
var cells = [];
|
|
1169
|
+
var cursorMs = candidateStartMs;
|
|
1170
|
+
for (var index = Math.max(0, left - 1); index < indexedCells.length; index += 1) {
|
|
1171
|
+
var _indexedCells$index2 = indexedCells[index],
|
|
1172
|
+
cell = _indexedCells$index2.cell,
|
|
1173
|
+
range = _indexedCells$index2.range;
|
|
1174
|
+
if (range.startMs >= candidateEndMs) break;
|
|
1175
|
+
if (range.endMs <= candidateStartMs) continue;
|
|
1176
|
+
var clippedStartMs = Math.max(range.startMs, candidateStartMs);
|
|
1177
|
+
var clippedEndMs = Math.min(range.endMs, candidateEndMs);
|
|
1178
|
+
if (clippedEndMs <= clippedStartMs) continue;
|
|
1179
|
+
if (clippedStartMs > cursorMs) return undefined;
|
|
1180
|
+
cells.push(cell);
|
|
1181
|
+
if (clippedEndMs > cursorMs) cursorMs = clippedEndMs;
|
|
1182
|
+
if (cursorMs >= candidateEndMs) break;
|
|
1183
|
+
}
|
|
1184
|
+
if (!cells.length || cursorMs < candidateEndMs) return undefined;
|
|
1185
|
+
var statuses = cells.map(function (cell) {
|
|
1186
|
+
return cell.status;
|
|
1187
|
+
});
|
|
1188
|
+
var nonPastStatuses = statuses.filter(function (status) {
|
|
1189
|
+
return status !== 'past';
|
|
1190
|
+
});
|
|
1191
|
+
var status = function () {
|
|
1192
|
+
if (nonPastStatuses.some(function (cellStatus) {
|
|
1193
|
+
return cellStatus === 'unavailable';
|
|
1194
|
+
})) return 'unavailable';
|
|
1195
|
+
if (nonPastStatuses.some(function (cellStatus) {
|
|
1196
|
+
return cellStatus === 'full';
|
|
1197
|
+
})) return 'full';
|
|
1198
|
+
if (nonPastStatuses.some(function (cellStatus) {
|
|
1199
|
+
return cellStatus === 'limited';
|
|
1200
|
+
})) return 'limited';
|
|
1201
|
+
if (nonPastStatuses.every(function (cellStatus) {
|
|
1202
|
+
return cellStatus === 'available';
|
|
1203
|
+
})) return 'available';
|
|
1204
|
+
if (statuses.every(function (cellStatus) {
|
|
1205
|
+
return cellStatus === 'past';
|
|
1206
|
+
})) return 'past';
|
|
1207
|
+
return 'available';
|
|
1208
|
+
}();
|
|
1209
|
+
var capacityCells = cells.filter(function (cell) {
|
|
1210
|
+
return cell.remainingCapacity !== undefined || cell.maxCapacity !== undefined;
|
|
1211
|
+
});
|
|
571
1212
|
return {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
1213
|
+
status: status,
|
|
1214
|
+
remainingCapacity: capacityCells.length ? Math.min.apply(Math, _toConsumableArray(capacityCells.map(function (cell) {
|
|
1215
|
+
var _cell$remainingCapaci2;
|
|
1216
|
+
return (_cell$remainingCapaci2 = cell.remainingCapacity) !== null && _cell$remainingCapaci2 !== void 0 ? _cell$remainingCapaci2 : Number.MAX_SAFE_INTEGER;
|
|
1217
|
+
}))) : undefined,
|
|
1218
|
+
maxCapacity: capacityCells.length ? Math.min.apply(Math, _toConsumableArray(capacityCells.map(function (cell) {
|
|
1219
|
+
var _cell$maxCapacity2;
|
|
1220
|
+
return (_cell$maxCapacity2 = cell.maxCapacity) !== null && _cell$maxCapacity2 !== void 0 ? _cell$maxCapacity2 : Number.MAX_SAFE_INTEGER;
|
|
1221
|
+
}))) : undefined,
|
|
1222
|
+
reasonCodes: Array.from(new Set(cells.flatMap(function (cell) {
|
|
1223
|
+
return cell.reasonCodes;
|
|
1224
|
+
})))
|
|
582
1225
|
};
|
|
583
1226
|
}
|
|
584
|
-
function
|
|
585
|
-
var
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
1227
|
+
function buildAvailabilityCellIndex(projection) {
|
|
1228
|
+
var index = new Map();
|
|
1229
|
+
projection.cells.forEach(function (cell) {
|
|
1230
|
+
if (cell.requirementGroupId == null || cell.resourceId == null) return;
|
|
1231
|
+
var key = [String(cell.productId), cell.requirementGroupId, String(cell.resourceId)].join(':');
|
|
1232
|
+
var entries = index.get(key) || [];
|
|
1233
|
+
entries.push({
|
|
1234
|
+
cell: cell,
|
|
1235
|
+
range: toIndexedRange({
|
|
1236
|
+
start: toDateTime(cell.startAt, projection.meta.timezone),
|
|
1237
|
+
end: toDateTime(cell.endAt, projection.meta.timezone)
|
|
1238
|
+
})
|
|
1239
|
+
});
|
|
1240
|
+
index.set(key, entries);
|
|
589
1241
|
});
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
1242
|
+
index.forEach(function (entries, key) {
|
|
1243
|
+
index.set(key, entries.sort(function (left, right) {
|
|
1244
|
+
return left.range.startMs - right.range.startMs;
|
|
1245
|
+
}));
|
|
1246
|
+
});
|
|
1247
|
+
return index;
|
|
1248
|
+
}
|
|
1249
|
+
function buildEvaluationContext(params) {
|
|
1250
|
+
var _params$resourceIds2;
|
|
1251
|
+
var evaluatedAt = toDateTime(params.evaluatedAt || params.projection.meta.now, params.projection.meta.timezone);
|
|
1252
|
+
var selectionsByGroupKey = new Map();
|
|
1253
|
+
(params.requirementSelections || []).forEach(function (selection) {
|
|
1254
|
+
var _selection$formId;
|
|
1255
|
+
var key = selection.requirementGroupId || String((_selection$formId = selection.formId) !== null && _selection$formId !== void 0 ? _selection$formId : '');
|
|
1256
|
+
selectionsByGroupKey.set(key, selection);
|
|
1257
|
+
});
|
|
1258
|
+
return {
|
|
1259
|
+
evaluatedAt: evaluatedAt,
|
|
1260
|
+
evaluatedAtMs: evaluatedAt,
|
|
1261
|
+
resourceIdSet: (_params$resourceIds2 = params.resourceIds) !== null && _params$resourceIds2 !== void 0 && _params$resourceIds2.length ? new Set(params.resourceIds.map(String)) : null,
|
|
1262
|
+
selectionsByGroupKey: selectionsByGroupKey
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
function summarizeStatuses(statuses) {
|
|
1266
|
+
if (!statuses.length) return 'unavailable';
|
|
1267
|
+
if (statuses.every(function (status) {
|
|
1268
|
+
return status === 'past';
|
|
1269
|
+
})) return 'past';
|
|
1270
|
+
if (statuses.some(function (status) {
|
|
1271
|
+
return status === 'unavailable';
|
|
1272
|
+
})) return 'unavailable';
|
|
1273
|
+
if (statuses.some(function (status) {
|
|
1274
|
+
return status === 'full';
|
|
1275
|
+
})) return 'full';
|
|
1276
|
+
if (statuses.some(function (status) {
|
|
1277
|
+
return status === 'limited';
|
|
1278
|
+
})) return 'limited';
|
|
1279
|
+
if (statuses.every(function (status) {
|
|
1280
|
+
return status === 'available';
|
|
1281
|
+
})) return 'available';
|
|
1282
|
+
if (statuses.some(function (status) {
|
|
1283
|
+
return status === 'past';
|
|
1284
|
+
})) return 'past';
|
|
1285
|
+
return statuses[0];
|
|
1286
|
+
}
|
|
1287
|
+
function createStatusAggregate() {
|
|
1288
|
+
return {
|
|
1289
|
+
count: 0,
|
|
1290
|
+
hasAvailable: false,
|
|
1291
|
+
hasLimited: false,
|
|
1292
|
+
hasFull: false,
|
|
1293
|
+
allPast: true
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
function pushStatusAggregate(aggregate, status) {
|
|
1297
|
+
aggregate.count += 1;
|
|
1298
|
+
if (status !== 'past') aggregate.allPast = false;
|
|
1299
|
+
if (status === 'available') aggregate.hasAvailable = true;
|
|
1300
|
+
if (status === 'limited') aggregate.hasLimited = true;
|
|
1301
|
+
if (status === 'full') aggregate.hasFull = true;
|
|
1302
|
+
}
|
|
1303
|
+
function summarizeStatusAggregate(aggregate) {
|
|
1304
|
+
if (aggregate.count === 0) return 'unavailable';
|
|
1305
|
+
if (aggregate.hasAvailable) return 'available';
|
|
1306
|
+
if (aggregate.hasLimited) return 'limited';
|
|
1307
|
+
if (aggregate.hasFull) return 'full';
|
|
1308
|
+
if (aggregate.allPast) return 'past';
|
|
1309
|
+
return 'unavailable';
|
|
1310
|
+
}
|
|
1311
|
+
function evaluateMatchedResourceSummary(params) {
|
|
1312
|
+
var _ref6, _params$matchedCell$m;
|
|
1313
|
+
var isPast = params.candidateRange.endMs <= params.evaluationContext.evaluatedAtMs;
|
|
1314
|
+
if (!params.matchedCell) {
|
|
593
1315
|
return {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
1316
|
+
status: isPast ? 'past' : 'unavailable',
|
|
1317
|
+
maxPartySize: 0,
|
|
1318
|
+
remainingCapacity: 0,
|
|
1319
|
+
maxCapacity: 0,
|
|
1320
|
+
reasonCodes: isPast ? ['past'] : ['outside_resource_window']
|
|
597
1321
|
};
|
|
598
1322
|
}
|
|
599
|
-
var
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
startTime: label,
|
|
610
|
-
endTime: dayjs(interval.end_at).format('HH:mm'),
|
|
611
|
-
status: (cell === null || cell === void 0 ? void 0 : cell.status) || 'unavailable',
|
|
612
|
-
resourceId: resource.id,
|
|
613
|
-
productId: product.id,
|
|
614
|
-
remainingCapacity: cell === null || cell === void 0 ? void 0 : cell.remainingCapacity,
|
|
615
|
-
maxCapacity: (cell === null || cell === void 0 ? void 0 : cell.maxCapacity) || resource.capacity,
|
|
616
|
-
reasonCodes: (cell === null || cell === void 0 ? void 0 : cell.reasonCodes) || ['outside_available_range']
|
|
617
|
-
};
|
|
618
|
-
});
|
|
619
|
-
return {
|
|
620
|
-
resourceId: resource.id,
|
|
621
|
-
resourceName: resource.name,
|
|
622
|
-
resourceFormId: resource.formId,
|
|
623
|
-
slots: slots
|
|
624
|
-
};
|
|
625
|
-
});
|
|
1323
|
+
var remainingCapacity = Math.max(0, params.matchedCell.remainingCapacity || 0);
|
|
1324
|
+
var maxCapacity = Math.max(0, (_ref6 = (_params$matchedCell$m = params.matchedCell.maxCapacity) !== null && _params$matchedCell$m !== void 0 ? _params$matchedCell$m : params.resourceCapacity) !== null && _ref6 !== void 0 ? _ref6 : 0);
|
|
1325
|
+
var maxPartySize = isPast ? 0 : remainingCapacity;
|
|
1326
|
+
var status = function () {
|
|
1327
|
+
if (isPast) return 'past';
|
|
1328
|
+
if (params.matchedCell.status === 'unavailable') return 'unavailable';
|
|
1329
|
+
if (remainingCapacity < params.requiredCapacity) return 'full';
|
|
1330
|
+
if (remainingCapacity < maxCapacity) return 'limited';
|
|
1331
|
+
return 'available';
|
|
1332
|
+
}();
|
|
626
1333
|
return {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
1334
|
+
status: status,
|
|
1335
|
+
remainingCapacity: remainingCapacity,
|
|
1336
|
+
maxCapacity: maxCapacity,
|
|
1337
|
+
maxPartySize: maxPartySize,
|
|
1338
|
+
reasonCodes: isPast ? Array.from(new Set([].concat(_toConsumableArray(params.matchedCell.reasonCodes || []), ['past']))) : params.matchedCell.reasonCodes
|
|
630
1339
|
};
|
|
631
1340
|
}
|
|
632
|
-
|
|
633
|
-
var
|
|
634
|
-
var
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
interval: interval,
|
|
677
|
-
events: eventsByResource.get(String(resource.id)) || []
|
|
678
|
-
})));
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
cells.push(evaluateResourceForInterval({
|
|
682
|
-
resource: resource,
|
|
683
|
-
interval: interval,
|
|
684
|
-
context: context
|
|
685
|
-
}));
|
|
686
|
-
});
|
|
687
|
-
};
|
|
688
|
-
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
689
|
-
if (_loop2()) continue;
|
|
1341
|
+
function evaluateRequirementGroupSummary(params) {
|
|
1342
|
+
var _requirementGroup$min2, _requirementGroup$max, _params$groupResource;
|
|
1343
|
+
var projection = params.projection,
|
|
1344
|
+
product = params.product,
|
|
1345
|
+
requirementGroup = params.requirementGroup,
|
|
1346
|
+
candidateRange = params.candidateRange,
|
|
1347
|
+
requestedPartySize = params.requestedPartySize,
|
|
1348
|
+
evaluationContext = params.evaluationContext;
|
|
1349
|
+
var required = requirementGroup.required !== false;
|
|
1350
|
+
var minimum = Math.max(required ? 1 : 0, Number((_requirementGroup$min2 = requirementGroup.min) !== null && _requirementGroup$min2 !== void 0 ? _requirementGroup$min2 : required ? 1 : 0) || 0);
|
|
1351
|
+
var maximum = Math.max(minimum, Number((_requirementGroup$max = requirementGroup.max) !== null && _requirementGroup$max !== void 0 ? _requirementGroup$max : Math.max(minimum, 1)) || Math.max(minimum, 1));
|
|
1352
|
+
var matchedResources = ((_params$groupResource = params.groupResourceIndex) === null || _params$groupResource === void 0 ? void 0 : _params$groupResource.get([String(product.id), requirementGroup.id].join(':'))) || projection.resources.filter(function (resource) {
|
|
1353
|
+
return matchesRequirementGroup(requirementGroup, resource, product, requestedPartySize !== null && requestedPartySize !== void 0 ? requestedPartySize : 1);
|
|
1354
|
+
}).filter(function (resource) {
|
|
1355
|
+
return !evaluationContext.resourceIdSet || evaluationContext.resourceIdSet.has(String(resource.id));
|
|
1356
|
+
});
|
|
1357
|
+
var requiredCapacity = resolveRequiredCapacity(requirementGroup, product, requestedPartySize);
|
|
1358
|
+
var optionStatusAggregate = createStatusAggregate();
|
|
1359
|
+
var reasonCodeSet = new Set();
|
|
1360
|
+
var totalResourceCount = 0;
|
|
1361
|
+
var availableResourceCount = 0;
|
|
1362
|
+
var groupMaxPartySize = 0;
|
|
1363
|
+
var groupRemainingCapacity = 0;
|
|
1364
|
+
var groupMaxCapacity = 0;
|
|
1365
|
+
matchedResources.forEach(function (resource) {
|
|
1366
|
+
var _params$cellIndex;
|
|
1367
|
+
totalResourceCount += 1;
|
|
1368
|
+
var matchedCell = aggregateMatchedCellSummary(candidateRange, ((_params$cellIndex = params.cellIndex) === null || _params$cellIndex === void 0 ? void 0 : _params$cellIndex.get([String(product.id), requirementGroup.id, String(resource.id)].join(':'))) || []);
|
|
1369
|
+
var option = evaluateMatchedResourceSummary({
|
|
1370
|
+
matchedCell: matchedCell,
|
|
1371
|
+
resourceCapacity: resource.capacity,
|
|
1372
|
+
requiredCapacity: requiredCapacity,
|
|
1373
|
+
candidateRange: candidateRange,
|
|
1374
|
+
evaluationContext: evaluationContext
|
|
1375
|
+
});
|
|
1376
|
+
pushStatusAggregate(optionStatusAggregate, option.status);
|
|
1377
|
+
option.reasonCodes.forEach(function (reasonCode) {
|
|
1378
|
+
return reasonCodeSet.add(reasonCode);
|
|
1379
|
+
});
|
|
1380
|
+
groupRemainingCapacity = Math.max(groupRemainingCapacity, option.remainingCapacity);
|
|
1381
|
+
groupMaxCapacity = Math.max(groupMaxCapacity, option.maxCapacity);
|
|
1382
|
+
if (option.status === 'available' || option.status === 'limited') {
|
|
1383
|
+
availableResourceCount += 1;
|
|
1384
|
+
groupMaxPartySize = Math.max(groupMaxPartySize, option.maxPartySize);
|
|
690
1385
|
}
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
1386
|
+
});
|
|
1387
|
+
var status = 'available';
|
|
1388
|
+
if (candidateRange.endMs <= evaluationContext.evaluatedAtMs) {
|
|
1389
|
+
status = 'past';
|
|
1390
|
+
} else if (availableResourceCount < minimum) {
|
|
1391
|
+
status = optionStatusAggregate.hasAvailable || optionStatusAggregate.hasLimited || optionStatusAggregate.hasFull ? 'full' : 'unavailable';
|
|
1392
|
+
} else if (availableResourceCount === totalResourceCount && !optionStatusAggregate.hasLimited && !optionStatusAggregate.hasFull && !optionStatusAggregate.allPast) {
|
|
1393
|
+
status = 'available';
|
|
1394
|
+
} else {
|
|
1395
|
+
status = 'limited';
|
|
695
1396
|
}
|
|
696
1397
|
return {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
1398
|
+
required: required,
|
|
1399
|
+
min: minimum,
|
|
1400
|
+
max: maximum,
|
|
1401
|
+
status: status,
|
|
1402
|
+
maxPartySize: groupMaxPartySize,
|
|
1403
|
+
remainingCapacity: groupRemainingCapacity,
|
|
1404
|
+
maxCapacity: groupMaxCapacity,
|
|
1405
|
+
reasonCodes: Array.from(reasonCodeSet)
|
|
704
1406
|
};
|
|
705
1407
|
}
|
|
706
|
-
function
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
cells = params.cells,
|
|
715
|
-
resources = params.resources;
|
|
716
|
-
var conflicts = [];
|
|
717
|
-
var groups = getIntervalRequirements(interval).map(function (requirement) {
|
|
718
|
-
var candidateCells = cells.filter(function (cell) {
|
|
719
|
-
if (!sameId(cell.productId, interval.productId)) return false;
|
|
720
|
-
if (cell.date !== interval.date) return false;
|
|
721
|
-
if (cell.resourceId === undefined) return false;
|
|
722
|
-
if (cell.status !== 'available' && cell.status !== 'limited') return false;
|
|
723
|
-
var cellStart = dayjs(cell.start_at);
|
|
724
|
-
var cellEnd = dayjs(cell.end_at);
|
|
725
|
-
var intervalStart = dayjs(interval.start_at);
|
|
726
|
-
var intervalEnd = dayjs(interval.end_at);
|
|
727
|
-
return containsRange(cellStart, cellEnd, intervalStart, intervalEnd) || interval.source === 'duration';
|
|
1408
|
+
function evaluateCandidateSummary(projection, candidate, request) {
|
|
1409
|
+
var _request$productIndex, _request$candidateRan;
|
|
1410
|
+
var product = ((_request$productIndex = request.productIndex) === null || _request$productIndex === void 0 ? void 0 : _request$productIndex.get(String(candidate.productId))) || projection.products.find(function (item) {
|
|
1411
|
+
return sameId(item.id, candidate.productId);
|
|
1412
|
+
});
|
|
1413
|
+
if (!product) {
|
|
1414
|
+
throw availabilityError('INVALID_TARGET_FILTER', 'candidate.productId 不存在于 projection.products', {
|
|
1415
|
+
candidate: candidate
|
|
728
1416
|
});
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
}];
|
|
1417
|
+
}
|
|
1418
|
+
var candidateRangeCacheKey = getCandidateRangeCacheKey(candidate);
|
|
1419
|
+
var candidateRange = ((_request$candidateRan = request.candidateRangeCache) === null || _request$candidateRan === void 0 ? void 0 : _request$candidateRan.get(candidateRangeCacheKey)) || function () {
|
|
1420
|
+
var normalized = normalizeAbsoluteRange(getCandidateBookingRange(candidate), projection.meta.timezone);
|
|
1421
|
+
return normalized ? toIndexedRange(normalized) : null;
|
|
1422
|
+
}();
|
|
1423
|
+
if (!candidateRange) {
|
|
1424
|
+
throw availabilityError('INVALID_TARGET_FILTER', 'candidate 时间范围非法', {
|
|
1425
|
+
candidate: candidate
|
|
739
1426
|
});
|
|
1427
|
+
}
|
|
1428
|
+
if (request.candidateRangeCache && !request.candidateRangeCache.has(candidateRangeCacheKey)) {
|
|
1429
|
+
request.candidateRangeCache.set(candidateRangeCacheKey, candidateRange);
|
|
1430
|
+
}
|
|
1431
|
+
var evaluationContext = request.evaluationContext || buildEvaluationContext({
|
|
1432
|
+
projection: projection,
|
|
1433
|
+
evaluatedAt: request.evaluatedAt,
|
|
1434
|
+
resourceIds: request.resourceIds
|
|
740
1435
|
});
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
1436
|
+
var requestedPartySize = normalizePartySize(request.partySize);
|
|
1437
|
+
var requiredGroups = (product.requirementGroups || []).filter(function (group) {
|
|
1438
|
+
return group.required !== false;
|
|
1439
|
+
}).map(function (requirementGroup) {
|
|
1440
|
+
return evaluateRequirementGroupSummary({
|
|
1441
|
+
projection: projection,
|
|
1442
|
+
product: product,
|
|
1443
|
+
requirementGroup: requirementGroup,
|
|
1444
|
+
candidateRange: candidateRange,
|
|
1445
|
+
requestedPartySize: requestedPartySize,
|
|
1446
|
+
evaluationContext: evaluationContext,
|
|
1447
|
+
cellIndex: request.cellIndex,
|
|
1448
|
+
groupResourceIndex: request.groupResourceIndex
|
|
749
1449
|
});
|
|
750
1450
|
});
|
|
1451
|
+
var gate = evaluateCandidateTimeGate({
|
|
1452
|
+
product: product,
|
|
1453
|
+
candidateRange: candidateRange,
|
|
1454
|
+
evaluationContext: evaluationContext,
|
|
1455
|
+
timezoneName: projection.meta.timezone
|
|
1456
|
+
});
|
|
1457
|
+
var candidateStatus = requiredGroups.length ? summarizeStatuses(requiredGroups.map(function (group) {
|
|
1458
|
+
return group.status;
|
|
1459
|
+
})) : candidateRange.endMs <= evaluationContext.evaluatedAtMs ? 'past' : 'available';
|
|
1460
|
+
var maxPartySize = requiredGroups.length ? requiredGroups.reduce(function (min, group) {
|
|
1461
|
+
return Math.min(min, group.maxPartySize);
|
|
1462
|
+
}, Number.MAX_SAFE_INTEGER) : 0;
|
|
1463
|
+
var remainingCapacity = requiredGroups.length ? requiredGroups.reduce(function (min, group) {
|
|
1464
|
+
return Math.min(min, group.remainingCapacity);
|
|
1465
|
+
}, Number.MAX_SAFE_INTEGER) : 0;
|
|
1466
|
+
var maxCapacity = requiredGroups.length ? requiredGroups.reduce(function (min, group) {
|
|
1467
|
+
return Math.min(min, group.maxCapacity);
|
|
1468
|
+
}, Number.MAX_SAFE_INTEGER) : 0;
|
|
1469
|
+
var status = (gate === null || gate === void 0 ? void 0 : gate.status) || candidateStatus;
|
|
751
1470
|
return {
|
|
752
|
-
|
|
753
|
-
|
|
1471
|
+
status: status,
|
|
1472
|
+
maxPartySize: gate ? 0 : Number.isFinite(maxPartySize) ? maxPartySize : 0,
|
|
1473
|
+
remainingCapacity: status === 'past' || status === 'unavailable' ? 0 : Number.isFinite(remainingCapacity) ? remainingCapacity : 0,
|
|
1474
|
+
maxCapacity: Number.isFinite(maxCapacity) ? maxCapacity : 0,
|
|
1475
|
+
reasonCodes: (gate === null || gate === void 0 ? void 0 : gate.reasonCodes) || Array.from(new Set(requiredGroups.flatMap(function (group) {
|
|
1476
|
+
return group.reasonCodes;
|
|
1477
|
+
})))
|
|
754
1478
|
};
|
|
755
1479
|
}
|
|
756
|
-
function
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
1480
|
+
function evaluateCandidate(projection, candidate, request) {
|
|
1481
|
+
var _request$productIndex2, _request$candidateRan2;
|
|
1482
|
+
var product = ((_request$productIndex2 = request.productIndex) === null || _request$productIndex2 === void 0 ? void 0 : _request$productIndex2.get(String(candidate.productId))) || projection.products.find(function (item) {
|
|
1483
|
+
return sameId(item.id, candidate.productId);
|
|
1484
|
+
});
|
|
1485
|
+
if (!product) {
|
|
1486
|
+
throw availabilityError('INVALID_TARGET_FILTER', 'candidate.productId 不存在于 projection.products', {
|
|
1487
|
+
candidate: candidate
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1490
|
+
var candidateRangeCacheKey = getCandidateRangeCacheKey(candidate);
|
|
1491
|
+
var candidateRange = ((_request$candidateRan2 = request.candidateRangeCache) === null || _request$candidateRan2 === void 0 ? void 0 : _request$candidateRan2.get(candidateRangeCacheKey)) || function () {
|
|
1492
|
+
var normalized = normalizeAbsoluteRange(getCandidateBookingRange(candidate), projection.meta.timezone);
|
|
1493
|
+
return normalized ? toIndexedRange(normalized) : null;
|
|
1494
|
+
}();
|
|
1495
|
+
if (!candidateRange) {
|
|
1496
|
+
throw availabilityError('INVALID_TARGET_FILTER', 'candidate 时间范围非法', {
|
|
1497
|
+
candidate: candidate
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
if (request.candidateRangeCache && !request.candidateRangeCache.has(candidateRangeCacheKey)) {
|
|
1501
|
+
request.candidateRangeCache.set(candidateRangeCacheKey, candidateRange);
|
|
1502
|
+
}
|
|
1503
|
+
var evaluationContext = request.evaluationContext || buildEvaluationContext({
|
|
1504
|
+
projection: projection,
|
|
1505
|
+
evaluatedAt: request.evaluatedAt,
|
|
1506
|
+
resourceIds: request.resourceIds,
|
|
1507
|
+
requirementSelections: request.requirementSelections
|
|
1508
|
+
});
|
|
1509
|
+
var requestedPartySize = normalizePartySize(request.partySize);
|
|
1510
|
+
var partySize = requestedPartySize !== null && requestedPartySize !== void 0 ? requestedPartySize : 1;
|
|
1511
|
+
var selectionsByGroupKey = evaluationContext.selectionsByGroupKey;
|
|
1512
|
+
var requirementGroups = (product.requirementGroups || []).map(function (requirementGroup) {
|
|
1513
|
+
var _requirementGroup$min3, _requirementGroup$max2, _requirementGroup$for2, _request$groupResourc;
|
|
1514
|
+
var required = requirementGroup.required !== false;
|
|
1515
|
+
var minimum = Math.max(required ? 1 : 0, Number((_requirementGroup$min3 = requirementGroup.min) !== null && _requirementGroup$min3 !== void 0 ? _requirementGroup$min3 : required ? 1 : 0) || 0);
|
|
1516
|
+
var maximum = Math.max(minimum, Number((_requirementGroup$max2 = requirementGroup.max) !== null && _requirementGroup$max2 !== void 0 ? _requirementGroup$max2 : Math.max(minimum, 1)) || Math.max(minimum, 1));
|
|
1517
|
+
var selection = selectionsByGroupKey.get(requirementGroup.id) || selectionsByGroupKey.get(String((_requirementGroup$for2 = requirementGroup.formId) !== null && _requirementGroup$for2 !== void 0 ? _requirementGroup$for2 : ''));
|
|
1518
|
+
var matchedResources = (((_request$groupResourc = request.groupResourceIndex) === null || _request$groupResourc === void 0 ? void 0 : _request$groupResourc.get([String(product.id), requirementGroup.id].join(':'))) || projection.resources.filter(function (resource) {
|
|
1519
|
+
return matchesRequirementGroup(requirementGroup, resource, product, partySize);
|
|
1520
|
+
}).filter(function (resource) {
|
|
1521
|
+
return !evaluationContext.resourceIdSet || evaluationContext.resourceIdSet.has(String(resource.id));
|
|
1522
|
+
})).filter(function (resource) {
|
|
1523
|
+
return !selection || selection.resourceIds.some(function (resourceId) {
|
|
1524
|
+
return sameId(resourceId, resource.id);
|
|
764
1525
|
});
|
|
765
1526
|
});
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
1527
|
+
var options = matchedResources.map(function (resource) {
|
|
1528
|
+
var _request$cellIndex;
|
|
1529
|
+
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(':'))) || []);
|
|
1530
|
+
if (!matchedCell) {
|
|
1531
|
+
var _isPast = candidateRange.endMs <= evaluationContext.evaluatedAtMs;
|
|
1532
|
+
return {
|
|
1533
|
+
resourceId: resource.id,
|
|
1534
|
+
resourceName: resource.name,
|
|
1535
|
+
resourceFormId: resource.formId,
|
|
1536
|
+
resourceType: resource.resourceType,
|
|
1537
|
+
status: _isPast ? 'past' : 'unavailable',
|
|
1538
|
+
maxPartySize: 0,
|
|
1539
|
+
reasonCodes: _isPast ? ['past'] : ['outside_resource_window']
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
var remainingCapacity = matchedCell.remainingCapacity;
|
|
1543
|
+
var maxCapacity = matchedCell.maxCapacity;
|
|
1544
|
+
var isPast = candidateRange.endMs <= evaluationContext.evaluatedAtMs;
|
|
1545
|
+
var maxPartySize = isPast ? 0 : Math.max(0, remainingCapacity || 0);
|
|
1546
|
+
var requiredCapacity = resolveRequiredCapacity(requirementGroup, product, requestedPartySize);
|
|
1547
|
+
var status = function () {
|
|
1548
|
+
if (isPast) return 'past';
|
|
1549
|
+
if (matchedCell.status === 'unavailable') return 'unavailable';
|
|
1550
|
+
if ((remainingCapacity || 0) < requiredCapacity) return 'full';
|
|
1551
|
+
if ((remainingCapacity || 0) < (maxCapacity || resource.capacity)) return 'limited';
|
|
1552
|
+
return 'available';
|
|
1553
|
+
}();
|
|
1554
|
+
return {
|
|
1555
|
+
resourceId: resource.id,
|
|
1556
|
+
resourceName: resource.name,
|
|
1557
|
+
resourceFormId: resource.formId,
|
|
1558
|
+
resourceType: resource.resourceType,
|
|
1559
|
+
status: status,
|
|
1560
|
+
remainingCapacity: remainingCapacity,
|
|
1561
|
+
maxCapacity: maxCapacity,
|
|
1562
|
+
maxPartySize: maxPartySize,
|
|
1563
|
+
reasonCodes: isPast ? Array.from(new Set([].concat(_toConsumableArray(matchedCell.reasonCodes), ['past']))) : matchedCell.reasonCodes,
|
|
1564
|
+
matchedCell: matchedCell
|
|
1565
|
+
};
|
|
1566
|
+
});
|
|
1567
|
+
var availableOptions = options.filter(function (option) {
|
|
1568
|
+
return option.status === 'available' || option.status === 'limited';
|
|
1569
|
+
});
|
|
1570
|
+
var totalResourceCount = options.length;
|
|
1571
|
+
var availableResourceCount = availableOptions.length;
|
|
1572
|
+
var groupMaxPartySize = availableOptions.reduce(function (max, option) {
|
|
1573
|
+
return Math.max(max, option.maxPartySize);
|
|
1574
|
+
}, 0);
|
|
1575
|
+
var status = 'available';
|
|
1576
|
+
if (candidateRange.endMs <= evaluationContext.evaluatedAtMs) {
|
|
1577
|
+
status = 'past';
|
|
1578
|
+
} else if (availableResourceCount < minimum) {
|
|
1579
|
+
status = options.some(function (option) {
|
|
1580
|
+
return option.status === 'full' || option.status === 'limited' || option.status === 'available';
|
|
1581
|
+
}) ? 'full' : 'unavailable';
|
|
1582
|
+
} else if (options.every(function (option) {
|
|
1583
|
+
return option.status === 'available';
|
|
1584
|
+
})) {
|
|
1585
|
+
status = 'available';
|
|
1586
|
+
} else {
|
|
1587
|
+
status = 'limited';
|
|
1588
|
+
}
|
|
778
1589
|
return {
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
1590
|
+
groupId: requirementGroup.id,
|
|
1591
|
+
formId: requirementGroup.formId,
|
|
1592
|
+
title: requirementGroup.title,
|
|
1593
|
+
code: requirementGroup.code,
|
|
1594
|
+
resourceType: requirementGroup.resourceType,
|
|
1595
|
+
required: required,
|
|
1596
|
+
min: minimum,
|
|
1597
|
+
max: maximum,
|
|
1598
|
+
status: status,
|
|
1599
|
+
availableResourceCount: availableResourceCount,
|
|
1600
|
+
totalResourceCount: totalResourceCount,
|
|
1601
|
+
maxPartySize: groupMaxPartySize,
|
|
1602
|
+
remainingCapacity: Math.max.apply(Math, _toConsumableArray(availableOptions.map(function (option) {
|
|
1603
|
+
return option.remainingCapacity || 0;
|
|
1604
|
+
})).concat([0])),
|
|
1605
|
+
maxCapacity: Math.max.apply(Math, _toConsumableArray(availableOptions.map(function (option) {
|
|
1606
|
+
return option.maxCapacity || 0;
|
|
1607
|
+
})).concat([0])),
|
|
1608
|
+
reasonCodes: Array.from(new Set(options.flatMap(function (option) {
|
|
1609
|
+
return option.reasonCodes;
|
|
1610
|
+
}))),
|
|
1611
|
+
options: options
|
|
784
1612
|
};
|
|
785
1613
|
});
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
1614
|
+
var requiredGroups = requirementGroups.filter(function (group) {
|
|
1615
|
+
return group.required;
|
|
1616
|
+
});
|
|
1617
|
+
var requiredStatuses = requiredGroups.map(function (group) {
|
|
1618
|
+
return group.status;
|
|
1619
|
+
});
|
|
1620
|
+
var gate = evaluateCandidateTimeGate({
|
|
1621
|
+
product: product,
|
|
1622
|
+
candidateRange: candidateRange,
|
|
1623
|
+
evaluationContext: evaluationContext,
|
|
1624
|
+
timezoneName: projection.meta.timezone
|
|
1625
|
+
});
|
|
1626
|
+
var candidateStatus = requiredGroups.length ? summarizeStatuses(requiredStatuses) : candidateRange.endMs <= evaluationContext.evaluatedAtMs ? 'past' : 'available';
|
|
1627
|
+
var maxPartySize = requiredGroups.length ? requiredGroups.reduce(function (min, group) {
|
|
1628
|
+
return Math.min(min, group.maxPartySize);
|
|
1629
|
+
}, Number.MAX_SAFE_INTEGER) : 0;
|
|
1630
|
+
var selectedAssignments = requirementGroups.flatMap(function (group) {
|
|
1631
|
+
var _group$formId;
|
|
1632
|
+
var selection = selectionsByGroupKey.get(group.groupId) || selectionsByGroupKey.get(String((_group$formId = group.formId) !== null && _group$formId !== void 0 ? _group$formId : ''));
|
|
1633
|
+
if (!selection && !(request.allowImplicitSelection && group.required)) return [];
|
|
1634
|
+
var availableOptions = group.options.filter(function (option) {
|
|
1635
|
+
return option.status === 'available' || option.status === 'limited';
|
|
1636
|
+
});
|
|
1637
|
+
var selectedOptions = selection ? availableOptions.slice(0, group.max) : availableOptions.slice(0, Math.max(group.min, 1));
|
|
1638
|
+
var originalGroup = (product.requirementGroups || []).find(function (item) {
|
|
1639
|
+
return item.id === group.groupId;
|
|
1640
|
+
}) || {
|
|
1641
|
+
id: group.groupId,
|
|
1642
|
+
resourceType: group.resourceType,
|
|
1643
|
+
min: group.min,
|
|
1644
|
+
max: group.max
|
|
1645
|
+
};
|
|
1646
|
+
return selectedOptions.map(function (selectedOption) {
|
|
1647
|
+
var _candidate$primarySch, _candidate$primarySch2;
|
|
799
1648
|
return {
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
1649
|
+
productId: product.id,
|
|
1650
|
+
resourceId: selectedOption.resourceId,
|
|
1651
|
+
formId: group.formId,
|
|
1652
|
+
startAt: candidate.bookingStartAt || candidate.startAt,
|
|
1653
|
+
endAt: candidate.bookingEndAt || candidate.endAt,
|
|
1654
|
+
capacityRequired: resolveRequiredCapacity(originalGroup, product, requestedPartySize),
|
|
1655
|
+
scheduleId: (_candidate$primarySch = candidate.primaryScheduleRef) === null || _candidate$primarySch === void 0 ? void 0 : _candidate$primarySch.scheduleId,
|
|
1656
|
+
timeSlotId: (_candidate$primarySch2 = candidate.primaryScheduleRef) === null || _candidate$primarySch2 === void 0 ? void 0 : _candidate$primarySch2.timeSlotId
|
|
807
1657
|
};
|
|
1658
|
+
});
|
|
1659
|
+
});
|
|
1660
|
+
return {
|
|
1661
|
+
item: _objectSpread(_objectSpread({}, candidate), {}, {
|
|
1662
|
+
status: (gate === null || gate === void 0 ? void 0 : gate.status) || candidateStatus,
|
|
1663
|
+
maxPartySize: gate ? 0 : Number.isFinite(maxPartySize) ? maxPartySize : 0,
|
|
1664
|
+
reasonCodes: (gate === null || gate === void 0 ? void 0 : gate.reasonCodes) || Array.from(new Set(requiredGroups.flatMap(function (group) {
|
|
1665
|
+
return group.reasonCodes;
|
|
1666
|
+
}))),
|
|
1667
|
+
requirementGroups: requirementGroups
|
|
808
1668
|
}),
|
|
809
|
-
assignments:
|
|
810
|
-
|
|
1669
|
+
assignments: gate ? [] : selectedAssignments,
|
|
1670
|
+
gate: gate || undefined
|
|
811
1671
|
};
|
|
812
1672
|
}
|
|
813
|
-
function
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1673
|
+
function assertTimeCandidates(request) {
|
|
1674
|
+
if (request.target === 'time' && (!Array.isArray(request.candidates) || request.candidates.length === 0)) {
|
|
1675
|
+
throw availabilityError('MISSING_TIME_CANDIDATES', 'target=time 必须传非空 candidates', {
|
|
1676
|
+
request: request
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
function assertSelectedRanges(request) {
|
|
1681
|
+
if (request.target === 'resource' && (!Array.isArray(request.selectedRanges) || request.selectedRanges.length === 0)) {
|
|
1682
|
+
throw availabilityError('MISSING_SELECTED_RANGE', 'target=resource 必须传非空 selectedRanges', {
|
|
1683
|
+
request: request
|
|
1684
|
+
});
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
function assertRangesWithinProjection(projection, ranges) {
|
|
1688
|
+
var coverage = {
|
|
1689
|
+
start: toDateTime(projection.meta.coverage.startAt, projection.meta.timezone),
|
|
1690
|
+
end: toDateTime(projection.meta.coverage.endAt, projection.meta.timezone)
|
|
1691
|
+
};
|
|
1692
|
+
ranges.forEach(function (range) {
|
|
1693
|
+
var displayRange = normalizeAbsoluteRange(range, projection.meta.timezone);
|
|
1694
|
+
var bookingRange = normalizeAbsoluteRange(getCandidateBookingRange(range), projection.meta.timezone);
|
|
1695
|
+
if (!displayRange || !bookingRange) {
|
|
1696
|
+
throw availabilityError('INVALID_TARGET_FILTER', '候选时间范围非法', {
|
|
1697
|
+
range: range
|
|
1698
|
+
});
|
|
1699
|
+
}
|
|
1700
|
+
if (!contains(coverage, displayRange) || !contains(coverage, bookingRange)) {
|
|
1701
|
+
throw availabilityError('OUT_OF_PROJECTION_COVERAGE', '候选时间范围超出 projection.coverage', {
|
|
1702
|
+
range: range,
|
|
1703
|
+
coverage: projection.meta.coverage
|
|
836
1704
|
});
|
|
837
|
-
return slot ? [slot] : [];
|
|
838
1705
|
}
|
|
839
|
-
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1708
|
+
export function queryAvailabilityProjection(projection, request) {
|
|
1709
|
+
var _extractFixedOffsetMi6, _request$productIds2;
|
|
1710
|
+
var normalizedPartySize = normalizePartySize(request.partySize);
|
|
1711
|
+
var evaluatedAt = formatDateTime(localDateToScalar(new Date()), projection.meta.timezone, (_extractFixedOffsetMi6 = extractFixedOffsetMinutes(projection.meta.now)) !== null && _extractFixedOffsetMi6 !== void 0 ? _extractFixedOffsetMi6 : captureRuntimeOffsetMinutes());
|
|
1712
|
+
if (request.target === 'time') {
|
|
1713
|
+
assertTimeCandidates(request);
|
|
1714
|
+
assertRangesWithinProjection(projection, request.candidates);
|
|
1715
|
+
}
|
|
1716
|
+
if (request.target === 'resource') {
|
|
1717
|
+
assertSelectedRanges(request);
|
|
1718
|
+
assertRangesWithinProjection(projection, request.selectedRanges);
|
|
840
1719
|
}
|
|
841
|
-
var
|
|
842
|
-
var
|
|
843
|
-
var
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
1720
|
+
var productIdSet = (_request$productIds2 = request.productIds) !== null && _request$productIds2 !== void 0 && _request$productIds2.length ? new Set(request.productIds.map(String)) : null;
|
|
1721
|
+
var productIndex = buildProductIndex(projection);
|
|
1722
|
+
var cellIndex = buildAvailabilityCellIndex(projection);
|
|
1723
|
+
var groupResourceIndex = buildGroupResourceIndex({
|
|
1724
|
+
projection: projection,
|
|
1725
|
+
resourceIds: request.resourceIds,
|
|
1726
|
+
partySize: normalizedPartySize
|
|
1727
|
+
});
|
|
1728
|
+
var candidateRangeCache = new Map();
|
|
1729
|
+
var evaluationContext = buildEvaluationContext({
|
|
1730
|
+
projection: projection,
|
|
1731
|
+
evaluatedAt: evaluatedAt,
|
|
1732
|
+
resourceIds: request.resourceIds
|
|
1733
|
+
});
|
|
1734
|
+
if (request.target === 'time') {
|
|
1735
|
+
var candidates = dedupeProductRanges(request.candidates);
|
|
1736
|
+
var candidatesByProduct = new Map();
|
|
1737
|
+
candidates.forEach(function (candidate) {
|
|
1738
|
+
var key = String(candidate.productId);
|
|
1739
|
+
candidatesByProduct.set(key, [].concat(_toConsumableArray(candidatesByProduct.get(key) || []), [candidate]));
|
|
1740
|
+
});
|
|
1741
|
+
var _data = projection.products.filter(function (product) {
|
|
1742
|
+
return !productIdSet || productIdSet.has(String(product.id));
|
|
1743
|
+
}).map(function (product) {
|
|
1744
|
+
return {
|
|
1745
|
+
productId: product.id,
|
|
1746
|
+
title: product.title,
|
|
1747
|
+
times: (candidatesByProduct.get(String(product.id)) || []).map(function (candidate) {
|
|
1748
|
+
return evaluateCandidate(projection, candidate, {
|
|
1749
|
+
partySize: normalizedPartySize,
|
|
1750
|
+
resourceIds: request.resourceIds,
|
|
1751
|
+
evaluatedAt: evaluatedAt,
|
|
1752
|
+
cellIndex: cellIndex,
|
|
1753
|
+
productIndex: productIndex,
|
|
1754
|
+
groupResourceIndex: groupResourceIndex,
|
|
1755
|
+
candidateRangeCache: candidateRangeCache,
|
|
1756
|
+
evaluationContext: evaluationContext
|
|
1757
|
+
}).item;
|
|
1758
|
+
})
|
|
1759
|
+
};
|
|
1760
|
+
}).filter(function (group) {
|
|
1761
|
+
return group.times.length > 0;
|
|
851
1762
|
});
|
|
852
|
-
|
|
853
|
-
|
|
1763
|
+
return {
|
|
1764
|
+
target: 'time',
|
|
1765
|
+
evaluatedAt: evaluatedAt,
|
|
1766
|
+
snapshotId: projection.meta.snapshotId,
|
|
1767
|
+
data: _data
|
|
1768
|
+
};
|
|
854
1769
|
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1770
|
+
if (request.target === 'resource') {
|
|
1771
|
+
var selectedRanges = dedupeProductRanges(request.selectedRanges);
|
|
1772
|
+
var rangesByProduct = new Map();
|
|
1773
|
+
selectedRanges.forEach(function (candidate) {
|
|
1774
|
+
var key = String(candidate.productId);
|
|
1775
|
+
rangesByProduct.set(key, [].concat(_toConsumableArray(rangesByProduct.get(key) || []), [candidate]));
|
|
1776
|
+
});
|
|
1777
|
+
var _data2 = projection.products.filter(function (product) {
|
|
1778
|
+
return !productIdSet || productIdSet.has(String(product.id));
|
|
1779
|
+
}).map(function (product) {
|
|
1780
|
+
return {
|
|
1781
|
+
productId: product.id,
|
|
1782
|
+
title: product.title,
|
|
1783
|
+
ranges: (rangesByProduct.get(String(product.id)) || []).map(function (candidate) {
|
|
1784
|
+
var evaluated = evaluateCandidate(projection, candidate, {
|
|
1785
|
+
partySize: normalizedPartySize,
|
|
1786
|
+
resourceIds: request.resourceIds,
|
|
1787
|
+
evaluatedAt: evaluatedAt,
|
|
1788
|
+
cellIndex: cellIndex,
|
|
1789
|
+
productIndex: productIndex,
|
|
1790
|
+
groupResourceIndex: groupResourceIndex,
|
|
1791
|
+
candidateRangeCache: candidateRangeCache,
|
|
1792
|
+
evaluationContext: evaluationContext
|
|
1793
|
+
}).item;
|
|
1794
|
+
return {
|
|
1795
|
+
range: candidate,
|
|
1796
|
+
status: evaluated.status,
|
|
1797
|
+
maxPartySize: evaluated.maxPartySize,
|
|
1798
|
+
reasonCodes: evaluated.reasonCodes,
|
|
1799
|
+
requirementGroups: evaluated.requirementGroups
|
|
1800
|
+
};
|
|
1801
|
+
})
|
|
1802
|
+
};
|
|
1803
|
+
}).filter(function (group) {
|
|
1804
|
+
return group.ranges.length > 0;
|
|
882
1805
|
});
|
|
1806
|
+
return {
|
|
1807
|
+
target: 'resource',
|
|
1808
|
+
evaluatedAt: evaluatedAt,
|
|
1809
|
+
snapshotId: projection.meta.snapshotId,
|
|
1810
|
+
data: _data2
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1813
|
+
var dateRange = resolveProjectionDateRange(projection, request.dateRange);
|
|
1814
|
+
var candidateGroups = getProductTimeRanges(projection, {
|
|
1815
|
+
productIds: request.productIds,
|
|
1816
|
+
dateRange: dateRange,
|
|
1817
|
+
resourceIds: request.resourceIds,
|
|
1818
|
+
partySize: normalizedPartySize
|
|
883
1819
|
});
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
message: 'duration 商品需要先确定开始时间后才能自动分配资源',
|
|
909
|
-
productId: interval.productId
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
return 0; // continue
|
|
913
|
-
}
|
|
914
|
-
var contextWithAssignments = withAssignmentEvents(baseContext, assignments);
|
|
915
|
-
var candidateResources = getQueryResources(contextWithAssignments.resources, query).filter(function (resource) {
|
|
916
|
-
var requirements = interval.resourceRequirements || [];
|
|
917
|
-
if (!requirements.length) return true;
|
|
918
|
-
return requirements.some(function (requirement) {
|
|
919
|
-
return matchesResourceRequirement(resource, requirement);
|
|
920
|
-
});
|
|
921
|
-
});
|
|
922
|
-
var availableCell = candidateResources.map(function (resource) {
|
|
923
|
-
return evaluateResourceForInterval({
|
|
924
|
-
resource: resource,
|
|
925
|
-
interval: interval,
|
|
926
|
-
context: contextWithAssignments
|
|
927
|
-
});
|
|
928
|
-
}).find(function (cell) {
|
|
929
|
-
return cell.status === 'available' || cell.status === 'limited';
|
|
1820
|
+
var candidateGroupMap = new Map(candidateGroups.map(function (group) {
|
|
1821
|
+
return [String(group.productId), group];
|
|
1822
|
+
}));
|
|
1823
|
+
if (request.target === 'date') {
|
|
1824
|
+
var _data3 = projection.products.filter(function (product) {
|
|
1825
|
+
return !productIdSet || productIdSet.has(String(product.id));
|
|
1826
|
+
}).map(function (product) {
|
|
1827
|
+
var group = candidateGroupMap.get(String(product.id)) || {
|
|
1828
|
+
productId: product.id,
|
|
1829
|
+
title: product.title,
|
|
1830
|
+
kind: product.kind,
|
|
1831
|
+
ranges: []
|
|
1832
|
+
};
|
|
1833
|
+
var byDate = new Map();
|
|
1834
|
+
group.ranges.forEach(function (candidate) {
|
|
1835
|
+
var evaluated = evaluateCandidateSummary(projection, candidate, {
|
|
1836
|
+
partySize: normalizedPartySize,
|
|
1837
|
+
resourceIds: request.resourceIds,
|
|
1838
|
+
evaluatedAt: evaluatedAt,
|
|
1839
|
+
cellIndex: cellIndex,
|
|
1840
|
+
productIndex: productIndex,
|
|
1841
|
+
groupResourceIndex: groupResourceIndex,
|
|
1842
|
+
candidateRangeCache: candidateRangeCache,
|
|
1843
|
+
evaluationContext: evaluationContext
|
|
930
1844
|
});
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1845
|
+
var aggregate = byDate.get(candidate.date) || {
|
|
1846
|
+
availableCount: 0,
|
|
1847
|
+
totalCount: 0,
|
|
1848
|
+
maxPartySize: 0,
|
|
1849
|
+
remainingCapacity: 0,
|
|
1850
|
+
maxCapacity: 0,
|
|
1851
|
+
reasonCodeSet: new Set(),
|
|
1852
|
+
statusAggregate: createStatusAggregate()
|
|
1853
|
+
};
|
|
1854
|
+
aggregate.totalCount += 1;
|
|
1855
|
+
if (evaluated.status === 'available' || evaluated.status === 'limited') {
|
|
1856
|
+
aggregate.availableCount += 1;
|
|
938
1857
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1858
|
+
aggregate.maxPartySize = Math.max(aggregate.maxPartySize, evaluated.maxPartySize);
|
|
1859
|
+
aggregate.remainingCapacity += evaluated.remainingCapacity;
|
|
1860
|
+
aggregate.maxCapacity += evaluated.maxCapacity;
|
|
1861
|
+
pushStatusAggregate(aggregate.statusAggregate, evaluated.status);
|
|
1862
|
+
evaluated.reasonCodes.forEach(function (reasonCode) {
|
|
1863
|
+
return aggregate.reasonCodeSet.add(reasonCode);
|
|
945
1864
|
});
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1865
|
+
byDate.set(candidate.date, aggregate);
|
|
1866
|
+
});
|
|
1867
|
+
var dates = enumerateDates(dateRange.startDate, dateRange.endDate).map(function (date) {
|
|
1868
|
+
var aggregate = byDate.get(date);
|
|
1869
|
+
return {
|
|
1870
|
+
date: date,
|
|
1871
|
+
status: aggregate ? summarizeStatusAggregate(aggregate.statusAggregate) : 'unavailable',
|
|
1872
|
+
availableCount: (aggregate === null || aggregate === void 0 ? void 0 : aggregate.availableCount) || 0,
|
|
1873
|
+
totalCount: (aggregate === null || aggregate === void 0 ? void 0 : aggregate.totalCount) || 0,
|
|
1874
|
+
maxPartySize: (aggregate === null || aggregate === void 0 ? void 0 : aggregate.maxPartySize) || 0,
|
|
1875
|
+
remainingCapacity: (aggregate === null || aggregate === void 0 ? void 0 : aggregate.remainingCapacity) || 0,
|
|
1876
|
+
maxCapacity: (aggregate === null || aggregate === void 0 ? void 0 : aggregate.maxCapacity) || 0,
|
|
1877
|
+
reasonCodes: aggregate ? Array.from(aggregate.reasonCodeSet) : []
|
|
1878
|
+
};
|
|
1879
|
+
});
|
|
1880
|
+
return {
|
|
1881
|
+
productId: product.id,
|
|
1882
|
+
title: product.title,
|
|
1883
|
+
dates: dates
|
|
1884
|
+
};
|
|
1885
|
+
});
|
|
1886
|
+
return {
|
|
1887
|
+
target: 'date',
|
|
1888
|
+
evaluatedAt: evaluatedAt,
|
|
1889
|
+
snapshotId: projection.meta.snapshotId,
|
|
1890
|
+
data: _data3
|
|
1891
|
+
};
|
|
956
1892
|
}
|
|
1893
|
+
var data = projection.products.filter(function (product) {
|
|
1894
|
+
return !productIdSet || productIdSet.has(String(product.id));
|
|
1895
|
+
}).map(function (product) {
|
|
1896
|
+
var group = candidateGroupMap.get(String(product.id)) || {
|
|
1897
|
+
productId: product.id,
|
|
1898
|
+
title: product.title,
|
|
1899
|
+
kind: product.kind,
|
|
1900
|
+
ranges: []
|
|
1901
|
+
};
|
|
1902
|
+
var productStatusAggregate = createStatusAggregate();
|
|
1903
|
+
var productReasonCodeSet = new Set();
|
|
1904
|
+
var dateStatusMap = new Map();
|
|
1905
|
+
var availableTimeCount = 0;
|
|
1906
|
+
var totalTimeCount = 0;
|
|
1907
|
+
var maxPartySize = 0;
|
|
1908
|
+
group.ranges.forEach(function (candidate) {
|
|
1909
|
+
var evaluated = evaluateCandidateSummary(projection, candidate, {
|
|
1910
|
+
partySize: normalizedPartySize,
|
|
1911
|
+
resourceIds: request.resourceIds,
|
|
1912
|
+
evaluatedAt: evaluatedAt,
|
|
1913
|
+
cellIndex: cellIndex,
|
|
1914
|
+
productIndex: productIndex,
|
|
1915
|
+
groupResourceIndex: groupResourceIndex,
|
|
1916
|
+
candidateRangeCache: candidateRangeCache,
|
|
1917
|
+
evaluationContext: evaluationContext
|
|
1918
|
+
});
|
|
1919
|
+
totalTimeCount += 1;
|
|
1920
|
+
if (evaluated.status === 'available' || evaluated.status === 'limited') {
|
|
1921
|
+
availableTimeCount += 1;
|
|
1922
|
+
}
|
|
1923
|
+
maxPartySize = Math.max(maxPartySize, evaluated.maxPartySize);
|
|
1924
|
+
pushStatusAggregate(productStatusAggregate, evaluated.status);
|
|
1925
|
+
evaluated.reasonCodes.forEach(function (reasonCode) {
|
|
1926
|
+
return productReasonCodeSet.add(reasonCode);
|
|
1927
|
+
});
|
|
1928
|
+
var dateAggregate = dateStatusMap.get(candidate.date) || createStatusAggregate();
|
|
1929
|
+
pushStatusAggregate(dateAggregate, evaluated.status);
|
|
1930
|
+
dateStatusMap.set(candidate.date, dateAggregate);
|
|
1931
|
+
});
|
|
1932
|
+
var availableDateCount = Array.from(dateStatusMap.values()).filter(function (aggregate) {
|
|
1933
|
+
var status = summarizeStatusAggregate(aggregate);
|
|
1934
|
+
return status === 'available' || status === 'limited';
|
|
1935
|
+
}).length;
|
|
1936
|
+
var totalDateCount = dateStatusMap.size;
|
|
1937
|
+
var status = summarizeStatusAggregate(productStatusAggregate);
|
|
1938
|
+
var item = {
|
|
1939
|
+
productId: group.productId,
|
|
1940
|
+
title: group.title,
|
|
1941
|
+
status: status,
|
|
1942
|
+
reasonCodes: Array.from(productReasonCodeSet),
|
|
1943
|
+
availableDateCount: availableDateCount,
|
|
1944
|
+
totalDateCount: totalDateCount,
|
|
1945
|
+
availableTimeCount: availableTimeCount,
|
|
1946
|
+
totalTimeCount: totalTimeCount,
|
|
1947
|
+
maxPartySize: maxPartySize
|
|
1948
|
+
};
|
|
1949
|
+
return {
|
|
1950
|
+
productId: product.id,
|
|
1951
|
+
title: product.title,
|
|
1952
|
+
item: item
|
|
1953
|
+
};
|
|
1954
|
+
});
|
|
957
1955
|
return {
|
|
958
|
-
|
|
959
|
-
|
|
1956
|
+
target: 'product',
|
|
1957
|
+
evaluatedAt: evaluatedAt,
|
|
1958
|
+
snapshotId: projection.meta.snapshotId,
|
|
1959
|
+
data: data
|
|
960
1960
|
};
|
|
961
1961
|
}
|
|
962
|
-
function
|
|
963
|
-
if (!
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1962
|
+
function summarizeDateStatus(statuses) {
|
|
1963
|
+
if (!statuses.length) return 'unavailable';
|
|
1964
|
+
if (statuses.some(function (status) {
|
|
1965
|
+
return status === 'available';
|
|
1966
|
+
})) return 'available';
|
|
1967
|
+
if (statuses.some(function (status) {
|
|
1968
|
+
return status === 'limited';
|
|
1969
|
+
})) return 'limited';
|
|
1970
|
+
if (statuses.some(function (status) {
|
|
1971
|
+
return status === 'full';
|
|
1972
|
+
})) return 'full';
|
|
1973
|
+
if (statuses.every(function (status) {
|
|
1974
|
+
return status === 'past';
|
|
1975
|
+
})) return 'past';
|
|
1976
|
+
return 'unavailable';
|
|
975
1977
|
}
|
|
976
|
-
|
|
977
|
-
if (
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
}
|
|
1027
|
-
var priorContext = withAssignmentEvents(context, selection.assignments.slice(0, index));
|
|
1028
|
-
var selfConflictCell = evaluateResourceForInterval({
|
|
1029
|
-
resource: resource,
|
|
1030
|
-
interval: interval,
|
|
1031
|
-
context: priorContext
|
|
1032
|
-
});
|
|
1033
|
-
if (selfConflictCell.status !== 'available' && selfConflictCell.status !== 'limited') {
|
|
1034
|
-
_conflicts.push.apply(_conflicts, _toConsumableArray(selfConflictCell.conflicts.map(function (conflict) {
|
|
1035
|
-
return _objectSpread(_objectSpread({}, conflict), {}, {
|
|
1036
|
-
type: 'cart_overlap',
|
|
1037
|
-
message: '本次选择中存在资源时间冲突'
|
|
1038
|
-
});
|
|
1039
|
-
})));
|
|
1040
|
-
}
|
|
1041
|
-
},
|
|
1042
|
-
_ret3;
|
|
1043
|
-
for (var index = 0; index < selection.assignments.length; index += 1) {
|
|
1044
|
-
_ret3 = _loop4();
|
|
1045
|
-
if (_ret3 === 0) continue;
|
|
1046
|
-
}
|
|
1978
|
+
function summarizeProductStatus(statuses) {
|
|
1979
|
+
if (!statuses.length) return 'unavailable';
|
|
1980
|
+
if (statuses.some(function (status) {
|
|
1981
|
+
return status === 'available';
|
|
1982
|
+
})) return 'available';
|
|
1983
|
+
if (statuses.some(function (status) {
|
|
1984
|
+
return status === 'limited';
|
|
1985
|
+
})) return 'limited';
|
|
1986
|
+
if (statuses.some(function (status) {
|
|
1987
|
+
return status === 'full';
|
|
1988
|
+
})) return 'full';
|
|
1989
|
+
if (statuses.every(function (status) {
|
|
1990
|
+
return status === 'past';
|
|
1991
|
+
})) return 'past';
|
|
1992
|
+
return 'unavailable';
|
|
1993
|
+
}
|
|
1994
|
+
export function validateAvailabilitySelection(projection, params) {
|
|
1995
|
+
var _extractFixedOffsetMi7;
|
|
1996
|
+
var normalizedPartySize = normalizePartySize(params.partySize);
|
|
1997
|
+
var normalizedBookingCount = normalizeBookingCount(params.bookingCount);
|
|
1998
|
+
assertRangesWithinProjection(projection, [params.candidate]);
|
|
1999
|
+
var productIndex = buildProductIndex(projection);
|
|
2000
|
+
var evaluatedAt = formatDateTime(localDateToScalar(new Date()), projection.meta.timezone, (_extractFixedOffsetMi7 = extractFixedOffsetMinutes(projection.meta.now)) !== null && _extractFixedOffsetMi7 !== void 0 ? _extractFixedOffsetMi7 : captureRuntimeOffsetMinutes());
|
|
2001
|
+
var evaluated = evaluateCandidate(projection, params.candidate, {
|
|
2002
|
+
partySize: normalizedPartySize,
|
|
2003
|
+
evaluatedAt: evaluatedAt,
|
|
2004
|
+
requirementSelections: params.requirementSelections,
|
|
2005
|
+
allowImplicitSelection: false,
|
|
2006
|
+
cellIndex: buildAvailabilityCellIndex(projection),
|
|
2007
|
+
productIndex: productIndex,
|
|
2008
|
+
groupResourceIndex: buildGroupResourceIndex({
|
|
2009
|
+
projection: projection,
|
|
2010
|
+
partySize: normalizedPartySize
|
|
2011
|
+
}),
|
|
2012
|
+
candidateRangeCache: new Map(),
|
|
2013
|
+
evaluationContext: buildEvaluationContext({
|
|
2014
|
+
projection: projection,
|
|
2015
|
+
evaluatedAt: evaluatedAt,
|
|
2016
|
+
requirementSelections: params.requirementSelections
|
|
2017
|
+
})
|
|
2018
|
+
});
|
|
2019
|
+
var conflicts = [];
|
|
2020
|
+
if (evaluated.gate) {
|
|
2021
|
+
conflicts.push({
|
|
2022
|
+
code: evaluated.gate.conflictCode,
|
|
2023
|
+
message: evaluated.gate.status === 'past' ? '候选时间已过期' : '候选时间不满足预订时间门禁',
|
|
2024
|
+
reasonCodes: evaluated.gate.reasonCodes,
|
|
2025
|
+
productId: params.candidate.productId,
|
|
2026
|
+
candidateKey: params.candidate.key
|
|
2027
|
+
});
|
|
1047
2028
|
return {
|
|
1048
|
-
ok:
|
|
1049
|
-
|
|
2029
|
+
ok: false,
|
|
2030
|
+
status: evaluated.gate.status === 'past' ? 'past' : 'conflict',
|
|
2031
|
+
assignments: [],
|
|
2032
|
+
conflicts: conflicts,
|
|
2033
|
+
availability: evaluated.item
|
|
1050
2034
|
};
|
|
1051
2035
|
}
|
|
1052
|
-
var
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
2036
|
+
var requiredGroups = evaluated.item.requirementGroups.filter(function (group) {
|
|
2037
|
+
return group.required;
|
|
2038
|
+
});
|
|
2039
|
+
var findSelectionForGroup = function findSelectionForGroup(group) {
|
|
2040
|
+
return params.requirementSelections.find(function (selection) {
|
|
2041
|
+
return selection.requirementGroupId && selection.requirementGroupId === group.groupId || selection.formId !== undefined && sameId(selection.formId, group.formId);
|
|
2042
|
+
});
|
|
2043
|
+
};
|
|
2044
|
+
requiredGroups.forEach(function (group) {
|
|
2045
|
+
var selection = findSelectionForGroup(group);
|
|
2046
|
+
var selectedCount = new Set(((selection === null || selection === void 0 ? void 0 : selection.resourceIds) || []).map(String)).size;
|
|
2047
|
+
if (selectedCount < group.min) {
|
|
2048
|
+
conflicts.push({
|
|
2049
|
+
code: 'missing_resource',
|
|
2050
|
+
message: "\u8D44\u6E90\u7EC4 ".concat(group.groupId, " \u7F3A\u5C11\u5DF2\u9009\u8D44\u6E90"),
|
|
2051
|
+
productId: params.candidate.productId,
|
|
2052
|
+
requirementGroupId: group.groupId,
|
|
2053
|
+
formId: group.formId,
|
|
2054
|
+
candidateKey: params.candidate.key
|
|
2055
|
+
});
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2058
|
+
if (selectedCount > group.max) {
|
|
2059
|
+
conflicts.push({
|
|
2060
|
+
code: 'invalid_selection_count',
|
|
2061
|
+
message: "\u8D44\u6E90\u7EC4 ".concat(group.groupId, " \u5DF2\u9009\u8D44\u6E90\u6570\u91CF\u8D85\u51FA\u4E0A\u9650"),
|
|
2062
|
+
productId: params.candidate.productId,
|
|
2063
|
+
requirementGroupId: group.groupId,
|
|
2064
|
+
formId: group.formId,
|
|
2065
|
+
candidateKey: params.candidate.key
|
|
2066
|
+
});
|
|
2067
|
+
return;
|
|
2068
|
+
}
|
|
2069
|
+
if (group.status === 'available' || group.status === 'limited') return;
|
|
2070
|
+
conflicts.push({
|
|
2071
|
+
code: group.status === 'past' ? 'past' : group.status === 'full' ? 'resource_full' : 'missing_resource',
|
|
2072
|
+
message: "\u8D44\u6E90\u7EC4 ".concat(group.groupId, " \u5F53\u524D\u4E0D\u53EF\u63D0\u4EA4"),
|
|
2073
|
+
productId: params.candidate.productId,
|
|
2074
|
+
requirementGroupId: group.groupId,
|
|
2075
|
+
formId: group.formId,
|
|
2076
|
+
candidateKey: params.candidate.key
|
|
2077
|
+
});
|
|
1059
2078
|
});
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
2079
|
+
params.requirementSelections.forEach(function (selection) {
|
|
2080
|
+
var matchedGroup = evaluated.item.requirementGroups.find(function (group) {
|
|
2081
|
+
return selection.requirementGroupId ? group.groupId === selection.requirementGroupId : sameId(group.formId, selection.formId);
|
|
2082
|
+
});
|
|
2083
|
+
if (!matchedGroup) {
|
|
2084
|
+
conflicts.push({
|
|
2085
|
+
code: 'invalid_requirement_group',
|
|
2086
|
+
message: '选择的资源组不属于当前商品',
|
|
2087
|
+
productId: params.candidate.productId,
|
|
2088
|
+
requirementGroupId: selection.requirementGroupId,
|
|
2089
|
+
formId: selection.formId,
|
|
2090
|
+
candidateKey: params.candidate.key
|
|
2091
|
+
});
|
|
2092
|
+
return;
|
|
2093
|
+
}
|
|
2094
|
+
var selectedCount = new Set(selection.resourceIds.map(String)).size;
|
|
2095
|
+
if (!matchedGroup.required && (selectedCount < matchedGroup.min || selectedCount > matchedGroup.max)) {
|
|
2096
|
+
conflicts.push({
|
|
2097
|
+
code: 'invalid_selection_count',
|
|
2098
|
+
message: "\u8D44\u6E90\u7EC4 ".concat(matchedGroup.groupId, " \u5DF2\u9009\u8D44\u6E90\u6570\u91CF\u4E0D\u6EE1\u8DB3 ").concat(matchedGroup.min, "-").concat(matchedGroup.max),
|
|
2099
|
+
productId: params.candidate.productId,
|
|
2100
|
+
requirementGroupId: matchedGroup.groupId,
|
|
2101
|
+
formId: matchedGroup.formId,
|
|
2102
|
+
candidateKey: params.candidate.key
|
|
2103
|
+
});
|
|
2104
|
+
}
|
|
2105
|
+
selection.resourceIds.forEach(function (resourceId) {
|
|
2106
|
+
var option = matchedGroup === null || matchedGroup === void 0 ? void 0 : matchedGroup.options.find(function (item) {
|
|
2107
|
+
return sameId(item.resourceId, resourceId);
|
|
2108
|
+
});
|
|
2109
|
+
var matchedCell = option === null || option === void 0 ? void 0 : option.matchedCell;
|
|
2110
|
+
var remainingCapacity = matchedCell === null || matchedCell === void 0 ? void 0 : matchedCell.remainingCapacity;
|
|
2111
|
+
if (option && (option.status === 'available' || option.status === 'limited') && remainingCapacity !== undefined) {
|
|
2112
|
+
var capacityPerBooking = normalizedPartySize !== null && normalizedPartySize !== void 0 ? normalizedPartySize : 1;
|
|
2113
|
+
var availableQuantity = Math.max(0, Math.floor(remainingCapacity / capacityPerBooking));
|
|
2114
|
+
if (normalizedBookingCount > availableQuantity) {
|
|
2115
|
+
conflicts.push({
|
|
2116
|
+
code: 'resource_capacity_exceeded',
|
|
2117
|
+
message: "\u6240\u9009\u8D44\u6E90\u300C".concat(option.resourceName || String(resourceId), "\u300D\u5F53\u524D\u6700\u591A\u53EF\u9884\u8BA2 ").concat(availableQuantity, " \u4E2A"),
|
|
2118
|
+
productId: params.candidate.productId,
|
|
2119
|
+
requirementGroupId: matchedGroup === null || matchedGroup === void 0 ? void 0 : matchedGroup.groupId,
|
|
2120
|
+
formId: matchedGroup === null || matchedGroup === void 0 ? void 0 : matchedGroup.formId,
|
|
2121
|
+
resourceId: resourceId,
|
|
2122
|
+
resourceName: option.resourceName,
|
|
2123
|
+
candidateKey: params.candidate.key,
|
|
2124
|
+
requestedQuantity: normalizedBookingCount,
|
|
2125
|
+
availableQuantity: availableQuantity,
|
|
2126
|
+
remainingCapacity: remainingCapacity,
|
|
2127
|
+
capacityPerBooking: capacityPerBooking
|
|
2128
|
+
});
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
if (!option || option.status !== 'available' && option.status !== 'limited') {
|
|
2133
|
+
conflicts.push({
|
|
2134
|
+
code: 'resource_unavailable',
|
|
2135
|
+
message: "\u8D44\u6E90 ".concat(String(resourceId), " \u5F53\u524D\u4E0D\u53EF\u63D0\u4EA4"),
|
|
2136
|
+
productId: params.candidate.productId,
|
|
2137
|
+
requirementGroupId: matchedGroup === null || matchedGroup === void 0 ? void 0 : matchedGroup.groupId,
|
|
2138
|
+
formId: matchedGroup === null || matchedGroup === void 0 ? void 0 : matchedGroup.formId,
|
|
2139
|
+
resourceId: resourceId,
|
|
2140
|
+
candidateKey: params.candidate.key
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
});
|
|
1064
2144
|
});
|
|
1065
2145
|
return {
|
|
1066
|
-
ok: conflicts.length === 0,
|
|
1067
|
-
conflicts:
|
|
2146
|
+
ok: conflicts.length === 0 && (evaluated.item.status === 'available' || evaluated.item.status === 'limited'),
|
|
2147
|
+
status: conflicts.length === 0 ? evaluated.item.status : 'conflict',
|
|
2148
|
+
assignments: evaluated.assignments,
|
|
2149
|
+
conflicts: conflicts,
|
|
2150
|
+
availability: evaluated.item
|
|
1068
2151
|
};
|
|
1069
2152
|
}
|