@pisell/pisellos 2.2.233 → 2.2.234

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 (49) hide show
  1. package/dist/modules/Order/index.d.ts +36 -1
  2. package/dist/modules/Order/index.js +496 -308
  3. package/dist/modules/Order/types.d.ts +11 -0
  4. package/dist/modules/Order/utils.d.ts +1 -0
  5. package/dist/modules/Order/utils.js +1 -1
  6. package/dist/server/index.d.ts +5 -0
  7. package/dist/server/index.js +855 -633
  8. package/dist/server/modules/order/index.d.ts +2 -0
  9. package/dist/server/modules/order/index.js +86 -32
  10. package/dist/solution/BaseSales/index.d.ts +6 -0
  11. package/dist/solution/BaseSales/index.js +240 -197
  12. package/dist/solution/BookingByStep/index.d.ts +1 -1
  13. package/dist/solution/BookingTicket/index.d.ts +38 -2
  14. package/dist/solution/BookingTicket/index.js +642 -313
  15. package/dist/solution/BookingTicket/types.d.ts +62 -0
  16. package/dist/solution/BookingTicket/types.js +30 -0
  17. package/dist/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
  18. package/dist/solution/BookingTicket/utils/addTimeAvailability.js +95 -0
  19. package/dist/solution/BookingTicket/utils/cartView.d.ts +9 -1
  20. package/dist/solution/BookingTicket/utils/cartView.js +76 -5
  21. package/dist/solution/BookingTicket/utils/exampleData.d.ts +123 -0
  22. package/dist/solution/BookingTicket/utils/exampleData.js +183 -0
  23. package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +9 -13
  24. package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.js +42 -33
  25. package/lib/model/strategy/adapter/promotion/index.js +0 -49
  26. package/lib/modules/Order/index.d.ts +36 -1
  27. package/lib/modules/Order/index.js +98 -10
  28. package/lib/modules/Order/types.d.ts +11 -0
  29. package/lib/modules/Order/utils.d.ts +1 -0
  30. package/lib/modules/Order/utils.js +1 -1
  31. package/lib/server/index.d.ts +5 -0
  32. package/lib/server/index.js +224 -63
  33. package/lib/server/modules/order/index.d.ts +2 -0
  34. package/lib/server/modules/order/index.js +43 -1
  35. package/lib/solution/BaseSales/index.d.ts +6 -0
  36. package/lib/solution/BaseSales/index.js +20 -1
  37. package/lib/solution/BookingByStep/index.d.ts +1 -1
  38. package/lib/solution/BookingTicket/index.d.ts +38 -2
  39. package/lib/solution/BookingTicket/index.js +194 -3
  40. package/lib/solution/BookingTicket/types.d.ts +62 -0
  41. package/lib/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
  42. package/lib/solution/BookingTicket/utils/addTimeAvailability.js +125 -0
  43. package/lib/solution/BookingTicket/utils/cartView.d.ts +9 -1
  44. package/lib/solution/BookingTicket/utils/cartView.js +48 -2
  45. package/lib/solution/BookingTicket/utils/exampleData.d.ts +123 -0
  46. package/lib/solution/BookingTicket/utils/exampleData.js +0 -0
  47. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +9 -13
  48. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +41 -28
  49. package/package.json +1 -1
@@ -2144,6 +2144,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2144
2144
  hasOrderProductLineNote(product) {
2145
2145
  return typeof (product == null ? void 0 : product.note) === "string" && product.note.trim().length > 0;
2146
2146
  }
2147
+ /**
2148
+ * 判断商品行是否显式绑定了 booking。
2149
+ *
2150
+ * @example
2151
+ * this.hasLinkedBookingUid({ metadata: { booking_uid: 'booking-1' } });
2152
+ */
2153
+ hasLinkedBookingUid(product) {
2154
+ var _a;
2155
+ const bookingUid = (product == null ? void 0 : product.booking_uid) ?? ((_a = product == null ? void 0 : product.metadata) == null ? void 0 : _a.booking_uid);
2156
+ return bookingUid !== void 0 && bookingUid !== null && String(bookingUid) !== "";
2157
+ }
2147
2158
  // ─── TempOrder: 商品 CRUD ───
2148
2159
  /**
2149
2160
  * 预约商品加车后的统一收尾:促销、折扣、summary 重算与持久化(批量拆行时只调用一次)。
@@ -2151,11 +2162,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2151
2162
  * @example
2152
2163
  * await this.finalizeProductOrderMutation(tempOrder);
2153
2164
  */
