@pisell/pisellos 2.3.43 → 2.3.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/dist/modules/Discount/index.d.ts +0 -1
  2. package/dist/modules/Discount/index.js +11 -40
  3. package/dist/modules/Discount/types.d.ts +0 -1
  4. package/dist/modules/OpenData/index.js +2 -2
  5. package/dist/modules/Order/index.d.ts +2 -3
  6. package/dist/modules/Order/index.js +98 -82
  7. package/dist/modules/Order/types.d.ts +2 -9
  8. package/dist/modules/Order/utils.d.ts +0 -10
  9. package/dist/modules/Order/utils.js +3 -35
  10. package/dist/modules/Payment/walletpass.d.ts +10 -3
  11. package/dist/modules/Payment/walletpass.js +425 -282
  12. package/dist/modules/Product/types.d.ts +0 -1
  13. package/dist/modules/ProductList/index.js +14 -33
  14. package/dist/modules/Quotation/index.d.ts +1 -1
  15. package/dist/modules/Quotation/index.js +2 -2
  16. package/dist/modules/Rules/index.d.ts +2 -0
  17. package/dist/modules/Rules/index.js +72 -102
  18. package/dist/modules/SalesSummary/index.js +13 -12
  19. package/dist/modules/ScanOrderLogger/index.d.ts +0 -2
  20. package/dist/modules/ScanOrderLogger/index.js +2 -15
  21. package/dist/modules/ScanOrderLogger/providers/feishu.js +27 -45
  22. package/dist/modules/ScanOrderLogger/types.d.ts +0 -1
  23. package/dist/modules/Schedule/index.d.ts +1 -3
  24. package/dist/modules/Schedule/index.js +16 -21
  25. package/dist/modules/Summary/utils.js +13 -38
  26. package/dist/modules/index.d.ts +0 -2
  27. package/dist/modules/index.js +1 -3
  28. package/dist/plugins/window.d.ts +0 -1
  29. package/dist/server/index.d.ts +0 -14
  30. package/dist/server/index.js +657 -772
  31. package/dist/solution/BaseSales/index.d.ts +0 -7
  32. package/dist/solution/BaseSales/index.js +48 -67
  33. package/dist/solution/BaseSales/types.d.ts +3 -1
  34. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +6 -3
  35. package/dist/solution/BookingByStep/index.d.ts +1 -16
  36. package/dist/solution/BookingByStep/index.js +215 -294
  37. package/dist/solution/BookingByStep/types.d.ts +1 -2
  38. package/dist/solution/BookingByStep/types.js +1 -3
  39. package/dist/solution/BookingByStep/utils/capacity.js +1 -17
  40. package/dist/solution/BookingByStep/utils/timeslots.d.ts +1 -3
  41. package/dist/solution/BookingByStep/utils/timeslots.js +12 -19
  42. package/dist/solution/BookingTicket/index.d.ts +1 -13
  43. package/dist/solution/BookingTicket/index.js +8 -5
  44. package/dist/solution/BookingTicket/types.d.ts +1 -0
  45. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +3 -3
  46. package/dist/solution/BookingTicket/utils/scan/handleScan.d.ts +1 -3
  47. package/dist/solution/BookingTicket/utils/scan/handleScan.js +2 -10
  48. package/dist/solution/ScanOrder/index.d.ts +14 -26
  49. package/dist/solution/ScanOrder/index.js +739 -1142
  50. package/dist/solution/ScanOrder/types.d.ts +1 -21
  51. package/dist/solution/ScanOrder/utils.d.ts +0 -8
  52. package/dist/solution/ScanOrder/utils.js +25 -66
  53. package/dist/solution/ShopDiscount/index.d.ts +0 -1
  54. package/dist/solution/ShopDiscount/index.js +87 -125
  55. package/dist/solution/VenueBooking/index.d.ts +12 -39
  56. package/dist/solution/VenueBooking/index.js +841 -1167
  57. package/dist/solution/VenueBooking/types.d.ts +0 -3
  58. package/dist/solution/VenueBooking/utils/resource.js +0 -1
  59. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +4 -4
  60. package/dist/solution/VenueBooking/utils/slotMerge.js +5 -10
  61. package/dist/solution/index.d.ts +0 -1
  62. package/dist/solution/index.js +1 -2
  63. package/dist/types/index.d.ts +0 -1
  64. package/lib/model/strategy/adapter/promotion/index.js +51 -0
  65. package/lib/modules/Discount/index.d.ts +0 -1
  66. package/lib/modules/Discount/index.js +0 -9
  67. package/lib/modules/Discount/types.d.ts +0 -1
  68. package/lib/modules/OpenData/index.js +1 -1
  69. package/lib/modules/Order/index.d.ts +2 -3
  70. package/lib/modules/Order/index.js +56 -33
  71. package/lib/modules/Order/types.d.ts +2 -9
  72. package/lib/modules/Order/utils.d.ts +0 -10
  73. package/lib/modules/Order/utils.js +5 -36
  74. package/lib/modules/Payment/walletpass.d.ts +10 -3
  75. package/lib/modules/Payment/walletpass.js +218 -56
  76. package/lib/modules/Product/types.d.ts +0 -1
  77. package/lib/modules/ProductList/index.js +31 -40
  78. package/lib/modules/Quotation/index.d.ts +1 -1
  79. package/lib/modules/Quotation/index.js +2 -2
  80. package/lib/modules/Rules/index.d.ts +2 -0
  81. package/lib/modules/Rules/index.js +24 -36
  82. package/lib/modules/SalesSummary/index.js +7 -6
  83. package/lib/modules/ScanOrderLogger/index.d.ts +0 -2
  84. package/lib/modules/ScanOrderLogger/index.js +1 -13
  85. package/lib/modules/ScanOrderLogger/providers/feishu.js +6 -15
  86. package/lib/modules/ScanOrderLogger/types.d.ts +0 -1
  87. package/lib/modules/Schedule/index.d.ts +1 -3
  88. package/lib/modules/Schedule/index.js +8 -10
  89. package/lib/modules/Summary/utils.js +1 -21
  90. package/lib/modules/index.d.ts +0 -2
  91. package/lib/modules/index.js +1 -5
  92. package/lib/plugins/window.d.ts +0 -1
  93. package/lib/server/index.d.ts +0 -14
  94. package/lib/server/index.js +5 -65
  95. package/lib/solution/BaseSales/index.d.ts +0 -7
  96. package/lib/solution/BaseSales/index.js +9 -24
  97. package/lib/solution/BaseSales/types.d.ts +3 -1
  98. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +3 -2
  99. package/lib/solution/BookingByStep/index.d.ts +1 -16
  100. package/lib/solution/BookingByStep/index.js +18 -60
  101. package/lib/solution/BookingByStep/types.d.ts +1 -2
  102. package/lib/solution/BookingByStep/types.js +0 -5
  103. package/lib/solution/BookingByStep/utils/capacity.js +1 -20
  104. package/lib/solution/BookingByStep/utils/timeslots.d.ts +1 -3
  105. package/lib/solution/BookingByStep/utils/timeslots.js +11 -19
  106. package/lib/solution/BookingTicket/index.d.ts +1 -13
  107. package/lib/solution/BookingTicket/index.js +3 -10
  108. package/lib/solution/BookingTicket/types.d.ts +1 -0
  109. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +2 -2
  110. package/lib/solution/BookingTicket/utils/scan/handleScan.d.ts +1 -3
  111. package/lib/solution/BookingTicket/utils/scan/handleScan.js +2 -2
  112. package/lib/solution/ScanOrder/index.d.ts +14 -26
  113. package/lib/solution/ScanOrder/index.js +182 -449
  114. package/lib/solution/ScanOrder/types.d.ts +1 -21
  115. package/lib/solution/ScanOrder/utils.d.ts +0 -8
  116. package/lib/solution/ScanOrder/utils.js +0 -31
  117. package/lib/solution/ShopDiscount/index.d.ts +0 -1
  118. package/lib/solution/ShopDiscount/index.js +0 -14
  119. package/lib/solution/VenueBooking/index.d.ts +12 -39
  120. package/lib/solution/VenueBooking/index.js +110 -322
  121. package/lib/solution/VenueBooking/types.d.ts +0 -3
  122. package/lib/solution/VenueBooking/utils/resource.js +0 -1
  123. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +4 -4
  124. package/lib/solution/VenueBooking/utils/slotMerge.js +4 -6
  125. package/lib/solution/index.d.ts +0 -1
  126. package/lib/solution/index.js +1 -3
  127. package/lib/types/index.d.ts +0 -1
  128. package/package.json +1 -1
  129. package/dist/modules/Holder/index.d.ts +0 -48
  130. package/dist/modules/Holder/index.js +0 -640
  131. package/dist/modules/Holder/types.d.ts +0 -123
  132. package/dist/modules/Holder/types.js +0 -1
  133. package/dist/modules/Holder/utils.d.ts +0 -13
  134. package/dist/modules/Holder/utils.js +0 -34
  135. package/dist/modules/ResourcePlanner/index.d.ts +0 -24
  136. package/dist/modules/ResourcePlanner/index.js +0 -181
  137. package/dist/modules/ResourcePlanner/planner.d.ts +0 -14
  138. package/dist/modules/ResourcePlanner/planner.js +0 -1069
  139. package/dist/modules/ResourcePlanner/types.d.ts +0 -227
  140. package/dist/modules/ResourcePlanner/types.js +0 -1
  141. package/dist/solution/UnifiedBookingSales/index.d.ts +0 -183
  142. package/dist/solution/UnifiedBookingSales/index.js +0 -1891
  143. package/dist/solution/UnifiedBookingSales/types.d.ts +0 -143
  144. package/dist/solution/UnifiedBookingSales/types.js +0 -11
  145. package/lib/modules/Holder/index.d.ts +0 -48
  146. package/lib/modules/Holder/index.js +0 -402
  147. package/lib/modules/Holder/types.d.ts +0 -123
  148. package/lib/modules/Holder/types.js +0 -17
  149. package/lib/modules/Holder/utils.d.ts +0 -13
  150. package/lib/modules/Holder/utils.js +0 -71
  151. package/lib/modules/ResourcePlanner/index.d.ts +0 -24
  152. package/lib/modules/ResourcePlanner/index.js +0 -148
  153. package/lib/modules/ResourcePlanner/planner.d.ts +0 -14
  154. package/lib/modules/ResourcePlanner/planner.js +0 -933
  155. package/lib/modules/ResourcePlanner/types.d.ts +0 -227
  156. package/lib/modules/ResourcePlanner/types.js +0 -17
  157. package/lib/solution/UnifiedBookingSales/index.d.ts +0 -183
  158. package/lib/solution/UnifiedBookingSales/index.js +0 -1084
  159. package/lib/solution/UnifiedBookingSales/types.d.ts +0 -143
  160. package/lib/solution/UnifiedBookingSales/types.js +0 -33
