@pisell/pisellos 2.2.302 → 2.2.303

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 (153) hide show
  1. package/dist/model/strategy/adapter/dataVariant/evaluator.d.ts +18 -2
  2. package/dist/model/strategy/adapter/dataVariant/evaluator.js +287 -101
  3. package/dist/model/strategy/adapter/dataVariant/type.d.ts +25 -0
  4. package/dist/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
  5. package/dist/model/strategy/adapter/itemRule/adapter.js +135 -35
  6. package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
  7. package/dist/model/strategy/adapter/itemRule/evaluator.js +25 -1
  8. package/dist/model/strategy/adapter/itemRule/type.d.ts +44 -0
  9. package/dist/model/strategy/adapter/itemRule/type.js +19 -1
  10. package/dist/model/strategy/adapter/promotion/index.js +0 -9
  11. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +10 -7
  12. package/dist/modules/Discount/index.d.ts +2 -0
  13. package/dist/modules/Discount/index.js +41 -5
  14. package/dist/modules/Discount/types.d.ts +21 -0
  15. package/dist/modules/OpenData/index.d.ts +15 -2
  16. package/dist/modules/OpenData/index.js +307 -19
  17. package/dist/modules/OpenData/types.d.ts +55 -0
  18. package/dist/modules/OpenData/types.js +7 -1
  19. package/dist/modules/OpenData/utils.d.ts +21 -1
  20. package/dist/modules/OpenData/utils.js +138 -0
  21. package/dist/modules/Order/index.d.ts +50 -7
  22. package/dist/modules/Order/index.js +1690 -815
  23. package/dist/modules/Order/salesNotes.d.ts +31 -0
  24. package/dist/modules/Order/salesNotes.js +492 -0
  25. package/dist/modules/Order/types.d.ts +117 -10
  26. package/dist/modules/Order/types.js +6 -1
  27. package/dist/modules/Order/utils/virtualSettlement.d.ts +63 -0
  28. package/dist/modules/Order/utils/virtualSettlement.js +284 -0
  29. package/dist/modules/Order/utils.d.ts +5 -1
  30. package/dist/modules/Order/utils.js +138 -33
  31. package/dist/modules/Payment/walletpass.js +20 -21
  32. package/dist/modules/Product/types.d.ts +23 -0
  33. package/dist/modules/ProductList/index.d.ts +2 -1
  34. package/dist/modules/ProductList/index.js +100 -20
  35. package/dist/modules/ProductList/types.d.ts +10 -0
  36. package/dist/modules/Rules/index.d.ts +5 -0
  37. package/dist/modules/Rules/index.js +30 -14
  38. package/dist/modules/SalesSummary/index.d.ts +1 -0
  39. package/dist/modules/SalesSummary/index.js +4 -3
  40. package/dist/modules/SalesSummary/types.d.ts +1 -0
  41. package/dist/modules/SalesSummary/utils.d.ts +1 -0
  42. package/dist/modules/SalesSummary/utils.js +21 -1
  43. package/dist/plugins/request.d.ts +2 -1
  44. package/dist/plugins/request.js +4 -2
  45. package/dist/server/index.d.ts +21 -0
  46. package/dist/server/index.js +1774 -1285
  47. package/dist/server/modules/order/types.d.ts +50 -0
  48. package/dist/server/modules/order/types.js +2 -0
  49. package/dist/server/utils/small-ticket.d.ts +4 -1
  50. package/dist/server/utils/small-ticket.js +461 -96
  51. package/dist/solution/BaseSales/index.d.ts +24 -3
  52. package/dist/solution/BaseSales/index.js +1484 -873
  53. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +2 -0
  54. package/dist/solution/BaseSales/utils/parseSalesResponse.js +1 -1
  55. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +1 -0
  56. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +72 -9
  57. package/dist/solution/BookingByStep/index.d.ts +1 -1
  58. package/dist/solution/BookingTicket/index.d.ts +22 -5
  59. package/dist/solution/BookingTicket/index.js +558 -443
  60. package/dist/solution/BookingTicket/utils/addProductDecision.js +2 -1
  61. package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
  62. package/dist/solution/RegisterAndLogin/config.d.ts +9 -3
  63. package/dist/solution/RegisterAndLogin/config.js +95 -40
  64. package/dist/solution/RegisterAndLogin/index.js +4 -1
  65. package/dist/solution/Sales/index.d.ts +16 -1
  66. package/dist/solution/Sales/index.js +338 -63
  67. package/dist/solution/Sales/types.d.ts +10 -0
  68. package/dist/solution/ScanOrder/index.d.ts +1 -0
  69. package/dist/solution/ScanOrder/index.js +31 -27
  70. package/dist/solution/ScanOrder/utils.d.ts +1 -0
  71. package/dist/solution/ScanOrder/utils.js +2 -0
  72. package/dist/solution/UnifiedBookingSales/index.d.ts +20 -6
  73. package/dist/solution/UnifiedBookingSales/index.js +874 -527
  74. package/dist/solution/UnifiedBookingSales/types.d.ts +21 -2
  75. package/dist/solution/VenueBooking/index.d.ts +1 -0
  76. package/dist/solution/VenueBooking/index.js +8 -4
  77. package/lib/model/strategy/adapter/dataVariant/evaluator.d.ts +18 -2
  78. package/lib/model/strategy/adapter/dataVariant/evaluator.js +137 -6
  79. package/lib/model/strategy/adapter/dataVariant/type.d.ts +25 -0
  80. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
  81. package/lib/model/strategy/adapter/itemRule/adapter.js +73 -4
  82. package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
  83. package/lib/model/strategy/adapter/itemRule/evaluator.js +23 -1
  84. package/lib/model/strategy/adapter/itemRule/type.d.ts +44 -0
  85. package/lib/model/strategy/adapter/itemRule/type.js +19 -1
  86. package/lib/model/strategy/adapter/promotion/index.js +1 -46
  87. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +4 -0
  88. package/lib/modules/Discount/index.d.ts +2 -0
  89. package/lib/modules/Discount/index.js +34 -2
  90. package/lib/modules/Discount/types.d.ts +21 -0
  91. package/lib/modules/OpenData/index.d.ts +15 -2
  92. package/lib/modules/OpenData/index.js +230 -3
  93. package/lib/modules/OpenData/types.d.ts +55 -0
  94. package/lib/modules/OpenData/types.js +9 -1
  95. package/lib/modules/OpenData/utils.d.ts +21 -1
  96. package/lib/modules/OpenData/utils.js +126 -0
  97. package/lib/modules/Order/index.d.ts +50 -7
  98. package/lib/modules/Order/index.js +704 -64
  99. package/lib/modules/Order/salesNotes.d.ts +31 -0
  100. package/lib/modules/Order/salesNotes.js +382 -0
  101. package/lib/modules/Order/types.d.ts +117 -10
  102. package/lib/modules/Order/types.js +5 -1
  103. package/lib/modules/Order/utils/virtualSettlement.d.ts +63 -0
  104. package/lib/modules/Order/utils/virtualSettlement.js +262 -0
  105. package/lib/modules/Order/utils.d.ts +5 -1
  106. package/lib/modules/Order/utils.js +127 -21
  107. package/lib/modules/Payment/walletpass.js +14 -11
  108. package/lib/modules/Product/types.d.ts +23 -0
  109. package/lib/modules/ProductList/index.d.ts +2 -1
  110. package/lib/modules/ProductList/index.js +47 -2
  111. package/lib/modules/ProductList/types.d.ts +10 -0
  112. package/lib/modules/Rules/index.d.ts +5 -0
  113. package/lib/modules/Rules/index.js +22 -12
  114. package/lib/modules/SalesSummary/index.d.ts +1 -0
  115. package/lib/modules/SalesSummary/index.js +4 -2
  116. package/lib/modules/SalesSummary/types.d.ts +1 -0
  117. package/lib/modules/SalesSummary/utils.d.ts +1 -0
  118. package/lib/modules/SalesSummary/utils.js +17 -1
  119. package/lib/plugins/request.d.ts +2 -1
  120. package/lib/plugins/request.js +3 -2
  121. package/lib/server/index.d.ts +21 -0
  122. package/lib/server/index.js +492 -108
  123. package/lib/server/modules/order/types.d.ts +50 -0
  124. package/lib/server/modules/order/types.js +1 -0
  125. package/lib/server/utils/small-ticket.d.ts +4 -1
  126. package/lib/server/utils/small-ticket.js +427 -72
  127. package/lib/solution/BaseSales/index.d.ts +24 -3
  128. package/lib/solution/BaseSales/index.js +433 -30
  129. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +2 -0
  130. package/lib/solution/BaseSales/utils/parseSalesResponse.js +1 -1
  131. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +1 -0
  132. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +38 -0
  133. package/lib/solution/BookingByStep/index.d.ts +1 -1
  134. package/lib/solution/BookingTicket/index.d.ts +22 -5
  135. package/lib/solution/BookingTicket/index.js +60 -15
  136. package/lib/solution/BookingTicket/utils/addProductDecision.js +1 -0
  137. package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
  138. package/lib/solution/RegisterAndLogin/config.d.ts +9 -3
  139. package/lib/solution/RegisterAndLogin/config.js +49 -8
  140. package/lib/solution/RegisterAndLogin/index.js +4 -1
  141. package/lib/solution/Sales/index.d.ts +16 -1
  142. package/lib/solution/Sales/index.js +168 -1
  143. package/lib/solution/Sales/types.d.ts +10 -0
  144. package/lib/solution/ScanOrder/index.d.ts +1 -0
  145. package/lib/solution/ScanOrder/index.js +5 -1
  146. package/lib/solution/ScanOrder/utils.d.ts +1 -0
  147. package/lib/solution/ScanOrder/utils.js +1 -0
  148. package/lib/solution/UnifiedBookingSales/index.d.ts +20 -6
  149. package/lib/solution/UnifiedBookingSales/index.js +169 -16
  150. package/lib/solution/UnifiedBookingSales/types.d.ts +21 -2
  151. package/lib/solution/VenueBooking/index.d.ts +1 -0
  152. package/lib/solution/VenueBooking/index.js +5 -1
  153. package/package.json +1 -1
