@pisell/pisellos 2.2.292 → 2.2.293

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 (173) hide show
  1. package/dist/core/index.d.ts +7 -0
  2. package/dist/core/index.js +108 -65
  3. package/dist/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
  4. package/dist/model/strategy/adapter/itemRule/adapter.js +135 -35
  5. package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
  6. package/dist/model/strategy/adapter/itemRule/evaluator.js +25 -1
  7. package/dist/model/strategy/adapter/itemRule/type.d.ts +44 -0
  8. package/dist/model/strategy/adapter/itemRule/type.js +19 -1
  9. package/dist/modules/BookingContext/index.d.ts +3 -0
  10. package/dist/modules/BookingContext/index.js +42 -13
  11. package/dist/modules/BookingContext/types.d.ts +4 -4
  12. package/dist/modules/BookingContext/types.js +3 -3
  13. package/dist/modules/OpenData/types.d.ts +1 -0
  14. package/dist/modules/Order/index.d.ts +19 -4
  15. package/dist/modules/Order/index.js +885 -485
  16. package/dist/modules/Order/salesNotes.d.ts +31 -0
  17. package/dist/modules/Order/salesNotes.js +492 -0
  18. package/dist/modules/Order/types.d.ts +93 -5
  19. package/dist/modules/Order/types.js +5 -0
  20. package/dist/modules/Order/utils.d.ts +12 -0
  21. package/dist/modules/Order/utils.js +96 -31
  22. package/dist/modules/Product/types.d.ts +2 -0
  23. package/dist/modules/ProductList/clientDataVariants.d.ts +50 -0
  24. package/dist/modules/ProductList/clientDataVariants.js +216 -0
  25. package/dist/modules/ProductList/index.d.ts +18 -3
  26. package/dist/modules/ProductList/index.js +220 -38
  27. package/dist/modules/ProductList/types.d.ts +10 -0
  28. package/dist/modules/Quotation/index.d.ts +22 -0
  29. package/dist/modules/Quotation/index.js +293 -28
  30. package/dist/modules/ResourcePlanner/index.d.ts +0 -22
  31. package/dist/modules/ResourcePlanner/index.js +1 -180
  32. package/dist/modules/ResourcePlanner/localClock.d.ts +33 -0
  33. package/dist/modules/ResourcePlanner/localClock.js +183 -0
  34. package/dist/modules/ResourcePlanner/planner.d.ts +5 -14
  35. package/dist/modules/ResourcePlanner/planner.js +2111 -958
  36. package/dist/modules/ResourcePlanner/types.d.ts +359 -187
  37. package/dist/modules/ResourcePlanner/types.js +33 -1
  38. package/dist/plugins/request.d.ts +1 -0
  39. package/dist/server/index.d.ts +17 -2
  40. package/dist/server/index.js +1134 -932
  41. package/dist/server/modules/floor-plan/index.d.ts +4 -0
  42. package/dist/server/modules/floor-plan/index.js +240 -98
  43. package/dist/server/modules/menu/index.js +48 -23
  44. package/dist/server/modules/order/index.d.ts +17 -7
  45. package/dist/server/modules/order/index.js +419 -207
  46. package/dist/server/modules/products/index.d.ts +10 -4
  47. package/dist/server/modules/products/index.js +177 -84
  48. package/dist/server/modules/resource/index.js +21 -13
  49. package/dist/server/utils/product.d.ts +1 -1
  50. package/dist/server/utils/product.js +186 -22
  51. package/dist/server/utils/small-ticket.d.ts +9 -1
  52. package/dist/server/utils/small-ticket.js +136 -41
  53. package/dist/solution/BaseSales/index.d.ts +26 -3
  54. package/dist/solution/BaseSales/index.js +1384 -905
  55. package/dist/solution/BaseSales/types.d.ts +2 -0
  56. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +3 -0
  57. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +10 -3
  58. package/dist/solution/BaseSales/utils.js +6 -3
  59. package/dist/solution/BookingByStep/index.d.ts +4 -4
  60. package/dist/solution/BookingTicket/index.d.ts +1 -1
  61. package/dist/solution/BookingTicket/index.js +4 -2
  62. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +2 -1
  63. package/dist/solution/BookingTicket/utils/addProductDecision.js +6 -3
  64. package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
  65. package/dist/solution/RegisterAndLogin/config.d.ts +9 -3
  66. package/dist/solution/RegisterAndLogin/config.js +95 -40
  67. package/dist/solution/RegisterAndLogin/index.js +4 -1
  68. package/dist/solution/ScanOrder/index.d.ts +1 -0
  69. package/dist/solution/ScanOrder/index.js +31 -27
  70. package/dist/solution/ScanOrder/types.d.ts +2 -0
  71. package/dist/solution/ScanOrder/utils.d.ts +1 -0
  72. package/dist/solution/ScanOrder/utils.js +8 -3
  73. package/dist/solution/UnifiedBookingSales/index.d.ts +128 -74
  74. package/dist/solution/UnifiedBookingSales/index.js +3877 -963
  75. package/dist/solution/UnifiedBookingSales/remoteOccupancyCache.d.ts +49 -0
  76. package/dist/solution/UnifiedBookingSales/remoteOccupancyCache.js +430 -0
  77. package/dist/solution/UnifiedBookingSales/types.d.ts +161 -77
  78. package/dist/solution/UnifiedBookingSales/types.js +4 -9
  79. package/dist/solution/VenueBooking/index.d.ts +3 -0
  80. package/dist/solution/VenueBooking/index.js +549 -497
  81. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  82. package/dist/solution/VenueBooking/utils/dateSummary.js +3 -1
  83. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +2 -0
  84. package/dist/solution/VenueBooking/utils/slotMerge.js +3 -1
  85. package/dist/utils/platform.d.ts +8 -0
  86. package/dist/utils/platform.js +15 -0
  87. package/lib/core/index.d.ts +7 -0
  88. package/lib/core/index.js +20 -2
  89. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
  90. package/lib/model/strategy/adapter/itemRule/adapter.js +73 -4
  91. package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
  92. package/lib/model/strategy/adapter/itemRule/evaluator.js +23 -1
  93. package/lib/model/strategy/adapter/itemRule/type.d.ts +44 -0
  94. package/lib/model/strategy/adapter/itemRule/type.js +19 -1
  95. package/lib/modules/BookingContext/index.d.ts +3 -0
  96. package/lib/modules/BookingContext/index.js +35 -8
  97. package/lib/modules/BookingContext/types.d.ts +4 -4
  98. package/lib/modules/BookingContext/types.js +3 -3
  99. package/lib/modules/OpenData/types.d.ts +1 -0
  100. package/lib/modules/Order/index.d.ts +19 -4
  101. package/lib/modules/Order/index.js +325 -28
  102. package/lib/modules/Order/salesNotes.d.ts +31 -0
  103. package/lib/modules/Order/salesNotes.js +382 -0
  104. package/lib/modules/Order/types.d.ts +93 -5
  105. package/lib/modules/Order/types.js +4 -0
  106. package/lib/modules/Order/utils.d.ts +12 -0
  107. package/lib/modules/Order/utils.js +85 -16
  108. package/lib/modules/Product/types.d.ts +2 -0
  109. package/lib/modules/ProductList/clientDataVariants.d.ts +50 -0
  110. package/lib/modules/ProductList/clientDataVariants.js +204 -0
  111. package/lib/modules/ProductList/index.d.ts +18 -3
  112. package/lib/modules/ProductList/index.js +137 -8
  113. package/lib/modules/ProductList/types.d.ts +10 -0
  114. package/lib/modules/Quotation/index.d.ts +22 -0
  115. package/lib/modules/Quotation/index.js +158 -11
  116. package/lib/modules/ResourcePlanner/index.d.ts +0 -22
  117. package/lib/modules/ResourcePlanner/index.js +9 -129
  118. package/lib/modules/ResourcePlanner/localClock.d.ts +33 -0
  119. package/lib/modules/ResourcePlanner/localClock.js +205 -0
  120. package/lib/modules/ResourcePlanner/planner.d.ts +5 -14
  121. package/lib/modules/ResourcePlanner/planner.js +1807 -783
  122. package/lib/modules/ResourcePlanner/types.d.ts +359 -187
  123. package/lib/modules/ResourcePlanner/types.js +13 -1
  124. package/lib/plugins/request.d.ts +1 -0
  125. package/lib/server/index.d.ts +17 -2
  126. package/lib/server/index.js +149 -21
  127. package/lib/server/modules/floor-plan/index.d.ts +4 -0
  128. package/lib/server/modules/floor-plan/index.js +60 -7
  129. package/lib/server/modules/menu/index.js +26 -4
  130. package/lib/server/modules/order/index.d.ts +17 -7
  131. package/lib/server/modules/order/index.js +194 -57
  132. package/lib/server/modules/products/index.d.ts +10 -4
  133. package/lib/server/modules/products/index.js +93 -23
  134. package/lib/server/modules/resource/index.js +12 -2
  135. package/lib/server/utils/product.d.ts +1 -1
  136. package/lib/server/utils/product.js +163 -3
  137. package/lib/server/utils/small-ticket.d.ts +9 -1
  138. package/lib/server/utils/small-ticket.js +126 -33
  139. package/lib/solution/BaseSales/index.d.ts +26 -3
  140. package/lib/solution/BaseSales/index.js +396 -70
  141. package/lib/solution/BaseSales/types.d.ts +2 -0
  142. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +3 -0
  143. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +9 -2
  144. package/lib/solution/BaseSales/utils.js +3 -0
  145. package/lib/solution/BookingByStep/index.d.ts +4 -4
  146. package/lib/solution/BookingTicket/index.d.ts +1 -1
  147. package/lib/solution/BookingTicket/index.js +2 -1
  148. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +2 -1
  149. package/lib/solution/BookingTicket/utils/addProductDecision.js +5 -2
  150. package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
  151. package/lib/solution/RegisterAndLogin/config.d.ts +9 -3
  152. package/lib/solution/RegisterAndLogin/config.js +49 -8
  153. package/lib/solution/RegisterAndLogin/index.js +4 -1
  154. package/lib/solution/ScanOrder/index.d.ts +1 -0
  155. package/lib/solution/ScanOrder/index.js +5 -1
  156. package/lib/solution/ScanOrder/types.d.ts +2 -0
  157. package/lib/solution/ScanOrder/utils.d.ts +1 -0
  158. package/lib/solution/ScanOrder/utils.js +4 -0
  159. package/lib/solution/UnifiedBookingSales/index.d.ts +128 -74
  160. package/lib/solution/UnifiedBookingSales/index.js +2592 -424
  161. package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.d.ts +49 -0
  162. package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.js +316 -0
  163. package/lib/solution/UnifiedBookingSales/types.d.ts +161 -77
  164. package/lib/solution/UnifiedBookingSales/types.js +4 -6
  165. package/lib/solution/VenueBooking/index.d.ts +3 -0
  166. package/lib/solution/VenueBooking/index.js +33 -9
  167. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  168. package/lib/solution/VenueBooking/utils/dateSummary.js +3 -1
  169. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +2 -0
  170. package/lib/solution/VenueBooking/utils/slotMerge.js +3 -1
  171. package/lib/utils/platform.d.ts +8 -0
  172. package/lib/utils/platform.js +22 -0
  173. package/package.json +1 -1
