@pisell/pisellos 0.10.8 → 0.10.9

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 (98) hide show
  1. package/dist/core/index.js +0 -1
  2. package/dist/model/strategy/adapter/dataVariant/evaluator.d.ts +18 -2
  3. package/dist/model/strategy/adapter/dataVariant/evaluator.js +287 -101
  4. package/dist/model/strategy/adapter/dataVariant/type.d.ts +25 -0
  5. package/dist/model/strategy/adapter/promotion/index.js +0 -9
  6. package/dist/model/strategy/adapter/walletPass/evaluator.js +0 -2
  7. package/dist/model/strategy/adapter/walletPass/type.d.ts +5 -0
  8. package/dist/model/strategy/adapter/walletPass/utils.js +57 -26
  9. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +7 -3
  10. package/dist/modules/BookingContext/utils/productExtend.js +5 -9
  11. package/dist/modules/BookingContext/utils/timeSlices.js +24 -3
  12. package/dist/modules/Customer/index.d.ts +4 -0
  13. package/dist/modules/Customer/index.js +91 -59
  14. package/dist/modules/Customer/types.d.ts +2 -0
  15. package/dist/modules/Discount/index.d.ts +2 -0
  16. package/dist/modules/Discount/index.js +38 -5
  17. package/dist/modules/Discount/types.d.ts +11 -0
  18. package/dist/modules/Order/index.d.ts +20 -1
  19. package/dist/modules/Order/index.js +976 -730
  20. package/dist/modules/Order/types.d.ts +6 -1
  21. package/dist/modules/Order/utils/virtualSettlement.d.ts +56 -0
  22. package/dist/modules/Order/utils/virtualSettlement.js +271 -0
  23. package/dist/modules/Order/utils.js +89 -48
  24. package/dist/modules/Payment/types.d.ts +2 -0
  25. package/dist/modules/Payment/utils.js +4 -1
  26. package/dist/modules/Payment/walletpass.d.ts +11 -0
  27. package/dist/modules/Payment/walletpass.js +235 -60
  28. package/dist/modules/Product/types.d.ts +23 -0
  29. package/dist/modules/ProductList/index.d.ts +15 -2
  30. package/dist/modules/ProductList/index.js +92 -13
  31. package/dist/modules/SalesSummary/index.d.ts +1 -0
  32. package/dist/modules/SalesSummary/index.js +4 -3
  33. package/dist/modules/SalesSummary/types.d.ts +1 -0
  34. package/dist/modules/SalesSummary/utils.d.ts +1 -0
  35. package/dist/modules/SalesSummary/utils.js +21 -1
  36. package/dist/server/index.d.ts +9 -0
  37. package/dist/server/index.js +90 -58
  38. package/dist/server/modules/order/index.d.ts +1 -0
  39. package/dist/server/modules/order/index.js +252 -94
  40. package/dist/server/utils/small-ticket.js +2 -1
  41. package/dist/solution/BaseSales/index.d.ts +26 -2
  42. package/dist/solution/BaseSales/index.js +1310 -931
  43. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +1 -0
  44. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +78 -12
  45. package/dist/solution/BookingByStep/index.d.ts +3 -3
  46. package/dist/solution/BookingTicket/index.d.ts +5 -1
  47. package/dist/solution/BookingTicket/index.js +221 -159
  48. package/dist/solution/BookingTicket/utils/addProductDecision.js +2 -1
  49. package/dist/solution/ScanOrder/index.js +25 -64
  50. package/lib/core/index.js +0 -1
  51. package/lib/model/strategy/adapter/dataVariant/evaluator.d.ts +18 -2
  52. package/lib/model/strategy/adapter/dataVariant/evaluator.js +170 -7
  53. package/lib/model/strategy/adapter/dataVariant/type.d.ts +25 -0
  54. package/lib/model/strategy/adapter/walletPass/evaluator.js +0 -2
  55. package/lib/model/strategy/adapter/walletPass/type.d.ts +5 -0
  56. package/lib/model/strategy/adapter/walletPass/utils.js +62 -11
  57. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +3 -0
  58. package/lib/modules/BookingContext/utils/productExtend.js +4 -3
  59. package/lib/modules/BookingContext/utils/timeSlices.js +18 -3
  60. package/lib/modules/Customer/index.d.ts +4 -0
  61. package/lib/modules/Customer/index.js +11 -0
  62. package/lib/modules/Customer/types.d.ts +2 -0
  63. package/lib/modules/Discount/index.d.ts +2 -0
  64. package/lib/modules/Discount/index.js +37 -3
  65. package/lib/modules/Discount/types.d.ts +11 -0
  66. package/lib/modules/Order/index.d.ts +20 -1
  67. package/lib/modules/Order/index.js +393 -128
  68. package/lib/modules/Order/types.d.ts +6 -1
  69. package/lib/modules/Order/utils/virtualSettlement.d.ts +56 -0
  70. package/lib/modules/Order/utils/virtualSettlement.js +300 -0
  71. package/lib/modules/Order/utils.js +65 -21
  72. package/lib/modules/Payment/types.d.ts +2 -0
  73. package/lib/modules/Payment/utils.js +5 -1
  74. package/lib/modules/Payment/walletpass.d.ts +11 -0
  75. package/lib/modules/Payment/walletpass.js +198 -17
  76. package/lib/modules/Product/types.d.ts +23 -0
  77. package/lib/modules/ProductList/index.d.ts +15 -2
  78. package/lib/modules/ProductList/index.js +85 -3
  79. package/lib/modules/SalesSummary/index.d.ts +1 -0
  80. package/lib/modules/SalesSummary/index.js +10 -2
  81. package/lib/modules/SalesSummary/types.d.ts +1 -0
  82. package/lib/modules/SalesSummary/utils.d.ts +1 -0
  83. package/lib/modules/SalesSummary/utils.js +25 -1
  84. package/lib/server/index.d.ts +9 -0
  85. package/lib/server/index.js +54 -14
  86. package/lib/server/modules/order/index.d.ts +1 -0
  87. package/lib/server/modules/order/index.js +66 -2
  88. package/lib/server/utils/small-ticket.js +1 -1
  89. package/lib/solution/BaseSales/index.d.ts +26 -2
  90. package/lib/solution/BaseSales/index.js +261 -23
  91. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +1 -0
  92. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +61 -11
  93. package/lib/solution/BookingByStep/index.d.ts +3 -3
  94. package/lib/solution/BookingTicket/index.d.ts +5 -1
  95. package/lib/solution/BookingTicket/index.js +30 -2
  96. package/lib/solution/BookingTicket/utils/addProductDecision.js +2 -1
  97. package/lib/solution/ScanOrder/index.js +17 -54
  98. package/package.json +1 -1
