@pisell/pisellos 0.0.504 → 0.0.506

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";
@@ -84,11 +84,11 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
84
84
  }
85
85
  serializeError(error) {
86
86
  if (error instanceof Error) {
87
- return {
87
+ return JSON.stringify({
88
88
  name: error.name,
89
89
  message: error.message,
90
90
  stack: error.stack
91
- };
91
+ });
92
92
  }
93
93
  return {
94
94
  message: String(error)
@@ -496,11 +496,24 @@ 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;
506
+ const resolveResourceCapacity = () => {
507
+ var _a;
508
+ if (resourceSelectType === "single") {
509
+ const raw = (_a = resourceState == null ? void 0 : resourceState.table_form_record) == null ? void 0 : _a.capacity;
510
+ const num = Number(raw);
511
+ if (Number.isFinite(num) && num > 0)
512
+ return num;
513
+ return 1;
514
+ }
515
+ return 1;
516
+ };
504
517
  return (payload, { bookingUuid, tempOrder }) => {
505
518
  var _a;
506
519
  const resourceId = tempOrder.resource_id ?? (resourceState == null ? void 0 : resourceState.relationId);
@@ -519,13 +532,19 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
519
532
  main_field: mainField,
520
533
  form_id: resourceState.tableFormId,
521
534
  relation_id: resourceState.relationId ?? resourceId,
522
- capacity: 1,
535
+ capacity: resolveResourceCapacity(),
523
536
  metadata: {}
524
537
  } : void 0;
525
538
  const ruleProductUid = ruleProduct ? (0, import_utils2.createUuidV4)() : void 0;
539
+ const bookingCapacityValue = resolveResourceCapacity();
526
540
  const nextBookings = (payload.bookings || []).map((booking, idx) => ({
527
541
  ...booking,
528
542
  appointment_status: "started",
543
+ metadata: {
544
+ ...booking.metadata || {},
545
+ ...resourceSelectType ? { resource_select_type: resourceSelectType } : {},
546
+ ...resourceSelectType ? { capacity: [{ id: 0, value: bookingCapacityValue, name: "" }] } : {}
547
+ },
529
548
  ...idx === 0 && resourceEntry ? { resources: [resourceEntry] } : {},
530
549
  ...idx === 0 && ruleProductUid ? { product_uid: ruleProductUid } : {}
531
550
  }));
@@ -1009,20 +1028,22 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1009
1028
  strategyCount: Array.isArray(this.itemRuleRuntimeConfig.strategyConfigs) ? this.itemRuleRuntimeConfig.strategyConfigs.length : 0
1010
1029
  });
1011
1030
  }
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);
1031
+ normalizeResourceState(detail, resourceSelectType, hasOrderId) {
1032
+ var _a, _b, _c, _d;
1033
+ const currentOrderId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.order_id);
1034
+ const lastOrderId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.last_order_id);
1035
+ const relationId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.form_record_id);
1036
+ const tableFormId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.form_id);
1037
+ const formRecord = (detail == null ? void 0 : detail.form_record) ?? null;
1021
1038
  const allowSnack = ((_b = (_a = this.otherParams) == null ? void 0 : _a.dineInConfig) == null ? void 0 : _b["workflow.allow_add_items"]) || false;
1022
1039
  const deskmateValid = false;
1023
- const isExclusive = tableMaxNumber > 0 ? tableMaxNumber <= 1 : false;
1024
- const isFull = tableMaxNumber > 0 ? orderCount >= tableMaxNumber : false;
1025
- const isBlock = tableMaxNumber === -1;
1040
+ const isExclusive = resourceSelectType === "single";
1041
+ const isFull = (0, import_utils.computeResourceIsFull)({
1042
+ resourceSelectType,
1043
+ lastOrderId,
1044
+ capacityList: ((_d = (_c = detail == null ? void 0 : detail.resource_capacity) == null ? void 0 : _c[0]) == null ? void 0 : _d.capacity_list) ?? [],
1045
+ capacity: formRecord == null ? void 0 : formRecord.capacity
1046
+ });
1026
1047
  let availabilityInfo = {
1027
1048
  mode: "idle",
1028
1049
  deskmate_valid: deskmateValid
@@ -1046,33 +1067,19 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1046
1067
  };
1047
1068
  }
