@pisell/pisellos 2.2.278 → 2.2.280

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 (188) hide show
  1. package/dist/modules/BaseModule.d.ts +1 -0
  2. package/dist/modules/BaseModule.js +24 -28
  3. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +7 -3
  4. package/dist/modules/BookingContext/utils/productExtend.js +5 -9
  5. package/dist/modules/BookingContext/utils/timeSlices.js +24 -3
  6. package/dist/modules/Customer/index.d.ts +4 -0
  7. package/dist/modules/Customer/index.js +91 -59
  8. package/dist/modules/Customer/types.d.ts +2 -0
  9. package/dist/modules/Discount/index.d.ts +1 -0
  10. package/dist/modules/Discount/index.js +40 -11
  11. package/dist/modules/Discount/types.d.ts +1 -0
  12. package/dist/modules/Holder/index.d.ts +48 -0
  13. package/dist/modules/Holder/index.js +640 -0
  14. package/dist/modules/Holder/types.d.ts +123 -0
  15. package/dist/modules/Holder/types.js +1 -0
  16. package/dist/modules/Holder/utils.d.ts +13 -0
  17. package/dist/modules/Holder/utils.js +34 -0
  18. package/dist/modules/OpenData/index.js +2 -2
  19. package/dist/modules/Order/index.d.ts +32 -4
  20. package/dist/modules/Order/index.js +868 -675
  21. package/dist/modules/Order/types.d.ts +22 -1
  22. package/dist/modules/Order/utils/discountProductLineIdentity.d.ts +45 -0
  23. package/dist/modules/Order/utils/discountProductLineIdentity.js +227 -0
  24. package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +15 -0
  25. package/dist/modules/Order/utils/orderCollectionIdentity.js +75 -0
  26. package/dist/modules/Order/utils.d.ts +10 -0
  27. package/dist/modules/Order/utils.js +71 -34
  28. package/dist/modules/Payment/index.d.ts +4 -0
  29. package/dist/modules/Payment/index.js +14 -4
  30. package/dist/modules/Payment/walletpass.js +21 -7
  31. package/dist/modules/Product/types.d.ts +1 -0
  32. package/dist/modules/ProductList/index.js +33 -13
  33. package/dist/modules/Quotation/index.d.ts +1 -1
  34. package/dist/modules/Quotation/index.js +2 -2
  35. package/dist/modules/ResourcePlanner/index.d.ts +24 -0
  36. package/dist/modules/ResourcePlanner/index.js +181 -0
  37. package/dist/modules/ResourcePlanner/planner.d.ts +14 -0
  38. package/dist/modules/ResourcePlanner/planner.js +1069 -0
  39. package/dist/modules/ResourcePlanner/types.d.ts +227 -0
  40. package/dist/modules/ResourcePlanner/types.js +1 -0
  41. package/dist/modules/Rules/index.js +153 -56
  42. package/dist/modules/SalesSummary/index.js +12 -13
  43. package/dist/modules/ScanOrderLogger/index.d.ts +2 -0
  44. package/dist/modules/ScanOrderLogger/index.js +15 -2
  45. package/dist/modules/ScanOrderLogger/providers/feishu.js +45 -27
  46. package/dist/modules/ScanOrderLogger/types.d.ts +1 -0
  47. package/dist/modules/Schedule/index.d.ts +3 -1
  48. package/dist/modules/Schedule/index.js +21 -16
  49. package/dist/modules/Summary/utils.js +38 -13
  50. package/dist/modules/index.d.ts +2 -0
  51. package/dist/modules/index.js +3 -1
  52. package/dist/plugins/window.d.ts +1 -0
  53. package/dist/server/index.d.ts +20 -0
  54. package/dist/server/index.js +1101 -909
  55. package/dist/server/modules/order/index.d.ts +2 -0
  56. package/dist/server/modules/order/index.js +261 -96
  57. package/dist/server/modules/order/types.d.ts +1 -1
  58. package/dist/solution/BaseSales/index.d.ts +20 -1
  59. package/dist/solution/BaseSales/index.js +989 -881
  60. package/dist/solution/BaseSales/utils/cartPromotion.js +19 -1
  61. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +6 -3
  62. package/dist/solution/BaseSales/utils.js +31 -20
  63. package/dist/solution/BookingByStep/index.d.ts +17 -2
  64. package/dist/solution/BookingByStep/index.js +294 -215
  65. package/dist/solution/BookingByStep/types.d.ts +2 -1
  66. package/dist/solution/BookingByStep/types.js +3 -1
  67. package/dist/solution/BookingByStep/utils/capacity.js +17 -1
  68. package/dist/solution/BookingByStep/utils/timeslots.d.ts +3 -1
  69. package/dist/solution/BookingByStep/utils/timeslots.js +19 -12
  70. package/dist/solution/BookingTicket/index.d.ts +21 -3
  71. package/dist/solution/BookingTicket/index.js +312 -226
  72. package/dist/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  73. package/dist/solution/BookingTicket/utils/scan/handleScan.js +10 -2
  74. package/dist/solution/ScanOrder/index.d.ts +26 -14
  75. package/dist/solution/ScanOrder/index.js +1142 -739
  76. package/dist/solution/ScanOrder/types.d.ts +21 -1
  77. package/dist/solution/ScanOrder/utils.d.ts +8 -0
  78. package/dist/solution/ScanOrder/utils.js +97 -45
  79. package/dist/solution/ShopDiscount/index.d.ts +1 -0
  80. package/dist/solution/ShopDiscount/index.js +125 -87
  81. package/dist/solution/UnifiedBookingSales/index.d.ts +204 -0
  82. package/dist/solution/UnifiedBookingSales/index.js +2211 -0
  83. package/dist/solution/UnifiedBookingSales/types.d.ts +150 -0
  84. package/dist/solution/UnifiedBookingSales/types.js +11 -0
  85. package/dist/solution/VenueBooking/index.d.ts +39 -11
  86. package/dist/solution/VenueBooking/index.js +1167 -841
  87. package/dist/solution/VenueBooking/types.d.ts +3 -0
  88. package/dist/solution/VenueBooking/utils/resource.js +1 -0
  89. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +4 -4
  90. package/dist/solution/VenueBooking/utils/slotMerge.js +10 -5
  91. package/dist/solution/index.d.ts +1 -0
  92. package/dist/solution/index.js +2 -1
  93. package/dist/types/index.d.ts +1 -0
  94. package/lib/model/strategy/adapter/promotion/index.js +0 -51
  95. package/lib/modules/BaseModule.d.ts +1 -0
  96. package/lib/modules/BaseModule.js +24 -37
  97. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +3 -0
  98. package/lib/modules/BookingContext/utils/productExtend.js +4 -3
  99. package/lib/modules/BookingContext/utils/timeSlices.js +18 -3
  100. package/lib/modules/Customer/index.d.ts +4 -0
  101. package/lib/modules/Customer/index.js +11 -0
  102. package/lib/modules/Customer/types.d.ts +2 -0
  103. package/lib/modules/Discount/index.d.ts +1 -0
  104. package/lib/modules/Discount/index.js +9 -0
  105. package/lib/modules/Discount/types.d.ts +1 -0
  106. package/lib/modules/Holder/index.d.ts +48 -0
  107. package/lib/modules/Holder/index.js +402 -0
  108. package/lib/modules/Holder/types.d.ts +123 -0
  109. package/lib/modules/Holder/types.js +17 -0
  110. package/lib/modules/Holder/utils.d.ts +13 -0
  111. package/lib/modules/Holder/utils.js +71 -0
  112. package/lib/modules/OpenData/index.js +1 -1
  113. package/lib/modules/Order/index.d.ts +32 -4
  114. package/lib/modules/Order/index.js +372 -156
  115. package/lib/modules/Order/types.d.ts +22 -1
  116. package/lib/modules/Order/utils/discountProductLineIdentity.d.ts +45 -0
  117. package/lib/modules/Order/utils/discountProductLineIdentity.js +170 -0
  118. package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +15 -0
  119. package/lib/modules/Order/utils/orderCollectionIdentity.js +70 -0
  120. package/lib/modules/Order/utils.d.ts +10 -0
  121. package/lib/modules/Order/utils.js +39 -6
  122. package/lib/modules/Payment/index.d.ts +4 -0
  123. package/lib/modules/Payment/index.js +7 -0
  124. package/lib/modules/Payment/walletpass.js +12 -0
  125. package/lib/modules/Product/types.d.ts +1 -0
  126. package/lib/modules/ProductList/index.js +49 -31
  127. package/lib/modules/Quotation/index.d.ts +1 -1
  128. package/lib/modules/Quotation/index.js +2 -2
  129. package/lib/modules/ResourcePlanner/index.d.ts +24 -0
  130. package/lib/modules/ResourcePlanner/index.js +148 -0
  131. package/lib/modules/ResourcePlanner/planner.d.ts +14 -0
  132. package/lib/modules/ResourcePlanner/planner.js +933 -0
  133. package/lib/modules/ResourcePlanner/types.d.ts +227 -0
  134. package/lib/modules/ResourcePlanner/types.js +17 -0
  135. package/lib/modules/Rules/index.js +117 -25
  136. package/lib/modules/SalesSummary/index.js +6 -7
  137. package/lib/modules/ScanOrderLogger/index.d.ts +2 -0
  138. package/lib/modules/ScanOrderLogger/index.js +13 -1
  139. package/lib/modules/ScanOrderLogger/providers/feishu.js +15 -6
  140. package/lib/modules/ScanOrderLogger/types.d.ts +1 -0
  141. package/lib/modules/Schedule/index.d.ts +3 -1
  142. package/lib/modules/Schedule/index.js +10 -8
  143. package/lib/modules/Summary/utils.js +21 -1
  144. package/lib/modules/index.d.ts +2 -0
  145. package/lib/modules/index.js +5 -1
  146. package/lib/plugins/window.d.ts +1 -0
  147. package/lib/server/index.d.ts +20 -0
  148. package/lib/server/index.js +124 -19
  149. package/lib/server/modules/order/index.d.ts +2 -0
  150. package/lib/server/modules/order/index.js +75 -5
  151. package/lib/server/modules/order/types.d.ts +1 -1
  152. package/lib/solution/BaseSales/index.d.ts +20 -1
  153. package/lib/solution/BaseSales/index.js +105 -31
  154. package/lib/solution/BaseSales/utils/cartPromotion.js +18 -0
  155. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +1 -0
  156. package/lib/solution/BaseSales/utils.js +29 -18
  157. package/lib/solution/BookingByStep/index.d.ts +17 -2
  158. package/lib/solution/BookingByStep/index.js +60 -18
  159. package/lib/solution/BookingByStep/types.d.ts +2 -1
  160. package/lib/solution/BookingByStep/types.js +5 -0
  161. package/lib/solution/BookingByStep/utils/capacity.js +20 -1
  162. package/lib/solution/BookingByStep/utils/timeslots.d.ts +3 -1
  163. package/lib/solution/BookingByStep/utils/timeslots.js +19 -11
  164. package/lib/solution/BookingTicket/index.d.ts +21 -3
  165. package/lib/solution/BookingTicket/index.js +75 -16
  166. package/lib/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  167. package/lib/solution/BookingTicket/utils/scan/handleScan.js +2 -2
  168. package/lib/solution/ScanOrder/index.d.ts +26 -14
  169. package/lib/solution/ScanOrder/index.js +449 -182
  170. package/lib/solution/ScanOrder/types.d.ts +21 -1
  171. package/lib/solution/ScanOrder/utils.d.ts +8 -0
  172. package/lib/solution/ScanOrder/utils.js +60 -18
  173. package/lib/solution/ShopDiscount/index.d.ts +1 -0
  174. package/lib/solution/ShopDiscount/index.js +14 -0
  175. package/lib/solution/UnifiedBookingSales/index.d.ts +204 -0
  176. package/lib/solution/UnifiedBookingSales/index.js +1273 -0
  177. package/lib/solution/UnifiedBookingSales/types.d.ts +150 -0
  178. package/lib/solution/UnifiedBookingSales/types.js +33 -0
  179. package/lib/solution/VenueBooking/index.d.ts +39 -11
  180. package/lib/solution/VenueBooking/index.js +322 -110
  181. package/lib/solution/VenueBooking/types.d.ts +3 -0
  182. package/lib/solution/VenueBooking/utils/resource.js +1 -0
  183. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +4 -4
  184. package/lib/solution/VenueBooking/utils/slotMerge.js +6 -4
  185. package/lib/solution/index.d.ts +1 -0
  186. package/lib/solution/index.js +3 -1
  187. package/lib/types/index.d.ts +1 -0
  188. package/package.json +1 -1
