@pisell/pisellos 2.1.129 → 2.1.131

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 (54) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  2. package/dist/modules/Order/index.d.ts +7 -6
  3. package/dist/modules/Order/index.js +137 -42
  4. package/dist/modules/Order/types.d.ts +32 -6
  5. package/dist/modules/Order/types.js +2 -0
  6. package/dist/modules/Order/utils.d.ts +73 -11
  7. package/dist/modules/Order/utils.js +304 -52
  8. package/dist/modules/SalesSummary/utils.js +33 -68
  9. package/dist/modules/ScanOrderLogger/providers/feishu.js +168 -60
  10. package/dist/modules/ScanOrderLogger/types.d.ts +6 -0
  11. package/dist/modules/Summary/utils.js +6 -21
  12. package/dist/solution/ScanOrder/index.d.ts +57 -8
  13. package/dist/solution/ScanOrder/index.js +1531 -583
  14. package/dist/solution/ScanOrder/types.d.ts +86 -26
  15. package/dist/solution/ScanOrder/types.js +20 -1
  16. package/dist/solution/ScanOrder/utils.d.ts +53 -5
  17. package/dist/solution/ScanOrder/utils.js +257 -37
  18. package/dist/solution/VenueBooking/index.d.ts +30 -10
  19. package/dist/solution/VenueBooking/index.js +460 -217
  20. package/dist/solution/VenueBooking/types.d.ts +23 -0
  21. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -1
  22. package/dist/solution/VenueBooking/utils/dateSummary.js +1 -1
  23. package/dist/solution/VenueBooking/utils/resource.d.ts +11 -1
  24. package/dist/solution/VenueBooking/utils/resource.js +57 -21
  25. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +5 -0
  26. package/dist/solution/VenueBooking/utils/slotMerge.js +33 -12
  27. package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -1
  28. package/dist/solution/VenueBooking/utils/timeSlot.js +259 -62
  29. package/lib/modules/Order/index.d.ts +7 -6
  30. package/lib/modules/Order/index.js +123 -31
  31. package/lib/modules/Order/types.d.ts +32 -6
  32. package/lib/modules/Order/utils.d.ts +73 -11
  33. package/lib/modules/Order/utils.js +203 -28
  34. package/lib/modules/SalesSummary/utils.js +13 -47
  35. package/lib/modules/ScanOrderLogger/providers/feishu.js +100 -34
  36. package/lib/modules/ScanOrderLogger/types.d.ts +6 -0
  37. package/lib/modules/Summary/utils.js +4 -18
  38. package/lib/solution/ScanOrder/index.d.ts +57 -8
  39. package/lib/solution/ScanOrder/index.js +713 -117
  40. package/lib/solution/ScanOrder/types.d.ts +86 -26
  41. package/lib/solution/ScanOrder/utils.d.ts +53 -5
  42. package/lib/solution/ScanOrder/utils.js +186 -19
  43. package/lib/solution/VenueBooking/index.d.ts +30 -10
  44. package/lib/solution/VenueBooking/index.js +206 -51
  45. package/lib/solution/VenueBooking/types.d.ts +23 -0
  46. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -1
  47. package/lib/solution/VenueBooking/utils/dateSummary.js +1 -1
  48. package/lib/solution/VenueBooking/utils/resource.d.ts +11 -1
  49. package/lib/solution/VenueBooking/utils/resource.js +15 -4
  50. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +5 -0
  51. package/lib/solution/VenueBooking/utils/slotMerge.js +29 -12
  52. package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -1
  53. package/lib/solution/VenueBooking/utils/timeSlot.js +182 -43
  54. package/package.json +1 -1
@@ -107,10 +107,32 @@ function buildPriceBreakdown(params) {
107
107
  return entries;
108
108
  }
