@pisell/pisellos 2.1.174 → 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.
Files changed (60) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +0 -9
  2. package/dist/model/strategy/adapter/walletPass/evaluator.d.ts +0 -2
  3. package/dist/model/strategy/adapter/walletPass/evaluator.js +2 -4
  4. package/dist/modules/Discount/index.d.ts +3 -6
  5. package/dist/modules/Discount/index.js +23 -125
  6. package/dist/modules/Discount/types.d.ts +5 -34
  7. package/dist/modules/OpenData/types.d.ts +1 -0
  8. package/dist/modules/ProductList/index.js +13 -6
  9. package/dist/modules/Rules/index.d.ts +2 -12
  10. package/dist/modules/Rules/index.js +113 -377
  11. package/dist/modules/Rules/types.d.ts +2 -2
  12. package/dist/plugins/window.d.ts +0 -2
  13. package/dist/solution/BookingByStep/index.d.ts +1 -1
  14. package/dist/solution/ScanOrder/index.d.ts +7 -5
  15. package/dist/solution/ScanOrder/index.js +123 -31
  16. package/dist/solution/ScanOrder/resourceSchedule.d.ts +12 -0
  17. package/dist/solution/ScanOrder/resourceSchedule.js +300 -0
  18. package/dist/solution/ScanOrder/types.d.ts +9 -1
  19. package/dist/solution/ShopDiscount/index.d.ts +0 -3
  20. package/dist/solution/ShopDiscount/index.js +78 -161
  21. package/dist/solution/ShopDiscount/utils.d.ts +0 -1
  22. package/dist/solution/ShopDiscount/utils.js +1 -40
  23. package/lib/model/strategy/adapter/walletPass/evaluator.d.ts +0 -2
  24. package/lib/model/strategy/adapter/walletPass/evaluator.js +2 -4
  25. package/lib/modules/Discount/index.d.ts +3 -6
  26. package/lib/modules/Discount/index.js +16 -66
  27. package/lib/modules/Discount/types.d.ts +5 -34
  28. package/lib/modules/OpenData/types.d.ts +1 -0
  29. package/lib/modules/ProductList/index.js +8 -1
  30. package/lib/modules/Rules/index.d.ts +2 -12
  31. package/lib/modules/Rules/index.js +69 -303
  32. package/lib/modules/Rules/types.d.ts +2 -2
  33. package/lib/plugins/window.d.ts +0 -2
  34. package/lib/solution/BookingByStep/index.d.ts +1 -1
  35. package/lib/solution/ScanOrder/index.d.ts +7 -5
  36. package/lib/solution/ScanOrder/index.js +90 -12
  37. package/lib/solution/ScanOrder/resourceSchedule.d.ts +12 -0
  38. package/lib/solution/ScanOrder/resourceSchedule.js +272 -0
  39. package/lib/solution/ScanOrder/types.d.ts +9 -1
  40. package/lib/solution/ShopDiscount/index.d.ts +0 -3
  41. package/lib/solution/ShopDiscount/index.js +28 -78
  42. package/lib/solution/ShopDiscount/utils.d.ts +0 -1
  43. package/lib/solution/ShopDiscount/utils.js +1 -42
  44. package/package.json +1 -1
  45. package/dist/modules/Discount/availability.d.ts +0 -7
  46. package/dist/modules/Discount/availability.js +0 -52
  47. package/dist/modules/Discount/entitlementPass.d.ts +0 -34
  48. package/dist/modules/Discount/entitlementPass.js +0 -147
  49. package/dist/modules/Discount/entitlementState.d.ts +0 -8
  50. package/dist/modules/Discount/entitlementState.js +0 -205
  51. package/dist/modules/Rules/entitlementLines.d.ts +0 -8
  52. package/dist/modules/Rules/entitlementLines.js +0 -157
  53. package/lib/modules/Discount/availability.d.ts +0 -7
  54. package/lib/modules/Discount/availability.js +0 -91
  55. package/lib/modules/Discount/entitlementPass.d.ts +0 -34
  56. package/lib/modules/Discount/entitlementPass.js +0 -184
  57. package/lib/modules/Discount/entitlementState.d.ts +0 -8
  58. package/lib/modules/Discount/entitlementState.js +0 -206
  59. package/lib/modules/Rules/entitlementLines.d.ts +0 -8
  60. package/lib/modules/Rules/entitlementLines.js +0 -158