1048
1069
  } else if (lastOrderId) {
1049
- availabilityInfo = allowSnack ? {
1050
- mode: deskmateValid ? "additional_order_with_code" : "additional_order",
1051
- order_id: lastOrderId,
1052
- relation_id: relationId,
1053
- table_form_id: tableFormId,
1054
- deskmate_valid: deskmateValid
1055
- } : {
1056
- mode: "resource_busy",
1057
- order_id: lastOrderId,
1058
- relation_id: relationId,
1059
- table_form_id: tableFormId,
1060
- deskmate_valid: deskmateValid
1061
- };
1062
- }
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
- };
1070
+ const canFallthroughToIdle = resourceSelectType === "multiple" && !isFull;
1071
+ if (!canFallthroughToIdle) {
1072
+ availabilityInfo = {
1073
+ mode: "resource_busy",
1074
+ order_id: "0",
1075
+ relation_id: relationId,
1076
+ table_form_id: tableFormId,
1077
+ deskmate_valid: deskmateValid
1078
+ };
1079
+ }
1071
1080
  }
1072
1081
  return {
1073
1082
  ...availabilityInfo,
1074
- tableMaxNumber,
1075
- orderCount,
1076
1083
  currentOrderId,
1077
1084
  lastOrderId,
1078
1085
  relationId,
@@ -1081,27 +1088,46 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1081
1088
  deskmateValid,
1082
1089
  isExclusive,
1083
1090
  isFull,
1084
- orderNumberPrefix,
1085
- raw: config,
1086
- table_form_record: (config == null ? void 0 : config.table_form_record) ?? null
1091
+ resourceSelectType,
1092
+ raw: detail,
1093
+ table_form_record: formRecord
1087
1094
  };
1088
1095
  }
1089
- async fetchTableConfigByResourceId(resourceId) {
1090
- const tableResourceId = Number(resourceId);
1091
- if (!Number.isFinite(tableResourceId) || tableResourceId <= 0) {
1096
+ // 从首个预约规则商品的 product_resource.resources 中,按 form_id 匹配返回 type
1097
+ resolveResourceSelectType(formId) {
1098
+ var _a;
1099
+ const firstProduct = this.enabledReservationRuleProducts[0];
1100
+ const resources = ((_a = firstProduct == null ? void 0 : firstProduct.product_resource) == null ? void 0 : _a.resources) || [];
1101
+ const numericFormId = Number(formId);
1102
+ if (!Number.isFinite(numericFormId) || numericFormId <= 0)
1103
+ return void 0;
1104
+ const matched = resources.find((r) => Number(r == null ? void 0 : r.id) === numericFormId);
1105
+ return matched == null ? void 0 : matched.type;
1106
+ }
1107
+ async fetchResourceOccupyDetailByResourceId(resourceId) {
1108
+ var _a, _b, _c, _d;
1109
+ const formRecordId = Number(resourceId);
1110
+ if (!Number.isFinite(formRecordId) || formRecordId <= 0) {
1092
1111
  throw new Error(`[ScanOrder] 非法桌台 resourceId: ${resourceId}`);
1093
1112
  }
1113
+ const shopId = (_b = (_a = this.otherParams) == null ? void 0 : _a.getStateData) == null ? void 0 : _b.call(_a, "shop_id");
1114
+ if (!shopId) {
1115
+ throw new Error("[ScanOrder] 无法获取 shop_id");
1116
+ }
1094
1117
  const response = await this.request.get(
1095
- "/order/dining/table/config",
1118
+ "/order/resource/occupy-detail",
1096
1119
  {
1097
- table_resource_id: tableResourceId,
1098
- with_table_form_info: 1
1120
+ shop_id: shopId,
1121
+ "form_record_ids[]": formRecordId,
1122
+ with_resource_capacity_info: 1,
1123
+ with_resource_order_info: 1,
1124
+ with_resource_form_info: 1
1099
1125
  }
1100
1126
  );
1101
1127
  if (!(response == null ? void 0 : response.status)) {
1102
- throw new Error((response == null ? void 0 : response.message) || "获取桌台配置失败");
1128
+ throw new Error((response == null ? void 0 : response.message) || "获取资源占用详情失败");
1103
1129
  }
1104
- return (response == null ? void 0 : response.data) || null;
1130
+ return ((_d = (_c = response == null ? void 0 : response.data) == null ? void 0 : _c.occupy_details) == null ? void 0 : _d[0]) ?? null;
1105
1131
  }
1106
1132
  // 检测当前链接是否可用
1107
1133
  // 通过 resource_id + 店铺配置
@@ -1156,33 +1182,15 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1156
1182
  if (outsideOperatingHours && closedBehaviorValue !== "show_menu_disabled") {
1157
1183
  return makeShopClosed(closedMessage, closedBehaviorValue);
1158
1184
  }
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
1185
  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
1186
  const reservationLinkIds = (0, import_utils.collectLinkProductIdsFromReservationRules)(
1177
1187
  dineInConfig["fulfillment.enabled_resource_rules"]
1178
1188
  );
1189
+ let pendingRequestEntryPax;
1190
+ let pendingRequestPaxMin;
1191
+ let pendingRequestPaxMax;
1179
1192
  if (reservationLinkIds.length === 0) {
1180
1193
  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
1194
  } else {
1187
1195
  tempOrder.metadata = { ...tempOrder.metadata || {} };
1188
1196
  delete tempOrder.metadata.table_occupancy_duration;
@@ -1214,38 +1222,15 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1214
1222
  tempOrder.metadata.table_occupancy_duration = occupancyMinutes;
1215
1223
  }
1216
1224
  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
- }
1225
+ pendingRequestEntryPax = 1;
1226
1226
  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;
1227
+ if ((paxBounds == null ? void 0 : paxBounds.min) !== void 0)
1228
+ pendingRequestPaxMin = paxBounds.min;
1229
+ if ((paxBounds == null ? void 0 : paxBounds.max) !== void 0)
1230
+ pendingRequestPaxMax = paxBounds.max;
1241
1231
  }