2154
- async finalizeProductOrderMutation(tempOrder) {
2165
+ async finalizeProductOrderMutation(tempOrder, options) {
2155
2166
  this.syncTempOrderHolderFromBookings(tempOrder);
2156
- await this.ensureEditDiscountConfigForProductChange(tempOrder);
2157
- await this.applyPromotion();
2158
- this.applyDiscount();
2167
+ if (!(options == null ? void 0 : options.skipEditDiscountConfigRefresh)) {
2168
+ await this.ensureEditDiscountConfigForProductChange(tempOrder);
2169
+ }
2170
+ if (!(options == null ? void 0 : options.skipDiscountRecalculation)) {
2171
+ await this.applyPromotion();
2172
+ }
2173
+ if (!(options == null ? void 0 : options.skipDiscountRecalculation)) {
2174
+ this.applyDiscount();
2175
+ }
2159
2176
  this.sanitizeTempOrderProducts(tempOrder);
2160
2177
  await this.recalculateSummary({ createIfMissing: true });
2161
2178
  this.persistTempOrder();
@@ -2168,7 +2185,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2168
2185
  * await this.appendProductLineToTempOrder(tempOrder, { product_id: 1, num: 1 }, booking);
2169
2186
  */
2170
2187
  async appendProductLineToTempOrder(tempOrder, product, booking) {
2171
- var _a;
2188
+ var _a, _b;
2172
2189
  const linked = booking ? this.createLinkedProductAndBooking({ product, booking }) : null;
2173
2190
  const productToAdd = (linked == null ? void 0 : linked.product) || product;
2174
2191
  const incomingFingerprint = (0, import_utils3.buildProductLineFingerprint)(
@@ -2179,6 +2196,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2179
2196
  productToAdd,
2180
2197
  (0, import_utils3.normalizeOrderProduct)(productToAdd)
2181
2198
  );
2199
+ const incomingBookingUid = productToAdd.booking_uid ?? ((_a = productToAdd.metadata) == null ? void 0 : _a.booking_uid);
2200
+ if (incomingBookingUid !== void 0 && incomingBookingUid !== null && String(incomingBookingUid) !== "") {
2201
+ normalizedIncoming.booking_uid = String(incomingBookingUid);
2202
+ normalizedIncoming.metadata = {
2203
+ ...normalizedIncoming.metadata || {},
2204
+ booking_uid: String(incomingBookingUid)
2205
+ };
2206
+ }
2182
2207
  normalizedIncoming.discount_list = (0, import_utils.normalizeOrderProductDiscountList)(
2183
2208
  normalizedIncoming.discount_list
2184
2209
  );
@@ -2186,7 +2211,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2186
2211
  normalizedIncoming
2187
2212
  );
2188
2213
  const hasIncomingProductNote = this.hasOrderProductLineNote(normalizedIncoming);
2189
- const shouldForceNewLine = !!booking || hasIncomingProductNote;
2214
+ const hasIncomingBookingUid = this.hasLinkedBookingUid(productToAdd) || this.hasLinkedBookingUid(normalizedIncoming);
2215
+ const shouldForceNewLine = !!booking || hasIncomingProductNote || hasIncomingBookingUid;
2190
2216
  const matchedIndex = hasIncomingGoodPass || shouldForceNewLine ? -1 : tempOrder.products.findIndex((item) => {
2191
2217
  if (this.hasGoodPassDiscount(item))
2192
2218
  return false;
@@ -2232,10 +2258,10 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2232
2258
  booking_uid: linked.bookingUid
2233
2259
  };
2234
2260
  }
2235
- tempOrder.products.push(normalizedIncoming);
2261
+ tempOrder.products = [...tempOrder.products, normalizedIncoming];
2236
2262
  } else {
2237
2263
  const targetProduct = matchedProduct;
2238
- const targetUid = (_a = targetProduct.metadata) == null ? void 0 : _a.unique_identification_number;
2264
+ const targetUid = (_b = targetProduct.metadata) == null ? void 0 : _b.unique_identification_number;
2239
2265
  const normalizedProduct = (0, import_utils.mergeOrderProductDisplayFields)(
2240
2266
  targetProduct,
2241
2267
  (0, import_utils3.normalizeOrderProduct)({
@@ -2329,6 +2355,47 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2329
2355
  await this.finalizeProductOrderMutation(tempOrder);
2330
2356
  return tempOrder.products;
2331
2357
  }
2358
+ /**
2359
+ * 批量添加商品行,所有行追加完成后只触发一次促销、折扣、summary 重算与持久化。
2360
+ *
2361
+ * @example
2362
+ * await order.addProductsToOrder([
2363
+ * { product: { product_id: 1, product_variant_id: 0, num: 1 } },
2364
+ * { product: { product_id: 2, product_variant_id: 0, num: 1 } },
2365
+ * ]);
2366
+ */
2367
+ async addProductsToOrder(items, options) {
2368
+ const tempOrder = this.ensureTempOrder();
2369
+ if (!Array.isArray(items) || items.length === 0)
2370
+ return tempOrder.products;
2371
+ for (const item of items || []) {
2372
+ const { product, booking } = item;
2373
+ if (!product)
2374
+ continue;
2375
+ if (booking) {
2376
+ const productRecord = product;
2377
+ const splitCount = (0, import_utils3.getSafeProductNum)(
2378
+ productRecord.num ?? productRecord.product_quantity ?? 1
2379
+ );
2380
+ if (splitCount > 1) {
2381
+ for (let i = 0; i < splitCount; i += 1) {
2382
+ const singleLine = (0, import_lodash_es.cloneDeep)(productRecord);
2383
+ singleLine.num = 1;
2384
+ delete singleLine.product_quantity;
2385
+ await this.appendProductLineToTempOrder(
2386
+ tempOrder,
2387
+ singleLine,
2388
+ (0, import_lodash_es.cloneDeep)(booking)
2389
+ );
2390
+ }
2391
+ continue;
2392
+ }
2393
+ }
2394
+ await this.appendProductLineToTempOrder(tempOrder, product, booking);
2395
+ }
2396
+ await this.finalizeProductOrderMutation(tempOrder, options);
2397
+ return tempOrder.products;
2398
+ }
2332
2399
  hasGoodPassDiscount(product) {
2333
2400
  var _a, _b;
2334
2401
  const hasMainGoodPass = (product.discount_list || []).some(
@@ -2752,6 +2819,15 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2752
2819
  * 应传 1,确保 /shop/order/sales/checkout 返回打印所需数据。
2753
2820
  */
2754
2821
  async submitTempOrder(params) {
2822
+ return this.runSubmitTempOrder(params);
2823
+ }
2824
+ async submitTempOrderAsync(params) {
2825
+ return this.runSubmitTempOrder({
2826
+ ...params,
2827
+ syncMode: true
2828
+ });
2829
+ }
2830
+ async runSubmitTempOrder(params) {
2755
2831
  var _a, _b, _c, _d, _e;
2756
2832
  const tempOrder = this.ensureTempOrder();
2757
2833
  this.persistTempOrder();
@@ -2782,8 +2858,12 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2782
2858
  channel: params == null ? void 0 : params.channel,
2783
2859
  type: params == null ? void 0 : params.type,
2784
2860
  summary: latestSummary,
2861
+ request_unique_idempotency_token: params == null ? void 0 : params.request_unique_idempotency_token,
2785
2862
  enhance: enhancePayload
2786
2863
  });
2864
+ if (params == null ? void 0 : params.syncMode) {
2865
+ payload.sync_mode = true;
2866
+ }
2787
2867
  if (!payload.created_at) {
2788
2868
  payload.created_at = (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss");
2789
2869
  }
@@ -2796,7 +2876,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2796
2876
  requestUniqueIdempotencyToken: payload.request_unique_idempotency_token,
2797
2877
  paymentStatus: payload.payment_status,
2798
2878
  paymentsCount: ((_d = payload.payments) == null ? void 0 : _d.length) || 0,
2799
- smallTicketDataFlag: payload.small_ticket_data_flag
2879
+ smallTicketDataFlag: payload.small_ticket_data_flag,
2880
+ syncMode: payload.sync_mode
2800
2881
  });
2801
2882
  result = await this.submitSalesOrder({
2802
2883
  query: payload
@@ -2895,6 +2976,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2895
2976
  }
2896
2977
  });
2897
2978
  }
2979
+ generateIdempotencyToken() {
2980
+ const tempOrder = this.ensureTempOrder();
2981
+ const token = this.buildPaymentSyncIdempotencyToken(
2982
+ tempOrder,
2983
+ tempOrder.payments
2984
+ );
2985
+ return `${token}_${Date.now()}`;
2986
+ }
2898
2987
  async syncPaymentsToOrder(params) {
2899
2988
  const tempOrder = this.ensureTempOrder();
2900
2989
  const mappedPayments = (0, import_utils.mapPaymentItemsToOrderPayments)(
@@ -3229,7 +3318,6 @@ var OrderModule = class extends import_BaseModule.BaseModule {
3229
3318
  with: ["products", "bookings", "payments", "customer", "summary", "contacts_info"],
3230
3319
  ...params.forceRemote ? { forceRemote: true } : {}
3231
3320
  }, { osServer: true });
3232
- debugger;
3233
3321
  if (res.code === 200) {
3234
3322
  this.store.lastOrderInfo = res.data;
3235
3323
  }
@@ -678,6 +678,17 @@ export interface OrderModuleAPI {
678
678
  smallTicketDataFlag?: number;
679
679
  enhancePayload?: SubmitPayloadEnhancer;
680
680
  }) => Promise<T>;
681
+ submitTempOrderAsync: <T = any>(params?: {
682
+ cacheId?: string;
683
+ platform?: string;
684
+ businessCode?: string;
685
+ channel?: string;
686
+ type?: string;
687
+ payments?: OrderPaymentSource[];
688
+ paymentStatus?: SubmitSalesOrderParams['query']['payment_status'];
689
+ smallTicketDataFlag?: number;
690
+ enhancePayload?: SubmitPayloadEnhancer;
691
+ }) => Promise<T>;
681
692
  getOrderPayments: () => OrderPaymentData[];
682
693
  setOrderPayments: (payments: OrderPaymentSource[]) => OrderPaymentData[];
683
694
  addOrderPayment: (payment: OrderPaymentSource) => OrderPaymentData[];
@@ -188,6 +188,7 @@ export declare function buildSubmitPayload(params: {
188
188
  channel?: string;
189
189
  type?: string;
190
190
  summary?: OrderSummary | null;
191
+ request_unique_idempotency_token?: string;
191
192
  enhance?: SubmitPayloadEnhancer;
192
193
  }): OrderSubmitPayload;
193
194
  export declare function mapPaymentItemToOrderPayment(paymentItem: OrderPaymentSource): OrderPaymentData;
@@ -754,7 +754,7 @@ function buildSubmitPayload(params) {
754
754
  shop_order_number: tempOrder.shop_order_number ?? null,
755
755
  shop_full_order_number: tempOrder.shop_full_order_number ?? null,
756
756
  platform: normalizeSubmitPlatform(platform),
757
- request_unique_idempotency_token: tempOrder.request_unique_idempotency_token || `${tempOrder.external_sale_number}_${now.getTime()}`,
757
+ request_unique_idempotency_token: (params == null ? void 0 : params.request_unique_idempotency_token) || tempOrder.request_unique_idempotency_token || `${tempOrder.external_sale_number}_${now.getTime()}`,
758
758
  type: submitType,
759
759
  business_code: businessCode ?? tempOrder.business_code,
760
760
  sales_channel: tempOrder.sales_channel || "my_pisel",
@@ -95,6 +95,9 @@ declare class Server {
95
95
  }[]>;
96
96
  private registerDeviceTaskActions;
97
97
  private handleSyncSalesTask;
98
+ private runCheckoutSync;
99
+ private omitCheckoutSyncMode;
100
+ private persistSyncedCheckoutOrder;
98
101
  /**
99
102
  * 将普通层 QuotationModule 的报价单计算能力桥接到 Server Products 模块。
100
103
  */
@@ -401,6 +404,7 @@ declare class Server {
401
404
  private getLocalPrintOrderLookup;
402
405
  private handleLocalPrintOrder;
403
406
  private handleOrderCheckoutSubmit;
407
+ private handleSyncCheckoutSubmit;
404
408
  private handlePendingSyncCheckoutOrder;
405
409
  private buildPendingSyncCheckoutOrder;
406
410
  private shouldBuildSmallTicketData;
@@ -455,6 +459,7 @@ declare class Server {
455
459
  private buildSyncedOrderForPrint;
456
460
  private isMergeableCheckoutOrder;
457
461
  private normalizeCheckoutResponse;
462
+ private withSyncedOrderIdInCheckoutResponse;
458
463
  private isCheckoutResponseRejected;
459
464
  private getUnknownErrorMessage;
460
465
  private normalizeUnknownError;
@@ -1170,75 +1170,28 @@ var Server = class {
1170
1170
  return this.taskTimeout("app.request 不可用");
1171
1171
  }
1172
1172
  try {
1173
- const response = await this.app.request.post(backendPath, data, {
1174
- isShopApi: true,
1175
- customToast: () => {
1176
- }
1173
+ const syncResult = await this.runCheckoutSync({
1174
+ backendPath,
1175
+ data,
1176
+ title,
1177
+ externalSaleNumber: payload.external_sale_number,
1178
+ deviceId: (_c = ctx.task) == null ? void 0 : _c.device_id,
1179
+ persistCheckoutDataWithResponse: false
1177
1180
  });
1178
- if (this.isCheckoutResponseRejected(response)) {
1179
- const message = (response == null ? void 0 : response.message) || "后端明确拒绝 checkout";
1181
+ if (syncResult.rejected) {
1180
1182
  this.logError(`${title}: sync_sales 后端明确失败`, {
1181
1183
  backendPath,
1182
- response
1183
- });
1184
- return this.taskFail("BACKEND_REJECTED", message, response);
1185
- }
1186
- const fresh = this.extractOrderDataFromCheckoutResponse(response);
1187
- const externalSaleNumber = this.getCheckoutExternalSaleNumber(data, payload);
1188
- const shouldMergeRemoteOrder = fresh ? this.isMergeableCheckoutOrder(fresh, externalSaleNumber) : false;
1189
- let syncedOrder;
1190
- if (this.order && fresh && shouldMergeRemoteOrder) {
1191
- syncedOrder = {
1192
- ...fresh,
1193
- external_sale_number: fresh.external_sale_number ?? externalSaleNumber,
1194
- need_sync: 0
1195
- };
1196
- await this.order.upsertOrdersFromRemote([syncedOrder]);
1197
- this.logInfo(`${title}: sync_sales 完整订单已同步到本地`, {
1198
- order_id: fresh.order_id,
1199
- external_sale_number: syncedOrder.external_sale_number,
1200
- order_number: fresh.order_number
1201
- });
1202
- } else if (this.order && externalSaleNumber && typeof this.order.markOrderSyncedByExternalSaleNumber === "function") {
1203
- const patch = this.buildCheckoutSyncSuccessPatch(fresh);
1204
- const result = await this.order.markOrderSyncedByExternalSaleNumber({
1205
- externalSaleNumber,
1206
- patch
1207
- });
1208
- syncedOrder = (result == null ? void 0 : result.order) || {
1209
- ...data,
1210
- ...patch,
1211
- external_sale_number: externalSaleNumber,
1212
- need_sync: 0
1213
- };
1214
- this.logInfo(`${title}: sync_sales 已按 external_sale_number 标记本地订单`, {
1215
- order_id: (syncedOrder == null ? void 0 : syncedOrder.order_id) ?? null,
1216
- external_sale_number: externalSaleNumber,
1217
- hasRemoteOrder: !!fresh,
1218
- mergedRemoteOrder: false
1219
- });
1220
- } else {
1221
- this.logWarning(`${title}: sync_sales Order 模块未注册`, {
1222
- backendPath,
1223
- external_sale_number: externalSaleNumber,
1224
- order_id: fresh == null ? void 0 : fresh.order_id
1225
- });
1226
- }
1227
- const printableSyncedOrder = syncedOrder ? this.buildSyncedOrderForPrint({
1228
- syncedOrder,
1229
- checkoutResponseOrder: fresh
1230
- }) : void 0;
1231
- if (printableSyncedOrder && this.shouldPrintSyncedOrder({ checkoutData: data, syncedOrder: printableSyncedOrder })) {
1232
- await this.dispatchPrintOtherReceiptTask({
1233
- checkoutData: data,
1234
- syncedOrder: printableSyncedOrder,
1235
- response,
1236
- deviceId: (_c = ctx.task) == null ? void 0 : _c.device_id
1184
+ response: syncResult.errorResponse
1237
1185
  });
1186
+ return this.taskFail(
1187
+ "BACKEND_REJECTED",
1188
+ syncResult.message || "后端明确拒绝 checkout",
1189
+ syncResult.errorResponse
1190
+ );
1238
1191
  }
1239
1192
  return this.taskOk({
1240
- order: syncedOrder ?? null,
1241
- response: this.normalizeCheckoutResponse(response)
1193
+ order: syncResult.syncedOrder ?? null,
1194
+ response: syncResult.normalizedResponse
1242
1195
  });
1243
1196
  } catch (error) {
1244
1197
  const backendError = this.extractBackendErrorResponse(error);
@@ -1259,6 +1212,135 @@ var Server = class {
1259
1212
  return this.taskTimeout(errorMessage);
1260
1213
  }
1261
1214
  }
1215
+ async runCheckoutSync(params) {
1216
+ var _a, _b;
1217
+ const { backendPath, title } = params;
1218
+ const normalizedData = await this.normalizeCheckoutSubmitData(params.data, title);
1219
+ const checkoutData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
1220
+ const remoteCheckoutData = this.omitCheckoutSyncMode(checkoutData);
1221
+ if (!((_b = (_a = this.app) == null ? void 0 : _a.request) == null ? void 0 : _b.post)) {
1222
+ throw new Error("app.request 不可用");
1223
+ }
1224
+ const response = await this.app.request.post(backendPath, remoteCheckoutData, {
1225
+ isShopApi: true,
1226
+ customToast: () => {
1227
+ }
1228
+ });
1229
+ if (this.isCheckoutResponseRejected(response)) {
1230
+ return {
1231
+ rejected: true,
1232
+ message: (response == null ? void 0 : response.message) || "后端明确拒绝 checkout",
1233
+ errorResponse: response,
1234
+ response,
1235
+ normalizedResponse: this.normalizeCheckoutResponse(response),
1236
+ checkoutData: remoteCheckoutData
1237
+ };
1238
+ }
1239
+ const fresh = this.extractOrderDataFromCheckoutResponse(response);
1240
+ const externalSaleNumber = this.getCheckoutExternalSaleNumber(
1241
+ remoteCheckoutData,
1242
+ {
1243
+ external_sale_number: params.externalSaleNumber,
1244
+ data: remoteCheckoutData
1245
+ }
1246
+ );
1247
+ const syncedOrder = await this.persistSyncedCheckoutOrder({
1248
+ backendPath,
1249
+ checkoutData: remoteCheckoutData,
1250
+ externalSaleNumber,
1251
+ fresh,
1252
+ title,
1253
+ persistCheckoutDataWithResponse: params.persistCheckoutDataWithResponse === true
1254
+ });
1255
+ const printableSyncedOrder = syncedOrder ? this.buildSyncedOrderForPrint({
1256
+ syncedOrder,
1257
+ checkoutResponseOrder: fresh
1258
+ }) : void 0;
1259
+ if (printableSyncedOrder && this.shouldPrintSyncedOrder({
1260
+ checkoutData: remoteCheckoutData,
1261
+ syncedOrder: printableSyncedOrder
1262
+ })) {
1263
+ await this.dispatchPrintOtherReceiptTask({
1264
+ checkoutData: remoteCheckoutData,
1265
+ syncedOrder: printableSyncedOrder,
1266
+ response,
1267
+ deviceId: params.deviceId
1268
+ });
1269
+ }
1270
+ return {
1271
+ rejected: false,
1272
+ response,
1273
+ normalizedResponse: this.normalizeCheckoutResponse(response),
1274
+ checkoutData: remoteCheckoutData,
1275
+ syncedOrder,
1276
+ fresh
1277
+ };
1278
+ }
1279
+ omitCheckoutSyncMode(data) {
1280
+ if (!data || typeof data !== "object")
1281
+ return data;
1282
+ const next = { ...data };
1283
+ delete next.sync_mode;
1284
+ delete next.syncMode;
1285
+ return next;
1286
+ }
1287
+ async persistSyncedCheckoutOrder(params) {
1288
+ const {
1289
+ backendPath,
1290
+ checkoutData,
1291
+ externalSaleNumber,
1292
+ fresh,
1293
+ title,
1294
+ persistCheckoutDataWithResponse
1295
+ } = params;
1296
+ const shouldMergeRemoteOrder = fresh ? this.isMergeableCheckoutOrder(fresh, externalSaleNumber) : false;
1297
+ let syncedOrder;
1298
+ if (this.order && fresh && (shouldMergeRemoteOrder || persistCheckoutDataWithResponse)) {
1299
+ syncedOrder = persistCheckoutDataWithResponse ? {
1300
+ ...checkoutData,
1301
+ ...fresh,
1302
+ external_sale_number: fresh.external_sale_number ?? externalSaleNumber,
1303
+ need_sync: 0
1304
+ } : {
1305
+ ...fresh,
1306
+ external_sale_number: fresh.external_sale_number ?? externalSaleNumber,
1307
+ need_sync: 0
1308
+ };
1309
+ await this.order.upsertOrdersFromRemote([syncedOrder]);
1310
+ this.logInfo(`${title}: sync_sales 完整订单已同步到本地`, {
1311
+ order_id: fresh.order_id,
1312
+ external_sale_number: syncedOrder.external_sale_number,
1313
+ order_number: fresh.order_number,
1314
+ mergedRemoteOrder: shouldMergeRemoteOrder,
1315
+ persistedCheckoutDataWithResponse: persistCheckoutDataWithResponse
1316
+ });
1317
+ } else if (this.order && externalSaleNumber && typeof this.order.markOrderSyncedByExternalSaleNumber === "function") {
1318
+ const patch = this.buildCheckoutSyncSuccessPatch(fresh);
1319
+ const result = await this.order.markOrderSyncedByExternalSaleNumber({
1320
+ externalSaleNumber,
1321
+ patch
1322
+ });
1323
+ syncedOrder = (result == null ? void 0 : result.order) || {
1324
+ ...checkoutData,
1325
+ ...patch,
1326
+ external_sale_number: externalSaleNumber,
1327
+ need_sync: 0
1328
+ };
1329
+ this.logInfo(`${title}: sync_sales 已按 external_sale_number 标记本地订单`, {
1330
+ order_id: (syncedOrder == null ? void 0 : syncedOrder.order_id) ?? null,
1331
+ external_sale_number: externalSaleNumber,
1332
+ hasRemoteOrder: !!fresh,
1333
+ mergedRemoteOrder: false
1334
+ });
1335
+ } else {
1336
+ this.logWarning(`${title}: sync_sales Order 模块未注册`, {
1337
+ backendPath,
1338
+ external_sale_number: externalSaleNumber,
1339
+ order_id: fresh == null ? void 0 : fresh.order_id
1340
+ });
1341
+ }
1342
+ return syncedOrder;
1343
+ }
1262
1344
  /**
1263
1345
  * 将普通层 QuotationModule 的报价单计算能力桥接到 Server Products 模块。
1264
1346
  */
@@ -2605,6 +2687,13 @@ var Server = class {
2605
2687
  external_sale_number: data == null ? void 0 : data.external_sale_number,
2606
2688
  order_number: data == null ? void 0 : data.order_number
2607
2689
  });
2690
+ if ((data == null ? void 0 : data.sync_mode) || (data == null ? void 0 : data.syncMode)) {
2691
+ return this.handleSyncCheckoutSubmit({
2692
+ backendPath,
2693
+ data,
2694
+ title
2695
+ });
2696
+ }
2608
2697
  const normalizedData = await this.normalizeCheckoutSubmitData(data, title);
2609
2698
  const checkoutData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
2610
2699
  const pendingResult = await this.handlePendingSyncCheckoutOrder({
@@ -2642,6 +2731,63 @@ var Server = class {
2642
2731
  }
2643
2732
  return pendingResult;
2644
2733
  }
2734
+ async handleSyncCheckoutSubmit(params) {
2735
+ var _a, _b;
2736
+ const { backendPath, data, title } = params;
2737
+ if (!((_b = (_a = this.app) == null ? void 0 : _a.request) == null ? void 0 : _b.post)) {
2738
+ this.logError(`${title}: sync checkout app.request 不可用`);
2739
+ return {
2740
+ code: 500,
2741
+ status: false,
2742
+ message: "app.request 不可用",
2743
+ data: null
2744
+ };
2745
+ }
2746
+ try {
2747
+ const syncResult = await this.runCheckoutSync({
2748
+ backendPath,
2749
+ data,
2750
+ title,
2751
+ persistCheckoutDataWithResponse: true
2752
+ });
2753
+ if (syncResult.rejected) {
2754
+ const normalized = this.normalizeCheckoutResponse(syncResult.errorResponse);
2755
+ return {
2756
+ ...normalized,
2757
+ status: false,
2758
+ message: syncResult.message || (normalized == null ? void 0 : normalized.message) || "后端明确拒绝 checkout",
2759
+ data: (normalized == null ? void 0 : normalized.data) ?? null
2760
+ };
2761
+ }
2762
+ return this.withSyncedOrderIdInCheckoutResponse(
2763
+ syncResult.normalizedResponse,
2764
+ syncResult.syncedOrder
2765
+ );
2766
+ } catch (error) {
2767
+ const backendError = this.extractBackendErrorResponse(error);
2768
+ if (backendError) {
2769
+ const normalized = this.normalizeCheckoutResponse(backendError);
2770
+ return {
2771
+ ...normalized,
2772
+ status: false,
2773
+ message: (backendError == null ? void 0 : backendError.message) || (normalized == null ? void 0 : normalized.message) || "后端明确拒绝 checkout",
2774
+ data: (normalized == null ? void 0 : normalized.data) ?? null
2775
+ };
2776
+ }
2777
+ const message = this.getUnknownErrorMessage(error);
2778
+ this.logError(`${title}: sync checkout 请求未明确成功`, {
2779
+ backendPath,
2780
+ error: message,
2781
+ error_detail: this.normalizeUnknownError(error)
2782
+ });
2783
+ return {
2784
+ code: 500,
2785
+ status: false,
2786
+ message,
2787
+ data: null
2788
+ };
2789
+ }
2790
+ }
2645
2791
  async handlePendingSyncCheckoutOrder(params) {
2646
2792
  var _a, _b, _c, _d;
2647
2793
  const { backendPath, data, title, reason } = params;
@@ -3219,6 +3365,21 @@ var Server = class {
3219
3365
  data: response
3220
3366
  };
3221
3367
  }
3368
+ withSyncedOrderIdInCheckoutResponse(response, syncedOrder) {
3369
+ const normalized = this.normalizeCheckoutResponse(response);
3370
+ const orderId = syncedOrder == null ? void 0 : syncedOrder.order_id;
3371
+ if (orderId === void 0 || orderId === null)
3372
+ return normalized;
3373
+ if (!(normalized == null ? void 0 : normalized.data) || typeof normalized.data !== "object")
3374
+ return normalized;
3375
+ return {
3376
+ ...normalized,
3377
+ data: {
3378
+ ...normalized.data,
3379
+ order_id: normalized.data.order_id ?? orderId
3380
+ }
3381
+ };
3382
+ }
3222
3383
  isCheckoutResponseRejected(response) {
3223
3384
  const normalized = this.normalizeCheckoutResponse(response);
3224
3385
  const isErrorCode = (normalized == null ? void 0 : normalized.code) !== void 0 && normalized.code !== 200 && normalized.code !== "200";
@@ -192,6 +192,8 @@ export declare class OrderModule extends BaseModule implements Module {
192
192
  private findOrderIndexByIdentity;
193
193
  private getOrderCompletenessScore;
194
194
  private pickPreferredOrder;
195
+ private getProductMergeKey;
196
+ private mergeOrderProductLists;
195
197
  private mergeOrderRecords;
196
198
  private summarizeDuplicateOrders;
197
199
  private logDuplicateOrders;
@@ -1130,6 +1130,45 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1130
1130
  return right;
1131
1131
  return left;
1132
1132
  }
1133
+ getProductMergeKey(product) {
1134
+ var _a;
1135
+ const record = product;
1136
+ if (!record)
1137
+ return "";
1138
+ const metadataUid = (_a = record.metadata) == null ? void 0 : _a.unique_identification_number;
1139
+ if (!this.isBlankIdentityValue(metadataUid))
1140
+ return `uid:${String(metadataUid)}`;
1141
+ const topLevelUid = record.unique_identification_number;
1142
+ if (!this.isBlankIdentityValue(topLevelUid))
1143
+ return `uid:${String(topLevelUid)}`;
1144
+ const bookingUid = record.booking_uid;
1145
+ if (!this.isBlankIdentityValue(bookingUid))
1146
+ return `booking:${String(bookingUid)}`;
1147
+ return "";
1148
+ }
1149
+ mergeOrderProductLists(preferred, secondary) {
1150
+ const preferredList = Array.isArray(preferred) ? preferred : [];
1151
+ const secondaryList = Array.isArray(secondary) ? secondary : [];
1152
+ if (secondaryList.length === 0)
1153
+ return preferredList;
1154
+ if (preferredList.length === 0)
1155
+ return secondaryList;
1156
+ const seenKeys = /* @__PURE__ */ new Set();
1157
+ for (const item of preferredList) {
1158
+ const key = this.getProductMergeKey(item);
1159
+ if (key)
1160
+ seenKeys.add(key);
1161
+ }
1162
+ const result = [...preferredList];
1163
+ for (const item of secondaryList) {
1164
+ const key = this.getProductMergeKey(item);
1165
+ if (!key || seenKeys.has(key))
1166
+ continue;
1167
+ seenKeys.add(key);
1168
+ result.push((0, import_lodash_es.cloneDeep)(item));
1169
+ }
1170
+ return result;
1171
+ }
1133
1172
  mergeOrderRecords(existing, incoming) {
1134
1173
  const preferred = this.pickPreferredOrder(existing, incoming);
1135
1174
  const secondary = preferred === existing ? incoming : existing;
@@ -1150,7 +1189,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1150
1189
  "shipping_status",
1151
1190
  "updated_at",
1152
1191
  "summary",
1153
- "payments"
1192
+ "payments",
1193
+ "products",
1194
+ "bookings"
1154
1195
  ];
1155
1196
  const mergedRecord = merged;
1156
1197
  const preferredRecord = preferred;
@@ -1174,6 +1215,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1174
1215
  continue;
1175
1216
  mergedRecord[field] = (0, import_lodash_es.cloneDeep)(incomingRecord[field]);
1176
1217
  }
1218
+ mergedRecord.products = this.mergeOrderProductLists(preferredRecord.products, secondaryRecord.products);
1177
1219
  if (!this.isPendingSyncOrder(preferred) || !this.isPendingSyncOrder(secondary)) {
1178
1220
  merged.need_sync = 0;
1179
1221
  }
@@ -203,6 +203,12 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
203
203
  smallTicketDataFlag?: number;
204
204
  enhancePayload?: SubmitPayloadEnhancer;
205
205
  }): Promise<T>;
206
+ submitTempOrderAsync<T = any>(params?: {
207
+ payments?: OrderPaymentSource[];
208
+ paymentStatus?: BaseSalesPaymentStatus;
209
+ smallTicketDataFlag?: number;
210
+ enhancePayload?: SubmitPayloadEnhancer;
211
+ }): Promise<T>;
206
212
  printLocalOrderReceipt<T = any>(lookup?: BaseSalesPrintLocalOrderReceiptParams): Promise<T>;
207
213
  private getSubmitPaymentStatus;
208
214
  syncPaymentsToOrder<T = any>(params: SyncPaymentsToOrderParams): Promise<SyncPaymentsToOrderResult<T>>;