@pisell/pisellos 0.0.503 → 0.0.505

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.
@@ -44,7 +44,7 @@ var import_utils2 = require("../../modules/Order/utils");
44
44
  var import_dayjs = __toESM(require("dayjs"));
45
45
  var import_itemRule = require("../../model/strategy/adapter/itemRule");
46
46
  __reExport(ScanOrder_exports, require("./types"), module.exports);
47
- var ScanOrderImpl = class extends import_BaseModule.BaseModule {
47
+ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
48
48
  constructor(name, version) {
49
49
  super(name, version);
50
50
  this.defaultName = "scanOrder";
@@ -204,7 +204,7 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
204
204
  const loggerProvider = ((_c2 = this.otherParams) == null ? void 0 : _c2.scanOrderLoggerProvider) || "feishu";
205
205
  const loggerConfig = ((_d2 = this.otherParams) == null ? void 0 : _d2.scanOrderLoggerConfig) || {
206
206
  feishu: {
207
- // webhook: 'https://open.feishu.cn/open-apis/bot/v2/hook/216b3fe6-af98-424e-8706-f0471241a7ed',
207
+ webhook: "https://open.feishu.cn/open-apis/bot/v2/hook/216b3fe6-af98-424e-8706-f0471241a7ed"
208
208
  }
209
209
  };
210
210
  this.core.registerModule(targetModule, {
@@ -496,11 +496,13 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
496
496
  }
497
497
  // ScanOrder 提交 payload enhancer:
498
498
  // - 给所有 booking 注入 appointment_status: 'started'(扫码点餐语义)
499
+ // - 给所有 booking 的 metadata 注入 resource_select_type(来自预约规则商品的 resource.type)
499
500
  // - 给第一条 booking 补 resources 与 product_uid(仅当存在 resource / rule product)
500
501
  // - 追加一条 is_rule=true 的 rule product,与 booking 互相关联
501
502
  buildSubmitPayloadEnhancer() {
502
503
  const ruleProduct = this.enabledReservationRuleProducts[0];
503
504
  const resourceState = this.store.resource;
505
+ const resourceSelectType = resourceState == null ? void 0 : resourceState.resourceSelectType;
504
506
  return (payload, { bookingUuid, tempOrder }) => {
505
507
  var _a;
506
508
  const resourceId = tempOrder.resource_id ?? (resourceState == null ? void 0 : resourceState.relationId);
@@ -526,6 +528,10 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
526
528
  const nextBookings = (payload.bookings || []).map((booking, idx) => ({
527
529
  ...booking,
528
530
  appointment_status: "started",
531
+ metadata: {
532
+ ...booking.metadata || {},
533
+ ...resourceSelectType ? { resource_select_type: resourceSelectType } : {}
534
+ },
529
535
  ...idx === 0 && resourceEntry ? { resources: [resourceEntry] } : {},
530
536
  ...idx === 0 && ruleProductUid ? { product_uid: ruleProductUid } : {}
531
537
  }));
@@ -1009,20 +1015,22 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1009
1015
  strategyCount: Array.isArray(this.itemRuleRuntimeConfig.strategyConfigs) ? this.itemRuleRuntimeConfig.strategyConfigs.length : 0
1010
1016
  });
1011
1017
  }
1012
- normalizeResourceState(config, hasOrderId) {
1013
- var _a, _b;
1014
- const orderNumberPrefix = Array.isArray(config == null ? void 0 : config.order_number_prefix) ? (config == null ? void 0 : config.order_number_prefix) || [] : [];
1015
- const tableMaxNumber = Number((config == null ? void 0 : config.table_max_number) || 1);
1016
- const orderCount = (0, import_utils.toNonNegativeNumber)(config == null ? void 0 : config.order_count);
1017
- const currentOrderId = (0, import_utils.toPositiveString)(config == null ? void 0 : config.order_id);
1018
- const lastOrderId = (0, import_utils.toPositiveString)(config == null ? void 0 : config.last_order_id);
1019
- const relationId = (0, import_utils.toPositiveString)(config == null ? void 0 : config.relation_id);
1020
- const tableFormId = (0, import_utils.toPositiveString)(config == null ? void 0 : config.table_form_id);
1018
+ normalizeResourceState(detail, resourceSelectType, hasOrderId) {
1019
+ var _a, _b, _c, _d;
1020
+ const currentOrderId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.order_id);
1021
+ const lastOrderId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.last_order_id);
1022
+ const relationId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.form_record_id);
1023
+ const tableFormId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.form_id);
1024
+ const formRecord = (detail == null ? void 0 : detail.form_record) ?? null;
1021
1025
  const allowSnack = ((_b = (_a = this.otherParams) == null ? void 0 : _a.dineInConfig) == null ? void 0 : _b["workflow.allow_add_items"]) || false;
