@pisell/pisellos 2.2.174 → 2.2.176

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.
@@ -54,6 +54,7 @@ __export(utils_exports, {
54
54
  normalizeOrderProductDiscountList: () => normalizeOrderProductDiscountList,
55
55
  normalizeSubmitBooking: () => normalizeSubmitBooking,
56
56
  normalizeSubmitCollectPaxValue: () => normalizeSubmitCollectPaxValue,
57
+ resolveEffectivePerUnitDiscount: () => resolveEffectivePerUnitDiscount,
57
58
  resolveManualDiscountMessage: () => import_manualProductDiscount.resolveManualDiscountMessage,
58
59
  resolveManualDiscountOriginTotal: () => import_manualProductDiscount.resolveManualDiscountOriginTotal,
59
60
  resolveManualDiscountReasonFromSources: () => import_manualProductDiscount.resolveManualDiscountReasonFromSources,
@@ -169,6 +170,33 @@ function resolveManualOverrideLineOriginalPrice(params, fallbackCompositeOrigina
169
170
  }
170
171
  return fallbackCompositeOriginal;
171
172
  }
173
+ function resolveEffectivePerUnitDiscount(product) {
174
+ const discountList = Array.isArray(product == null ? void 0 : product.discount_list) ? product.discount_list : [];
175
+ const fromList = discountList.reduce(
176
+ (sum, discount) => sum + Number((discount == null ? void 0 : discount.amount) || 0),
177
+ 0
178
+ );
179
+ const hasPromoDiscountItem = discountList.some((item) => (item == null ? void 0 : item.type) === "promotion");
180
+ if (hasPromoDiscountItem)
181
+ return fromList;
182
+ const metadata = (product == null ? void 0 : product.metadata) || {};
183
+ const optionSum = sumOptionUnitPrice(product == null ? void 0 : product.product_option_item).toNumber();
184
+ if (metadata.source_product_price != null && metadata.main_product_selling_price != null) {
185
+ const source = Number(metadata.source_product_price);
186
+ const selling = Number(metadata.main_product_selling_price) - optionSum;
187
+ if (Number.isFinite(source) && Number.isFinite(selling) && source !== selling) {
188
+ return fromList + (source - selling);
189
+ }
190
+ }
191
+ const promo = metadata._promotion;
192
+ if ((promo == null ? void 0 : promo.inPromotion) !== true)
193
+ return fromList;
194
+ const originalPrice = Number(promo.originalPrice);
195
+ const finalPrice = Number(promo.finalPrice);
196
+ if (!Number.isFinite(originalPrice) || !Number.isFinite(finalPrice))
197
+ return fromList;
198
+ return fromList + (originalPrice - finalPrice);
199
+ }
172
200
  function createDefaultOrderRulesHooks() {
173
201
  const toUnitPriceString = (totalLike, num) => {
174
202
  const effectiveNum = Number(num) > 0 ? Number(num) : 1;
@@ -176,7 +204,7 @@ function createDefaultOrderRulesHooks() {
176
204
  };
177
205
  return {
178
206
  getProduct: (product) => {
179
- var _a, _b, _c, _d, _e;
207
+ var _a, _b, _c, _d, _e, _f;
180
208
  const metadataAny = product.metadata || {};
181
209
  const optionSum = sumOptionUnitPrice(product.product_option_item);
182
210
  let sourcePrice;
@@ -228,7 +256,10 @@ function createDefaultOrderRulesHooks() {
228
256
  // 无券时会把 bundle 价还原成 original_price(手动改价套餐会从 20 回到 30)。
229
257
  isManualDiscount: resolveRulesManualDiscountFlag(metadataAny),
230
258
  holder_id: (_c = product.metadata) == null ? void 0 : _c.holder_id,
231
- startDate: ((_d = product.metadata) == null ? void 0 : _d.start_date) || ((_e = product.metadata) == null ? void 0 : _e.startDate)
259
+ startDate: ((_d = product.metadata) == null ? void 0 : _d.start_date) || ((_e = product.metadata) == null ? void 0 : _e.startDate),
260
+ main_product_selling_price: metadataAny.main_product_selling_price !== void 0 ? new import_decimal.default(Number(metadataAny.main_product_selling_price) || 0).minus(optionSum).toDecimalPlaces(2).toString() : void 0,
261
+ inPromotion: ((_f = metadataAny._promotion) == null ? void 0 : _f.inPromotion) === true,
262
+ _promotion: metadataAny._promotion
232
263
  };
233
264
  },
234
265
  setProduct: (product, values) => {
@@ -797,6 +828,7 @@ function clearTempOrderHolderAssignments(tempOrder) {
797
828
  normalizeOrderProductDiscountList,
798
829
  normalizeSubmitBooking,
799
830
  normalizeSubmitCollectPaxValue,
831
+ resolveEffectivePerUnitDiscount,
800
832
  resolveManualDiscountMessage,
801
833
  resolveManualDiscountOriginTotal,
802
834
  resolveManualDiscountReasonFromSources,
@@ -393,8 +393,8 @@ function calculateSalesSummary(params) {
393
393
  );
394
394
  }
395
395
  const shopDiscountAmount = import_decimal.default.max(new import_decimal.default(Number(shopDiscount) || 0), 0);
396
- const expectAmount = import_decimal.default.max(0, productAmount.plus(surchargeAmount).minus(shopDiscountAmount));
397
- const totalAmount = isPriceIncludeTax === 1 ? expectAmount : expectAmount.plus(productTaxFee);
396
+ const preTaxExpectAmount = import_decimal.default.max(0, productAmount.plus(surchargeAmount).minus(shopDiscountAmount));
397
+ const totalAmount = isPriceIncludeTax === 1 ? preTaxExpectAmount : preTaxExpectAmount.plus(productTaxFee);
398
398
  const deposit = calculateProductsDeposit(products);
399
399
  return {
400
400
  product_quantity: productQuantity,
@@ -412,7 +412,7 @@ function calculateSalesSummary(params) {
412
412
  ),
413
413
  deposit_amount: deposit ? deposit.total : "0.00",
414
414
  deposit,
415
- expect_amount: toFixed2(expectAmount),
415
+ expect_amount: toFixed2(totalAmount),
416
416
  total_amount: toFixed2(totalAmount),
417
417
  total_refund_amount: "0.00",
418
418
  customer_paid_amount: "0.00",
@@ -149,10 +149,11 @@ var Server = class {
149
149
  */
150
150
  this.handleProductQuery = async ({ url, method, data, config }) => {
151
151
  console.log("[Server] handleProductQuery:", url, method, data, config);
152
- const { menu_list_ids, schedule_datetime, schedule_date, customer_id } = data;
152
+ const { menu_list_ids, schedule_datetime, schedule_date, customer_id, ids } = data;
153
153
  const { callback, subscriberId } = config || {};
154
154
  this.logInfo("handleProductQuery: 开始处理商品查询请求", {
155
155
  menu_list_ids,
156
+ ids,
156
157
  schedule_datetime,
157
158
  schedule_date,
158
159
  customer_id
@@ -160,14 +161,14 @@ var Server = class {
160
161
  if (subscriberId && typeof callback === "function") {
161
162
  this.productQuerySubscribers.set(subscriberId, {
162
163
  callback,
163
- context: { menu_list_ids, schedule_date, schedule_datetime, customer_id }
164
+ context: { menu_list_ids, ids, schedule_date, schedule_datetime, customer_id }
164
165
  });
165
166
  this.logInfo("handleProductQuery: 已注册订阅者", {
166
167
  subscriberId,
167
168
  totalSubscribers: this.productQuerySubscribers.size
168
169
  });
169
170
  }
170
- return this.computeProductQueryResult({ menu_list_ids, schedule_date, schedule_datetime, customer_id });
171
+ return this.computeProductQueryResult({ menu_list_ids, ids, schedule_date, schedule_datetime, customer_id });
171
172
  };
172
173
  /**
173
174
  * 按商品 id 查询单条(GET /shop/product/query/:productId)
@@ -2051,9 +2052,11 @@ var Server = class {
2051
2052
  */
2052
2053
  async computeProductQueryResult(context, options) {
2053
2054
  const tTotal = performance.now();
2054
- const { menu_list_ids, schedule_date, schedule_datetime, customer_id, product_id } = context;
2055
+ const { menu_list_ids, ids, schedule_date, schedule_datetime, customer_id, product_id } = context;
2056
+ const queryIds = Array.isArray(ids) ? ids.map((id) => Number(id)).filter((id) => Number.isFinite(id)) : [];
2055
2057
  this.logInfo("computeProductQueryResult 开始", {
2056
2058
  menuListIdsCount: (menu_list_ids == null ? void 0 : menu_list_ids.length) ?? 0,
2059
+ ids: queryIds,
2057
2060
  schedule_datetime,
2058
2061
  schedule_date,
2059
2062
  customer_id,
@@ -2064,6 +2067,38 @@ var Server = class {
2064
2067
  this.logError("computeProductQueryResult: Products 模块未注册");
2065
2068
  return { message: "Products 模块未注册", data: { list: [], count: 0 } };
2066
2069
  }
2070
+ if (queryIds.length > 0) {
2071
+ const uniqueIds = Array.from(new Set(queryIds));
2072
+ const tPrice2 = performance.now();
2073
+ const productsWithPrice = await this.products.getProductsWithPrice(schedule_date, {
2074
+ scheduleModule: this.getSchedule(),
2075
+ schedule_datetime,
2076
+ customer_id
2077
+ }, {
2078
+ changedIds: options == null ? void 0 : options.changedIds,
2079
+ productIds: uniqueIds
2080
+ });
2081
+ (0, import_product.perfMark)("computeQuery.getProductsWithPrice(ids)", performance.now() - tPrice2, {
2082
+ count: productsWithPrice.length,
2083
+ ids: uniqueIds
2084
+ });
2085
+ const filteredProducts2 = productsWithPrice.filter((p) => uniqueIds.includes(Number(p == null ? void 0 : p.id))).filter((p) => ((p == null ? void 0 : p.status) || "published") === "published");
2086
+ (0, import_product.perfMark)("computeProductQueryResult", performance.now() - tTotal, {
2087
+ mode: "ids",
2088
+ ids: uniqueIds,
2089
+ count: filteredProducts2.length
2090
+ });
2091
+ this.logInfo("computeProductQueryResult 完成(ids)", {
2092
+ ids: uniqueIds,
2093
+ count: filteredProducts2.length
2094
+ });
2095
+ return {
2096
+ code: 200,
2097
+ data: { list: filteredProducts2, count: filteredProducts2.length },
2098
+ message: "",
2099
+ status: true
2100
+ };
2101
+ }
2067
2102
  if (product_id != null && Number.isFinite(Number(product_id))) {
2068
2103
  const pid = Number(product_id);
2069
2104
  const tPrice2 = performance.now();
@@ -2,7 +2,7 @@ import { Module, ModuleOptions, PisellCore } from '../../types';
2
2
  import { BaseModule } from '../../modules/BaseModule';
3
3
  import { BaseSalesOrderProduct, BaseSalesOrderProductIdentity, BaseSalesPaymentStatus, BaseSalesUpdateOrderProductQuantityParams, BaseSalesCalculateProductBookingPriceParams, BaseSalesProductBookingPriceResult, BaseSalesScanCodeResult } from './types';
4
4
  import { OrderModule } from '../../modules/Order';
5
- import type { AddProductBookingInput, LoadSalesDetailParams, OrderPaymentData, OrderPaymentSource, SendCustomerPayLinkParams, SyncPaymentsToOrderParams, SyncPaymentsToOrderResult, UpdateTempOrderCustomerInput, UpdateOrderBookingParams, UpdateOrderProductParams, OrderTempOrder } from '../../modules/Order/types';
5
+ import type { AddProductBookingInput, LoadSalesDetailParams, OrderPaymentData, OrderPaymentSource, SendCustomerPayLinkParams, SyncPaymentsToOrderParams, SyncPaymentsToOrderResult, UpdateTempOrderCustomerInput, UpdateOrderBookingParams, UpdateOrderProductParams, OrderTempOrder, OrderPromotionEvaluator, OrderGiftSelectResolver, OrderUnfulfilledPromotion, OrderLastGiftActions } from '../../modules/Order/types';
6
6
  import type { SubmitPayloadEnhancer } from '../../modules/Order/utils';
7
7
  import type { Discount } from '../../modules/Discount/types';
8
8
  import { RequestPlugin, WindowPlugin } from '../../plugins';
@@ -63,6 +63,13 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
63
63
  private getCurrentOrderCustomerId;
64
64
  private applyQuotationScheduleResolver;
65
65
  private loadQuotationForPriceQuery;
66
+ private getPriceQueryProductId;
67
+ private getPriceQuerySchedule;
68
+ private loadProductForPriceQuery;
69
+ private getAuthoritativeBundleItems;
70
+ private findAuthoritativeBundleItem;
71
+ private getAuthoritativeBundleUnitPrice;
72
+ private mergeProductForPriceQuery;
66
73
  protected getSubmitOrderSalesChannel(): string | undefined;
67
74
  /**
68
75
  * 工厂入口:根据子模块名实例化对应模块。
@@ -237,6 +244,35 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
237
244
  */
238
245
  setOrderProductLineNote(identity: BaseSalesOrderProductIdentity, note: string): Promise<import("../../modules/Order/types").OrderProduct[]>;
239
246
  removeProductFromOrder(identity: BaseSalesOrderProductIdentity): Promise<import("../../modules/Order/types").OrderProduct[]>;
247
+ /**
248
+ * 注入促销评估器到底层 OrderModule。
249
+ *
250
+ * @example
251
+ * solution.setPromotionEvaluator(appHelper.utils.promotionEvaluator);
252
+ */
253
+ setPromotionEvaluator(evaluator: OrderPromotionEvaluator | null): void;
254
+ /**
255
+ * 注入赠品选择 resolver。UI 层(SalesSdk)通过 bridge 提供。
256
+ *
257
+ * @example
258
+ * solution.setGiftSelectResolver(async (ctx) => bridge.request(ctx));
259
+ */
260
+ setGiftSelectResolver(resolver: OrderGiftSelectResolver | null): void;
261
+ /**
262
+ * 为商品目录追加促销标签,供 SalesSdkProductProvider 等商品列表入口复用。
263
+ *
264
+ * @example
265
+ * const products = solution.appendPromotionTags(catalogProducts);
266
+ */
267
+ appendPromotionTags<T extends Record<string, any>>(products: T[]): T[];
268
+ /**
269
+ * 主动触发一次促销应用(一般写路径会自动触发,少数场景如客户切换后可手动调)。
270
+ */
271
+ applyPromotion(): Promise<void>;
272
+ /** 最近一次促销计算输出的未满足提示。 */
273
+ getUnfulfilledPromotions(): OrderUnfulfilledPromotion[];
274
+ /** 最近一次促销计算输出的赠品操作 diff。 */
275
+ getLastGiftActions(): OrderLastGiftActions | null;
240
276
  getProductList(): Promise<any>;
241
277
  getOtherParams(): Record<string, any>;
242
278
  setOtherParams(params: Record<string, any>, { cover }?: {
@@ -137,6 +137,140 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
137
137
  customer_id: params.customer_id
138
138
  });
139
139
  }
140
+ getPriceQueryProductId(product) {
141
+ const productId = Number((product == null ? void 0 : product.product_id) ?? (product == null ? void 0 : product.id));
142
+ if (!Number.isFinite(productId))
143
+ return null;
144
+ return productId;
145
+ }
146
+ getPriceQuerySchedule(params) {
147
+ const booking = params.booking || {};
148
+ if (typeof booking.start_date === "string" && booking.start_date.trim()) {
149
+ const scheduleDate = booking.start_date.trim();
150
+ const startTime = typeof booking.start_time === "string" && booking.start_time.trim() ? booking.start_time.trim() : "00:00:00";
151
+ const datetime = (0, import_dayjs.default)(`${scheduleDate} ${startTime}`);
152
+ return {
153
+ schedule_date: scheduleDate,
154
+ schedule_datetime: datetime.isValid() ? datetime.format("YYYY-MM-DD HH:mm:ss") : `${scheduleDate} ${startTime}`
155
+ };
156
+ }
157
+ if (typeof params.datetime === "string" && params.datetime.trim()) {
158
+ const datetime = (0, import_dayjs.default)(params.datetime.trim());
159
+ if (datetime.isValid()) {
160
+ return {
161
+ schedule_date: datetime.format("YYYY-MM-DD"),
162
+ schedule_datetime: datetime.format("YYYY-MM-DD HH:mm:ss")
163
+ };
164
+ }
165
+ }
166
+ const now = (0, import_dayjs.default)();
167
+ return {
168
+ schedule_date: now.format("YYYY-MM-DD"),
169
+ schedule_datetime: now.format("YYYY-MM-DD HH:mm:ss")
170
+ };
171
+ }
172
+ async loadProductForPriceQuery(params, customerId) {
173
+ var _a, _b;
174
+ const product = params.product || {};
175
+ const productId = this.getPriceQueryProductId(product);
176
+ if (productId === null || !this.request)
177
+ return null;
178
+ const schedule = this.getPriceQuerySchedule(params);
179
+ try {
180
+ const response = await this.request.post("/product/query", {
181
+ ids: [productId],
182
+ open_quotation: 1,
183
+ open_bundle: 1,
184
+ status: "published",
185
+ num: 1,
186
+ skip: 1,
187
+ customer_id: customerId,
188
+ schedule_date: schedule.schedule_date,
189
+ schedule_datetime: schedule.schedule_datetime,
190
+ application_code: params.channel || ((_a = this.otherParams) == null ? void 0 : _a.channel)
191
+ }, {
192
+ osServer: true,
193
+ customToast: () => {
194
+ }
195
+ });
196
+ const list = ((_b = response == null ? void 0 : response.data) == null ? void 0 : _b.list) || (response == null ? void 0 : response.list) || [];
197
+ if (!Array.isArray(list))
198
+ return null;
199
+ return list.find((item) => Number((item == null ? void 0 : item.id) ?? (item == null ? void 0 : item.product_id)) === productId) || null;
200
+ } catch (error) {
201
+ this.logWarning("loadProductForPriceQuery: 商品重查失败,使用入参 fallback", {
202
+ productId,
203
+ error: error instanceof Error ? error.message : String(error)
204
+ });
205
+ return null;
206
+ }
207
+ }
208
+ getAuthoritativeBundleItems(product) {
209
+ if (Array.isArray(product == null ? void 0 : product.product_bundle))
210
+ return product.product_bundle;
211
+ const groups = Array.isArray(product == null ? void 0 : product.bundle_group) ? product.bundle_group : Array.isArray(product == null ? void 0 : product.bundleGroup) ? product.bundleGroup : [];
212
+ return groups.flatMap((group) => {
213
+ const items = Array.isArray(group == null ? void 0 : group.bundle_item) ? group.bundle_item : Array.isArray(group == null ? void 0 : group.bundleItem) ? group.bundleItem : [];
214
+ return items.map((item) => ({
215
+ ...item,
216
+ group_id: (item == null ? void 0 : item.group_id) ?? (group == null ? void 0 : group.id),
217
+ bundle_group_id: (item == null ? void 0 : item.bundle_group_id) ?? (item == null ? void 0 : item.group_id) ?? (group == null ? void 0 : group.id)
218
+ }));
219
+ });
220
+ }
221
+ findAuthoritativeBundleItem(bundle, candidates) {
222
+ const bundleId = bundle.bundle_id ?? bundle.id;
223
+ if (bundleId != null) {
224
+ const matchedById = candidates.find((item) => String((item == null ? void 0 : item.bundle_id) ?? (item == null ? void 0 : item.id)) === String(bundleId));
225
+ if (matchedById)
226
+ return matchedById;
227
+ }
228
+ const productId = bundle.bundle_product_id ?? bundle._bundle_product_id;
229
+ const groupId = bundle.bundle_group_id ?? bundle.group_id;
230
+ return candidates.find((item) => {
231
+ const itemProductId = (item == null ? void 0 : item.bundle_product_id) ?? (item == null ? void 0 : item._bundle_product_id);
232
+ const itemGroupId = (item == null ? void 0 : item.bundle_group_id) ?? (item == null ? void 0 : item.group_id);
233
+ if (productId == null || String(itemProductId) !== String(productId))
234
+ return false;
235
+ if (groupId == null)
236
+ return true;
237
+ return String(itemGroupId) === String(groupId);
238
+ }) || null;
239
+ }
240
+ getAuthoritativeBundleUnitPrice(bundle) {
241
+ return bundle.price ?? bundle.bundle_selling_price ?? bundle.custom_price ?? bundle.product_price ?? bundle.base_price;
242
+ }
243
+ mergeProductForPriceQuery(product, authoritativeProduct) {
244
+ if (!authoritativeProduct)
245
+ return product;
246
+ const selectedBundles = Array.isArray(product.product_bundle) ? product.product_bundle : [];
247
+ const authoritativeBundles = this.getAuthoritativeBundleItems(authoritativeProduct);
248
+ const productBundle = selectedBundles.map((bundle) => {
249
+ const authoritativeBundle = this.findAuthoritativeBundleItem(bundle, authoritativeBundles);
250
+ const unitPrice = authoritativeBundle ? this.getAuthoritativeBundleUnitPrice(authoritativeBundle) : void 0;
251
+ if (unitPrice === void 0 || unitPrice === null || unitPrice === "")
252
+ return bundle;
253
+ return {
254
+ ...bundle,
255
+ price: unitPrice,
256
+ bundle_selling_price: unitPrice,
257
+ base_price: (authoritativeBundle == null ? void 0 : authoritativeBundle.base_price) ?? bundle.base_price,
258
+ product_price: (authoritativeBundle == null ? void 0 : authoritativeBundle.product_price) ?? bundle.product_price
259
+ };
260
+ });
261
+ return {
262
+ ...product,
263
+ ...authoritativeProduct,
264
+ id: product.id ?? authoritativeProduct.id,
265
+ product_id: product.product_id ?? authoritativeProduct.product_id ?? authoritativeProduct.id,
266
+ product_variant_id: product.product_variant_id ?? authoritativeProduct.product_variant_id ?? 0,
267
+ num: product.num ?? product.quantity ?? authoritativeProduct.num,
268
+ quantity: product.quantity ?? product.num ?? authoritativeProduct.quantity,
269
+ product_option_item: product.product_option_item ?? authoritativeProduct.product_option_item,
270
+ product_bundle: productBundle,
271
+ metadata: product.metadata ?? authoritativeProduct.metadata
272
+ };
273
+ }
140
274
  getSubmitOrderSalesChannel() {
141
275
  var _a;
142
276
  return (_a = this.otherParams) == null ? void 0 : _a.channel;
@@ -189,10 +323,18 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
189
323
  return coreData == null ? void 0 : coreData[key];
190
324
  }
191
325
  syncAppDataToTempOrder(tempOrder) {
326
+ const isPriceIncludeTax = this.getAppData("is_price_include_tax");
192
327
  const taxCountryCode = this.getAppData("tax_country_code");
193
328
  const currencyCode = this.getAppData("shop_currency_code");
194
329
  const currencySymbol = this.getAppData("shop_symbol");
195
330
  let isChanged = false;
331
+ if (isPriceIncludeTax !== void 0) {
332
+ const nextIsPriceIncludeTax = Number(isPriceIncludeTax);
333
+ if ((nextIsPriceIncludeTax === 0 || nextIsPriceIncludeTax === 1) && tempOrder.is_price_include_tax !== nextIsPriceIncludeTax) {
334
+ tempOrder.is_price_include_tax = nextIsPriceIncludeTax;
335
+ isChanged = true;
336
+ }
337
+ }
196
338
  if (taxCountryCode !== void 0) {
197
339
  const nextTaxCountryCode = String(taxCountryCode || "");
198
340
  if (tempOrder.tax_country_code !== nextTaxCountryCode) {
@@ -998,7 +1140,15 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
998
1140
  addOrderPayment(payment) {
999
1141
  if (!this.store.order)
1000
1142
  throw new Error("order 模块未初始化");
1001
- const payments = this.store.order.addOrderPayment(payment);
1143
+ const paymentRecord = payment;
1144
+ const metadata = paymentRecord.metadata && typeof paymentRecord.metadata === "object" ? { ...paymentRecord.metadata } : {};
1145
+ if (!metadata.unique_identification_number) {
1146
+ metadata.unique_identification_number = (0, import_utils.createUuidV4)();
1147
+ }
1148
+ const payments = this.store.order.addOrderPayment({
1149
+ ...paymentRecord,
1150
+ metadata
1151
+ });
1002
1152
  const amountSnapshot = this.store.order.getOrderAmountSnapshot();
1003
1153
  const syncState = this.store.order.getOrderSyncState();
1004
1154
  if (amountSnapshot && syncState !== "submitting") {
@@ -1209,6 +1359,8 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1209
1359
  async calculateProductBookingPrice(params) {
1210
1360
  const quotation = this.store.quotation;
1211
1361
  const customerId = params.customer_id ?? this.getCurrentOrderCustomerId();
1362
+ const authoritativeProduct = await this.loadProductForPriceQuery(params, customerId);
1363
+ const product = this.mergeProductForPriceQuery(params.product, authoritativeProduct);
1212
1364
  await this.loadQuotationForPriceQuery({
1213
1365
  quotation,
1214
1366
  customer_id: customerId,
@@ -1216,6 +1368,8 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1216
1368
  });
1217
1369
  return (0, import_quotationPrice.calculateBaseSalesProductBookingPrice)({
1218
1370
  ...params,
1371
+ product,
1372
+ fallback_price: authoritativeProduct ? void 0 : params.fallback_price,
1219
1373
  customer_id: customerId,
1220
1374
  quotation
1221
1375
  });
@@ -1294,6 +1448,58 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1294
1448
  throw error;
1295
1449
  }
1296
1450
  }
1451
+ // ─── 促销/赠品 透传(迁移自 booking usePromotion) ────────────────
1452
+ /**
1453
+ * 注入促销评估器到底层 OrderModule。
1454
+ *
1455
+ * @example
1456
+ * solution.setPromotionEvaluator(appHelper.utils.promotionEvaluator);
1457
+ */
1458
+ setPromotionEvaluator(evaluator) {
1459
+ if (!this.store.order)
1460
+ throw new Error("order 模块未初始化");
1461
+ this.store.order.setPromotionEvaluator(evaluator);
1462
+ }
1463
+ /**
1464
+ * 注入赠品选择 resolver。UI 层(SalesSdk)通过 bridge 提供。
1465
+ *
1466
+ * @example
1467
+ * solution.setGiftSelectResolver(async (ctx) => bridge.request(ctx));
1468
+ */
1469
+ setGiftSelectResolver(resolver) {
1470
+ if (!this.store.order)
1471
+ throw new Error("order 模块未初始化");
1472
+ this.store.order.setGiftSelectResolver(resolver);
1473
+ }
1474
+ /**
1475
+ * 为商品目录追加促销标签,供 SalesSdkProductProvider 等商品列表入口复用。
1476
+ *
1477
+ * @example
1478
+ * const products = solution.appendPromotionTags(catalogProducts);
1479
+ */
1480
+ appendPromotionTags(products) {
1481
+ if (!this.store.order)
1482
+ throw new Error("order 模块未初始化");
1483
+ return this.store.order.appendPromotionTags(products);
1484
+ }
1485
+ /**
1486
+ * 主动触发一次促销应用(一般写路径会自动触发,少数场景如客户切换后可手动调)。
1487
+ */
1488
+ async applyPromotion() {
1489
+ if (!this.store.order)
1490
+ throw new Error("order 模块未初始化");
1491
+ await this.store.order.applyPromotion();
1492
+ }
1493
+ /** 最近一次促销计算输出的未满足提示。 */
1494
+ getUnfulfilledPromotions() {
1495
+ var _a;
1496
+ return ((_a = this.store.order) == null ? void 0 : _a.getUnfulfilledPromotions()) || [];
1497
+ }
1498
+ /** 最近一次促销计算输出的赠品操作 diff。 */
1499
+ getLastGiftActions() {
1500
+ var _a;
1501
+ return ((_a = this.store.order) == null ? void 0 : _a.getLastGiftActions()) || null;
1502
+ }
1297
1503
  // 获取商品列表
1298
1504
  // TODO 需要跟 webpos 内的对齐一下
1299
1505
  async getProductList() {