@pisell/pisellos 2.3.43 → 2.3.44

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 (33) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  2. package/dist/modules/Order/index.d.ts +2 -1
  3. package/dist/modules/Order/index.js +73 -28
  4. package/dist/modules/Order/types.d.ts +2 -0
  5. package/dist/modules/Payment/walletpass.d.ts +10 -3
  6. package/dist/modules/Payment/walletpass.js +425 -282
  7. package/dist/modules/Rules/index.d.ts +2 -0
  8. package/dist/modules/Rules/index.js +61 -57
  9. package/dist/solution/BaseSales/index.js +6 -3
  10. package/dist/solution/BaseSales/types.d.ts +3 -1
  11. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +6 -3
  12. package/dist/solution/BookingTicket/index.d.ts +1 -1
  13. package/dist/solution/BookingTicket/index.js +6 -3
  14. package/dist/solution/BookingTicket/types.d.ts +1 -0
  15. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +3 -3
  16. package/dist/solution/VenueBooking/index.d.ts +1 -0
  17. package/lib/model/strategy/adapter/promotion/index.js +51 -0
  18. package/lib/modules/Order/index.d.ts +2 -1
  19. package/lib/modules/Order/index.js +46 -7
  20. package/lib/modules/Order/types.d.ts +2 -0
  21. package/lib/modules/Payment/walletpass.d.ts +10 -3
  22. package/lib/modules/Payment/walletpass.js +218 -56
  23. package/lib/modules/Rules/index.d.ts +2 -0
  24. package/lib/modules/Rules/index.js +8 -3
  25. package/lib/solution/BaseSales/index.js +1 -0
  26. package/lib/solution/BaseSales/types.d.ts +3 -1
  27. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +3 -2
  28. package/lib/solution/BookingTicket/index.d.ts +1 -1
  29. package/lib/solution/BookingTicket/index.js +1 -0
  30. package/lib/solution/BookingTicket/types.d.ts +1 -0
  31. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +2 -2
  32. package/lib/solution/VenueBooking/index.d.ts +1 -0
  33. package/package.json +1 -1
@@ -0,0 +1,51 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/model/strategy/adapter/promotion/index.ts
30
+ var promotion_exports = {};
31
+ __export(promotion_exports, {
32
+ BUY_X_GET_Y_FREE_STRATEGY: () => import_examples.BUY_X_GET_Y_FREE_STRATEGY,
33
+ ITEM_REWARD_STRATEGY: () => import_examples.ITEM_REWARD_STRATEGY,
34
+ PromotionAdapter: () => import_adapter.PromotionAdapter,
35
+ PromotionEvaluator: () => import_evaluator.PromotionEvaluator,
36
+ X_ITEMS_FOR_Y_PRICE_STRATEGY: () => import_examples.X_ITEMS_FOR_Y_PRICE_STRATEGY,
37
+ default: () => import_adapter2.default
38
+ });
39
+ module.exports = __toCommonJS(promotion_exports);
40
+ var import_evaluator = require("./evaluator");
41
+ var import_adapter = require("./adapter");
42
+ var import_adapter2 = __toESM(require("./adapter"));
43
+ var import_examples = require("./examples");
44
+ // Annotate the CommonJS export names for ESM import in node:
45
+ 0 && (module.exports = {
46
+ BUY_X_GET_Y_FREE_STRATEGY,
47
+ ITEM_REWARD_STRATEGY,
48
+ PromotionAdapter,
49
+ PromotionEvaluator,
50
+ X_ITEMS_FOR_Y_PRICE_STRATEGY
51
+ });
@@ -81,6 +81,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
81
81
  private ensureEditDiscountConfigForProductChange;
