@pisell/pisellos 2.2.276 → 2.2.278

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.
@@ -311,7 +311,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
311
311
  date: string;
312
312
  status: string;
313
313
  week: string;
314
- weekNum: 0 | 1 | 2 | 3 | 6 | 4 | 5;
314
+ weekNum: 0 | 1 | 2 | 3 | 4 | 5 | 6;
315
315
  }[]>;
316
316
  submitTimeSlot(timeSlots: TimeSliceItem): void;
317
317
  private getScheduleDataByIds;
@@ -326,7 +326,7 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
326
326
  * 获取当前的客户搜索条件
327
327
  * @returns 当前搜索条件
328
328
  */
329
- getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "num" | "skip">;
329
+ getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
330
330
  /**
331
331
  * 获取客户列表状态(包含滚动加载相关状态)
332
332
  * @returns 客户状态
@@ -445,19 +445,7 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
445
445
  * 加车两阶段主决策(规格弹窗 / 资源编辑 / 直接加车)。
446
446
  * 与 ticketBooking `handleSelectProduct` + `handleBooking4Service` 等价。
447
447
  */
448
- decideAddProduct(item: any, options?: Partial<AddProductDecideContext>): {
449
- action: "reloadCatalog";
450
- } | {
451
- action: "add";
452
- cacheItem: any;
453
- } | {
454
- action: "requiresDetail";
455
- payload: AddProductRequiresDetailPayload;
456
- } | {
457
- action: "requiresBookingEdit";
458
- cacheItem: any;
459
- payload: import("./utils/addProductDecision").AddProductRequiresBookingEditPayload;
460
- };
448
+ decideAddProduct(item: any, options?: Partial<AddProductDecideContext>): import("./utils/addProductDecision").AddProductDecision;
461
449
  /** 规格弹窗 callback 后的第二段决策。 */
462
450
  decideAfterDetail(cacheItem: any, options?: Partial<AddProductDecideContext>): import("./utils/addProductDecision").AddProductDecision;
463
451
  /**
package/lib/core/index.js CHANGED
@@ -50,7 +50,6 @@ var PisellOSCore = class {
50
50
  this.serverOptions = options.server;
51
51
  }
52
52
  this.initialize(options);
53
- console.log("constructor1234");
54
53
  }
55
54
  initialize(options) {
56
55
  if (options == null ? void 0 : options.plugins) {
@@ -214,7 +214,6 @@ var WalletPassEvaluator = class {
214
214
  }
215
215
  const rawText = this.getText(reasonCode);
216
216
  const currency = failedField && AMOUNT_FIELDS.has(failedField) ? ((_g = (_f = this.otherParams) == null ? void 0 : _f.getData) == null ? void 0 : _g.call(_f, "shop_symbol")) || "" : "";
217
- console.log(currency, "currency12345");
218
217
  const reason = thresholdValue !== void 0 ? formatReason(rawText, thresholdValue, currency) : rawText;
219
218
  results.push({
220
219
  voucher,
@@ -416,7 +415,6 @@ var WalletPassEvaluator = class {
416
415
  * 计算适用商品的总金额和数量
417
416
  */
