@pisell/pisellos 2.3.67 → 2.3.69

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 (61) hide show
  1. package/dist/model/strategy/adapter/walletPass/evaluator.js +0 -1
  2. package/dist/model/strategy/adapter/walletPass/utils.js +0 -2
  3. package/dist/modules/BaseModule.d.ts +1 -0
  4. package/dist/modules/BaseModule.js +24 -28
  5. package/dist/modules/Customer/index.d.ts +4 -0
  6. package/dist/modules/Customer/index.js +91 -59
  7. package/dist/modules/Customer/types.d.ts +2 -0
  8. package/dist/modules/Order/index.d.ts +22 -2
  9. package/dist/modules/Order/index.js +313 -142
  10. package/dist/modules/Order/types.d.ts +13 -1
  11. package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +15 -0
  12. package/dist/modules/Order/utils/orderCollectionIdentity.js +75 -0
  13. package/dist/modules/Order/utils.js +0 -1
  14. package/dist/modules/Payment/index.d.ts +4 -0
  15. package/dist/modules/Payment/index.js +14 -4
  16. package/dist/modules/Payment/walletpass.js +52 -18
  17. package/dist/modules/Rules/index.js +46 -31
  18. package/dist/server/index.js +3 -6
  19. package/dist/server/modules/order/index.d.ts +1 -0
  20. package/dist/server/modules/order/index.js +252 -93
  21. package/dist/server/utils/small-ticket.js +2 -1
  22. package/dist/solution/BaseSales/index.d.ts +13 -1
  23. package/dist/solution/BaseSales/index.js +903 -831
  24. package/dist/solution/BaseSales/utils.js +31 -20
  25. package/dist/solution/BookingTicket/index.d.ts +7 -1
  26. package/dist/solution/BookingTicket/index.js +310 -224
  27. package/dist/solution/ScanOrder/utils.js +31 -20
  28. package/dist/solution/UnifiedBookingSales/index.d.ts +25 -0
  29. package/dist/solution/UnifiedBookingSales/index.js +640 -321
  30. package/dist/solution/UnifiedBookingSales/types.d.ts +7 -0
  31. package/lib/model/strategy/adapter/walletPass/evaluator.js +0 -1
  32. package/lib/model/strategy/adapter/walletPass/utils.js +0 -1
  33. package/lib/modules/BaseModule.d.ts +1 -0
  34. package/lib/modules/BaseModule.js +24 -37
  35. package/lib/modules/Customer/index.d.ts +4 -0
  36. package/lib/modules/Customer/index.js +11 -0
  37. package/lib/modules/Customer/types.d.ts +2 -0
  38. package/lib/modules/Order/index.d.ts +22 -2
  39. package/lib/modules/Order/index.js +212 -54
  40. package/lib/modules/Order/types.d.ts +13 -1
  41. package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +15 -0
  42. package/lib/modules/Order/utils/orderCollectionIdentity.js +70 -0
  43. package/lib/modules/Order/utils.js +0 -1
  44. package/lib/modules/Payment/index.d.ts +4 -0
  45. package/lib/modules/Payment/index.js +7 -0
  46. package/lib/modules/Payment/walletpass.js +27 -2
  47. package/lib/modules/Rules/index.js +17 -1
  48. package/lib/server/index.js +3 -6
  49. package/lib/server/modules/order/index.d.ts +1 -0
  50. package/lib/server/modules/order/index.js +66 -2
  51. package/lib/server/utils/small-ticket.js +1 -1
  52. package/lib/solution/BaseSales/index.d.ts +13 -1
  53. package/lib/solution/BaseSales/index.js +61 -18
  54. package/lib/solution/BaseSales/utils.js +29 -18
  55. package/lib/solution/BookingTicket/index.d.ts +7 -1
  56. package/lib/solution/BookingTicket/index.js +65 -14
  57. package/lib/solution/ScanOrder/utils.js +29 -18
  58. package/lib/solution/UnifiedBookingSales/index.d.ts +25 -0
  59. package/lib/solution/UnifiedBookingSales/index.js +188 -0
  60. package/lib/solution/UnifiedBookingSales/types.d.ts +7 -0
  61. package/package.json +1 -1
@@ -1206,7 +1206,6 @@ function productRequiresFormSubject(tempOrder, product) {
1206
1206
  }