@@ -29,7 +29,7 @@ export interface RulesModuleAPI {
29
29
  discountList: any[];
30
30
  productList: any[];
31
31
  holders: {
32
- form_record_id: number | string;
32
+ form_record_id: number;
33
33
  }[];
34
34
  isFormSubject: boolean;
35
35
  }) => DiscountResult;
@@ -49,7 +49,7 @@ type ProductDetail = {
49
49
  original_price?: number | string;
50
50
  num?: number;
51
51
  quantity: number;
52
- holder_id?: number | string | Array<number | string>;
52
+ holder_id?: number | string;
53
53
  vouchersApplicable?: boolean;
54
54
  startDate?: any;
55
55
  };
@@ -1,5 +1,4 @@
1
1
  import { Plugin } from '../types';
2
- import type { WalletPassEvaluator } from '../model/strategy/adapter/walletPass/evaluator';
3
2
  /**
4
3
  * WindowPlugin 接口定义
5
4
  */
@@ -15,7 +14,6 @@ export interface WindowPlugin extends Plugin {
15
14
  document: Partial<Document>;
16
15
  history: History;
17
16
  interaction?: any;
18
- getWalletPassEvaluator?: () => WalletPassEvaluator | undefined;
19
17
  getLocalStorage?: (key: string) => string | null;
20
18
  }
21
19
  /**
@@ -325,7 +325,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
325
325
  date: string;
326
326
  status: string;
327
327
  week: string;
328
- weekNum: 0 | 2 | 1 | 5 | 3 | 4 | 6;
328
+ weekNum: 0 | 2 | 1 | 5 | 4 | 3 | 6;
329
329
  }[]>;
330
330
  submitTimeSlot(timeSlots: TimeSliceItem): void;
331
331
  private getScheduleDataByIds;
@@ -1,6 +1,6 @@
1
1
  import { Module, ModuleOptions, PisellCore } from '../../types';
2
2
  import { BaseModule } from '../../modules/BaseModule';
3
- import { ScanOrderAddLogParams, ScanOrderAvailabilityInfo, ScanOrderAllowAddItemsInfo, ScanOrderOrderProduct, ScanOrderOrderProductIdentity, ScanOrderReservationResourceSelectInfo, ScanOrderScanCodeResult, ScanOrderSyncAdditionalOrderResult, ScanOrderTempOrder } from './types';
3
+ import { ScanOrderAddLogParams, ScanOrderAvailabilityInfo, ScanOrderFulfillmentModes, ScanOrderAllowAddItemsInfo, ScanOrderOrderProduct, ScanOrderOrderProductIdentity, ScanOrderReservationResourceSelectInfo, ScanOrderScanCodeResult, ScanOrderSyncAdditionalOrderResult, ScanOrderTempOrder } from './types';
4
4
  import type { UpdateProductInOrderParams } from '../../modules/Order/types';
5
5
  import type { Discount } from '../../modules/Discount/types';
6
6
  import { type CartItemSummary, type PaxInfo, type QuantityCheckResult, type QuantityLimitResult } from '../../model/strategy/adapter/itemRule';
@@ -75,6 +75,9 @@ export declare class ScanOrderImpl extends BaseModule implements Module {
75
75
  service_type: 'dine_in' | '';
76
76
  pickup_reference_mode: 'counter_pickup' | 'table_service';
77
77
  };
78
+ setFulfillmentMode(mode: string): Record<string, any> & {
79
+ service_type?: string | undefined;
80
+ };
78
81
  setPickupRef(buzzer: string): string;
79
82
  private ensureTempOrder;
80
83
  addNewOrder(): Promise<ScanOrderTempOrder>;
@@ -116,6 +119,8 @@ export declare class ScanOrderImpl extends BaseModule implements Module {
116
119
  private validateBeforeSubmitByItemRule;
117
120
  private refreshCartValidationPassed;
118
121
  setItemRuleRuntimeConfig(config?: ScanOrderItemRuleRuntimeConfig): Promise<void>;
122
+ /** 只根据已加载的桌台和日程计算当前可用性,不改变数据加载与缓存行为。 */
123
+ private checkResourceSchedule;
119
124
  private normalizeResourceState;
120
125
  private resolveResourceSelectType;
121
126
  private fetchResourceOccupyDetailsByResourceId;
