@pisell/pisellos 0.10.20 → 0.10.22

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 (88) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +0 -9
  2. package/dist/modules/BookingContext/index.d.ts +3 -0
  3. package/dist/modules/BookingContext/index.js +42 -13
  4. package/dist/modules/BookingContext/types.d.ts +4 -4
  5. package/dist/modules/BookingContext/types.js +3 -3
  6. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +10 -7
  7. package/dist/modules/OpenData/index.d.ts +10 -1
  8. package/dist/modules/OpenData/index.js +268 -14
  9. package/dist/modules/OpenData/types.d.ts +14 -1
  10. package/dist/modules/OpenData/utils.d.ts +1 -0
  11. package/dist/modules/OpenData/utils.js +8 -5
  12. package/dist/modules/Order/index.d.ts +6 -1
  13. package/dist/modules/Order/index.js +148 -84
  14. package/dist/modules/ProductList/clientDataVariants.d.ts +50 -0
  15. package/dist/modules/ProductList/clientDataVariants.js +216 -0
  16. package/dist/modules/ProductList/index.d.ts +14 -13
  17. package/dist/modules/ProductList/index.js +105 -77
  18. package/dist/modules/Quotation/index.d.ts +16 -0
  19. package/dist/modules/Quotation/index.js +240 -27
  20. package/dist/modules/ResourcePlanner/planner.d.ts +2 -2
  21. package/dist/modules/ResourcePlanner/planner.js +88 -21
  22. package/dist/modules/ResourcePlanner/types.d.ts +12 -0
  23. package/dist/modules/Rules/index.d.ts +5 -0
  24. package/dist/modules/Rules/index.js +27 -12
  25. package/dist/plugins/request.d.ts +2 -1
  26. package/dist/plugins/request.js +4 -2
  27. package/dist/server/index.js +33 -16
  28. package/dist/solution/BaseSales/index.d.ts +16 -1
  29. package/dist/solution/BaseSales/index.js +690 -349
  30. package/dist/solution/BookingByStep/index.d.ts +1 -1
  31. package/dist/solution/BookingTicket/index.d.ts +14 -2
  32. package/dist/solution/BookingTicket/index.js +25 -18
  33. package/dist/solution/Sales/index.js +29 -22
  34. package/dist/solution/UnifiedBookingSales/index.d.ts +1 -1
  35. package/dist/solution/UnifiedBookingSales/index.js +315 -99
  36. package/dist/solution/UnifiedBookingSales/types.d.ts +4 -0
  37. package/dist/solution/VenueBooking/index.d.ts +2 -0
  38. package/dist/solution/VenueBooking/index.js +542 -494
  39. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  40. package/dist/solution/VenueBooking/utils/dateSummary.js +3 -1
  41. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +2 -0
  42. package/dist/solution/VenueBooking/utils/slotMerge.js +3 -1
  43. package/dist/utils/platform.d.ts +8 -0
  44. package/dist/utils/platform.js +15 -0
  45. package/lib/modules/BookingContext/index.d.ts +3 -0
  46. package/lib/modules/BookingContext/index.js +35 -8
  47. package/lib/modules/BookingContext/types.d.ts +4 -4
  48. package/lib/modules/BookingContext/types.js +3 -3
  49. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +4 -0
  50. package/lib/modules/OpenData/index.d.ts +10 -1
  51. package/lib/modules/OpenData/index.js +162 -2
  52. package/lib/modules/OpenData/types.d.ts +14 -1
  53. package/lib/modules/OpenData/utils.d.ts +1 -0
  54. package/lib/modules/OpenData/utils.js +9 -5
  55. package/lib/modules/Order/index.d.ts +6 -1
  56. package/lib/modules/Order/index.js +65 -8
  57. package/lib/modules/ProductList/clientDataVariants.d.ts +50 -0
  58. package/lib/modules/ProductList/clientDataVariants.js +204 -0
  59. package/lib/modules/ProductList/index.d.ts +14 -13
  60. package/lib/modules/ProductList/index.js +77 -58
  61. package/lib/modules/Quotation/index.d.ts +16 -0
  62. package/lib/modules/Quotation/index.js +112 -9
  63. package/lib/modules/ResourcePlanner/planner.d.ts +2 -2
  64. package/lib/modules/ResourcePlanner/planner.js +80 -20
  65. package/lib/modules/ResourcePlanner/types.d.ts +12 -0
  66. package/lib/modules/Rules/index.d.ts +5 -0
  67. package/lib/modules/Rules/index.js +20 -11
  68. package/lib/plugins/request.d.ts +2 -1
  69. package/lib/plugins/request.js +3 -2
  70. package/lib/server/index.js +16 -1
  71. package/lib/solution/BaseSales/index.d.ts +16 -1
  72. package/lib/solution/BaseSales/index.js +314 -66
  73. package/lib/solution/BookingByStep/index.d.ts +1 -1
  74. package/lib/solution/BookingTicket/index.d.ts +14 -2
  75. package/lib/solution/BookingTicket/index.js +3 -0
  76. package/lib/solution/Sales/index.js +3 -0
  77. package/lib/solution/UnifiedBookingSales/index.d.ts +1 -1
  78. package/lib/solution/UnifiedBookingSales/index.js +228 -20
  79. package/lib/solution/UnifiedBookingSales/types.d.ts +4 -0
  80. package/lib/solution/VenueBooking/index.d.ts +2 -0
  81. package/lib/solution/VenueBooking/index.js +28 -8
  82. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  83. package/lib/solution/VenueBooking/utils/dateSummary.js +3 -1
  84. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +2 -0
  85. package/lib/solution/VenueBooking/utils/slotMerge.js +3 -1
  86. package/lib/utils/platform.d.ts +8 -0
  87. package/lib/utils/platform.js +22 -0
  88. package/package.json +1 -1
