@pisell/pisellos 2.2.315 → 2.2.316
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/evaluator.js +89 -104
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +3 -1
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +28 -4
- package/dist/modules/Holder/utils.d.ts +2 -0
- package/dist/modules/Holder/utils.js +32 -2
- 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/holderRelations.d.ts +14 -0
- package/dist/modules/Order/holderRelations.js +154 -0
- package/dist/modules/Order/index.d.ts +2 -2
- package/dist/modules/Order/index.js +89 -44
- package/dist/modules/Order/types.d.ts +19 -1
- package/dist/modules/Order/types.js +2 -0
- package/dist/modules/Order/utils/discountProductLineIdentity.d.ts +1 -1
- package/dist/modules/Order/utils/discountProductLineIdentity.js +142 -7
- package/dist/modules/Order/utils/orderCollectionIdentity.js +24 -10
- package/dist/modules/Order/utils.d.ts +2 -1
- package/dist/modules/Order/utils.js +36 -10
- package/dist/modules/Rules/index.js +62 -61
- package/dist/server/index.js +36 -28
- package/dist/server/modules/order/index.d.ts +4 -0
- package/dist/server/modules/order/index.js +255 -190
- package/dist/server/modules/order/types.d.ts +3 -0
- package/dist/server/modules/products/index.d.ts +28 -7
- package/dist/server/modules/products/index.js +1297 -770
- package/dist/server/modules/products/replication.d.ts +8 -0
- package/dist/server/modules/products/replication.js +69 -0
- package/dist/server/modules/products/types.d.ts +12 -1
- package/dist/server/modules/products/types.js +4 -0
- package/dist/shared/receipt/small-ticket.js +24 -12
- package/dist/shared/receipt/templateVisibility.d.ts +3 -0
- package/dist/shared/receipt/templateVisibility.js +6 -2
- package/dist/shared/sales/loadSalesOrderSnapshot.js +1 -1
- package/dist/solution/BaseSales/index.d.ts +4 -0
- package/dist/solution/BaseSales/index.js +1116 -935
- package/dist/solution/BaseSales/types.d.ts +2 -1
- package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -1
- package/dist/solution/BaseSales/utils/parseSalesResponse.js +7 -3
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +14 -4
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.d.ts +15 -1
- package/dist/solution/BookingTicket/index.js +784 -501
- package/dist/solution/ScanOrder/index.js +65 -31
- package/dist/solution/ScanOrder/types.d.ts +3 -0
- package/dist/solution/UnifiedBookingSales/index.js +1 -1
- package/lib/model/strategy/adapter/promotion/evaluator.js +9 -14
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +3 -1
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +17 -1
- package/lib/modules/Holder/utils.d.ts +2 -0
- package/lib/modules/Holder/utils.js +27 -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/modules/Order/holderRelations.d.ts +14 -0
- package/lib/modules/Order/holderRelations.js +98 -0
- package/lib/modules/Order/index.d.ts +2 -2
- package/lib/modules/Order/index.js +44 -12
- package/lib/modules/Order/types.d.ts +19 -1
- package/lib/modules/Order/types.js +1 -0
- package/lib/modules/Order/utils/discountProductLineIdentity.d.ts +1 -1
- package/lib/modules/Order/utils/discountProductLineIdentity.js +51 -2
- package/lib/modules/Order/utils/orderCollectionIdentity.js +5 -0
- package/lib/modules/Order/utils.d.ts +2 -1
- package/lib/modules/Order/utils.js +28 -6
- package/lib/modules/Rules/index.js +2 -1
- package/lib/server/index.js +8 -2
- package/lib/server/modules/order/index.d.ts +4 -0
- package/lib/server/modules/order/index.js +46 -4
- package/lib/server/modules/order/types.d.ts +3 -0
- package/lib/server/modules/products/index.d.ts +28 -7
- package/lib/server/modules/products/index.js +226 -63
- package/lib/server/modules/products/replication.d.ts +8 -0
- package/lib/server/modules/products/replication.js +30 -0
- package/lib/server/modules/products/types.d.ts +12 -1
- package/lib/server/modules/products/types.js +3 -0
- package/lib/shared/receipt/small-ticket.js +22 -13
- package/lib/shared/receipt/templateVisibility.d.ts +3 -0
- package/lib/shared/receipt/templateVisibility.js +6 -2
- package/lib/shared/sales/loadSalesOrderSnapshot.js +1 -1
- package/lib/solution/BaseSales/index.d.ts +4 -0
- package/lib/solution/BaseSales/index.js +68 -2
- package/lib/solution/BaseSales/types.d.ts +2 -1
- package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -1
- package/lib/solution/BaseSales/utils/parseSalesResponse.js +4 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +16 -8
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.d.ts +15 -1
- package/lib/solution/BookingTicket/index.js +132 -3
- package/lib/solution/ScanOrder/index.js +59 -20
- package/lib/solution/ScanOrder/types.d.ts +3 -0
- package/lib/solution/UnifiedBookingSales/index.js +1 -1
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ exports.buildSmallTicketData = buildSmallTicketData;
|
|
|
7
7
|
exports.hasSmallTicketData = hasSmallTicketData;
|
|
8
8
|
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
9
9
|
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
10
|
+
var _holderRelations = require("../../modules/Order/holderRelations");
|
|
10
11
|
var _pickupTiming = require("../../modules/Order/pickupTiming");
|
|
11
12
|
var _templateVisibility = require("./templateVisibility");
|
|
12
13
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -97,6 +98,7 @@ const LABELS = {
|
|
|
97
98
|
bookingDateTime: 'Date & time',
|
|
98
99
|
bookingResource: 'Resource',
|
|
99
100
|
bookingHolder: 'Holder',
|
|
101
|
+
productHolder: 'Holder',
|
|
100
102
|
orderDiscount: 'Order discount',
|
|
101
103
|
manualDiscount: 'Manual discount',
|
|
102
104
|
manualMarkup: 'Manual markup',
|
|
@@ -142,6 +144,7 @@ const LABELS = {
|
|
|
142
144
|
bookingDateTime: '日期和时间',
|
|
143
145
|
bookingResource: '资源',
|
|
144
146
|
bookingHolder: '预约人',
|
|
147
|
+
productHolder: '持有人',
|
|
145
148
|
orderDiscount: '整单折扣',
|
|
146
149
|
manualDiscount: '手动折扣',
|
|
147
150
|
manualMarkup: '手动加价',
|
|
@@ -187,6 +190,7 @@ const LABELS = {
|
|
|
187
190
|
bookingDateTime: '日期和時間',
|
|
188
191
|
bookingResource: '資源',
|
|
189
192
|
bookingHolder: '預約人',
|
|
193
|
+
productHolder: '持有人',
|
|
190
194
|
orderDiscount: '整單折扣',
|
|
191
195
|
manualDiscount: '手動折扣',
|
|
192
196
|
manualMarkup: '手動加價',
|
|
@@ -230,6 +234,7 @@ const LABELS = {
|
|
|
230
234
|
bookingDateTime: '日時',
|
|
231
235
|
bookingResource: 'リソース',
|
|
232
236
|
bookingHolder: '予約者',
|
|
237
|
+
productHolder: '利用者',
|
|
233
238
|
orderDiscount: '注文割引',
|
|
234
239
|
manualDiscount: '手動割引',
|
|
235
240
|
manualMarkup: '手動値上げ',
|
|
@@ -274,6 +279,7 @@ const LABELS = {
|
|
|
274
279
|
bookingDateTime: 'Data e hora',
|
|
275
280
|
bookingResource: 'Recurso',
|
|
276
281
|
bookingHolder: 'Titular',
|
|
282
|
+
productHolder: 'Titular',
|
|
277
283
|
orderDiscount: 'Desconto do pedido',
|
|
278
284
|
manualDiscount: 'Desconto manual',
|
|
279
285
|
manualMarkup: 'Acréscimo manual',
|
|
@@ -341,7 +347,7 @@ function buildLocaleData(params) {
|
|
|
341
347
|
financials
|
|
342
348
|
}),
|
|
343
349
|
customer: buildCustomer(order, locale, visibility.customer),
|
|
344
|
-
appointment: buildAppointment(order, locale),
|
|
350
|
+
appointment: buildAppointment(order, locale, visibility.lineGroups.resourceTitle),
|
|
345
351
|
...(delivery.length ? {
|
|
346
352
|
delivery
|
|
347
353
|
} : {}),
|
|
@@ -445,6 +451,15 @@ function buildProducts(order, locale, productMap) {
|
|
|
445
451
|
quantity,
|
|
446
452
|
originalPrice
|
|
447
453
|
});
|
|
454
|
+
const holderName = (0, _holderRelations.hasProductBooking)(product) ? '' : (0, _holderRelations.getProductHolderRelations)(order, product).map(relation => getLocalizedValue(relation.metadata?.name, locale).trim()).filter(Boolean).join(', ');
|
|
455
|
+
if (holderName) {
|
|
456
|
+
// 复用 Web Invoice / 原生均支持的文本扩展行,不混入金额或商品备注。
|
|
457
|
+
extensionList.push({
|
|
458
|
+
...receiptKey(rawProduct, 'product-holder'),
|
|
459
|
+
item: `${label(locale, 'productHolder')}: ${holderName}`,
|
|
460
|
+
value: ''
|
|
461
|
+
});
|
|
462
|
+
}
|
|
448
463
|
const productNote = resolveProductNote(product, locale);
|
|
449
464
|
const allergy = isPresentId(productUid) ? allergyByProductUid.get(String(productUid).trim()) || '' : '';
|
|
450
465
|
const basePrice = resolveProductBasePrice(product);
|
|
@@ -1165,13 +1180,6 @@ function buildFees(params) {
|
|
|
1165
1180
|
const isPriceIncludeTax = firstPresentValue(summary.is_price_include_tax, order.is_price_include_tax, shopInfo.is_price_include_tax);
|
|
1166
1181
|
const roundingAmount = firstPresentValue(summary.rounding_amount, order.rounding_amount);
|
|
1167
1182
|
const payProcessingFee = financials.payProcessingFee;
|
|
1168
|
-
if (isNonZero(payProcessingFee)) {
|
|
1169
|
-
fees.push({
|
|
1170
|
-
...receiptKey(order, 'payment-processing-fee'),
|
|
1171
|
-
item: label(locale, 'payProcessingFee'),
|
|
1172
|
-
value: formatMoney(payProcessingFee, currencySymbol)
|
|
1173
|
-
});
|
|
1174
|
-
}
|
|
1175
1183
|
const productExpectAmount = firstPresentValue(summary.product_expect_amount, summary.product_amount, order.product_expect_amount);
|
|
1176
1184
|
const productOriginalAmount = firstPresentValue(summary.product_original_amount, order.product_original_amount) ?? calculateProductOriginalAmount(order);
|
|
1177
1185
|
const totalSavingsAmount = new _decimal.default(toMoneyNumber(productOriginalAmount)).minus(toMoneyNumber(productExpectAmount)).plus(toMoneyNumber(discountAmount));
|
|
@@ -1322,7 +1330,7 @@ function formatPickupBookingStart(bookings, locale) {
|
|
|
1322
1330
|
const dateText = locale === 'en' ? `${day}/${month}/${year}` : `${year}/${month}/${day}`;
|
|
1323
1331
|
return `${time.slice(0, 5)} ${dateText}`;
|
|
1324
1332
|
}
|
|
1325
|
-
function buildAppointment(order, locale) {
|
|
1333
|
+
function buildAppointment(order, locale, showResourceTitle) {
|
|
1326
1334
|
const parentScheduleId = order.metadata?.appointment_booking_parent_schedule_id;
|
|
1327
1335
|
const bookings = sortBookings(normalizeArray(order.bookings).filter(booking => !(0, _pickupTiming.isFulfillmentTimingBooking)(booking) && isReceiptAppointmentBooking(booking, parentScheduleId)));
|
|
1328
1336
|
if (bookings.length) {
|
|
@@ -1331,7 +1339,8 @@ function buildAppointment(order, locale) {
|
|
|
1331
1339
|
booking,
|
|
1332
1340
|
allResources,
|
|
1333
1341
|
bookingCount: bookings.length,
|
|
1334
|
-
locale
|
|
1342
|
+
locale,
|
|
1343
|
+
showResourceTitle
|
|
1335
1344
|
}));
|
|
1336
1345
|
}
|
|
1337
1346
|
const tableName = order.table_name || order.metadata?.table_name || order.metadata?.table?.name || order.form_data?.table_name;
|
|
@@ -1370,7 +1379,7 @@ function buildAppointmentRows(params) {
|
|
|
1370
1379
|
};
|
|
1371
1380
|
const bookingId = stringify(firstPresentValue(booking.booking_id, booking.schedule_event_id, bookingUid)).trim();
|
|
1372
1381
|
const bookingTime = formatBookingRange(booking, locale);
|
|
1373
|
-
const resourceText = formatBookingResources(resolveBookingResourcesForReceipt(booking, allResources, bookingCount), locale);
|
|
1382
|
+
const resourceText = formatBookingResources(resolveBookingResourcesForReceipt(booking, allResources, bookingCount), locale, params.showResourceTitle);
|
|
1374
1383
|
const holder = getLocalizedValue(booking.holder?.name ?? booking.metadata?.holder?.name, locale).trim();
|
|
1375
1384
|
return [{
|
|
1376
1385
|
...receiptKey(booking, 'booking-id'),
|
|
@@ -1432,8 +1441,8 @@ function resolveBookingResourcesForReceipt(booking, allResources, bookingCount)
|
|
|
1432
1441
|
if (bookingCount === 1 && allResources.length) return allResources;
|
|
1433
1442
|
return [];
|
|
1434
1443
|
}
|
|
1435
|
-
function formatBookingResources(resources, locale) {
|
|
1436
|
-
return resources.map(resource => [getLocalizedValue(resource.form_title || resource.metadata?.form_name || resource.relation_form_name || resource.metadata?.relation_form_name, locale), getLocalizedValue(resource.title || resource.name || resource.main_field || resource.metadata?.main_field || resource.metadata?.resource_name, locale)].filter(Boolean).join(' ')).filter(Boolean).join(', ');
|
|
1444
|
+
function formatBookingResources(resources, locale, showResourceTitle) {
|
|
1445
|
+
return resources.map(resource => [showResourceTitle ? getLocalizedValue(resource.form_title || resource.metadata?.form_name || resource.relation_form_name || resource.metadata?.relation_form_name, locale) : '', getLocalizedValue(resource.title || resource.name || resource.main_field || resource.metadata?.main_field || resource.metadata?.resource_name, locale)].filter(Boolean).join(' ')).filter(Boolean).join(', ');
|
|
1437
1446
|
}
|
|
1438
1447
|
function sortBookings(bookings) {
|
|
1439
1448
|
return bookings.map((booking, index) => ({
|
|
@@ -34,6 +34,9 @@ function resolveInvoiceDataVisibility(settings) {
|
|
|
34
34
|
const field = (config, key) => invoiceSettingIsVisible(config?.[key]?.is_show);
|
|
35
35
|
const passTypes = Array.isArray(card?.pass_type) && card.pass_type.length > 0 ? new Set(card.pass_type.filter(tag => typeof tag === 'string' && tag.length > 0)) : null;
|
|
36
36
|
return {
|
|
37
|
+
lineGroups: {
|
|
38
|
+
resourceTitle: field(module('line_groups'), 'resource_title')
|
|
39
|
+
},
|
|
37
40
|
handover: {
|
|
38
41
|
show: invoiceSettingIsVisible(handover?.is_show),
|
|
39
42
|
method: field(handover, 'method'),
|
|
@@ -57,7 +60,8 @@ function requiresInvoiceDataFiltering(settings) {
|
|
|
57
60
|
const {
|
|
58
61
|
handover,
|
|
59
62
|
customer,
|
|
60
|
-
card
|
|
63
|
+
card,
|
|
64
|
+
lineGroups
|
|
61
65
|
} = resolveInvoiceDataVisibility(settings);
|
|
62
|
-
return !handover.show || !handover.method || !handover.fulfillmentTime || !customer.show || !customer.name || !customer.email || !customer.phone || !card.show || card.passTypes !== null;
|
|
66
|
+
return !lineGroups.resourceTitle || !handover.show || !handover.method || !handover.fulfillmentTime || !customer.show || !customer.name || !customer.email || !customer.phone || !card.show || card.passTypes !== null;
|
|
63
67
|
}
|
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.SALES_ORDER_RELATIONS = void 0;
|
|
7
7
|
exports.loadSalesOrderSnapshot = loadSalesOrderSnapshot;
|
|
8
|
-
const SALES_ORDER_RELATIONS = exports.SALES_ORDER_RELATIONS = ['products', 'bookings', 'payments', 'customer', 'summary', 'contacts_info', 'order_meta_list', 'refunds', 'notes'];
|
|
8
|
+
const SALES_ORDER_RELATIONS = exports.SALES_ORDER_RELATIONS = ['products', 'bookings', 'payments', 'customer', 'summary', 'contacts_info', 'order_meta_list', 'refunds', 'notes', 'holder_relations'];
|
|
9
9
|
|
|
10
10
|
/** Shared transport contract; remote callers use their host's /h5 prefix. */
|
|
11
11
|
function loadSalesOrderSnapshot(request, params) {
|
|
@@ -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';
|
|
@@ -281,6 +282,9 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
|
|
|
281
282
|
setPickupTimingBooking(input: PickupTimingBookingInput): import("../../modules/Order").PickupTimingBooking;
|
|
282
283
|
clearPickupTimingBooking(): void;
|
|
283
284
|
updateTempOrderContactsInfo(contactsInfo: Record<string, any> | null): Record<string, any> | null;
|
|
285
|
+
protected loadContactInfoOpenData(): Promise<OpenDataConfig | null>;
|
|
286
|
+
getContactInfoConfig(): Promise<ContactInfoConfig>;
|
|
287
|
+
validateContactInfo(): Promise<void>;
|
|
284
288
|
/** @deprecated OrderModule 不再负责 tempOrder localStorage 持久化。 */
|
|
285
289
|
setEnableTempOrderPersist(enabled: boolean): void;
|
|
286
290
|
/** 是否允许 saveDraft 写入 IndexedDB 草稿。 */
|
|
@@ -25,6 +25,8 @@ var _Order = require("../../modules/Order");
|
|
|
25
25
|
var _pickupTiming = require("../../modules/Order/pickupTiming");
|
|
26
26
|
var _pickupTiming2 = require("./pickupTiming");
|
|
27
27
|
var _fulfillmentItemRules = require("./fulfillmentItemRules");
|
|
28
|
+
var _holderRelations = require("../../modules/Order/holderRelations");
|
|
29
|
+
var _contactInfo = require("../../modules/OpenData/contactInfo");
|
|
28
30
|
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
29
31
|
var _lodashEs = require("lodash-es");
|
|
30
32
|
var _types2 = require("../BookingByStep/types");
|
|
@@ -349,6 +351,38 @@ function areBaseSalesNotesEquivalent(currentValue, loadedValue, options) {
|
|
|
349
351
|
const loadedNotes = normalizeBaseSalesNotesForComparison(loadedValue);
|
|
350
352
|
return currentNotes !== null && loadedNotes !== null && (0, _lodashEs.isEqual)(currentNotes, loadedNotes);
|
|
351
353
|
}
|
|
354
|
+
function mergeBaseSalesHolderRelations(currentRecord, incomingRecord, strategy) {
|
|
355
|
+
if (!Array.isArray(incomingRecord.holder_relations)) {
|
|
356
|
+
return (0, _lodashEs.cloneDeep)(currentRecord.holder_relations);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// 仅保留商品集合合并实际留下的本地行关系;匹配的行沿用 incoming 快照。
|
|
360
|
+
// 未提供 products 的稀疏包不能据此判断哪些行已被删除或被本地保留。
|
|
361
|
+
const retainedProductUids = new Set();
|
|
362
|
+
if (Array.isArray(incomingRecord.products)) {
|
|
363
|
+
for (const product of currentRecord.products || []) {
|
|
364
|
+
const retained = !incomingRecord.products.some(incomingProduct => (0, _orderCollectionIdentity.isSameOrderCollectionItem)('product', product, incomingProduct)) && (strategy === 'preserve-local' || !(0, _orderCollectionIdentity.getOrderCollectionPersistentId)('product', product));
|
|
365
|
+
const uid = (0, _orderCollectionIdentity.getOrderCollectionReferenceUid)('product', product);
|
|
366
|
+
if (retained && uid) retainedProductUids.add(uid);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
const belongsToRetainedProduct = relation => relation?.relation_model === 'order_detail' && retainedProductUids.has(String(relation.relation_uuid));
|
|
370
|
+
const selectedRelations = [...incomingRecord.holder_relations.filter(relation => !belongsToRetainedProduct(relation)), ...(Array.isArray(currentRecord.holder_relations) ? currentRecord.holder_relations.filter(belongsToRetainedProduct) : [])];
|
|
371
|
+
const result = [];
|
|
372
|
+
const indexByIdentity = new Map();
|
|
373
|
+
for (const relation of selectedRelations) {
|
|
374
|
+
const identityParts = [relation?.relation_model, relation?.relation_uuid, relation?.holder_type, relation?.holder_id];
|
|
375
|
+
const identity = identityParts.every(value => value !== undefined && value !== null) ? JSON.stringify(identityParts) : null;
|
|
376
|
+
const existingIndex = identity === null ? undefined : indexByIdentity.get(identity);
|
|
377
|
+
if (existingIndex !== undefined) {
|
|
378
|
+
result[existingIndex] = (0, _lodashEs.cloneDeep)(relation);
|
|
379
|
+
} else {
|
|
380
|
+
if (identity !== null) indexByIdentity.set(identity, result.length);
|
|
381
|
+
result.push((0, _lodashEs.cloneDeep)(relation));
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
352
386
|
function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, strategy) {
|
|
353
387
|
const loadedHasOrderMetaList = Object.prototype.hasOwnProperty.call(loadedRecord || {}, 'order_meta_list');
|
|
354
388
|
const loadedHasSalesNotes = Object.prototype.hasOwnProperty.call(loadedRecord || {}, 'notes');
|
|
@@ -367,8 +401,12 @@ function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, str
|
|
|
367
401
|
const normalizedCurrentTempOrder = normalizeBaseSalesRecordCollections(currentTempOrder, {
|
|
368
402
|
ensureUid: false
|
|
369
403
|
});
|
|
404
|
+
const mergedHolderRelations = mergeBaseSalesHolderRelations(normalizedCurrentTempOrder, normalizedLoadedRecord, strategy);
|
|
370
405
|
const uidRemaps = [];
|
|
371
|
-
const mergedRecord = (0, _lodashEs.mergeWith)(normalizedCurrentTempOrder, normalizedLoadedRecord, (currentValue, loadedValue, key) => {
|
|
406
|
+
const mergedRecord = (0, _lodashEs.mergeWith)(normalizedCurrentTempOrder, normalizedLoadedRecord, (currentValue, loadedValue, key, object) => {
|
|
407
|
+
if (key === 'holder_relations' && object === normalizedCurrentTempOrder) {
|
|
408
|
+
return (0, _lodashEs.cloneDeep)(mergedHolderRelations);
|
|
409
|
+
}
|
|
372
410
|
// order_meta_list 是服务端/本地订单携带的完整业务快照。若 incoming
|
|
373
411
|
// 明确提供它,必须整体替换;若省略,则保留当前值,避免 snapshot
|
|
374
412
|
// 数组递归拼接后出现同 ID 的旧数据。
|
|
@@ -1862,6 +1900,29 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
1862
1900
|
if (!this.store.order) throw new Error('order 模块未初始化');
|
|
1863
1901
|
return this.store.order.updateTempOrderContactsInfo(contactsInfo);
|
|
1864
1902
|
}
|
|
1903
|
+
async loadContactInfoOpenData() {
|
|
1904
|
+
return this.otherParams?.openData ?? null;
|
|
1905
|
+
}
|
|
1906
|
+
async getContactInfoConfig() {
|
|
1907
|
+
const scope = this.getPickupTimingScope();
|
|
1908
|
+
const data = await this.loadContactInfoOpenData();
|
|
1909
|
+
if (scope !== this.getPickupTimingScope()) throw new Error('Store settings changed. Please try again.');
|
|
1910
|
+
return (0, _contactInfo.resolveContactInfoConfig)(data);
|
|
1911
|
+
}
|
|
1912
|
+
async validateContactInfo() {
|
|
1913
|
+
const order = this.getTempOrder();
|
|
1914
|
+
const customerId = order?.customer_id;
|
|
1915
|
+
// Missing customer IDs and the reserved Walk-In IDs do not require contact info.
|
|
1916
|
+
if (!Number.isFinite(Number(customerId)) || Number(customerId) <= 1) return;
|
|
1917
|
+
const identity = order?._extend?.localId || order?.external_sale_number || order?.order_id || order;
|
|
1918
|
+
const config = await this.getContactInfoConfig();
|
|
1919
|
+
const current = this.getTempOrder();
|
|
1920
|
+
if (identity !== (current?._extend?.localId || current?.external_sale_number || current?.order_id || current) || customerId !== current?.customer_id) {
|
|
1921
|
+
throw new Error('The order changed. Please try again.');
|
|
1922
|
+
}
|
|
1923
|
+
const missing = (0, _contactInfo.getMissingContactInfoFields)(config, current?.contacts_info);
|
|
1924
|
+
if (missing.length) throw new _contactInfo.ContactInfoRequiredError(missing);
|
|
1925
|
+
}
|
|
1865
1926
|
|
|
1866
1927
|
/** @deprecated OrderModule 不再负责 tempOrder localStorage 持久化。 */
|
|
1867
1928
|
setEnableTempOrderPersist(enabled) {
|
|
@@ -2664,6 +2725,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2664
2725
|
if (!this.store.order) {
|
|
2665
2726
|
throw new Error('BaseSales 解决方案需要 order 模块支持');
|
|
2666
2727
|
}
|
|
2728
|
+
await this.validateContactInfo();
|
|
2667
2729
|
if (this.isPickupTimingEnabled()) await this.preparePickupTimingForCheckout({
|
|
2668
2730
|
freeze: true
|
|
2669
2731
|
});
|
|
@@ -2824,6 +2886,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2824
2886
|
for (const params of unfinished) await this.finalizeLocalEftposPayment(params);
|
|
2825
2887
|
throw new Error('订单已更新,请确认商品和金额后重新付款');
|
|
2826
2888
|
}
|
|
2889
|
+
await this.validateContactInfo();
|
|
2827
2890
|
const lookup = temp?.order_id || temp?.external_sale_number;
|
|
2828
2891
|
let localOrder = null;
|
|
2829
2892
|
if (lookup && this.core?.server?.hasRoute?.('post', '/shop/local/orderPaymentReadiness')) {
|
|
@@ -2875,6 +2938,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2875
2938
|
if (!this.store.order) {
|
|
2876
2939
|
throw new Error('BaseSales 解决方案需要 order 模块支持');
|
|
2877
2940
|
}
|
|
2941
|
+
await this.validateContactInfo();
|
|
2878
2942
|
if (this.isPickupTimingEnabled()) await this.preparePickupTimingForCheckout({
|
|
2879
2943
|
freeze: true
|
|
2880
2944
|
});
|
|
@@ -3293,7 +3357,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
3293
3357
|
|
|
3294
3358
|
const walletProducts = tempOrder?.products.map(product => {
|
|
3295
3359
|
const metadata = product.metadata || {};
|
|
3296
|
-
const rawHolderId = product.holder_id ?? metadata.holder_id;
|
|
3360
|
+
const rawHolderId = (0, _holderRelations.hasProductBooking)(product) ? product.holder_id ?? metadata.holder_id : (0, _holderRelations.getProductHolderRelations)(tempOrder, product).filter(relation => relation.holder_type === 'form').map(relation => relation.holder_id);
|
|
3297
3361
|
const holderIdValue = Array.isArray(rawHolderId) ? rawHolderId[0] : rawHolderId;
|
|
3298
3362
|
const holder_id = Number.isInteger(Number(holderIdValue)) ? Number(holderIdValue) : undefined;
|
|
3299
3363
|
return {
|
|
@@ -3709,6 +3773,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
3709
3773
|
*/
|
|
3710
3774
|
async confirmWalletPayment() {
|
|
3711
3775
|
if (!this.store.order) throw new Error('order 模块未初始化');
|
|
3776
|
+
await this.validateContactInfo();
|
|
3712
3777
|
const beforePayments = (0, _lodashEs.cloneDeep)(this.store.order.getOrderPayments());
|
|
3713
3778
|
const pendingWalletAmount = this.getPendingWalletPaymentAmount(beforePayments);
|
|
3714
3779
|
const amountSnapshot = this.store.order.getOrderAmountSnapshot?.();
|
|
@@ -3780,6 +3845,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
3780
3845
|
*/
|
|
3781
3846
|
async payCash(params) {
|
|
3782
3847
|
if (!this.store.order) throw new Error('order 模块未初始化');
|
|
3848
|
+
await this.validateContactInfo();
|
|
3783
3849
|
const amount = Number(params.amount || 0);
|
|
3784
3850
|
if (!Number.isFinite(amount) || amount <= 0) {
|
|
3785
3851
|
throw new Error('现金支付金额必须大于 0');
|
|
@@ -4,7 +4,7 @@ import type { ScanOrderLogInput as BaseSalesLogInput, ScanOrderLoggerProviderCon
|
|
|
4
4
|
import type { QuantityCheckResult, QuantityLimitResult } from '../../model/strategy/adapter/itemRule';
|
|
5
5
|
import type { QuotationCustomerScopeInfo } from '../../modules/Quotation/types';
|
|
6
6
|
import type { PaymentMethod } from '../../modules/Payment/types';
|
|
7
|
-
import type { OrderPaymentSource } from '../../modules/Order/types';
|
|
7
|
+
import type { OrderHolderRelation, OrderPaymentSource } from '../../modules/Order/types';
|
|
8
8
|
import type { SubmitPayloadEnhancer } from '../../modules/Order/utils';
|
|
9
9
|
/**
|
|
10
10
|
* BaseSales 流程 hook 后缀。
|
|
@@ -277,6 +277,7 @@ export interface BaseSalesTempOrder {
|
|
|
277
277
|
contacts: any[];
|
|
278
278
|
contacts_info: Record<string, any> | null;
|
|
279
279
|
holder: Record<string, any> | null;
|
|
280
|
+
holder_relations?: OrderHolderRelation[];
|
|
280
281
|
metadata: Record<string, any>;
|
|
281
282
|
_extend?: {
|
|
282
283
|
productsByUid?: Record<string, Record<string, any>>;
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* - 不在此处发明等价关系;UI 侧(ticketBooking)若需要别的字段,由 view-model 适配层做转换
|
|
15
15
|
*/
|
|
16
16
|
import type { BaseSalesOrderProduct } from '../types';
|
|
17
|
-
import type { OrderMetaList } from '../../../modules/Order/types';
|
|
17
|
+
import type { OrderHolderRelation, OrderMetaList } from '../../../modules/Order/types';
|
|
18
18
|
export interface ISalesDetailHeader {
|
|
19
19
|
order_id: number | string | null;
|
|
20
20
|
order_number?: string;
|
|
@@ -121,6 +121,8 @@ export interface ISalesDetail {
|
|
|
121
121
|
header: ISalesDetailHeader;
|
|
122
122
|
/** 商品行快照(保留 tempOrder / 后端详情原字段) */
|
|
123
123
|
products: BaseSalesOrderProduct[];
|
|
124
|
+
/** 非预约商品 Holder 关系,保留关系及 metadata 的扩展字段。 */
|
|
125
|
+
holder_relations?: OrderHolderRelation[];
|
|
124
126
|
/** 全部 bookings,按服务端原始顺序输出(含 root + child) */
|
|
125
127
|
bookings: ISalesBooking[];
|
|
126
128
|
/** root booking(parent_id === 0),多个时取首个,无则为 null */
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.parseSalesResponse = parseSalesResponse;
|
|
7
7
|
var _orderCollectionIdentity = require("../../../modules/Order/utils/orderCollectionIdentity");
|
|
8
8
|
var _pickupTiming = require("../../../modules/Order/pickupTiming");
|
|
9
|
+
var _lodashEs = require("lodash-es");
|
|
9
10
|
/**
|
|
10
11
|
* 把 `/order/sales/{id}` 响应(OrderData)解析为 ISalesDetail。
|
|
11
12
|
*
|
|
@@ -130,6 +131,9 @@ function parseSalesResponse(input) {
|
|
|
130
131
|
return {
|
|
131
132
|
header: pickHeader(raw),
|
|
132
133
|
products,
|
|
134
|
+
...(Array.isArray(raw?.holder_relations) ? {
|
|
135
|
+
holder_relations: (0, _lodashEs.cloneDeep)(raw.holder_relations)
|
|
136
|
+
} : {}),
|
|
133
137
|
bookings,
|
|
134
138
|
rootBooking,
|
|
135
139
|
bookingsByProductUid,
|
|
@@ -5,7 +5,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.transformBaseProductToOrderProduct = transformBaseProductToOrderProduct;
|
|
7
7
|
var _weighedProduct = require("../../../modules/Order/utils/weighedProduct");
|
|
8
|
-
var _utils = require("../../../modules/
|
|
8
|
+
var _utils = require("../../../modules/Holder/utils");
|
|
9
|
+
var _serviceTimes = require("../../../modules/BookingContext/utils/serviceTimes");
|
|
10
|
+
var _utils2 = require("../../../modules/Order/utils");
|
|
9
11
|
var _manualProductDiscount = require("../../../modules/Order/utils/manualProductDiscount");
|
|
10
12
|
function toNumber(value, fallback = 0) {
|
|
11
13
|
const parsedValue = Number(value);
|
|
@@ -108,7 +110,7 @@ function normalizeBundleItems(bundleItems, preferPriceField = false, preferBundl
|
|
|
108
110
|
discount_list: preferPriceField ? [] : bundleItem.discount_list
|
|
109
111
|
};
|
|
110
112
|
// bundle_selling_price 落「正净额」:markdown = |price| − Σoptions;markup/原价维持原值。
|
|
111
|
-
normalizedItem.bundle_selling_price = (0,
|
|
113
|
+
normalizedItem.bundle_selling_price = (0, _utils2.getBundleSellingMagnitude)(magnitudeSource).toFixed(2);
|
|
112
114
|
if ((normalizedItem.price_type ?? normalizedItem.custom_price_type) === 'markdown' && (normalizedItem.price_type_ext === '' || normalizedItem.price_type_ext === undefined || normalizedItem.price_type_ext === null)) {
|
|
113
115
|
normalizedItem.custom_price = normalizedItem.custom_price ?? normalizedItem.price;
|
|
114
116
|
normalizedItem.price = normalizedItem.bundle_selling_price;
|
|
@@ -158,14 +160,14 @@ function transformBaseProductToOrderProduct(params) {
|
|
|
158
160
|
const explicitLineTotal = payload.line_total ?? payload.total ?? payload._extend?.total;
|
|
159
161
|
const hasExplicitLineTotal = Number.isFinite(Number(explicitLineTotal));
|
|
160
162
|
const lineCompositeTotal = toPriceString(explicitLineTotal, sourceProductPrice);
|
|
161
|
-
const productOptionItem = (0,
|
|
163
|
+
const productOptionItem = (0, _utils2.normalizeProductSkuOptions)(normalizeOptionItems(payload.product_sku?.option ?? payload.option ?? payload.options ?? payload.product_option_item, hasPriceOverride || weighedSourcePrice !== undefined));
|
|
162
164
|
const productBundle = normalizeBundleItems(payload.bundle ?? payload.product_bundle, hasPriceOverride, hasPriceOverride && weighedSourcePrice !== undefined);
|
|
163
165
|
// `payload.unique` 是业务侧商品/票务标识,可能在连续加同一商品时重复;
|
|
164
166
|
// 行级唯一值只接受显式协议字段,缺省交给 OrderModule 生成。
|
|
165
167
|
const uniqueIdentificationNumber = payload.unique_identification_number;
|
|
166
168
|
const resolvedQuantity = toSafePositiveInt(payload.quantity ?? payload.num, 1);
|
|
167
|
-
const originTotal = weighedSourcePrice !== undefined ? Number((0,
|
|
168
|
-
mainPrice: Number(weighedSourcePrice) + (0,
|
|
169
|
+
const originTotal = weighedSourcePrice !== undefined ? Number((0, _utils2.composeLinePrice)({
|
|
170
|
+
mainPrice: Number(weighedSourcePrice) + (0, _utils2.sumOptionUnitPrice)(productOptionItem).toNumber(),
|
|
169
171
|
bundle: hasPriceOverride ? (0, _weighedProduct.withOriginalBundleOptionPrices)(productBundle) : productBundle,
|
|
170
172
|
useOriginalBundle: true
|
|
171
173
|
})) : (0, _manualProductDiscount.resolveManualDiscountOriginTotal)({
|
|
@@ -197,14 +199,15 @@ function transformBaseProductToOrderProduct(params) {
|
|
|
197
199
|
})) : payload.discount_list || [];
|
|
198
200
|
|
|
199
201
|
// v2:source 不含 option;main_product_* 含 option(与 normalizeOrderProduct 一致)
|
|
200
|
-
const optionSum = (0,
|
|
202
|
+
const optionSum = (0, _utils2.sumOptionUnitPrice)(productOptionItem).toNumber();
|
|
201
203
|
const mainProductOriginalPrice = toPriceString(Number(sourceProductPrice) + optionSum);
|
|
202
|
-
const bundleOnlyComposite = (0,
|
|
204
|
+
const bundleOnlyComposite = (0, _utils2.composeLinePrice)({
|
|
203
205
|
mainPrice: 0,
|
|
204
206
|
bundle: productBundle
|
|
205
207
|
});
|
|
206
208
|
const derivedMainSelling = hasExplicitLineTotal ? Number(lineCompositeTotal) - Number(bundleOnlyComposite) : Number(mainProductOriginalPrice);
|
|
207
209
|
const mainProductSellingPrice = toPriceString(Number.isFinite(derivedMainSelling) && derivedMainSelling >= 0 ? derivedMainSelling : Number(mainProductOriginalPrice));
|
|
210
|
+
const holderRelations = [payload._extend, callbackOrigin?._extend, sourceProduct?._extend].find(source => Array.isArray(source?.holder_relations))?.holder_relations;
|
|
208
211
|
const metadata = {
|
|
209
212
|
unique: payload.unique,
|
|
210
213
|
session: payload.session,
|
|
@@ -219,7 +222,7 @@ function transformBaseProductToOrderProduct(params) {
|
|
|
219
222
|
main_product_selling_price: mainProductSellingPrice,
|
|
220
223
|
main_product_original_price: mainProductOriginalPrice,
|
|
221
224
|
price_schema_version: 2,
|
|
222
|
-
holder_config: sourceProduct
|
|
225
|
+
holder_config: (0, _utils.resolveProductHolderConfig)(sourceProduct) ?? undefined,
|
|
223
226
|
...(dataVariantIds !== undefined ? {
|
|
224
227
|
data_variant_ids: dataVariantIds
|
|
225
228
|
} : {}),
|
|
@@ -264,6 +267,11 @@ function transformBaseProductToOrderProduct(params) {
|
|
|
264
267
|
tax_fee: toPriceString(payload.tax_fee ?? matchedVariant?.tax_fee ?? origin?.tax_fee ?? sourceProduct?.tax_fee, '0.00'),
|
|
265
268
|
is_charge_tax: toNumber(payload.is_charge_tax ?? matchedVariant?.is_charge_tax ?? origin?.is_charge_tax ?? sourceProduct?.is_charge_tax, 0),
|
|
266
269
|
metadata,
|
|
270
|
+
...(holderRelations !== undefined && !(0, _serviceTimes.isBookingProduct)(origin) && !(0, _serviceTimes.isBookingProduct)(payload) ? {
|
|
271
|
+
_extend: {
|
|
272
|
+
holder_relations: holderRelations
|
|
273
|
+
}
|
|
274
|
+
} : {}),
|
|
267
275
|
note: payload.note != null ? String(payload.note) : '',
|
|
268
276
|
product_title: productTitle,
|
|
269
277
|
product_cover: firstNonBlank(matchedVariant?.cover, sourceProduct?.cover),
|
|
@@ -328,7 +328,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
328
328
|
date: string;
|
|
329
329
|
status: string;
|
|
330
330
|
week: string;
|
|
331
|
-
weekNum: 0 | 5 |
|
|
331
|
+
weekNum: 0 | 5 | 1 | 2 | 4 | 3 | 6;
|
|
332
332
|
}[]>;
|
|
333
333
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
334
334
|
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;
|
|
@@ -62,6 +63,7 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
62
63
|
private getIdGeneratorPlugin;
|
|
63
64
|
protected loadOpenDataConfig(): Promise<OpenDataConfig | null>;
|
|
64
65
|
getOpenData(): Promise<OpenDataConfig | null>;
|
|
66
|
+
protected loadContactInfoOpenData(): Promise<OpenDataConfig | null>;
|
|
65
67
|
protected loadFulfillmentItemRuleOpenData(): Promise<OpenDataConfig | null>;
|
|
66
68
|
getInvoiceLayoutConfig(): Promise<InvoiceLayoutConfig>;
|
|
67
69
|
getInvoiceTemplateSettings(): Promise<InvoiceTemplateSettings>;
|
|
@@ -281,6 +283,16 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
281
283
|
setOrderCustomer(customer: ICustomer | null, options?: {
|
|
282
284
|
source?: OrderCustomerChangePayload['source'];
|
|
283
285
|
}): void;
|
|
286
|
+
private resetOrderCustomerContactInfo;
|
|
287
|
+
/** Explicit edits, including clearing the contact, must win over a pending default. */
|
|
288
|
+
updateTempOrderContactsInfo(contact: Record<string, any> | null): Record<string, any> | null;
|
|
289
|
+
private getCustomerContactScope;
|
|
290
|
+
private getCustomerContactOrderKey;
|
|
291
|
+
replaceTempOrder(...args: Parameters<BaseSalesImpl['replaceTempOrder']>): Promise<import("../../modules/Order/types").OrderTempOrder>;
|
|
292
|
+
private applyDefaultOrderCustomerContactInfo;
|
|
293
|
+
private prefillOrderCustomerContactInfo;
|
|
294
|
+
protected waitForOrderCustomerContactInfo(): Promise<void>;
|
|
295
|
+
validateContactInfo(): Promise<void>;
|
|
284
296
|
/**
|
|
285
297
|
* 读取 tempOrder 上的下单客户协议字段;customer_id 缺失时返回 null。
|
|
286
298
|
*/
|
|
@@ -292,7 +304,9 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
292
304
|
/**
|
|
293
305
|
* 清空下单客户(协议字段 + snapshot)。
|
|
294
306
|
*/
|
|
295
|
-
clearOrderCustomer(
|
|
307
|
+
clearOrderCustomer(options?: {
|
|
308
|
+
preserveContactInfo?: boolean;
|
|
309
|
+
}): void;
|
|
296
310
|
/**
|
|
297
311
|
* 重置 tempOrder,并同步清空下单客户运行态。
|
|
298
312
|
*
|