@pisell/pisellos 2.2.267 → 2.2.269

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 (121) hide show
  1. package/dist/core/index.d.ts +2 -0
  2. package/dist/core/index.js +7 -0
  3. package/dist/model/strategy/adapter/promotion/adapter.d.ts +4 -0
  4. package/dist/model/strategy/adapter/promotion/adapter.js +23 -0
  5. package/dist/model/strategy/adapter/promotion/evaluator.d.ts +17 -0
  6. package/dist/model/strategy/adapter/promotion/evaluator.js +279 -6
  7. package/dist/model/strategy/adapter/promotion/examples.d.ts +86 -0
  8. package/dist/model/strategy/adapter/promotion/examples.js +99 -0
  9. package/dist/model/strategy/adapter/promotion/index.d.ts +1 -1
  10. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  11. package/dist/model/strategy/adapter/promotion/type.d.ts +90 -2
  12. package/dist/model/strategy/adapter/promotion/type.js +45 -0
  13. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +12 -10
  14. package/dist/modules/OpenData/index.d.ts +2 -0
  15. package/dist/modules/OpenData/index.js +8 -0
  16. package/dist/modules/Order/index.d.ts +33 -13
  17. package/dist/modules/Order/index.js +883 -524
  18. package/dist/modules/Order/payment-utils.d.ts +26 -0
  19. package/dist/modules/Order/payment-utils.js +225 -0
  20. package/dist/modules/Order/types.d.ts +56 -4
  21. package/dist/modules/Order/types.js +11 -0
  22. package/dist/modules/Order/utils/orderCollectionIdentity.js +7 -2
  23. package/dist/modules/Order/utils.js +10 -6
  24. package/dist/modules/Payment/index.d.ts +2 -0
  25. package/dist/modules/Payment/index.js +78 -49
  26. package/dist/modules/Payment/types.d.ts +104 -25
  27. package/dist/modules/Payment/types.js +17 -0
  28. package/dist/modules/Payment/walletpass.d.ts +38 -1
  29. package/dist/modules/Payment/walletpass.js +917 -114
  30. package/dist/modules/Rules/index.d.ts +3 -0
  31. package/dist/modules/Rules/index.js +146 -106
  32. package/dist/modules/SalesSummary/utils.js +7 -1
  33. package/dist/server/index.d.ts +16 -0
  34. package/dist/server/index.js +1940 -1043
  35. package/dist/server/modules/index.d.ts +1 -1
  36. package/dist/server/modules/order/index.d.ts +70 -4
  37. package/dist/server/modules/order/index.js +1816 -728
  38. package/dist/server/modules/order/types.d.ts +25 -3
  39. package/dist/server/modules/order/types.js +7 -1
  40. package/dist/solution/BaseSales/index.d.ts +118 -9
  41. package/dist/solution/BaseSales/index.js +1849 -480
  42. package/dist/solution/BaseSales/types.d.ts +55 -1
  43. package/dist/solution/BaseSales/types.js +2 -1
  44. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +3 -1
  45. package/dist/solution/BaseSales/utils/cartPromotion.js +90 -31
  46. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +6 -1
  47. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +6 -3
  48. package/dist/solution/BookingTicket/index.d.ts +8 -16
  49. package/dist/solution/BookingTicket/index.js +138 -52
  50. package/dist/solution/BookingTicket/types.d.ts +4 -0
  51. package/dist/solution/BookingTicket/utils/cartView.js +44 -32
  52. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +8 -0
  53. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +173 -83
  54. package/dist/solution/Sales/index.d.ts +2 -0
  55. package/dist/solution/Sales/index.js +26 -4
  56. package/dist/solution/ScanOrder/index.js +31 -20
  57. package/dist/solution/VenueBooking/index.d.ts +1 -0
  58. package/dist/solution/VenueBooking/index.js +30 -19
  59. package/dist/types/index.d.ts +1 -0
  60. package/dist/utils/payment-number.d.ts +9 -0
  61. package/dist/utils/payment-number.js +69 -0
  62. package/lib/core/index.d.ts +2 -0
  63. package/lib/core/index.js +4 -0
  64. package/lib/model/strategy/adapter/promotion/adapter.d.ts +4 -0
  65. package/lib/model/strategy/adapter/promotion/adapter.js +20 -0
  66. package/lib/model/strategy/adapter/promotion/evaluator.d.ts +17 -0
  67. package/lib/model/strategy/adapter/promotion/evaluator.js +235 -0
  68. package/lib/model/strategy/adapter/promotion/examples.d.ts +86 -0
  69. package/lib/model/strategy/adapter/promotion/examples.js +91 -0
  70. package/lib/model/strategy/adapter/promotion/index.d.ts +1 -1
  71. package/lib/model/strategy/adapter/promotion/type.d.ts +90 -2
  72. package/lib/model/strategy/adapter/promotion/type.js +2 -0
  73. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +3 -1
  74. package/lib/modules/OpenData/index.d.ts +2 -0
  75. package/lib/modules/OpenData/index.js +5 -0
  76. package/lib/modules/Order/index.d.ts +33 -13
  77. package/lib/modules/Order/index.js +306 -79
  78. package/lib/modules/Order/payment-utils.d.ts +26 -0
  79. package/lib/modules/Order/payment-utils.js +224 -0
  80. package/lib/modules/Order/types.d.ts +56 -4
  81. package/lib/modules/Order/utils/orderCollectionIdentity.js +3 -0
  82. package/lib/modules/Order/utils.js +11 -4
  83. package/lib/modules/Payment/index.d.ts +2 -0
  84. package/lib/modules/Payment/index.js +33 -12
  85. package/lib/modules/Payment/types.d.ts +104 -25
  86. package/lib/modules/Payment/types.js +1 -0
  87. package/lib/modules/Payment/walletpass.d.ts +38 -1
  88. package/lib/modules/Payment/walletpass.js +730 -21
  89. package/lib/modules/Rules/index.d.ts +3 -0
  90. package/lib/modules/Rules/index.js +54 -21
  91. package/lib/modules/SalesSummary/utils.js +5 -1
  92. package/lib/server/index.d.ts +16 -0
  93. package/lib/server/index.js +670 -18
  94. package/lib/server/modules/index.d.ts +1 -1
  95. package/lib/server/modules/order/index.d.ts +70 -4
  96. package/lib/server/modules/order/index.js +818 -69
  97. package/lib/server/modules/order/types.d.ts +25 -3
  98. package/lib/server/modules/order/types.js +1 -0
  99. package/lib/solution/BaseSales/index.d.ts +118 -9
  100. package/lib/solution/BaseSales/index.js +902 -38
  101. package/lib/solution/BaseSales/types.d.ts +55 -1
  102. package/lib/solution/BaseSales/types.js +2 -1
  103. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +3 -1
  104. package/lib/solution/BaseSales/utils/cartPromotion.js +63 -12
  105. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +6 -1
  106. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +3 -2
  107. package/lib/solution/BookingTicket/index.d.ts +8 -16
  108. package/lib/solution/BookingTicket/index.js +63 -9
  109. package/lib/solution/BookingTicket/types.d.ts +4 -0
  110. package/lib/solution/BookingTicket/utils/cartView.js +43 -30
  111. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +8 -0
  112. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +37 -5
  113. package/lib/solution/Sales/index.d.ts +2 -0
  114. package/lib/solution/Sales/index.js +20 -6
  115. package/lib/solution/ScanOrder/index.js +8 -0
  116. package/lib/solution/VenueBooking/index.d.ts +1 -0
  117. package/lib/solution/VenueBooking/index.js +8 -0
  118. package/lib/types/index.d.ts +1 -0
  119. package/lib/utils/payment-number.d.ts +9 -0
  120. package/lib/utils/payment-number.js +64 -0
  121. package/package.json +1 -1