1022
1026
  const deskmateValid = false;
1023
- const isExclusive = tableMaxNumber > 0 ? tableMaxNumber <= 1 : false;
1024
- const isFull = tableMaxNumber > 0 ? orderCount >= tableMaxNumber : false;
1025
- const isBlock = tableMaxNumber === -1;
1027
+ const isExclusive = resourceSelectType === "single";
1028
+ const isFull = (0, import_utils.computeResourceIsFull)({
1029
+ resourceSelectType,
1030
+ lastOrderId,
1031
+ capacityList: ((_d = (_c = detail == null ? void 0 : detail.resource_capacity) == null ? void 0 : _c[0]) == null ? void 0 : _d.capacity_list) ?? [],
1032
+ capacity: formRecord == null ? void 0 : formRecord.capacity
1033
+ });
1026
1034
  let availabilityInfo = {
1027
1035
  mode: "idle",
1028
1036
  deskmate_valid: deskmateValid
@@ -1060,19 +1068,8 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1060
1068
  deskmate_valid: deskmateValid
1061
1069
  };
1062
1070
  }
1063
- if (isBlock) {
1064
- availabilityInfo = {
1065
- mode: "resource_block",
1066
- order_id: lastOrderId,
1067
- relation_id: relationId,
1068
- table_form_id: tableFormId,
1069
- deskmate_valid: deskmateValid
1070
- };
1071
- }
1072
1071
  return {
1073
1072
  ...availabilityInfo,
1074
- tableMaxNumber,
1075
- orderCount,
1076
1073
  currentOrderId,
1077
1074
  lastOrderId,
1078
1075
  relationId,
@@ -1081,27 +1078,46 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1081
1078
  deskmateValid,
1082
1079
  isExclusive,
1083
1080
  isFull,
1084
- orderNumberPrefix,
1085
- raw: config,
1086
- table_form_record: (config == null ? void 0 : config.table_form_record) ?? null
1081
+ resourceSelectType,
1082
+ raw: detail,
1083
+ table_form_record: formRecord
1087
1084
  };
1088
1085
  }