@@ -29,6 +29,8 @@ var _paymentUtils = require("../../modules/Order/payment-utils");
29
29
  var _paymentNumber = require("../../utils/payment-number");
30
30
  var _orderCollectionIdentity = require("../../modules/Order/utils/orderCollectionIdentity");
31
31
  var _dayjs = _interopRequireDefault(require("dayjs"));
32
+ var _clientDataVariants = require("../../modules/ProductList/clientDataVariants");
33
+ var _platform = require("../../utils/platform");
32
34
  var _Quotation = require("../../modules/Quotation");
33
35
  var _transformBaseProductToOrderProduct = require("./utils/transformBaseProductToOrderProduct");
34
36
  var _quotationPrice = require("./utils/quotationPrice");
@@ -281,7 +283,56 @@ function mergeBaseSalesUniqueArray(currentValue, loadedValue, metadataKey) {
281
283
  loadedValue.forEach(appendOrReplaceByUid);
282
284
  return result;
283
285
  }
286
+
287
+ // checkout / realtime snapshots may reorder protocol arrays or append persistence fields.
288
+ // Compare only the Sales Note protocol fields so a matching remote echo can acknowledge dirty state.
289
+ function normalizeBaseSalesNotesForComparison(value, options) {
290
+ if (!Array.isArray(value)) return null;
291
+ const unresolvedOrderTargetUuid = String(options?.unresolvedOrderTargetUuid ?? '').trim();
292
+ return value.map(note => {
293
+ if (!note || typeof note !== 'object' || Array.isArray(note)) {
294
+ return (0, _lodashEs.cloneDeep)(note);
295
+ }
296
+ const noteRecord = note;
297
+ const relations = Array.isArray(noteRecord.note_relations) ? noteRecord.note_relations.map(relation => {
298
+ if (!relation || typeof relation !== 'object' || Array.isArray(relation)) {
299
+ return (0, _lodashEs.cloneDeep)(relation);
300
+ }
301
+ const shouldResolveOrderTarget = relation.is_primary === 0 && relation.relation_type === 'related_to' && relation.target_type === 'order' && String(relation.target_uuid ?? '').trim() === '' && unresolvedOrderTargetUuid.length > 0;
302
+ return {
303
+ uuid: relation.uuid,
304
+ relation_type: relation.relation_type,
305
+ target_type: relation.target_type,
306
+ target_uuid: shouldResolveOrderTarget ? unresolvedOrderTargetUuid : relation.target_uuid,
307
+ is_primary: relation.is_primary
308
+ };
309
+ }).sort((left, right) => String(left?.uuid ?? '').localeCompare(String(right?.uuid ?? ''))) : (0, _lodashEs.cloneDeep)(noteRecord.note_relations);
310
+ return {
311
+ uuid: noteRecord.uuid,
312
+ note_type: noteRecord.note_type,
313
+ content: {
314
+ text: (0, _lodashEs.cloneDeep)(noteRecord.content?.text)
315
+ },
316
+ importance: noteRecord.importance,
317
+ note_relations: relations
318
+ };
319
+ }).sort((left, right) => String(left?.uuid ?? '').localeCompare(String(right?.uuid ?? '')));
320
+ }
321
+ function areBaseSalesNotesEquivalent(currentValue, loadedValue, options) {
322
+ const currentNotes = normalizeBaseSalesNotesForComparison(currentValue, {
323
+ unresolvedOrderTargetUuid: options?.currentOrderTargetUuid
324
+ });
325
+ const loadedNotes = normalizeBaseSalesNotesForComparison(loadedValue);
326
+ return currentNotes !== null && loadedNotes !== null && (0, _lodashEs.isEqual)(currentNotes, loadedNotes);
327
+ }
284
328
  function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, strategy) {
329
+ const loadedHasSalesNotes = Object.prototype.hasOwnProperty.call(loadedRecord || {}, 'notes');
330
+ const hasDirtyLocalSalesNotes = currentTempOrder?._extend?.salesNotesState?.dirty === true;
331
+ const incomingSalesNotesMatchLocal = loadedHasSalesNotes && areBaseSalesNotesEquivalent(currentTempOrder?.notes, loadedRecord.notes, {
332
+ currentOrderTargetUuid: loadedRecord?.shop_order_number || currentTempOrder?.shop_order_number
333
+ });
334
+ const shouldAwaitAuthoritativeSalesNotes = strategy === 'authoritative-incoming' && hasDirtyLocalSalesNotes && (Number(loadedRecord?.need_sync) === 1 || !incomingSalesNotesMatchLocal);
335
+ const preserveDirtyLocalSalesNotes = hasDirtyLocalSalesNotes && (strategy === 'preserve-local' || shouldAwaitAuthoritativeSalesNotes);
285
336
  const normalizedLoadedRecord = normalizeBaseSalesRecordCollections(loadedRecord || {}, {
286
337
  ensureUid: false
287
338
  });
@@ -293,6 +344,12 @@ function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, str
293
344
  });