@@ -147,10 +152,7 @@ export declare class ScanOrderImpl extends BaseModule implements Module {
147
152
  syncAdditionalOrderFromResource(resourceId?: string | null): Promise<ScanOrderSyncAdditionalOrderResult>;
148
153
  getReservationResourceSelectType(): ScanOrderReservationResourceSelectInfo;
149
154
  checkAllowAddItems(): ScanOrderAllowAddItemsInfo;
150
- getFulfillmentModes(): {
151
- enablePickup: boolean;
152
- enableTableService: boolean;
153
- };
155
+ getFulfillmentModes(): ScanOrderFulfillmentModes;
154
156
  checkManualPickupRef(): {
155
157
  enabled: boolean;
156
158
  manualInputType?: string;
@@ -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);
@@ -577,6 +578,38 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
577
578
  throw error;
578
579
  }
579
580
  }
581
+ // 新版履约方式直接保存配置值;旧取餐标识方式继续由 setPickupReferenceMode 处理。
582
+ setFulfillmentMode(mode) {
583
+ this.logMethodStart("setFulfillmentMode", { mode });
584
+ try {
585
+ const { handoverModes } = this.getFulfillmentModes();
586
+ const clearMode = mode === "" && (handoverModes == null ? void 0 : handoverModes.length) === 0;
587
+ if (!clearMode && !(handoverModes == null ? void 0 : handoverModes.includes(mode))) {
588
+ throw new Error("Fulfillment mode is not enabled");
589
+ }
590
+ if (!this.store.order)
591
+ throw new Error("order 模块未初始化");
592
+ const tempOrder = this.store.order.ensureTempOrder();
593
+ const serviceData = {
594
+ ...(tempOrder.metadata || {}).shop_service_data || {}
595
+ };
596
+ if (clearMode) {
597
+ delete serviceData.service_type;
598
+ } else {
599
+ serviceData.service_type = mode;
600
+ }
601
+ tempOrder.metadata = {
602
+ ...tempOrder.metadata || {},
603
+ shop_service_data: serviceData
604
+ };
605
+ this.store.order.persistTempOrder();
606
+ this.logMethodSuccess("setFulfillmentMode", { mode });
607
+ return tempOrder.metadata.shop_service_data;
608
+ } catch (error) {
609
+ this.logMethodError("setFulfillmentMode", error, { mode });
610
+ throw error;
611
+ }
612
+ }
580
613
  // 存储用户自定义取餐标识到 tempOrder.buzzer
581
614
  setPickupRef(buzzer) {
582
615
  this.logMethodStart("setPickupRef", {
@@ -975,16 +1008,19 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
975
1008
  };
976
1009
  }
977
1010
  async submitScanOrder() {
978
- var _a, _b, _c, _d, _e, _f, _g;
1011
+ var _a, _b, _c, _d, _e, _f, _g, _h;
979
1012
  this.logMethodStart("submitScanOrder");
980
1013
  try {
981
1014
  await this.validateBeforeSubmitByItemRule();
982
1015
  if (!this.store.order) {
983
1016
  throw new Error("scanOrder解决方案需要 order 模块支持");
984
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);
985
1021
  const tempOrderForSubmit = this.store.order.ensureTempOrder();
986
1022
  tempOrderForSubmit.delivery_type = "shop_service";
987
- 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;
988
1024
  if (resourceTableName && typeof resourceTableName === "object") {
989
1025
  tempOrderForSubmit.table_number = resourceTableName;
990
1026
  } else {
@@ -994,15 +1030,15 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
994
1030
  const enhancePayload = this.buildSubmitPayloadEnhancer();
995
1031
  const result = await this.store.order.submitTempOrder({
996
1032
  cacheId: this.cacheId,
997
- platform: (_c = this.otherParams) == null ? void 0 : _c.platform,
998
- businessCode: (_d = this.otherParams) == null ? void 0 : _d.businessCode,
999
- channel: (_e = this.otherParams) == null ? void 0 : _e.channel,
1000
- 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,
1001
1037
  enhancePayload
1002
1038
  });
1003
1039
  const tempOrder = this.store.order.getTempOrder();
1004
1040
  this.logMethodSuccess("submitScanOrder", {
1005
- 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
1006
1042
  });
1007
1043
  return result;
1008
1044
  } catch (error) {
@@ -1453,6 +1489,27 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
1453
1489
  strategyCount: Array.isArray(this.itemRuleRuntimeConfig.strategyConfigs) ? this.itemRuleRuntimeConfig.strategyConfigs.length : 0
1454
1490
  });
1455
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
+ }
1456
1513
  normalizeResourceState(detail, resourceSelectType, hasOrderId) {
1457
1514
  var _a, _b, _c, _d;
1458
1515
  const currentOrderId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.order_id);
@@ -1725,6 +1782,9 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
1725
1782
  }
1726
1783
  const occupyDetails = await this.fetchResourceOccupyDetailsByResourceId(normalizedResourceId);