1089
- async fetchTableConfigByResourceId(resourceId) {
1090
- const tableResourceId = Number(resourceId);
1091
- if (!Number.isFinite(tableResourceId) || tableResourceId <= 0) {
1086
+ // 从首个预约规则商品的 product_resource.resources 中,按 form_id 匹配返回 type
1087
+ resolveResourceSelectType(formId) {
1088
+ var _a;
1089
+ const firstProduct = this.enabledReservationRuleProducts[0];
1090
+ const resources = ((_a = firstProduct == null ? void 0 : firstProduct.product_resource) == null ? void 0 : _a.resources) || [];
1091
+ const numericFormId = Number(formId);
1092
+ if (!Number.isFinite(numericFormId) || numericFormId <= 0)
1093
+ return void 0;
1094
+ const matched = resources.find((r) => Number(r == null ? void 0 : r.id) === numericFormId);
1095
+ return matched == null ? void 0 : matched.type;
1096
+ }
1097
+ async fetchResourceOccupyDetailByResourceId(resourceId) {
1098
+ var _a, _b, _c, _d;
1099
+ const formRecordId = Number(resourceId);
1100
+ if (!Number.isFinite(formRecordId) || formRecordId <= 0) {
1092
1101
  throw new Error(`[ScanOrder] 非法桌台 resourceId: ${resourceId}`);
1093
1102
  }
1103
+ const shopId = (_b = (_a = this.otherParams) == null ? void 0 : _a.getStateData) == null ? void 0 : _b.call(_a, "shop_id");
1104
+ if (!shopId) {
1105
+ throw new Error("[ScanOrder] 无法获取 shop_id");
1106
+ }
1094
1107
  const response = await this.request.get(
1095
- "/order/dining/table/config",
1108
+ "/order/resource/occupy-detail",
1096
1109
  {
1097
- table_resource_id: tableResourceId,
1098
- with_table_form_info: 1
1110
+ shop_id: shopId,
1111
+ "form_record_ids[]": formRecordId,
1112
+ with_resource_capacity_info: 1,
1113
+ with_resource_order_info: 1,
1114
+ with_resource_form_info: 1
1099
1115
  }
1100
1116
  );
1101
1117
  if (!(response == null ? void 0 : response.status)) {
1102
- throw new Error((response == null ? void 0 : response.message) || "获取桌台配置失败");
1118
+ throw new Error((response == null ? void 0 : response.message) || "获取资源占用详情失败");
1103
1119
  }
1104
- return (response == null ? void 0 : response.data) || null;
1120
+ return ((_d = (_c = response == null ? void 0 : response.data) == null ? void 0 : _c.occupy_details) == null ? void 0 : _d[0]) ?? null;
1105
1121
  }
1106
1122
  // 检测当前链接是否可用
1107
1123
  // 通过 resource_id + 店铺配置
@@ -1156,33 +1172,15 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1156
1172
  if (outsideOperatingHours && closedBehaviorValue !== "show_menu_disabled") {
1157
1173
  return makeShopClosed(closedMessage, closedBehaviorValue);
1158
1174
  }
1159
- const config = await this.fetchTableConfigByResourceId(resourceId);
1160
- const resourceState = this.normalizeResourceState(config, hasOrderId);
1161
- this.store.resource = resourceState;
1162
- const availabilityInfo = {
1163
- mode: resourceState.mode,
1164
- order_id: resourceState.order_id,
1165
- relation_id: resourceState.relationId,
1166
- table_form_id: resourceState.tableFormId,
1167
- deskmate_valid: resourceState.deskmate_valid,
1168
- table_form_record: resourceState.table_form_record,
1169
- policy: (_c = config == null ? void 0 : config.table_form_record) == null ? void 0 : _c.policy,
1170
- partyroom_booking: (_d = config == null ? void 0 : config.table_form_record) == null ? void 0 : _d.partyroom_booking
1171
- };
1172
1175
  const tempOrder = this.ensureTempOrder();
1173
- tempOrder.relation_id = resourceId || ((_e = this.otherParams) == null ? void 0 : _e.relation_id);
1174
- tempOrder.table_form_id = resourceState.tableFormId;
1175
- tempOrder.resource_id = resourceId;
1176
1176
  const reservationLinkIds = (0, import_utils.collectLinkProductIdsFromReservationRules)(
1177
1177
  dineInConfig["fulfillment.enabled_resource_rules"]
1178
1178
  );
1179
+ let pendingRequestEntryPax;
1180
+ let pendingRequestPaxMin;
1181
+ let pendingRequestPaxMax;
1179
1182
  if (reservationLinkIds.length === 0) {
1180
1183
  this.enabledReservationRuleProducts = [];
1181
- if (this.store.resource) {
1182
- delete this.store.resource.requestEntryPax;
1183
- delete this.store.resource.requestPaxMin;
1184
- delete this.store.resource.requestPaxMax;
1185
- }
1186
1184
  } else {
1187
1185
  tempOrder.metadata = { ...tempOrder.metadata || {} };
1188
1186
  delete tempOrder.metadata.table_occupancy_duration;
@@ -1214,38 +1212,15 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1214
1212
  tempOrder.metadata.table_occupancy_duration = occupancyMinutes;
1215
1213
  }
1216
1214
  if ((0, import_utils.hasCustomCapacityProduct)(loaded)) {
1217
- availabilityInfo.requestEntryPax = 1;
1218
- if (this.store.resource)
1219
- this.store.resource.requestEntryPax = 1;
1220
- delete availabilityInfo.requestPaxMin;
1221
- delete availabilityInfo.requestPaxMax;
1222
- if (this.store.resource) {
1223
- delete this.store.resource.requestPaxMin;
1224
- delete this.store.resource.requestPaxMax;
1225
- }
1215
+ pendingRequestEntryPax = 1;
1226
1216
  const paxBounds = (0, import_utils.pickFirstCustomCapacityPaxBounds)(loaded);
1227
- if ((paxBounds == null ? void 0 : paxBounds.min) !== void 0) {
1228
- availabilityInfo.requestPaxMin = paxBounds.min;
1229
- if (this.store.resource)
1230
- this.store.resource.requestPaxMin = paxBounds.min;
1231
- }
1232
- if ((paxBounds == null ? void 0 : paxBounds.max) !== void 0) {
1233
- availabilityInfo.requestPaxMax = paxBounds.max;
1234
- if (this.store.resource)
1235
- this.store.resource.requestPaxMax = paxBounds.max;
1236
- }
1237
- } else if (this.store.resource) {
1238
- delete this.store.resource.requestEntryPax;
1239
- delete this.store.resource.requestPaxMin;
1240
- delete this.store.resource.requestPaxMax;
1217
+ if ((paxBounds == null ? void 0 : paxBounds.min) !== void 0)
1218
+ pendingRequestPaxMin = paxBounds.min;
1219
+ if ((paxBounds == null ? void 0 : paxBounds.max) !== void 0)
1220
+ pendingRequestPaxMax = paxBounds.max;
1241
1221
  }
1242
1222
  } else {
1243
1223
  this.enabledReservationRuleProducts = [];
1244
- if (this.store.resource) {
1245
- delete this.store.resource.requestEntryPax;
1246
- delete this.store.resource.requestPaxMin;
1247
- delete this.store.resource.requestPaxMax;
1248
- }
1249
1224
  void this.addScanOrderLog({
1250
1225
  level: "error",
1251
1226
  title: "[ScanOrder] enabled_reservation_rules product query failed",
@@ -1256,6 +1231,41 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1256
1231
  });
1257
1232
  }
1258
1233
  }