@@ -3,6 +3,7 @@ import { OrderModule, ProductList, QuotationModule, SalesSummaryModule, ScanOrde
3
3
  import type { ScanOrderLogInput as BaseSalesLogInput, ScanOrderLoggerProviderConfig as BaseSalesLoggerProviderConfig, ScanOrderLoggerProviderType as BaseSalesLoggerProviderType } from '../../modules/ScanOrderLogger/types';
4
4
  import type { QuantityCheckResult, QuantityLimitResult } from '../../model/strategy/adapter/itemRule';
5
5
  import type { QuotationCustomerScopeInfo } from '../../modules/Quotation/types';
6
+ import type { PaymentMethod } from '../../modules/Payment/types';
6
7
  /**
7
8
  * BaseSales 流程 hook 后缀。
8
9
  * 完整事件名应由当前模块名动态拼接,避免多个实例共享固定事件 key。
@@ -16,11 +17,62 @@ export declare const BaseSalesHookNames: {
16
17
  readonly onPaymentSyncStart: "onPaymentSyncStart";
17
18
  readonly onPaymentSyncEnd: "onPaymentSyncEnd";
18
19
  readonly onDepositPaymentSyncEnd: "onDepositPaymentSyncEnd";
20
+ readonly onWalletAssetsStateChanged: "onWalletAssetsStateChanged";
19
21
  };
20
22
  export type BaseSalesHookName = typeof BaseSalesHookNames[keyof typeof BaseSalesHookNames];
21
23
  export declare function createBaseSalesHook(moduleName: string, hookName: BaseSalesHookName): string;
22
24
  export type BaseSalesStatus = 'idle' | 'initializing' | 'ready' | 'error';
23
25
  export type BaseSalesPaymentStatus = 'paid' | 'partially_paid' | 'unpaid' | 'payment_processing' | string;
26
+ export interface BaseSalesCashPaymentConfig {
27
+ available: boolean;
28
+ paymentMethod?: PaymentMethod;
29
+ amountDue: number;
30
+ recommendedAmounts: number[];
31
+ }
32
+ export interface BaseSalesPayCashParams {
33
+ /** 实际计入订单的现金支付金额(部分支付时小于待付金额)。 */
34
+ amount: number;
35
+ /** 顾客实际交付现金;可大于 amount。 */
36
+ actualPaidAmount?: number;
37
+ changeAmount?: number;
38
+ roundingAmount?: number;
39
+ }
40
+ export interface BaseSalesPayCashResult {
41
+ payment: Record<string, any>;
42
+ payments: Record<string, any>[];
43
+ syncResult: Record<string, any>;
44
+ isOrderFullyPaid: boolean;
45
+ }
46
+ export interface BaseSalesCommitPaymentMethodParams {
47
+ /** 支付方式 id;与 paymentMethodCode 至少传一个。 */
48
+ paymentMethodId?: number | string;
49
+ /** 支付方式 code,大小写不敏感。 */
50
+ paymentMethodCode?: string;
51
+ /** 本次计入订单的支付金额。 */
52
+ amount: number;
53
+ /** 支付设备返回的原始金额;缺省时与 amount 相同。 */
54
+ originAmount?: number;
55
+ /** 支付设备返回的实际手续费配置。 */
56
+ serviceCharge?: {
57
+ amount?: string | number;
58
+ percentage?: string | number;
59
+ } | null;
60
+ /** 支付设备返回的唯一流水号等扩展信息。 */
61
+ metadata?: Record<string, any>;
62
+ /** 少数支付方式需要透传的额外 payment 字段。 */
63
+ paymentData?: Record<string, any>;
64
+ }
65
+ export interface BaseSalesCommitPaymentMethodResult {
66
+ payment: Record<string, any>;
67
+ payments: Record<string, any>[];
68
+ syncResult: Record<string, any>;
69
+ isOrderFullyPaid: boolean;
70
+ }
71
+ export interface BaseSalesConfirmWalletPaymentResult<T = Record<string, any>> {
72
+ submitResult: T;
73
+ payments: Record<string, any>[];
74
+ isOrderFullyPaid: boolean;
75
+ }
24
76
  export interface BaseSalesEntryContext {
25
77
  biz: string;
26
78
  mode: 'scan' | 'append';
@@ -387,8 +439,10 @@ export interface BaseSalesAddLogParams extends BaseSalesLogInput {
387
439
  /** BaseSales.scanPromotionCode 对外的轻量结果(UI 可用 getDiscountList 或 scannedDiscountList 补 Holder) */
388
440
  export interface BaseSalesScanCodeResult {
389
441
  isAvailable: boolean;
442
+ /** 卡券已加入 Promotion;false 时可能是无效、过期或已使用。 */
443
+ isAccepted?: boolean;
390
444
  type?: 'server' | string;
391
445
  unavailableReason?: 'time_limit' | string;
392
- /** batchSearch 原始结果;isAvailable=false 时 discountList 可能未写入 store,Holder 补全需读此字段 */
446
+ /** batchSearch 原始结果;Holder 补全等流程可从这里读取原始卡券。 */
393
447
  scannedDiscountList?: Discount[];
394
448
  }
@@ -31,7 +31,8 @@ var BaseSalesHookNames = {
31
31
  onCartValidationChanged: "onCartValidationChanged",
32
32
  onPaymentSyncStart: "onPaymentSyncStart",
33
33
  onPaymentSyncEnd: "onPaymentSyncEnd",
34
- onDepositPaymentSyncEnd: "onDepositPaymentSyncEnd"
34
+ onDepositPaymentSyncEnd: "onDepositPaymentSyncEnd",
35
+ onWalletAssetsStateChanged: "onWalletAssetsStateChanged"
35
36
  };
36
37
  function createBaseSalesHook(moduleName, hookName) {
37
38
  return `${moduleName}:${hookName}`;
@@ -78,6 +78,7 @@ export interface CartPromotionEvaluator {
78
78
  }>;
79
79
  hasApplicableStrategy: boolean;
80
80
  }>;
81
+ getStrategyConfigs?: () => unknown[];
81
82
  }
82
83
  /** 单个赠品减量项 */
83
84
  export interface GiftReduceItem {
@@ -267,7 +268,8 @@ export declare function appendPromotionTags<T extends Record<string, any>>(produ
267
268
  * 处理购物车的促销计算与赠品差异化。
268
269
  *
269
270
  * 流程:
270
- * 1. 分离 main / gift / edit-product 三类(带 `booking_id` 的编辑态商品不参与促销)
271
+ * 1. 分离 main / gift / edit-product 三类(普通促销下带 `booking_id` 的编辑态商品不参与;
272
+ * 配置 ITEM_REWARD 时,编辑态商品也参与商品奖励重算)
271
273
  * 2. 转 PromotionProduct 喂评估器 → 拿到 `evaluateCartWithPricing` 与 `getProductsApplicableStrategies`
272
274
  * 3. 把 PricedProduct 映射回 OrderProduct 形态:
273
275
  * - 参与促销的,写 `metadata._promotion` + 调整 `selling_price` / `main_product_selling_price`
@@ -77,19 +77,43 @@ function getOrderProductOriginalPriceForPromotion(product) {
77
77
  }
78
78
  return getOrderProductBasePrice(product);
79
79
  }
80
- function updatePromotionDiscountList(item, promotionDiscount) {
80
+ var X_ITEMS_FOR_Y_PRICE = "X_ITEMS_FOR_Y_PRICE";
81
+ var ITEM_REWARD = "ITEM_REWARD";
82
+ function isItemRewardPromotionDiscountItem(item) {
83
+ var _a, _b;
84
+ return (item == null ? void 0 : item.type) === "product" && ((_a = item == null ? void 0 : item.metadata) == null ? void 0 : _a.source) === "promotion" && ((_b = item == null ? void 0 : item.metadata) == null ? void 0 : _b.actionType) === ITEM_REWARD;
85
+ }
86
+ function isManagedPromotionDiscountItem(item) {
87
+ if ((item == null ? void 0 : item.type) === "promotion")
88
+ return true;
89
+ return isItemRewardPromotionDiscountItem(item);
90
+ }
91
+ function wasInPromotionBeforeEvaluation(item) {
92
+ var _a, _b;
93
+ if (((_b = (_a = item == null ? void 0 : item.metadata) == null ? void 0 : _a._promotion) == null ? void 0 : _b.inPromotion) === true)
94
+ return true;
95
+ return Array.isArray(item == null ? void 0 : item.discount_list) && item.discount_list.some(isItemRewardPromotionDiscountItem);
96
+ }
97
+ function updatePromotionDiscountList(item, promotionDiscount, options) {
81
98
  let discountList = Array.isArray(item.discount_list) ? [...item.discount_list] : [];
82
- discountList = discountList.filter((d) => (d == null ? void 0 : d.type) !== "promotion");
99
+ discountList = discountList.filter((d) => !isManagedPromotionDiscountItem(d));
83
100
  if (promotionDiscount) {
101
+ const discountType = (options == null ? void 0 : options.discountType) || "promotion";
84
102
  discountList.push({
85
- type: "promotion",
103
+ type: discountType,
86
104
  amount: promotionDiscount.amount,
87
105
  discount: {
88
106
  original_amount: promotionDiscount.original_amount,
89
107
  fixed_amount: promotionDiscount.fixed_amount,
90
108
  title: promotionDiscount.title,
91
109
  resource_id: ""
92
- }
110
+ },
111
+ ...discountType === "product" ? {
112
+ metadata: {
113
+ source: "promotion",
114
+ actionType: (options == null ? void 0 : options.actionType) || ITEM_REWARD
115
+ }
116
+ } : {}
93
117
  });
94
118
  }
95
119
  if (discountList.length > 0) {
@@ -98,7 +122,15 @@ function updatePromotionDiscountList(item, promotionDiscount) {
98
122
  delete item.discount_list;
99
123
  }
100
124
  }
101
- var X_ITEMS_FOR_Y_PRICE = "X_ITEMS_FOR_Y_PRICE";
125
+ function evaluatorHasActionType(evaluator, actionType) {
126
+ var _a;
127
+ const configs = (_a = evaluator == null ? void 0 : evaluator.getStrategyConfigs) == null ? void 0 : _a.call(evaluator);
128
+ if (!Array.isArray(configs))
129
+ return false;
130
+ return configs.some(
131
+ (config) => ((config == null ? void 0 : config.actions) || []).some((action) => (action == null ? void 0 : action.type) === actionType)
132
+ );
133
+ }
102
134
  function applyPromoUnitPriceToLine(item, input) {
103
135
  const metadata = item.metadata || (item.metadata = {});
104
136
  const optionSum = (0, import_utils.sumOptionUnitPrice)((0, import_utils.getProductSkuOptions)(item)).toNumber();
@@ -125,6 +157,9 @@ function applyPromoUnitPriceToLine(item, input) {
125
157
  amount: discountAmount,
126
158
  original_amount: catalogSource,
127
159
  fixed_amount: discountAmount
160
+ }, {
161
+ actionType: input.actionType,
162
+ discountType: input.actionType === ITEM_REWARD ? "product" : "promotion"
128
163
  });
129
164
  } else {
130
165
  updatePromotionDiscountList(item, null);
@@ -399,14 +434,13 @@ function buildConsolidateKeyForPromotion(item) {
399
434
  ].join("#");
400
435
  }
401
436
  function consolidateMainProductsForPromotion(entries) {
402
- var _a, _b;
403
437
  if (!Array.isArray(entries) || entries.length <= 1) {
404
438
  return [...entries || []];
405
439
  }
406
440
  const groups = /* @__PURE__ */ new Map();
407
441
  for (const { item, originalListIndex } of entries) {
408
442
  const key = buildConsolidateKeyForPromotion(item);
409
- const wasInPromotion = ((_b = (_a = item == null ? void 0 : item.metadata) == null ? void 0 : _a._promotion) == null ? void 0 : _b.inPromotion) === true;
443
+ const wasInPromotion = wasInPromotionBeforeEvaluation(item);
410
444
  const existing = groups.get(key);
411
445
  if (!existing) {
412
446
  const mergedItem = (0, import_lodash_es.cloneDeep)(item);
@@ -472,6 +506,9 @@ function splitXItemsPromoLinesByGroup(lines, strategyActionMap) {
472
506
  function generateNumericId() {
473
507
  return Math.floor(Math.random() * 1e9) + Date.now();
474
508
  }
509
+ function isBookingEditProductLine(product) {
510
+ return (product == null ? void 0 : product.booking_id) !== void 0 && (product == null ? void 0 : product.booking_id) !== null && product.booking_id !== 0 && product.booking_id !== "0";
511
+ }
475
512
  function convertToPromotionProduct(product, index) {
476
513
  const metadata = (product == null ? void 0 : product.metadata) || {};
477
514
  const numericId = generateNumericId();
@@ -484,6 +521,7 @@ function convertToPromotionProduct(product, index) {
484
521
  price: getOrderProductOriginalPriceForPromotion(product),
485
522
  quantity: Number((product == null ? void 0 : product.num) ?? 1) || 1,
486
523
  bundle: getProductBundleForEvaluator(product),
524
+ _promotionOnlyForItemReward: isBookingEditProductLine(product),
487
525
  _originalItem: originalItem,
488
526
  _originalId: getOrderProductUid(product),
489
527
  _originalIndex: index
@@ -585,6 +623,9 @@ function resolveStrategyRequiredQuantity(matched) {
585
623
  if (matched.actionType === "BUY_X_GET_Y_FREE") {
586
624
  return Number(detail.buyQuantity) || 1;
587
625
  }
626
+ if (matched.actionType === "ITEM_REWARD") {
627
+ return Number(detail.triggerQuantity) || 1;
628
+ }
588
629
  return 1;
589
630
  }
590
631
  function resolveStrategyEligibleProducts(matched) {
@@ -695,6 +736,10 @@ function processCartPromotion(list, evaluator, options) {
695
736
  };
696
737
  if (!list || list.length === 0 || !evaluator)
697
738
  return empty;
739
+ const shouldIncludeEditProductsForItemReward = evaluatorHasActionType(
740
+ evaluator,
741
+ ITEM_REWARD
742
+ );
698
743
  const mainProductsWithIndex = [];
699
744
  const existingGiftsWithIndex = [];
700
745
  const editProductsWithIndex = [];
@@ -705,8 +750,12 @@ function processCartPromotion(list, evaluator, options) {
705
750
  editProductsWithIndex.push({ item, originalListIndex: i });
706
751
  } else if (giftInfo) {
707
752
  existingGiftsWithIndex.push({ item, originalListIndex: i });
708
- } else if ((item == null ? void 0 : item.booking_id) !== void 0 && (item == null ? void 0 : item.booking_id) !== null && item.booking_id !== 0 && item.booking_id !== "0") {
709
- editProductsWithIndex.push({ item, originalListIndex: i });
753
+ } else if (isBookingEditProductLine(item)) {
754
+ if (shouldIncludeEditProductsForItemReward) {
755
+ mainProductsWithIndex.push({ item, originalListIndex: i });
756
+ } else {
757
+ editProductsWithIndex.push({ item, originalListIndex: i });
758
+ }
710
759
  } else {
711
760
  mainProductsWithIndex.push({ item, originalListIndex: i });
712
761
  }
@@ -749,7 +798,8 @@ function processCartPromotion(list, evaluator, options) {
749
798
  name: ((_a2 = item == null ? void 0 : item.metadata) == null ? void 0 : _a2.product_name) || (item == null ? void 0 : item.title) || "",
750
799
  price: getOrderProductBasePrice(item),
751
800
  quantity: Number((item == null ? void 0 : item.num) ?? 1) || 1,
752
- bundle: getProductBundleForEvaluator(item)
801
+ bundle: getProductBundleForEvaluator(item),
802
+ _promotionOnlyForItemReward: isBookingEditProductLine(item)
753
803
  };
754
804
  });
755
805
  for (let i = 0; i < consolidatedMainProductsWithIndex.length; i++) {
@@ -818,7 +868,7 @@ function processCartPromotion(list, evaluator, options) {
818
868
  const metadata = newItem.metadata || {};
819
869
  const sourceCartNum = (0, import_utils2.getSafeProductNum)(originalItem.num);
820
870
  newItem._sourceCartNum = sourceCartNum;
821
- const wasInPromotion = ((_a = metadata._promotion) == null ? void 0 : _a.inPromotion) === true || originalItem._wasInPromotionBeforeConsolidate === true;
871
+ const wasInPromotion = wasInPromotionBeforeEvaluation(originalItem) || originalItem._wasInPromotionBeforeConsolidate === true;
822
872
  delete metadata._promotion;
823
873
  if (Array.isArray(newItem.product_bundle)) {
824
874
  for (const b of newItem.product_bundle) {
@@ -835,7 +885,8 @@ function processCartPromotion(list, evaluator, options) {
835
885
  applyPromoUnitPriceToLine(newItem, {
836
886
  promoUnitPrice,
837
887
  originalBasePrice,
838
- strategyTitle: (_b = priced.strategyMetadata) == null ? void 0 : _b.name
888
+ strategyTitle: (_a = priced.strategyMetadata) == null ? void 0 : _a.name,
889
+ actionType: priced.strategyId ? (_b = strategyActionMap.get(priced.strategyId)) == null ? void 0 : _b.actionType : void 0
839
890
  });
840
891
  } else {
841
892
  const optionSum = (0, import_utils.sumOptionUnitPrice)((0, import_utils.getProductSkuOptions)(newItem)).toNumber();
@@ -82,8 +82,13 @@ export interface ISalesBooking {
82
82
  metadata?: Record<string, any> | null;
83
83
  [key: string]: any;
84
84
  }
85
+ export interface ISalesPaymentMetadata extends Record<string, any> {
86
+ /** 刷卡机设备快照;OS 不解析内部字段 */
87
+ card_reader_snapshot?: Record<string, unknown>;
88
+ }
85
89
  export interface ISalesPayment {
86
90
  order_payment_id?: number | null;
91
+ payment_number?: string;
87
92
  custom_payment_id?: number;
88
93
  code?: string;
89
94
  name?: string;
@@ -95,7 +100,7 @@ export interface ISalesPayment {
95
100
  status?: string;
96
101
  payment_time?: string | null;
97
102
  service_charge?: Record<string, any> | null;
98
- metadata?: Record<string, any> | null;
103
+ metadata?: ISalesPaymentMetadata | null;
99
104
  [key: string]: any;
100
105
  }
101
106
  export interface ISalesSurcharge {
@@ -132,14 +132,15 @@ function transformBaseProductToOrderProduct(params) {
132
132
  ...callbackOrigin || {}
133
133
  };
134
134
  const matchedVariant = findVariantById(callbackOrigin, productVariantId) ?? findVariantById(sourceProduct, productVariantId) ?? findVariantById(origin, productVariantId);
135
+ const hasPriceOverride = payload.price_override !== void 0 && payload.price_override !== null && payload.price_override !== "";
136
+ const catalogSourcePrice = (matchedVariant == null ? void 0 : matchedVariant.price) ?? (matchedVariant == null ? void 0 : matchedVariant.base_price) ?? (origin == null ? void 0 : origin.price) ?? (origin == null ? void 0 : origin.base_price) ?? (sourceProduct == null ? void 0 : sourceProduct.price) ?? (sourceProduct == null ? void 0 : sourceProduct.selling_price) ?? (sourceProduct == null ? void 0 : sourceProduct.base_price);
135
137
  const sourceProductPrice = toPriceString(
136
- payload.price ?? payload.selling_price ?? (matchedVariant == null ? void 0 : matchedVariant.price) ?? (matchedVariant == null ? void 0 : matchedVariant.base_price) ?? (origin == null ? void 0 : origin.price) ?? (origin == null ? void 0 : origin.base_price) ?? (sourceProduct == null ? void 0 : sourceProduct.price) ?? (sourceProduct == null ? void 0 : sourceProduct.selling_price) ?? (sourceProduct == null ? void 0 : sourceProduct.base_price),
138
+ hasPriceOverride ? catalogSourcePrice ?? payload.price ?? payload.selling_price : payload.price ?? payload.selling_price ?? catalogSourcePrice,
137
139
  "0.00"
138
140
  );
139
141
  const explicitLineTotal = payload.line_total ?? payload.total ?? ((_c = payload._extend) == null ? void 0 : _c.total);
140
142
  const hasExplicitLineTotal = Number.isFinite(Number(explicitLineTotal));
141
143
  const lineCompositeTotal = toPriceString(explicitLineTotal, sourceProductPrice);
142
- const hasPriceOverride = payload.price_override !== void 0 && payload.price_override !== null && payload.price_override !== "";
143
144
  const productOptionItem = (0, import_utils.normalizeProductSkuOptions)(
144
145
  normalizeOptionItems(
145
146
  ((_d = payload.product_sku) == null ? void 0 : _d.option) ?? payload.option ?? payload.options ?? payload.product_option_item
@@ -46,6 +46,8 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
46
46
  private getIdGeneratorPlugin;
47
47
  private loadOpenDataConfig;
48
48
  getOpenData(): Promise<OpenDataConfig | null>;
49
+ /** 清除店铺级 OpenData;下一次 getOpenData 时会重新拉取。 */
50
+ clearOpenDataCache(): void;
49
51
  private getShortNumberOrDeviceId;
50
52
  private configureIdGeneratorFromOpenData;
51
53
  private normalizePositiveInteger;
@@ -189,10 +191,12 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
189
191
  */
190
192
  getCustomer(): ICustomer | null;
191
193
  /**
192
- * 通过 ids 获取商品列表(不加载到模块中)
193
- * @returns 商品列表
194
+ * 通过 ids 获取商品列表;默认只读取内存,显式开启 loadMissing 时按当前订单上下文补查缺失商品。
195
+ * 补查直接写入 ProductList,不更新 BookingTicket.productCatalog,避免当前商品列表被查询子集覆盖。
194
196
  */
195
- getProductByIds(ids: number[]): Promise<ProductData[] | undefined>;
197
+ getProductByIds(ids: number[], options?: {
198
+ loadMissing?: boolean;
199
+ }): Promise<ProductData[]>;
196
200
  /**
197
201
  * 获取日程时间段点
198
202
  * @param params 参数
@@ -441,19 +445,7 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
441
445
  * 加车两阶段主决策(规格弹窗 / 资源编辑 / 直接加车)。
442
446
  * 与 ticketBooking `handleSelectProduct` + `handleBooking4Service` 等价。
443
447
  */
444
- decideAddProduct(item: any, options?: Partial<AddProductDecideContext>): {
445
- action: "reloadCatalog";
446
- } | {
447
- action: "add";
448
- cacheItem: any;
449
- } | {
450
- action: "requiresDetail";
451
- payload: AddProductRequiresDetailPayload;
452
- } | {
453
- action: "requiresBookingEdit";
454
- cacheItem: any;
455
- payload: import("./utils/addProductDecision").AddProductRequiresBookingEditPayload;
456
- };
448
+ decideAddProduct(item: any, options?: Partial<AddProductDecideContext>): import("./utils/addProductDecision").AddProductDecision;
457
449
  /** 规格弹窗 callback 后的第二段决策。 */
458
450
  decideAfterDetail(cacheItem: any, options?: Partial<AddProductDecideContext>): import("./utils/addProductDecision").AddProductDecision;
459
451
  /**
@@ -219,8 +219,14 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
219
219
  }
220
220
  }
221
221
  async getOpenData() {
222
+ return this.loadOpenDataConfig();
223
+ }
224
+ /** 清除店铺级 OpenData;下一次 getOpenData 时会重新拉取。 */
225
+ clearOpenDataCache() {
222
226
  var _a;
223
- return (_a = this.store.openData) == null ? void 0 : _a.getOpenData();
227
+ this.loadOpenDataConfigInFlight = null;
228
+ this.otherParams.openData = null;
229
+ (_a = this.store.openData) == null ? void 0 : _a.clearCache();
224
230
  }
225
231
  async getShortNumberOrDeviceId() {
226
232
  const getAsyncIotDeviceInfo = this.getAppData("async_iot_device_info");
@@ -233,6 +239,7 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
233
239
  return String(this.getAppData("device_id") || 0).slice(-2);
234
240
  }
235
241
  async configureIdGeneratorFromOpenData() {
242
+ this.setPaymentNumberDevicePrefix(null);
236
243
  let openDataConfig = null;
237
244
  try {
238
245
  openDataConfig = await this.loadOpenDataConfig();
@@ -264,6 +271,7 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
264
271
  const resetReceiptSequenceDaily = typeof (openDataConfig == null ? void 0 : openDataConfig["sale.short_number_daily_reset"]) === "boolean" ? openDataConfig == null ? void 0 : openDataConfig["sale.short_number_daily_reset"] : false;
265
272
  const operatingDayBoundary = this.getAppData("operating_day_boundary");
266
273
  const deviceId = await this.getShortNumberOrDeviceId();
274
+ this.setPaymentNumberDevicePrefix(deviceId);
267
275
  const businessCode = this.getBookingTicketBusinessCode();
268
276
  if (!businessCode) {
269
277
  console.warn("[BookingTicket] businessCode 缺失,跳过 idGenerator 配置");
@@ -360,6 +368,7 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
360
368
  }
361
369
  return {
362
370
  isAvailable: raw.isAvailable,
371
+ ...raw.isAccepted !== void 0 ? { isAccepted: raw.isAccepted } : {},
363
372
  type: raw.type,
364
373
  unavailableReason: raw.unavailableReason,
365
374
  scannedDiscountList: raw.scannedDiscountList
@@ -714,11 +723,53 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
714
723
  return this.getOrderCustomerSnapshot();
715
724
  }
716
725
  /**
717
- * 通过 ids 获取商品列表(不加载到模块中)
718
- * @returns 商品列表
726
+ * 通过 ids 获取商品列表;默认只读取内存,显式开启 loadMissing 时按当前订单上下文补查缺失商品。
727
+ * 补查直接写入 ProductList,不更新 BookingTicket.productCatalog,避免当前商品列表被查询子集覆盖。
719
728
  */
720
- async getProductByIds(ids) {
721
- return this.store.products.getProductByIds(ids);
729
+ async getProductByIds(ids, options) {
730
+ var _a, _b;
731
+ const normalizedIds = Array.from(
732
+ new Set(
733
+ (ids || []).map((id) => Number(id)).filter((id) => Number.isFinite(id))
734
+ )
735
+ );
736
+ const localProducts = await this.store.products.getProductByIds(normalizedIds) || [];
737
+ if (!(options == null ? void 0 : options.loadMissing)) {
738
+ return localProducts;
739
+ }
740
+ const localProductIds = new Set(
741
+ localProducts.map((product) => Number(product.id))
742
+ );
743
+ const missingIds = normalizedIds.filter(
744
+ (id) => !localProductIds.has(id)
745
+ );
746
+ if (!missingIds.length) {
747
+ return localProducts;
748
+ }
749
+ const rawBookingDate = this.getBookingDate();
750
+ const bookingDate = rawBookingDate && (0, import_dayjs.default)(rawBookingDate).isValid() ? (0, import_dayjs.default)(rawBookingDate) : null;
751
+ const customerId = Number(
752
+ ((_a = this.getOrderCustomerSnapshot()) == null ? void 0 : _a.id) ?? ((_b = this.getOrderCustomer()) == null ? void 0 : _b.customer_id)
753
+ );
754
+ const loadedProducts = await this.store.products.loadProducts({
755
+ product_ids: missingIds,
756
+ with_count: ["bundleGroup", "optionGroup"],
757
+ with_schedule: 1,
758
+ cacheId: this.cacheId,
759
+ ...bookingDate ? {
760
+ schedule_date: bookingDate.format("YYYY-MM-DD"),
761
+ schedule_datetime: bookingDate.format("YYYY-MM-DD HH:mm:ss")
762
+ } : {},
763
+ ...Number.isFinite(customerId) && customerId > 0 ? { customer_id: customerId } : {}
764
+ });
765
+ const productById = /* @__PURE__ */ new Map();
766
+ [...localProducts, ...loadedProducts || []].forEach((product) => {
767
+ const productId = Number(product.id);
768
+ if (Number.isFinite(productId)) {
769
+ productById.set(productId, product);
770
+ }
771
+ });
772
+ return normalizedIds.map((id) => productById.get(id)).filter((product) => Boolean(product));
722
773
  }
723
774
  /**
724
775
  * 获取日程时间段点
@@ -1153,7 +1204,8 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
1153
1204
  }
1154
1205
  /** 读取当前 booking config。 */
1155
1206
  getBookingConfig() {
1156
- return this.store.bookingContext.getBookingConfig();
1207
+ var _a;
1208
+ return (_a = this.store.bookingContext) == null ? void 0 : _a.getBookingConfig();
1157
1209
  }
1158
1210
  /** 写入资源全集(来源:`/form/schedule`),同步派生 resourcesOriginMap。 */
1159
1211
  setResources(resources) {
@@ -1389,7 +1441,9 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
1389
1441
  decideAddProduct: (item, options) => this.decideAddProduct(item, options),
1390
1442
  decideAfterDetail: (cacheItem, options) => this.decideAfterDetail(cacheItem, options),
1391
1443
  transformDetailToProductAndBooking: (input) => this.transformDetailToProductAndBooking(input),
1392
- addProductToOrder: (product, booking) => this.addProductToOrder(product, booking)
1444
+ addProductToOrder: (product, booking) => this.addProductToOrder(product, booking),
1445
+ refreshWalletAssets: (params) => this.refreshWalletAssets(params),
1446
+ scanWalletAsset: (scanCode) => this.scanWalletAsset(scanCode)
1393
1447
  },
1394
1448
  formatted,
1395
1449
  bridge
@@ -1526,8 +1580,8 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
1526
1580
  return result;
1527
1581
  }
1528
1582
  async setOtherParams(params, { cover = false } = {}) {
1529
- super.setOtherParams(params, { cover });
1530
- if (params == null ? void 0 : params.businessCode) {
1583
+ await super.setOtherParams(params, { cover });
1584
+ if ((params == null ? void 0 : params.businessCode) !== void 0 || (params == null ? void 0 : params.business_code) !== void 0) {
1531
1585
  await this.configureIdGeneratorFromOpenData();
1532
1586
  }
1533
1587
  }
@@ -118,12 +118,15 @@ export interface BookingTicketCartBookingView extends ISalesBooking {
118
118
  canAddTime: boolean;
119
119
  };
120
120
  }
121
+ export type BookingTicketCartLineView = BookingTicketCartBookingView | BookingTicketCartItemView;
121
122
  /**
122
123
  * BookingTicket 面向 UI 的购物车视图。
123
124
  */
124
125
  export interface BookingTicketCartView {
125
126
  bookings: BookingTicketCartBookingView[];
126
127
  items: BookingTicketCartItemView[];
128
+ /** 以 tempOrder.products 为唯一顺序真源的统一购物车行(旧到新)。 */
129
+ lines: BookingTicketCartLineView[];
127
130
  }
128
131
  /**
129
132
  * BookingTicket 面向 UI 的客户展示视图。
@@ -229,6 +232,7 @@ export interface GlobalScanHostBridge {
229
232
  */
230
233
  applyPromotionCode?: (code: string, customerId?: number) => Promise<{
231
234
  isAvailable?: boolean;
235
+ isAccepted?: boolean;
232
236
  type?: string;
233
237
  unavailableReason?: string;
234
238
  }>;
@@ -86,29 +86,27 @@ function isGiftProductLine(line) {
86
86
  function buildCartView(lines, bookings, options = {}) {
87
87
  const productLines = Array.isArray(lines) ? lines : [];
88
88
  const bookingList = Array.isArray(bookings) ? bookings : [];
89
+ const childBookings = bookingList.filter((booking) => booking.parent_id !== 0);
90
+ const bookingsByProductUid = indexBookingsByProductUid(childBookings);
89
91
  const linesByUid = indexProductsByUid(productLines);
90
92
  const addTimeLinesByBookingUid = indexAddTimeProductsByBookingUid(productLines);
91
- const claimedUids = /* @__PURE__ */ new Set();
92
93
  const claimedAddTimeLines = /* @__PURE__ */ new Set();
93
- const cartBookings = bookingList.filter((booking) => booking.parent_id !== 0).reduce((acc, booking) => {
94
- const uid = booking == null ? void 0 : booking.product_uid;
94
+ childBookings.forEach((booking) => {
95
+ const bookingUid = getBookingUid(booking);
96
+ if (!bookingUid)
97
+ return;
98
+ (addTimeLinesByBookingUid[bookingUid] || []).forEach((line) => {
99
+ claimedAddTimeLines.add(line);
100
+ });
101
+ });
102
+ const claimedBookings = /* @__PURE__ */ new Set();
103
+ const cartBookings = [];
104
+ const items = [];
105
+ const orderedLines = [];
106
+ const buildBookingView = (booking, product) => {
95
107
  const bookingUid = getBookingUid(booking);
96
- let product = null;
97
- if (uid !== void 0 && uid !== null && uid !== "") {
98
- const key = String(uid);
99
- if (!claimedUids.has(key)) {
100
- const line = linesByUid[key];
101
- if (isGiftProductLine(line))
102
- return acc;
103
- if (line) {
104
- claimedUids.add(key);
105
- product = line;
106
- }
107
- }
108
- }
109
108
  const addTimeProduct = bookingUid ? addTimeLinesByBookingUid[bookingUid] || [] : [];
110
- addTimeProduct.forEach((line) => claimedAddTimeLines.add(line));
111
- acc.push({
109
+ return {
112
110
  ...booking,
113
111
  _extend: {
114
112
  product,
@@ -122,21 +120,36 @@ function buildCartView(lines, bookings, options = {}) {
122
120
  shopOpeningHours: options.shopOpeningHours
123
121
  })
124
122
  }
125
- });
126
- return acc;
127
- }, []);
128
- const items = productLines.filter((line) => {
123
+ };
124
+ };
125
+ productLines.forEach((line) => {
129
126
  var _a;
130
- if (isGiftProductLine(line))
131
- return false;
132
- if (claimedAddTimeLines.has(line))
133
- return false;
127
+ if (isGiftProductLine(line) || claimedAddTimeLines.has(line))
128
+ return;
134
129
  const uid = (_a = line.metadata) == null ? void 0 : _a.unique_identification_number;
135
- if (uid === void 0 || uid === null || uid === "")
136
- return true;
137
- return !claimedUids.has(String(uid));
130
+ const matchedBooking = uid === void 0 || uid === null || uid === "" ? void 0 : (bookingsByProductUid[String(uid)] || []).find(
131
+ (booking) => !claimedBookings.has(booking)
132
+ );
133
+ if (matchedBooking) {
134
+ claimedBookings.add(matchedBooking);
135
+ const bookingView = buildBookingView(matchedBooking, line);
136
+ cartBookings.push(bookingView);
137
+ orderedLines.push(bookingView);
138
+ return;
139
+ }
140
+ items.push(line);
141
+ orderedLines.push(line);
142
+ });
143
+ childBookings.forEach((booking) => {
144
+ if (claimedBookings.has(booking))
145
+ return;
146
+ const uid = booking == null ? void 0 : booking.product_uid;
147
+ const linkedProduct = uid === void 0 || uid === null || uid === "" ? null : linesByUid[String(uid)] || null;
148
+ if (isGiftProductLine(linkedProduct))
149
+ return;
150
+ cartBookings.push(buildBookingView(booking, null));
138
151
  });
139
- return { bookings: cartBookings, items };
152
+ return { bookings: cartBookings, items, lines: orderedLines };
140
153
  }
141
154
  function isWalkInCustomer(customer) {
142
155
  if (!customer)
@@ -24,6 +24,14 @@ export interface GlobalScanApplyHost {
24
24
  cacheItem: any;
25
25
  };
26
26
  addProductToOrder(product: any, booking?: any): Promise<any>;
27
+ refreshWalletAssets?(params?: {
28
+ preserveSelection?: boolean;
29
+ }): Promise<any>;
30
+ scanWalletAsset?(code: string): Promise<{
31
+ type: 'walletCode' | 'normalCode';
32
+ asset?: any;
33
+ selected: boolean;
34
+ }>;
27
35
  }
28
36
  /**
29
37
  * 应用全局扫码格式化结果:设客户 / 加车 / 开单 / 弹窗链。