294
345
  const uidRemaps = [];
295
346
  const mergedRecord = (0, _lodashEs.mergeWith)(normalizedCurrentTempOrder, normalizedLoadedRecord, (currentValue, loadedValue, key) => {
347
+ if (key === 'notes') {
348
+ if (preserveDirtyLocalSalesNotes && Array.isArray(currentValue)) {
349
+ return (0, _lodashEs.cloneDeep)(currentValue);
350
+ }
351
+ if (Array.isArray(loadedValue)) return (0, _lodashEs.cloneDeep)(loadedValue);
352
+ }
296
353
  const collectionKind = getBaseSalesOrderCollectionKind(key);
297
354
  if (collectionKind && Array.isArray(loadedValue)) {
298
355
  return mergeBaseSalesOrderCollection(collectionKind, Array.isArray(currentValue) ? currentValue : [], loadedValue, strategy, uidRemaps);
@@ -316,7 +373,24 @@ function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, str
316
373
  return undefined;
317
374
  });
318
375
  delete mergedRecord.request_unique_idempotency_token;
319
- return normalizeBaseSalesRecordCollections((0, _orderCollectionIdentity.applyOrderCollectionUidRemaps)(mergedRecord, uidRemaps));
376
+ const normalizedMergedRecord = normalizeBaseSalesRecordCollections((0, _orderCollectionIdentity.applyOrderCollectionUidRemaps)(mergedRecord, uidRemaps));
377
+ normalizedMergedRecord._extend = {
378
+ ...(normalizedMergedRecord._extend || {}),
379
+ salesNotesState: preserveDirtyLocalSalesNotes ? {
380
+ loaded: true,
381
+ dirty: true
382
+ } : loadedHasSalesNotes ? {
383
+ loaded: true,
384
+ dirty: normalizedLoadedRecord._extend?.salesNotesState?.dirty === true
385
+ } : {
386
+ loaded: false,
387
+ dirty: false
388
+ }
389
+ };
390
+ if (!loadedHasSalesNotes && !preserveDirtyLocalSalesNotes) {
391
+ delete normalizedMergedRecord.notes;
392
+ }
393
+ return normalizedMergedRecord;
320
394
  }
