@pisell/pisellos 0.10.39 → 0.10.41

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 (40) hide show
  1. package/dist/modules/BookingContext/index.js +4 -4
  2. package/dist/modules/Customer/index.js +4 -2
  3. package/dist/modules/Customer/types.d.ts +2 -0
  4. package/dist/modules/Payment/types.d.ts +21 -1
  5. package/dist/modules/Payment/types.js +2 -0
  6. package/dist/modules/Payment/walletpass.d.ts +1 -1
  7. package/dist/modules/Payment/walletpass.js +80 -47
  8. package/dist/solution/BaseSales/index.d.ts +1 -0
  9. package/dist/solution/BaseSales/index.js +64 -43
  10. package/dist/solution/BookingByStep/index.d.ts +1 -1
  11. package/dist/solution/BookingTicket/index.d.ts +1 -2
  12. package/dist/solution/BookingTicket/index.js +60 -63
  13. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +2 -0
  14. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +266 -150
  15. package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +5 -3
  16. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +4 -0
  17. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +8 -4
  18. package/dist/solution/UnifiedBookingSales/index.d.ts +2 -2
  19. package/dist/solution/UnifiedBookingSales/index.js +10 -10
  20. package/lib/model/strategy/adapter/promotion/index.js +46 -1
  21. package/lib/modules/BookingContext/index.js +3 -3
  22. package/lib/modules/Customer/index.js +4 -1
  23. package/lib/modules/Customer/types.d.ts +2 -0
  24. package/lib/modules/Payment/types.d.ts +21 -1
  25. package/lib/modules/Payment/types.js +1 -0
  26. package/lib/modules/Payment/walletpass.d.ts +1 -1
  27. package/lib/modules/Payment/walletpass.js +29 -9
  28. package/lib/solution/BaseSales/index.d.ts +1 -0
  29. package/lib/solution/BaseSales/index.js +18 -3
  30. package/lib/solution/BookingByStep/index.d.ts +1 -1
  31. package/lib/solution/BookingTicket/index.d.ts +1 -2
  32. package/lib/solution/BookingTicket/index.js +36 -33
  33. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +2 -0
  34. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +127 -42
  35. package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +5 -3
  36. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +4 -0
  37. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +7 -3
  38. package/lib/solution/UnifiedBookingSales/index.d.ts +2 -2
  39. package/lib/solution/UnifiedBookingSales/index.js +4 -4
  40. package/package.json +1 -1
@@ -5,10 +5,21 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.applyGlobalScan = applyGlobalScan;
7
7
  var _addProductDecision = require("../addProductDecision");
8
- var _cartView = require("../cartView");
9
8
  var _scanProductValues = require("./scanProductValues");
10
9
  /** applyGlobalScan 所需的 BookingTicket 运行时能力 */
11
10
 
11
+ function getRealCustomerId(customer) {
12
+ const customerId = Number(customer?.id ?? customer?.customer_id);
13
+ return Number.isInteger(customerId) && customerId > 1 ? customerId : undefined;
14
+ }
15
+ function getWalletAssetCustomerId(asset) {
16
+ const customerId = Number(asset?.customer_id ?? asset?.holder?.customer_id);
17
+ return Number.isInteger(customerId) && customerId > 1 ? customerId : undefined;
18
+ }
19
+ function getErrorMessage(error) {
20
+ return error instanceof Error ? error.message : String(error || '');
21
+ }
22
+
12
23
  /**
13
24
  * 处理单条扫码商品的加车决策(含弹窗链)。
14
25
  */
