@pisell/pisellos 2.3.94 → 2.3.95

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 (72) hide show
  1. package/dist/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
  2. package/dist/model/strategy/adapter/itemRule/adapter.js +135 -35
  3. package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
  4. package/dist/model/strategy/adapter/itemRule/evaluator.js +25 -1
  5. package/dist/model/strategy/adapter/itemRule/type.d.ts +44 -0
  6. package/dist/model/strategy/adapter/itemRule/type.js +19 -1
  7. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  8. package/dist/modules/Order/index.d.ts +18 -3
  9. package/dist/modules/Order/index.js +885 -485
  10. package/dist/modules/Order/salesNotes.d.ts +31 -0
  11. package/dist/modules/Order/salesNotes.js +492 -0
  12. package/dist/modules/Order/types.d.ts +91 -5
  13. package/dist/modules/Order/types.js +5 -0
  14. package/dist/modules/Order/utils.d.ts +12 -0
  15. package/dist/modules/Order/utils.js +75 -26
  16. package/dist/modules/ProductList/index.d.ts +2 -1
  17. package/dist/modules/ProductList/index.js +100 -20
  18. package/dist/modules/ProductList/types.d.ts +10 -0
  19. package/dist/server/index.d.ts +6 -0
  20. package/dist/server/index.js +43 -3
  21. package/dist/solution/BaseSales/index.d.ts +9 -1
  22. package/dist/solution/BaseSales/index.js +894 -706
  23. package/dist/solution/BookingByStep/index.d.ts +2 -2
  24. package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
  25. package/dist/solution/RegisterAndLogin/config.d.ts +9 -3
  26. package/dist/solution/RegisterAndLogin/config.js +95 -40
  27. package/dist/solution/RegisterAndLogin/index.js +4 -1
  28. package/dist/solution/ScanOrder/index.d.ts +1 -0
  29. package/dist/solution/ScanOrder/index.js +31 -27
  30. package/dist/solution/ScanOrder/utils.d.ts +1 -0
  31. package/dist/solution/ScanOrder/utils.js +2 -0
  32. package/dist/solution/UnifiedBookingSales/index.d.ts +15 -2
  33. package/dist/solution/UnifiedBookingSales/index.js +751 -483
  34. package/dist/solution/UnifiedBookingSales/types.d.ts +14 -1
  35. package/dist/solution/VenueBooking/index.d.ts +1 -0
  36. package/dist/solution/VenueBooking/index.js +8 -4
  37. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
  38. package/lib/model/strategy/adapter/itemRule/adapter.js +73 -4
  39. package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
  40. package/lib/model/strategy/adapter/itemRule/evaluator.js +23 -1
  41. package/lib/model/strategy/adapter/itemRule/type.d.ts +44 -0
  42. package/lib/model/strategy/adapter/itemRule/type.js +19 -1
  43. package/lib/modules/Order/index.d.ts +18 -3
  44. package/lib/modules/Order/index.js +325 -28
  45. package/lib/modules/Order/salesNotes.d.ts +31 -0
  46. package/lib/modules/Order/salesNotes.js +382 -0
  47. package/lib/modules/Order/types.d.ts +91 -5
  48. package/lib/modules/Order/types.js +4 -0
  49. package/lib/modules/Order/utils.d.ts +12 -0
  50. package/lib/modules/Order/utils.js +68 -16
  51. package/lib/modules/ProductList/index.d.ts +2 -1
  52. package/lib/modules/ProductList/index.js +47 -2
  53. package/lib/modules/ProductList/types.d.ts +10 -0
  54. package/lib/server/index.d.ts +6 -0
  55. package/lib/server/index.js +37 -3
  56. package/lib/solution/BaseSales/index.d.ts +9 -1
  57. package/lib/solution/BaseSales/index.js +105 -1
  58. package/lib/solution/BookingByStep/index.d.ts +2 -2
  59. package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
  60. package/lib/solution/RegisterAndLogin/config.d.ts +9 -3
  61. package/lib/solution/RegisterAndLogin/config.js +49 -8
  62. package/lib/solution/RegisterAndLogin/index.js +4 -1
  63. package/lib/solution/ScanOrder/index.d.ts +1 -0
  64. package/lib/solution/ScanOrder/index.js +5 -1
  65. package/lib/solution/ScanOrder/utils.d.ts +1 -0
  66. package/lib/solution/ScanOrder/utils.js +1 -0
  67. package/lib/solution/UnifiedBookingSales/index.d.ts +15 -2
  68. package/lib/solution/UnifiedBookingSales/index.js +125 -1
  69. package/lib/solution/UnifiedBookingSales/types.d.ts +14 -1
  70. package/lib/solution/VenueBooking/index.d.ts +1 -0
  71. package/lib/solution/VenueBooking/index.js +5 -1
  72. package/package.json +1 -1