321
395
  function filterPendingPaymentsFromLoadedSalesDetail(sourceRecord) {
322
396
  if (!sourceRecord || typeof sourceRecord !== 'object') return sourceRecord;
@@ -638,8 +712,10 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
638
712
  async loadQuotationForPriceQuery(params) {
639
713
  if (!params.quotation) return;
640
714
  this.applyQuotationScheduleResolver(params.quotation);
641
- await params.quotation.loadQuotations({
642
- channel: params.channel || this.otherParams?.channel,
715
+ const quotation = params.quotation;
716
+ const load = typeof quotation.ensureQuotations === 'function' ? quotation.ensureQuotations.bind(quotation) : quotation.loadQuotations.bind(quotation);
717
+ await load({
718
+ channel: this.getPriceQueryChannel(params.channel),
643
719
  customer_id: params.customer_id
644
720
  });
645
721
  }
@@ -721,34 +797,175 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
721
797
  schedule_datetime: now.format('YYYY-MM-DD HH:mm:ss')
722
798
  };
723
799
  }
800
+ isCustomerUserPriceQuery() {
801
+ return (0, _platform.isCustomerUserPlatform)(this.otherParams?.platform);
802
+ }
803
+ getPriceQueryScheduleList() {
804
+ const storeScheduleList = this.store.schedule?.getScheduleList?.();
805
+ if (Array.isArray(storeScheduleList) && storeScheduleList.length > 0) {
806
+ return storeScheduleList;
807
+ }
808
+ const contextScheduleList = this.core?.context?.scheduleList;
809
+ return Array.isArray(contextScheduleList) && contextScheduleList.length > 0 ? contextScheduleList : undefined;
810
+ }
811
+ buildPriceQueryPayload(params) {
812
+ const channel = this.getPriceQueryChannel(params.channel);
813
+ const strategyContext = this.getPriceQueryStrategyContext(channel);
814
+ return {
815
+ ids: params.ids,
816
+ ...(this.isCustomerUserPriceQuery() ? {} : {
817
+ open_quotation: 1
818
+ }),
819
+ open_bundle: 1,
820
+ with: [..._clientDataVariants.PRODUCT_QUERY_DATA_VARIANT_RELATIONS],
821
+ status: 'published',
822
+ num: Math.max(params.ids.length, 1),
823
+ skip: 1,
824
+ customer_id: params.customerId,
825
+ schedule_date: params.schedule.schedule_date,
826
+ schedule_datetime: params.schedule.schedule_datetime,
827
+ application_code: channel,
828
+ ...(Object.keys(strategyContext).length ? {
829
+ strategy_context: strategyContext
830
+ } : {})
831
+ };
832
+ }
833
+ getRawProductPriceSources(ids) {
834
+ const products = this.store.products;
835
+ if (typeof products?.getRawProductPriceSources !== 'function') {
836
+ return new Map();
837
+ }
838
+ try {
839
+ const result = products.getRawProductPriceSources(ids);
840
+ return result instanceof Map ? result : new Map();
841
+ } catch (error) {
842
+ this.logWarning('getRawProductPriceSources: 读取商品原始报价源失败', {
843
+ ids,
844
+ error: error instanceof Error ? error.message : String(error)
845
+ });
846
+ return new Map();
847
+ }
848
+ }
849
+ rememberRawProductPriceSources(products) {
850
+ const productList = this.store.products;
851
+ if (typeof productList?.rememberRawProductQueryResult !== 'function') return;
852
+ try {
853
+ productList.rememberRawProductQueryResult(products);
854
+ } catch (error) {
855
+ this.logWarning('rememberRawProductPriceSources: 保存商品原始报价源失败', {
856
+ error: error instanceof Error ? error.message : String(error)
857
+ });
858
+ }
859
+ }
860
+ getSelectedProductVariantId(product) {
861
+ const variantId = Number(product?.product_variant_id ?? product?.variant_id ?? 0);
862
+ return Number.isFinite(variantId) && variantId > 0 ? variantId : 0;
863
+ }
864
+
865
+ /**
866
+ * 将目录原始商品还原为本次选择的 SKU 基线。
867
+ *
868
+ * 这里绝不能复用已评估后的目录商品,否则从命中智能价切回不命中的
869
+ * 日期时会残留旧价格。套餐仅验证所选关系存在,最终仍由 merge 方法把
870
+ * 评估后的套餐价格映射回用户的选择数据。
871
+ */
872
+ prepareRawProductForPriceQuery(rawProduct, selectedProduct) {
873
+ const product = (0, _lodashEs.cloneDeep)(rawProduct);
874
+ const rawProductId = this.getPriceQueryProductId(product);
875
+ const selectedProductId = this.getPriceQueryProductId(selectedProduct);
876
+ if (rawProductId === null || selectedProductId === null || rawProductId !== selectedProductId) {
877
+ return null;
878
+ }
879
+ const selectedVariantId = this.getSelectedProductVariantId(selectedProduct);
880
+ product.product_id = product.product_id ?? product.id;
881
+ product.product_variant_id = selectedVariantId;
882
+ product.variant_id = selectedVariantId;
883
+ if (selectedVariantId > 0) {
884
+ const variants = Array.isArray(product.variant) ? product.variant : [];
885
+ const selectedVariant = variants.find(variant => Number(variant?.id) === selectedVariantId);
886
+ if (!selectedVariant) return null;
887
+ const variantPrice = selectedVariant.price ?? selectedVariant.selling_price ?? selectedVariant.base_price;
888
+ if (variantPrice === undefined || variantPrice === null || variantPrice === '') {
889
+ return null;
890
+ }
891
+ product.price = variantPrice;
892
+ product.selling_price = selectedVariant.selling_price ?? variantPrice;
893
+ product.base_price = selectedVariant.base_price ?? variantPrice;
894
+ }
895
+ const selectedBundles = Array.isArray(selectedProduct.product_bundle) ? selectedProduct.product_bundle : [];
896
+ if (selectedBundles.length > 0) {
897
+ const rawBundleItems = this.getAuthoritativeBundleItems(product);
898
+ const allSelectedBundlesExist = selectedBundles.every(bundle => {
899
+ const authoritativeBundle = this.findAuthoritativeBundleItem(bundle, rawBundleItems);
900
+ if (!authoritativeBundle) return false;
901
+ const selectedBundleVariantId = Number(bundle?.bundle_variant_id ?? bundle?.variant_id ?? 0);
902
+ const authoritativeBundleVariantId = Number(authoritativeBundle?.bundle_variant_id ?? authoritativeBundle?.variant_id ?? 0);
903
+ if ((Number.isFinite(selectedBundleVariantId) ? selectedBundleVariantId : 0) !== (Number.isFinite(authoritativeBundleVariantId) ? authoritativeBundleVariantId : 0)) {
904
+ return false;
905
+ }
906
+
907
+ // 省略字段表示该套餐子商品关系没有被价格查询完整水合,不能把它
908
+ // 当作“没有规则”而静默使用基础价。
909
+ return Array.isArray(authoritativeBundle.data_variants);
910
+ });
911
+ if (!allSelectedBundlesExist) return null;
912
+ }
913
+ return product;
914
+ }
915
+ resolveLocalProductForPriceQuery(params) {
916
+ if (!params.rawProduct) return null;
917
+ const preparedProduct = this.prepareRawProductForPriceQuery(params.rawProduct, params.selectedProduct);
918
+ if (!preparedProduct) return null;
919
+ const scheduleList = this.getPriceQueryScheduleList();
920
+ const result = (0, _clientDataVariants.tryResolveClientDataVariants)({
921
+ core: this.core,
922
+ otherParams: this.otherParams,
923
+ products: [preparedProduct],
924
+ queryPayload: params.queryPayload,
925
+ handledByOsServer: false,
926
+ ...(scheduleList ? {
927
+ scheduleList
928
+ } : {})
929
+ });
930
+ if (result.status !== 'resolved') return null;
931
+ return result.products[0] || null;
932
+ }
724
933
  async loadProductsForPriceQuery(params) {
725
934
  const ids = Array.from(new Set(params.ids.filter(id => Number.isFinite(id))));
726
935
  const productsById = new Map();
727
936
  if (!ids.length || !this.request) return productsById;
728
- const channel = this.getPriceQueryChannel(params.channel);
729
- const strategyContext = this.getPriceQueryStrategyContext(channel);
937
+ const handledByOsServer = (0, _clientDataVariants.isProductQueryHandledByOsServer)(this.core, this.request);
730
938
  try {
731
- const response = await this.request.post('/product/query', {
732
- ids,
733
- open_quotation: 1,
734
- open_bundle: 1,
735
- status: 'published',
736
- num: 1,
737
- skip: 1,
738
- customer_id: params.customerId,
739
- schedule_date: params.schedule.schedule_date,
740
- schedule_datetime: params.schedule.schedule_datetime,
741
- application_code: channel,
742
- ...(Object.keys(strategyContext).length ? {
743
- strategy_context: strategyContext
744
- } : {})
745
- }, {
939
+ const queryPayload = this.buildPriceQueryPayload({
940
+ ...params,
941
+ ids
942
+ });
943
+ const response = await this.request.post('/product/query', queryPayload, {
746
944
  osServer: true,
747
945
  customToast: () => {}
748
946
  });
749
947
  const list = response?.data?.list || response?.list || [];
750
948
  if (!Array.isArray(list)) return productsById;
751
- list.forEach(item => {
949
+ if (this.isCustomerUserPriceQuery() && !handledByOsServer) {
950
+ this.rememberRawProductPriceSources(list);
951
+ list.forEach(item => {
952
+ const productId = Number(item?.id ?? item?.product_id);
953
+ if (Number.isFinite(productId)) productsById.set(productId, item);
954
+ });
955
+ return productsById;
956
+ }
957
+ const scheduleList = this.getPriceQueryScheduleList();
958
+ const resolvedList = (0, _clientDataVariants.resolveClientDataVariants)({
959
+ core: this.core,
960
+ otherParams: this.otherParams,
961
+ products: list,
962
+ queryPayload,
963
+ handledByOsServer,
964
+ ...(scheduleList ? {
965
+ scheduleList
966
+ } : {})
967
+ });
968
+ resolvedList.forEach(item => {
752
969
  const productId = Number(item?.id ?? item?.product_id);
753
970
  if (Number.isFinite(productId)) productsById.set(productId, item);
754
971
  });
@@ -761,18 +978,69 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
761
978
  return productsById;
762
979
  }
763
980
  }
764
- async loadProductForPriceQuery(params, customerId) {
765
- const product = params.product || {};
766
- const productId = this.getPriceQueryProductId(product);
767
- if (productId === null || !this.request) return null;
768
- const schedule = this.getPriceQuerySchedule(params);
769
- const productsById = await this.loadProductsForPriceQuery({
770
- ids: [productId],
771
- schedule,
772
- customerId,
981
+ async resolveProductsForPriceQuery(params) {
982
+ const selectedProducts = params.priceQueries.map(item => item.product || {});
983
+ const productIds = selectedProducts.map(product => this.getPriceQueryProductId(product));
984
+ const ids = Array.from(new Set(productIds.filter(id => id !== null)));
985
+ if (!ids.length) {
986
+ return selectedProducts.map(() => null);
987
+ }
988
+ const handledByOsServer = this.request ? (0, _clientDataVariants.isProductQueryHandledByOsServer)(this.core, this.request) : false;
989
+ const canUseLocalFastPath = this.isCustomerUserPriceQuery() && !handledByOsServer;
990
+ if (!canUseLocalFastPath) {
991
+ if (!this.request) return selectedProducts.map(() => null);
992
+ const productsById = await this.loadProductsForPriceQuery({
993
+ ids,
994
+ schedule: params.schedule,
995
+ customerId: params.customerId,
996
+ channel: params.channel
997
+ });
998
+ return productIds.map(productId => productId === null ? null : productsById.get(productId) || null);
999
+ }
1000
+ const queryPayload = this.buildPriceQueryPayload({
1001
+ ids,
1002
+ schedule: params.schedule,
1003
+ customerId: params.customerId,
773
1004
  channel: params.channel
774
1005
  });
775
- return productsById.get(productId) || null;
1006
+ const cachedRawProducts = this.getRawProductPriceSources(ids);
1007
+ const authoritativeProducts = selectedProducts.map((selectedProduct, index) => {
1008
+ const productId = productIds[index];
1009
+ return productId === null ? null : this.resolveLocalProductForPriceQuery({
1010
+ selectedProduct,
1011
+ rawProduct: cachedRawProducts.get(productId),
1012
+ queryPayload
1013
+ });
1014
+ });
1015
+ const missingIds = Array.from(new Set(productIds.filter((productId, index) => productId !== null && !authoritativeProducts[index])));
1016
+ if (!missingIds.length) return authoritativeProducts;
1017
+ if (!this.request) return authoritativeProducts;
1018
+ const remoteRawProducts = await this.loadProductsForPriceQuery({
1019
+ ids: missingIds,
1020
+ schedule: params.schedule,
1021
+ customerId: params.customerId,
1022
+ channel: params.channel
1023
+ });
1024
+ authoritativeProducts.forEach((authoritativeProduct, index) => {
1025
+ if (authoritativeProduct) return;
1026
+ const productId = productIds[index];
1027
+ if (productId === null) return;
1028
+ const rawProduct = remoteRawProducts.get(productId);
1029
+ const locallyResolved = this.resolveLocalProductForPriceQuery({
1030
+ selectedProduct: selectedProducts[index],
1031
+ rawProduct,
1032
+ queryPayload
1033
+ });
1034
+ if (locallyResolved) {
1035
+ authoritativeProducts[index] = locallyResolved;
1036
+ return;
1037
+ }
1038
+
1039
+ // 严格客户端能力仍不可用时,真实请求已经是最后的权威降级。
1040
+ // 尽量应用所选 SKU 基线;关系不完整时保持旧的远端商品语义。
1041
+ authoritativeProducts[index] = rawProduct ? this.prepareRawProductForPriceQuery(rawProduct, selectedProducts[index]) || rawProduct : null;
1042
+ });
1043
+ return authoritativeProducts;
776
1044
  }
777
1045
  getAuthoritativeBundleItems(product) {
778
1046
  if (Array.isArray(product?.product_bundle)) return product.product_bundle;
@@ -1230,6 +1498,30 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1230
1498
  if (!this.store.order) return '';
1231
1499
  return this.store.order.getTempOrderNote();
1232
1500
  }
1501
+ getSalesNotes() {
1502
+ if (!this.store.order) return [];
1503
+ return this.store.order.getSalesNotes();
1504
+ }
1505
+ createSalesNotesMutationSnapshot() {
1506
+ if (!this.store.order) throw new Error('order 模块未初始化');
1507
+ return this.store.order.createSalesNotesMutationSnapshot();
1508
+ }
1509
+ restoreSalesNotesMutationSnapshot(snapshot) {
1510
+ if (!this.store.order) throw new Error('order 模块未初始化');
1511
+ this.store.order.restoreSalesNotesMutationSnapshot(snapshot);
1512
+ }
1513
+ async setSalesNote(input) {
1514
+ if (!this.store.order) throw new Error('order 模块未初始化');
1515
+ return this.store.order.setSalesNote(input);
1516
+ }
1517
+ async removeSalesNote(uuid) {
1518
+ if (!this.store.order) throw new Error('order 模块未初始化');
1519
+ await this.store.order.removeSalesNote(uuid);
1520
+ }
1521
+ async clearSalesNotes() {
1522
+ if (!this.store.order) throw new Error('order 模块未初始化');
1523
+ await this.store.order.clearSalesNotes();
1524
+ }
1233
1525
  updateTempOrderNote(note, sync = false) {
1234
1526
  try {
1235
1527
  if (!this.store.order) throw new Error('order 模块未初始化');
@@ -1251,6 +1543,14 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1251
1543
  if (!this.store.order) throw new Error('order 模块未初始化');
1252
1544
  return this.store.order.updateTempOrderDepositAmount(amount);
1253
1545
  }
1546
+ setPickupReferenceMode(mode) {
1547
+ if (!this.store.order) throw new Error('order 模块未初始化');
1548
+ return this.store.order.updateTempOrderPickupReferenceMode(mode);
1549
+ }
1550
+ setServiceType(serviceType) {
1551
+ if (!this.store.order) throw new Error('order 模块未初始化');
1552
+ return this.store.order.updateTempOrderServiceType(serviceType);
1553
+ }
1254
1554
  updateTempOrderContactsInfo(contactsInfo) {
1255
1555
  if (!this.store.order) throw new Error('order 模块未初始化');
1256
1556
  return this.store.order.updateTempOrderContactsInfo(contactsInfo);
@@ -1300,6 +1600,8 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1300
1600
  }
1301
1601
  // this.store.resource = null;
1302
1602
  const tempOrder = this.store.order.restoreOrder();
1603
+ // 先补齐店铺级订单字段,再通知监听方,避免发布带空税区/币种的中间快照。
1604
+ const appDataChanged = this.syncAppDataToTempOrder(tempOrder);
1303
1605
  this.currentSalesDetail = null;
1304
1606
  this.discountConfigCacheKey = null;
1305
1607
  this.hasManualDiscountSelection = false;
@@ -1312,7 +1614,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1312
1614
  await this.effectsEmit('onTempOrderReplaced', {
1313
1615
  tempOrder
1314
1616
  });
1315
- if (this.syncAppDataToTempOrder(tempOrder) && this.isTempOrderPersistEnabled()) {
1617
+ if (appDataChanged && this.isTempOrderPersistEnabled()) {
1316
1618
  this.store.order.persistTempOrder();
1317
1619
  await this.store.order.saveDraft();
1318
1620
  }
@@ -2986,18 +3288,28 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
2986
3288
  return (0, _transformBaseProductToOrderProduct.transformBaseProductToOrderProduct)(params);
2987
3289
  }
2988
3290
  async calculateProductBookingPrice(params) {
2989
- const quotation = this.store.quotation;
3291
+ const useCustomerUserPricing = this.isCustomerUserPriceQuery();
3292
+ const quotation = useCustomerUserPricing ? undefined : this.store.quotation;
2990
3293
  const customerId = params.customer_id ?? this.getCurrentOrderCustomerId();
2991
3294
  await this.prepareProductPriceQueryContext(customerId);
2992
- const authoritativeProduct = await this.loadProductForPriceQuery(params, customerId);
2993
- const product = this.mergeProductForPriceQuery(params.product, authoritativeProduct);
2994
- await this.loadQuotationForPriceQuery({
2995
- quotation,
2996
- customer_id: customerId,
2997
- channel: params.channel
3295
+ const channel = this.getPriceQueryChannel(params.channel);
3296
+ const [authoritativeProduct] = await this.resolveProductsForPriceQuery({
3297
+ priceQueries: [params],
3298
+ schedule: this.getPriceQuerySchedule(params),
3299
+ customerId,
3300
+ channel
2998
3301
  });
3302
+ const product = this.mergeProductForPriceQuery(params.product, authoritativeProduct);
3303
+ if (!useCustomerUserPricing) {
3304
+ await this.loadQuotationForPriceQuery({
3305
+ quotation,
3306
+ customer_id: customerId,
3307
+ channel
3308
+ });
3309
+ }
2999
3310
  return (0, _quotationPrice.calculateBaseSalesProductBookingPrice)({
3000
3311
  ...params,
3312
+ channel,
3001
3313
  product,
3002
3314
  fallback_price: authoritativeProduct ? undefined : params.fallback_price,
3003
3315
  customer_id: customerId,
@@ -3045,13 +3357,13 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
3045
3357
  return Array.isArray(configs) && configs.length > 0;
3046
3358
  }
3047
3359
  shouldRecalculateProductBookingPricesForContextChange(params) {
3048
- const scopeInfo = this.getQuotationCustomerScopeInfo();
3049
- const scopedCustomerIds = new Set(scopeInfo.customerIds.map(id => String(id)));
3050
3360
  const previousCustomerId = this.normalizePriceContextCustomerId(params.previousCustomerId);
3051
3361
  const nextCustomerId = this.normalizePriceContextCustomerId(params.nextCustomerId);
3052
- if (this.hasSmartPricingStrategies()) {
3362
+ if (this.isCustomerUserPriceQuery() || this.hasSmartPricingStrategies()) {
3053
3363
  return previousCustomerId !== nextCustomerId;
3054
3364
  }
3365
+ const scopeInfo = this.getQuotationCustomerScopeInfo();
3366
+ const scopedCustomerIds = new Set(scopeInfo.customerIds.map(id => String(id)));
3055
3367
  const previousInScope = previousCustomerId ? scopedCustomerIds.has(previousCustomerId) : false;
3056
3368
  const nextInScope = nextCustomerId ? scopedCustomerIds.has(nextCustomerId) : false;
3057
3369
  let shouldRecalculate = false;
@@ -3068,50 +3380,64 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
3068
3380
  }
3069
3381
  async calculateProductBookingPrices(paramsList) {
3070
3382
  if (!paramsList.length) return [];
3071
- const quotation = this.store.quotation;
3072
- const customerId = paramsList[0]?.customer_id ?? this.getCurrentOrderCustomerId();
3073
- await this.prepareProductPriceQueryContext(customerId);
3074
- const productMapsByGroup = new Map();
3383
+ const useCustomerUserPricing = this.isCustomerUserPriceQuery();
3384
+ const quotation = useCustomerUserPricing ? undefined : this.store.quotation;
3385
+ const currentOrderCustomerId = this.getCurrentOrderCustomerId();
3386
+ const resolvedChannels = paramsList.map(params => this.getPriceQueryChannel(params.channel));
3387
+ const authoritativeProducts = paramsList.map(() => null);
3075
3388
  const groups = new Map();
3076
- paramsList.forEach(params => {
3077
- const product = params.product || {};
3078
- const productId = this.getPriceQueryProductId(product);
3079
- if (productId === null) return;
3389
+ paramsList.forEach((params, index) => {
3080
3390
  const schedule = this.getPriceQuerySchedule(params);
3081
- const channel = params.channel || this.otherParams?.channel;
3082
- const groupKey = [schedule.schedule_date, schedule.schedule_datetime, channel || ''].join('|');
3391
+ const channel = resolvedChannels[index];
3392
+ const customerId = params.customer_id ?? currentOrderCustomerId;
3393
+ const groupKey = [schedule.schedule_date, schedule.schedule_datetime, channel || '', this.normalizePriceContextCustomerId(customerId) || ''].join('|');
3083
3394
  const group = groups.get(groupKey) || {
3084
- ids: new Set(),
3395
+ indices: [],
3085
3396
  schedule,
3397
+ customerId,
3086
3398
  channel
3087
3399
  };
3088
- group.ids.add(productId);
3400
+ group.indices.push(index);
3089
3401
  groups.set(groupKey, group);
3090
3402
  });
3091
- await Promise.all(Array.from(groups.entries()).map(async ([groupKey, group]) => {
3092
- const productsById = await this.loadProductsForPriceQuery({
3093
- ids: Array.from(group.ids),
3403
+ await Promise.all(Array.from(groups.values()).map(async group => {
3404
+ await this.prepareProductPriceQueryContext(group.customerId);
3405
+ const priceQueries = group.indices.map(index => paramsList[index]);
3406
+ const resolvedProducts = await this.resolveProductsForPriceQuery({
3407
+ priceQueries,
3094
3408
  schedule: group.schedule,
3095
- customerId,
3409
+ customerId: group.customerId,
3096
3410
  channel: group.channel
3097
3411
  });
3098
- productMapsByGroup.set(groupKey, productsById);
3412
+ group.indices.forEach((originalIndex, groupIndex) => {
3413
+ authoritativeProducts[originalIndex] = resolvedProducts[groupIndex] || null;
3414
+ });
3099
3415
  }));
3100
- await this.loadQuotationForPriceQuery({
3101
- quotation,
3102
- customer_id: customerId,
3103
- channel: paramsList[0]?.channel
3104
- });
3105
- return paramsList.map(params => {
3416
+ if (!useCustomerUserPricing) {
3417
+ const quotationContexts = new Map();
3418
+ paramsList.forEach((params, index) => {
3419
+ const channel = resolvedChannels[index];
3420
+ const key = channel || '';
3421
+ if (quotationContexts.has(key)) return;
3422
+ quotationContexts.set(key, {
3423
+ channel,
3424
+ customer_id: params.customer_id ?? currentOrderCustomerId
3425
+ });
3426
+ });
3427
+ await Promise.all(Array.from(quotationContexts.values()).map(context => this.loadQuotationForPriceQuery({
3428
+ quotation,
3429
+ ...context
3430
+ })));
3431
+ }
3432
+ return paramsList.map((params, index) => {
3106
3433
  const productInput = params.product || {};
3107
- const productId = this.getPriceQueryProductId(productInput);
3108
- const schedule = this.getPriceQuerySchedule(params);
3109
- const channel = params.channel || this.otherParams?.channel;
3110
- const groupKey = [schedule.schedule_date, schedule.schedule_datetime, channel || ''].join('|');
3111
- const authoritativeProduct = productId === null ? null : productMapsByGroup.get(groupKey)?.get(productId) || null;
3434
+ const authoritativeProduct = authoritativeProducts[index];
3112
3435
  const product = this.mergeProductForPriceQuery(productInput, authoritativeProduct);
3436
+ const customerId = params.customer_id ?? currentOrderCustomerId;
3437
+ const channel = resolvedChannels[index];
3113
3438
  return (0, _quotationPrice.calculateBaseSalesProductBookingPrice)({
3114
3439
  ...params,
3440
+ channel,
3115
3441
  product,
3116
3442
  fallback_price: authoritativeProduct ? undefined : params.fallback_price,
3117
3443
  customer_id: customerId,
@@ -112,6 +112,8 @@ export interface BaseSalesUpdateOrderProductQuantityParams extends BaseSalesOrde
112
112
  export interface BaseSalesOrderProduct extends BaseSalesOrderProductIdentity {
113
113
  order_detail_id: number | null;
114
114
  num: number;
115
+ /** Product 或当前 Variant 的生产编码 */
116
+ production_code?: string;
115
117
  product_sku: BaseSalesProductSku;
116
118
  /**
117
119
  * 券后 **行 composite 单价** = `metadata.main_product_selling_price`
@@ -3,6 +3,7 @@ import type { ProductData } from '../../../modules/Product/types';
3
3
  export interface BaseProductDetailOptionItem {
4
4
  option_group_item_id?: number | string;
5
5
  option_group_id?: number | string;
6
+ production_code?: string;
6
7
  num?: number | string;
7
8
  quantity?: number | string;
8
9
  price?: number | string;
@@ -10,6 +11,7 @@ export interface BaseProductDetailOptionItem {
10
11
  [key: string]: any;
11
12
  }
12
13
  export interface BaseProductDetailBundleItem {
14
+ production_code?: string;
13
15
  option?: BaseProductDetailOptionItem[];
14
16
  [key: string]: any;
15
17
  }
@@ -18,6 +20,7 @@ export interface BaseProductDetailPayload {
18
20
  product_id?: number | string | null;
19
21
  variant_id?: number | string | null;
20
22
  product_variant_id?: number | string | null;
23
+ production_code?: string;
21
24
  quantity?: number | string;
22
25
  num?: number | string;
23
26
  option?: BaseProductDetailOptionItem[];
@@ -27,6 +27,10 @@ function toPriceString(value, fallback = '0.00') {
27
27
  if (!Number.isFinite(parsedValue)) return fallback;
28
28
  return parsedValue.toFixed(2);
29
29
  }
30
+ function normalizeProductionCode(value) {
31
+ if (value === undefined || value === null) return '';
32
+ return String(value).trim();
33
+ }
30
34
  function isBlankValue(value) {
31
35
  return value === undefined || value === null || typeof value === 'string' && value.trim() === '';
32
36
  }
@@ -108,6 +112,7 @@ function transformBaseProductToOrderProduct(params) {
108
112
  ...(callbackOrigin || {})
109
113
  };
110
114
  const matchedVariant = findVariantById(callbackOrigin, productVariantId) ?? findVariantById(sourceProduct, productVariantId) ?? findVariantById(origin, productVariantId);
115
+ const productionCode = normalizeProductionCode(Object.prototype.hasOwnProperty.call(payload, 'production_code') ? payload.production_code : productVariantId > 0 ? matchedVariant?.production_code : origin?.production_code ?? sourceProduct?.production_code);
111
116
  const hasPriceOverride = payload.price_override !== undefined && payload.price_override !== null && payload.price_override !== '';
112
117
  // 手动改价后 payload.price 是分摊后的主商品售价,不能再作为券前 source。
113
118
  // 此时优先从商品/variant 快照读取目录价;售价仍由 line_total - bundle 推导。
@@ -186,13 +191,15 @@ function transformBaseProductToOrderProduct(params) {
186
191
  metadata.unique_identification_number = String(uniqueIdentificationNumber);
187
192
  }
188
193
  // 从源头补充商品多语言标题
194
+ const productTitleI18n = sourceProduct?.title_i18n || {};
189
195
  const productTitle = {
190
- ...(sourceProduct?.title_i18n || {}),
191
- original: sourceProduct?.original_title || sourceProduct?.title || ''
196
+ ...productTitleI18n,
197
+ original: productTitleI18n.original || sourceProduct?.original_title || sourceProduct?.title || ''
192
198
  };
193
199
  return {
194
200
  product_id: productId,
195
201
  product_variant_id: productVariantId,
202
+ production_code: productionCode,
196
203
  unique_identification_number: uniqueIdentificationNumber ? String(uniqueIdentificationNumber) : undefined,
197
204
  num: resolvedQuantity,
198
205
  product_sku: {