1207
1207
  function resolveIsFormSubject(tempOrder, product) {
1208
1208
  var _a;
1209
- debugger;
1210
1209
  if (((_a = tempOrder == null ? void 0 : tempOrder.holder) == null ? void 0 : _a.type) === "form")
1211
1210
  return true;
1212
1211
  if (product)
@@ -41,6 +41,10 @@ export declare class PaymentModule extends BaseModule implements Module, Payment
41
41
  * paymentModule.updateOtherParams({ fatherModule: 'bookingTicket' });
42
42
  */
43
43
  updateOtherParams(params: Record<string, any>): void;
44
+ /**
45
+ * 获取标准化后的宿主平台标识,供支付子能力做平台边界判断。
46
+ */
47
+ getPlatform(): string;
44
48
  private getPaymentNumberAppData;
45
49
  /**
46
50
  * 记录信息日志
@@ -139,6 +139,13 @@ var PaymentModule = class extends import_BaseModule.BaseModule {
139
139
  updateOtherParams(params) {
140
140
  this.otherParams = params || {};
141
141
  }
142
+ /**
143
+ * 获取标准化后的宿主平台标识,供支付子能力做平台边界判断。
144
+ */
145
+ getPlatform() {
146
+ var _a;
147
+ return String(((_a = this.otherParams) == null ? void 0 : _a.platform) || "").trim().toLowerCase();
148
+ }
142
149
  getPaymentNumberAppData(key) {
143
150
  var _a, _b, _c, _d, _e, _f, _g;
144
151
  const getData = (_b = (_a = this.appPlugin) == null ? void 0 : _a.getData) == null ? void 0 : _b.call(_a);
@@ -109,6 +109,16 @@ function mergeWalletAssets(...groups) {
109
109
  });
110
110
  return merged;
111
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
+ }
112
122
  function createInitialWalletAssetsState() {
113
123
  return {
114
124
  status: "idle",
@@ -543,7 +553,6 @@ var WalletPassPaymentImpl = class {
543
553
  vouchers: allList
544
554
  });
545
555
  const { recommended, transformList, noApplicableVoucher, recommendedAmount } = res;
546
- console.log(res, "resres12");
547
556
  this.walletRecommendList = recommended;
548
557
  this.walletInitData = {
549
558
  transformList,
@@ -606,8 +615,12 @@ var WalletPassPaymentImpl = class {
606
615
  const customerWalletPassList = Array.isArray(
607
616
  responseData == null ? void 0 : responseData.customer_wallet_pass_list
608
617
  ) ? responseData.customer_wallet_pass_list : [];
609
- const allList = mergeWalletAssets(
618
+ const orderedRecalculateList = orderWalletAssetsByIds(
610
619
  recalculateList,
620
+ selectedIds
621
+ );
622
+ const allList = mergeWalletAssets(
623
+ orderedRecalculateList,
611
624
  customerWalletPassList
612
625
  );
613
626
  const walletPassEvaluator = (_b = (_a = this.paymentModule.window) == null ? void 0 : _a.getWalletPassEvaluator) == null ? void 0 : _b.call(_a);
@@ -709,6 +722,18 @@ var WalletPassPaymentImpl = class {
709
722
  this.paymentModule.logInfo("[WalletPass] 商品列表为空,跳过获取用户识别码列表");
710
723
  return [];
711
724
  }
725
+ const platform = this.paymentModule.getPlatform();
726
+ if (platform === "pc" || platform === "h5") {
727
+ this.userIdentificationCodes = [];
728
+ this.emitEvent(import_types.WalletPassHooks.OnUserIdentificationCodesUpdated, {
729
+ userIdentificationCodes: []
730
+ });
731
+ this.paymentModule.logInfo(
732
+ "[WalletPass] 用户端平台跳过获取用户识别码列表",
733
+ { platform }
734
+ );
735
+ return [];
736
+ }
712
737
  if (typeof newParams.payment_order_id === "string" && newParams.payment_order_id.startsWith("LOCAL_")) {
713
738
  newParams.payment_order_id = void 0;
714
739
  }
@@ -865,10 +865,26 @@ var RulesModule = class extends import_BaseModule.BaseModule {
865
865
  );
866
866
  }) : void 0;
867
867
  const explicitlySelectedDiscountCard = explicitlySelectedDiscount && (explicitlySelectedDiscount.tag || explicitlySelectedDiscount.type) !== "good_pass" ? explicitlySelectedDiscount : void 0;
868
+ const appliedDiscountCardIds = new Set(
869
+ (product.discount_list || []).filter(
870
+ (item) => ["discount_card", "product_discount_card"].includes(
871
+ (item == null ? void 0 : item.tag) || (item == null ? void 0 : item.type)
872
+ )
873
+ ).map(
874
+ (item) => {
875
+ var _a3;
876
+ return ((_a3 = item == null ? void 0 : item.discount) == null ? void 0 : _a3.resource_id) ?? (item == null ? void 0 : item.resource_id) ?? (item == null ? void 0 : item.id);
877
+ }
878
+ ).filter((id) => id !== void 0 && id !== null).map(String)
879
+ );
880
+ const appliedDiscountCard = (options == null ? void 0 : options.scan) ? void 0 : applicableDiscounts.find((discount) => {
881
+ const discountType = discount.tag || discount.type;
882
+ return discount.isSelected === true && ["discount_card", "product_discount_card"].includes(discountType) && appliedDiscountCardIds.has(String(discount.id));
883
+ });
868
884
  const scannedSelectedDiscountCard = applicableDiscounts.find(
869
885
  (n) => n.isScan && n.isSelected && (n.tag || n.type) !== "good_pass"
870
886
  );
871
- const selectedDiscountCard = explicitlySelectedDiscountCard || scannedSelectedDiscountCard;
887
+ const selectedDiscountCard = explicitlySelectedDiscountCard || appliedDiscountCard || scannedSelectedDiscountCard;
872
888
  const selectedDiscount = explicitlySelectedDiscount || selectedDiscountCard || applicableDiscounts[0];
873
889
  const prioritizedApplicableDiscounts = explicitlySelectedDiscount && (explicitlySelectedDiscount.tag || explicitlySelectedDiscount.type) === "good_pass" ? [
874
890
  explicitlySelectedDiscount,
@@ -3887,9 +3887,7 @@ var Server = class {
3887
3887
  checkoutData,
3888
3888
  order: pendingOrder,
3889
3889
  response: pendingResult,
3890
- requireFullyPaid: true,
3891
- // 自动小票固定使用 print_all 的收据类型 0,不随订单 type 改成券/手环类型 99。
3892
- isManualPrint: true
3890
+ requireFullyPaid: true
3893
3891
  });
3894
3892
  }
3895
3893
  await this.dispatchCheckoutSyncTask({
@@ -3947,9 +3945,8 @@ var Server = class {
3947
3945
  checkoutData: syncResult.checkoutData,
3948
3946
  order: syncedOrder,
3949
3947
  response: syncResult.normalizedResponse,
3950
- requireFullyPaid: true,
3948
+ requireFullyPaid: true
3951
3949
  // print_all 的 type=0 是收据打印;不要沿用同步成功后的券/手环打印 type=99。
3952
- isManualPrint: true
3953
3950
  });
3954
3951
  }
3955
3952
  return response;
@@ -4126,7 +4123,7 @@ var Server = class {
4126
4123
  var _a, _b, _c, _d, _e;
4127
4124
  if (((_b = (_a = this.core.context) == null ? void 0 : _a.getPlatform) == null ? void 0 : _b.call(_a)) !== "android")
4128
4125
  return true;
4129
- return ((_e = (_d = (_c = this.app) == null ? void 0 : _c.storage) == null ? void 0 : _d.getStorage) == null ? void 0 : _e.call(_d, AUTO_PRINT_STORAGE_KEY)) === "true";
4126
+ return ((_e = (_d = (_c = this.app) == null ? void 0 : _c.storage) == null ? void 0 : _d.getStorage) == null ? void 0 : _e.call(_d, AUTO_PRINT_STORAGE_KEY)) !== "false";
4130
4127
  }
4131
4128
  shouldBuildSmallTicketData(order) {
4132
4129
  if (!order || typeof order !== "object")
@@ -283,6 +283,7 @@ export declare class OrderModule extends BaseModule implements Module {
283
283
  * // => 'S-1001'
284
284
  */
285
285
  private getOrderStorageKey;
286
+ private getPersistedOrderStorageKey;
286
287
  private getOrderIdentityMatchKeys;
287
288
  private doOrdersShareIdentity;
288
289
  private findOrderIndexByIdentity;
@@ -39,6 +39,7 @@ var import_orderCollectionIdentity = require("../../../modules/Order/utils/order
39
39
  var import_types = require("./types");
40
40
  var import_payment_utils = require("../../../modules/Order/payment-utils");
41
41
  var INDEXDB_STORE_NAME = "orders";
42
+ var ORDER_STORAGE_KEY_FIELD = "__order_storage_key";
42
43
  var ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY = "server_order_last_full_fetch_at";
43
44
  var ORDER_LAST_FULL_FETCH_SHOP_ID_STORAGE_KEY = "server_order_last_full_fetch_shop_id";
44
45
  var ORDER_SYNC_THROTTLE_MS_STORAGE_KEY = "order_sync_throttle_ms";
@@ -1628,6 +1629,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1628
1629
  const patchedOrders = [];
1629
1630
  const remoteProductMembershipChanges = [];
1630
1631
  const forceWriteStorageKeys = /* @__PURE__ */ new Set();
1632
+ const storageKeyMigrations = [];
1631
1633
  const mergeActions = {};
1632
1634
  const existingProductsHaveStableIdentity = this.store.list.map(
1633
1635
  (order) => this.doProductsHaveStableMembershipIdentity(
@@ -1652,6 +1654,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1652
1654
  const mergeKey = storageKey || (fresh.order_id !== void 0 && fresh.order_id !== null ? this.getIdKey(fresh.order_id) : "");
1653
1655
  if (matchIndex >= 0) {
1654
1656
  const existingOrder = updatedList[matchIndex];
1657
+ const previousStorageKey = this.getPersistedOrderStorageKey(existingOrder);
1655
1658
  if (this.isPendingSyncOrder(existingOrder) && this.isRemoteEchoMergeSource(source)) {
1656
1659
  this.logInfo("mergeOrdersToStore-跳过 pending 订单远端覆盖", {
1657
1660
  source,
@@ -1713,6 +1716,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1713
1716
  }
1714
1717
  }
1715
1718
  }
1719
+ const nextStorageKey = this.getOrderStorageKey(mergedOrder);
1720
+ if (previousStorageKey && nextStorageKey && previousStorageKey !== nextStorageKey) {
1721
+ storageKeyMigrations.push({
1722
+ orderId: mergedOrder.order_id ?? existingOrder.order_id ?? fresh.order_id ?? null,
1723
+ from: previousStorageKey,
1724
+ to: nextStorageKey
1725
+ });
1726
+ }
1716
1727
  updatedList[adjustedMatchIndex] = mergedOrder;
1717
1728
  existingProductsHaveStableIdentity[adjustedMatchIndex] = mergedProductsHaveStableIdentity;
1718
1729
  if (membershipChange) {
@@ -1750,7 +1761,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1750
1761
  patchedOrders,
1751
1762
  source,
1752
1763
  mergeActions,
1753
- {},
1764
+ { storageKeyMigrations },
1754
1765
  forceWriteStorageKeys
1755
1766
  );
1756
1767
  this.logInfo("mergeOrdersToStore-结束", {
@@ -1955,6 +1966,13 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1955
1966
  }
1956
1967
  return "";
1957
1968
  }
1969
+ getPersistedOrderStorageKey(order) {
1970
+ const persistedStorageKey = order == null ? void 0 : order[ORDER_STORAGE_KEY_FIELD];
1971
+ if (persistedStorageKey !== void 0 && persistedStorageKey !== null && persistedStorageKey !== "") {
1972
+ return String(persistedStorageKey);
1973
+ }
1974
+ return this.getOrderStorageKey(order);
1975
+ }
1958
1976
  getOrderIdentityMatchKeys(order) {
1959
1977
  return this.getOrderIdentityEntries(order).map((entry) => `${entry.field}:${entry.key}`);
1960
1978
  }
@@ -2442,7 +2460,18 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2442
2460
  throw new Error("订单 SQLite 数据库未初始化");
2443
2461
  return;
2444
2462
  }
2445
- const ordersSnapshot = (0, import_lodash_es.cloneDeep)(orders).map((order) => this.normalizeOrderCollectionsForIngress(order, `${source}.sqliteIngress`)).filter((order) => this.getOrderStorageKey(order));
2463
+ const migrationTargetStorageKeys = new Set(
2464
+ (options.storageKeyMigrations || []).map((migration) => migration.to)
2465
+ );
2466
+ const ordersSnapshot = (0, import_lodash_es.cloneDeep)(orders).map((order) => this.normalizeOrderCollectionsForIngress(order, `${source}.sqliteIngress`)).map((order) => {
2467
+ const storageKey = this.getOrderStorageKey(order);
2468
+ if (!storageKey || !migrationTargetStorageKeys.has(storageKey))
2469
+ return order;
2470
+ return {
2471
+ ...order,
2472
+ [ORDER_STORAGE_KEY_FIELD]: storageKey
2473
+ };
2474
+ }).filter((order) => this.getOrderStorageKey(order));
2446
2475
  if (ordersSnapshot.length === 0) {
2447
2476
  return;
2448
2477
  }
@@ -2459,6 +2488,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2459
2488
  if (compactedToWrite.length === 0) {
2460
2489
  return;
2461
2490
  }
2491
+ const writtenStorageKeys = new Set(
2492
+ compactedToWrite.map((order) => this.getOrderStorageKey(order)).filter(Boolean)
2493
+ );
2494
+ const storageKeyMigrations = /* @__PURE__ */ new Map();
2495
+ for (const migration of options.storageKeyMigrations || []) {
2496
+ if (!writtenStorageKeys.has(migration.to))
2497
+ continue;
2498
+ if (writtenStorageKeys.has(migration.from))
2499
+ continue;
2500
+ storageKeyMigrations.set(migration.from, migration);
2501
+ }
2462
2502
  try {
2463
2503
  await this.runInOrderSQLiteSaveQueue(async () => {
2464
2504
  let writeMethod = "none";
@@ -2482,6 +2522,30 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2482
2522
  throw new Error("订单 SQLite 数据库不支持写入");
2483
2523
  }
2484
2524
  this.recordRecentSqliteWrite(source, compactedToWrite);
2525
+ if (writeMethod !== "none") {
2526
+ for (const migration of storageKeyMigrations.values()) {
2527
+ try {
2528
+ await this.dbManager.delete(INDEXDB_STORE_NAME, migration.from);
2529
+ this.recentSqliteWriteByStorageKey.delete(migration.from);
2530
+ for (const order of orders) {
2531
+ if (this.getOrderStorageKey(order) !== migration.to)
2532
+ continue;
2533
+ const orderRecord = order;
2534
+ if (String(orderRecord[ORDER_STORAGE_KEY_FIELD] ?? "") !== migration.from)
2535
+ continue;
2536
+ orderRecord[ORDER_STORAGE_KEY_FIELD] = migration.to;
2537
+ }
2538
+ } catch (error) {
2539
+ this.logError("删除订单旧 SQLite storage key 失败", {
2540
+ source,
2541
+ order_id: migration.orderId,
2542
+ oldStorageKey: migration.from,
2543
+ newStorageKey: migration.to,
2544
+ error: error instanceof Error ? error.message : String(error)
2545
+ });
2546
+ }
2547
+ }
2548
+ }
2485
2549
  this.logInfo("patchOrdersInSQLite-完成", {
2486
2550
  source,
2487
2551
  count: compactedToWrite.length,
@@ -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);
@@ -119,6 +119,15 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
119
119
  * application_code 仅作为接口兼容字段,不能替代 channel / business_code。
120
120
  */
121
121
  private getPriceQueryStrategyContext;
122
+ /**
123
+ * 子类可在商品重报价前准备客户维度的策略上下文,例如等待 Wallet/优惠资产加载。
124
+ */
125
+ protected prepareProductPriceQueryContext(_customerId?: number | string): Promise<void>;
126
+ /**
127
+ * 默认保留 BaseSales 的 legacy quotation 行为;已由 /product/query 统一完成
128
+ * 智能定价与报价合并的业务可在子类中覆盖,避免二次覆盖权威价格。
129
+ */
130
+ protected preferAuthoritativeProductQueryPrice(): boolean;
122
131
  private getPriceQuerySchedule;
123
132
  private loadProductsForPriceQuery;
124
133
  private loadProductForPriceQuery;
@@ -141,7 +150,9 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
141
150
  * const params = this.buildSubModuleOtherParams();
142
151
  * this.core.registerModule(orderModule, { otherParams: params });
143
152
  */
144
- protected buildSubModuleOtherParams(): Record<string, any>;
153
+ protected isSessionStoragePersistEnabled(): boolean;
154
+ protected shouldUseSessionStorageForSubModule(_moduleName?: string): boolean;
155
+ protected buildSubModuleOtherParams(moduleName?: string): Record<string, any>;
145
156
  /**
146
157
  * 将父级 otherParams 的变更显式同步给已注册的子模块。
147
158
  *
@@ -434,6 +445,7 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
434
445
  transformBaseProductToOrderProduct(params: TransformBaseProductToOrderProductParams): import("./utils/transformBaseProductToOrderProduct").BaseProductOrderProductInput | null;
435
446
  calculateProductBookingPrice(params: BaseSalesCalculateProductBookingPriceParams): Promise<BaseSalesProductBookingPriceResult>;
436
447
  getQuotationCustomerScopeInfo(): BaseSalesQuotationCustomerScopeInfo;
448
+ private hasSmartPricingStrategies;
437
449
  shouldRecalculateProductBookingPricesForContextChange(params: BaseSalesPriceRecalculationContextChange): boolean;
438
450
  calculateProductBookingPrices(paramsList: BaseSalesCalculateProductBookingPriceParams[]): Promise<BaseSalesProductBookingPriceResult[]>;
439
451
  addProductToOrder(product: Partial<BaseSalesOrderProduct> & BaseSalesOrderProductIdentity, booking?: AddProductBookingInput, options?: AddProductToOrderOptions): Promise<import("../../modules/Order/types").OrderProduct[]>;
@@ -591,7 +591,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
591
591
  });
592
592
  });
593
593
  }
594
- async hydrateLatestLoadedSalesDetail(record, loadSequence) {
594
+ async hydrateLatestLoadedSalesDetail(record, loadSequence, hydrateSource) {
595
595
  let hydratedSales = null;
596
596
  let skippedBeforeHydrate = false;
597
597
  const hydrateTask = async () => {
@@ -601,9 +601,10 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
601
601
  }
602
602
  if (!this.store.order)
603
603
  throw new Error("order 模块未初始化");
604
- const sales = await this.store.order.hydrateTempOrderFromRecord(record, {
605
- recalcOnHydrate: false
606
- });
604
+ const sales = await this.store.order.hydrateTempOrderFromRecord(
605
+ record,
606
+ hydrateSource === "sessionStorage" ? { recalcOnHydrate: true, source: "sessionStorage" } : { recalcOnHydrate: false }
607
+ );
607
608
  hydratedSales = sales;
608
609
  if (loadSequence !== this.salesDetailLoadSequence)
609
610
  return;
@@ -733,8 +734,24 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
733
734
  if (businessCode !== void 0 && businessCode !== null && String(businessCode).trim() !== "") {
734
735
  strategyContext.business_code = String(businessCode).trim();
735
736
  }
737
+ const availableWalletIds = this.getAvailableWalletIds();
738
+ if (availableWalletIds.length > 0 && !Array.isArray(strategyContext.available_wallet_ids)) {
739
+ strategyContext.available_wallet_ids = availableWalletIds;
740
+ }
736
741
  return strategyContext;
737
742
  }
743
+ /**
744
+ * 子类可在商品重报价前准备客户维度的策略上下文,例如等待 Wallet/优惠资产加载。
745
+ */
746
+ async prepareProductPriceQueryContext(_customerId) {
747
+ }
748
+ /**
749
+ * 默认保留 BaseSales 的 legacy quotation 行为;已由 /product/query 统一完成
750
+ * 智能定价与报价合并的业务可在子类中覆盖,避免二次覆盖权威价格。
751
+ */
752
+ preferAuthoritativeProductQueryPrice() {
753
+ return false;
754
+ }
738
755
  getPriceQuerySchedule(params) {
739
756
  const booking = params.booking || {};
740
757
  if (typeof booking.start_date === "string" && booking.start_date.trim()) {
@@ -927,12 +944,18 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
927
944
  * const params = this.buildSubModuleOtherParams();
928
945
  * this.core.registerModule(orderModule, { otherParams: params });
929
946
  */
930
- buildSubModuleOtherParams() {
947
+ isSessionStoragePersistEnabled() {
948
+ return Boolean(this.cacheId);
949
+ }
950
+ shouldUseSessionStorageForSubModule(_moduleName) {
951
+ return this.isSessionStoragePersistEnabled();
952
+ }
953
+ buildSubModuleOtherParams(moduleName) {
931
954
  return {
932
955
  ...this.otherParams,
933
956
  salesSummaryModuleName: `${this.name}_salesSummary`,
934
957
  fatherModule: this.name,
935
- openCache: this.cacheId ? true : false,
958
+ openCache: this.shouldUseSessionStorageForSubModule(moduleName),
936
959
  cacheId: this.cacheId
937
960
  };
938
961
  }
@@ -943,7 +966,6 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
943
966
  * await this.syncOtherParamsToSubModules({ channel: 'pos' });
944
967
  */
945
968
  async syncOtherParamsToSubModules(changedParams, { cover = false } = {}) {
946
- const nextSubModuleOtherParams = this.buildSubModuleOtherParams();
947
969
  await Promise.all(
948
970
  Object.entries(this.store).map(async ([moduleName, subModule]) => {
949
971
  if (this.reusedSharedSubModuleNames.has(moduleName)) {
@@ -953,6 +975,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
953
975
  if (!targetModule || typeof targetModule.updateOtherParams !== "function") {
954
976
  return;
955
977
  }
978
+ const nextSubModuleOtherParams = this.buildSubModuleOtherParams(moduleName);
956
979
  await targetModule.updateOtherParams(nextSubModuleOtherParams, {
957
980
  changedParams,
958
981
  cover
@@ -965,14 +988,16 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
965
988
  * sessionStorage 损坏时静默忽略,按空状态注册。
966
989
  */
967
990
  readSessionCacheData() {
968
- if (!this.cacheId || !this.window)
991
+ const cacheId = this.cacheId;
992
+ if (!cacheId || !this.isSessionStoragePersistEnabled() || !this.window) {
969
993
  return {};
994
+ }
970
995
  try {
971
996
  const sessionData = this.window.sessionStorage.getItem(this.name);
972
997
  if (!sessionData)
973
998
  return {};
974
999
  const data = JSON.parse(sessionData);
975
- return data && data[this.cacheId] || {};
1000
+ return data && data[cacheId] || {};
976
1001
  } catch {
977
1002
  return {};
978
1003
  }
@@ -1074,9 +1099,9 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1074
1099
  const hooks = this.getSubModuleHooks(step);
1075
1100
  this.store[step] = targetModule;
1076
1101
  this.core.registerModule(targetModule, {
1077
- initialState: (targetCacheData == null ? void 0 : targetCacheData[targetModule.name]) || {},
1102
+ initialState: this.shouldUseSessionStorageForSubModule(step) ? (targetCacheData == null ? void 0 : targetCacheData[targetModule.name]) || {} : {},
1078
1103
  ...hooks ? { hooks } : {},
1079
- otherParams: this.buildSubModuleOtherParams()
1104
+ otherParams: this.buildSubModuleOtherParams(step)
1080
1105
  });
1081
1106
  });
1082
1107
  if (this.store.schedule && !this.isScheduleListLoaded(this.store.schedule)) {
@@ -1143,17 +1168,21 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1143
1168
  const message = loadedRecord && (loadedRecord.message || loadedRecord.msg) || `加载销售详情失败: ${lookup}`;
1144
1169
  throw new Error(message);
1145
1170
  }
1146
- const remoteRecord = filterPendingPaymentsFromLoadedSalesDetail(
1147
- preloadedSalesDetail ?? loadedRecord.data
1148
- );
1171
+ const loadedSalesDetail = preloadedSalesDetail ?? loadedRecord.data;
1172
+ const shouldFilterPendingPayments = params.hydrateSource !== "sessionStorage";
1173
+ const remoteRecord = shouldFilterPendingPayments ? filterPendingPaymentsFromLoadedSalesDetail(loadedSalesDetail) : loadedSalesDetail;
1149
1174
  const currentTempOrder = params.merge ? this.store.order.getTempOrder() : null;
1150
1175
  const mergedRecord = params.merge ? mergeSalesDetailRecordWithTempOrder(
1151
1176
  currentTempOrder,
1152
1177
  remoteRecord,
1153
1178
  "preserve-local"
1154
1179
  ) : remoteRecord;
1155
- const record = filterPendingPaymentsFromLoadedSalesDetail(mergedRecord);
1156
- return await this.hydrateLatestLoadedSalesDetail(record, loadSequence);
1180
+ const record = shouldFilterPendingPayments ? filterPendingPaymentsFromLoadedSalesDetail(mergedRecord) : mergedRecord;
1181
+ return await this.hydrateLatestLoadedSalesDetail(
1182
+ record,
1183
+ loadSequence,
1184
+ params.hydrateSource
1185
+ );
1157
1186
  } finally {
1158
1187
  this.salesDetailLoadInFlightCount = Math.max(
1159
1188
  0,
@@ -3116,6 +3145,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
3116
3145
  async calculateProductBookingPrice(params) {
3117
3146
  const quotation = this.store.quotation;
3118
3147
  const customerId = params.customer_id ?? this.getCurrentOrderCustomerId();
3148
+ await this.prepareProductPriceQueryContext(customerId);
3119
3149
  const authoritativeProduct = await this.loadProductForPriceQuery(params, customerId);
3120
3150
  const product = this.mergeProductForPriceQuery(params.product, authoritativeProduct);
3121
3151
  await this.loadQuotationForPriceQuery({
@@ -3128,7 +3158,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
3128
3158
  product,
3129
3159
  fallback_price: authoritativeProduct ? void 0 : params.fallback_price,
3130
3160
  customer_id: customerId,
3131
- quotation
3161
+ quotation: authoritativeProduct && this.preferAuthoritativeProductQueryPrice() ? void 0 : quotation
3132
3162
  });
3133
3163
  }
3134
3164
  getQuotationCustomerScopeInfo() {
@@ -3165,11 +3195,23 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
3165
3195
  quotationScopes
3166
3196
  };
3167
3197
  }
3198
+ hasSmartPricingStrategies() {
3199
+ var _a, _b;
3200
+ const evaluator = (_b = (_a = this.core) == null ? void 0 : _a.context) == null ? void 0 : _b.dataVariantEvaluator;
3201
+ if (!evaluator || typeof evaluator.getStrategyConfigs !== "function") {
3202
+ return false;
3203
+ }
3204
+ const configs = evaluator.getStrategyConfigs();
3205
+ return Array.isArray(configs) && configs.length > 0;
3206
+ }
3168
3207
  shouldRecalculateProductBookingPricesForContextChange(params) {
3169
3208
  const scopeInfo = this.getQuotationCustomerScopeInfo();
3170
3209
  const scopedCustomerIds = new Set(scopeInfo.customerIds.map((id) => String(id)));
3171
3210
  const previousCustomerId = this.normalizePriceContextCustomerId(params.previousCustomerId);
3172
3211
  const nextCustomerId = this.normalizePriceContextCustomerId(params.nextCustomerId);
3212
+ if (this.hasSmartPricingStrategies()) {
3213
+ return previousCustomerId !== nextCustomerId;
3214
+ }
3173
3215
  const previousInScope = previousCustomerId ? scopedCustomerIds.has(previousCustomerId) : false;
3174
3216
  const nextInScope = nextCustomerId ? scopedCustomerIds.has(nextCustomerId) : false;
3175
3217
  let shouldRecalculate = false;
@@ -3190,6 +3232,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
3190
3232
  return [];
3191
3233
  const quotation = this.store.quotation;
3192
3234
  const customerId = ((_a = paramsList[0]) == null ? void 0 : _a.customer_id) ?? this.getCurrentOrderCustomerId();
3235
+ await this.prepareProductPriceQueryContext(customerId);
3193
3236
  const productMapsByGroup = /* @__PURE__ */ new Map();
3194
3237
  const groups = /* @__PURE__ */ new Map();
3195
3238
  paramsList.forEach((params) => {
@@ -3247,7 +3290,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
3247
3290
  product,
3248
3291
  fallback_price: authoritativeProduct ? void 0 : params.fallback_price,
3249
3292
  customer_id: customerId,
3250
- quotation
3293
+ quotation: authoritativeProduct && this.preferAuthoritativeProductQueryPrice() ? void 0 : quotation
3251
3294
  });
3252
3295
  });
3253
3296
  }
@@ -60,33 +60,44 @@ function createEmptySummary() {
60
60
  };
61
61
  }
62
62
  function buildProductLineFingerprint(productOptionItem, productBundle) {
63
- const optArr = Array.isArray(productOptionItem) ? productOptionItem : [];
64
- const normalizedOpts = optArr.map((item) => ({
65
- option_group_item_id: Number(item == null ? void 0 : item.option_group_item_id) || 0,
66
- option_group_id: Number(item == null ? void 0 : item.option_group_id) || 0,
67
- num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
68
- price: String((item == null ? void 0 : item.add_price) ?? (item == null ? void 0 : item.price) ?? "")
69
- })).sort((p, q) => {
70
- if (p.option_group_item_id !== q.option_group_item_id) {
71
- return p.option_group_item_id - q.option_group_item_id;
72
- }
73
- if (p.option_group_id !== q.option_group_id)
74
- return p.option_group_id - q.option_group_id;
75
- if (p.num !== q.num)
76
- return p.num - q.num;
77
- return p.price.localeCompare(q.price);
78
- });
63
+ const normalizeOptions = (options) => {
64
+ const optionArr = Array.isArray(options) ? options : [];
65
+ return optionArr.map((item) => ({
66
+ option_group_item_id: Number(item == null ? void 0 : item.option_group_item_id) || 0,
67
+ option_group_id: Number(item == null ? void 0 : item.option_group_id) || 0,
68
+ num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
69
+ price: String((item == null ? void 0 : item.add_price) ?? (item == null ? void 0 : item.price) ?? "")
70
+ })).sort((p, q) => {
71
+ if (p.option_group_item_id !== q.option_group_item_id) {
72
+ return p.option_group_item_id - q.option_group_item_id;
73
+ }
74
+ if (p.option_group_id !== q.option_group_id) {
75
+ return p.option_group_id - q.option_group_id;
76
+ }
77
+ if (p.num !== q.num)
78
+ return p.num - q.num;
79
+ return p.price.localeCompare(q.price);
80
+ });
81
+ };
82
+ const normalizedOpts = normalizeOptions(productOptionItem);
79
83
  const bundleArr = Array.isArray(productBundle) ? productBundle : [];
80
84
  const normalizedBundles = bundleArr.map((item) => ({
81
85
  bundle_id: Number((item == null ? void 0 : item.bundle_id) ?? (item == null ? void 0 : item.id) ?? 0) || 0,
82
86
  product_id: Number(item == null ? void 0 : item.product_id) || 0,
83
- num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1
87
+ product_variant_id: Number((item == null ? void 0 : item.bundle_variant_id) ?? (item == null ? void 0 : item.product_variant_id)) || 0,
88
+ num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
89
+ option: normalizeOptions(item == null ? void 0 : item.option)
84
90
  })).sort((p, q) => {
85
91
  if (p.bundle_id !== q.bundle_id)
86
92
  return p.bundle_id - q.bundle_id;
87
93
  if (p.product_id !== q.product_id)
88
94
  return p.product_id - q.product_id;
89
- return p.num - q.num;
95
+ if (p.product_variant_id !== q.product_variant_id) {
96
+ return p.product_variant_id - q.product_variant_id;
97
+ }
98
+ if (p.num !== q.num)
99
+ return p.num - q.num;
100
+ return JSON.stringify(p.option).localeCompare(JSON.stringify(q.option));
90
101
  });
91
102
  return JSON.stringify([normalizedOpts, normalizedBundles]);
92
103
  }
@@ -29,6 +29,7 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
29
29
  private addTimeProductsCatalog;
30
30
  private orderCustomerDiscountRefreshInFlight;
31
31
  private orderCustomerDiscountRefreshCustomerId;
32
+ private orderCustomerPromotionRefreshInFlight;
32
33
  private loadOpenDataConfigInFlight;
33
34
  /**
34
35
  * 在 BaseSales 默认模块清单(products/order/salesSummary/schedule)基础上追加 customer + bookingContext。
@@ -63,6 +64,8 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
63
64
  scanPromotionCode(code: string, customerId?: number): Promise<BaseSalesScanCodeResult>;
64
65
  private hydrateOrderCustomerFromScanDiscounts;
65
66
  private getDiscountCustomerId;
67
+ resolveCustomerById(customerId: string | number): Promise<ICustomer | null>;
68
+ /** 兼容既有优惠码扫码逻辑。 */
66
69
  private resolveCustomerByDiscountCustomerId;
67
70
  private findCustomerById;
68
71
  private normalizeCustomer;
@@ -282,7 +285,10 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
282
285
  restoreOrder(): Promise<import("../../modules/Order/types").OrderTempOrder>;
283
286
  /** 内部:基于 OrderModule 当前状态构造一份 OrderCustomerChangePayload。 */
284
287
  private buildOrderCustomerPayload;
285
- private refreshDiscountConfigAfterOrderCustomerChange;
288
+ protected refreshDiscountConfigAfterOrderCustomerChange(customerId: number | null): Promise<void>;
289
+ protected waitForOrderCustomerPromotionRefresh(): Promise<void>;
290
+ protected prepareProductPriceQueryContext(customerId?: number | string): Promise<void>;
291
+ protected preferAuthoritativeProductQueryPrice(): boolean;
286
292
  /**
287
293
  * 获取客户分页信息
288
294
  * @returns 分页信息