@pisell/pisellos 2.1.175 → 2.1.176

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.
@@ -119,6 +119,8 @@ export declare class ScanOrderImpl extends BaseModule implements Module {
119
119
  private validateBeforeSubmitByItemRule;
120
120
  private refreshCartValidationPassed;
121
121
  setItemRuleRuntimeConfig(config?: ScanOrderItemRuleRuntimeConfig): Promise<void>;
122
+ /** 只根据已加载的桌台和日程计算当前可用性,不改变数据加载与缓存行为。 */
123
+ private checkResourceSchedule;
122
124
  private normalizeResourceState;
123
125
  private resolveResourceSelectType;
124
126
  private fetchResourceOccupyDetailsByResourceId;
@@ -38,6 +38,7 @@ import { ProductList } from "../../modules/ProductList";
38
38
  import { ScheduleModule } from "../../modules/Schedule";
39
39
  import { getDateIsInSchedule } from "../../modules/Schedule/getDateIsInSchedule";
40
40
  import { composeLinePrice, createUuidV4, normalizeSubmitCollectPaxValue, sumOptionUnitPrice } from "../../modules/Order/utils";
41
+ import { evaluateResourceSchedules } from "./resourceSchedule";
41
42
  import dayjs from 'dayjs';
42
43
  import { ItemRuleEvaluator } from "../../model/strategy/adapter/itemRule";
43
44
  export * from "./types";
@@ -1559,7 +1560,7 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
1559
1560
  key: "submitScanOrder",
1560
1561
  value: function () {
1561
1562
  var _submitScanOrder = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee18() {
1562
- var _this$store$resource, _this$otherParams6, _this$otherParams7, _this$otherParams8, _this$otherParams9, _tempOrder$products, tempOrderForSubmit, resourceTableName, enhancePayload, result, tempOrder;
1563
+ var _this$store$resource, _this$store$resource2, _this$otherParams6, _this$otherParams7, _this$otherParams8, _this$otherParams9, _tempOrder$products, scheduleBlocked, tempOrderForSubmit, resourceTableName, enhancePayload, result, tempOrder;
1563
1564
  return _regeneratorRuntime().wrap(function _callee18$(_context18) {
1564
1565
  while (1) switch (_context18.prev = _context18.next) {
1565
1566
  case 0:
@@ -1574,9 +1575,16 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
1574
1575
  }
1575
1576
  throw new Error('scanOrder解决方案需要 order 模块支持');
1576
1577
  case 6:
1578
+ scheduleBlocked = this.checkResourceSchedule((_this$store$resource = this.store.resource) === null || _this$store$resource === void 0 ? void 0 : _this$store$resource.table_form_record);
1579
+ if (!scheduleBlocked) {
1580
+ _context18.next = 9;
1581
+ break;
1582
+ }
1583
+ throw new Error(scheduleBlocked.errorTips);
1584
+ case 9:
1577
1585
  tempOrderForSubmit = this.store.order.ensureTempOrder();
1578
1586
  tempOrderForSubmit.delivery_type = 'shop_service';
1579
- resourceTableName = (_this$store$resource = this.store.resource) === null || _this$store$resource === void 0 || (_this$store$resource = _this$store$resource.table_form_record) === null || _this$store$resource === void 0 ? void 0 : _this$store$resource.name;
1587
+ resourceTableName = (_this$store$resource2 = this.store.resource) === null || _this$store$resource2 === void 0 || (_this$store$resource2 = _this$store$resource2.table_form_record) === null || _this$store$resource2 === void 0 ? void 0 : _this$store$resource2.name;
1580
1588
  if (resourceTableName && _typeof(resourceTableName) === 'object') {
1581
1589
  tempOrderForSubmit.table_number = resourceTableName;
1582
1590
  } else {
@@ -1584,7 +1592,7 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
1584
1592
  }
1585
1593
  this.store.order.persistTempOrder();
1586
1594
  enhancePayload = this.buildSubmitPayloadEnhancer();
1587
- _context18.next = 14;
1595
+ _context18.next = 17;
1588
1596
  return this.store.order.submitTempOrder({
1589
1597
  cacheId: this.cacheId,
1590
1598
  platform: (_this$otherParams6 = this.otherParams) === null || _this$otherParams6 === void 0 ? void 0 : _this$otherParams6.platform,
@@ -1593,23 +1601,23 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
1593
1601
  type: (_this$otherParams9 = this.otherParams) === null || _this$otherParams9 === void 0 ? void 0 : _this$otherParams9.type,
1594
1602
  enhancePayload: enhancePayload
1595
1603
  });
1596
- case 14:
1604
+ case 17:
1597
1605
  result = _context18.sent;
1598
1606
  tempOrder = this.store.order.getTempOrder();
1599
1607
  this.logMethodSuccess('submitScanOrder', {
1600
1608
  productCount: (tempOrder === null || tempOrder === void 0 || (_tempOrder$products = tempOrder.products) === null || _tempOrder$products === void 0 ? void 0 : _tempOrder$products.length) || 0
1601
1609
  });
1602
1610
  return _context18.abrupt("return", result);
1603
- case 20:
1604
- _context18.prev = 20;
1611
+ case 23:
1612
+ _context18.prev = 23;
1605
1613
  _context18.t0 = _context18["catch"](1);
1606
1614
  this.logMethodError('submitScanOrder', _context18.t0);
1607
1615
  throw _context18.t0;
1608
- case 24:
1616
+ case 27:
1609
1617
  case "end":
1610
1618
  return _context18.stop();
1611
1619
  }
1612
- }, _callee18, this, [[1, 20]]);
1620
+ }, _callee18, this, [[1, 23]]);
1613
1621
  }));
1614
1622
  function submitScanOrder() {
1615
1623
  return _submitScanOrder.apply(this, arguments);
@@ -2516,7 +2524,29 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
2516
2524
  return _setItemRuleRuntimeConfig.apply(this, arguments);
2517
2525
  }
2518
2526
  return setItemRuleRuntimeConfig;