1234
+ const occupyDetail = await this.fetchResourceOccupyDetailByResourceId(resourceId);
1235
+ const resourceSelectType = this.resolveResourceSelectType(
1236
+ (0, import_utils.toPositiveString)(occupyDetail == null ? void 0 : occupyDetail.form_id)
1237
+ );
1238
+ const resourceState = this.normalizeResourceState(
1239
+ occupyDetail,
1240
+ resourceSelectType,
1241
+ hasOrderId
1242
+ );
1243
+ this.store.resource = resourceState;
1244
+ if (pendingRequestEntryPax !== void 0) {
1245
+ this.store.resource.requestEntryPax = pendingRequestEntryPax;
1246
+ }
1247
+ if (pendingRequestPaxMin !== void 0) {
1248
+ this.store.resource.requestPaxMin = pendingRequestPaxMin;
1249
+ }
1250
+ if (pendingRequestPaxMax !== void 0) {
1251
+ this.store.resource.requestPaxMax = pendingRequestPaxMax;
1252
+ }
1253
+ const availabilityInfo = {
1254
+ mode: resourceState.mode,
1255
+ order_id: resourceState.order_id,
1256
+ relation_id: resourceState.relationId,
1257
+ table_form_id: resourceState.tableFormId,
1258
+ deskmate_valid: resourceState.deskmate_valid,
1259
+ table_form_record: resourceState.table_form_record,
1260
+ policy: (_c = occupyDetail == null ? void 0 : occupyDetail.form_record) == null ? void 0 : _c.policy,
1261
+ partyroom_booking: (_d = occupyDetail == null ? void 0 : occupyDetail.form_record) == null ? void 0 : _d.partyroom_booking,
1262
+ ...this.store.resource.requestEntryPax !== void 0 ? { requestEntryPax: this.store.resource.requestEntryPax } : {},
1263
+ ...this.store.resource.requestPaxMin !== void 0 ? { requestPaxMin: this.store.resource.requestPaxMin } : {},
1264
+ ...this.store.resource.requestPaxMax !== void 0 ? { requestPaxMax: this.store.resource.requestPaxMax } : {}
1265
+ };
1266
+ tempOrder.relation_id = resourceId || ((_e = this.otherParams) == null ? void 0 : _e.relation_id);
1267
+ tempOrder.table_form_id = resourceState.tableFormId;
1268
+ tempOrder.resource_id = resourceId;
1259
1269
  (_f = this.store.order) == null ? void 0 : _f.persistTempOrder();
