@pisell/pisellos 2.2.309 → 2.2.311
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/promotion/index.js +0 -9
- package/dist/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
- package/dist/model/strategy/adapter/walletPass/evaluator.js +4 -2
- package/dist/modules/BookingContext/index.js +4 -4
- package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +58 -19
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +24 -12
- package/dist/modules/Customer/index.js +6 -3
- package/dist/modules/Customer/types.d.ts +2 -0
- package/dist/modules/Discount/availability.d.ts +7 -0
- package/dist/modules/Discount/availability.js +49 -0
- package/dist/modules/Discount/entitlementPass.d.ts +34 -0
- package/dist/modules/Discount/entitlementPass.js +147 -0
- package/dist/modules/Discount/index.d.ts +2 -2
- package/dist/modules/Discount/index.js +37 -151
- package/dist/modules/Discount/types.d.ts +36 -5
- package/dist/modules/OpenData/index.d.ts +1 -1
- package/dist/modules/OpenData/index.js +21 -12
- package/dist/modules/Order/index.d.ts +32 -4
- package/dist/modules/Order/index.js +733 -373
- package/dist/modules/Order/types.d.ts +17 -0
- package/dist/modules/Order/utils/orderCollectionIdentity.js +16 -12
- package/dist/modules/Order/utils.d.ts +19 -5
- package/dist/modules/Order/utils.js +306 -86
- package/dist/modules/Payment/index.d.ts +0 -5
- package/dist/modules/Payment/index.js +35 -85
- package/dist/modules/Payment/types.d.ts +21 -1
- package/dist/modules/Payment/types.js +2 -0
- package/dist/modules/Payment/walletpass.d.ts +1 -1
- package/dist/modules/Payment/walletpass.js +80 -47
- package/dist/modules/ProductList/index.js +18 -5
- package/dist/modules/Rules/index.d.ts +2 -2
- package/dist/modules/Rules/index.js +480 -186
- package/dist/modules/Rules/types.d.ts +4 -0
- package/dist/server/index.js +8 -5
- package/dist/server/modules/floor-plan/index.js +34 -3
- package/dist/server/modules/order/index.js +6 -0
- package/dist/server/modules/order/types.d.ts +12 -0
- package/dist/server/modules/payment/index.d.ts +25 -6
- package/dist/server/modules/payment/index.js +660 -133
- package/dist/server/utils/small-ticket.d.ts +8 -5
- package/dist/server/utils/small-ticket.js +1269 -199
- package/dist/solution/BaseSales/index.d.ts +11 -2
- package/dist/solution/BaseSales/index.js +1463 -1173
- package/dist/solution/BaseSales/types.d.ts +7 -3
- package/dist/solution/BaseSales/utils/cartPromotion.js +141 -126
- package/dist/solution/BookingTicket/index.d.ts +11 -2
- package/dist/solution/BookingTicket/index.js +663 -542
- package/dist/solution/BookingTicket/types.d.ts +5 -1
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +2 -0
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +338 -147
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +8 -4
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +6 -0
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +19 -6
- package/dist/solution/ShopDiscount/index.d.ts +2 -0
- package/dist/solution/ShopDiscount/index.js +99 -42
- package/dist/solution/ShopDiscount/utils.d.ts +4 -2
- package/dist/solution/ShopDiscount/utils.js +43 -23
- package/dist/solution/UnifiedBookingSales/index.d.ts +2 -2
- package/dist/solution/UnifiedBookingSales/index.js +5 -5
- package/dist/utils/os-private-payload.d.ts +5 -0
- package/dist/utils/os-private-payload.js +17 -0
- package/lib/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
- package/lib/model/strategy/adapter/walletPass/evaluator.js +2 -0
- package/lib/modules/BookingContext/index.js +3 -3
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +35 -6
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +13 -6
- package/lib/modules/Customer/index.js +5 -1
- package/lib/modules/Customer/types.d.ts +2 -0
- package/lib/modules/Discount/availability.d.ts +7 -0
- package/lib/modules/Discount/availability.js +43 -0
- package/lib/modules/Discount/entitlementPass.d.ts +34 -0
- package/lib/modules/Discount/entitlementPass.js +141 -0
- package/lib/modules/Discount/index.d.ts +2 -2
- package/lib/modules/Discount/index.js +23 -129
- package/lib/modules/Discount/types.d.ts +36 -5
- package/lib/modules/OpenData/index.d.ts +1 -1
- package/lib/modules/OpenData/index.js +11 -2
- package/lib/modules/Order/index.d.ts +32 -4
- package/lib/modules/Order/index.js +341 -71
- package/lib/modules/Order/types.d.ts +17 -0
- package/lib/modules/Order/utils/orderCollectionIdentity.js +14 -11
- package/lib/modules/Order/utils.d.ts +19 -5
- package/lib/modules/Order/utils.js +215 -13
- package/lib/modules/Payment/index.d.ts +0 -5
- package/lib/modules/Payment/index.js +3 -28
- package/lib/modules/Payment/types.d.ts +21 -1
- package/lib/modules/Payment/types.js +1 -0
- package/lib/modules/Payment/walletpass.d.ts +1 -1
- package/lib/modules/Payment/walletpass.js +29 -9
- package/lib/modules/ProductList/index.js +14 -1
- package/lib/modules/Rules/index.d.ts +2 -2
- package/lib/modules/Rules/index.js +357 -110
- package/lib/modules/Rules/types.d.ts +4 -0
- package/lib/server/index.js +8 -5
- package/lib/server/modules/floor-plan/index.js +29 -1
- package/lib/server/modules/order/index.js +6 -0
- package/lib/server/modules/order/types.d.ts +12 -0
- package/lib/server/modules/payment/index.d.ts +25 -6
- package/lib/server/modules/payment/index.js +350 -78
- package/lib/server/utils/small-ticket.d.ts +8 -5
- package/lib/server/utils/small-ticket.js +1009 -121
- package/lib/solution/BaseSales/index.d.ts +11 -2
- package/lib/solution/BaseSales/index.js +164 -30
- package/lib/solution/BaseSales/types.d.ts +7 -3
- package/lib/solution/BaseSales/utils/cartPromotion.js +12 -4
- package/lib/solution/BookingTicket/index.d.ts +11 -2
- package/lib/solution/BookingTicket/index.js +103 -41
- package/lib/solution/BookingTicket/types.d.ts +5 -1
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +2 -0
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +174 -42
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +8 -4
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +6 -0
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +11 -3
- package/lib/solution/ShopDiscount/index.d.ts +2 -0
- package/lib/solution/ShopDiscount/index.js +55 -9
- package/lib/solution/ShopDiscount/utils.d.ts +4 -2
- package/lib/solution/ShopDiscount/utils.js +42 -25
- package/lib/solution/UnifiedBookingSales/index.d.ts +2 -2
- package/lib/solution/UnifiedBookingSales/index.js +5 -5
- package/lib/utils/os-private-payload.d.ts +5 -0
- package/lib/utils/os-private-payload.js +22 -0
- package/package.json +1 -1
|
@@ -10,6 +10,8 @@ var _utils = require("./utils");
|
|
|
10
10
|
var _utils2 = require("../Product/utils");
|
|
11
11
|
var _weighedProduct = require("./utils/weighedProduct");
|
|
12
12
|
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
13
|
+
var _availability = require("../Discount/availability");
|
|
14
|
+
var _osPrivatePayload = require("../../utils/os-private-payload");
|
|
13
15
|
var _salesNotes = require("./salesNotes");
|
|
14
16
|
var _paymentUtils = require("./payment-utils");
|
|
15
17
|
var _paymentNumber = require("../../utils/payment-number");
|
|
@@ -22,6 +24,7 @@ var _discountProductLineIdentity = require("./utils/discountProductLineIdentity"
|
|
|
22
24
|
var _virtualSettlement = require("./utils/virtualSettlement");
|
|
23
25
|
var _Discount = require("../Discount");
|
|
24
26
|
var _Rules = require("../Rules");
|
|
27
|
+
var _entitlementPass = require("../Discount/entitlementPass");
|
|
25
28
|
var _types2 = require("../Rules/types");
|
|
26
29
|
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
27
30
|
var _lodashEs = require("lodash-es");
|
|
@@ -41,9 +44,27 @@ function isSameDiscountList(left, right) {
|
|
|
41
44
|
function isManualProductDiscountProduct(product) {
|
|
42
45
|
return (0, _manualProductDiscount.hasManualProductDiscountFlag)(product?.metadata) || (product?.discount_list || []).some(item => item?.type === 'product');
|
|
43
46
|
}
|
|
47
|
+
function syncReducedEntitlementPassUsageCount(discountList, previousQuantity, nextQuantity) {
|
|
48
|
+
if (!Array.isArray(discountList)) return discountList;
|
|
49
|
+
const previousCount = (0, _utils3.getSafeProductNum)(previousQuantity);
|
|
50
|
+
const nextCount = (0, _utils3.getSafeProductNum)(nextQuantity);
|
|
51
|
+
if (nextCount >= previousCount) return discountList;
|
|
52
|
+
return discountList.map(discount => {
|
|
53
|
+
if (!(0, _entitlementPass.isEntitlementPass)(discount)) return discount;
|
|
54
|
+
const rawUsageCount = Number(discount?.metadata?.num ?? previousCount);
|
|
55
|
+
const usageCount = Number.isFinite(rawUsageCount) ? Math.max(Math.floor(rawUsageCount), 0) : previousCount;
|
|
56
|
+
return {
|
|
57
|
+
...discount,
|
|
58
|
+
metadata: {
|
|
59
|
+
...(discount?.metadata || {}),
|
|
60
|
+
num: Math.min(usageCount, nextCount)
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
}
|
|
44
65
|
const ORDER_PAYMENT_STATUS_PAID = 'paid';
|
|
45
66
|
const ORDER_PAYMENT_STATUS_PENDING = 'payment_pending';
|
|
46
|
-
const CUSTOMER_PROMOTION_DISCOUNT_TAGS = new Set(['good_pass', 'product_discount_card', 'discount_card']);
|
|
67
|
+
const CUSTOMER_PROMOTION_DISCOUNT_TAGS = new Set(['entitlement_pass', 'good_pass', 'product_discount_card', 'discount_card']);
|
|
47
68
|
function isCustomerPromotionDiscount(discount) {
|
|
48
69
|
return CUSTOMER_PROMOTION_DISCOUNT_TAGS.has(String(discount?.tag || discount?.type || '').toLowerCase());
|
|
49
70
|
}
|
|
@@ -73,12 +94,27 @@ function getVoucherPaymentLifecycleKey(payment) {
|
|
|
73
94
|
if (!isVoucherPaymentRecord(payment)) return null;
|
|
74
95
|
return `${String(payment.voucher_id)}::${String(payment.status || '')}`;
|
|
75
96
|
}
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
function getDiscountIdentityKey(discount) {
|
|
98
|
+
const id = discount?.id;
|
|
99
|
+
if (id === undefined || id === null || id === '') return null;
|
|
100
|
+
return String(id);
|
|
101
|
+
}
|
|
102
|
+
function mergeCommittedEntitlementWithRuntime(committed, runtime) {
|
|
103
|
+
const committedAppliedProductDetails = committed.committedAppliedProductDetails?.length ? committed.committedAppliedProductDetails : committed.appliedProductDetails || [];
|
|
104
|
+
return {
|
|
105
|
+
...committed,
|
|
106
|
+
...runtime,
|
|
107
|
+
id: runtime.id ?? committed.id,
|
|
108
|
+
tag: runtime.tag || committed.tag,
|
|
109
|
+
type: runtime.type || committed.type,
|
|
110
|
+
isEditMode: false,
|
|
111
|
+
isDisabled: runtime.isDisabled ?? false,
|
|
112
|
+
isSelected: runtime.isSelected ?? committed.isSelected,
|
|
113
|
+
isUsed: committed.isUsed || runtime.isUsed,
|
|
114
|
+
committedAppliedProductDetails: [...committedAppliedProductDetails],
|
|
115
|
+
applicableProductDetails: [...(committed.applicableProductDetails || []), ...(runtime.applicableProductDetails || [])],
|
|
116
|
+
appliedProductDetails: [...committedAppliedProductDetails, ...(runtime.appliedProductDetails || [])]
|
|
117
|
+
};
|
|
82
118
|
}
|
|
83
119
|
class OrderModule extends _BaseModule.BaseModule {
|
|
84
120
|
defaultName = 'order';
|
|
@@ -93,6 +129,10 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
93
129
|
openCache = false;
|
|
94
130
|
sessionStoragePersistEnabled = false;
|
|
95
131
|
sessionStorageRestoreRecord = null;
|
|
132
|
+
/** 只允许当前订单最新的 prepare/config 请求回写客户资产态。 */
|
|
133
|
+
customerWalletPrepareConfigEpoch = 0;
|
|
134
|
+
/** Payment 可能早于 prepare/config 返回;先按实例 ID 暂存卡号等待 Snapshot。 */
|
|
135
|
+
pointCardSnapshotCodeCandidates = new Map();
|
|
96
136
|
salesSummaryModuleName;
|
|
97
137
|
rulesHooksOverride;
|
|
98
138
|
moduleHooksOverride;
|
|
@@ -131,6 +171,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
131
171
|
const key = Number.isFinite(id) ? id : String(resourceId);
|
|
132
172
|
const title = item?.discount?.title ?? {};
|
|
133
173
|
const amount = Number(item?.amount || 0);
|
|
174
|
+
const encoded = item?.metadata?.encoded || item?.encoded || item?.discount?.encoded || '';
|
|
134
175
|
const discountCardType = item?.discount?.discount_card_type;
|
|
135
176
|
const parValue = isDiscountCard ? discountCardType === 'fixed_amount' ? item?.discount?.fixed_amount ?? item?.amount : item?.discount?.percent : '0';
|
|
136
177
|
const detail = {
|
|
@@ -141,11 +182,21 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
141
182
|
original_amount: String(item?.discount?.original_amount ?? item?.amount ?? '0'),
|
|
142
183
|
num: Number(item?.metadata?.num ?? product?.num ?? product?.product_quantity ?? 1) || 1,
|
|
143
184
|
metadata: item?.metadata,
|
|
144
|
-
discount:
|
|
185
|
+
discount: {
|
|
186
|
+
...(item?.discount || {}),
|
|
187
|
+
product_id: item?.discount?.product_id ?? product?.product_id
|
|
188
|
+
},
|
|
145
189
|
_num: Number(item?.metadata?.num ?? product?.num ?? product?.product_quantity ?? 1) || 1
|
|
146
190
|
};
|
|
147
191
|
const existed = discountMap.get(key);
|
|
148
192
|
if (existed) {
|
|
193
|
+
if (!existed.encoded && encoded) {
|
|
194
|
+
existed.encoded = encoded;
|
|
195
|
+
existed.metadata = {
|
|
196
|
+
...existed.metadata,
|
|
197
|
+
encoded
|
|
198
|
+
};
|
|
199
|
+
}
|
|
149
200
|
existed.amount = Number(existed.amount || 0) + amount;
|
|
150
201
|
existed.used_par_value = String(Number(existed.used_par_value || 0) + amount);
|
|
151
202
|
if (!isDiscountCard) {
|
|
@@ -165,8 +216,11 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
165
216
|
discountMap.set(key, {
|
|
166
217
|
id: key,
|
|
167
218
|
product_name: '',
|
|
168
|
-
encoded
|
|
169
|
-
code: '',
|
|
219
|
+
encoded,
|
|
220
|
+
code: item?.code || item?.discount?.code || '',
|
|
221
|
+
metadata: {
|
|
222
|
+
encoded
|
|
223
|
+
},
|
|
170
224
|
tag: type,
|
|
171
225
|
product_id: item?.discount?.discount_product_id ?? product?.product_id ?? 0,
|
|
172
226
|
relation_type: '',
|
|
@@ -457,13 +511,24 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
457
511
|
this.store.rules = rules;
|
|
458
512
|
}
|
|
459
513
|
createDefaultRulesHooks() {
|
|
460
|
-
return (0, _utils.createDefaultOrderRulesHooks)();
|
|
514
|
+
return (0, _utils.createDefaultOrderRulesHooks)(() => this.store.tempOrder?.bookings || []);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Stable across notifyTempOrderChanged's immutable copy, but changes when a
|
|
519
|
+
* different local/server order becomes current.
|
|
520
|
+
*/
|
|
521
|
+
getDiscountConfigOrderIdentityKey(tempOrder) {
|
|
522
|
+
const identity = this.getOrderIdentity();
|
|
523
|
+
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 ?? '']);
|
|
461
524
|
}
|
|
462
525
|
|
|
463
526
|
// ─── Discount: 公共 API ───
|
|
464
527
|
|
|
465
528
|
async loadDiscountConfig(params) {
|
|
466
529
|
const orderId = params.orderId || this.store.tempOrder?.order_id || undefined;
|
|
530
|
+
const requestEpoch = ++this.customerWalletPrepareConfigEpoch;
|
|
531
|
+
const requestOrderIdentityKey = this.getDiscountConfigOrderIdentityKey(this.store.tempOrder);
|
|
467
532
|
const prepareConfigResult = await this.store.discount?.loadPrepareConfig({
|
|
468
533
|
customer_id: params.customerId,
|
|
469
534
|
action: params.action || (orderId ? 'edit' : 'create'),
|
|
@@ -472,38 +537,90 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
472
537
|
with_wallet_pass_holder: 1,
|
|
473
538
|
with_available_wallet_flag: 1,
|
|
474
539
|
with_available_wallet_pass_flag: 1,
|
|
540
|
+
with_entitlement_pass: 1,
|
|
541
|
+
filter_order_wallet_pass_usage: 0,
|
|
475
542
|
with_virtual_currency: 1,
|
|
476
543
|
request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
477
544
|
...(orderId ? {
|
|
478
545
|
order_id: orderId
|
|
479
546
|
} : {})
|
|
480
547
|
});
|
|
548
|
+
const isCurrentRequest = () => requestEpoch === this.customerWalletPrepareConfigEpoch && this.getDiscountConfigOrderIdentityKey(this.store.tempOrder) === requestOrderIdentityKey && String(this.store.tempOrder?.customer_id ?? '') === String(params.customerId);
|
|
549
|
+
if (!isCurrentRequest()) {
|
|
550
|
+
return this.getDiscountList();
|
|
551
|
+
}
|
|
481
552
|
const discountList = prepareConfigResult?.discountList;
|
|
482
|
-
this.store.availableWalletIds = prepareConfigResult?.availableWalletIds || [];
|
|
483
|
-
this.store.virtualCurrencyList = prepareConfigResult?.virtualCurrencyList || [];
|
|
484
553
|
if (prepareConfigResult) {
|
|
485
|
-
this.
|
|
486
|
-
orderId,
|
|
554
|
+
this.writeCustomerWalletSnapshotsFromPrepareConfig({
|
|
487
555
|
customerId: params.customerId,
|
|
488
|
-
|
|
556
|
+
availableWalletIds: prepareConfigResult.availableWalletIds || [],
|
|
557
|
+
availableWalletPassList: prepareConfigResult.availableWalletPassList || [],
|
|
558
|
+
virtualCurrencyList: prepareConfigResult.virtualCurrencyList || []
|
|
489
559
|
});
|
|
490
560
|
}
|
|
491
561
|
if (discountList) {
|
|
492
562
|
const currentDiscountList = this.store.discount?.getDiscountList() || [];
|
|
493
|
-
const
|
|
563
|
+
const currentCommittedDiscounts = currentDiscountList.filter(discount => discount?.isEditMode || discount?.committedAppliedProductDetails?.length);
|
|
494
564
|
const currentScanDiscounts = currentDiscountList.filter(discount => discount?.isScan);
|
|
495
|
-
const
|
|
565
|
+
const currentScanDiscountById = new Map(currentScanDiscounts.flatMap(discount => {
|
|
566
|
+
const id = getDiscountIdentityKey(discount);
|
|
567
|
+
return id === null ? [] : [[id, discount]];
|
|
568
|
+
}));
|
|
569
|
+
const committedDiscountById = new Map(currentCommittedDiscounts.flatMap(discount => {
|
|
570
|
+
const id = getDiscountIdentityKey(discount);
|
|
571
|
+
return id === null ? [] : [[id, discount]];
|
|
572
|
+
}));
|
|
573
|
+
const mergedCommittedDiscountIds = new Set();
|
|
574
|
+
const refreshedScanDiscountIds = new Set();
|
|
496
575
|
const runtimeDiscountIds = new Set();
|
|
497
|
-
const runtimeDiscounts = discountList.
|
|
498
|
-
|
|
499
|
-
if (
|
|
500
|
-
runtimeDiscountIds.
|
|
501
|
-
|
|
576
|
+
const runtimeDiscounts = discountList.flatMap(discount => {
|
|
577
|
+
const id = getDiscountIdentityKey(discount);
|
|
578
|
+
if (id === null) return [discount];
|
|
579
|
+
if (runtimeDiscountIds.has(id)) return [];
|
|
580
|
+
runtimeDiscountIds.add(id);
|
|
581
|
+
const currentScanDiscount = currentScanDiscountById.get(id);
|
|
582
|
+
if (currentScanDiscount && (!(0, _entitlementPass.isEntitlementPass)(currentScanDiscount) || !(0, _entitlementPass.isEntitlementPass)(discount))) {
|
|
583
|
+
return [];
|
|
584
|
+
}
|
|
585
|
+
const committedDiscount = committedDiscountById.get(id);
|
|
586
|
+
let nextDiscount = discount;
|
|
587
|
+
if (committedDiscount && (0, _entitlementPass.isEntitlementPass)(committedDiscount) && (0, _entitlementPass.isEntitlementPass)(discount)) {
|
|
588
|
+
mergedCommittedDiscountIds.add(id);
|
|
589
|
+
nextDiscount = mergeCommittedEntitlementWithRuntime(committedDiscount, discount);
|
|
590
|
+
}
|
|
591
|
+
if (currentScanDiscount) {
|
|
592
|
+
refreshedScanDiscountIds.add(id);
|
|
593
|
+
nextDiscount = {
|
|
594
|
+
...nextDiscount,
|
|
595
|
+
isScan: true,
|
|
596
|
+
isSelected: currentScanDiscount.isSelected ?? nextDiscount.isSelected,
|
|
597
|
+
isManualSelect: currentScanDiscount.isManualSelect ?? nextDiscount.isManualSelect
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
return [nextDiscount];
|
|
601
|
+
});
|
|
602
|
+
const retainedCommittedDiscounts = currentCommittedDiscounts.filter(discount => {
|
|
603
|
+
const id = getDiscountIdentityKey(discount);
|
|
604
|
+
return id === null || !mergedCommittedDiscountIds.has(id);
|
|
605
|
+
}).map(discount => discount.committedAppliedProductDetails?.length ? {
|
|
606
|
+
...discount,
|
|
607
|
+
isEditMode: true,
|
|
608
|
+
isDisabled: true,
|
|
609
|
+
isSelected: true,
|
|
610
|
+
isUsed: true,
|
|
611
|
+
appliedProductDetails: [...discount.committedAppliedProductDetails]
|
|
612
|
+
} : discount);
|
|
613
|
+
const retainedScanDiscounts = currentScanDiscounts.filter(discount => {
|
|
614
|
+
const id = getDiscountIdentityKey(discount);
|
|
615
|
+
return id === null || !refreshedScanDiscountIds.has(id);
|
|
502
616
|
});
|
|
503
|
-
const mergedDiscountList = [...
|
|
617
|
+
const mergedDiscountList = [...retainedCommittedDiscounts, ...retainedScanDiscounts, ...runtimeDiscounts];
|
|
618
|
+
if (!isCurrentRequest()) return this.getDiscountList();
|
|
504
619
|
await this.store.discount?.setOriginalDiscountList(mergedDiscountList);
|
|
620
|
+
if (!isCurrentRequest()) return this.getDiscountList();
|
|
505
621
|
await this.store.discount?.setDiscountList(mergedDiscountList);
|
|
506
622
|
}
|
|
623
|
+
if (!isCurrentRequest()) return this.getDiscountList();
|
|
507
624
|
if (params.apply !== false && this.store.tempOrder?.products?.length) {
|
|
508
625
|
this.applyDiscount();
|
|
509
626
|
}
|
|
@@ -518,6 +635,9 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
518
635
|
getVirtualCurrencyList() {
|
|
519
636
|
return (0, _lodashEs.cloneDeep)(this.store.virtualCurrencyList || []);
|
|
520
637
|
}
|
|
638
|
+
getCustomerWalletPrepareConfigEpoch() {
|
|
639
|
+
return this.customerWalletPrepareConfigEpoch;
|
|
640
|
+
}
|
|
521
641
|
async syncScannedDiscountsToOriginalDiscountList(discountList) {
|
|
522
642
|
const scanDiscounts = (discountList || []).filter(discount => discount?.isScan);
|
|
523
643
|
if (!scanDiscounts.length) return;
|
|
@@ -615,19 +735,34 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
615
735
|
isAvailable: false,
|
|
616
736
|
discountList: this.getDiscountList()
|
|
617
737
|
};
|
|
618
|
-
const
|
|
738
|
+
const scannedIds = new Set(withScanList.map(discount => String(discount.id)));
|
|
739
|
+
const evaluatedScannedDiscounts = (evaluatedDiscountList || withScanList).filter(discount => scannedIds.has(String(discount.id)) && isCustomerPromotionDiscount(discount) && !(0, _availability.getDiscountLifecycleReason)(discount));
|
|
740
|
+
const shouldRetainForLater = !isAvailable && evaluatedScannedDiscounts.length > 0;
|
|
619
741
|
const normalizedNewDiscountList = newDiscountList ? markScannedDiscounts(newDiscountList, withScanList) : undefined;
|
|
620
|
-
const retainedDiscountList = evaluatedDiscountList ? markScannedDiscounts(evaluatedDiscountList, withScanList) : normalizedNewDiscountList;
|
|
621
742
|
let resolvedDiscountList = normalizedNewDiscountList || this.getDiscountList();
|
|
622
743
|
if (isAvailable && normalizedNewDiscountList) {
|
|
623
744
|
await this.store.discount?.setDiscountList(normalizedNewDiscountList);
|
|
624
|
-
|
|
745
|
+
// 实际写回商品时沿用扫码试算的优先级,避免旧商品行的选卡覆盖本次扫码。
|
|
746
|
+
this.applyDiscount({
|
|
747
|
+
preferredEntitlementPassIds: withScanList.filter(discount => (0, _entitlementPass.isEntitlementPass)(discount)).map(discount => discount.id)
|
|
748
|
+
});
|
|
625
749
|
await this.syncScannedDiscountsToOriginalDiscountList(this.getDiscountList());
|
|
626
750
|
resolvedDiscountList = this.getDiscountList();
|
|
627
|
-
} else if (shouldRetainForLater
|
|
628
|
-
//
|
|
629
|
-
|
|
630
|
-
|
|
751
|
+
} else if (shouldRetainForLater) {
|
|
752
|
+
// 只合并本次识别的卡券;不把试算产生的其他选券状态写回订单。
|
|
753
|
+
const retainedDiscounts = new Map(this.getDiscountList().map(discount => [String(discount.id), discount]));
|
|
754
|
+
evaluatedScannedDiscounts.forEach(discount => {
|
|
755
|
+
retainedDiscounts.set(String(discount.id), {
|
|
756
|
+
...discount,
|
|
757
|
+
isScan: true,
|
|
758
|
+
isAvailable: false,
|
|
759
|
+
isSelected: false,
|
|
760
|
+
reasonCode: discount.reasonCode || 'product_not_applicable',
|
|
761
|
+
appliedProductDetails: [],
|
|
762
|
+
savedAmount: 0
|
|
763
|
+
});
|
|
764
|
+
});
|
|
765
|
+
await this.store.discount?.setDiscountList([...retainedDiscounts.values()]);
|
|
631
766
|
await this.syncScannedDiscountsToOriginalDiscountList(this.getDiscountList());
|
|
632
767
|
resolvedDiscountList = this.getDiscountList();
|
|
633
768
|
}
|
|
@@ -636,7 +771,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
636
771
|
isAvailable: isAvailable || false,
|
|
637
772
|
isAccepted: Boolean(isAvailable || shouldRetainForLater),
|
|
638
773
|
discountList: resolvedDiscountList,
|
|
639
|
-
scannedDiscountList:
|
|
774
|
+
scannedDiscountList: evaluatedScannedDiscounts,
|
|
640
775
|
unavailableReason
|
|
641
776
|
};
|
|
642
777
|
}
|
|
@@ -669,7 +804,8 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
669
804
|
orderTotalAmount: Number(this.store.summary?.total_amount || 0)
|
|
670
805
|
}, {
|
|
671
806
|
reapplyBookingDiscountProductUids: options?.reapplyBookingDiscountProductUids,
|
|
672
|
-
reapplyWeighedDiscountProductUids: options?.reapplyWeighedDiscountProductUids
|
|
807
|
+
reapplyWeighedDiscountProductUids: options?.reapplyWeighedDiscountProductUids,
|
|
808
|
+
preferredEntitlementPassIds: options?.preferredEntitlementPassIds
|
|
673
809
|
});
|
|
674
810
|
if (result?.productList) {
|
|
675
811
|
const previousProductsByUid = (0, _utils.indexOrderProductsByUid)(tempOrder.products);
|
|
@@ -1007,32 +1143,107 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1007
1143
|
now: (0, _utils.formatDateTime)(new Date())
|
|
1008
1144
|
});
|
|
1009
1145
|
}
|
|
1010
|
-
|
|
1146
|
+
clearTempOrderCustomerWalletSnapshots(tempOrder) {
|
|
1011
1147
|
const orderMetaList = (0, _utils.normalizeOrderMetaList)(tempOrder.order_meta_list);
|
|
1012
1148
|
if (!orderMetaList) return;
|
|
1013
1149
|
const nextOrderMetaList = {
|
|
1014
1150
|
...orderMetaList
|
|
1015
1151
|
};
|
|
1016
1152
|
delete nextOrderMetaList.point_cards_snapshot;
|
|
1153
|
+
delete nextOrderMetaList.virtual_currency_snapshot;
|
|
1017
1154
|
if (Object.keys(nextOrderMetaList).length === 0) {
|
|
1018
1155
|
delete tempOrder.order_meta_list;
|
|
1019
1156
|
return;
|
|
1020
1157
|
}
|
|
1021
1158
|
tempOrder.order_meta_list = nextOrderMetaList;
|
|
1022
1159
|
}
|
|
1023
|
-
|
|
1160
|
+
clearCustomerWalletState(tempOrder) {
|
|
1161
|
+
this.clearCustomerWalletRuntimeState();
|
|
1162
|
+
this.clearTempOrderCustomerWalletSnapshots(tempOrder);
|
|
1163
|
+
}
|
|
1164
|
+
clearCustomerWalletRuntimeState() {
|
|
1165
|
+
this.customerWalletPrepareConfigEpoch += 1;
|
|
1166
|
+
this.pointCardSnapshotCodeCandidates.clear();
|
|
1167
|
+
this.store.availableWalletIds = [];
|
|
1168
|
+
this.store.virtualCurrencyList = [];
|
|
1169
|
+
}
|
|
1170
|
+
rememberPointCardSnapshotCodeCandidates(value) {
|
|
1171
|
+
(0, _utils.collectPointCardSnapshotCodeCandidates)(value).forEach(({
|
|
1172
|
+
id,
|
|
1173
|
+
code
|
|
1174
|
+
}) => {
|
|
1175
|
+
if (!this.pointCardSnapshotCodeCandidates.has(id)) {
|
|
1176
|
+
this.pointCardSnapshotCodeCandidates.set(id, code);
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
getRememberedPointCardSnapshotCodeCandidates() {
|
|
1181
|
+
return [...this.pointCardSnapshotCodeCandidates].map(([id, code]) => ({
|
|
1182
|
+
id,
|
|
1183
|
+
code
|
|
1184
|
+
}));
|
|
1185
|
+
}
|
|
1186
|
+
getPointCardSnapshotOrderIdentityKey(identity) {
|
|
1187
|
+
if (identity?.externalSaleNumber) {
|
|
1188
|
+
return `external:${identity.externalSaleNumber}`;
|
|
1189
|
+
}
|
|
1190
|
+
if (identity?.orderId !== undefined && identity.orderId !== null) {
|
|
1191
|
+
return `order:${identity.orderId}`;
|
|
1192
|
+
}
|
|
1193
|
+
if (identity?.localId) return `local:${identity.localId}`;
|
|
1194
|
+
if (identity?.orderNumber) return `number:${identity.orderNumber}`;
|
|
1195
|
+
return '';
|
|
1196
|
+
}
|
|
1197
|
+
isPointCardSnapshotMergeContextCurrent(context) {
|
|
1198
|
+
if (!context) return true;
|
|
1199
|
+
if (context.customerId !== undefined && String(context.customerId ?? '') !== String(this.store.tempOrder?.customer_id ?? '')) {
|
|
1200
|
+
return false;
|
|
1201
|
+
}
|
|
1202
|
+
const expectedOrderKey = this.getPointCardSnapshotOrderIdentityKey(context.orderIdentity);
|
|
1203
|
+
return !expectedOrderKey || expectedOrderKey === this.getPointCardSnapshotOrderIdentityKey(this.getOrderIdentity());
|
|
1204
|
+
}
|
|
1205
|
+
writeCustomerWalletSnapshotsFromPrepareConfig(params) {
|
|
1024
1206
|
const tempOrder = this.store.tempOrder;
|
|
1025
1207
|
if (!tempOrder) return;
|
|
1026
1208
|
if (String(tempOrder.customer_id ?? '') !== String(params.customerId)) return;
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1209
|
+
const currentOrderMetaList = (0, _utils.normalizeOrderMetaList)(tempOrder.order_meta_list);
|
|
1210
|
+
const virtualCurrencyList = Array.isArray(params.virtualCurrencyList) ? params.virtualCurrencyList : [];
|
|
1211
|
+
const snapshotsWithExistingCodes = (0, _utils.mergePointCardSnapshotCodes)(params.availableWalletPassList, currentOrderMetaList?.point_cards_snapshot);
|
|
1029
1212
|
tempOrder.order_meta_list = {
|
|
1030
|
-
...(
|
|
1031
|
-
point_cards_snapshot: (0, _utils.
|
|
1213
|
+
...(currentOrderMetaList || {}),
|
|
1214
|
+
point_cards_snapshot: (0, _utils.mergePointCardSnapshotCodes)(snapshotsWithExistingCodes, this.getRememberedPointCardSnapshotCodeCandidates()),
|
|
1215
|
+
virtual_currency_snapshot: (0, _utils.buildVirtualCurrencySnapshotFromList)(virtualCurrencyList)
|
|
1032
1216
|
};
|
|
1217
|
+
this.store.availableWalletIds = [...params.availableWalletIds];
|
|
1218
|
+
this.store.virtualCurrencyList = (0, _lodashEs.cloneDeep)(virtualCurrencyList);
|
|
1033
1219
|
this.persistTempOrder();
|
|
1034
1220
|
this.saveDraftInBackground();
|
|
1035
1221
|
}
|
|
1222
|
+
|
|
1223
|
+
/**
|
|
1224
|
+
* 将 Payment WalletPass 返回的实例 code 回填到当前订单 Snapshot。
|
|
1225
|
+
* 这里只按资产 ID 补空值,不新增卡、不覆盖 prepare/config 冻结的余额。
|
|
1226
|
+
*/
|
|
1227
|
+
mergePointCardSnapshotCodes(walletPassList, context) {
|
|
1228
|
+
const tempOrder = this.store.tempOrder;
|
|
1229
|
+
const currentOrderMetaList = (0, _utils.normalizeOrderMetaList)(tempOrder?.order_meta_list);
|
|
1230
|
+
const currentSnapshots = currentOrderMetaList?.point_cards_snapshot || [];
|
|
1231
|
+
if (!tempOrder || !this.isPointCardSnapshotMergeContextCurrent(context)) {
|
|
1232
|
+
return currentSnapshots;
|
|
1233
|
+
}
|
|
1234
|
+
this.rememberPointCardSnapshotCodeCandidates(walletPassList);
|
|
1235
|
+
if (!currentSnapshots.length) return currentSnapshots;
|
|
1236
|
+
const nextSnapshots = (0, _utils.mergePointCardSnapshotCodes)(currentSnapshots, this.getRememberedPointCardSnapshotCodeCandidates());
|
|
1237
|
+
const hasChanged = nextSnapshots.some((snapshot, index) => snapshot.code !== currentSnapshots[index]?.code);
|
|
1238
|
+
if (!hasChanged) return currentSnapshots;
|
|
1239
|
+
tempOrder.order_meta_list = {
|
|
1240
|
+
...(currentOrderMetaList || {}),
|
|
1241
|
+
point_cards_snapshot: nextSnapshots
|
|
1242
|
+
};
|
|
1243
|
+
this.persistTempOrder();
|
|
1244
|
+
this.saveDraftInBackground();
|
|
1245
|
+
return nextSnapshots;
|
|
1246
|
+
}
|
|
1036
1247
|
createExternalSaleNumber() {
|
|
1037
1248
|
const timestamp = (0, _dayjs.default)().format('YYYYMMDDHHmmssSSS');
|
|
1038
1249
|
const random6 = String(Math.floor(Math.random() * 1000000)).padStart(6, '0');
|
|
@@ -1550,7 +1761,8 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1550
1761
|
const sku = product?.product_sku || {};
|
|
1551
1762
|
const metadata = product?.metadata || {};
|
|
1552
1763
|
const discountTypes = (product?.discount_list || []).map(discount => discount?.type ?? discount?.tag);
|
|
1553
|
-
const
|
|
1764
|
+
const fullCoveragePassType = discountTypes.find(type => ['entitlement_pass', 'good_pass'].includes(type));
|
|
1765
|
+
const extensionType = product?.extension_type || fullCoveragePassType || 'normal';
|
|
1554
1766
|
return {
|
|
1555
1767
|
product_id: product?.product_id ?? null,
|
|
1556
1768
|
product_variant_id: product?.product_variant_id ?? null,
|
|
@@ -1603,7 +1815,8 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1603
1815
|
const sku = product?.product_sku || {};
|
|
1604
1816
|
const metadata = product?.metadata || {};
|
|
1605
1817
|
const discountTypes = (product?.discount_list || []).map(discount => discount?.type ?? discount?.tag);
|
|
1606
|
-
const
|
|
1818
|
+
const fullCoveragePassType = discountTypes.find(type => ['entitlement_pass', 'good_pass'].includes(type));
|
|
1819
|
+
const extensionType = product?.extension_type || fullCoveragePassType || 'normal';
|
|
1607
1820
|
return {
|
|
1608
1821
|
product_id: product?.product_id ?? null,
|
|
1609
1822
|
product_variant_id: product?.product_variant_id ?? null,
|
|
@@ -1812,6 +2025,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1812
2025
|
const freshTempOrder = this.createDefaultTempOrderInstance();
|
|
1813
2026
|
this.ensureExternalSaleNumber(freshTempOrder);
|
|
1814
2027
|
this.store.tempOrder = freshTempOrder;
|
|
2028
|
+
this.clearCustomerWalletRuntimeState();
|
|
1815
2029
|
this.store.summary = (0, _utils.createEmptySummary)();
|
|
1816
2030
|
this.store.lastOrderInfo = undefined;
|
|
1817
2031
|
this.store.syncState = undefined;
|
|
@@ -1832,6 +2046,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1832
2046
|
ensureIdentity: false
|
|
1833
2047
|
});
|
|
1834
2048
|
this.store.tempOrder = nextTempOrder;
|
|
2049
|
+
this.clearCustomerWalletRuntimeState();
|
|
1835
2050
|
if (options?.recalculateSummary !== false) {
|
|
1836
2051
|
await this.recalculateSummary({
|
|
1837
2052
|
createIfMissing: false
|
|
@@ -2616,7 +2831,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2616
2831
|
}
|
|
2617
2832
|
isCustomerBoundDiscount(discount) {
|
|
2618
2833
|
const type = discount?.type ?? discount?.tag;
|
|
2619
|
-
return type === 'good_pass' || type === 'discount_card' || type === 'product_discount_card';
|
|
2834
|
+
return type === 'entitlement_pass' || type === 'good_pass' || type === 'discount_card' || type === 'product_discount_card';
|
|
2620
2835
|
}
|
|
2621
2836
|
shouldKeepCustomerBoundDiscountAfterCustomerChange(discount) {
|
|
2622
2837
|
return Boolean(discount?.isScan || discount?.isEditMode);
|
|
@@ -2701,9 +2916,8 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2701
2916
|
email: tempOrder.email
|
|
2702
2917
|
});
|
|
2703
2918
|
const nextCustomerId = tempOrder.customer_id ?? null;
|
|
2704
|
-
if (String(previousCustomerId ?? '') !== String(nextCustomerId ?? '')
|
|
2705
|
-
this.
|
|
2706
|
-
this.clearTempOrderPointCardSnapshot(tempOrder);
|
|
2919
|
+
if (String(previousCustomerId ?? '') !== String(nextCustomerId ?? '')) {
|
|
2920
|
+
this.clearCustomerWalletState(tempOrder);
|
|
2707
2921
|
}
|
|
2708
2922
|
this.persistTempOrder();
|
|
2709
2923
|
return {
|
|
@@ -2739,11 +2953,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2739
2953
|
}
|
|
2740
2954
|
const nextCustomerId = tempOrder.customer_id ?? null;
|
|
2741
2955
|
if (String(previousCustomerId ?? '') !== String(nextCustomerId ?? '')) {
|
|
2742
|
-
this.
|
|
2743
|
-
this.store.virtualCurrencyList = [];
|
|
2744
|
-
if (!hasPersistedOrderId(tempOrder.order_id)) {
|
|
2745
|
-
this.clearTempOrderPointCardSnapshot(tempOrder);
|
|
2746
|
-
}
|
|
2956
|
+
this.clearCustomerWalletState(tempOrder);
|
|
2747
2957
|
(0, _utils.clearTempOrderHolderAssignments)(tempOrder);
|
|
2748
2958
|
this.clearCustomerBoundDiscounts(tempOrder);
|
|
2749
2959
|
this.applyDiscount();
|
|
@@ -2781,11 +2991,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2781
2991
|
tempOrder.email = '';
|
|
2782
2992
|
tempOrder.customer = null;
|
|
2783
2993
|
if (tempOrder._extend) delete tempOrder._extend.customerSnapshot;
|
|
2784
|
-
this.
|
|
2785
|
-
this.store.virtualCurrencyList = [];
|
|
2786
|
-
if (!hasPersistedOrderId(tempOrder.order_id)) {
|
|
2787
|
-
this.clearTempOrderPointCardSnapshot(tempOrder);
|
|
2788
|
-
}
|
|
2994
|
+
this.clearCustomerWalletState(tempOrder);
|
|
2789
2995
|
this.clearCustomerBoundDiscounts(tempOrder);
|
|
2790
2996
|
this.applyDiscount();
|
|
2791
2997
|
this.persistTempOrder();
|
|
@@ -3248,17 +3454,35 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3248
3454
|
}
|
|
3249
3455
|
normalizedIncoming.discount_list = (0, _utils.normalizeOrderProductDiscountList)(normalizedIncoming.discount_list);
|
|
3250
3456
|
const hasIncomingGoodPass = this.hasGoodPassDiscount(normalizedIncoming);
|
|
3457
|
+
const hasIncomingEntitlementPass = this.hasEntitlementPassDiscount(normalizedIncoming);
|
|
3458
|
+
const isIncomingEntitlementAppend = this.isRuntimeEntitlementAppend(productToAdd, tempOrder);
|
|
3459
|
+
const isIncomingPersistedEntitlementPass = hasIncomingEntitlementPass && this.isPersistedProductLine(normalizedIncoming);
|
|
3251
3460
|
const hasIncomingProductNote = this.hasOrderProductLineNote(normalizedIncoming);
|
|
3252
3461
|
const hasIncomingManualProductDiscount = isManualProductDiscountProduct(normalizedIncoming);
|
|
3253
3462
|
const hasIncomingBookingUid = this.hasLinkedBookingUid(productToAdd) || this.hasLinkedBookingUid(normalizedIncoming);
|
|
3254
3463
|
const isIncomingCustomItem = productToAdd.metadata?.origin?.isCustomItem === true || productToAdd._origin?.isCustomItem === true;
|
|
3255
|
-
const shouldForceNewLine = disableMerge || !!booking ||
|
|
3256
|
-
const matchedIndex =
|
|
3464
|
+
const shouldForceNewLine = disableMerge || !!booking || hasIncomingGoodPass || isIncomingPersistedEntitlementPass || hasIncomingBookingUid || isIncomingCustomItem || isWeighingProduct;
|
|
3465
|
+
const matchedIndex = shouldForceNewLine ? -1 : tempOrder.products.findIndex(item => {
|
|
3257
3466
|
if (this.hasGoodPassDiscount(item)) return false;
|
|
3258
|
-
|
|
3259
|
-
if (
|
|
3260
|
-
|
|
3261
|
-
|
|
3467
|
+
const isRuntimeEntitlementPass = this.isRuntimeEntitlementAppend(item, tempOrder);
|
|
3468
|
+
if (isIncomingEntitlementAppend !== isRuntimeEntitlementPass) {
|
|
3469
|
+
return false;
|
|
3470
|
+
}
|
|
3471
|
+
if (isIncomingEntitlementAppend && this.isPersistedProductLine(item)) {
|
|
3472
|
+
return false;
|
|
3473
|
+
}
|
|
3474
|
+
const hasExistingProductNote = this.hasOrderProductLineNote(item);
|
|
3475
|
+
if ((hasIncomingProductNote || hasExistingProductNote) && (!isRuntimeEntitlementPass || String(normalizedIncoming.note || '').trim() !== String(item.note || '').trim())) {
|
|
3476
|
+
return false;
|
|
3477
|
+
}
|
|
3478
|
+
const hasExistingManualProductDiscount = isManualProductDiscountProduct(item);
|
|
3479
|
+
if (isRuntimeEntitlementPass && !this.hasSameEntitlementMergePriceConfig(normalizedIncoming, item)) {
|
|
3480
|
+
return false;
|
|
3481
|
+
}
|
|
3482
|
+
if ((hasIncomingManualProductDiscount || hasExistingManualProductDiscount) && !isRuntimeEntitlementPass) {
|
|
3483
|
+
return false;
|
|
3484
|
+
}
|
|
3485
|
+
if (this.isPersistedProductLine(item)) return false;
|
|
3262
3486
|
if (item.product_id !== productToAdd.product_id) return false;
|
|
3263
3487
|
if (item.product_variant_id !== productToAdd.product_variant_id) return false;
|
|
3264
3488
|
const existedFingerprint = (0, _utils3.buildProductLineFingerprint)((0, _utils.getProductSkuOptions)(item), item.product_bundle);
|
|
@@ -3559,17 +3783,51 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3559
3783
|
});
|
|
3560
3784
|
return tempOrder.products;
|
|
3561
3785
|
}
|
|
3562
|
-
|
|
3563
|
-
const
|
|
3564
|
-
if (hasMainGoodPass) return true;
|
|
3565
|
-
const bundleDiscountList = [];
|
|
3786
|
+
getFullCoveragePassDiscounts(product) {
|
|
3787
|
+
const discountList = [...(product.discount_list || [])];
|
|
3566
3788
|
(product.product_bundle || []).forEach(item => {
|
|
3567
|
-
|
|
3789
|
+
discountList.push(...(item?.discount_list || []));
|
|
3568
3790
|
});
|
|
3569
3791
|
(product?._extend?.other?.bundle || []).forEach(item => {
|
|
3570
|
-
|
|
3792
|
+
discountList.push(...(item?.discount_list || []));
|
|
3571
3793
|
});
|
|
3572
|
-
return
|
|
3794
|
+
return discountList.filter(item => (0, _entitlementPass.isFullCoveragePass)(item));
|
|
3795
|
+
}
|
|
3796
|
+
hasEntitlementPassDiscount(product) {
|
|
3797
|
+
return this.getFullCoveragePassDiscounts(product).some(item => (0, _entitlementPass.isEntitlementPass)(item));
|
|
3798
|
+
}
|
|
3799
|
+
hasGoodPassDiscount(product) {
|
|
3800
|
+
return this.getFullCoveragePassDiscounts(product).some(item => !(0, _entitlementPass.isEntitlementPass)(item));
|
|
3801
|
+
}
|
|
3802
|
+
isPersistedProductLine(product) {
|
|
3803
|
+
return product.order_detail_id !== undefined && product.order_detail_id !== null && product.order_detail_id !== '' || product.metadata?.is_edit_for_runtime === true;
|
|
3804
|
+
}
|
|
3805
|
+
isRuntimeEntitlementAppend(product, tempOrder) {
|
|
3806
|
+
if (this.isPersistedProductLine(product)) return false;
|
|
3807
|
+
const uid = product.metadata?.unique_identification_number ?? product.unique_identification_number;
|
|
3808
|
+
const storedOrigin = uid !== undefined && uid !== null && uid !== '' ? tempOrder._extend?.productsByUid?.[String(uid)]?.origin : undefined;
|
|
3809
|
+
return Boolean(product?._origin?.__entitlementPassRuntimeAppend === true || storedOrigin?.__entitlementPassRuntimeAppend === true);
|
|
3810
|
+
}
|
|
3811
|
+
hasSameEntitlementMergePriceConfig(incoming, existing) {
|
|
3812
|
+
const buildPriceConfig = product => {
|
|
3813
|
+
const metadata = product.metadata || {};
|
|
3814
|
+
const bundle = (product.product_bundle || []).map(item => ({
|
|
3815
|
+
bundle_id: item.bundle_id ?? item.id ?? null,
|
|
3816
|
+
bundle_product_id: item.bundle_product_id ?? item._bundle_product_id ?? item.product_id ?? null,
|
|
3817
|
+
bundle_variant_id: item.bundle_variant_id ?? item.product_variant_id ?? 0,
|
|
3818
|
+
price_type: item.price_type ?? item.custom_price_type ?? '',
|
|
3819
|
+
price_type_ext: item.price_type_ext ?? item.custom_price_type_ext ?? ''
|
|
3820
|
+
})).sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
|
|
3821
|
+
return JSON.stringify({
|
|
3822
|
+
original_price: this.normalizeFingerprintMoney(product.original_price),
|
|
3823
|
+
source_product_price: this.normalizeFingerprintMoney(metadata.source_product_price),
|
|
3824
|
+
main_product_original_price: this.normalizeFingerprintMoney(metadata.main_product_original_price),
|
|
3825
|
+
price_override: this.normalizeFingerprintMoney(metadata.price_override),
|
|
3826
|
+
is_manual_discount: Boolean(metadata.is_manual_discount),
|
|
3827
|
+
bundle
|
|
3828
|
+
});
|
|
3829
|
+
};
|
|
3830
|
+
return buildPriceConfig(incoming) === buildPriceConfig(existing);
|
|
3573
3831
|
}
|
|
3574
3832
|
getBundleRuntimeIdentity(bundle) {
|
|
3575
3833
|
if (!bundle || typeof bundle !== 'object') return '';
|
|
@@ -3807,7 +4065,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3807
4065
|
// normalizeOrderProduct 幂等:v2 metadata 存在 → 保留 main_product_* 折扣重算 composite;
|
|
3808
4066
|
// 否则 → 把顶层 selling_price 视作 source,派生 main_product_* 并写 price_schema_version: 2。
|
|
3809
4067
|
let normalizedProduct = this.normalizeVirtualSettlementProductIfEnabled((0, _utils.mergeOrderProductDisplayFields)(nextProduct, (0, _utils3.normalizeOrderProduct)(nextProduct)));
|
|
3810
|
-
normalizedProduct.discount_list = (0, _utils.normalizeOrderProductDiscountList)(normalizedProduct.discount_list);
|
|
4068
|
+
normalizedProduct.discount_list = (0, _utils.normalizeOrderProductDiscountList)(isPersistedOrderLine ? syncReducedEntitlementPassUsageCount(normalizedProduct.discount_list, targetProduct.num, normalizedProduct.num) : normalizedProduct.discount_list);
|
|
3811
4069
|
const preservedBookingUid = nextProduct.booking_uid || nextProduct.metadata?.booking_uid || targetProduct.booking_uid || targetProduct.metadata?.booking_uid;
|
|
3812
4070
|
if (!booking && preservedBookingUid) {
|
|
3813
4071
|
normalizedProduct.booking_uid = preservedBookingUid;
|
|
@@ -3932,6 +4190,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3932
4190
|
throw new Error('[Order] 目标商品不存在,无法更新数量');
|
|
3933
4191
|
}
|
|
3934
4192
|
const targetProduct = tempOrder.products[productIndex];
|
|
4193
|
+
const isPersistedOrderLine = targetProduct.order_detail_id !== undefined && targetProduct.order_detail_id !== null;
|
|
3935
4194
|
const normalizedProduct = this.normalizeVirtualSettlementProductIfEnabled((0, _utils3.normalizeOrderProduct)({
|
|
3936
4195
|
...targetProduct,
|
|
3937
4196
|
num: (0, _utils3.getSafeProductNum)(num),
|
|
@@ -3940,7 +4199,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3940
4199
|
unique_identification_number: targetProduct.metadata?.unique_identification_number || unique_identification_number
|
|
3941
4200
|
}
|
|
3942
4201
|
}));
|
|
3943
|
-
normalizedProduct.discount_list = (0, _utils.normalizeOrderProductDiscountList)(normalizedProduct.discount_list);
|
|
4202
|
+
normalizedProduct.discount_list = (0, _utils.normalizeOrderProductDiscountList)(isPersistedOrderLine ? syncReducedEntitlementPassUsageCount(normalizedProduct.discount_list, targetProduct.num, normalizedProduct.num) : normalizedProduct.discount_list);
|
|
3944
4203
|
const settlementSnapshot = (0, _virtualSettlement.getSelectedSettlementSnapshot)(normalizedProduct);
|
|
3945
4204
|
if (this.isMultiCurrencySettlementEnabled() && settlementSnapshot?.settlement_type === 'virtual_currency' && tempOrder.customer_id && Number(tempOrder.customer_id) !== 1) {
|
|
3946
4205
|
const currencyProductId = Number(settlementSnapshot.currency_product_id);
|
|
@@ -4740,11 +4999,21 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4740
4999
|
query
|
|
4741
5000
|
} = params;
|
|
4742
5001
|
const fetchUrl = url || '/order/sales/checkout';
|
|
4743
|
-
|
|
5002
|
+
const requestQuery = this.isSalesCheckoutHandledByOsServer(fetchUrl) ? query : (0, _osPrivatePayload.stripOsPrivatePayloadFields)(query);
|
|
5003
|
+
return this.request.post(fetchUrl, requestQuery, {
|
|
4744
5004
|
osServer: true,
|
|
4745
5005
|
customToast: () => {}
|
|
4746
5006
|
});
|
|
4747
5007
|
}
|
|
5008
|
+
isSalesCheckoutHandledByOsServer(fetchUrl) {
|
|
5009
|
+
const server = this.core?.server;
|
|
5010
|
+
if (!server || typeof server.hasRoute !== 'function') return false;
|
|
5011
|
+
const requestBaseUrl = String(this.request?.baseUrl || '/shop').replace(/\/+$/, '');
|
|
5012
|
+
if (!requestBaseUrl.startsWith('/')) return false;
|
|
5013
|
+
const normalizedFetchUrl = fetchUrl.startsWith('/') ? fetchUrl : `/${fetchUrl}`;
|
|
5014
|
+
const routePath = normalizedFetchUrl === requestBaseUrl || normalizedFetchUrl.startsWith(`${requestBaseUrl}/`) ? normalizedFetchUrl : `${requestBaseUrl}${normalizedFetchUrl}`;
|
|
5015
|
+
return server.hasRoute('post', routePath) === true;
|
|
5016
|
+
}
|
|
4748
5017
|
|
|
4749
5018
|
/**
|
|
4750
5019
|
* 发送支付链接邮件。
|
|
@@ -4833,6 +5102,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4833
5102
|
allowUnresolvedSalesNotes: isSessionStorageHydrate
|
|
4834
5103
|
});
|
|
4835
5104
|
this.store.tempOrder = nextTempOrder;
|
|
5105
|
+
this.clearCustomerWalletRuntimeState();
|
|
4836
5106
|
if (isSessionStorageHydrate) {
|
|
4837
5107
|
// 会话快照是未提交购物车,不应获得编辑订单的 saved/edit 标记与服务端基线。
|
|
4838
5108
|
const restoredSessionDiscounts = Array.isArray(raw.discount_list) ? (0, _lodashEs.cloneDeep)(raw.discount_list) : [];
|