@pisell/pisellos 0.0.500 → 0.0.502
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/model/strategy/adapter/itemRule/adapter.d.ts +8 -0
- package/dist/model/strategy/adapter/itemRule/adapter.js +52 -8
- package/dist/model/strategy/adapter/itemRule/examples.d.ts +15 -0
- package/dist/model/strategy/adapter/itemRule/examples.js +68 -1
- package/dist/model/strategy/adapter/itemRule/index.d.ts +1 -1
- package/dist/model/strategy/adapter/itemRule/index.js +1 -1
- package/dist/model/strategy/adapter/itemRule/type.d.ts +20 -1
- package/dist/model/strategy/adapter/promotion/index.js +9 -0
- package/dist/modules/Order/index.d.ts +10 -0
- package/dist/modules/Order/index.js +64 -40
- package/dist/modules/Order/types.d.ts +13 -1
- package/dist/modules/Order/utils.d.ts +36 -1
- package/dist/modules/Order/utils.js +120 -24
- package/dist/modules/ProductList/index.js +2 -2
- package/dist/modules/Quotation/index.js +6 -3
- package/dist/modules/SalesSummary/types.d.ts +2 -1
- package/dist/modules/SalesSummary/utils.js +10 -10
- package/dist/modules/Schedule/utils.d.ts +1 -1
- package/dist/solution/ScanOrder/index.d.ts +22 -0
- package/dist/solution/ScanOrder/index.js +863 -369
- package/dist/solution/ScanOrder/types.d.ts +31 -8
- package/dist/solution/ScanOrder/utils.d.ts +26 -0
- package/dist/solution/ScanOrder/utils.js +191 -44
- package/dist/solution/VenueBooking/index.d.ts +42 -5
- package/dist/solution/VenueBooking/index.js +692 -280
- package/dist/solution/VenueBooking/types.d.ts +23 -0
- package/dist/solution/VenueBooking/utils/dateSummary.d.ts +2 -1
- package/dist/solution/VenueBooking/utils/dateSummary.js +7 -5
- package/dist/solution/VenueBooking/utils/resource.d.ts +11 -1
- package/dist/solution/VenueBooking/utils/resource.js +57 -21
- package/dist/solution/VenueBooking/utils/slotMerge.d.ts +5 -0
- package/dist/solution/VenueBooking/utils/slotMerge.js +33 -12
- package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -1
- package/dist/solution/VenueBooking/utils/timeSlot.js +259 -62
- package/lib/model/strategy/adapter/itemRule/adapter.d.ts +8 -0
- package/lib/model/strategy/adapter/itemRule/adapter.js +41 -2
- package/lib/model/strategy/adapter/itemRule/examples.d.ts +15 -0
- package/lib/model/strategy/adapter/itemRule/examples.js +47 -0
- package/lib/model/strategy/adapter/itemRule/index.d.ts +1 -1
- package/lib/model/strategy/adapter/itemRule/index.js +2 -0
- package/lib/model/strategy/adapter/itemRule/type.d.ts +20 -1
- package/lib/modules/Order/index.d.ts +10 -0
- package/lib/modules/Order/index.js +40 -43
- package/lib/modules/Order/types.d.ts +13 -1
- package/lib/modules/Order/utils.d.ts +36 -1
- package/lib/modules/Order/utils.js +100 -21
- package/lib/modules/ProductList/index.js +3 -2
- package/lib/modules/Quotation/index.js +3 -0
- package/lib/modules/SalesSummary/types.d.ts +2 -1
- package/lib/modules/SalesSummary/utils.js +2 -2
- package/lib/modules/Schedule/utils.d.ts +1 -1
- package/lib/solution/ScanOrder/index.d.ts +22 -0
- package/lib/solution/ScanOrder/index.js +376 -21
- package/lib/solution/ScanOrder/types.d.ts +31 -8
- package/lib/solution/ScanOrder/utils.d.ts +26 -0
- package/lib/solution/ScanOrder/utils.js +128 -19
- package/lib/solution/VenueBooking/index.d.ts +42 -5
- package/lib/solution/VenueBooking/index.js +356 -84
- package/lib/solution/VenueBooking/types.d.ts +23 -0
- package/lib/solution/VenueBooking/utils/dateSummary.d.ts +2 -1
- package/lib/solution/VenueBooking/utils/dateSummary.js +14 -5
- package/lib/solution/VenueBooking/utils/resource.d.ts +11 -1
- package/lib/solution/VenueBooking/utils/resource.js +15 -4
- package/lib/solution/VenueBooking/utils/slotMerge.d.ts +5 -0
- package/lib/solution/VenueBooking/utils/slotMerge.js +29 -12
- package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -1
- package/lib/solution/VenueBooking/utils/timeSlot.js +182 -43
- package/package.json +1 -1
|
@@ -1,3 +1,13 @@
|
|
|
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); }
|
|
2
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
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 _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
5
|
+
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
6
|
+
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; }
|
|
7
|
+
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; }
|
|
8
|
+
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; }
|
|
9
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
10
|
+
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
11
|
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; } } }; }
|
|
2
12
|
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
3
13
|
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
@@ -6,6 +16,7 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
|
|
|
6
16
|
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
7
17
|
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
8
18
|
import dayjs from 'dayjs';
|
|
19
|
+
import Decimal from 'decimal.js';
|
|
9
20
|
export function isBusinessHoursCrossDay(config) {
|
|
10
21
|
var _config$businessStart = config.businessStartTime.split(':').map(Number),
|
|
11
22
|
_config$businessStart2 = _slicedToArray(_config$businessStart, 2),
|
|
@@ -152,6 +163,160 @@ export function computeSlotStatus(params) {
|
|
|
152
163
|
remainingCapacity: remaining
|
|
153
164
|
};
|
|
154
165
|
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 合并多个子商品的同一个时段为一个"外层 slot"。
|
|
169
|
+
* - status 取优先级最高的(available > partially_occupied > occupied > unavailable / past)
|
|
170
|
+
* - price 取非空子 slot 中最小的那个
|
|
171
|
+
* - 其它字段使用第一个参考子 slot(外层 slot 不带 productId)
|
|
172
|
+
*/
|
|
173
|
+
function mergeSubSlots(subSlots) {
|
|
174
|
+
var priority = {
|
|
175
|
+
available: 5,
|
|
176
|
+
partially_occupied: 4,
|
|
177
|
+
occupied: 3,
|
|
178
|
+
unavailable: 2,
|
|
179
|
+
past: 1
|
|
180
|
+
};
|
|
181
|
+
var best = subSlots[0];
|
|
182
|
+
var _iterator5 = _createForOfIteratorHelper(subSlots),
|
|
183
|
+
_step5;
|
|
184
|
+
try {
|
|
185
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
186
|
+
var _priority$slot$status, _priority$best$status;
|
|
187
|
+
var slot = _step5.value;
|
|
188
|
+
if (((_priority$slot$status = priority[slot.status]) !== null && _priority$slot$status !== void 0 ? _priority$slot$status : 0) > ((_priority$best$status = priority[best.status]) !== null && _priority$best$status !== void 0 ? _priority$best$status : 0)) best = slot;
|
|
189
|
+
}
|
|
190
|
+
} catch (err) {
|
|
191
|
+
_iterator5.e(err);
|
|
192
|
+
} finally {
|
|
193
|
+
_iterator5.f();
|
|
194
|
+
}
|
|
195
|
+
var minPrice = null;
|
|
196
|
+
var _iterator6 = _createForOfIteratorHelper(subSlots),
|
|
197
|
+
_step6;
|
|
198
|
+
try {
|
|
199
|
+
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
|
|
200
|
+
var _slot = _step6.value;
|
|
201
|
+
if (_slot.price == null) continue;
|
|
202
|
+
var decimal = new Decimal(_slot.price || '0');
|
|
203
|
+
if (!minPrice || decimal.lt(minPrice)) minPrice = decimal;
|
|
204
|
+
}
|
|
205
|
+
} catch (err) {
|
|
206
|
+
_iterator6.e(err);
|
|
207
|
+
} finally {
|
|
208
|
+
_iterator6.f();
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
startTime: best.startTime,
|
|
212
|
+
endTime: best.endTime,
|
|
213
|
+
status: best.status,
|
|
214
|
+
price: minPrice ? minPrice.toFixed(2) : null,
|
|
215
|
+
resourceId: best.resourceId,
|
|
216
|
+
resourceFormId: best.resourceFormId,
|
|
217
|
+
capacity: best.capacity,
|
|
218
|
+
remainingCapacity: best.remainingCapacity
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function buildProductSlots(params) {
|
|
222
|
+
var date = params.date,
|
|
223
|
+
config = params.config,
|
|
224
|
+
resource = params.resource,
|
|
225
|
+
mapping = params.mapping,
|
|
226
|
+
timeLabels = params.timeLabels,
|
|
227
|
+
timesForDate = params.timesForDate,
|
|
228
|
+
events = params.events,
|
|
229
|
+
resCapacity = params.resCapacity,
|
|
230
|
+
now = params.now,
|
|
231
|
+
crossDay = params.crossDay,
|
|
232
|
+
quotationPriceMap = params.quotationPriceMap,
|
|
233
|
+
childRawResources = params.childRawResources,
|
|
234
|
+
childTimesCache = params.childTimesCache,
|
|
235
|
+
childEventsCache = params.childEventsCache,
|
|
236
|
+
config_businessStartHour = params.config_businessStartHour;
|
|
237
|
+
var slots = [];
|
|
238
|
+
var _iterator7 = _createForOfIteratorHelper(timeLabels),
|
|
239
|
+
_step7;
|
|
240
|
+
try {
|
|
241
|
+
for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
|
|
242
|
+
var _quotationPriceMap$ge;
|
|
243
|
+
var label = _step7.value;
|
|
244
|
+
var _label$split$map = label.split(':').map(Number),
|
|
245
|
+
_label$split$map2 = _slicedToArray(_label$split$map, 1),
|
|
246
|
+
h = _label$split$map2[0];
|
|
247
|
+
var isNextDay = crossDay && h < config_businessStartHour;
|
|
248
|
+
var slotDate = isNextDay ? dayjs(date).add(1, 'day').format('YYYY-MM-DD') : date;
|
|
249
|
+
var slotStart = dayjs("".concat(slotDate, " ").concat(label));
|
|
250
|
+
var slotEnd = slotStart.add(config.slotDurationMinutes, 'minute');
|
|
251
|
+
var status = void 0;
|
|
252
|
+
var remainingCapacity = null;
|
|
253
|
+
if (slotStart.isBefore(now)) {
|
|
254
|
+
status = 'past';
|
|
255
|
+
} else if (!isSlotWithinResourceTimes(slotStart, slotEnd, timesForDate)) {
|
|
256
|
+
status = 'unavailable';
|
|
257
|
+
} else {
|
|
258
|
+
var result = computeSlotStatus({
|
|
259
|
+
slotStart: slotStart,
|
|
260
|
+
slotEnd: slotEnd,
|
|
261
|
+
events: events,
|
|
262
|
+
resourceType: mapping.resourceType,
|
|
263
|
+
capacity: resCapacity
|
|
264
|
+
});
|
|
265
|
+
status = result.status;
|
|
266
|
+
remainingCapacity = result.remainingCapacity;
|
|
267
|
+
if (status !== 'occupied' && childRawResources && childRawResources.length) {
|
|
268
|
+
for (var i = 0; i < childRawResources.length; i++) {
|
|
269
|
+
var _childTimesCache$i, _childEventsCache$i, _child$capacity;
|
|
270
|
+
var child = childRawResources[i];
|
|
271
|
+
var childTimes = (_childTimesCache$i = childTimesCache === null || childTimesCache === void 0 ? void 0 : childTimesCache[i]) !== null && _childTimesCache$i !== void 0 ? _childTimesCache$i : getResourceTimesForDate(child, date, config);
|
|
272
|
+
var childEvents = (_childEventsCache$i = childEventsCache === null || childEventsCache === void 0 ? void 0 : childEventsCache[i]) !== null && _childEventsCache$i !== void 0 ? _childEventsCache$i : collectEventsForDate(child, date, config);
|
|
273
|
+
if (!isSlotWithinResourceTimes(slotStart, slotEnd, childTimes)) {
|
|
274
|
+
status = 'unavailable';
|
|
275
|
+
remainingCapacity = null;
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
var childType = child.type || 'single';
|
|
279
|
+
var childResult = computeSlotStatus({
|
|
280
|
+
slotStart: slotStart,
|
|
281
|
+
slotEnd: slotEnd,
|
|
282
|
+
events: childEvents,
|
|
283
|
+
resourceType: childType,
|
|
284
|
+
capacity: (_child$capacity = child.capacity) !== null && _child$capacity !== void 0 ? _child$capacity : 1
|
|
285
|
+
});
|
|
286
|
+
if (childResult.status === 'occupied') {
|
|
287
|
+
status = 'occupied';
|
|
288
|
+
remainingCapacity = 0;
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
if (childResult.status === 'partially_occupied' && status === 'available') {
|
|
292
|
+
var _remainingCapacity;
|
|
293
|
+
status = 'partially_occupied';
|
|
294
|
+
remainingCapacity = Math.min((_remainingCapacity = remainingCapacity) !== null && _remainingCapacity !== void 0 ? _remainingCapacity : childResult.remainingCapacity, childResult.remainingCapacity);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
var isBookable = status === 'available' || status === 'partially_occupied';
|
|
300
|
+
var slotStartStr = slotStart.format('YYYY-MM-DD HH:mm');
|
|
301
|
+
slots.push({
|
|
302
|
+
startTime: slotStartStr,
|
|
303
|
+
endTime: slotEnd.format('YYYY-MM-DD HH:mm'),
|
|
304
|
+
status: status,
|
|
305
|
+
price: isBookable ? (_quotationPriceMap$ge = quotationPriceMap === null || quotationPriceMap === void 0 ? void 0 : quotationPriceMap.get("".concat(mapping.productId, ":").concat(slotStartStr))) !== null && _quotationPriceMap$ge !== void 0 ? _quotationPriceMap$ge : mapping.price : null,
|
|
306
|
+
resourceId: resource.resourceId,
|
|
307
|
+
resourceFormId: resource.formId,
|
|
308
|
+
capacity: status === 'past' || status === 'unavailable' ? null : resCapacity,
|
|
309
|
+
remainingCapacity: remainingCapacity,
|
|
310
|
+
productId: mapping.productId
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
} catch (err) {
|
|
314
|
+
_iterator7.e(err);
|
|
315
|
+
} finally {
|
|
316
|
+
_iterator7.f();
|
|
317
|
+
}
|
|
318
|
+
return slots;
|
|
319
|
+
}
|
|
155
320
|
export function buildTimeSlotGrid(params) {
|
|
156
321
|
var date = params.date,
|
|
157
322
|
config = params.config,
|
|
@@ -161,83 +326,115 @@ export function buildTimeSlotGrid(params) {
|
|
|
161
326
|
var timeLabels = generateTimeLabels(config);
|
|
162
327
|
var now = dayjs();
|
|
163
328
|
var crossDay = isBusinessHoursCrossDay(config);
|
|
329
|
+
var config_businessStartHour = Number(config.businessStartTime.split(':')[0]);
|
|
330
|
+
var rawResourceById = new Map();
|
|
331
|
+
var _iterator8 = _createForOfIteratorHelper(rawResources),
|
|
332
|
+
_step8;
|
|
333
|
+
try {
|
|
334
|
+
for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
|
|
335
|
+
var item = _step8.value;
|
|
336
|
+
rawResourceById.set(item.resourceId, item);
|
|
337
|
+
}
|
|
338
|
+
} catch (err) {
|
|
339
|
+
_iterator8.e(err);
|
|
340
|
+
} finally {
|
|
341
|
+
_iterator8.f();
|
|
342
|
+
}
|
|
164
343
|
var resources = [];
|
|
165
|
-
var
|
|
166
|
-
|
|
344
|
+
var _iterator9 = _createForOfIteratorHelper(rawResources),
|
|
345
|
+
_step9;
|
|
167
346
|
try {
|
|
168
|
-
|
|
347
|
+
var _loop = function _loop() {
|
|
169
348
|
var _resource$capacity;
|
|
170
|
-
var resource =
|
|
171
|
-
var
|
|
172
|
-
if (!
|
|
349
|
+
var resource = _step9.value;
|
|
350
|
+
var mappings = resourceProductMap.get(resource.resourceId);
|
|
351
|
+
if (!mappings || !mappings.length) return 1; // continue
|
|
173
352
|
var timesForDate = getResourceTimesForDate(resource, date, config);
|
|
174
353
|
var events = collectEventsForDate(resource, date, config);
|
|
175
354
|
var resCapacity = (_resource$capacity = resource.capacity) !== null && _resource$capacity !== void 0 ? _resource$capacity : 1;
|
|
176
|
-
var
|
|
177
|
-
var
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
355
|
+
var combined = resource.combined_resource;
|
|
356
|
+
var isCombined = !!(combined && combined.status === 1 && Array.isArray(combined.resource_ids) && combined.resource_ids.length);
|
|
357
|
+
var childRawResources = isCombined ? combined.resource_ids.map(function (id) {
|
|
358
|
+
return rawResourceById.get(id);
|
|
359
|
+
}).filter(function (r) {
|
|
360
|
+
return !!r;
|
|
361
|
+
}) : undefined;
|
|
362
|
+
var childTimesCache = childRawResources === null || childRawResources === void 0 ? void 0 : childRawResources.map(function (child) {
|
|
363
|
+
return getResourceTimesForDate(child, date, config);
|
|
364
|
+
});
|
|
365
|
+
var childEventsCache = childRawResources === null || childRawResources === void 0 ? void 0 : childRawResources.map(function (child) {
|
|
366
|
+
return collectEventsForDate(child, date, config);
|
|
367
|
+
});
|
|
368
|
+
var subRows = mappings.map(function (mapping) {
|
|
369
|
+
var productSlots = buildProductSlots({
|
|
370
|
+
date: date,
|
|
371
|
+
config: config,
|
|
372
|
+
resource: resource,
|
|
373
|
+
mapping: mapping,
|
|
374
|
+
timeLabels: timeLabels,
|
|
375
|
+
timesForDate: timesForDate,
|
|
376
|
+
events: events,
|
|
377
|
+
resCapacity: resCapacity,
|
|
378
|
+
now: now,
|
|
379
|
+
crossDay: crossDay,
|
|
380
|
+
quotationPriceMap: quotationPriceMap,
|
|
381
|
+
childRawResources: childRawResources,
|
|
382
|
+
childTimesCache: childTimesCache,
|
|
383
|
+
childEventsCache: childEventsCache,
|
|
384
|
+
config_businessStartHour: config_businessStartHour
|
|
385
|
+
});
|
|
386
|
+
return {
|
|
387
|
+
productId: mapping.productId,
|
|
388
|
+
productTitle: mapping.productTitle,
|
|
389
|
+
price: mapping.price,
|
|
390
|
+
slots: productSlots
|
|
391
|
+
};
|
|
392
|
+
});
|
|
393
|
+
var hasMultipleProducts = subRows.length > 1;
|
|
394
|
+
var primary = mappings[0];
|
|
395
|
+
var outerSlots;
|
|
396
|
+
if (hasMultipleProducts) {
|
|
397
|
+
outerSlots = timeLabels.map(function (_label, slotIndex) {
|
|
398
|
+
var group = subRows.map(function (row) {
|
|
399
|
+
return row.slots[slotIndex];
|
|
219
400
|
});
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
401
|
+
return mergeSubSlots(group);
|
|
402
|
+
});
|
|
403
|
+
} else {
|
|
404
|
+
outerSlots = subRows[0].slots.map(function (slot) {
|
|
405
|
+
return _objectSpread(_objectSpread({}, slot), {}, {
|
|
406
|
+
productId: undefined
|
|
407
|
+
});
|
|
408
|
+
});
|
|
225
409
|
}
|
|
226
|
-
|
|
410
|
+
var row = {
|
|
227
411
|
resourceId: resource.resourceId,
|
|
228
412
|
resourceFormId: resource.formId,
|
|
229
|
-
resourceName: resource.main_field ||
|
|
230
|
-
productId:
|
|
231
|
-
productTitle:
|
|
232
|
-
slots:
|
|
233
|
-
}
|
|
413
|
+
resourceName: resource.main_field || primary.resourceName,
|
|
414
|
+
productId: primary.productId,
|
|
415
|
+
productTitle: primary.productTitle,
|
|
416
|
+
slots: outerSlots
|
|
417
|
+
};
|
|
418
|
+
if (hasMultipleProducts) {
|
|
419
|
+
row.products = subRows;
|
|
420
|
+
row.hasMultipleProducts = true;
|
|
421
|
+
}
|
|
422
|
+
if (isCombined && combined) {
|
|
423
|
+
row.combinedResource = {
|
|
424
|
+
resourceIds: _toConsumableArray(combined.resource_ids)
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
resources.push(row);
|
|
428
|
+
};
|
|
429
|
+
for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
|
|
430
|
+
if (_loop()) continue;
|
|
234
431
|
}
|
|
235
432
|
|
|
236
433
|
// 按 productId 分组排序,确保同一商品的场地连续排列
|
|
237
434
|
} catch (err) {
|
|
238
|
-
|
|
435
|
+
_iterator9.e(err);
|
|
239
436
|
} finally {
|
|
240
|
-
|
|
437
|
+
_iterator9.f();
|
|
241
438
|
}
|
|
242
439
|
var productOrder = new Map();
|
|
243
440
|
for (var _i = 0, _resources = resources; _i < _resources.length; _i++) {
|
|
@@ -50,6 +50,14 @@ export declare class ItemRuleAdapter implements BusinessAdapter {
|
|
|
50
50
|
* 不适用或无配置时返回 null。
|
|
51
51
|
*/
|
|
52
52
|
extractQuantityLimits(action: ActionEffect, businessData: ItemRuleBusinessData): QuantityLimitResult | null;
|
|
53
|
+
/**
|
|
54
|
+
* 计算当前提交剩余可选的最大数量。
|
|
55
|
+
*
|
|
56
|
+
* 仅在 scope='cumulative' 且存在 requiredMax 时生效:
|
|
57
|
+
* 用 requiredMax 减去 historicalItems 中目标商品的累计数量,保底 0。
|
|
58
|
+
* 其余场景返回 undefined,保持向后兼容。
|
|
59
|
+
*/
|
|
60
|
+
private calculateRemainingMax;
|
|
53
61
|
private processPrefillCart;
|
|
54
62
|
/**
|
|
55
63
|
* 根据 quantityFrom 计算预填数量
|
|
@@ -250,7 +250,7 @@ var ItemRuleAdapter = class {
|
|
|
250
250
|
const config = action.config;
|
|
251
251
|
if (!config)
|
|
252
252
|
return null;
|
|
253
|
-
const { scope, quantityRules, targetType, targets } = config;
|
|
253
|
+
const { scope, quantityRules, targetType, targets, validationMessage } = config;
|
|
254
254
|
if (!this.shouldEvaluateForScope(scope, businessData.submissionIndex ?? 0)) {
|
|
255
255
|
return null;
|
|
256
256
|
}
|
|
@@ -283,17 +283,56 @@ var ItemRuleAdapter = class {
|
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
285
|
}
|
|
286
|
+
const remainingMax = this.calculateRemainingMax(
|
|
287
|
+
scope,
|
|
288
|
+
requiredMax,
|
|
289
|
+
targets,
|
|
290
|
+
businessData
|
|
291
|
+
);
|
|
292
|
+
const formattedValidationMessage = this.formatValidationMessage(
|
|
293
|
+
validationMessage,
|
|
294
|
+
requiredMin,
|
|
295
|
+
requiredMax,
|
|
296
|
+
requiredExact
|
|
297
|
+
);
|
|
286
298
|
return {
|
|
287
299
|
ruleId: action.id,
|
|
288
300
|
targetType,
|
|
289
301
|
targets,
|
|
290
302
|
requiredMin,
|
|
291
303
|
requiredMax,
|
|
304
|
+
remainingMax,
|
|
292
305
|
requiredExact,
|
|
293
306
|
mustInclude,
|
|
294
|
-
scope
|
|
307
|
+
scope,
|
|
308
|
+
validationMessage: formattedValidationMessage,
|
|
309
|
+
rawValidationMessage: validationMessage
|
|
295
310
|
};
|
|
296
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* 计算当前提交剩余可选的最大数量。
|
|
314
|
+
*
|
|
315
|
+
* 仅在 scope='cumulative' 且存在 requiredMax 时生效:
|
|
316
|
+
* 用 requiredMax 减去 historicalItems 中目标商品的累计数量,保底 0。
|
|
317
|
+
* 其余场景返回 undefined,保持向后兼容。
|
|
318
|
+
*/
|
|
319
|
+
calculateRemainingMax(scope, requiredMax, targets, businessData) {
|
|
320
|
+
if (scope !== "cumulative" || typeof requiredMax !== "number") {
|
|
321
|
+
return void 0;
|
|
322
|
+
}
|
|
323
|
+
const historicalItems = businessData.historicalItems;
|
|
324
|
+
if (!(historicalItems == null ? void 0 : historicalItems.length)) {
|
|
325
|
+
return Math.max(0, requiredMax);
|
|
326
|
+
}
|
|
327
|
+
const targetIds = new Set(targets.map((t) => t.product_id));
|
|
328
|
+
let historicalQty = 0;
|
|
329
|
+
for (const item of historicalItems) {
|
|
330
|
+
if (targetIds.has(item.product_id)) {
|
|
331
|
+
historicalQty += item.quantity;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return Math.max(0, requiredMax - historicalQty);
|
|
335
|
+
}
|
|
297
336
|
// ============================================
|
|
298
337
|
// Prefill Cart 处理
|
|
299
338
|
// ============================================
|
|
@@ -25,6 +25,21 @@ export declare const MAX_BUNS_PER_TABLE_STRATEGY: StrategyConfig;
|
|
|
25
25
|
* - Prefill Cart: 自动添加默认锅底 x 1
|
|
26
26
|
*/
|
|
27
27
|
export declare const HOTPOT_BASE_REQUIRED_STRATEGY: StrategyConfig;
|
|
28
|
+
/**
|
|
29
|
+
* 场景:4 款锅底里任选至少 1 款,不自动预填,不限制多选上限
|
|
30
|
+
*
|
|
31
|
+
* WHEN: Always active
|
|
32
|
+
* THEN:
|
|
33
|
+
* - Quantity Check: 4 个锅底商品合计数量 >= 1 (OR 组语义)
|
|
34
|
+
*
|
|
35
|
+
* 说明:
|
|
36
|
+
* - targets 里放多个 product_id 时,ItemRuleAdapter 会把它们视为 OR 组,
|
|
37
|
+
* 仅校验「合计数量」,不会要求每款各自满足 min。
|
|
38
|
+
* - ScanOrder 工具层 buildQuantityLimitIndex 会跳过多目标规则的
|
|
39
|
+
* 单品索引,避免 UI 把组级 min 误当作每张商品卡的减号下限,
|
|
40
|
+
* 锁死用户切换锅底的操作。
|
|
41
|
+
*/
|
|
42
|
+
export declare const HOTPOT_BASE_PICK_ONE_STRATEGY: StrategyConfig;
|
|
28
43
|
/**
|
|
29
44
|
* ItemRuleEvaluator 简洁调用示例
|
|
30
45
|
*
|
|
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
// src/model/strategy/adapter/itemRule/examples.ts
|
|
20
20
|
var examples_exports = {};
|
|
21
21
|
__export(examples_exports, {
|
|
22
|
+
HOTPOT_BASE_PICK_ONE_STRATEGY: () => HOTPOT_BASE_PICK_ONE_STRATEGY,
|
|
22
23
|
HOTPOT_BASE_REQUIRED_STRATEGY: () => HOTPOT_BASE_REQUIRED_STRATEGY,
|
|
23
24
|
MAX_BUNS_PER_TABLE_STRATEGY: () => MAX_BUNS_PER_TABLE_STRATEGY,
|
|
24
25
|
MIN_CONDIMENT_PER_PERSON_STRATEGY: () => MIN_CONDIMENT_PER_PERSON_STRATEGY,
|
|
@@ -190,6 +191,51 @@ var HOTPOT_BASE_REQUIRED_STRATEGY = {
|
|
|
190
191
|
}
|
|
191
192
|
]
|
|
192
193
|
};
|
|
194
|
+
var HOTPOT_BASE_PICK_ONE_STRATEGY = {
|
|
195
|
+
metadata: {
|
|
196
|
+
id: "RULE_HOTPOT_BASE_PICK_ONE",
|
|
197
|
+
name: {
|
|
198
|
+
"zh-CN": "锅底必选(四选一)",
|
|
199
|
+
en: "Hotpot base required (pick any)"
|
|
200
|
+
},
|
|
201
|
+
type: "item_rule",
|
|
202
|
+
description: {
|
|
203
|
+
"zh-CN": "4 款锅底任选至少 1 份",
|
|
204
|
+
en: "Pick at least 1 hotpot base from the available options"
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
conditions: {
|
|
208
|
+
operator: "and",
|
|
209
|
+
rules: [],
|
|
210
|
+
actionIds: ["quantity_check_hotpot_base_group"]
|
|
211
|
+
},
|
|
212
|
+
actions: [
|
|
213
|
+
{
|
|
214
|
+
id: "quantity_check_hotpot_base_group",
|
|
215
|
+
type: import_type.ITEM_RULE_ACTION_TYPES.QUANTITY_CHECK,
|
|
216
|
+
value: null,
|
|
217
|
+
target: "cart",
|
|
218
|
+
priority: 10,
|
|
219
|
+
config: {
|
|
220
|
+
targetType: "product",
|
|
221
|
+
targets: [
|
|
222
|
+
{ product_id: 30001 },
|
|
223
|
+
{ product_id: 30002 },
|
|
224
|
+
{ product_id: 30003 },
|
|
225
|
+
{ product_id: 30004 }
|
|
226
|
+
],
|
|
227
|
+
quantityRules: [
|
|
228
|
+
{ comparison: "minimum", source: "fixed", value: 1 }
|
|
229
|
+
],
|
|
230
|
+
scope: "first_submission_only",
|
|
231
|
+
validationMessage: {
|
|
232
|
+
"zh-CN": "请至少选择 {min} 款锅底",
|
|
233
|
+
en: "Please select at least {min} hotpot base"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
]
|
|
238
|
+
};
|
|
193
239
|
function quickUseItemRuleEvaluator() {
|
|
194
240
|
const evaluator = new import_evaluator.ItemRuleEvaluator();
|
|
195
241
|
evaluator.setStrategyConfigs([
|
|
@@ -261,6 +307,7 @@ function runItemRuleEvaluatorDemo() {
|
|
|
261
307
|
}
|
|
262
308
|
// Annotate the CommonJS export names for ESM import in node:
|
|
263
309
|
0 && (module.exports = {
|
|
310
|
+
HOTPOT_BASE_PICK_ONE_STRATEGY,
|
|
264
311
|
HOTPOT_BASE_REQUIRED_STRATEGY,
|
|
265
312
|
MAX_BUNS_PER_TABLE_STRATEGY,
|
|
266
313
|
MIN_CONDIMENT_PER_PERSON_STRATEGY,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ItemRuleEvaluator } from './evaluator';
|
|
2
2
|
export { ItemRuleAdapter } from './adapter';
|
|
3
3
|
export { default } from './adapter';
|
|
4
|
-
export { MIN_CONDIMENT_PER_PERSON_STRATEGY, MAX_BUNS_PER_TABLE_STRATEGY, HOTPOT_BASE_REQUIRED_STRATEGY, quickUseItemRuleEvaluator, runItemRuleEvaluatorDemo, } from './examples';
|
|
4
|
+
export { MIN_CONDIMENT_PER_PERSON_STRATEGY, MAX_BUNS_PER_TABLE_STRATEGY, HOTPOT_BASE_REQUIRED_STRATEGY, HOTPOT_BASE_PICK_ONE_STRATEGY, quickUseItemRuleEvaluator, runItemRuleEvaluatorDemo, } from './examples';
|
|
5
5
|
export * from './type';
|
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/model/strategy/adapter/itemRule/index.ts
|
|
31
31
|
var itemRule_exports = {};
|
|
32
32
|
__export(itemRule_exports, {
|
|
33
|
+
HOTPOT_BASE_PICK_ONE_STRATEGY: () => import_examples.HOTPOT_BASE_PICK_ONE_STRATEGY,
|
|
33
34
|
HOTPOT_BASE_REQUIRED_STRATEGY: () => import_examples.HOTPOT_BASE_REQUIRED_STRATEGY,
|
|
34
35
|
ItemRuleAdapter: () => import_adapter.ItemRuleAdapter,
|
|
35
36
|
ItemRuleEvaluator: () => import_evaluator.ItemRuleEvaluator,
|
|
@@ -47,6 +48,7 @@ var import_examples = require("./examples");
|
|
|
47
48
|
__reExport(itemRule_exports, require("./type"), module.exports);
|
|
48
49
|
// Annotate the CommonJS export names for ESM import in node:
|
|
49
50
|
0 && (module.exports = {
|
|
51
|
+
HOTPOT_BASE_PICK_ONE_STRATEGY,
|
|
50
52
|
HOTPOT_BASE_REQUIRED_STRATEGY,
|
|
51
53
|
ItemRuleAdapter,
|
|
52
54
|
ItemRuleEvaluator,
|
|
@@ -180,14 +180,33 @@ export interface QuantityLimitResult {
|
|
|
180
180
|
targets: TargetItem[];
|
|
181
181
|
/** 要求的最小数量 */
|
|
182
182
|
requiredMin?: number;
|
|
183
|
-
/**
|
|
183
|
+
/** 要求的最大数量(规则配置的原始上限,用于 UI 文案展示) */
|
|
184
184
|
requiredMax?: number;
|
|
185
|
+
/**
|
|
186
|
+
* 当前提交剩余可选的最大数量。
|
|
187
|
+
*
|
|
188
|
+
* 仅在 scope='cumulative' 且存在 requiredMax 时计算:
|
|
189
|
+
* remainingMax = max(0, requiredMax - historicalItems 中目标商品累计数量)
|
|
190
|
+
*
|
|
191
|
+
* 非 cumulative 场景不设置此字段;调用方用于限制加购上限时,
|
|
192
|
+
* 应优先读取本字段,未定义时回退到 requiredMax。
|
|
193
|
+
*/
|
|
194
|
+
remainingMax?: number;
|
|
185
195
|
/** 要求的精确数量 */
|
|
186
196
|
requiredExact?: number;
|
|
187
197
|
/** 是否必须包含 */
|
|
188
198
|
mustInclude?: boolean;
|
|
189
199
|
/** 作用范围 */
|
|
190
200
|
scope: RuleScope;
|
|
201
|
+
/**
|
|
202
|
+
* 校验失败时的提示语(已做 {min}/{max}/{exact} 占位符替换)。
|
|
203
|
+
* 当 rawValidationMessage 为多语言对象时,后端不做 locale 选择,
|
|
204
|
+
* 默认按 en → Object.values()[0] 的顺序兜底,前端可再根据自身 locale
|
|
205
|
+
* 读取 rawValidationMessage 做精确匹配。
|
|
206
|
+
*/
|
|
207
|
+
validationMessage?: string;
|
|
208
|
+
/** 原始多语言模板,前端可根据自身 locale 做二次选择 */
|
|
209
|
+
rawValidationMessage?: string | Record<string, string>;
|
|
191
210
|
}
|
|
192
211
|
/**
|
|
193
212
|
* ItemRule 评估器的完整输出结果
|
|
@@ -2,6 +2,7 @@ import { Module, PisellCore, ModuleOptions } from '../../types';
|
|
|
2
2
|
import { BaseModule } from '../BaseModule';
|
|
3
3
|
import { OrderModuleAPI, CommitOrderParams, SubmitScanOrderParams, ScanOrderMoreParams, CheckoutOrderParams } from './types';
|
|
4
4
|
import { CartItem } from '../Cart/types';
|
|
5
|
+
import { type SubmitPayloadEnhancer } from './utils';
|
|
5
6
|
import type { ScanOrderOrderProduct, ScanOrderOrderProductIdentity, ScanOrderSummary, ScanOrderTempOrder } from '../../solution/ScanOrder/types';
|
|
6
7
|
import type { Discount } from '../Discount/types';
|
|
7
8
|
import { UnavailableReason } from '../Rules/types';
|
|
@@ -57,6 +58,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
57
58
|
persistTempOrder(): void;
|
|
58
59
|
private createDefaultTempOrderInstance;
|
|
59
60
|
ensureTempOrder(): ScanOrderTempOrder;
|
|
61
|
+
restoreOrder(): ScanOrderTempOrder;
|
|
60
62
|
getTempOrder(): ScanOrderTempOrder | null;
|
|
61
63
|
addNewOrder(): Promise<ScanOrderTempOrder>;
|
|
62
64
|
getOrderProducts(): ScanOrderOrderProduct[];
|
|
@@ -66,6 +68,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
66
68
|
}): Promise<ScanOrderSummary | null>;
|
|
67
69
|
getScanOrderSummary(): Promise<ScanOrderSummary>;
|
|
68
70
|
updateTempOrderNote(note: string): string;
|
|
71
|
+
updateTempOrderBuzzer(buzzer: string): string;
|
|
69
72
|
updateTempOrderContactsInfo(contactsInfo: any[]): any[];
|
|
70
73
|
addProductToOrder(product: Partial<ScanOrderOrderProduct> & ScanOrderOrderProductIdentity): Promise<ScanOrderOrderProduct[]>;
|
|
71
74
|
updateProductInOrder(params: {
|
|
@@ -76,6 +79,11 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
76
79
|
removeProductFromOrder(identity: ScanOrderOrderProductIdentity): Promise<ScanOrderOrderProduct[]>;
|
|
77
80
|
submitTempOrder<T = any>(params?: {
|
|
78
81
|
cacheId?: string;
|
|
82
|
+
platform?: string;
|
|
83
|
+
businessCode?: string;
|
|
84
|
+
channel?: string;
|
|
85
|
+
type?: string;
|
|
86
|
+
enhancePayload?: SubmitPayloadEnhancer;
|
|
79
87
|
}): Promise<T>;
|
|
80
88
|
createOrder(params: CommitOrderParams['query']): {
|
|
81
89
|
type: "virtual" | "appointment_booking";
|
|
@@ -108,4 +116,6 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
108
116
|
createOrderByCheckout(params: CheckoutOrderParams): Promise<any>;
|
|
109
117
|
submitScanOrder<T = any>(params: SubmitScanOrderParams): Promise<T>;
|
|
110
118
|
scanOrderMore<T = any>(params: ScanOrderMoreParams): Promise<T>;
|
|
119
|
+
getOrderInfoByRemote(order_id: number): Promise<any>;
|
|
120
|
+
getLastOrderInfo(): Record<string, any> | undefined;
|
|
111
121
|
}
|