@pisell/pisellos 2.3.28 → 2.3.30

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 (57) hide show
  1. package/dist/modules/Order/index.d.ts +32 -8
  2. package/dist/modules/Order/index.js +883 -521
  3. package/dist/modules/Order/payment-utils.d.ts +23 -0
  4. package/dist/modules/Order/payment-utils.js +203 -0
  5. package/dist/modules/Order/types.d.ts +33 -1
  6. package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  7. package/dist/modules/Order/utils/orderCollectionIdentity.js +405 -0
  8. package/dist/modules/Order/utils.d.ts +2 -1
  9. package/dist/modules/Order/utils.js +6 -12
  10. package/dist/modules/Payment/index.d.ts +2 -0
  11. package/dist/modules/Payment/index.js +78 -49
  12. package/dist/modules/Payment/types.d.ts +26 -24
  13. package/dist/modules/Payment/types.js +2 -0
  14. package/dist/modules/Product/index.d.ts +1 -1
  15. package/dist/server/index.d.ts +14 -0
  16. package/dist/server/index.js +1826 -1000
  17. package/dist/server/modules/order/index.d.ts +47 -4
  18. package/dist/server/modules/order/index.js +1415 -711
  19. package/dist/server/modules/order/types.d.ts +11 -3
  20. package/dist/server/modules/order/types.js +1 -1
  21. package/dist/solution/BaseSales/index.d.ts +40 -4
  22. package/dist/solution/BaseSales/index.js +1268 -749
  23. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +9 -4
  24. package/dist/solution/BaseSales/utils/parseSalesResponse.js +6 -8
  25. package/dist/solution/BookingByStep/index.d.ts +1 -1
  26. package/dist/solution/BookingTicket/index.d.ts +13 -1
  27. package/dist/solution/BookingTicket/index.js +2 -1
  28. package/dist/utils/payment-number.d.ts +9 -0
  29. package/dist/utils/payment-number.js +69 -0
  30. package/lib/modules/Order/index.d.ts +32 -8
  31. package/lib/modules/Order/index.js +270 -64
  32. package/lib/modules/Order/payment-utils.d.ts +23 -0
  33. package/lib/modules/Order/payment-utils.js +193 -0
  34. package/lib/modules/Order/types.d.ts +33 -1
  35. package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  36. package/lib/modules/Order/utils/orderCollectionIdentity.js +415 -0
  37. package/lib/modules/Order/utils.d.ts +2 -1
  38. package/lib/modules/Order/utils.js +6 -14
  39. package/lib/modules/Payment/index.d.ts +2 -0
  40. package/lib/modules/Payment/index.js +33 -12
  41. package/lib/modules/Payment/types.d.ts +26 -24
  42. package/lib/modules/Product/index.d.ts +1 -1
  43. package/lib/server/index.d.ts +14 -0
  44. package/lib/server/index.js +604 -8
  45. package/lib/server/modules/order/index.d.ts +47 -4
  46. package/lib/server/modules/order/index.js +578 -116
  47. package/lib/server/modules/order/types.d.ts +11 -3
  48. package/lib/solution/BaseSales/index.d.ts +40 -4
  49. package/lib/solution/BaseSales/index.js +363 -37
  50. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +9 -4
  51. package/lib/solution/BaseSales/utils/parseSalesResponse.js +4 -5
  52. package/lib/solution/BookingByStep/index.d.ts +1 -1
  53. package/lib/solution/BookingTicket/index.d.ts +13 -1
  54. package/lib/solution/BookingTicket/index.js +2 -1
  55. package/lib/utils/payment-number.d.ts +9 -0
  56. package/lib/utils/payment-number.js +64 -0
  57. package/package.json +1 -1
@@ -40,6 +40,9 @@ 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_payment_utils = require("../../modules/Order/payment-utils");
44
+ var import_payment_number = require("../../utils/payment-number");
45
+ var import_orderCollectionIdentity = require("../../modules/Order/utils/orderCollectionIdentity");
43
46
  var import_dayjs = __toESM(require("dayjs"));
44
47
  __reExport(BaseSales_exports, require("./types"), module.exports);
45
48
  var import_Quotation = require("../../modules/Quotation");