@@ -7,5 +7,6 @@ export declare function buildDateRangeSummary(params: {
7
7
  rawResources: VenueResourceRawData[];
8
8
  resourceProductMap: Map<number | string, ResourceProductMapping[]>;
9
9
  quotationModule?: QuotationModule;
10
+ channel?: string;
10
11
  resolveConfig?: (date: string) => VenueBookingSlotConfig;
11
12
  }): VenueDateSummaryItem[];
@@ -14,6 +14,7 @@ export function buildDateRangeSummary(params) {
14
14
  rawResources = params.rawResources,
15
15
  resourceProductMap = params.resourceProductMap,
16
16
  quotationModule = params.quotationModule,
17
+ channel = params.channel,
17
18
  resolveConfig = params.resolveConfig;
18
19
  var result = [];
19
20
  var productIds = quotationModule ? _toConsumableArray(new Set(_toConsumableArray(resourceProductMap.values()).flat().map(function (m) {
@@ -32,7 +33,8 @@ export function buildDateRangeSummary(params) {
32
33
  });
33
34
  quotationPriceMap = quotationModule.buildProductPriceMap({
34
35
  productIds: productIds,
35
- timePoints: timePoints
36
+ timePoints: timePoints,
37
+ channel: channel
36
38
  });
37
39
  }
38
40
  var grid = buildTimeSlotGrid({
@@ -5,10 +5,12 @@ export declare function mergeConsecutiveSlots(slots: VenueSlotSelection[]): Merg
5
5
  export declare function buildPriceBreakdown(params: {
6
6
  group: MergedSlotGroup;
7
7
  productId: number;
8
+ channel?: string;
8
9
  quotation?: {
9
10
  getQuotationShelfId: (p: {
10
11
  productId: number;
11
12
  datetime: string;
13
+ channel?: string;
12
14
  }) => number;
13
15
  };
14
16
  }): PriceBreakdownEntry[];
@@ -53,6 +53,7 @@ export function mergeConsecutiveSlots(slots) {
53
53
  export function buildPriceBreakdown(params) {
54
54
  var group = params.group,
55
55
  productId = params.productId,
56
+ channel = params.channel,
56
57
  quotation = params.quotation;
57
58
  if (!group.slots.length) return [];
58
59
  var entries = [];
@@ -64,7 +65,8 @@ export function buildPriceBreakdown(params) {
64
65
  var unitPrice = Number(slot.price || '0');
65
66
  var shelfId = quotation ? quotation.getQuotationShelfId({
66
67
  productId: productId,
67
- datetime: slot.startTime
68
+ datetime: slot.startTime,
69
+ channel: channel
68
70
  }) : 0;
69
71
  var startHm = dayjs(slot.startTime, 'YYYY-MM-DD HH:mm').format('YYYY-MM-DD HH:mm');
70
72
  var endHm = dayjs(slot.endTime, 'YYYY-MM-DD HH:mm').format('YYYY-MM-DD HH:mm');
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Normalize the runtime platform passed through solution/module otherParams.
3
+ */
4
+ export declare function normalizeRuntimePlatform(platform: unknown): string;
5
+ /**
6
+ * PC and H5 are authenticated-customer clients rather than staff terminals.
7
+ */
8
+ export declare function isCustomerUserPlatform(platform: unknown): boolean;
@@ -0,0 +1,15 @@
1
+ var CUSTOMER_USER_PLATFORMS = new Set(['pc', 'h5']);
2
+
3
+ /**
4
+ * Normalize the runtime platform passed through solution/module otherParams.
5
+ */
6
+ export function normalizeRuntimePlatform(platform) {
7
+ return String(platform !== null && platform !== void 0 ? platform : '').trim().toLowerCase();
8
+ }
9
+
10
+ /**
11
+ * PC and H5 are authenticated-customer clients rather than staff terminals.
12
+ */
13
+ export function isCustomerUserPlatform(platform) {
14
+ return CUSTOMER_USER_PLATFORMS.has(normalizeRuntimePlatform(platform));
15
+ }
@@ -19,12 +19,15 @@ export declare class BookingContextModule extends BaseModule implements Module,
19
19
  protected defaultVersion: string;
20
20
  private request;
21
21
  private store;
22
+ private otherParams;
22
23
  /** 上次成功 loadBookingConfig 的 query key;命中则不再请求。 */
23
24
  private lastConfigQueryKey;
24
25
  /** 上次成功 loadResources 的 query key(date|bookingIds);命中则不再请求。 */
25
26
  private lastResourcesQueryKey;
26
27
  constructor(name?: string, version?: string);
27
28
  initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
29
+ /** 同步父级解决方案的运行态参数;platform 变化后会自然切换 config endpoint/cache key。 */
30
+ updateOtherParams(params: Record<string, any>): void;
28
31
  loadBookingConfig(params?: LoadBookingConfigParams, options?: {
29
32
  forceRefresh?: boolean;
30
33
  }): Promise<BookingContextConfig | null>;
@@ -16,6 +16,7 @@ exports.buildBookingResourcesQueryKey = buildBookingResourcesQueryKey;
16
16
  var _dayjs = _interopRequireDefault(require("dayjs"));
17
17
  var _BaseModule = require("../BaseModule");
18
18
  var _request = require("../../plugins/request");
19
+ var _platform = require("../../utils/platform");
19
20
  var _types = require("./types");
20
21
  Object.keys(_types).forEach(function (key) {
21
22
  if (key === "default" || key === "__esModule") return;
@@ -52,6 +53,8 @@ const DEFAULT_BOOKING_CONFIG_PARAMS = {
52
53
  item_type: 'appointment_booking',
53
54
  sub_type: 'ticket'
54
55
  };
56
+ const BOOKING_CONFIG_ENDPOINT = '/core/board/management/config';
57
+ const H5_BOOKING_CONFIG_ENDPOINT = '/core/board/config';
55
58
  const BOARD_CONFIG_CACHE = {
56
59
  useCache: true,
57
60
  cache: {
@@ -167,6 +170,26 @@ function buildBookingConfigQueryKey(params = {}) {
167
170
  });
168
171
  return JSON.stringify(sorted);
169
172
  }
173
+ function resolveBookingConfigRequest(platform, params = {}) {
174
+ const isH5 = (0, _platform.normalizeRuntimePlatform)(platform) === 'h5';
175
+ const endpoint = isH5 ? H5_BOOKING_CONFIG_ENDPOINT : BOOKING_CONFIG_ENDPOINT;
176
+ const query = isH5 ? {
177
+ item_type: params.item_type || DEFAULT_BOOKING_CONFIG_PARAMS.item_type
178
+ } : {
179
+ ...DEFAULT_BOOKING_CONFIG_PARAMS,
180
+ ...params
181
+ };
182
+ const keys = Object.keys(query).sort();
183
+ const sortedQuery = {};
184
+ keys.forEach(key => {
185
+ sortedQuery[key] = query[key];
186
+ });
187
+ return {
188
+ endpoint,
189
+ query,
190
+ key: `${endpoint}:${JSON.stringify(sortedQuery)}`
191
+ };
192
+ }
170
193
 
171
194
  /** 稳定序列化资源列表查询(date + bookingIds),用于跳过重复 loadResources。 */
172
195
  function buildBookingResourcesQueryKey(params = {}) {
@@ -181,6 +204,7 @@ class BookingContextModule extends _BaseModule.BaseModule {
181
204
  store = {
182
205
  ...EMPTY_STATE
183
206
  };
207
+ otherParams = {};
184
208
  /** 上次成功 loadBookingConfig 的 query key;命中则不再请求。 */
185
209
  lastConfigQueryKey = null;
186
210
  /** 上次成功 loadResources 的 query key(date|bookingIds);命中则不再请求。 */
@@ -194,6 +218,7 @@ class BookingContextModule extends _BaseModule.BaseModule {
194
218
  if (!this.request) {
195
219
  throw new Error('BookingContextModule 需要 request 插件支持');
196
220
  }
221
+ this.otherParams = options.otherParams || {};
197
222
  this.store = {
198
223
  bookingConfig: null,
199
224
  resourcesOrigin: [],
@@ -212,15 +237,17 @@ class BookingContextModule extends _BaseModule.BaseModule {
212
237
  this.store.date = initialState.date;
213
238
  }
214
239
  }
240
+
241
+ /** 同步父级解决方案的运行态参数;platform 变化后会自然切换 config endpoint/cache key。 */
242
+ updateOtherParams(params) {
243
+ this.otherParams = params || {};
244
+ }
215
245
  async loadBookingConfig(params = {}, options = {}) {
216
- const configKey = buildBookingConfigQueryKey(params);
246
+ const requestConfig = resolveBookingConfigRequest(this.otherParams?.platform, params);
247
+ const configKey = requestConfig.key;
217
248
  if (!options.forceRefresh && this.store.bookingConfig != null && this.lastConfigQueryKey === configKey) {
218
249
  return this.store.bookingConfig;
219
250
  }
220
- const query = {
221
- ...DEFAULT_BOOKING_CONFIG_PARAMS,
222
- ...params
223
- };
224
251
  const sharedKey = buildSharedCacheKey(this.request, configKey);
225
252
  if (!options.forceRefresh && sharedBookingConfigCache.has(sharedKey)) {
226
253
  const cached = sharedBookingConfigCache.get(sharedKey) ?? null;
@@ -232,7 +259,7 @@ class BookingContextModule extends _BaseModule.BaseModule {
232
259
  if (!pending) {
233
260
  const requestPromise = (async () => {
234
261
  try {
235
- const res = await this.request.get('/core/board/management/config', query, BOARD_CONFIG_CACHE);
262
+ const res = await this.request.get(requestConfig.endpoint, requestConfig.query, BOARD_CONFIG_CACHE);
236
263
  const config = res?.data ?? null;
237
264
  sharedBookingConfigCache.set(sharedKey, config);
238
265
  return config;
@@ -293,7 +320,7 @@ class BookingContextModule extends _BaseModule.BaseModule {
293
320
  * store 内是否已具备与本次 init 入参等价的 config / resources(不再打接口)。
294
321
  */
295
322
  isBookingContextReady(params = {}) {
296
- const configKey = buildBookingConfigQueryKey(params.bookingConfigParams);
323
+ const configKey = resolveBookingConfigRequest(this.otherParams?.platform, params.bookingConfigParams).key;
297
324
  const dateInput = params.date ?? this.store.date ?? (0, _dayjs.default)();
298
325
  const queryDate = toScheduleQueryDate(dateInput);
299
326
  const resourcesKey = buildBookingResourcesQueryKey({
@@ -311,7 +338,7 @@ class BookingContextModule extends _BaseModule.BaseModule {
311
338
  if (!params.forceRefresh && this.isBookingContextReady(params)) {
312
339
  return this.getState();
313
340
  }
314
- const configKey = buildBookingConfigQueryKey(params.bookingConfigParams);
341
+ const configKey = resolveBookingConfigRequest(this.otherParams?.platform, params.bookingConfigParams).key;
315
342
  const resourcesKey = buildBookingResourcesQueryKey({
316
343
  date: queryDate,
317
344
  bookingIds: params.bookingIds
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * BookingContext 子模块:承载「预约 UI 上下文」三件套。
3
3
  *
4
- * - bookingConfig:来自 `/core/board/management/config` 的整套预约配置(service_time / resource_tab /
4
+ * - bookingConfig:来自 staff `/core/board/management/config` 或 H5 `/core/board/config` 的整套预约配置(service_time / resource_tab /
5
5
  * appointment_subject / subject_require / stock_setting / basic / capacity 等)。
6
6
  * - resourcesOrigin / resourcesOriginMap:来自 `/schedule/resource/list` 的资源全集,含 times / event_list /
7
7
  * combined_resource 等运行态信息,是 getResourceByIds / getResourceTimeIsUsable 的输入。
@@ -11,7 +11,7 @@
11
11
  * - 默认由 `loadBookingConfig` / `loadResources` 拉取并写入 store;也支持消费方 `set*` 直接灌入(复用 ticketBooking 已拉到的数据)。
12
12
  * - 所有读 API 返回浅克隆(数组)或原引用(map / config),避免 UI 写穿。
13
13
  */
14
- /** `loadBookingConfig` 入参,与 ticketBooking `getBookingConfig` 一致。 */
14
+ /** `loadBookingConfig` 入参;H5 公共接口只消费 `item_type`。 */
15
15
  export interface LoadBookingConfigParams {
16
16
  item_type?: string;
17
17
  sub_type?: string;
@@ -42,7 +42,7 @@ export interface InitBookingContextParams {
42
42
  export type BookingContextResource = Record<string, any>;
43
43
  export type BookingContextResourceMap = Record<string, BookingContextResource>;
44
44
  /**
45
- * 形参 booking_config(结构来自 `/core/board/management/config` 接口)。
45
+ * 形参 booking_config(结构来自 staff management 或 H5 board config 接口的 `data`)。
46
46
  * OS 不强制 schema,保留 `Record<string, any>` 兼容现网;常用路径文档化在 SDK docs。
47
47
  */
48
48
  export type BookingContextConfig = Record<string, any>;
@@ -80,7 +80,7 @@ export interface BookingContextModuleAPI {
80
80
  getState(): BookingContextState;
81
81
  /** 一键清空,destroy / 切租户场景用。 */
82
82
  clear(): void;
83
- /** 拉取 board config 并写入 store(`/core/board/management/config`)。 */
83
+ /** 按运行平台拉取 board config 并写入 store。 */
84
84
  loadBookingConfig(params?: LoadBookingConfigParams): Promise<BookingContextConfig | null>;
85
85
  /** 拉取资源全集并写入 store(`/schedule/resource/list`)。 */
86
86
  loadResources(params?: LoadBookingResourcesParams): Promise<BookingContextResource[]>;
@@ -7,7 +7,7 @@ exports.BookingContextEvent = void 0;
7
7
  /**
8
8
  * BookingContext 子模块:承载「预约 UI 上下文」三件套。
9
9
  *
10
- * - bookingConfig:来自 `/core/board/management/config` 的整套预约配置(service_time / resource_tab /
10
+ * - bookingConfig:来自 staff `/core/board/management/config` 或 H5 `/core/board/config` 的整套预约配置(service_time / resource_tab /
11
11
  * appointment_subject / subject_require / stock_setting / basic / capacity 等)。
12
12
  * - resourcesOrigin / resourcesOriginMap:来自 `/schedule/resource/list` 的资源全集,含 times / event_list /
13
13
  * combined_resource 等运行态信息,是 getResourceByIds / getResourceTimeIsUsable 的输入。
@@ -17,7 +17,7 @@ exports.BookingContextEvent = void 0;
17
17
  * - 默认由 `loadBookingConfig` / `loadResources` 拉取并写入 store;也支持消费方 `set*` 直接灌入(复用 ticketBooking 已拉到的数据)。
18
18
  * - 所有读 API 返回浅克隆(数组)或原引用(map / config),避免 UI 写穿。
19
19
  */
20
- /** `loadBookingConfig` 入参,与 ticketBooking `getBookingConfig` 一致。 */
20
+ /** `loadBookingConfig` 入参;H5 公共接口只消费 `item_type`。 */
21
21
  /** `loadResources` 入参,与 info2 `getResourceList` 一致。 */
22
22
  /** `initBookingContext` 入参(BookingTicket 聚合入口)。 */
23
23
  /**
@@ -25,7 +25,7 @@ exports.BookingContextEvent = void 0;
25
25
  * 这里只声明 Record 形态;具体字段保留 `any`,与 ticketBooking 现网传入的数据形状兼容。
26
26
  */
27
27
  /**
28
- * 形参 booking_config(结构来自 `/core/board/management/config` 接口)。
28
+ * 形参 booking_config(结构来自 staff management 或 H5 board config 接口的 `data`)。
29
29
  * OS 不强制 schema,保留 `Record<string, any>` 兼容现网;常用路径文档化在 SDK docs。
30
30
  */
31
31
  /**
@@ -181,6 +181,7 @@ function buildCacheItemFromOrderLine(input) {
181
181
  booking,
182
182
  sourceProduct
183
183
  } = input;
184
+ const holderConfig = sourceProduct?.holder_config ?? sourceProduct?.origin?.holder_config ?? product.metadata?.holder_config;
184
185
  const resolvedId = product.product_id ?? sourceProduct?.id;
185
186
  const selling = Number(product.selling_price ?? 0);
186
187
  const original = Number(product.original_price ?? selling);
@@ -249,6 +250,9 @@ function buildCacheItemFromOrderLine(input) {
249
250
  }
250
251
  const base = {
251
252
  ...(sourceProduct || {}),
253
+ ...(holderConfig !== undefined && holderConfig !== null ? {
254
+ holder_config: holderConfig
255
+ } : {}),
252
256
  id: resolvedId,
253
257
  product_id: resolvedId,
254
258
  extension_type: sourceProduct?.extension_type ?? booking?.product?.extension_type ?? sourceProduct?.extension_type,
@@ -3,7 +3,7 @@ import { BaseModule } from '../BaseModule';
3
3
  import type { ScheduleModule } from '../Schedule';
4
4
  import type { OpenDataConfig, OpenDataFetchParams, OpenDataAvailabilityResult, InvoiceLayoutConfig, InvoiceTemplateSettings } from './types';
5
5
  export * from './types';
6
- export { computeAvailability, getDefaultInvoiceLayoutConfig, getDefaultInvoiceTemplateSettings, normalizeInvoiceLayoutConfig, normalizeInvoiceTemplateSettings, normalizeRawInvoiceTemplateSettings, resolveInvoiceReceiptLocales, } from './utils';
6
+ export { computeAvailability, getDefaultInvoiceLayoutConfig, getDefaultInvoiceTemplateSettings, normalizeInvoiceLayoutConfig, normalizeInvoiceLayoutFromTemplateSettings, normalizeInvoiceTemplateSettings, normalizeRawInvoiceTemplateSettings, resolveInvoiceReceiptLocales, } from './utils';
7
7
  export declare class OpenDataModule extends BaseModule implements Module {
8
8
  protected defaultName: string;
9
9
  protected defaultVersion: string;
@@ -16,6 +16,9 @@ export declare class OpenDataModule extends BaseModule implements Module {
16
16
  private fatherModule;
17
17
  private otherParams;
18
18
  private fetchRevision;
19
+ private invoiceTemplateCacheRevision;
20
+ private invoiceTemplateDocumentCache;
21
+ private invoiceTemplateDocumentInFlight;
19
22
  constructor(name?: string, version?: string);
20
23
  initialize(core: PisellCore, options: ModuleOptions): Promise<void>;
21
24
  /**
@@ -29,6 +32,12 @@ export declare class OpenDataModule extends BaseModule implements Module {
29
32
  getOpenData(): OpenDataConfig | null;
30
33
  getInvoiceLayoutConfig(): InvoiceLayoutConfig;
31
34
  getInvoiceTemplateSettings(): InvoiceTemplateSettings;
35
+ ensureInvoiceTemplateSettings(target: string): Promise<InvoiceTemplateSettings>;
36
+ private commitInvoiceTemplateSettings;
37
+ private resolveInvoiceTemplateSettings;
38
+ private buildInvoiceTemplateCacheKey;
39
+ private fetchInvoiceTemplateDocument;
40
+ private logInvoiceTemplateDocumentFallback;
32
41
  getLastFetchedAt(): number | null;
33
42
  /** 清除内存与 sessionStorage 镜像中的 OpenData,供宿主切店时调用。 */
34
43
  clearCache(): void;
@@ -9,6 +9,7 @@ var _exportNames = {
9
9
  getDefaultInvoiceLayoutConfig: true,
10
10
  getDefaultInvoiceTemplateSettings: true,
11
11
  normalizeInvoiceLayoutConfig: true,
12
+ normalizeInvoiceLayoutFromTemplateSettings: true,
12
13
  normalizeInvoiceTemplateSettings: true,
13
14
  normalizeRawInvoiceTemplateSettings: true,
14
15
  resolveInvoiceReceiptLocales: true
@@ -38,6 +39,12 @@ Object.defineProperty(exports, "normalizeInvoiceLayoutConfig", {
38
39
  return _utils.normalizeInvoiceLayoutConfig;
39
40
  }
40
41
  });
42
+ Object.defineProperty(exports, "normalizeInvoiceLayoutFromTemplateSettings", {
43
+ enumerable: true,
44
+ get: function () {
45
+ return _utils.normalizeInvoiceLayoutFromTemplateSettings;
46
+ }
47
+ });
41
48
  Object.defineProperty(exports, "normalizeInvoiceTemplateSettings", {
42
49
  enumerable: true,
43
50
  get: function () {
@@ -70,6 +77,25 @@ Object.keys(_types).forEach(function (key) {
70
77
  }
71
78
  });
72
79
  });
80
+ function isRecord(value) {
81
+ return !!value && typeof value === 'object' && !Array.isArray(value);
82
+ }
83
+ function resolveInvoiceDocumentId(value) {
84
+ if (typeof value === 'number') {
85
+ return Number.isSafeInteger(value) && value > 0 ? String(value) : null;
86
+ }
87
+ if (typeof value !== 'string') return null;
88
+ const normalized = value.trim();
89
+ if (!/^\d+$/.test(normalized)) return null;
90
+ const numericId = Number(normalized);
91
+ return Number.isSafeInteger(numericId) && numericId > 0 ? String(numericId) : null;
92
+ }
93
+ function isLegacyInvoiceTemplate(value) {
94
+ if (typeof value !== 'string' || value.trim().length === 0) return false;
95
+ const normalized = value.trim();
96
+ const numericLike = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i;
97
+ return !numericLike.test(normalized);
98
+ }
73
99
  class OpenDataModule extends _BaseModule.BaseModule {
74
100
  defaultName = 'openData';
75
101
  defaultVersion = '1.0.0';
@@ -82,6 +108,9 @@ class OpenDataModule extends _BaseModule.BaseModule {
82
108
  fatherModule;
83
109
  otherParams = {};
84
110
  fetchRevision = 0;
111
+ invoiceTemplateCacheRevision = 0;
112
+ invoiceTemplateDocumentCache = new Map();
113
+ invoiceTemplateDocumentInFlight = new Map();
85
114
  constructor(name, version) {
86
115
  super(name, version);
87
116
  }
@@ -90,6 +119,12 @@ class OpenDataModule extends _BaseModule.BaseModule {
90
119
  this.store = options.store;
91
120
  this.store.data = options.initialState?.data ?? null;
92
121
  this.store.lastFetchedAt = options.initialState?.lastFetchedAt ?? null;
122
+ // Document 解析结果只在当前模块实例内复用。页面刷新后即使恢复了原始
123
+ // OpenData,也必须重新查询一次 NocoBase,以取得当前模板内容。
124
+ this.store.invoiceTemplateSettings = null;
125
+ this.store.invoiceTemplateTarget = null;
126
+ this.store.invoiceTemplateDocumentId = null;
127
+ this.store.invoiceTemplateCacheKey = null;
93
128
  this.otherParams = options.otherParams || {};
94
129
  if (this.otherParams.cacheId) {
95
130
  this.openCache = this.otherParams.openCache ?? false;
@@ -139,10 +174,12 @@ class OpenDataModule extends _BaseModule.BaseModule {
139
174
  throw new Error(response?.message || '获取 OpenData 配置失败');
140
175
  }
141
176
  const data = response?.data || {};
177
+ const resolvedInvoiceTemplate = await this.resolveInvoiceTemplateSettings(data, params.target);
142
178
  // clearCache() 或后续请求发生后,旧响应不得复活已失效的 target 配置。
143
179
  if (fetchRevision === this.fetchRevision) {
144
180
  this.store.data = data;
145
181
  this.store.lastFetchedAt = Date.now();
182
+ this.commitInvoiceTemplateSettings(params.target, resolvedInvoiceTemplate);
146
183
  }
147
184
  return data;
148
185
  }
@@ -150,10 +187,126 @@ class OpenDataModule extends _BaseModule.BaseModule {
150
187
  return this.store.data;
151
188
  }
152
189
  getInvoiceLayoutConfig() {
153
- return (0, _utils.normalizeInvoiceLayoutConfig)(this.store.data);
190
+ return (0, _utils.normalizeInvoiceLayoutFromTemplateSettings)(this.getInvoiceTemplateSettings());
154
191
  }
155
192
  getInvoiceTemplateSettings() {
156
- return (0, _utils.normalizeInvoiceTemplateSettings)(this.store.data);
193
+ if (isRecord(this.store.invoiceTemplateSettings)) {
194
+ const documentId = resolveInvoiceDocumentId(this.store.data?.['documents.invoice_template']);
195
+ if (documentId) {
196
+ const target = this.store.invoiceTemplateTarget;
197
+ if (!target || this.store.invoiceTemplateCacheKey !== this.buildInvoiceTemplateCacheKey(target, documentId)) {
198
+ return (0, _utils.getDefaultInvoiceTemplateSettings)();
199
+ }
200
+ }
201
+ return (0, _utils.normalizeRawInvoiceTemplateSettings)(this.store.invoiceTemplateSettings);
202
+ }
203
+ if (isLegacyInvoiceTemplate(this.store.data?.['documents.invoice_template'])) {
204
+ return (0, _utils.normalizeInvoiceTemplateSettings)(this.store.data);
205
+ }
206
+ return (0, _utils.getDefaultInvoiceTemplateSettings)();
207
+ }
208
+ async ensureInvoiceTemplateSettings(target) {
209
+ const normalizedTarget = String(target || '').trim();
210
+ const config = this.store.data;
211
+ if (!normalizedTarget || !config) {
212
+ return (0, _utils.getDefaultInvoiceTemplateSettings)();
213
+ }
214
+ const fetchRevision = this.fetchRevision;
215
+ const resolved = await this.resolveInvoiceTemplateSettings(config, normalizedTarget);
216
+ if (fetchRevision !== this.fetchRevision || this.store.data !== config) {
217
+ return (0, _utils.getDefaultInvoiceTemplateSettings)();
218
+ }
219
+ this.commitInvoiceTemplateSettings(normalizedTarget, resolved);
220
+ return (0, _utils.normalizeRawInvoiceTemplateSettings)(resolved.settings);
221
+ }
222
+ commitInvoiceTemplateSettings(target, resolved) {
223
+ this.store.invoiceTemplateSettings = (0, _utils.normalizeRawInvoiceTemplateSettings)(resolved.settings);
224
+ this.store.invoiceTemplateTarget = target;
225
+ this.store.invoiceTemplateDocumentId = resolved.documentId;
226
+ this.store.invoiceTemplateCacheKey = resolved.cacheKey;
227
+ }
228
+ async resolveInvoiceTemplateSettings(config, target) {
229
+ const rawTemplate = config['documents.invoice_template'];
230
+ const documentId = resolveInvoiceDocumentId(rawTemplate);
231
+ if (!documentId) {
232
+ return {
233
+ settings: isLegacyInvoiceTemplate(rawTemplate) ? (0, _utils.normalizeInvoiceTemplateSettings)(config) : (0, _utils.getDefaultInvoiceTemplateSettings)(),
234
+ documentId: null,
235
+ cacheKey: null
236
+ };
237
+ }
238
+ const cacheKey = this.buildInvoiceTemplateCacheKey(target, documentId);
239
+ if (this.store.invoiceTemplateTarget === target && this.store.invoiceTemplateDocumentId === documentId && this.store.invoiceTemplateCacheKey === cacheKey && isRecord(this.store.invoiceTemplateSettings)) {
240
+ return {
241
+ settings: (0, _utils.normalizeRawInvoiceTemplateSettings)(this.store.invoiceTemplateSettings),
242
+ documentId,
243
+ cacheKey
244
+ };
245
+ }
246
+ const cachedSettings = this.invoiceTemplateDocumentCache.get(cacheKey);
247
+ if (cachedSettings) {
248
+ return {
249
+ settings: (0, _utils.normalizeRawInvoiceTemplateSettings)(cachedSettings),
250
+ documentId,
251
+ cacheKey
252
+ };
253
+ }
254
+ let request = this.invoiceTemplateDocumentInFlight.get(cacheKey);
255
+ if (!request) {
256
+ const cacheRevision = this.invoiceTemplateCacheRevision;
257
+ request = this.fetchInvoiceTemplateDocument(documentId).catch(error => {
258
+ this.logInvoiceTemplateDocumentFallback(error, target, documentId);
259
+ return (0, _utils.getDefaultInvoiceTemplateSettings)();
260
+ }).then(settings => {
261
+ const normalized = (0, _utils.normalizeRawInvoiceTemplateSettings)(settings);
262
+ if (cacheRevision === this.invoiceTemplateCacheRevision) {
263
+ this.invoiceTemplateDocumentCache.set(cacheKey, normalized);
264
+ }
265
+ return normalized;
266
+ }).finally(() => {
267
+ if (this.invoiceTemplateDocumentInFlight.get(cacheKey) === request) {
268
+ this.invoiceTemplateDocumentInFlight.delete(cacheKey);
269
+ }
270
+ });
271
+ this.invoiceTemplateDocumentInFlight.set(cacheKey, request);
272
+ }
273
+ return {
274
+ settings: await request,
275
+ documentId,
276
+ cacheKey
277
+ };
278
+ }
279
+ buildInvoiceTemplateCacheKey(target, documentId) {
280
+ const getData = this.appPlugin?.getData?.();
281
+ return JSON.stringify([getData?.('tenant_id') ?? null, getData?.('shop_id') ?? null, target, documentId]);
282
+ }
283
+ async fetchInvoiceTemplateDocument(documentId) {
284
+ const response = await this.request.get(`/document:get?filterByTk=${encodeURIComponent(documentId)}`, {}, {
285
+ isNocobase: true,
286
+ prefix: false,
287
+ headers: {
288
+ 'x-data-source': 'pisell2'
289
+ },
290
+ customToast: () => {}
291
+ });
292
+ const document = response?.data;
293
+ if (!document || resolveInvoiceDocumentId(document.id) !== documentId) {
294
+ throw new Error(`Invoice Document ${documentId} 返回的 ID 不匹配`);
295
+ }
296
+ if (document.deleted_at !== null && document.deleted_at !== undefined) {
297
+ throw new Error(`Invoice Document ${documentId} 已被删除`);
298
+ }
299
+ if (!isRecord(document.data) || !isRecord(document.data.template_settings)) {
300
+ throw new Error(`Invoice Document ${documentId} 缺少 template_settings`);
301
+ }
302
+ return (0, _utils.normalizeRawInvoiceTemplateSettings)(document.data.template_settings);
303
+ }
304
+ logInvoiceTemplateDocumentFallback(error, target, documentId) {
305
+ console.warn('[OpenDataModule] Invoice Document 加载失败,使用 DEFAULT', {
306
+ target,
307
+ documentId,
308
+ message: error instanceof Error ? error.message : String(error)
309
+ });
157
310
  }
158
311
  getLastFetchedAt() {
159
312
  return this.store.lastFetchedAt ?? null;
@@ -162,8 +315,15 @@ class OpenDataModule extends _BaseModule.BaseModule {
162
315
  /** 清除内存与 sessionStorage 镜像中的 OpenData,供宿主切店时调用。 */
163
316
  clearCache() {
164
317
  this.fetchRevision += 1;
318
+ this.invoiceTemplateCacheRevision += 1;
319
+ this.invoiceTemplateDocumentCache.clear();
320
+ this.invoiceTemplateDocumentInFlight.clear();
165
321
  this.store.data = null;
166
322
  this.store.lastFetchedAt = null;
323
+ this.store.invoiceTemplateSettings = null;
324
+ this.store.invoiceTemplateTarget = null;
325
+ this.store.invoiceTemplateDocumentId = null;
326
+ this.store.invoiceTemplateCacheKey = null;
167
327
  }
168
328
  checkAvailability(scheduleModule) {
169
329
  const scheduleList = scheduleModule ? scheduleModule?.getScheduleListByIds(this.store.data?.['availability.operating_hours'] || []) : undefined;
@@ -32,6 +32,15 @@ export interface InvoiceTemplateSettings {
32
32
  modules: InvoiceTemplateModuleConfig[];
33
33
  [key: string]: unknown;
34
34
  }
35
+ export interface InvoiceDocumentResponse {
36
+ data?: {
37
+ id?: number | string;
38
+ deleted_at?: string | null;
39
+ data?: {
40
+ template_settings?: unknown;
41
+ };
42
+ };
43
+ }
35
44
  export type RawInvoiceEditConfig = string | {
36
45
  language?: unknown;
37
46
  modules?: unknown;
@@ -79,7 +88,7 @@ export interface OpenDataConfig {
79
88
  label: string;
80
89
  value: number;
81
90
  }>;
82
- 'documents.invoice_template'?: string;
91
+ 'documents.invoice_template'?: string | number;
83
92
  'documents.invoice_edit'?: RawInvoiceEditConfig;
84
93
  /** @deprecated 仅保留旧调用方的类型兼容,Invoice 读取逻辑不再消费该字段。 */
85
94
  'documents.invoice'?: RawInvoiceLayoutConfig;
@@ -92,6 +101,10 @@ export interface OpenDataConfig {
92
101
  export interface OpenDataState {
93
102
  data: OpenDataConfig | null;
94
103
  lastFetchedAt: number | null;
104
+ invoiceTemplateSettings?: InvoiceTemplateSettings | null;
105
+ invoiceTemplateTarget?: string | null;
106
+ invoiceTemplateDocumentId?: string | null;
107
+ invoiceTemplateCacheKey?: string | null;
95
108
  }
96
109
  export interface OpenDataFetchParams {
97
110
  scope: string;
@@ -14,6 +14,7 @@ export declare function normalizeRawInvoiceTemplateSettings(value: unknown): Inv
14
14
  * 从 OpenData Documents section 的两个扁平字段中构造 Native Invoice 配置。
15
15
  */
16
16
  export declare function normalizeInvoiceTemplateSettings(config: OpenDataConfig | null | undefined): InvoiceTemplateSettings;
17
+ export declare function normalizeInvoiceLayoutFromTemplateSettings(settings: InvoiceTemplateSettings | null | undefined): InvoiceLayoutConfig;
17
18
  /**
18
19
  * 将 invoice_edit.modules 原样投影为 Web Invoice 使用的 module 字段。
19
20
  */
@@ -7,6 +7,7 @@ exports.computeAvailability = computeAvailability;
7
7
  exports.getDefaultInvoiceLayoutConfig = getDefaultInvoiceLayoutConfig;
8
8
  exports.getDefaultInvoiceTemplateSettings = getDefaultInvoiceTemplateSettings;
9
9
  exports.normalizeInvoiceLayoutConfig = normalizeInvoiceLayoutConfig;
10
+ exports.normalizeInvoiceLayoutFromTemplateSettings = normalizeInvoiceLayoutFromTemplateSettings;
10
11
  exports.normalizeInvoiceTemplateSettings = normalizeInvoiceTemplateSettings;
11
12
  exports.normalizeRawInvoiceTemplateSettings = normalizeRawInvoiceTemplateSettings;
12
13
  exports.resolveInvoiceReceiptLocales = resolveInvoiceReceiptLocales;
@@ -118,16 +119,19 @@ function normalizeInvoiceTemplateSettings(config) {
118
119
  template: normalizeInvoiceTemplate(config?.['documents.invoice_template'])
119
120
  });
120
121
  }
122
+ function normalizeInvoiceLayoutFromTemplateSettings(settings) {
123
+ const modules = normalizeRawInvoiceTemplateSettings(settings).modules;
124
+ if (modules.length === 0) return getDefaultInvoiceLayoutConfig();
125
+ return {
126
+ module: modules
127
+ };
128
+ }
121
129
 
122
130
  /**
123
131
  * 将 invoice_edit.modules 原样投影为 Web Invoice 使用的 module 字段。
124
132
  */
125
133
  function normalizeInvoiceLayoutConfig(config) {
126
- const modules = normalizeInvoiceTemplateSettings(config).modules;
127
- if (modules.length === 0) return getDefaultInvoiceLayoutConfig();
128
- return {
129
- module: modules
130
- };
134
+ return normalizeInvoiceLayoutFromTemplateSettings(normalizeInvoiceTemplateSettings(config));
131
135
  }
132
136
  function toBoolean(value) {
133
137
  if (typeof value === 'boolean') return value;
@@ -415,6 +415,11 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
415
415
  * if (pending) await pending;
416
416
  */
417
417
  private appendProductLineToTempOrder;
418
+ /**
419
+ * booking 数量拆行时,若 Holder 数量与拆行数严格一致,则按下标分配。
420
+ * 数量不一致时保持旧数据,避免猜测 Holder 与商品份数的对应关系。
421
+ */
422
+ private createSplitBookingLine;
418
423
  /**
419
424
  * 添加商品行。带 booking 时强制独立行;num>1 时拆成多条 num=1 的 product+booking(对齐 BookingByStep / ticketBooking addService)。
420
425
  *
@@ -528,7 +533,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
528
533
  generateIdempotencyToken(): string;
529
534
  syncPaymentsToOrder<T = any>(params: SyncPaymentsToOrderParams): Promise<SyncPaymentsToOrderResult<T>>;
530
535
  createOrder(params: CommitOrderParams['query']): {
531
- type: "virtual" | "appointment_booking";
536
+ type: "appointment_booking" | "virtual";
532
537
  platform: string;
533
538
  sales_channel: string;
534
539
  order_sales_channel: string;