2519
- }()
2527
+ }() /** 只根据已加载的桌台和日程计算当前可用性,不改变数据加载与缓存行为。 */
2528
+ }, {
2529
+ key: "checkResourceSchedule",
2530
+ value: function checkResourceSchedule(formRecord) {
2531
+ var _this$core$getPlugin, _this$store$schedule, _coreState$core, _coreState$core2;
2532
+ var ids = formRecord === null || formRecord === void 0 ? void 0 : formRecord.schedule;
2533
+ if (!Array.isArray(ids) || !ids.length) return null;
2534
+ var coreState = (_this$core$getPlugin = this.core.getPlugin('shopStore')) === null || _this$core$getPlugin === void 0 ? void 0 : _this$core$getPlugin.get('core');
2535
+ var schedules = ((_this$store$schedule = this.store.schedule) === null || _this$store$schedule === void 0 ? void 0 : _this$store$schedule.getScheduleListByIds(ids.map(Number))) || [];
2536
+ var result = evaluateResourceSchedules({
2537
+ schedules: schedules,
2538
+ timeZone: coreState === null || coreState === void 0 || (_coreState$core = coreState.core) === null || _coreState$core === void 0 || (_coreState$core = _coreState$core.shop) === null || _coreState$core === void 0 ? void 0 : _coreState$core.timezone,
2539
+ now: new Date()
2540
+ });
2541
+ if (result.available) return null;
2542
+ var locale = String((coreState === null || coreState === void 0 ? void 0 : coreState.translateLocale) || (coreState === null || coreState === void 0 || (_coreState$core2 = coreState.core) === null || _coreState$core2 === void 0 || (_coreState$core2 = _coreState$core2.shop) === null || _coreState$core2 === void 0 ? void 0 : _coreState$core2.primary_locale) || 'en').toLowerCase();
2543
+ var errorTips = locale === 'zh-hk' || locale === 'zh-tw' ? '目前桌檯不在可用時段,請聯絡工作人員。' : locale.startsWith('zh') ? '当前桌台不在可用时段,请联系工作人员。' : 'This table is outside its available hours. Please contact staff.';
2544
+ return {
2545
+ mode: 'resource_block',
2546
+ table_form_record: formRecord,
2547
+ errorTips: errorTips
2548
+ };
2549
+ }
2520
2550
  }, {
2521
2551
  key: "normalizeResourceState",
2522
2552
  value: function normalizeResourceState(detail, resourceSelectType, hasOrderId) {
@@ -2759,6 +2789,7 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
2759
2789
  error,
2760
2790
  occupyDetails,
2761
2791
  occupyDetail,
2792
+ scheduleBlocked,
2762
2793
  resourceSelectType,
2763
2794
  allowAddItems,
2764
2795
  additionalOrderResult,
@@ -2930,13 +2961,21 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
2930
2961
  return this.fetchResourceOccupyDetailsByResourceId(normalizedResourceId);
2931
2962
  case 50:
2932
2963
  occupyDetails = _context37.sent;
2933
- occupyDetail = (_occupyDetails$ = occupyDetails[0]) !== null && _occupyDetails$ !== void 0 ? _occupyDetails$ : null; // Step3: 结合预约规则商品的 product_resource.resources 与占用接口返回的 form_id,
2964
+ occupyDetail = (_occupyDetails$ = occupyDetails[0]) !== null && _occupyDetails$ !== void 0 ? _occupyDetails$ : null;
2965
+ scheduleBlocked = this.checkResourceSchedule(occupyDetail === null || occupyDetail === void 0 ? void 0 : occupyDetail.form_record);
2966
+ if (!scheduleBlocked) {
2967
+ _context37.next = 55;
2968
+ break;
2969
+ }
2970
+ return _context37.abrupt("return", scheduleBlocked);
2971
+ case 55:
2972
+ // Step3: 结合预约规则商品的 product_resource.resources 与占用接口返回的 form_id,
2934
2973
  // 计算当前桌台的 resourceSelectType(single/multiple/capacity)
2935
2974
  resourceSelectType = this.resolveResourceSelectType(toPositiveString(occupyDetail === null || occupyDetail === void 0 ? void 0 : occupyDetail.form_id));
2936
2975
  allowAddItems = toBoolean(dineInConfig === null || dineInConfig === void 0 ? void 0 : dineInConfig['sale.allow_add_items']);
2937
2976
  additionalOrderResult = this.resolveAdditionalOrderFromOccupyDetail(occupyDetail, resourceSelectType, allowAddItems);
2938
2977
  if (!(additionalOrderResult.reason === 'multiple_orders')) {
2939
- _context37.next = 61;
2978
+ _context37.next = 64;
2940
2979
  break;
2941
2980
  }
2942
2981
  multipleOrdersState = _objectSpread(_objectSpread({}, this.normalizeResourceState(occupyDetail, resourceSelectType, hasOrderId)), {}, {
@@ -2957,7 +2996,7 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
2957
2996
  resourceSelectType: resourceSelectType
2958
2997
  });
2959
2998
  return _context37.abrupt("return", _availabilityInfo);
2960
- case 61:
2999
+ case 64:
2961
3000
  // Step4: 用 occupyDetail + resourceSelectType 规整为 resourceState
2962
3001
  resourceState = this.normalizeResourceState(occupyDetail, resourceSelectType, hasOrderId);
2963
3002
  additionalOrderId = additionalOrderResult.order_id;
@@ -3006,12 +3045,12 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
3006
3045
 
3007
3046
  // 空闲状态下自动准备本地临时订单,供后续“购物车式”商品操作使用
3008
3047
  if (!(availabilityInfo.mode === 'idle')) {
3009
- _context37.next = 76;
3048
+ _context37.next = 79;
3010
3049
  break;
3011
3050
  }
3012
- _context37.next = 76;
3051
+ _context37.next = 79;
3013
3052
  return this.addNewOrder();
3014
- case 76:
3053
+ case 79:
3015
3054
  isAdditionalOrderMode = availabilityInfo.mode === 'additional_order' || availabilityInfo.mode === 'additional_order_with_code'; // 如果是加单模式,tempOrder 里需要记录目标订单 id,提交的时候走加单接口
3016
3055
  if (isAdditionalOrderMode) {
3017
3056
  tempOrder.order_id = additionalOrderId;
@@ -3027,13 +3066,13 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
3027
3066
  } : {}));
3028
3067
  return acc;
3029
3068
  }, []) : []; // pax 由 setEntryPaxNumber 负责写入 itemRuleRuntimeConfig.pax
3030
- _context37.next = 82;
3069
+ _context37.next = 85;
3031
3070
  return this.setItemRuleRuntimeConfig({
3032
3071
  serviceType: (_this$otherParams16 = this.otherParams) === null || _this$otherParams16 === void 0 ? void 0 : _this$otherParams16.businessCode,
3033
3072
  submissionIndex: isAdditionalOrderMode ? 1 : 0,
3034
3073
  historicalItems: historicalItems
3035
3074
  });
3036
- case 82:
3075
+ case 85:
3037
3076
  // operating_hours 超出营业时段 + closed_behavior=show_menu_disabled:
3038
3077
  // 允许浏览商品但 UI 层需拦截“下一步”,这里覆盖最终 mode 并回传错误提示
3039
3078
  if (outsideOperatingHours && closedBehaviorValue === 'show_menu_disabled') {
@@ -3055,18 +3094,18 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
3055
3094
  isFull: resourceState.isFull
3056
3095
  });
3057
3096
  return _context37.abrupt("return", availabilityInfo);
