@pisell/pisellos 2.2.291 → 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.
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.js +108 -65
- package/dist/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
- package/dist/model/strategy/adapter/itemRule/adapter.js +135 -35
- package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
- package/dist/model/strategy/adapter/itemRule/evaluator.js +25 -1
- package/dist/model/strategy/adapter/itemRule/type.d.ts +44 -0
- package/dist/model/strategy/adapter/itemRule/type.js +19 -1
- package/dist/model/strategy/adapter/promotion/index.js +0 -9
- package/dist/modules/BookingContext/index.d.ts +3 -0
- package/dist/modules/BookingContext/index.js +42 -13
- package/dist/modules/BookingContext/types.d.ts +4 -4
- package/dist/modules/BookingContext/types.js +3 -3
- package/dist/modules/OpenData/types.d.ts +1 -0
- package/dist/modules/Order/index.d.ts +18 -3
- package/dist/modules/Order/index.js +885 -485
- package/dist/modules/Order/salesNotes.d.ts +31 -0
- package/dist/modules/Order/salesNotes.js +492 -0
- package/dist/modules/Order/types.d.ts +93 -5
- package/dist/modules/Order/types.js +5 -0
- package/dist/modules/Order/utils.d.ts +12 -0
- package/dist/modules/Order/utils.js +96 -31
- package/dist/modules/Product/types.d.ts +2 -0
- package/dist/modules/ProductList/clientDataVariants.d.ts +50 -0
- package/dist/modules/ProductList/clientDataVariants.js +216 -0
- package/dist/modules/ProductList/index.d.ts +18 -3
- package/dist/modules/ProductList/index.js +220 -38
- package/dist/modules/ProductList/types.d.ts +10 -0
- package/dist/modules/Quotation/index.d.ts +22 -0
- package/dist/modules/Quotation/index.js +293 -28
- package/dist/modules/ResourcePlanner/index.d.ts +0 -22
- package/dist/modules/ResourcePlanner/index.js +1 -180
- package/dist/modules/ResourcePlanner/localClock.d.ts +33 -0
- package/dist/modules/ResourcePlanner/localClock.js +183 -0
- package/dist/modules/ResourcePlanner/planner.d.ts +5 -14
- package/dist/modules/ResourcePlanner/planner.js +2111 -958
- package/dist/modules/ResourcePlanner/types.d.ts +359 -187
- package/dist/modules/ResourcePlanner/types.js +33 -1
- package/dist/plugins/request.d.ts +1 -0
- package/dist/server/index.d.ts +17 -2
- package/dist/server/index.js +1134 -932
- package/dist/server/modules/floor-plan/index.d.ts +4 -0
- package/dist/server/modules/floor-plan/index.js +240 -98
- package/dist/server/modules/menu/index.js +48 -23
- package/dist/server/modules/order/index.d.ts +17 -7
- package/dist/server/modules/order/index.js +419 -207
- package/dist/server/modules/products/index.d.ts +10 -4
- package/dist/server/modules/products/index.js +177 -84
- package/dist/server/modules/resource/index.js +21 -13
- package/dist/server/utils/product.d.ts +1 -1
- package/dist/server/utils/product.js +186 -22
- package/dist/server/utils/small-ticket.d.ts +9 -1
- package/dist/server/utils/small-ticket.js +131 -84
- package/dist/solution/BaseSales/index.d.ts +26 -3
- package/dist/solution/BaseSales/index.js +1384 -905
- package/dist/solution/BaseSales/types.d.ts +2 -0
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +3 -0
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +10 -3
- package/dist/solution/BaseSales/utils.js +6 -3
- package/dist/solution/BookingByStep/index.d.ts +4 -4
- package/dist/solution/BookingTicket/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.js +4 -2
- package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +2 -1
- package/dist/solution/BookingTicket/utils/addProductDecision.js +6 -3
- package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
- package/dist/solution/RegisterAndLogin/config.d.ts +9 -3
- package/dist/solution/RegisterAndLogin/config.js +95 -40
- package/dist/solution/RegisterAndLogin/index.js +4 -1
- package/dist/solution/ScanOrder/index.d.ts +1 -0
- package/dist/solution/ScanOrder/index.js +31 -27
- package/dist/solution/ScanOrder/types.d.ts +2 -0
- package/dist/solution/ScanOrder/utils.d.ts +1 -0
- package/dist/solution/ScanOrder/utils.js +8 -3
- package/dist/solution/UnifiedBookingSales/index.d.ts +128 -74
- package/dist/solution/UnifiedBookingSales/index.js +3877 -963
- package/dist/solution/UnifiedBookingSales/remoteOccupancyCache.d.ts +49 -0
- package/dist/solution/UnifiedBookingSales/remoteOccupancyCache.js +430 -0
- package/dist/solution/UnifiedBookingSales/types.d.ts +161 -77
- package/dist/solution/UnifiedBookingSales/types.js +4 -9
- package/dist/solution/VenueBooking/index.d.ts +3 -0
- package/dist/solution/VenueBooking/index.js +549 -497
- package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
- package/dist/solution/VenueBooking/utils/dateSummary.js +3 -1
- package/dist/solution/VenueBooking/utils/slotMerge.d.ts +2 -0
- package/dist/solution/VenueBooking/utils/slotMerge.js +3 -1
- package/dist/utils/platform.d.ts +8 -0
- package/dist/utils/platform.js +15 -0
- package/lib/core/index.d.ts +7 -0
- package/lib/core/index.js +20 -2
- package/lib/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
- package/lib/model/strategy/adapter/itemRule/adapter.js +73 -4
- package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
- package/lib/model/strategy/adapter/itemRule/evaluator.js +23 -1
- package/lib/model/strategy/adapter/itemRule/type.d.ts +44 -0
- package/lib/model/strategy/adapter/itemRule/type.js +19 -1
- package/lib/modules/BookingContext/index.d.ts +3 -0
- package/lib/modules/BookingContext/index.js +35 -8
- package/lib/modules/BookingContext/types.d.ts +4 -4
- package/lib/modules/BookingContext/types.js +3 -3
- package/lib/modules/OpenData/types.d.ts +1 -0
- package/lib/modules/Order/index.d.ts +18 -3
- package/lib/modules/Order/index.js +325 -28
- package/lib/modules/Order/salesNotes.d.ts +31 -0
- package/lib/modules/Order/salesNotes.js +382 -0
- package/lib/modules/Order/types.d.ts +93 -5
- package/lib/modules/Order/types.js +4 -0
- package/lib/modules/Order/utils.d.ts +12 -0
- package/lib/modules/Order/utils.js +85 -16
- package/lib/modules/Product/types.d.ts +2 -0
- package/lib/modules/ProductList/clientDataVariants.d.ts +50 -0
- package/lib/modules/ProductList/clientDataVariants.js +204 -0
- package/lib/modules/ProductList/index.d.ts +18 -3
- package/lib/modules/ProductList/index.js +137 -8
- package/lib/modules/ProductList/types.d.ts +10 -0
- package/lib/modules/Quotation/index.d.ts +22 -0
- package/lib/modules/Quotation/index.js +158 -11
- package/lib/modules/ResourcePlanner/index.d.ts +0 -22
- package/lib/modules/ResourcePlanner/index.js +9 -129
- package/lib/modules/ResourcePlanner/localClock.d.ts +33 -0
- package/lib/modules/ResourcePlanner/localClock.js +205 -0
- package/lib/modules/ResourcePlanner/planner.d.ts +5 -14
- package/lib/modules/ResourcePlanner/planner.js +1807 -783
- package/lib/modules/ResourcePlanner/types.d.ts +359 -187
- package/lib/modules/ResourcePlanner/types.js +13 -1
- package/lib/plugins/request.d.ts +1 -0
- package/lib/server/index.d.ts +17 -2
- package/lib/server/index.js +149 -21
- package/lib/server/modules/floor-plan/index.d.ts +4 -0
- package/lib/server/modules/floor-plan/index.js +60 -7
- package/lib/server/modules/menu/index.js +26 -4
- package/lib/server/modules/order/index.d.ts +17 -7
- package/lib/server/modules/order/index.js +194 -57
- package/lib/server/modules/products/index.d.ts +10 -4
- package/lib/server/modules/products/index.js +93 -23
- package/lib/server/modules/resource/index.js +12 -2
- package/lib/server/utils/product.d.ts +1 -1
- package/lib/server/utils/product.js +163 -3
- package/lib/server/utils/small-ticket.d.ts +9 -1
- package/lib/server/utils/small-ticket.js +123 -79
- package/lib/solution/BaseSales/index.d.ts +26 -3
- package/lib/solution/BaseSales/index.js +396 -70
- package/lib/solution/BaseSales/types.d.ts +2 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +3 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +9 -2
- package/lib/solution/BaseSales/utils.js +3 -0
- package/lib/solution/BookingByStep/index.d.ts +4 -4
- package/lib/solution/BookingTicket/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.js +2 -1
- package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +2 -1
- package/lib/solution/BookingTicket/utils/addProductDecision.js +5 -2
- package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
- package/lib/solution/RegisterAndLogin/config.d.ts +9 -3
- package/lib/solution/RegisterAndLogin/config.js +49 -8
- package/lib/solution/RegisterAndLogin/index.js +4 -1
- package/lib/solution/ScanOrder/index.d.ts +1 -0
- package/lib/solution/ScanOrder/index.js +5 -1
- package/lib/solution/ScanOrder/types.d.ts +2 -0
- package/lib/solution/ScanOrder/utils.d.ts +1 -0
- package/lib/solution/ScanOrder/utils.js +4 -0
- package/lib/solution/UnifiedBookingSales/index.d.ts +128 -74
- package/lib/solution/UnifiedBookingSales/index.js +2592 -424
- package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.d.ts +49 -0
- package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.js +316 -0
- package/lib/solution/UnifiedBookingSales/types.d.ts +161 -77
- package/lib/solution/UnifiedBookingSales/types.js +4 -6
- package/lib/solution/VenueBooking/index.d.ts +3 -0
- package/lib/solution/VenueBooking/index.js +33 -9
- package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
- package/lib/solution/VenueBooking/utils/dateSummary.js +3 -1
- package/lib/solution/VenueBooking/utils/slotMerge.d.ts +2 -0
- package/lib/solution/VenueBooking/utils/slotMerge.js +3 -1
- package/lib/utils/platform.d.ts +8 -0
- package/lib/utils/platform.js +22 -0
- 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
|
-
|
|
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
|
-
|
|
642
|
-
|
|
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
|
|
729
|
-
const strategyContext = this.getPriceQueryStrategyContext(channel);
|
|
937
|
+
const handledByOsServer = (0, _clientDataVariants.isProductQueryHandledByOsServer)(this.core, this.request);
|
|
730
938
|
try {
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
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
|
-
|
|
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
|
|
765
|
-
const
|
|
766
|
-
const
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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
|
|
2993
|
-
const
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
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
|
|
3072
|
-
const
|
|
3073
|
-
|
|
3074
|
-
const
|
|
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 =
|
|
3082
|
-
const
|
|
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
|
-
|
|
3395
|
+
indices: [],
|
|
3085
3396
|
schedule,
|
|
3397
|
+
customerId,
|
|
3086
3398
|
channel
|
|
3087
3399
|
};
|
|
3088
|
-
group.
|
|
3400
|
+
group.indices.push(index);
|
|
3089
3401
|
groups.set(groupKey, group);
|
|
3090
3402
|
});
|
|
3091
|
-
await Promise.all(Array.from(groups.
|
|
3092
|
-
|
|
3093
|
-
|
|
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
|
-
|
|
3412
|
+
group.indices.forEach((originalIndex, groupIndex) => {
|
|
3413
|
+
authoritativeProducts[originalIndex] = resolvedProducts[groupIndex] || null;
|
|
3414
|
+
});
|
|
3099
3415
|
}));
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
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
|
|
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
|
-
...
|
|
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: {
|