1242
1232
  } else {
1243
1233
  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
1234
  void this.addScanOrderLog({
1250
1235
  level: "error",
1251
1236
  title: "[ScanOrder] enabled_reservation_rules product query failed",
@@ -1256,6 +1241,41 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1256
1241
  });
1257
1242
  }
1258
1243
  }
1244
+ const occupyDetail = await this.fetchResourceOccupyDetailByResourceId(resourceId);
1245
+ const resourceSelectType = this.resolveResourceSelectType(
1246
+ (0, import_utils.toPositiveString)(occupyDetail == null ? void 0 : occupyDetail.form_id)
1247
+ );
1248
+ const resourceState = this.normalizeResourceState(
1249
+ occupyDetail,
1250
+ resourceSelectType,
1251
+ hasOrderId
1252
+ );
1253
+ this.store.resource = resourceState;
1254
+ if (pendingRequestEntryPax !== void 0) {
1255
+ this.store.resource.requestEntryPax = pendingRequestEntryPax;
1256
+ }
1257
+ if (pendingRequestPaxMin !== void 0) {
1258
+ this.store.resource.requestPaxMin = pendingRequestPaxMin;
1259
+ }
1260
+ if (pendingRequestPaxMax !== void 0) {
1261
+ this.store.resource.requestPaxMax = pendingRequestPaxMax;
1262
+ }
1263
+ const availabilityInfo = {
1264
+ mode: resourceState.mode,
1265
+ order_id: resourceState.order_id,
1266
+ relation_id: resourceState.relationId,
1267
+ table_form_id: resourceState.tableFormId,
1268
+ deskmate_valid: resourceState.deskmate_valid,
1269
+ table_form_record: resourceState.table_form_record,
1270
+ policy: (_c = occupyDetail == null ? void 0 : occupyDetail.form_record) == null ? void 0 : _c.policy,
1271
+ partyroom_booking: (_d = occupyDetail == null ? void 0 : occupyDetail.form_record) == null ? void 0 : _d.partyroom_booking,
1272
+ ...this.store.resource.requestEntryPax !== void 0 ? { requestEntryPax: this.store.resource.requestEntryPax } : {},
1273
+ ...this.store.resource.requestPaxMin !== void 0 ? { requestPaxMin: this.store.resource.requestPaxMin } : {},
1274
+ ...this.store.resource.requestPaxMax !== void 0 ? { requestPaxMax: this.store.resource.requestPaxMax } : {}
1275
+ };
1276
+ tempOrder.relation_id = resourceId || ((_e = this.otherParams) == null ? void 0 : _e.relation_id);
1277
+ tempOrder.table_form_id = resourceState.tableFormId;
1278
+ tempOrder.resource_id = resourceId;
1259
1279
  (_f = this.store.order) == null ? void 0 : _f.persistTempOrder();
1260
1280
  if (availabilityInfo.mode === "idle") {
1261
1281
  await this.addNewOrder();
@@ -1303,14 +1323,13 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1303
1323
  persistedRelationId: tempOrder.relation_id,
1304
1324
  persistedResourceId: tempOrder.resource_id,
1305
1325
  deskmateValid: availabilityInfo.deskmate_valid,
1306
- orderCount: resourceState.orderCount,
1307
- tableMaxNumber: resourceState.tableMaxNumber,
1326
+ resourceSelectType: resourceState.resourceSelectType,
1308
1327
  isExclusive: resourceState.isExclusive,
1309
1328
  isFull: resourceState.isFull
1310
1329
  });
1311
1330
  return availabilityInfo;
