@pisell/pisellos 0.10.56 → 0.10.57

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 (111) hide show
  1. package/dist/modules/OpenData/index.js +2 -1
  2. package/dist/modules/Order/index.d.ts +2 -1
  3. package/dist/modules/Order/index.js +118 -59
  4. package/dist/modules/Order/utils/weighedProduct.d.ts +12 -0
  5. package/dist/modules/Order/utils/weighedProduct.js +94 -0
  6. package/dist/modules/Order/utils.d.ts +1 -1
  7. package/dist/modules/Order/utils.js +9 -6
  8. package/dist/modules/Payment/index.d.ts +0 -5
  9. package/dist/modules/Payment/index.js +35 -85
  10. package/dist/modules/ProductList/index.js +18 -5
  11. package/dist/modules/ResourcePlanner/planner.js +10 -8
  12. package/dist/modules/Rules/index.js +16 -14
  13. package/dist/modules/Rules/types.d.ts +2 -0
  14. package/dist/server/index.js +122 -69
  15. package/dist/server/modules/floor-plan/index.js +34 -3
  16. package/dist/server/modules/payment/index.d.ts +25 -6
  17. package/dist/server/modules/payment/index.js +660 -133
  18. package/dist/server/test.json +1 -1
  19. package/dist/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
  20. package/dist/shared/receipt/customerInvoiceLanguages.js +60 -0
  21. package/dist/shared/receipt/loadCustomerInvoiceLayout.d.ts +5 -0
  22. package/dist/shared/receipt/loadCustomerInvoiceLayout.js +46 -4
  23. package/dist/shared/receipt/small-ticket.d.ts +11 -0
  24. package/dist/shared/receipt/small-ticket.js +430 -255
  25. package/dist/shared/receipt/templateVisibility.d.ts +23 -0
  26. package/dist/shared/receipt/templateVisibility.js +64 -0
  27. package/dist/solution/BaseSales/index.d.ts +4 -2
  28. package/dist/solution/BaseSales/index.js +1328 -1214
  29. package/dist/solution/BaseSales/types.d.ts +5 -1
  30. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +40 -25
  31. package/dist/solution/BaseSales/utils.js +3 -2
  32. package/dist/solution/BookingByStep/index.d.ts +1 -1
  33. package/dist/solution/BookingTicket/index.d.ts +4 -1
  34. package/dist/solution/BookingTicket/index.js +120 -80
  35. package/dist/solution/OrderInvoicePreview/index.d.ts +10 -0
  36. package/dist/solution/OrderInvoicePreview/index.js +85 -14
  37. package/dist/solution/RegisterAndLogin/config.js +54 -11
  38. package/dist/solution/RegisterAndLogin/index.d.ts +2 -0
  39. package/dist/solution/RegisterAndLogin/index.js +1 -1
  40. package/dist/solution/RegisterAndLogin/utils.d.ts +12 -4
  41. package/dist/solution/RegisterAndLogin/utils.js +36 -16
  42. package/dist/solution/Sales/index.d.ts +2 -0
  43. package/dist/solution/Sales/index.js +15 -1
  44. package/dist/solution/ScanOrder/utils.js +3 -2
  45. package/dist/solution/UnifiedBookingSales/index.js +12 -13
  46. package/dist/solution/VenueBooking/index.d.ts +15 -2
  47. package/dist/solution/VenueBooking/index.js +1062 -785
  48. package/dist/solution/VenueBooking/types.d.ts +2 -1
  49. package/dist/solution/VenueBooking/types.js +1 -0
  50. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  51. package/dist/solution/VenueBooking/utils/dateSummary.js +7 -4
  52. package/dist/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  53. package/dist/solution/VenueBooking/utils/smartPricing.js +273 -0
  54. package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
  55. package/dist/solution/VenueBooking/utils/timeSlot.js +6 -3
  56. package/lib/modules/OpenData/index.js +2 -1
  57. package/lib/modules/Order/index.d.ts +2 -1
  58. package/lib/modules/Order/index.js +71 -11
  59. package/lib/modules/Order/utils/weighedProduct.d.ts +12 -0
  60. package/lib/modules/Order/utils/weighedProduct.js +89 -0
  61. package/lib/modules/Order/utils.d.ts +1 -1
  62. package/lib/modules/Order/utils.js +5 -3
  63. package/lib/modules/Payment/index.d.ts +0 -5
  64. package/lib/modules/Payment/index.js +3 -28
  65. package/lib/modules/ProductList/index.js +14 -1
  66. package/lib/modules/ResourcePlanner/planner.js +10 -8
  67. package/lib/modules/Rules/index.js +16 -14
  68. package/lib/modules/Rules/types.d.ts +2 -0
  69. package/lib/server/index.js +72 -28
  70. package/lib/server/modules/floor-plan/index.js +29 -1
  71. package/lib/server/modules/payment/index.d.ts +25 -6
  72. package/lib/server/modules/payment/index.js +350 -78
  73. package/lib/server/test.json +1 -1
  74. package/lib/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
  75. package/lib/shared/receipt/customerInvoiceLanguages.js +58 -0
  76. package/lib/shared/receipt/loadCustomerInvoiceLayout.d.ts +5 -0
  77. package/lib/shared/receipt/loadCustomerInvoiceLayout.js +42 -4
  78. package/lib/shared/receipt/small-ticket.d.ts +11 -0
  79. package/lib/shared/receipt/small-ticket.js +234 -30
  80. package/lib/shared/receipt/templateVisibility.d.ts +23 -0
  81. package/lib/shared/receipt/templateVisibility.js +63 -0
  82. package/lib/solution/BaseSales/index.d.ts +4 -2
  83. package/lib/solution/BaseSales/index.js +79 -18
  84. package/lib/solution/BaseSales/types.d.ts +5 -1
  85. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +24 -9
  86. package/lib/solution/BaseSales/utils.js +2 -1
  87. package/lib/solution/BookingByStep/index.d.ts +1 -1
  88. package/lib/solution/BookingTicket/index.d.ts +4 -1
  89. package/lib/solution/BookingTicket/index.js +14 -2
  90. package/lib/solution/OrderInvoicePreview/index.d.ts +10 -0
  91. package/lib/solution/OrderInvoicePreview/index.js +61 -7
  92. package/lib/solution/RegisterAndLogin/config.js +57 -11
  93. package/lib/solution/RegisterAndLogin/index.d.ts +2 -0
  94. package/lib/solution/RegisterAndLogin/index.js +9 -1
  95. package/lib/solution/RegisterAndLogin/utils.d.ts +12 -4
  96. package/lib/solution/RegisterAndLogin/utils.js +24 -17
  97. package/lib/solution/Sales/index.d.ts +2 -0
  98. package/lib/solution/Sales/index.js +10 -1
  99. package/lib/solution/ScanOrder/utils.js +2 -1
  100. package/lib/solution/UnifiedBookingSales/index.js +5 -6
  101. package/lib/solution/VenueBooking/index.d.ts +15 -2
  102. package/lib/solution/VenueBooking/index.js +243 -96
  103. package/lib/solution/VenueBooking/types.d.ts +2 -1
  104. package/lib/solution/VenueBooking/types.js +1 -0
  105. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  106. package/lib/solution/VenueBooking/utils/dateSummary.js +7 -4
  107. package/lib/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  108. package/lib/solution/VenueBooking/utils/smartPricing.js +224 -0
  109. package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
  110. package/lib/solution/VenueBooking/utils/timeSlot.js +5 -2
  111. package/package.json +1 -1
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.OrderInvoicePreviewImpl = void 0;
7
7
  var _BaseModule = require("../../modules/BaseModule");