@@ -79,8 +82,20 @@ function isBaseSalesHistoricalDiscountEntry(discount) {
79
82
  function getBaseSalesUniqueArrayMetadataKey(key) {
80
83
  if (key === "products" || key === "bookings")
81
84
  return "unique_identification_number";
85
+ return null;
86
+ }
87
+ var BASE_SALES_PERSISTENT_ID_FIELD_BY_KIND = {
88
+ product: "order_detail_id",
89
+ booking: "schedule_event_id",
90
+ payment: "order_payment_id"
91
+ };
92
+ function getBaseSalesOrderCollectionKind(key) {
93
+ if (key === "products")
94
+ return "product";
95
+ if (key === "bookings")
96
+ return "booking";
82
97
  if (key === "payments" || key === "payment")
83
- return "unique_payment_number";
98
+ return "payment";
84
99
  return null;
85
100
  }
86
101
  function isSameBaseSalesOrderRecord(left, right) {
@@ -95,13 +110,6 @@ function isSameBaseSalesOrderRecord(left, right) {
95
110
  (value) => value !== void 0 && value !== null && value !== "" && leftIdentities.has(String(value))
96
111
  );
97
112
  }
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);
104
- }
105
113
  function isBaseSalesBlankValue(value) {
106
114
  return value === void 0 || value === null || value === "";
107
115
  }
@@ -120,7 +128,102 @@ function mergeBaseSalesProductSnapshotFields(currentItem, loadedItem) {
120
128
  }
121
129
  return mergedItem;
122
130
  }
