@pisell/pisellos 0.10.49 → 0.10.51
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/Discount/types.d.ts +2 -0
- package/dist/modules/FulfillmentTiming/index.d.ts +11 -0
- package/dist/modules/FulfillmentTiming/index.js +407 -0
- package/dist/modules/FulfillmentTiming/types.d.ts +67 -0
- package/dist/modules/FulfillmentTiming/types.js +1 -0
- package/dist/modules/OpenData/types.d.ts +6 -0
- package/dist/modules/Order/index.d.ts +19 -1
- package/dist/modules/Order/index.js +362 -217
- package/dist/modules/Order/pickupTiming.d.ts +46 -0
- package/dist/modules/Order/pickupTiming.js +53 -0
- package/dist/modules/Order/types.d.ts +11 -1
- package/dist/modules/Order/utils.d.ts +10 -0
- package/dist/modules/Order/utils.js +145 -85
- package/dist/modules/Rules/index.js +45 -26
- package/dist/modules/Rules/types.d.ts +4 -0
- package/dist/modules/index.d.ts +1 -0
- package/dist/modules/index.js +1 -0
- package/dist/server/modules/order/index.d.ts +8 -0
- package/dist/server/modules/order/index.js +506 -453
- package/dist/server/modules/order/types.d.ts +1 -0
- package/dist/server/utils/small-ticket.js +173 -95
- package/dist/solution/BaseSales/index.d.ts +23 -0
- package/dist/solution/BaseSales/index.js +1216 -1031
- package/dist/solution/BaseSales/pickupTiming.d.ts +37 -0
- package/dist/solution/BaseSales/pickupTiming.js +482 -0
- package/dist/solution/BaseSales/utils/parseSalesResponse.js +3 -0
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/utils/cartView.js +3 -1
- package/dist/solution/UnifiedBookingSales/index.d.ts +2 -0
- package/dist/solution/UnifiedBookingSales/index.js +583 -531
- package/lib/model/strategy/adapter/promotion/index.js +46 -1
- package/lib/modules/Discount/types.d.ts +2 -0
- package/lib/modules/FulfillmentTiming/index.d.ts +11 -0
- package/lib/modules/FulfillmentTiming/index.js +359 -0
- package/lib/modules/FulfillmentTiming/types.d.ts +67 -0
- package/lib/modules/FulfillmentTiming/types.js +5 -0
- package/lib/modules/OpenData/types.d.ts +6 -0
- package/lib/modules/Order/index.d.ts +19 -1
- package/lib/modules/Order/index.js +120 -7
- package/lib/modules/Order/pickupTiming.d.ts +46 -0
- package/lib/modules/Order/pickupTiming.js +61 -0
- package/lib/modules/Order/types.d.ts +11 -1
- package/lib/modules/Order/utils.d.ts +10 -0
- package/lib/modules/Order/utils.js +58 -4
- package/lib/modules/Rules/index.js +24 -13
- package/lib/modules/Rules/types.d.ts +4 -0
- package/lib/modules/index.d.ts +1 -0
- package/lib/modules/index.js +11 -0
- package/lib/server/modules/order/index.d.ts +8 -0
- package/lib/server/modules/order/index.js +19 -0
- package/lib/server/modules/order/types.d.ts +1 -0
- package/lib/server/utils/small-ticket.js +93 -22
- package/lib/solution/BaseSales/index.d.ts +23 -0
- package/lib/solution/BaseSales/index.js +104 -1
- package/lib/solution/BaseSales/pickupTiming.d.ts +37 -0
- package/lib/solution/BaseSales/pickupTiming.js +222 -0
- package/lib/solution/BaseSales/utils/parseSalesResponse.js +3 -0
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/utils/cartView.js +3 -1
- package/lib/solution/UnifiedBookingSales/index.d.ts +2 -0
- package/lib/solution/UnifiedBookingSales/index.js +20 -0
- package/package.json +1 -1
|
@@ -4,10 +4,17 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.OrderModule = void 0;
|
|
7
|
+
Object.defineProperty(exports, "isFulfillmentTimingBooking", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () {
|
|
10
|
+
return _pickupTiming.isFulfillmentTimingBooking;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
7
13
|
var _BaseModule = require("../BaseModule");
|
|
8
14
|
var _types = require("./types");
|
|
9
15
|
var _utils = require("./utils");
|
|
10
16
|
var _utils2 = require("../Product/utils");
|
|
17
|
+
var _pickupTiming = require("./pickupTiming");
|
|
11
18
|
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
12
19
|
var _availability = require("../Discount/availability");
|
|
13
20
|
var _osPrivatePayload = require("../../utils/os-private-payload");
|
|
@@ -130,6 +137,8 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
130
137
|
sessionStorageRestoreRecord = null;
|
|
131
138
|
/** 只允许当前订单最新的 prepare/config 请求回写客户资产态。 */
|
|
132
139
|
customerWalletPrepareConfigEpoch = 0;
|
|
140
|
+
/** Payment 可能早于 prepare/config 返回;先按实例 ID 暂存卡号等待 Snapshot。 */
|
|
141
|
+
pointCardSnapshotCodeCandidates = new Map();
|
|
133
142
|
salesSummaryModuleName;
|
|
134
143
|
rulesHooksOverride;
|
|
135
144
|
moduleHooksOverride;
|
|
@@ -168,6 +177,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
168
177
|
const key = Number.isFinite(id) ? id : String(resourceId);
|
|
169
178
|
const title = item?.discount?.title ?? {};
|
|
170
179
|
const amount = Number(item?.amount || 0);
|
|
180
|
+
const encoded = item?.metadata?.encoded || item?.encoded || item?.discount?.encoded || '';
|
|
171
181
|
const discountCardType = item?.discount?.discount_card_type;
|
|
172
182
|
const parValue = isDiscountCard ? discountCardType === 'fixed_amount' ? item?.discount?.fixed_amount ?? item?.amount : item?.discount?.percent : '0';
|
|
173
183
|
const detail = {
|
|
@@ -186,6 +196,13 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
186
196
|
};
|
|
187
197
|
const existed = discountMap.get(key);
|
|
188
198
|
if (existed) {
|
|
199
|
+
if (!existed.encoded && encoded) {
|
|
200
|
+
existed.encoded = encoded;
|
|
201
|
+
existed.metadata = {
|
|
202
|
+
...existed.metadata,
|
|
203
|
+
encoded
|
|
204
|
+
};
|
|
205
|
+
}
|
|
189
206
|
existed.amount = Number(existed.amount || 0) + amount;
|
|
190
207
|
existed.used_par_value = String(Number(existed.used_par_value || 0) + amount);
|
|
191
208
|
if (!isDiscountCard) {
|
|
@@ -205,8 +222,11 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
205
222
|
discountMap.set(key, {
|
|
206
223
|
id: key,
|
|
207
224
|
product_name: '',
|
|
208
|
-
encoded
|
|
209
|
-
code: '',
|
|
225
|
+
encoded,
|
|
226
|
+
code: item?.code || item?.discount?.code || '',
|
|
227
|
+
metadata: {
|
|
228
|
+
encoded
|
|
229
|
+
},
|
|
210
230
|
tag: type,
|
|
211
231
|
product_id: item?.discount?.discount_product_id ?? product?.product_id ?? 0,
|
|
212
232
|
relation_type: '',
|
|
@@ -728,7 +748,10 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
728
748
|
let resolvedDiscountList = normalizedNewDiscountList || this.getDiscountList();
|
|
729
749
|
if (isAvailable && normalizedNewDiscountList) {
|
|
730
750
|
await this.store.discount?.setDiscountList(normalizedNewDiscountList);
|
|
731
|
-
|
|
751
|
+
// 实际写回商品时沿用扫码试算的优先级,避免旧商品行的选卡覆盖本次扫码。
|
|
752
|
+
this.applyDiscount({
|
|
753
|
+
preferredEntitlementPassIds: withScanList.filter(discount => (0, _entitlementPass.isEntitlementPass)(discount)).map(discount => discount.id)
|
|
754
|
+
});
|
|
732
755
|
await this.syncScannedDiscountsToOriginalDiscountList(this.getDiscountList());
|
|
733
756
|
resolvedDiscountList = this.getDiscountList();
|
|
734
757
|
} else if (shouldRetainForLater) {
|
|
@@ -786,7 +809,8 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
786
809
|
isFormSubject: product => (0, _utils.resolveIsFormSubject)(tempOrder, product),
|
|
787
810
|
orderTotalAmount: Number(this.store.summary?.total_amount || 0)
|
|
788
811
|
}, {
|
|
789
|
-
reapplyBookingDiscountProductUids: options?.reapplyBookingDiscountProductUids
|
|
812
|
+
reapplyBookingDiscountProductUids: options?.reapplyBookingDiscountProductUids,
|
|
813
|
+
preferredEntitlementPassIds: options?.preferredEntitlementPassIds
|
|
790
814
|
});
|
|
791
815
|
if (result?.productList) {
|
|
792
816
|
const previousProductsByUid = (0, _utils.indexOrderProductsByUid)(tempOrder.products);
|
|
@@ -1144,17 +1168,55 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1144
1168
|
}
|
|
1145
1169
|
clearCustomerWalletRuntimeState() {
|
|
1146
1170
|
this.customerWalletPrepareConfigEpoch += 1;
|
|
1171
|
+
this.pointCardSnapshotCodeCandidates.clear();
|
|
1147
1172
|
this.store.availableWalletIds = [];
|
|
1148
1173
|
this.store.virtualCurrencyList = [];
|
|
1149
1174
|
}
|
|
1175
|
+
rememberPointCardSnapshotCodeCandidates(value) {
|
|
1176
|
+
(0, _utils.collectPointCardSnapshotCodeCandidates)(value).forEach(({
|
|
1177
|
+
id,
|
|
1178
|
+
code
|
|
1179
|
+
}) => {
|
|
1180
|
+
if (!this.pointCardSnapshotCodeCandidates.has(id)) {
|
|
1181
|
+
this.pointCardSnapshotCodeCandidates.set(id, code);
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
getRememberedPointCardSnapshotCodeCandidates() {
|
|
1186
|
+
return [...this.pointCardSnapshotCodeCandidates].map(([id, code]) => ({
|
|
1187
|
+
id,
|
|
1188
|
+
code
|
|
1189
|
+
}));
|
|
1190
|
+
}
|
|
1191
|
+
getPointCardSnapshotOrderIdentityKey(identity) {
|
|
1192
|
+
if (identity?.externalSaleNumber) {
|
|
1193
|
+
return `external:${identity.externalSaleNumber}`;
|
|
1194
|
+
}
|
|
1195
|
+
if (identity?.orderId !== undefined && identity.orderId !== null) {
|
|
1196
|
+
return `order:${identity.orderId}`;
|
|
1197
|
+
}
|
|
1198
|
+
if (identity?.localId) return `local:${identity.localId}`;
|
|
1199
|
+
if (identity?.orderNumber) return `number:${identity.orderNumber}`;
|
|
1200
|
+
return '';
|
|
1201
|
+
}
|
|
1202
|
+
isPointCardSnapshotMergeContextCurrent(context) {
|
|
1203
|
+
if (!context) return true;
|
|
1204
|
+
if (context.customerId !== undefined && String(context.customerId ?? '') !== String(this.store.tempOrder?.customer_id ?? '')) {
|
|
1205
|
+
return false;
|
|
1206
|
+
}
|
|
1207
|
+
const expectedOrderKey = this.getPointCardSnapshotOrderIdentityKey(context.orderIdentity);
|
|
1208
|
+
return !expectedOrderKey || expectedOrderKey === this.getPointCardSnapshotOrderIdentityKey(this.getOrderIdentity());
|
|
1209
|
+
}
|
|
1150
1210
|
writeCustomerWalletSnapshotsFromPrepareConfig(params) {
|
|
1151
1211
|
const tempOrder = this.store.tempOrder;
|
|
1152
1212
|
if (!tempOrder) return;
|
|
1153
1213
|
if (String(tempOrder.customer_id ?? '') !== String(params.customerId)) return;
|
|
1214
|
+
const currentOrderMetaList = (0, _utils.normalizeOrderMetaList)(tempOrder.order_meta_list);
|
|
1154
1215
|
const virtualCurrencyList = Array.isArray(params.virtualCurrencyList) ? params.virtualCurrencyList : [];
|
|
1216
|
+
const snapshotsWithExistingCodes = (0, _utils.mergePointCardSnapshotCodes)(params.availableWalletPassList, currentOrderMetaList?.point_cards_snapshot);
|
|
1155
1217
|
tempOrder.order_meta_list = {
|
|
1156
|
-
...(
|
|
1157
|
-
point_cards_snapshot: (0, _utils.
|
|
1218
|
+
...(currentOrderMetaList || {}),
|
|
1219
|
+
point_cards_snapshot: (0, _utils.mergePointCardSnapshotCodes)(snapshotsWithExistingCodes, this.getRememberedPointCardSnapshotCodeCandidates()),
|
|
1158
1220
|
virtual_currency_snapshot: (0, _utils.buildVirtualCurrencySnapshotFromList)(virtualCurrencyList)
|
|
1159
1221
|
};
|
|
1160
1222
|
this.store.availableWalletIds = [...params.availableWalletIds];
|
|
@@ -1162,6 +1224,31 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
1162
1224
|
this.persistTempOrder();
|
|
1163
1225
|
this.saveDraftInBackground();
|
|
1164
1226
|
}
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* 将 Payment WalletPass 返回的实例 code 回填到当前订单 Snapshot。
|
|
1230
|
+
* 这里只按资产 ID 补空值,不新增卡、不覆盖 prepare/config 冻结的余额。
|
|
1231
|
+
*/
|
|
1232
|
+
mergePointCardSnapshotCodes(walletPassList, context) {
|
|
1233
|
+
const tempOrder = this.store.tempOrder;
|
|
1234
|
+
const currentOrderMetaList = (0, _utils.normalizeOrderMetaList)(tempOrder?.order_meta_list);
|
|
1235
|
+
const currentSnapshots = currentOrderMetaList?.point_cards_snapshot || [];
|
|
1236
|
+
if (!tempOrder || !this.isPointCardSnapshotMergeContextCurrent(context)) {
|
|
1237
|
+
return currentSnapshots;
|
|
1238
|
+
}
|
|
1239
|
+
this.rememberPointCardSnapshotCodeCandidates(walletPassList);
|
|
1240
|
+
if (!currentSnapshots.length) return currentSnapshots;
|
|
1241
|
+
const nextSnapshots = (0, _utils.mergePointCardSnapshotCodes)(currentSnapshots, this.getRememberedPointCardSnapshotCodeCandidates());
|
|
1242
|
+
const hasChanged = nextSnapshots.some((snapshot, index) => snapshot.code !== currentSnapshots[index]?.code);
|
|
1243
|
+
if (!hasChanged) return currentSnapshots;
|
|
1244
|
+
tempOrder.order_meta_list = {
|
|
1245
|
+
...(currentOrderMetaList || {}),
|
|
1246
|
+
point_cards_snapshot: nextSnapshots
|
|
1247
|
+
};
|
|
1248
|
+
this.persistTempOrder();
|
|
1249
|
+
this.saveDraftInBackground();
|
|
1250
|
+
return nextSnapshots;
|
|
1251
|
+
}
|
|
1165
1252
|
createExternalSaleNumber() {
|
|
1166
1253
|
const timestamp = (0, _dayjs.default)().format('YYYYMMDDHHmmssSSS');
|
|
1167
1254
|
const random6 = String(Math.floor(Math.random() * 1000000)).padStart(6, '0');
|
|
@@ -2193,6 +2280,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2193
2280
|
const bookingByBookingUid = new Map();
|
|
2194
2281
|
bookings.forEach(booking => {
|
|
2195
2282
|
if (!booking || typeof booking !== 'object') return;
|
|
2283
|
+
if ((0, _pickupTiming.isFulfillmentTimingBooking)(booking)) return;
|
|
2196
2284
|
const productUid = booking.product_uid || booking.metadata?.product_uid;
|
|
2197
2285
|
if (productUid !== undefined && productUid !== null && String(productUid) !== '') {
|
|
2198
2286
|
bookingByProductUid.set(String(productUid), booking);
|
|
@@ -2243,6 +2331,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2243
2331
|
let baseHolder = null;
|
|
2244
2332
|
const seen = new Set();
|
|
2245
2333
|
for (const booking of tempOrder.bookings || []) {
|
|
2334
|
+
if ((0, _pickupTiming.isFulfillmentTimingBooking)(booking)) continue;
|
|
2246
2335
|
const holder = booking?.holder && typeof booking.holder === 'object' ? booking.holder : null;
|
|
2247
2336
|
if (!baseHolder && holder) {
|
|
2248
2337
|
baseHolder = holder;
|
|
@@ -2283,7 +2372,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2283
2372
|
* this.syncTempOrderHolderFromBookings(tempOrder);
|
|
2284
2373
|
*/
|
|
2285
2374
|
syncTempOrderHolderFromBookings(tempOrder) {
|
|
2286
|
-
const bookings = tempOrder.bookings || [];
|
|
2375
|
+
const bookings = (tempOrder.bookings || []).filter(booking => !(0, _pickupTiming.isFulfillmentTimingBooking)(booking));
|
|
2287
2376
|
const hasHolderSource = bookings.some(booking => {
|
|
2288
2377
|
return booking?.holder !== undefined || booking?.metadata?.holder_id !== undefined;
|
|
2289
2378
|
});
|
|
@@ -2308,6 +2397,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2308
2397
|
const bookingUid = product?.booking_uid || product?.metadata?.booking_uid;
|
|
2309
2398
|
if (!productUid && !bookingUid) return;
|
|
2310
2399
|
tempOrder.bookings = (tempOrder.bookings || []).filter(booking => {
|
|
2400
|
+
if ((0, _pickupTiming.isFulfillmentTimingBooking)(booking)) return true;
|
|
2311
2401
|
const currentBookingUid = this.getBookingUniqueIdentificationNumber(booking);
|
|
2312
2402
|
if (bookingUid && currentBookingUid === String(bookingUid)) return false;
|
|
2313
2403
|
if (productUid && String(booking?.product_uid || '') === String(productUid)) return false;
|
|
@@ -2721,10 +2811,33 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2721
2811
|
});
|
|
2722
2812
|
}
|
|
2723
2813
|
updateTempOrderServiceType(serviceType) {
|
|
2814
|
+
if (serviceType !== 'pick_up') {
|
|
2815
|
+
const tempOrder = this.ensureTempOrder();
|
|
2816
|
+
tempOrder.bookings = (tempOrder.bookings || []).filter(booking => !(0, _pickupTiming.isFulfillmentTimingBooking)(booking));
|
|
2817
|
+
}
|
|
2724
2818
|
return this.updateTempOrderShopServiceData({
|
|
2725
2819
|
service_type: serviceType
|
|
2726
2820
|
});
|
|
2727
2821
|
}
|
|
2822
|
+
getPickupTimingBooking() {
|
|
2823
|
+
return (this.store.tempOrder?.bookings || []).find(_pickupTiming.isFulfillmentTimingBooking) || null;
|
|
2824
|
+
}
|
|
2825
|
+
setPickupTimingBooking(input) {
|
|
2826
|
+
const tempOrder = this.ensureTempOrder();
|
|
2827
|
+
const bookings = tempOrder.bookings || [];
|
|
2828
|
+
const firstIndex = bookings.findIndex(_pickupTiming.isFulfillmentTimingBooking);
|
|
2829
|
+
const nextBooking = (0, _pickupTiming.buildPickupTimingBooking)(input, bookings[firstIndex]);
|
|
2830
|
+
// Keep the first row's identity and position; repair duplicate marked rows only.
|
|
2831
|
+
tempOrder.bookings = firstIndex === -1 ? [...bookings, nextBooking] : bookings.flatMap((booking, index) => index === firstIndex ? [nextBooking] : (0, _pickupTiming.isFulfillmentTimingBooking)(booking) ? [] : [booking]);
|
|
2832
|
+
this.notifyTempOrderChanged();
|
|
2833
|
+
return nextBooking;
|
|
2834
|
+
}
|
|
2835
|
+
clearPickupTimingBooking() {
|
|
2836
|
+
const tempOrder = this.store.tempOrder;
|
|
2837
|
+
if (!tempOrder?.bookings?.some(_pickupTiming.isFulfillmentTimingBooking)) return;
|
|
2838
|
+
tempOrder.bookings = tempOrder.bookings.filter(booking => !(0, _pickupTiming.isFulfillmentTimingBooking)(booking));
|
|
2839
|
+
this.notifyTempOrderChanged();
|
|
2840
|
+
}
|
|
2728
2841
|
updateTempOrderContactsInfo(contactsInfo) {
|
|
2729
2842
|
const tempOrder = this.ensureTempOrder();
|
|
2730
2843
|
tempOrder.contacts_info = contactsInfo && typeof contactsInfo === 'object' && !Array.isArray(contactsInfo) ? contactsInfo : null;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type PickupTimingSelectionMode = 'asap' | 'scheduled';
|
|
2
|
+
export type PickupTimingSource = 'asap' | 'schedule' | 'pos_manual';
|
|
3
|
+
export interface PickupTimingBookingInput {
|
|
4
|
+
mode: PickupTimingSelectionMode;
|
|
5
|
+
source: PickupTimingSource;
|
|
6
|
+
/** Store-local calendar date and minute; the caller validates availability. */
|
|
7
|
+
date: string;
|
|
8
|
+
time: string;
|
|
9
|
+
/** Absolute timestamp disambiguates a repeated local minute at a DST change. */
|
|
10
|
+
instant?: string;
|
|
11
|
+
timezone?: string;
|
|
12
|
+
frozen?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface PickupTimingBookingMetadata {
|
|
15
|
+
unique_identification_number: string;
|
|
16
|
+
is_fulfillment_timing: true;
|
|
17
|
+
fulfillment_timing: {
|
|
18
|
+
method: 'pick_up';
|
|
19
|
+
mode: PickupTimingSelectionMode;
|
|
20
|
+
source: PickupTimingSource;
|
|
21
|
+
instant?: string;
|
|
22
|
+
timezone?: string;
|
|
23
|
+
frozen?: boolean;
|
|
24
|
+
};
|
|
25
|
+
[key: string]: any;
|
|
26
|
+
}
|
|
27
|
+
export interface PickupTimingBooking {
|
|
28
|
+
schedule_id: number | string;
|
|
29
|
+
start_date: string;
|
|
30
|
+
end_date: string;
|
|
31
|
+
start_time: string;
|
|
32
|
+
end_time: string;
|
|
33
|
+
select_date: string;
|
|
34
|
+
duration: 0;
|
|
35
|
+
is_all: 0;
|
|
36
|
+
sub_type: 'minutes';
|
|
37
|
+
like_status: string;
|
|
38
|
+
resources: [];
|
|
39
|
+
metadata: PickupTimingBookingMetadata;
|
|
40
|
+
schedule_event_id?: number | string;
|
|
41
|
+
[key: string]: any;
|
|
42
|
+
}
|
|
43
|
+
/** The explicit marker is the only authority; unlinked ordinary bookings remain ordinary. */
|
|
44
|
+
export declare function isFulfillmentTimingBooking(booking: unknown): boolean;
|
|
45
|
+
/** Update an independent pickup appointment without changing its persisted row identity. */
|
|
46
|
+
export declare function buildPickupTimingBooking(input: PickupTimingBookingInput, existing?: Record<string, any> | null): PickupTimingBooking;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.buildPickupTimingBooking = buildPickupTimingBooking;
|
|
7
|
+
exports.isFulfillmentTimingBooking = isFulfillmentTimingBooking;
|
|
8
|
+
var _orderCollectionIdentity = require("./utils/orderCollectionIdentity");
|
|
9
|
+
/** The explicit marker is the only authority; unlinked ordinary bookings remain ordinary. */
|
|
10
|
+
function isFulfillmentTimingBooking(booking) {
|
|
11
|
+
return booking?.metadata?.is_fulfillment_timing === true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Update an independent pickup appointment without changing its persisted row identity. */
|
|
15
|
+
function buildPickupTimingBooking(input, existing) {
|
|
16
|
+
const booking = {
|
|
17
|
+
...(existing || {})
|
|
18
|
+
};
|
|
19
|
+
const metadata = {
|
|
20
|
+
...(booking.metadata || {})
|
|
21
|
+
};
|
|
22
|
+
// A fulfillment appointment never becomes a product/resource/holder purchase line.
|
|
23
|
+
for (const key of ['product_uid', 'product', 'holder', 'holder_id', 'collect_pax', 'capacity']) {
|
|
24
|
+
delete booking[key];
|
|
25
|
+
delete metadata[key];
|
|
26
|
+
}
|
|
27
|
+
delete metadata.booking_uid;
|
|
28
|
+
return {
|
|
29
|
+
...booking,
|
|
30
|
+
schedule_id: booking.schedule_id || 0,
|
|
31
|
+
start_date: input.date,
|
|
32
|
+
end_date: input.date,
|
|
33
|
+
start_time: input.time,
|
|
34
|
+
end_time: input.time,
|
|
35
|
+
select_date: input.date,
|
|
36
|
+
duration: 0,
|
|
37
|
+
is_all: 0,
|
|
38
|
+
sub_type: 'minutes',
|
|
39
|
+
like_status: booking.like_status || 'common',
|
|
40
|
+
resources: [],
|
|
41
|
+
metadata: {
|
|
42
|
+
...metadata,
|
|
43
|
+
unique_identification_number: String(metadata.unique_identification_number || (0, _orderCollectionIdentity.createUuidV4)()),
|
|
44
|
+
is_fulfillment_timing: true,
|
|
45
|
+
fulfillment_timing: {
|
|
46
|
+
method: 'pick_up',
|
|
47
|
+
mode: input.mode,
|
|
48
|
+
source: input.source,
|
|
49
|
+
...(input.instant !== undefined ? {
|
|
50
|
+
instant: input.instant
|
|
51
|
+
} : {}),
|
|
52
|
+
...(input.timezone !== undefined ? {
|
|
53
|
+
timezone: input.timezone
|
|
54
|
+
} : {}),
|
|
55
|
+
...(input.frozen !== undefined ? {
|
|
56
|
+
frozen: input.frozen
|
|
57
|
+
} : {})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -7,9 +7,10 @@ import type { SubmitPayloadEnhancer } from './utils';
|
|
|
7
7
|
import type { PaymentItem } from '../Payment/types';
|
|
8
8
|
import type { ICustomer } from '../AccountList/types';
|
|
9
9
|
import type { OrderProductDiscountItem } from '../../server/modules/order/types';
|
|
10
|
+
import type { PickupTimingBooking, PickupTimingBookingInput } from './pickupTiming';
|
|
10
11
|
export type { OrderProductDiscountItem } from '../../server/modules/order/types';
|
|
11
12
|
export type PickupReferenceMode = 'counter_pickup' | 'table_service';
|
|
12
|
-
export type ShopServiceType = 'dine_in' | 'takeaway';
|
|
13
|
+
export type ShopServiceType = 'dine_in' | 'takeaway' | 'pick_up' | 'delivery';
|
|
13
14
|
export interface ShopServiceData {
|
|
14
15
|
pickup_reference_mode?: PickupReferenceMode;
|
|
15
16
|
service_type?: ShopServiceType | '';
|
|
@@ -199,10 +200,15 @@ export interface PointCardSnapshotItem {
|
|
|
199
200
|
id: number | string;
|
|
200
201
|
tag: 'point_card';
|
|
201
202
|
product_id?: number | string;
|
|
203
|
+
code?: string;
|
|
202
204
|
product_name: string;
|
|
203
205
|
par_value: string;
|
|
204
206
|
used_par_value: string;
|
|
205
207
|
}
|
|
208
|
+
export interface PointCardSnapshotMergeContext {
|
|
209
|
+
customerId?: number | string | null;
|
|
210
|
+
orderIdentity?: OrderIdentitySnapshot | null;
|
|
211
|
+
}
|
|
206
212
|
export interface VirtualCurrencySnapshotItem {
|
|
207
213
|
id: number | string;
|
|
208
214
|
tag: 'virtual_currency';
|
|
@@ -858,6 +864,9 @@ export interface OrderModuleAPI {
|
|
|
858
864
|
updateTempOrderCustomer: (customer: UpdateTempOrderCustomerInput) => OrderSnapshot['customer'];
|
|
859
865
|
updateTempOrderPickupReferenceMode: (mode: PickupReferenceMode) => ShopServiceData;
|
|
860
866
|
updateTempOrderServiceType: (serviceType: ShopServiceType) => ShopServiceData;
|
|
867
|
+
getPickupTimingBooking: () => PickupTimingBooking | null;
|
|
868
|
+
setPickupTimingBooking: (input: PickupTimingBookingInput) => PickupTimingBooking;
|
|
869
|
+
clearPickupTimingBooking: () => void;
|
|
861
870
|
updateTempOrderBuzzer: (buzzer: string) => string;
|
|
862
871
|
updateTempOrderContactsInfo: (contactsInfo: Record<string, any> | null) => Record<string, any> | null;
|
|
863
872
|
addProductToOrder: (product: Partial<OrderProduct> & OrderProductIdentity, booking?: AddProductBookingInput, options?: AddProductToOrderOptions) => Promise<OrderProduct[]>;
|
|
@@ -975,6 +984,7 @@ export interface OrderModuleAPI {
|
|
|
975
984
|
getDiscountList: () => Discount[];
|
|
976
985
|
getAvailableWalletIds: () => number[];
|
|
977
986
|
getCustomerWalletPrepareConfigEpoch: () => number;
|
|
987
|
+
mergePointCardSnapshotCodes: (walletPassList: unknown, context?: PointCardSnapshotMergeContext) => PointCardSnapshotItem[];
|
|
978
988
|
applyDiscount: () => void;
|
|
979
989
|
/**
|
|
980
990
|
* 注入促销评估器。
|
|
@@ -18,6 +18,16 @@ export interface OrderProductsDepositSummary {
|
|
|
18
18
|
import type { WindowPlugin } from '../../plugins';
|
|
19
19
|
export declare function normalizePointCardSnapshotItem(value: unknown): PointCardSnapshotItem | null;
|
|
20
20
|
export declare function buildPointCardSnapshotFromWalletPassList(value: unknown): PointCardSnapshotItem[];
|
|
21
|
+
export declare function collectPointCardSnapshotCodeCandidates(value: unknown): Array<{
|
|
22
|
+
id: string;
|
|
23
|
+
code: string;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Payment 的 WalletPass 列表包含实例 code,而 prepare/config 通常只提供
|
|
27
|
+
* 卡名称和余额。按实例 ID 仅补齐 Snapshot 缺失的 code,避免后到的运行态
|
|
28
|
+
* 数据覆盖下单时已经冻结的卡名称和余额。
|
|
29
|
+
*/
|
|
30
|
+
export declare function mergePointCardSnapshotCodes(snapshotsValue: unknown, walletPassesValue: unknown): PointCardSnapshotItem[];
|
|
21
31
|
export declare function normalizeVirtualCurrencySnapshotItem(value: unknown): VirtualCurrencySnapshotItem | null;
|
|
22
32
|
export declare function buildVirtualCurrencySnapshotFromList(value: unknown): VirtualCurrencySnapshotItem[];
|
|
23
33
|
export declare function normalizeOrderMetaList(value: unknown): OrderMetaList | undefined;
|
|
@@ -14,6 +14,7 @@ exports.buildSubmitPayload = buildSubmitPayload;
|
|
|
14
14
|
exports.buildVirtualCurrencySnapshotFromList = buildVirtualCurrencySnapshotFromList;
|
|
15
15
|
exports.calculateOrderProductsDeposit = calculateOrderProductsDeposit;
|
|
16
16
|
exports.clearTempOrderHolderAssignments = clearTempOrderHolderAssignments;
|
|
17
|
+
exports.collectPointCardSnapshotCodeCandidates = collectPointCardSnapshotCodeCandidates;
|
|
17
18
|
exports.composeLinePrice = composeLinePrice;
|
|
18
19
|
exports.createDefaultOrderRulesHooks = createDefaultOrderRulesHooks;
|
|
19
20
|
exports.createDefaultTempOrder = createDefaultTempOrder;
|
|
@@ -66,6 +67,7 @@ Object.defineProperty(exports, "mergeManualProductDiscountIntoList", {
|
|
|
66
67
|
}
|
|
67
68
|
});
|
|
68
69
|
exports.mergeOrderProductDisplayFields = mergeOrderProductDisplayFields;
|
|
70
|
+
exports.mergePointCardSnapshotCodes = mergePointCardSnapshotCodes;
|
|
69
71
|
exports.mergeRelationForms = void 0;
|
|
70
72
|
exports.normalizeBookingIsAll = normalizeBookingIsAll;
|
|
71
73
|
exports.normalizeBookingSubType = normalizeBookingSubType;
|
|
@@ -133,6 +135,7 @@ var _utils = require("../Holder/utils");
|
|
|
133
135
|
var _utils2 = require("../../solution/ScanOrder/utils");
|
|
134
136
|
var _orderCollectionIdentity = require("./utils/orderCollectionIdentity");
|
|
135
137
|
var _salesNotes = require("./salesNotes");
|
|
138
|
+
var _pickupTiming = require("./pickupTiming");
|
|
136
139
|
var _manualProductDiscount = require("./utils/manualProductDiscount");
|
|
137
140
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
138
141
|
_dayjs.default.extend(_customParseFormat.default);
|
|
@@ -165,6 +168,10 @@ function normalizePointCardSnapshotItem(value) {
|
|
|
165
168
|
if (productId !== undefined) {
|
|
166
169
|
snapshot.product_id = productId;
|
|
167
170
|
}
|
|
171
|
+
const code = normalizeSnapshotScalar(value.code);
|
|
172
|
+
if (code !== undefined) {
|
|
173
|
+
snapshot.code = String(code).trim();
|
|
174
|
+
}
|
|
168
175
|
return snapshot;
|
|
169
176
|
}
|
|
170
177
|
function buildPointCardSnapshotFromWalletPassList(value) {
|
|
@@ -175,6 +182,46 @@ function buildPointCardSnapshotFromWalletPassList(value) {
|
|
|
175
182
|
return result;
|
|
176
183
|
}, []);
|
|
177
184
|
}
|
|
185
|
+
function collectPointCardSnapshotCodeCandidates(value) {
|
|
186
|
+
if (!Array.isArray(value)) return [];
|
|
187
|
+
const codeById = new Map();
|
|
188
|
+
value.forEach(item => {
|
|
189
|
+
if (!isPlainObject(item)) return;
|
|
190
|
+
const id = normalizeSnapshotScalar(item.id ?? item.voucher_id);
|
|
191
|
+
const code = normalizeSnapshotScalar(item.code);
|
|
192
|
+
if (id === undefined || code === undefined) return;
|
|
193
|
+
const identity = String(id).trim();
|
|
194
|
+
const normalizedCode = String(code).trim();
|
|
195
|
+
if (!identity || !normalizedCode || codeById.has(identity)) return;
|
|
196
|
+
codeById.set(identity, normalizedCode);
|
|
197
|
+
});
|
|
198
|
+
return [...codeById].map(([id, code]) => ({
|
|
199
|
+
id,
|
|
200
|
+
code
|
|
201
|
+
}));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Payment 的 WalletPass 列表包含实例 code,而 prepare/config 通常只提供
|
|
206
|
+
* 卡名称和余额。按实例 ID 仅补齐 Snapshot 缺失的 code,避免后到的运行态
|
|
207
|
+
* 数据覆盖下单时已经冻结的卡名称和余额。
|
|
208
|
+
*/
|
|
209
|
+
function mergePointCardSnapshotCodes(snapshotsValue, walletPassesValue) {
|
|
210
|
+
const snapshots = buildPointCardSnapshotFromWalletPassList(snapshotsValue);
|
|
211
|
+
if (!snapshots.length || !Array.isArray(walletPassesValue)) return snapshots;
|
|
212
|
+
const codeById = new Map(collectPointCardSnapshotCodeCandidates(walletPassesValue).map(({
|
|
213
|
+
id,
|
|
214
|
+
code
|
|
215
|
+
}) => [id, code]));
|
|
216
|
+
return snapshots.map(snapshot => {
|
|
217
|
+
if (snapshot.code) return snapshot;
|
|
218
|
+
const code = codeById.get(String(snapshot.id).trim());
|
|
219
|
+
return code ? {
|
|
220
|
+
...snapshot,
|
|
221
|
+
code
|
|
222
|
+
} : snapshot;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
178
225
|
function normalizeSnapshotNameMap(value) {
|
|
179
226
|
if (typeof value === 'string' || typeof value === 'number') {
|
|
180
227
|
const normalizedText = String(value).trim();
|
|
@@ -1251,7 +1298,7 @@ function normalizeSubmitProduct(product, formatOptions = {}) {
|
|
|
1251
1298
|
payment_price: formatOptions.virtualSettlementMode ? virtualPrice : submitProduct.payment_price ?? submitProduct.selling_price
|
|
1252
1299
|
};
|
|
1253
1300
|
}
|
|
1254
|
-
const SUBMIT_BOOKING_METADATA_WHITELIST = ['unique_identification_number', 'collect_pax', 'capacity', 'holder_id', 'resource_select_type', 'holder'];
|
|
1301
|
+
const SUBMIT_BOOKING_METADATA_WHITELIST = ['unique_identification_number', 'collect_pax', 'capacity', 'holder_id', 'resource_select_type', 'holder', 'is_fulfillment_timing', 'fulfillment_timing'];
|
|
1255
1302
|
function normalizeBookingIsAll(booking) {
|
|
1256
1303
|
return booking.is_all === true || booking.is_all === 1 || booking.is_all === '1' || booking.is_all === 'true' ? 1 : 0;
|
|
1257
1304
|
}
|
|
@@ -1270,7 +1317,9 @@ function normalizeSubmitBooking(booking) {
|
|
|
1270
1317
|
cleanMetadata[key] = rawMetadata[key];
|
|
1271
1318
|
}
|
|
1272
1319
|
}
|
|
1273
|
-
if (cleanMetadata.holder_id === undefined
|
|
1320
|
+
if (cleanMetadata.holder_id === undefined && !(0, _pickupTiming.isFulfillmentTimingBooking)(booking)) {
|
|
1321
|
+
cleanMetadata.holder_id = null;
|
|
1322
|
+
}
|
|
1274
1323
|
const bookingRecord = booking;
|
|
1275
1324
|
const startDate = bookingRecord.start_date;
|
|
1276
1325
|
const shouldRestoreSelectDate = bookingRecord.select_date === undefined || bookingRecord.select_date === null || bookingRecord.select_date === '';
|
|
@@ -1282,6 +1331,10 @@ function normalizeSubmitBooking(booking) {
|
|
|
1282
1331
|
} : {}),
|
|
1283
1332
|
is_all: normalizeBookingIsAll(bookingRecord),
|
|
1284
1333
|
sub_type: normalizeBookingSubType(bookingRecord),
|
|
1334
|
+
...((0, _pickupTiming.isFulfillmentTimingBooking)(booking) ? {
|
|
1335
|
+
like_status: bookingRecord.like_status || 'common',
|
|
1336
|
+
schedule_id: bookingRecord.schedule_id || 0
|
|
1337
|
+
} : {}),
|
|
1285
1338
|
metadata: cleanMetadata
|
|
1286
1339
|
};
|
|
1287
1340
|
}
|
|
@@ -1367,6 +1420,7 @@ function hasBoundBookingResource(bookings) {
|
|
|
1367
1420
|
if (!Array.isArray(bookings)) return false;
|
|
1368
1421
|
return bookings.some(booking => {
|
|
1369
1422
|
if (!booking || typeof booking !== 'object') return false;
|
|
1423
|
+
if ((0, _pickupTiming.isFulfillmentTimingBooking)(booking)) return false;
|
|
1370
1424
|
if (Number(booking.parent_id) === 0) return false;
|
|
1371
1425
|
const resources = booking.resources;
|
|
1372
1426
|
if (!Array.isArray(resources)) return false;
|
|
@@ -1402,7 +1456,7 @@ function resolveSubmitOrderType(params) {
|
|
|
1402
1456
|
return 'table-order';
|
|
1403
1457
|
}
|
|
1404
1458
|
if (currentType) return currentType;
|
|
1405
|
-
if (Array.isArray(bookings) && bookings.
|
|
1459
|
+
if (Array.isArray(bookings) && bookings.some(booking => !(0, _pickupTiming.isFulfillmentTimingBooking)(booking))) {
|
|
1406
1460
|
return businessCode === 'dine_in' ? 'table-order' : 'appointment_booking';
|
|
1407
1461
|
}
|
|
1408
1462
|
return 'virtual';
|
|
@@ -1487,7 +1541,7 @@ function buildSubmitPayload(params) {
|
|
|
1487
1541
|
delivery_type: tempOrder.delivery_type || 'nil',
|
|
1488
1542
|
schedule_date: scheduleDate,
|
|
1489
1543
|
// created_at: tempOrder.created_at || formatDateTime(now), 和后端协定这个字段先不提交到接口
|
|
1490
|
-
bookings: (tempOrder.bookings || []).filter(booking => Number(booking.parent_id) !== 0).map(booking => normalizeSubmitBooking(booking)),
|
|
1544
|
+
bookings: (tempOrder.bookings || []).filter(booking => (0, _pickupTiming.isFulfillmentTimingBooking)(booking) || Number(booking.parent_id) !== 0).map(booking => normalizeSubmitBooking(booking)),
|
|
1491
1545
|
payments: tempOrder.payments || [],
|
|
1492
1546
|
relation_forms: tempOrder.relation_forms || [],
|
|
1493
1547
|
// contacts: tempOrder.contacts || [],
|
|
@@ -104,10 +104,12 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
104
104
|
unavailableReason: _types.UnavailableReason.AlreadyUsed
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
|
+
const scannedEntitlementPassIds = newDiscountList.filter(discount => (0, _entitlementPass.isEntitlementPass)(discount)).map(discount => discount.id);
|
|
108
|
+
const scannedEntitlementPassIdSet = new Set(scannedEntitlementPassIds.map(String));
|
|
107
109
|
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
const filteredOldDiscountList = oldDiscountList.filter(discount => !discount.isEditMode && (!(0, _entitlementPass.isFullCoveragePass)(discount) || discount.isScan));
|
|
110
|
+
// 客户次卡必须保留在完整列表中;同一实例以本次扫码数据更新。
|
|
111
|
+
// 历史折扣不参与试算,商品券继续沿用仅保留扫码来源的规则。
|
|
112
|
+
const filteredOldDiscountList = oldDiscountList.filter(discount => !discount.isEditMode && (!(0, _entitlementPass.isFullCoveragePass)(discount) || (0, _entitlementPass.isEntitlementPass)(discount) || discount.isScan) && !scannedEntitlementPassIdSet.has(String(discount.id)));
|
|
111
113
|
const mergedDiscountList = (0, _utils.uniqueById)([...filteredOldDiscountList, ...newDiscountList]);
|
|
112
114
|
const result = this.calcDiscount({
|
|
113
115
|
discountList: mergedDiscountList,
|
|
@@ -116,7 +118,8 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
116
118
|
holders,
|
|
117
119
|
isFormSubject
|
|
118
120
|
}, {
|
|
119
|
-
scan: true
|
|
121
|
+
scan: true,
|
|
122
|
+
preferredEntitlementPassIds: scannedEntitlementPassIds
|
|
120
123
|
});
|
|
121
124
|
|
|
122
125
|
// 检查是否有产品使用了新折扣
|
|
@@ -216,6 +219,7 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
216
219
|
isFormSubject,
|
|
217
220
|
orderTotalAmount
|
|
218
221
|
}, options) {
|
|
222
|
+
const preferredEntitlementPassIds = new Set((options?.preferredEntitlementPassIds || []).map(String));
|
|
219
223
|
const resolveIsFormSubject = product => typeof isFormSubject === 'function' ? Boolean(isFormSubject(product)) : Boolean(isFormSubject);
|
|
220
224
|
|
|
221
225
|
// 识别出来是不是在编辑的界面里又新增了商品
|
|
@@ -969,6 +973,7 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
969
973
|
discount_product_id: discount.product_id
|
|
970
974
|
},
|
|
971
975
|
metadata: {
|
|
976
|
+
encoded: discount.metadata?.encoded || discount.encoded,
|
|
972
977
|
num: num,
|
|
973
978
|
discount_rule_uncheck_flag: discount?.discount_rule_uncheck_flag
|
|
974
979
|
}
|
|
@@ -1164,7 +1169,7 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
1164
1169
|
return false;
|
|
1165
1170
|
});
|
|
1166
1171
|
|
|
1167
|
-
//
|
|
1172
|
+
// 识别用户本次显式选择的卡券,供次卡和折扣卡各自的优先级使用。
|
|
1168
1173
|
// 目标仍必须在当前商品的 applicableDiscounts 中,因此不会绕过 Holder、时间或商品范围校验。
|
|
1169
1174
|
const explicitlySelectedDiscount = options?.isSelected === true && options.discountId !== undefined ? applicableDiscounts.find(discount => {
|
|
1170
1175
|
const discountType = discount.tag || discount.type || '';
|
|
@@ -1173,21 +1178,24 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
1173
1178
|
// 折扣卡会复用于整条商品行;商品券必须继续按单张拆分,不能复用该分支。
|
|
1174
1179
|
const explicitlySelectedDiscountCard = explicitlySelectedDiscount && !(0, _entitlementPass.isFullCoveragePass)(explicitlySelectedDiscount) ? explicitlySelectedDiscount : undefined;
|
|
1175
1180
|
|
|
1176
|
-
// 商品数量等变化会触发无 discountId
|
|
1181
|
+
// 商品数量等变化会触发无 discountId 的重算。只要当前行已应用的卡仍在
|
|
1177
1182
|
// applicableDiscounts 中,就应保留该行选择,避免被自动排序中力度更大的卡替换。
|
|
1178
|
-
const
|
|
1183
|
+
const appliedWalletDiscountIds = new Set((product.discount_list || []).filter(item => ['entitlement_pass', 'discount_card', 'product_discount_card'].includes(item?.tag || item?.type)).map(item => item?.discount?.resource_id ?? item?.resource_id ?? item?.id).filter(id => id !== undefined && id !== null).map(String));
|
|
1179
1184
|
const appliedDiscountCard = options?.scan ? undefined : applicableDiscounts.find(discount => {
|
|
1180
1185
|
const discountType = discount.tag || discount.type || '';
|
|
1181
|
-
return discount.isSelected === true && ['discount_card', 'product_discount_card'].includes(discountType) &&
|
|
1186
|
+
return discount.isSelected === true && ['discount_card', 'product_discount_card'].includes(discountType) && appliedWalletDiscountIds.has(String(discount.id));
|
|
1182
1187
|
});
|
|
1188
|
+
const appliedEntitlementPass = options?.scan || options?.preserveEntitlementSelection === false ? undefined : applicableDiscounts.find(discount => (0, _entitlementPass.isEntitlementPass)(discount) && discount.isSelected === true && appliedWalletDiscountIds.has(String(discount.id)));
|
|
1183
1189
|
|
|
1184
|
-
//
|
|
1190
|
+
// 次卡内部按本次手选/扫码、当前行已应用、默认排序选择。
|
|
1185
1191
|
const scannedSelectedDiscountCard = applicableDiscounts.find(n => n.isScan && n.isSelected && !(0, _entitlementPass.isFullCoveragePass)(n));
|
|
1186
1192
|
const selectedDiscountCard = explicitlySelectedDiscountCard || appliedDiscountCard || scannedSelectedDiscountCard;
|
|
1187
|
-
const
|
|
1188
|
-
|
|
1189
|
-
const
|
|
1190
|
-
|
|
1193
|
+
const selectedEntitlementPass = (explicitlySelectedDiscount && (0, _entitlementPass.isEntitlementPass)(explicitlySelectedDiscount) ? explicitlySelectedDiscount : undefined) || applicableDiscounts.find(discount => (0, _entitlementPass.isEntitlementPass)(discount) && preferredEntitlementPassIds.has(String(discount.id))) || appliedEntitlementPass || applicableDiscounts.find(discount => (0, _entitlementPass.isEntitlementPass)(discount));
|
|
1194
|
+
// 手选优先;普通重算保留当前折扣卡,扫码和最优选择仍使用原有优先级。
|
|
1195
|
+
const selectedDiscount = explicitlySelectedDiscount || (options?.preserveEntitlementSelection !== false && preferredEntitlementPassIds.size === 0 ? appliedDiscountCard : undefined) || selectedEntitlementPass || selectedDiscountCard || applicableDiscounts[0];
|
|
1196
|
+
// 实际次数分配必须使用同一顺序,避免选中状态切换后仍将次数分配给默认卡。
|
|
1197
|
+
const prioritizedApplicableDiscounts = selectedDiscount && (0, _entitlementPass.isFullCoveragePass)(selectedDiscount) ? [selectedDiscount, ...applicableDiscounts.filter(discount => {
|
|
1198
|
+
return (0, _entitlementPass.isFullCoveragePass)(discount) && String(discount.id) !== String(selectedDiscount.id);
|
|
1191
1199
|
}), ...applicableDiscounts.filter(discount => !(0, _entitlementPass.isFullCoveragePass)(discount))] : applicableDiscounts;
|
|
1192
1200
|
|
|
1193
1201
|
// 如果是手动折扣,则不适用优惠券
|
|
@@ -1461,6 +1469,7 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
1461
1469
|
_num: isFullCoverage ? appliedUseCount : product.num,
|
|
1462
1470
|
config: selectedDiscount?.config,
|
|
1463
1471
|
metadata: {
|
|
1472
|
+
encoded: selectedDiscount.metadata?.encoded || selectedDiscount.encoded,
|
|
1464
1473
|
num: isFullCoverage ? appliedUseCount : 1,
|
|
1465
1474
|
discount_rule_uncheck_flag: selectedDiscount?.discount_rule_uncheck_flag,
|
|
1466
1475
|
// 🔥 order_level 分摊差值
|
|
@@ -1551,6 +1560,7 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
1551
1560
|
discount_product_id: selectedDiscount.product_id
|
|
1552
1561
|
},
|
|
1553
1562
|
metadata: {
|
|
1563
|
+
encoded: selectedDiscount.metadata?.encoded || selectedDiscount.encoded,
|
|
1554
1564
|
// 🔥 使用拆分后的唯一 _id
|
|
1555
1565
|
custom_product_bundle_map_id: uniqueId,
|
|
1556
1566
|
discount_rule_uncheck_flag: selectedDiscount?.discount_rule_uncheck_flag,
|
|
@@ -1646,6 +1656,7 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
1646
1656
|
discount_product_id: selectedDiscount.product_id
|
|
1647
1657
|
},
|
|
1648
1658
|
metadata: {
|
|
1659
|
+
encoded: selectedDiscount.metadata?.encoded || selectedDiscount.encoded,
|
|
1649
1660
|
// 🔥 使用唯一的 _id
|
|
1650
1661
|
custom_product_bundle_map_id: uniqueId,
|
|
1651
1662
|
discount_rule_uncheck_flag: selectedDiscount?.discount_rule_uncheck_flag,
|