@pisell/pisellos 0.10.7 → 0.10.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/model/strategy/adapter/promotion/index.js +9 -0
- package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +12 -10
- package/dist/modules/Order/index.d.ts +12 -5
- package/dist/modules/Order/index.js +310 -210
- package/dist/modules/Order/types.d.ts +26 -3
- package/dist/modules/Order/types.js +11 -0
- package/dist/modules/Order/utils/discountProductLineIdentity.d.ts +45 -0
- package/dist/modules/Order/utils/discountProductLineIdentity.js +227 -0
- package/dist/modules/Rules/index.js +125 -52
- package/dist/modules/SalesSummary/utils.js +7 -1
- package/dist/server/index.d.ts +6 -0
- package/dist/server/index.js +810 -729
- package/dist/server/modules/order/index.d.ts +1 -0
- package/dist/server/modules/order/index.js +9 -2
- package/dist/server/modules/order/types.d.ts +1 -1
- package/dist/solution/BaseSales/index.d.ts +15 -5
- package/dist/solution/BaseSales/index.js +211 -159
- package/dist/solution/BaseSales/utils/cartPromotion.js +26 -3
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +1 -1
- package/dist/solution/BaseSales/utils.js +31 -20
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.js +1 -4
- package/dist/solution/BookingTicket/types.d.ts +3 -0
- package/dist/solution/BookingTicket/utils/cartView.js +44 -32
- package/dist/solution/Sales/index.d.ts +1 -0
- package/dist/solution/Sales/index.js +16 -2
- package/dist/solution/ScanOrder/utils.js +31 -20
- package/dist/solution/UnifiedBookingSales/index.d.ts +5 -4
- package/dist/solution/UnifiedBookingSales/index.js +51 -39
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +3 -1
- package/lib/modules/Order/index.d.ts +12 -5
- package/lib/modules/Order/index.js +148 -43
- package/lib/modules/Order/types.d.ts +26 -3
- package/lib/modules/Order/utils/discountProductLineIdentity.d.ts +45 -0
- package/lib/modules/Order/utils/discountProductLineIdentity.js +170 -0
- package/lib/modules/Rules/index.js +100 -20
- package/lib/modules/SalesSummary/utils.js +5 -1
- package/lib/server/index.d.ts +6 -0
- package/lib/server/index.js +51 -0
- package/lib/server/modules/order/index.d.ts +1 -0
- package/lib/server/modules/order/index.js +9 -3
- package/lib/server/modules/order/types.d.ts +1 -1
- package/lib/solution/BaseSales/index.d.ts +15 -5
- package/lib/solution/BaseSales/index.js +64 -16
- package/lib/solution/BaseSales/utils/cartPromotion.js +26 -2
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +1 -1
- package/lib/solution/BaseSales/utils.js +29 -18
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.js +1 -4
- package/lib/solution/BookingTicket/types.d.ts +3 -0
- package/lib/solution/BookingTicket/utils/cartView.js +43 -30
- package/lib/solution/Sales/index.d.ts +1 -0
- package/lib/solution/Sales/index.js +15 -3
- package/lib/solution/ScanOrder/utils.js +29 -18
- package/lib/solution/UnifiedBookingSales/index.d.ts +5 -4
- package/lib/solution/UnifiedBookingSales/index.js +35 -26
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import { BaseModule } from '../BaseModule';
|
|
|
3
3
|
import { OrderModuleAPI, CommitOrderParams, UpdateOrderBookingParams, UpdateOrderProductParams, UpdateOrderProductQuantityParams, CancelOrderParams, ChangeBookingStatusParams, CheckoutOrderParams } from './types';
|
|
4
4
|
import { CartItem } from '../Cart/types';
|
|
5
5
|
import { type SubmitPayloadEnhancer } from './utils';
|
|
6
|
-
import type { OrderAmountSnapshot, AddProductBookingInput, OrderIdentitySnapshot, OrderPaymentSource, OrderSnapshot, OrderSyncState, SyncPaymentsToOrderParams, SyncPaymentsToOrderResult, SubmitSalesOrderParams, OrderProduct, OrderProductIdentity, OrderSummary, OrderTempOrder, OrderPaymentData, OrderPaymentUpdateOptions, OrderPaymentUpdateResult, SendCustomerPayLinkParams, UpdateTempOrderCustomerInput, OrderCustomerPatch, OrderCustomerView, ReplaceTempOrderOptions, OrderPromotionEvaluator, OrderGiftSelectResolver, OrderUnfulfilledPromotion, OrderLastGiftActions } from './types';
|
|
6
|
+
import type { OrderAmountSnapshot, AddProductBookingInput, AddProductToOrderOptions, OrderIdentitySnapshot, OrderPaymentSource, OrderSnapshot, OrderSyncState, SyncPaymentsToOrderParams, SyncPaymentsToOrderResult, SubmitSalesOrderParams, OrderProduct, OrderProductIdentity, OrderSummary, OrderTempOrder, OrderPaymentData, OrderPaymentUpdateOptions, OrderPaymentUpdateResult, SendCustomerPayLinkParams, UpdateTempOrderCustomerInput, OrderCustomerPatch, OrderCustomerView, ReplaceTempOrderOptions, OrderPromotionEvaluator, OrderGiftSelectResolver, OrderUnfulfilledPromotion, OrderLastGiftActions, RemoveProductsFromOrderOptions } from './types';
|
|
7
7
|
import type { ICustomer } from '../AccountList/types';
|
|
8
8
|
import type { Discount } from '../Discount/types';
|
|
9
9
|
import { UnavailableReason } from '../Rules/types';
|
|
@@ -156,6 +156,13 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
156
156
|
* sessionStorage 的 cacheId 分桶。写入失败由 BaseModule 静默降级。
|
|
157
157
|
*/
|
|
158
158
|
persistTempOrder(): void;
|
|
159
|
+
/**
|
|
160
|
+
* 终止当前模块实例的 sessionStorage 订单恢复会话。
|
|
161
|
+
*
|
|
162
|
+
* 支付跳转等终态场景需要同时删除当前 cacheId 的 tempOrder 快照,并关闭
|
|
163
|
+
* 本实例后续异步任务的再次写入;新模块实例初始化时会按 otherParams 重新启用。
|
|
164
|
+
*/
|
|
165
|
+
clearSessionStoragePersistedTempOrder(): void;
|
|
159
166
|
consumeSessionStorageRestore(): Record<string, any> | null;
|
|
160
167
|
notifyTempOrderChanged(): void;
|
|
161
168
|
private createDefaultTempOrderInstance;
|
|
@@ -396,7 +403,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
396
403
|
* await order.addProductToOrder({ product_id: 1, num: 3 }, { start_time: '10:00' });
|
|
397
404
|
* // → tempOrder.products 新增 3 行,每行 num=1,bookings 3 条
|
|
398
405
|
*/
|
|
399
|
-
addProductToOrder(product: Partial<OrderProduct> & OrderProductIdentity, booking?: AddProductBookingInput): Promise<OrderProduct[]>;
|
|
406
|
+
addProductToOrder(product: Partial<OrderProduct> & OrderProductIdentity, booking?: AddProductBookingInput, options?: AddProductToOrderOptions): Promise<OrderProduct[]>;
|
|
400
407
|
/**
|
|
401
408
|
* 批量添加商品行,所有行追加完成后只触发一次促销、折扣、summary 重算与持久化。
|
|
402
409
|
*
|
|
@@ -434,8 +441,8 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
434
441
|
* @example
|
|
435
442
|
* await order.removeProductsFromOrder([identityA, identityB]);
|
|
436
443
|
*/
|
|
437
|
-
removeProductsFromOrder(identities: OrderProductIdentity[]): Promise<OrderProduct[]>;
|
|
438
|
-
removeProductFromOrder(identity: OrderProductIdentity): Promise<OrderProduct[]>;
|
|
444
|
+
removeProductsFromOrder(identities: OrderProductIdentity[], options?: RemoveProductsFromOrderOptions): Promise<OrderProduct[]>;
|
|
445
|
+
removeProductFromOrder(identity: OrderProductIdentity, options?: RemoveProductsFromOrderOptions): Promise<OrderProduct[]>;
|
|
439
446
|
/**
|
|
440
447
|
* 仅清空购物车行(products / bookings),不重置整单。
|
|
441
448
|
* 用于「仅清空商品」;客户、备注、支付等协议字段保持不变。
|
|
@@ -575,4 +582,4 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
575
582
|
getOrderInfo(order_id: number): Promise<any>;
|
|
576
583
|
getVouchers(): any[];
|
|
577
584
|
}
|
|
578
|
-
export type { UpdateOrderBookingParams, UpdateOrderProductParams, UpdateOrderProductQuantityParams, } from './types';
|
|
585
|
+
export type { AddProductToOrderOptions, RemoveProductsFromOrderOptions, UpdateOrderBookingParams, UpdateOrderProductParams, UpdateOrderProductQuantityParams, } from './types';
|
|
@@ -44,6 +44,7 @@ var import_utils3 = require("../../solution/ScanOrder/utils");
|
|
|
44
44
|
var import_manualProductDiscount = require("./utils/manualProductDiscount");
|
|
45
45
|
var import_bundleDiscountHydration = require("./utils/bundleDiscountHydration");
|
|
46
46
|
var import_orderCollectionIdentity = require("./utils/orderCollectionIdentity");
|
|
47
|
+
var import_discountProductLineIdentity = require("./utils/discountProductLineIdentity");
|
|
47
48
|
var import_Discount = require("../Discount");
|
|
48
49
|
var import_Rules = require("../Rules");
|
|
49
50
|
var import_types2 = require("../Rules/types");
|
|
@@ -65,8 +66,7 @@ function isSameDiscountList(left, right) {
|
|
|
65
66
|
);
|
|
66
67
|
}
|
|
67
68
|
function isManualProductDiscountProduct(product) {
|
|
68
|
-
|
|
69
|
-
return ((_a = product == null ? void 0 : product.metadata) == null ? void 0 : _a.is_manual_discount) === true || ((_b = product == null ? void 0 : product.metadata) == null ? void 0 : _b.is_manual_discount) === 1 || ((product == null ? void 0 : product.discount_list) || []).some((item) => (item == null ? void 0 : item.type) === "product");
|
|
69
|
+
return (0, import_manualProductDiscount.hasManualProductDiscountFlag)(product == null ? void 0 : product.metadata) || ((product == null ? void 0 : product.discount_list) || []).some((item) => (item == null ? void 0 : item.type) === "product");
|
|
70
70
|
}
|
|
71
71
|
var ORDER_PAYMENT_STATUS_PAID = "paid";
|
|
72
72
|
var ORDER_PAYMENT_STATUS_PENDING = "payment_pending";
|
|
@@ -136,7 +136,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
136
136
|
this.draftPersistEnabled = true;
|
|
137
137
|
}
|
|
138
138
|
collectHydratedEditDiscounts(products) {
|
|
139
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
139
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
140
140
|
const discountMap = /* @__PURE__ */ new Map();
|
|
141
141
|
for (const product of (0, import_bundleDiscountHydration.expandHydratedProductsForDiscountCollection)(products)) {
|
|
142
142
|
for (const item of (product == null ? void 0 : product.discount_list) || []) {
|
|
@@ -151,16 +151,18 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
151
151
|
const key = Number.isFinite(id) ? id : String(resourceId);
|
|
152
152
|
const title = ((_b = item == null ? void 0 : item.discount) == null ? void 0 : _b.title) ?? {};
|
|
153
153
|
const amount = Number((item == null ? void 0 : item.amount) || 0);
|
|
154
|
+
const discountCardType = (_c = item == null ? void 0 : item.discount) == null ? void 0 : _c.discount_card_type;
|
|
155
|
+
const parValue = isDiscountCard ? discountCardType === "fixed_amount" ? ((_d = item == null ? void 0 : item.discount) == null ? void 0 : _d.fixed_amount) ?? (item == null ? void 0 : item.amount) : (_e = item == null ? void 0 : item.discount) == null ? void 0 : _e.percent : "0";
|
|
154
156
|
const detail = {
|
|
155
157
|
amount: String((item == null ? void 0 : item.amount) ?? "0"),
|
|
156
158
|
type,
|
|
157
159
|
resource_id: key,
|
|
158
160
|
title,
|
|
159
|
-
original_amount: String(((
|
|
160
|
-
num: Number(((
|
|
161
|
+
original_amount: String(((_f = item == null ? void 0 : item.discount) == null ? void 0 : _f.original_amount) ?? (item == null ? void 0 : item.amount) ?? "0"),
|
|
162
|
+
num: Number(((_g = item == null ? void 0 : item.metadata) == null ? void 0 : _g.num) ?? (product == null ? void 0 : product.num) ?? (product == null ? void 0 : product.product_quantity) ?? 1) || 1,
|
|
161
163
|
metadata: item == null ? void 0 : item.metadata,
|
|
162
164
|
discount: item == null ? void 0 : item.discount,
|
|
163
|
-
_num: Number(((
|
|
165
|
+
_num: Number(((_h = item == null ? void 0 : item.metadata) == null ? void 0 : _h.num) ?? (product == null ? void 0 : product.num) ?? (product == null ? void 0 : product.product_quantity) ?? 1) || 1
|
|
164
166
|
};
|
|
165
167
|
const existed = discountMap.get(key);
|
|
166
168
|
if (existed) {
|
|
@@ -169,7 +171,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
169
171
|
if (!isDiscountCard) {
|
|
170
172
|
existed.balance = String(Number(existed.balance || 0) + amount);
|
|
171
173
|
}
|
|
172
|
-
const productIds = Array.isArray((
|
|
174
|
+
const productIds = Array.isArray((_i = existed.limited_relation_product_data) == null ? void 0 : _i.product_ids) ? existed.limited_relation_product_data.product_ids : [];
|
|
173
175
|
if ((product == null ? void 0 : product.product_id) !== void 0 && (product == null ? void 0 : product.product_id) !== null && !productIds.includes(product.product_id)) {
|
|
174
176
|
existed.limited_relation_product_data = {
|
|
175
177
|
...existed.limited_relation_product_data || {},
|
|
@@ -192,9 +194,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
192
194
|
encoded: "",
|
|
193
195
|
code: "",
|
|
194
196
|
tag: type,
|
|
195
|
-
product_id: ((
|
|
197
|
+
product_id: ((_j = item == null ? void 0 : item.discount) == null ? void 0 : _j.discount_product_id) ?? (product == null ? void 0 : product.product_id) ?? 0,
|
|
196
198
|
relation_type: "",
|
|
197
|
-
par_value: String(
|
|
199
|
+
par_value: String(parValue ?? "0"),
|
|
198
200
|
used_par_value: String((item == null ? void 0 : item.amount) ?? "0"),
|
|
199
201
|
limit_status: "enable",
|
|
200
202
|
limited_relation_product_data: {
|
|
@@ -224,8 +226,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
224
226
|
discount: {
|
|
225
227
|
...(item == null ? void 0 : item.discount) || {},
|
|
226
228
|
resource_id: key,
|
|
227
|
-
discount_product_id: ((
|
|
228
|
-
discount_calculation_mode: ((
|
|
229
|
+
discount_product_id: ((_k = item == null ? void 0 : item.discount) == null ? void 0 : _k.discount_product_id) ?? (product == null ? void 0 : product.product_id) ?? 0,
|
|
230
|
+
discount_calculation_mode: ((_l = item == null ? void 0 : item.discount) == null ? void 0 : _l.discount_calculation_mode) ?? "item_level"
|
|
229
231
|
},
|
|
230
232
|
applicableProductDetails: [detail],
|
|
231
233
|
appliedProductDetails: [detail]
|
|
@@ -277,7 +279,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
277
279
|
const isPersistedBundleProduct = isPersistedProduct || Boolean(
|
|
278
280
|
(bundle == null ? void 0 : bundle.order_detail_id) || (bundle == null ? void 0 : bundle.orderDetailId) || (bundle == null ? void 0 : bundle.booking_id)
|
|
279
281
|
);
|
|
280
|
-
const bundleQty =
|
|
282
|
+
const bundleQty = Number(qty) || 1;
|
|
281
283
|
for (const pd of (bundle == null ? void 0 : bundle.discount_list) || []) {
|
|
282
284
|
addProductDiscountAmount(pd, bundleQty, isPersistedBundleProduct);
|
|
283
285
|
}
|
|
@@ -694,7 +696,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
694
696
|
};
|
|
695
697
|
})
|
|
696
698
|
);
|
|
697
|
-
|
|
699
|
+
(0, import_discountProductLineIdentity.ensureUniqueProductLineUids)(tempOrder);
|
|
700
|
+
(0, import_discountProductLineIdentity.consolidateDiscountFreeProductLines)(tempOrder);
|
|
698
701
|
}
|
|
699
702
|
if (result == null ? void 0 : result.discountList) {
|
|
700
703
|
OrderModule.populateSavedAmounts(
|
|
@@ -917,6 +920,52 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
917
920
|
cacheKey: ["tempOrder"]
|
|
918
921
|
});
|
|
919
922
|
}
|
|
923
|
+
/**
|
|
924
|
+
* 终止当前模块实例的 sessionStorage 订单恢复会话。
|
|
925
|
+
*
|
|
926
|
+
* 支付跳转等终态场景需要同时删除当前 cacheId 的 tempOrder 快照,并关闭
|
|
927
|
+
* 本实例后续异步任务的再次写入;新模块实例初始化时会按 otherParams 重新启用。
|
|
928
|
+
*/
|
|
929
|
+
clearSessionStoragePersistedTempOrder() {
|
|
930
|
+
var _a;
|
|
931
|
+
this.sessionStorageRestoreRecord = null;
|
|
932
|
+
if (!this.sessionStoragePersistEnabled)
|
|
933
|
+
return;
|
|
934
|
+
this.sessionStoragePersistEnabled = false;
|
|
935
|
+
if (!this.cacheId || !this.fatherModule || !((_a = this.window) == null ? void 0 : _a.sessionStorage)) {
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
const sessionStorage = this.window.sessionStorage;
|
|
939
|
+
const rawCacheData = sessionStorage.getItem(this.fatherModule);
|
|
940
|
+
if (!rawCacheData)
|
|
941
|
+
return;
|
|
942
|
+
let parsedCacheData;
|
|
943
|
+
try {
|
|
944
|
+
parsedCacheData = JSON.parse(rawCacheData);
|
|
945
|
+
} catch {
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
if (!parsedCacheData || typeof parsedCacheData !== "object" || Array.isArray(parsedCacheData)) {
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
const cacheRoot = parsedCacheData;
|
|
952
|
+
const cacheBucket = cacheRoot[this.cacheId];
|
|
953
|
+
if (!cacheBucket || typeof cacheBucket !== "object" || Array.isArray(cacheBucket)) {
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
const moduleBucket = cacheBucket[this.name];
|
|
957
|
+
if (!moduleBucket || typeof moduleBucket !== "object" || Array.isArray(moduleBucket)) {
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
delete moduleBucket.tempOrder;
|
|
961
|
+
if (Object.keys(moduleBucket).length === 0) {
|
|
962
|
+
delete cacheBucket[this.name];
|
|
963
|
+
}
|
|
964
|
+
if (Object.keys(cacheBucket).length === 0) {
|
|
965
|
+
delete cacheRoot[this.cacheId];
|
|
966
|
+
}
|
|
967
|
+
sessionStorage.setItem(this.fatherModule, JSON.stringify(cacheRoot));
|
|
968
|
+
}
|
|
920
969
|
consumeSessionStorageRestore() {
|
|
921
970
|
const record = this.sessionStorageRestoreRecord;
|
|
922
971
|
this.sessionStorageRestoreRecord = null;
|
|
@@ -1922,6 +1971,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1922
1971
|
tempOrder.products = (tempOrder.products || []).map(
|
|
1923
1972
|
(product) => this.sanitizeOrderProductForTempOrder(product)
|
|
1924
1973
|
);
|
|
1974
|
+
(0, import_discountProductLineIdentity.ensureUniqueProductLineUids)(tempOrder);
|
|
1925
1975
|
}
|
|
1926
1976
|
ensureExtend(tempOrder) {
|
|
1927
1977
|
if (!tempOrder._extend || typeof tempOrder._extend !== "object") {
|
|
@@ -2594,6 +2644,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2594
2644
|
delete tempOrder._extend.customerSnapshot;
|
|
2595
2645
|
this.store.availableWalletIds = [];
|
|
2596
2646
|
this.clearCustomerBoundDiscounts(tempOrder);
|
|
2647
|
+
this.applyDiscount();
|
|
2597
2648
|
this.persistTempOrder();
|
|
2598
2649
|
this.saveDraftInBackground();
|
|
2599
2650
|
this.core.effects.emit(import_types.OrderHooks.OnOrderCustomerChange, null);
|
|
@@ -2958,8 +3009,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2958
3009
|
* @example
|
|
2959
3010
|
* await this.appendProductLineToTempOrder(tempOrder, { product_id: 1, num: 1 }, booking);
|
|
2960
3011
|
*/
|
|
2961
|
-
async appendProductLineToTempOrder(tempOrder, product, booking) {
|
|
2962
|
-
var _a, _b;
|
|
3012
|
+
async appendProductLineToTempOrder(tempOrder, product, booking, options) {
|
|
3013
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
2963
3014
|
const linked = booking ? this.createLinkedProductAndBooking({ product, booking }) : null;
|
|
2964
3015
|
const productToAdd = (linked == null ? void 0 : linked.product) || product;
|
|
2965
3016
|
const incomingFingerprint = (0, import_utils3.buildProductLineFingerprint)(
|
|
@@ -2970,7 +3021,12 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2970
3021
|
productToAdd,
|
|
2971
3022
|
(0, import_utils3.normalizeOrderProduct)(productToAdd)
|
|
2972
3023
|
);
|
|
2973
|
-
const
|
|
3024
|
+
const isWeighingProduct = Number(((_a = normalizedIncoming.product_sku) == null ? void 0 : _a.open_sold_weight) || 0) === 1;
|
|
3025
|
+
const netWeight = Number((_b = normalizedIncoming.product_sku) == null ? void 0 : _b.net_weight);
|
|
3026
|
+
if (isWeighingProduct && (!Number.isFinite(netWeight) || netWeight <= 0)) {
|
|
3027
|
+
throw new Error("[Order] 称重商品净重必须大于 0");
|
|
3028
|
+
}
|
|
3029
|
+
const incomingBookingUid = productToAdd.booking_uid ?? ((_c = productToAdd.metadata) == null ? void 0 : _c.booking_uid);
|
|
2974
3030
|
if (incomingBookingUid !== void 0 && incomingBookingUid !== null && String(incomingBookingUid) !== "") {
|
|
2975
3031
|
normalizedIncoming.booking_uid = String(incomingBookingUid);
|
|
2976
3032
|
normalizedIncoming.metadata = {
|
|
@@ -2985,14 +3041,18 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2985
3041
|
normalizedIncoming
|
|
2986
3042
|
);
|
|
2987
3043
|
const hasIncomingProductNote = this.hasOrderProductLineNote(normalizedIncoming);
|
|
3044
|
+
const hasIncomingManualProductDiscount = isManualProductDiscountProduct(normalizedIncoming);
|
|
2988
3045
|
const hasIncomingBookingUid = this.hasLinkedBookingUid(productToAdd) || this.hasLinkedBookingUid(normalizedIncoming);
|
|
2989
|
-
const
|
|
3046
|
+
const isIncomingCustomItem = ((_e = (_d = productToAdd.metadata) == null ? void 0 : _d.origin) == null ? void 0 : _e.isCustomItem) === true || ((_f = productToAdd._origin) == null ? void 0 : _f.isCustomItem) === true;
|
|
3047
|
+
const shouldForceNewLine = !!booking || hasIncomingProductNote || hasIncomingManualProductDiscount || hasIncomingBookingUid || isIncomingCustomItem || isWeighingProduct;
|
|
2990
3048
|
const matchedIndex = hasIncomingGoodPass || shouldForceNewLine ? -1 : tempOrder.products.findIndex((item) => {
|
|
2991
3049
|
var _a2;
|
|
2992
3050
|
if (this.hasGoodPassDiscount(item))
|
|
2993
3051
|
return false;
|
|
2994
3052
|
if (this.hasOrderProductLineNote(item))
|
|
2995
3053
|
return false;
|
|
3054
|
+
if (isManualProductDiscountProduct(item))
|
|
3055
|
+
return false;
|
|
2996
3056
|
if (item.order_detail_id !== void 0 && item.order_detail_id !== null)
|
|
2997
3057
|
return false;
|
|
2998
3058
|
if ((_a2 = item.metadata) == null ? void 0 : _a2.is_edit_for_runtime)
|
|
@@ -3035,10 +3095,19 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
3035
3095
|
booking_uid: linked.bookingUid
|
|
3036
3096
|
};
|
|
3037
3097
|
}
|
|
3038
|
-
|
|
3098
|
+
const insertAtIndex = options == null ? void 0 : options.insertAtIndex;
|
|
3099
|
+
if (Number.isInteger(insertAtIndex) && Number(insertAtIndex) >= 0 && Number(insertAtIndex) < tempOrder.products.length) {
|
|
3100
|
+
tempOrder.products = [
|
|
3101
|
+
...tempOrder.products.slice(0, insertAtIndex),
|
|
3102
|
+
normalizedIncoming,
|
|
3103
|
+
...tempOrder.products.slice(insertAtIndex)
|
|
3104
|
+
];
|
|
3105
|
+
} else {
|
|
3106
|
+
tempOrder.products = [...tempOrder.products, normalizedIncoming];
|
|
3107
|
+
}
|
|
3039
3108
|
} else {
|
|
3040
3109
|
const targetProduct = matchedProduct;
|
|
3041
|
-
const targetUid = (
|
|
3110
|
+
const targetUid = (_g = targetProduct.metadata) == null ? void 0 : _g.unique_identification_number;
|
|
3042
3111
|
const normalizedProduct = (0, import_utils.mergeOrderProductDisplayFields)(
|
|
3043
3112
|
targetProduct,
|
|
3044
3113
|
(0, import_utils3.normalizeOrderProduct)({
|
|
@@ -3053,17 +3122,10 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
3053
3122
|
normalizedProduct.discount_list
|
|
3054
3123
|
);
|
|
3055
3124
|
const nextNum = (0, import_utils3.getSafeProductNum)(targetProduct.num + normalizedProduct.num);
|
|
3056
|
-
const targetIsManualProductDiscount = isManualProductDiscountProduct(targetProduct);
|
|
3057
|
-
const nextDiscountList = targetIsManualProductDiscount ? (0, import_utils.normalizeOrderProductDiscountList)(
|
|
3058
|
-
(0, import_manualProductDiscount.syncManualProductDiscountProductNum)(
|
|
3059
|
-
targetProduct.discount_list,
|
|
3060
|
-
nextNum
|
|
3061
|
-
)
|
|
3062
|
-
) : normalizedProduct.discount_list;
|
|
3063
3125
|
tempOrder.products[matchedIndex] = {
|
|
3064
3126
|
...targetProduct,
|
|
3065
3127
|
...normalizedProduct,
|
|
3066
|
-
discount_list:
|
|
3128
|
+
discount_list: normalizedProduct.discount_list,
|
|
3067
3129
|
metadata: {
|
|
3068
3130
|
...targetProduct.metadata || {},
|
|
3069
3131
|
...normalizedProduct.metadata || {},
|
|
@@ -3074,16 +3136,6 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
3074
3136
|
...normalizedProduct.product_sku || { option: [] },
|
|
3075
3137
|
option: (0, import_utils.getProductSkuOptions)(normalizedProduct)
|
|
3076
3138
|
},
|
|
3077
|
-
...targetIsManualProductDiscount ? {
|
|
3078
|
-
selling_price: targetProduct.selling_price,
|
|
3079
|
-
original_price: targetProduct.original_price,
|
|
3080
|
-
payment_price: targetProduct.payment_price,
|
|
3081
|
-
metadata: {
|
|
3082
|
-
...normalizedProduct.metadata || {},
|
|
3083
|
-
...targetProduct.metadata || {},
|
|
3084
|
-
unique_identification_number: targetUid
|
|
3085
|
-
}
|
|
3086
|
-
} : {},
|
|
3087
3139
|
note: targetProduct.note,
|
|
3088
3140
|
order_detail_id: targetProduct.order_detail_id,
|
|
3089
3141
|
num: nextNum
|
|
@@ -3094,6 +3146,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
3094
3146
|
tempOrder.products[matchedIndex],
|
|
3095
3147
|
targetUid
|
|
3096
3148
|
);
|
|
3149
|
+
if (matchedIndex !== tempOrder.products.length - 1) {
|
|
3150
|
+
const mergedProduct = tempOrder.products[matchedIndex];
|
|
3151
|
+
tempOrder.products = [
|
|
3152
|
+
...tempOrder.products.slice(0, matchedIndex),
|
|
3153
|
+
...tempOrder.products.slice(matchedIndex + 1),
|
|
3154
|
+
mergedProduct
|
|
3155
|
+
];
|
|
3156
|
+
}
|
|
3097
3157
|
}
|
|
3098
3158
|
if (linked) {
|
|
3099
3159
|
tempOrder.bookings = [...tempOrder.bookings || [], linked.booking];
|
|
@@ -3106,8 +3166,15 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
3106
3166
|
* await order.addProductToOrder({ product_id: 1, num: 3 }, { start_time: '10:00' });
|
|
3107
3167
|
* // → tempOrder.products 新增 3 行,每行 num=1,bookings 3 条
|
|
3108
3168
|
*/
|
|
3109
|
-
async addProductToOrder(product, booking) {
|
|
3169
|
+
async addProductToOrder(product, booking, options) {
|
|
3110
3170
|
const tempOrder = this.ensureTempOrder();
|
|
3171
|
+
const insertAfterProductUid = options == null ? void 0 : options.insertAfterProductUid;
|
|
3172
|
+
const anchorIndex = booking && insertAfterProductUid ? tempOrder.products.findIndex((line) => {
|
|
3173
|
+
var _a;
|
|
3174
|
+
const uid = ((_a = line.metadata) == null ? void 0 : _a.unique_identification_number) || line.unique_identification_number;
|
|
3175
|
+
return uid !== void 0 && uid !== null && String(uid) === String(insertAfterProductUid);
|
|
3176
|
+
}) : -1;
|
|
3177
|
+
let insertAtIndex = anchorIndex >= 0 ? anchorIndex + 1 : void 0;
|
|
3111
3178
|
if (booking) {
|
|
3112
3179
|
const productRecord = product;
|
|
3113
3180
|
const splitCount = (0, import_utils3.getSafeProductNum)(
|
|
@@ -3121,14 +3188,22 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
3121
3188
|
await this.appendProductLineToTempOrder(
|
|
3122
3189
|
tempOrder,
|
|
3123
3190
|
singleLine,
|
|
3124
|
-
(0, import_lodash_es.cloneDeep)(booking)
|
|
3191
|
+
(0, import_lodash_es.cloneDeep)(booking),
|
|
3192
|
+
{ insertAtIndex }
|
|
3125
3193
|
);
|
|
3194
|
+
if (insertAtIndex !== void 0)
|
|
3195
|
+
insertAtIndex += 1;
|
|
3126
3196
|
}
|
|
3127
3197
|
await this.finalizeProductOrderMutation(tempOrder);
|
|
3128
3198
|
return tempOrder.products;
|
|
3129
3199
|
}
|
|
3130
3200
|
}
|
|
3131
|
-
await this.appendProductLineToTempOrder(
|
|
3201
|
+
await this.appendProductLineToTempOrder(
|
|
3202
|
+
tempOrder,
|
|
3203
|
+
product,
|
|
3204
|
+
booking,
|
|
3205
|
+
{ insertAtIndex }
|
|
3206
|
+
);
|
|
3132
3207
|
await this.finalizeProductOrderMutation(tempOrder);
|
|
3133
3208
|
this.logInfo("addProductToOrder success", {
|
|
3134
3209
|
product_id: product.product_id,
|
|
@@ -3610,7 +3685,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
3610
3685
|
* @example
|
|
3611
3686
|
* await order.removeProductsFromOrder([identityA, identityB]);
|
|
3612
3687
|
*/
|
|
3613
|
-
async removeProductsFromOrder(identities) {
|
|
3688
|
+
async removeProductsFromOrder(identities, options) {
|
|
3614
3689
|
var _a, _b, _c, _d;
|
|
3615
3690
|
const tempOrder = this.ensureTempOrder();
|
|
3616
3691
|
this.repairDuplicateProductLineIdentities(tempOrder);
|
|
@@ -3619,6 +3694,13 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
3619
3694
|
}
|
|
3620
3695
|
const productsBeforeRemove = tempOrder.products || [];
|
|
3621
3696
|
const bookingsBeforeRemove = tempOrder.bookings || [];
|
|
3697
|
+
const preserveDisplayPosition = options == null ? void 0 : options.preserveDisplayPosition;
|
|
3698
|
+
const anchorIndex = preserveDisplayPosition ? productsBeforeRemove.findIndex((item) => {
|
|
3699
|
+
var _a2;
|
|
3700
|
+
const uid = ((_a2 = item.metadata) == null ? void 0 : _a2.unique_identification_number) || item.unique_identification_number;
|
|
3701
|
+
return uid !== void 0 && uid !== null && String(uid) === String(preserveDisplayPosition.anchorProductUid);
|
|
3702
|
+
}) : -1;
|
|
3703
|
+
const productsAfterAnchor = anchorIndex >= 0 ? productsBeforeRemove.slice(anchorIndex + 1) : [];
|
|
3622
3704
|
const matchedProducts = productsBeforeRemove.filter((item) => {
|
|
3623
3705
|
return identities.some((identity) => (0, import_utils3.isIdentityMatch)(item, identity));
|
|
3624
3706
|
});
|
|
@@ -3659,14 +3741,37 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
3659
3741
|
return !(isAddTimeProduct && lineBookingUid !== void 0 && lineBookingUid !== null && linkedBookingUidsToRemove.has(String(lineBookingUid)));
|
|
3660
3742
|
});
|
|
3661
3743
|
}
|
|
3744
|
+
if (preserveDisplayPosition && anchorIndex >= 0) {
|
|
3745
|
+
const remainingProductIndex = tempOrder.products.findIndex((item) => {
|
|
3746
|
+
var _a2;
|
|
3747
|
+
const uid = ((_a2 = item.metadata) == null ? void 0 : _a2.unique_identification_number) || item.unique_identification_number;
|
|
3748
|
+
return uid !== void 0 && uid !== null && String(uid) === String(preserveDisplayPosition.remainingProductUid);
|
|
3749
|
+
});
|
|
3750
|
+
if (remainingProductIndex >= 0) {
|
|
3751
|
+
const remainingProduct = tempOrder.products[remainingProductIndex];
|
|
3752
|
+
const nextProduct = productsAfterAnchor.find(
|
|
3753
|
+
(item) => tempOrder.products.includes(item)
|
|
3754
|
+
);
|
|
3755
|
+
const productsWithoutRemaining = [
|
|
3756
|
+
...tempOrder.products.slice(0, remainingProductIndex),
|
|
3757
|
+
...tempOrder.products.slice(remainingProductIndex + 1)
|
|
3758
|
+
];
|
|
3759
|
+
const nextProductIndex = nextProduct ? productsWithoutRemaining.indexOf(nextProduct) : -1;
|
|
3760
|
+
tempOrder.products = nextProductIndex >= 0 ? [
|
|
3761
|
+
...productsWithoutRemaining.slice(0, nextProductIndex),
|
|
3762
|
+
remainingProduct,
|
|
3763
|
+
...productsWithoutRemaining.slice(nextProductIndex)
|
|
3764
|
+
] : [...productsWithoutRemaining, remainingProduct];
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3662
3767
|
await this.finalizeAfterProductsMutation(tempOrder);
|
|
3663
3768
|
this.logInfo("removeProductsFromOrder success", {
|
|
3664
3769
|
identities
|
|
3665
3770
|
});
|
|
3666
3771
|
return tempOrder.products;
|
|
3667
3772
|
}
|
|
3668
|
-
async removeProductFromOrder(identity) {
|
|
3669
|
-
return this.removeProductsFromOrder([identity]);
|
|
3773
|
+
async removeProductFromOrder(identity, options) {
|
|
3774
|
+
return this.removeProductsFromOrder([identity], options);
|
|
3670
3775
|
}
|
|
3671
3776
|
/**
|
|
3672
3777
|
* 仅清空购物车行(products / bookings),不重置整单。
|
|
@@ -321,6 +321,24 @@ export interface ReplaceTempOrderOptions {
|
|
|
321
321
|
saveDraft?: boolean;
|
|
322
322
|
}
|
|
323
323
|
export type AddProductBookingInput = Record<string, any>;
|
|
324
|
+
/**
|
|
325
|
+
* 添加商品行时的顺序控制。
|
|
326
|
+
* 默认追加到 products 尾部;预约数量同步可插入到既有预约组的最新行之后,
|
|
327
|
+
* 避免步进器修改数量改变该组展示位置。
|
|
328
|
+
*/
|
|
329
|
+
export interface AddProductToOrderOptions {
|
|
330
|
+
insertAfterProductUid?: string;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* 删除商品后的可选位置保持策略。
|
|
334
|
+
* 预约合并行减量时仍删除最新真实行,再把剩余代表行移动到被删除代表行原位置。
|
|
335
|
+
*/
|
|
336
|
+
export interface RemoveProductsFromOrderOptions {
|
|
337
|
+
preserveDisplayPosition?: {
|
|
338
|
+
remainingProductUid: string;
|
|
339
|
+
anchorProductUid: string;
|
|
340
|
+
};
|
|
341
|
+
}
|
|
324
342
|
/** 更新购物车行:仅传 SKU 时命中同 SKU 第一行;多行同 SKU 须传 unique_identification_number / 选项指纹等 */
|
|
325
343
|
export interface UpdateOrderProductParams {
|
|
326
344
|
product_id: number | null;
|
|
@@ -726,17 +744,22 @@ export interface OrderModuleAPI {
|
|
|
726
744
|
updateTempOrderCustomer: (customer: UpdateTempOrderCustomerInput) => OrderSnapshot['customer'];
|
|
727
745
|
updateTempOrderBuzzer: (buzzer: string) => string;
|
|
728
746
|
updateTempOrderContactsInfo: (contactsInfo: Record<string, any> | null) => Record<string, any> | null;
|
|
729
|
-
addProductToOrder: (product: Partial<OrderProduct> & OrderProductIdentity, booking?: AddProductBookingInput) => Promise<OrderProduct[]>;
|
|
747
|
+
addProductToOrder: (product: Partial<OrderProduct> & OrderProductIdentity, booking?: AddProductBookingInput, options?: AddProductToOrderOptions) => Promise<OrderProduct[]>;
|
|
730
748
|
updateOrderProduct: (params: UpdateOrderProductParams) => Promise<OrderProduct[]>;
|
|
731
749
|
/** 批量更新购物车行,末尾仅触发一次促销重算与 summary 刷新 */
|
|
732
750
|
updateOrderProducts: (paramsList: UpdateOrderProductParams[]) => Promise<OrderProduct[]>;
|
|
733
751
|
updateOrderProductQuantity: (params: UpdateOrderProductQuantityParams) => Promise<OrderProduct[]>;
|
|
734
752
|
updateOrderBooking: (params: UpdateOrderBookingParams) => any[];
|
|
735
|
-
removeProductFromOrder: (identity: OrderProductIdentity) => Promise<OrderProduct[]>;
|
|
753
|
+
removeProductFromOrder: (identity: OrderProductIdentity, options?: RemoveProductsFromOrderOptions) => Promise<OrderProduct[]>;
|
|
736
754
|
/** 批量删除购物车行,末尾仅触发一次促销重算与 summary 刷新 */
|
|
737
|
-
removeProductsFromOrder: (identities: OrderProductIdentity[]) => Promise<OrderProduct[]>;
|
|
755
|
+
removeProductsFromOrder: (identities: OrderProductIdentity[], options?: RemoveProductsFromOrderOptions) => Promise<OrderProduct[]>;
|
|
738
756
|
/** 在显式开启时保存当前 tempOrder 的 sessionStorage 会话快照。 */
|
|
739
757
|
persistTempOrder: () => void;
|
|
758
|
+
/**
|
|
759
|
+
* 删除当前 cacheId 的 tempOrder 会话快照,并阻止当前模块实例再次写入。
|
|
760
|
+
* 新模块实例初始化时会按 otherParams 重新启用持久化。
|
|
761
|
+
*/
|
|
762
|
+
clearSessionStoragePersistedTempOrder: () => void;
|
|
740
763
|
/** 取出一次待恢复的 sessionStorage tempOrder 快照。 */
|
|
741
764
|
consumeSessionStorageRestore: () => Record<string, any> | null;
|
|
742
765
|
/**
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { OrderTempOrder } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* 商品券拆行的身份与回并工具。
|
|
4
|
+
*
|
|
5
|
+
* 背景:
|
|
6
|
+
* Rules.calcDiscount 应用 good_pass 时,会把一个数量行拆成若干「用券行」和
|
|
7
|
+
* 「普通余量行」。Rules 返回的拆分行可能复制同一份
|
|
8
|
+
* metadata.unique_identification_number,进而造成:
|
|
9
|
+
* - 按 UID 删除时同时命中多行;
|
|
10
|
+
* - UI 把不同券行视为同一个商品;
|
|
11
|
+
* - 后续商品更新无法准确定位目标行。
|
|
12
|
+
*
|
|
13
|
+
* 处理原则:
|
|
14
|
+
* 1. 拆分后每一行必须拥有独立 UID;
|
|
15
|
+
* 2. 原始拆分来源仅记录在 tempOrder._extend.productsByUid 的运行态索引中,
|
|
16
|
+
* 不写入商品 metadata,不污染提交协议;
|
|
17
|
+
* 3. 券被移除后,只回并被本工具确认来自同一折扣拆分源的行;
|
|
18
|
+
* 4. 预约、赠品、促销、手动折扣、备注、称重、holder 和已落库行均为硬边界。
|
|
19
|
+
*
|
|
20
|
+
* 两个导出函数都会原地更新 tempOrder。调用顺序必须是:
|
|
21
|
+
* ensureUniqueProductLineUids -> consolidateDiscountFreeProductLines。
|
|
22
|
+
*/
|
|
23
|
+
/** productsByUid 运行态记录中用于关联同一次折扣拆分的字段。 */
|
|
24
|
+
export declare const DISCOUNT_SPLIT_ORIGIN_UID_KEY = "discountSplitOriginUid";
|
|
25
|
+
/**
|
|
26
|
+
* 修复 Rules 折扣拆行复制 UID 的问题,并为拆分行记录共同来源。
|
|
27
|
+
*
|
|
28
|
+
* 第一条商品保留原 UID,后续重复行生成新 UUID;对应的 productsByUid.runtime
|
|
29
|
+
* 会复制原行 runtime(包括 origin),以保证删除、编辑及展示字段回读仍可工作。
|
|
30
|
+
*
|
|
31
|
+
* 没有 UID 的裸商品不会被补写 UID。这是为了兼容 Rules/mock 的历史协议形状;
|
|
32
|
+
* 正常 Order 写路径会在 normalize 阶段提前创建 UID。
|
|
33
|
+
*/
|
|
34
|
+
export declare function ensureUniqueProductLineUids(tempOrder: OrderTempOrder): void;
|
|
35
|
+
/**
|
|
36
|
+
* 把券移除后已经恢复原价的同源折扣拆分行重新合并。
|
|
37
|
+
*
|
|
38
|
+
* 此函数刻意不执行通用的「同 SKU 合并」:没有 discountSplitOriginUid 的普通行
|
|
39
|
+
* 可能是业务 hook、导入订单或其它流程主动拆开的,不能在折扣重算时擅自合并。
|
|
40
|
+
*
|
|
41
|
+
* payment_price、tax_fee、surcharge_fee 属于 summary 计算层的派生值。券行与普通行
|
|
42
|
+
* 在回并前可能暂时不同,因此它们不参与 mergeKey;原始价格、SKU/option/bundle
|
|
43
|
+
* 结构以及税务/商品类型配置仍参与比较,防止不同商品语义被误合并。
|
|
44
|
+
*/
|
|
45
|
+
export declare function consolidateDiscountFreeProductLines(tempOrder: OrderTempOrder): void;
|