3058
- case 87:
3059
- _context37.prev = 87;
3097
+ case 90:
3098
+ _context37.prev = 90;
3060
3099
  _context37.t0 = _context37["catch"](3);
3061
3100
  this.logMethodError('checkResourceAvailable', _context37.t0.message, {
3062
3101
  resourceId: normalizedResourceId
3063
3102
  });
3064
3103
  throw _context37.t0;
3065
- case 91:
3104
+ case 94:
3066
3105
  case "end":
3067
3106
  return _context37.stop();
3068
3107
  }
3069
- }, _callee37, this, [[3, 87]]);
3108
+ }, _callee37, this, [[3, 90]]);
3070
3109
  }));
3071
3110
  function checkResourceAvailable(_x19) {
3072
3111
  return _checkResourceAvailable.apply(this, arguments);
@@ -3371,13 +3410,13 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
3371
3410
  key: "syncAdditionalOrderFromResource",
3372
3411
  value: function () {
3373
3412
  var _syncAdditionalOrderFromResource = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee43(resourceId) {
3374
- var _ref10, _ref11, _ref12, _this$store$resource2, _this$store$resource3, _occupyDetails$2, _this$store$resource4, _this$otherParams18, _this$store$order9;
3413
+ var _ref10, _ref11, _ref12, _this$store$resource3, _this$store$resource4, _occupyDetails$2, _this$store$resource5, _this$otherParams18, _this$store$order9;
3375
3414
  var tempOrder, normalizedResourceId, _result, occupyDetails, occupyDetail, resourceSelectType, allowAddItems, result;
3376
3415
  return _regeneratorRuntime().wrap(function _callee43$(_context43) {
3377
3416
  while (1) switch (_context43.prev = _context43.next) {
3378
3417
  case 0:
3379
3418
  tempOrder = this.ensureTempOrder();
3380
- normalizedResourceId = String((_ref10 = (_ref11 = (_ref12 = resourceId !== null && resourceId !== void 0 ? resourceId : tempOrder.resource_id) !== null && _ref12 !== void 0 ? _ref12 : (_this$store$resource2 = this.store.resource) === null || _this$store$resource2 === void 0 ? void 0 : _this$store$resource2.relationId) !== null && _ref11 !== void 0 ? _ref11 : (_this$store$resource3 = this.store.resource) === null || _this$store$resource3 === void 0 ? void 0 : _this$store$resource3.relation_id) !== null && _ref10 !== void 0 ? _ref10 : '').trim();
3419
+ normalizedResourceId = String((_ref10 = (_ref11 = (_ref12 = resourceId !== null && resourceId !== void 0 ? resourceId : tempOrder.resource_id) !== null && _ref12 !== void 0 ? _ref12 : (_this$store$resource3 = this.store.resource) === null || _this$store$resource3 === void 0 ? void 0 : _this$store$resource3.relationId) !== null && _ref11 !== void 0 ? _ref11 : (_this$store$resource4 = this.store.resource) === null || _this$store$resource4 === void 0 ? void 0 : _this$store$resource4.relation_id) !== null && _ref10 !== void 0 ? _ref10 : '').trim();
3381
3420
  this.logMethodStart('syncAdditionalOrderFromResource', {
3382
3421
  resourceId: normalizedResourceId
3383
3422
  });
@@ -3397,7 +3436,7 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
3397
3436
  case 9:
3398
3437
  occupyDetails = _context43.sent;
3399
3438
  occupyDetail = (_occupyDetails$2 = occupyDetails[0]) !== null && _occupyDetails$2 !== void 0 ? _occupyDetails$2 : null;
3400
- resourceSelectType = ((_this$store$resource4 = this.store.resource) === null || _this$store$resource4 === void 0 ? void 0 : _this$store$resource4.resourceSelectType) || this.resolveResourceSelectType(toPositiveString(occupyDetail === null || occupyDetail === void 0 ? void 0 : occupyDetail.form_id));
3439
+ resourceSelectType = ((_this$store$resource5 = this.store.resource) === null || _this$store$resource5 === void 0 ? void 0 : _this$store$resource5.resourceSelectType) || this.resolveResourceSelectType(toPositiveString(occupyDetail === null || occupyDetail === void 0 ? void 0 : occupyDetail.form_id));
3401
3440
  allowAddItems = toBoolean((_this$otherParams18 = this.otherParams) === null || _this$otherParams18 === void 0 || (_this$otherParams18 = _this$otherParams18.dineInConfig) === null || _this$otherParams18 === void 0 ? void 0 : _this$otherParams18['sale.allow_add_items']);
3402
3441
  result = this.resolveAdditionalOrderFromOccupyDetail(occupyDetail, resourceSelectType, allowAddItems);
3403
3442
  if (!(!result.matched || !result.order_id)) {
@@ -3437,9 +3476,9 @@ export var ScanOrderImpl = /*#__PURE__*/function (_BaseModule) {
3437
3476
  }, {
3438
3477
  key: "getReservationResourceSelectType",
3439
3478
  value: function getReservationResourceSelectType() {
3440
- var _this$store$resource5;
3479
+ var _this$store$resource6;
3441
3480
  this.logMethodStart('getReservationResourceSelectType');
3442
- var resourceSelectType = (_this$store$resource5 = this.store.resource) === null || _this$store$resource5 === void 0 ? void 0 : _this$store$resource5.resourceSelectType;
3481
+ var resourceSelectType = (_this$store$resource6 = this.store.resource) === null || _this$store$resource6 === void 0 ? void 0 : _this$store$resource6.resourceSelectType;
3443
3482
  var result = _objectSpread(_objectSpread({}, resourceSelectType ? {
3444
3483
  resourceSelectType: resourceSelectType
3445
3484
  } : {}), {}, {
@@ -0,0 +1,12 @@
1
+ import type { ScheduleItem } from '../../modules/Schedule/types';
2
+ type Evaluation = {
3
+ available: boolean;
4
+ reason?: 'outside_schedule' | 'invalid_schedule' | 'invalid_timezone';
5
+ };
6
+ /** Current time is projected into the shop's calendar; host timezone is not used. */
7
+ export declare function evaluateResourceSchedules({ schedules, timeZone, now, }: {
8
+ schedules: ScheduleItem[];
9
+ timeZone: string;
10
+ now: Date;
11
+ }): Evaluation;
12
+ export {};
@@ -0,0 +1,300 @@
1
+ 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
+ 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); }
3
+ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
4
+ 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."); }
5
+ function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
6
+ function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
7
+ 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; }
8
+ 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; }
9
+ 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; }
10
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
11
+ 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); }
12
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
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."); }
14
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
15
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
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; } }
17
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
18
+ import dayjs from 'dayjs';
19
+ import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
20
+ import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
21
+ import { getScheduleStartEndTimePoints } from "../../modules/Schedule/getDateIsInSchedule";
22
+ // Schedule's date expander uses these methods but does not initialize them itself.
23
+ dayjs.extend(isSameOrAfter);
24
+ dayjs.extend(isSameOrBefore);
25
+ var DAY = 24 * 60 * 60 * 1000;
26
+ function dateScalar(value) {
27
+ if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
28
+ throw new Error('Invalid schedule date');
29
+ }
30
+ var _value$split$map = value.split('-').map(Number),
31
+ _value$split$map2 = _slicedToArray(_value$split$map, 3),
32
+ year = _value$split$map2[0],
33
+ month = _value$split$map2[1],
34
+ day = _value$split$map2[2];
35
+ var result = new Date(0);
36
+ result.setUTCFullYear(year, month - 1, day);
37
+ result.setUTCHours(0, 0, 0, 0);
38
+ if (year < 1 || result.getUTCFullYear() !== year || result.getUTCMonth() !== month - 1 || result.getUTCDate() !== day) {
39
+ throw new Error('Invalid schedule date');
40
+ }
41
+ return result.getTime();
42
+ }
43
+ function timeScalar(value) {
44
+ if (typeof value !== 'string') throw new Error('Invalid schedule time');
45
+ var match = /^(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(value);
46
+ if (!match) throw new Error('Invalid schedule time');
47
+ var _match = _slicedToArray(match, 4),
48
+ hours = _match[1],
49
+ minutes = _match[2],
50
+ _match$ = _match[3],
51
+ seconds = _match$ === void 0 ? '0' : _match$;
52
+ if (Number(hours) > 23 || Number(minutes) > 59 || Number(seconds) > 59) {
53
+ throw new Error('Invalid schedule time');
54
+ }
55
+ return ((Number(hours) * 60 + Number(minutes)) * 60 + Number(seconds)) * 1000;
56
+ }
57
+ function datetimeScalar(value) {
58
+ var allowDateOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
59
+ if (typeof value !== 'string') throw new Error('Invalid schedule datetime');
60
+ var parts = value.split(/[ T]/);
61
+ if (parts.length === 1 && allowDateOnly) return dateScalar(parts[0]);
62
+ if (parts.length !== 2) throw new Error('Invalid schedule datetime');
63
+ return dateScalar(parts[0]) + timeScalar(parts[1]);
64
+ }
65
+ function dayStart(value) {
66
+ return Math.floor(value / DAY) * DAY;
67
+ }
68
+ function dateLabel(value) {
69
+ return new Date(value).toISOString().slice(0, 10);
70
+ }
71
+ function absoluteRange(start, end, allDay) {
72
+ if (allDay) {
73
+ if (dayStart(end) < dayStart(start)) throw new Error('Reversed schedule');
74
+ start = dayStart(start);
75
+ // An explicit following-day midnight is already an exclusive day boundary.
76
+ end = end > start && end === dayStart(end) ? end : dayStart(end) + DAY;
77
+ }
78
+ if (end <= start) throw new Error('Empty or reversed schedule');
79
+ return {
80
+ start: start,
81
+ end: end
82
+ };
83
+ }
84
+ function contains(range, now) {
85
+ return range.start <= now && now < range.end;
86
+ }
87
+ function normalizeDateRanges(value) {
88
+ if (value === undefined || value === null) return [];
89
+ if (!Array.isArray(value)) throw new Error('Invalid schedule date ranges');
90
+ return value.map(function (range) {
91
+ if (!range || dateScalar(range.end) < dateScalar(range.start)) {
92
+ throw new Error('Invalid schedule date range');
93
+ }
94
+ return {
95
+ start: range.start,
96
+ end: range.end
97
+ };
98
+ });
99
+ }
100
+
101
+ /** Reuse Schedule's date/include/exclude rules without its truncated clock ranges. */
102
+ function dateProbe(schedule, initialDate) {
103
+ var _rule$end, _rule$end2;
104
+ var probe = _objectSpread(_objectSpread({}, schedule), {}, {
105
+ type: 'time-slots',
106
+ start_time: "".concat(dateLabel(initialDate), " 00:00:00"),
107
+ time_slot: [{
108
+ id: 0,
109
+ start_time: '00:00',
110
+ end_time: '23:59'
111
+ }]
112
+ });
113
+ if (schedule.repeat_type === 'none') return probe;
114
+ if (schedule.repeat_type !== 'daily' && schedule.repeat_type !== 'weekly') {
115
+ throw new Error('Invalid schedule repeat type');
116
+ }
117
+ var rule = schedule.repeat_rule;
118
+ if (!rule || !Number.isInteger(rule.frequency) || rule.frequency < 1) {
119
+ throw new Error('Invalid schedule repeat frequency');
120
+ }
121
+ if (((_rule$end = rule.end) === null || _rule$end === void 0 ? void 0 : _rule$end.type) !== 'never' && ((_rule$end2 = rule.end) === null || _rule$end2 === void 0 ? void 0 : _rule$end2.type) !== 'date') {
122
+ throw new Error('Invalid schedule repeat end');
123
+ }
124
+ if (rule.end.type === 'date') dateScalar(rule.end.end_date);
125
+ var included = normalizeDateRanges(rule.included_date);
126
+ var excluded = normalizeDateRanges(rule.excluded_date);
127
+ if (schedule.repeat_type === 'weekly') {
128
+ if (!Array.isArray(rule.frequency_date) || !rule.frequency_date.length || rule.frequency_date.some(function (weekday) {
129
+ return !Number.isInteger(weekday) || weekday < 0 || weekday > 6;
130
+ })) {
131
+ throw new Error('Invalid schedule weekdays');
132
+ }
133
+ // Weekly frequency counts calendar weeks, not elapsed seven-day periods.
134
+ // Keep the real first date as an exclusion; included dates retain priority.
135
+ var weekStart = initialDate - new Date(initialDate).getUTCDay() * DAY;
136
+ probe.start_time = "".concat(dateLabel(weekStart), " 00:00:00");
137
+ if (weekStart < initialDate) {
138
+ excluded.push({
139
+ start: dateLabel(weekStart),
140
+ end: dateLabel(initialDate - DAY)
141
+ });
142
+ }
143
+ }
144
+ probe.repeat_rule = _objectSpread(_objectSpread({}, rule), {}, {
145
+ included_date: included,
146
+ excluded_date: excluded
147
+ });
148
+ return probe;
149
+ }
150
+ function recurringRangeAvailable(schedule, initialDate, offsets, now) {
151
+ var probe = dateProbe(schedule, initialDate);
152
+ var maxDaysBack = Math.ceil(Math.max.apply(Math, _toConsumableArray(offsets.map(function (range) {
153
+ return range.end;
154
+ }))) / DAY);
155
+ var today = dayStart(now);
156
+ var _loop = function _loop() {
157
+ var anchor = today - daysBack * DAY;
158
+ // Do not expand an entire year: only dates whose windows can contain now.
159
+ if (!offsets.some(function (range) {
160
+ return contains({
161
+ start: anchor + range.start,
162
+ end: anchor + range.end
163
+ }, now);
164
+ })) {
165
+ return 0; // continue
166
+ }
167
+ if (getScheduleStartEndTimePoints(dateLabel(anchor), [probe]).length > 0) return {
168
+ v: true
169
+ };
170
+ },
171
+ _ret;
172
+ for (var daysBack = 0; daysBack <= maxDaysBack; daysBack += 1) {
173
+ _ret = _loop();
174
+ if (_ret === 0) continue;
175
+ if (_ret) return _ret.v;
176
+ }
177
+ return false;
178
+ }
179
+ function scheduleAvailable(schedule, now) {
180
+ if (!schedule || _typeof(schedule) !== 'object') throw new Error('Invalid schedule');
181
+ var allDay = schedule.is_all === 1;
182
+ if (schedule.type === 'designation') {
183
+ var entries = Array.isArray(schedule.designation) ? schedule.designation : [schedule.designation];
184
+ if (!entries.length) throw new Error('Missing designated schedule');
185
+ // Designation stores explicit absolute intervals, including multi-day spans.
186
+ var ranges = entries.map(function (entry) {
187
+ if (!entry) throw new Error('Invalid designated schedule');
188
+ return absoluteRange(dateScalar(entry.start_date) + (allDay ? 0 : timeScalar(entry.start_time)), dateScalar(entry.end_date) + (allDay ? timeScalar(entry.end_time || '00:00') : timeScalar(entry.end_time)), allDay);
189
+ });
190
+ return ranges.some(function (range) {
191
+ return contains(range, now);
192
+ });
193
+ }
194
+ if (schedule.type === 'standard') {
195
+ var range = absoluteRange(datetimeScalar(schedule.start_time, allDay), datetimeScalar(schedule.end_time, allDay), allDay);
196
+ if (schedule.repeat_type === 'none') return contains(range, now);
197
+ var initialDate = dayStart(range.start);
198
+ return recurringRangeAvailable(schedule, initialDate, [{
199
+ start: range.start - initialDate,
200
+ end: range.end - initialDate
201
+ }], now);
202
+ }
203
+ if (schedule.type === 'time-slots') {
204
+ var _initialDate = dayStart(datetimeScalar(schedule.start_time, true));
205
+ var offsets;
206
+ if (allDay) {
207
+ offsets = [{
208
+ start: 0,
209
+ end: DAY
210
+ }];
211
+ } else {
212
+ if (!Array.isArray(schedule.time_slot) || !schedule.time_slot.length) {
213
+ throw new Error('Missing schedule time slots');
214
+ }
215
+ offsets = schedule.time_slot.map(function (slot) {
216
+ var start = timeScalar(slot === null || slot === void 0 ? void 0 : slot.start_time);
217
+ var end = timeScalar(slot === null || slot === void 0 ? void 0 : slot.end_time);
218
+ if (start === end) throw new Error('Empty schedule time slot');
219
+ if (end < start) end += DAY;
220
+ return {
221
+ start: start,
222
+ end: end
223
+ };
224
+ });
225
+ }
226
+ return recurringRangeAvailable(schedule, _initialDate, offsets, now);
227
+ }
228
+ throw new Error('Unsupported schedule type');
229
+ }
230
+
231
+ /** Current time is projected into the shop's calendar; host timezone is not used. */
232
+ export function evaluateResourceSchedules(_ref) {
233
+ var schedules = _ref.schedules,
234
+ timeZone = _ref.timeZone,
235
+ now = _ref.now;
236
+ var formatter;
237
+ try {
238
+ if (typeof timeZone !== 'string' || !timeZone.trim()) throw new Error('Missing timezone');
239
+ var options = {
240
+ timeZone: timeZone,
241
+ calendar: 'gregory',
242
+ numberingSystem: 'latn',
243
+ year: 'numeric',
244
+ month: '2-digit',
245
+ day: '2-digit',
246
+ hour: '2-digit',
247
+ minute: '2-digit',
248
+ second: '2-digit',
249
+ hourCycle: 'h23'
250
+ };
251
+ formatter = new Intl.DateTimeFormat('en-GB', options);
252
+ } catch (_unused) {
253
+ return {
254
+ available: false,
255
+ reason: 'invalid_timezone'
256
+ };
257
+ }
258
+ if (!(now instanceof Date) || !Number.isFinite(now.getTime()) || !Array.isArray(schedules) || !schedules.length) {
259
+ return {
260
+ available: false,
261
+ reason: 'invalid_schedule'
262
+ };
263
+ }
264
+ var parts = {};
265
+ formatter.formatToParts(now).forEach(function (part) {
266
+ parts[part.type] = part.value;
267
+ });
268
+ var localNow;
269
+ try {
270
+ localNow = datetimeScalar("".concat(parts.year, "-").concat(parts.month, "-").concat(parts.day, " ").concat(parts.hour, ":").concat(parts.minute, ":").concat(parts.second)) + now.getUTCMilliseconds();
271
+ } catch (_unused2) {
272
+ return {
273
+ available: false,
274
+ reason: 'invalid_schedule'
275
+ };
276
+ }
277
+ var invalid = false;
278
+ var _iterator = _createForOfIteratorHelper(schedules),
279
+ _step;
280
+ try {
281
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
282
+ var schedule = _step.value;
283
+ try {
284
+ if (scheduleAvailable(schedule, localNow)) return {
285
+ available: true
286
+ };
287
+ } catch (_unused3) {
288
+ invalid = true;
289
+ }
290
+ }
291
+ } catch (err) {
292
+ _iterator.e(err);
293
+ } finally {
294
+ _iterator.f();
295
+ }
296
+ return {
297
+ available: false,
298
+ reason: invalid ? 'invalid_schedule' : 'outside_schedule'
299
+ };
300
+ }
@@ -192,8 +192,10 @@ export interface ScanOrderSubmitPayload extends Omit<ScanOrderTempOrder, 'platfo
192
192
  }>;
