@pisell/pisellos 0.10.41 → 0.10.43
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/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +58 -19
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +24 -12
- 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 -0
- package/dist/modules/Discount/index.js +39 -13
- package/dist/modules/Discount/types.d.ts +26 -5
- package/dist/modules/Order/index.d.ts +5 -0
- package/dist/modules/Order/index.js +283 -118
- package/dist/modules/Order/utils/orderCollectionIdentity.js +16 -12
- package/dist/modules/Order/utils.js +17 -1
- package/dist/modules/ResourcePlanner/planner.d.ts +2 -2
- package/dist/modules/ResourcePlanner/planner.js +163 -46
- package/dist/modules/ResourcePlanner/types.d.ts +17 -0
- package/dist/modules/Rules/index.js +312 -135
- package/dist/solution/BaseSales/index.d.ts +2 -0
- package/dist/solution/BaseSales/index.js +9 -1
- package/dist/solution/BaseSales/utils/cartPromotion.js +141 -126
- package/dist/solution/BookingTicket/types.d.ts +5 -1
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +171 -96
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +3 -1
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +2 -0
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +12 -3
- package/dist/solution/ShopDiscount/index.d.ts +1 -0
- package/dist/solution/ShopDiscount/index.js +41 -21
- package/dist/solution/ShopDiscount/utils.js +3 -2
- package/dist/solution/UnifiedBookingSales/index.js +94 -21
- package/lib/model/strategy/adapter/promotion/index.js +1 -46
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +35 -6
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +13 -6
- 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 -0
- package/lib/modules/Discount/index.js +25 -9
- package/lib/modules/Discount/types.d.ts +26 -5
- package/lib/modules/Order/index.d.ts +5 -0
- package/lib/modules/Order/index.js +172 -28
- package/lib/modules/Order/utils/orderCollectionIdentity.js +14 -11
- package/lib/modules/Order/utils.js +19 -1
- package/lib/modules/ResourcePlanner/planner.d.ts +2 -2
- package/lib/modules/ResourcePlanner/planner.js +137 -35
- package/lib/modules/ResourcePlanner/types.d.ts +17 -0
- package/lib/modules/Rules/index.js +232 -97
- package/lib/solution/BaseSales/index.d.ts +2 -0
- package/lib/solution/BaseSales/index.js +7 -1
- package/lib/solution/BaseSales/utils/cartPromotion.js +12 -4
- package/lib/solution/BookingTicket/types.d.ts +5 -1
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +65 -18
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +3 -1
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +2 -0
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +5 -1
- package/lib/solution/ShopDiscount/index.d.ts +1 -0
- package/lib/solution/ShopDiscount/index.js +26 -6
- package/lib/solution/ShopDiscount/utils.js +3 -2
- package/lib/solution/UnifiedBookingSales/index.js +86 -22
- package/package.json +1 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.GOOD_PASS_TYPE = exports.ENTITLEMENT_PASS_TYPE = void 0;
|
|
7
|
+
exports.compareEntitlementPassPriority = compareEntitlementPassPriority;
|
|
8
|
+
exports.getRemainingEntitlementUses = getRemainingEntitlementUses;
|
|
9
|
+
exports.hasAvailableEntitlementUses = hasAvailableEntitlementUses;
|
|
10
|
+
exports.isEntitlementPass = isEntitlementPass;
|
|
11
|
+
exports.isFullCoveragePass = isFullCoveragePass;
|
|
12
|
+
exports.resolveEntitlementQuota = resolveEntitlementQuota;
|
|
13
|
+
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
14
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
+
const ENTITLEMENT_PASS_TYPE = exports.ENTITLEMENT_PASS_TYPE = 'entitlement_pass';
|
|
16
|
+
const GOOD_PASS_TYPE = exports.GOOD_PASS_TYPE = 'good_pass';
|
|
17
|
+
function getDiscountType(discount) {
|
|
18
|
+
const nestedDiscount = discount?.discount;
|
|
19
|
+
return String(discount?.tag || discount?.type || nestedDiscount?.tag || nestedDiscount?.type || '');
|
|
20
|
+
}
|
|
21
|
+
function isEntitlementPass(discount) {
|
|
22
|
+
return getDiscountType(discount) === ENTITLEMENT_PASS_TYPE;
|
|
23
|
+
}
|
|
24
|
+
function isFullCoveragePass(discount) {
|
|
25
|
+
const type = getDiscountType(discount);
|
|
26
|
+
return type === GOOD_PASS_TYPE || type === ENTITLEMENT_PASS_TYPE;
|
|
27
|
+
}
|
|
28
|
+
function resolveIncludedUses(discount) {
|
|
29
|
+
const metadata = discount.metadata;
|
|
30
|
+
const hasMetadataIncludedUses = !!metadata && typeof metadata === 'object' && Object.prototype.hasOwnProperty.call(metadata, 'included_uses');
|
|
31
|
+
const rawValue = hasMetadataIncludedUses ? metadata?.included_uses : Array.isArray(discount.extension_data) ? discount.extension_data.find(item => item?.field_key === 'included_uses')?.value : undefined;
|
|
32
|
+
if (!rawValue || typeof rawValue !== 'object' || Array.isArray(rawValue)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const includedUses = rawValue;
|
|
36
|
+
if (includedUses.type === 'unlimited' && includedUses.value === null) {
|
|
37
|
+
return {
|
|
38
|
+
type: 'unlimited',
|
|
39
|
+
value: null
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (includedUses.type === 'limited' && typeof includedUses.value === 'number' && Number.isInteger(includedUses.value) && includedUses.value >= 1 && includedUses.value <= 9999) {
|
|
43
|
+
return {
|
|
44
|
+
type: 'limited',
|
|
45
|
+
value: includedUses.value
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
function resolveNonNegativeInteger(value) {
|
|
51
|
+
if (typeof value !== 'number' && typeof value !== 'string' || typeof value === 'string' && value.trim() === '') {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const normalizedValue = Number(value);
|
|
55
|
+
if (!Number.isInteger(normalizedValue) || normalizedValue < 0) return null;
|
|
56
|
+
return normalizedValue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolve the authoritative total-use contract for an Entitlement Pass.
|
|
61
|
+
* Invalid or missing configuration fails closed. Legacy mode/total_uses fields
|
|
62
|
+
* are intentionally unsupported.
|
|
63
|
+
*/
|
|
64
|
+
function resolveEntitlementQuota(discount) {
|
|
65
|
+
if (!isEntitlementPass(discount)) return null;
|
|
66
|
+
const includedUses = resolveIncludedUses(discount);
|
|
67
|
+
if (!includedUses) return null;
|
|
68
|
+
if (includedUses.type === 'unlimited') {
|
|
69
|
+
return {
|
|
70
|
+
type: 'unlimited',
|
|
71
|
+
value: null,
|
|
72
|
+
balance: null
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const balance = resolveNonNegativeInteger(discount?.balance);
|
|
76
|
+
if (balance === null) return null;
|
|
77
|
+
return {
|
|
78
|
+
type: 'limited',
|
|
79
|
+
value: includedUses.value,
|
|
80
|
+
balance
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function getRemainingEntitlementUses(discount, usedCount, requestedCount) {
|
|
84
|
+
const quota = resolveEntitlementQuota(discount);
|
|
85
|
+
const normalizedRequestedCount = Math.max(Math.floor(Number(requestedCount) || 0), 0);
|
|
86
|
+
if (!quota || normalizedRequestedCount <= 0) return 0;
|
|
87
|
+
if (quota.type === 'unlimited') return normalizedRequestedCount;
|
|
88
|
+
const normalizedUsedCount = Math.max(Math.floor(Number(usedCount) || 0), 0);
|
|
89
|
+
return Math.min(Math.max(quota.balance - normalizedUsedCount, 0), normalizedRequestedCount);
|
|
90
|
+
}
|
|
91
|
+
function hasAvailableEntitlementUses(discount) {
|
|
92
|
+
const quota = resolveEntitlementQuota(discount);
|
|
93
|
+
return !!quota && (quota.type === 'unlimited' || quota.balance > 0);
|
|
94
|
+
}
|
|
95
|
+
function resolveExpireTimestamp(value) {
|
|
96
|
+
if (typeof value !== 'string' || !value.trim()) return Number.POSITIVE_INFINITY;
|
|
97
|
+
const normalizedValue = value.trim();
|
|
98
|
+
const calendarParts = normalizedValue.match(/^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?)?/);
|
|
99
|
+
if (calendarParts) {
|
|
100
|
+
const [, year, month, day, hour = '0', minute = '0', second = '0'] = calendarParts;
|
|
101
|
+
const yearNumber = Number(year);
|
|
102
|
+
const monthNumber = Number(month);
|
|
103
|
+
const dayNumber = Number(day);
|
|
104
|
+
const hourNumber = Number(hour);
|
|
105
|
+
const minuteNumber = Number(minute);
|
|
106
|
+
const secondNumber = Number(second);
|
|
107
|
+
const daysInMonth = new Date(Date.UTC(yearNumber, monthNumber, 0)).getUTCDate();
|
|
108
|
+
if (yearNumber <= 0 || monthNumber < 1 || monthNumber > 12 || dayNumber < 1 || dayNumber > daysInMonth || hourNumber < 0 || hourNumber > 23 || minuteNumber < 0 || minuteNumber > 59 || secondNumber < 0 || secondNumber > 59) {
|
|
109
|
+
return Number.POSITIVE_INFINITY;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const date = (0, _dayjs.default)(normalizedValue);
|
|
113
|
+
return date.isValid() ? date.valueOf() : Number.POSITIVE_INFINITY;
|
|
114
|
+
}
|
|
115
|
+
function compareIds(left, right) {
|
|
116
|
+
const leftNumber = Number(left);
|
|
117
|
+
const rightNumber = Number(right);
|
|
118
|
+
if (Number.isFinite(leftNumber) && Number.isFinite(rightNumber)) {
|
|
119
|
+
return leftNumber - rightNumber;
|
|
120
|
+
}
|
|
121
|
+
return String(left ?? '').localeCompare(String(right ?? ''), undefined, {
|
|
122
|
+
numeric: true
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Sort Entitlement Passes by expiry, finite balance, then card instance id. */
|
|
127
|
+
function compareEntitlementPassPriority(left, right) {
|
|
128
|
+
const expireDifference = resolveExpireTimestamp(left.expire_time) - resolveExpireTimestamp(right.expire_time);
|
|
129
|
+
if (!Number.isNaN(expireDifference) && expireDifference !== 0) {
|
|
130
|
+
return expireDifference;
|
|
131
|
+
}
|
|
132
|
+
const leftQuota = resolveEntitlementQuota(left);
|
|
133
|
+
const rightQuota = resolveEntitlementQuota(right);
|
|
134
|
+
const leftBalance = leftQuota?.type === 'limited' ? leftQuota.balance : Number.POSITIVE_INFINITY;
|
|
135
|
+
const rightBalance = rightQuota?.type === 'limited' ? rightQuota.balance : Number.POSITIVE_INFINITY;
|
|
136
|
+
const balanceDifference = leftBalance - rightBalance;
|
|
137
|
+
if (!Number.isNaN(balanceDifference) && balanceDifference !== 0) {
|
|
138
|
+
return balanceDifference;
|
|
139
|
+
}
|
|
140
|
+
return compareIds(left.id, right.id);
|
|
141
|
+
}
|
|
@@ -26,6 +26,7 @@ export declare class DiscountModule extends BaseModule implements Module, Discou
|
|
|
26
26
|
with_available_wallet_flag?: 0 | 1;
|
|
27
27
|
with_available_wallet_pass_flag?: 0 | 1;
|
|
28
28
|
with_virtual_currency?: 0 | 1;
|
|
29
|
+
with_entitlement_pass?: 0 | 1;
|
|
29
30
|
request_timezone: string;
|
|
30
31
|
order_id?: number | string;
|
|
31
32
|
}): Promise<DiscountPrepareConfigResult>;
|
|
@@ -36,6 +37,7 @@ export declare class DiscountModule extends BaseModule implements Module, Discou
|
|
|
36
37
|
}): Promise<DiscountWithReason>;
|
|
37
38
|
batchSearchByProductIds(productIds: number[]): Promise<any>;
|
|
38
39
|
filterEnabledDiscountList(discountList: Discount[]): Discount[];
|
|
40
|
+
private hasAvailableBalance;
|
|
39
41
|
private checkUsageCreditsLimit;
|
|
40
42
|
filterEnabledDiscountListWithReason(discountList: Discount[]): DiscountWithReason;
|
|
41
43
|
private checkUsageCreditsLimitWithFailKey;
|
|
@@ -8,6 +8,7 @@ var _utils = require("../../solution/ShopDiscount/utils");
|
|
|
8
8
|
var _BaseModule = require("../BaseModule");
|
|
9
9
|
var _types = require("./types");
|
|
10
10
|
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
11
|
+
var _entitlementPass = require("./entitlementPass");
|
|
11
12
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
13
|
class DiscountModule extends _BaseModule.BaseModule {
|
|
13
14
|
defaultName = 'discount';
|
|
@@ -83,7 +84,14 @@ class DiscountModule extends _BaseModule.BaseModule {
|
|
|
83
84
|
with_available_wallet_pass_flag: params.with_available_wallet_pass_flag ?? 1,
|
|
84
85
|
with_virtual_currency: params.with_virtual_currency ?? 1
|
|
85
86
|
});
|
|
86
|
-
const
|
|
87
|
+
const rawEntitlementPassList = prepareConfig?.data?.entitlement_pass_list;
|
|
88
|
+
const entitlementPassList = Array.isArray(rawEntitlementPassList) ? rawEntitlementPassList : [];
|
|
89
|
+
const entitlementPassIds = new Set(entitlementPassList.map(discount => discount?.id).filter(id => id !== undefined && id !== null).map(id => String(id)));
|
|
90
|
+
const excludeEntitlementDuplicates = discount => {
|
|
91
|
+
const id = discount?.id;
|
|
92
|
+
return id === undefined || id === null || !entitlementPassIds.has(String(id));
|
|
93
|
+
};
|
|
94
|
+
const rawDiscountList = [...entitlementPassList, ...(prepareConfig?.data?.good_pass_list || []).filter(excludeEntitlementDuplicates), ...(prepareConfig?.data?.discount_card_list || []).filter(excludeEntitlementDuplicates)];
|
|
87
95
|
const availableWalletList = [...(prepareConfig?.data?.avaiable_wallet_list || []), ...(prepareConfig?.data?.available_wallet_list || [])];
|
|
88
96
|
const availableWalletPassList = prepareConfig?.data?.available_wallet_pass_list || [];
|
|
89
97
|
const availableWalletIds = this.normalizePositiveNumberIds([...availableWalletList.map(item => item?.wallet_id), ...availableWalletPassList.map(item => item?.product_id)]);
|
|
@@ -135,7 +143,7 @@ class DiscountModule extends _BaseModule.BaseModule {
|
|
|
135
143
|
const result = await this.request.get(`/machinecode/batch-search`, {
|
|
136
144
|
code,
|
|
137
145
|
translate_flag: 1,
|
|
138
|
-
tags: ['good_pass', 'product_discount_card'],
|
|
146
|
+
tags: ['entitlement_pass', 'good_pass', 'product_discount_card'],
|
|
139
147
|
// available: 1,
|
|
140
148
|
relation_product: 1,
|
|
141
149
|
with: ['extensionData', 'customScheduleSnapshot', 'holder.detail'],
|
|
@@ -154,13 +162,24 @@ class DiscountModule extends _BaseModule.BaseModule {
|
|
|
154
162
|
const result = await this.request.get(`/machinecode/batch-search`, {
|
|
155
163
|
ids: productIds,
|
|
156
164
|
translate_flag: 1,
|
|
157
|
-
tags: ['good_pass', 'product_discount_card'],
|
|
158
|
-
relation_product: 1
|
|
165
|
+
tags: ['entitlement_pass', 'good_pass', 'product_discount_card'],
|
|
166
|
+
relation_product: 1,
|
|
167
|
+
with: ['extensionData', 'customScheduleSnapshot', 'holder.detail'],
|
|
168
|
+
unified_status_info_flag: 1,
|
|
169
|
+
request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
159
170
|
});
|
|
160
171
|
return result?.data || [];
|
|
161
172
|
}
|
|
162
173
|
filterEnabledDiscountList(discountList) {
|
|
163
|
-
return discountList.filter(discount => discount.limit_status === 'enable' &&
|
|
174
|
+
return discountList.filter(discount => discount.limit_status === 'enable' && this.hasAvailableBalance(discount) && this.checkUsageCreditsLimit(discount));
|
|
175
|
+
}
|
|
176
|
+
hasAvailableBalance(discount) {
|
|
177
|
+
if ((0, _entitlementPass.isEntitlementPass)(discount)) {
|
|
178
|
+
return (0, _entitlementPass.hasAvailableEntitlementUses)(discount);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// good_pass 与折扣卡继续沿用原有金额余额判断。
|
|
182
|
+
return new _decimal.default(discount?.par_value || 0).minus(new _decimal.default(discount?.used_par_value || 0)).greaterThan(0);
|
|
164
183
|
}
|
|
165
184
|
|
|
166
185
|
// 检查使用次数限制
|
|
@@ -220,10 +239,7 @@ class DiscountModule extends _BaseModule.BaseModule {
|
|
|
220
239
|
|
|
221
240
|
// 过滤启用且可用的优惠券,并返回校验失败原因
|
|
222
241
|
filterEnabledDiscountListWithReason(discountList) {
|
|
223
|
-
let lists = discountList.filter(discount => discount.limit_status === 'enable').filter(discount => {
|
|
224
|
-
const remaining = new _decimal.default(discount?.par_value || 0).minus(new _decimal.default(discount?.used_par_value || 0)).greaterThan(0);
|
|
225
|
-
return remaining;
|
|
226
|
-
}).map(discount => {
|
|
242
|
+
let lists = discountList.filter(discount => discount.limit_status === 'enable').filter(discount => this.hasAvailableBalance(discount)).map(discount => {
|
|
227
243
|
const limitFailKey = this.checkUsageCreditsLimitWithFailKey(discount);
|
|
228
244
|
return {
|
|
229
245
|
...discount,
|
|
@@ -63,6 +63,13 @@ interface UsageCreditsValue {
|
|
|
63
63
|
max_per_week: number;
|
|
64
64
|
max_per_month: number;
|
|
65
65
|
}
|
|
66
|
+
export type IncludedUses = {
|
|
67
|
+
type: 'limited';
|
|
68
|
+
value: number;
|
|
69
|
+
} | {
|
|
70
|
+
type: 'unlimited';
|
|
71
|
+
value: null;
|
|
72
|
+
};
|
|
66
73
|
export type DiscountFilterRejectKey = 'total_credits' | 'per_user_limit' | 'max_per_day' | 'max_per_week' | 'max_per_month';
|
|
67
74
|
export interface DiscountWithReason {
|
|
68
75
|
discountList: Discount[];
|
|
@@ -107,30 +114,36 @@ interface ExtensionData {
|
|
|
107
114
|
}
|
|
108
115
|
export interface Discount {
|
|
109
116
|
id: number;
|
|
117
|
+
shop_id?: number;
|
|
110
118
|
product_name: string;
|
|
111
119
|
encoded: string;
|
|
112
120
|
code: string;
|
|
113
121
|
tag: string;
|
|
114
|
-
expire_time?: string;
|
|
122
|
+
expire_time?: string | null;
|
|
123
|
+
expire_date?: string | null;
|
|
124
|
+
end_time?: string | null;
|
|
115
125
|
product_id: number;
|
|
116
126
|
relation_type: string;
|
|
117
127
|
par_value: string;
|
|
118
128
|
used_par_value: string;
|
|
119
129
|
limit_status: string;
|
|
120
130
|
limited_relation_product_data: Limitedrelationproductdata;
|
|
121
|
-
balance
|
|
131
|
+
balance?: string | number | null;
|
|
132
|
+
available_num?: number;
|
|
133
|
+
available_products_info?: any[];
|
|
122
134
|
format_title: Formattitle;
|
|
123
135
|
metadata?: {
|
|
124
136
|
discount_card_type?: 'fixed_amount' | 'percent';
|
|
125
137
|
custom_product_bundle_map_id?: string;
|
|
126
138
|
validity_type?: 'custom_schedule_validity' | 'fixed_validity';
|
|
127
|
-
|
|
128
|
-
|
|
139
|
+
included_uses?: IncludedUses;
|
|
140
|
+
discount_calculation_mode?: 'item_level' | 'order_level';
|
|
141
|
+
holder?: {
|
|
129
142
|
type: 'customer' | 'custom';
|
|
130
143
|
};
|
|
131
144
|
};
|
|
132
145
|
product: Product;
|
|
133
|
-
type
|
|
146
|
+
type?: 'product' | 'good_pass' | 'entitlement_pass' | 'discount_card' | 'product_discount_card';
|
|
134
147
|
resource_id?: number;
|
|
135
148
|
savedAmount?: number;
|
|
136
149
|
isEditMode?: boolean;
|
|
@@ -163,9 +176,17 @@ export interface Discount {
|
|
|
163
176
|
applicableProductIds?: number[];
|
|
164
177
|
applicableProductDetails: ApplicableProductDetails[];
|
|
165
178
|
appliedProductDetails: ApplicableProductDetails[];
|
|
179
|
+
/** Entitlement Pass 编辑订单时已经提交的使用明细。 */
|
|
180
|
+
committedAppliedProductDetails?: ApplicableProductDetails[];
|
|
166
181
|
isDisabledForProductUsed?: boolean;
|
|
167
182
|
amount?: number;
|
|
168
183
|
customer_id?: number | string | null;
|
|
184
|
+
holder?: {
|
|
185
|
+
holder_type?: 'customer' | 'form' | string;
|
|
186
|
+
holder_id?: number | string | Array<number | string>;
|
|
187
|
+
created_at?: string | null;
|
|
188
|
+
[key: string]: any;
|
|
189
|
+
};
|
|
169
190
|
extension_data?: ExtensionData[];
|
|
170
191
|
total_order_behavior_count?: number;
|
|
171
192
|
today_order_behavior_count?: number;
|
|
@@ -445,7 +445,12 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
445
445
|
}>, options?: {
|
|
446
446
|
skipEditDiscountConfigRefresh?: boolean;
|
|
447
447
|
}): Promise<OrderProduct[]>;
|
|
448
|
+
private getFullCoveragePassDiscounts;
|
|
449
|
+
private hasEntitlementPassDiscount;
|
|
448
450
|
private hasGoodPassDiscount;
|
|
451
|
+
private isPersistedProductLine;
|
|
452
|
+
private isRuntimeEntitlementAppend;
|
|
453
|
+
private hasSameEntitlementMergePriceConfig;
|
|
449
454
|
private getBundleRuntimeIdentity;
|
|
450
455
|
private preservePersistedBundlePriceFields;
|
|
451
456
|
private applyOrderProductUpdateToTempOrder;
|
|
@@ -21,6 +21,7 @@ var _discountProductLineIdentity = require("./utils/discountProductLineIdentity"
|
|
|
21
21
|
var _virtualSettlement = require("./utils/virtualSettlement");
|
|
22
22
|
var _Discount = require("../Discount");
|
|
23
23
|
var _Rules = require("../Rules");
|
|
24
|
+
var _entitlementPass = require("../Discount/entitlementPass");
|
|
24
25
|
var _types2 = require("../Rules/types");
|
|
25
26
|
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
26
27
|
var _lodashEs = require("lodash-es");
|
|
@@ -40,9 +41,27 @@ function isSameDiscountList(left, right) {
|
|
|
40
41
|
function isManualProductDiscountProduct(product) {
|
|
41
42
|
return (0, _manualProductDiscount.hasManualProductDiscountFlag)(product?.metadata) || (product?.discount_list || []).some(item => item?.type === 'product');
|
|
42
43
|
}
|
|
44
|
+
function syncReducedEntitlementPassUsageCount(discountList, previousQuantity, nextQuantity) {
|
|
45
|
+
if (!Array.isArray(discountList)) return discountList;
|
|
46
|
+
const previousCount = (0, _utils3.getSafeProductNum)(previousQuantity);
|
|
47
|
+
const nextCount = (0, _utils3.getSafeProductNum)(nextQuantity);
|
|
48
|
+
if (nextCount >= previousCount) return discountList;
|
|
49
|
+
return discountList.map(discount => {
|
|
50
|
+
if (!(0, _entitlementPass.isEntitlementPass)(discount)) return discount;
|
|
51
|
+
const rawUsageCount = Number(discount?.metadata?.num ?? previousCount);
|
|
52
|
+
const usageCount = Number.isFinite(rawUsageCount) ? Math.max(Math.floor(rawUsageCount), 0) : previousCount;
|
|
53
|
+
return {
|
|
54
|
+
...discount,
|
|
55
|
+
metadata: {
|
|
56
|
+
...(discount?.metadata || {}),
|
|
57
|
+
num: Math.min(usageCount, nextCount)
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}
|
|
43
62
|
const ORDER_PAYMENT_STATUS_PAID = 'paid';
|
|
44
63
|
const ORDER_PAYMENT_STATUS_PENDING = 'payment_pending';
|
|
45
|
-
const CUSTOMER_PROMOTION_DISCOUNT_TAGS = new Set(['good_pass', 'product_discount_card', 'discount_card']);
|
|
64
|
+
const CUSTOMER_PROMOTION_DISCOUNT_TAGS = new Set(['entitlement_pass', 'good_pass', 'product_discount_card', 'discount_card']);
|
|
46
65
|
function isCustomerPromotionDiscount(discount) {
|
|
47
66
|
return CUSTOMER_PROMOTION_DISCOUNT_TAGS.has(String(discount?.tag || discount?.type || '').toLowerCase());
|
|
48
67
|
}
|
|
@@ -72,6 +91,28 @@ function getVoucherPaymentLifecycleKey(payment) {
|
|
|
72
91
|
if (!isVoucherPaymentRecord(payment)) return null;
|
|
73
92
|
return `${String(payment.voucher_id)}::${String(payment.status || '')}`;
|
|
74
93
|
}
|
|
94
|
+
function getDiscountIdentityKey(discount) {
|
|
95
|
+
const id = discount?.id;
|
|
96
|
+
if (id === undefined || id === null || id === '') return null;
|
|
97
|
+
return String(id);
|
|
98
|
+
}
|
|
99
|
+
function mergeCommittedEntitlementWithRuntime(committed, runtime) {
|
|
100
|
+
const committedAppliedProductDetails = committed.committedAppliedProductDetails?.length ? committed.committedAppliedProductDetails : committed.appliedProductDetails || [];
|
|
101
|
+
return {
|
|
102
|
+
...committed,
|
|
103
|
+
...runtime,
|
|
104
|
+
id: runtime.id ?? committed.id,
|
|
105
|
+
tag: runtime.tag || committed.tag,
|
|
106
|
+
type: runtime.type || committed.type,
|
|
107
|
+
isEditMode: false,
|
|
108
|
+
isDisabled: runtime.isDisabled ?? false,
|
|
109
|
+
isSelected: runtime.isSelected ?? committed.isSelected,
|
|
110
|
+
isUsed: committed.isUsed || runtime.isUsed,
|
|
111
|
+
committedAppliedProductDetails: [...committedAppliedProductDetails],
|
|
112
|
+
applicableProductDetails: [...(committed.applicableProductDetails || []), ...(runtime.applicableProductDetails || [])],
|
|
113
|
+
appliedProductDetails: [...committedAppliedProductDetails, ...(runtime.appliedProductDetails || [])]
|
|
114
|
+
};
|
|
115
|
+
}
|
|
75
116
|
class OrderModule extends _BaseModule.BaseModule {
|
|
76
117
|
defaultName = 'order';
|
|
77
118
|
defaultVersion = '1.0.0';
|
|
@@ -131,7 +172,10 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
131
172
|
original_amount: String(item?.discount?.original_amount ?? item?.amount ?? '0'),
|
|
132
173
|
num: Number(item?.metadata?.num ?? product?.num ?? product?.product_quantity ?? 1) || 1,
|
|
133
174
|
metadata: item?.metadata,
|
|
134
|
-
discount:
|
|
175
|
+
discount: {
|
|
176
|
+
...(item?.discount || {}),
|
|
177
|
+
product_id: item?.discount?.product_id ?? product?.product_id
|
|
178
|
+
},
|
|
135
179
|
_num: Number(item?.metadata?.num ?? product?.num ?? product?.product_quantity ?? 1) || 1
|
|
136
180
|
};
|
|
137
181
|
const existed = discountMap.get(key);
|
|
@@ -463,6 +507,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
463
507
|
with_available_wallet_flag: 1,
|
|
464
508
|
with_available_wallet_pass_flag: 1,
|
|
465
509
|
with_virtual_currency: 1,
|
|
510
|
+
with_entitlement_pass: 1,
|
|
466
511
|
request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
467
512
|
...(orderId ? {
|
|
468
513
|
order_id: orderId
|
|
@@ -479,17 +524,61 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
479
524
|
}
|
|
480
525
|
if (discountList) {
|
|
481
526
|
const currentDiscountList = this.store.discount?.getDiscountList() || [];
|
|
482
|
-
const
|
|
527
|
+
const currentCommittedDiscounts = currentDiscountList.filter(discount => discount?.isEditMode || discount?.committedAppliedProductDetails?.length);
|
|
483
528
|
const currentScanDiscounts = currentDiscountList.filter(discount => discount?.isScan);
|
|
484
|
-
const
|
|
529
|
+
const currentScanDiscountById = new Map(currentScanDiscounts.flatMap(discount => {
|
|
530
|
+
const id = getDiscountIdentityKey(discount);
|
|
531
|
+
return id === null ? [] : [[id, discount]];
|
|
532
|
+
}));
|
|
533
|
+
const committedDiscountById = new Map(currentCommittedDiscounts.flatMap(discount => {
|
|
534
|
+
const id = getDiscountIdentityKey(discount);
|
|
535
|
+
return id === null ? [] : [[id, discount]];
|
|
536
|
+
}));
|
|
537
|
+
const mergedCommittedDiscountIds = new Set();
|
|
538
|
+
const refreshedScanDiscountIds = new Set();
|
|
485
539
|
const runtimeDiscountIds = new Set();
|
|
486
|
-
const runtimeDiscounts = discountList.
|
|
487
|
-
|
|
488
|
-
if (
|
|
489
|
-
runtimeDiscountIds.
|
|
490
|
-
|
|
540
|
+
const runtimeDiscounts = discountList.flatMap(discount => {
|
|
541
|
+
const id = getDiscountIdentityKey(discount);
|
|
542
|
+
if (id === null) return [discount];
|
|
543
|
+
if (runtimeDiscountIds.has(id)) return [];
|
|
544
|
+
runtimeDiscountIds.add(id);
|
|
545
|
+
const currentScanDiscount = currentScanDiscountById.get(id);
|
|
546
|
+
if (currentScanDiscount && (!(0, _entitlementPass.isEntitlementPass)(currentScanDiscount) || !(0, _entitlementPass.isEntitlementPass)(discount))) {
|
|
547
|
+
return [];
|
|
548
|
+
}
|
|
549
|
+
const committedDiscount = committedDiscountById.get(id);
|
|
550
|
+
let nextDiscount = discount;
|
|
551
|
+
if (committedDiscount && (0, _entitlementPass.isEntitlementPass)(committedDiscount) && (0, _entitlementPass.isEntitlementPass)(discount)) {
|
|
552
|
+
mergedCommittedDiscountIds.add(id);
|
|
553
|
+
nextDiscount = mergeCommittedEntitlementWithRuntime(committedDiscount, discount);
|
|
554
|
+
}
|
|
555
|
+
if (currentScanDiscount) {
|
|
556
|
+
refreshedScanDiscountIds.add(id);
|
|
557
|
+
nextDiscount = {
|
|
558
|
+
...nextDiscount,
|
|
559
|
+
isScan: true,
|
|
560
|
+
isSelected: currentScanDiscount.isSelected ?? nextDiscount.isSelected,
|
|
561
|
+
isManualSelect: currentScanDiscount.isManualSelect ?? nextDiscount.isManualSelect
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
return [nextDiscount];
|
|
491
565
|
});
|
|
492
|
-
const
|
|
566
|
+
const retainedCommittedDiscounts = currentCommittedDiscounts.filter(discount => {
|
|
567
|
+
const id = getDiscountIdentityKey(discount);
|
|
568
|
+
return id === null || !mergedCommittedDiscountIds.has(id);
|
|
569
|
+
}).map(discount => discount.committedAppliedProductDetails?.length ? {
|
|
570
|
+
...discount,
|
|
571
|
+
isEditMode: true,
|
|
572
|
+
isDisabled: true,
|
|
573
|
+
isSelected: true,
|
|
574
|
+
isUsed: true,
|
|
575
|
+
appliedProductDetails: [...discount.committedAppliedProductDetails]
|
|
576
|
+
} : discount);
|
|
577
|
+
const retainedScanDiscounts = currentScanDiscounts.filter(discount => {
|
|
578
|
+
const id = getDiscountIdentityKey(discount);
|
|
579
|
+
return id === null || !refreshedScanDiscountIds.has(id);
|
|
580
|
+
});
|
|
581
|
+
const mergedDiscountList = [...retainedCommittedDiscounts, ...retainedScanDiscounts, ...runtimeDiscounts];
|
|
493
582
|
await this.store.discount?.setOriginalDiscountList(mergedDiscountList);
|
|
494
583
|
await this.store.discount?.setDiscountList(mergedDiscountList);
|
|
495
584
|
}
|
|
@@ -1502,7 +1591,8 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1502
1591
|
const sku = product?.product_sku || {};
|
|
1503
1592
|
const metadata = product?.metadata || {};
|
|
1504
1593
|
const discountTypes = (product?.discount_list || []).map(discount => discount?.type ?? discount?.tag);
|
|
1505
|
-
const
|
|
1594
|
+
const fullCoveragePassType = discountTypes.find(type => ['entitlement_pass', 'good_pass'].includes(type));
|
|
1595
|
+
const extensionType = product?.extension_type || fullCoveragePassType || 'normal';
|
|
1506
1596
|
return {
|
|
1507
1597
|
product_id: product?.product_id ?? null,
|
|
1508
1598
|
product_variant_id: product?.product_variant_id ?? null,
|
|
@@ -1555,7 +1645,8 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1555
1645
|
const sku = product?.product_sku || {};
|
|
1556
1646
|
const metadata = product?.metadata || {};
|
|
1557
1647
|
const discountTypes = (product?.discount_list || []).map(discount => discount?.type ?? discount?.tag);
|
|
1558
|
-
const
|
|
1648
|
+
const fullCoveragePassType = discountTypes.find(type => ['entitlement_pass', 'good_pass'].includes(type));
|
|
1649
|
+
const extensionType = product?.extension_type || fullCoveragePassType || 'normal';
|
|
1559
1650
|
return {
|
|
1560
1651
|
product_id: product?.product_id ?? null,
|
|
1561
1652
|
product_variant_id: product?.product_variant_id ?? null,
|
|
@@ -2370,7 +2461,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2370
2461
|
}
|
|
2371
2462
|
isCustomerBoundDiscount(discount) {
|
|
2372
2463
|
const type = discount?.type ?? discount?.tag;
|
|
2373
|
-
return type === 'good_pass' || type === 'discount_card' || type === 'product_discount_card';
|
|
2464
|
+
return type === 'entitlement_pass' || type === 'good_pass' || type === 'discount_card' || type === 'product_discount_card';
|
|
2374
2465
|
}
|
|
2375
2466
|
shouldKeepCustomerBoundDiscountAfterCustomerChange(discount) {
|
|
2376
2467
|
return Boolean(discount?.isScan || discount?.isEditMode);
|
|
@@ -2999,17 +3090,35 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2999
3090
|
}
|
|
3000
3091
|
normalizedIncoming.discount_list = (0, _utils.normalizeOrderProductDiscountList)(normalizedIncoming.discount_list);
|
|
3001
3092
|
const hasIncomingGoodPass = this.hasGoodPassDiscount(normalizedIncoming);
|
|
3093
|
+
const hasIncomingEntitlementPass = this.hasEntitlementPassDiscount(normalizedIncoming);
|
|
3094
|
+
const isIncomingEntitlementAppend = this.isRuntimeEntitlementAppend(productToAdd, tempOrder);
|
|
3095
|
+
const isIncomingPersistedEntitlementPass = hasIncomingEntitlementPass && this.isPersistedProductLine(normalizedIncoming);
|
|
3002
3096
|
const hasIncomingProductNote = this.hasOrderProductLineNote(normalizedIncoming);
|
|
3003
3097
|
const hasIncomingManualProductDiscount = isManualProductDiscountProduct(normalizedIncoming);
|
|
3004
3098
|
const hasIncomingBookingUid = this.hasLinkedBookingUid(productToAdd) || this.hasLinkedBookingUid(normalizedIncoming);
|
|
3005
3099
|
const isIncomingCustomItem = productToAdd.metadata?.origin?.isCustomItem === true || productToAdd._origin?.isCustomItem === true;
|
|
3006
|
-
const shouldForceNewLine = disableMerge || !!booking ||
|
|
3007
|
-
const matchedIndex =
|
|
3100
|
+
const shouldForceNewLine = disableMerge || !!booking || hasIncomingGoodPass || isIncomingPersistedEntitlementPass || hasIncomingBookingUid || isIncomingCustomItem || isWeighingProduct;
|
|
3101
|
+
const matchedIndex = shouldForceNewLine ? -1 : tempOrder.products.findIndex(item => {
|
|
3008
3102
|
if (this.hasGoodPassDiscount(item)) return false;
|
|
3009
|
-
|
|
3010
|
-
if (
|
|
3011
|
-
|
|
3012
|
-
|
|
3103
|
+
const isRuntimeEntitlementPass = this.isRuntimeEntitlementAppend(item, tempOrder);
|
|
3104
|
+
if (isIncomingEntitlementAppend !== isRuntimeEntitlementPass) {
|
|
3105
|
+
return false;
|
|
3106
|
+
}
|
|
3107
|
+
if (isIncomingEntitlementAppend && this.isPersistedProductLine(item)) {
|
|
3108
|
+
return false;
|
|
3109
|
+
}
|
|
3110
|
+
const hasExistingProductNote = this.hasOrderProductLineNote(item);
|
|
3111
|
+
if ((hasIncomingProductNote || hasExistingProductNote) && (!isRuntimeEntitlementPass || String(normalizedIncoming.note || '').trim() !== String(item.note || '').trim())) {
|
|
3112
|
+
return false;
|
|
3113
|
+
}
|
|
3114
|
+
const hasExistingManualProductDiscount = isManualProductDiscountProduct(item);
|
|
3115
|
+
if (isRuntimeEntitlementPass && !this.hasSameEntitlementMergePriceConfig(normalizedIncoming, item)) {
|
|
3116
|
+
return false;
|
|
3117
|
+
}
|
|
3118
|
+
if ((hasIncomingManualProductDiscount || hasExistingManualProductDiscount) && !isRuntimeEntitlementPass) {
|
|
3119
|
+
return false;
|
|
3120
|
+
}
|
|
3121
|
+
if (this.isPersistedProductLine(item)) return false;
|
|
3013
3122
|
if (item.product_id !== productToAdd.product_id) return false;
|
|
3014
3123
|
if (item.product_variant_id !== productToAdd.product_variant_id) return false;
|
|
3015
3124
|
const existedFingerprint = (0, _utils3.buildProductLineFingerprint)((0, _utils.getProductSkuOptions)(item), item.product_bundle);
|
|
@@ -3242,17 +3351,51 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3242
3351
|
});
|
|
3243
3352
|
return tempOrder.products;
|
|
3244
3353
|
}
|
|
3245
|
-
|
|
3246
|
-
const
|
|
3247
|
-
if (hasMainGoodPass) return true;
|
|
3248
|
-
const bundleDiscountList = [];
|
|
3354
|
+
getFullCoveragePassDiscounts(product) {
|
|
3355
|
+
const discountList = [...(product.discount_list || [])];
|
|
3249
3356
|
(product.product_bundle || []).forEach(item => {
|
|
3250
|
-
|
|
3357
|
+
discountList.push(...(item?.discount_list || []));
|
|
3251
3358
|
});
|
|
3252
3359
|
(product?._extend?.other?.bundle || []).forEach(item => {
|
|
3253
|
-
|
|
3360
|
+
discountList.push(...(item?.discount_list || []));
|
|
3254
3361
|
});
|
|
3255
|
-
return
|
|
3362
|
+
return discountList.filter(item => (0, _entitlementPass.isFullCoveragePass)(item));
|
|
3363
|
+
}
|
|
3364
|
+
hasEntitlementPassDiscount(product) {
|
|
3365
|
+
return this.getFullCoveragePassDiscounts(product).some(item => (0, _entitlementPass.isEntitlementPass)(item));
|
|
3366
|
+
}
|
|
3367
|
+
hasGoodPassDiscount(product) {
|
|
3368
|
+
return this.getFullCoveragePassDiscounts(product).some(item => !(0, _entitlementPass.isEntitlementPass)(item));
|
|
3369
|
+
}
|
|
3370
|
+
isPersistedProductLine(product) {
|
|
3371
|
+
return product.order_detail_id !== undefined && product.order_detail_id !== null && product.order_detail_id !== '' || product.metadata?.is_edit_for_runtime === true;
|
|
3372
|
+
}
|
|
3373
|
+
isRuntimeEntitlementAppend(product, tempOrder) {
|
|
3374
|
+
if (this.isPersistedProductLine(product)) return false;
|
|
3375
|
+
const uid = product.metadata?.unique_identification_number ?? product.unique_identification_number;
|
|
3376
|
+
const storedOrigin = uid !== undefined && uid !== null && uid !== '' ? tempOrder._extend?.productsByUid?.[String(uid)]?.origin : undefined;
|
|
3377
|
+
return Boolean(product?._origin?.__entitlementPassRuntimeAppend === true || storedOrigin?.__entitlementPassRuntimeAppend === true);
|
|
3378
|
+
}
|
|
3379
|
+
hasSameEntitlementMergePriceConfig(incoming, existing) {
|
|
3380
|
+
const buildPriceConfig = product => {
|
|
3381
|
+
const metadata = product.metadata || {};
|
|
3382
|
+
const bundle = (product.product_bundle || []).map(item => ({
|
|
3383
|
+
bundle_id: item.bundle_id ?? item.id ?? null,
|
|
3384
|
+
bundle_product_id: item.bundle_product_id ?? item._bundle_product_id ?? item.product_id ?? null,
|
|
3385
|
+
bundle_variant_id: item.bundle_variant_id ?? item.product_variant_id ?? 0,
|
|
3386
|
+
price_type: item.price_type ?? item.custom_price_type ?? '',
|
|
3387
|
+
price_type_ext: item.price_type_ext ?? item.custom_price_type_ext ?? ''
|
|
3388
|
+
})).sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
|
|
3389
|
+
return JSON.stringify({
|
|
3390
|
+
original_price: this.normalizeFingerprintMoney(product.original_price),
|
|
3391
|
+
source_product_price: this.normalizeFingerprintMoney(metadata.source_product_price),
|
|
3392
|
+
main_product_original_price: this.normalizeFingerprintMoney(metadata.main_product_original_price),
|
|
3393
|
+
price_override: this.normalizeFingerprintMoney(metadata.price_override),
|
|
3394
|
+
is_manual_discount: Boolean(metadata.is_manual_discount),
|
|
3395
|
+
bundle
|
|
3396
|
+
});
|
|
3397
|
+
};
|
|
3398
|
+
return buildPriceConfig(incoming) === buildPriceConfig(existing);
|
|
3256
3399
|
}
|
|
3257
3400
|
getBundleRuntimeIdentity(bundle) {
|
|
3258
3401
|
if (!bundle || typeof bundle !== 'object') return '';
|
|
@@ -3441,7 +3584,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3441
3584
|
// normalizeOrderProduct 幂等:v2 metadata 存在 → 保留 main_product_* 折扣重算 composite;
|
|
3442
3585
|
// 否则 → 把顶层 selling_price 视作 source,派生 main_product_* 并写 price_schema_version: 2。
|
|
3443
3586
|
let normalizedProduct = this.normalizeVirtualSettlementProductIfEnabled((0, _utils.mergeOrderProductDisplayFields)(nextProduct, (0, _utils3.normalizeOrderProduct)(nextProduct)));
|
|
3444
|
-
normalizedProduct.discount_list = (0, _utils.normalizeOrderProductDiscountList)(normalizedProduct.discount_list);
|
|
3587
|
+
normalizedProduct.discount_list = (0, _utils.normalizeOrderProductDiscountList)(isPersistedOrderLine ? syncReducedEntitlementPassUsageCount(normalizedProduct.discount_list, targetProduct.num, normalizedProduct.num) : normalizedProduct.discount_list);
|
|
3445
3588
|
const preservedBookingUid = nextProduct.booking_uid || nextProduct.metadata?.booking_uid || targetProduct.booking_uid || targetProduct.metadata?.booking_uid;
|
|
3446
3589
|
if (!booking && preservedBookingUid) {
|
|
3447
3590
|
normalizedProduct.booking_uid = preservedBookingUid;
|
|
@@ -3557,6 +3700,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3557
3700
|
throw new Error('[Order] 目标商品不存在,无法更新数量');
|
|
3558
3701
|
}
|
|
3559
3702
|
const targetProduct = tempOrder.products[productIndex];
|
|
3703
|
+
const isPersistedOrderLine = targetProduct.order_detail_id !== undefined && targetProduct.order_detail_id !== null;
|
|
3560
3704
|
const normalizedProduct = this.normalizeVirtualSettlementProductIfEnabled((0, _utils3.normalizeOrderProduct)({
|
|
3561
3705
|
...targetProduct,
|
|
3562
3706
|
num: (0, _utils3.getSafeProductNum)(num),
|
|
@@ -3565,7 +3709,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3565
3709
|
unique_identification_number: targetProduct.metadata?.unique_identification_number || unique_identification_number
|
|
3566
3710
|
}
|
|
3567
3711
|
}));
|
|
3568
|
-
normalizedProduct.discount_list = (0, _utils.normalizeOrderProductDiscountList)(normalizedProduct.discount_list);
|
|
3712
|
+
normalizedProduct.discount_list = (0, _utils.normalizeOrderProductDiscountList)(isPersistedOrderLine ? syncReducedEntitlementPassUsageCount(normalizedProduct.discount_list, targetProduct.num, normalizedProduct.num) : normalizedProduct.discount_list);
|
|
3569
3713
|
const settlementSnapshot = (0, _virtualSettlement.getSelectedSettlementSnapshot)(normalizedProduct);
|
|
3570
3714
|
if (this.isMultiCurrencySettlementEnabled() && settlementSnapshot?.settlement_type === 'virtual_currency' && tempOrder.customer_id && Number(tempOrder.customer_id) !== 1) {
|
|
3571
3715
|
const currencyProductId = Number(settlementSnapshot.currency_product_id);
|