@@ -1,933 +0,0 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
- // If the importer is in node compatibility mode or this is not an ESM
21
- // file that has been converted to a CommonJS file using a Babel-
22
- // compatible transform (i.e. "__esModule" has not been set), then set
23
- // "default" to the CommonJS "module.exports" for node compatibility.
24
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
- mod
26
- ));
27
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
-
29
- // src/modules/ResourcePlanner/planner.ts
30
- var planner_exports = {};
31
- __export(planner_exports, {
32
- DEFAULT_RESOURCE_PLANNER_CONTEXT: () => DEFAULT_RESOURCE_PLANNER_CONTEXT,
33
- DEFAULT_RESOURCE_PLANNER_SELECTION: () => DEFAULT_RESOURCE_PLANNER_SELECTION,
34
- autoAssignResources: () => autoAssignResources,
35
- buildDemandIntervals: () => buildDemandIntervals,
36
- evaluateResourceForInterval: () => evaluateResourceForInterval,
37
- normalizePlannerContext: () => normalizePlannerContext,
38
- queryPlannerAvailability: () => queryPlannerAvailability,
39
- resolveAssignableSlots: () => resolveAssignableSlots,
40
- validateAssignments: () => validateAssignments
41
- });
42
- module.exports = __toCommonJS(planner_exports);
43
- var import_dayjs = __toESM(require("dayjs"));
44
- var DEFAULT_RESOURCE_PLANNER_CONTEXT = {
45
- products: [],
46
- resources: [],
47
- externalEvents: [],
48
- slotDurationMinutes: 30,
49
- businessStartTime: "09:00",
50
- businessEndTime: "18:00"
51
- };
52
- var DEFAULT_RESOURCE_PLANNER_SELECTION = {
53
- productIds: [],
54
- resourceIds: [],
55
- assignments: []
56
- };
57
- function normalizePlannerContext(input = {}) {
58
- return {
59
- products: Array.isArray(input.products) ? input.products : [],
60
- resources: Array.isArray(input.resources) ? input.resources : [],
61
- externalEvents: Array.isArray(input.externalEvents) ? input.externalEvents : [],
62
- slotDurationMinutes: input.slotDurationMinutes || DEFAULT_RESOURCE_PLANNER_CONTEXT.slotDurationMinutes,
63
- businessStartTime: input.businessStartTime || DEFAULT_RESOURCE_PLANNER_CONTEXT.businessStartTime,
64
- businessEndTime: input.businessEndTime || DEFAULT_RESOURCE_PLANNER_CONTEXT.businessEndTime
65
- };
66
- }
67
- function sameId(left, right) {
68
- return String(left) === String(right);
69
- }
70
- function dateOf(value) {
71
- return (0, import_dayjs.default)(value).format("YYYY-MM-DD");
72
- }
73
- function timeOf(value) {
74
- return (0, import_dayjs.default)(value).format("HH:mm");
75
- }
76
- function toDateTime(date, time) {
77
- if (/^\d{4}-\d{2}-\d{2}/.test(time))
78
- return (0, import_dayjs.default)(time);
79
- return (0, import_dayjs.default)(`${date} ${time.length === 5 ? `${time}:00` : time}`);
80
- }
81
- function overlaps(startA, endA, startB, endB) {
82
- return startA.isBefore(endB) && endA.isAfter(startB);
83
- }
84
- function containsRange(outerStart, outerEnd, innerStart, innerEnd) {
85
- return !innerStart.isBefore(outerStart) && !innerEnd.isAfter(outerEnd);
86
- }
87
- function maxDayjs(left, right) {
88
- return left.isAfter(right) ? left : right;
89
- }
90
- function minDayjs(left, right) {
91
- return left.isBefore(right) ? left : right;
92
- }
93
- function addDays(start, end) {
94
- const result = [];
95
- let cursor = (0, import_dayjs.default)(start);
96
- const finalDay = (0, import_dayjs.default)(end);
97
- while (!cursor.isAfter(finalDay, "day")) {
98
- result.push(cursor.format("YYYY-MM-DD"));
99
- cursor = cursor.add(1, "day");
100
- }
101
- return result;
102
- }
103
- function getQueryProducts(products, query) {
104
- var _a;
105
- if (!((_a = query.productIds) == null ? void 0 : _a.length))
106
- return products;
107
- return products.filter((product) => query.productIds.some((id) => sameId(id, product.id)));
108
- }
109
- function getQueryResources(resources, query) {
110
- var _a;
111
- if (!((_a = query.resourceIds) == null ? void 0 : _a.length))
112
- return resources;
113
- return resources.filter((resource) => query.resourceIds.some((id) => sameId(id, resource.id)));
114
- }
115
- function matchesResourceRequirement(resource, requirement) {
116
- var _a;
117
- const isFormlessCandidate = resource.formId == null;
118
- const formMatches = requirement.formId == null || isFormlessCandidate || sameId(requirement.formId, resource.formId);
119
- const resourceMatches = !((_a = requirement.resourceIds) == null ? void 0 : _a.length) || isFormlessCandidate || requirement.resourceIds.some((id) => sameId(id, resource.id));
120
- return formMatches && resourceMatches;
121
- }
122
- function resolveProductTitle(product) {
123
- var _a, _b, _c;
124
- const rawTitle = ((_a = product.raw) == null ? void 0 : _a.title) || ((_b = product.raw) == null ? void 0 : _b.name) || ((_c = product.raw) == null ? void 0 : _c.product_title);
125
- if (typeof product.title === "string")
126
- return product.title;
127
- if (typeof rawTitle === "string")
128
- return rawTitle;
129
- if (rawTitle && typeof rawTitle === "object") {
130
- return rawTitle.en || rawTitle.zh || rawTitle.default || Object.values(rawTitle)[0];
131
- }
132
- return void 0;
133
- }
134
- function buildDemandIntervals(products, query, context) {
135
- const dates = query.date ? [query.date] : query.dateRange ? addDays(query.dateRange.start, query.dateRange.end) : [];
136
- const selectedProducts = getQueryProducts(products, query);
137
- const intervals = [];
138
- for (const product of selectedProducts) {
139
- const quantity = Math.max(1, Number(product.quantity || 1));
140
- const capacityRequired = Math.max(1, Number(query.pax || product.capacityRequired || 1));
141
- const requirements = product.resourceRequirements || [];
142
- if (product.kind === "session" || product.kind === "venue_slot") {
143
- const schedules = product.schedule || [];
144
- for (const schedule of schedules) {
145
- const scheduleDate = dateOf(schedule.start_at);
146
- if (dates.length > 0 && !dates.includes(scheduleDate))
147
- continue;
148
- const start = (0, import_dayjs.default)(schedule.start_at);
149
- const end = (0, import_dayjs.default)(schedule.end_at);
150
- intervals.push({
151
- id: `${product.id}:${start.toISOString()}:${end.toISOString()}`,
152
- productId: product.id,
153
- source: product.kind,
154
- date: dateOf(schedule.start_at),
155
- start_at: start.toISOString(),
156
- end_at: end.toISOString(),
157
- durationMinutes: Math.max(1, end.diff(start, "minute")),
158
- quantity,
159
- capacityRequired,
160
- resourceRequirements: requirements
161
- });
162
- }
163
- continue;
164
- }
165
- if (product.kind === "duration") {
166
- const duration = Math.max(1, Number(product.durationMinutes || 30));
167
- for (const currentDate of dates) {
168
- const start = query.startTime ? toDateTime(currentDate, query.startTime) : toDateTime(currentDate, (context == null ? void 0 : context.businessStartTime) || DEFAULT_RESOURCE_PLANNER_CONTEXT.businessStartTime);
169
- let end = query.startTime && query.endTime ? toDateTime(currentDate, query.endTime) : query.startTime ? start.add(duration, "minute") : toDateTime(currentDate, (context == null ? void 0 : context.businessEndTime) || DEFAULT_RESOURCE_PLANNER_CONTEXT.businessEndTime);
170
- if (!end.isAfter(start))
171
- end = end.add(1, "day");
172
- intervals.push({
173
- id: `${product.id}:${currentDate}:${query.startTime || "range"}`,
174
- productId: product.id,
175
- source: "duration",
176
- date: currentDate,
177
- start_at: start.toISOString(),
178
- end_at: end.toISOString(),
179
- durationMinutes: duration,
180
- quantity,
181
- capacityRequired,
182
- resourceRequirements: requirements
183
- });
184
- }
185
- }
186
- }
187
- return intervals;
188
- }
189
- function getEventsForResource(resource, contextEvents) {
190
- const embeddedEvents = resource.times.flatMap((time) => time.event_list || []);
191
- return [
192
- ...embeddedEvents,
193
- ...contextEvents.filter((event) => sameId(event.resourceId ?? event.resource_id, resource.id))
194
- ].filter(Boolean);
195
- }
196
- function collectEventsByResource(resources, contextEvents) {
197
- const eventsByResource = /* @__PURE__ */ new Map();
198
- resources.forEach((resource) => {
199
- eventsByResource.set(String(resource.id), getEventsForResource(resource, contextEvents));
200
- });
201
- return eventsByResource;
202
- }
203
- function getWorkingSegmentsForInterval(resource, interval) {
204
- const intervalStart = (0, import_dayjs.default)(interval.start_at);
205
- const intervalEnd = (0, import_dayjs.default)(interval.end_at);
206
- return resource.times.map((time) => {
207
- const start = maxDayjs((0, import_dayjs.default)(time.start_at), intervalStart);
208
- const end = minDayjs((0, import_dayjs.default)(time.end_at), intervalEnd);
209
- return { start, end };
210
- }).filter((segment) => segment.end.isAfter(segment.start));
211
- }
212
- function mergeBusySegments(segments) {
213
- const sorted = [...segments].filter((segment) => segment.end.isAfter(segment.start)).sort((left, right) => left.start.valueOf() - right.start.valueOf());
214
- const merged = [];
215
- sorted.forEach((segment) => {
216
- const previous = merged[merged.length - 1];
217
- if (previous && !segment.start.isAfter(previous.end)) {
218
- previous.end = maxDayjs(previous.end, segment.end);
219
- } else {
220
- merged.push({ ...segment });
221
- }
222
- });
223
- return merged;
224
- }
225
- function subtractBusyIntervals(workingSegments, events) {
226
- const available = [];
227
- workingSegments.forEach((working) => {
228
- const busySegments = mergeBusySegments(events.filter((event) => overlaps(working.start, working.end, (0, import_dayjs.default)(event.start_at), (0, import_dayjs.default)(event.end_at))).map((event) => ({
229
- start: maxDayjs((0, import_dayjs.default)(event.start_at), working.start),
230
- end: minDayjs((0, import_dayjs.default)(event.end_at), working.end)
231
- })));
232
- let cursor = working.start;
233
- busySegments.forEach((busy) => {
234
- if (busy.start.isAfter(cursor)) {
235
- available.push({ start: cursor, end: busy.start });
236
- }
237
- if (busy.end.isAfter(cursor))
238
- cursor = busy.end;
239
- });
240
- if (working.end.isAfter(cursor)) {
241
- available.push({ start: cursor, end: working.end });
242
- }
243
- });
244
- return available;
245
- }
246
- function buildCapacityRanges(params) {
247
- const { workingSegments, events, maxCapacity, requiredCapacity } = params;
248
- const ranges = [];
249
- workingSegments.forEach((working) => {
250
- const boundaries = /* @__PURE__ */ new Map([
251
- [working.start.valueOf(), working.start],
252
- [working.end.valueOf(), working.end]
253
- ]);
254
- events.forEach((event) => {
255
- const eventStart = (0, import_dayjs.default)(event.start_at);
256
- const eventEnd = (0, import_dayjs.default)(event.end_at);
257
- if (!overlaps(working.start, working.end, eventStart, eventEnd))
258
- return;
259
- const clippedStart = maxDayjs(eventStart, working.start);
260
- const clippedEnd = minDayjs(eventEnd, working.end);
261
- boundaries.set(clippedStart.valueOf(), clippedStart);
262
- boundaries.set(clippedEnd.valueOf(), clippedEnd);
263
- });
264
- const points = Array.from(boundaries.values()).sort((left, right) => left.valueOf() - right.valueOf());
265
- for (let index = 0; index < points.length - 1; index += 1) {
266
- const start = points[index];
267
- const end = points[index + 1];
268
- if (!end.isAfter(start))
269
- continue;
270
- const usedCapacity = events.filter((event) => overlaps(start, end, (0, import_dayjs.default)(event.start_at), (0, import_dayjs.default)(event.end_at))).reduce((sum, event) => sum + Math.max(1, Number(event.pax || 1)), 0);
271
- const remainingCapacity = Math.max(0, maxCapacity - usedCapacity);
272
- if (remainingCapacity < requiredCapacity)
273
- continue;
274
- const status = remainingCapacity < maxCapacity ? "limited" : "available";
275
- const previous = ranges[ranges.length - 1];
276
- if (previous && previous.end.isSame(start) && previous.remainingCapacity === remainingCapacity && previous.maxCapacity === maxCapacity && previous.status === status) {
277
- previous.end = end;
278
- } else {
279
- ranges.push({
280
- start,
281
- end,
282
- remainingCapacity,
283
- maxCapacity,
284
- status
285
- });
286
- }
287
- }
288
- });
289
- return ranges;
290
- }
291
- function rangeFitsDuration(segment, durationMinutes) {
292
- return segment.end.diff(segment.start, "minute") >= durationMinutes;
293
- }
294
- function makeAvailabilityRangeCell(params) {
295
- const { interval, resource, segment } = params;
296
- const maxCapacity = Math.max(1, Number(segment.maxCapacity || resource.capacity || 1));
297
- const remainingCapacity = resource.resourceType === "single" ? 1 : Math.max(0, Number(segment.remainingCapacity ?? maxCapacity));
298
- const status = resource.resourceType === "single" ? "available" : segment.status || (remainingCapacity < maxCapacity ? "limited" : "available");
299
- return {
300
- key: `${interval.productId}:${resource.id}:${segment.start.toISOString()}:${segment.end.toISOString()}`,
301
- productId: interval.productId,
302
- resourceId: resource.id,
303
- date: interval.date,
304
- start_at: segment.start.toISOString(),
305
- end_at: segment.end.toISOString(),
306
- startTime: segment.start.format("HH:mm"),
307
- endTime: segment.end.format("HH:mm"),
308
- durationMinutes: interval.durationMinutes,
309
- cellType: "availability_range",
310
- status,
311
- remainingCapacity,
312
- maxCapacity,
313
- reasonCodes: [],
314
- conflicts: []
315
- };
316
- }
317
- function buildAvailabilityCellsForResource(params) {
318
- const { resource, interval, events } = params;
319
- const requiredCapacity = Math.max(1, Number(interval.capacityRequired || 1));
320
- const durationMinutes = Math.max(1, Number(interval.durationMinutes || 1));
321
- const maxCapacity = Math.max(1, Number(resource.capacity || 1));
322
- let workingSegments = getWorkingSegmentsForInterval(resource, interval);
323
- const now = (0, import_dayjs.default)();
324
- workingSegments = workingSegments.map((segment) => ({
325
- start: segment.end.isAfter(now) ? maxDayjs(segment.start, now) : segment.start,
326
- end: segment.end
327
- })).filter((segment) => segment.end.isAfter(segment.start));
328
- const availableSegments = resource.resourceType === "single" ? subtractBusyIntervals(workingSegments, events).map((segment) => ({
329
- ...segment,
330
- remainingCapacity: 1,
331
- maxCapacity: 1,
332
- status: "available"
333
- })) : buildCapacityRanges({
334
- workingSegments,
335
- events,
336
- maxCapacity,
337
- requiredCapacity
338
- });
339
- return availableSegments.filter((segment) => rangeFitsDuration(segment, durationMinutes)).map((segment) => makeAvailabilityRangeCell({
340
- interval,
341
- resource,
342
- segment
343
- }));
344
- }
345
- function evaluateResourceForInterval(params) {
346
- const { resource, interval, context } = params;
347
- const start = (0, import_dayjs.default)(interval.start_at);
348
- const end = (0, import_dayjs.default)(interval.end_at);
349
- const conflicts = [];
350
- const reasonCodes = [];
351
- const maxCapacity = Math.max(1, Number(resource.capacity || 1));
352
- const requiredCapacity = Math.max(1, Number(interval.capacityRequired || 1));
353
- if (start.isBefore((0, import_dayjs.default)())) {
354
- reasonCodes.push("past");
355
- conflicts.push({
356
- type: "past",
357
- message: "时间已过期",
358
- resourceId: resource.id,
359
- productId: interval.productId
360
- });
361
- }
362
- const insideResourceTime = resource.times.some((time) => {
363
- return containsRange((0, import_dayjs.default)(time.start_at), (0, import_dayjs.default)(time.end_at), start, end);
364
- });
365
- if (!insideResourceTime) {
366
- reasonCodes.push("outside_resource_time");
367
- conflicts.push({
368
- type: "outside_resource_time",
369
- message: "所选时间不在资源可用时间内",
370
- resourceId: resource.id,
371
- productId: interval.productId
372
- });
373
- }
374
- const events = getEventsForResource(resource, context.externalEvents);
375
- const overlapping = events.filter((event) => overlaps(start, end, (0, import_dayjs.default)(event.start_at), (0, import_dayjs.default)(event.end_at)));
376
- const usedCapacity = resource.resourceType === "single" ? overlapping.length : overlapping.reduce((sum, event) => sum + Math.max(1, Number(event.pax || 1)), 0);
377
- const remainingCapacity = resource.resourceType === "single" ? overlapping.length > 0 ? 0 : 1 : Math.max(0, maxCapacity - usedCapacity);
378
- if (overlapping.length > 0 && resource.resourceType === "single") {
379
- reasonCodes.push("remote_event_overlap");
380
- conflicts.push(...overlapping.map((event) => ({
381
- type: "remote_event_overlap",
382
- message: "资源时间被已有预约占用",
383
- event,
384
- resourceId: resource.id,
385
- productId: interval.productId
386
- })));
387
- }
388
- if (resource.resourceType !== "single" && remainingCapacity < requiredCapacity) {
389
- reasonCodes.push("capacity_full");
390
- conflicts.push({
391
- type: "capacity_full",
392
- message: "资源剩余容量不足",
393
- resourceId: resource.id,
394
- productId: interval.productId
395
- });
396
- }
397
- let status = "available";
398
- if (reasonCodes.includes("past"))
399
- status = "past";
400
- else if (reasonCodes.includes("outside_resource_time"))
401
- status = "unavailable";
402
- else if (reasonCodes.includes("remote_event_overlap") || reasonCodes.includes("capacity_full")) {
403
- status = "full";
404
- } else if (remainingCapacity < maxCapacity) {
405
- status = "limited";
406
- }
407
- return {
408
- key: `${interval.productId}:${resource.id}:${interval.start_at}:${interval.end_at}`,
409
- productId: interval.productId,
410
- resourceId: resource.id,
411
- date: interval.date,
412
- start_at: interval.start_at,
413
- end_at: interval.end_at,
414
- startTime: timeOf(interval.start_at),
415
- endTime: timeOf(interval.end_at),
416
- durationMinutes: interval.durationMinutes,
417
- cellType: status === "available" || status === "limited" ? "fixed_interval" : "unavailable",
418
- status,
419
- remainingCapacity,
420
- maxCapacity,
421
- reasonCodes,
422
- conflicts
423
- };
424
- }
425
- function aggregateStatus(cells) {
426
- if (!cells.length)
427
- return "unavailable";
428
- if (cells.some((cell) => cell.status === "available"))
429
- return "available";
430
- if (cells.some((cell) => cell.status === "limited"))
431
- return "limited";
432
- if (cells.some((cell) => cell.status === "past"))
433
- return "past";
434
- if (cells.some((cell) => cell.status === "full"))
435
- return "full";
436
- if (cells.some((cell) => cell.status === "conflict"))
437
- return "conflict";
438
- return "unavailable";
439
- }
440
- function uniqueReasonCodes(cells) {
441
- return Array.from(new Set(cells.flatMap((cell) => cell.reasonCodes || [])));
442
- }
443
- function buildProductOptions(products, cells, query) {
444
- const queryProducts = getQueryProducts(products, query);
445
- return queryProducts.map((product) => {
446
- const productCells = cells.filter((cell) => sameId(cell.productId, product.id));
447
- const status = product.kind === "normal" ? "available" : aggregateStatus(productCells);
448
- return {
449
- productId: product.id,
450
- title: resolveProductTitle(product),
451
- status,
452
- reasonCodes: uniqueReasonCodes(productCells)
453
- };
454
- });
455
- }
456
- function buildResourceOptions(resources, cells, query) {
457
- const queryResources = getQueryResources(resources, query);
458
- return queryResources.map((resource) => {
459
- const resourceCells = cells.filter((cell) => cell.resourceId !== void 0 && sameId(cell.resourceId, resource.id));
460
- const bestCell = resourceCells.find((cell) => cell.status === "available") || resourceCells.find((cell) => cell.status === "limited") || resourceCells[0];
461
- return {
462
- resourceId: resource.id,
463
- resourceName: resource.name,
464
- resourceFormId: resource.formId,
465
- resourceType: resource.resourceType,
466
- status: resourceCells.length ? aggregateStatus(resourceCells) : "unavailable",
467
- remainingCapacity: bestCell == null ? void 0 : bestCell.remainingCapacity,
468
- maxCapacity: (bestCell == null ? void 0 : bestCell.maxCapacity) || resource.capacity,
469
- reasonCodes: uniqueReasonCodes(resourceCells)
470
- };
471
- });
472
- }
473
- function buildDateSummary(cells, query) {
474
- const dates = query.date ? [query.date] : query.dateRange ? addDays(query.dateRange.start, query.dateRange.end) : Array.from(new Set(cells.map((cell) => cell.date).filter(Boolean)));
475
- return dates.map((date) => {
476
- const dateCells = cells.filter((cell) => cell.date === date);
477
- const availableCount = dateCells.filter((cell) => cell.status === "available" || cell.status === "limited").length;
478
- return {
479
- date,
480
- status: dateCells.length ? aggregateStatus(dateCells) : "unavailable",
481
- availableCount,
482
- totalCount: dateCells.length
483
- };
484
- });
485
- }
486
- function buildTimeLabels(date, context, query) {
487
- if (!date)
488
- return [];
489
- const slotMinutes = query.slotDurationMinutes || context.slotDurationMinutes;
490
- const labels = [];
491
- let cursor = toDateTime(date, context.businessStartTime);
492
- let end = toDateTime(date, context.businessEndTime);
493
- if (!end.isAfter(cursor))
494
- end = end.add(1, "day");
495
- while (cursor.isBefore(end)) {
496
- labels.push(cursor.format("HH:mm"));
497
- cursor = cursor.add(slotMinutes, "minute");
498
- }
499
- return labels;
500
- }
501
- function buildGridSlotInterval(product, date, label, context, query) {
502
- const slotMinutes = product.kind === "duration" ? Math.max(1, Number(product.durationMinutes || query.slotDurationMinutes || context.slotDurationMinutes)) : query.slotDurationMinutes || context.slotDurationMinutes;
503
- const start = toDateTime(date, label);
504
- const end = start.add(slotMinutes, "minute");
505
- return {
506
- id: `${product.id}:${date}:${label}`,
507
- productId: product.id,
508
- source: product.kind === "session" ? "session" : product.kind === "venue_slot" ? "venue_slot" : "duration",
509
- date,
510
- start_at: start.toISOString(),
511
- end_at: end.toISOString(),
512
- durationMinutes: slotMinutes,
513
- quantity: Math.max(1, Number(product.quantity || 1)),
514
- capacityRequired: Math.max(1, Number(query.pax || product.capacityRequired || 1)),
515
- resourceRequirements: product.resourceRequirements || []
516
- };
517
- }
518
- function buildTimeGrid(context, query, cells) {
519
- var _a;
520
- const date = query.date || ((_a = query.dateRange) == null ? void 0 : _a.start);
521
- const products = getQueryProducts(context.products, query).filter((product) => product.kind !== "normal");
522
- const resources = getQueryResources(context.resources, query);
523
- const timeLabels = buildTimeLabels(date, context, query);
524
- if (!date || !products.length || !resources.length) {
525
- return { date, timeLabels, resources: [] };
526
- }
527
- const rows = resources.map((resource) => {
528
- const slots = timeLabels.map((label) => {
529
- const product = products[0];
530
- const interval = buildGridSlotInterval(product, date, label, context, query);
531
- const start = (0, import_dayjs.default)(interval.start_at);
532
- const end = (0, import_dayjs.default)(interval.end_at);
533
- const cell = cells.find((item) => sameId(item.productId, product.id) && sameId(item.resourceId, resource.id) && item.date === date && (item.status === "available" || item.status === "limited") && containsRange((0, import_dayjs.default)(item.start_at), (0, import_dayjs.default)(item.end_at), start, end));
534
- return {
535
- startTime: label,
536
- endTime: (0, import_dayjs.default)(interval.end_at).format("HH:mm"),
537
- status: (cell == null ? void 0 : cell.status) || "unavailable",
538
- resourceId: resource.id,
539
- productId: product.id,
540
- remainingCapacity: cell == null ? void 0 : cell.remainingCapacity,
541
- maxCapacity: (cell == null ? void 0 : cell.maxCapacity) || resource.capacity,
542
- reasonCodes: (cell == null ? void 0 : cell.reasonCodes) || ["outside_available_range"]
543
- };
544
- });
545
- return {
546
- resourceId: resource.id,
547
- resourceName: resource.name,
548
- resourceFormId: resource.formId,
549
- slots
550
- };
551
- });
552
- return { date, timeLabels, resources: rows };
553
- }
554
- function queryPlannerAvailability(contextInput, query = {}) {
555
- const context = normalizePlannerContext(contextInput);
556
- const intervals = buildDemandIntervals(context.products, query, context);
557
- const resources = getQueryResources(context.resources, query);
558
- const eventsByResource = collectEventsByResource(resources, context.externalEvents);
559
- const cells = [];
560
- for (const interval of intervals) {
561
- const candidateResources = resources.filter((resource) => {
562
- const requirements = interval.resourceRequirements || [];
563
- if (!requirements.length)
564
- return true;
565
- return requirements.some((requirement) => matchesResourceRequirement(resource, requirement));
566
- });
567
- if (!candidateResources.length) {
568
- cells.push({
569
- key: `${interval.productId}:missing-resource:${interval.start_at}`,
570
- productId: interval.productId,
571
- date: interval.date,
572
- start_at: interval.start_at,
573
- end_at: interval.end_at,
574
- startTime: timeOf(interval.start_at),
575
- endTime: timeOf(interval.end_at),
576
- durationMinutes: interval.durationMinutes,
577
- cellType: "unavailable",
578
- status: "unavailable",
579
- reasonCodes: ["missing_resource"],
580
- conflicts: [{
581
- type: "missing_resource",
582
- message: "没有匹配商品需求的资源",
583
- productId: interval.productId
584
- }]
585
- });
586
- continue;
587
- }
588
- candidateResources.forEach((resource) => {
589
- if (interval.source === "duration" && !query.startTime) {
590
- cells.push(...buildAvailabilityCellsForResource({
591
- resource,
592
- interval,
593
- events: eventsByResource.get(String(resource.id)) || []
594
- }));
595
- return;
596
- }
597
- cells.push(evaluateResourceForInterval({ resource, interval, context }));
598
- });
599
- }
600
- return {
601
- query,
602
- demandIntervals: intervals,
603
- productOptions: buildProductOptions(context.products, cells, query),
604
- resourceOptions: buildResourceOptions(context.resources, cells, query),
605
- dateSummary: buildDateSummary(cells, query),
606
- timeGrid: buildTimeGrid(context, query, cells),
607
- cells
608
- };
609
- }
610
- function getIntervalRequirements(interval) {
611
- return interval.resourceRequirements.length ? interval.resourceRequirements : [{
612
- capacityRequired: interval.capacityRequired,
613
- required: true
614
- }];
615
- }
616
- function buildAssignableCandidateGroups(params) {
617
- const { interval, cells, resources } = params;
618
- const conflicts = [];
619
- const groups = getIntervalRequirements(interval).map((requirement) => {
620
- const candidateCells = cells.filter((cell) => {
621
- if (!sameId(cell.productId, interval.productId))
622
- return false;
623
- if (cell.date !== interval.date)
624
- return false;
625
- if (cell.resourceId === void 0)
626
- return false;
627
- if (cell.status !== "available" && cell.status !== "limited")
628
- return false;
629
- const cellStart = (0, import_dayjs.default)(cell.start_at);
630
- const cellEnd = (0, import_dayjs.default)(cell.end_at);
631
- const intervalStart = (0, import_dayjs.default)(interval.start_at);
632
- const intervalEnd = (0, import_dayjs.default)(interval.end_at);
633
- return containsRange(cellStart, cellEnd, intervalStart, intervalEnd) || interval.source === "duration";
634
- });
635
- return candidateCells.flatMap((cell) => {
636
- const resource = resources.find((item) => sameId(item.id, cell.resourceId));
637
- if (!resource || !matchesResourceRequirement(resource, requirement))
638
- return [];
639
- return [{ requirement, resource, cell }];
640
- });
641
- });
642
- groups.forEach((group, index) => {
643
- var _a, _b;
644
- if (group.length)
645
- return;
646
- conflicts.push({
647
- type: "missing_resource",
648
- message: "没有匹配商品需求的可用资源",
649
- productId: interval.productId,
650
- resourceId: (_b = (_a = interval.resourceRequirements[index]) == null ? void 0 : _a.resourceIds) == null ? void 0 : _b[0]
651
- });
652
- });
653
- return { groups, conflicts };
654
- }
655
- function combineCandidateGroups(groups) {
656
- if (!groups.length || groups.some((group) => group.length === 0))
657
- return [];
658
- return groups.reduce((combinations, group) => combinations.flatMap((combination) => group.map((candidate) => [...combination, candidate])), [[]]);
659
- }
660
- function buildSlotFromCandidates(params) {
661
- const { interval, candidates, start, end } = params;
662
- if (!end.isAfter(start))
663
- return null;
664
- const status = candidates.some((candidate) => candidate.cell.status === "limited") ? "limited" : "available";
665
- const assignments = candidates.map((candidate) => ({
666
- productId: interval.productId,
667
- resourceId: candidate.resource.id,
668
- start_at: start.toISOString(),
669
- end_at: end.toISOString(),
670
- capacityRequired: Math.max(1, Number(
671
- candidate.requirement.capacityRequired || interval.capacityRequired || 1
672
- ))
673
- }));
674
- return {
675
- key: [
676
- interval.productId,
677
- start.toISOString(),
678
- end.toISOString(),
679
- ...candidates.map((candidate) => candidate.resource.id)
680
- ].join(":"),
681
- productId: interval.productId,
682
- date: interval.date,
683
- start_at: start.toISOString(),
684
- end_at: end.toISOString(),
685
- startTime: start.format("HH:mm"),
686
- endTime: end.format("HH:mm"),
687
- durationMinutes: Math.max(1, end.diff(start, "minute")),
688
- status,
689
- resources: candidates.map((candidate) => ({
690
- resourceId: candidate.resource.id,
691
- resourceName: candidate.resource.name,
692
- resourceFormId: candidate.resource.formId,
693
- resourceType: candidate.resource.resourceType,
694
- capacityRequired: Math.max(1, Number(
695
- candidate.requirement.capacityRequired || interval.capacityRequired || 1
696
- )),
697
- remainingCapacity: candidate.cell.remainingCapacity,
698
- maxCapacity: candidate.cell.maxCapacity
699
- })),
700
- assignments,
701
- conflicts: []
702
- };
703
- }
704
- function buildSlotsForInterval(params) {
705
- const { interval, candidates, query, context } = params;
706
- const start = candidates.reduce(
707
- (current, candidate) => maxDayjs(current, (0, import_dayjs.default)(candidate.cell.start_at)),
708
- (0, import_dayjs.default)(interval.start_at)
709
- );
710
- const end = candidates.reduce(
711
- (current, candidate) => minDayjs(current, (0, import_dayjs.default)(candidate.cell.end_at)),
712
- (0, import_dayjs.default)(interval.end_at)
713
- );
714
- const durationMinutes = Math.max(1, Number(interval.durationMinutes || 1));
715
- if (interval.source !== "duration" || query.startTime) {
716
- const fixedStart = (0, import_dayjs.default)(interval.start_at);
717
- const fixedEnd = (0, import_dayjs.default)(interval.end_at);
718
- if (containsRange(start, end, fixedStart, fixedEnd) || candidates.every((candidate) => candidate.cell.cellType === "fixed_interval")) {
719
- const slot = buildSlotFromCandidates({
720
- interval,
721
- candidates,
722
- start: fixedStart,
723
- end: fixedEnd
724
- });
725
- return slot ? [slot] : [];
726
- }
727
- return [];
728
- }
729
- const stepMinutes = Math.max(1, Number(query.slotDurationMinutes || context.slotDurationMinutes || 30));
730
- const slots = [];
731
- let cursor = start;
732
- while (!cursor.add(durationMinutes, "minute").isAfter(end)) {
733
- const slotEnd = cursor.add(durationMinutes, "minute");
734
- const slot = buildSlotFromCandidates({
735
- interval,
736
- candidates,
737
- start: cursor,
738
- end: slotEnd
739
- });
740
- if (slot)
741
- slots.push(slot);
742
- cursor = cursor.add(stepMinutes, "minute");
743
- }
744
- return slots;
745
- }
746
- function resolveAssignableSlots(contextInput, query = {}) {
747
- const context = normalizePlannerContext(contextInput);
748
- const projection = queryPlannerAvailability(context, query);
749
- const resources = getQueryResources(context.resources, query);
750
- const slotsByKey = /* @__PURE__ */ new Map();
751
- const conflicts = [];
752
- projection.demandIntervals.forEach((interval) => {
753
- const { groups, conflicts: groupConflicts } = buildAssignableCandidateGroups({
754
- interval,
755
- cells: projection.cells,
756
- resources
757
- });
758
- conflicts.push(...groupConflicts);
759
- combineCandidateGroups(groups).forEach((candidates) => {
760
- buildSlotsForInterval({
761
- interval,
762
- candidates,
763
- query,
764
- context
765
- }).forEach((slot) => {
766
- if (!slotsByKey.has(slot.key))
767
- slotsByKey.set(slot.key, slot);
768
- });
769
- });
770
- });
771
- return {
772
- projection,
773
- slots: Array.from(slotsByKey.values()),
774
- conflicts
775
- };
776
- }
777
- function autoAssignResources(contextInput, query = {}) {
778
- const baseContext = normalizePlannerContext(contextInput);
779
- const projection = queryPlannerAvailability(baseContext, query);
780
- const assignments = [];
781
- const conflicts = [];
782
- const missingTimeProductIds = /* @__PURE__ */ new Set();
783
- for (const interval of projection.demandIntervals) {
784
- if (interval.source === "duration" && !query.startTime) {
785
- const productKey = String(interval.productId);
786
- if (!missingTimeProductIds.has(productKey)) {
787
- missingTimeProductIds.add(productKey);
788
- conflicts.push({
789
- type: "missing_time",
790
- message: "duration 商品需要先确定开始时间后才能自动分配资源",
791
- productId: interval.productId
792
- });
793
- }
794
- continue;
795
- }
796
- const contextWithAssignments = withAssignmentEvents(baseContext, assignments);
797
- const candidateResources = getQueryResources(contextWithAssignments.resources, query).filter((resource) => {
798
- const requirements = interval.resourceRequirements || [];
799
- if (!requirements.length)
800
- return true;
801
- return requirements.some((requirement) => matchesResourceRequirement(resource, requirement));
802
- });
803
- const availableCell = candidateResources.map((resource) => evaluateResourceForInterval({
804
- resource,
805
- interval,
806
- context: contextWithAssignments
807
- })).find((cell) => cell.status === "available" || cell.status === "limited");
808
- if (!(availableCell == null ? void 0 : availableCell.resourceId)) {
809
- conflicts.push({
810
- type: "missing_resource",
811
- message: "无法为商品自动分配可用资源",
812
- productId: interval.productId
813
- });
814
- continue;
815
- }
816
- assignments.push({
817
- productId: interval.productId,
818
- resourceId: availableCell.resourceId,
819
- start_at: interval.start_at,
820
- end_at: interval.end_at,
821
- capacityRequired: interval.capacityRequired
822
- });
823
- }
824
- return { assignments, conflicts };
825
- }
826
- function withAssignmentEvents(context, assignments) {
827
- if (!assignments.length)
828
- return context;
829
- return {
830
- ...context,
831
- externalEvents: [
832
- ...context.externalEvents,
833
- ...assignments.map((assignment) => ({
834
- resourceId: assignment.resourceId,
835
- start_at: assignment.start_at,
836
- end_at: assignment.end_at,
837
- pax: assignment.capacityRequired || 1,
838
- source: "selection"
839
- }))
840
- ]
841
- };
842
- }
843
- function validateAssignments(contextInput, selection) {
844
- if (selection.assignments.length > 0) {
845
- const context = normalizePlannerContext(contextInput);
846
- const conflicts2 = [];
847
- for (let index = 0; index < selection.assignments.length; index += 1) {
848
- const assignment = selection.assignments[index];
849
- const product = context.products.find((item) => sameId(item.id, assignment.productId));
850
- const resource = context.resources.find((item) => sameId(item.id, assignment.resourceId));
851
- if (!product) {
852
- conflicts2.push({
853
- type: "missing_resource",
854
- message: "assignment 对应的商品不存在",
855
- productId: assignment.productId
856
- });
857
- continue;
858
- }
859
- if (!resource) {
860
- conflicts2.push({
861
- type: "missing_resource",
862
- message: "assignment 对应的资源不存在",
863
- productId: assignment.productId,
864
- resourceId: assignment.resourceId
865
- });
866
- continue;
867
- }
868
- const start = (0, import_dayjs.default)(assignment.start_at);
869
- const end = (0, import_dayjs.default)(assignment.end_at);
870
- const interval = {
871
- id: `${assignment.productId}:${assignment.resourceId}:${assignment.start_at}:${assignment.end_at}`,
872
- productId: assignment.productId,
873
- source: product.kind === "session" ? "session" : product.kind === "venue_slot" ? "venue_slot" : "duration",
874
- date: start.format("YYYY-MM-DD"),
875
- start_at: assignment.start_at,
876
- end_at: assignment.end_at,
877
- durationMinutes: Math.max(1, end.diff(start, "minute")),
878
- quantity: Math.max(1, Number(product.quantity || 1)),
879
- capacityRequired: Math.max(1, Number(assignment.capacityRequired || product.capacityRequired || 1)),
880
- resourceRequirements: product.resourceRequirements || []
881
- };
882
- const cell = evaluateResourceForInterval({ resource, interval, context });
883
- if (cell.status !== "available" && cell.status !== "limited") {
884
- conflicts2.push(...cell.conflicts);
885
- }
886
- const priorContext = withAssignmentEvents(
887
- context,
888
- selection.assignments.slice(0, index)
889
- );
890
- const selfConflictCell = evaluateResourceForInterval({
891
- resource,
892
- interval,
893
- context: priorContext
894
- });
895
- if (selfConflictCell.status !== "available" && selfConflictCell.status !== "limited") {
896
- conflicts2.push(...selfConflictCell.conflicts.map((conflict) => ({
897
- ...conflict,
898
- type: "cart_overlap",
899
- message: "本次选择中存在资源时间冲突"
900
- })));
901
- }
902
- }
903
- return {
904
- ok: conflicts2.length === 0,
905
- conflicts: conflicts2
906
- };
907
- }
908
- const projection = queryPlannerAvailability(contextInput, {
909
- productIds: selection.productIds,
910
- date: selection.date,
911
- startTime: selection.startTime,
912
- endTime: selection.endTime,
913
- resourceIds: selection.resourceIds,
914
- mode: selection.mode
915
- });
916
- const conflicts = projection.cells.filter((cell) => cell.status !== "available" && cell.status !== "limited").flatMap((cell) => cell.conflicts);
917
- return {
918
- ok: conflicts.length === 0,
919
- conflicts
920
- };
921
- }
922
- // Annotate the CommonJS export names for ESM import in node:
923
- 0 && (module.exports = {
924
- DEFAULT_RESOURCE_PLANNER_CONTEXT,
925
- DEFAULT_RESOURCE_PLANNER_SELECTION,
926
- autoAssignResources,
927
- buildDemandIntervals,
928
- evaluateResourceForInterval,
929
- normalizePlannerContext,
930
- queryPlannerAvailability,
931
- resolveAssignableSlots,
932
- validateAssignments
933
- });