@pisell/pisellos 0.10.64 → 0.10.66
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/model/strategy/adapter/promotion/index.js +0 -9
- 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/modules/Order/index.d.ts +6 -1
- package/dist/modules/Order/index.js +564 -475
- package/dist/modules/Order/types.d.ts +2 -0
- package/dist/modules/Order/utils/discountProductLineIdentity.js +2 -2
- package/dist/modules/Order/utils/productVoucherGroups.d.ts +15 -0
- package/dist/modules/Order/utils/productVoucherGroups.js +115 -0
- package/dist/modules/Product/index.d.ts +1 -1
- package/dist/modules/Rules/index.d.ts +2 -0
- package/dist/modules/Rules/index.js +29 -21
- package/dist/solution/BaseSales/index.d.ts +15 -1
- package/dist/solution/BaseSales/index.js +1018 -884
- package/dist/solution/BookingByStep/index.d.ts +2 -2
- package/dist/solution/BookingTicket/index.d.ts +4 -1
- package/dist/solution/BookingTicket/index.js +485 -433
- package/dist/solution/BookingTicket/types.d.ts +2 -0
- 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/modules/Order/index.d.ts +6 -1
- package/lib/modules/Order/index.js +49 -6
- package/lib/modules/Order/types.d.ts +2 -0
- package/lib/modules/Order/utils/discountProductLineIdentity.js +1 -1
- package/lib/modules/Order/utils/productVoucherGroups.d.ts +15 -0
- package/lib/modules/Order/utils/productVoucherGroups.js +64 -0
- package/lib/modules/Product/index.d.ts +1 -1
- package/lib/modules/Rules/index.d.ts +2 -0
- package/lib/modules/Rules/index.js +6 -0
- package/lib/solution/BaseSales/index.d.ts +15 -1
- package/lib/solution/BaseSales/index.js +40 -2
- package/lib/solution/BookingByStep/index.d.ts +2 -2
- package/lib/solution/BookingTicket/index.d.ts +4 -1
- package/lib/solution/BookingTicket/index.js +27 -2
- package/lib/solution/BookingTicket/types.d.ts +2 -0
- package/package.json +1 -1
|
@@ -208,6 +208,8 @@ export interface AddAddTimeProductsToBookingInput {
|
|
|
208
208
|
}
|
|
209
209
|
/** 扫码宿主桥接:SalesSdk 在 pubsub 订阅回调里注入,OS 需要 UI 时直接调用 */
|
|
210
210
|
export interface GlobalScanHostBridge {
|
|
211
|
+
/** Restrict redemption scans before customer/product side effects. */
|
|
212
|
+
scanScope?: 'all' | 'product-voucher';
|
|
211
213
|
openProductDetail?: (payload: Record<string, any>) => Promise<any | null>;
|
|
212
214
|
openBookingEdit?: (payload: Record<string, any>) => Promise<any | null>;
|
|
213
215
|
confirmDetail?: (input: {
|
|
@@ -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;
|
|
@@ -4,6 +4,7 @@ import { OrderModuleAPI, CommitOrderParams, UpdateOrderBookingParams, UpdateOrde
|
|
|
4
4
|
import { CartItem } from '../Cart/types';
|
|
5
5
|
import { type SubmitPayloadEnhancer } from './utils';
|
|
6
6
|
import { type PickupTimingBooking, type PickupTimingBookingInput } from './pickupTiming';
|
|
7
|
+
import { type ProductVoucherGroup } from './utils/productVoucherGroups';
|
|
7
8
|
import type { OrderAmountSnapshot, AddProductBookingInput, AddProductToOrderOptions, OrderIdentitySnapshot, OrderPaymentSource, OrderSnapshot, OrderSyncState, SyncPaymentsToOrderParams, SyncPaymentsToOrderResult, SubmitSalesOrderParams, OrderProduct, OrderProductIdentity, OrderSummary, OrderTempOrder, OrderPaymentData, OrderPaymentUpdateOptions, OrderPaymentUpdateResult, SendCustomerPayLinkParams, UpdateTempOrderCustomerInput, OrderCustomerPatch, OrderCustomerView, OrderHooksConfig, OrderCartIntentCommitContext, ReplaceTempOrderOptions, OrderPromotionEvaluator, OrderGiftSelectResolver, OrderUnfulfilledPromotion, OrderLastGiftActions, RemoveProductsFromOrderOptions, SalesNote, SalesNotesMutationSnapshot, SetSalesNoteInput, PickupReferenceMode, PointCardSnapshotMergeContext, PointCardSnapshotItem, ShopServiceData, ShopServiceType } from './types';
|
|
8
9
|
import type { ICustomer } from '../AccountList/types';
|
|
9
10
|
import type { Discount } from '../Discount/types';
|
|
@@ -94,6 +95,8 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
94
95
|
*/
|
|
95
96
|
private logError;
|
|
96
97
|
private registerDiscountModules;
|
|
98
|
+
setProductVoucherGroups(groups: ProductVoucherGroup[]): void;
|
|
99
|
+
validateProductVoucherSelection(): void;
|
|
97
100
|
private createDefaultRulesHooks;
|
|
98
101
|
/**
|
|
99
102
|
* Stable across notifyTempOrderChanged's immutable copy, but changes when a
|
|
@@ -112,7 +115,9 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
112
115
|
getCustomerWalletPrepareConfigEpoch(): number;
|
|
113
116
|
private syncScannedDiscountsToOriginalDiscountList;
|
|
114
117
|
private ensureEditDiscountConfigForProductChange;
|
|
115
|
-
scanCode(code: string, customerId?: number
|
|
118
|
+
scanCode(code: string, customerId?: number, options?: {
|
|
119
|
+
productVouchersOnly?: boolean;
|
|
120
|
+
}): Promise<{
|
|
116
121
|
isAvailable: boolean;
|
|
117
122
|
isAccepted?: boolean;
|
|
118
123
|
discountList: Discount[];
|
|
@@ -20,6 +20,7 @@ var _weighedProduct = require("./utils/weighedProduct");
|
|
|
20
20
|
var _pickupTiming = require("./pickupTiming");
|
|
21
21
|
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
22
22
|
var _availability = require("../Discount/availability");
|
|
23
|
+
var _productVoucherGroups = require("./utils/productVoucherGroups");
|
|
23
24
|
var _osPrivatePayload = require("../../utils/os-private-payload");
|
|
24
25
|
var _salesNotes = require("./salesNotes");
|
|
25
26
|
var _paymentUtils = require("./payment-utils");
|
|
@@ -148,7 +149,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
148
149
|
orderHooks;
|
|
149
150
|
cartCommandQueue = Promise.resolve();
|
|
150
151
|
queueCartCommand(run) {
|
|
151
|
-
if (!this.orderHooks?.onBeforeCartCommit) return run();
|
|
152
|
+
if (!this.orderHooks?.onBeforeCartCommit && !this.store.tempOrder?._extend?.productVoucherGroups?.length) return run();
|
|
152
153
|
const pending = this.cartCommandQueue.then(run, run);
|
|
153
154
|
this.cartCommandQueue = pending.catch(() => undefined);
|
|
154
155
|
return pending;
|
|
@@ -158,7 +159,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
158
159
|
beginCartMutation() {
|
|
159
160
|
const current = this.ensureTempOrder();
|
|
160
161
|
const guard = this.orderHooks?.onBeforeCartCommit;
|
|
161
|
-
if (!guard) return {
|
|
162
|
+
if (!guard && !current._extend?.productVoucherGroups?.length) return {
|
|
162
163
|
current,
|
|
163
164
|
draft: current,
|
|
164
165
|
commit: undefined
|
|
@@ -169,7 +170,8 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
169
170
|
current,
|
|
170
171
|
draft,
|
|
171
172
|
commit: async beforeCommit => {
|
|
172
|
-
|
|
173
|
+
(0, _productVoucherGroups.validateProductVoucherGroups)(draft, this.getDiscountList(), before);
|
|
174
|
+
await guard?.({
|
|
173
175
|
currentItems: (0, _lodashEs.cloneDeep)(before.products),
|
|
174
176
|
proposedItems: (0, _lodashEs.cloneDeep)(draft.products)
|
|
175
177
|
});
|
|
@@ -190,6 +192,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
190
192
|
async prepareCheckout(prepare) {
|
|
191
193
|
const run = async () => {
|
|
192
194
|
const order = this.ensureTempOrder();
|
|
195
|
+
(0, _productVoucherGroups.validateProductVoucherGroups)(order, this.getDiscountList(), undefined, true);
|
|
193
196
|
const items = (0, _lodashEs.cloneDeep)(order.products);
|
|
194
197
|
const result = await prepare(items);
|
|
195
198
|
if (this.store.tempOrder !== order || !(0, _lodashEs.isEqual)(order.products, items)) {
|
|
@@ -197,6 +200,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
197
200
|
code: 'SALES_GUARD_REJECTED'
|
|
198
201
|
});
|
|
199
202
|
}
|
|
203
|
+
(0, _productVoucherGroups.validateProductVoucherGroups)(order, this.getDiscountList(), undefined, true);
|
|
200
204
|
return result;
|
|
201
205
|
};
|
|
202
206
|
const pending = this.cartCommandQueue.then(run, run);
|
|
@@ -601,6 +605,26 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
601
605
|
hooks: this.rulesHooksOverride || this.createDefaultRulesHooks()
|
|
602
606
|
});
|
|
603
607
|
this.store.rules = rules;
|
|
608
|
+
rules.setDiscountScope((product, discount) => (0, _productVoucherGroups.allowsProductVoucher)(this.store.tempOrder, product, discount));
|
|
609
|
+
}
|
|
610
|
+
setProductVoucherGroups(groups) {
|
|
611
|
+
const order = this.ensureTempOrder();
|
|
612
|
+
const ids = new Set();
|
|
613
|
+
const keys = new Set();
|
|
614
|
+
const known = new Map(this.getDiscountList().map(discount => [String(discount.id), discount]));
|
|
615
|
+
for (const group of groups) {
|
|
616
|
+
if (!group.key || keys.has(group.key)) throw (0, _productVoucherGroups.voucherGroupError)();
|
|
617
|
+
keys.add(group.key);
|
|
618
|
+
for (const id of group.discountIds) {
|
|
619
|
+
const discount = known.get(String(id));
|
|
620
|
+
if (!discount || (discount.tag || discount.type) !== 'good_pass' || ids.has(String(id))) throw (0, _productVoucherGroups.voucherGroupError)();
|
|
621
|
+
ids.add(String(id));
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
this.ensureExtend(order).productVoucherGroups = (0, _lodashEs.cloneDeep)(groups);
|
|
625
|
+
}
|
|
626
|
+
validateProductVoucherSelection() {
|
|
627
|
+
(0, _productVoucherGroups.validateProductVoucherGroups)(this.ensureTempOrder(), this.getDiscountList(), undefined, true);
|
|
604
628
|
}
|
|
605
629
|
createDefaultRulesHooks() {
|
|
606
630
|
return (0, _utils.createDefaultOrderRulesHooks)(() => this.store.tempOrder?.bookings || [], () => this.store.tempOrder?.holder_relations);
|
|
@@ -752,7 +776,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
752
776
|
apply: false
|
|
753
777
|
});
|
|
754
778
|
}
|
|
755
|
-
async scanCode(code, customerId) {
|
|
779
|
+
async scanCode(code, customerId, options) {
|
|
756
780
|
const beforeCommit = this.createCartIntentCommitGuard('scan_promotion');
|
|
757
781
|
if (this.isActiveVirtualSettlementOrder(this.store.tempOrder)) {
|
|
758
782
|
return {
|
|
@@ -764,16 +788,22 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
764
788
|
unavailableReason: _types2.UnavailableReason.ProductNotApplicable
|
|
765
789
|
};
|
|
766
790
|
}
|
|
791
|
+
const scanOrderKey = this.getDiscountConfigOrderIdentityKey(this.store.tempOrder);
|
|
767
792
|
const resultDiscountWithReason = (await this.store.discount?.batchSearch(code, {
|
|
768
793
|
customerId
|
|
769
794
|
})) || {
|
|
770
795
|
discountList: [],
|
|
771
796
|
unavailableReasonKey: null
|
|
772
797
|
};
|
|
798
|
+
if (scanOrderKey !== this.getDiscountConfigOrderIdentityKey(this.store.tempOrder)) {
|
|
799
|
+
throw Object.assign(new Error('The scan session ended.'), {
|
|
800
|
+
code: 'SALES_GUARD_REJECTED'
|
|
801
|
+
});
|
|
802
|
+
}
|
|
773
803
|
const {
|
|
774
|
-
discountList: resultDiscountList,
|
|
775
804
|
unavailableReasonKey
|
|
776
805
|
} = resultDiscountWithReason;
|
|
806
|
+
const resultDiscountList = resultDiscountWithReason.discountList.filter(discount => !options?.productVouchersOnly || (discount.tag || discount.type) === 'good_pass');
|
|
777
807
|
const rulesModule = this.store.rules;
|
|
778
808
|
if (!rulesModule) {
|
|
779
809
|
return {
|
|
@@ -800,7 +830,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
800
830
|
isScan: true
|
|
801
831
|
}));
|
|
802
832
|
const currentSelected = this.getDiscountList().filter(n => n.isSelected);
|
|
803
|
-
if (currentSelected.length && currentSelected.some(n => withScanList.some(m => m.id === n.id))) {
|
|
833
|
+
if (currentSelected.length && (options?.productVouchersOnly ? withScanList.every(scanned => currentSelected.some(selected => selected.id === scanned.id)) : currentSelected.some(n => withScanList.some(m => m.id === n.id)))) {
|
|
804
834
|
beforeCommit();
|
|
805
835
|
await this.syncScannedDiscountsToOriginalDiscountList(this.getDiscountList());
|
|
806
836
|
return {
|
|
@@ -2307,8 +2337,12 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
2307
2337
|
...existed,
|
|
2308
2338
|
...(origin !== undefined ? {
|
|
2309
2339
|
origin
|
|
2340
|
+
} : {}),
|
|
2341
|
+
...(rawProduct._productVoucherGroupKey ? {
|
|
2342
|
+
productVoucherGroupKey: rawProduct._productVoucherGroupKey
|
|
2310
2343
|
} : {})
|
|
2311
2344
|
};
|
|
2345
|
+
delete normalizedProduct._productVoucherGroupKey;
|
|
2312
2346
|
delete normalizedProduct._origin;
|
|
2313
2347
|
delete normalizedProduct.unique_identification_number;
|
|
2314
2348
|
}
|
|
@@ -3602,6 +3636,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3602
3636
|
const isIncomingCustomItem = productToAdd.metadata?.origin?.isCustomItem === true || productToAdd._origin?.isCustomItem === true;
|
|
3603
3637
|
const shouldForceNewLine = disableMerge || !!booking || hasIncomingGoodPass || isIncomingPersistedEntitlementPass || hasIncomingBookingUid || hasIncomingHolder || isIncomingCustomItem || isWeighingProduct;
|
|
3604
3638
|
const matchedIndex = shouldForceNewLine ? -1 : tempOrder.products.findIndex(item => {
|
|
3639
|
+
if ((0, _productVoucherGroups.productVoucherGroupKey)(tempOrder, item) !== productToAdd._productVoucherGroupKey) return false;
|
|
3605
3640
|
if (this.hasGoodPassDiscount(item)) return false;
|
|
3606
3641
|
if ((0, _holderRelations.getProductHolderRelations)(tempOrder, item).length > 0 || (0, _utils.hasAssignedHolderId)(item.metadata?.holder_id)) return false;
|
|
3607
3642
|
const isRuntimeEntitlementPass = this.isRuntimeEntitlementAppend(item, tempOrder);
|
|
@@ -3791,6 +3826,13 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
3791
3826
|
const mutation = this.beginCartMutation();
|
|
3792
3827
|
let tempOrder = mutation.draft;
|
|
3793
3828
|
const beforeCommit = this.createCartIntentCommitGuard('add_product', mutation.current);
|
|
3829
|
+
if (options?.productVoucherGroupKey) {
|
|
3830
|
+
if (!tempOrder._extend?.productVoucherGroups?.some(group => group.key === options.productVoucherGroupKey)) throw (0, _productVoucherGroups.voucherGroupError)();
|
|
3831
|
+
product = {
|
|
3832
|
+
...product,
|
|
3833
|
+
_productVoucherGroupKey: options.productVoucherGroupKey
|
|
3834
|
+
};
|
|
3835
|
+
}
|
|
3794
3836
|
const salesNoteDrafts = (options?.salesNotes || []).map(draft => ({
|
|
3795
3837
|
note_type: String(draft.note_type || '').trim(),
|
|
3796
3838
|
text: {
|
|
@@ -4734,6 +4776,7 @@ class OrderModule extends _BaseModule.BaseModule {
|
|
|
4734
4776
|
}
|
|
4735
4777
|
async runSubmitTempOrder(params) {
|
|
4736
4778
|
const tempOrder = this.ensureTempOrder();
|
|
4779
|
+
(0, _productVoucherGroups.validateProductVoucherGroups)(tempOrder, this.getDiscountList(), undefined, true);
|
|
4737
4780
|
this.prepareVirtualSettlementTempOrder(tempOrder);
|
|
4738
4781
|
const isOnlineStorePlatform = this.isOnlineStorePlatform();
|
|
4739
4782
|
const shouldConfirmPendingVoucherPayments = params?.confirmPendingVoucherPayments !== false;
|
|
@@ -456,6 +456,8 @@ export type AddProductBookingInput = Record<string, any>;
|
|
|
456
456
|
* 避免步进器修改数量改变该组展示位置。
|
|
457
457
|
*/
|
|
458
458
|
export interface AddProductToOrderOptions {
|
|
459
|
+
/** Associates a selection with an existing runtime voucher group. */
|
|
460
|
+
productVoucherGroupKey?: string;
|
|
459
461
|
insertAfterProductUid?: string;
|
|
460
462
|
/** 跳过同商品/规格匹配,始终新增购物车行。 */
|
|
461
463
|
disableMerge?: boolean;
|
|
@@ -241,7 +241,7 @@ function consolidateDiscountFreeProductLines(tempOrder) {
|
|
|
241
241
|
bundle_selling_price: String(bundle?.bundle_selling_price ?? '')
|
|
242
242
|
}))
|
|
243
243
|
});
|
|
244
|
-
const mergeKey = [discountSplitOriginUid, product.product_id, product.product_variant_id ?? 0, fingerprint, priceSignature].join('#');
|
|
244
|
+
const mergeKey = [discountSplitOriginUid, productUid ? tempOrder._extend?.productsByUid?.[productUid]?.productVoucherGroupKey ?? '' : '', product.product_id, product.product_variant_id ?? 0, fingerprint, priceSignature].join('#');
|
|
245
245
|
const matchedIndex = productIndexByKey.get(mergeKey);
|
|
246
246
|
|
|
247
247
|
// 当前拆分来源 + 商品结构组合第一次出现时,保留该行作为回并目标。
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Discount } from '../../Discount/types';
|
|
2
|
+
import type { OrderTempOrder } from '../types';
|
|
3
|
+
/** Runtime-only allocation constraints. Never copied into order/product metadata. */
|
|
4
|
+
export interface ProductVoucherGroup {
|
|
5
|
+
key: string;
|
|
6
|
+
discountIds: Array<number | string>;
|
|
7
|
+
}
|
|
8
|
+
export declare function productVoucherGroupKey(order: OrderTempOrder, product: any): string | undefined;
|
|
9
|
+
export declare function isUsableProductVoucher(discount: Discount): boolean;
|
|
10
|
+
export declare function voucherGroupError(): Error & {
|
|
11
|
+
code: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function allowsProductVoucher(order: OrderTempOrder | null | undefined, product: any, discount: Discount): boolean;
|
|
14
|
+
/** Reductions stay possible when a voucher expires; checkout always requires full coverage. */
|
|
15
|
+
export declare function validateProductVoucherGroups(order: OrderTempOrder, discounts: Discount[], previous?: OrderTempOrder, requireCoverage?: boolean): void;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.allowsProductVoucher = allowsProductVoucher;
|
|
7
|
+
exports.isUsableProductVoucher = isUsableProductVoucher;
|
|
8
|
+
exports.productVoucherGroupKey = productVoucherGroupKey;
|
|
9
|
+
exports.validateProductVoucherGroups = validateProductVoucherGroups;
|
|
10
|
+
exports.voucherGroupError = voucherGroupError;
|
|
11
|
+
var _availability = require("../../Discount/availability");
|
|
12
|
+
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
13
|
+
var _utils = require("../../../solution/ShopDiscount/utils");
|
|
14
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
+
/** Runtime-only allocation constraints. Never copied into order/product metadata. */
|
|
16
|
+
|
|
17
|
+
function productVoucherGroupKey(order, product) {
|
|
18
|
+
const uid = product?.metadata?.unique_identification_number ?? product?.unique_identification_number;
|
|
19
|
+
return order._extend?.productsByUid?.[String(uid)]?.productVoucherGroupKey;
|
|
20
|
+
}
|
|
21
|
+
function isUsableProductVoucher(discount) {
|
|
22
|
+
return (discount.tag || discount.type) === 'good_pass' && !discount.isEditMode && !discount.isDisabled && !(0, _availability.getDiscountUsageBlockReason)(discount) && !(0, _utils.getDiscountTimeUnavailableReason)(discount, (0, _dayjs.default)().format('YYYY-MM-DD HH:mm:ss'));
|
|
23
|
+
}
|
|
24
|
+
function voucherGroupError() {
|
|
25
|
+
return Object.assign(new Error('The selected items no longer match the available vouchers. Please adjust your selection.'), {
|
|
26
|
+
code: 'PRODUCT_VOUCHER_GROUP_INVALID'
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function allowsProductVoucher(order, product, discount) {
|
|
30
|
+
const groups = order?._extend?.productVoucherGroups;
|
|
31
|
+
if (!order || !groups?.length) return true;
|
|
32
|
+
const key = productVoucherGroupKey(order, product);
|
|
33
|
+
if (!key) return !groups.some(group => group.discountIds.some(id => String(id) === String(discount.id)));
|
|
34
|
+
return (discount.tag || discount.type) === 'good_pass' && Boolean(groups.find(group => group.key === key)?.discountIds.some(id => String(id) === String(discount.id)));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Reductions stay possible when a voucher expires; checkout always requires full coverage. */
|
|
38
|
+
function validateProductVoucherGroups(order, discounts, previous, requireCoverage = false) {
|
|
39
|
+
const groups = order._extend?.productVoucherGroups;
|
|
40
|
+
if (!groups?.length) return;
|
|
41
|
+
const usedIds = new Set();
|
|
42
|
+
const byId = new Map(discounts.filter(isUsableProductVoucher).map(discount => [String(discount.id), discount]));
|
|
43
|
+
const quantity = item => Number(item.num ?? item.product_quantity ?? 0);
|
|
44
|
+
for (const group of groups) {
|
|
45
|
+
const items = order.products.filter(item => productVoucherGroupKey(order, item) === group.key);
|
|
46
|
+
const selected = items.reduce((sum, item) => sum + quantity(item), 0);
|
|
47
|
+
const before = previous?.products.filter(item => productVoucherGroupKey(previous, item) === group.key).reduce((sum, item) => sum + quantity(item), 0) ?? 0;
|
|
48
|
+
if (!requireCoverage && previous && selected <= before) continue;
|
|
49
|
+
const ids = new Set(group.discountIds.map(String).filter(id => byId.has(id)));
|
|
50
|
+
if (!Number.isInteger(selected) || selected < 0 || selected > ids.size) throw voucherGroupError();
|
|
51
|
+
if (!requireCoverage) continue;
|
|
52
|
+
for (const item of items) {
|
|
53
|
+
const applied = (item.discount_list || []).filter(entry => (entry.type || entry.tag) === 'good_pass');
|
|
54
|
+
// Rules splits a good_pass allocation into one physical line per instance.
|
|
55
|
+
const id = String(applied[0]?.discount?.resource_id ?? '');
|
|
56
|
+
const voucher = byId.get(id);
|
|
57
|
+
if (quantity(item) !== 1 || applied.length !== 1 || !ids.has(id) || !voucher?.isAvailable || voucher.config?.isAvailable === false || usedIds.has(id)) throw voucherGroupError();
|
|
58
|
+
usedIds.add(id);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (order.products.some(item => !groups.some(group => group.key === productVoucherGroupKey(order, item)))) {
|
|
62
|
+
throw voucherGroupError();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -49,5 +49,5 @@ export declare class Product extends BaseModule implements Module {
|
|
|
49
49
|
getCategories(): ProductCategory[];
|
|
50
50
|
setOtherParams(key: string, value: any): void;
|
|
51
51
|
getOtherParams(): any;
|
|
52
|
-
getProductType(): "
|
|
52
|
+
getProductType(): "normal" | "duration" | "session";
|
|
53
53
|
}
|
|
@@ -9,6 +9,8 @@ export declare class RulesModule extends BaseModule implements Module, RulesModu
|
|
|
9
9
|
private store;
|
|
10
10
|
window: WindowPlugin;
|
|
11
11
|
private hooks;
|
|
12
|
+
private discountScope?;
|
|
13
|
+
setDiscountScope(scope: (product: any, discount: Discount) => boolean): void;
|
|
12
14
|
constructor(name?: string, version?: string);
|
|
13
15
|
initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
|
|
14
16
|
setRulesList(rulesList: Rules[]): Promise<void>;
|
|
@@ -20,6 +20,10 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
20
20
|
store;
|
|
21
21
|
window; // WindowPlugin 实例
|
|
22
22
|
hooks;
|
|
23
|
+
discountScope;
|
|
24
|
+
setDiscountScope(scope) {
|
|
25
|
+
this.discountScope = scope;
|
|
26
|
+
}
|
|
23
27
|
constructor(name, version) {
|
|
24
28
|
super(name, version);
|
|
25
29
|
this.hooks = {};
|
|
@@ -722,6 +726,7 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
722
726
|
|
|
723
727
|
// 辅助函数:检查商品是否对某个折扣卡可用
|
|
724
728
|
const checkItemApplicableForDiscount = (flatItem, discount) => {
|
|
729
|
+
if (this.discountScope && !this.discountScope(flatItem.originProduct, discount)) return false;
|
|
725
730
|
if (discount.config?.isAvailable === false) return false;
|
|
726
731
|
let product;
|
|
727
732
|
if (flatItem.type === 'main') {
|
|
@@ -1093,6 +1098,7 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
1093
1098
|
|
|
1094
1099
|
// 找到适用于此商品的所有优惠券,仅考虑isSelected不为false的优惠券
|
|
1095
1100
|
const applicableDiscounts = sortedDiscountList.filter(discount => {
|
|
1101
|
+
if (this.discountScope && !this.discountScope(originProduct, discount)) return false;
|
|
1096
1102
|
if (this.isItemRewardProductDiscount(product)) return false;
|
|
1097
1103
|
// 🔥 检查策略可用性(针对全额抵扣卡和折扣卡)
|
|
1098
1104
|
const discountType = discount.tag || discount.type;
|
|
@@ -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 草稿。 */
|
|
@@ -338,7 +343,9 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
|
|
|
338
343
|
discountList: Discount[];
|
|
339
344
|
originalDiscountList?: Discount[];
|
|
340
345
|
}): Promise<void>;
|
|
341
|
-
scanPromotionCode(code: string, customerId?: number
|
|
346
|
+
scanPromotionCode(code: string, customerId?: number, options?: {
|
|
347
|
+
productVouchersOnly?: boolean;
|
|
348
|
+
}): Promise<BaseSalesScanCodeResult>;
|
|
342
349
|
setDiscountSelected(params: {
|
|
343
350
|
discountId: number;
|
|
344
351
|
isSelected: boolean;
|
|
@@ -357,6 +364,8 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
|
|
|
357
364
|
* payments 会交给 OrderModule 统一映射为 Sales 协议支付项。
|
|
358
365
|
*/
|
|
359
366
|
submitSalesOrder<T = any>(params?: BaseSalesSubmitSalesOrderParams): Promise<T>;
|
|
367
|
+
/** Validate required contact fields before a new payment is initiated. */
|
|
368
|
+
ensureSalesOrderReadyForPayment(): Promise<void>;
|
|
360
369
|
saveSalesOrderDraft<T = any>(params?: {
|
|
361
370
|
platform?: string;
|
|
362
371
|
businessCode?: string;
|
|
@@ -519,6 +528,11 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
|
|
|
519
528
|
private hasSmartPricingStrategies;
|
|
520
529
|
shouldRecalculateProductBookingPricesForContextChange(params: BaseSalesPriceRecalculationContextChange): boolean;
|
|
521
530
|
calculateProductBookingPrices(paramsList: BaseSalesCalculateProductBookingPriceParams[]): Promise<BaseSalesProductBookingPriceResult[]>;
|
|
531
|
+
setProductVoucherGroups(groups: Array<{
|
|
532
|
+
key: string;
|
|
533
|
+
discountIds: Array<number | string>;
|
|
534
|
+
}>): void;
|
|
535
|
+
validateProductVoucherSelection(): void;
|
|
522
536
|
addProductToOrder(product: Partial<BaseSalesOrderProduct> & BaseSalesOrderProductIdentity, booking?: AddProductBookingInput, options?: AddProductToOrderOptions): Promise<import("../../modules/Order/types").OrderProduct[]>;
|
|
523
537
|
updateOrderProduct(params: UpdateOrderProductParams): Promise<import("../../modules/Order/types").OrderProduct[]>;
|
|
524
538
|
updateOrderProducts(paramsList: UpdateOrderProductParams[]): Promise<import("../../modules/Order/types").OrderProduct[]>;
|
|
@@ -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) {
|
|
@@ -2412,10 +2434,10 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2412
2434
|
}
|
|
2413
2435
|
await discountModule.setDiscountList(params.discountList);
|
|
2414
2436
|
}
|
|
2415
|
-
async scanPromotionCode(code, customerId) {
|
|
2437
|
+
async scanPromotionCode(code, customerId, options) {
|
|
2416
2438
|
try {
|
|
2417
2439
|
if (!this.store.order) throw new Error('order 模块未初始化');
|
|
2418
|
-
const raw = await this.store.order.scanCode(code, customerId);
|
|
2440
|
+
const raw = await this.store.order.scanCode(code, customerId, options);
|
|
2419
2441
|
if (raw.isAvailable) {
|
|
2420
2442
|
await this.store.order.recalculateSummary({
|
|
2421
2443
|
createIfMissing: true
|
|
@@ -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');
|
|
@@ -4112,6 +4143,13 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
4112
4143
|
});
|
|
4113
4144
|
});
|
|
4114
4145
|
}
|
|
4146
|
+
setProductVoucherGroups(groups) {
|
|
4147
|
+
if (!this.store.order) throw new Error('order 模块未初始化');
|
|
4148
|
+
this.store.order.setProductVoucherGroups(groups);
|
|
4149
|
+
}
|
|
4150
|
+
validateProductVoucherSelection() {
|
|
4151
|
+
this.store.order?.validateProductVoucherSelection();
|
|
4152
|
+
}
|
|
4115
4153
|
async addProductToOrder(product, booking, options) {
|
|
4116
4154
|
try {
|
|
4117
4155
|
if (!this.store.order) throw new Error('order 模块未初始化');
|
|
@@ -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 | 2 | 1 |
|
|
329
|
+
weekNum: 0 | 2 | 1 | 6 | 4 | 5 | 3;
|
|
330
330
|
}[]>;
|
|
331
331
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
332
332
|
private getScheduleDataByIds;
|
|
@@ -373,7 +373,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
373
373
|
};
|
|
374
374
|
setOtherData(key: string, value: any): void;
|
|
375
375
|
getOtherData(key: string): any;
|
|
376
|
-
getProductTypeById(id: number): Promise<"
|
|
376
|
+
getProductTypeById(id: number): Promise<"normal" | "duration" | "session">;
|
|
377
377
|
/**
|
|
378
378
|
* 提供给 UI 的方法,减轻 UI 层的计算压力,UI 层只需要传递 cartItemId 和 resourceCode 即返回对应的 renderList
|
|
379
379
|
*
|