193
193
  products: ScanOrderSubmitProduct[];
194
194
  }
195
- export type ScanOrderAvailabilityMode = 'idle' | 'shop_closed' | 'submit_disabled' | 'resource_busy' | 'multiple_orders' | 'additional_order_with_code' | 'additional_order';
195
+ export type ScanOrderAvailabilityMode = 'idle' | 'shop_closed' | 'submit_disabled' | 'resource_busy' | 'resource_block' | 'multiple_orders' | 'additional_order_with_code' | 'additional_order';
196
196
  export interface ScanOrderTableFormRecord {
197
+ /** 桌台可用日程;未配置时不限制可用时段。 */
198
+ schedule?: Array<number | string> | null;
197
199
  policy?: string | null;
198
200
  partyroom_booking?: string | null;
199
201
  capacity?: number | string | null;
@@ -119,6 +119,8 @@ export declare class ScanOrderImpl extends BaseModule implements Module {
119
119
  private validateBeforeSubmitByItemRule;
120
120
  private refreshCartValidationPassed;
121
121
  setItemRuleRuntimeConfig(config?: ScanOrderItemRuleRuntimeConfig): Promise<void>;
122
+ /** 只根据已加载的桌台和日程计算当前可用性,不改变数据加载与缓存行为。 */
123
+ private checkResourceSchedule;
122
124
  private normalizeResourceState;
123
125
  private resolveResourceSelectType;
124
126
  private fetchResourceOccupyDetailsByResourceId;
@@ -45,6 +45,7 @@ var import_ProductList = require("../../modules/ProductList");
45
45
  var import_Schedule = require("../../modules/Schedule");
46
46
  var import_getDateIsInSchedule = require("../../modules/Schedule/getDateIsInSchedule");
47
47
  var import_utils2 = require("../../modules/Order/utils");
48
+ var import_resourceSchedule = require("./resourceSchedule");
48
49
  var import_dayjs = __toESM(require("dayjs"));
49
50
  var import_itemRule = require("../../model/strategy/adapter/itemRule");
50
51
  __reExport(ScanOrder_exports, require("./types"), module.exports);
@@ -1007,16 +1008,19 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
1007
1008
  };
1008
1009
  }
