@pisell/pisellos 2.3.51 → 2.3.54

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.
Files changed (37) hide show
  1. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +12 -10
  2. package/dist/modules/Order/index.d.ts +6 -6
  3. package/dist/modules/Order/index.js +140 -86
  4. package/dist/modules/Order/types.d.ts +21 -3
  5. package/dist/modules/Order/types.js +11 -0
  6. package/dist/modules/ProductList/index.js +1 -1
  7. package/dist/modules/Rules/index.js +19 -9
  8. package/dist/modules/SalesSummary/utils.js +7 -1
  9. package/dist/solution/BaseSales/index.d.ts +15 -5
  10. package/dist/solution/BaseSales/index.js +173 -135
  11. package/dist/solution/BaseSales/utils/cartPromotion.js +5 -3
  12. package/dist/solution/BookingByStep/index.d.ts +1 -1
  13. package/dist/solution/BookingTicket/types.d.ts +3 -0
  14. package/dist/solution/BookingTicket/utils/cartView.js +44 -32
  15. package/dist/solution/Sales/index.d.ts +1 -0
  16. package/dist/solution/Sales/index.js +16 -2
  17. package/dist/solution/UnifiedBookingSales/index.d.ts +0 -4
  18. package/dist/solution/UnifiedBookingSales/index.js +27 -26
  19. package/lib/model/strategy/adapter/promotion/index.js +0 -51
  20. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +3 -1
  21. package/lib/modules/Order/index.d.ts +6 -6
  22. package/lib/modules/Order/index.js +79 -12
  23. package/lib/modules/Order/types.d.ts +21 -3
  24. package/lib/modules/ProductList/index.js +9 -1
  25. package/lib/modules/Rules/index.js +16 -11
  26. package/lib/modules/SalesSummary/utils.js +5 -1
  27. package/lib/solution/BaseSales/index.d.ts +15 -5
  28. package/lib/solution/BaseSales/index.js +44 -11
  29. package/lib/solution/BaseSales/utils/cartPromotion.js +5 -2
  30. package/lib/solution/BookingByStep/index.d.ts +1 -1
  31. package/lib/solution/BookingTicket/types.d.ts +3 -0
  32. package/lib/solution/BookingTicket/utils/cartView.js +43 -30
  33. package/lib/solution/Sales/index.d.ts +1 -0
  34. package/lib/solution/Sales/index.js +15 -3
  35. package/lib/solution/UnifiedBookingSales/index.d.ts +0 -4
  36. package/lib/solution/UnifiedBookingSales/index.js +27 -26
  37. package/package.json +1 -1
@@ -468,11 +468,13 @@ function getProductLineNoteSignature(product) {
468
468
  }
469
469
 
470
470
  /**
471
- * 编辑订单时,历史行仍参与整车促销评估,但不能与本次新增行合并。
472
- * 以历史行 uid 作为促销前后的行边界;新增行返回空边界,仍保留原有合并语义。
471
+ * 称重行以及编辑订单历史行都有各自的促销合并边界。
473
472
  */
474
473
  function getPromotionLineMergeBoundary(product) {
475
- var _product$metadata3;
474
+ var _product$product_sku, _product$metadata3;
475
+ if (Number((product === null || product === void 0 || (_product$product_sku = product.product_sku) === null || _product$product_sku === void 0 ? void 0 : _product$product_sku.open_sold_weight) || 0) === 1) {
476
+ return "weighing:".concat(getOrderProductUid(product));
477
+ }
476
478
  if ((product === null || product === void 0 || (_product$metadata3 = product.metadata) === null || _product$metadata3 === void 0 ? void 0 : _product$metadata3.is_edit_for_runtime) !== true) return '';
477
479
  return "edit:".concat(getOrderProductUid(product));
478
480
  }
@@ -326,7 +326,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
326
326
  date: string;
327
327
  status: string;
328
328
  week: string;
329
- weekNum: 0 | 1 | 2 | 3 | 4 | 5 | 6;
329
+ weekNum: 0 | 5 | 2 | 1 | 6 | 4 | 3;
330
330
  }[]>;
331
331
  submitTimeSlot(timeSlots: TimeSliceItem): void;
332
332
  private getScheduleDataByIds;
@@ -118,12 +118,15 @@ export interface BookingTicketCartBookingView extends ISalesBooking {
118
118
  canAddTime: boolean;
119
119
  };
120
120
  }
121
+ export type BookingTicketCartLineView = BookingTicketCartBookingView | BookingTicketCartItemView;
121
122
  /**
122
123
  * BookingTicket 面向 UI 的购物车视图。
123
124
  */