1727
1784
  const occupyDetail = occupyDetails[0] ?? null;
1785
+ const scheduleBlocked = this.checkResourceSchedule(occupyDetail == null ? void 0 : occupyDetail.form_record);
1786
+ if (scheduleBlocked)
1787
+ return scheduleBlocked;
1728
1788
  const resourceSelectType = this.resolveResourceSelectType(
1729
1789
  (0, import_utils.toPositiveString)(occupyDetail == null ? void 0 : occupyDetail.form_id)
1730
1790
  );
@@ -2083,16 +2143,34 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
2083
2143
  this.logMethodSuccess("checkAllowAddItems", result);
2084
2144
  return result;
2085
2145
  }
2086
- // 读取取餐/送餐开关
2146
+ // 新版履约方式配置存在时优先使用,缺失时保留旧取餐/送餐开关语义。
2087
2147
  // 依赖 checkResourceAvailable 已缓存的 dineInConfig
2088
2148
  getFulfillmentModes() {
2089
2149
  var _a;
2090
2150
  this.logMethodStart("getFulfillmentModes");
2091
2151
  const dineInConfig = ((_a = this.otherParams) == null ? void 0 : _a.dineInConfig) || {};
2092
- const result = {
2093
- enablePickup: Boolean(dineInConfig["fulfillment.enable_pickup"]),
2094
- enableTableService: Boolean(dineInConfig["fulfillment.enable_table_service"])
2095
- };
2152
+ const configuredModes = dineInConfig["fulfillment.handover_mode"];
2153
+ let result;
2154
+ if (Object.prototype.hasOwnProperty.call(
2155
+ dineInConfig,
2156
+ "fulfillment.handover_mode"
2157
+ )) {
2158
+ const handoverModes = Array.isArray(configuredModes) ? Array.from(
2159
+ new Set(
2160
+ configuredModes.filter((mode) => typeof mode === "string").map((mode) => mode.trim()).filter(Boolean)
2161
+ )
2162
+ ) : [];
2163
+ result = {
2164
+ enablePickup: handoverModes.includes("pick_up"),
2165
+ enableTableService: handoverModes.includes("table_service"),
2166
+ handoverModes
2167
+ };
2168
+ } else {
2169
+ result = {
2170
+ enablePickup: Boolean(dineInConfig["fulfillment.enable_pickup"]),
2171
+ enableTableService: Boolean(dineInConfig["fulfillment.enable_table_service"])
2172
+ };
2173
+ }
2096
2174
  this.logMethodSuccess("getFulfillmentModes", result);
2097
2175
  return result;
2098
2176
  }
@@ -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
+ });
@@ -27,6 +27,12 @@ export interface ScanOrderConfig {
27
27
  verifyResource: boolean;
28
28
  };
29
29
  }
30
+ export interface ScanOrderFulfillmentModes {
31
+ enablePickup: boolean;
32
+ enableTableService: boolean;
33
+ /** Present only when fulfillment.handover_mode is configured, including an empty list. */
34
+ handoverModes?: string[];
35
+ }
30
36
  export interface ScanOrderOrderProductIdentity {
31
37
  product_id: number | null;
32
38
  product_variant_id: number;
@@ -186,8 +192,10 @@ export interface ScanOrderSubmitPayload extends Omit<ScanOrderTempOrder, 'platfo
186
192
  }>;
187
193
  products: ScanOrderSubmitProduct[];
188
194
  }
189
- 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';
190
196
  export interface ScanOrderTableFormRecord {
197
+ /** 桌台可用日程;未配置时不限制可用时段。 */
198
+ schedule?: Array<number | string> | null;
191
199
  policy?: string | null;
192
200
  partyroom_booking?: string | null;
193
201
  capacity?: number | string | null;
@@ -12,8 +12,6 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
12
12
  private store;
13
13
  private options;
14
14
  private hooks?;
15
- private entitlementPassEnabled;
16
- setEntitlementPassEnabled(enabled: boolean): void;
17
15
  constructor(name?: string, version?: string);
18
16
  initialize(core: PisellCore, options: any): Promise<void>;
19
17
  destroy(): Promise<void>;
@@ -44,7 +42,6 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
44
42
  batchSearchByProductIds(productIds: number[]): Promise<Discount[]>;
45
43
  scanCode(code: string, customerId?: number): Promise<{
46
44
  isAvailable: boolean;
47
- isAccepted?: boolean;
48
45
  productList: Record<string, any>[];
49
46
  discountList: Discount[];
50
47
  type: "server" | "clientCalc";