1009
1010
  async submitScanOrder() {
1010
- var _a, _b, _c, _d, _e, _f, _g;
1011
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1011
1012
  this.logMethodStart("submitScanOrder");
1012
1013
  try {
1013
1014
  await this.validateBeforeSubmitByItemRule();
1014
1015
  if (!this.store.order) {
1015
1016
  throw new Error("scanOrder解决方案需要 order 模块支持");
1016
1017
  }
1018
+ const scheduleBlocked = this.checkResourceSchedule((_a = this.store.resource) == null ? void 0 : _a.table_form_record);
1019
+ if (scheduleBlocked)
1020
+ throw new Error(scheduleBlocked.errorTips);
1017
1021
  const tempOrderForSubmit = this.store.order.ensureTempOrder();
1018
1022
  tempOrderForSubmit.delivery_type = "shop_service";
1019
- const resourceTableName = (_b = (_a = this.store.resource) == null ? void 0 : _a.table_form_record) == null ? void 0 : _b.name;
1023
+ const resourceTableName = (_c = (_b = this.store.resource) == null ? void 0 : _b.table_form_record) == null ? void 0 : _c.name;
1020
1024
  if (resourceTableName && typeof resourceTableName === "object") {
1021
1025
  tempOrderForSubmit.table_number = resourceTableName;
1022
1026
  } else {
@@ -1026,15 +1030,15 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
1026
1030
  const enhancePayload = this.buildSubmitPayloadEnhancer();
1027
1031
  const result = await this.store.order.submitTempOrder({
1028
1032
  cacheId: this.cacheId,
1029
- platform: (_c = this.otherParams) == null ? void 0 : _c.platform,
1030
- businessCode: (_d = this.otherParams) == null ? void 0 : _d.businessCode,
1031
- channel: (_e = this.otherParams) == null ? void 0 : _e.channel,
1032
- type: (_f = this.otherParams) == null ? void 0 : _f.type,
1033
+ platform: (_d = this.otherParams) == null ? void 0 : _d.platform,
1034
+ businessCode: (_e = this.otherParams) == null ? void 0 : _e.businessCode,
1035
+ channel: (_f = this.otherParams) == null ? void 0 : _f.channel,
1036
+ type: (_g = this.otherParams) == null ? void 0 : _g.type,
1033
1037
  enhancePayload
1034
1038
  });
1035
1039
  const tempOrder = this.store.order.getTempOrder();
1036
1040
  this.logMethodSuccess("submitScanOrder", {
1037
- productCount: ((_g = tempOrder == null ? void 0 : tempOrder.products) == null ? void 0 : _g.length) || 0
1041
+ productCount: ((_h = tempOrder == null ? void 0 : tempOrder.products) == null ? void 0 : _h.length) || 0
1038
1042
  });
1039
1043
  return result;
1040
1044
  } catch (error) {
@@ -1485,6 +1489,27 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
1485
1489
  strategyCount: Array.isArray(this.itemRuleRuntimeConfig.strategyConfigs) ? this.itemRuleRuntimeConfig.strategyConfigs.length : 0
1486
1490
  });
1487
1491
  }