1312
1331
  } catch (error) {
1313
- this.logMethodError("checkResourceAvailable", error, {
1332
+ this.logMethodError("checkResourceAvailable", error.message, {
1314
1333
  resourceId
1315
1334
  });
1316
1335
  throw error;
@@ -1353,7 +1372,7 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1353
1372
  });
1354
1373
  return formattedRes;
1355
1374
  } catch (error) {
1356
- this.logMethodError("getProductList", error);
1375
+ this.logMethodError("getProductList", error.message);
1357
1376
  throw error;
1358
1377
  }
1359
1378
  }
@@ -1367,6 +1386,69 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1367
1386
  this.otherParams = { ...this.otherParams, ...params };
1368
1387
  }
1369
1388
  }
1389
+ getUIStateBucketKey() {
1390
+ if (!this.cacheId)
1391
+ return null;
1392
+ return `${_ScanOrderImpl.UI_STATE_KEY_PREFIX}${this.cacheId}`;
1393
+ }
1394
+ readUIStateBucket() {
1395
+ var _a;
1396
+ const key = this.getUIStateBucketKey();
1397
+ if (!key || !((_a = this.window) == null ? void 0 : _a.sessionStorage))
1398
+ return {};
1399
+ try {
1400
+ const raw = this.window.sessionStorage.getItem(key) || "{}";
1401
+ const parsed = JSON.parse(raw);
1402
+ return parsed && typeof parsed === "object" ? parsed : {};
1403
+ } catch {
1404
+ return {};
1405
+ }
1406
+ }
1407
+ writeUIStateBucket(bucket) {
1408
+ var _a;
1409
+ const key = this.getUIStateBucketKey();
1410
+ if (!key || !((_a = this.window) == null ? void 0 : _a.sessionStorage))
1411
+ return;
1412
+ try {
1413
+ this.window.sessionStorage.setItem(key, JSON.stringify(bucket));
1414
+ } catch (error) {
1415
+ console.warn("[ScanOrder] writeUIStateBucket failed", error);
1416
+ }
1417
+ }
1418
+ setUIState(key, value) {
1419
+ if (!this.getUIStateBucketKey())
1420
+ return;
1421
+ const bucket = this.readUIStateBucket();
1422
+ bucket[key] = value;
1423
+ this.writeUIStateBucket(bucket);
1424
+ }
1425
+ getUIState(key) {
1426
+ if (!this.getUIStateBucketKey())
1427
+ return void 0;
1428
+ const bucket = this.readUIStateBucket();
1429
+ return bucket[key];
1430
+ }
1431
+ deleteUIState(key) {
1432
+ if (!this.getUIStateBucketKey())
1433
+ return;
1434
+ const bucket = this.readUIStateBucket();
1435
+ if (key in bucket) {
1436
+ delete bucket[key];
1437
+ this.writeUIStateBucket(bucket);
1438
+ }
1439
+ }
1440
+ // 整桶清空(用于扫新桌、提交成功、restoreOrder 等场景)
1441
+ clearUIState() {
1442
+ var _a;
1443
+ const key = this.getUIStateBucketKey();
1444
+ if (!key || !((_a = this.window) == null ? void 0 : _a.sessionStorage))
1445
+ return;
1446
+ try {
1447
+ this.window.sessionStorage.removeItem(key);
1448
+ } catch (error) {
1449
+ console.warn("[ScanOrder] clearUIState failed", error);
1450
+ }
1451
+ }
1370
1452
  async setEntryPaxNumber(number) {
1371
1453
  const pax = (0, import_utils2.normalizeSubmitCollectPaxValue)(number);
1372
1454
  this.store.entryPaxNumber = pax;
@@ -1416,6 +1498,13 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1416
1498
  return result;
1417
1499
  }
1418
1500
  };
1501
+ var ScanOrderImpl = _ScanOrderImpl;
1502
+ // ─── UI 状态缓存(按 cacheId 分桶,sessionStorage) ───
1503
+ //
1504
+ // 用于物料层持久化 UI 层面的轻量状态(如当前步骤、gate 标记、登录回跳意图等)。
1505
+ // 桶键:pisell.scanOrder.uiState:<cacheId>;内部以 JSON object 形式存储多个字段。
1506
+ // 无 cacheId 时所有方法自动降级为 no-op,上层不用判空。
1507
+ ScanOrderImpl.UI_STATE_KEY_PREFIX = "pisell.scanOrder.uiState:";
1419
1508
  // Annotate the CommonJS export names for ESM import in node:
1420
1509
  0 && (module.exports = {
1421
1510
  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.504",
4
+ "version": "0.0.506",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",