1260
1270
  if (availabilityInfo.mode === "idle") {
1261
1271
  await this.addNewOrder();
@@ -1303,8 +1313,7 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1303
1313
  persistedRelationId: tempOrder.relation_id,
1304
1314
  persistedResourceId: tempOrder.resource_id,
1305
1315
  deskmateValid: availabilityInfo.deskmate_valid,
1306
- orderCount: resourceState.orderCount,
1307
- tableMaxNumber: resourceState.tableMaxNumber,
1316
+ resourceSelectType: resourceState.resourceSelectType,
1308
1317
  isExclusive: resourceState.isExclusive,
1309
1318
  isFull: resourceState.isFull
1310
1319
  });
@@ -1367,6 +1376,69 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1367
1376
  this.otherParams = { ...this.otherParams, ...params };
1368
1377
  }
1369
1378
  }
1379
+ getUIStateBucketKey() {
1380
+ if (!this.cacheId)
1381
+ return null;
1382
+ return `${_ScanOrderImpl.UI_STATE_KEY_PREFIX}${this.cacheId}`;
1383
+ }
1384
+ readUIStateBucket() {
1385
+ var _a;
1386
+ const key = this.getUIStateBucketKey();
1387
+ if (!key || !((_a = this.window) == null ? void 0 : _a.sessionStorage))
1388
+ return {};
1389
+ try {
1390
+ const raw = this.window.sessionStorage.getItem(key) || "{}";
1391
+ const parsed = JSON.parse(raw);
1392
+ return parsed && typeof parsed === "object" ? parsed : {};
1393
+ } catch {
1394
+ return {};
1395
+ }
1396
+ }
1397
+ writeUIStateBucket(bucket) {
1398
+ var _a;
1399
+ const key = this.getUIStateBucketKey();
1400
+ if (!key || !((_a = this.window) == null ? void 0 : _a.sessionStorage))
1401
+ return;
1402
+ try {
1403
+ this.window.sessionStorage.setItem(key, JSON.stringify(bucket));
1404
+ } catch (error) {
1405
+ console.warn("[ScanOrder] writeUIStateBucket failed", error);
1406
+ }
1407
+ }
1408
+ setUIState(key, value) {
1409
+ if (!this.getUIStateBucketKey())
1410
+ return;
1411
+ const bucket = this.readUIStateBucket();
1412
+ bucket[key] = value;
1413
+ this.writeUIStateBucket(bucket);
1414
+ }
1415
+ getUIState(key) {
1416
+ if (!this.getUIStateBucketKey())
1417
+ return void 0;
1418
+ const bucket = this.readUIStateBucket();
1419
+ return bucket[key];
1420
+ }
1421
+ deleteUIState(key) {
1422
+ if (!this.getUIStateBucketKey())
1423
+ return;
1424
+ const bucket = this.readUIStateBucket();
1425
+ if (key in bucket) {
1426
+ delete bucket[key];
1427
+ this.writeUIStateBucket(bucket);
1428
+ }
1429
+ }
1430
+ // 整桶清空(用于扫新桌、提交成功、restoreOrder 等场景)
1431
+ clearUIState() {
1432
+ var _a;
1433
+ const key = this.getUIStateBucketKey();
1434
+ if (!key || !((_a = this.window) == null ? void 0 : _a.sessionStorage))
1435
+ return;
1436
+ try {
1437
+ this.window.sessionStorage.removeItem(key);
1438
+ } catch (error) {
1439
+ console.warn("[ScanOrder] clearUIState failed", error);
1440
+ }
1441
+ }
1370
1442
  async setEntryPaxNumber(number) {
1371
1443
  const pax = (0, import_utils2.normalizeSubmitCollectPaxValue)(number);
1372
1444
  this.store.entryPaxNumber = pax;
@@ -1416,6 +1488,13 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1416
1488
  return result;
1417
1489
  }