124
125
  export interface BookingTicketCartView {
125
126
  bookings: BookingTicketCartBookingView[];
126
127
  items: BookingTicketCartItemView[];
128
+ /** 以 tempOrder.products 为唯一顺序真源的统一购物车行(旧到新)。 */
129
+ lines: BookingTicketCartLineView[];
127
130
  }
128
131
  /**
129
132
  * BookingTicket 面向 UI 的客户展示视图。
@@ -108,33 +108,28 @@ export function buildCartView(lines, bookings) {
108
108
  var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
109
109
  var productLines = Array.isArray(lines) ? lines : [];
110
110
  var bookingList = Array.isArray(bookings) ? bookings : [];
111
+ var childBookings = bookingList.filter(function (booking) {
112
+ return booking.parent_id !== 0;
113
+ });
114
+ var bookingsByProductUid = indexBookingsByProductUid(childBookings);
111
115
  var linesByUid = indexProductsByUid(productLines);
112
116
  var addTimeLinesByBookingUid = indexAddTimeProductsByBookingUid(productLines);
113
- var claimedUids = new Set();
114
117
  var claimedAddTimeLines = new Set();
115
- var cartBookings = bookingList.filter(function (booking) {
116
- return booking.parent_id !== 0;
117
- }).reduce(function (acc, booking) {
118
- var uid = booking === null || booking === void 0 ? void 0 : booking.product_uid;
118
+ childBookings.forEach(function (booking) {
119
119
  var bookingUid = getBookingUid(booking);
120
- var product = null;
121
- if (uid !== undefined && uid !== null && uid !== '') {
122
- var key = String(uid);
123
- if (!claimedUids.has(key)) {
124
- var line = linesByUid[key];
125
- // 赠品预约只作为提交数据保留,不在购物车顶部预约卡片区展示。
126
- if (isGiftProductLine(line)) return acc;
127
- if (line) {
128
- claimedUids.add(key);
129
- product = line;
130
- }
131
- }
132
- }
133
- var addTimeProduct = bookingUid ? addTimeLinesByBookingUid[bookingUid] || [] : [];
134
- addTimeProduct.forEach(function (line) {
135
- return claimedAddTimeLines.add(line);
120
+ if (!bookingUid) return;
121
+ (addTimeLinesByBookingUid[bookingUid] || []).forEach(function (line) {
122
+ claimedAddTimeLines.add(line);
136
123
  });
137
- acc.push(_objectSpread(_objectSpread({}, booking), {}, {
124
+ });
125
+ var claimedBookings = new Set();
126
+ var cartBookings = [];
127
+ var items = [];
128
+ var orderedLines = [];
129
+ var buildBookingView = function buildBookingView(booking, product) {
130
+ var bookingUid = getBookingUid(booking);
131
+ var addTimeProduct = bookingUid ? addTimeLinesByBookingUid[bookingUid] || [] : [];
132
+ return _objectSpread(_objectSpread({}, booking), {}, {
138
133
  _extend: {
139
134
  product: product,
140
135
  addTimeProduct: addTimeProduct,
@@ -147,21 +142,38 @@ export function buildCartView(lines, bookings) {
147
142
  shopOpeningHours: options.shopOpeningHours
148
143
  })
149
144
  }
150
- }));
151
- return acc;
152
- }, []);
153
- var items = productLines.filter(function (line) {
145
+ });
146
+ };
147
+ productLines.forEach(function (line) {
154
148
  var _line$metadata6;
155
- // 促销赠品行仅通过主商品 Gift 行展示,不在 items 单独占一行(对齐 legacy `isGift: !!_giftInfo`)
156
- if (isGiftProductLine(line)) return false;
157
- if (claimedAddTimeLines.has(line)) return false;
149
+ // 赠品由主商品 Gift 行展示;加时商品挂在 booking._extend.addTimeProduct。
150
+ if (isGiftProductLine(line) || claimedAddTimeLines.has(line)) return;
158
151
  var uid = (_line$metadata6 = line.metadata) === null || _line$metadata6 === void 0 ? void 0 : _line$metadata6.unique_identification_number;
159
- if (uid === undefined || uid === null || uid === '') return true;
160
- return !claimedUids.has(String(uid));
152
+ var matchedBooking = uid === undefined || uid === null || uid === '' ? undefined : (bookingsByProductUid[String(uid)] || []).find(function (booking) {
153
+ return !claimedBookings.has(booking);
154
+ });
155
+ if (matchedBooking) {
156
+ claimedBookings.add(matchedBooking);
157
+ var bookingView = buildBookingView(matchedBooking, line);
158
+ cartBookings.push(bookingView);
159
+ orderedLines.push(bookingView);
160
+ return;
161
+ }
162
+ items.push(line);
163
+ orderedLines.push(line);
164
+ });
165
+ childBookings.forEach(function (booking) {
166
+ if (claimedBookings.has(booking)) return;
167
+ var uid = booking === null || booking === void 0 ? void 0 : booking.product_uid;
168
+ var linkedProduct = uid === undefined || uid === null || uid === '' ? null : linesByUid[String(uid)] || null;
169
+ // 赠品预约只作为提交数据保留,不在购物车预约卡片区展示。
170
+ if (isGiftProductLine(linkedProduct)) return;
171
+ cartBookings.push(buildBookingView(booking, null));
161
172
  });
162
173
  return {
163
174
  bookings: cartBookings,
164
- items: items
175
+ items: items,
176
+ lines: orderedLines
165
177
  };
166
178
  }
167
179
  export function isWalkInCustomer(customer) {
@@ -95,6 +95,7 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
95
95
  * 2) 若无 active,返回已超时且仍占用中的预约(允许并发多条)
96
96
  * 3) 若仍无,返回最近一组未来预约(同 start_time 的并发预约全部返回)
97
97
  * 4) 最后兜底返回一条(避免资源有历史预约时完全无反馈)
98
+ * - includeAllFuture 时,保留选中预约并返回当天全部后续预约;仅详情接口启用。
98
99
  */
