@pisell/pisellos 0.10.65 → 0.10.67
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.
- package/dist/modules/OpenData/contactInfo.d.ts +20 -0
- package/dist/modules/OpenData/contactInfo.js +70 -0
- package/dist/modules/OpenData/index.d.ts +1 -0
- package/dist/modules/OpenData/index.js +1 -0
- package/dist/modules/OpenData/types.d.ts +9 -0
- package/dist/solution/BaseSales/index.d.ts +7 -0
- package/dist/solution/BaseSales/index.js +966 -844
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.d.ts +16 -2
- package/dist/solution/BookingTicket/index.js +781 -501
- package/dist/solution/UnifiedBookingSales/index.js +1 -1
- package/lib/model/strategy/adapter/promotion/index.js +46 -1
- package/lib/modules/OpenData/contactInfo.d.ts +20 -0
- package/lib/modules/OpenData/contactInfo.js +56 -0
- package/lib/modules/OpenData/index.d.ts +1 -0
- package/lib/modules/OpenData/index.js +12 -0
- package/lib/modules/OpenData/types.d.ts +9 -0
- package/lib/solution/BaseSales/index.d.ts +7 -0
- package/lib/solution/BaseSales/index.js +31 -0
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.d.ts +16 -2
- package/lib/solution/BookingTicket/index.js +128 -2
- package/lib/solution/UnifiedBookingSales/index.js +1 -1
- package/package.json +1 -1
|
@@ -2152,7 +2152,7 @@ export var UnifiedBookingSalesImpl = /*#__PURE__*/function (_BookingTicket) {
|
|
|
2152
2152
|
(_this$store$customer = this.store.customer) === null || _this$store$customer === void 0 || (_this$store$customer$ = _this$store$customer.setSelectedCustomer) === null || _this$store$customer$ === void 0 || _this$store$customer$.call(_this$store$customer, authenticatedCustomer);
|
|
2153
2153
|
this.setOrderCustomer(authenticatedCustomer);
|
|
2154
2154
|
_context8.next = 15;
|
|
2155
|
-
return Promise.all([this.refreshDiscountConfigAfterOrderCustomerChange(authenticatedCustomerId), this.waitForOrderCustomerPromotionRefresh()]);
|
|
2155
|
+
return Promise.all([this.refreshDiscountConfigAfterOrderCustomerChange(authenticatedCustomerId), this.waitForOrderCustomerPromotionRefresh(), this.waitForOrderCustomerContactInfo()]);
|
|
2156
2156
|
case 15:
|
|
2157
2157
|
return _context8.abrupt("return", true);
|
|
2158
2158
|
case 16:
|
|
@@ -1 +1,46 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "BUY_X_GET_Y_FREE_STRATEGY", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _examples.BUY_X_GET_Y_FREE_STRATEGY;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "ITEM_REWARD_STRATEGY", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _examples.ITEM_REWARD_STRATEGY;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "PromotionAdapter", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _adapter.PromotionAdapter;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, "PromotionEvaluator", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () {
|
|
27
|
+
return _evaluator.PromotionEvaluator;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(exports, "X_ITEMS_FOR_Y_PRICE_STRATEGY", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get: function () {
|
|
33
|
+
return _examples.X_ITEMS_FOR_Y_PRICE_STRATEGY;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
Object.defineProperty(exports, "default", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function () {
|
|
39
|
+
return _adapter.default;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
var _evaluator = require("./evaluator");
|
|
43
|
+
var _adapter = _interopRequireWildcard(require("./adapter"));
|
|
44
|
+
var _examples = require("./examples");
|
|
45
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
46
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { OpenDataConfig } from './types';
|
|
2
|
+
export declare const CONTACT_INFO_FIELDS: readonly ["first_name", "last_name", "phone", "email"];
|
|
3
|
+
export type ContactInfoField = typeof CONTACT_INFO_FIELDS[number];
|
|
4
|
+
export interface ContactInfoConfig {
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
fields: Record<ContactInfoField, {
|
|
7
|
+
visible: boolean;
|
|
8
|
+
required: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
export declare function resolveContactInfoConfig(data?: OpenDataConfig | null): ContactInfoConfig;
|
|
12
|
+
export declare function getMissingContactInfoFields(config: ContactInfoConfig, value: unknown): ContactInfoField[];
|
|
13
|
+
export declare function isContactRecord(value: unknown): value is Record<string, any>;
|
|
14
|
+
/** Customer relations wrap the contact in value; sales orders store the object itself. */
|
|
15
|
+
export declare function extractCustomerContactInfo(customer: unknown): Record<string, any> | null;
|
|
16
|
+
export declare class ContactInfoRequiredError extends Error {
|
|
17
|
+
readonly fields: ContactInfoField[];
|
|
18
|
+
readonly code = "CONTACT_INFO_REQUIRED";
|
|
19
|
+
constructor(fields: ContactInfoField[]);
|
|
20
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ContactInfoRequiredError = exports.CONTACT_INFO_FIELDS = void 0;
|
|
7
|
+
exports.extractCustomerContactInfo = extractCustomerContactInfo;
|
|
8
|
+
exports.getMissingContactInfoFields = getMissingContactInfoFields;
|
|
9
|
+
exports.isContactRecord = isContactRecord;
|
|
10
|
+
exports.resolveContactInfoConfig = resolveContactInfoConfig;
|
|
11
|
+
const CONTACT_INFO_FIELDS = exports.CONTACT_INFO_FIELDS = ['first_name', 'last_name', 'phone', 'email'];
|
|
12
|
+
function resolveContactInfoConfig(data) {
|
|
13
|
+
const enabled = data?.['sale.enable_contact_info'] === true;
|
|
14
|
+
const fields = CONTACT_INFO_FIELDS.reduce((result, field) => {
|
|
15
|
+
const visible = data?.[`sale.${field}.visible`] !== false;
|
|
16
|
+
result[field] = {
|
|
17
|
+
visible,
|
|
18
|
+
required: enabled && visible && data?.[`sale.${field}.required`] === true
|
|
19
|
+
};
|
|
20
|
+
return result;
|
|
21
|
+
}, {});
|
|
22
|
+
return {
|
|
23
|
+
enabled: enabled && CONTACT_INFO_FIELDS.some(field => fields[field].visible),
|
|
24
|
+
fields
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function getMissingContactInfoFields(config, value) {
|
|
28
|
+
if (!config.enabled) return [];
|
|
29
|
+
const contact = isContactRecord(value) ? value : {};
|
|
30
|
+
return CONTACT_INFO_FIELDS.filter(field => {
|
|
31
|
+
if (!config.fields[field].visible || !config.fields[field].required) return false;
|
|
32
|
+
const fieldValue = field === 'phone' ? isContactRecord(contact.phone) ? contact.phone.phone : undefined : contact[field];
|
|
33
|
+
return typeof fieldValue !== 'string' || !fieldValue.trim();
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function isContactRecord(value) {
|
|
37
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Customer relations wrap the contact in value; sales orders store the object itself. */
|
|
41
|
+
function extractCustomerContactInfo(customer) {
|
|
42
|
+
if (!isContactRecord(customer)) return null;
|
|
43
|
+
const contact = customer.contacts_info;
|
|
44
|
+
if (!isContactRecord(contact)) return null;
|
|
45
|
+
const value = Object.prototype.hasOwnProperty.call(contact, 'value') ? contact.value : contact;
|
|
46
|
+
return isContactRecord(value) ? value : null;
|
|
47
|
+
}
|
|
48
|
+
class ContactInfoRequiredError extends Error {
|
|
49
|
+
code = 'CONTACT_INFO_REQUIRED';
|
|
50
|
+
constructor(fields) {
|
|
51
|
+
super(`Contact information is required: ${fields.join(', ')}`);
|
|
52
|
+
this.fields = fields;
|
|
53
|
+
this.name = 'ContactInfoRequiredError';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.ContactInfoRequiredError = ContactInfoRequiredError;
|
|
@@ -3,6 +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 * from './contactInfo';
|
|
6
7
|
export { computeAvailability, getDefaultInvoiceLayoutConfig, getDefaultInvoiceTemplateSettings, normalizeInvoiceLayoutConfig, normalizeInvoiceLayoutFromTemplateSettings, normalizeInvoiceTemplateSettings, normalizeRawInvoiceTemplateSettings, resolveInvoiceReceiptLocales, } from './utils';
|
|
7
8
|
export declare class OpenDataModule extends BaseModule implements Module {
|
|
8
9
|
protected defaultName: string;
|
|
@@ -77,6 +77,18 @@ Object.keys(_types).forEach(function (key) {
|
|
|
77
77
|
}
|
|
78
78
|
});
|
|
79
79
|
});
|
|
80
|
+
var _contactInfo = require("./contactInfo");
|
|
81
|
+
Object.keys(_contactInfo).forEach(function (key) {
|
|
82
|
+
if (key === "default" || key === "__esModule") return;
|
|
83
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
84
|
+
if (key in exports && exports[key] === _contactInfo[key]) return;
|
|
85
|
+
Object.defineProperty(exports, key, {
|
|
86
|
+
enumerable: true,
|
|
87
|
+
get: function () {
|
|
88
|
+
return _contactInfo[key];
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
80
92
|
function isRecord(value) {
|
|
81
93
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
82
94
|
}
|
|
@@ -69,6 +69,15 @@ export interface OpenDataConfig {
|
|
|
69
69
|
'reservation.time_slice'?: number | string;
|
|
70
70
|
'sale.allow_item_notes'?: boolean;
|
|
71
71
|
'sale.allow_order_notes'?: boolean;
|
|
72
|
+
'sale.enable_contact_info'?: boolean;
|
|
73
|
+
'sale.first_name.visible'?: boolean;
|
|
74
|
+
'sale.first_name.required'?: boolean;
|
|
75
|
+
'sale.last_name.visible'?: boolean;
|
|
76
|
+
'sale.last_name.required'?: boolean;
|
|
77
|
+
'sale.phone.visible'?: boolean;
|
|
78
|
+
'sale.phone.required'?: boolean;
|
|
79
|
+
'sale.email.visible'?: boolean;
|
|
80
|
+
'sale.email.required'?: boolean;
|
|
72
81
|
/** False submits an order without starting or confirming payment. */
|
|
73
82
|
'sale.require_payment_on_submit'?: boolean | null;
|
|
74
83
|
'sale.enable_item_rules'?: boolean;
|
|
@@ -5,6 +5,7 @@ import { BaseSalesOrderProduct, BaseSalesOrderProductIdentity, BaseSalesPaymentS
|
|
|
5
5
|
import { OrderModule } from '../../modules/Order';
|
|
6
6
|
import { type PickupTimingBookingInput } from '../../modules/Order/pickupTiming';
|
|
7
7
|
import type { PickupTimingAvailability, PickupTimingSelection, PickupTimingValidationResult } from '../../modules/FulfillmentTiming';
|
|
8
|
+
import { type ContactInfoConfig } from '../../modules/OpenData/contactInfo';
|
|
8
9
|
import type { AddProductBookingInput, AddProductToOrderOptions, LoadSalesDetailParams, OrderPaymentData, OrderPaymentUpdateOptions, OrderPaymentUpdateResult, OrderPaymentSource, SendCustomerPayLinkParams, SyncPaymentsToOrderParams, SyncPaymentsToOrderResult, UpdateTempOrderCustomerInput, UpdateOrderBookingParams, UpdateOrderProductParams, OrderTempOrder, OrderSummary, OrderPromotionEvaluator, OrderGiftSelectResolver, OrderUnfulfilledPromotion, OrderLastGiftActions, RemoveProductsFromOrderOptions, ReplaceTempOrderOptions, SalesNote, SalesNotesMutationSnapshot, SetSalesNoteInput, PickupReferenceMode, ShopServiceData, ShopServiceType } from '../../modules/Order/types';
|
|
9
10
|
import type { SubmitPayloadEnhancer } from '../../modules/Order/utils';
|
|
10
11
|
import type { Discount, VirtualCurrencyAsset } from '../../modules/Discount/types';
|
|
@@ -19,6 +20,7 @@ import { QuotationModule } from '../../modules/Quotation';
|
|
|
19
20
|
import type { ISalesDetail, ISalesDetailHeader, ISalesBooking, ISalesPayment, ISalesSurcharge } from './utils/parseSalesResponse';
|
|
20
21
|
import { AppPlugin } from '../../plugins/app';
|
|
21
22
|
import { type TransformBaseProductToOrderProductParams } from './utils/transformBaseProductToOrderProduct';
|
|
23
|
+
import type { OpenDataConfig } from '../../modules/OpenData/types';
|
|
22
24
|
export interface BaseSalesState {
|
|
23
25
|
order?: OrderModule;
|
|
24
26
|
products?: ProductList;
|
|
@@ -283,6 +285,9 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
|
|
|
283
285
|
setPickupTimingBooking(input: PickupTimingBookingInput): import("../../modules/Order").PickupTimingBooking;
|
|
284
286
|
clearPickupTimingBooking(): void;
|
|
285
287
|
updateTempOrderContactsInfo(contactsInfo: Record<string, any> | null): Record<string, any> | null;
|
|
288
|
+
protected loadContactInfoOpenData(): Promise<OpenDataConfig | null>;
|
|
289
|
+
getContactInfoConfig(): Promise<ContactInfoConfig>;
|
|
290
|
+
validateContactInfo(): Promise<void>;
|
|
286
291
|
/** @deprecated OrderModule 不再负责 tempOrder localStorage 持久化。 */
|
|
287
292
|
setEnableTempOrderPersist(enabled: boolean): void;
|
|
288
293
|
/** 是否允许 saveDraft 写入 IndexedDB 草稿。 */
|
|
@@ -359,6 +364,8 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
|
|
|
359
364
|
* payments 会交给 OrderModule 统一映射为 Sales 协议支付项。
|
|
360
365
|
*/
|
|
361
366
|
submitSalesOrder<T = any>(params?: BaseSalesSubmitSalesOrderParams): Promise<T>;
|
|
367
|
+
/** Validate required contact fields before a new payment is initiated. */
|
|
368
|
+
ensureSalesOrderReadyForPayment(): Promise<void>;
|
|
362
369
|
saveSalesOrderDraft<T = any>(params?: {
|
|
363
370
|
platform?: string;
|
|
364
371
|
businessCode?: string;
|
|
@@ -25,6 +25,7 @@ var _Order = require("../../modules/Order");
|
|
|
25
25
|
var _pickupTiming = require("../../modules/Order/pickupTiming");
|
|
26
26
|
var _pickupTiming2 = require("./pickupTiming");
|
|
27
27
|
var _holderRelations = require("../../modules/Order/holderRelations");
|
|
28
|
+
var _contactInfo = require("../../modules/OpenData/contactInfo");
|
|
28
29
|
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
29
30
|
var _lodashEs = require("lodash-es");
|
|
30
31
|
var _types2 = require("../BookingByStep/types");
|
|
@@ -1906,6 +1907,27 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
1906
1907
|
if (!this.store.order) throw new Error('order 模块未初始化');
|
|
1907
1908
|
return this.store.order.updateTempOrderContactsInfo(contactsInfo);
|
|
1908
1909
|
}
|
|
1910
|
+
async loadContactInfoOpenData() {
|
|
1911
|
+
return this.otherParams?.openData ?? null;
|
|
1912
|
+
}
|
|
1913
|
+
async getContactInfoConfig() {
|
|
1914
|
+
const scope = this.getPickupTimingScope();
|
|
1915
|
+
const data = await this.loadContactInfoOpenData();
|
|
1916
|
+
if (scope !== this.getPickupTimingScope()) throw new Error('Store settings changed. Please try again.');
|
|
1917
|
+
return (0, _contactInfo.resolveContactInfoConfig)(data);
|
|
1918
|
+
}
|
|
1919
|
+
async validateContactInfo() {
|
|
1920
|
+
const order = this.getTempOrder();
|
|
1921
|
+
const identity = order?._extend?.localId || order?.external_sale_number || order?.order_id || order;
|
|
1922
|
+
const customerId = order?.customer_id;
|
|
1923
|
+
const config = await this.getContactInfoConfig();
|
|
1924
|
+
const current = this.getTempOrder();
|
|
1925
|
+
if (identity !== (current?._extend?.localId || current?.external_sale_number || current?.order_id || current) || customerId !== current?.customer_id) {
|
|
1926
|
+
throw new Error('The order changed. Please try again.');
|
|
1927
|
+
}
|
|
1928
|
+
const missing = (0, _contactInfo.getMissingContactInfoFields)(config, current?.contacts_info);
|
|
1929
|
+
if (missing.length) throw new _contactInfo.ContactInfoRequiredError(missing);
|
|
1930
|
+
}
|
|
1909
1931
|
|
|
1910
1932
|
/** @deprecated OrderModule 不再负责 tempOrder localStorage 持久化。 */
|
|
1911
1933
|
setEnableTempOrderPersist(enabled) {
|
|
@@ -2706,6 +2728,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2706
2728
|
if (!this.store.order) {
|
|
2707
2729
|
throw new Error('BaseSales 解决方案需要 order 模块支持');
|
|
2708
2730
|
}
|
|
2731
|
+
await this.validateContactInfo();
|
|
2709
2732
|
if (this.isPickupTimingEnabled()) await this.preparePickupTimingForCheckout({
|
|
2710
2733
|
freeze: true
|
|
2711
2734
|
});
|
|
@@ -2779,6 +2802,11 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2779
2802
|
};
|
|
2780
2803
|
return this.shouldUseCheckoutSyncTask() ? this.store.order.submitTempOrder(submitParams) : this.store.order.submitTempOrderAsync(submitParams);
|
|
2781
2804
|
}
|
|
2805
|
+
|
|
2806
|
+
/** Validate required contact fields before a new payment is initiated. */
|
|
2807
|
+
async ensureSalesOrderReadyForPayment() {
|
|
2808
|
+
await this.validateContactInfo();
|
|
2809
|
+
}
|
|
2782
2810
|
async saveSalesOrderDraft(params) {
|
|
2783
2811
|
if (!this.store.order) {
|
|
2784
2812
|
throw new Error('BaseSales 解决方案需要 order 模块支持');
|
|
@@ -2801,6 +2829,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2801
2829
|
if (!this.store.order) {
|
|
2802
2830
|
throw new Error('BaseSales 解决方案需要 order 模块支持');
|
|
2803
2831
|
}
|
|
2832
|
+
await this.validateContactInfo();
|
|
2804
2833
|
if (this.isPickupTimingEnabled()) {
|
|
2805
2834
|
await this.preparePickupTimingForCheckout({
|
|
2806
2835
|
freeze: true
|
|
@@ -3637,6 +3666,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
3637
3666
|
*/
|
|
3638
3667
|
async confirmWalletPayment() {
|
|
3639
3668
|
if (!this.store.order) throw new Error('order 模块未初始化');
|
|
3669
|
+
await this.validateContactInfo();
|
|
3640
3670
|
const beforePayments = (0, _lodashEs.cloneDeep)(this.store.order.getOrderPayments());
|
|
3641
3671
|
const pendingWalletAmount = this.getPendingWalletPaymentAmount(beforePayments);
|
|
3642
3672
|
const amountSnapshot = this.store.order.getOrderAmountSnapshot?.();
|
|
@@ -3708,6 +3738,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
3708
3738
|
*/
|
|
3709
3739
|
async payCash(params) {
|
|
3710
3740
|
if (!this.store.order) throw new Error('order 模块未初始化');
|
|
3741
|
+
await this.validateContactInfo();
|
|
3711
3742
|
const amount = Number(params.amount || 0);
|
|
3712
3743
|
if (!Number.isFinite(amount) || amount <= 0) {
|
|
3713
3744
|
throw new Error('现金支付金额必须大于 0');
|
|
@@ -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 |
|
|
329
|
+
weekNum: 0 | 6 | 5 | 1 | 2 | 3 | 4;
|
|
330
330
|
}[]>;
|
|
331
331
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
332
332
|
private getScheduleDataByIds;
|
|
@@ -32,6 +32,7 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
32
32
|
private orderCustomerDiscountRefreshCustomerId;
|
|
33
33
|
private orderCustomerDiscountRefreshTarget;
|
|
34
34
|
private orderCustomerPromotionRefreshInFlight;
|
|
35
|
+
private orderCustomerContactRequest;
|
|
35
36
|
private readonly customerResolveRequests;
|
|
36
37
|
protected loadOpenDataInFlight: Promise<OpenDataConfig> | null;
|
|
37
38
|
protected loadOpenDataInFlightTarget: string | null;
|
|
@@ -58,6 +59,7 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
58
59
|
private getIdGeneratorPlugin;
|
|
59
60
|
protected loadOpenDataConfig(): Promise<OpenDataConfig | null>;
|
|
60
61
|
getOpenData(): Promise<OpenDataConfig | null>;
|
|
62
|
+
protected loadContactInfoOpenData(): Promise<OpenDataConfig | null>;
|
|
61
63
|
getInvoiceLayoutConfig(): Promise<InvoiceLayoutConfig>;
|
|
62
64
|
getInvoiceTemplateSettings(): Promise<InvoiceTemplateSettings>;
|
|
63
65
|
protected isInvoiceOpenDataCacheCurrent(): boolean;
|
|
@@ -275,6 +277,16 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
275
277
|
setOrderCustomer(customer: ICustomer | null, options?: {
|
|
276
278
|
source?: OrderCustomerChangePayload['source'];
|
|
277
279
|
}): void;
|
|
280
|
+
private resetOrderCustomerContactInfo;
|
|
281
|
+
/** Explicit edits, including clearing the contact, must win over a pending default. */
|
|
282
|
+
updateTempOrderContactsInfo(contact: Record<string, any> | null): Record<string, any> | null;
|
|
283
|
+
private getCustomerContactScope;
|
|
284
|
+
private getCustomerContactOrderKey;
|
|
285
|
+
replaceTempOrder(...args: Parameters<BaseSalesImpl['replaceTempOrder']>): Promise<import("../../modules/Order/types").OrderTempOrder>;
|
|
286
|
+
private applyDefaultOrderCustomerContactInfo;
|
|
287
|
+
private prefillOrderCustomerContactInfo;
|
|
288
|
+
protected waitForOrderCustomerContactInfo(): Promise<void>;
|
|
289
|
+
validateContactInfo(): Promise<void>;
|
|
278
290
|
/**
|
|
279
291
|
* 读取 tempOrder 上的下单客户协议字段;customer_id 缺失时返回 null。
|
|
280
292
|
*/
|
|
@@ -286,7 +298,9 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
286
298
|
/**
|
|
287
299
|
* 清空下单客户(协议字段 + snapshot)。
|
|
288
300
|
*/
|
|
289
|
-
clearOrderCustomer(
|
|
301
|
+
clearOrderCustomer(options?: {
|
|
302
|
+
preserveContactInfo?: boolean;
|
|
303
|
+
}): void;
|
|
290
304
|
/**
|
|
291
305
|
* 重置 tempOrder,并同步清空下单客户运行态。
|
|
292
306
|
*
|
|
@@ -346,7 +360,7 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
346
360
|
* 获取当前的客户搜索条件
|
|
347
361
|
* @returns 当前搜索条件
|
|
348
362
|
*/
|
|
349
|
-
getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "
|
|
363
|
+
getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
|
|
350
364
|
/**
|
|
351
365
|
* 获取客户列表状态(包含滚动加载相关状态)
|
|
352
366
|
* @returns 客户状态
|
|
@@ -39,6 +39,9 @@ var _orderLineDisplay = require("../../modules/BookingContext/utils/orderLineDis
|
|
|
39
39
|
var _bookingStatus = require("./utils/bookingStatus");
|
|
40
40
|
var _resolveBestAddTimePlan = require("./utils/resolveBestAddTimePlan");
|
|
41
41
|
var _utils = require("../../modules/Order/utils");
|
|
42
|
+
var _contactInfo = require("../../modules/OpenData/contactInfo");
|
|
43
|
+
var _platform = require("../../utils/platform");
|
|
44
|
+
var _lodashEs = require("lodash-es");
|
|
42
45
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
43
46
|
const OPEN_DATA_SECTION_CODES = ['sale', 'reservation', 'fulfillment', 'menu', 'workflow', 'documents'];
|
|
44
47
|
const OPEN_DATA_CACHE_TTL = 5 * 60 * 1000;
|
|
@@ -162,6 +165,7 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
162
165
|
orderCustomerDiscountRefreshCustomerId = null;
|
|
163
166
|
orderCustomerDiscountRefreshTarget = null;
|
|
164
167
|
orderCustomerPromotionRefreshInFlight = null;
|
|
168
|
+
orderCustomerContactRequest = null;
|
|
165
169
|
customerResolveRequests = new Map();
|
|
166
170
|
loadOpenDataInFlight = null;
|
|
167
171
|
loadOpenDataInFlightTarget = null;
|
|
@@ -289,6 +293,9 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
289
293
|
async getOpenData() {
|
|
290
294
|
return this.loadOpenDataConfig();
|
|
291
295
|
}
|
|
296
|
+
async loadContactInfoOpenData() {
|
|
297
|
+
return this.store.openData ? this.getOpenData() : super.loadContactInfoOpenData();
|
|
298
|
+
}
|
|
292
299
|
async getInvoiceLayoutConfig() {
|
|
293
300
|
const openData = await this.loadOpenDataConfig();
|
|
294
301
|
if (!openData) return (0, _OpenData.getDefaultInvoiceLayoutConfig)();
|
|
@@ -1009,7 +1016,9 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1009
1016
|
*/
|
|
1010
1017
|
setOrderCustomer(customer, options) {
|
|
1011
1018
|
if (!customer) {
|
|
1012
|
-
this.clearOrderCustomer(
|
|
1019
|
+
this.clearOrderCustomer({
|
|
1020
|
+
preserveContactInfo: options?.source === 'detailHydrate'
|
|
1021
|
+
});
|
|
1013
1022
|
return;
|
|
1014
1023
|
}
|
|
1015
1024
|
const previousCustomerId = this.store.order.getOrderCustomer()?.customer_id;
|
|
@@ -1020,7 +1029,16 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1020
1029
|
this.discountConfigCacheKey = null;
|
|
1021
1030
|
this.hasManualDiscountSelection = false;
|
|
1022
1031
|
}
|
|
1032
|
+
if (!customerUnchanged && options?.source !== 'detailHydrate') {
|
|
1033
|
+
this.resetOrderCustomerContactInfo();
|
|
1034
|
+
this.store.order.updateTempOrderContactsInfo((0, _lodashEs.cloneDeep)((0, _contactInfo.extractCustomerContactInfo)(customer)));
|
|
1035
|
+
}
|
|
1023
1036
|
this.store.order.setOrderCustomer(patch, customer);
|
|
1037
|
+
if (options?.source === 'detailHydrate') {
|
|
1038
|
+
this.orderCustomerContactRequest = null;
|
|
1039
|
+
} else {
|
|
1040
|
+
this.prefillOrderCustomerContactInfo(customer, !customerUnchanged);
|
|
1041
|
+
}
|
|
1024
1042
|
const snapshotAfterOrderSet = this.store.order.getOrderCustomerSnapshot();
|
|
1025
1043
|
if (customerUnchanged) {
|
|
1026
1044
|
// hydrate 后 customer_id 已存在但 _extend.customerSnapshot 仍为空时,
|
|
@@ -1053,6 +1071,105 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1053
1071
|
this.orderCustomerPromotionRefreshInFlight = null;
|
|
1054
1072
|
});
|
|
1055
1073
|
}
|
|
1074
|
+
resetOrderCustomerContactInfo() {
|
|
1075
|
+
this.orderCustomerContactRequest = null;
|
|
1076
|
+
const order = this.getTempOrder();
|
|
1077
|
+
if (order?._extend) delete order._extend.contactInfoEdited;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/** Explicit edits, including clearing the contact, must win over a pending default. */
|
|
1081
|
+
updateTempOrderContactsInfo(contact) {
|
|
1082
|
+
const order = this.store.order.ensureTempOrder();
|
|
1083
|
+
order._extend || (order._extend = {});
|
|
1084
|
+
order._extend.contactInfoEdited = true;
|
|
1085
|
+
this.orderCustomerContactRequest = null;
|
|
1086
|
+
return super.updateTempOrderContactsInfo(contact);
|
|
1087
|
+
}
|
|
1088
|
+
getCustomerContactScope() {
|
|
1089
|
+
return JSON.stringify([this.getPickupTimingScope(), this.otherParams?.channel, this.otherParams?.shop_id, this.otherParams?.tenantId]);
|
|
1090
|
+
}
|
|
1091
|
+
getCustomerContactOrderKey() {
|
|
1092
|
+
const order = this.getTempOrder();
|
|
1093
|
+
// Cart mutations can replace the object without changing the order.
|
|
1094
|
+
const identity = order?.external_sale_number || order?.request_unique_idempotency_token || order?._extend?.localId || order?.order_id;
|
|
1095
|
+
return identity ? JSON.stringify([identity, order?.order_id]) : order;
|
|
1096
|
+
}
|
|
1097
|
+
async replaceTempOrder(...args) {
|
|
1098
|
+
this.orderCustomerContactRequest = null;
|
|
1099
|
+
return super.replaceTempOrder(...args);
|
|
1100
|
+
}
|
|
1101
|
+
async applyDefaultOrderCustomerContactInfo(contact) {
|
|
1102
|
+
this.store.order.updateTempOrderContactsInfo((0, _lodashEs.cloneDeep)(contact));
|
|
1103
|
+
// Metadata may finish after promotion/discount refresh; publish the updated cart explicitly.
|
|
1104
|
+
await this.effectsEmit('onTempOrderReplaced', {
|
|
1105
|
+
tempOrder: this.getTempOrder()
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
prefillOrderCustomerContactInfo(customer, customerChanged) {
|
|
1109
|
+
const order = this.getTempOrder();
|
|
1110
|
+
const customerId = Number(order?.customer_id);
|
|
1111
|
+
if (!order || !Number.isInteger(customerId) || customerId <= 1 || order.contacts_info != null || order._extend?.contactInfoEdited) return;
|
|
1112
|
+
// A restored new cart may already have its customer ID. Existing sales keep their snapshot,
|
|
1113
|
+
// even when it is empty; only an explicit customer switch replaces it.
|
|
1114
|
+
if (!customerChanged && order.order_id && !String(order.order_id).startsWith('local_order')) return;
|
|
1115
|
+
const contact = (0, _contactInfo.extractCustomerContactInfo)(customer);
|
|
1116
|
+
if (contact) {
|
|
1117
|
+
this.orderCustomerContactRequest = null;
|
|
1118
|
+
void this.applyDefaultOrderCustomerContactInfo(contact);
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
// A relation explicitly returned as null is authoritative. Only a missing relation needs fetching.
|
|
1122
|
+
if (customer.contacts_info !== undefined) return;
|
|
1123
|
+
const userPlatform = (0, _platform.isCustomerUserPlatform)(this.otherParams?.platform);
|
|
1124
|
+
const getAuthenticatedCustomerId = () => Number(this.core?.getPlugin?.('user')?.get?.()?.id);
|
|
1125
|
+
if (userPlatform && getAuthenticatedCustomerId() !== customerId) return;
|
|
1126
|
+
const scope = this.getCustomerContactScope();
|
|
1127
|
+
const orderKey = this.getCustomerContactOrderKey();
|
|
1128
|
+
const previous = this.orderCustomerContactRequest;
|
|
1129
|
+
if (previous?.orderKey === orderKey && previous.customerId === customerId && previous.scope === scope) return;
|
|
1130
|
+
const pending = {
|
|
1131
|
+
orderKey,
|
|
1132
|
+
customerId,
|
|
1133
|
+
scope,
|
|
1134
|
+
promise: Promise.resolve()
|
|
1135
|
+
};
|
|
1136
|
+
this.orderCustomerContactRequest = pending;
|
|
1137
|
+
const loadContact = async () => {
|
|
1138
|
+
if (userPlatform) {
|
|
1139
|
+
// Same endpoint as BookingByStep.getContactInfo; the PC/H5 host adds /h5 and login headers.
|
|
1140
|
+
const result = await this.request.get('/customer/metadata', {
|
|
1141
|
+
key: 'contacts_info'
|
|
1142
|
+
});
|
|
1143
|
+
if (result?.code !== 200 || result?.status === false) throw new Error('Customer contact lookup failed');
|
|
1144
|
+
return (0, _contactInfo.isContactRecord)(result.data) ? result.data : null;
|
|
1145
|
+
}
|
|
1146
|
+
// POS and physical Kiosk share the /shop request plugin and current-shop customer resolver.
|
|
1147
|
+
const resolved = await this.resolveCustomerById(customerId);
|
|
1148
|
+
return Number(resolved?.id) === customerId ? (0, _contactInfo.extractCustomerContactInfo)(resolved) : null;
|
|
1149
|
+
};
|
|
1150
|
+
pending.promise = loadContact().then(async value => {
|
|
1151
|
+
const current = this.getTempOrder();
|
|
1152
|
+
if (this.orderCustomerContactRequest !== pending || this.getCustomerContactOrderKey() !== orderKey || Number(current?.customer_id) !== customerId || scope !== this.getCustomerContactScope() || current?._extend?.contactInfoEdited || current?.contacts_info != null || userPlatform && getAuthenticatedCustomerId() !== customerId) return;
|
|
1153
|
+
if (value) await this.applyDefaultOrderCustomerContactInfo(value);
|
|
1154
|
+
}).catch(() => {
|
|
1155
|
+
// A failed default does not prevent manual entry; a subsequent sync can retry.
|
|
1156
|
+
if (this.orderCustomerContactRequest === pending) this.orderCustomerContactRequest = null;
|
|
1157
|
+
console.warn('[BookingTicket] Failed to load saved customer contact information');
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
waitForOrderCustomerContactInfo() {
|
|
1161
|
+
return this.orderCustomerContactRequest?.promise ?? Promise.resolve();
|
|
1162
|
+
}
|
|
1163
|
+
async validateContactInfo() {
|
|
1164
|
+
const orderKey = this.getCustomerContactOrderKey();
|
|
1165
|
+
const customerId = this.getTempOrder()?.customer_id;
|
|
1166
|
+
const scope = this.getCustomerContactScope();
|
|
1167
|
+
await this.waitForOrderCustomerContactInfo();
|
|
1168
|
+
if (this.getCustomerContactOrderKey() !== orderKey || this.getTempOrder()?.customer_id !== customerId || this.getCustomerContactScope() !== scope) {
|
|
1169
|
+
throw new Error('The order changed. Please try again.');
|
|
1170
|
+
}
|
|
1171
|
+
await super.validateContactInfo();
|
|
1172
|
+
}
|
|
1056
1173
|
|
|
1057
1174
|
/**
|
|
1058
1175
|
* 读取 tempOrder 上的下单客户协议字段;customer_id 缺失时返回 null。
|
|
@@ -1071,7 +1188,10 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1071
1188
|
/**
|
|
1072
1189
|
* 清空下单客户(协议字段 + snapshot)。
|
|
1073
1190
|
*/
|
|
1074
|
-
clearOrderCustomer() {
|
|
1191
|
+
clearOrderCustomer(options) {
|
|
1192
|
+
this.orderCustomerContactRequest = null;
|
|
1193
|
+
if (!options?.preserveContactInfo) this.resetOrderCustomerContactInfo();
|
|
1194
|
+
if (!options?.preserveContactInfo) this.store.order.updateTempOrderContactsInfo(null);
|
|
1075
1195
|
this.store.order.clearOrderCustomer();
|
|
1076
1196
|
this.discountConfigCacheKey = null;
|
|
1077
1197
|
this.hasManualDiscountSelection = false;
|
|
@@ -1891,6 +2011,11 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1891
2011
|
async setOtherParams(params, {
|
|
1892
2012
|
cover = false
|
|
1893
2013
|
} = {}) {
|
|
2014
|
+
const nextParams = cover ? params : {
|
|
2015
|
+
...this.otherParams,
|
|
2016
|
+
...params
|
|
2017
|
+
};
|
|
2018
|
+
if (['businessCode', 'business_code', 'channel', 'platform', 'shop_id', 'tenantId', 'cacheId'].some(key => nextParams?.[key] !== this.otherParams?.[key])) this.orderCustomerContactRequest = null;
|
|
1894
2019
|
await super.setOtherParams(params, {
|
|
1895
2020
|
cover
|
|
1896
2021
|
});
|
|
@@ -2006,6 +2131,7 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
2006
2131
|
* 再清理 BookingTicket 自有资源(scan 监听 + scan 内存缓存)。
|
|
2007
2132
|
*/
|
|
2008
2133
|
async destroy() {
|
|
2134
|
+
this.orderCustomerContactRequest = null;
|
|
2009
2135
|
await super.destroy();
|
|
2010
2136
|
try {
|
|
2011
2137
|
this.scan?.removeAllListeners();
|
|
@@ -1718,7 +1718,7 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
1718
1718
|
}
|
|
1719
1719
|
this.store.customer?.setSelectedCustomer?.(authenticatedCustomer);
|
|
1720
1720
|
this.setOrderCustomer(authenticatedCustomer);
|
|
1721
|
-
await Promise.all([this.refreshDiscountConfigAfterOrderCustomerChange(authenticatedCustomerId), this.waitForOrderCustomerPromotionRefresh()]);
|
|
1721
|
+
await Promise.all([this.refreshDiscountConfigAfterOrderCustomerChange(authenticatedCustomerId), this.waitForOrderCustomerPromotionRefresh(), this.waitForOrderCustomerContactInfo()]);
|
|
1722
1722
|
return true;
|
|
1723
1723
|
}
|
|
1724
1724
|
isAuthenticatedCustomerUserPlatform() {
|