1418
1490
  };
1491
+ var ScanOrderImpl = _ScanOrderImpl;
1492
+ // ─── UI 状态缓存(按 cacheId 分桶,sessionStorage) ───
1493
+ //
1494
+ // 用于物料层持久化 UI 层面的轻量状态(如当前步骤、gate 标记、登录回跳意图等)。
1495
+ // 桶键:pisell.scanOrder.uiState:<cacheId>;内部以 JSON object 形式存储多个字段。
1496
+ // 无 cacheId 时所有方法自动降级为 no-op,上层不用判空。
1497
+ ScanOrderImpl.UI_STATE_KEY_PREFIX = "pisell.scanOrder.uiState:";
1419
1498
  // Annotate the CommonJS export names for ESM import in node:
1420
1499
  0 && (module.exports = {
1421
1500
  ScanOrderImpl,
@@ -141,10 +141,11 @@ export interface ScanOrderSubmitPayload extends Omit<ScanOrderTempOrder, 'platfo
141
141
  }>;
142
142
  products: ScanOrderSubmitProduct[];
143
143
  }
144
- export type ScanOrderAvailabilityMode = 'idle' | 'shop_closed' | 'submit_disabled' | 'resource_block' | 'resource_busy' | 'additional_order_with_code' | 'additional_order';
144
+ export type ScanOrderAvailabilityMode = 'idle' | 'shop_closed' | 'submit_disabled' | 'resource_busy' | 'additional_order_with_code' | 'additional_order';
145
145
  export interface ScanOrderTableFormRecord {
146
146
  policy?: string | null;
147
147
  partyroom_booking?: string | null;
148
+ capacity?: number | string | null;
148
149
  [key: string]: any;
149
150
  }
