@pisell/pisellos 0.10.43 → 0.10.44
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/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/modules/Order/index.d.ts +41 -9
- package/dist/modules/Order/index.js +1438 -878
- package/dist/modules/Order/salesNotes.d.ts +31 -0
- package/dist/modules/Order/salesNotes.js +492 -0
- package/dist/modules/Order/types.d.ts +105 -8
- package/dist/modules/Order/types.js +6 -1
- package/dist/modules/Order/utils.d.ts +3 -1
- package/dist/modules/Order/utils.js +191 -71
- package/dist/modules/Payment/cashRecommendationAlgorithm.d.ts +4 -5
- package/dist/modules/Payment/cashRecommendationAlgorithm.js +17 -367
- package/dist/modules/ProductList/index.d.ts +2 -1
- package/dist/modules/ProductList/index.js +100 -20
- package/dist/modules/ProductList/types.d.ts +10 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.js +45 -3
- package/dist/server/modules/order/index.js +6 -0
- package/dist/server/modules/order/types.d.ts +9 -0
- package/dist/server/utils/small-ticket.d.ts +1 -0
- package/dist/server/utils/small-ticket.js +142 -14
- package/dist/solution/BaseSales/index.d.ts +15 -1
- package/dist/solution/BaseSales/index.js +1240 -877
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.d.ts +1 -0
- package/dist/solution/BookingTicket/index.js +37 -16
- 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/utils.d.ts +1 -0
- package/dist/solution/ScanOrder/utils.js +2 -0
- package/dist/solution/UnifiedBookingSales/index.d.ts +15 -2
- package/dist/solution/UnifiedBookingSales/index.js +777 -510
- package/dist/solution/UnifiedBookingSales/types.d.ts +14 -1
- package/dist/solution/VenueBooking/index.d.ts +1 -0
- package/dist/solution/VenueBooking/index.js +8 -4
- 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/Order/index.d.ts +41 -9
- package/lib/modules/Order/index.js +434 -61
- package/lib/modules/Order/salesNotes.d.ts +31 -0
- package/lib/modules/Order/salesNotes.js +382 -0
- package/lib/modules/Order/types.d.ts +105 -8
- package/lib/modules/Order/types.js +5 -1
- package/lib/modules/Order/utils.d.ts +3 -1
- package/lib/modules/Order/utils.js +114 -1
- package/lib/modules/Payment/cashRecommendationAlgorithm.d.ts +4 -5
- package/lib/modules/Payment/cashRecommendationAlgorithm.js +9 -354
- package/lib/modules/ProductList/index.d.ts +2 -1
- package/lib/modules/ProductList/index.js +47 -2
- package/lib/modules/ProductList/types.d.ts +10 -0
- package/lib/server/index.d.ts +7 -0
- package/lib/server/index.js +39 -3
- package/lib/server/modules/order/index.js +6 -0
- package/lib/server/modules/order/types.d.ts +9 -0
- package/lib/server/utils/small-ticket.d.ts +1 -0
- package/lib/server/utils/small-ticket.js +136 -12
- package/lib/solution/BaseSales/index.d.ts +15 -1
- package/lib/solution/BaseSales/index.js +192 -22
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.d.ts +1 -0
- package/lib/solution/BookingTicket/index.js +23 -5
- 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/utils.d.ts +1 -0
- package/lib/solution/ScanOrder/utils.js +1 -0
- package/lib/solution/UnifiedBookingSales/index.d.ts +15 -2
- package/lib/solution/UnifiedBookingSales/index.js +125 -1
- package/lib/solution/UnifiedBookingSales/types.d.ts +14 -1
- package/lib/solution/VenueBooking/index.d.ts +1 -0
- package/lib/solution/VenueBooking/index.js +5 -1
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@ var _utils = require("./utils");
|
|
|
10
10
|
var _utils2 = require("../Product/utils");
|
|
11
11
|
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
12
12
|
var _osPrivatePayload = require("../../utils/os-private-payload");
|
|
13
|
+
var _salesNotes = require("./salesNotes");
|
|
13
14
|
var _paymentUtils = require("./payment-utils");
|
|
14
15
|
var _paymentNumber = require("../../utils/payment-number");
|
|
15
16
|
var _cartPromotion = require("../../solution/BaseSales/utils/cartPromotion");
|
|
@@ -126,6 +127,8 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
126
127
|
openCache = false;
|
|
127
128
|
sessionStoragePersistEnabled = false;
|
|
128
129
|
sessionStorageRestoreRecord = null;
|
|
130
|
+
/** 只允许当前订单最新的 prepare/config 请求回写客户资产态。 */
|
|
131
|
+
customerWalletPrepareConfigEpoch = 0;
|
|
129
132
|
salesSummaryModuleName;
|
|
130
133
|
rulesHooksOverride;
|
|
131
134
|
moduleHooksOverride;
|
|
@@ -136,8 +139,10 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
136
139
|
promotionEvaluator = null;
|
|
137
140
|
/** 赠品选择 resolver;由 SDK host bridge 注入 */
|
|
138
141
|
giftSelectResolver = null;
|
|
139
|
-
/**
|
|
142
|
+
/** 促销评估递归保护 flag(写路径同步评估促销,禁止重入) */
|
|
140
143
|
isApplyingPromotion = false;
|
|
144
|
+
/** 保持旧事件语义:同步事件监听器内触发的订单写入不得递归评估促销。 */
|
|
145
|
+
isEmittingPromotionApplied = false;
|
|
141
146
|
/** 商品曾应用过客户券卡,客户变更剥离后需强制跑一次 calcDiscount 复位价格 */
|
|
142
147
|
hasActiveCustomerBoundDiscount = false;
|
|
143
148
|
/** 最近一次 applyPromotion 的未满足促销提示 */
|
|
@@ -494,10 +499,21 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
494
499
|
return (0, _utils.createDefaultOrderRulesHooks)();
|
|
495
500
|
}
|
|
496
501
|
|
|
502
|
+
/**
|
|
503
|
+
* Stable across notifyTempOrderChanged's immutable copy, but changes when a
|
|
504
|
+
* different local/server order becomes current.
|
|
505
|
+
*/
|
|
506
|
+
getDiscountConfigOrderIdentityKey(tempOrder) {
|
|
507
|
+
const identity = this.getOrderIdentity();
|
|
508
|
+
return JSON.stringify([identity?.orderId ?? tempOrder?.order_id ?? '', tempOrder?.request_unique_idempotency_token ?? '', identity?.externalSaleNumber ?? tempOrder?.external_sale_number ?? '', identity?.localId ?? tempOrder?._extend?.localId ?? this.cacheId ?? '']);
|
|
509
|
+
}
|
|
510
|
+
|
|
497
511
|
// ─── Discount: 公共 API ───
|
|
498
512
|
|
|
499
513
|
async loadDiscountConfig(params) {
|
|
500
514
|
const orderId = params.orderId || this.store.tempOrder?.order_id || undefined;
|
|
515
|
+
const requestEpoch = ++this.customerWalletPrepareConfigEpoch;
|
|
516
|
+
const requestOrderIdentityKey = this.getDiscountConfigOrderIdentityKey(this.store.tempOrder);
|
|
501
517
|
const prepareConfigResult = await this.store.discount?.loadPrepareConfig({
|
|
502
518
|
customer_id: params.customerId,
|
|
503
519
|
action: params.action || (orderId ? 'edit' : 'create'),
|
|
@@ -513,13 +529,17 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
513
529
|
order_id: orderId
|
|
514
530
|
} : {})
|
|
515
531
|
});
|
|
532
|
+
const isCurrentRequest = () => requestEpoch === this.customerWalletPrepareConfigEpoch && this.getDiscountConfigOrderIdentityKey(this.store.tempOrder) === requestOrderIdentityKey && String(this.store.tempOrder?.customer_id ?? '') === String(params.customerId);
|
|
533
|
+
if (!isCurrentRequest()) {
|
|
534
|
+
return this.getDiscountList();
|
|
535
|
+
}
|
|
516
536
|
const discountList = prepareConfigResult?.discountList;
|
|
517
|
-
this.store.availableWalletIds = prepareConfigResult?.availableWalletIds || [];
|
|
518
|
-
this.store.virtualCurrencyList = prepareConfigResult?.virtualCurrencyList || [];
|
|
519
537
|
if (prepareConfigResult) {
|
|
520
|
-
this.
|
|
538
|
+
this.writeCustomerWalletSnapshotsFromPrepareConfig({
|
|
521
539
|
customerId: params.customerId,
|
|
522
|
-
|
|
540
|
+
availableWalletIds: prepareConfigResult.availableWalletIds || [],
|
|
541
|
+
availableWalletPassList: prepareConfigResult.availableWalletPassList || [],
|
|
542
|
+
virtualCurrencyList: prepareConfigResult.virtualCurrencyList || []
|
|
523
543
|
});
|
|
524
544
|
}
|
|
525
545
|
if (discountList) {
|
|
@@ -579,9 +599,12 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
579
599
|
return id === null || !refreshedScanDiscountIds.has(id);
|
|
580
600
|
});
|
|
581
601
|
const mergedDiscountList = [...retainedCommittedDiscounts, ...retainedScanDiscounts, ...runtimeDiscounts];
|
|
602
|
+
if (!isCurrentRequest()) return this.getDiscountList();
|
|
582
603
|
await this.store.discount?.setOriginalDiscountList(mergedDiscountList);
|
|
604
|
+
if (!isCurrentRequest()) return this.getDiscountList();
|
|
583
605
|
await this.store.discount?.setDiscountList(mergedDiscountList);
|
|
584
606
|
}
|
|
607
|
+
if (!isCurrentRequest()) return this.getDiscountList();
|
|
585
608
|
if (params.apply !== false && this.store.tempOrder?.products?.length) {
|
|
586
609
|
this.applyDiscount();
|
|
587
610
|
}
|
|
@@ -596,6 +619,9 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
596
619
|
getVirtualCurrencyList() {
|
|
597
620
|
return (0, _lodashEs.cloneDeep)(this.store.virtualCurrencyList || []);
|
|
598
621
|
}
|
|
622
|
+
getCustomerWalletPrepareConfigEpoch() {
|
|
623
|
+
return this.customerWalletPrepareConfigEpoch;
|
|
624
|
+
}
|
|
599
625
|
async syncScannedDiscountsToOriginalDiscountList(discountList) {
|
|
600
626
|
const scanDiscounts = (discountList || []).filter(discount => discount?.isScan);
|
|
601
627
|
if (!scanDiscounts.length) return;
|
|
@@ -808,7 +834,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
808
834
|
}
|
|
809
835
|
|
|
810
836
|
/**
|
|
811
|
-
*
|
|
837
|
+
* 只执行购物车促销评估与赠品写回,不发布稳定态事件。
|
|
812
838
|
*
|
|
813
839
|
* 调用时机:写路径(add/update/remove/clear/setCustomer)末尾自动触发;外部一般无需手动调。
|
|
814
840
|
*
|
|
@@ -816,26 +842,27 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
816
842
|
* - 不调用任何公开 CRUD API(如 removeProductFromOrder),全部内部 mutate tempOrder.products,避免事件风暴;
|
|
817
843
|
* - 用 `isApplyingPromotion` 防递归;
|
|
818
844
|
* - 多选项赠品依赖 giftSelectResolver,未注入则跳过且 console.warn;
|
|
819
|
-
* - 不主动调用 applyDiscount / recalculateSummary
|
|
845
|
+
* - 不主动调用 applyDiscount / recalculateSummary,调用方负责跟进;
|
|
846
|
+
* - 返回的 payload 只能在 applyDiscount(商品写路径还需 summary/persist)完成后发布。
|
|
820
847
|
*/
|
|
821
|
-
async
|
|
822
|
-
if (this.isApplyingPromotion) {
|
|
823
|
-
return;
|
|
848
|
+
async evaluatePromotion() {
|
|
849
|
+
if (this.isApplyingPromotion || this.isEmittingPromotionApplied) {
|
|
850
|
+
return null;
|
|
824
851
|
}
|
|
825
852
|
if (!this.promotionEvaluator) {
|
|
826
|
-
return;
|
|
853
|
+
return null;
|
|
827
854
|
}
|
|
828
855
|
const tempOrder = this.store.tempOrder;
|
|
829
856
|
if (!tempOrder) {
|
|
830
|
-
return;
|
|
857
|
+
return null;
|
|
831
858
|
}
|
|
832
859
|
if (this.isActiveVirtualSettlementOrder(tempOrder)) {
|
|
833
860
|
tempOrder.products = (0, _virtualSettlement.normalizeVirtualSettlementProducts)(tempOrder.products);
|
|
834
|
-
return;
|
|
861
|
+
return null;
|
|
835
862
|
}
|
|
836
863
|
const strategyConfigs = this.promotionEvaluator.getStrategyConfigs?.();
|
|
837
864
|
if (Array.isArray(strategyConfigs) && strategyConfigs.length === 0) {
|
|
838
|
-
return;
|
|
865
|
+
return null;
|
|
839
866
|
}
|
|
840
867
|
this.isApplyingPromotion = true;
|
|
841
868
|
try {
|
|
@@ -927,14 +954,46 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
927
954
|
})),
|
|
928
955
|
products: tempOrder.products
|
|
929
956
|
};
|
|
930
|
-
|
|
957
|
+
return payload;
|
|
931
958
|
} catch (error) {
|
|
932
959
|
console.error('[OrderModule] applyPromotion failed', error);
|
|
960
|
+
return null;
|
|
933
961
|
} finally {
|
|
934
962
|
this.isApplyingPromotion = false;
|
|
935
963
|
}
|
|
936
964
|
}
|
|
937
965
|
|
|
966
|
+
/** 发布促销稳定态;products 必须读取折扣计算后的当前数组。 */
|
|
967
|
+
emitPromotionApplied(payload) {
|
|
968
|
+
if (!payload) return;
|
|
969
|
+
const products = this.store.tempOrder?.products || payload.products;
|
|
970
|
+
this.isEmittingPromotionApplied = true;
|
|
971
|
+
try {
|
|
972
|
+
void this.effectsEmit('onPromotionApplied', {
|
|
973
|
+
...payload,
|
|
974
|
+
products
|
|
975
|
+
});
|
|
976
|
+
} finally {
|
|
977
|
+
// effectsEmit 同步调用监听器后返回 Promise;保持既有的非阻塞事件行为。
|
|
978
|
+
this.isEmittingPromotionApplied = false;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/** 公开入口:促销、折扣、summary 与持久化完成后发布稳定态事件。 */
|
|
983
|
+
async applyPromotion() {
|
|
984
|
+
const payload = await this.evaluatePromotion();
|
|
985
|
+
if (!payload) return;
|
|
986
|
+
const tempOrder = this.store.tempOrder;
|
|
987
|
+
if (!tempOrder) return;
|
|
988
|
+
this.applyDiscount();
|
|
989
|
+
this.sanitizeTempOrderProducts(tempOrder);
|
|
990
|
+
await this.recalculateSummary({
|
|
991
|
+
createIfMissing: true
|
|
992
|
+
});
|
|
993
|
+
this.persistTempOrder();
|
|
994
|
+
this.emitPromotionApplied(payload);
|
|
995
|
+
}
|
|
996
|
+
|
|
938
997
|
/** 最近一次 applyPromotion 输出的未满足促销列表 */
|
|
939
998
|
getUnfulfilledPromotions() {
|
|
940
999
|
return this.lastUnfulfilledPromotions;
|
|
@@ -1038,6 +1097,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1038
1097
|
this.store.tempOrder = {
|
|
1039
1098
|
...this.store.tempOrder,
|
|
1040
1099
|
products: [...this.store.tempOrder.products],
|
|
1100
|
+
notes: [...(this.store.tempOrder.notes || [])],
|
|
1041
1101
|
bookings: this.store.tempOrder.bookings ? [...this.store.tempOrder.bookings] : []
|
|
1042
1102
|
};
|
|
1043
1103
|
this.persistTempOrder();
|
|
@@ -1050,27 +1110,41 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1050
1110
|
now: (0, _utils.formatDateTime)(new Date())
|
|
1051
1111
|
});
|
|
1052
1112
|
}
|
|
1053
|
-
|
|
1113
|
+
clearTempOrderCustomerWalletSnapshots(tempOrder) {
|
|
1054
1114
|
const orderMetaList = (0, _utils.normalizeOrderMetaList)(tempOrder.order_meta_list);
|
|
1055
1115
|
if (!orderMetaList) return;
|
|
1056
1116
|
const nextOrderMetaList = {
|
|
1057
1117
|
...orderMetaList
|
|
1058
1118
|
};
|
|
1059
1119
|
delete nextOrderMetaList.point_cards_snapshot;
|
|
1120
|
+
delete nextOrderMetaList.virtual_currency_snapshot;
|
|
1060
1121
|
if (Object.keys(nextOrderMetaList).length === 0) {
|
|
1061
1122
|
delete tempOrder.order_meta_list;
|
|
1062
1123
|
return;
|
|
1063
1124
|
}
|
|
1064
1125
|
tempOrder.order_meta_list = nextOrderMetaList;
|
|
1065
1126
|
}
|
|
1066
|
-
|
|
1127
|
+
clearCustomerWalletState(tempOrder) {
|
|
1128
|
+
this.clearCustomerWalletRuntimeState();
|
|
1129
|
+
this.clearTempOrderCustomerWalletSnapshots(tempOrder);
|
|
1130
|
+
}
|
|
1131
|
+
clearCustomerWalletRuntimeState() {
|
|
1132
|
+
this.customerWalletPrepareConfigEpoch += 1;
|
|
1133
|
+
this.store.availableWalletIds = [];
|
|
1134
|
+
this.store.virtualCurrencyList = [];
|
|
1135
|
+
}
|
|
1136
|
+
writeCustomerWalletSnapshotsFromPrepareConfig(params) {
|
|
1067
1137
|
const tempOrder = this.store.tempOrder;
|
|
1068
1138
|
if (!tempOrder) return;
|
|
1069
1139
|
if (String(tempOrder.customer_id ?? '') !== String(params.customerId)) return;
|
|
1140
|
+
const virtualCurrencyList = Array.isArray(params.virtualCurrencyList) ? params.virtualCurrencyList : [];
|
|
1070
1141
|
tempOrder.order_meta_list = {
|
|
1071
1142
|
...((0, _utils.normalizeOrderMetaList)(tempOrder.order_meta_list) || {}),
|
|
1072
|
-
point_cards_snapshot: (0, _utils.buildPointCardSnapshotFromWalletPassList)(params.availableWalletPassList)
|
|
1143
|
+
point_cards_snapshot: (0, _utils.buildPointCardSnapshotFromWalletPassList)(params.availableWalletPassList),
|
|
1144
|
+
virtual_currency_snapshot: (0, _utils.buildVirtualCurrencySnapshotFromList)(virtualCurrencyList)
|
|
1073
1145
|
};
|
|
1146
|
+
this.store.availableWalletIds = [...params.availableWalletIds];
|
|
1147
|
+
this.store.virtualCurrencyList = (0, _lodashEs.cloneDeep)(virtualCurrencyList);
|
|
1074
1148
|
this.persistTempOrder();
|
|
1075
1149
|
this.saveDraftInBackground();
|
|
1076
1150
|
}
|
|
@@ -1855,6 +1929,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1855
1929
|
const freshTempOrder = this.createDefaultTempOrderInstance();
|
|
1856
1930
|
this.ensureExternalSaleNumber(freshTempOrder);
|
|
1857
1931
|
this.store.tempOrder = freshTempOrder;
|
|
1932
|
+
this.clearCustomerWalletRuntimeState();
|
|
1858
1933
|
this.store.summary = (0, _utils.createEmptySummary)();
|
|
1859
1934
|
this.store.lastOrderInfo = undefined;
|
|
1860
1935
|
this.store.syncState = undefined;
|
|
@@ -1875,6 +1950,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1875
1950
|
ensureIdentity: false
|
|
1876
1951
|
});
|
|
1877
1952
|
this.store.tempOrder = nextTempOrder;
|
|
1953
|
+
this.clearCustomerWalletRuntimeState();
|
|
1878
1954
|
if (options?.recalculateSummary !== false) {
|
|
1879
1955
|
await this.recalculateSummary({
|
|
1880
1956
|
createIfMissing: false
|
|
@@ -2398,6 +2474,179 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2398
2474
|
getTempOrderNote() {
|
|
2399
2475
|
return this.store.tempOrder?.note || '';
|
|
2400
2476
|
}
|
|
2477
|
+
ensureSalesNotesRuntimeState(tempOrder) {
|
|
2478
|
+
const hadNotesSnapshot = Array.isArray(tempOrder.notes);
|
|
2479
|
+
if (!Array.isArray(tempOrder.notes)) tempOrder.notes = [];
|
|
2480
|
+
if (!tempOrder._extend || typeof tempOrder._extend !== 'object') {
|
|
2481
|
+
tempOrder._extend = {
|
|
2482
|
+
productsByUid: {}
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2485
|
+
if (!tempOrder._extend.salesNotesState) {
|
|
2486
|
+
tempOrder._extend.salesNotesState = {
|
|
2487
|
+
loaded: hadNotesSnapshot,
|
|
2488
|
+
dirty: false
|
|
2489
|
+
};
|
|
2490
|
+
}
|
|
2491
|
+
return tempOrder._extend.salesNotesState;
|
|
2492
|
+
}
|
|
2493
|
+
ensureSalesNotesLoadedForMutation(tempOrder) {
|
|
2494
|
+
const state = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2495
|
+
if (state.loaded) return;
|
|
2496
|
+
if (tempOrder.order_id === null || tempOrder.order_id === undefined) {
|
|
2497
|
+
state.loaded = true;
|
|
2498
|
+
tempOrder.notes = [];
|
|
2499
|
+
return;
|
|
2500
|
+
}
|
|
2501
|
+
throw new Error('[Order Notes] 当前订单 Notes 尚未完整加载,禁止提交部分快照');
|
|
2502
|
+
}
|
|
2503
|
+
normalizeSalesNoteUuid(value, field) {
|
|
2504
|
+
const uuid = String(value ?? '').trim();
|
|
2505
|
+
if (!uuid) throw new Error(`[Order Notes] ${field} 不能为空`);
|
|
2506
|
+
if (uuid.length > 255) {
|
|
2507
|
+
throw new Error(`[Order Notes] ${field} 不能超过 255 个字符`);
|
|
2508
|
+
}
|
|
2509
|
+
return uuid;
|
|
2510
|
+
}
|
|
2511
|
+
getSalesNotePrimaryRelation(note) {
|
|
2512
|
+
return note.note_relations.find(relation => relation.is_primary === 1) || null;
|
|
2513
|
+
}
|
|
2514
|
+
removeSalesNotesForMissingProductLines(tempOrder) {
|
|
2515
|
+
const state = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2516
|
+
if (!state.loaded || !Array.isArray(tempOrder.notes) || tempOrder.notes.length === 0) {
|
|
2517
|
+
return;
|
|
2518
|
+
}
|
|
2519
|
+
const remainingProductUids = new Set((tempOrder.products || []).map(product => product.metadata?.unique_identification_number || product.unique_identification_number).filter(uid => typeof uid === 'string' && uid.length > 0));
|
|
2520
|
+
const nextNotes = tempOrder.notes.filter(note => {
|
|
2521
|
+
const primary = this.getSalesNotePrimaryRelation(note);
|
|
2522
|
+
if (!primary || primary.target_type !== 'order_detail') return true;
|
|
2523
|
+
return remainingProductUids.has(primary.target_uuid);
|
|
2524
|
+
});
|
|
2525
|
+
if (nextNotes.length === tempOrder.notes.length) return;
|
|
2526
|
+
tempOrder.notes = nextNotes;
|
|
2527
|
+
state.dirty = true;
|
|
2528
|
+
}
|
|
2529
|
+
setSalesNoteOnTempOrder(tempOrder, input) {
|
|
2530
|
+
this.ensureSalesNotesLoadedForMutation(tempOrder);
|
|
2531
|
+
const note_type = String(input.note_type || '').trim();
|
|
2532
|
+
const policy = (0, _salesNotes.getSalesNotePolicy)(note_type);
|
|
2533
|
+
if (!policy) {
|
|
2534
|
+
throw new Error(`[Order Notes] 当前未注册备注类型: ${note_type || '(empty)'}`);
|
|
2535
|
+
}
|
|
2536
|
+
if (input.target?.target_type !== 'order_detail') {
|
|
2537
|
+
throw new Error('[Order Notes] 本期只支持 order_detail 作为主体');
|
|
2538
|
+
}
|
|
2539
|
+
const target_uuid = this.normalizeSalesNoteUuid(input.target.target_uuid, 'target_uuid');
|
|
2540
|
+
const targetProductExists = (tempOrder.products || []).some(product => {
|
|
2541
|
+
const productLineUid = product.metadata?.unique_identification_number || product.unique_identification_number;
|
|
2542
|
+
return String(productLineUid ?? '') === target_uuid;
|
|
2543
|
+
});
|
|
2544
|
+
if (!targetProductExists) {
|
|
2545
|
+
throw new Error('[Order Notes] 目标商品行不存在');
|
|
2546
|
+
}
|
|
2547
|
+
const text = (0, _salesNotes.buildSalesNoteText)(input.text);
|
|
2548
|
+
const notes = tempOrder.notes || [];
|
|
2549
|
+
// 当前 allergy policy 固定同一商品行只保留一条;业务调用方不参与去重规则。
|
|
2550
|
+
const existingIndex = policy.uniquePerPrimaryTarget ? notes.findIndex(note => {
|
|
2551
|
+
const primary = this.getSalesNotePrimaryRelation(note);
|
|
2552
|
+
return note.note_type === note_type && primary?.target_type === 'order_detail' && primary.target_uuid === target_uuid;
|
|
2553
|
+
}) : -1;
|
|
2554
|
+
const existing = existingIndex >= 0 ? notes[existingIndex] : null;
|
|
2555
|
+
const existingPrimary = existing?.note_relations.find(relation => relation.is_primary === 1 && relation.target_type === 'order_detail');
|
|
2556
|
+
const existingOrderRelation = existing?.note_relations.find(relation => relation.is_primary === 0 && relation.target_type === 'order');
|
|
2557
|
+
const note = {
|
|
2558
|
+
uuid: existing?.uuid || (0, _utils.createUuidV4)(),
|
|
2559
|
+
note_type: policy.note_type,
|
|
2560
|
+
content: {
|
|
2561
|
+
text
|
|
2562
|
+
},
|
|
2563
|
+
importance: policy.importance,
|
|
2564
|
+
note_relations: [{
|
|
2565
|
+
uuid: existingPrimary?.uuid || (0, _utils.createUuidV4)(),
|
|
2566
|
+
relation_type: policy.primary_relation_type,
|
|
2567
|
+
target_type: 'order_detail',
|
|
2568
|
+
target_uuid,
|
|
2569
|
+
is_primary: 1
|
|
2570
|
+
}, {
|
|
2571
|
+
uuid: existingOrderRelation?.uuid || (0, _utils.createUuidV4)(),
|
|
2572
|
+
relation_type: policy.secondary_order_relation_type,
|
|
2573
|
+
target_type: 'order',
|
|
2574
|
+
target_uuid: String(tempOrder.shop_order_number ?? ''),
|
|
2575
|
+
is_primary: 0
|
|
2576
|
+
}]
|
|
2577
|
+
};
|
|
2578
|
+
const duplicateIndex = notes.findIndex((item, index) => item.uuid === note.uuid && index !== existingIndex);
|
|
2579
|
+
if (duplicateIndex >= 0) {
|
|
2580
|
+
throw new Error('[Order Notes] uuid 已被其他 Note 使用');
|
|
2581
|
+
}
|
|
2582
|
+
tempOrder.notes = existingIndex >= 0 ? notes.map((item, index) => index === existingIndex ? note : item) : [...notes, note];
|
|
2583
|
+
const state = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2584
|
+
state.loaded = true;
|
|
2585
|
+
state.dirty = true;
|
|
2586
|
+
return note;
|
|
2587
|
+
}
|
|
2588
|
+
getSalesNotes() {
|
|
2589
|
+
const tempOrder = this.ensureTempOrder();
|
|
2590
|
+
this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2591
|
+
return (0, _lodashEs.cloneDeep)(tempOrder.notes || []);
|
|
2592
|
+
}
|
|
2593
|
+
createSalesNotesMutationSnapshot() {
|
|
2594
|
+
const tempOrder = this.ensureTempOrder();
|
|
2595
|
+
const state = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2596
|
+
return {
|
|
2597
|
+
orderIdentity: {
|
|
2598
|
+
orderId: tempOrder.order_id ?? null,
|
|
2599
|
+
externalSaleNumber: String(tempOrder.external_sale_number || '')
|
|
2600
|
+
},
|
|
2601
|
+
notes: (0, _lodashEs.cloneDeep)(tempOrder.notes || []),
|
|
2602
|
+
state: (0, _lodashEs.cloneDeep)(state)
|
|
2603
|
+
};
|
|
2604
|
+
}
|
|
2605
|
+
restoreSalesNotesMutationSnapshot(snapshot) {
|
|
2606
|
+
if (!snapshot || !Array.isArray(snapshot.notes) || !snapshot.orderIdentity || typeof snapshot.orderIdentity.externalSaleNumber !== 'string' || typeof snapshot.state?.loaded !== 'boolean' || typeof snapshot.state?.dirty !== 'boolean') {
|
|
2607
|
+
throw new Error('[Order Notes] 无效的 mutation snapshot');
|
|
2608
|
+
}
|
|
2609
|
+
const tempOrder = this.store.tempOrder;
|
|
2610
|
+
if (!tempOrder) throw new Error('[Order Notes] 当前订单不存在');
|
|
2611
|
+
const snapshotOrderId = snapshot.orderIdentity.orderId;
|
|
2612
|
+
const currentOrderId = tempOrder.order_id;
|
|
2613
|
+
const hasSnapshotOrderId = snapshotOrderId !== null && snapshotOrderId !== undefined && String(snapshotOrderId).trim().length > 0;
|
|
2614
|
+
const hasCurrentOrderId = currentOrderId !== null && currentOrderId !== undefined && String(currentOrderId).trim().length > 0;
|
|
2615
|
+
const isSameOrder = hasSnapshotOrderId && hasCurrentOrderId ? String(snapshotOrderId) === String(currentOrderId) : Boolean(snapshot.orderIdentity.externalSaleNumber) && snapshot.orderIdentity.externalSaleNumber === String(tempOrder.external_sale_number || '');
|
|
2616
|
+
if (!isSameOrder) {
|
|
2617
|
+
throw new Error('[Order Notes] 当前订单已切换,拒绝恢复旧订单快照');
|
|
2618
|
+
}
|
|
2619
|
+
this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2620
|
+
tempOrder.notes = (0, _lodashEs.cloneDeep)(snapshot.notes);
|
|
2621
|
+
tempOrder._extend.salesNotesState = (0, _lodashEs.cloneDeep)(snapshot.state);
|
|
2622
|
+
this.notifyTempOrderChanged();
|
|
2623
|
+
}
|
|
2624
|
+
async setSalesNote(input) {
|
|
2625
|
+
const tempOrder = this.ensureTempOrder();
|
|
2626
|
+
const note = this.setSalesNoteOnTempOrder(tempOrder, input);
|
|
2627
|
+
this.notifyTempOrderChanged();
|
|
2628
|
+
return (0, _lodashEs.cloneDeep)(note);
|
|
2629
|
+
}
|
|
2630
|
+
async removeSalesNote(uuid) {
|
|
2631
|
+
const tempOrder = this.ensureTempOrder();
|
|
2632
|
+
this.ensureSalesNotesLoadedForMutation(tempOrder);
|
|
2633
|
+
const normalizedUuid = this.normalizeSalesNoteUuid(uuid, 'uuid');
|
|
2634
|
+
const notes = tempOrder.notes || [];
|
|
2635
|
+
const nextNotes = notes.filter(note => note.uuid !== normalizedUuid);
|
|
2636
|
+
if (nextNotes.length === notes.length) return;
|
|
2637
|
+
tempOrder.notes = nextNotes;
|
|
2638
|
+
const state = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2639
|
+
state.dirty = true;
|
|
2640
|
+
this.notifyTempOrderChanged();
|
|
2641
|
+
}
|
|
2642
|
+
async clearSalesNotes() {
|
|
2643
|
+
const tempOrder = this.ensureTempOrder();
|
|
2644
|
+
tempOrder.notes = [];
|
|
2645
|
+
const state = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
2646
|
+
state.loaded = true;
|
|
2647
|
+
state.dirty = true;
|
|
2648
|
+
this.notifyTempOrderChanged();
|
|
2649
|
+
}
|
|
2401
2650
|
updateTempOrderShopDiscount(amount) {
|
|
2402
2651
|
const tempOrder = this.ensureTempOrder();
|
|
2403
2652
|
const discount = _decimal.default.max(new _decimal.default(Number(amount) || 0), 0).toDecimalPlaces(2).toFixed(2);
|
|
@@ -2437,6 +2686,31 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2437
2686
|
this.persistTempOrder();
|
|
2438
2687
|
return tempOrder.buzzer;
|
|
2439
2688
|
}
|
|
2689
|
+
updateTempOrderShopServiceData(patch) {
|
|
2690
|
+
const tempOrder = this.ensureTempOrder();
|
|
2691
|
+
const metadata = tempOrder.metadata && typeof tempOrder.metadata === 'object' && !Array.isArray(tempOrder.metadata) ? tempOrder.metadata : {};
|
|
2692
|
+
const currentShopServiceData = metadata.shop_service_data && typeof metadata.shop_service_data === 'object' && !Array.isArray(metadata.shop_service_data) ? metadata.shop_service_data : {};
|
|
2693
|
+
const shopServiceData = {
|
|
2694
|
+
...currentShopServiceData,
|
|
2695
|
+
...patch
|
|
2696
|
+
};
|
|
2697
|
+
tempOrder.metadata = {
|
|
2698
|
+
...metadata,
|
|
2699
|
+
shop_service_data: shopServiceData
|
|
2700
|
+
};
|
|
2701
|
+
this.persistTempOrder();
|
|
2702
|
+
return shopServiceData;
|
|
2703
|
+
}
|
|
2704
|
+
updateTempOrderPickupReferenceMode(mode) {
|
|
2705
|
+
return this.updateTempOrderShopServiceData({
|
|
2706
|
+
pickup_reference_mode: mode
|
|
2707
|
+
});
|
|
2708
|
+
}
|
|
2709
|
+
updateTempOrderServiceType(serviceType) {
|
|
2710
|
+
return this.updateTempOrderShopServiceData({
|
|
2711
|
+
service_type: serviceType
|
|
2712
|
+
});
|
|
2713
|
+
}
|
|
2440
2714
|
updateTempOrderContactsInfo(contactsInfo) {
|
|
2441
2715
|
const tempOrder = this.ensureTempOrder();
|
|
2442
2716
|
tempOrder.contacts_info = contactsInfo && typeof contactsInfo === 'object' && !Array.isArray(contactsInfo) ? contactsInfo : null;
|
|
@@ -2547,8 +2821,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2547
2821
|
});
|
|
2548
2822
|
const nextCustomerId = tempOrder.customer_id ?? null;
|
|
2549
2823
|
if (String(previousCustomerId ?? '') !== String(nextCustomerId ?? '')) {
|
|
2550
|
-
this.
|
|
2551
|
-
this.clearTempOrderPointCardSnapshot(tempOrder);
|
|
2824
|
+
this.clearCustomerWalletState(tempOrder);
|
|
2552
2825
|
}
|
|
2553
2826
|
this.persistTempOrder();
|
|
2554
2827
|
return {
|
|
@@ -2584,9 +2857,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2584
2857
|
}
|
|
2585
2858
|
const nextCustomerId = tempOrder.customer_id ?? null;
|
|
2586
2859
|
if (String(previousCustomerId ?? '') !== String(nextCustomerId ?? '')) {
|
|
2587
|
-
this.
|
|
2588
|
-
this.store.virtualCurrencyList = [];
|
|
2589
|
-
this.clearTempOrderPointCardSnapshot(tempOrder);
|
|
2860
|
+
this.clearCustomerWalletState(tempOrder);
|
|
2590
2861
|
(0, _utils.clearTempOrderHolderAssignments)(tempOrder);
|
|
2591
2862
|
this.clearCustomerBoundDiscounts(tempOrder);
|
|
2592
2863
|
this.applyDiscount();
|
|
@@ -2624,9 +2895,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2624
2895
|
tempOrder.email = '';
|
|
2625
2896
|
tempOrder.customer = null;
|
|
2626
2897
|
if (tempOrder._extend) delete tempOrder._extend.customerSnapshot;
|
|
2627
|
-
this.
|
|
2628
|
-
this.store.virtualCurrencyList = [];
|
|
2629
|
-
this.clearTempOrderPointCardSnapshot(tempOrder);
|
|
2898
|
+
this.clearCustomerWalletState(tempOrder);
|
|
2630
2899
|
this.clearCustomerBoundDiscounts(tempOrder);
|
|
2631
2900
|
this.applyDiscount();
|
|
2632
2901
|
this.persistTempOrder();
|
|
@@ -3034,9 +3303,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3034
3303
|
if (!options?.skipEditDiscountConfigRefresh && !isVirtualSettlement) {
|
|
3035
3304
|
await this.ensureEditDiscountConfigForProductChange(tempOrder);
|
|
3036
3305
|
}
|
|
3037
|
-
|
|
3038
|
-
await this.applyPromotion();
|
|
3039
|
-
}
|
|
3306
|
+
const promotionPayload = !options?.skipDiscountRecalculation && !isVirtualSettlement ? await this.evaluatePromotion() : null;
|
|
3040
3307
|
if (!options?.skipDiscountRecalculation && !isVirtualSettlement) {
|
|
3041
3308
|
this.applyDiscount();
|
|
3042
3309
|
}
|
|
@@ -3045,6 +3312,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3045
3312
|
createIfMissing: true
|
|
3046
3313
|
});
|
|
3047
3314
|
this.persistTempOrder();
|
|
3315
|
+
this.emitPromotionApplied(promotionPayload);
|
|
3048
3316
|
}
|
|
3049
3317
|
|
|
3050
3318
|
/**
|
|
@@ -3127,6 +3395,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3127
3395
|
const matchedProduct = matchedIndex === -1 ? null : tempOrder.products[matchedIndex];
|
|
3128
3396
|
const existedFingerprint = matchedProduct ? (0, _utils3.buildProductLineFingerprint)((0, _utils.getProductSkuOptions)(matchedProduct), matchedProduct.product_bundle) : '';
|
|
3129
3397
|
const commitProductLine = shouldMerge => {
|
|
3398
|
+
let committedProduct;
|
|
3130
3399
|
if (matchedIndex === -1 || !matchedProduct || !shouldMerge) {
|
|
3131
3400
|
this.captureProductRuntime(tempOrder, productToAdd, normalizedIncoming);
|
|
3132
3401
|
if (linked) {
|
|
@@ -3142,6 +3411,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3142
3411
|
} else {
|
|
3143
3412
|
tempOrder.products = [...tempOrder.products, normalizedIncoming];
|
|
3144
3413
|
}
|
|
3414
|
+
committedProduct = normalizedIncoming;
|
|
3145
3415
|
} else {
|
|
3146
3416
|
const targetProduct = matchedProduct;
|
|
3147
3417
|
const targetUid = targetProduct.metadata?.unique_identification_number;
|
|
@@ -3177,6 +3447,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3177
3447
|
num: nextNum
|
|
3178
3448
|
};
|
|
3179
3449
|
this.captureProductRuntime(tempOrder, productToAdd, tempOrder.products[matchedIndex], targetUid);
|
|
3450
|
+
committedProduct = tempOrder.products[matchedIndex];
|
|
3180
3451
|
if (matchedIndex !== tempOrder.products.length - 1) {
|
|
3181
3452
|
const mergedProduct = tempOrder.products[matchedIndex];
|
|
3182
3453
|
tempOrder.products = [...tempOrder.products.slice(0, matchedIndex), ...tempOrder.products.slice(matchedIndex + 1), mergedProduct];
|
|
@@ -3185,10 +3456,10 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3185
3456
|
if (linked) {
|
|
3186
3457
|
tempOrder.bookings = [...(tempOrder.bookings || []), linked.booking];
|
|
3187
3458
|
}
|
|
3459
|
+
return committedProduct;
|
|
3188
3460
|
};
|
|
3189
3461
|
if (!matchedProduct) {
|
|
3190
|
-
commitProductLine(false);
|
|
3191
|
-
return;
|
|
3462
|
+
return commitProductLine(false);
|
|
3192
3463
|
}
|
|
3193
3464
|
const shouldMerge = this.shouldMergeProductToOrder({
|
|
3194
3465
|
incomingProduct: productToAdd,
|
|
@@ -3203,7 +3474,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3203
3474
|
if (shouldMerge && typeof shouldMerge === 'object' && typeof shouldMerge.then === 'function') {
|
|
3204
3475
|
return Promise.resolve(shouldMerge).then(commitProductLine);
|
|
3205
3476
|
}
|
|
3206
|
-
commitProductLine(shouldMerge);
|
|
3477
|
+
return commitProductLine(shouldMerge);
|
|
3207
3478
|
}
|
|
3208
3479
|
|
|
3209
3480
|
/**
|
|
@@ -3276,34 +3547,99 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3276
3547
|
*/
|
|
3277
3548
|
async addProductToOrder(product, booking, options) {
|
|
3278
3549
|
const tempOrder = this.ensureTempOrder();
|
|
3550
|
+
const salesNoteDrafts = (options?.salesNotes || []).map(draft => ({
|
|
3551
|
+
note_type: String(draft.note_type || '').trim(),
|
|
3552
|
+
text: {
|
|
3553
|
+
kind: 'resolved',
|
|
3554
|
+
text: (0, _salesNotes.buildSalesNoteText)(draft.text)
|
|
3555
|
+
}
|
|
3556
|
+
}));
|
|
3557
|
+
const mutationSnapshot = salesNoteDrafts.length > 0 ? {
|
|
3558
|
+
products: (0, _lodashEs.cloneDeep)(tempOrder.products || []),
|
|
3559
|
+
bookings: (0, _lodashEs.cloneDeep)(tempOrder.bookings || []),
|
|
3560
|
+
notes: (0, _lodashEs.cloneDeep)(tempOrder.notes || []),
|
|
3561
|
+
extend: (0, _lodashEs.cloneDeep)(tempOrder._extend || {})
|
|
3562
|
+
} : null;
|
|
3563
|
+
const attachSalesNoteDrafts = orderProduct => {
|
|
3564
|
+
if (!salesNoteDrafts.length) return;
|
|
3565
|
+
const productLineUid = orderProduct.metadata?.unique_identification_number || orderProduct.unique_identification_number;
|
|
3566
|
+
if (!productLineUid) {
|
|
3567
|
+
throw new Error('[Order Notes] 商品行缺少 unique_identification_number');
|
|
3568
|
+
}
|
|
3569
|
+
salesNoteDrafts.forEach(draft => {
|
|
3570
|
+
this.setSalesNoteOnTempOrder(tempOrder, {
|
|
3571
|
+
note_type: draft.note_type,
|
|
3572
|
+
target: {
|
|
3573
|
+
target_type: 'order_detail',
|
|
3574
|
+
target_uuid: String(productLineUid)
|
|
3575
|
+
},
|
|
3576
|
+
text: draft.text
|
|
3577
|
+
});
|
|
3578
|
+
});
|
|
3579
|
+
};
|
|
3580
|
+
const rollbackProductAndNotesMutation = () => {
|
|
3581
|
+
if (!mutationSnapshot) return;
|
|
3582
|
+
tempOrder.products = mutationSnapshot.products;
|
|
3583
|
+
tempOrder.bookings = mutationSnapshot.bookings;
|
|
3584
|
+
tempOrder.notes = mutationSnapshot.notes;
|
|
3585
|
+
tempOrder._extend = mutationSnapshot.extend;
|
|
3586
|
+
};
|
|
3279
3587
|
const insertAfterProductUid = options?.insertAfterProductUid;
|
|
3280
3588
|
const anchorIndex = booking && insertAfterProductUid ? tempOrder.products.findIndex(line => {
|
|
3281
3589
|
const uid = line.metadata?.unique_identification_number || line.unique_identification_number;
|
|
3282
3590
|
return uid !== undefined && uid !== null && String(uid) === String(insertAfterProductUid);
|
|
3283
3591
|
}) : -1;
|
|
3284
3592
|
let insertAtIndex = anchorIndex >= 0 ? anchorIndex + 1 : undefined;
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3593
|
+
try {
|
|
3594
|
+
if (booking) {
|
|
3595
|
+
const productRecord = product;
|
|
3596
|
+
const splitCount = (0, _utils3.getSafeProductNum)(productRecord.num ?? productRecord.product_quantity ?? 1);
|
|
3597
|
+
if (splitCount > 1) {
|
|
3598
|
+
for (let i = 0; i < splitCount; i += 1) {
|
|
3599
|
+
const splitLine = this.createSplitBookingLine(productRecord, booking, i, splitCount);
|
|
3600
|
+
if (i > 0) {
|
|
3601
|
+
delete splitLine.product.unique_identification_number;
|
|
3602
|
+
delete splitLine.product.identity_key;
|
|
3603
|
+
if (splitLine.product.metadata) {
|
|
3604
|
+
splitLine.product.metadata = {
|
|
3605
|
+
...splitLine.product.metadata
|
|
3606
|
+
};
|
|
3607
|
+
delete splitLine.product.metadata.unique_identification_number;
|
|
3608
|
+
delete splitLine.product.metadata.identity_key;
|
|
3609
|
+
}
|
|
3610
|
+
delete splitLine.booking.booking_uid;
|
|
3611
|
+
if (splitLine.booking.metadata) {
|
|
3612
|
+
splitLine.booking.metadata = {
|
|
3613
|
+
...splitLine.booking.metadata
|
|
3614
|
+
};
|
|
3615
|
+
delete splitLine.booking.metadata.unique_identification_number;
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
const committedProduct = await this.appendProductLineToTempOrder(tempOrder, splitLine.product, splitLine.booking, {
|
|
3619
|
+
insertAtIndex,
|
|
3620
|
+
disableMerge: options?.disableMerge
|
|
3621
|
+
});
|
|
3622
|
+
attachSalesNoteDrafts(committedProduct);
|
|
3623
|
+
if (insertAtIndex !== undefined) insertAtIndex += 1;
|
|
3624
|
+
}
|
|
3625
|
+
} else {
|
|
3626
|
+
const committedProduct = await this.appendProductLineToTempOrder(tempOrder, product, (0, _lodashEs.cloneDeep)(booking), {
|
|
3292
3627
|
insertAtIndex,
|
|
3293
3628
|
disableMerge: options?.disableMerge
|
|
3294
3629
|
});
|
|
3295
|
-
|
|
3296
|
-
if (insertAtIndex !== undefined) insertAtIndex += 1;
|
|
3630
|
+
attachSalesNoteDrafts(committedProduct);
|
|
3297
3631
|
}
|
|
3298
|
-
|
|
3299
|
-
|
|
3632
|
+
} else {
|
|
3633
|
+
const committedProduct = await this.appendProductLineToTempOrder(tempOrder, product, undefined, {
|
|
3634
|
+
insertAtIndex,
|
|
3635
|
+
disableMerge: options?.disableMerge
|
|
3636
|
+
});
|
|
3637
|
+
attachSalesNoteDrafts(committedProduct);
|
|
3300
3638
|
}
|
|
3639
|
+
} catch (error) {
|
|
3640
|
+
rollbackProductAndNotesMutation();
|
|
3641
|
+
throw error;
|
|
3301
3642
|
}
|
|
3302
|
-
const appendResult = this.appendProductLineToTempOrder(tempOrder, product, booking, {
|
|
3303
|
-
insertAtIndex,
|
|
3304
|
-
disableMerge: options?.disableMerge
|
|
3305
|
-
});
|
|
3306
|
-
if (appendResult) await appendResult;
|
|
3307
3643
|
await this.finalizeProductOrderMutation(tempOrder);
|
|
3308
3644
|
this.logInfo('addProductToOrder success', {
|
|
3309
3645
|
product_id: product.product_id,
|
|
@@ -3626,7 +3962,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3626
3962
|
async updateOrderProduct(params) {
|
|
3627
3963
|
const tempOrder = this.ensureTempOrder();
|
|
3628
3964
|
const updatedProductUid = this.applyOrderProductUpdateToTempOrder(tempOrder, params);
|
|
3629
|
-
await this.
|
|
3965
|
+
const promotionPayload = await this.evaluatePromotion();
|
|
3630
3966
|
this.applyDiscount({
|
|
3631
3967
|
reapplyBookingDiscountProductUids: params.allow_booking_discount_reapply && updatedProductUid ? [String(updatedProductUid)] : undefined
|
|
3632
3968
|
});
|
|
@@ -3635,6 +3971,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3635
3971
|
createIfMissing: true
|
|
3636
3972
|
});
|
|
3637
3973
|
this.persistTempOrder();
|
|
3974
|
+
this.emitPromotionApplied(promotionPayload);
|
|
3638
3975
|
this.logInfo('updateOrderProduct success', {
|
|
3639
3976
|
params
|
|
3640
3977
|
});
|
|
@@ -3729,8 +4066,9 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3729
4066
|
}
|
|
3730
4067
|
tempOrder.products[productIndex] = normalizedProduct;
|
|
3731
4068
|
this.prepareVirtualSettlementTempOrder(tempOrder);
|
|
3732
|
-
|
|
3733
|
-
|
|
4069
|
+
const isVirtualSettlement = this.isActiveVirtualSettlementOrder(tempOrder);
|
|
4070
|
+
const promotionPayload = !isVirtualSettlement ? await this.evaluatePromotion() : null;
|
|
4071
|
+
if (!isVirtualSettlement) {
|
|
3734
4072
|
this.applyDiscount();
|
|
3735
4073
|
}
|
|
3736
4074
|
this.sanitizeTempOrderProducts(tempOrder);
|
|
@@ -3738,6 +4076,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3738
4076
|
createIfMissing: true
|
|
3739
4077
|
});
|
|
3740
4078
|
this.persistTempOrder();
|
|
4079
|
+
this.emitPromotionApplied(promotionPayload);
|
|
3741
4080
|
this.logInfo('updateOrderProductQuantity success', {
|
|
3742
4081
|
params
|
|
3743
4082
|
});
|
|
@@ -3782,7 +4121,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3782
4121
|
*/
|
|
3783
4122
|
async finalizeAfterProductsMutation(tempOrder, options) {
|
|
3784
4123
|
this.syncTempOrderHolderFromBookings(tempOrder);
|
|
3785
|
-
await this.
|
|
4124
|
+
const promotionPayload = await this.evaluatePromotion();
|
|
3786
4125
|
this.applyDiscount({
|
|
3787
4126
|
reapplyBookingDiscountProductUids: options?.reapplyBookingDiscountProductUids
|
|
3788
4127
|
});
|
|
@@ -3791,6 +4130,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3791
4130
|
createIfMissing: true
|
|
3792
4131
|
});
|
|
3793
4132
|
this.persistTempOrder();
|
|
4133
|
+
this.emitPromotionApplied(promotionPayload);
|
|
3794
4134
|
}
|
|
3795
4135
|
|
|
3796
4136
|
/**
|
|
@@ -3801,10 +4141,12 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3801
4141
|
*/
|
|
3802
4142
|
async removeProductsFromOrder(identities, options) {
|
|
3803
4143
|
const tempOrder = this.ensureTempOrder();
|
|
3804
|
-
this.repairDuplicateProductLineIdentities(tempOrder);
|
|
3805
4144
|
if (!identities.length) {
|
|
3806
4145
|
return tempOrder.products;
|
|
3807
4146
|
}
|
|
4147
|
+
// 商品删除会同步删除以商品行为主关系的 Notes;快照不完整时禁止产生孤儿 Relation。
|
|
4148
|
+
this.ensureSalesNotesLoadedForMutation(tempOrder);
|
|
4149
|
+
this.repairDuplicateProductLineIdentities(tempOrder);
|
|
3808
4150
|
const productsBeforeRemove = tempOrder.products || [];
|
|
3809
4151
|
const bookingsBeforeRemove = tempOrder.bookings || [];
|
|
3810
4152
|
const preserveDisplayPosition = options?.preserveDisplayPosition;
|
|
@@ -3864,6 +4206,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3864
4206
|
tempOrder.products = nextProductIndex >= 0 ? [...productsWithoutRemaining.slice(0, nextProductIndex), remainingProduct, ...productsWithoutRemaining.slice(nextProductIndex)] : [...productsWithoutRemaining, remainingProduct];
|
|
3865
4207
|
}
|
|
3866
4208
|
}
|
|
4209
|
+
this.removeSalesNotesForMissingProductLines(tempOrder);
|
|
3867
4210
|
await this.finalizeAfterProductsMutation(tempOrder);
|
|
3868
4211
|
this.logInfo('removeProductsFromOrder success', {
|
|
3869
4212
|
identities
|
|
@@ -3876,13 +4219,15 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3876
4219
|
|
|
3877
4220
|
/**
|
|
3878
4221
|
* 仅清空购物车行(products / bookings),不重置整单。
|
|
3879
|
-
*
|
|
4222
|
+
* 普通整单 note 保持不变;以商品行为主体的 Sales Notes 同步删除。
|
|
3880
4223
|
*/
|
|
3881
4224
|
async clearOrderCartLines() {
|
|
3882
4225
|
const tempOrder = this.ensureTempOrder();
|
|
4226
|
+
this.ensureSalesNotesLoadedForMutation(tempOrder);
|
|
3883
4227
|
tempOrder.products = [];
|
|
3884
4228
|
tempOrder.bookings = [];
|
|
3885
4229
|
(0, _virtualSettlement.releaseTempOrderSettlementState)(tempOrder);
|
|
4230
|
+
this.removeSalesNotesForMissingProductLines(tempOrder);
|
|
3886
4231
|
this.syncTempOrderHolderFromBookings(tempOrder);
|
|
3887
4232
|
if (tempOrder._extend && typeof tempOrder._extend === 'object') {
|
|
3888
4233
|
tempOrder._extend = {
|
|
@@ -3890,13 +4235,14 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3890
4235
|
productsByUid: {}
|
|
3891
4236
|
};
|
|
3892
4237
|
}
|
|
3893
|
-
await this.
|
|
4238
|
+
const promotionPayload = await this.evaluatePromotion();
|
|
3894
4239
|
this.applyDiscount();
|
|
3895
4240
|
this.sanitizeTempOrderProducts(tempOrder);
|
|
3896
4241
|
await this.recalculateSummary({
|
|
3897
4242
|
createIfMissing: true
|
|
3898
4243
|
});
|
|
3899
4244
|
this.persistTempOrder();
|
|
4245
|
+
this.emitPromotionApplied(promotionPayload);
|
|
3900
4246
|
this.logInfo('clearOrderCartLines success', {});
|
|
3901
4247
|
return tempOrder;
|
|
3902
4248
|
}
|
|
@@ -3906,8 +4252,10 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3906
4252
|
*/
|
|
3907
4253
|
async clearOrderForSettlementSwitch() {
|
|
3908
4254
|
const tempOrder = this.ensureTempOrder();
|
|
4255
|
+
this.ensureSalesNotesLoadedForMutation(tempOrder);
|
|
3909
4256
|
tempOrder.products = [];
|
|
3910
4257
|
tempOrder.bookings = [];
|
|
4258
|
+
this.removeSalesNotesForMissingProductLines(tempOrder);
|
|
3911
4259
|
tempOrder.discount_list = [];
|
|
3912
4260
|
tempOrder.payments = [];
|
|
3913
4261
|
tempOrder.refunds = [];
|
|
@@ -4050,6 +4398,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4050
4398
|
})) || this.store.summary || (0, _utils.createEmptySummary)();
|
|
4051
4399
|
const effectiveCacheId = params?.cacheId ?? this.cacheId;
|
|
4052
4400
|
const hasPaymentStatusOverride = params?.paymentStatus !== undefined;
|
|
4401
|
+
const shouldOverridePaymentStatus = hasPaymentStatusOverride && !isOnlineStorePlatform && !shouldConfirmPendingVoucherPayments;
|
|
4053
4402
|
const hasSmallTicketDataFlagOverride = params?.smallTicketDataFlag !== undefined;
|
|
4054
4403
|
const enhancePayload = hasPaymentOverride || hasPaymentStatusOverride || hasSmallTicketDataFlagOverride || params?.enhancePayload ? (payload, ctx) => {
|
|
4055
4404
|
const nextPayload = {
|
|
@@ -4057,7 +4406,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4057
4406
|
...(hasPaymentOverride ? {
|
|
4058
4407
|
payments: preparedPaymentOverride || []
|
|
4059
4408
|
} : {}),
|
|
4060
|
-
...(
|
|
4409
|
+
...(shouldOverridePaymentStatus ? {
|
|
4061
4410
|
payment_status: params?.paymentStatus
|
|
4062
4411
|
} : {}),
|
|
4063
4412
|
...(hasSmallTicketDataFlagOverride ? {
|
|
@@ -4124,6 +4473,12 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4124
4473
|
this.logSubmitBackendRejected(result, payload);
|
|
4125
4474
|
return result;
|
|
4126
4475
|
}
|
|
4476
|
+
const isLocalPendingSubmit = this.isLocalPendingSubmitResult(result);
|
|
4477
|
+
// need_sync=1 仅表示已进入本地同步队列,不能视为远端已确认 Notes。
|
|
4478
|
+
if (Object.prototype.hasOwnProperty.call(payload, 'notes') && !isLocalPendingSubmit) {
|
|
4479
|
+
const salesNotesState = this.ensureSalesNotesRuntimeState(tempOrder);
|
|
4480
|
+
salesNotesState.dirty = false;
|
|
4481
|
+
}
|
|
4127
4482
|
const submittedOrderId = this.extractOrderIdFromSubmitResult(result);
|
|
4128
4483
|
this.logInfo('runSubmitTempOrder: 提交成功', {
|
|
4129
4484
|
submittedOrderId,
|
|
@@ -4137,9 +4492,10 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4137
4492
|
tempOrder.order_id = submittedOrderId;
|
|
4138
4493
|
const resultRecord = result;
|
|
4139
4494
|
await this.markLocalOrderSynced(submittedOrderId, resultRecord?.data || resultRecord || {});
|
|
4140
|
-
}
|
|
4495
|
+
}
|
|
4496
|
+
if (isLocalPendingSubmit) {
|
|
4141
4497
|
this.store.syncState = 'local';
|
|
4142
|
-
} else {
|
|
4498
|
+
} else if (submittedOrderId === null || submittedOrderId === undefined) {
|
|
4143
4499
|
this.store.syncState = 'submitted';
|
|
4144
4500
|
}
|
|
4145
4501
|
return result;
|
|
@@ -4535,7 +4891,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4535
4891
|
params.forceRemote = false;
|
|
4536
4892
|
}
|
|
4537
4893
|
const res = await this.request.get(`/order/sales/${encodeURIComponent(lookup)}`, {
|
|
4538
|
-
with: ['products', 'bookings', 'payments', 'customer', 'summary', 'contacts_info', 'order_meta_list', 'refunds'],
|
|
4894
|
+
with: ['products', 'bookings', 'payments', 'customer', 'summary', 'contacts_info', 'order_meta_list', 'refunds', 'notes'],
|
|
4539
4895
|
...(params.forceRemote ? {
|
|
4540
4896
|
forceRemote: true
|
|
4541
4897
|
} : {})
|
|
@@ -4588,9 +4944,11 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4588
4944
|
const isMergedSalesDetailHydrate = options?.source === 'mergedSalesDetail';
|
|
4589
4945
|
const nextTempOrder = this.normalizeTempOrderForRuntime(raw, {
|
|
4590
4946
|
// merge 前已只给远端商品补标记;这里不能把本地未提交商品一并变成 saved item。
|
|
4591
|
-
makeEditMark: !isSessionStorageHydrate && !isMergedSalesDetailHydrate
|
|
4947
|
+
makeEditMark: !isSessionStorageHydrate && !isMergedSalesDetailHydrate,
|
|
4948
|
+
allowUnresolvedSalesNotes: isSessionStorageHydrate
|
|
4592
4949
|
});
|
|
4593
4950
|
this.store.tempOrder = nextTempOrder;
|
|
4951
|
+
this.clearCustomerWalletRuntimeState();
|
|
4594
4952
|
if (isSessionStorageHydrate) {
|
|
4595
4953
|
// 会话快照是未提交购物车,不应获得编辑订单的 saved/edit 标记与服务端基线。
|
|
4596
4954
|
const restoredSessionDiscounts = Array.isArray(raw.discount_list) ? (0, _lodashEs.cloneDeep)(raw.discount_list) : [];
|
|
@@ -4810,6 +5168,17 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4810
5168
|
nextTempOrder.payments = Array.isArray(raw.payments) ? raw.payments.map(p => ({
|
|
4811
5169
|
...(p || {})
|
|
4812
5170
|
})) : [];
|
|
5171
|
+
const rawHasSalesNotes = Object.prototype.hasOwnProperty.call(raw, 'notes');
|
|
5172
|
+
const restoredSalesNotesState = raw._extend?.salesNotesState;
|
|
5173
|
+
const allowUnresolvedSalesNotes = options?.allowUnresolvedSalesNotes === true || restoredSalesNotesState?.dirty === true || Number(raw.is_draft_order || 0) === 1;
|
|
5174
|
+
const decodedSalesNotes = rawHasSalesNotes ? (0, _salesNotes.decodeSalesNotesSnapshot)(raw.notes, {
|
|
5175
|
+
allowUnresolvedOrderTarget: allowUnresolvedSalesNotes
|
|
5176
|
+
}) : {
|
|
5177
|
+
notes: [],
|
|
5178
|
+
complete: false
|
|
5179
|
+
};
|
|
5180
|
+
const hasUnresolvedSalesNoteOrderTarget = decodedSalesNotes.notes.some(note => note.note_relations.some(relation => relation.is_primary === 0 && relation.relation_type === 'related_to' && relation.target_type === 'order' && relation.target_uuid === ''));
|
|
5181
|
+
nextTempOrder.notes = decodedSalesNotes.notes;
|
|
4813
5182
|
nextTempOrder.surcharges = Array.isArray(raw.surcharges) ? raw.surcharges.map(s => ({
|
|
4814
5183
|
...(s || {})
|
|
4815
5184
|
})) : [];
|
|
@@ -4830,6 +5199,10 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4830
5199
|
productsByUid: {}
|
|
4831
5200
|
};
|
|
4832
5201
|
const tempOrderExtend = nextTempOrder._extend;
|
|
5202
|
+
tempOrderExtend.salesNotesState = {
|
|
5203
|
+
loaded: rawHasSalesNotes ? decodedSalesNotes.complete : restoredSalesNotesState?.loaded === true,
|
|
5204
|
+
dirty: decodedSalesNotes.complete && (restoredSalesNotesState?.dirty === true || allowUnresolvedSalesNotes && hasUnresolvedSalesNoteOrderTarget)
|
|
5205
|
+
};
|
|
4833
5206
|
const productsByUid = tempOrderExtend.productsByUid || {};
|
|
4834
5207
|
for (const [index, product] of nextTempOrder.products.entries()) {
|
|
4835
5208
|
const uid = product.metadata?.unique_identification_number;
|
|
@@ -5021,7 +5394,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
5021
5394
|
}
|
|
5022
5395
|
async getOrderInfo(order_id) {
|
|
5023
5396
|
const res = await this.request.get(`/order/sales/${order_id}`, {
|
|
5024
|
-
with: ['products', 'bookings']
|
|
5397
|
+
with: ['products', 'bookings', 'notes']
|
|
5025
5398
|
}, {
|
|
5026
5399
|
osServer: true
|
|
5027
5400
|
});
|