@@ -52,6 +52,25 @@ function normalizeWalletAssetCode(code) {
52
52
  function getWalletAssetCodeKeys(asset) {
53
53
  return [asset == null ? void 0 : asset.code, asset == null ? void 0 : asset.encoded].map(normalizeWalletAssetCode).filter(Boolean);
54
54
  }
55
+ function withWalletAssetRecalculationBaseAmount(asset) {
56
+ const existingBaseAmount = Number(
57
+ asset == null ? void 0 : asset._wallet_asset_recalculation_base_amount
58
+ );
59
+ if (Number.isFinite(existingBaseAmount) && existingBaseAmount >= 0) {
60
+ return asset;
61
+ }
62
+ if (!["product_voucher", "gift_card"].includes(asset == null ? void 0 : asset.tag)) {
63
+ return asset;
64
+ }
65
+ const balanceParValue = Number(asset == null ? void 0 : asset.balance_par_value);
66
+ if (!Number.isFinite(balanceParValue) || balanceParValue < 0) {
67
+ return asset;
68
+ }
69
+ return {
70
+ ...asset,
71
+ _wallet_asset_recalculation_base_amount: balanceParValue
72
+ };
73
+ }
55
74
  function isWalletAssetAvailable(asset) {
56
75
  if (!asset)
57
76
  return false;
@@ -90,6 +109,16 @@ function mergeWalletAssets(...groups) {
90
109
  });
91
110
  return merged;
92
111
  }
112
+ function orderWalletAssetsByIds(assets, orderedIds) {
113
+ const assetByKey = new Map(
114
+ assets.map((asset) => [getWalletAssetKey(asset), asset])
115
+ );
116
+ const orderedKeys = new Set(orderedIds.map((id) => String(id)));
117
+ return [
118
+ ...orderedIds.map((id) => assetByKey.get(String(id))).filter((asset) => asset !== void 0),
119
+ ...assets.filter((asset) => !orderedKeys.has(getWalletAssetKey(asset)))
120
+ ];
121
+ }
93
122
  function createInitialWalletAssetsState() {
94
123
  return {
95
124
  status: "idle",
@@ -222,6 +251,42 @@ var WalletPassPaymentImpl = class {
222
251
  error: null
223
252
  });
224
253
  }
254
+ /**
255
+ * 搜索接口只返回后端 Wallet Pass 数据,不包含前端策略 config。
256
+ * 在资产进入共享选择状态前补齐策略结果,避免增量重算回退到接口状态。
257
+ */
258
+ formatSearchedWalletPaymentAssets(assets) {
259
+ var _a, _b, _c, _d;
260
+ if (!assets.length)
261
+ return assets;
262
+ const assetsWithRecalculationBase = assets.map(
263
+ withWalletAssetRecalculationBaseAmount
264
+ );
265
+ const evaluator = (_b = (_a = this.paymentModule.window) == null ? void 0 : _a.getWalletPassEvaluator) == null ? void 0 : _b.call(_a);
266
+ if (!(evaluator == null ? void 0 : evaluator.searchVoucherFormat))
267
+ return assetsWithRecalculationBase;
268
+ const hasCalculationProducts = this.walletAssetsCalculationContext.products.length > 0;
269
+ const products = hasCalculationProducts ? this.walletAssetsCalculationContext.products : ((_c = this.walletParams) == null ? void 0 : _c.products) || [];
270
+ const orderTotalAmount = Number(
271
+ hasCalculationProducts ? this.walletAssetsCalculationContext.orderTotalAmount : ((_d = this.walletParams) == null ? void 0 : _d.order_wait_pay_amount) || 0
272
+ );
273
+ try {
274
+ const formattedAssets = evaluator.searchVoucherFormat({
275
+ orderTotalAmount,
276
+ products,
277
+ vouchers: assetsWithRecalculationBase
278
+ });
279
+ return formattedAssets;
280
+ } catch (error) {
281
+ this.paymentModule.logWarning(
282
+ "[WalletPass] 搜索资产策略格式化失败,保留接口结果",
283
+ {
284
+ error: error instanceof Error ? error.message : String(error)
285
+ }
286
+ );
287
+ return assetsWithRecalculationBase;
288
+ }
289
+ }
225
290
  /** 获取当前缓存中所有支付类 Wallet 资产的扫码 code。 */