1492
+ /** 只根据已加载的桌台和日程计算当前可用性,不改变数据加载与缓存行为。 */
1493
+ checkResourceSchedule(formRecord) {
1494
+ var _a, _b, _c, _d, _e, _f;
1495
+ const ids = formRecord == null ? void 0 : formRecord.schedule;
1496
+ if (!Array.isArray(ids) || !ids.length)
1497
+ return null;
1498
+ const coreState = (_a = this.core.getPlugin("shopStore")) == null ? void 0 : _a.get("core");
1499
+ const schedules = ((_b = this.store.schedule) == null ? void 0 : _b.getScheduleListByIds(ids.map(Number))) || [];
1500
+ const result = (0, import_resourceSchedule.evaluateResourceSchedules)({
1501
+ schedules,
1502
+ timeZone: (_d = (_c = coreState == null ? void 0 : coreState.core) == null ? void 0 : _c.shop) == null ? void 0 : _d.timezone,
1503
+ now: /* @__PURE__ */ new Date()
1504
+ });
1505
+ if (result.available)
1506
+ return null;
1507
+ const locale = String(
1508
+ (coreState == null ? void 0 : coreState.translateLocale) || ((_f = (_e = coreState == null ? void 0 : coreState.core) == null ? void 0 : _e.shop) == null ? void 0 : _f.primary_locale) || "en"
1509
+ ).toLowerCase();
1510
+ const errorTips = locale === "zh-hk" || locale === "zh-tw" ? "目前桌檯不在可用時段,請聯絡工作人員。" : locale.startsWith("zh") ? "当前桌台不在可用时段,请联系工作人员。" : "This table is outside its available hours. Please contact staff.";
1511
+ return { mode: "resource_block", table_form_record: formRecord, errorTips };
1512
+ }
1488
1513
  normalizeResourceState(detail, resourceSelectType, hasOrderId) {
1489
1514
  var _a, _b, _c, _d;
1490
1515
  const currentOrderId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.order_id);
@@ -1757,6 +1782,9 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
1757
1782
  }
1758
1783
  const occupyDetails = await this.fetchResourceOccupyDetailsByResourceId(normalizedResourceId);
1759
1784
  const occupyDetail = occupyDetails[0] ?? null;