@@ -414,6 +414,7 @@ export interface CommitOrderParams {
414
414
  type: 'virtual' | 'appointment_booking';
415
415
  platform?: string;
416
416
  extraData?: Record<string, any>;
417
+ business_code?: string;
417
418
  };
418
419
  }
419
420
  export interface OrderSubmitBooking {
@@ -578,6 +579,10 @@ export interface LoadSalesDetailParams {
578
579
  * 存在时 loadSalesDetail 会跳过远端 lookup,直接复用 hydrate 后半段逻辑。
579
580
  */
580
581
  preloadedSalesDetail?: Record<string, any>;
582
+ /**
583
+ * sessionStorage 快照复用详情 hydrate 时使用;不带该值时保持既有编辑订单语义。
584
+ */
585
+ hydrateSource?: 'sessionStorage';
581
586
  forceRemote?: boolean;
582
587
  merge?: boolean;
583
588
  }
@@ -589,6 +594,12 @@ export interface SyncPaymentsToOrderParams {
589
594
  payments: OrderPaymentSource[];
590
595
  paymentStatus?: SubmitSalesOrderParams['query']['payment_status'];
591
596
  submitWhenPaid?: boolean;
597
+ /**
598
+ * Whether the checkout should be queued as the local `sync_sales` task.
599
+ * Defaults to true to preserve the WebPOS retry path. Set to false when
600
+ * the caller must wait for the cloud checkout response before proceeding.
601
+ */
602
+ checkoutSyncTaskEnabled?: boolean;
592
603
  smallTicketDataFlag?: number;
593
604
  businessCode?: string;
594
605
  channel?: string;
@@ -742,8 +753,15 @@ export interface OrderModuleAPI {
742
753
  removeProductFromOrder: (identity: OrderProductIdentity, options?: RemoveProductsFromOrderOptions) => Promise<OrderProduct[]>;
743
754
  /** 批量删除购物车行,末尾仅触发一次促销重算与 summary 刷新 */
744
755
  removeProductsFromOrder: (identities: OrderProductIdentity[], options?: RemoveProductsFromOrderOptions) => Promise<OrderProduct[]>;
745
- /** @deprecated no-op;OrderModule 不再负责 tempOrder localStorage 持久化。 */
756
+ /** 在显式开启时保存当前 tempOrder sessionStorage 会话快照。 */
746
757
  persistTempOrder: () => void;
758
+ /**
759
+ * 删除当前 cacheId 的 tempOrder 会话快照,并阻止当前模块实例再次写入。
760
+ * 新模块实例初始化时会按 otherParams 重新启用持久化。
761
+ */
762
+ clearSessionStoragePersistedTempOrder: () => void;
763
+ /** 取出一次待恢复的 sessionStorage tempOrder 快照。 */
764
+ consumeSessionStorageRestore: () => Record<string, any> | null;
747
765
  /**
748
766
  * 控制 IndexedDB 草稿(saveDraft)开关;localStorage tempOrder 持久化已废弃。
749
767
  * BigSale 弹窗等场景传 false 可跳过 saveDraft。
@@ -751,6 +769,8 @@ export interface OrderModuleAPI {
751
769
  setEnableTempOrderPersist: (enabled: boolean) => void;
752
770
  /** 是否允许 saveDraft 写入 IndexedDB 草稿。 */
753
771
  isTempOrderPersistEnabled: () => boolean;
772
+ /** 提交 tempOrder 变更并触发 `{moduleName}:changed` 事件 */
773
+ notifyTempOrderChanged: () => void;
754
774
  submitTempOrder: <T = any>(params?: {
755
775
  cacheId?: string;
756
776
  platform?: string;
@@ -799,6 +819,7 @@ export interface OrderModuleAPI {
799
819
  saveDraft: () => Promise<void>;
800
820
  hydrateTempOrderFromRecord: (record: Record<string, any>, options?: {
801
821
  recalcOnHydrate?: boolean;
822
+ source?: 'salesDetail' | 'sessionStorage';
802
823
  }) => Promise<OrderTempOrder>;
803
824
  syncPaymentsToOrder: <T = any>(params: SyncPaymentsToOrderParams) => Promise<SyncPaymentsToOrderResult<T>>;
804
825
  getSalesOrderByLookup: (params: SalesOrderLookupParams) => Promise<any>;
@@ -0,0 +1,45 @@
1
+ import type { OrderTempOrder } from '../types';
2
+ /**
3
+ * 商品券拆行的身份与回并工具。
4
+ *
5
+ * 背景:
6
+ * Rules.calcDiscount 应用 good_pass 时,会把一个数量行拆成若干「用券行」和
7
+ * 「普通余量行」。Rules 返回的拆分行可能复制同一份
8
+ * metadata.unique_identification_number,进而造成:
9
+ * - 按 UID 删除时同时命中多行;
10
+ * - UI 把不同券行视为同一个商品;
11
+ * - 后续商品更新无法准确定位目标行。
12
+ *
13
+ * 处理原则:
14
+ * 1. 拆分后每一行必须拥有独立 UID;
15
+ * 2. 原始拆分来源仅记录在 tempOrder._extend.productsByUid 的运行态索引中,
16
+ * 不写入商品 metadata,不污染提交协议;
17
+ * 3. 券被移除后,只回并被本工具确认来自同一折扣拆分源的行;
18
+ * 4. 预约、赠品、促销、手动折扣、备注、称重、holder 和已落库行均为硬边界。
19
+ *
20
+ * 两个导出函数都会原地更新 tempOrder。调用顺序必须是:
21
+ * ensureUniqueProductLineUids -> consolidateDiscountFreeProductLines。
22
+ */
23
+ /** productsByUid 运行态记录中用于关联同一次折扣拆分的字段。 */
24
+ export declare const DISCOUNT_SPLIT_ORIGIN_UID_KEY = "discountSplitOriginUid";
25
+ /**
26
+ * 修复 Rules 折扣拆行复制 UID 的问题,并为拆分行记录共同来源。
27
+ *
28
+ * 第一条商品保留原 UID,后续重复行生成新 UUID;对应的 productsByUid.runtime
29
+ * 会复制原行 runtime(包括 origin),以保证删除、编辑及展示字段回读仍可工作。
30
+ *
31
+ * 没有 UID 的裸商品不会被补写 UID。这是为了兼容 Rules/mock 的历史协议形状;
32
+ * 正常 Order 写路径会在 normalize 阶段提前创建 UID。
33
+ */
34
+ export declare function ensureUniqueProductLineUids(tempOrder: OrderTempOrder): void;
35
+ /**
36
+ * 把券移除后已经恢复原价的同源折扣拆分行重新合并。
37
+ *
38
+ * 此函数刻意不执行通用的「同 SKU 合并」:没有 discountSplitOriginUid 的普通行
39
+ * 可能是业务 hook、导入订单或其它流程主动拆开的,不能在折扣重算时擅自合并。
40
+ *
41
+ * payment_price、tax_fee、surcharge_fee 属于 summary 计算层的派生值。券行与普通行
42
+ * 在回并前可能暂时不同,因此它们不参与 mergeKey;原始价格、SKU/option/bundle
43
+ * 结构以及税务/商品类型配置仍参与比较,防止不同商品语义被误合并。
44
+ */
45
+ export declare function consolidateDiscountFreeProductLines(tempOrder: OrderTempOrder): void;
@@ -0,0 +1,170 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/modules/Order/utils/discountProductLineIdentity.ts
20
+ var discountProductLineIdentity_exports = {};
21
+ __export(discountProductLineIdentity_exports, {
22
+ DISCOUNT_SPLIT_ORIGIN_UID_KEY: () => DISCOUNT_SPLIT_ORIGIN_UID_KEY,
23
+ consolidateDiscountFreeProductLines: () => consolidateDiscountFreeProductLines,
24
+ ensureUniqueProductLineUids: () => ensureUniqueProductLineUids
25
+ });
26
+ module.exports = __toCommonJS(discountProductLineIdentity_exports);
27
+ var import_lodash_es = require("lodash-es");
28
+ var import_utils = require("../../../solution/ScanOrder/utils");
29
+ var import_orderCollectionIdentity = require("./orderCollectionIdentity");
30
+ var DISCOUNT_SPLIT_ORIGIN_UID_KEY = "discountSplitOriginUid";
31
+ function getProductLineUid(product) {
32
+ var _a;
33
+ if (!product || typeof product !== "object")
34
+ return null;
35
+ const uid = ((_a = product.metadata) == null ? void 0 : _a.unique_identification_number) ?? product.unique_identification_number;
36
+ if (uid === void 0 || uid === null || uid === "")
37
+ return null;
38
+ return String(uid);
39
+ }
40
+ function getProductSkuOptions(product) {
41
+ var _a;
42
+ return Array.isArray((_a = product.product_sku) == null ? void 0 : _a.option) ? product.product_sku.option : [];
43
+ }
44
+ function hasProductLineNote(product) {
45
+ return typeof product.note === "string" && product.note.trim().length > 0;
46
+ }
47
+ function isManualProductDiscountLine(product) {
48
+ var _a, _b;
49
+ return ((_a = product.metadata) == null ? void 0 : _a.is_manual_discount) === true || ((_b = product.metadata) == null ? void 0 : _b.is_manual_discount) === 1 || (product.discount_list || []).some((item) => (item == null ? void 0 : item.type) === "product");
50
+ }
51
+ function ensureProductsByUid(tempOrder) {
52
+ if (!tempOrder._extend || typeof tempOrder._extend !== "object") {
53
+ tempOrder._extend = { productsByUid: {} };
54
+ }
55
+ if (!tempOrder._extend.productsByUid) {
56
+ tempOrder._extend.productsByUid = {};
57
+ }
58
+ return tempOrder._extend.productsByUid;
59
+ }
60
+ function ensureUniqueProductLineUids(tempOrder) {
61
+ const usedUids = /* @__PURE__ */ new Set();
62
+ const productsByUid = ensureProductsByUid(tempOrder);
63
+ tempOrder.products = (tempOrder.products || []).map((product) => {
64
+ const sourceUid = getProductLineUid(product);
65
+ if (!sourceUid)
66
+ return product;
67
+ if (!usedUids.has(sourceUid)) {
68
+ usedUids.add(sourceUid);
69
+ return product;
70
+ }
71
+ let splitUid = (0, import_orderCollectionIdentity.createUuidV4)();
72
+ while (usedUids.has(splitUid)) {
73
+ splitUid = (0, import_orderCollectionIdentity.createUuidV4)();
74
+ }
75
+ product.metadata = {
76
+ ...product.metadata || {},
77
+ unique_identification_number: splitUid
78
+ };
79
+ const sourceRuntime = productsByUid[sourceUid] || {};
80
+ const discountSplitOriginUid = String(
81
+ sourceRuntime[DISCOUNT_SPLIT_ORIGIN_UID_KEY] || sourceUid
82
+ );
83
+ productsByUid[sourceUid] = {
84
+ ...sourceRuntime,
85
+ [DISCOUNT_SPLIT_ORIGIN_UID_KEY]: discountSplitOriginUid
86
+ };
87
+ productsByUid[splitUid] = {
88
+ ...(0, import_lodash_es.cloneDeep)(sourceRuntime),
89
+ [DISCOUNT_SPLIT_ORIGIN_UID_KEY]: discountSplitOriginUid
90
+ };
91
+ usedUids.add(splitUid);
92
+ return product;
93
+ });
94
+ }
95
+ function consolidateDiscountFreeProductLines(tempOrder) {
96
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
97
+ const consolidatedProducts = [];
98
+ const productIndexByKey = /* @__PURE__ */ new Map();
99
+ for (const product of tempOrder.products || []) {
100
+ const productBundle = Array.isArray(product.product_bundle) ? product.product_bundle : [];
101
+ const hasDiscount = (product.discount_list || []).length > 0 || productBundle.some((bundle) => ((bundle == null ? void 0 : bundle.discount_list) || []).length > 0);
102
+ const bookingUid = product.booking_uid ?? ((_a = product.metadata) == null ? void 0 : _a.booking_uid);
103
+ const bookingId = product.booking_id ?? ((_b = product.metadata) == null ? void 0 : _b.booking_id);
104
+ const orderDetailId = product.order_detail_id ?? product.orderDetailId;
105
+ const holderId = product.holder_id ?? ((_c = product.metadata) == null ? void 0 : _c.holder_id);
106
+ const hasMergeBoundary = hasDiscount || Boolean(((_d = product.metadata) == null ? void 0 : _d._giftInfo) || product._giftInfo) || Boolean((_e = product.metadata) == null ? void 0 : _e._promotion) || isManualProductDiscountLine(product) || hasProductLineNote(product) || Number(((_f = product.product_sku) == null ? void 0 : _f.open_sold_weight) || 0) === 1 || ((_g = product.metadata) == null ? void 0 : _g.is_edit_for_runtime) === true || bookingId !== void 0 && bookingId !== null && bookingId !== 0 && bookingId !== "0" || bookingUid !== void 0 && bookingUid !== null && String(bookingUid) !== "" || holderId !== void 0 && holderId !== null && String(holderId) !== "" || orderDetailId !== void 0 && orderDetailId !== null && orderDetailId !== "";
107
+ if (hasMergeBoundary) {
108
+ consolidatedProducts.push(product);
109
+ continue;
110
+ }
111
+ const productUid = getProductLineUid(product);
112
+ const discountSplitOriginUid = productUid ? (_j = (_i = (_h = tempOrder._extend) == null ? void 0 : _h.productsByUid) == null ? void 0 : _i[productUid]) == null ? void 0 : _j[DISCOUNT_SPLIT_ORIGIN_UID_KEY] : void 0;
113
+ if (!discountSplitOriginUid) {
114
+ consolidatedProducts.push(product);
115
+ continue;
116
+ }
117
+ const fingerprint = (0, import_utils.buildProductLineFingerprint)(
118
+ getProductSkuOptions(product),
119
+ productBundle
120
+ );
121
+ const priceSignature = JSON.stringify({
122
+ selling_price: String(product.selling_price ?? ""),
123
+ original_price: String(product.original_price ?? ""),
124
+ custom_price: String(product.custom_price ?? ""),
125
+ is_charge_tax: Number(product.is_charge_tax ?? 0),
126
+ gift_card: Number(product.gift_card ?? 0),
127
+ bundle_edit: Number(product.bundle_edit ?? 0),
128
+ price_schema_version: ((_k = product.metadata) == null ? void 0 : _k.price_schema_version) ?? "",
129
+ source_product_price: String(((_l = product.metadata) == null ? void 0 : _l.source_product_price) ?? ""),
130
+ main_product_selling_price: String(
131
+ ((_m = product.metadata) == null ? void 0 : _m.main_product_selling_price) ?? ""
132
+ ),
133
+ main_product_original_price: String(
134
+ ((_n = product.metadata) == null ? void 0 : _n.main_product_original_price) ?? ""
135
+ ),
136
+ bundle: productBundle.map((bundle) => ({
137
+ bundle_id: (bundle == null ? void 0 : bundle.bundle_id) ?? (bundle == null ? void 0 : bundle.id) ?? "",
138
+ product_id: (bundle == null ? void 0 : bundle.bundle_product_id) ?? (bundle == null ? void 0 : bundle._bundle_product_id) ?? (bundle == null ? void 0 : bundle.product_id) ?? "",
139
+ product_variant_id: (bundle == null ? void 0 : bundle.bundle_variant_id) ?? (bundle == null ? void 0 : bundle.variant_id) ?? (bundle == null ? void 0 : bundle.product_variant_id) ?? 0,
140
+ price: String((bundle == null ? void 0 : bundle.price) ?? ""),
141
+ original_price: String((bundle == null ? void 0 : bundle.original_price) ?? ""),
142
+ bundle_selling_price: String((bundle == null ? void 0 : bundle.bundle_selling_price) ?? "")
143
+ }))
144
+ });
145
+ const mergeKey = [
146
+ discountSplitOriginUid,
147
+ product.product_id,
148
+ product.product_variant_id ?? 0,
149
+ fingerprint,
150
+ priceSignature
151
+ ].join("#");
152
+ const matchedIndex = productIndexByKey.get(mergeKey);
153
+ if (matchedIndex === void 0) {
154
+ productIndexByKey.set(mergeKey, consolidatedProducts.length);
155
+ consolidatedProducts.push(product);
156
+ continue;
157
+ }
158
+ const matchedProduct = consolidatedProducts[matchedIndex];
159
+ matchedProduct.num = (0, import_utils.getSafeProductNum)(
160
+ Number(matchedProduct.num || 0) + Number(product.num || 0)
161
+ );
162
+ }
163
+ tempOrder.products = consolidatedProducts;
164
+ }
165
+ // Annotate the CommonJS export names for ESM import in node:
166
+ 0 && (module.exports = {
167
+ DISCOUNT_SPLIT_ORIGIN_UID_KEY,
168
+ consolidateDiscountFreeProductLines,
169
+ ensureUniqueProductLineUids
170
+ });
@@ -24,6 +24,16 @@ export interface NormalizeOrderCollectionsResult<T> {
24
24
  stats: Record<OrderCollectionKind, OrderCollectionIdentityStats>;
25
25
  uidRemaps: OrderCollectionUidRemap[];
26
26
  }
27
+ export interface ProductLineIdentityRepair {
28
+ index: number;
29
+ from: string;
30
+ to: string;
31
+ }
32
+ export interface RepairDuplicateProductLineIdentitiesResult<T> {
33
+ products: T[];
34
+ productsByUid?: Record<string, Record<string, any>>;
35
+ repairs: ProductLineIdentityRepair[];
36
+ }
27
37
  export declare function createUuidV4(): string;
28
38
  export declare function isBlankOrderCollectionIdentity(value: unknown): boolean;
29
39
  export declare function getOrderCollectionPersistentId(kind: OrderCollectionKind, item: unknown): string | null;
@@ -31,6 +41,11 @@ export declare function getOrderCollectionUid(kind: OrderCollectionKind, item: u
31
41
  export declare function buildOrderCollectionFallbackUid(kind: OrderCollectionKind, persistentId: string | number): string;
32
42
  export declare function getOrderCollectionReferenceUid(kind: OrderCollectionKind, item: unknown): string | null;
33
43
  export declare function setOrderCollectionUid<T>(kind: OrderCollectionKind, item: T, uid: string): T;
44
+ /**
45
+ * Rules 会把同一商品按商品券拆成多条真实订单行。拆行结果会继承原商品 UID,
46
+ * 因此必须在通用 collection normalization 之前为后续行派生稳定身份,避免被折叠。
47
+ */
48
+ export declare function repairDuplicateProductLineIdentities<T extends Record<string, any>>(products: T[] | null | undefined, productsByUid?: Record<string, Record<string, any>> | null): RepairDuplicateProductLineIdentitiesResult<T>;
34
49
  /**
35
50
  * 持久化行 ID 是服务端事实主键;只有至少一侧尚未持久化时,才允许用 runtime UID 对齐。
36
51
  */
@@ -30,6 +30,7 @@ __export(orderCollectionIdentity_exports, {
30
30
  mergeOrderCollectionItems: () => mergeOrderCollectionItems,
31
31
  normalizeOrderCollection: () => normalizeOrderCollection,
32
32
  normalizeOrderCollections: () => normalizeOrderCollections,
33
+ repairDuplicateProductLineIdentities: () => repairDuplicateProductLineIdentities,
33
34
  setOrderCollectionUid: () => setOrderCollectionUid
34
35
  });
35
36
  module.exports = __toCommonJS(orderCollectionIdentity_exports);
@@ -105,6 +106,74 @@ function setOrderCollectionUid(kind, item, uid) {
105
106
  delete record[uidField];
106
107
  return record;
107
108
  }
109
+ function getGoodPassResourceId(product) {
110
+ var _a;
111
+ const goodPass = (product.discount_list || []).find((item) => {
112
+ var _a2, _b;
113
+ const type = (item == null ? void 0 : item.type) || (item == null ? void 0 : item.tag) || ((_a2 = item == null ? void 0 : item.discount) == null ? void 0 : _a2.type) || ((_b = item == null ? void 0 : item.discount) == null ? void 0 : _b.tag);
114
+ return type === "good_pass";
115
+ });
116
+ if (!goodPass)
117
+ return null;
118
+ const resourceId = ((_a = goodPass.discount) == null ? void 0 : _a.resource_id) ?? goodPass.resource_id ?? goodPass.discount_id ?? goodPass.id;
119
+ return isBlankOrderCollectionIdentity(resourceId) ? null : String(resourceId);
120
+ }
121
+ function buildSplitProductUid(originalUid, product, duplicateOrdinal) {
122
+ const encodedOriginalUid = encodeURIComponent(originalUid);
123
+ const resourceId = getGoodPassResourceId(product);
124
+ if (resourceId) {
125
+ return `os-split:product:${encodedOriginalUid}:good-pass:${encodeURIComponent(resourceId)}:${duplicateOrdinal}`;
126
+ }
127
+ return `os-split:product:${encodedOriginalUid}:duplicate:${duplicateOrdinal}`;
128
+ }
129
+ function repairDuplicateProductLineIdentities(products, productsByUid) {
130
+ const repairedProducts = (0, import_lodash_es.cloneDeep)(products || []);
131
+ const repairedProductsByUid = productsByUid ? (0, import_lodash_es.cloneDeep)(productsByUid) : void 0;
132
+ const occupiedUids = new Set(
133
+ repairedProducts.map((product) => getOrderCollectionUid("product", product)).filter((uid) => Boolean(uid))
134
+ );
135
+ Object.keys(repairedProductsByUid || {}).forEach((uid) => occupiedUids.add(uid));
136
+ const occurrences = /* @__PURE__ */ new Map();
137
+ const repairs = [];
138
+ repairedProducts.forEach((product, index) => {
139
+ const originalUid = getOrderCollectionUid("product", product);
140
+ if (!originalUid)
141
+ return;
142
+ const occurrence = (occurrences.get(originalUid) || 0) + 1;
143
+ occurrences.set(originalUid, occurrence);
144
+ if (occurrence === 1)
145
+ return;
146
+ const duplicateOrdinal = occurrence - 1;
147
+ const baseCandidate = buildSplitProductUid(
148
+ originalUid,
149
+ product,
150
+ duplicateOrdinal
151
+ );
152
+ let nextUid = baseCandidate;
153
+ let conflictOrdinal = 1;
154
+ while (occupiedUids.has(nextUid)) {
155
+ nextUid = `${baseCandidate}:conflict:${conflictOrdinal}`;
156
+ conflictOrdinal += 1;
157
+ }
158
+ const repairedProduct = setOrderCollectionUid("product", product, nextUid);
159
+ repairedProduct.identity_key = nextUid;
160
+ repairedProducts[index] = repairedProduct;
161
+ occupiedUids.add(nextUid);
162
+ repairs.push({ index, from: originalUid, to: nextUid });
163
+ const runtimeSource = repairedProductsByUid == null ? void 0 : repairedProductsByUid[originalUid];
164
+ if (runtimeSource && repairedProductsByUid) {
165
+ repairedProductsByUid[nextUid] = {
166
+ ...(0, import_lodash_es.cloneDeep)(runtimeSource),
167
+ ...runtimeSource.rowKey !== void 0 ? { rowKey: nextUid } : {}
168
+ };
169
+ }
170
+ });
171
+ return {
172
+ products: repairedProducts,
173
+ ...repairedProductsByUid ? { productsByUid: repairedProductsByUid } : {},
174
+ repairs
175
+ };
176
+ }
108
177
  function isSameOrderCollectionItem(kind, left, right) {
109
178
  const leftPersistentId = getOrderCollectionPersistentId(kind, left);
110
179
  const rightPersistentId = getOrderCollectionPersistentId(kind, right);
@@ -414,5 +483,6 @@ function normalizeOrderCollections(sourceOrder, options) {
414
483
  mergeOrderCollectionItems,
415
484
  normalizeOrderCollection,
416
485
  normalizeOrderCollections,
486
+ repairDuplicateProductLineIdentities,
417
487
  setOrderCollectionUid
418
488
  });
@@ -15,6 +15,7 @@ export interface OrderProductsDepositSummary {
15
15
  deposit_policy_ids: number[];
16
16
  hasDeposit: boolean;
17
17
  }
18
+ import type { WindowPlugin } from '../../plugins';
18
19
  /**
19
20
  * 把"含 option 的主商品单价"与 bundle 合成"单行 composite 单价"。
20
21
  *
@@ -177,6 +178,15 @@ export declare const mergeRelationForms: (relationForms: {
177
178
  form_id: number;
178
179
  form_record_ids: number[];
179
180
  }[];
181
+ export interface CartOrderHolder {
182
+ customer_id?: number;
183
+ form_id: number | string;
184
+ form_record: (number | string)[] | null;
185
+ }
186
+ /**
187
+ * 为购物车行补齐订单所需的 holder 信息(与 cartAccount 结构保持一致)
188
+ */
189
+ export declare function ensureCartItemOriginHolder(cartItem: CartItem, window?: WindowPlugin): void;
180
190
  export declare const getAllDiscountList: (cartItem: CartItem) => any;
181
191
  export { createUuidV4 };
182
192
  export declare function isTempOrder(data: any): data is OrderTempOrder;
@@ -36,8 +36,9 @@ __export(utils_exports, {
36
36
  composeLinePrice: () => composeLinePrice,
37
37
  createDefaultOrderRulesHooks: () => createDefaultOrderRulesHooks,
38
38
  createDefaultTempOrder: () => createDefaultTempOrder,
39
- createEmptySummary: () => import_utils2.createEmptySummary,
39
+ createEmptySummary: () => import_utils3.createEmptySummary,
40
40
  createUuidV4: () => import_orderCollectionIdentity.createUuidV4,
41
+ ensureCartItemOriginHolder: () => ensureCartItemOriginHolder,
41
42
  ensureManualProductDiscountList: () => import_manualProductDiscount.ensureManualProductDiscountList,
42
43
  ensureProductSku: () => ensureProductSku,
43
44
  findManualProductDiscountItem: () => import_manualProductDiscount.findManualProductDiscountItem,
@@ -85,9 +86,10 @@ __export(utils_exports, {
85
86
  module.exports = __toCommonJS(utils_exports);
86
87
  var import_dayjs = __toESM(require("dayjs"));
87
88
  var import_decimal = __toESM(require("decimal.js"));
88
- var import_utils = require("../../solution/ScanOrder/utils");
89
- var import_orderCollectionIdentity = require("./utils/orderCollectionIdentity");
89
+ var import_utils = require("../Holder/utils");
90
90
  var import_utils2 = require("../../solution/ScanOrder/utils");
91
+ var import_orderCollectionIdentity = require("./utils/orderCollectionIdentity");
92
+ var import_utils3 = require("../../solution/ScanOrder/utils");
91
93
  var import_manualProductDiscount = require("./utils/manualProductDiscount");
92
94
  function composeLinePrice(params) {
93
95
  const { mainPrice, bundle, useOriginalBundle } = params;
@@ -546,7 +548,7 @@ function createDefaultOrderRulesHooks() {
546
548
  // 否则同 SKU 不同小料会在 calcDiscount 重组时互相覆盖。
547
549
  // 不透明 identity 契约下,normalizeOrderProduct / hydrate 已保证唯一值存在;
548
550
  // 留 fingerprint 分支仅作 Rules 单测里直接传裸 product 时的兜底。
549
- _id: ((_a = product.metadata) == null ? void 0 : _a.unique_identification_number) ? `${product.product_id}_${product.product_variant_id}_${product.metadata.unique_identification_number}` : `${product.product_id}_${product.product_variant_id}_${(0, import_utils.buildProductLineFingerprint)(
551
+ _id: ((_a = product.metadata) == null ? void 0 : _a.unique_identification_number) ? `${product.product_id}_${product.product_variant_id}_${product.metadata.unique_identification_number}` : `${product.product_id}_${product.product_variant_id}_${(0, import_utils2.buildProductLineFingerprint)(
550
552
  getProductSkuOptions(product),
551
553
  product.product_bundle
552
554
  )}`,
@@ -638,6 +640,34 @@ var mergeRelationForms = (relationForms) => {
638
640
  }, {})
639
641
  ).filter((item) => item.form_record_ids.length > 0);
640
642
  };
643
+ function getCustomerIdFromStorage(window) {
644
+ var _a;
645
+ try {
646
+ const customer = JSON.parse(((_a = window == null ? void 0 : window.getLocalStorage) == null ? void 0 : _a.call(window, "customer")) || "{}");
647
+ const customerId = Number(customer == null ? void 0 : customer.id);
648
+ return Number.isFinite(customerId) && customerId > 0 ? customerId : void 0;
649
+ } catch {
650
+ return void 0;
651
+ }
652
+ }
653
+ function ensureCartItemOriginHolder(cartItem, window) {
654
+ var _a;
655
+ if ((_a = cartItem == null ? void 0 : cartItem._origin) == null ? void 0 : _a.holder) {
656
+ return;
657
+ }
658
+ const holderConfig = (0, import_utils.getProductHolderConfig)(
659
+ (cartItem == null ? void 0 : cartItem._productInit) ?? (cartItem == null ? void 0 : cartItem._productOrigin)
660
+ );
661
+ const formRecordId = cartItem == null ? void 0 : cartItem.holder_id;
662
+ if (!(holderConfig == null ? void 0 : holderConfig.resource_id) || formRecordId == null) {
663
+ return;
664
+ }
665
+ cartItem._origin.holder = {
666
+ customer_id: getCustomerIdFromStorage(window),
667
+ form_id: holderConfig.resource_id,
668
+ form_record: [formRecordId]
669
+ };
670
+ }
641
671
  var getAllDiscountList = (cartItem) => {
642
672
  var _a, _b, _c, _d;
643
673
  let discountList = ((_b = (_a = cartItem == null ? void 0 : cartItem._origin) == null ? void 0 : _a.product) == null ? void 0 : _b.discount_list) || [];
@@ -771,6 +801,8 @@ function normalizeSubmitProduct(product) {
771
801
  delete productRest[`product_${"option"}_item`];
772
802
  const rawMetadata = submitProduct.metadata || {};
773
803
  const bookingUid = rawMetadata.booking_uid;
804
+ const hasProductBundle = Array.isArray(submitProduct.product_bundle) && submitProduct.product_bundle.length > 0;
805
+ const bundleEdit = submitProduct.bundle_edit ?? rawMetadata.bundle_edit ?? (hasProductBundle ? 0 : void 0);
774
806
  const cleanMetadata = {};
775
807
  if (rawMetadata.unique_identification_number) {
776
808
  cleanMetadata.unique_identification_number = rawMetadata.unique_identification_number;
@@ -813,6 +845,7 @@ function normalizeSubmitProduct(product) {
813
845
  return {
814
846
  ...productRest,
815
847
  ...bookingUid ? { booking_uid: bookingUid } : {},
848
+ ...bundleEdit !== void 0 ? { bundle_edit: bundleEdit } : {},
816
849
  product_quantity: toBundleNumber(num ?? submitProduct.product_quantity, 1),
817
850
  product_sku: productSku,
818
851
  discount_list: collectSubmitProductDiscountList(submitProduct),
@@ -1003,7 +1036,7 @@ function buildSubmitPayload(params) {
1003
1036
  // contacts: tempOrder.contacts || [],
1004
1037
  contacts_info: tempOrder.contacts_info && !Array.isArray(tempOrder.contacts_info) ? tempOrder.contacts_info : null,
1005
1038
  // holder: tempOrder.holder || null,
1006
- summary: summary || tempOrder.summary || (0, import_utils.createEmptySummary)(),
1039
+ summary: summary || tempOrder.summary || (0, import_utils2.createEmptySummary)(),
1007
1040
  metadata: (() => {
1008
1041
  const {
1009
1042
  collect_pax: _collectPax,
@@ -1173,7 +1206,6 @@ function productRequiresFormSubject(tempOrder, product) {
1173
1206
  }
1174
1207
  function resolveIsFormSubject(tempOrder, product) {
1175
1208
  var _a;
1176
- debugger;
1177
1209
  if (((_a = tempOrder == null ? void 0 : tempOrder.holder) == null ? void 0 : _a.type) === "form")
1178
1210
  return true;
1179
1211
  if (product)
@@ -1252,6 +1284,7 @@ function indexOrderProductsByUid(products) {
1252
1284
  createDefaultTempOrder,
1253
1285
  createEmptySummary,
1254
1286
  createUuidV4,
1287
+ ensureCartItemOriginHolder,
1255
1288
  ensureManualProductDiscountList,
1256
1289
  ensureProductSku,
1257
1290
  findManualProductDiscountItem,
@@ -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);
@@ -722,6 +722,18 @@ var WalletPassPaymentImpl = class {
722
722
  this.paymentModule.logInfo("[WalletPass] 商品列表为空,跳过获取用户识别码列表");
723
723
  return [];
724
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
+ }
725
737
  if (typeof newParams.payment_order_id === "string" && newParams.payment_order_id.startsWith("LOCAL_")) {
726
738
  newParams.payment_order_id = void 0;
727
739
  }
@@ -193,6 +193,7 @@ export interface ProductData {
193
193
  status?: string;
194
194
  required?: number | string | boolean;
195
195
  resource_id?: number | string | null;
196
+ [key: string]: any;
196
197
  };
197
198
  /** 组合商品id,动态附加 */
198
199
  product_variant_id?: number;