@pisell/pisellos 2.2.261 → 2.2.263

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 (134) hide show
  1. package/dist/model/strategy/adapter/dataVariant/adapter.d.ts +50 -0
  2. package/dist/model/strategy/adapter/dataVariant/adapter.js +167 -0
  3. package/dist/model/strategy/adapter/dataVariant/evaluator.d.ts +89 -0
  4. package/dist/model/strategy/adapter/dataVariant/evaluator.js +780 -0
  5. package/dist/model/strategy/adapter/dataVariant/examples.d.ts +39 -0
  6. package/dist/model/strategy/adapter/dataVariant/examples.js +277 -0
  7. package/dist/model/strategy/adapter/dataVariant/index.d.ts +4 -0
  8. package/dist/model/strategy/adapter/dataVariant/index.js +4 -0
  9. package/dist/model/strategy/adapter/dataVariant/type.d.ts +148 -0
  10. package/dist/model/strategy/adapter/dataVariant/type.js +54 -0
  11. package/dist/model/strategy/adapter/index.d.ts +4 -0
  12. package/dist/model/strategy/adapter/index.js +5 -1
  13. package/dist/model/strategy/adapter/promotion/adapter.d.ts +0 -4
  14. package/dist/model/strategy/adapter/promotion/adapter.js +0 -23
  15. package/dist/model/strategy/adapter/promotion/evaluator.d.ts +0 -17
  16. package/dist/model/strategy/adapter/promotion/evaluator.js +6 -279
  17. package/dist/model/strategy/adapter/promotion/examples.d.ts +0 -86
  18. package/dist/model/strategy/adapter/promotion/examples.js +0 -99
  19. package/dist/model/strategy/adapter/promotion/index.d.ts +1 -1
  20. package/dist/model/strategy/adapter/promotion/index.js +1 -1
  21. package/dist/model/strategy/adapter/promotion/type.d.ts +2 -90
  22. package/dist/model/strategy/adapter/promotion/type.js +0 -45
  23. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +16 -10
  24. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +2 -1
  25. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +1 -1
  26. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.js +32 -1
  27. package/dist/modules/Discount/index.d.ts +5 -2
  28. package/dist/modules/Discount/index.js +30 -7
  29. package/dist/modules/Discount/types.d.ts +4 -0
  30. package/dist/modules/Order/index.d.ts +14 -2
  31. package/dist/modules/Order/index.js +1035 -736
  32. package/dist/modules/Order/types.d.ts +16 -0
  33. package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  34. package/dist/modules/Order/utils/orderCollectionIdentity.js +405 -0
  35. package/dist/modules/Order/utils.d.ts +4 -3
  36. package/dist/modules/Order/utils.js +54 -61
  37. package/dist/modules/Product/types.d.ts +22 -0
  38. package/dist/modules/ProductList/index.d.ts +1 -1
  39. package/dist/modules/ProductList/index.js +4 -2
  40. package/dist/modules/ProductList/types.d.ts +2 -0
  41. package/dist/modules/Rules/index.d.ts +2 -9
  42. package/dist/modules/Rules/index.js +69 -43
  43. package/dist/modules/Rules/types.d.ts +10 -1
  44. package/dist/server/index.d.ts +55 -0
  45. package/dist/server/index.js +1173 -753
  46. package/dist/server/modules/order/index.d.ts +10 -4
  47. package/dist/server/modules/order/index.js +404 -399
  48. package/dist/server/modules/order/types.d.ts +4 -0
  49. package/dist/server/modules/products/index.d.ts +31 -8
  50. package/dist/server/modules/products/index.js +549 -390
  51. package/dist/server/modules/products/types.d.ts +18 -0
  52. package/dist/solution/BaseSales/index.d.ts +21 -1
  53. package/dist/solution/BaseSales/index.js +1136 -789
  54. package/dist/solution/BaseSales/types.d.ts +6 -1
  55. package/dist/solution/BaseSales/types.js +1 -1
  56. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +1 -3
  57. package/dist/solution/BaseSales/utils/cartPromotion.js +48 -81
  58. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -3
  59. package/dist/solution/BaseSales/utils/parseSalesResponse.js +6 -8
  60. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +14 -1
  61. package/dist/solution/BookingByStep/index.d.ts +1 -1
  62. package/dist/solution/BookingTicket/index.d.ts +1 -1
  63. package/dist/solution/BookingTicket/index.js +59 -25
  64. package/dist/solution/BookingTicket/types.d.ts +2 -0
  65. package/dist/solution/ScanOrder/index.js +20 -31
  66. package/dist/solution/ShopDiscount/index.js +15 -14
  67. package/dist/solution/VenueBooking/index.js +19 -30
  68. package/lib/model/strategy/adapter/dataVariant/adapter.d.ts +50 -0
  69. package/lib/model/strategy/adapter/dataVariant/adapter.js +149 -0
  70. package/lib/model/strategy/adapter/dataVariant/evaluator.d.ts +89 -0
  71. package/lib/model/strategy/adapter/dataVariant/evaluator.js +583 -0
  72. package/lib/model/strategy/adapter/dataVariant/examples.d.ts +39 -0
  73. package/lib/model/strategy/adapter/dataVariant/examples.js +293 -0
  74. package/lib/model/strategy/adapter/dataVariant/index.d.ts +4 -0
  75. package/lib/model/strategy/adapter/dataVariant/index.js +38 -0
  76. package/lib/model/strategy/adapter/dataVariant/type.d.ts +148 -0
  77. package/lib/model/strategy/adapter/dataVariant/type.js +31 -0
  78. package/lib/model/strategy/adapter/index.d.ts +4 -0
  79. package/lib/model/strategy/adapter/index.js +13 -2
  80. package/lib/model/strategy/adapter/promotion/adapter.d.ts +0 -4
  81. package/lib/model/strategy/adapter/promotion/adapter.js +0 -20
  82. package/lib/model/strategy/adapter/promotion/evaluator.d.ts +0 -17
  83. package/lib/model/strategy/adapter/promotion/evaluator.js +0 -235
  84. package/lib/model/strategy/adapter/promotion/examples.d.ts +0 -86
  85. package/lib/model/strategy/adapter/promotion/examples.js +0 -91
  86. package/lib/model/strategy/adapter/promotion/index.d.ts +1 -1
  87. package/lib/model/strategy/adapter/promotion/index.js +0 -51
  88. package/lib/model/strategy/adapter/promotion/type.d.ts +2 -90
  89. package/lib/model/strategy/adapter/promotion/type.js +0 -2
  90. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +5 -0
  91. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +1 -0
  92. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +1 -1
  93. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +23 -2
  94. package/lib/modules/Discount/index.d.ts +5 -2
  95. package/lib/modules/Discount/index.js +23 -3
  96. package/lib/modules/Discount/types.d.ts +4 -0
  97. package/lib/modules/Order/index.d.ts +14 -2
  98. package/lib/modules/Order/index.js +326 -60
  99. package/lib/modules/Order/types.d.ts +16 -0
  100. package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  101. package/lib/modules/Order/utils/orderCollectionIdentity.js +415 -0
  102. package/lib/modules/Order/utils.d.ts +4 -3
  103. package/lib/modules/Order/utils.js +22 -23
  104. package/lib/modules/Product/types.d.ts +22 -0
  105. package/lib/modules/ProductList/index.d.ts +1 -1
  106. package/lib/modules/ProductList/index.js +4 -2
  107. package/lib/modules/ProductList/types.d.ts +2 -0
  108. package/lib/modules/Rules/index.d.ts +2 -9
  109. package/lib/modules/Rules/index.js +61 -29
  110. package/lib/modules/Rules/types.d.ts +10 -1
  111. package/lib/server/index.d.ts +55 -0
  112. package/lib/server/index.js +332 -34
  113. package/lib/server/modules/order/index.d.ts +10 -4
  114. package/lib/server/modules/order/index.js +198 -139
  115. package/lib/server/modules/order/types.d.ts +4 -0
  116. package/lib/server/modules/products/index.d.ts +31 -8
  117. package/lib/server/modules/products/index.js +134 -35
  118. package/lib/server/modules/products/types.d.ts +18 -0
  119. package/lib/solution/BaseSales/index.d.ts +21 -1
  120. package/lib/solution/BaseSales/index.js +313 -72
  121. package/lib/solution/BaseSales/types.d.ts +6 -1
  122. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +1 -3
  123. package/lib/solution/BaseSales/utils/cartPromotion.js +29 -60
  124. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -3
  125. package/lib/solution/BaseSales/utils/parseSalesResponse.js +4 -5
  126. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +8 -0
  127. package/lib/solution/BookingByStep/index.d.ts +1 -1
  128. package/lib/solution/BookingTicket/index.d.ts +1 -1
  129. package/lib/solution/BookingTicket/index.js +33 -8
  130. package/lib/solution/BookingTicket/types.d.ts +2 -0
  131. package/lib/solution/ScanOrder/index.js +0 -8
  132. package/lib/solution/ShopDiscount/index.js +2 -1
  133. package/lib/solution/VenueBooking/index.js +0 -8
  134. package/package.json +1 -1