@@ -101,6 +101,7 @@ Object.defineProperty(exports, "resolveSafeProductNum", {
101
101
  }
102
102
  });
103
103
  exports.resolveSubmitCollectPax = resolveSubmitCollectPax;
104
+ exports.resolveSubmitOrderType = resolveSubmitOrderType;
104
105
  Object.defineProperty(exports, "shouldBuildManualProductDiscount", {
105
106
  enumerable: true,
106
107
  get: function () {
@@ -125,6 +126,7 @@ var _decimal = _interopRequireDefault(require("decimal.js"));
125
126
  var _utils = require("../Holder/utils");
126
127
  var _utils2 = require("../../solution/ScanOrder/utils");
127
128
  var _orderCollectionIdentity = require("./utils/orderCollectionIdentity");
129
+ var _salesNotes = require("./salesNotes");
128
130
  var _manualProductDiscount = require("./utils/manualProductDiscount");
129
131
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
130
132
  /**
@@ -1127,6 +1129,7 @@ function createDefaultTempOrder(params) {
1127
1129
  request_unique_idempotency_token: '',
1128
1130
  vouchers: [],
1129
1131
  products: [],
1132
+ notes: [],
1130
1133
  bookings: [],
1131
1134
  payments: [],
1132
1135
  surcharges: [],
@@ -1136,10 +1139,58 @@ function createDefaultTempOrder(params) {
1136
1139
  holder: null,
1137
1140
  metadata: {},
1138
1141
  _extend: {
1139
- productsByUid: {}
1142
+ productsByUid: {},
1143
+ salesNotesState: {
1144
+ loaded: true,
1145
+ dirty: false
1146
+ }
1140
1147
  }
1141
1148
  };
1142
1149
  }
1150
+ function hasBoundBookingResource(bookings) {
1151
+ if (!Array.isArray(bookings)) return false;
1152
+ return bookings.some(booking => {
1153
+ if (!booking || typeof booking !== 'object') return false;
1154
+ if (Number(booking.parent_id) === 0) return false;
1155
+ const resources = booking.resources;
1156
+ if (!Array.isArray(resources)) return false;
1157
+ return resources.some(resource => {
1158
+ if (!resource || typeof resource !== 'object') return false;
1159
+ const relationId = String(resource.relation_id ?? '').trim();
1160
+ return relationId !== '' && relationId !== '0';
1161
+ });
1162
+ });
1163
+ }
1164
+ function isPersistedSubmitOrderId(orderId) {
1165
+ if (orderId === undefined || orderId === null || orderId === '') return false;
1166
+ const normalizedOrderId = Number(orderId);
1167
+ return Number.isFinite(normalizedOrderId) && normalizedOrderId > 0;
1168
+ }
1169
+
1170
+ /**
1171
+ * 解析 checkout 的订单类型。
1172
+ * dine_in 普通落库单在新增桌台资源后,即使原类型仍是 virtual,也必须按 table-order 提交。
1173
+ */
1174
+ function resolveSubmitOrderType(params) {
1175
+ const {
1176
+ requestedType,
1177
+ tempOrderType,
1178
+ orderId,
1179
+ businessCode,
1180
+ tempOrderBusinessCode,
1181
+ bookings
1182
+ } = params;
1183
+ const currentType = requestedType ?? tempOrderType;
1184
+ const resolvedBusinessCode = businessCode ?? tempOrderBusinessCode;
1185
+ if (isPersistedSubmitOrderId(orderId) && resolvedBusinessCode === 'dine_in' && currentType === 'virtual' && hasBoundBookingResource(bookings)) {
1186
+ return 'table-order';
1187
+ }
1188
+ if (currentType) return currentType;
1189
+ if (Array.isArray(bookings) && bookings.length > 0) {
1190
+ return businessCode === 'dine_in' ? 'table-order' : 'appointment_booking';
1191
+ }
1192
+ return 'virtual';
1193
+ }
1143
1194
  function buildSubmitPayload(params) {
1144
1195
  const {
1145
1196
  tempOrder,
@@ -1173,22 +1224,18 @@ function buildSubmitPayload(params) {
1173
1224
  total_refund_amount: _totalRefundAmount,
1174
1225
  create_date: _createDate,
1175
1226
  holder_id: _holderId,
1227
+ notes: _notes,
1176
1228
  ...tempOrderRest
1177
1229
  } = tempOrder;
1178
- let submitType = type ?? tempOrder.type;
1179
- if (!submitType) {
1180
- // 如果外部没有传递 types,根据订单里当前是否有 bookings 来决定是 appointment_booking 还是 virtual
1181
- if (tempOrder?.bookings?.length) {
1182
- // 260807 如果 biz 是堂食,type 使用table-order
1183
- if (businessCode === 'dine_in') {
1184
- submitType = 'table-order';
1185
- } else {
1186
- submitType = 'appointment_booking';
1187
- }
1188
- } else {
1189
- submitType = 'virtual';
1190
- }
1191
- }
1230
+ const resolvedBusinessCode = businessCode ?? tempOrder.business_code;
1231
+ const submitType = resolveSubmitOrderType({
1232
+ requestedType: type,
1233
+ tempOrderType: tempOrder.type,
1234
+ orderId: tempOrder.order_id,
1235
+ businessCode,
1236
+ tempOrderBusinessCode: tempOrder.business_code,
1237
+ bookings: tempOrder.bookings
1238
+ });
1192
1239
  const payload = {
1193
1240
  ...tempOrderRest,
1194
1241
  customer_id: tempOrder.customer_id ?? 1,
@@ -1202,7 +1249,7 @@ function buildSubmitPayload(params) {
1202
1249
  platform: normalizeSubmitPlatform(platform),
1203
1250
  request_unique_idempotency_token: params?.request_unique_idempotency_token || tempOrder.request_unique_idempotency_token || `${tempOrder.external_sale_number}_${now.getTime()}`,
1204
1251
  type: submitType,
1205
- business_code: businessCode ?? tempOrder.business_code,
1252
+ business_code: resolvedBusinessCode,
1206
1253
  sales_channel: tempOrder.sales_channel || 'my_pisel',
1207
1254
  order_sales_channel: channel ?? tempOrder.order_sales_channel ?? 'online_store',
1208
1255
  status: tempOrder.status || 'normal',
@@ -1235,6 +1282,11 @@ function buildSubmitPayload(params) {
1235
1282
  ...rest
1236
1283
  };
1237
1284
  })(),
1285
+ ...(tempOrder._extend?.salesNotesState?.dirty ? {
1286
+ notes: (0, _salesNotes.encodeSalesNotes)(tempOrder.notes || [], {
1287
+ allowUnresolvedOrderTarget: true
1288
+ })
1289
+ } : {}),
1238
1290
  products: (tempOrder.products || []).map(product => normalizeSubmitProduct(product))
1239
1291
  };
1240
1292
  if (!enhance) return payload;
@@ -1,6 +1,6 @@
1
1
  import { Module, PisellCore } from '../../types';
2
2
  import { BaseModule } from '../BaseModule';
3
- import { ProductListLoadProductsParams } from './types';
3
+ import { ProductListLoadProductsParams, ProductListStoreQueryParams } from './types';
4
4
  import { ProductData } from '../Product/types';
5
5
  export * from './types';
6
6
  export declare class ProductList extends BaseModule implements Module {
@@ -52,6 +52,7 @@ export declare class ProductList extends BaseModule implements Module {
52
52
  getRawProductPriceSources(ids: number[]): Map<number, ProductData>;
53
53
  getProducts(): Promise<ProductData[]>;
54
54
  getProduct(id: number): Promise<ProductData | undefined>;
55
+ queryProducts(params?: ProductListStoreQueryParams): Promise<ProductData[]>;
55
56
  getProductByIds(ids: number[]): Promise<ProductData[] | undefined>;
56
57
  addProduct(products: ProductData[]): Promise<void>;
57
58
  selectProducts(products: ProductData[]): Promise<void>;
@@ -25,6 +25,15 @@ Object.keys(_types).forEach(function (key) {
25
25
  });
26
26
  });
27
27
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28
+ function normalizeQuerySet(values) {
29
+ if (!Array.isArray(values) || values.length === 0) return null;
30
+ const normalized = values.map(value => String(value ?? '').trim()).filter(Boolean);
31
+ return normalized.length ? new Set(normalized) : null;
32
+ }
33
+ function matchesAny(values, querySet) {
34
+ if (!querySet) return true;
35
+ return values.some(value => querySet.has(String(value ?? '').trim()));
36
+ }
28
37
  class ProductList extends _BaseModule.BaseModule {
29
38
  defaultName = 'productList';
30
39
  defaultVersion = '1.0.0';
@@ -281,10 +290,46 @@ class ProductList extends _BaseModule.BaseModule {
281
290
  const product = this.store.list.find(product => product.id === id);
282
291
  return product ? (0, _lodashEs.cloneDeep)(product) : undefined;
283
292
  }
293
+ async queryProducts(params = {}) {
294
+ const includeIds = normalizeQuerySet(params.includeIds);
295
+ const excludeIds = normalizeQuerySet(params.excludeIds);
296
+ const categoryIds = normalizeQuerySet(params.categoryIds);
297
+ const collectionIds = normalizeQuerySet(params.collectionIds);
298
+ const extensionTypes = normalizeQuerySet(params.extensionTypes);
299
+ const statuses = normalizeQuerySet(params.statuses);
300
+ const channelApplications = normalizeQuerySet(params.channelApplications);
301
+ const codeOrBarcode = typeof params.codeOrBarcode === 'string' ? params.codeOrBarcode.trim() : '';
302
+ const products = (this.store.list || []).filter(product => {
303
+ const productId = String(product.id ?? '').trim();
304
+ if (includeIds && !includeIds.has(productId)) return false;
305
+ if (excludeIds && excludeIds.has(productId)) return false;
306
+ if (categoryIds) {
307
+ const productCategoryIds = (product.categories || []).flatMap(category => [category?.id, category?.pivot?.product_category_id]);
308
+ if (!matchesAny(productCategoryIds, categoryIds)) return false;
309
+ }
310
+ if (collectionIds) {
311
+ const productCollectionIds = (product.collection || []).map(collection => collection?.id);
312
+ if (!matchesAny(productCollectionIds, collectionIds)) return false;
313
+ }
314
+ if (extensionTypes && !extensionTypes.has(String(product.extension_type ?? '').trim())) return false;
315
+ if (statuses && !statuses.has(String(product.status ?? '').trim())) return false;
316
+ if (channelApplications) {
317
+ if (!matchesAny(product.channel_application || [], channelApplications)) return false;
318
+ }
319
+ if (codeOrBarcode) {
320
+ const code = typeof product.code === 'string' ? product.code.trim() : '';
321
+ const barcode = typeof product.barcode === 'string' ? product.barcode.trim() : '';
322
+ if (code !== codeOrBarcode && barcode !== codeOrBarcode) return false;
323
+ }
324
+ return true;
325
+ });
326
+ return (0, _lodashEs.cloneDeep)(products);
327
+ }
284
328
  async getProductByIds(ids) {
285
329
  await this.core.effects.emit(`${this.name}:onGetProductByIds`, this.store.list);
286
- const products = this.store.list.filter(product => ids.includes(product.id));
287
- return products;
330
+ return this.queryProducts({
331
+ includeIds: ids
332
+ });
288
333
  }
289
334
  async addProduct(products) {
290
335
  // list 需要根据 id 去重
@@ -15,6 +15,16 @@ export interface ProductListLoadProductsParams {
15
15
  /** 智能定价条件上下文,仅影响策略命中,不参与商品集合筛选 */
16
16
  strategy_context?: Record<string, any>;
17
17
  }
18
+ export interface ProductListStoreQueryParams {
19
+ includeIds?: Array<number | string>;
20
+ excludeIds?: Array<number | string>;
21
+ categoryIds?: Array<number | string>;
22
+ collectionIds?: Array<number | string>;
23
+ extensionTypes?: string[];
24
+ statuses?: ProductData['status'][];
25
+ channelApplications?: string[];
26
+ codeOrBarcode?: string;
27
+ }
18
28
  export interface ProductListData {
19
29
  list: ProductData[];
20
30
  selectProducts: ProductData[];
@@ -520,6 +520,12 @@ declare class Server {
520
520
  private stringifyCheckoutResourceIdentifier;
521
521
  private normalizeCheckoutSubmitData;
522
522
  private ensureCheckoutOrderNumbers;
523
+ /**
524
+ * Notes 允许在本地草稿阶段暂存空订单目标;checkout 的统一出口负责用最终
525
+ * shop_order_number 完成绑定并校验,保证后端请求、pending、设备任务和打印
526
+ * 都使用同一份完整快照。
527
+ */
528
+ private finalizeCheckoutSalesNotes;
523
529
  private dispatchCheckoutSyncTask;
524
530
  private dispatchPrintOtherReceiptTask;
525
531
  private buildPrintOtherReceiptIdempotencyKey;
@@ -27,6 +27,7 @@ var _filterBookings = require("./modules/order/utils/filterBookings");
27
27
  var _smallTicket = require("./utils/small-ticket");
28
28
  var _BaseSales = require("../solution/BaseSales");
29
29
  var _paymentUtils = require("../modules/Order/payment-utils");
30
+ var _salesNotes = require("../modules/Order/salesNotes");
30
31
  var _modules = require("./modules");
31
32
  Object.keys(_modules).forEach(function (key) {
32
33
  if (key === "default" || key === "__esModule") return;
@@ -3585,14 +3586,16 @@ class Server {
3585
3586
  };
3586
3587
  const needsShopOrderNumber = next.shop_order_number === undefined || next.shop_order_number === null || next.shop_order_number === '';
3587
3588
  const needsShopFullOrderNumber = next.shop_full_order_number === undefined || next.shop_full_order_number === null || next.shop_full_order_number === '';
3588
- if (!needsShopOrderNumber && !needsShopFullOrderNumber) return next;
3589
+ if (!needsShopOrderNumber && !needsShopFullOrderNumber) {
3590
+ return this.finalizeCheckoutSalesNotes(next, title);
3591
+ }
3589
3592
  const idGenerator = this.getIdGeneratorPlugin();
3590
3593
  if (!idGenerator) {
3591
3594
  this.logWarning(`${title}: idGenerator 插件不可用,无法生成订单号`, {
3592
3595
  needsShopOrderNumber,
3593
3596
  needsShopFullOrderNumber
3594
3597
  });
3595
- return next;
3598
+ return this.finalizeCheckoutSalesNotes(next, title);
3596
3599
  }
3597
3600
  try {
3598
3601
  if (needsShopOrderNumber && idGenerator.generateShopOrderNumber) {
@@ -3614,7 +3617,38 @@ class Server {
3614
3617
  error: error instanceof Error ? error.message : String(error)
3615
3618
  });
3616
3619
  }
3617
- return next;
3620
+ return this.finalizeCheckoutSalesNotes(next, title);
3621
+ }
3622
+
3623
+ /**
3624
+ * Notes 允许在本地草稿阶段暂存空订单目标;checkout 的统一出口负责用最终
3625
+ * shop_order_number 完成绑定并校验,保证后端请求、pending、设备任务和打印
3626
+ * 都使用同一份完整快照。
3627
+ */
3628
+ finalizeCheckoutSalesNotes(data, title) {
3629
+ if (!data || typeof data !== 'object') return data;
3630
+ if (!Object.prototype.hasOwnProperty.call(data, 'notes')) return data;
3631
+ if (data.notes === undefined || data.notes === null) return data;
3632
+ if (!Array.isArray(data.notes)) {
3633
+ throw new Error('[Order Notes] checkout notes 必须为数组');
3634
+ }
3635
+ if (data.notes.length === 0) return data;
3636
+ const productTargetUuids = (Array.isArray(data.products) ? data.products : []).map(product => product?.metadata?.unique_identification_number ?? product?.unique_identification_number).filter(targetUuid => targetUuid !== undefined && targetUuid !== null && String(targetUuid).trim().length > 0).map(targetUuid => String(targetUuid));
3637
+ const shopOrderNumber = String(data.shop_order_number ?? '').trim();
3638
+ const notes = (0, _salesNotes.finalizeSalesNotesForCheckout)({
3639
+ notes: data.notes,
3640
+ shopOrderNumber,
3641
+ productTargetUuids
3642
+ });
3643
+ this.logInfo(`${title}: checkout Notes 已绑定订单号`, {
3644
+ shop_order_number: shopOrderNumber,
3645
+ note_count: notes.length
3646
+ });
3647
+ return {
3648
+ ...data,
3649
+ shop_order_number: shopOrderNumber,
3650
+ notes
3651
+ };
3618
3652
  }
3619
3653
  async dispatchCheckoutSyncTask(params) {
3620
3654
  this.registerDeviceTaskActions();
@@ -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, BaseSalesPriceRecalculationContextChange, BaseSalesProductBookingPriceResult, BaseSalesQuotationCustomerScopeInfo, BaseSalesScanCodeResult, BaseSalesCashPaymentConfig, BaseSalesPayCashParams, BaseSalesPayCashResult, BaseSalesCommitPaymentMethodParams, BaseSalesCommitPaymentMethodResult, BaseSalesConfirmWalletPaymentResult } from './types';
4
4
  import { OrderModule } from '../../modules/Order';
5
- import type { AddProductBookingInput, AddProductToOrderOptions, LoadSalesDetailParams, OrderPaymentData, OrderPaymentUpdateOptions, OrderPaymentUpdateResult, OrderPaymentSource, SendCustomerPayLinkParams, SyncPaymentsToOrderParams, SyncPaymentsToOrderResult, UpdateTempOrderCustomerInput, UpdateOrderBookingParams, UpdateOrderProductParams, OrderTempOrder, OrderSummary, OrderPromotionEvaluator, OrderGiftSelectResolver, OrderUnfulfilledPromotion, OrderLastGiftActions, RemoveProductsFromOrderOptions, ReplaceTempOrderOptions } from '../../modules/Order/types';
5
+ import type { AddProductBookingInput, AddProductToOrderOptions, LoadSalesDetailParams, OrderPaymentData, OrderPaymentUpdateOptions, OrderPaymentUpdateResult, OrderPaymentSource, SendCustomerPayLinkParams, SyncPaymentsToOrderParams, SyncPaymentsToOrderResult, UpdateTempOrderCustomerInput, UpdateOrderBookingParams, UpdateOrderProductParams, OrderTempOrder, OrderSummary, OrderPromotionEvaluator, OrderGiftSelectResolver, OrderUnfulfilledPromotion, OrderLastGiftActions, RemoveProductsFromOrderOptions, ReplaceTempOrderOptions, SalesNote, SalesNotesMutationSnapshot, SetSalesNoteInput, PickupReferenceMode, ShopServiceData, ShopServiceType } 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';
@@ -225,12 +225,20 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
225
225
  getOrderSyncState(): import("../../modules/Order/types").OrderSyncState;
226
226
  getOrderAmountSnapshot(): import("../../modules/Order/types").OrderAmountSnapshot | null;
227
227
  getTempOrderNote(): string;
228
+ getSalesNotes(): SalesNote[];
229
+ createSalesNotesMutationSnapshot(): SalesNotesMutationSnapshot;
230
+ restoreSalesNotesMutationSnapshot(snapshot: SalesNotesMutationSnapshot): void;
231
+ setSalesNote(input: SetSalesNoteInput): Promise<SalesNote>;
232
+ removeSalesNote(uuid: string): Promise<void>;
233
+ clearSalesNotes(): Promise<void>;
228
234
  updateTempOrderNote(note: string): string;
229
235
  updateTempOrderNote(note: string, sync: true): Promise<string>;
230
236
  updateTempOrderNote(note: string, sync: boolean): string | Promise<string>;
231
237
  updateRemoteOrderNote(orderId: number | string, note: string): void;
232
238
  updateTempOrderShopDiscount(amount: string | number): string;
233
239
  updateTempOrderDepositAmount(amount: string | number): OrderSummary;
240
+ setPickupReferenceMode(mode: PickupReferenceMode): ShopServiceData;
241
+ setServiceType(serviceType: ShopServiceType): ShopServiceData;
234
242
  updateTempOrderContactsInfo(contactsInfo: Record<string, any> | null): Record<string, any> | null;
235
243
  /** @deprecated OrderModule 不再负责 tempOrder localStorage 持久化。 */
236
244
  setEnableTempOrderPersist(enabled: boolean): void;
@@ -283,7 +283,56 @@ function mergeBaseSalesUniqueArray(currentValue, loadedValue, metadataKey) {
283
283
  loadedValue.forEach(appendOrReplaceByUid);
284
284
  return result;
285
285
  }
286
+
287
+ // checkout / realtime snapshots may reorder protocol arrays or append persistence fields.
288
+ // Compare only the Sales Note protocol fields so a matching remote echo can acknowledge dirty state.
289
+ function normalizeBaseSalesNotesForComparison(value, options) {
290
+ if (!Array.isArray(value)) return null;
291
+ const unresolvedOrderTargetUuid = String(options?.unresolvedOrderTargetUuid ?? '').trim();
292
+ return value.map(note => {
293
+ if (!note || typeof note !== 'object' || Array.isArray(note)) {
294
+ return (0, _lodashEs.cloneDeep)(note);
295
+ }
296
+ const noteRecord = note;
297
+ const relations = Array.isArray(noteRecord.note_relations) ? noteRecord.note_relations.map(relation => {
298
+ if (!relation || typeof relation !== 'object' || Array.isArray(relation)) {
299
+ return (0, _lodashEs.cloneDeep)(relation);
300
+ }
301
+ const shouldResolveOrderTarget = relation.is_primary === 0 && relation.relation_type === 'related_to' && relation.target_type === 'order' && String(relation.target_uuid ?? '').trim() === '' && unresolvedOrderTargetUuid.length > 0;
302
+ return {
303
+ uuid: relation.uuid,
304
+ relation_type: relation.relation_type,
305
+ target_type: relation.target_type,
306
+ target_uuid: shouldResolveOrderTarget ? unresolvedOrderTargetUuid : relation.target_uuid,
307
+ is_primary: relation.is_primary
308
+ };
309
+ }).sort((left, right) => String(left?.uuid ?? '').localeCompare(String(right?.uuid ?? ''))) : (0, _lodashEs.cloneDeep)(noteRecord.note_relations);
310
+ return {
311
+ uuid: noteRecord.uuid,
312
+ note_type: noteRecord.note_type,
313
+ content: {
314
+ text: (0, _lodashEs.cloneDeep)(noteRecord.content?.text)
315
+ },
316
+ importance: noteRecord.importance,
317
+ note_relations: relations
318
+ };
319
+ }).sort((left, right) => String(left?.uuid ?? '').localeCompare(String(right?.uuid ?? '')));
320
+ }
321
+ function areBaseSalesNotesEquivalent(currentValue, loadedValue, options) {
322
+ const currentNotes = normalizeBaseSalesNotesForComparison(currentValue, {
323
+ unresolvedOrderTargetUuid: options?.currentOrderTargetUuid
324
+ });
325
+ const loadedNotes = normalizeBaseSalesNotesForComparison(loadedValue);
326
+ return currentNotes !== null && loadedNotes !== null && (0, _lodashEs.isEqual)(currentNotes, loadedNotes);
327
+ }
286
328
  function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, strategy) {
329
+ const loadedHasSalesNotes = Object.prototype.hasOwnProperty.call(loadedRecord || {}, 'notes');
330
+ const hasDirtyLocalSalesNotes = currentTempOrder?._extend?.salesNotesState?.dirty === true;
331
+ const incomingSalesNotesMatchLocal = loadedHasSalesNotes && areBaseSalesNotesEquivalent(currentTempOrder?.notes, loadedRecord.notes, {
332
+ currentOrderTargetUuid: loadedRecord?.shop_order_number || currentTempOrder?.shop_order_number
333
+ });
334
+ const shouldAwaitAuthoritativeSalesNotes = strategy === 'authoritative-incoming' && hasDirtyLocalSalesNotes && (Number(loadedRecord?.need_sync) === 1 || !incomingSalesNotesMatchLocal);
335
+ const preserveDirtyLocalSalesNotes = hasDirtyLocalSalesNotes && (strategy === 'preserve-local' || shouldAwaitAuthoritativeSalesNotes);
287
336
  const normalizedLoadedRecord = normalizeBaseSalesRecordCollections(loadedRecord || {}, {
288
337
  ensureUid: false
289
338
  });
@@ -295,6 +344,12 @@ function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, str
295
344
  });
296
345
  const uidRemaps = [];
297
346
  const mergedRecord = (0, _lodashEs.mergeWith)(normalizedCurrentTempOrder, normalizedLoadedRecord, (currentValue, loadedValue, key) => {
347
+ if (key === 'notes') {
348
+ if (preserveDirtyLocalSalesNotes && Array.isArray(currentValue)) {
349
+ return (0, _lodashEs.cloneDeep)(currentValue);
350
+ }
351
+ if (Array.isArray(loadedValue)) return (0, _lodashEs.cloneDeep)(loadedValue);
352
+ }
298
353
  const collectionKind = getBaseSalesOrderCollectionKind(key);
299
354
  if (collectionKind && Array.isArray(loadedValue)) {
300
355
  return mergeBaseSalesOrderCollection(collectionKind, Array.isArray(currentValue) ? currentValue : [], loadedValue, strategy, uidRemaps);
@@ -318,7 +373,24 @@ function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, str
318
373
  return undefined;
319
374
  });
320
375
  delete mergedRecord.request_unique_idempotency_token;
321
- return normalizeBaseSalesRecordCollections((0, _orderCollectionIdentity.applyOrderCollectionUidRemaps)(mergedRecord, uidRemaps));
376
+ const normalizedMergedRecord = normalizeBaseSalesRecordCollections((0, _orderCollectionIdentity.applyOrderCollectionUidRemaps)(mergedRecord, uidRemaps));
377
+ normalizedMergedRecord._extend = {
378
+ ...(normalizedMergedRecord._extend || {}),
379
+ salesNotesState: preserveDirtyLocalSalesNotes ? {
380
+ loaded: true,
381
+ dirty: true
382
+ } : loadedHasSalesNotes ? {
383
+ loaded: true,
384
+ dirty: normalizedLoadedRecord._extend?.salesNotesState?.dirty === true
385
+ } : {
386
+ loaded: false,
387
+ dirty: false
388
+ }
389
+ };
390
+ if (!loadedHasSalesNotes && !preserveDirtyLocalSalesNotes) {
391
+ delete normalizedMergedRecord.notes;
392
+ }
393
+ return normalizedMergedRecord;
322
394
  }
323
395
  function filterPendingPaymentsFromLoadedSalesDetail(sourceRecord) {
324
396
  if (!sourceRecord || typeof sourceRecord !== 'object') return sourceRecord;
@@ -1426,6 +1498,30 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1426
1498
  if (!this.store.order) return '';
1427
1499
  return this.store.order.getTempOrderNote();
1428
1500
  }
1501
+ getSalesNotes() {
1502
+ if (!this.store.order) return [];
1503
+ return this.store.order.getSalesNotes();
1504
+ }
1505
+ createSalesNotesMutationSnapshot() {
1506
+ if (!this.store.order) throw new Error('order 模块未初始化');
1507
+ return this.store.order.createSalesNotesMutationSnapshot();
1508
+ }
1509
+ restoreSalesNotesMutationSnapshot(snapshot) {
1510
+ if (!this.store.order) throw new Error('order 模块未初始化');
1511
+ this.store.order.restoreSalesNotesMutationSnapshot(snapshot);
1512
+ }
1513
+ async setSalesNote(input) {
1514
+ if (!this.store.order) throw new Error('order 模块未初始化');
1515
+ return this.store.order.setSalesNote(input);
1516
+ }
1517
+ async removeSalesNote(uuid) {
1518
+ if (!this.store.order) throw new Error('order 模块未初始化');
1519
+ await this.store.order.removeSalesNote(uuid);
1520
+ }
1521
+ async clearSalesNotes() {
1522
+ if (!this.store.order) throw new Error('order 模块未初始化');
1523
+ await this.store.order.clearSalesNotes();
1524
+ }
1429
1525
  updateTempOrderNote(note, sync = false) {
1430
1526
  try {
1431
1527
  if (!this.store.order) throw new Error('order 模块未初始化');
@@ -1447,6 +1543,14 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1447
1543
  if (!this.store.order) throw new Error('order 模块未初始化');
1448
1544
  return this.store.order.updateTempOrderDepositAmount(amount);
1449
1545
  }
1546
+ setPickupReferenceMode(mode) {
1547
+ if (!this.store.order) throw new Error('order 模块未初始化');
1548
+ return this.store.order.updateTempOrderPickupReferenceMode(mode);
1549
+ }
1550
+ setServiceType(serviceType) {
1551
+ if (!this.store.order) throw new Error('order 模块未初始化');
1552
+ return this.store.order.updateTempOrderServiceType(serviceType);
1553
+ }
1450
1554
  updateTempOrderContactsInfo(contactsInfo) {
1451
1555
  if (!this.store.order) throw new Error('order 模块未初始化');
1452
1556
  return this.store.order.updateTempOrderContactsInfo(contactsInfo);
@@ -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 | 5 | 4 | 3 | 6;
329
+ weekNum: 0 | 2 | 1 | 3 | 4 | 5 | 6;
330
330
  }[]>;
331
331
  submitTimeSlot(timeSlots: TimeSliceItem): void;
332
332
  private getScheduleDataByIds;
@@ -345,7 +345,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
345
345
  count: number;
346
346
  left: number;
347
347
  summaryCount: number;
348
- status: "sold_out" | "lots_of_space" | "filling_up_fast";
348
+ status: "lots_of_space" | "filling_up_fast" | "sold_out";
349
349
  }[];
350
350
  /**
351
351
  * 找到多个资源的公共可用时间段
@@ -23,7 +23,8 @@ function resolveCustomerDisplayName(customer) {
23
23
 
24
24
  /** 将 ICustomer 格式化为写入 tempOrder 的协议字段。 */
25
25
  function formatOrderCustomerPatch(customer) {
26
- const rawId = customer.id;
26
+ const c = customer;
27
+ const rawId = c.customer_id ?? c.customerId ?? customer.id;
27
28
  const numericId = typeof rawId === 'number' ? rawId : rawId !== undefined && rawId !== null && rawId !== '' ? Number(rawId) : null;
28
29
  return {
29
30
  customer_id: Number.isFinite(numericId) ? numericId : null,
@@ -10,7 +10,11 @@ export interface ApiConfig {
10
10
  method: 'GET' | 'POST' | 'PUT' | 'DELETE';
11
11
  transformParams?: (params: any) => any;
12
12
  transformResponse?: (response: any) => any;
13
- options?: Record<string, any>;
13
+ options?: Record<string, any> | ((params: any) => Record<string, any>);
14
+ }
15
+ export interface ApiCallerOptions {
16
+ baseUrl?: string;
17
+ shopDomain?: string;
14
18
  }
15
19
  /**
16
20
  * 渠道配置接口
@@ -65,12 +69,14 @@ export declare function getChannelConfig(channel: string): ChannelConfig;
65
69
  export declare class ApiCaller {
66
70
  private request;
67
71
  private config;
72
+ private options;
68
73
  constructor(request: {
69
74
  get: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
70
75
  post: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
71
76
  put: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
72
77
  delete: <T = any>(url: string, options?: any) => Promise<T>;
73
- }, config: ChannelConfig);
78
+ }, config: ChannelConfig, options?: ApiCallerOptions);
79
+ private resolveUrl;
74
80
  /**
75
81
  * 执行 API 调用
76
82
  */
@@ -84,4 +90,4 @@ export declare function createApiCaller(request: {
84
90
  post: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
85
91
  put: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
86
92
  delete: <T = any>(url: string, options?: any) => Promise<T>;
87
- }, channel: string): ApiCaller;
93
+ }, channel: string, options?: ApiCallerOptions): ApiCaller;