@pisell/pisellos 0.10.42 → 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/model/strategy/adapter/promotion/index.js +0 -9
- package/dist/modules/Order/index.d.ts +40 -8
- 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/modules/Rules/index.js +2 -1
- 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/types.d.ts +5 -1
- package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +171 -96
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +1 -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/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/model/strategy/adapter/promotion/index.js +1 -46
- package/lib/modules/Order/index.d.ts +40 -8
- 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/modules/Rules/index.js +2 -1
- 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/types.d.ts +5 -1
- package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +65 -18
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +1 -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/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
|
@@ -8,10 +8,9 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
|
|
|
8
8
|
* 现金支付推荐算法
|
|
9
9
|
*
|
|
10
10
|
* 核心原理:
|
|
11
|
-
* 1.
|
|
12
|
-
* 2.
|
|
13
|
-
* 3.
|
|
14
|
-
* 4. 根据组合判断去重,避免扩展组合
|
|
11
|
+
* 1. 第一项始终是精确应付金额
|
|
12
|
+
* 2. 其余推荐只能是严格大于应付金额的真实币种面额
|
|
13
|
+
* 3. 最多推荐三个更高面额
|
|
15
14
|
*/
|
|
16
15
|
|
|
17
16
|
/**
|
|
@@ -47,377 +46,28 @@ export var CURRENCY_DENOMINATIONS = {
|
|
|
47
46
|
'DEFAULT': [100, 50, 20, 10, 5, 1, 0.5, 0.25, 0.1, 0.05, 0.01]
|
|
48
47
|
};
|
|
49
48
|
|
|
50
|
-
/**
|
|
51
|
-
* 支付组合类型定义
|
|
52
|
-
*/
|
|
53
|
-
|
|
54
49
|
/**
|
|
55
50
|
* 最优支付金额推荐算法
|
|
56
|
-
*
|
|
51
|
+
* 返回精确应付金额,以及最多三个更高的真实币种面额
|
|
57
52
|
*
|
|
58
53
|
* @param targetAmount 目标金额
|
|
59
54
|
* @param denominations 币种面值数组
|
|
60
55
|
* @returns 推荐支付金额数组
|
|
61
56
|
*/
|
|
62
57
|
export function recommendOptimalPayments(targetAmount, denominations) {
|
|
63
|
-
|
|
64
|
-
if (targetAmount <= 0 || !isFinite(targetAmount) || isNaN(targetAmount)) {
|
|
58
|
+
if (targetAmount <= 0 || !Number.isFinite(targetAmount)) {
|
|
65
59
|
return [];
|
|
66
60
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return
|
|
75
|
-
}).
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return [Math.ceil(targetAmount)];
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
// 处理精度问题,将金额转换为整数计算
|
|
83
|
-
var precision = 100; // 假设最小单位是0.01
|
|
84
|
-
var target = Math.round(targetAmount * precision);
|
|
85
|
-
var denoms = validDenoms.map(function (d) {
|
|
86
|
-
return Math.round(d * precision);
|
|
87
|
-
}).sort(function (a, b) {
|
|
88
|
-
return b - a;
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// 存储不同的支付组合方案
|
|
92
|
-
var paymentOptions = [];
|
|
93
|
-
|
|
94
|
-
// 首先尝试找到精确匹配的组合
|
|
95
|
-
var exactCombination = findExactCombination(target, denoms, precision);
|
|
96
|
-
if (exactCombination) {
|
|
97
|
-
paymentOptions.push(exactCombination);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// 为了提高效率,我们将按币种数量来生成组合
|
|
101
|
-
// 1币种组合:只用一种面额
|
|
102
|
-
for (var i = 0; i < Math.min(denoms.length, 6); i++) {
|
|
103
|
-
var denom = denoms[i];
|
|
104
|
-
var count = Math.ceil(target / denom);
|
|
105
|
-
var sum = count * denom;
|
|
106
|
-
if (sum >= target && count <= 12) {
|
|
107
|
-
// 限制硬币数量以提高性能
|
|
108
|
-
paymentOptions.push({
|
|
109
|
-
combination: Array(count).fill(denom / precision),
|
|
110
|
-
sum: sum / precision,
|
|
111
|
-
coinCount: count,
|
|
112
|
-
denomTypes: 1
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// 2币种组合:使用两种面额(大幅限制搜索范围以提高性能)
|
|
118
|
-
for (var _i = 0; _i < Math.min(denoms.length - 1, 5); _i++) {
|
|
119
|
-
for (var j = _i + 1; j < Math.min(denoms.length, 6); j++) {
|
|
120
|
-
var denom1 = denoms[_i];
|
|
121
|
-
var denom2 = denoms[j];
|
|
122
|
-
|
|
123
|
-
// 大幅限制搜索范围,基于目标金额动态调整
|
|
124
|
-
var maxCount1 = Math.min(Math.ceil(target / denom1) + 1, 8);
|
|
125
|
-
var maxCount2 = Math.min(Math.ceil(target / denom2) + 1, 10);
|
|
126
|
-
for (var count1 = 1; count1 <= maxCount1; count1++) {
|
|
127
|
-
for (var count2 = 1; count2 <= maxCount2; count2++) {
|
|
128
|
-
var _sum = count1 * denom1 + count2 * denom2;
|
|
129
|
-
if (_sum >= target) {
|
|
130
|
-
var combination = [].concat(_toConsumableArray(Array(count1).fill(denom1 / precision)), _toConsumableArray(Array(count2).fill(denom2 / precision)));
|
|
131
|
-
paymentOptions.push({
|
|
132
|
-
combination: combination,
|
|
133
|
-
sum: _sum / precision,
|
|
134
|
-
coinCount: count1 + count2,
|
|
135
|
-
denomTypes: 2
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// 3币种组合:使用三种面额(大幅限制搜索以提高效率)
|
|
144
|
-
for (var _i2 = 0; _i2 < Math.min(denoms.length - 2, 3); _i2++) {
|
|
145
|
-
for (var _j = _i2 + 1; _j < Math.min(denoms.length - 1, 4); _j++) {
|
|
146
|
-
for (var k = _j + 1; k < Math.min(denoms.length, 5); k++) {
|
|
147
|
-
var _denom = denoms[_i2];
|
|
148
|
-
var _denom2 = denoms[_j];
|
|
149
|
-
var denom3 = denoms[k];
|
|
150
|
-
|
|
151
|
-
// 大幅限制搜索范围
|
|
152
|
-
var _maxCount = Math.min(Math.ceil(target / _denom) + 1, 5);
|
|
153
|
-
var _maxCount2 = Math.min(Math.ceil(target / _denom2) + 1, 6);
|
|
154
|
-
var maxCount3 = Math.min(Math.ceil(target / denom3) + 1, 8);
|
|
155
|
-
for (var _count = 1; _count <= _maxCount; _count++) {
|
|
156
|
-
for (var _count2 = 1; _count2 <= _maxCount2; _count2++) {
|
|
157
|
-
for (var count3 = 1; count3 <= maxCount3; count3++) {
|
|
158
|
-
var _sum2 = _count * _denom + _count2 * _denom2 + count3 * denom3;
|
|
159
|
-
if (_sum2 >= target) {
|
|
160
|
-
var _combination = [].concat(_toConsumableArray(Array(_count).fill(_denom / precision)), _toConsumableArray(Array(_count2).fill(_denom2 / precision)), _toConsumableArray(Array(count3).fill(denom3 / precision)));
|
|
161
|
-
paymentOptions.push({
|
|
162
|
-
combination: _combination,
|
|
163
|
-
sum: _sum2 / precision,
|
|
164
|
-
coinCount: _count + _count2 + count3,
|
|
165
|
-
denomTypes: 3
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// 4币种组合:使用四种面额(极度限制搜索以提高性能)
|
|
176
|
-
for (var _i3 = 0; _i3 < Math.min(denoms.length - 3, 2); _i3++) {
|
|
177
|
-
for (var _j2 = _i3 + 1; _j2 < Math.min(denoms.length - 2, 3); _j2++) {
|
|
178
|
-
for (var _k = _j2 + 1; _k < Math.min(denoms.length - 1, 4); _k++) {
|
|
179
|
-
for (var l = _k + 1; l < Math.min(denoms.length, 5); l++) {
|
|
180
|
-
var _denom3 = denoms[_i3];
|
|
181
|
-
var _denom4 = denoms[_j2];
|
|
182
|
-
var _denom5 = denoms[_k];
|
|
183
|
-
var denom4 = denoms[l];
|
|
184
|
-
|
|
185
|
-
// 极度限制搜索范围
|
|
186
|
-
var _maxCount3 = Math.min(Math.ceil(target / _denom3) + 1, 3);
|
|
187
|
-
var _maxCount4 = Math.min(Math.ceil(target / _denom4) + 1, 4);
|
|
188
|
-
var _maxCount5 = Math.min(Math.ceil(target / _denom5) + 1, 5);
|
|
189
|
-
var maxCount4 = Math.min(Math.ceil(target / denom4) + 1, 6);
|
|
190
|
-
for (var _count3 = 1; _count3 <= _maxCount3; _count3++) {
|
|
191
|
-
for (var _count4 = 1; _count4 <= _maxCount4; _count4++) {
|
|
192
|
-
for (var _count5 = 1; _count5 <= _maxCount5; _count5++) {
|
|
193
|
-
for (var count4 = 1; count4 <= maxCount4; count4++) {
|
|
194
|
-
var _sum3 = _count3 * _denom3 + _count4 * _denom4 + _count5 * _denom5 + count4 * denom4;
|
|
195
|
-
if (_sum3 >= target) {
|
|
196
|
-
var _combination2 = [].concat(_toConsumableArray(Array(_count3).fill(_denom3 / precision)), _toConsumableArray(Array(_count4).fill(_denom4 / precision)), _toConsumableArray(Array(_count5).fill(_denom5 / precision)), _toConsumableArray(Array(count4).fill(denom4 / precision)));
|
|
197
|
-
paymentOptions.push({
|
|
198
|
-
combination: _combination2,
|
|
199
|
-
sum: _sum3 / precision,
|
|
200
|
-
coinCount: _count3 + _count4 + _count5 + count4,
|
|
201
|
-
denomTypes: 4
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// 如果没有找到任何组合,提供默认建议
|
|
214
|
-
if (paymentOptions.length === 0) {
|
|
215
|
-
// 找到大于等于目标金额的最小面额
|
|
216
|
-
var minValidDenom = validDenoms.find(function (denom) {
|
|
217
|
-
return denom >= targetAmount;
|
|
218
|
-
});
|
|
219
|
-
if (minValidDenom) {
|
|
220
|
-
return [minValidDenom];
|
|
221
|
-
} else {
|
|
222
|
-
// 使用最大面额的组合
|
|
223
|
-
var maxDenom = Math.max.apply(Math, _toConsumableArray(validDenoms));
|
|
224
|
-
var _count6 = Math.ceil(targetAmount / maxDenom);
|
|
225
|
-
return [_count6 * maxDenom];
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// 移除重复和扩展组合
|
|
230
|
-
var uniqueCombinations = removeDuplicateAndExtendedCombinations(paymentOptions, targetAmount);
|
|
231
|
-
|
|
232
|
-
// 按币种类型数量排序,然后按总硬币数量排序,最后按金额排序
|
|
233
|
-
uniqueCombinations.sort(function (a, b) {
|
|
234
|
-
if (a.denomTypes !== b.denomTypes) {
|
|
235
|
-
return a.denomTypes - b.denomTypes;
|
|
236
|
-
}
|
|
237
|
-
if (a.coinCount !== b.coinCount) {
|
|
238
|
-
return a.coinCount - b.coinCount;
|
|
239
|
-
}
|
|
240
|
-
return a.sum - b.sum;
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
// 按金额去重并返回推荐的支付金额
|
|
244
|
-
var uniqueAmounts = new Set();
|
|
245
|
-
var finalResults = [];
|
|
246
|
-
for (var _i4 = 0, _uniqueCombinations = uniqueCombinations; _i4 < _uniqueCombinations.length; _i4++) {
|
|
247
|
-
var item = _uniqueCombinations[_i4];
|
|
248
|
-
var roundedAmount = Math.round(item.sum * 100) / 100; // 处理浮点数精度
|
|
249
|
-
if (!uniqueAmounts.has(roundedAmount) && finalResults.length < 10) {
|
|
250
|
-
uniqueAmounts.add(roundedAmount);
|
|
251
|
-
finalResults.push(roundedAmount);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
return finalResults.sort(function (a, b) {
|
|
255
|
-
return a - b;
|
|
256
|
-
});
|
|
257
|
-
} catch (error) {
|
|
258
|
-
// 发生错误时返回安全的默认值
|
|
259
|
-
console.warn('推荐支付金额计算出错:', error);
|
|
260
|
-
// 返回最接近的整数金额作为兜底
|
|
261
|
-
var safeAmount = Math.ceil(targetAmount);
|
|
262
|
-
return [targetAmount, safeAmount];
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* 尝试找到精确匹配目标金额的组合
|
|
268
|
-
*/
|
|
269
|
-
function findExactCombination(target, denoms, precision) {
|
|
270
|
-
// 简单的深度优先搜索,限制搜索深度
|
|
271
|
-
function dfs(remaining, denomIndex, currentCombination) {
|
|
272
|
-
if (remaining === 0) {
|
|
273
|
-
return currentCombination;
|
|
274
|
-
}
|
|
275
|
-
if (remaining < 0 || denomIndex >= denoms.length || currentCombination.length > 10) {
|
|
276
|
-
return null;
|
|
277
|
-
}
|
|
278
|
-
var denom = denoms[denomIndex];
|
|
279
|
-
|
|
280
|
-
// 尝试使用当前面额 0 到 maxCount 次
|
|
281
|
-
var maxCount = Math.min(Math.floor(remaining / denom), 8);
|
|
282
|
-
for (var count = maxCount; count >= 0; count--) {
|
|
283
|
-
var newCombination = [].concat(_toConsumableArray(currentCombination), _toConsumableArray(Array(count).fill(denom)));
|
|
284
|
-
var result = dfs(remaining - count * denom, denomIndex + 1, newCombination);
|
|
285
|
-
if (result) {
|
|
286
|
-
return result;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return null;
|
|
290
|
-
}
|
|
291
|
-
var exactMatch = dfs(target, 0, []);
|
|
292
|
-
if (exactMatch && exactMatch.length > 0) {
|
|
293
|
-
var denomTypes = new Set(exactMatch).size;
|
|
294
|
-
return {
|
|
295
|
-
combination: exactMatch.map(function (d) {
|
|
296
|
-
return d / precision;
|
|
297
|
-
}),
|
|
298
|
-
sum: target / precision,
|
|
299
|
-
coinCount: exactMatch.length,
|
|
300
|
-
denomTypes: denomTypes
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
return null;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* 移除重复和扩展组合
|
|
308
|
-
* 核心原理:如果组合A包含组合B的所有硬币,并且还有额外硬币,则A是B的扩展,应该被移除
|
|
309
|
-
*/
|
|
310
|
-
function removeDuplicateAndExtendedCombinations(combinations, targetAmount) {
|
|
311
|
-
var result = [];
|
|
312
|
-
|
|
313
|
-
// 按币种类型数量、硬币数量、金额排序,确保较简单的组合在前面
|
|
314
|
-
combinations.sort(function (a, b) {
|
|
315
|
-
if (a.denomTypes !== b.denomTypes) {
|
|
316
|
-
return a.denomTypes - b.denomTypes;
|
|
317
|
-
}
|
|
318
|
-
if (a.coinCount !== b.coinCount) {
|
|
319
|
-
return a.coinCount - b.coinCount;
|
|
320
|
-
}
|
|
321
|
-
return a.sum - b.sum;
|
|
322
|
-
});
|
|
323
|
-
for (var i = 0; i < combinations.length; i++) {
|
|
324
|
-
var current = combinations[i];
|
|
325
|
-
var shouldSkip = false;
|
|
326
|
-
|
|
327
|
-
// 检查当前组合是否与已添加的组合重复或者是扩展
|
|
328
|
-
for (var j = 0; j < result.length; j++) {
|
|
329
|
-
var existing = result[j];
|
|
330
|
-
|
|
331
|
-
// 如果完全相同,跳过
|
|
332
|
-
if (isSameCombination(current.combination, existing.combination)) {
|
|
333
|
-
shouldSkip = true;
|
|
334
|
-
break;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// 如果当前组合是已存在组合的扩展,跳过
|
|
338
|
-
if (isExtensionOf(current.combination, existing.combination)) {
|
|
339
|
-
shouldSkip = true;
|
|
340
|
-
break;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// 检查是否有其他组合是当前组合的扩展,如果有,移除那些扩展
|
|
345
|
-
if (!shouldSkip) {
|
|
346
|
-
// 移除所有当前组合的扩展
|
|
347
|
-
for (var _j3 = result.length - 1; _j3 >= 0; _j3--) {
|
|
348
|
-
if (isExtensionOf(result[_j3].combination, current.combination)) {
|
|
349
|
-
result.splice(_j3, 1);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
result.push(current);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
return result;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* 检查组合A是否是组合B的扩展(即A包含B的所有硬币,并且还有额外的硬币)
|
|
360
|
-
*/
|
|
361
|
-
function isExtensionOf(combinationA, combinationB) {
|
|
362
|
-
// 如果A的硬币数量少于等于B,A不可能是B的扩展
|
|
363
|
-
if (combinationA.length <= combinationB.length) {
|
|
364
|
-
return false;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// 统计每个面额的数量
|
|
368
|
-
var countA = {};
|
|
369
|
-
var countB = {};
|
|
370
|
-
combinationA.forEach(function (coin) {
|
|
371
|
-
countA[coin] = (countA[coin] || 0) + 1;
|
|
372
|
-
});
|
|
373
|
-
combinationB.forEach(function (coin) {
|
|
374
|
-
countB[coin] = (countB[coin] || 0) + 1;
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
// 检查B的所有面额在A中是否都有足够的数量
|
|
378
|
-
for (var coin in countB) {
|
|
379
|
-
if (!countA[coin] || countA[coin] < countB[coin]) {
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// 检查A是否有B没有的额外硬币
|
|
385
|
-
var hasExtra = false;
|
|
386
|
-
for (var _coin in countA) {
|
|
387
|
-
if (countA[_coin] > (countB[_coin] || 0)) {
|
|
388
|
-
hasExtra = true;
|
|
389
|
-
break;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
return hasExtra;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* 检查两个组合是否本质相同(相同的面额组合)
|
|
397
|
-
*/
|
|
398
|
-
function isSameCombination(combinationA, combinationB) {
|
|
399
|
-
if (combinationA.length !== combinationB.length) {
|
|
400
|
-
return false;
|
|
401
|
-
}
|
|
402
|
-
var countA = {};
|
|
403
|
-
var countB = {};
|
|
404
|
-
combinationA.forEach(function (coin) {
|
|
405
|
-
countA[coin] = (countA[coin] || 0) + 1;
|
|
406
|
-
});
|
|
407
|
-
combinationB.forEach(function (coin) {
|
|
408
|
-
countB[coin] = (countB[coin] || 0) + 1;
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
// 检查两个组合是否有相同的面额和数量
|
|
412
|
-
for (var coin in countA) {
|
|
413
|
-
if (countA[coin] !== (countB[coin] || 0)) {
|
|
414
|
-
return false;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
for (var _coin2 in countB) {
|
|
418
|
-
if (countB[_coin2] !== (countA[_coin2] || 0)) {
|
|
419
|
-
return false;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
return true;
|
|
61
|
+
var precision = 100;
|
|
62
|
+
var normalizedTarget = Math.round(targetAmount * precision) / precision;
|
|
63
|
+
var higherDenominations = _toConsumableArray(new Set((Array.isArray(denominations) ? denominations : []).filter(function (denomination) {
|
|
64
|
+
return denomination > 0 && Number.isFinite(denomination);
|
|
65
|
+
}).map(function (denomination) {
|
|
66
|
+
return Math.round(denomination * precision) / precision;
|
|
67
|
+
}))).filter(function (denomination) {
|
|
68
|
+
return denomination > normalizedTarget;
|
|
69
|
+
}).sort(function (a, b) {
|
|
70
|
+
return a - b;
|
|
71
|
+
}).slice(0, 3);
|
|
72
|
+
return [normalizedTarget].concat(_toConsumableArray(higherDenominations));
|
|
423
73
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Module, PisellCore } from '../../types';
|
|
2
2
|
import { BaseModule } from '../BaseModule';
|
|
3
|
-
import { ProductListLoadProductsParams } from './types';
|
|
3
|
+
import { ProductListLoadProductsParams, ProductListStoreQueryParams } from './types';
|
|
4
4
|
import { ProductData } from '../Product/types';
|
|
5
5
|
export * from './types';
|
|
6
6
|
export declare class ProductList extends BaseModule implements Module {
|
|
@@ -52,6 +52,7 @@ export declare class ProductList extends BaseModule implements Module {
|
|
|
52
52
|
getRawProductPriceSources(ids: number[]): Map<number, ProductData>;
|
|
53
53
|
getProducts(): Promise<ProductData[]>;
|
|
54
54
|
getProduct(id: number): Promise<ProductData | undefined>;
|
|
55
|
+
queryProducts(params?: ProductListStoreQueryParams): Promise<ProductData[]>;
|
|
55
56
|
getProductByIds(ids: number[]): Promise<ProductData[] | undefined>;
|
|
56
57
|
addProduct(products: ProductData[]): Promise<void>;
|
|
57
58
|
selectProducts(products: ProductData[]): Promise<void>;
|
|
@@ -29,6 +29,19 @@ import dayjs from 'dayjs';
|
|
|
29
29
|
import { isProductQueryHandledByOsServer, PRODUCT_QUERY_DATA_VARIANT_RELATIONS, resolveClientDataVariants } from "./clientDataVariants";
|
|
30
30
|
import { isCustomerUserPlatform } from "../../utils/platform";
|
|
31
31
|
export * from "./types";
|
|
32
|
+
function normalizeQuerySet(values) {
|
|
33
|
+
if (!Array.isArray(values) || values.length === 0) return null;
|
|
34
|
+
var normalized = values.map(function (value) {
|
|
35
|
+
return String(value !== null && value !== void 0 ? value : '').trim();
|
|
36
|
+
}).filter(Boolean);
|
|
37
|
+
return normalized.length ? new Set(normalized) : null;
|
|
38
|
+
}
|
|
39
|
+
function matchesAny(values, querySet) {
|
|
40
|
+
if (!querySet) return true;
|
|
41
|
+
return values.some(function (value) {
|
|
42
|
+
return querySet.has(String(value !== null && value !== void 0 ? value : '').trim());
|
|
43
|
+
});
|
|
44
|
+
}
|
|
32
45
|
export var ProductList = /*#__PURE__*/function (_BaseModule) {
|
|
33
46
|
_inherits(ProductList, _BaseModule);
|
|
34
47
|
var _super = _createSuper(ProductList);
|
|
@@ -468,26 +481,93 @@ export var ProductList = /*#__PURE__*/function (_BaseModule) {
|
|
|
468
481
|
return getProduct;
|
|
469
482
|
}()
|
|
470
483
|
}, {
|
|
471
|
-
key: "
|
|
484
|
+
key: "queryProducts",
|
|
472
485
|
value: function () {
|
|
473
|
-
var
|
|
474
|
-
var
|
|
486
|
+
var _queryProducts = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee9() {
|
|
487
|
+
var params,
|
|
488
|
+
includeIds,
|
|
489
|
+
excludeIds,
|
|
490
|
+
categoryIds,
|
|
491
|
+
collectionIds,
|
|
492
|
+
extensionTypes,
|
|
493
|
+
statuses,
|
|
494
|
+
channelApplications,
|
|
495
|
+
codeOrBarcode,
|
|
496
|
+
products,
|
|
497
|
+
_args9 = arguments;
|
|
475
498
|
return _regeneratorRuntime().wrap(function _callee9$(_context9) {
|
|
476
499
|
while (1) switch (_context9.prev = _context9.next) {
|
|
477
500
|
case 0:
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
501
|
+
params = _args9.length > 0 && _args9[0] !== undefined ? _args9[0] : {};
|
|
502
|
+
includeIds = normalizeQuerySet(params.includeIds);
|
|
503
|
+
excludeIds = normalizeQuerySet(params.excludeIds);
|
|
504
|
+
categoryIds = normalizeQuerySet(params.categoryIds);
|
|
505
|
+
collectionIds = normalizeQuerySet(params.collectionIds);
|
|
506
|
+
extensionTypes = normalizeQuerySet(params.extensionTypes);
|
|
507
|
+
statuses = normalizeQuerySet(params.statuses);
|
|
508
|
+
channelApplications = normalizeQuerySet(params.channelApplications);
|
|
509
|
+
codeOrBarcode = typeof params.codeOrBarcode === 'string' ? params.codeOrBarcode.trim() : '';
|
|
510
|
+
products = (this.store.list || []).filter(function (product) {
|
|
511
|
+
var _product$id, _product$extension_ty, _product$status;
|
|
512
|
+
var productId = String((_product$id = product.id) !== null && _product$id !== void 0 ? _product$id : '').trim();
|
|
513
|
+
if (includeIds && !includeIds.has(productId)) return false;
|
|
514
|
+
if (excludeIds && excludeIds.has(productId)) return false;
|
|
515
|
+
if (categoryIds) {
|
|
516
|
+
var productCategoryIds = (product.categories || []).flatMap(function (category) {
|
|
517
|
+
var _category$pivot;
|
|
518
|
+
return [category === null || category === void 0 ? void 0 : category.id, category === null || category === void 0 || (_category$pivot = category.pivot) === null || _category$pivot === void 0 ? void 0 : _category$pivot.product_category_id];
|
|
519
|
+
});
|
|
520
|
+
if (!matchesAny(productCategoryIds, categoryIds)) return false;
|
|
521
|
+
}
|
|
522
|
+
if (collectionIds) {
|
|
523
|
+
var productCollectionIds = (product.collection || []).map(function (collection) {
|
|
524
|
+
return collection === null || collection === void 0 ? void 0 : collection.id;
|
|
525
|
+
});
|
|
526
|
+
if (!matchesAny(productCollectionIds, collectionIds)) return false;
|
|
527
|
+
}
|
|
528
|
+
if (extensionTypes && !extensionTypes.has(String((_product$extension_ty = product.extension_type) !== null && _product$extension_ty !== void 0 ? _product$extension_ty : '').trim())) return false;
|
|
529
|
+
if (statuses && !statuses.has(String((_product$status = product.status) !== null && _product$status !== void 0 ? _product$status : '').trim())) return false;
|
|
530
|
+
if (channelApplications) {
|
|
531
|
+
if (!matchesAny(product.channel_application || [], channelApplications)) return false;
|
|
532
|
+
}
|
|
533
|
+
if (codeOrBarcode) {
|
|
534
|
+
var code = typeof product.code === 'string' ? product.code.trim() : '';
|
|
535
|
+
var barcode = typeof product.barcode === 'string' ? product.barcode.trim() : '';
|
|
536
|
+
if (code !== codeOrBarcode && barcode !== codeOrBarcode) return false;
|
|
537
|
+
}
|
|
538
|
+
return true;
|
|
483
539
|
});
|
|
484
|
-
return _context9.abrupt("return", products);
|
|
485
|
-
case
|
|
540
|
+
return _context9.abrupt("return", cloneDeep(products));
|
|
541
|
+
case 11:
|
|
486
542
|
case "end":
|
|
487
543
|
return _context9.stop();
|
|
488
544
|
}
|
|
489
545
|
}, _callee9, this);
|
|
490
546
|
}));
|
|
547
|
+
function queryProducts() {
|
|
548
|
+
return _queryProducts.apply(this, arguments);
|
|
549
|
+
}
|
|
550
|
+
return queryProducts;
|
|
551
|
+
}()
|
|
552
|
+
}, {
|
|
553
|
+
key: "getProductByIds",
|
|
554
|
+
value: function () {
|
|
555
|
+
var _getProductByIds = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee10(ids) {
|
|
556
|
+
return _regeneratorRuntime().wrap(function _callee10$(_context10) {
|
|
557
|
+
while (1) switch (_context10.prev = _context10.next) {
|
|
558
|
+
case 0:
|
|
559
|
+
_context10.next = 2;
|
|
560
|
+
return this.core.effects.emit("".concat(this.name, ":onGetProductByIds"), this.store.list);
|
|
561
|
+
case 2:
|
|
562
|
+
return _context10.abrupt("return", this.queryProducts({
|
|
563
|
+
includeIds: ids
|
|
564
|
+
}));
|
|
565
|
+
case 3:
|
|
566
|
+
case "end":
|
|
567
|
+
return _context10.stop();
|
|
568
|
+
}
|
|
569
|
+
}, _callee10, this);
|
|
570
|
+
}));
|
|
491
571
|
function getProductByIds(_x8) {
|
|
492
572
|
return _getProductByIds.apply(this, arguments);
|
|
493
573
|
}
|
|
@@ -496,10 +576,10 @@ export var ProductList = /*#__PURE__*/function (_BaseModule) {
|
|
|
496
576
|
}, {
|
|
497
577
|
key: "addProduct",
|
|
498
578
|
value: function () {
|
|
499
|
-
var _addProduct = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function
|
|
579
|
+
var _addProduct = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee11(products) {
|
|
500
580
|
var _this6 = this;
|
|
501
|
-
return _regeneratorRuntime().wrap(function
|
|
502
|
-
while (1) switch (
|
|
581
|
+
return _regeneratorRuntime().wrap(function _callee11$(_context11) {
|
|
582
|
+
while (1) switch (_context11.prev = _context11.next) {
|
|
503
583
|
case 0:
|
|
504
584
|
// list 需要根据 id 去重
|
|
505
585
|
if (!this.store.list) {
|
|
@@ -522,9 +602,9 @@ export var ProductList = /*#__PURE__*/function (_BaseModule) {
|
|
|
522
602
|
this.core.effects.emit("".concat(this.name, ":changed"), this.store.list);
|
|
523
603
|
case 4:
|
|
524
604
|
case "end":
|
|
525
|
-
return
|
|
605
|
+
return _context11.stop();
|
|
526
606
|
}
|
|
527
|
-
},
|
|
607
|
+
}, _callee11, this);
|
|
528
608
|
}));
|
|
529
609
|
function addProduct(_x9) {
|
|
530
610
|
return _addProduct.apply(this, arguments);
|
|
@@ -534,16 +614,16 @@ export var ProductList = /*#__PURE__*/function (_BaseModule) {
|
|
|
534
614
|
}, {
|
|
535
615
|
key: "selectProducts",
|
|
536
616
|
value: function () {
|
|
537
|
-
var _selectProducts = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function
|
|
538
|
-
return _regeneratorRuntime().wrap(function
|
|
539
|
-
while (1) switch (
|
|
617
|
+
var _selectProducts = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee12(products) {
|
|
618
|
+
return _regeneratorRuntime().wrap(function _callee12$(_context12) {
|
|
619
|
+
while (1) switch (_context12.prev = _context12.next) {
|
|
540
620
|
case 0:
|
|
541
621
|
this.store.selectProducts = products;
|
|
542
622
|
case 1:
|
|
543
623
|
case "end":
|
|
544
|
-
return
|
|
624
|
+
return _context12.stop();
|
|
545
625
|
}
|
|
546
|
-
},
|
|
626
|
+
}, _callee12, this);
|
|
547
627
|
}));
|
|
548
628
|
function selectProducts(_x10) {
|
|
549
629
|
return _selectProducts.apply(this, arguments);
|
|
@@ -15,6 +15,16 @@ export interface ProductListLoadProductsParams {
|
|
|
15
15
|
/** 智能定价条件上下文,仅影响策略命中,不参与商品集合筛选 */
|
|
16
16
|
strategy_context?: Record<string, any>;
|
|
17
17
|
}
|
|
18
|
+
export interface ProductListStoreQueryParams {
|
|
19
|
+
includeIds?: Array<number | string>;
|
|
20
|
+
excludeIds?: Array<number | string>;
|
|
21
|
+
categoryIds?: Array<number | string>;
|
|
22
|
+
collectionIds?: Array<number | string>;
|
|
23
|
+
extensionTypes?: string[];
|
|
24
|
+
statuses?: ProductData['status'][];
|
|
25
|
+
channelApplications?: string[];
|
|
26
|
+
codeOrBarcode?: string;
|
|
27
|
+
}
|
|
18
28
|
export interface ProductListData {
|
|
19
29
|
list: ProductData[];
|
|
20
30
|
selectProducts: ProductData[];
|
|
@@ -1813,7 +1813,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
|
|
|
1813
1813
|
|
|
1814
1814
|
// 计算折扣后的价格(使用 getDiscountAmount,内部已处理 maxDeductionAmount)
|
|
1815
1815
|
var discountedPrice = getDiscountAmount(_selectedDiscount2, product.origin_total, product.origin_total);
|
|
1816
|
-
var
|
|
1816
|
+
var bundleDiscountAmountPerItem = new Decimal(product.origin_total || 0).minus(discountedPrice).toNumber();
|
|
1817
|
+
var bundleDiscountAmount = new Decimal(bundleDiscountAmountPerItem).mul(_appliedUseCount).toNumber();
|
|
1817
1818
|
debugger;
|
|
1818
1819
|
var _discountDetail = {
|
|
1819
1820
|
amount: bundleDiscountAmount,
|
package/dist/server/index.d.ts
CHANGED
|
@@ -421,6 +421,7 @@ declare class Server {
|
|
|
421
421
|
private shouldUseLocalOrderQuery;
|
|
422
422
|
/**
|
|
423
423
|
* 预约查询是否走本地:
|
|
424
|
+
* - 日历周/月/资源视图通过 compact_fields 显式标识,始终走远端;
|
|
424
425
|
* - 当 sales_time_between 的开始和结束都不是今天时,走远端;
|
|
425
426
|
* - 其余情况走本地。
|
|
426
427
|
*/
|
|
@@ -524,6 +525,12 @@ declare class Server {
|
|
|
524
525
|
private stringifyCheckoutResourceIdentifier;
|
|
525
526
|
private normalizeCheckoutSubmitData;
|
|
526
527
|
private ensureCheckoutOrderNumbers;
|
|
528
|
+
/**
|
|
529
|
+
* Notes 允许在本地草稿阶段暂存空订单目标;checkout 的统一出口负责用最终
|
|
530
|
+
* shop_order_number 完成绑定并校验,保证后端请求、pending、设备任务和打印
|
|
531
|
+
* 都使用同一份完整快照。
|
|
532
|
+
*/
|
|
533
|
+
private finalizeCheckoutSalesNotes;
|
|
527
534
|
private dispatchCheckoutSyncTask;
|
|
528
535
|
private dispatchPrintOtherReceiptTask;
|
|
529
536
|
private buildPrintOtherReceiptIdempotencyKey;
|