8
8
  var _loadCustomerInvoiceLayout = require("../../shared/receipt/loadCustomerInvoiceLayout");
9
+ var _customerInvoiceLanguages = require("../../shared/receipt/customerInvoiceLanguages");
9
10
  var _smallTicket = require("../../shared/receipt/small-ticket");
10
11
  var _loadSalesOrderSnapshot = require("../../shared/sales/loadSalesOrderSnapshot");
11
12
  var _loadPaymentMethods = require("../../shared/payment/loadPaymentMethods");
@@ -25,6 +26,8 @@ class OrderInvoicePreviewImpl extends _BaseModule.BaseModule {
25
26
  disposed = false;
26
27
  input;
27
28
  activeKey = '';
29
+ // Hosts retain this readonly instance while the same order modal is closed.
30
+ languageSelection;
28
31
  pending;
29
32
  snapshotCache = new Map();
30
33
  constructor(name = 'orderInvoicePreview') {
@@ -56,7 +59,7 @@ class OrderInvoicePreviewImpl extends _BaseModule.BaseModule {
56
59
  cache = new Map();
57
60
  contextCaches.set(this.request, cache);
58
61
  }
59
- const key = JSON.stringify([input.tenantId, input.shopId, input.identity, input.locale]);
62
+ const key = JSON.stringify([input.tenantId, input.shopId, input.identity]);
60
63
  const cached = cache.get(key);
61
64
  if (cached) return cached;
62
65
  const task = Promise.all([this.request.get('/shop/show', {}, {
@@ -115,6 +118,7 @@ class OrderInvoicePreviewImpl extends _BaseModule.BaseModule {
115
118
  if (this.state.status === 'ready' && this.state.result) return Promise.resolve(this.state.result);
116
119
  }
117
120
  if (this.input && this.getScope(this.input) !== this.getScope(input)) this.snapshotCache.clear();
121
+ if (this.languageSelection?.scope !== this.getSelectionScope(input)) this.languageSelection = undefined;
118
122
  this.input = input;
119
123
  this.activeKey = key;
120
124
  const generation = ++this.generation;
@@ -124,7 +128,7 @@ class OrderInvoicePreviewImpl extends _BaseModule.BaseModule {
124
128
  error: null
125
129
  });
126
130
  const task = (async () => {
127
- const [snapshot, context, layout] = await Promise.all([this.loadSource(input), this.loadContext(input), (0, _loadCustomerInvoiceLayout.loadCustomerInvoiceLayout)(this.request, input)]);
131
+ const [snapshot, context, template] = await Promise.all([this.loadSource(input), this.loadContext(input), (0, _loadCustomerInvoiceLayout.loadCustomerInvoiceTemplate)(this.request, input)]);
128
132
  const source = (0, _loadPaymentMethods.withPaymentMethodNames)(snapshot, context.methods);
129
133
  const getData = this.core.getPlugin('app')?.getData?.();
130
134
  if (getData?.('shop_id') != null && String(getData('shop_id')) !== String(input.shopId)) throw new Error('Invoice shop changed');
@@ -139,21 +143,30 @@ class OrderInvoicePreviewImpl extends _BaseModule.BaseModule {
139
143
  } : {})
140
144
  };
141
145
  if (generation !== this.generation || this.disposed) throw new Error('Invoice request superseded');
142
- const requestedLocale = input.locale.toLowerCase().replace(/_/g, '-');
143
- const locale = requestedLocale.startsWith('zh-tw') || requestedLocale.startsWith('zh-hk') ? 'zh_HK' : requestedLocale.startsWith('zh') ? 'zh_CN' : requestedLocale.startsWith('ja') ? 'ja' : requestedLocale.startsWith('pt') ? 'pt' : 'en';
146
+ const languageOptions = (0, _customerInvoiceLanguages.resolveCustomerInvoiceLanguages)(template.language, input.locale);
147
+ const remembered = this.languageSelection?.code;
148
+ const selected = languageOptions.find(option => option.code === remembered) || languageOptions[0];
149
+ const locale = selected.locales[0];
144
150
  const result = {
145
- identity: key,
151
+ identity: this.documentIdentity(key, selected.code),
152
+ languageOptions,
153
+ selectedLanguage: selected.code,
154
+ ...(selected.locales[1] ? {
155
+ secondaryLocale: selected.locales[1]
156
+ } : {}),
146
157
  orderId: input.orderId,
147
158
  sourceRevision: input.sourceRevision,
148
159
  generatedAt: new Date().toISOString(),
149
160
  locale,
150
161
  source,
151
162
  shop,
152
- layout,
163
+ layout: template.layout,
153
164
  smallTicketData: (0, _smallTicket.buildSmallTicketData)({
154
165
  order: source,
155
166
  shopInfo: shop,
156
- locales: [locale]
167
+ locales: [...new Set(languageOptions.flatMap(option => option.locales))],
168
+ includeReceiptKeys: true,
169
+ templateSettings: template.layout
157
170
  })
158
171
  };
159
172
  this.publish({
@@ -177,6 +190,47 @@ class OrderInvoicePreviewImpl extends _BaseModule.BaseModule {
177
190
  this.pending = task;
178
191
  return task;
179
192
  }
193
+ getSelectionScope(input) {
194
+ return JSON.stringify([this.getScope(input), input.orderId]);
195
+ }
196
+ documentIdentity(key, language) {
197
+ return JSON.stringify([key, language]);
198
+ }
199
+
200
+ /** Switch only the ready receipt projection; no fetching or rebuilding data. */
201
+ selectLanguage(code) {
202
+ if (this.disposed) throw new Error('Invoice preview disposed');
203
+ const current = this.state.result;
204
+ if (this.state.status !== 'ready' || !current || !this.input) throw new Error('Invoice preview is not ready');
205
+ const currentShop = this.core.getPlugin('app')?.getData?.()?.('shop_id');
206
+ if (currentShop != null && String(currentShop) !== String(this.input.shopId)) throw new Error('Invoice shop changed');
207
+ const selected = current.languageOptions.find(option => option.code === code);
208
+ if (!selected) throw new Error('Invoice language is unavailable');
209
+ this.languageSelection = {
210
+ scope: this.getSelectionScope(this.input),
211
+ code: selected.code
212
+ };
213
+ if (selected.code === current.selectedLanguage) return current;
214
+ const {
215
+ secondaryLocale: _previousSecondary,
216
+ ...rest
217
+ } = current;
218
+ const result = {
219
+ ...rest,
220
+ identity: this.documentIdentity(this.activeKey, selected.code),
221
+ selectedLanguage: selected.code,
222
+ locale: selected.locales[0],
223
+ ...(selected.locales[1] ? {
224
+ secondaryLocale: selected.locales[1]
225
+ } : {})
226
+ };
227
+ this.publish({
228
+ status: 'ready',
229
+ result,
230
+ error: null
231
+ });
232
+ return result;
233
+ }
180
234
  retry() {
181
235
  if (!this.input) return Promise.reject(new Error('Invoice input is missing'));
182
236
  return this.prepare(this.input);
@@ -305,6 +305,30 @@ function withH5AuthPrefix(config) {
305
305
  return nextConfig;
306
306
  }
307
307
  const h5OnlineStoreConfig = withH5AuthPrefix(onlineStoreConfig);
308
+ function withKioskSalesChannel(config) {
309
+ return {
310
+ ...config,
311
+ options: params => {
312
+ const options = (typeof config.options === 'function' ? config.options(params) : config.options) || {};
313
+ const headers = typeof options.headers === 'function' ? options.headers(params) : options.headers;
314
+ return {
315
+ ...options,
316
+ headers: {
317
+ ...headers,
318
+ 'Sales-Channel': 'kiosk'
319
+ }
320
+ };
321
+ }
322
+ };
323
+ }
324
+
325
+ // Kiosk 复用网店顾客认证接口,通过注册(含验证码登录自动注册)请求头标识会员归属渠道。
326
+ const kioskConfig = {
327
+ ...h5OnlineStoreConfig,
328
+ emailCodeRegister: withKioskSalesChannel(h5OnlineStoreConfig.emailCodeRegister),
329
+ phoneCodeRegister: withKioskSalesChannel(h5OnlineStoreConfig.phoneCodeRegister),
330
+ phoneCodeLogin: withKioskSalesChannel(h5OnlineStoreConfig.phoneCodeLogin)
331
+ };
308
332
 
309
333
  /**
310
334
  * 默认配置(通用配置)
@@ -564,6 +588,16 @@ const defaultConfig = {
564
588
  /**
565
589
  * webpos 渠道配置
566
590
  */
591
+ const webposPasswordResetOptions = {
592
+ prefix: false,
593
+ isDefaultSaasDomain: true,
594
+ cache: false,
595
+ noToast: true,
596
+ headers: {
597
+ Accept: 'application/json',
598
+ 'Content-Type': 'application/json'
599
+ }
600
+ };
567
601
  const webposConfig = {
568
602
  sendEmailVerificationCode: {
569
603
  url: '/auth/email/register-code',
@@ -686,13 +720,20 @@ const webposConfig = {
686
720
  })
687
721
  },
688
722
  checkEmailCode: {
689
- url: '/auth/email/check-email-code',
690
- method: 'GET',
723
+ url: '/tenant/account/reset-password/email-code/verify',
724
+ method: 'POST',
691
725
  transformParams: params => ({
692
726
  email: params.email,
693
- code: params.code,
694
- action: params.action
695
- })
727
+ code: params.code
728
+ }),
729
+ // SaaS 成功时 data 为 null,统一为既有验证码校验返回结构。
730
+ transformResponse: response => response?.status === true ? {
731
+ ...response,
732
+ data: {
733
+ valid: true
734
+ }
735
+ } : response,
736
+ options: webposPasswordResetOptions
696
737
  },
697
738
  checkMobileCode: {
698
739
  url: '/auth/mobile/check-mobile-code',
@@ -714,11 +755,12 @@ const webposConfig = {
714
755
  })
715
756
  },
716
757
  sendPasswordResetEmail: {
717
- url: '/auth/email/reset-password-code',
718
- method: 'GET',
758
+ url: '/tenant/account/reset-password/email-code/send',
759
+ method: 'POST',
719
760
  transformParams: params => ({
720
761
  email: params.email
721
- })
762
+ }),
763
+ options: webposPasswordResetOptions
722
764
  },
723
765
  sendPasswordResetSms: {
724
766
  url: '/auth/mobile/sms-forget',
@@ -751,13 +793,16 @@ const webposConfig = {
751
793
  })
752
794
  },
753
795
  resetPasswordByEmail: {
754
- url: '/auth/email/reset-password',
796
+ url: '/tenant/account/reset-password/email-code',
755
797
  method: 'POST',
756
798
  transformParams: params => ({
757
799
  email: params.email,
758
800
  password: params.password,
759
- code: params.code
760
- })
801
+ code: params.code,
802
+ // 页面先校验两次输入完全一致,SDK 保持原有重置参数。
803
+ password_confirmation: params.password
804
+ }),
805
+ options: webposPasswordResetOptions
761
806
  },