123
- function mergeBaseSalesUniqueArray(currentValue, loadedValue, metadataKey, arrayKey) {
131
+ function mergeBaseSalesCollectionItem(kind, currentItem, incomingItem, uidRemaps) {
132
+ const currentReferenceUid = (0, import_orderCollectionIdentity.getOrderCollectionReferenceUid)(kind, currentItem);
133
+ const incomingReferenceUid = (0, import_orderCollectionIdentity.getOrderCollectionReferenceUid)(kind, incomingItem);
134
+ const identityMergedItem = (0, import_orderCollectionIdentity.mergeOrderCollectionItems)(
135
+ kind,
136
+ currentItem,
137
+ incomingItem
138
+ );
139
+ const mergedUid = (0, import_orderCollectionIdentity.getOrderCollectionUid)(kind, identityMergedItem);
140
+ if (mergedUid) {
141
+ for (const from of /* @__PURE__ */ new Set([currentReferenceUid, incomingReferenceUid])) {
142
+ if (!from || from === mergedUid)
143
+ continue;
144
+ uidRemaps.push({
145
+ kind,
146
+ from,
147
+ to: mergedUid,
148
+ persistentId: (0, import_orderCollectionIdentity.getOrderCollectionPersistentId)(kind, incomingItem) || (0, import_orderCollectionIdentity.getOrderCollectionPersistentId)(kind, currentItem)
149
+ });
150
+ }
151
+ }
152
+ const mergedItem = mergedUid ? (0, import_orderCollectionIdentity.setOrderCollectionUid)(kind, incomingItem, mergedUid) : (0, import_lodash_es.cloneDeep)(incomingItem);
153
+ const persistentIdField = BASE_SALES_PERSISTENT_ID_FIELD_BY_KIND[kind];
154
+ if (isBaseSalesBlankValue(mergedItem[persistentIdField]) && !isBaseSalesBlankValue(identityMergedItem[persistentIdField])) {
155
+ mergedItem[persistentIdField] = identityMergedItem[persistentIdField];
156
+ }
157
+ return kind === "product" ? mergeBaseSalesProductSnapshotFields(currentItem, mergedItem) : mergedItem;
158
+ }
159
+ function mergeBaseSalesOrderCollection(kind, currentValue, incomingValue, strategy, uidRemaps) {
160
+ const currentItems = (0, import_orderCollectionIdentity.normalizeOrderCollection)(kind, currentValue, {
161
+ ensureUid: false
162
+ }).items;
163
+ const incomingItems = (0, import_orderCollectionIdentity.normalizeOrderCollection)(kind, incomingValue, {
164
+ ensureUid: false
165
+ }).items;
166
+ if (strategy === "authoritative-incoming") {
167
+ const result2 = (0, import_lodash_es.cloneDeep)(incomingItems);
168
+ currentItems.forEach((currentItem) => {
169
+ const incomingIndex = result2.findIndex(
170
+ (incomingItem) => (0, import_orderCollectionIdentity.isSameOrderCollectionItem)(kind, currentItem, incomingItem)
171
+ );
172
+ if (incomingIndex !== -1) {
173
+ result2[incomingIndex] = mergeBaseSalesCollectionItem(
174
+ kind,
175
+ currentItem,
176
+ result2[incomingIndex],
177
+ uidRemaps
178
+ );
179
+ return;
180
+ }
181
+ if (!(0, import_orderCollectionIdentity.getOrderCollectionPersistentId)(kind, currentItem)) {
182
+ result2.push((0, import_lodash_es.cloneDeep)(currentItem));
183
+ }
184
+ });
185
+ return result2;
186
+ }
187
+ const result = (0, import_lodash_es.cloneDeep)(currentItems);
188
+ incomingItems.forEach((incomingItem) => {
189
+ const currentIndex = result.findIndex(
190
+ (currentItem) => (0, import_orderCollectionIdentity.isSameOrderCollectionItem)(kind, currentItem, incomingItem)
191
+ );
192
+ if (currentIndex !== -1) {
193
+ result[currentIndex] = mergeBaseSalesCollectionItem(
194
+ kind,
195
+ result[currentIndex],
196
+ incomingItem,
197
+ uidRemaps
198
+ );
199
+ return;
200
+ }
201
+ result.push((0, import_lodash_es.cloneDeep)(incomingItem));
202
+ });
203
+ return result;
204
+ }
205
+ function normalizeBaseSalesRecordCollections(sourceRecord, options) {
206
+ const normalizedRecord = (0, import_orderCollectionIdentity.normalizeOrderCollections)(
207
+ (0, import_lodash_es.cloneDeep)(sourceRecord || {}),
208
+ options
209
+ ).order;
210
+ if (Array.isArray(normalizedRecord.payment)) {
211
+ normalizedRecord.payment = (0, import_orderCollectionIdentity.normalizeOrderCollection)(
212
+ "payment",
213
+ normalizedRecord.payment,
214
+ options
215
+ ).items;
216
+ }
217
+ return normalizedRecord;
218
+ }
219
+ function getBaseSalesMetadataUid(item, metadataKey) {
220
+ var _a;
221
+ const uid = ((_a = item == null ? void 0 : item.metadata) == null ? void 0 : _a[metadataKey]) ?? (item == null ? void 0 : item[metadataKey]);
222
+ if (uid === void 0 || uid === null || uid === "")
223
+ return null;
224
+ return String(uid);
225
+ }
226
+ function mergeBaseSalesUniqueArray(currentValue, loadedValue, metadataKey) {
124
227
  const result = [];
125
228
  const indexByUid = /* @__PURE__ */ new Map();
126
229
  const appendOrReplaceByUid = (item) => {
@@ -132,7 +235,7 @@ function mergeBaseSalesUniqueArray(currentValue, loadedValue, metadataKey, array
132
235
  }
133
236
  const existingIndex = indexByUid.get(uid);
134
237
  if (existingIndex !== void 0) {
135
- result[existingIndex] = arrayKey === "products" ? mergeBaseSalesProductSnapshotFields(result[existingIndex], clonedItem) : clonedItem;
238
+ result[existingIndex] = clonedItem;
136
239
  return;
137
240
  }
138
241
  indexByUid.set(uid, result.length);
@@ -142,17 +245,42 @@ function mergeBaseSalesUniqueArray(currentValue, loadedValue, metadataKey, array
142
245
  loadedValue.forEach(appendOrReplaceByUid);
143
246
  return result;
144
247
  }
145
- function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord) {
146
- if (!currentTempOrder)
147
- return loadedRecord;
248
+ function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, strategy) {
249
+ const normalizedLoadedRecord = normalizeBaseSalesRecordCollections(
250
+ loadedRecord || {},
251
+ { ensureUid: false }
252
+ );
253
+ if (!currentTempOrder) {
254
+ return normalizeBaseSalesRecordCollections(normalizedLoadedRecord);
255
+ }
256
+ const normalizedCurrentTempOrder = normalizeBaseSalesRecordCollections(
257
+ currentTempOrder,
258
+ { ensureUid: false }
259
+ );
260
+ const uidRemaps = [];
148
261
  const mergedRecord = (0, import_lodash_es.mergeWith)(
149
- (0, import_lodash_es.cloneDeep)(currentTempOrder),
150
- (0, import_lodash_es.cloneDeep)(loadedRecord || {}),
262
+ normalizedCurrentTempOrder,
263
+ normalizedLoadedRecord,
151
264
  (currentValue, loadedValue, key) => {
265
+ const collectionKind = getBaseSalesOrderCollectionKind(key);
266
+ if (collectionKind && Array.isArray(loadedValue)) {
267
+ return mergeBaseSalesOrderCollection(
268
+ collectionKind,
269
+ Array.isArray(currentValue) ? currentValue : [],
270
+ loadedValue,
271
+ strategy,
272
+ uidRemaps
273
+ );
274
+ }
152
275
  if (Array.isArray(currentValue) && Array.isArray(loadedValue)) {
276
+ if (key === "payments" || key === "payment") {
277
+ return (0, import_payment_utils.mergeOrderPaymentListsByIdentity)(currentValue, loadedValue, {
278
+ preserveUnmatchedExisting: true
279
+ });
280
+ }
153
281
  const metadataKey = getBaseSalesUniqueArrayMetadataKey(key);
154
282
  if (metadataKey) {
155
- return mergeBaseSalesUniqueArray(currentValue, loadedValue, metadataKey, key);
283
+ return mergeBaseSalesUniqueArray(currentValue, loadedValue, metadataKey);
156
284
  }
157
285
  return [
158
286
  ...(0, import_lodash_es.cloneDeep)(currentValue),
@@ -168,7 +296,9 @@ function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord) {
168
296
  }
169
297
  );
170
298
  delete mergedRecord.request_unique_idempotency_token;
171
- return mergedRecord;
299
+ return normalizeBaseSalesRecordCollections(
300
+ (0, import_orderCollectionIdentity.applyOrderCollectionUidRemaps)(mergedRecord, uidRemaps)
301
+ );
172
302
  }
173
303
  var BaseSalesImpl = class extends import_BaseModule.BaseModule {
174
304
  constructor(name, version) {
@@ -190,9 +320,12 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
190
320
  this.queuedAutoPaymentSync = false;
191
321
  this.autoPaymentSyncFlushing = false;
192
322
  this.autoPaymentSyncTimer = null;
193
- this.salesDetailLoadInFlight = false;
323
+ this.salesDetailLoadInFlightCount = 0;
324
+ this.salesDetailLoadSequence = 0;
325
+ this.salesDetailHydrateQueue = Promise.resolve();
194
326
  this.serverOrderChangeUnsubscribe = null;
195
327
  this.serverOrderChangeHydrateInFlight = null;
328
+ this.queuedServerOrderChanges = [];
196
329
  this.serverOrderChangeSyncEnabled = false;
197
330
  }
198
331
  /**
@@ -269,22 +402,56 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
269
402
  }
270
403
  setServerOrderChangeSyncEnabled(enabled) {
271
404
  this.serverOrderChangeSyncEnabled = enabled;
405
+ if (!enabled)
406
+ this.queuedServerOrderChanges = [];
407
+ }
408
+ queueLatestServerOrderChange(order) {
409
+ const snapshot = (0, import_lodash_es.cloneDeep)(order);
410
+ const existingIndex = this.queuedServerOrderChanges.findIndex(
411
+ (queued) => isSameBaseSalesOrderRecord(queued, snapshot)
412
+ );
413
+ if (existingIndex >= 0) {
414
+ this.queuedServerOrderChanges[existingIndex] = snapshot;
415
+ return;
416
+ }
417
+ this.queuedServerOrderChanges.push(snapshot);
418
+ }
419
+ async drainQueuedServerOrderChanges() {
420
+ if (!this.serverOrderChangeSyncEnabled || this.salesDetailLoadInFlightCount > 0 || this.serverOrderChangeHydrateInFlight || this.queuedServerOrderChanges.length === 0)
421
+ return;
422
+ const changedOrders = this.queuedServerOrderChanges;
423
+ this.queuedServerOrderChanges = [];
424
+ await this.syncCurrentSalesDetailFromServerOrderChange({ changedOrders });
272
425
  }
273
426
  async syncCurrentSalesDetailFromServerOrderChange(payload) {
274
427
  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] : [];
275
- if (!this.serverOrderChangeSyncEnabled || this.salesDetailLoadInFlight || this.serverOrderChangeHydrateInFlight)
428
+ if (!this.serverOrderChangeSyncEnabled)
429
+ return;
430
+ if (this.salesDetailLoadInFlightCount > 0) {
431
+ changedOrders.forEach(
432
+ (order) => this.queueLatestServerOrderChange(order)
433
+ );
276
434
  return;
435
+ }
277
436
  const changedOrder = changedOrders.find(
278
437
  (order) => this.isCurrentSalesOrderRecord(order)
279
438
  );
280
439
  if (!changedOrder)
281
440
  return;
441
+ if (this.serverOrderChangeHydrateInFlight) {
442
+ this.queueLatestServerOrderChange(changedOrder);
443
+ return;
444
+ }
282
445
  if (!this.store.order)
283
446
  return;
284
447
  this.serverOrderChangeHydrateInFlight = (async () => {
285
448
  var _a, _b;
286
449
  const currentTempOrder = (_b = (_a = this.store.order).getTempOrder) == null ? void 0 : _b.call(_a);
287
- const record = mergeSalesDetailRecordWithTempOrder(currentTempOrder, changedOrder);
450
+ const record = mergeSalesDetailRecordWithTempOrder(
451
+ currentTempOrder,
452
+ changedOrder,
453
+ "authoritative-incoming"
454
+ );
288
455
  const sales = await this.store.order.hydrateTempOrderFromRecord(record, {
289
456
  recalcOnHydrate: false
290
457
  });
@@ -295,6 +462,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
295
462
  await this.serverOrderChangeHydrateInFlight;
296
463
  } finally {
297
464
  this.serverOrderChangeHydrateInFlight = null;
465
+ await this.drainQueuedServerOrderChanges();
298
466
  }
299
467
  }
300
468
  registerServerOrderChangeSync() {
@@ -304,13 +472,42 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
304
472
  if (!effects || typeof effects.on !== "function")
305
473
  return;
306
474
  this.serverOrderChangeUnsubscribe = effects.on(SERVER_ORDER_CHANGED_EVENT, (payload) => {
307
- void this.syncCurrentSalesDetailFromServerOrderChange(payload).catch((error) => {
475
+ return this.syncCurrentSalesDetailFromServerOrderChange(payload).catch((error) => {
308
476
  this.logError("sync sales detail from server order change failed", {
309
477
  error: error instanceof Error ? error.message : String(error)
310
478
  });
311
479
  });
312
480
  });
313
481
  }
482
+ async hydrateLatestLoadedSalesDetail(record, loadSequence) {
483
+ let hydratedSales = null;
484
+ let skippedBeforeHydrate = false;
485
+ const hydrateTask = async () => {
486
+ if (loadSequence !== this.salesDetailLoadSequence) {
487
+ skippedBeforeHydrate = true;
488
+ return;
489
+ }
490
+ if (!this.store.order)
491
+ throw new Error("order 模块未初始化");
492
+ const sales = await this.store.order.hydrateTempOrderFromRecord(record, {
493
+ recalcOnHydrate: false
494
+ });
495
+ hydratedSales = sales;
496
+ if (loadSequence !== this.salesDetailLoadSequence)
497
+ return;
498
+ this.currentSalesDetail = sales;
499
+ await this.core.effects.emit(`${this.name}:onSalesOrderLoaded`, { sales });
500
+ };
501
+ const queuedTask = this.salesDetailHydrateQueue.then(hydrateTask, hydrateTask);
502
+ this.salesDetailHydrateQueue = queuedTask.then(
503
+ () => void 0,
504
+ () => void 0
505
+ );
506
+ await queuedTask;
507
+ if (skippedBeforeHydrate)
508
+ return record;
509
+ return hydratedSales || this.currentSalesDetail || record;
510
+ }
314
511
  hydrateOrderPaymentNames(payments) {
315
512
  if (!payments.length)
316
513
  return [];
@@ -644,6 +841,16 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
644
841
  const coreData = (_f = (_d = (_c = app == null ? void 0 : app.models) == null ? void 0 : _c.getStore) == null ? void 0 : (_e = _d.call(_c)).getDataByModel) == null ? void 0 : _f.call(_e, "core", "core");
645
842
  return coreData == null ? void 0 : coreData[key];
646
843
  }
844
+ getPaymentNumberDevicePrefixSync() {
845
+ if (!this.appPlugin)
846
+ return "LOCAL";
847
+ return (0, import_payment_number.resolvePaymentNumberDevicePrefixFromDeviceId)((key) => this.getAppData(key));
848
+ }
849
+ getPaymentNumberDevicePrefixAsync() {
850
+ if (!this.appPlugin)
851
+ return Promise.resolve("LOCAL");
852
+ return (0, import_payment_number.resolvePaymentNumberDevicePrefix)((key) => this.getAppData(key));
853
+ }
647
854
  syncAppDataToTempOrder(tempOrder) {
648
855
  const isPriceIncludeTax = this.getAppData("is_price_include_tax");
649
856
  const taxCountryCode = this.getAppData("tax_country_code");
@@ -745,6 +952,8 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
745
952
  }
746
953
  });
747
954
  this.currentSalesDetail = null;
955
+ this.queuedServerOrderChanges = [];
956
+ this.salesDetailLoadSequence += 1;
748
957
  await this.core.effects.emit(`${this.name}:onDestroy`, {});
749
958
  super.destroy();
750
959
  }
@@ -758,7 +967,8 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
758
967
  async loadSalesDetail(orderIdOrParams) {
759
968
  if (!this.store.order)
760
969
  throw new Error("order 模块未初始化");
761
- this.salesDetailLoadInFlight = true;
970
+ const loadSequence = ++this.salesDetailLoadSequence;
971
+ this.salesDetailLoadInFlightCount += 1;
762
972
  try {
763
973
  const params = typeof orderIdOrParams === "object" ? orderIdOrParams : { orderId: orderIdOrParams };
764
974
  const externalSaleNumber = params.externalSaleNumber || params.external_sale_number;
@@ -777,15 +987,26 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
777
987
  }
778
988
  const remoteRecord = preloadedSalesDetail ?? loadedRecord.data;
779
989
  const currentTempOrder = params.merge ? this.store.order.getTempOrder() : null;
780
- const record = params.merge ? mergeSalesDetailRecordWithTempOrder(currentTempOrder, remoteRecord) : remoteRecord;
781
- const sales = await this.store.order.hydrateTempOrderFromRecord(record, {
782
- recalcOnHydrate: false
783
- });
784
- this.currentSalesDetail = sales;
785
- await this.core.effects.emit(`${this.name}:onSalesOrderLoaded`, { sales });
786
- return sales;
990
+ const record = params.merge ? mergeSalesDetailRecordWithTempOrder(
991
+ currentTempOrder,
992
+ remoteRecord,
993
+ "preserve-local"
994
+ ) : remoteRecord;
995
+ return await this.hydrateLatestLoadedSalesDetail(record, loadSequence);
787
996
  } finally {
788
- this.salesDetailLoadInFlight = false;
997
+ this.salesDetailLoadInFlightCount = Math.max(
998
+ 0,
999
+ this.salesDetailLoadInFlightCount - 1
1000
+ );
1001
+ if (this.salesDetailLoadInFlightCount === 0) {
1002
+ try {
1003
+ await this.drainQueuedServerOrderChanges();
1004
+ } catch (error) {
1005
+ this.logError("drain queued server order changes failed", {
1006
+ error: error instanceof Error ? error.message : String(error)
1007
+ });
1008
+ }
1009
+ }
789
1010
  }
790
1011
  }
791
1012
  /** 获取当前已加载的销售详情(只读快照)。未加载过则返回 null。 */
@@ -1552,6 +1773,20 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1552
1773
  };
1553
1774
  return this.store.order.submitTempOrder(submitParams);
1554
1775
  }
1776
+ async saveSalesOrderDraft(params) {
1777
+ var _a, _b, _c, _d;
1778
+ if (!this.store.order) {
1779
+ throw new Error("BaseSales 解决方案需要 order 模块支持");
1780
+ }
1781
+ return this.store.order.saveTempOrderAsDraft({
1782
+ cacheId: this.cacheId,
1783
+ platform: (params == null ? void 0 : params.platform) ?? ((_a = this.otherParams) == null ? void 0 : _a.platform),
1784
+ businessCode: (params == null ? void 0 : params.businessCode) ?? ((_b = this.otherParams) == null ? void 0 : _b.businessCode) ?? ((_c = this.otherParams) == null ? void 0 : _c.business_code),
1785
+ channel: (params == null ? void 0 : params.channel) ?? this.getSubmitOrderSalesChannel(),
1786
+ type: (params == null ? void 0 : params.type) ?? ((_d = this.otherParams) == null ? void 0 : _d.type),
1787
+ ...(params == null ? void 0 : params.enhancePayload) !== void 0 ? { enhancePayload: params.enhancePayload } : {}
1788
+ });
1789
+ }
1555
1790
  async submitTempOrderAsync(params) {
1556
1791
  var _a, _b, _c, _d;
1557
1792
  if (!this.store.order) {
@@ -1752,8 +1987,26 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1752
1987
  }
1753
1988
  }
1754
1989
  }
1755
- /** 追加单个支付项到当前订单。 */
1990
+ /**
1991
+ * 同步兼容入口。无法等待 IoT 短号时,按调用当下的 device_id 或 LOCAL 生成。
1992
+ */
1756
1993
  addOrderPayment(payment) {
1994
+ const hasPaymentNumber = String(
1995
+ payment.payment_number || ""
1996
+ ).trim() !== "";
1997
+ return this.addOrderPaymentWithDevicePrefix(
1998
+ payment,
1999
+ hasPaymentNumber ? "LOCAL" : this.getPaymentNumberDevicePrefixSync()
2000
+ );
2001
+ }
2002
+ /** 创建新支付项时读取最新设备短号,不缓存运行时设备信息。 */
2003
+ async addOrderPaymentAsync(payment) {
2004
+ const paymentRecord = payment;
2005
+ const hasPaymentNumber = String(paymentRecord.payment_number || "").trim() !== "";
2006
+ const devicePrefix = hasPaymentNumber ? "LOCAL" : await this.getPaymentNumberDevicePrefixAsync();
2007
+ return this.addOrderPaymentWithDevicePrefix(payment, devicePrefix);
2008
+ }
2009
+ addOrderPaymentWithDevicePrefix(payment, devicePrefix) {
1757
2010
  var _a;
1758
2011
  if (!this.store.order)
1759
2012
  throw new Error("order 模块未初始化");
@@ -1770,21 +2023,26 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1770
2023
  if (shopWalletPassId !== void 0 && shopWalletPassId !== null) {
1771
2024
  metadata.shop_wallet_pass_id = shopWalletPassId;
1772
2025
  }
1773
- if (!metadata.unique_payment_number) {
1774
- metadata.unique_payment_number = (0, import_utils.createUuidV4)();
1775
- }
2026
+ const normalizedPaymentRecord = (0, import_payment_utils.ensureOrderPaymentNumber)(
2027
+ paymentRecord,
2028
+ devicePrefix,
2029
+ import_utils.createUuidV4
2030
+ );
1776
2031
  const paymentTimestamp = (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss");
1777
2032
  const paymentTime = paymentRecord.payment_time ?? paymentTimestamp;
1778
2033
  const createdAt = paymentRecord.created_at ?? paymentTimestamp;
1779
2034
  const serviceCharge = paymentRecord.service_charge && typeof paymentRecord.service_charge === "object" ? paymentRecord.service_charge : null;
1780
2035
  const calculatedServiceFee = serviceCharge ? new import_decimal.default(paymentRecord.origin_amount ?? paymentRecord.amount ?? 0).times(serviceCharge.percentage || 0).plus(serviceCharge.amount || 0).toDecimalPlaces(2).toFixed(2) : void 0;
1781
2036
  const payments = this.store.order.addOrderPayment({
1782
- ...paymentRecord,
2037
+ ...normalizedPaymentRecord,
1783
2038
  ...calculatedServiceFee !== void 0 ? { service_fee: calculatedServiceFee } : {},
1784
2039
  metadata,
1785
2040
  payment_time: paymentTime,
1786
2041
  created_at: createdAt
1787
2042
  });
2043
+ if (paymentRecord.status === "payment_pending") {
2044
+ return payments;
2045
+ }
1788
2046
  const amountSnapshot = this.store.order.getOrderAmountSnapshot();
1789
2047
  const syncState = this.store.order.getOrderSyncState();
1790
2048
  if (amountSnapshot) {
@@ -1806,10 +2064,73 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1806
2064
  return payments;
1807
2065
  }
1808
2066
  /** 更新当前订单中的指定支付项。 */
1809
- updateOrderPayment(paymentIdentity, updates) {
2067
+ async updateOrderPayment(paymentIdentity, updates, options = {}) {
2068
+ var _a, _b, _c;
1810
2069
  if (!this.store.order)
1811
2070
  throw new Error("order 模块未初始化");
1812
- return this.store.order.updateOrderPayment(paymentIdentity, updates);
2071
+ const matchMode = options.matchBy === "compat" || options.matchBy === "legacy" ? "compat" : "payment_number";
2072
+ const previousMatch = (0, import_payment_utils.resolveOrderPaymentIdentity)(
2073
+ this.store.order.getOrderPayments(),
2074
+ paymentIdentity,
2075
+ matchMode
2076
+ );
2077
+ const previousPayment = previousMatch == null ? void 0 : previousMatch.payment;
2078
+ if ((previousPayment == null ? void 0 : previousPayment.status) === "paid" && !options.applyUpdatesWhenPaid) {
2079
+ const currentPayments = this.store.order.getOrderPayments();
2080
+ let syncResult2;
2081
+ if (options.submitWhenPaid && options.forceSubmitIfPaid) {
2082
+ syncResult2 = await this.syncPaymentsToOrder({
2083
+ payments: currentPayments,
2084
+ paymentStatus: this.getPaymentStatusForSync(currentPayments),
2085
+ submitWhenPaid: true,
2086
+ smallTicketDataFlag: options.smallTicketDataFlag ?? 1
2087
+ });
2088
+ }
2089
+ return {
2090
+ updated: false,
2091
+ payment: previousPayment,
2092
+ payments: currentPayments,
2093
+ summary: ((_a = this.store.order.getOrderAmountSnapshot()) == null ? void 0 : _a.summary) || null,
2094
+ ...syncResult2 !== void 0 ? { syncResult: syncResult2 } : {}
2095
+ };
2096
+ }
2097
+ const result = await this.store.order.updateOrderPayment(paymentIdentity, updates, {
2098
+ matchBy: matchMode
2099
+ });
2100
+ let draftResult;
2101
+ let draftError;
2102
+ if (options.persistDraft !== false) {
2103
+ try {
2104
+ draftResult = await this.saveSalesOrderDraft();
2105
+ } catch (error) {
2106
+ draftError = error;
2107
+ this.logError("updateOrderPayment: 保存支付草稿失败", {
2108
+ paymentIdentity,
2109
+ error: error instanceof Error ? error.message : String(error)
2110
+ });
2111
+ if (!options.submitWhenPaid)
2112
+ throw error;
2113
+ }
2114
+ }
2115
+ const becamePaid = (previousPayment == null ? void 0 : previousPayment.status) !== "paid" && ((_b = result.payment) == null ? void 0 : _b.status) === "paid";
2116
+ const shouldSubmit = Boolean(
2117
+ options.submitWhenPaid && ((_c = result.payment) == null ? void 0 : _c.status) === "paid" && (becamePaid || options.forceSubmitIfPaid)
2118
+ );
2119
+ let syncResult;
2120
+ if (shouldSubmit) {
2121
+ syncResult = await this.syncPaymentsToOrder({
2122
+ payments: result.payments,
2123
+ paymentStatus: this.getPaymentStatusForSync(result.payments),
2124
+ submitWhenPaid: true,
2125
+ smallTicketDataFlag: options.smallTicketDataFlag ?? 1
2126
+ });
2127
+ }
2128
+ return {
2129
+ ...result,
2130
+ ...draftResult !== void 0 ? { draftResult } : {},
2131
+ ...draftError !== void 0 ? { draftError } : {},
2132
+ ...syncResult !== void 0 ? { syncResult } : {}
2133
+ };
1813
2134
  }
1814
2135
  /** 删除当前订单中的指定支付项。 */
1815
2136
  deleteOrderPayment(paymentIdentity) {
@@ -1823,6 +2144,11 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1823
2144
  throw new Error("order 模块未初始化");
1824
2145
  return this.store.order.updateVoucherOrderPayments(payments, options);
1825
2146
  }
2147
+ async updateVoucherOrderPaymentsAsync(payments, options) {
2148
+ if (!this.store.order)
2149
+ throw new Error("order 模块未初始化");
2150
+ return this.store.order.updateVoucherOrderPaymentsAsync(payments, options);
2151
+ }
1826
2152
  confirmPendingVoucherPayments() {
1827
2153
  if (!this.store.order)
1828
2154
  throw new Error("order 模块未初始化");
@@ -2,7 +2,7 @@
2
2
  * 把 `/order/sales/{id}` 响应(OrderData)解析为 ISalesDetail。
3
3
  *
4
4
  * - 纯函数:不依赖任何运行时模块/插件,便于单测与 view-model 适配
5
- * - products 保留 tempOrder / 后端详情原字段,仅做浅拷贝,避免丢失 product_sku 等协议字段
5
+ * - products 保留 tempOrder / 后端详情原字段,仅规范化集合身份并浅拷贝,避免丢失 product_sku 等协议字段
6
6
  * - bookings 平铺保留服务端原顺序,同时输出 byProductUid 反查表与 rootBooking
7
7
  * - header 收敛"非数组顶层字段",便于 ticketBooking view-model 直接消费
8
8
  *
@@ -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 {
@@ -130,7 +135,7 @@ export interface ISalesDetail {
130
135
  customer: ISalesDetailCustomer | null;
131
136
  /** 服务端 summary(仅输出) */
132
137
  summary: Record<string, any>;
133
- /** 原始响应快照,方便 UI 兜底使用 */
138
+ /** 规范化后的响应快照,方便 UI 兜底使用 */
134
139
  raw: Record<string, any>;
135
140
  }
136
141
  /**
@@ -138,7 +143,7 @@ export interface ISalesDetail {
138
143
  *
139
144
  * 注意:
140
145
  * - 入参允许是 `{ data: OrderData }` 或直接 `OrderData`,自动识别。
141
- * - products 保留原字段;字段补齐由 OrderModule hydrate 负责。
146
+ * - products/bookings/payments 在此补齐集合身份;其余 runtime 字段由 OrderModule hydrate 负责。
142
147
  * - 不做远程请求;不修改入参;返回新对象。
143
148
  */
144
149
  export declare function parseSalesResponse(input: Record<string, any> | null | undefined): ISalesDetail;
@@ -22,6 +22,7 @@ __export(parseSalesResponse_exports, {
22
22
  parseSalesResponse: () => parseSalesResponse
23
23
  });
24
24
  module.exports = __toCommonJS(parseSalesResponse_exports);
25
+ var import_orderCollectionIdentity = require("../../../modules/Order/utils/orderCollectionIdentity");
25
26
  var HEADER_KEYS = [
26
27
  "order_id",
27
28
  "order_number",
@@ -121,14 +122,12 @@ function indexBookingsByProductUid(bookings) {
121
122
  return result;
122
123
  }
123
124
  function parseSalesResponse(input) {
124
- const raw = input && typeof input === "object" && input.data && input.order_id == null ? input.data : input || {};
125
+ const sourceRaw = input && typeof input === "object" && input.data && input.order_id == null ? input.data : input || {};
126
+ const raw = (0, import_orderCollectionIdentity.normalizeOrderCollections)(sourceRaw).order;
125
127
  const productsRaw = Array.isArray(raw == null ? void 0 : raw.products) ? raw.products : [];
126
- const products = productsRaw.map((p, index) => {
128
+ const products = productsRaw.map((p) => {
127
129
  const row = { ...p || {} };
128
130
  const metadata = row.metadata && typeof row.metadata === "object" ? { ...row.metadata } : {};
129
- if (!metadata.unique_identification_number) {
130
- metadata.unique_identification_number = `sales-product-${row.product_id ?? "unknown"}-${row.product_variant_id ?? 0}-${index}`;
131
- }
132
131
  delete metadata.price_schema_version;
133
132
  delete row.identity_key;
134
133
  return { ...row, metadata };
@@ -311,7 +311,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
311
311
  date: string;
312
312
  status: string;
313
313
  week: string;
314
- weekNum: 0 | 2 | 1 | 6 | 3 | 4 | 5;
314
+ weekNum: 0 | 2 | 1 | 6 | 4 | 3 | 5;
315
315
  }[]>;
316
316
  submitTimeSlot(timeSlots: TimeSliceItem): void;
317
317
  private getScheduleDataByIds;