@@ -93,17 +104,55 @@ async function applyGlobalScan(host, formatted, bridge) {
93
104
  const {
94
105
  searchType,
95
106
  data,
96
- scanCode
107
+ scanCode,
108
+ customerId: formattedCustomerId,
109
+ isCrossShopWallet
97
110
  } = formatted;
98
111
  if (searchType === 'wallet') {
99
- if (!data) {
112
+ const currentCustomer = host.getOrderCustomerSnapshot();
113
+ if (getRealCustomerId(currentCustomer) !== undefined) {
114
+ return {
115
+ status: 'success',
116
+ kind: 'customer'
117
+ };
118
+ }
119
+ let customer = data;
120
+ if (isCrossShopWallet) {
121
+ const sourceCustomerId = getRealCustomerId({
122
+ id: formattedCustomerId
123
+ });
124
+ if (sourceCustomerId === undefined || !host.resolveCustomerById) {
125
+ bridge?.onNotify?.('fail', 'pisell2.text.scan-global-failed');
126
+ return {
127
+ status: 'failed',
128
+ reason: 'invalid_data'
129
+ };
130
+ }
131
+ try {
132
+ customer = await host.resolveCustomerById(sourceCustomerId);
133
+ } catch (error) {
134
+ bridge?.onNotify?.('fail', getErrorMessage(error) || 'pisell2.text.scan-global-failed');
135
+ return {
136
+ status: 'failed',
137
+ reason: 'invalid_data'
138
+ };
139
+ }
140
+ }
141
+ const customerId = getRealCustomerId(customer) ?? (!isCrossShopWallet ? getRealCustomerId({
142
+ id: formattedCustomerId
143
+ }) : undefined);
144
+ if (!customer || customerId === undefined) {
100
145
  bridge?.onNotify?.('fail', 'pisell2.text.scan-global-failed');
101
146
  return {
102
147
  status: 'failed',
103
148
  reason: 'invalid_data'
104
149
  };
105
150
  }
106
- host.setOrderCustomer(data);
151
+ const orderCustomer = customer.id === undefined || customer.id === null ? {
152
+ ...customer,
153
+ id: customerId
154
+ } : customer;
155
+ host.setOrderCustomer(orderCustomer);
107
156
  bridge?.onNotify?.('success', 'pisell2.text.add-client-success');
108
157
  return {
109
158
  status: 'success',
@@ -112,7 +161,58 @@ async function applyGlobalScan(host, formatted, bridge) {
112
161
  }
113
162
  if (searchType === 'walletPass') {
114
163
  const snapshot = host.getOrderCustomerSnapshot();
115
- const scannedCustomerIsWalkIn = (0, _cartView.isWalkInCustomer)(data);
164
+ const currentCustomerIsWalkIn = getRealCustomerId(snapshot) === undefined;
165
+ let activeCustomer = snapshot;
166
+ const bindWalletPassCustomer = async asset => {
167
+ if (!currentCustomerIsWalkIn) {
168
+ return {
169
+ resolveFailed: false
170
+ };
171
+ }
172
+ const assetCustomerId = getWalletAssetCustomerId(asset) ?? getRealCustomerId({
173
+ id: formattedCustomerId
174
+ });
175
+ let customer = null;
176
+ if (assetCustomerId !== undefined) {
177
+ if (!host.resolveCustomerById) {
178
+ bridge?.onNotify?.('fail', 'pisell2.text.scan-global-failed');
179
+ return {
180
+ resolveFailed: true
181
+ };
182
+ }
183
+ try {
184
+ customer = await host.resolveCustomerById(assetCustomerId);
185
+ } catch (error) {
186
+ console.warn('[GlobalScan] Wallet asset customer lookup failed', error);
187
+ bridge?.onNotify?.('fail', getErrorMessage(error) || 'pisell2.text.scan-global-failed');
188
+ return {
189
+ resolveFailed: true
190
+ };
191
+ }
192
+ }
193
+ const customerId = getRealCustomerId(customer);
194
+ if (!customer || customerId === undefined) {
195
+ if (assetCustomerId !== undefined) {
196
+ bridge?.onNotify?.('fail', 'pisell2.text.scan-global-failed');
197
+ }
198
+ return {
199
+ resolveFailed: assetCustomerId !== undefined
200
+ };
201
+ }
202
+ const orderCustomer = customer.id === undefined || customer.id === null ? {
203
+ ...customer,
204
+ id: customerId
205
+ } : customer;
206
+ host.setOrderCustomer(orderCustomer);
207
+ activeCustomer = orderCustomer;
208
+ await host.refreshWalletAssets?.({
209
+ preserveSelection: false,
210
+ retainSearchResultCodes: [scanCode]
211
+ });
212
+ return {
213
+ resolveFailed: false
214
+ };
215
+ };
116
216
  const trySelectWalletAsset = async () => {
117
217
  if (!host.scanWalletAsset) return null;
118
218
  const walletResult = await host.scanWalletAsset(scanCode).catch(error => {
@@ -123,61 +223,46 @@ async function applyGlobalScan(host, formatted, bridge) {
123
223
  if (walletResult.type !== 'normalCode' || !walletResult.asset) {
124
224
  return null;
125
225
  }
226
+ const bindResult = await bindWalletPassCustomer(walletResult.asset);
126
227
  if (walletResult.selected) {
127
- bridge?.onNotify?.('success');
228
+ if (!bindResult.resolveFailed) {
229
+ bridge?.onNotify?.('success');
230
+ }
128
231
  return {
129
232
  status: 'success',
130
233
  kind: 'promotion'
131
234
  };
132
235
  }
133
236
  // 已识别为 Wallet 支付资产但当前不可用:保留禁用展示,不回退优惠码。
134
- bridge?.onNotify?.('fail', 'pisell2.text.goodpass.code-invalid-cart');
237
+ if (!bindResult.resolveFailed) {
238
+ bridge?.onNotify?.('fail', 'pisell2.text.goodpass.code-invalid-cart');
239
+ }
135
240
  return {
136
241
  status: 'failed',
137
242
  reason: 'invalid_data'
138
243
  };
139
244
  };
140
- if ((0, _cartView.isWalkInCustomer)(snapshot) || scannedCustomerIsWalkIn) {
141
- // 当前购物车或 walletPass 绑定客户为 walk-in 时不绑客户,避免覆盖已有下单客户。
142
- const walletSelection = await trySelectWalletAsset();
143
- if (walletSelection) return walletSelection;
144
- if (!bridge?.applyPromotionCode) {
145
- return {
146
- status: 'failed',
147
- reason: 'no_bridge'
148
- };
149
- }
150
- const result = await bridge.applyPromotionCode(scanCode);
151
- if (result?.isAccepted ?? result?.isAvailable) {
152
- return {
153
- status: 'success',
154
- kind: 'promotion'
155
- };
156
- }
245
+ const walletSelection = await trySelectWalletAsset();
246
+ if (walletSelection) return walletSelection;
247
+
248
+ // 非支付类 Wallet Pass 仍可能由 batch-search 返回所属客户。
249
+ await bindWalletPassCustomer();
250
+ if (!bridge?.applyPromotionCode) {
157
251
  return {
158
252
  status: 'failed',
159
- reason: 'invalid_data'
253
+ reason: 'no_bridge'
160
254
  };
161
255
  }
162
- host.setOrderCustomer(data);
163
- await host.refreshWalletAssets?.({
164
- preserveSelection: false
165
- });
166
- const walletSelection = await trySelectWalletAsset();
167
- if (walletSelection) return walletSelection;
168
- if (bridge?.applyPromotionCode) {
169
- const promotionResult = await bridge.applyPromotionCode(scanCode, Number(data?.id ?? data?.customer_id) || undefined);
170
- if (promotionResult?.isAccepted ?? promotionResult?.isAvailable) {
171
- return {
172
- status: 'success',
173
- kind: 'promotion'
174
- };
175
- }
256
+ const promotionResult = await bridge.applyPromotionCode(scanCode, getRealCustomerId(activeCustomer));
257
+ if (promotionResult?.isAccepted ?? promotionResult?.isAvailable) {
258
+ return {
259
+ status: 'success',
260
+ kind: 'promotion'
261
+ };
176
262
  }
177
- bridge?.onNotify?.('success', 'pisell2.text.add-client-success');
178
263
  return {
179
- status: 'success',
180
- kind: 'customer'
264
+ status: 'failed',
265
+ reason: 'invalid_data'
181
266
  };
182
267
  }
183
268
  if (searchType === 'product' || searchType === 'local_product') {
@@ -14,6 +14,7 @@ const searchWalletPass = async (request, code) => {
14
14
  // 翻译识别码名称 0:不翻译 1:翻译
15
15
  relation_product: 1,
16
16
  // 组装关联商品: 0不处理 1处理
17
+ with_franchisor_data: 1,
17
18
  with: ['customer'],
18
19
  tags: ['point_card',
19
20
  // 积分卡
@@ -51,17 +52,18 @@ exports.searchWalletPass = searchWalletPass;
51
52
  const searchWallet = async (request, code) => {
52
53
  const params = {
53
54
  code,
54
- with_customer: 1
55
+ with_customer: 1,
56
+ with_franchisor_data: 1
55
57
  };
56
58
  try {
57
59
  const res = await request?.post('/wallet/detail/search', params);
58
- if (res?.code == 200 && !!res?.data) {
60
+ if (res?.status === true && res?.code === 200 && !!res?.data) {
59
61
  return {
60
62
  searchType: 'wallet',
61
63
  response: res
62
64
  };
63
65
  }
64
- throw new Error('没有找到对应的识别码');
66
+ throw new Error(res?.message || '没有找到对应的识别码');
65
67
  } catch (error) {
66
68
  throw error;
67
69
  }
@@ -6,6 +6,10 @@ export interface GlobalScanFormattedResult {
6
6
  searchType: GlobalScanSearchType;
7
7
  data: any;
8
8
  scanCode: string;
9
+ /** Wallet / Wallet Pass 来源客户 ID;跨店时必须先解析为当前店客户。 */
10
+ customerId?: string | number;
11
+ /** 仅 Wallet 返回;true 时禁止直接使用响应内的来源客户。 */
12
+ isCrossShopWallet?: boolean;
9
13
  }
10
14
  interface RawScanSearchResult {
11
15
  searchType?: GlobalScanSearchType;
@@ -36,14 +36,18 @@ function formatGlobalScanResult(raw, scanCode) {
36
36
  return {
37
37
  searchType,
38
38
  data: resultData?.customer,
39
- scanCode
39
+ scanCode,
40
+ customerId: resultData?.customer_id,
41
+ isCrossShopWallet: resultData?.is_cross_shop_wallet === true || Number(resultData?.is_cross_shop_wallet) === 1
40
42
  };
41
43
  }
42
44
  if (searchType === 'walletPass') {
45
+ const firstResult = resultData?.[0];
43
46
  return {
44
47
  searchType,
45
- data: resultData?.[0]?.customer,
46
- scanCode
48
+ data: firstResult?.customer,
49
+ scanCode,
50
+ customerId: firstResult?.customer_id ?? firstResult?.holder?.customer_id ?? firstResult?.customer?.id ?? firstResult?.customer?.customer_id
47
51
  };
48
52
  }
49
53
  if (searchType === 'product' || searchType === 'local_product') {
@@ -123,8 +123,8 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
123
123
  */
124
124
  clearSessionStorageOrderSnapshot(): void;
125
125
  /**
126
- * H5/Kiosk 不能把已收款订单留在本地等待重试,必须直接等待云端 checkout
127
- * 其它平台保持 WebPOS/POS 的本地待同步任务;显式 true 仍可强制使用本地任务。
126
+ * PC/H5/Kiosk 结账默认直接等待云端 checkout;POS/WebPOS 仍保持本地待同步。
127
+ * 显式传 checkoutSyncTaskEnabled: true 时仍可回退到本地同步模式。
128
128
  */
129
129
  protected shouldUseCheckoutSyncTask(): boolean;
130
130
  /** UnifiedBookingSales 的真实结账默认请求收据数据,供云端确认后再本地打印。 */
@@ -1522,14 +1522,14 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
1522
1522
  }
1523
1523
 
1524
1524
  /**
1525
- * H5/Kiosk 不能把已收款订单留在本地等待重试,必须直接等待云端 checkout
1526
- * 其它平台保持 WebPOS/POS 的本地待同步任务;显式 true 仍可强制使用本地任务。
1525
+ * PC/H5/Kiosk 结账默认直接等待云端 checkout;POS/WebPOS 仍保持本地待同步。
1526
+ * 显式传 checkoutSyncTaskEnabled: true 时仍可回退到本地同步模式。
1527
1527
  */
1528
1528
  shouldUseCheckoutSyncTask() {
1529
1529
  const configured = this.otherParams?.checkoutSyncTaskEnabled;
1530
1530
  if (configured === true) return true;
1531
- const platform = (0, _platform.normalizeRuntimePlatform)(this.otherParams?.platform);
1532
- return platform !== 'h5' && platform !== 'kiosk';
1531
+ const platform = String(this.otherParams?.platform ?? '').trim().toLowerCase();
1532
+ return !(0, _platform.isCustomerUserPlatform)(platform) && platform !== 'kiosk';
1533
1533
  }
1534
1534
 
1535
1535
  /** UnifiedBookingSales 的真实结账默认请求收据数据,供云端确认后再本地打印。 */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "0.10.39",
4
+ "version": "0.10.41",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",