762
807
  resetPasswordByPhone: {
763
808
  url: '/auth/mobile/reset-password',
@@ -1094,6 +1139,7 @@ const shopWebposConfig = {
1094
1139
  const channelConfigMap = exports.channelConfigMap = {
1095
1140
  'online-store': onlineStoreConfig,
1096
1141
  'online-store-h5': h5OnlineStoreConfig,
1142
+ 'kiosk': kioskConfig,
1097
1143
  'default': defaultConfig,
1098
1144
  'webpos': webposConfig,
1099
1145
  'shop.webpos': shopWebposConfig
@@ -4,6 +4,8 @@ import { RegisterParams, LoginParams, OAuthLoginParams, SendVerificationCodePara
4
4
  import { ApiCaller } from './config';
5
5
  export * from './types';
6
6
  export * from './config';
7
+ export { validatePassword } from './utils';
8
+ export type { PasswordValidationOptions } from './utils';
7
9
  /**
8
10
  * 注册登录解决方案实现
9
11
  */
@@ -4,9 +4,16 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  var _exportNames = {
7
- RegisterAndLoginImpl: true
7
+ RegisterAndLoginImpl: true,
8
+ validatePassword: true
8
9
  };
9
10
  exports.RegisterAndLoginImpl = void 0;
11
+ Object.defineProperty(exports, "validatePassword", {
12
+ enumerable: true,
13
+ get: function () {
14
+ return _utils.validatePassword;
15
+ }
16
+ });
10
17
  var _BaseModule = require("../../modules/BaseModule");
11
18
  var _types = require("./types");
12
19
  Object.keys(_types).forEach(function (key) {
@@ -32,6 +39,7 @@ Object.keys(_config).forEach(function (key) {
32
39
  }
33
40
  });
34
41
  });
42
+ var _utils = require("./utils");
35
43
  /**
36
44
  * 注册登录解决方案实现
37
45
  */
@@ -10,10 +10,18 @@ export declare function validateEmail(email: string): boolean;
10
10
  * 验证手机号格式(支持多种国际格式)
11
11
  */
12
12
  export declare function validatePhone(phone: string): boolean;
13
- /**
14
- * 验证密码强度
15
- */
16
- export declare function validatePassword(password: string): {
13
+ export interface PasswordValidationOptions {
14
+ minLength?: number;
15
+ maxLength?: number;
16
+ requireLowercase?: boolean;
17
+ requireUppercase?: boolean;
18
+ requireNumber?: boolean;
19
+ requireSpecialCharacters?: boolean;
20
+ specialCharactersPattern?: RegExp;
21
+ messages?: Partial<Record<'minLength' | 'maxLength' | 'requireLowercase' | 'requireUppercase' | 'requireNumber' | 'requireSpecialCharacters', string>>;
22
+ }
23
+ /** 验证密码强度;不传 options 时保持原有规则与返回格式。 */
24
+ export declare function validatePassword(password: string, options?: PasswordValidationOptions): {
17
25
  isValid: boolean;
18
26
  errors: string[];
19
27
  };
@@ -49,29 +49,36 @@ function validatePhone(phone) {
49
49
  const phoneRegex = /^(\+\d{1,3}[- ]?)?\d{10,14}$/;
50
50
  return phoneRegex.test(cleanPhone);
51
51
  }
52
-
53
- /**
54
- * 验证密码强度
55
- */
56
- function validatePassword(password) {
52
+ /** 验证密码强度;不传 options 时保持原有规则与返回格式。 */
53
+ function validatePassword(password, options = {}) {
57
54
  const errors = [];
58
- if (password.length < 8) {
59
- errors.push('密码长度至少为8位');
55
+ const {
56
+ minLength = 8,
57
+ maxLength = 128,
58
+ requireLowercase = true,
59
+ requireUppercase = true,
60
+ requireNumber = true,
61
+ requireSpecialCharacters = true,
62
+ specialCharactersPattern = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/,
63
+ messages = {}
64
+ } = options;
65
+ if (password.length < minLength) {
66
+ errors.push(messages.minLength ?? `密码长度至少为${minLength}位`);
60
67
  }
61
- if (password.length > 128) {
62
- errors.push('密码长度不能超过128位');
68
+ if (password.length > maxLength) {
69
+ errors.push(messages.maxLength ?? `密码长度不能超过${maxLength}位`);
63
70
  }
64
- if (!/[a-z]/.test(password)) {
65
- errors.push('密码必须包含至少一个小写字母');
71
+ if (requireLowercase && !/[a-z]/.test(password)) {
72
+ errors.push(messages.requireLowercase ?? '密码必须包含至少一个小写字母');
66
73
  }
67
- if (!/[A-Z]/.test(password)) {
68
- errors.push('密码必须包含至少一个大写字母');
74
+ if (requireUppercase && !/[A-Z]/.test(password)) {
75
+ errors.push(messages.requireUppercase ?? '密码必须包含至少一个大写字母');
69
76
  }
70
- if (!/\d/.test(password)) {
71
- errors.push('密码必须包含至少一个数字');
77
+ if (requireNumber && !/\d/.test(password)) {
78
+ errors.push(messages.requireNumber ?? '密码必须包含至少一个数字');
72
79
  }
73
- if (!/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) {
74
- errors.push('密码必须包含至少一个特殊字符');
80
+ if (requireSpecialCharacters && !specialCharactersPattern.test(password)) {
81
+ errors.push(messages.requireSpecialCharacters ?? '密码必须包含至少一个特殊字符');
75
82
  }
76
83
  return {
77
84
  isValid: errors.length === 0,
@@ -105,6 +105,8 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
105
105
  private groupBookingsByResource;
106
106
  private isBookingMatchedResource;
107
107
  private sortMatchedBookings;
108
+ /** 已完成:按订单创建时间降序,避免实时追加和重新加载导致显示顺序不同。 */
109
+ private sortCompletedBookings;
108
110
  /** Unpaid:与 Current 重合的订单沿用 Current 顺序,其余欠款订单按开始时间降序追加。 */
109
111
  private sortUnpaidBookings;
110
112
  /**
@@ -507,6 +507,15 @@ class SalesImpl extends _BaseModule.BaseModule {
507
507
  });
508
508
  }
509
509
 
510
+ /** 已完成:按订单创建时间降序,避免实时追加和重新加载导致显示顺序不同。 */
511
+ sortCompletedBookings(bookings) {
512
+ const createdAt = booking => {
513
+ const timestamp = (0, _dayjs.default)(booking.order?.created_at || '').valueOf();
514
+ return Number.isFinite(timestamp) ? timestamp : 0;
515
+ };
516
+ return [...bookings].sort((left, right) => createdAt(right) - createdAt(left));
517
+ }
518
+
510
519
  /** Unpaid:与 Current 重合的订单沿用 Current 顺序,其余欠款订单按开始时间降序追加。 */
511
520
  sortUnpaidBookings(bookings, currentBookings) {
512
521
  const currentOrderIndexes = new Map(currentBookings.map((booking, index) => [String(booking.order.order_id), index]));
@@ -881,7 +890,7 @@ class SalesImpl extends _BaseModule.BaseModule {
881
890
  const bookings = this.pickBookingsForCurrentPoint(current, this.normalizeResourceBookings(current, deviceCurrent, matchedBookingList), {
882
891
  includeAllFuture: true
883
892
  });
884
- const completedBookings = completedBookingList.map(booking => {
893
+ const completedBookings = this.sortCompletedBookings(completedBookingList).map(booking => {
885
894
  return {
886
895
  ...booking,
887
896
  status: 'compelete',
@@ -34,6 +34,7 @@ exports.toNonNegativeInt = toNonNegativeInt;
34
34
  exports.toNonNegativeNumber = toNonNegativeNumber;
35
35
  exports.toPositiveString = toPositiveString;
36
36
  exports.toPriceString = toPriceString;
37
+ var _weighedProduct = require("../../modules/Order/utils/weighedProduct");
37
38
  var _dayjs = _interopRequireDefault(require("dayjs"));
38
39
  var _decimal = _interopRequireDefault(require("decimal.js"));
39
40
  var _utils = require("../../modules/Order/utils");
@@ -612,7 +613,7 @@ function normalizeOrderProduct(product) {
612
613
  });
613
614
  const composedOriginalPrice = (0, _utils.composeLinePrice)({
614
615
  mainPrice: mainOriginalStr,
615
- bundle: normalizedBundle,
616
+ bundle: Number(productSku.open_sold_weight) === 1 && (0, _manualProductDiscount.hasManualProductDiscountFlag)(metadata) ? (0, _weighedProduct.withOriginalBundleOptionPrices)(normalizedBundle) : normalizedBundle,
616
617
  useOriginalBundle: true
617
618
  });
618
619
  const finalOriginalPrice = (0, _utils.resolveManualOverrideLineOriginalPrice)({
@@ -55,7 +55,7 @@ var _localClock = require("../../modules/ResourcePlanner/localClock");
55
55
  var _platform = require("../../utils/platform");
56
56
  var _itemRule = require("../../model/strategy/adapter/itemRule");
57
57
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
58
- const OPEN_DATA_SECTION_CODES = ['sale', 'reservation', 'fulfillment', 'menu', 'workflow', 'basic', 'checkout', 'availability', 'documents'];
58
+ const OPEN_DATA_SECTION_CODES = ['sale', 'reservation', 'fulfillment', 'menu', 'workflow', 'documents', 'basic', 'checkout', 'availability'];
59
59
  function normalizePositiveInteger(value) {
60
60
  const normalized = Number(value);
61
61
  if (!Number.isInteger(normalized) || normalized <= 0) return undefined;
@@ -283,10 +283,9 @@ function assertAvailabilityCatalogProductIds(productIds, context) {
283
283
  });
284
284
  }
285
285
  }
286
- function normalizeResourceType(_value) {
287
- // 当前业务只支持每个资源组选择一个具体资源。
288
- // 保留公共联合类型以兼容已有调用方,但暂不启用 multiple/capacity 分支。
289
- return 'single';
286
+ function normalizeResourceType(value) {
287
+ // 预约是否独占与每组选择几个资源是两个独立配置。
288
+ return value === 'multiple' || value === 'capacity' ? value : 'single';
290
289
  }
291
290
  function resolveProductKind(product) {
292
291
  if (product.duration || product.service_duration) return 'duration';
@@ -1655,7 +1654,7 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
1655
1654
  shouldUseCheckoutSyncTask() {
1656
1655
  const configured = this.otherParams?.checkoutSyncTaskEnabled;
1657
1656
  if (configured === true) return true;
1658
- const platform = String(this.otherParams?.platform ?? '').trim().toLowerCase();
1657
+ const platform = (0, _platform.normalizeRuntimePlatform)(this.otherParams?.platform);
1659
1658
  return !(0, _platform.isCustomerUserPlatform)(platform) && platform !== 'kiosk';
1660
1659
  }
1661
1660
 
@@ -41,6 +41,11 @@ export declare class VenueBookingImpl extends BaseSalesImpl implements Module {
41
41
  private customerLoginRefreshIdInFlight;
42
42
  private loadAllProductsInFlight;
43
43
  private productsLoaded;
44
+ private smartPricingCustomerId;
45
+ private smartPricingProductCatalog;
46
+ private slotPriceMapCache;
47
+ private slotPriceStrategyConfigs;
48
+ private slotPriceScheduleList;
44
49
  private loadOpenDataConfigInFlight;
45
50
  private static readonly OPEN_DATA_CACHE_TTL;
46
51
  private getLoggerContext;
@@ -69,7 +74,7 @@ export declare class VenueBookingImpl extends BaseSalesImpl implements Module {
69
74
  private clearLoginEffectListeners;
70
75
  private registerLoginEffect;
71
76
  private registerCustomerLoginListeners;
72
- private refreshOrderMarketingAfterLogin;
77
+ private refreshOrderMarketingForCustomer;
73
78
  initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
74
79
  destroy(): Promise<void>;
75
80
  retryInit(): Promise<void>;
@@ -82,6 +87,11 @@ export declare class VenueBookingImpl extends BaseSalesImpl implements Module {
82
87
  passed: boolean | null;
83
88
  failures: QuantityCheckResult[];
84
89
  };
90
+ private resolveCustomerIdForSmartPricing;
91
+ private buildSmartPricingContext;
92
+ private buildSlotPriceMapForDate;
93
+ /** 规则或客户上下文更新后,同步商品展示、已选时段和订单汇总。 */
94
+ refreshSmartPricing(): Promise<void>;
85
95
  loadAllProducts(): Promise<{
86
96
  venueProducts: ProductData[];
87
97
  addonProducts: ProductData[];
@@ -167,7 +177,10 @@ export declare class VenueBookingImpl extends BaseSalesImpl implements Module {
167
177
  onCustomerLogin(params: {
168
178
  customerId: number;
169
179
  }): Promise<void>;
170
- private recalculateOrderPricesFromQuotation;
180
+ /** 宿主退出登录或以匿名身份重新进入时,清理客户资产并恢复匿名价格。 */
181
+ onCustomerLogout(): Promise<void>;
182
+ private recalculateOrderPricesFromSmartPricing;
183
+ private resetProductPriceForSmartPricing;
171
184
  scanCode(code: string, customerId?: number): Promise<{
172
185
  isAvailable: boolean;
173
186
  isAccepted?: boolean | undefined;