@@ -14,6 +14,7 @@
14
14
  * - 不在此处发明等价关系;UI 侧(ticketBooking)若需要别的字段,由 view-model 适配层做转换
15
15
  */
16
16
  import type { BaseSalesOrderProduct } from '../types';
17
+ import type { OrderMetaList } from '../../../modules/Order/types';
17
18
  export interface ISalesDetailHeader {
18
19
  order_id: number | string | null;
19
20
  order_number?: string;
@@ -47,6 +48,7 @@ export interface ISalesDetailHeader {
47
48
  schedule_date?: string;
48
49
  created_at?: string;
49
50
  updated_at?: string | number;
51
+ order_meta_list?: OrderMetaList;
50
52
  metadata?: Record<string, any> | null;
51
53
  total_amount?: string | number;
52
54
  [key: string]: any;
@@ -22,7 +22,7 @@ var _orderCollectionIdentity = require("../../../modules/Order/utils/orderCollec
22
22
  */
23
23
 
24
24
  /** OrderData 中除大数组外的"顶层 header 字段"白名单,按需扩展。 */
25
- const HEADER_KEYS = ['order_id', 'order_number', 'shop_order_number', 'shop_full_order_number', 'type', 'business_code', 'platform', 'sales_channel', 'order_sales_channel', 'vouchers', 'status', 'payment_status', 'shipping_status', 'customer_id', 'customer_name', 'country_calling_code', 'phone', 'email', 'is_price_include_tax', 'tax_title', 'tax_country_code', 'currency_code', 'currency_symbol', 'currency_format', 'is_deposit', 'deposit_amount', 'shop_discount', 'surcharge_fee', 'note', 'schedule_date', 'created_at', 'updated_at', 'metadata', 'total_amount', 'shop_id', 'create_date', 'holder', 'relation_forms', 'contacts', 'contacts_info'];
25
+ const HEADER_KEYS = ['order_id', 'order_number', 'shop_order_number', 'shop_full_order_number', 'type', 'business_code', 'platform', 'sales_channel', 'order_sales_channel', 'vouchers', 'status', 'payment_status', 'shipping_status', 'customer_id', 'customer_name', 'country_calling_code', 'phone', 'email', 'is_price_include_tax', 'tax_title', 'tax_country_code', 'currency_code', 'currency_symbol', 'currency_format', 'is_deposit', 'deposit_amount', 'shop_discount', 'surcharge_fee', 'note', 'schedule_date', 'created_at', 'updated_at', 'order_meta_list', 'metadata', 'total_amount', 'shop_id', 'create_date', 'holder', 'relation_forms', 'contacts', 'contacts_info'];
26
26
  function pickHeader(raw) {
27
27
  const header = {
28
28
  order_id: raw?.order_id ?? null
@@ -46,6 +46,7 @@ export interface BaseProductDetailPayload {
46
46
  original_price?: number | string;
47
47
  tax_fee?: number | string;
48
48
  is_charge_tax?: number;
49
+ metadata?: Record<string, any>;
49
50
  note?: string;
50
51
  [key: string]: any;
51
52
  }
@@ -37,6 +37,34 @@ function isBlankValue(value) {
37
37
  function firstNonBlank(...values) {
38
38
  return values.find(value => !isBlankValue(value));
39
39
  }
40
+
41
+ /**
42
+ * 取第一份接口权威 Data Variant 命中结果并复制数组。
43
+ * 空数组表示规则已计算但无命中,必须与字段缺失区分。
44
+ */
45
+ function cloneFirstDataVariantIds(...values) {
46
+ const ids = values.find(value => Array.isArray(value));
47
+ return Array.isArray(ids) ? [...ids] : undefined;
48
+ }
49
+
50
+ /**
51
+ * 在接口权威命中结果后追加本地应用及最终选中的 Data Variant ID。
52
+ * 以字符串身份去重,同时将新增 ID 归一为正整数。
53
+ */
54
+ function appendDataVariantIds(base, ...values) {
55
+ const result = Array.isArray(base) ? [...base] : [];
56
+ const seen = new Set(result.map(id => String(id)));
57
+ for (const value of values) {
58
+ const candidates = Array.isArray(value) ? value : [value];
59
+ for (const candidate of candidates) {
60
+ const id = Number(candidate);
61
+ if (!Number.isFinite(id) || id <= 0 || seen.has(String(id))) continue;
62
+ result.push(id);
63
+ seen.add(String(id));
64
+ }
65
+ }
66
+ return Array.isArray(base) || result.length ? result : undefined;
67
+ }
40
68
  function normalizeOptionItems(optionItems) {
41
69
  if (!Array.isArray(optionItems)) return [];
42
70
  return optionItems.map(optionItem => {
@@ -111,6 +139,10 @@ function transformBaseProductToOrderProduct(params) {
111
139
  ...(sourceProduct || {}),
112
140
  ...(callbackOrigin || {})
113
141
  };
142
+ const authoritativeDataVariantIds = cloneFirstDataVariantIds(payload.metadata?.data_variant_ids, sourceProduct?.metadata?.data_variant_ids, callbackOrigin?.metadata?.data_variant_ids, origin?.metadata?.data_variant_ids);
143
+ const selectedSettlementSnapshot = firstNonBlank(payload.selected_settlement_snapshot, payload.metadata?.selected_settlement_snapshot, payload._extend?.metadata?.selected_settlement_snapshot, sourceProduct?.metadata?.selected_settlement_snapshot, sourceProduct?._extend?.metadata?.selected_settlement_snapshot, callbackOrigin?.metadata?.selected_settlement_snapshot, callbackOrigin?._extend?.metadata?.selected_settlement_snapshot);
144
+ const locallyAppliedVariantIds = authoritativeDataVariantIds === undefined ? cloneFirstDataVariantIds(payload._dataVariant?.appliedVariantIds, sourceProduct?._dataVariant?.appliedVariantIds, callbackOrigin?._dataVariant?.appliedVariantIds, origin?._dataVariant?.appliedVariantIds) : undefined;
145
+ const dataVariantIds = appendDataVariantIds(authoritativeDataVariantIds, locallyAppliedVariantIds, selectedSettlementSnapshot?.data_variant_id);
114
146
  const matchedVariant = findVariantById(callbackOrigin, productVariantId) ?? findVariantById(sourceProduct, productVariantId) ?? findVariantById(origin, productVariantId);
115
147
  const productionCode = normalizeProductionCode(Object.prototype.hasOwnProperty.call(payload, 'production_code') ? payload.production_code : productVariantId > 0 ? matchedVariant?.production_code : origin?.production_code ?? sourceProduct?.production_code);
116
148
  const hasPriceOverride = payload.price_override !== undefined && payload.price_override !== null && payload.price_override !== '';
@@ -179,6 +211,12 @@ function transformBaseProductToOrderProduct(params) {
179
211
  main_product_original_price: mainProductOriginalPrice,
180
212
  price_schema_version: 2,
181
213
  holder_config: sourceProduct?.holder_config,
214
+ ...(dataVariantIds !== undefined ? {
215
+ data_variant_ids: dataVariantIds
216
+ } : {}),
217
+ ...(selectedSettlementSnapshot ? {
218
+ selected_settlement_snapshot: selectedSettlementSnapshot
219
+ } : {}),
182
220
  ...(hasPriceOverride ? {
183
221
  price_override: String(payload.price_override),
184
222
  is_manual_discount: 1
@@ -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 | 5 | 3 | 1 | 2 | 4 | 6;
330
330
  }[]>;
331
331
  submitTimeSlot(timeSlots: TimeSliceItem): void;
332
332
  private getScheduleDataByIds;
@@ -1,7 +1,7 @@
1
1
  import { Module, ModuleOptions, PisellCore } from '../../types';
2
2
  import { BookingTicketCartView, BookingTicketProductCatalogView, BookingTicketState, IGetCustomerListParams, ICustomer, ILoadProductsParams, ILoadProductDetailParams, IScanResult } from './types';
3
3
  import type { ProductData } from '../../modules';
4
- import type { OpenDataConfig } from '../../modules/OpenData';
4
+ import { type InvoiceLayoutConfig, type InvoiceTemplateSettings, type OpenDataConfig } from '../../modules/OpenData';
5
5
  import { type BookingContextConfig, type BookingContextDateInput, type BookingContextResource, type BookingContextResourceMap, type BookingContextState, type InitBookingContextParams, type LoadBookingConfigParams, type LoadBookingResourcesParams, type ResourceError, type BookingCalcContext } from '../../modules/BookingContext';
6
6
  import type { OrderCustomerView, OrderCustomerChangePayload } from '../../modules/Order/types';
7
7
  import { BaseSalesImpl } from '../BaseSales';
@@ -30,7 +30,9 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
30
30
  private orderCustomerDiscountRefreshInFlight;
31
31
  private orderCustomerDiscountRefreshCustomerId;
32
32
  private orderCustomerPromotionRefreshInFlight;
33
- private loadOpenDataConfigInFlight;
33
+ protected loadOpenDataInFlight: Promise<OpenDataConfig> | null;
34
+ protected loadOpenDataInFlightTarget: string | null;
35
+ protected openDataTarget: string | null;
34
36
  /**
35
37
  * 在 BaseSales 默认模块清单(products/order/salesSummary/schedule)基础上追加 customer + bookingContext。
36
38
  * bookingContext 是 SalesSdk 加车下沉所需的「预约 UI 上下文」(bookingConfig / resourcesOrigin / date)。
@@ -43,10 +45,13 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
43
45
  */
44
46
  protected createSubModule(moduleName: string): Module | null;
45
47
  initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
46
- private getBookingTicketBusinessCode;
48
+ protected getBookingTicketBusinessCode(): string | null;
47
49
  private getIdGeneratorPlugin;
48
50
  private loadOpenDataConfig;
49
51
  getOpenData(): Promise<OpenDataConfig | null>;
52
+ getInvoiceLayoutConfig(): Promise<InvoiceLayoutConfig>;
53
+ getInvoiceTemplateSettings(): Promise<InvoiceTemplateSettings>;
54
+ protected isInvoiceOpenDataCacheCurrent(): boolean;
50
55
  /** 清除店铺级 OpenData;下一次 getOpenData 时会重新拉取。 */
51
56
  clearOpenDataCache(): void;
52
57
  private getShortNumberOrDeviceId;
@@ -334,7 +339,7 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
334
339
  * 获取当前的客户搜索条件
335
340
  * @returns 当前搜索条件
336
341
  */
337
- getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
342
+ getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "num" | "skip">;
338
343
  /**
339
344
  * 获取客户列表状态(包含滚动加载相关状态)
340
345
  * @returns 客户状态
@@ -453,7 +458,19 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
453
458
  * 加车两阶段主决策(规格弹窗 / 资源编辑 / 直接加车)。
454
459
  * 与 ticketBooking `handleSelectProduct` + `handleBooking4Service` 等价。
455
460
  */
456
- decideAddProduct(item: any, options?: Partial<AddProductDecideContext>): import("./utils/addProductDecision").AddProductDecision;
461
+ decideAddProduct(item: any, options?: Partial<AddProductDecideContext>): {
462
+ action: "reloadCatalog";
463
+ } | {
464
+ action: "add";
465
+ cacheItem: any;
466
+ } | {
467
+ action: "requiresDetail";
468
+ payload: AddProductRequiresDetailPayload;
469
+ } | {
470
+ action: "requiresBookingEdit";
471
+ cacheItem: any;
472
+ payload: import("./utils/addProductDecision").AddProductRequiresBookingEditPayload;
473
+ };
457
474
  /** 规格弹窗 callback 后的第二段决策。 */
458
475
  decideAfterDetail(cacheItem: any, options?: Partial<AddProductDecideContext>): import("./utils/addProductDecision").AddProductDecision;
459
476
  /**
@@ -21,6 +21,7 @@ Object.keys(_types).forEach(function (key) {
21
21
  }
22
22
  });
23
23
  });
24
+ var _OpenData = require("../../modules/OpenData");
24
25
  var _BookingContext = require("../../modules/BookingContext");
25
26
  var _BaseSales = require("../BaseSales");
26
27
  var _scan = _interopRequireDefault(require("./utils/scan"));
@@ -39,7 +40,7 @@ var _bookingStatus = require("./utils/bookingStatus");
39
40
  var _resolveBestAddTimePlan = require("./utils/resolveBestAddTimePlan");
40
41
  var _utils = require("../../modules/Order/utils");
41
42
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
42
- const OPEN_DATA_SECTION_CODES = ['sale', 'reservation', 'fulfillment', 'menu', 'workflow'];
43
+ const OPEN_DATA_SECTION_CODES = ['sale', 'reservation', 'fulfillment', 'menu', 'workflow', 'documents'];
43
44
  const OPEN_DATA_CACHE_TTL = 5 * 60 * 1000;
44
45
  const USER_PLATFORM = ['pc', 'h5'];
45
46
 
@@ -160,7 +161,9 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
160
161
  orderCustomerDiscountRefreshInFlight = null;
161
162
  orderCustomerDiscountRefreshCustomerId = null;
162
163
  orderCustomerPromotionRefreshInFlight = null;
163
- loadOpenDataConfigInFlight = null;
164
+ loadOpenDataInFlight = null;
165
+ loadOpenDataInFlightTarget = null;
166
+ openDataTarget = null;
164
167
 
165
168
  /**
166
169
  * 在 BaseSales 默认模块清单(products/order/salesSummary/schedule)基础上追加 customer + bookingContext。
@@ -193,10 +196,13 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
193
196
  await this.configureIdGeneratorFromOpenData();
194
197
  }
195
198
  getBookingTicketBusinessCode() {
196
- const businessCode = this.otherParams?.businessCode ?? this.otherParams?.business_code;
197
- if (businessCode === undefined || businessCode === null) return null;
198
- const normalized = String(businessCode).trim();
199
- return normalized || null;
199
+ const candidates = [this.otherParams?.businessCode, this.otherParams?.business_code, this.getOrderHeader()?.business_code, this.store.order?.getTempOrder()?.business_code, this.getSalesOrder()?.raw?.business_code];
200
+ for (const candidate of candidates) {
201
+ if (candidate === undefined || candidate === null) continue;
202
+ const normalized = String(candidate).trim();
203
+ if (normalized) return normalized;
204
+ }
205
+ return null;
200
206
  }
201
207
  getIdGeneratorPlugin() {
202
208
  const appPlugin = this.core.getPlugin('app');
@@ -210,35 +216,72 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
210
216
  console.warn('[BookingTicket] businessCode 缺失,跳过 OpenData 配置加载,当前 module name: ', this.name);
211
217
  return null;
212
218
  }
219
+ const channel = this.getSubmitOrderSalesChannel();
220
+ const target = `${businessCode}+${channel}`;
213
221
  const lastFetchedAt = this.store.openData.getLastFetchedAt();
214
222
  const cachedData = this.store.openData.getOpenData();
215
- if (cachedData && lastFetchedAt && Date.now() - lastFetchedAt < OPEN_DATA_CACHE_TTL) {
223
+ if (cachedData && this.openDataTarget === target && lastFetchedAt && Date.now() - lastFetchedAt < OPEN_DATA_CACHE_TTL) {
224
+ if (typeof this.store.openData.ensureInvoiceTemplateSettings === 'function') {
225
+ await this.store.openData.ensureInvoiceTemplateSettings(target);
226
+ }
216
227
  this.otherParams.openData = cachedData;
217
228
  return cachedData;
218
229
  }
219
- if (this.loadOpenDataConfigInFlight) return await this.loadOpenDataConfigInFlight;
220
- const channel = this.getSubmitOrderSalesChannel();
221
- this.loadOpenDataConfigInFlight = this.store.openData.fetchOpenData({
230
+ if (this.loadOpenDataInFlight) {
231
+ if (this.loadOpenDataInFlightTarget === target) {
232
+ return await this.loadOpenDataInFlight;
233
+ }
234
+ await this.loadOpenDataInFlight;
235
+ return this.loadOpenDataConfig();
236
+ }
237
+ this.loadOpenDataInFlightTarget = target;
238
+ const request = this.store.openData.fetchOpenData({
222
239
  scope: 'board',
223
- target: `${businessCode}+${channel}`,
240
+ target,
224
241
  // target: `dine_in+pos`,
225
242
  section_code: [...OPEN_DATA_SECTION_CODES]
226
243
  });
244
+ this.loadOpenDataInFlight = request;
227
245
  try {
228
- const openDataConfig = await this.loadOpenDataConfigInFlight;
229
- this.otherParams.openData = openDataConfig;
246
+ const openDataConfig = await request;
247
+ // clearOpenDataCache() 或新请求已接管时,旧响应只能返回给原调用方,不能复活缓存归属。
248
+ if (this.loadOpenDataInFlight === request) {
249
+ this.openDataTarget = target;
250
+ this.otherParams.openData = openDataConfig;
251
+ }
230
252
  return openDataConfig;
231
253
  } finally {
232
- this.loadOpenDataConfigInFlight = null;
254
+ if (this.loadOpenDataInFlight === request) {
255
+ this.loadOpenDataInFlight = null;
256
+ this.loadOpenDataInFlightTarget = null;
257
+ }
233
258
  }
234
259
  }
235
260
  async getOpenData() {
236
261
  return this.loadOpenDataConfig();
237
262
  }
263
+ async getInvoiceLayoutConfig() {
264
+ const openData = await this.loadOpenDataConfig();
265
+ if (!openData) return (0, _OpenData.getDefaultInvoiceLayoutConfig)();
266
+ return this.store.openData.getInvoiceLayoutConfig();
267
+ }
268
+ async getInvoiceTemplateSettings() {
269
+ const openData = await this.loadOpenDataConfig();
270
+ if (!openData) return (0, _OpenData.getDefaultInvoiceTemplateSettings)();
271
+ return this.store.openData.getInvoiceTemplateSettings();
272
+ }
273
+ isInvoiceOpenDataCacheCurrent() {
274
+ const businessCode = this.getBookingTicketBusinessCode();
275
+ const channel = this.getSubmitOrderSalesChannel();
276
+ if (!businessCode || !channel) return false;
277
+ return this.openDataTarget === `${businessCode}+${channel}`;
278
+ }
238
279
 
239
280
  /** 清除店铺级 OpenData;下一次 getOpenData 时会重新拉取。 */
240
281
  clearOpenDataCache() {
241
- this.loadOpenDataConfigInFlight = null;
282
+ this.loadOpenDataInFlight = null;
283
+ this.loadOpenDataInFlightTarget = null;
284
+ this.openDataTarget = null;
242
285
  this.otherParams.openData = null;
243
286
  this.store.openData?.clearCache();
244
287
  }
@@ -1743,6 +1786,8 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
1743
1786
  });
1744
1787
  if (params?.businessCode !== undefined || params?.business_code !== undefined) {
1745
1788
  await this.configureIdGeneratorFromOpenData();
1789
+ } else if (params?.channel !== undefined) {
1790
+ await this.loadOpenDataConfig();
1746
1791
  }
1747
1792
  }
1748
1793
  getDeviceTaskPlugin() {
@@ -358,6 +358,7 @@ function buildBasePayloadFromCacheItem(cacheItem, ctx) {
358
358
  bundle_edit: extend?.bundle_edit,
359
359
  discount_reason: extend?.discount_reason,
360
360
  discount_list: cacheItem?.discount_list,
361
+ selected_settlement_snapshot: cacheItem?.metadata?.selected_settlement_snapshot ?? extend?.metadata?.selected_settlement_snapshot,
361
362
  _extend: extend
362
363
  };
363
364
  }
@@ -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;
@@ -15,6 +15,8 @@ exports.getChannelConfig = getChannelConfig;
15
15
  * API 配置接口
16
16
  */
17
17
 
18
+ const h5CustomerAuthApiNames = new Set(['sendEmailVerificationCode', 'sendEmailLoginCode', 'sendSmsRegisterCode', 'sendEmailRegisterLink', 'verifyEmailRegistrationLink', 'emailPasswordLogin', 'emailCodeRegister', 'phoneCodeRegister', 'resendEmailRegisterLink', 'checkEmailLinkCode', 'sendSmsLoginCode', 'phoneCodeLogin', 'guestLogin', 'checkEmailExists', 'checkEmailCode', 'checkMobileCode', 'phonePasswordLogin', 'sendPasswordResetEmail', 'sendPasswordResetSms', 'sendResetPasswordLink', 'checkResetPasswordCode', 'resetPasswordByCode', 'resetPasswordByEmail', 'resetPasswordByPhone', 'verifyCode', 'register', 'login', 'oauthLogin', 'facebookLogin', 'appleLogin']);
19
+
18
20
  /**
19
21
  * 渠道配置接口
20
22
  */
@@ -278,6 +280,31 @@ const onlineStoreConfig = {
278
280
  })
279
281
  }
280
282
  };
283
+ function withH5AuthPrefix(config) {
284
+ const nextConfig = {};
285
+ Object.entries(config).forEach(([key, apiConfig]) => {
286
+ if (!apiConfig.url.startsWith('/auth/')) {
287
+ nextConfig[key] = apiConfig;
288
+ return;
289
+ }
290
+ nextConfig[key] = {
291
+ ...apiConfig,
292
+ url: apiConfig.url.startsWith('/h5/') ? apiConfig.url : `/h5${apiConfig.url}`,
293
+ options: params => {
294
+ const {
295
+ withCredentials,
296
+ ...baseOptions
297
+ } = (typeof apiConfig.options === 'function' ? apiConfig.options(params) : apiConfig.options) || {};
298
+ return {
299
+ ...baseOptions,
300
+ prefix: false
301
+ };
302
+ }
303
+ };
304
+ });
305
+ return nextConfig;
306
+ }
307
+ const h5OnlineStoreConfig = withH5AuthPrefix(onlineStoreConfig);
281
308
 
282
309
  /**
283
310
  * 默认配置(通用配置)
@@ -1066,6 +1093,7 @@ const shopWebposConfig = {
1066
1093
  */
1067
1094
  const channelConfigMap = exports.channelConfigMap = {
1068
1095
  'online-store': onlineStoreConfig,
1096
+ 'online-store-h5': h5OnlineStoreConfig,
1069
1097
  'default': defaultConfig,
1070
1098
  'webpos': webposConfig,
1071
1099
  'shop.webpos': shopWebposConfig
@@ -1083,9 +1111,16 @@ function getChannelConfig(channel) {
1083
1111
  * 负责根据配置执行 API 调用
1084
1112
  */
1085
1113
  class ApiCaller {
1086
- constructor(request, config) {
1114
+ constructor(request, config, options = {}) {
1087
1115
  this.request = request;
1088
1116
  this.config = config;
1117
+ this.options = options;
1118
+ }
1119
+ resolveUrl(url) {
1120
+ if (!this.options.baseUrl || !url.startsWith('/h5/auth/')) {
1121
+ return url;
1122
+ }
1123
+ return `${this.options.baseUrl.replace(/\/$/, '')}${url}`;
1089
1124
  }
1090
1125
 
1091
1126
  /**
@@ -1111,6 +1146,12 @@ class ApiCaller {
1111
1146
  ...options,
1112
1147
  ...additionalOptions
1113
1148
  };
1149
+ if (apiConfig.url.startsWith('/h5/auth/') && h5CustomerAuthApiNames.has(apiName)) {
1150
+ requestOptions.h5CustomerAuth = true;
1151
+ if (this.options.shopDomain) {
1152
+ requestOptions.h5AuthShopDomain = this.options.shopDomain;
1153
+ }
1154
+ }
1114
1155
 
1115
1156
  // 处理动态 headers
1116
1157
  if (requestOptions.headers && typeof requestOptions.headers === 'function') {
@@ -1123,19 +1164,19 @@ class ApiCaller {
1123
1164
  switch (apiConfig.method) {
1124
1165
  case 'GET':
1125
1166
  if (transformedParams && Object.keys(transformedParams).length > 0) {
1126
- response = await this.request.get(apiConfig.url, transformedParams, requestOptions);
1167
+ response = await this.request.get(this.resolveUrl(apiConfig.url), transformedParams, requestOptions);
1127
1168
  } else {
1128
- response = await this.request.get(apiConfig.url, undefined, requestOptions);
1169
+ response = await this.request.get(this.resolveUrl(apiConfig.url), undefined, requestOptions);
1129
1170
  }
1130
1171
  break;
1131
1172
  case 'POST':
1132
- response = await this.request.post(apiConfig.url, transformedParams, requestOptions);
1173
+ response = await this.request.post(this.resolveUrl(apiConfig.url), transformedParams, requestOptions);
1133
1174
  break;
1134
1175
  case 'PUT':
1135
- response = await this.request.put(apiConfig.url, transformedParams, requestOptions);
1176
+ response = await this.request.put(this.resolveUrl(apiConfig.url), transformedParams, requestOptions);
1136
1177
  break;
1137
1178
  case 'DELETE':
1138
- response = await this.request.delete(apiConfig.url, requestOptions);
1179
+ response = await this.request.delete(this.resolveUrl(apiConfig.url), requestOptions);
1139
1180
  break;
1140
1181
  default:
1141
1182
  throw new Error(`不支持的 HTTP 方法: ${apiConfig.method}`);
@@ -1150,7 +1191,7 @@ class ApiCaller {
1150
1191
  * 创建 API 调用器
1151
1192
  */
1152
1193
  exports.ApiCaller = ApiCaller;
1153
- function createApiCaller(request, channel) {
1194
+ function createApiCaller(request, channel, options) {
1154
1195
  const config = getChannelConfig(channel);
1155
- return new ApiCaller(request, config);
1196
+ return new ApiCaller(request, config, options);
1156
1197
  }
@@ -227,7 +227,10 @@ class RegisterAndLoginImpl extends _BaseModule.BaseModule {
227
227
  }
228
228
 
229
229
  // 创建 API 调用器
230
- this.apiCaller = (0, _config.createApiCaller)(this.request, this.channel);
230
+ this.apiCaller = (0, _config.createApiCaller)(this.request, this.channel, {
231
+ baseUrl: this.otherParams.h5AuthBaseUrl,
232
+ shopDomain: this.otherParams.h5AuthShopDomain
233
+ });
231
234
 
232
235
  // 尝试从本地存储恢复登录状态
233
236
  await this.restoreLoginState();
@@ -2,7 +2,7 @@ import dayjs from 'dayjs';
2
2
  import { Module, ModuleOptions, PisellCore } from '../../types';
3
3
  import { BaseModule } from '../../modules/BaseModule';
4
4
  import type { RequestPlugin } from '../../plugins/request';
5
- import { SalesState, SalesModuleAPI, Reservation, SalesResourceBookingItem, SalesResourceBookingItemWithGroups, SalesTimelineHighlights, ChangeBookingStatusParams, GetResourceBookingItemParams } from './types';
5
+ import { SalesState, SalesModuleAPI, Reservation, SalesResourceBookingItem, SalesResourceBookingItemWithGroups, SalesTimelineHighlights, ChangeBookingStatusParams, GetResourceBookingItemParams, SalesPrintLocalOrderReceiptOptions } from './types';
6
6
  import type { BookingData } from '../../server/modules/order/types';
7
7
  export * from './types';
8
8
  /**
@@ -26,6 +26,10 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
26
26
  protected otherParams: Record<string, any>;
27
27
  protected request: RequestPlugin;
28
28
  private logger;
29
+ private invoiceOpenDataModule;
30
+ private invoiceOpenDataTarget;
31
+ private invoiceOpenDataInFlight;
32
+ private invoiceOpenDataInFlightTarget;
29
33
  constructor(name?: string, version?: string);
30
34
  initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
31
35
  /** 获取当前 store 快照 */
@@ -42,6 +46,17 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
42
46
  resetReservationList(): Reservation[];
43
47
  cancelOrder(orderIds: Array<number | string>): Promise<any>;
44
48
  changeBookingStatus(params: ChangeBookingStatusParams): Promise<any>;
49
+ /**
50
+ * 将显式 channel 或当前运行上下文归一为 Invoice OpenData 使用的销售入口。
51
+ * Legacy Sales 的系统上下文没有可打印的 system target,因此统一回退 pos。
52
+ */
53
+ private getInvoiceSalesChannel;
54
+ private loadInvoiceOpenData;
55
+ private logInvoiceOpenDataFallback;
56
+ private resolveInvoiceConfigs;
57
+ printLocalOrderReceipt<T = any>(lookup: string | number, options: SalesPrintLocalOrderReceiptOptions): Promise<T>;
58
+ /** 清除独立 Documents 缓存;下一次打印会按当前 target 重新获取。 */
59
+ clearOpenDataCache(): void;
45
60
  destroy(): Promise<void>;
46
61
  /**
47
62
  * 时间轴粒度(分钟)