150
151
  export interface ScanOrderAvailabilityInfo {
@@ -156,7 +157,7 @@ export interface ScanOrderAvailabilityInfo {
156
157
  errorTips?: string;
157
158
  /** 透传 `availability.closed_behavior`,便于 UI 识别拦截类型(如 show_menu_disabled) */
158
159
  closed_behavior?: string;
159
- /** `/order/dining/table/config` 返回的 `table_form_record` 原样透出 */
160
+ /** `/order/resource/occupy-detail` 返回的 `form_record` 原样透出 */
160
161
  table_form_record?: ScanOrderTableFormRecord | null;
161
162
  policy?: string | null;
162
163
  partyroom_booking?: string | null;
@@ -167,34 +168,36 @@ export interface ScanOrderAvailabilityInfo {
167
168
  /** 首个 `capacity.type === 'custom'` 商品里 `custom[0]` 的 max(人数上限) */
168
169
  requestPaxMax?: number;
169
170
  }
170
- export interface ScanOrderTableSnackConfig {
171
- snack?: boolean | number | string;
172
- table_validate?: boolean | number | string;
171
+ /** `resource_capacity[i].capacity_list[j]` */
172
+ export interface ScanOrderResourceCapacitySlot {
173
+ start_at?: string;
174
+ end_at?: string;
175
+ pax?: number | string;
173
176
  }
174
- export interface ScanOrderOrderNumberPrefixConfig {
175
- table_order?: string;
176
- pos?: string;
177
+ export interface ScanOrderResourceCapacity {
178
+ capacity?: number | string;
179
+ capacity_list?: ScanOrderResourceCapacitySlot[];
177
180
  }
178
- export interface ScanOrderTableConfigApiData {
179
- table_max_number?: number | string | null;
180
- order_count?: number | string | null;
181
+ /** `/order/resource/occupy-detail` 单条 `occupy_details[i]` */
182
+ export interface ScanOrderResourceOccupyDetail {
183
+ form_record_id?: number | string | null;
184
+ form_id?: number | string | null;
181
185
  order_id?: number | string | null;
182
186
  last_order_id?: number | string | null;
183
- relation_id?: number | string | null;
184
- table_form_id?: number | string | null;
185
- table_snack?: ScanOrderTableSnackConfig[] | null;
186
- order_number_prefix?: ScanOrderOrderNumberPrefixConfig[] | null;
187
+ resource_capacity?: ScanOrderResourceCapacity[] | null;
188
+ form_record?: ScanOrderTableFormRecord | null;
187
189
  [key: string]: any;
188
190
  }
189
- export interface ScanOrderTableConfigApiResponse {
191
+ export interface ScanOrderResourceOccupyDetailApiResponse {
190
192
  status?: boolean;
191
193
  code?: number;
192
194
  message?: string;
193
- data?: ScanOrderTableConfigApiData | null;
195
+ data?: {
196
+ occupy_details?: ScanOrderResourceOccupyDetail[] | null;
197
+ } | null;
194
198
  }
199
+ export type ScanOrderResourceSelectType = 'single' | 'multiple' | 'capacity';
195
200
  export interface ScanOrderResourceState extends ScanOrderAvailabilityInfo {
196
- tableMaxNumber: number;
197
- orderCount: number;
198
201
  relationId?: string;
199
202
  tableFormId?: string;
200
203
  currentOrderId?: string;
@@ -203,13 +206,14 @@ export interface ScanOrderResourceState extends ScanOrderAvailabilityInfo {
203
206
  deskmateValid: boolean;
204
207
  isExclusive: boolean;
205
208
  isFull: boolean;
206
- orderNumberPrefix: ScanOrderOrderNumberPrefixConfig[];
207
- raw: ScanOrderTableConfigApiData | null;
209
+ /** 来自首个预约规则商品 product_resource.resources 中与 form_id 匹配的 resource type */
210
+ resourceSelectType?: ScanOrderResourceSelectType;
211
+ raw: ScanOrderResourceOccupyDetail | null;
208
212
  }
209
213
  export interface ScanOrderState {
210
214
  entryContext: ScanOrderEntryContext | null;
211
215
  status: ScanOrderStatus;
212
- config: ScanOrderTableConfigApiData | null;
216
+ config: Record<string, any> | null;
213
217
  resource: ScanOrderResourceState | null;
214
218
  flow: Record<string, any>;
215
219
  error: string | null;
@@ -1,4 +1,4 @@
1
- import { ScanOrderOrderProduct, ScanOrderOrderProductIdentity, ScanOrderSummary, ScanOrderTempOrder } from './types';
1
+ import { ScanOrderOrderProduct, ScanOrderOrderProductIdentity, ScanOrderResourceCapacitySlot, ScanOrderResourceSelectType, ScanOrderSummary, ScanOrderTempOrder } from './types';
2
2
  import type { CartItemSummary, ItemRuleBusinessData, PaxInfo, QuantityLimitResult } from '../../model/strategy/adapter/itemRule';
3
3
  import type { StrategyConfig } from '../../model/strategy/type';
4
4
  import type { ProductData } from '../../modules/Product/types';
@@ -109,6 +109,18 @@ export declare function collectLinkProductIdsFromReservationRules(rules: unknown
109
109
  export declare function pickFirstDurationMinutesFromProducts(products: ProductData[]): number | undefined;
110
110
  /** 是否存在 capacity.type === 'custom' 的商品 */
111
111
  export declare function hasCustomCapacityProduct(products: ProductData[]): boolean;
112
+ /**
113
+ * 根据预约规则商品的 resource.type 计算桌台是否已被"占满"。
114
+ * - single:只要有 `lastOrderId` 即视为占用
115
+ * - multiple:当前时间落在 `capacity_list[i]` 的 `[start_at, end_at]`(inclusive)内的 pax 之和 > 总容量
116
+ * - 其他('capacity' / undefined):返回 false,不施加限制
117
+ */
118
+ export declare function computeResourceIsFull(params: {
119
+ resourceSelectType?: ScanOrderResourceSelectType;
120
+ lastOrderId?: string;
121
+ capacityList?: ScanOrderResourceCapacitySlot[];
122
+ capacity?: number | string | null;
123
+ }): boolean;
112
124
  /**
113
125
  * 在商品列表中找到第一个 `capacity.type === 'custom'` 的商品,取其 `custom` 数组第一项的 min/max。
114
126
  * 仅返回有限数字字段;若均无法解析则返回 `undefined`。
@@ -1,6 +1,8 @@
1
+ var __create = Object.create;
1
2
  var __defProp = Object.defineProperty;
2
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
4
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
7
  var __export = (target, all) => {
6
8
  for (var name in all)
@@ -14,6 +16,14 @@ var __copyProps = (to, from, except, desc) => {
14
16
  }
15
17
  return to;
16
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
+ ));
17
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
28
 
19
29
  // src/solution/ScanOrder/utils.ts
@@ -25,6 +35,7 @@ __export(utils_exports, {
25
35
  buildProductKey: () => buildProductKey,
26
36
  buildQuantityLimitIndex: () => buildQuantityLimitIndex,
27
37
  collectLinkProductIdsFromReservationRules: () => collectLinkProductIdsFromReservationRules,
38
+ computeResourceIsFull: () => computeResourceIsFull,
28
39
  createEmptySummary: () => createEmptySummary,
29
40
  extractStrategyModelIdsFromTableConfig: () => extractStrategyModelIdsFromTableConfig,
30
41
  getProductIdentityIndex: () => getProductIdentityIndex,
@@ -46,6 +57,7 @@ __export(utils_exports, {
46
57
  toPriceString: () => toPriceString
47
58
  });
48
59
  module.exports = __toCommonJS(utils_exports);
60
+ var import_dayjs = __toESM(require("dayjs"));
49
61
  function createEmptySummary() {
50
62
  return {
51
63
  product_quantity: 0,
@@ -411,6 +423,30 @@ function hasCustomCapacityProduct(products) {
411
423
  return ((_a = p == null ? void 0 : p.capacity) == null ? void 0 : _a.type) === "custom";
412
424
  });
413
425
  }
426
+ function computeResourceIsFull(params) {
427
+ const { resourceSelectType, lastOrderId, capacityList, capacity } = params;
428
+ if (resourceSelectType === "single")
429
+ return Boolean(lastOrderId);
430
+ if (resourceSelectType !== "multiple")
431
+ return false;
432
+ const totalCapacity = Number(capacity);
433
+ if (!Number.isFinite(totalCapacity) || totalCapacity <= 0)
434
+ return false;
435
+ const now = (0, import_dayjs.default)();
436
+ let occupied = 0;
437
+ for (const slot of capacityList || []) {
438
+ const start = (0, import_dayjs.default)(slot == null ? void 0 : slot.start_at);
439
+ const end = (0, import_dayjs.default)(slot == null ? void 0 : slot.end_at);
440
+ if (!start.isValid() || !end.isValid())
441
+ continue;
442
+ if ((now.isAfter(start) || now.isSame(start)) && (now.isBefore(end) || now.isSame(end))) {
443
+ const pax = Number(slot == null ? void 0 : slot.pax);
444
+ if (Number.isFinite(pax) && pax > 0)
445
+ occupied += pax;
446
+ }
447
+ }
448
+ return occupied > totalCapacity;
449
+ }
414
450
  function pickFirstCustomCapacityPaxBounds(products) {
415
451
  for (const p of products) {
416
452
  const cap = p == null ? void 0 : p.capacity;
@@ -446,6 +482,7 @@ function pickFirstCustomCapacityPaxBounds(products) {
446
482
  buildProductKey,
447
483
  buildQuantityLimitIndex,
448
484
  collectLinkProductIdsFromReservationRules,
485
+ computeResourceIsFull,
449
486
  createEmptySummary,
450
487
  extractStrategyModelIdsFromTableConfig,
451
488
  getProductIdentityIndex,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "0.0.503",
4
+ "version": "0.0.505",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",