418
417
  calculateApplicableProducts(products, applicableProductIds, deductTaxAndFee) {
419
- console.log(products, "products1234");
420
418
  let total = 0;
421
419
  let count = 0;
422
420
  if (applicableProductIds !== null && applicableProductIds.length === 0) {
@@ -26,6 +26,11 @@ export interface Voucher {
26
26
  recommended_usage_amount: number;
27
27
  /** 后端计算推荐使用金额(仅商品) 不包含税费附加费 */
28
28
  recommended_pure_product_usage_amount: number;
29
+ /**
30
+ * 扫码资产自身可供前端重新计算的金额基准。
31
+ * 后端 recommended_usage_amount 会包含当前已选券上下文,取消勾选后不能继续把它当作资产永久上限。
32
+ */
33
+ _wallet_asset_recalculation_base_amount?: number;
29
34
  /** 实际抵扣金额 */
30
35
  actualDeduction: number;
31
36
  /** 抵扣详情 */
@@ -43,6 +43,21 @@ __export(utils_exports, {
43
43
  });
44
44
  module.exports = __toCommonJS(utils_exports);
45
45
  var import_decimal = __toESM(require("decimal.js"));
46
+ var DEFAULT_WALLET_PASS_CALCULATION_CONFIG = {
47
+ maxDeductionAmount: 99999,
48
+ maxUsagePerOrder: 0,
49
+ allowCrossProduct: true,
50
+ applicableProductLimit: 0,
51
+ deductTaxAndFee: true,
52
+ maxPassesPerItem: 0,
53
+ deductOptionPrice: false
54
+ };
55
+ function getWalletPassCalculationConfig(voucher) {
56
+ return {
57
+ ...DEFAULT_WALLET_PASS_CALCULATION_CONFIG,
58
+ ...(voucher == null ? void 0 : voucher.config) || {}
59
+ };
60
+ }
46
61
  var getProductQuantity = (product) => {
47
62
  return product.quantity || product.product_quantity || 1;
48
63
  };
@@ -105,9 +120,15 @@ function applyMaxPassUsageIncrements(usageMap, walletPassProductId, maxPassesPer
105
120
  });
106
121
  }
107
122
  var getRecommendedAmount = (voucher) => {
108
- console.log("voucher312", voucher);
109
123
  const { config, recommended_usage_amount, recommended_pure_product_usage_amount } = voucher;
110
124
  const deductTaxAndFee = (config == null ? void 0 : config.deductTaxAndFee) ?? true;
125
+ const rawRecalculationBaseAmount = voucher._wallet_asset_recalculation_base_amount;
126
+ if (rawRecalculationBaseAmount !== void 0 && rawRecalculationBaseAmount !== null) {
127
+ const recalculationBaseAmount = Number(rawRecalculationBaseAmount);
128
+ if (Number.isFinite(recalculationBaseAmount) && recalculationBaseAmount >= 0) {
129
+ return recalculationBaseAmount;
130
+ }
131
+ }
111
132
  return deductTaxAndFee ? recommended_usage_amount : recommended_pure_product_usage_amount ?? recommended_usage_amount;
112
133
  };
113
134
  var getApplicableProductIds = (voucher) => {
@@ -145,7 +166,6 @@ var getApplicableProducts = (voucher, productsData) => {
145
166
  return productsData.filter((p) => applicableProductIds.includes(p.product_id));
146
167
  };
147
168
  function processVouchers(applicableVouchers, orderTotalAmount, products) {
148
- console.log(products, "products123");
149
169
  const productsCopy = expandProductsWithBundleItems(products, true);
150
170
  let remainingOrderAmount = new import_decimal.default(orderTotalAmount);
151
171
  const getItemPassUsage = (usageMap, walletPassProductId, lineKey) => {
@@ -218,7 +238,7 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
218
238
  return import_decimal.default.min(baseAmount, finalApplicableAmount, remainingOrderAmount);
219
239
  };
220
240
  const isVoucherAvailable = (voucher, productsData, usedVoucherCounts2, itemPassUsage) => {
221
- const { config, id, product_id } = voucher;
241
+ const { config, product_id } = voucher;
222
242
  const recommendedAmount = getRecommendedAmount(voucher);
223
243
  if (recommendedAmount <= 0) {
224
244
  return { isAvailable: false, reasonCode: "not_meet_the_required_conditions" };
@@ -453,6 +473,7 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
453
473
  const productsForCalc = expandProductsWithBundleItems(products, true);
454
474
  let remainingOrderAmount = new import_decimal.default(orderTotalAmount);
455
475
  const selectedWithDetails = [];
476
+ const usedVoucherCounts = /* @__PURE__ */ new Map();
456
477
  const itemPassUsageMap = /* @__PURE__ */ new Map();
457
478
  const getItemPassUsageRecalc = (walletPassProductId, lineKey) => {
458
479
  var _a;
@@ -469,8 +490,29 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
469
490
  });
470
491
  };
471
492
  selectedVouchers.forEach((selectedVoucher) => {
472
- const { config, id } = selectedVoucher;
473
- const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true, maxPassesPerItem = 0 } = config;
493
+ const { product_id } = selectedVoucher;
494
+ const config = getWalletPassCalculationConfig(selectedVoucher);
495
+ const {
496
+ maxDeductionAmount,
497
+ maxUsagePerOrder,
498
+ allowCrossProduct,
499
+ applicableProductLimit,
500
+ deductTaxAndFee,
501
+ maxPassesPerItem
502
+ } = config;
503
+ const usedCount = usedVoucherCounts.get(product_id) || 0;
504
+ if (maxUsagePerOrder > 0 && usedCount >= maxUsagePerOrder) {
505
+ selectedWithDetails.push({
506
+ ...selectedVoucher,
507
+ config,
508
+ actualDeduction: 0,
509
+ deductionDetails: [],
510
+ _available_max_amount: 0,
511
+ _unified_available_status: 0,
512
+ reasonCode: "usage_limit_reached"
513
+ });
514
+ return;
515
+ }
474
516
  const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
475
517
  const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
476
518
  let applicableProducts = getApplicableProducts(
@@ -481,6 +523,7 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
481
523
  if (applicableProducts.length === 0) {
482
524
  selectedWithDetails.push({
483
525
  ...selectedVoucher,
526
+ config,
484
527
  actualDeduction: 0,
485
528
  deductionDetails: [],
486
529
  _available_max_amount: 0,
@@ -558,6 +601,7 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
558
601
  applyMaxPassUsageIncrements(itemPassUsageMap, selectedVoucher.product_id, maxPassesPerItem, deductionDetails);
559
602
  selectedWithDetails.push({
560
603
  ...selectedVoucher,
604
+ config,
561
605
  actualDeduction: totalDeducted.toNumber(),
562
606
  // 转换为数字
563
607
  deductionDetails,
@@ -565,12 +609,11 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
565
609
  // 转换为数字
566
610
  _unified_available_status: totalDeducted.greaterThan(0) ? 1 : 0
567
611
  });
612
+ if (totalDeducted.greaterThan(0)) {
613
+ usedVoucherCounts.set(product_id, usedCount + 1);
614
+ }
568
615
  });
569
616
  const selectedIds = new Set(selectedVouchers.map((v) => v.id));
570
- const usedVoucherCounts = /* @__PURE__ */ new Map();
571
- selectedVouchers.forEach((v) => {
572
- usedVoucherCounts.set(v.product_id, (usedVoucherCounts.get(v.product_id) || 0) + 1);
573
- });
574
617
  const allWithUpdatedStatus = allVouchers.map((voucher) => {
575
618
  if (selectedIds.has(voucher.id)) {
576
619
  const selectedDetail = selectedWithDetails.find(
@@ -578,8 +621,15 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
578
621
  );
579
622
  return selectedDetail || voucher;
580
623
  }
581
- const { config, id, product_id } = voucher;
582
- const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true, maxPassesPerItem = 0 } = config;
624
+ const { product_id } = voucher;
625
+ const config = getWalletPassCalculationConfig(voucher);
626
+ const {
627
+ maxDeductionAmount,
628
+ allowCrossProduct,
629
+ applicableProductLimit,
630
+ deductTaxAndFee,
631
+ maxPassesPerItem
632
+ } = config;
583
633
  const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
584
634
  const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
585
635
  const recommendedAmount = getRecommendedAmount(voucher);
@@ -663,6 +713,7 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
663
713
  }
664
714
  return {
665
715
  ...voucher,
716
+ config,
666
717
  _available_max_amount: calculatedMaxAmount.toNumber(),
667
718
  // 转换为数字
668
719
  _unified_available_status: isAvailable ? 1 : 0,
@@ -486,6 +486,8 @@ export interface WalletPassPayment {
486
486
  importWalletAssetsSnapshot: (snapshot: WalletAssetsSnapshot) => WalletAssetsState;
487
487
  /** 设置单张钱包资产的选择状态并重新计算其实际抵扣额。 */
488
488
  selectWalletAsset: (assetId: WalletAssetId, options: SelectWalletAssetOptions) => WalletAssetsState;
489
+ /** 更新已选钱包资产的手动使用金额,并保持原有选择顺序重新计算。 */
490
+ updateWalletAssetAmount: (assetId: WalletAssetId, amount: number) => WalletAssetsState;
489
491
  /** 精确搜索钱包资产;仅当前券可用时自动选择。 */
490
492
  scanWalletAssetAsync: (code: string) => Promise<ScanWalletAssetResult>;
491
493
  /** 清空未确认的钱包资产选择。 */
@@ -34,7 +34,11 @@ var formatWalletPassList2PreparePayments = (list) => {
34
34
  const formatted = (list || []).map((item) => {
35
35
  return {
36
36
  voucher_id: item.id || 0,
37
- amount: Number(item.edit_current_amount || getAvailableMaxAmount(item)) || 0,
37
+ // 前端策略重算后的 actualDeduction 才是最终可同步金额;
38
+ // edit_current_amount 只是用户请求值,仍可能被策略和订单剩余金额裁剪。
39
+ amount: Number(
40
+ item.actualDeduction ?? item.edit_current_amount ?? getAvailableMaxAmount(item)
41
+ ) || 0,
38
42
  tag: item.tag || "",
39
43
  wallet_pass_usage_unit: item.wallet_pass_usage_unit || {},
40
44
  wallet_pass_use_value: item.wallet_pass_use_value || void 0
@@ -24,6 +24,11 @@ export declare class WalletPassPaymentImpl implements WalletPassPayment {
24
24
  private emitEvent;
25
25
  private publishWalletAssetsState;
26
26
  private recalculateWalletAssets;
27
+ /**
28
+ * 搜索接口只返回后端 Wallet Pass 数据,不包含前端策略 config。
29
+ * 在资产进入共享选择状态前补齐策略结果,避免增量重算回退到接口状态。
30
+ */
31
+ private formatSearchedWalletPaymentAssets;
27
32
  /** 获取当前缓存中所有支付类 Wallet 资产的扫码 code。 */
28
33
  private getSearchedWalletAssetCodes;
29
34
  /**
@@ -106,7 +111,13 @@ export declare class WalletPassPaymentImpl implements WalletPassPayment {
106
111
  getWalletAssetsState(): WalletAssetsState;
107
112
  exportWalletAssetsSnapshot(): WalletAssetsSnapshot;
108
113
  importWalletAssetsSnapshot(snapshot: WalletAssetsSnapshot): WalletAssetsState;
114
+ private updateWalletAssetEditAmountInCaches;
109
115
  selectWalletAsset(assetId: WalletAssetId, options: SelectWalletAssetOptions): WalletAssetsState;
116
+ /**
117
+ * 更新已选资产的手动金额。只允许在当前实际抵扣额内向下调整,
118
+ * 金额为 0 时沿用 legacy 行为取消选择。
119
+ */
120
+ updateWalletAssetAmount(assetId: WalletAssetId, amount: number): WalletAssetsState;
110
121
  scanWalletAssetAsync(code: string): Promise<ScanWalletAssetResult>;
111
122
  clearWalletAssetSelection(): WalletAssetsState;
112
123
  setCommittedWalletAssets(committedIds: WalletAssetId[], committedAssets?: any[]): WalletAssetsState;
@@ -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))
@@ -818,6 +902,17 @@ var WalletPassPaymentImpl = class {
818
902
  const assetId = getWalletAssetId(asset);
819
903
  return assetId !== void 0 && assetId !== null && selectionSources[String(assetId)] === "scan";
820
904
  }) : [];
905
+ const manualEditAmountByKey = /* @__PURE__ */ new Map();
906
+ if (preserveSelection) {
907
+ this.walletAssetsState.selectedItems.forEach((asset) => {
908
+ if (typeof (asset == null ? void 0 : asset.edit_current_amount) !== "number")
909
+ return;
910
+ manualEditAmountByKey.set(
911
+ getWalletAssetKey(asset),
912
+ asset.edit_current_amount
913
+ );
914
+ });
915
+ }
821
916
  if (!((_a = businessData.products) == null ? void 0 : _a.length)) {
822
917
  this.queuePendingAutoSelectForAssets(scanSelectedAssets);
823
918
  this.generateWalletParams(businessData);
@@ -916,12 +1011,16 @@ var WalletPassPaymentImpl = class {
916
1011
  searchedWalletAssets,
917
1012
  result.transformList || [],
918
1013
  result.noApplicableVoucher || []
919
- ).map((asset) => ({
920
- ...asset,
921
- _wallet_asset_recommended: recommendedKeys.has(
922
- getWalletAssetKey(asset)
923
- )
924
- }));
1014
+ ).map((asset) => {
1015
+ const assetKey = getWalletAssetKey(asset);
1016
+ return {
1017
+ ...asset,
1018
+ ...manualEditAmountByKey.has(assetKey) ? {
1019
+ edit_current_amount: manualEditAmountByKey.get(assetKey)
1020
+ } : {},
1021
+ _wallet_asset_recommended: recommendedKeys.has(assetKey)
1022
+ };
1023
+ });
925
1024
  const latestSelectedIds = preserveSelection ? authoritativeSelectedIds : [];
926
1025
  const latestSelectionSources = preserveSelection ? { ...this.walletAssetsState.selectionSources } : {};
927
1026
  const pendingAutoSelectAssets = this.walletAssetsBehavior.autoSelectAfterSearch ? items.filter(
@@ -1035,6 +1134,13 @@ var WalletPassPaymentImpl = class {
1035
1134
  selectionSources: snapshot.state.selectionSources || {}
1036
1135
  });
1037
1136
  }
1137
+ updateWalletAssetEditAmountInCaches(assetKey, amount) {
1138
+ const updateAsset = (asset) => getWalletAssetKey(asset) === assetKey ? { ...asset, edit_current_amount: amount } : asset;
1139
+ this.searchResults = this.searchResults.map(
1140
+ updateAsset
1141
+ );
1142
+ return this.walletAssetsState.items.map(updateAsset);
1143
+ }
1038
1144
  selectWalletAsset(assetId, options) {
1039
1145
  const assetKey = String(assetId);
1040
1146
  const asset = this.walletAssetsState.items.find(
@@ -1050,6 +1156,7 @@ var WalletPassPaymentImpl = class {
1050
1156
  );
1051
1157
  const selectionSources = { ...this.walletAssetsState.selectionSources };
1052
1158
  delete selectionSources[assetKey];
1159
+ let nextItems = this.walletAssetsState.items;
1053
1160
  if (options.selected) {
1054
1161
  if (!asset || !isWalletAssetAvailable(asset)) {
1055
1162
  return this.getWalletAssetsState();
@@ -1058,9 +1165,13 @@ var WalletPassPaymentImpl = class {
1058
1165
  selectionSources[assetKey] = options.source || "manual";
1059
1166
  } else if (asset) {
1060
1167
  this.clearPendingAutoSelectForAsset(asset);
1168
+ nextItems = this.updateWalletAssetEditAmountInCaches(
1169
+ assetKey,
1170
+ void 0
1171
+ );
1061
1172
  }
1062
1173
  const nextState = this.recalculateWalletAssets(
1063
- this.walletAssetsState.items,
1174
+ nextItems,
1064
1175
  selectedIds,
1065
1176
  selectionSources
1066
1177
  );
@@ -1069,6 +1180,54 @@ var WalletPassPaymentImpl = class {
1069
1180
  }
1070
1181
  return nextState;
1071
1182
  }
1183
+ /**
1184
+ * 更新已选资产的手动金额。只允许在当前实际抵扣额内向下调整,
1185
+ * 金额为 0 时沿用 legacy 行为取消选择。
1186
+ */
1187
+ updateWalletAssetAmount(assetId, amount) {
1188
+ const assetKey = String(assetId);
1189
+ const committedKeys = new Set(
1190
+ this.walletAssetsState.committedIds.map((id) => String(id))
1191
+ );
1192
+ const selectedKeys = new Set(
1193
+ this.walletAssetsState.selectedIds.map((id) => String(id))
1194
+ );
1195
+ const asset = this.walletAssetsState.items.find(
1196
+ (item) => getWalletAssetKey(item) === assetKey
1197
+ );
1198
+ const requestedAmount = Number(amount);
1199
+ if (!asset || committedKeys.has(assetKey) || !selectedKeys.has(assetKey) || !Number.isFinite(requestedAmount) || requestedAmount < 0) {
1200
+ return this.getWalletAssetsState();
1201
+ }
1202
+ const selectedIds = [...this.walletAssetsState.selectedIds];
1203
+ const selectionSources = { ...this.walletAssetsState.selectionSources };
1204
+ if (requestedAmount === 0) {
1205
+ const nextItems2 = this.updateWalletAssetEditAmountInCaches(
1206
+ assetKey,
1207
+ void 0
1208
+ );
1209
+ delete selectionSources[assetKey];
1210
+ this.clearPendingAutoSelectForAsset(asset);
1211
+ return this.recalculateWalletAssets(
1212
+ nextItems2,
1213
+ selectedIds.filter((id) => String(id) !== assetKey),
1214
+ selectionSources
1215
+ );
1216
+ }
1217
+ const currentDeductionAmount = getWalletAssetDeductionAmount(asset);
1218
+ const editAmount = Math.min(requestedAmount, currentDeductionAmount);
1219
+ if (editAmount <= 0)
1220
+ return this.getWalletAssetsState();
1221
+ const nextItems = this.updateWalletAssetEditAmountInCaches(
1222
+ assetKey,
1223
+ editAmount
1224
+ );
1225
+ return this.recalculateWalletAssets(
1226
+ nextItems,
1227
+ selectedIds,
1228
+ selectionSources
1229
+ );
1230
+ }
1072
1231
  async scanWalletAssetAsync(code) {
1073
1232
  const normalizedCode = String(code || "").trim();
1074
1233
  if (!normalizedCode) {
@@ -1097,7 +1256,15 @@ var WalletPassPaymentImpl = class {
1097
1256
  this.walletAssetsState.selectedItems
1098
1257
  )
1099
1258
  });
1100
- const paymentAssets = result.data.filter(isWalletPaymentAsset);
1259
+ const paymentAssets = this.formatSearchedWalletPaymentAssets(
1260
+ result.data.filter(isWalletPaymentAsset)
1261
+ );
1262
+ if (paymentAssets.length > 0) {
1263
+ this.searchResults = mergeWalletAssets(
1264
+ this.searchResults,
1265
+ paymentAssets
1266
+ );
1267
+ }
1101
1268
  const asset = paymentAssets.find(
1102
1269
  (item) => String((item == null ? void 0 : item.code) || "") === normalizedCode
1103
1270
  ) || paymentAssets[0];
@@ -1116,30 +1283,44 @@ var WalletPassPaymentImpl = class {
1116
1283
  this.walletAssetsState.selectionSources
1117
1284
  );
1118
1285
  const assetId = getWalletAssetId(asset);
1119
- const selected = this.walletAssetsBehavior.autoSelectAfterSearch && assetId !== void 0 && isWalletAssetAvailable(asset) ? this.selectWalletAsset(assetId, {
1286
+ const recalculatedAsset = assetId === void 0 ? void 0 : recalculatedState.items.find(
1287
+ (item) => getWalletAssetKey(item) === String(assetId)
1288
+ );
1289
+ const selected = this.walletAssetsBehavior.autoSelectAfterSearch && assetId !== void 0 && isWalletAssetAvailable(recalculatedAsset) ? this.selectWalletAsset(assetId, {
1120
1290
  selected: true,
1121
1291
  source: "scan"
1122
1292
  }) : recalculatedState;
1123
1293
  const isSelected = assetId !== void 0 && selected.selectedIds.some((id) => String(id) === String(assetId));
1294
+ const selectedStateAsset = assetId === void 0 ? void 0 : selected.items.find(
1295
+ (item) => getWalletAssetKey(item) === String(assetId)
1296
+ );
1297
+ const resolvedAsset = selectedStateAsset || recalculatedAsset || asset;
1124
1298
  if (isSelected) {
1125
- this.clearPendingAutoSelectForAsset(asset);
1299
+ this.clearPendingAutoSelectForAsset(resolvedAsset);
1126
1300
  } else if (this.walletAssetsBehavior.autoSelectAfterSearch) {
1127
1301
  this.pendingAutoSelectSearchCodes.add(
1128
1302
  normalizeWalletAssetCode(normalizedCode)
1129
1303
  );
1130
- this.queuePendingAutoSelectForAssets([asset]);
1304
+ this.queuePendingAutoSelectForAssets([resolvedAsset]);
1131
1305
  }
1132
1306
  return {
1133
1307
  type: result.type,
1134
- asset,
1308
+ asset: resolvedAsset,
1135
1309
  selected: isSelected,
1136
1310
  state: selected
1137
1311
  };
1138
1312
  }
1139
1313
  clearWalletAssetSelection() {
1140
1314
  this.pendingAutoSelectSearchCodes.clear();
1315
+ const selectedKeys = new Set(
1316
+ this.walletAssetsState.selectedIds.map((id) => String(id))
1317
+ );
1318
+ const clearSelectedEditAmount = (asset) => selectedKeys.has(getWalletAssetKey(asset)) ? { ...asset, edit_current_amount: void 0 } : asset;
1319
+ this.searchResults = this.searchResults.map(
1320
+ clearSelectedEditAmount
1321
+ );
1141
1322
  return this.recalculateWalletAssets(
1142
- this.walletAssetsState.items,
1323
+ this.walletAssetsState.items.map(clearSelectedEditAmount),
1143
1324
  [],
1144
1325
  {}
1145
1326
  );
@@ -562,7 +562,7 @@ function buildOptionTitleSuffix(options) {
562
562
  function formatBundlePrice(bundle, currencySymbol) {
563
563
  const value = bundle.bundle_selling_price ?? bundle.bundle_sum_price ?? bundle.price ?? 0;
564
564
  const amount = new import_decimal.default(toMoneyNumber(value));
565
- const isNegative = bundle.price_type === "markdown" || bundle.price_type === "markup" && bundle.price_type_ext === "product_price";
565
+ const isNegative = bundle.price_type === "markdown";
566
566
  if (isNegative)
567
567
  return `-${currencySymbol}${amount.abs().toFixed(2)}`;
568
568
  return formatMoney(amount, currencySymbol);
@@ -355,6 +355,11 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
355
355
  selected: boolean;
356
356
  source?: WalletAssetSelectionSource;
357
357
  }): Promise<WalletAssetsState>;
358
+ /** 更新已选钱包资产的手动使用金额,并同步 pending payment。 */
359
+ updateWalletAssetAmount(params: {
360
+ assetId: WalletAssetId;
361
+ amount: number;
362
+ }): Promise<WalletAssetsState>;
358
363
  /** 扫码精确查券;仅可用资产会被自动选中。 */
359
364
  scanWalletAsset(code: string): Promise<ScanWalletAssetResult>;
360
365
  /** 清空全部未确认钱包选择,已支付钱包项由 Order 模块继续保留。 */