109
109
  function buildVenueBookingEntry(params) {
110
- const { group, resourceId, mapping, rawResource, bookingUuid, productUid } = params;
110
+ const { group, resourceId, mapping, rawResource, bookingUuid, productUid, childResources } = params;
111
111
  const startMoment = (0, import_dayjs.default)(group.startTime, "YYYY-MM-DD HH:mm");
112
112
  const endMoment = (0, import_dayjs.default)(group.endTime, "YYYY-MM-DD HH:mm");
113
113
  const duration = endMoment.diff(startMoment, "minute");
114
+ const resourceEntry = {
115
+ relation_type: "form",
116
+ like_status: "common",
117
+ id: resourceId,
118
+ main_field: mapping.resourceName,
119
+ form_id: (rawResource == null ? void 0 : rawResource.form_id) ?? mapping.formId,
120
+ relation_id: resourceId,
121
+ capacity: 1,
122
+ metadata: {}
123
+ };
124
+ if (childResources && childResources.length) {
125
+ resourceEntry.children = childResources.map((child) => ({
126
+ relation_type: "form",
127
+ like_status: "common",
128
+ id: child.resourceId,
129
+ main_field: child.main_field || "",
130
+ form_id: child.form_id ?? child.formId,
131
+ relation_id: child.resourceId,
132
+ capacity: child.capacity ?? 1,
133
+ metadata: {}
134
+ }));
135
+ }
114
136
  return {
115
137
  schedule_event_id: null,
116
138
  appointment_status: "new",
@@ -126,23 +148,15 @@ function buildVenueBookingEntry(params) {
126
148
  is_all: false,
127
149
  schedule_id: 0,
128
150
  product_uid: productUid,
129
- resources: [{
130
- relation_type: "form",
131
- like_status: "common",
132
- id: resourceId,
133
- main_field: mapping.resourceName,
134
- form_id: (rawResource == null ? void 0 : rawResource.form_id) ?? mapping.formId,
135
- relation_id: resourceId,
136
- capacity: 1,
137
- metadata: {}
138
- }],
151
+ resources: [resourceEntry],
139
152
  relation_products: [],
140
153
  relation_forms: [],
141
154
  holder: null,
142
155
  metadata: {
143
156
  unique_identification_number: bookingUuid,
144
157
  venue_booking: true,
145
- resource_id: resourceId
158
+ resource_id: resourceId,
159
+ product_id: mapping.productId
146
160
  }
147
161
  };
148
162
  }
@@ -155,6 +169,7 @@ function expandMergedSlotToIndividual(product, slotDurationMinutes) {
155
169
  const endTime = meta.end_time;
156
170
  if (!startTime || !endTime)
157
171
  return [];
172
+ const productId = Number(product.product_id);
158
173
  const breakdown = meta.price_breakdown;
159
174
  const result = [];
160
175
  let cursor = (0, import_dayjs.default)(startTime, "YYYY-MM-DD HH:mm");
@@ -175,6 +190,7 @@ function expandMergedSlotToIndividual(product, slotDurationMinutes) {
175
190
  const price = priceMap.get(hm) ?? 0;
176
191
  result.push({
177
192
  resourceId,
193
+ productId,
178
194
  startTime: cursor.format("YYYY-MM-DD HH:mm"),
179
195
  endTime: slotEnd.format("YYYY-MM-DD HH:mm"),
180
196
  price: new import_decimal.default(price).toFixed(2)
@@ -189,6 +205,7 @@ function expandMergedSlotToIndividual(product, slotDurationMinutes) {
189
205
  const slotEnd = cursor.add(slotDurationMinutes, "minute");
190
206
  result.push({
191
207
  resourceId,
208
+ productId,
192
209
  startTime: cursor.format("YYYY-MM-DD HH:mm"),
193
210
  endTime: slotEnd.format("YYYY-MM-DD HH:mm"),
194
211
  price: perSlotPrice
@@ -26,7 +26,7 @@ export declare function buildTimeSlotGrid(params: {
26
26
  date: string;
27
27
  config: VenueBookingSlotConfig;
28
28
  rawResources: VenueResourceRawData[];
29
- resourceProductMap: Map<number | string, ResourceProductMapping>;
29
+ resourceProductMap: Map<number | string, ResourceProductMapping[]>;
30
30
  quotationPriceMap?: Map<string, string | null>;
31
31
  }): VenueTimeSlotGrid;
32
32
  export {};
@@ -38,6 +38,7 @@ __export(timeSlot_exports, {
38
38
  });
39
39
  module.exports = __toCommonJS(timeSlot_exports);
40
40
  var import_dayjs = __toESM(require("dayjs"));
41
+ var import_decimal = __toESM(require("decimal.js"));
41
42
  function isBusinessHoursCrossDay(config) {
42
43
  const [startH, startM] = config.businessStartTime.split(":").map(Number);
43
44
  const [endH, endM] = config.businessEndTime.split(":").map(Number);
@@ -122,64 +123,202 @@ function computeSlotStatus(params) {
122
123
  return { status: "partially_occupied", remainingCapacity: remaining };
123
124
  return { status: "available", remainingCapacity: remaining };
124
125
  }
126
+ function mergeSubSlots(subSlots) {
127
+ const priority = {
128
+ available: 5,
129
+ partially_occupied: 4,
130
+ occupied: 3,
131
+ unavailable: 2,
132
+ past: 1
133
+ };
134
+ let best = subSlots[0];
135
+ for (const slot of subSlots) {
136
+ if ((priority[slot.status] ?? 0) > (priority[best.status] ?? 0))
137
+ best = slot;
138
+ }
139
+ let minPrice = null;
140
+ for (const slot of subSlots) {
141
+ if (slot.price == null)
142
+ continue;
143
+ const decimal = new import_decimal.default(slot.price || "0");
144
+ if (!minPrice || decimal.lt(minPrice))
145
+ minPrice = decimal;
146
+ }
147
+ return {
148
+ startTime: best.startTime,
149
+ endTime: best.endTime,
150
+ status: best.status,
151
+ price: minPrice ? minPrice.toFixed(2) : null,
152
+ resourceId: best.resourceId,
153
+ resourceFormId: best.resourceFormId,
154
+ capacity: best.capacity,
155
+ remainingCapacity: best.remainingCapacity
156
+ };
157
+ }
158
+ function buildProductSlots(params) {
159
+ const {
160
+ date,
161
+ config,
162
+ resource,
163
+ mapping,
164
+ timeLabels,
165
+ timesForDate,
166
+ events,
167
+ resCapacity,
168
+ now,
169
+ crossDay,
170
+ quotationPriceMap,
171
+ childRawResources,
172
+ childTimesCache,
173
+ childEventsCache,
174
+ config_businessStartHour
175
+ } = params;
176
+ const slots = [];
177
+ for (const label of timeLabels) {
178
+ const [h] = label.split(":").map(Number);
179
+ const isNextDay = crossDay && h < config_businessStartHour;
180
+ const slotDate = isNextDay ? (0, import_dayjs.default)(date).add(1, "day").format("YYYY-MM-DD") : date;
181
+ const slotStart = (0, import_dayjs.default)(`${slotDate} ${label}`);
182
+ const slotEnd = slotStart.add(config.slotDurationMinutes, "minute");
183
+ let status;
184
+ let remainingCapacity = null;
185
+ if (slotStart.isBefore(now)) {
186
+ status = "past";
187
+ } else if (!isSlotWithinResourceTimes(slotStart, slotEnd, timesForDate)) {
188
+ status = "unavailable";
189
+ } else {
190
+ const result = computeSlotStatus({
191
+ slotStart,
192
+ slotEnd,
193
+ events,
194
+ resourceType: mapping.resourceType,
195
+ capacity: resCapacity
196
+ });
197
+ status = result.status;
198
+ remainingCapacity = result.remainingCapacity;
199
+ if (status !== "occupied" && childRawResources && childRawResources.length) {
200
+ for (let i = 0; i < childRawResources.length; i++) {
201
+ const child = childRawResources[i];
202
+ const childTimes = (childTimesCache == null ? void 0 : childTimesCache[i]) ?? getResourceTimesForDate(child, date, config);
203
+ const childEvents = (childEventsCache == null ? void 0 : childEventsCache[i]) ?? collectEventsForDate(child, date, config);
204
+ if (!isSlotWithinResourceTimes(slotStart, slotEnd, childTimes)) {
205
+ status = "unavailable";
206
+ remainingCapacity = null;
207
+ break;
208
+ }
209
+ const childType = child.type || "single";
210
+ const childResult = computeSlotStatus({
211
+ slotStart,
212
+ slotEnd,
213
+ events: childEvents,
214
+ resourceType: childType,
215
+ capacity: child.capacity ?? 1
216
+ });
217
+ if (childResult.status === "occupied") {
218
+ status = "occupied";
219
+ remainingCapacity = 0;
220
+ break;
221
+ }
222
+ if (childResult.status === "partially_occupied" && status === "available") {
223
+ status = "partially_occupied";
224
+ remainingCapacity = Math.min(
225
+ remainingCapacity ?? childResult.remainingCapacity,
226
+ childResult.remainingCapacity
227
+ );
228
+ }
229
+ }
230
+ }
231
+ }
232
+ const isBookable = status === "available" || status === "partially_occupied";
233
+ const slotStartStr = slotStart.format("YYYY-MM-DD HH:mm");
234
+ slots.push({
235
+ startTime: slotStartStr,
236
+ endTime: slotEnd.format("YYYY-MM-DD HH:mm"),
237
+ status,
238
+ price: isBookable ? (quotationPriceMap == null ? void 0 : quotationPriceMap.get(`${mapping.productId}:${slotStartStr}`)) ?? mapping.price : null,
239
+ resourceId: resource.resourceId,
240
+ resourceFormId: resource.formId,
241
+ capacity: status === "past" || status === "unavailable" ? null : resCapacity,
242
+ remainingCapacity,
243
+ productId: mapping.productId
244
+ });
245
+ }
246
+ return slots;
247
+ }
125
248
  function buildTimeSlotGrid(params) {
126
249
  const { date, config, rawResources, resourceProductMap, quotationPriceMap } = params;
127
250
  const timeLabels = generateTimeLabels(config);
128
251
  const now = (0, import_dayjs.default)();
129
252
  const crossDay = isBusinessHoursCrossDay(config);
253
+ const config_businessStartHour = Number(config.businessStartTime.split(":")[0]);
254
+ const rawResourceById = /* @__PURE__ */ new Map();
255
+ for (const item of rawResources)
256
+ rawResourceById.set(item.resourceId, item);
130
257
  const resources = [];
131
258
  for (const resource of rawResources) {
132
- const mapping = resourceProductMap.get(resource.resourceId);
133
- if (!mapping)
259
+ const mappings = resourceProductMap.get(resource.resourceId);
260
+ if (!mappings || !mappings.length)
134
261
  continue;
135
262
  const timesForDate = getResourceTimesForDate(resource, date, config);
136
263
  const events = collectEventsForDate(resource, date, config);
137
264
  const resCapacity = resource.capacity ?? 1;
138
- const slots = [];
139
- for (const label of timeLabels) {
140
- const [h, m] = label.split(":").map(Number);
141
- const isNextDay = crossDay && h < Number(config.businessStartTime.split(":")[0]);
142
- const slotDate = isNextDay ? (0, import_dayjs.default)(date).add(1, "day").format("YYYY-MM-DD") : date;
143
- const slotStart = (0, import_dayjs.default)(`${slotDate} ${label}`);
144
- const slotEnd = slotStart.add(config.slotDurationMinutes, "minute");
145
- let status;
146
- let remainingCapacity = null;
147
- if (slotStart.isBefore(now)) {
148
- status = "past";
149
- } else if (!isSlotWithinResourceTimes(slotStart, slotEnd, timesForDate)) {
150
- status = "unavailable";
151
- } else {
152
- const result = computeSlotStatus({
153
- slotStart,
154
- slotEnd,
155
- events,
156
- resourceType: mapping.resourceType,
157
- capacity: resCapacity
158
- });
159
- status = result.status;
160
- remainingCapacity = result.remainingCapacity;
161
- }
162
- const isBookable = status === "available" || status === "partially_occupied";
163
- const slotStartStr = slotStart.format("YYYY-MM-DD HH:mm");
164
- slots.push({
165
- startTime: slotStartStr,
166
- endTime: slotEnd.format("YYYY-MM-DD HH:mm"),
167
- status,
168
- price: isBookable ? (quotationPriceMap == null ? void 0 : quotationPriceMap.get(`${mapping.productId}:${slotStartStr}`)) ?? mapping.price : null,
169
- resourceId: resource.resourceId,
170
- resourceFormId: resource.formId,
171
- capacity: status === "past" || status === "unavailable" ? null : resCapacity,
172
- remainingCapacity
265
+ const combined = resource.combined_resource;
266
+ const isCombined = !!(combined && combined.status === 1 && Array.isArray(combined.resource_ids) && combined.resource_ids.length);
267
+ const childRawResources = isCombined ? combined.resource_ids.map((id) => rawResourceById.get(id)).filter((r) => !!r) : void 0;
268
+ const childTimesCache = childRawResources == null ? void 0 : childRawResources.map((child) => getResourceTimesForDate(child, date, config));
269
+ const childEventsCache = childRawResources == null ? void 0 : childRawResources.map((child) => collectEventsForDate(child, date, config));
270
+ const subRows = mappings.map((mapping) => {
271
+ const productSlots = buildProductSlots({
272
+ date,
273
+ config,
274
+ resource,
275
+ mapping,
276
+ timeLabels,
277
+ timesForDate,
278
+ events,
279
+ resCapacity,
280
+ now,
281
+ crossDay,
282
+ quotationPriceMap,
283
+ childRawResources,
284
+ childTimesCache,
285
+ childEventsCache,
286
+ config_businessStartHour
173
287
  });
288
+ return {
289
+ productId: mapping.productId,
290
+ productTitle: mapping.productTitle,
291
+ price: mapping.price,
292
+ slots: productSlots
293
+ };
294
+ });
295
+ const hasMultipleProducts = subRows.length > 1;
296
+ const primary = mappings[0];
297
+ let outerSlots;
298
+ if (hasMultipleProducts) {
299
+ outerSlots = timeLabels.map((_label, slotIndex) => {
300
+ const group = subRows.map((row2) => row2.slots[slotIndex]);
301
+ return mergeSubSlots(group);
302
+ });
303
+ } else {
304
+ outerSlots = subRows[0].slots.map((slot) => ({ ...slot, productId: void 0 }));
174
305
  }
175
- resources.push({
306
+ const row = {
176
307
  resourceId: resource.resourceId,
177
308
  resourceFormId: resource.formId,
178
- resourceName: resource.main_field || mapping.resourceName,
179
- productId: mapping.productId,
180
- productTitle: mapping.productTitle,
181
- slots
182
- });
309
+ resourceName: resource.main_field || primary.resourceName,
310
+ productId: primary.productId,
311
+ productTitle: primary.productTitle,
312
+ slots: outerSlots
313
+ };
314
+ if (hasMultipleProducts) {
315
+ row.products = subRows;
316
+ row.hasMultipleProducts = true;
317
+ }
318
+ if (isCombined && combined) {
319
+ row.combinedResource = { resourceIds: [...combined.resource_ids] };
320
+ }
321
+ resources.push(row);
183
322
  }
184
323
  const productOrder = /* @__PURE__ */ new Map();
185
324
  for (const row of resources) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "2.1.129",
4
+ "version": "2.1.131",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",