1785
+ const scheduleBlocked = this.checkResourceSchedule(occupyDetail == null ? void 0 : occupyDetail.form_record);
1786
+ if (scheduleBlocked)
1787
+ return scheduleBlocked;
1760
1788
  const resourceSelectType = this.resolveResourceSelectType(
1761
1789
  (0, import_utils.toPositiveString)(occupyDetail == null ? void 0 : occupyDetail.form_id)
1762
1790
  );
@@ -0,0 +1,12 @@
1
+ import type { ScheduleItem } from '../../modules/Schedule/types';
2
+ type Evaluation = {
3
+ available: boolean;
4
+ reason?: 'outside_schedule' | 'invalid_schedule' | 'invalid_timezone';
5
+ };
6
+ /** Current time is projected into the shop's calendar; host timezone is not used. */
7
+ export declare function evaluateResourceSchedules({ schedules, timeZone, now, }: {
8
+ schedules: ScheduleItem[];
9
+ timeZone: string;
10
+ now: Date;
11
+ }): Evaluation;
12
+ export {};
@@ -0,0 +1,272 @@
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/solution/ScanOrder/resourceSchedule.ts
30
+ var resourceSchedule_exports = {};
31
+ __export(resourceSchedule_exports, {
32
+ evaluateResourceSchedules: () => evaluateResourceSchedules
33
+ });
34
+ module.exports = __toCommonJS(resourceSchedule_exports);
35
+ var import_dayjs = __toESM(require("dayjs"));
36
+ var import_isSameOrAfter = __toESM(require("dayjs/plugin/isSameOrAfter"));
37
+ var import_isSameOrBefore = __toESM(require("dayjs/plugin/isSameOrBefore"));
38
+ var import_getDateIsInSchedule = require("../../modules/Schedule/getDateIsInSchedule");
39
+ import_dayjs.default.extend(import_isSameOrAfter.default);
40
+ import_dayjs.default.extend(import_isSameOrBefore.default);
41
+ var DAY = 24 * 60 * 60 * 1e3;
42
+ function dateScalar(value) {
43
+ if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
44
+ throw new Error("Invalid schedule date");
45
+ }
46
+ const [year, month, day] = value.split("-").map(Number);
47
+ const result = /* @__PURE__ */ new Date(0);
48
+ result.setUTCFullYear(year, month - 1, day);
49
+ result.setUTCHours(0, 0, 0, 0);
50
+ if (year < 1 || result.getUTCFullYear() !== year || result.getUTCMonth() !== month - 1 || result.getUTCDate() !== day) {
51
+ throw new Error("Invalid schedule date");
52
+ }
53
+ return result.getTime();
54
+ }
55
+ function timeScalar(value) {
56
+ if (typeof value !== "string")
57
+ throw new Error("Invalid schedule time");
58
+ const match = /^(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(value);
59
+ if (!match)
60
+ throw new Error("Invalid schedule time");
61
+ const [, hours, minutes, seconds = "0"] = match;
62
+ if (Number(hours) > 23 || Number(minutes) > 59 || Number(seconds) > 59) {
63
+ throw new Error("Invalid schedule time");
64
+ }
65
+ return ((Number(hours) * 60 + Number(minutes)) * 60 + Number(seconds)) * 1e3;
66
+ }
67
+ function datetimeScalar(value, allowDateOnly = false) {
68
+ if (typeof value !== "string")
69
+ throw new Error("Invalid schedule datetime");
70
+ const parts = value.split(/[ T]/);
71
+ if (parts.length === 1 && allowDateOnly)
72
+ return dateScalar(parts[0]);
73
+ if (parts.length !== 2)
74
+ throw new Error("Invalid schedule datetime");
75
+ return dateScalar(parts[0]) + timeScalar(parts[1]);
76
+ }
77
+ function dayStart(value) {
78
+ return Math.floor(value / DAY) * DAY;
79
+ }
80
+ function dateLabel(value) {
81
+ return new Date(value).toISOString().slice(0, 10);
82
+ }
83
+ function absoluteRange(start, end, allDay) {
84
+ if (allDay) {
85
+ if (dayStart(end) < dayStart(start))
86
+ throw new Error("Reversed schedule");
87
+ start = dayStart(start);
88
+ end = end > start && end === dayStart(end) ? end : dayStart(end) + DAY;
89
+ }
90
+ if (end <= start)
91
+ throw new Error("Empty or reversed schedule");
92
+ return { start, end };
93
+ }
94
+ function contains(range, now) {
95
+ return range.start <= now && now < range.end;
96
+ }
97
+ function normalizeDateRanges(value) {
98
+ if (value === void 0 || value === null)
99
+ return [];
100
+ if (!Array.isArray(value))
101
+ throw new Error("Invalid schedule date ranges");
102
+ return value.map((range) => {
103
+ if (!range || dateScalar(range.end) < dateScalar(range.start)) {
104
+ throw new Error("Invalid schedule date range");
105
+ }
106
+ return { start: range.start, end: range.end };
107
+ });
108
+ }
109
+ function dateProbe(schedule, initialDate) {
110
+ var _a, _b;
111
+ const probe = {
112
+ ...schedule,
113
+ type: "time-slots",
114
+ start_time: `${dateLabel(initialDate)} 00:00:00`,
115
+ time_slot: [{ id: 0, start_time: "00:00", end_time: "23:59" }]
116
+ };
117
+ if (schedule.repeat_type === "none")
118
+ return probe;
119
+ if (schedule.repeat_type !== "daily" && schedule.repeat_type !== "weekly") {
120
+ throw new Error("Invalid schedule repeat type");
121
+ }
122
+ const rule = schedule.repeat_rule;
123
+ if (!rule || !Number.isInteger(rule.frequency) || rule.frequency < 1) {
124
+ throw new Error("Invalid schedule repeat frequency");
125
+ }
126
+ if (((_a = rule.end) == null ? void 0 : _a.type) !== "never" && ((_b = rule.end) == null ? void 0 : _b.type) !== "date") {
127
+ throw new Error("Invalid schedule repeat end");
128
+ }
129
+ if (rule.end.type === "date")
130
+ dateScalar(rule.end.end_date);
131
+ const included = normalizeDateRanges(rule.included_date);
132
+ const excluded = normalizeDateRanges(rule.excluded_date);
133
+ if (schedule.repeat_type === "weekly") {
134
+ if (!Array.isArray(rule.frequency_date) || !rule.frequency_date.length || rule.frequency_date.some((weekday) => !Number.isInteger(weekday) || weekday < 0 || weekday > 6)) {
135
+ throw new Error("Invalid schedule weekdays");
136
+ }
137
+ const weekStart = initialDate - new Date(initialDate).getUTCDay() * DAY;
138
+ probe.start_time = `${dateLabel(weekStart)} 00:00:00`;
139
+ if (weekStart < initialDate) {
140
+ excluded.push({ start: dateLabel(weekStart), end: dateLabel(initialDate - DAY) });
141
+ }
142
+ }
143
+ probe.repeat_rule = {
144
+ ...rule,
145
+ included_date: included,
146
+ excluded_date: excluded
147
+ };
148
+ return probe;
149
+ }
150
+ function recurringRangeAvailable(schedule, initialDate, offsets, now) {
151
+ const probe = dateProbe(schedule, initialDate);
152
+ const maxDaysBack = Math.ceil(Math.max(...offsets.map((range) => range.end)) / DAY);
153
+ const today = dayStart(now);
154
+ for (let daysBack = 0; daysBack <= maxDaysBack; daysBack += 1) {
155
+ const anchor = today - daysBack * DAY;
156
+ if (!offsets.some((range) => contains({ start: anchor + range.start, end: anchor + range.end }, now))) {
157
+ continue;
158
+ }
159
+ if ((0, import_getDateIsInSchedule.getScheduleStartEndTimePoints)(dateLabel(anchor), [probe]).length > 0)
160
+ return true;
161
+ }
162
+ return false;
163
+ }
164
+ function scheduleAvailable(schedule, now) {
165
+ if (!schedule || typeof schedule !== "object")
166
+ throw new Error("Invalid schedule");
167
+ const allDay = schedule.is_all === 1;
168
+ if (schedule.type === "designation") {
169
+ const entries = Array.isArray(schedule.designation) ? schedule.designation : [schedule.designation];
170
+ if (!entries.length)
171
+ throw new Error("Missing designated schedule");
172
+ const ranges = entries.map((entry) => {
173
+ if (!entry)
174
+ throw new Error("Invalid designated schedule");
175
+ return absoluteRange(
176
+ dateScalar(entry.start_date) + (allDay ? 0 : timeScalar(entry.start_time)),
177
+ dateScalar(entry.end_date) + (allDay ? timeScalar(entry.end_time || "00:00") : timeScalar(entry.end_time)),
178
+ allDay
179
+ );
180
+ });
181
+ return ranges.some((range) => contains(range, now));
182
+ }
183
+ if (schedule.type === "standard") {
184
+ const range = absoluteRange(
185
+ datetimeScalar(schedule.start_time, allDay),
186
+ datetimeScalar(schedule.end_time, allDay),
187
+ allDay
188
+ );
189
+ if (schedule.repeat_type === "none")
190
+ return contains(range, now);
191
+ const initialDate = dayStart(range.start);
192
+ return recurringRangeAvailable(schedule, initialDate, [{
193
+ start: range.start - initialDate,
194
+ end: range.end - initialDate
195
+ }], now);
196
+ }
197
+ if (schedule.type === "time-slots") {
198
+ const initialDate = dayStart(datetimeScalar(schedule.start_time, true));
199
+ let offsets;
200
+ if (allDay) {
201
+ offsets = [{ start: 0, end: DAY }];
202
+ } else {
203
+ if (!Array.isArray(schedule.time_slot) || !schedule.time_slot.length) {
204
+ throw new Error("Missing schedule time slots");
205
+ }
206
+ offsets = schedule.time_slot.map((slot) => {
207
+ const start = timeScalar(slot == null ? void 0 : slot.start_time);
208
+ let end = timeScalar(slot == null ? void 0 : slot.end_time);
209
+ if (start === end)
210
+ throw new Error("Empty schedule time slot");
211
+ if (end < start)
212
+ end += DAY;
213
+ return { start, end };
214
+ });
215
+ }
216
+ return recurringRangeAvailable(schedule, initialDate, offsets, now);
217
+ }
218
+ throw new Error("Unsupported schedule type");
219
+ }
220
+ function evaluateResourceSchedules({
221
+ schedules,
222
+ timeZone,
223
+ now
224
+ }) {
225
+ let formatter;
226
+ try {
227
+ if (typeof timeZone !== "string" || !timeZone.trim())
228
+ throw new Error("Missing timezone");
229
+ const options = {
230
+ timeZone,
231
+ calendar: "gregory",
232
+ numberingSystem: "latn",
233
+ year: "numeric",
234
+ month: "2-digit",
235
+ day: "2-digit",
236
+ hour: "2-digit",
237
+ minute: "2-digit",
238
+ second: "2-digit",
239
+ hourCycle: "h23"
240
+ };
241
+ formatter = new Intl.DateTimeFormat("en-GB", options);
242
+ } catch {
243
+ return { available: false, reason: "invalid_timezone" };
244
+ }
245
+ if (!(now instanceof Date) || !Number.isFinite(now.getTime()) || !Array.isArray(schedules) || !schedules.length) {
246
+ return { available: false, reason: "invalid_schedule" };
247
+ }
248
+ const parts = {};
249
+ formatter.formatToParts(now).forEach((part) => {
250
+ parts[part.type] = part.value;
251
+ });
252
+ let localNow;
253
+ try {
254
+ localNow = datetimeScalar(`${parts.year}-${parts.month}-${parts.day} ${parts.hour}:${parts.minute}:${parts.second}`) + now.getUTCMilliseconds();
255
+ } catch {
256
+ return { available: false, reason: "invalid_schedule" };
257
+ }
258
+ let invalid = false;
259
+ for (const schedule of schedules) {
260
+ try {
261
+ if (scheduleAvailable(schedule, localNow))
262
+ return { available: true };
263
+ } catch {
264
+ invalid = true;
265
+ }
266
+ }
267
+ return { available: false, reason: invalid ? "invalid_schedule" : "outside_schedule" };
268
+ }
269
+ // Annotate the CommonJS export names for ESM import in node:
270
+ 0 && (module.exports = {
271
+ evaluateResourceSchedules
272
+ });
@@ -192,8 +192,10 @@ export interface ScanOrderSubmitPayload extends Omit<ScanOrderTempOrder, 'platfo
192
192
  }>;
193
193
  products: ScanOrderSubmitProduct[];
194
194
  }
195
- export type ScanOrderAvailabilityMode = 'idle' | 'shop_closed' | 'submit_disabled' | 'resource_busy' | 'multiple_orders' | 'additional_order_with_code' | 'additional_order';
195
+ export type ScanOrderAvailabilityMode = 'idle' | 'shop_closed' | 'submit_disabled' | 'resource_busy' | 'resource_block' | 'multiple_orders' | 'additional_order_with_code' | 'additional_order';
196
196
  export interface ScanOrderTableFormRecord {
197
+ /** 桌台可用日程;未配置时不限制可用时段。 */
198
+ schedule?: Array<number | string> | null;
197
199
  policy?: string | null;
198
200
  partyroom_booking?: string | null;
199
201
  capacity?: number | string | null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "2.1.175",
4
+ "version": "2.1.176",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",