@@ -40,6 +40,7 @@ var import_decimal = __toESM(require("decimal.js"));
40
40
  var import_lodash_es = require("lodash-es");
41
41
  var import_types2 = require("../BookingByStep/types");
42
42
  var import_utils = require("../../modules/Order/utils");
43
+ var import_orderCollectionIdentity = require("../../modules/Order/utils/orderCollectionIdentity");
43
44
  var import_dayjs = __toESM(require("dayjs"));
44
45
  __reExport(BaseSales_exports, require("./types"), module.exports);
45
46
  var import_Quotation = require("../../modules/Quotation");
@@ -76,11 +77,18 @@ function isBaseSalesHistoricalDiscountEntry(discount) {
76
77
  (discount == null ? void 0 : discount.isEditMode) || (discount == null ? void 0 : discount.isDisabled) || (discount == null ? void 0 : discount.order_discount_id) != null
77
78
  );
78
79
  }
79
- function getBaseSalesUniqueArrayMetadataKey(key) {
80
- if (key === "products" || key === "bookings")
81
- return "unique_identification_number";
80
+ var BASE_SALES_PERSISTENT_ID_FIELD_BY_KIND = {
81
+ product: "order_detail_id",
82
+ booking: "schedule_event_id",
83
+ payment: "order_payment_id"
84
+ };
85
+ function getBaseSalesOrderCollectionKind(key) {
86
+ if (key === "products")
87
+ return "product";
88
+ if (key === "bookings")
89
+ return "booking";
82
90
  if (key === "payments" || key === "payment")
83
- return "unique_payment_number";
91
+ return "payment";
84
92
  return null;
85
93
  }
86
94
  function isSameBaseSalesOrderRecord(left, right) {
@@ -95,47 +103,140 @@ function isSameBaseSalesOrderRecord(left, right) {
95
103
  (value) => value !== void 0 && value !== null && value !== "" && leftIdentities.has(String(value))
96
104
  );
97
105
  }
98
- function getBaseSalesMetadataUid(item, metadataKey) {
99
- var _a;
100
- const uid = ((_a = item == null ? void 0 : item.metadata) == null ? void 0 : _a[metadataKey]) ?? (item == null ? void 0 : item[metadataKey]);
101
- if (uid === void 0 || uid === null || uid === "")
102
- return null;
103
- return String(uid);
106
+ function isBaseSalesBlankValue(value) {
107
+ return value === void 0 || value === null || value === "";
104
108
  }
105
- function mergeBaseSalesUniqueArray(currentValue, loadedValue, metadataKey) {
106
- const result = [];
107
- const indexByUid = /* @__PURE__ */ new Map();
108
- const appendOrReplaceByUid = (item) => {
109
- const clonedItem = (0, import_lodash_es.cloneDeep)(item);
110
- const uid = getBaseSalesMetadataUid(clonedItem, metadataKey);
111
- if (!uid) {
112
- result.push(clonedItem);
113
- return;
109
+ function mergeBaseSalesProductSnapshotFields(currentItem, loadedItem) {
110
+ var _a, _b, _c;
111
+ const mergedItem = (0, import_lodash_es.cloneDeep)(loadedItem);
112
+ if (isBaseSalesBlankValue(mergedItem.product_cover) && !isBaseSalesBlankValue(currentItem == null ? void 0 : currentItem.product_cover)) {
113
+ mergedItem.product_cover = currentItem.product_cover;
114
+ }
115
+ const currentHolderConfig = (_a = currentItem == null ? void 0 : currentItem.metadata) == null ? void 0 : _a.holder_config;
116
+ if ((((_b = mergedItem.metadata) == null ? void 0 : _b.holder_config) === void 0 || ((_c = mergedItem.metadata) == null ? void 0 : _c.holder_config) === null) && currentHolderConfig !== void 0 && currentHolderConfig !== null) {
117
+ mergedItem.metadata = {
118
+ ...mergedItem.metadata || {},
119
+ holder_config: currentHolderConfig
120
+ };
121
+ }
122
+ return mergedItem;
123
+ }
124
+ function mergeBaseSalesCollectionItem(kind, currentItem, incomingItem, uidRemaps) {
125
+ const currentReferenceUid = (0, import_orderCollectionIdentity.getOrderCollectionReferenceUid)(kind, currentItem);
126
+ const incomingReferenceUid = (0, import_orderCollectionIdentity.getOrderCollectionReferenceUid)(kind, incomingItem);
127
+ const identityMergedItem = (0, import_orderCollectionIdentity.mergeOrderCollectionItems)(
128
+ kind,
129
+ currentItem,
130
+ incomingItem
131
+ );
132
+ const mergedUid = (0, import_orderCollectionIdentity.getOrderCollectionUid)(kind, identityMergedItem);
133
+ if (mergedUid) {
134
+ for (const from of /* @__PURE__ */ new Set([currentReferenceUid, incomingReferenceUid])) {
135
+ if (!from || from === mergedUid)
136
+ continue;
137
+ uidRemaps.push({
138
+ kind,
139
+ from,
140
+ to: mergedUid,
141
+ persistentId: (0, import_orderCollectionIdentity.getOrderCollectionPersistentId)(kind, incomingItem) || (0, import_orderCollectionIdentity.getOrderCollectionPersistentId)(kind, currentItem)
142
+ });
114
143
  }
115
- const existingIndex = indexByUid.get(uid);
116
- if (existingIndex !== void 0) {
117
- result[existingIndex] = clonedItem;
144
+ }
145
+ const mergedItem = mergedUid ? (0, import_orderCollectionIdentity.setOrderCollectionUid)(kind, incomingItem, mergedUid) : (0, import_lodash_es.cloneDeep)(incomingItem);
146
+ const persistentIdField = BASE_SALES_PERSISTENT_ID_FIELD_BY_KIND[kind];
147
+ if (isBaseSalesBlankValue(mergedItem[persistentIdField]) && !isBaseSalesBlankValue(identityMergedItem[persistentIdField])) {
148
+ mergedItem[persistentIdField] = identityMergedItem[persistentIdField];
149
+ }
150
+ return kind === "product" ? mergeBaseSalesProductSnapshotFields(currentItem, mergedItem) : mergedItem;
151
+ }
152
+ function mergeBaseSalesOrderCollection(kind, currentValue, incomingValue, strategy, uidRemaps) {
153
+ const currentItems = (0, import_orderCollectionIdentity.normalizeOrderCollection)(kind, currentValue, {
154
+ ensureUid: false
155
+ }).items;
156
+ const incomingItems = (0, import_orderCollectionIdentity.normalizeOrderCollection)(kind, incomingValue, {
157
+ ensureUid: false
158
+ }).items;
159
+ if (strategy === "authoritative-incoming") {
160
+ const result2 = (0, import_lodash_es.cloneDeep)(incomingItems);
161
+ currentItems.forEach((currentItem) => {
162
+ const incomingIndex = result2.findIndex(
163
+ (incomingItem) => (0, import_orderCollectionIdentity.isSameOrderCollectionItem)(kind, currentItem, incomingItem)
164
+ );
165
+ if (incomingIndex !== -1) {
166
+ result2[incomingIndex] = mergeBaseSalesCollectionItem(
167
+ kind,
168
+ currentItem,
169
+ result2[incomingIndex],
170
+ uidRemaps
171
+ );
172
+ return;
173
+ }
174
+ if (!(0, import_orderCollectionIdentity.getOrderCollectionPersistentId)(kind, currentItem)) {
175
+ result2.push((0, import_lodash_es.cloneDeep)(currentItem));
176
+ }
177
+ });
178
+ return result2;
179
+ }
180
+ const result = (0, import_lodash_es.cloneDeep)(currentItems);
181
+ incomingItems.forEach((incomingItem) => {
182
+ const currentIndex = result.findIndex(
183
+ (currentItem) => (0, import_orderCollectionIdentity.isSameOrderCollectionItem)(kind, currentItem, incomingItem)
184
+ );
185
+ if (currentIndex !== -1) {
186
+ result[currentIndex] = mergeBaseSalesCollectionItem(
187
+ kind,
188
+ result[currentIndex],
189
+ incomingItem,
190
+ uidRemaps
191
+ );
118
192
  return;
119
193
  }
120
- indexByUid.set(uid, result.length);
121
- result.push(clonedItem);
122
- };
123
- currentValue.forEach(appendOrReplaceByUid);
124
- loadedValue.forEach(appendOrReplaceByUid);
194
+ result.push((0, import_lodash_es.cloneDeep)(incomingItem));
195
+ });
125
196
  return result;
126
197
  }
127
- function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord) {
128
- if (!currentTempOrder)
129
- return loadedRecord;
198
+ function normalizeBaseSalesRecordCollections(sourceRecord, options) {
199
+ const normalizedRecord = (0, import_orderCollectionIdentity.normalizeOrderCollections)(
200
+ (0, import_lodash_es.cloneDeep)(sourceRecord || {}),
201
+ options
202
+ ).order;
203
+ if (Array.isArray(normalizedRecord.payment)) {
204
+ normalizedRecord.payment = (0, import_orderCollectionIdentity.normalizeOrderCollection)(
205
+ "payment",
206
+ normalizedRecord.payment,
207
+ options
208
+ ).items;
209
+ }
210
+ return normalizedRecord;
211
+ }
212
+ function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, strategy) {
213
+ const normalizedLoadedRecord = normalizeBaseSalesRecordCollections(
214
+ loadedRecord || {},
215
+ { ensureUid: false }
216
+ );
217
+ if (!currentTempOrder) {
218
+ return normalizeBaseSalesRecordCollections(normalizedLoadedRecord);
219
+ }
220
+ const normalizedCurrentTempOrder = normalizeBaseSalesRecordCollections(
221
+ currentTempOrder,
222
+ { ensureUid: false }
223
+ );
224
+ const uidRemaps = [];
130
225
  const mergedRecord = (0, import_lodash_es.mergeWith)(
131
- (0, import_lodash_es.cloneDeep)(currentTempOrder),
132
- (0, import_lodash_es.cloneDeep)(loadedRecord || {}),
226
+ normalizedCurrentTempOrder,
227
+ normalizedLoadedRecord,
133
228
  (currentValue, loadedValue, key) => {
229
+ const collectionKind = getBaseSalesOrderCollectionKind(key);
230
+ if (collectionKind && Array.isArray(loadedValue)) {
231
+ return mergeBaseSalesOrderCollection(
232
+ collectionKind,
233
+ Array.isArray(currentValue) ? currentValue : [],
234
+ loadedValue,
235
+ strategy,
236
+ uidRemaps
237
+ );
238
+ }
134
239
  if (Array.isArray(currentValue) && Array.isArray(loadedValue)) {
135
- const metadataKey = getBaseSalesUniqueArrayMetadataKey(key);
136
- if (metadataKey) {
137
- return mergeBaseSalesUniqueArray(currentValue, loadedValue, metadataKey);
138
- }
139
240
  return [
140
241
  ...(0, import_lodash_es.cloneDeep)(currentValue),
141
242
  ...(0, import_lodash_es.cloneDeep)(loadedValue)
@@ -150,7 +251,9 @@ function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord) {
150
251
  }
151
252
  );
152
253
  delete mergedRecord.request_unique_idempotency_token;
153
- return mergedRecord;
254
+ return normalizeBaseSalesRecordCollections(
255
+ (0, import_orderCollectionIdentity.applyOrderCollectionUidRemaps)(mergedRecord, uidRemaps)
256
+ );
154
257
  }
155
258
  var BaseSalesImpl = class extends import_BaseModule.BaseModule {
156
259
  constructor(name, version) {
@@ -172,9 +275,13 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
172
275
  this.queuedAutoPaymentSync = false;
173
276
  this.autoPaymentSyncFlushing = false;
174
277
  this.autoPaymentSyncTimer = null;
175
- this.salesDetailLoadInFlight = false;
278
+ this.salesDetailLoadInFlightCount = 0;
279
+ this.salesDetailLoadSequence = 0;
280
+ this.salesDetailHydrateQueue = Promise.resolve();
176
281
  this.serverOrderChangeUnsubscribe = null;
177
282
  this.serverOrderChangeHydrateInFlight = null;
283
+ this.queuedServerOrderChanges = [];
284
+ this.serverOrderChangeSyncEnabled = false;
178
285
  }
179
286
  /**
180
287
  * 子类可覆盖此方法以追加/收敛要注册的子模块。
@@ -248,19 +355,59 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
248
355
  ].filter(Boolean);
249
356
  return currentRecords.some((record) => isSameBaseSalesOrderRecord(record, order));
250
357
  }
358
+ setServerOrderChangeSyncEnabled(enabled) {
359
+ this.serverOrderChangeSyncEnabled = enabled;
360
+ if (!enabled)
361
+ this.queuedServerOrderChanges = [];
362
+ }
363
+ queueLatestServerOrderChange(order) {
364
+ const snapshot = (0, import_lodash_es.cloneDeep)(order);
365
+ const existingIndex = this.queuedServerOrderChanges.findIndex(
366
+ (queued) => isSameBaseSalesOrderRecord(queued, snapshot)
367
+ );
368
+ if (existingIndex >= 0) {
369
+ this.queuedServerOrderChanges[existingIndex] = snapshot;
370
+ return;
371
+ }
372
+ this.queuedServerOrderChanges.push(snapshot);
373
+ }
374
+ async drainQueuedServerOrderChanges() {
375
+ if (!this.serverOrderChangeSyncEnabled || this.salesDetailLoadInFlightCount > 0 || this.serverOrderChangeHydrateInFlight || this.queuedServerOrderChanges.length === 0)
376
+ return;
377
+ const changedOrders = this.queuedServerOrderChanges;
378
+ this.queuedServerOrderChanges = [];
379
+ await this.syncCurrentSalesDetailFromServerOrderChange({ changedOrders });
380
+ }
251
381
  async syncCurrentSalesDetailFromServerOrderChange(payload) {
252
382
  const changedOrders = Array.isArray(payload) ? payload : Array.isArray(payload == null ? void 0 : payload.changedOrders) ? payload.changedOrders : (payload == null ? void 0 : payload.data) && typeof payload.data === "object" ? [payload.data] : [];
253
- if (this.salesDetailLoadInFlight || this.serverOrderChangeHydrateInFlight)
383
+ if (!this.serverOrderChangeSyncEnabled)
254
384
  return;
385
+ if (this.salesDetailLoadInFlightCount > 0) {
386
+ changedOrders.forEach(
387
+ (order) => this.queueLatestServerOrderChange(order)
388
+ );
389
+ return;
390
+ }
255
391
  const changedOrder = changedOrders.find(
256
392
  (order) => this.isCurrentSalesOrderRecord(order)
257
393
  );
258
394
  if (!changedOrder)
259
395
  return;
396
+ if (this.serverOrderChangeHydrateInFlight) {
397
+ this.queueLatestServerOrderChange(changedOrder);
398
+ return;
399
+ }
260
400
  if (!this.store.order)
261
401
  return;
262
402
  this.serverOrderChangeHydrateInFlight = (async () => {
263
- const sales = await this.store.order.hydrateTempOrderFromRecord(changedOrder, {
403
+ var _a, _b;
404
+ const currentTempOrder = (_b = (_a = this.store.order).getTempOrder) == null ? void 0 : _b.call(_a);
405
+ const record = mergeSalesDetailRecordWithTempOrder(
406
+ currentTempOrder,
407
+ changedOrder,
408
+ "authoritative-incoming"
409
+ );
410
+ const sales = await this.store.order.hydrateTempOrderFromRecord(record, {
264
411
  recalcOnHydrate: false
265
412
  });
266
413
  this.currentSalesDetail = sales;
@@ -270,6 +417,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
270
417
  await this.serverOrderChangeHydrateInFlight;
271
418
  } finally {
272
419
  this.serverOrderChangeHydrateInFlight = null;
420
+ await this.drainQueuedServerOrderChanges();
273
421
  }
274
422
  }
275
423
  registerServerOrderChangeSync() {
@@ -279,13 +427,42 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
279
427
  if (!effects || typeof effects.on !== "function")
280
428
  return;
281
429
  this.serverOrderChangeUnsubscribe = effects.on(SERVER_ORDER_CHANGED_EVENT, (payload) => {
282
- void this.syncCurrentSalesDetailFromServerOrderChange(payload).catch((error) => {
430
+ return this.syncCurrentSalesDetailFromServerOrderChange(payload).catch((error) => {
283
431
  this.logError("sync sales detail from server order change failed", {
284
432
  error: error instanceof Error ? error.message : String(error)
285
433
  });
286
434
  });
287
435
  });
288
436
  }
437
+ async hydrateLatestLoadedSalesDetail(record, loadSequence) {
438
+ let hydratedSales = null;
439
+ let skippedBeforeHydrate = false;
440
+ const hydrateTask = async () => {
441
+ if (loadSequence !== this.salesDetailLoadSequence) {
442
+ skippedBeforeHydrate = true;
443
+ return;
444
+ }
445
+ if (!this.store.order)
446
+ throw new Error("order 模块未初始化");
447
+ const sales = await this.store.order.hydrateTempOrderFromRecord(record, {
448
+ recalcOnHydrate: false
449
+ });
450
+ hydratedSales = sales;
451
+ if (loadSequence !== this.salesDetailLoadSequence)
452
+ return;
453
+ this.currentSalesDetail = sales;
454
+ await this.core.effects.emit(`${this.name}:onSalesOrderLoaded`, { sales });
455
+ };
456
+ const queuedTask = this.salesDetailHydrateQueue.then(hydrateTask, hydrateTask);
457
+ this.salesDetailHydrateQueue = queuedTask.then(
458
+ () => void 0,
459
+ () => void 0
460
+ );
461
+ await queuedTask;
462
+ if (skippedBeforeHydrate)
463
+ return record;
464
+ return hydratedSales || this.currentSalesDetail || record;
465
+ }
289
466
  hydrateOrderPaymentNames(payments) {
290
467
  if (!payments.length)
291
468
  return [];
@@ -720,6 +897,8 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
720
897
  }
721
898
  });
722
899
  this.currentSalesDetail = null;
900
+ this.queuedServerOrderChanges = [];
901
+ this.salesDetailLoadSequence += 1;
723
902
  await this.core.effects.emit(`${this.name}:onDestroy`, {});
724
903
  super.destroy();
725
904
  }
@@ -733,7 +912,8 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
733
912
  async loadSalesDetail(orderIdOrParams) {
734
913
  if (!this.store.order)
735
914
  throw new Error("order 模块未初始化");
736
- this.salesDetailLoadInFlight = true;
915
+ const loadSequence = ++this.salesDetailLoadSequence;
916
+ this.salesDetailLoadInFlightCount += 1;
737
917
  try {
738
918
  const params = typeof orderIdOrParams === "object" ? orderIdOrParams : { orderId: orderIdOrParams };
739
919
  const externalSaleNumber = params.externalSaleNumber || params.external_sale_number;
@@ -752,15 +932,26 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
752
932
  }
753
933
  const remoteRecord = preloadedSalesDetail ?? loadedRecord.data;
754
934
  const currentTempOrder = params.merge ? this.store.order.getTempOrder() : null;
755
- const record = params.merge ? mergeSalesDetailRecordWithTempOrder(currentTempOrder, remoteRecord) : remoteRecord;
756
- const sales = await this.store.order.hydrateTempOrderFromRecord(record, {
757
- recalcOnHydrate: false
758
- });
759
- this.currentSalesDetail = sales;
760
- await this.core.effects.emit(`${this.name}:onSalesOrderLoaded`, { sales });
761
- return sales;
935
+ const record = params.merge ? mergeSalesDetailRecordWithTempOrder(
936
+ currentTempOrder,
937
+ remoteRecord,
938
+ "preserve-local"
939
+ ) : remoteRecord;
940
+ return await this.hydrateLatestLoadedSalesDetail(record, loadSequence);
762
941
  } finally {
763
- this.salesDetailLoadInFlight = false;
942
+ this.salesDetailLoadInFlightCount = Math.max(
943
+ 0,
944
+ this.salesDetailLoadInFlightCount - 1
945
+ );
946
+ if (this.salesDetailLoadInFlightCount === 0) {
947
+ try {
948
+ await this.drainQueuedServerOrderChanges();
949
+ } catch (error) {
950
+ this.logError("drain queued server order changes failed", {
951
+ error: error instanceof Error ? error.message : String(error)
952
+ });
953
+ }
954
+ }
764
955
  }
765
956
  }
766
957
  /** 获取当前已加载的销售详情(只读快照)。未加载过则返回 null。 */
@@ -1094,7 +1285,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1094
1285
  });
1095
1286
  }
1096
1287
  async loadDiscountConfig(params) {
1097
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
1288
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
1098
1289
  if (!this.store.order)
1099
1290
  throw new Error("order 模块未初始化");
1100
1291
  const tempOrder = this.store.order.ensureTempOrder();
@@ -1132,7 +1323,8 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1132
1323
  if (hasManualDiscountSelection && currentDiscountList.length) {
1133
1324
  return {
1134
1325
  productList: tempOrder.products || [],
1135
- discountList: currentDiscountList
1326
+ discountList: currentDiscountList,
1327
+ availableWalletIds: this.getAvailableWalletIds()
1136
1328
  };
1137
1329
  }
1138
1330
  let nextDiscountList = this.mergeHistoricalDiscountList(
@@ -1161,17 +1353,17 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1161
1353
  });
1162
1354
  return {
1163
1355
  productList: tempOrder.products || [],
1164
- discountList: nextDiscountList
1356
+ discountList: nextDiscountList,
1357
+ availableWalletIds: this.getAvailableWalletIds()
1165
1358
  };
1166
1359
  }
1167
1360
  if (rulesModule) {
1168
- const holders = ((_l = tempOrder.holder) == null ? void 0 : _l.form_record) || [];
1169
- const orderTotalAmount = Number(((_m = orderStore.summary) == null ? void 0 : _m.total_amount) || 0);
1361
+ const orderTotalAmount = Number(((_l = orderStore.summary) == null ? void 0 : _l.total_amount) || 0);
1170
1362
  console.log("[BaseSales.applyDiscountConfig.calcDiscount]");
1171
1363
  const result = rulesModule.calcDiscount({
1172
1364
  productList: tempOrder.products,
1173
1365
  discountList: nextDiscountList,
1174
- holders,
1366
+ holders: [],
1175
1367
  isFormSubject: (product) => (0, import_utils.resolveIsFormSubject)(tempOrder, product),
1176
1368
  orderTotalAmount
1177
1369
  }) || { productList: tempOrder.products, discountList: nextDiscountList };
@@ -1182,8 +1374,8 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1182
1374
  );
1183
1375
  }
1184
1376
  for (const product of tempOrder.products || []) {
1185
- const productUid = (_n = product.metadata) == null ? void 0 : _n.unique_identification_number;
1186
- const runtimeOrigin = productUid ? (_q = (_p = (_o = tempOrder._extend) == null ? void 0 : _o.productsByUid) == null ? void 0 : _p[productUid]) == null ? void 0 : _q.origin : void 0;
1377
+ const productUid = (_m = product.metadata) == null ? void 0 : _m.unique_identification_number;
1378
+ const runtimeOrigin = productUid ? (_p = (_o = (_n = tempOrder._extend) == null ? void 0 : _n.productsByUid) == null ? void 0 : _o[productUid]) == null ? void 0 : _p.origin : void 0;
1187
1379
  if ((runtimeOrigin == null ? void 0 : runtimeOrigin.isManualDiscount) || isBaseSalesManualProductDiscountProduct(product))
1188
1380
  continue;
1189
1381
  const totalPerUnitDiscount = (product.discount_list || []).reduce(
@@ -1191,7 +1383,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1191
1383
  0
1192
1384
  );
1193
1385
  const optionSum = (0, import_utils.sumOptionUnitPrice)((0, import_utils.getProductSkuOptions)(product));
1194
- const sourcePrice = ((_r = product.metadata) == null ? void 0 : _r.source_product_price) ?? (((_s = product.metadata) == null ? void 0 : _s.main_product_original_price) != null ? new import_decimal.default(Number(product.metadata.main_product_original_price) || 0).minus(optionSum).toFixed(2) : product.original_price ?? "0");
1386
+ const sourcePrice = ((_q = product.metadata) == null ? void 0 : _q.source_product_price) ?? (((_r = product.metadata) == null ? void 0 : _r.main_product_original_price) != null ? new import_decimal.default(Number(product.metadata.main_product_original_price) || 0).minus(optionSum).toFixed(2) : product.original_price ?? "0");
1195
1387
  const newSourceSellingPrice = new import_decimal.default(Number(sourcePrice) || 0).minus(totalPerUnitDiscount).toDecimalPlaces(2).toString();
1196
1388
  const newMainSellingPrice = new import_decimal.default(Number(newSourceSellingPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
1197
1389
  if (product.metadata) {
@@ -1223,11 +1415,18 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1223
1415
  });
1224
1416
  return {
1225
1417
  productList: tempOrder.products || [],
1226
- discountList: nextDiscountList
1418
+ discountList: nextDiscountList,
1419
+ availableWalletIds: this.getAvailableWalletIds()
1227
1420
  };
1228
1421
  }
1422
+ getAvailableWalletIds() {
1423
+ var _a;
1424
+ if (!((_a = this.store.order) == null ? void 0 : _a.getAvailableWalletIds))
1425
+ return [];
1426
+ return this.store.order.getAvailableWalletIds();
1427
+ }
1229
1428
  async bestDiscount(cb) {
1230
- var _a, _b, _c, _d;
1429
+ var _a, _b, _c;
1231
1430
  if (!this.store.order)
1232
1431
  throw new Error("order 模块未初始化");
1233
1432
  const tempOrder = this.store.order.ensureTempOrder();
@@ -1246,13 +1445,12 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1246
1445
  };
1247
1446
  await (discountModule == null ? void 0 : discountModule.setDiscountList(nextDiscountList));
1248
1447
  if (rulesModule) {
1249
- const holders = ((_c = tempOrder.holder) == null ? void 0 : _c.form_record) || [];
1250
- const orderTotalAmount = Number(((_d = orderStore.summary) == null ? void 0 : _d.total_amount) || 0);
1448
+ const orderTotalAmount = Number(((_c = orderStore.summary) == null ? void 0 : _c.total_amount) || 0);
1251
1449
  console.log("[BaseSales.bestDiscount.calcDiscount]");
1252
1450
  result = rulesModule.calcDiscount({
1253
1451
  productList: tempOrder.products,
1254
1452
  discountList: nextDiscountList,
1255
- holders,
1453
+ holders: [],
1256
1454
  isFormSubject: (product) => (0, import_utils.resolveIsFormSubject)(tempOrder, product),
1257
1455
  orderTotalAmount
1258
1456
  }) || result;
@@ -1278,6 +1476,12 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1278
1476
  }
1279
1477
  await this.store.order.recalculateSummary({ createIfMissing: true });
1280
1478
  this.store.order.persistTempOrder();
1479
+ await this.effectsEmit("onDiscountApplied", {
1480
+ productList: tempOrder.products || [],
1481
+ discountList: nextDiscountList,
1482
+ selectedDiscountList: tempOrder.discount_list || [],
1483
+ tempOrder
1484
+ });
1281
1485
  cb == null ? void 0 : cb(result);
1282
1486
  return result;
1283
1487
  }
@@ -1286,6 +1490,30 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1286
1490
  return [];
1287
1491
  return this.store.order.getDiscountList();
1288
1492
  }
1493
+ /** 读取 Discount 模块原始券列表(未经过 bookingTime 等过滤)。 */
1494
+ getOriginalDiscountList() {
1495
+ var _a, _b;
1496
+ if (!this.store.order)
1497
+ return [];
1498
+ const discountModule = (_a = this.store.order.store) == null ? void 0 : _a.discount;
1499
+ return ((_b = discountModule == null ? void 0 : discountModule.getOriginalDiscountList) == null ? void 0 : _b.call(discountModule)) || [];
1500
+ }
1501
+ /**
1502
+ * 同步写入 Discount 模块的 discountList / originalDiscountList。
1503
+ * 供 Holder 补全等场景在 OS store 层更新 machinecode holder 字段。
1504
+ */
1505
+ async replaceDiscountStoreLists(params) {
1506
+ var _a;
1507
+ if (!this.store.order)
1508
+ throw new Error("order 模块未初始化");
1509
+ const discountModule = (_a = this.store.order.store) == null ? void 0 : _a.discount;
1510
+ if (!discountModule)
1511
+ return;
1512
+ if (params.originalDiscountList) {
1513
+ await discountModule.setOriginalDiscountList(params.originalDiscountList);
1514
+ }
1515
+ await discountModule.setDiscountList(params.discountList);
1516
+ }
1289
1517
  async scanPromotionCode(code, customerId) {
1290
1518
  try {
1291
1519
  if (!this.store.order)
@@ -1294,11 +1522,19 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1294
1522
  if (raw.isAvailable) {
1295
1523
  await this.store.order.recalculateSummary({ createIfMissing: true });
1296
1524
  this.store.order.persistTempOrder();
1525
+ const tempOrder = this.store.order.ensureTempOrder();
1526
+ await this.effectsEmit("onDiscountApplied", {
1527
+ productList: tempOrder.products || [],
1528
+ discountList: this.store.order.getDiscountList(),
1529
+ selectedDiscountList: tempOrder.discount_list || [],
1530
+ tempOrder
1531
+ });
1297
1532
  }
1298
1533
  return {
1299
1534
  isAvailable: raw.isAvailable,
1300
1535
  type: raw.type,
1301
- unavailableReason: raw.unavailableReason
1536
+ unavailableReason: raw.unavailableReason,
1537
+ scannedDiscountList: raw.scannedDiscountList
1302
1538
  };
1303
1539
  } catch (error) {
1304
1540
  throw error;
@@ -1306,7 +1542,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1306
1542
  }
1307
1543
  // TODO 需要优化,这段逻辑太长了,里面应该有些历史遗留问题
1308
1544
  async setDiscountSelected(params) {
1309
- var _a, _b, _c, _d, _e, _f, _g, _h;
1545
+ var _a, _b, _c, _d, _e, _f, _g;
1310
1546
  try {
1311
1547
  if (!this.store.order)
1312
1548
  throw new Error("order 模块未初始化");
@@ -1323,17 +1559,16 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1323
1559
  const orderStore = this.store.order.store || {};
1324
1560
  const discountModule = orderStore.discount;
1325
1561
  const rulesModule = orderStore.rules;
1326
- const holders = ((_a = tempOrder.holder) == null ? void 0 : _a.form_record) || [];
1327
1562
  let nextDiscountList = updated;
1328
1563
  await (discountModule == null ? void 0 : discountModule.setDiscountList(updated));
1329
1564
  if (rulesModule) {
1330
- const orderTotalAmount = Number(((_b = orderStore.summary) == null ? void 0 : _b.total_amount) || 0);
1565
+ const orderTotalAmount = Number(((_a = orderStore.summary) == null ? void 0 : _a.total_amount) || 0);
1331
1566
  console.log("[BaseSales.setDiscountSelected.calcDiscount]");
1332
1567
  const result = rulesModule.calcDiscount(
1333
1568
  {
1334
1569
  productList: tempOrder.products,
1335
1570
  discountList: updated,
1336
- holders,
1571
+ holders: [],
1337
1572
  isFormSubject: (product) => (0, import_utils.resolveIsFormSubject)(tempOrder, product),
1338
1573
  orderTotalAmount
1339
1574
  },
@@ -1373,8 +1608,8 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1373
1608
  return rid != null && selectedResourceIds.has(rid);
1374
1609
  });
1375
1610
  for (const product of tempOrder.products) {
1376
- const productUid = (_c = product.metadata) == null ? void 0 : _c.unique_identification_number;
1377
- const runtimeOrigin = productUid ? (_f = (_e = (_d = tempOrder._extend) == null ? void 0 : _d.productsByUid) == null ? void 0 : _e[productUid]) == null ? void 0 : _f.origin : void 0;
1611
+ const productUid = (_b = product.metadata) == null ? void 0 : _b.unique_identification_number;
1612
+ const runtimeOrigin = productUid ? (_e = (_d = (_c = tempOrder._extend) == null ? void 0 : _c.productsByUid) == null ? void 0 : _d[productUid]) == null ? void 0 : _e.origin : void 0;
1378
1613
  if ((runtimeOrigin == null ? void 0 : runtimeOrigin.isManualDiscount) || isBaseSalesManualProductDiscountProduct(product))
1379
1614
  continue;
1380
1615
  product.discount_list = filterSelectedDiscountDetails(product.discount_list);
@@ -1398,7 +1633,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1398
1633
  0
1399
1634
  );
1400
1635
  const optionSum = (0, import_utils.sumOptionUnitPrice)((0, import_utils.getProductSkuOptions)(product));
1401
- const sourcePrice = ((_g = product.metadata) == null ? void 0 : _g.source_product_price) ?? (((_h = product.metadata) == null ? void 0 : _h.main_product_original_price) != null ? new import_decimal.default(Number(product.metadata.main_product_original_price) || 0).minus(optionSum).toFixed(2) : product.original_price ?? "0");
1636
+ const sourcePrice = ((_f = product.metadata) == null ? void 0 : _f.source_product_price) ?? (((_g = product.metadata) == null ? void 0 : _g.main_product_original_price) != null ? new import_decimal.default(Number(product.metadata.main_product_original_price) || 0).minus(optionSum).toFixed(2) : product.original_price ?? "0");
1402
1637
  const newSourceSellingPrice = new import_decimal.default(Number(sourcePrice) || 0).minus(totalPerUnitDiscount).toDecimalPlaces(2).toString();
1403
1638
  const newMainSellingPrice = new import_decimal.default(Number(newSourceSellingPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
1404
1639
  if (product.metadata) {
@@ -1452,6 +1687,12 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1452
1687
  }
1453
1688
  await this.store.order.recalculateSummary({ createIfMissing: true });
1454
1689
  this.store.order.persistTempOrder();
1690
+ await this.effectsEmit("onDiscountApplied", {
1691
+ productList: tempOrder.products || [],
1692
+ discountList: result.discountList || [],
1693
+ selectedDiscountList: tempOrder.discount_list || [],
1694
+ tempOrder
1695
+ });
1455
1696
  } catch (error) {
1456
1697
  throw error;
1457
1698
  }
@@ -1,3 +1,4 @@
1
+ import type { Discount } from '../../modules/Discount/types';
1
2
  import { OrderModule, ProductList, QuotationModule, SalesSummaryModule, ScanOrderLoggerModule as BaseSalesLoggerModule, ScheduleModule } from '../../modules';
2
3
  import type { ScanOrderLogInput as BaseSalesLogInput, ScanOrderLoggerProviderConfig as BaseSalesLoggerProviderConfig, ScanOrderLoggerProviderType as BaseSalesLoggerProviderType } from '../../modules/ScanOrderLogger/types';
3
4
  import type { QuantityCheckResult, QuantityLimitResult } from '../../model/strategy/adapter/itemRule';
@@ -85,6 +86,8 @@ export interface BaseSalesOrderProduct extends BaseSalesOrderProductIdentity {
85
86
  booking_uid?: string;
86
87
  /** 多语言商品标题快照,随 tempOrder 提交到 checkout。 */
87
88
  product_title?: BaseSalesLocalizedText;
89
+ /** 商品封面图快照,随 tempOrder 提交到 checkout。 */
90
+ product_cover?: string;
88
91
  /**
89
92
  * 行级扩展 metadata。价格相关的权威字段:
90
93
  *
@@ -381,9 +384,11 @@ export interface BaseSalesLoggerRuntimeConfig {
381
384
  }
382
385
  export interface BaseSalesAddLogParams extends BaseSalesLogInput {
383
386
  }
384
- /** BaseSales.scanPromotionCode 对外的轻量结果(不含 discountList,UI resolve 后调 getDiscountList) */
387
+ /** BaseSales.scanPromotionCode 对外的轻量结果(UI 可用 getDiscountList scannedDiscountList 补 Holder) */
385
388
  export interface BaseSalesScanCodeResult {
386
389
  isAvailable: boolean;
387
390
  type?: 'server' | string;
388
391
  unavailableReason?: 'time_limit' | string;
392
+ /** batchSearch 原始结果;isAvailable=false 时 discountList 可能未写入 store,Holder 补全需读此字段 */
393
+ scannedDiscountList?: Discount[];
389
394
  }
@@ -78,7 +78,6 @@ export interface CartPromotionEvaluator {
78
78
  }>;
79
79
  hasApplicableStrategy: boolean;
80
80
  }>;
81
- getStrategyConfigs?: () => unknown[];
82
81
  }
83
82
  /** 单个赠品减量项 */
84
83
  export interface GiftReduceItem {
@@ -268,8 +267,7 @@ export declare function appendPromotionTags<T extends Record<string, any>>(produ
268
267
  * 处理购物车的促销计算与赠品差异化。
269
268
  *
270
269
  * 流程:
271
- * 1. 分离 main / gift / edit-product 三类(普通促销下带 `booking_id` 的编辑态商品不参与;
272
- * 配置 ITEM_REWARD 时,编辑态商品也参与商品奖励重算)
270
+ * 1. 分离 main / gift / edit-product 三类(带 `booking_id` 的编辑态商品不参与促销)
273
271
  * 2. 转 PromotionProduct 喂评估器 → 拿到 `evaluateCartWithPricing` 与 `getProductsApplicableStrategies`
274
272
  * 3. 把 PricedProduct 映射回 OrderProduct 形态:
275
273
  * - 参与促销的,写 `metadata._promotion` + 调整 `selling_price` / `main_product_selling_price`