99
100
  private pickBookingsForCurrentPoint;
100
101
  /**
@@ -485,11 +485,13 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
485
485
  * 2) 若无 active,返回已超时且仍占用中的预约(允许并发多条)
486
486
  * 3) 若仍无,返回最近一组未来预约(同 start_time 的并发预约全部返回)
487
487
  * 4) 最后兜底返回一条(避免资源有历史预约时完全无反馈)
488
+ * - includeAllFuture 时,保留选中预约并返回当天全部后续预约;仅详情接口启用。
488
489
  */
489
490
  }, {
490
491
  key: "pickBookingsForCurrentPoint",
491
492
  value: function pickBookingsForCurrentPoint(current, bookings) {
492
493
  var _this4 = this;
494
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
493
495
  if (bookings.length === 0) return [];
494
496
  var currentDayStart = current.startOf('day');
495
497
  var currentDayEnd = current.endOf('day');
@@ -504,6 +506,13 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
504
506
  if (dayScopedBookings.length === 0) return [];
505
507
  var appendNextStartGroupIfNeeded = function appendNextStartGroupIfNeeded(selectedBookings) {
506
508
  if (selectedBookings.length === 0) return selectedBookings;
509
+ if (options.includeAllFuture) {
510
+ var allFutureBookings = dayScopedBookings.filter(function (booking) {
511
+ var startAt = _this4.toBookingDateTime(booking.start_date, booking.start_time);
512
+ return startAt.isValid() && startAt.isAfter(current);
513
+ });
514
+ return [].concat(_toConsumableArray(selectedBookings), _toConsumableArray(allFutureBookings));
515
+ }
507
516
  var shouldAppendNextGroup = selectedBookings.some(function (booking) {
508
517
  return booking.status === 'reserved' || booking.status === 'occupied';
509
518
  });
@@ -554,6 +563,7 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
554
563
  return startAt.isValid() && startAt.isAfter(current) && startAt.isSame(current, 'day');
555
564
  });
556
565
  if (upcoming.length > 0) {
566
+ if (options.includeAllFuture) return upcoming;
557
567
  var firstStartAt = this.toBookingDateTime(upcoming[0].start_date, upcoming[0].start_time).valueOf();
558
568
  var upcomingStartGroup = upcoming.filter(function (booking) {
559
569
  var startAt = _this4.toBookingDateTime(booking.start_date, booking.start_time);
@@ -907,7 +917,9 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
907
917
  unpaidBookingList = matchedBookingList.filter(function (booking) {
908
918
  return _this7.isUnpaidBooking(booking);
909
919
  });
910
- bookings = this.pickBookingsForCurrentPoint(current, this.normalizeResourceBookings(current, deviceCurrent, matchedBookingList));
920
+ bookings = this.pickBookingsForCurrentPoint(current, this.normalizeResourceBookings(current, deviceCurrent, matchedBookingList), {
921
+ includeAllFuture: true
922
+ });
911
923
  completedBookings = completedBookingList.map(function (booking) {
912
924
  return _objectSpread(_objectSpread({}, booking), {}, {
913
925
  status: 'compelete',
@@ -919,7 +931,9 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
919
931
  remainingReserveTime: undefined
920
932
  });
921
933
  });
922
- unpaidBookings = this.pickBookingsForCurrentPoint(current, this.normalizeResourceBookings(current, deviceCurrent, unpaidBookingList));
934
+ unpaidBookings = this.pickBookingsForCurrentPoint(current, this.normalizeResourceBookings(current, deviceCurrent, unpaidBookingList), {
935
+ includeAllFuture: true
936
+ });
923
937
  orderCount = this.countBookingOrders(statsBookingList);
924
938
  bookingCount = statsBookingList.length;
925
939
  compeleteBookingCount = this.countCompletedBookings(statsBookingList);
@@ -102,10 +102,6 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
102
102
  * Read-only compatibility view for salesSdk skins. It reconnects a booking to its product but
103
103
  * must not be used to mutate tempOrder; all writes go through BaseSales / planner APIs.
104
104
  */
105
- getCart(): {
106
- items: any[];
107
- bookings: any[];
108
- };
109
105
  /**
110
106
  * Loads the current customer's product coupons and discount cards after planner lines exist,
111
107
  * then optionally lets the existing Rules engine select the best valid combination. This is a
@@ -880,32 +880,33 @@ export var UnifiedBookingSalesImpl = /*#__PURE__*/function (_BookingTicket) {
880
880
  * Read-only compatibility view for salesSdk skins. It reconnects a booking to its product but
881
881
  * must not be used to mutate tempOrder; all writes go through BaseSales / planner APIs.
882
882
  */
883
- }, {
884
- key: "getCart",
885
- value: function getCart() {
886
- var tempOrder = this.getTempOrder();
887
- var salesDetail = this.getSalesOrder();
888
- var items = (tempOrder === null || tempOrder === void 0 ? void 0 : tempOrder.products) || [];
889
- var bookings = (salesDetail === null || salesDetail === void 0 ? void 0 : salesDetail.bookings) || (tempOrder === null || tempOrder === void 0 ? void 0 : tempOrder.bookings) || [];
890
- return {
891
- items: items,
892
- bookings: bookings.map(function (booking) {
893
- var productUid = booking === null || booking === void 0 ? void 0 : booking.product_uid;
894
- var product = productUid ? items.find(function (item) {
895
- var _item$metadata;
896
- var uid = (item === null || item === void 0 || (_item$metadata = item.metadata) === null || _item$metadata === void 0 ? void 0 : _item$metadata.unique_identification_number) || (item === null || item === void 0 ? void 0 : item.unique_identification_number);
897
- return String(uid || '') === String(productUid);
898
- }) || null : null;
899
- return _objectSpread(_objectSpread({}, booking), {}, {
900
- _extend: _objectSpread(_objectSpread({}, (booking === null || booking === void 0 ? void 0 : booking._extend) || {}), {}, {
901
- product: product,
902
- addTimeProduct: [],
903
- canAddTime: false
904
- })
905
- });
906
- })
907
- };
908
- }
883
+ // getCart(): BookingTicketCartView {
884
+ // const tempOrder = this.getTempOrder();
885
+ // const salesDetail = this.getSalesOrder();
886
+ // const items = tempOrder?.products || [];
887
+ // const bookings = salesDetail?.bookings || tempOrder?.bookings || [];
888
+ // return {
889
+ // items,
890
+ // bookings: bookings.map((booking: any) => {
891
+ // const productUid = booking?.product_uid;
892
+ // const product = productUid
893
+ // ? items.find((item: any) => {
894
+ // const uid = item?.metadata?.unique_identification_number || item?.unique_identification_number;
895
+ // return String(uid || '') === String(productUid);
896
+ // }) || null
897
+ // : null;
898
+ // return {
899
+ // ...booking,
900
+ // _extend: {
901
+ // ...(booking?._extend || {}),
902
+ // product,
903
+ // addTimeProduct: [],
904
+ // canAddTime: false,
905
+ // },
906
+ // };
907
+ // }),
908
+ // };
909
+ // }
909
910
 
910
911
  /**
911
912
  * Loads the current customer's product coupons and discount cards after planner lines exist,
@@ -1,51 +0,0 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
- // If the importer is in node compatibility mode or this is not an ESM
21
- // file that has been converted to a CommonJS file using a Babel-
22
- // compatible transform (i.e. "__esModule" has not been set), then set
23
- // "default" to the CommonJS "module.exports" for node compatibility.
24
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
- mod
26
- ));
27
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
-
29
- // src/model/strategy/adapter/promotion/index.ts
30
- var promotion_exports = {};
31
- __export(promotion_exports, {
32
- BUY_X_GET_Y_FREE_STRATEGY: () => import_examples.BUY_X_GET_Y_FREE_STRATEGY,
33
- ITEM_REWARD_STRATEGY: () => import_examples.ITEM_REWARD_STRATEGY,
34
- PromotionAdapter: () => import_adapter.PromotionAdapter,
35
- PromotionEvaluator: () => import_evaluator.PromotionEvaluator,
36
- X_ITEMS_FOR_Y_PRICE_STRATEGY: () => import_examples.X_ITEMS_FOR_Y_PRICE_STRATEGY,
37
- default: () => import_adapter2.default
38
- });
39
- module.exports = __toCommonJS(promotion_exports);
40
- var import_evaluator = require("./evaluator");
41
- var import_adapter = require("./adapter");
42
- var import_adapter2 = __toESM(require("./adapter"));
43
- var import_examples = require("./examples");
44
- // Annotate the CommonJS export names for ESM import in node:
45
- 0 && (module.exports = {
46
- BUY_X_GET_Y_FREE_STRATEGY,
47
- ITEM_REWARD_STRATEGY,
48
- PromotionAdapter,
49
- PromotionEvaluator,
50
- X_ITEMS_FOR_Y_PRICE_STRATEGY
51
- });
@@ -149,7 +149,9 @@ function mapOrderBundleToOtherBundle(bundle) {
149
149
  return bundle.map((item) => {
150
150
  return {
151
151
  ...item,
152
- original_price: item.original_price ?? item.bundle_selling_price ?? item.price
152
+ // 后端历史订单可能没有 original_price;此时 price 是折前毛价,
153
+ // bundle_selling_price 已包含折扣,不能把它当成编辑时的新原价。
154
+ original_price: item.original_price ?? item.product_price ?? item.price ?? item.bundle_selling_price
153
155
  };
154
156
  });
155
157
  }
@@ -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';
@@ -385,7 +385,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
385
385
  * await order.addProductToOrder({ product_id: 1, num: 3 }, { start_time: '10:00' });
386
386
  * // → tempOrder.products 新增 3 行,每行 num=1,bookings 3 条
387
387
  */
388
- addProductToOrder(product: Partial<OrderProduct> & OrderProductIdentity, booking?: AddProductBookingInput): Promise<OrderProduct[]>;
388
+ addProductToOrder(product: Partial<OrderProduct> & OrderProductIdentity, booking?: AddProductBookingInput, options?: AddProductToOrderOptions): Promise<OrderProduct[]>;
389
389
  /**
390
390
  * 批量添加商品行,所有行追加完成后只触发一次促销、折扣、summary 重算与持久化。
391
391
  *
@@ -423,8 +423,8 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
423
423
  * @example
424
424
  * await order.removeProductsFromOrder([identityA, identityB]);
425
425
  */
426
- removeProductsFromOrder(identities: OrderProductIdentity[]): Promise<OrderProduct[]>;
427
- removeProductFromOrder(identity: OrderProductIdentity): Promise<OrderProduct[]>;
426
+ removeProductsFromOrder(identities: OrderProductIdentity[], options?: RemoveProductsFromOrderOptions): Promise<OrderProduct[]>;
427
+ removeProductFromOrder(identity: OrderProductIdentity, options?: RemoveProductsFromOrderOptions): Promise<OrderProduct[]>;
428
428
  /**
429
429
  * 仅清空购物车行(products / bookings),不重置整单。
430
430
  * 用于「仅清空商品」;客户、备注、支付等协议字段保持不变。
@@ -487,7 +487,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
487
487
  generateIdempotencyToken(): string;
488
488
  syncPaymentsToOrder<T = any>(params: SyncPaymentsToOrderParams): Promise<SyncPaymentsToOrderResult<T>>;
489
489
  createOrder(params: CommitOrderParams['query']): {
490
- type: "virtual" | "appointment_booking";
490
+ type: "appointment_booking" | "virtual";
491
491
  platform: string;
492
492
  sales_channel: string;
493
493
  order_sales_channel: string;
@@ -563,4 +563,4 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
563
563
  getOrderInfo(order_id: number): Promise<any>;
564
564
  getVouchers(): any[];
565
565
  }
566
- export type { UpdateOrderBookingParams, UpdateOrderProductParams, UpdateOrderProductQuantityParams, } from './types';
566
+ export type { AddProductToOrderOptions, RemoveProductsFromOrderOptions, UpdateOrderBookingParams, UpdateOrderProductParams, UpdateOrderProductQuantityParams, } from './types';
@@ -2897,8 +2897,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2897
2897
  * @example
2898
2898
  * await this.appendProductLineToTempOrder(tempOrder, { product_id: 1, num: 1 }, booking);
2899
2899
  */
2900
- async appendProductLineToTempOrder(tempOrder, product, booking) {
2901
- var _a, _b;
2900
+ async appendProductLineToTempOrder(tempOrder, product, booking, options) {
2901
+ var _a, _b, _c, _d;
2902
2902
  const linked = booking ? this.createLinkedProductAndBooking({ product, booking }) : null;
2903
2903
  const productToAdd = (linked == null ? void 0 : linked.product) || product;
2904
2904
  const incomingFingerprint = (0, import_utils3.buildProductLineFingerprint)(
@@ -2909,7 +2909,12 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2909
2909
  productToAdd,
2910
2910
  (0, import_utils3.normalizeOrderProduct)(productToAdd)
2911
2911
  );
2912
- const incomingBookingUid = productToAdd.booking_uid ?? ((_a = productToAdd.metadata) == null ? void 0 : _a.booking_uid);
2912
+ const isWeighingProduct = Number(((_a = normalizedIncoming.product_sku) == null ? void 0 : _a.open_sold_weight) || 0) === 1;
2913
+ const netWeight = Number((_b = normalizedIncoming.product_sku) == null ? void 0 : _b.net_weight);
2914
+ if (isWeighingProduct && (!Number.isFinite(netWeight) || netWeight <= 0)) {
2915
+ throw new Error("[Order] 称重商品净重必须大于 0");
2916
+ }
2917
+ const incomingBookingUid = productToAdd.booking_uid ?? ((_c = productToAdd.metadata) == null ? void 0 : _c.booking_uid);
2913
2918
  if (incomingBookingUid !== void 0 && incomingBookingUid !== null && String(incomingBookingUid) !== "") {
2914
2919
  normalizedIncoming.booking_uid = String(incomingBookingUid);
2915
2920
  normalizedIncoming.metadata = {
@@ -2925,7 +2930,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2925
2930
  );
2926
2931
  const hasIncomingProductNote = this.hasOrderProductLineNote(normalizedIncoming);
2927
2932
  const hasIncomingBookingUid = this.hasLinkedBookingUid(productToAdd) || this.hasLinkedBookingUid(normalizedIncoming);
2928
- const shouldForceNewLine = !!booking || hasIncomingProductNote || hasIncomingBookingUid;
2933
+ const shouldForceNewLine = !!booking || hasIncomingProductNote || hasIncomingBookingUid || isWeighingProduct;
2929
2934
  const matchedIndex = hasIncomingGoodPass || shouldForceNewLine ? -1 : tempOrder.products.findIndex((item) => {
2930
2935
  var _a2;
2931
2936
  if (this.hasGoodPassDiscount(item))
@@ -2974,10 +2979,19 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2974
2979
  booking_uid: linked.bookingUid
2975
2980
  };
2976
2981
  }
2977
- tempOrder.products = [...tempOrder.products, normalizedIncoming];
2982
+ const insertAtIndex = options == null ? void 0 : options.insertAtIndex;
2983
+ if (Number.isInteger(insertAtIndex) && Number(insertAtIndex) >= 0 && Number(insertAtIndex) < tempOrder.products.length) {
2984
+ tempOrder.products = [
2985
+ ...tempOrder.products.slice(0, insertAtIndex),
2986
+ normalizedIncoming,
2987
+ ...tempOrder.products.slice(insertAtIndex)
2988
+ ];
2989
+ } else {
2990
+ tempOrder.products = [...tempOrder.products, normalizedIncoming];
2991
+ }
2978
2992
  } else {
2979
2993
  const targetProduct = matchedProduct;
2980
- const targetUid = (_b = targetProduct.metadata) == null ? void 0 : _b.unique_identification_number;
2994
+ const targetUid = (_d = targetProduct.metadata) == null ? void 0 : _d.unique_identification_number;
2981
2995
  const normalizedProduct = (0, import_utils.mergeOrderProductDisplayFields)(
2982
2996
  targetProduct,
2983
2997
  (0, import_utils3.normalizeOrderProduct)({
@@ -3033,6 +3047,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
3033
3047
  tempOrder.products[matchedIndex],
3034
3048
  targetUid
3035
3049
  );
3050
+ if (matchedIndex !== tempOrder.products.length - 1) {
3051
+ const mergedProduct = tempOrder.products[matchedIndex];
3052
+ tempOrder.products = [
3053
+ ...tempOrder.products.slice(0, matchedIndex),
3054
+ ...tempOrder.products.slice(matchedIndex + 1),
3055
+ mergedProduct
3056
+ ];
3057
+ }
3036
3058
  }
3037
3059
  if (linked) {
3038
3060
  tempOrder.bookings = [...tempOrder.bookings || [], linked.booking];
@@ -3045,8 +3067,15 @@ var OrderModule = class extends import_BaseModule.BaseModule {
3045
3067
  * await order.addProductToOrder({ product_id: 1, num: 3 }, { start_time: '10:00' });
3046
3068
  * // → tempOrder.products 新增 3 行,每行 num=1,bookings 3 条
3047
3069
  */
3048
- async addProductToOrder(product, booking) {
3070
+ async addProductToOrder(product, booking, options) {
3049
3071
  const tempOrder = this.ensureTempOrder();
3072
+ const insertAfterProductUid = options == null ? void 0 : options.insertAfterProductUid;
3073
+ const anchorIndex = booking && insertAfterProductUid ? tempOrder.products.findIndex((line) => {
3074
+ var _a;
3075
+ const uid = ((_a = line.metadata) == null ? void 0 : _a.unique_identification_number) || line.unique_identification_number;
3076
+ return uid !== void 0 && uid !== null && String(uid) === String(insertAfterProductUid);
3077
+ }) : -1;
3078
+ let insertAtIndex = anchorIndex >= 0 ? anchorIndex + 1 : void 0;
3050
3079
  if (booking) {
3051
3080
  const productRecord = product;
3052
3081
  const splitCount = (0, import_utils3.getSafeProductNum)(
@@ -3060,14 +3089,22 @@ var OrderModule = class extends import_BaseModule.BaseModule {
3060
3089
  await this.appendProductLineToTempOrder(
3061
3090
  tempOrder,
3062
3091
  singleLine,
3063
- (0, import_lodash_es.cloneDeep)(booking)
3092
+ (0, import_lodash_es.cloneDeep)(booking),
3093
+ { insertAtIndex }
3064
3094
  );
3095
+ if (insertAtIndex !== void 0)
3096
+ insertAtIndex += 1;
3065
3097
  }
3066
3098
  await this.finalizeProductOrderMutation(tempOrder);
3067
3099
  return tempOrder.products;
3068
3100
  }
3069
3101
  }
3070
- await this.appendProductLineToTempOrder(tempOrder, product, booking);
3102
+ await this.appendProductLineToTempOrder(
3103
+ tempOrder,
3104
+ product,
3105
+ booking,
3106
+ { insertAtIndex }
3107
+ );
3071
3108
  await this.finalizeProductOrderMutation(tempOrder);
3072
3109
  this.logInfo("addProductToOrder success", {
3073
3110
  product_id: product.product_id,
@@ -3548,7 +3585,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
3548
3585
  * @example
3549
3586
  * await order.removeProductsFromOrder([identityA, identityB]);
3550
3587
  */
3551
- async removeProductsFromOrder(identities) {
3588
+ async removeProductsFromOrder(identities, options) {
3552
3589
  var _a, _b, _c, _d;
3553
3590
  const tempOrder = this.ensureTempOrder();
3554
3591
  if (!identities.length) {
@@ -3556,6 +3593,13 @@ var OrderModule = class extends import_BaseModule.BaseModule {
3556
3593
  }
3557
3594
  const productsBeforeRemove = tempOrder.products || [];
3558
3595
  const bookingsBeforeRemove = tempOrder.bookings || [];
3596
+ const preserveDisplayPosition = options == null ? void 0 : options.preserveDisplayPosition;
3597
+ const anchorIndex = preserveDisplayPosition ? productsBeforeRemove.findIndex((item) => {
3598
+ var _a2;
3599
+ const uid = ((_a2 = item.metadata) == null ? void 0 : _a2.unique_identification_number) || item.unique_identification_number;
3600
+ return uid !== void 0 && uid !== null && String(uid) === String(preserveDisplayPosition.anchorProductUid);
3601
+ }) : -1;
3602
+ const productsAfterAnchor = anchorIndex >= 0 ? productsBeforeRemove.slice(anchorIndex + 1) : [];
3559
3603
  const matchedProducts = productsBeforeRemove.filter((item) => {
3560
3604
  return identities.some((identity) => (0, import_utils3.isIdentityMatch)(item, identity));
3561
3605
  });
@@ -3596,14 +3640,37 @@ var OrderModule = class extends import_BaseModule.BaseModule {
3596
3640
  return !(isAddTimeProduct && lineBookingUid !== void 0 && lineBookingUid !== null && linkedBookingUidsToRemove.has(String(lineBookingUid)));
3597
3641
  });
3598
3642
  }
3643
+ if (preserveDisplayPosition && anchorIndex >= 0) {
3644
+ const remainingProductIndex = tempOrder.products.findIndex((item) => {
3645
+ var _a2;
3646
+ const uid = ((_a2 = item.metadata) == null ? void 0 : _a2.unique_identification_number) || item.unique_identification_number;
3647
+ return uid !== void 0 && uid !== null && String(uid) === String(preserveDisplayPosition.remainingProductUid);
3648
+ });
3649
+ if (remainingProductIndex >= 0) {
3650
+ const remainingProduct = tempOrder.products[remainingProductIndex];
3651
+ const nextProduct = productsAfterAnchor.find(
3652
+ (item) => tempOrder.products.includes(item)
3653
+ );
3654
+ const productsWithoutRemaining = [
3655
+ ...tempOrder.products.slice(0, remainingProductIndex),
3656
+ ...tempOrder.products.slice(remainingProductIndex + 1)
3657
+ ];
3658
+ const nextProductIndex = nextProduct ? productsWithoutRemaining.indexOf(nextProduct) : -1;
3659
+ tempOrder.products = nextProductIndex >= 0 ? [
3660
+ ...productsWithoutRemaining.slice(0, nextProductIndex),
3661
+ remainingProduct,
3662
+ ...productsWithoutRemaining.slice(nextProductIndex)
3663
+ ] : [...productsWithoutRemaining, remainingProduct];
3664
+ }
3665
+ }
3599
3666
  await this.finalizeAfterProductsMutation(tempOrder);
3600
3667
  this.logInfo("removeProductsFromOrder success", {
3601
3668
  identities
3602
3669
  });
3603
3670
  return tempOrder.products;
3604
3671
  }
3605
- async removeProductFromOrder(identity) {
3606
- return this.removeProductsFromOrder([identity]);
3672
+ async removeProductFromOrder(identity, options) {
3673
+ return this.removeProductsFromOrder([identity], options);
3607
3674
  }
3608
3675
  /**
3609
3676
  * 仅清空购物车行(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;
@@ -722,15 +740,15 @@ export interface OrderModuleAPI {
722
740
  updateTempOrderCustomer: (customer: UpdateTempOrderCustomerInput) => OrderSnapshot['customer'];
723
741
  updateTempOrderBuzzer: (buzzer: string) => string;
724
742
  updateTempOrderContactsInfo: (contactsInfo: Record<string, any> | null) => Record<string, any> | null;
725
- addProductToOrder: (product: Partial<OrderProduct> & OrderProductIdentity, booking?: AddProductBookingInput) => Promise<OrderProduct[]>;
743
+ addProductToOrder: (product: Partial<OrderProduct> & OrderProductIdentity, booking?: AddProductBookingInput, options?: AddProductToOrderOptions) => Promise<OrderProduct[]>;
726
744
  updateOrderProduct: (params: UpdateOrderProductParams) => Promise<OrderProduct[]>;
727
745
  /** 批量更新购物车行,末尾仅触发一次促销重算与 summary 刷新 */
728
746
  updateOrderProducts: (paramsList: UpdateOrderProductParams[]) => Promise<OrderProduct[]>;
729
747
  updateOrderProductQuantity: (params: UpdateOrderProductQuantityParams) => Promise<OrderProduct[]>;
730
748
  updateOrderBooking: (params: UpdateOrderBookingParams) => any[];
731
- removeProductFromOrder: (identity: OrderProductIdentity) => Promise<OrderProduct[]>;
749
+ removeProductFromOrder: (identity: OrderProductIdentity, options?: RemoveProductsFromOrderOptions) => Promise<OrderProduct[]>;
732
750
  /** 批量删除购物车行,末尾仅触发一次促销重算与 summary 刷新 */
733
- removeProductsFromOrder: (identities: OrderProductIdentity[]) => Promise<OrderProduct[]>;
751
+ removeProductsFromOrder: (identities: OrderProductIdentity[], options?: RemoveProductsFromOrderOptions) => Promise<OrderProduct[]>;
734
752
  /** @deprecated no-op;OrderModule 不再负责 tempOrder localStorage 持久化。 */
735
753
  persistTempOrder: () => void;
736
754
  /**
@@ -126,7 +126,15 @@ var ProductList = class extends import_BaseModule.BaseModule {
126
126
  "ticket",
127
127
  "event_item"
128
128
  ],
129
- with: ["category", "collection", "resourceRelation"],
129
+ with: [
130
+ "category",
131
+ "collection",
132
+ "resourceRelation",
133
+ "bundleGroup.bundleItem",
134
+ "optionGroup.optionItem",
135
+ "variantGroup.variantItem",
136
+ "dataVariants"
137
+ ],
130
138
  status,
131
139
  num: 500,
132
140
  skip: 1,