226
291
  getSearchedWalletAssetCodes() {
227
292
  const cachedAssets = this.searchResults.filter(isWalletPaymentAsset);
@@ -245,11 +310,23 @@ var WalletPassPaymentImpl = class {
245
310
  );
246
311
  if (searchedCodeKeys.size === 0)
247
312
  return cachedAssets;
313
+ const cachedAssetByCode = /* @__PURE__ */ new Map();
314
+ cachedAssets.forEach((item) => {
315
+ getWalletAssetCodeKeys(item).forEach((code) => {
316
+ cachedAssetByCode.set(code, item);
317
+ });
318
+ });
248
319
  const refreshedAssets = refreshedCandidates.filter(
249
320
  (item) => isWalletPaymentAsset(item) && searchedCodeKeys.has(
250
321
  String((item == null ? void 0 : item.code) || "").trim().toUpperCase()
251
322
  )
252
- );
323
+ ).map((item) => {
324
+ const cachedAsset = getWalletAssetCodeKeys(item).map((code) => cachedAssetByCode.get(code)).find(Boolean);
325
+ return withWalletAssetRecalculationBaseAmount({
326
+ ...cachedAsset,
327
+ ...item
328
+ });
329
+ });
253
330
  if (refreshedAssets.length > 0) {
254
331
  const replacedCodes = new Set(
255
332
  refreshedAssets.map(
@@ -321,6 +398,10 @@ var WalletPassPaymentImpl = class {
321
398
  payment_order_id
322
399
  };
323
400
  this.walletParams = walletParams;
401
+ this.walletAssetsCalculationContext = {
402
+ orderTotalAmount: Number(walletParams.order_wait_pay_amount || 0),
403
+ products: Array.isArray(walletParams.products) ? walletParams.products : []
404
+ };
324
405
  this.paymentModule.logInfo("[WalletPass] 钱包默认参数已生成并存储", {
325
406
  customer_id: walletParams.customer_id,
326
407
  holder_id: walletParams.holder_id,
@@ -472,7 +553,6 @@ var WalletPassPaymentImpl = class {
472
553
  vouchers: allList
473
554
  });
474
555
  const { recommended, transformList, noApplicableVoucher, recommendedAmount } = res;
475
- console.log(res, "resres12");
476
556
  this.walletRecommendList = recommended;
477
557
  this.walletInitData = {
478
558
  transformList,
@@ -535,8 +615,12 @@ var WalletPassPaymentImpl = class {
535
615
  const customerWalletPassList = Array.isArray(
536
616
  responseData == null ? void 0 : responseData.customer_wallet_pass_list
537
617
  ) ? responseData.customer_wallet_pass_list : [];
538
- const allList = mergeWalletAssets(
618
+ const orderedRecalculateList = orderWalletAssetsByIds(
539
619
  recalculateList,
620
+ selectedIds
621
+ );
622
+ const allList = mergeWalletAssets(
623
+ orderedRecalculateList,
540
624
  customerWalletPassList
541
625
  );
542
626
  const walletPassEvaluator = (_b = (_a = this.paymentModule.window) == null ? void 0 : _a.getWalletPassEvaluator) == null ? void 0 : _b.call(_a);
@@ -552,7 +636,7 @@ var WalletPassPaymentImpl = class {
552
636
  )
553
637
  };
554
638
  const returnedSelectedKeys = new Set(
555
- recalculateList.filter(isWalletAssetAvailable).map((item) => getWalletAssetKey(item))
639
+ allList.map((item) => getWalletAssetKey(item))
556
640
  );
557
641
  const authoritativeSelectedIds = selectedIds.filter(
558
642
  (id) => returnedSelectedKeys.has(String(id))
@@ -830,6 +914,17 @@ var WalletPassPaymentImpl = class {
830
914
  const assetId = getWalletAssetId(asset);
831
915
  return assetId !== void 0 && assetId !== null && selectionSources[String(assetId)] === "scan";
832
916
  }) : [];
917
+ const manualEditAmountByKey = /* @__PURE__ */ new Map();
918
+ if (preserveSelection) {
919
+ this.walletAssetsState.selectedItems.forEach((asset) => {
920
+ if (typeof (asset == null ? void 0 : asset.edit_current_amount) !== "number")
921
+ return;
922
+ manualEditAmountByKey.set(
923
+ getWalletAssetKey(asset),
924
+ asset.edit_current_amount
925
+ );
926
+ });
927
+ }
833
928
  if (!((_a = businessData.products) == null ? void 0 : _a.length)) {
834
929
  this.queuePendingAutoSelectForAssets(scanSelectedAssets);
835
930
  this.generateWalletParams(businessData);
@@ -928,12 +1023,16 @@ var WalletPassPaymentImpl = class {
928
1023
  searchedWalletAssets,
929
1024
  result.transformList || [],
930
1025
  result.noApplicableVoucher || []
931
- ).map((asset) => ({
932
- ...asset,
933
- _wallet_asset_recommended: recommendedKeys.has(
934
- getWalletAssetKey(asset)
935
- )
936
- }));
1026
+ ).map((asset) => {
1027
+ const assetKey = getWalletAssetKey(asset);
1028
+ return {
1029
+ ...asset,
1030
+ ...manualEditAmountByKey.has(assetKey) ? {
1031
+ edit_current_amount: manualEditAmountByKey.get(assetKey)
1032
+ } : {},
1033
+ _wallet_asset_recommended: recommendedKeys.has(assetKey)
1034
+ };
1035
+ });
937
1036
  const latestSelectedIds = preserveSelection ? authoritativeSelectedIds : [];
938
1037
  const latestSelectionSources = preserveSelection ? { ...this.walletAssetsState.selectionSources } : {};
939
1038
  const pendingAutoSelectAssets = this.walletAssetsBehavior.autoSelectAfterSearch ? items.filter(
@@ -1047,6 +1146,13 @@ var WalletPassPaymentImpl = class {
1047
1146
  selectionSources: snapshot.state.selectionSources || {}
1048
1147
  });
1049
1148
  }
1149
+ updateWalletAssetEditAmountInCaches(assetKey, amount) {
1150
+ const updateAsset = (asset) => getWalletAssetKey(asset) === assetKey ? { ...asset, edit_current_amount: amount } : asset;
1151
+ this.searchResults = this.searchResults.map(
1152
+ updateAsset
1153
+ );
1154
+ return this.walletAssetsState.items.map(updateAsset);
1155
+ }
1050
1156
  selectWalletAsset(assetId, options) {
1051
1157
  const assetKey = String(assetId);
1052
1158
  const asset = this.walletAssetsState.items.find(
@@ -1062,6 +1168,7 @@ var WalletPassPaymentImpl = class {
1062
1168
  );
1063
1169
  const selectionSources = { ...this.walletAssetsState.selectionSources };
1064
1170
  delete selectionSources[assetKey];
1171
+ let nextItems = this.walletAssetsState.items;
1065
1172
  if (options.selected) {
1066
1173
  if (!asset || !isWalletAssetAvailable(asset)) {
1067
1174
  return this.getWalletAssetsState();
@@ -1070,9 +1177,13 @@ var WalletPassPaymentImpl = class {
1070
1177
  selectionSources[assetKey] = options.source || "manual";
1071
1178
  } else if (asset) {
1072
1179
  this.clearPendingAutoSelectForAsset(asset);
1180
+ nextItems = this.updateWalletAssetEditAmountInCaches(
1181
+ assetKey,
1182
+ void 0
1183
+ );
1073
1184
  }
1074
1185
  const nextState = this.recalculateWalletAssets(
1075
- this.walletAssetsState.items,
1186
+ nextItems,
1076
1187
  selectedIds,
1077
1188
  selectionSources
1078
1189
  );
@@ -1081,6 +1192,54 @@ var WalletPassPaymentImpl = class {
1081
1192
  }
1082
1193
  return nextState;
1083
1194
  }
1195
+ /**
1196
+ * 更新已选资产的手动金额。只允许在当前实际抵扣额内向下调整,
1197
+ * 金额为 0 时沿用 legacy 行为取消选择。
1198
+ */
1199
+ updateWalletAssetAmount(assetId, amount) {
1200
+ const assetKey = String(assetId);
1201
+ const committedKeys = new Set(
1202
+ this.walletAssetsState.committedIds.map((id) => String(id))
1203
+ );
1204
+ const selectedKeys = new Set(
1205
+ this.walletAssetsState.selectedIds.map((id) => String(id))
1206
+ );
1207
+ const asset = this.walletAssetsState.items.find(
1208
+ (item) => getWalletAssetKey(item) === assetKey
1209
+ );
1210
+ const requestedAmount = Number(amount);
1211
+ if (!asset || committedKeys.has(assetKey) || !selectedKeys.has(assetKey) || !Number.isFinite(requestedAmount) || requestedAmount < 0) {
1212
+ return this.getWalletAssetsState();
1213
+ }
1214
+ const selectedIds = [...this.walletAssetsState.selectedIds];
1215
+ const selectionSources = { ...this.walletAssetsState.selectionSources };
1216
+ if (requestedAmount === 0) {
1217
+ const nextItems2 = this.updateWalletAssetEditAmountInCaches(
1218
+ assetKey,
1219
+ void 0
1220
+ );
1221
+ delete selectionSources[assetKey];
1222
+ this.clearPendingAutoSelectForAsset(asset);
1223
+ return this.recalculateWalletAssets(
1224
+ nextItems2,
1225
+ selectedIds.filter((id) => String(id) !== assetKey),
1226
+ selectionSources
1227
+ );
1228
+ }
1229
+ const currentDeductionAmount = getWalletAssetDeductionAmount(asset);
1230
+ const editAmount = Math.min(requestedAmount, currentDeductionAmount);
1231
+ if (editAmount <= 0)
1232
+ return this.getWalletAssetsState();
1233
+ const nextItems = this.updateWalletAssetEditAmountInCaches(
1234
+ assetKey,
1235
+ editAmount
1236
+ );
1237
+ return this.recalculateWalletAssets(
1238
+ nextItems,
1239
+ selectedIds,
1240
+ selectionSources
1241
+ );
1242
+ }
1084
1243
  async scanWalletAssetAsync(code) {
1085
1244
  const normalizedCode = String(code || "").trim();
1086
1245
  if (!normalizedCode) {
@@ -1109,7 +1268,15 @@ var WalletPassPaymentImpl = class {
1109
1268
  this.walletAssetsState.selectedItems
1110
1269
  )
1111
1270
  });
1112
- const paymentAssets = result.data.filter(isWalletPaymentAsset);
1271
+ const paymentAssets = this.formatSearchedWalletPaymentAssets(
1272
+ result.data.filter(isWalletPaymentAsset)
1273
+ );
1274
+ if (paymentAssets.length > 0) {
1275
+ this.searchResults = mergeWalletAssets(
1276
+ this.searchResults,
1277
+ paymentAssets
1278
+ );
1279
+ }
1113
1280
  const asset = paymentAssets.find(
1114
1281
  (item) => String((item == null ? void 0 : item.code) || "") === normalizedCode
1115
1282
  ) || paymentAssets[0];
@@ -1128,30 +1295,44 @@ var WalletPassPaymentImpl = class {
1128
1295
  this.walletAssetsState.selectionSources
1129
1296
  );
1130
1297
  const assetId = getWalletAssetId(asset);
1131
- const selected = this.walletAssetsBehavior.autoSelectAfterSearch && assetId !== void 0 && isWalletAssetAvailable(asset) ? this.selectWalletAsset(assetId, {
1298
+ const recalculatedAsset = assetId === void 0 ? void 0 : recalculatedState.items.find(
1299
+ (item) => getWalletAssetKey(item) === String(assetId)
1300
+ );
1301
+ const selected = this.walletAssetsBehavior.autoSelectAfterSearch && assetId !== void 0 && isWalletAssetAvailable(recalculatedAsset) ? this.selectWalletAsset(assetId, {
1132
1302
  selected: true,
1133
1303
  source: "scan"
1134
1304
  }) : recalculatedState;
1135
1305
  const isSelected = assetId !== void 0 && selected.selectedIds.some((id) => String(id) === String(assetId));
1306
+ const selectedStateAsset = assetId === void 0 ? void 0 : selected.items.find(
1307
+ (item) => getWalletAssetKey(item) === String(assetId)
1308
+ );
1309
+ const resolvedAsset = selectedStateAsset || recalculatedAsset || asset;
1136
1310
  if (isSelected) {
1137
- this.clearPendingAutoSelectForAsset(asset);
1311
+ this.clearPendingAutoSelectForAsset(resolvedAsset);
1138
1312
  } else if (this.walletAssetsBehavior.autoSelectAfterSearch) {
1139
1313
  this.pendingAutoSelectSearchCodes.add(
1140
1314
  normalizeWalletAssetCode(normalizedCode)
1141
1315
  );
1142
- this.queuePendingAutoSelectForAssets([asset]);
1316
+ this.queuePendingAutoSelectForAssets([resolvedAsset]);
1143
1317
  }
1144
1318
  return {
1145
1319
  type: result.type,
1146
- asset,
1320
+ asset: resolvedAsset,
1147
1321
  selected: isSelected,
1148
1322
  state: selected
1149
1323
  };
1150
1324
  }
1151
1325
  clearWalletAssetSelection() {
1152
1326
  this.pendingAutoSelectSearchCodes.clear();
1327
+ const selectedKeys = new Set(
1328
+ this.walletAssetsState.selectedIds.map((id) => String(id))
1329
+ );
1330
+ const clearSelectedEditAmount = (asset) => selectedKeys.has(getWalletAssetKey(asset)) ? { ...asset, edit_current_amount: void 0 } : asset;
1331
+ this.searchResults = this.searchResults.map(
1332
+ clearSelectedEditAmount
1333
+ );
1153
1334
  return this.recalculateWalletAssets(
1154
- this.walletAssetsState.items,
1335
+ this.walletAssetsState.items.map(clearSelectedEditAmount),
1155
1336
  [],
1156
1337
  {}
1157
1338
  );
@@ -23,12 +23,25 @@ export interface ProductDataVariant {
23
23
  module_data_id: number;
24
24
  /** 关联 data_variant_rule.id */
25
25
  data_variant_rule_id: number;
26
+ /** 多货币结算等业务类型 */
27
+ business_type?: 'multi_currency_settlement' | string;
28
+ /** 数据状态;多货币结算仅处理 enable 记录 */
29
+ status?: 'enable' | 'disable' | string;
26
30
  /** 命中策略后覆盖到商品上的字段 */
27
31
  data: Record<string, any>;
28
32
  created_at: string | null;
29
33
  updated_at: string | null;
30
34
  deleted_at: string | null;
31
35
  }
36
+ export interface ProductSettlementOption extends Record<string, any> {
37
+ settlement_type: 'legal_currency' | 'virtual_currency' | string;
38
+ currency_product_id?: number;
39
+ price: string;
40
+ data_variant_id?: number;
41
+ data_variant_rule_id: number | string;
42
+ available: boolean;
43
+ disabled_reason?: string;
44
+ }
32
45
  /**
33
46
  * 商品基本信息接口
34
47
  */
@@ -61,6 +74,8 @@ export interface ProductData {
61
74
  sum_stock: number;
62
75
  /** 商品价格 */
63
76
  price: string;
77
+ /** 当前上下文解析出的可选结算货币 */
78
+ available_settlement_options?: ProductSettlementOption[];
64
79
  /** 商品原价 */
65
80
  original_price: string;
66
81
  /** 商品成本价 */
@@ -237,6 +252,14 @@ export interface ProductData {
237
252
  option_group_count: number;
238
253
  /** 智能定价变体列表,/product/query with dataVariants 返回 */
239
254
  data_variants?: ProductDataVariant[];
255
+ /**
256
+ * 商品接口规则计算结果;data_variant_ids 按优先级从高到低排列。
257
+ * 空数组表示规则已经计算完成但没有命中 Data Variant。
258
+ */
259
+ metadata?: {
260
+ data_variant_ids?: Array<number | string>;
261
+ [key: string]: any;
262
+ };
240
263
  /** 服务时长 */
241
264
  service_times?: any;
242
265
  }
@@ -18,6 +18,19 @@ export declare class ProductList extends BaseModule implements Module {
18
18
  * productList.updateOtherParams({ channel: 'pos' });
19
19
  */
20
20
  updateOtherParams(params: Record<string, any>): void;
21
+ /**
22
+ * 判断当前商品查询是否会被本地 OS Server 接管。
23
+ *
24
+ * 不能只看请求参数中的 osServer=true:H5 RequestPlugin 会透传该参数,但仍然直接请求远端接口。
25
+ * 这里按 RequestPlugin 实际使用的 baseUrl 检查已注册路由,避免 OS Server 与客户端重复执行
26
+ * Data Variant 评估。
27
+ */
28
+ private isProductQueryHandledByOsServer;
29
+ /**
30
+ * 未启用商品 OS 路由时,在客户端补做 Data Variant 解析。
31
+ * OS Server 已接管时直接信任服务端结果,确保同一批商品只评估一次。
32
+ */
33
+ private resolveClientDataVariants;
21
34
  storeChange(path?: string, value?: any): Promise<void>;
22
35
  /**
23
36
  * 获取加时商品列表。
@@ -25,11 +38,11 @@ export declare class ProductList extends BaseModule implements Module {
25
38
  * @example
26
39
  * const products = await productList.getAddTimeProducts({ schedule_date: '2026-06-16' });
27
40
  */
28
- getAddTimeProducts(params?: ProductListLoadProductsParams): Promise<any>;
41
+ getAddTimeProducts(params?: ProductListLoadProductsParams): Promise<ProductData[]>;
29
42
  loadProducts({ category_ids, product_ids, collection, menu_list_ids, customer_id: paramsCustomerId, with_count, schedule_datetime, schedule_date, cacheId, with_schedule, extension_type, status, strategy_context, }?: ProductListLoadProductsParams, options?: {
30
43
  callback?: (result: any) => void;
31
44
  subscriberId?: string;
32
- }): Promise<any>;
45
+ }): Promise<ProductData[]>;
33
46
  loadProductsPrice({ ids, customer_id, schedule_date, channel, }: {
34
47
  ids?: number[];
35
48
  customer_id?: number;
@@ -67,6 +67,62 @@ var ProductList = class extends import_BaseModule.BaseModule {
67
67
  updateOtherParams(params) {
68
68
  this.otherParams = params || {};
69
69
  }
70
+ /**
71
+ * 判断当前商品查询是否会被本地 OS Server 接管。
72
+ *
73
+ * 不能只看请求参数中的 osServer=true:H5 RequestPlugin 会透传该参数,但仍然直接请求远端接口。
74
+ * 这里按 RequestPlugin 实际使用的 baseUrl 检查已注册路由,避免 OS Server 与客户端重复执行
75
+ * Data Variant 评估。
76
+ */
77
+ isProductQueryHandledByOsServer() {
78
+ var _a, _b;
79
+ const server = (_a = this.core) == null ? void 0 : _a.server;
80
+ if (!server || typeof server.hasRoute !== "function")
81
+ return false;
82
+ const requestBaseUrl = String(
83
+ ((_b = this.request) == null ? void 0 : _b.baseUrl) || "/shop"
84
+ ).replace(/\/+$/, "");
85
+ const routePath = `${requestBaseUrl}/product/query`;
86
+ return server.hasRoute("post", routePath) === true;
87
+ }
88
+ /**
89
+ * 未启用商品 OS 路由时,在客户端补做 Data Variant 解析。
90
+ * OS Server 已接管时直接信任服务端结果,确保同一批商品只评估一次。
91
+ */
92
+ resolveClientDataVariants(products, queryPayload, handledByOsServer) {
93
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
94
+ if (handledByOsServer || products.length === 0)
95
+ return products;
96
+ const evaluator = (_b = (_a = this.core) == null ? void 0 : _a.context) == null ? void 0 : _b.dataVariantEvaluator;
97
+ if (!evaluator || typeof evaluator.resolveProducts !== "function") {
98
+ return products;
99
+ }
100
+ const strategyContext = queryPayload.strategy_context && typeof queryPayload.strategy_context === "object" ? queryPayload.strategy_context : {};
101
+ const fatherModule = String(((_c = this.otherParams) == null ? void 0 : _c.fatherModule) || "").trim();
102
+ const scheduleModule = (fatherModule ? this.core.getModule(`${fatherModule}_schedule`) : null) || this.core.getModule("schedule");
103
+ const scheduleList = (_d = scheduleModule == null ? void 0 : scheduleModule.getScheduleList) == null ? void 0 : _d.call(scheduleModule);
104
+ const contextScheduleList = (_f = (_e = this.core) == null ? void 0 : _e.context) == null ? void 0 : _f.scheduleList;
105
+ const contextMenuList = (_h = (_g = this.core) == null ? void 0 : _g.context) == null ? void 0 : _h.menuList;
106
+ const businessData = {
107
+ products,
108
+ scheduleDateTime: queryPayload.schedule_datetime || queryPayload.schedule_date,
109
+ scheduleList: Array.isArray(scheduleList) ? scheduleList : Array.isArray(contextScheduleList) ? contextScheduleList : [],
110
+ menuList: Array.isArray((_i = this.otherParams) == null ? void 0 : _i.menuList) ? this.otherParams.menuList : Array.isArray(contextMenuList) ? contextMenuList : [],
111
+ customerId: queryPayload.customer_id,
112
+ channel: strategyContext.channel,
113
+ orderType: strategyContext.orderType || strategyContext.order_type,
114
+ businessCode: strategyContext.business_code ?? strategyContext.businessCode,
115
+ availableWalletIds: strategyContext.available_wallet_ids,
116
+ custom: strategyContext
117
+ };
118
+ try {
119
+ const result = evaluator.resolveProducts(businessData);
120
+ return Array.isArray(result == null ? void 0 : result.products) ? result.products : products;
121
+ } catch (error) {
122
+ console.error("[ProductList] Data Variant 客户端解析失败", error);
123
+ return products;
124
+ }
125
+ }
70
126
  async storeChange(path, value) {
71
127
  }
72
128
  /**
@@ -151,15 +207,41 @@ var ProductList = class extends import_BaseModule.BaseModule {
151
207
  is_eject: 1,
152
208
  with_schedule,
153
209
  schedule_datetime,
154
- extension_type
210
+ extension_type,
211
+ strategy_context
155
212
  };
213
+ const handledByOsServer = this.isProductQueryHandledByOsServer();
214
+ const callback = !handledByOsServer && typeof (options == null ? void 0 : options.callback) === "function" ? (result) => {
215
+ var _a2, _b2, _c;
216
+ const callbackList = (_a2 = result == null ? void 0 : result.data) == null ? void 0 : _a2.list;
217
+ if (!Array.isArray(callbackList)) {
218
+ (_b2 = options.callback) == null ? void 0 : _b2.call(options, result);
219
+ return;
220
+ }
221
+ (_c = options.callback) == null ? void 0 : _c.call(options, {
222
+ ...result,
223
+ data: {
224
+ ...result.data,
225
+ list: this.resolveClientDataVariants(
226
+ callbackList,
227
+ queryPayload,
228
+ false
229
+ )
230
+ }
231
+ });
232
+ } : options == null ? void 0 : options.callback;
156
233
  const productsData = await this.request.post(
157
234
  `/product/query`,
158
235
  queryPayload,
159
- { osServer: true, callback: options == null ? void 0 : options.callback, subscriberId: options == null ? void 0 : options.subscriberId, customToast: () => {
236
+ { osServer: true, callback, subscriberId: options == null ? void 0 : options.subscriberId, customToast: () => {
160
237
  } }
161
238
  );
162
- const sortedList = (productsData.data.list || []).slice().sort((a, b) => Number(b.sort) - Number(a.sort));
239
+ const resolvedList = this.resolveClientDataVariants(
240
+ productsData.data.list || [],
241
+ queryPayload,
242
+ handledByOsServer
243
+ );
244
+ const sortedList = resolvedList.slice().sort((a, b) => Number(b.sort) - Number(a.sort));
163
245
  this.addProduct(sortedList);
164
246
  return sortedList;
165
247
  }
@@ -30,6 +30,7 @@ export declare class SalesSummaryModule extends BaseModule implements Module, Sa
30
30
  taxRate?: number;
31
31
  shopDiscount?: string | number;
32
32
  channel?: string;
33
+ virtualSettlementMode?: boolean;
33
34
  }): Promise<{
34
35
  tax_title: string;
35
36
  tax_rate: number | undefined;
@@ -126,7 +126,14 @@ var SalesSummaryModule = class extends import_BaseModule.BaseModule {
126
126
  }
127
127
  async getSummary(params) {
128
128
  var _a, _b;
129
- const { products = [], isPriceIncludeTax, taxRate, shopDiscount, channel } = params;
129
+ const {
130
+ products = [],
131
+ isPriceIncludeTax,
132
+ taxRate,
133
+ shopDiscount,
134
+ channel,
135
+ virtualSettlementMode = false
136
+ } = params;
130
137
  const summaryChannel = channel || this.resolveChannel();
131
138
  if (summaryChannel !== this.surchargeListChannel) {
132
139
  await this.getSurchargeList(summaryChannel);
@@ -150,7 +157,8 @@ var SalesSummaryModule = class extends import_BaseModule.BaseModule {
150
157
  surchargeList: this.store.surchargeList,
151
158
  scheduleById,
152
159
  isInScheduleByDate: import_Schedule.ScheduleModule.isInScheduleByDate,
153
- shopDiscount
160
+ shopDiscount,
161
+ virtualSettlementMode
154
162
  });
155
163
  const summaryWithTaxMeta = {
156
164
  ...summary,
@@ -64,5 +64,6 @@ export interface SalesSummaryModuleAPI {
64
64
  taxRate?: number;
65
65
  shopDiscount?: string | number;
66
66
  channel?: string;
67
+ virtualSettlementMode?: boolean;
67
68
  }): Promise<SalesSummaryData>;
68
69
  }
@@ -8,4 +8,5 @@ export declare function calculateSalesSummary(params: {
8
8
  scheduleById?: Record<string, any>;
9
9
  isInScheduleByDate?: any;
10
10
  shopDiscount?: string | number;
11
+ virtualSettlementMode?: boolean;
11
12
  }): SalesSummaryData;
@@ -890,13 +890,37 @@ function calculateSalesSummary(params) {
890
890
  surchargeList,
891
891
  scheduleById = {},
892
892
  isInScheduleByDate,
893
- shopDiscount
893
+ shopDiscount,
894
+ virtualSettlementMode = false
894
895
  } = params;
895
896
  if (!(products == null ? void 0 : products.length))
896
897
  return createEmptySalesSummary();
897
898
  const summaryProducts = products.filter((product) => !isGeneratedVoucherProduct(product));
898
899
  if (!summaryProducts.length)
899
900
  return createEmptySalesSummary();
901
+ if (virtualSettlementMode) {
902
+ const productQuantity2 = summaryProducts.reduce(
903
+ (sum, item) => sum + getSafeNum(item.num),
904
+ 0
905
+ );
906
+ const productAmount2 = summaryProducts.reduce(
907
+ (sum, item) => sum.plus(
908
+ toDecimal(item.selling_price).times(getSafeNum(item.num))
909
+ ),
910
+ new import_decimal.default(0)
911
+ );
912
+ const amount = toFixed2(productAmount2);
913
+ return {
914
+ ...createEmptySalesSummary(),
915
+ product_quantity: productQuantity2,
916
+ product_original_amount: amount,
917
+ product_amount: amount,
918
+ product_expect_amount: amount,
919
+ expect_amount: amount,
920
+ total_amount: amount,
921
+ is_price_include_tax: isPriceIncludeTax
922
+ };
923
+ }
900
924
  const productQuantity = summaryProducts.reduce(
901
925
  (sum, item) => sum + getSafeNum(item.num),
902
926
  0
@@ -263,6 +263,11 @@ declare class Server {
263
263
  * 对单个商品 query 订阅者重算并推送(定时路径;不新建 schedule timer)。
264
264
  */
265
265
  private recomputeAndNotifySubscriber;
266
+ /**
267
+ * 根据餐牌 ID 获取其绑定日程中的全部时间点。
268
+ * 商品查询定时器与 schedule-time-points 路由共用此逻辑。
269
+ */
270
+ private getMenuScheduleTimePoints;
266
271
  /**
267
272
  * query 完成后为 subscriber 同步 schedule 定时器(先 clear 再 schedule)。
268
273
  */
@@ -518,6 +523,10 @@ declare class Server {
518
523
  private handleOrderDraftSubmit;
519
524
  private handlePendingSyncCheckoutOrder;
520
525
  private buildPendingSyncCheckoutOrder;
526
+ /**
527
+ * Android 结账自动小票由当前设备设置控制;其它平台保持原有行为。
528
+ */
529
+ private shouldAutoPrintCheckoutReceipt;
521
530
  private shouldBuildSmallTicketData;
522
531
  private shouldPrintSyncedOrder;
523
532
  private hasPaymentStatus;