82
82
  scanCode(code: string, customerId?: number): Promise<{
83
83
  isAvailable: boolean;
84
+ isAccepted?: boolean;
84
85
  discountList: Discount[];
85
86
  type: 'server' | 'clientCalc';
86
87
  unavailableReason?: UnavailableReason;
@@ -486,7 +487,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
486
487
  generateIdempotencyToken(): string;
487
488
  syncPaymentsToOrder<T = any>(params: SyncPaymentsToOrderParams): Promise<SyncPaymentsToOrderResult<T>>;
488
489
  createOrder(params: CommitOrderParams['query']): {
489
- type: "appointment_booking" | "virtual";
490
+ type: "virtual" | "appointment_booking";
490
491
  platform: string;
491
492
  sales_channel: string;
492
493
  order_sales_channel: string;
@@ -70,6 +70,24 @@ function isManualProductDiscountProduct(product) {
70
70
  }
71
71
  var ORDER_PAYMENT_STATUS_PAID = "paid";
72
72
  var ORDER_PAYMENT_STATUS_PENDING = "payment_pending";
73
+ var CUSTOMER_PROMOTION_DISCOUNT_TAGS = /* @__PURE__ */ new Set([
74
+ "good_pass",
75
+ "product_discount_card",
76
+ "discount_card"
77
+ ]);
78
+ function isCustomerPromotionDiscount(discount) {
79
+ return CUSTOMER_PROMOTION_DISCOUNT_TAGS.has(
80
+ String((discount == null ? void 0 : discount.tag) || (discount == null ? void 0 : discount.type) || "").toLowerCase()
81
+ );
82
+ }
83
+ function markScannedDiscounts(discountList, scannedDiscountList) {
84
+ const scannedIds = new Set(
85
+ scannedDiscountList.map((discount) => discount == null ? void 0 : discount.id).filter((id) => id !== void 0 && id !== null).map(String)
86
+ );
87
+ return discountList.map(
88
+ (discount) => scannedIds.has(String(discount == null ? void 0 : discount.id)) ? { ...discount, isScan: true } : discount
89
+ );
90
+ }
73
91
  function isVoucherPaymentRecord(payment) {
74
92
  return (payment == null ? void 0 : payment.voucher_id) !== void 0 && Number(payment.voucher_id) !== 0;
75
93
  }
@@ -122,6 +140,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
122
140
  const type = (item == null ? void 0 : item.type) ?? (item == null ? void 0 : item.tag);
123
141
  if (!type || type === "product")
124
142
  continue;
143
+ const isDiscountCard = type === "discount_card" || type === "product_discount_card";
125
144
  const resourceId = ((_a = item == null ? void 0 : item.discount) == null ? void 0 : _a.resource_id) ?? (item == null ? void 0 : item.resource_id) ?? (item == null ? void 0 : item.discount_id) ?? (item == null ? void 0 : item.order_discount_id);
126
145
  if (resourceId === void 0 || resourceId === null || resourceId === "")
127
146
  continue;
@@ -144,7 +163,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
144
163
  if (existed) {
145
164
  existed.amount = Number(existed.amount || 0) + amount;
146
165
  existed.used_par_value = String(Number(existed.used_par_value || 0) + amount);
147
- existed.balance = String(Number(existed.balance || 0) + amount);
166
+ if (!isDiscountCard) {
167
+ existed.balance = String(Number(existed.balance || 0) + amount);
168
+ }
148
169
  const productIds = Array.isArray((_f = existed.limited_relation_product_data) == null ? void 0 : _f.product_ids) ? existed.limited_relation_product_data.product_ids : [];
149
170
  if ((product == null ? void 0 : product.product_id) !== void 0 && (product == null ? void 0 : product.product_id) !== null && !productIds.includes(product.product_id)) {
150
171
  existed.limited_relation_product_data = {
@@ -181,7 +202,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
181
202
  filter: 0,
182
203
  exclude_product_ids: []
183
204
  },
184
- balance: String((item == null ? void 0 : item.amount) ?? "0"),
205
+ balance: isDiscountCard ? void 0 : String((item == null ? void 0 : item.amount) ?? "0"),
185
206
  format_title: title,
186
207
  product: {
187
208
  id: (product == null ? void 0 : product.product_id) ?? 0,
@@ -528,7 +549,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
528
549
  });
529
550
  }
530
551
  async scanCode(code, customerId) {
531
- var _a, _b, _c, _d;
552
+ var _a, _b, _c, _d, _e;
532
553
  const resultDiscountWithReason = await ((_a = this.store.discount) == null ? void 0 : _a.batchSearch(code, { customerId })) || { discountList: [], unavailableReasonKey: null };
533
554
  const { discountList: resultDiscountList, unavailableReasonKey } = resultDiscountWithReason;
534
555
  const rulesModule = this.store.rules;
@@ -536,6 +557,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
536
557
  return {
537
558
  type: "clientCalc",
538
559
  isAvailable: false,
560
+ isAccepted: false,
539
561
  discountList: this.getDiscountList(),
540
562
  scannedDiscountList: resultDiscountList,
541
563
  unavailableReason: import_types2.UnavailableReason.Unknown
@@ -545,6 +567,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
545
567
  return {
546
568
  type: "server",
547
569
  isAvailable: false,
570
+ isAccepted: false,
548
571
  discountList: this.getDiscountList(),
549
572
  scannedDiscountList: resultDiscountList,
550
573
  unavailableReasonKey
@@ -560,13 +583,19 @@ var OrderModule = class extends import_BaseModule.BaseModule {
560
583
  return {
561
584
  type: "clientCalc",
562
585
  isAvailable: true,
586
+ isAccepted: true,
563
587
  discountList: this.getDiscountList(),
564
588
  scannedDiscountList: resultDiscountList
565
589
  };
566
590
  }
567
591
  const tempOrder = this.store.tempOrder;
568
592
  const holders = ((_b = tempOrder == null ? void 0 : tempOrder.holder) == null ? void 0 : _b.form_record) || [];
569
- const { isAvailable, discountList: newDiscountList, unavailableReason } = rulesModule.isDiscountListAvailable({
593
+ const {
594
+ isAvailable,
595
+ discountList: newDiscountList,
596
+ evaluatedDiscountList,
597
+ unavailableReason
598
+ } = rulesModule.isDiscountListAvailable({
570
599
  productList: (tempOrder == null ? void 0 : tempOrder.products) || [],
571
600
  oldDiscountList: this.getDiscountList(),
572
601
  newDiscountList: withScanList,
@@ -574,15 +603,25 @@ var OrderModule = class extends import_BaseModule.BaseModule {
574
603
  holders,
575
604
  isFormSubject: (product) => (0, import_utils.resolveIsFormSubject)(tempOrder, product)
576
605
  }) || { isAvailable: false, discountList: this.getDiscountList() };
577
- if (isAvailable && newDiscountList) {
578
- (_d = this.store.discount) == null ? void 0 : _d.setDiscountList(newDiscountList);
606
+ const shouldRetainForLater = !isAvailable && unavailableReason === import_types2.UnavailableReason.ProductNotApplicable && withScanList.some(isCustomerPromotionDiscount);
607
+ const normalizedNewDiscountList = newDiscountList ? markScannedDiscounts(newDiscountList, withScanList) : void 0;
608
+ const retainedDiscountList = evaluatedDiscountList ? markScannedDiscounts(evaluatedDiscountList, withScanList) : normalizedNewDiscountList;
609
+ let resolvedDiscountList = normalizedNewDiscountList || this.getDiscountList();
610
+ if (isAvailable && normalizedNewDiscountList) {
611
+ await ((_d = this.store.discount) == null ? void 0 : _d.setDiscountList(normalizedNewDiscountList));
579
612
  this.applyDiscount();
580
613
  await this.syncScannedDiscountsToOriginalDiscountList(this.getDiscountList());
614
+ resolvedDiscountList = this.getDiscountList();
615
+ } else if (shouldRetainForLater && retainedDiscountList) {
616
+ await ((_e = this.store.discount) == null ? void 0 : _e.setDiscountList(retainedDiscountList));
617
+ await this.syncScannedDiscountsToOriginalDiscountList(this.getDiscountList());
618
+ resolvedDiscountList = this.getDiscountList();
581
619
  }
582
620
  return {
583
621
  type: "clientCalc",
584
622
  isAvailable: isAvailable || false,
585
- discountList: newDiscountList || this.getDiscountList(),
623
+ isAccepted: Boolean(isAvailable || shouldRetainForLater),
624
+ discountList: resolvedDiscountList,
586
625
  scannedDiscountList: resultDiscountList,
587
626
  unavailableReason
588
627
  };
@@ -165,6 +165,8 @@ export interface OrderCustomerChangePayload {
165
165
  }
166
166
  export interface OrderScanCodeResult {
167
167
  isAvailable: boolean;
168
+ /** 卡券已加入当前订单折扣列表,但不一定能立即应用。 */
169
+ isAccepted?: boolean;
168
170
  discountList: Discount[];
169
171
  scannedDiscountList: Discount[];
170
172
  type: 'server' | 'clientCalc';
@@ -21,11 +21,13 @@ export declare class WalletPassPaymentImpl implements WalletPassPayment {
21
21
  private emitEvent;
22
22
  private publishWalletAssetsState;
23
23
  private recalculateWalletAssets;
24
+ /** 获取当前缓存中所有支付类 Wallet 资产的扫码 code。 */
25
+ private getSearchedWalletAssetCodes;
24
26
  /**
25
- * 使用最新订单参数重验扫码搜索过的支付类 Wallet 资产。
26
- * 请求失败或暂时无匹配结果时保留旧资产,避免商品变化造成列表闪空。
27
+ * 用订单重算接口返回的数据更新扫码 Wallet 缓存。
28
+ * 接口暂时缺失某个扫码 code 时继续保留旧值,避免列表闪空。
27
29
  */
28
- private revalidateSearchedWalletAssets;
30
+ private syncSearchedWalletAssets;
29
31
  /**
30
32
  * 生成钱包API默认参数
31
33
  * 根据业务数据生成标准的钱包API参数,并存储在模块中
@@ -67,6 +69,11 @@ export declare class WalletPassPaymentImpl implements WalletPassPayment {
67
69
  noApplicableVoucher: any[];
68
70
  products: any[];
69
71
  }>;
72
+ /**
73
+ * 商品或订单金额变化后,使用聚合接口一次重算已选与剩余 WalletPass。
74
+ * Walk-In 不发送 customer_id;会员才携带真实 customer_id。
75
+ */
76
+ private aggregateRecalculateWalletAssetsAsync;
70
77
  getWalletPassRecommendListAsync(params: WalletDeductionRecommendParams): Promise<WalletRecommendItem[]>;
71
78
  formatWalletPassList2PreparePayments(list: WalletRecommendItem[]): {
72
79
  voucher_id: number;
@@ -41,6 +41,11 @@ function getWalletAssetKey(assetOrId) {
41
41
  const id = assetOrId && typeof assetOrId === "object" ? getWalletAssetId(assetOrId) : assetOrId;
42
42
  return id === void 0 || id === null ? "" : String(id);
43
43
  }
44
+ function matchesWalletAssetCode(asset, normalizedCode) {
45
+ return [asset == null ? void 0 : asset.code, asset == null ? void 0 : asset.encoded].some(
46
+ (identifier) => String(identifier ?? "").trim() === normalizedCode
47
+ );
48
+ }
44
49
  function isWalletAssetAvailable(asset) {
45
50
  if (!asset)
46
51
  return false;
@@ -150,12 +155,10 @@ var WalletPassPaymentImpl = class {
150
155
  const committedKeys = new Set(
151
156
  this.walletAssetsState.committedIds.map((id) => String(id))
152
157
  );
153
- const requestedKeys = new Set(
154
- requestedIds.map((id) => String(id)).filter((id) => !committedKeys.has(id))
155
- );
156
- const requested = items.filter(
157
- (item) => requestedKeys.has(getWalletAssetKey(item)) && isWalletAssetAvailable(item)
158
+ const itemsByKey = new Map(
159
+ items.map((item) => [getWalletAssetKey(item), item])
158
160
  );
161
+ const requested = requestedIds.map((id) => String(id)).filter((id) => !committedKeys.has(id)).map((id) => itemsByKey.get(id)).filter((item) => item && isWalletAssetAvailable(item));
159
162
  let nextItems = items;
160
163
  let selectedItems = requested;
161
164
  const evaluator = (_b = (_a = this.paymentModule.window) == null ? void 0 : _a.getWalletPassEvaluator) == null ? void 0 : _b.call(_a);
@@ -201,57 +204,40 @@ var WalletPassPaymentImpl = class {
201
204
  error: null
202
205
  });
203
206
  }
207
+ /** 获取当前缓存中所有支付类 Wallet 资产的扫码 code。 */
208
+ getSearchedWalletAssetCodes() {
209
+ const cachedAssets = this.searchResults.filter(isWalletPaymentAsset);
210
+ const seenCodes = /* @__PURE__ */ new Set();
211
+ return cachedAssets.map((item) => String((item == null ? void 0 : item.code) || "").trim()).filter((code) => {
212
+ const normalizedCode = code.toUpperCase();
213
+ if (!normalizedCode || seenCodes.has(normalizedCode))
214
+ return false;
215
+ seenCodes.add(normalizedCode);
216
+ return true;
217
+ });
218
+ }
204
219
  /**
205
- * 使用最新订单参数重验扫码搜索过的支付类 Wallet 资产。
206
- * 请求失败或暂时无匹配结果时保留旧资产,避免商品变化造成列表闪空。
220
+ * 用订单重算接口返回的数据更新扫码 Wallet 缓存。
221
+ * 接口暂时缺失某个扫码 code 时继续保留旧值,避免列表闪空。
207
222
  */
208
- async revalidateSearchedWalletAssets(requestSequence) {
223
+ syncSearchedWalletAssets(refreshedCandidates) {
209
224
  const cachedAssets = this.searchResults.filter(isWalletPaymentAsset);
210
- const searchCodes = [
211
- ...new Set(
212
- cachedAssets.map((item) => String((item == null ? void 0 : item.code) || "").trim()).filter(Boolean)
213
- )
214
- ];
215
- if (searchCodes.length === 0)
216
- return cachedAssets;
217
- const preparePayments = this.formatWalletPassList2PreparePayments(
218
- this.walletAssetsState.selectedItems
219
- );
220
- const refreshedGroups = await Promise.all(
221
- searchCodes.map(async (searchCode) => {
222
- try {
223
- const result = await this.searchIdentificationCodeAsync(
224
- {
225
- ...this.walletParams || {},
226
- code: searchCode,
227
- prepare_payments: preparePayments
228
- },
229
- { noCache: true }
230
- );
231
- return {
232
- searchCode,
233
- items: result.type === "normalCode" ? result.data.filter(isWalletPaymentAsset) : []
234
- };
235
- } catch (error) {
236
- this.paymentModule.logWarning(
237
- "[WalletPass] 重验扫码 Wallet 资产失败,保留旧结果",
238
- {
239
- code: searchCode,
240
- error: error instanceof Error ? error.message : String(error)
241
- }
242
- );
243
- return { searchCode, items: [] };
244
- }
245
- })
225
+ const searchedCodeKeys = new Set(
226
+ this.getSearchedWalletAssetCodes().map((code) => code.toUpperCase())
246
227
  );
247
- if (requestSequence !== this.walletAssetsRequestSequence) {
228
+ if (searchedCodeKeys.size === 0)
248
229
  return cachedAssets;
249
- }
250
- const replacedCodes = new Set(
251
- refreshedGroups.filter((group) => group.items.length > 0).map((group) => group.searchCode.toUpperCase())
230
+ const refreshedAssets = refreshedCandidates.filter(
231
+ (item) => isWalletPaymentAsset(item) && searchedCodeKeys.has(
232
+ String((item == null ? void 0 : item.code) || "").trim().toUpperCase()
233
+ )
252
234
  );
253
- const refreshedAssets = refreshedGroups.flatMap((group) => group.items);
254
235
  if (refreshedAssets.length > 0) {
236
+ const replacedCodes = new Set(
237
+ refreshedAssets.map(
238
+ (item) => String((item == null ? void 0 : item.code) || "").trim().toUpperCase()
239
+ )
240
+ );
255
241
  this.searchResults = [
256
242
  ...this.searchResults.filter(
257
243
  (item) => !replacedCodes.has(String((item == null ? void 0 : item.code) || "").trim().toUpperCase())
@@ -259,7 +245,10 @@ var WalletPassPaymentImpl = class {
259
245
  ...refreshedAssets
260
246
  ];
261
247
  }
262
- return mergeWalletAssets(cachedAssets, refreshedAssets);
248
+ return mergeWalletAssets(
249
+ cachedAssets,
250
+ refreshedAssets
251
+ );
263
252
  }
264
253
  /**
265
254
  * 生成钱包API默认参数
@@ -468,6 +457,96 @@ var WalletPassPaymentImpl = class {
468
457
  throw error;
469
458
  }
470
459
  }
460
+ /**
461
+ * 商品或订单金额变化后,使用聚合接口一次重算已选与剩余 WalletPass。
462
+ * Walk-In 不发送 customer_id;会员才携带真实 customer_id。
463
+ */
464
+ async aggregateRecalculateWalletAssetsAsync(businessData, selectedIds) {
465
+ var _a, _b;
466
+ const walletParams = this.generateWalletParams(businessData);
467
+ const customerId = Number(walletParams.customer_id || 0);
468
+ const requestParams = {
469
+ ids: selectedIds.map((id) => String(id)),
470
+ exact_codes: this.getSearchedWalletAssetCodes(),
471
+ order_product_amount: walletParams.order_product_amount,
472
+ order_expect_amount: walletParams.order_expect_amount,
473
+ order_wait_pay_amount: walletParams.order_wait_pay_amount,
474
+ products: walletParams.products,
475
+ available: 2,
476
+ filter_prepare_wallet_pass: 1,
477
+ sale_channel: walletParams.sale_channel || "pos"
478
+ };
479
+ if (customerId > 1) {
480
+ requestParams.customer_id = customerId;
481
+ }
482
+ const response = await this.paymentModule.request.post(
483
+ "/machinecode/prepare/deduction/aggregate-recalculate",
484
+ requestParams
485
+ );
486
+ const responseData = response == null ? void 0 : response.data;
487
+ const hasAggregateLists = responseData && (Object.prototype.hasOwnProperty.call(
488
+ responseData,
489
+ "recalculate_list"
490
+ ) || Object.prototype.hasOwnProperty.call(
491
+ responseData,
492
+ "customer_wallet_pass_list"
493
+ ));
494
+ if (!hasAggregateLists) {
495
+ throw new Error("WalletPass aggregate response is missing list data");
496
+ }
497
+ const recalculateList = Array.isArray(responseData == null ? void 0 : responseData.recalculate_list) ? responseData.recalculate_list : [];
498
+ const customerWalletPassList = Array.isArray(
499
+ responseData == null ? void 0 : responseData.customer_wallet_pass_list
500
+ ) ? responseData.customer_wallet_pass_list : [];
501
+ const allList = mergeWalletAssets(
502
+ recalculateList,
503
+ customerWalletPassList
504
+ );
505
+ const walletPassEvaluator = (_b = (_a = this.paymentModule.window) == null ? void 0 : _a.getWalletPassEvaluator) == null ? void 0 : _b.call(_a);
506
+ const evaluated = (walletPassEvaluator == null ? void 0 : walletPassEvaluator.getRecommendedVouchers) ? walletPassEvaluator.getRecommendedVouchers({
507
+ orderTotalAmount: walletParams.order_wait_pay_amount,
508
+ products: walletParams.products,
509
+ vouchers: allList
510
+ }) : {
511
+ recommended: [],
512
+ transformList: allList.filter(isWalletAssetAvailable),
513
+ noApplicableVoucher: allList.filter(
514
+ (item) => !isWalletAssetAvailable(item)
515
+ )
516
+ };
517
+ const returnedSelectedKeys = new Set(
518
+ recalculateList.filter(isWalletAssetAvailable).map((item) => getWalletAssetKey(item))
519
+ );
520
+ const authoritativeSelectedIds = selectedIds.filter(
521
+ (id) => returnedSelectedKeys.has(String(id))
522
+ );
523
+ this.walletRecommendList = evaluated.recommended || [];
524
+ this.userIdentificationCodes = allList;
525
+ this.emitEvent(import_types.WalletPassHooks.OnUserIdentificationCodesUpdated, {
526
+ userIdentificationCodes: this.userIdentificationCodes
527
+ });
528
+ this.walletInitData = {
529
+ transformList: evaluated.transformList || [],
530
+ noApplicableVoucher: evaluated.noApplicableVoucher || [],
531
+ products: walletParams.products || []
532
+ };
533
+ this.paymentModule.logInfo(
534
+ "[WalletPass] 聚合重算 WalletPass 完成",
535
+ {
536
+ selected_count: recalculateList.length,
537
+ remaining_count: customerWalletPassList.length,
538
+ searched_codes_count: requestParams.exact_codes.length
539
+ }
540
+ );
541
+ return {
542
+ walletRecommendList: this.walletRecommendList,
543
+ userIdentificationCodes: this.userIdentificationCodes,
544
+ transformList: evaluated.transformList || [],
545
+ noApplicableVoucher: evaluated.noApplicableVoucher || [],
546
+ products: walletParams.products || [],
547
+ authoritativeSelectedIds
548
+ };
549
+ }
471
550
  async getWalletPassRecommendListAsync(params) {
472
551
  var _a;
473
552
  try {
@@ -681,6 +760,7 @@ var WalletPassPaymentImpl = class {
681
760
  * 推荐结果只作为可用性/金额计算输入,不会自动成为 selectedIds。
682
761
  */
683
762
  async refreshWalletAssetsFromBusinessAsync(businessData, options = {}) {
763
+ var _a;
684
764
  const requestSequence = ++this.walletAssetsRequestSequence;
685
765
  const nextCustomerId = businessData.customer_id;
686
766
  const previousCustomerId = this.walletAssetsState.customerId;
@@ -697,6 +777,44 @@ var WalletPassPaymentImpl = class {
697
777
  );
698
778
  const selectedIds = preserveSelection ? [...this.walletAssetsState.selectedIds] : [];
699
779
  const selectionSources = preserveSelection ? { ...this.walletAssetsState.selectionSources } : {};
780
+ if (!((_a = businessData.products) == null ? void 0 : _a.length)) {
781
+ this.generateWalletParams(businessData);
782
+ const restoredSearchedAssets = this.searchResults.filter(isWalletPaymentAsset).map((item) => ({
783
+ ...item,
784
+ actualDeduction: 0,
785
+ deductionDetails: [],
786
+ _available_max_amount: 0,
787
+ _unified_available_status: 0,
788
+ _wallet_asset_recommended: false
789
+ }));
790
+ this.walletRecommendList = [];
791
+ this.userIdentificationCodes = [];
792
+ this.walletInitData = {
793
+ transformList: [],
794
+ noApplicableVoucher: restoredSearchedAssets,
795
+ products: []
796
+ };
797
+ this.walletAssetsCalculationContext = {
798
+ orderTotalAmount: 0,
799
+ products: []
800
+ };
801
+ this.emitEvent(import_types.WalletPassHooks.OnUserIdentificationCodesUpdated, {
802
+ userIdentificationCodes: []
803
+ });
804
+ return this.publishWalletAssetsState({
805
+ status: "ready",
806
+ customerId: nextCustomerId,
807
+ items: mergeWalletAssets(
808
+ restoredSearchedAssets,
809
+ committedItems
810
+ ),
811
+ selectedIds: [],
812
+ selectedItems: [],
813
+ selectionSources: {},
814
+ totalSelectedAmount: 0,
815
+ error: null
816
+ });
817
+ }
700
818
  this.publishWalletAssetsState({
701
819
  status: "loading",
702
820
  customerId: nextCustomerId,
@@ -708,9 +826,13 @@ var WalletPassPaymentImpl = class {
708
826
  error: null
709
827
  });
710
828
  try {
711
- const result = await this.initializeWalletDataFromBusinessAsync(
712
- businessData
713
- );
829
+ const searchedWalletAssetCodes = preserveSelection ? this.getSearchedWalletAssetCodes() : [];
830
+ const useAggregateRecalculate = preserveSelection && (selectedIds.length > 0 || searchedWalletAssetCodes.length > 0);
831
+ const aggregateResult = useAggregateRecalculate ? await this.aggregateRecalculateWalletAssetsAsync(
832
+ businessData,
833
+ selectedIds
834
+ ) : null;
835
+ const result = aggregateResult || await this.initializeWalletDataFromBusinessAsync(businessData);
714
836
  if (requestSequence !== this.walletAssetsRequestSequence) {
715
837
  return this.getWalletAssetsState();
716
838
  }
@@ -718,7 +840,27 @@ var WalletPassPaymentImpl = class {
718
840
  orderTotalAmount: Number(businessData.order_wait_pay_amount || 0),
719
841
  products: result.products || businessData.products || []
720
842
  };
721
- const searchedWalletAssets = preserveSelection ? await this.revalidateSearchedWalletAssets(requestSequence) : [];
843
+ const authoritativeSelectedIds = (aggregateResult == null ? void 0 : aggregateResult.authoritativeSelectedIds) || selectedIds;
844
+ if (aggregateResult) {
845
+ const returnedAggregateKeys = new Set(
846
+ [
847
+ ...result.transformList || [],
848
+ ...result.noApplicableVoucher || []
849
+ ].map((item) => getWalletAssetKey(item))
850
+ );
851
+ const missingSelectedKeys = new Set(
852
+ selectedIds.map((id) => String(id)).filter((id) => !returnedAggregateKeys.has(id))
853
+ );
854
+ if (missingSelectedKeys.size > 0) {
855
+ this.searchResults = this.searchResults.filter(
856
+ (item) => !missingSelectedKeys.has(getWalletAssetKey(item))
857
+ );
858
+ }
859
+ }
860
+ const searchedWalletAssets = preserveSelection ? this.syncSearchedWalletAssets([
861
+ ...result.transformList || [],
862
+ ...result.noApplicableVoucher || []
863
+ ]) : [];
722
864
  if (requestSequence !== this.walletAssetsRequestSequence) {
723
865
  return this.getWalletAssetsState();
724
866
  }
@@ -728,7 +870,7 @@ var WalletPassPaymentImpl = class {
728
870
  )
729
871
  );
730
872
  const items = mergeWalletAssets(
731
- preserveSelection ? this.walletAssetsState.selectedItems : [],
873
+ preserveSelection && !aggregateResult ? this.walletAssetsState.selectedItems : [],
732
874
  searchedWalletAssets,
733
875
  result.transformList || [],
734
876
  result.noApplicableVoucher || []
@@ -738,7 +880,7 @@ var WalletPassPaymentImpl = class {
738
880
  getWalletAssetKey(asset)
739
881
  )
740
882
  }));
741
- const latestSelectedIds = preserveSelection ? [...this.walletAssetsState.selectedIds] : [];
883
+ const latestSelectedIds = preserveSelection ? authoritativeSelectedIds : [];
742
884
  const latestSelectionSources = preserveSelection ? { ...this.walletAssetsState.selectionSources } : {};
743
885
  return this.recalculateWalletAssets(
744
886
  items,
@@ -749,6 +891,15 @@ var WalletPassPaymentImpl = class {
749
891
  if (requestSequence !== this.walletAssetsRequestSequence) {
750
892
  return this.getWalletAssetsState();
751
893
  }
894
+ this.paymentModule.logError(
895
+ "[WalletPass] 刷新共享钱包资产失败,保留刷新前状态",
896
+ error,
897
+ {
898
+ customer_id: businessData.customer_id,
899
+ selected_ids: selectedIds,
900
+ searched_codes: this.getSearchedWalletAssetCodes()
901
+ }
902
+ );
752
903
  return this.publishWalletAssetsState({
753
904
  status: "error",
754
905
  items: preserveSelection ? this.walletAssetsState.items : committedItems,
@@ -841,6 +992,17 @@ var WalletPassPaymentImpl = class {
841
992
  state: this.getWalletAssetsState()
842
993
  };
843
994
  }
995
+ const selectedAsset = this.walletAssetsState.selectedItems.find(
996
+ (asset2) => matchesWalletAssetCode(asset2, normalizedCode)
997
+ );
998
+ if (selectedAsset) {
999
+ return {
1000
+ type: "normalCode",
1001
+ asset: selectedAsset,
1002
+ selected: true,
1003
+ state: this.getWalletAssetsState()
1004
+ };
1005
+ }
844
1006
  this.walletAssetsRequestSequence += 1;
845
1007
  const result = await this.searchIdentificationCodeAsync({
846
1008
  ...this.walletParams || {},
@@ -27,6 +27,8 @@ export declare class RulesModule extends BaseModule implements Module, RulesModu
27
27
  }): {
28
28
  isAvailable: boolean;
29
29
  discountList: Discount[];
30
+ /** 完整规则计算后的列表;调用方可用于保留暂时无适用商品的扫码卡券。 */
31
+ evaluatedDiscountList?: Discount[];
30
32
  productList: any[];
31
33
  unavailableReason?: UnavailableReason;
32
34
  };
@@ -94,7 +94,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
94
94
  productList
95
95
  };
96
96
  }
97
- if (productList.every((item) => {
97
+ if (productList.length > 0 && productList.every((item) => {
98
98
  var _a;
99
99
  const product = this.hooks.getProduct(item);
100
100
  return product.booking_id && (((_a = product.discount_list) == null ? void 0 : _a.length) || product.total == 0);
@@ -152,6 +152,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
152
152
  return {
153
153
  isAvailable: hasApplicableDiscount,
154
154
  discountList: hasApplicableDiscount ? result.discountList : oldDiscountList,
155
+ evaluatedDiscountList: result.discountList,
155
156
  productList: hasApplicableDiscount ? result.productList : productList,
156
157
  unavailableReason
157
158
  };
@@ -181,9 +182,13 @@ var RulesModule = class extends import_BaseModule.BaseModule {
181
182
  getUnavailableReason(discountList, productList) {
182
183
  var _a;
183
184
  for (const discount of discountList) {
184
- for (const item of productList) {
185
+ const bookingTimes = productList.length ? productList.map((item) => {
185
186
  const product = this.hooks.getProduct(item);
186
- const bookingTime = ((product == null ? void 0 : product.startDate) || (0, import_dayjs.default)()).format("YYYY-MM-DD HH:mm:ss");
187
+ return ((product == null ? void 0 : product.startDate) || (0, import_dayjs.default)()).format(
188
+ "YYYY-MM-DD HH:mm:ss"
189
+ );
190
+ }) : [(0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")];
191
+ for (const bookingTime of bookingTimes) {
187
192
  const filteredList = (0, import_utils.filterDiscountListByBookingTime)([discount], bookingTime);
188
193
  if (filteredList.length === 0) {
189
194
  return import_types.UnavailableReason.TimeLimit;
@@ -1719,6 +1719,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1719
1719
  }
1720
1720
  return {
1721
1721
  isAvailable: raw.isAvailable,
1722
+ ...raw.isAccepted !== void 0 ? { isAccepted: raw.isAccepted } : {},
1722
1723
  type: raw.type,
1723
1724
  unavailableReason: raw.unavailableReason,
1724
1725
  scannedDiscountList: raw.scannedDiscountList
@@ -439,8 +439,10 @@ export interface BaseSalesAddLogParams extends BaseSalesLogInput {
439
439
  /** BaseSales.scanPromotionCode 对外的轻量结果(UI 可用 getDiscountList 或 scannedDiscountList 补 Holder) */
440
440
  export interface BaseSalesScanCodeResult {
441
441
  isAvailable: boolean;
442
+ /** 卡券已加入 Promotion;false 时可能是无效、过期或已使用。 */
443
+ isAccepted?: boolean;
442
444
  type?: 'server' | string;
443
445
  unavailableReason?: 'time_limit' | string;
444
- /** batchSearch 原始结果;isAvailable=false 时 discountList 可能未写入 store,Holder 补全需读此字段 */
446
+ /** batchSearch 原始结果;Holder 补全等流程可从这里读取原始卡券。 */
445
447
  scannedDiscountList?: Discount[];
446
448
  }