@pisell/pisellos 0.10.36 → 0.10.38
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/Order/index.d.ts +1 -1
- package/dist/server/utils/small-ticket.d.ts +3 -5
- package/dist/server/utils/small-ticket.js +433 -98
- package/lib/model/strategy/adapter/promotion/index.js +1 -46
- package/lib/modules/Order/index.d.ts +1 -1
- package/lib/server/utils/small-ticket.d.ts +3 -5
- package/lib/server/utils/small-ticket.js +306 -35
- package/package.json +1 -1
|
@@ -1,46 +1 @@
|
|
|
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; }
|
|
1
|
+
"use strict";
|
|
@@ -535,7 +535,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
535
535
|
generateIdempotencyToken(): string;
|
|
536
536
|
syncPaymentsToOrder<T = any>(params: SyncPaymentsToOrderParams): Promise<SyncPaymentsToOrderResult<T>>;
|
|
537
537
|
createOrder(params: CommitOrderParams['query']): {
|
|
538
|
-
type: "
|
|
538
|
+
type: "appointment_booking" | "virtual";
|
|
539
539
|
platform: string;
|
|
540
540
|
sales_channel: string;
|
|
541
541
|
order_sales_channel: string;
|
|
@@ -28,6 +28,8 @@ export interface SmallTicketItem {
|
|
|
28
28
|
value: string;
|
|
29
29
|
size?: number;
|
|
30
30
|
align?: string;
|
|
31
|
+
product_uid?: string | number;
|
|
32
|
+
booking_uid?: string | number;
|
|
31
33
|
}
|
|
32
34
|
export interface SmallTicketProduct {
|
|
33
35
|
product_title: string;
|
|
@@ -53,11 +55,7 @@ export interface SmallTicketProduct {
|
|
|
53
55
|
variant?: string;
|
|
54
56
|
combinations?: any[];
|
|
55
57
|
}
|
|
56
|
-
export
|
|
57
|
-
product_uid?: string | number;
|
|
58
|
-
booking_uid?: string | number;
|
|
59
|
-
}
|
|
60
|
-
export type SmallTicketAppointment = SmallTicketItem[] | SmallTicketBookingSnapshot[];
|
|
58
|
+
export type SmallTicketAppointment = SmallTicketItem[];
|
|
61
59
|
export interface SmallTicketLocaleData {
|
|
62
60
|
base_data: Record<string, any>;
|
|
63
61
|
products: SmallTicketProduct[];
|
|
@@ -29,12 +29,18 @@ const LABELS = {
|
|
|
29
29
|
giftCard: 'Gift card',
|
|
30
30
|
pointCard: 'Point card',
|
|
31
31
|
balance: 'Balance',
|
|
32
|
+
validity: 'Validity',
|
|
32
33
|
remainingAmount: 'Remaining amount',
|
|
33
34
|
redeemPoints: 'Redeem points',
|
|
34
35
|
remainingPoints: 'Points balance',
|
|
35
36
|
table: 'Table',
|
|
37
|
+
bookingId: 'Booking ID',
|
|
38
|
+
bookingDateTime: 'Date & time',
|
|
39
|
+
bookingResource: 'Resource',
|
|
40
|
+
bookingHolder: 'Holder',
|
|
36
41
|
orderDiscount: 'Order discount',
|
|
37
42
|
manualDiscount: 'Manual discount',
|
|
43
|
+
manualMarkup: 'Manual markup',
|
|
38
44
|
payProcessingFee: 'Pay processing fee',
|
|
39
45
|
surchargeIncluded: 'Surcharge(Included)',
|
|
40
46
|
rounding: 'Rounding',
|
|
@@ -62,12 +68,18 @@ const LABELS = {
|
|
|
62
68
|
giftCard: '礼品卡',
|
|
63
69
|
pointCard: '积分卡',
|
|
64
70
|
balance: '余额',
|
|
71
|
+
validity: '有效期',
|
|
65
72
|
remainingAmount: '剩余金额',
|
|
66
73
|
redeemPoints: '本次使用积分',
|
|
67
74
|
remainingPoints: '积分余额',
|
|
68
75
|
table: '桌台',
|
|
76
|
+
bookingId: '预约编号',
|
|
77
|
+
bookingDateTime: '日期和时间',
|
|
78
|
+
bookingResource: '资源',
|
|
79
|
+
bookingHolder: '预约人',
|
|
69
80
|
orderDiscount: '整单折扣',
|
|
70
81
|
manualDiscount: '手动折扣',
|
|
82
|
+
manualMarkup: '手动加价',
|
|
71
83
|
payProcessingFee: '支付手续费',
|
|
72
84
|
surchargeIncluded: '手续费(已含)',
|
|
73
85
|
rounding: '抹零',
|
|
@@ -95,12 +107,18 @@ const LABELS = {
|
|
|
95
107
|
giftCard: '禮品卡',
|
|
96
108
|
pointCard: '積分卡',
|
|
97
109
|
balance: '餘額',
|
|
110
|
+
validity: '有效期',
|
|
98
111
|
remainingAmount: '剩餘金額',
|
|
99
112
|
redeemPoints: '本次使用積分',
|
|
100
113
|
remainingPoints: '積分餘額',
|
|
101
114
|
table: '桌台',
|
|
115
|
+
bookingId: '預約編號',
|
|
116
|
+
bookingDateTime: '日期和時間',
|
|
117
|
+
bookingResource: '資源',
|
|
118
|
+
bookingHolder: '預約人',
|
|
102
119
|
orderDiscount: '整單折扣',
|
|
103
120
|
manualDiscount: '手動折扣',
|
|
121
|
+
manualMarkup: '手動加價',
|
|
104
122
|
payProcessingFee: '支付手續費',
|
|
105
123
|
surchargeIncluded: '手續費(已含)',
|
|
106
124
|
rounding: '抹零',
|
|
@@ -126,12 +144,18 @@ const LABELS = {
|
|
|
126
144
|
giftCard: 'ギフトカード',
|
|
127
145
|
pointCard: 'ポイントカード',
|
|
128
146
|
balance: '残高',
|
|
147
|
+
validity: '有効期限',
|
|
129
148
|
remainingAmount: '残高',
|
|
130
149
|
redeemPoints: '利用ポイント',
|
|
131
150
|
remainingPoints: 'ポイント残高',
|
|
132
151
|
table: 'テーブル',
|
|
152
|
+
bookingId: '予約ID',
|
|
153
|
+
bookingDateTime: '日時',
|
|
154
|
+
bookingResource: 'リソース',
|
|
155
|
+
bookingHolder: '予約者',
|
|
133
156
|
orderDiscount: '注文割引',
|
|
134
157
|
manualDiscount: '手動割引',
|
|
158
|
+
manualMarkup: '手動値上げ',
|
|
135
159
|
payProcessingFee: '決済手数料',
|
|
136
160
|
rounding: '端数調整',
|
|
137
161
|
gross: '総重量',
|
|
@@ -158,12 +182,18 @@ const LABELS = {
|
|
|
158
182
|
giftCard: 'Cartão-presente',
|
|
159
183
|
pointCard: 'Cartão de pontos',
|
|
160
184
|
balance: 'Saldo',
|
|
185
|
+
validity: 'Validade',
|
|
161
186
|
remainingAmount: 'Saldo restante',
|
|
162
187
|
redeemPoints: 'Pontos utilizados',
|
|
163
188
|
remainingPoints: 'Saldo de pontos',
|
|
164
189
|
table: 'Mesa',
|
|
190
|
+
bookingId: 'ID da reserva',
|
|
191
|
+
bookingDateTime: 'Data e hora',
|
|
192
|
+
bookingResource: 'Recurso',
|
|
193
|
+
bookingHolder: 'Titular',
|
|
165
194
|
orderDiscount: 'Desconto do pedido',
|
|
166
195
|
manualDiscount: 'Desconto manual',
|
|
196
|
+
manualMarkup: 'Acréscimo manual',
|
|
167
197
|
payProcessingFee: 'Taxa de processamento',
|
|
168
198
|
rounding: 'Arredondamento',
|
|
169
199
|
gross: 'Peso bruto',
|
|
@@ -281,7 +311,8 @@ function buildBaseData(params) {
|
|
|
281
311
|
}
|
|
282
312
|
function buildProducts(order, locale, productMap) {
|
|
283
313
|
const products = Array.isArray(order.products) ? order.products : [];
|
|
284
|
-
return products.map(
|
|
314
|
+
return products.map(rawProduct => {
|
|
315
|
+
const product = restoreReceiptDiscountHierarchy(rawProduct);
|
|
285
316
|
const quantity = toNumber(product.product_quantity ?? product.quantity ?? product.num ?? 1, 1);
|
|
286
317
|
const originalPrice = product.original_price ?? product.selling_price ?? product.payment_price ?? product.price ?? 0;
|
|
287
318
|
const sellingPrice = product.selling_price ?? product.payment_price ?? product.price ?? originalPrice;
|
|
@@ -361,6 +392,73 @@ function buildProducts(order, locale, productMap) {
|
|
|
361
392
|
};
|
|
362
393
|
});
|
|
363
394
|
}
|
|
395
|
+
const RECEIPT_BUNDLE_MAP_ID_KEY = 'custom_product_bundle_map_id';
|
|
396
|
+
function restoreReceiptDiscountHierarchy(source) {
|
|
397
|
+
if (!source || typeof source !== 'object' || Array.isArray(source)) {
|
|
398
|
+
return source;
|
|
399
|
+
}
|
|
400
|
+
const childKey = Array.isArray(source.combinations) && source.combinations.length ? 'combinations' : Array.isArray(source.product_bundle) && source.product_bundle.length ? 'product_bundle' : null;
|
|
401
|
+
if (!childKey) return {
|
|
402
|
+
...source
|
|
403
|
+
};
|
|
404
|
+
const children = source[childKey].map(child => child && typeof child === 'object' && !Array.isArray(child) ? restoreReceiptDiscountHierarchy(child) : child);
|
|
405
|
+
const restored = {
|
|
406
|
+
...source,
|
|
407
|
+
[childKey]: children
|
|
408
|
+
};
|
|
409
|
+
if (!Array.isArray(source.discount_list) || !source.discount_list.length) {
|
|
410
|
+
return restored;
|
|
411
|
+
}
|
|
412
|
+
const descendantsByMapId = new Map();
|
|
413
|
+
indexReceiptDiscountTargets(children, descendantsByMapId);
|
|
414
|
+
if (!descendantsByMapId.size) return restored;
|
|
415
|
+
const ownDiscounts = [];
|
|
416
|
+
for (const discount of source.discount_list) {
|
|
417
|
+
const mapId = getReceiptDiscountBundleMapId(discount);
|
|
418
|
+
const target = mapId ? descendantsByMapId.get(mapId) : undefined;
|
|
419
|
+
if (!target) {
|
|
420
|
+
ownDiscounts.push(discount);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
const targetDiscounts = Array.isArray(target.discount_list) ? target.discount_list : [];
|
|
424
|
+
if (!targetDiscounts.some(item => isSameReceiptDiscount(item, discount))) {
|
|
425
|
+
target.discount_list = [...targetDiscounts, discount];
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
restored.discount_list = ownDiscounts;
|
|
429
|
+
return restored;
|
|
430
|
+
}
|
|
431
|
+
function indexReceiptDiscountTargets(nodes, targets) {
|
|
432
|
+
for (const node of nodes) {
|
|
433
|
+
if (!node || typeof node !== 'object' || Array.isArray(node)) continue;
|
|
434
|
+
const mapId = getReceiptBundleMapId(node);
|
|
435
|
+
if (mapId && !targets.has(mapId)) targets.set(mapId, node);
|
|
436
|
+
const children = Array.isArray(node.combinations) && node.combinations.length ? node.combinations : Array.isArray(node.product_bundle) ? node.product_bundle : [];
|
|
437
|
+
indexReceiptDiscountTargets(children, targets);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function normalizeReceiptId(value) {
|
|
441
|
+
if (value === undefined || value === null) return null;
|
|
442
|
+
const normalized = String(value).trim();
|
|
443
|
+
return normalized ? normalized : null;
|
|
444
|
+
}
|
|
445
|
+
function getReceiptBundleMapId(node) {
|
|
446
|
+
return normalizeReceiptId(node.metadata?.[RECEIPT_BUNDLE_MAP_ID_KEY] ?? node._id);
|
|
447
|
+
}
|
|
448
|
+
function getReceiptDiscountBundleMapId(discount) {
|
|
449
|
+
return normalizeReceiptId(discount?.metadata?.[RECEIPT_BUNDLE_MAP_ID_KEY]);
|
|
450
|
+
}
|
|
451
|
+
function isSameReceiptDiscount(left, right) {
|
|
452
|
+
const leftOrderDiscountId = normalizeReceiptId(left?.order_discount_id);
|
|
453
|
+
const rightOrderDiscountId = normalizeReceiptId(right?.order_discount_id);
|
|
454
|
+
if (leftOrderDiscountId && rightOrderDiscountId) {
|
|
455
|
+
return leftOrderDiscountId === rightOrderDiscountId;
|
|
456
|
+
}
|
|
457
|
+
const leftResourceId = normalizeReceiptId(left?.discount?.resource_id ?? left?.resource_id ?? left?.discount_id);
|
|
458
|
+
const rightResourceId = normalizeReceiptId(right?.discount?.resource_id ?? right?.resource_id ?? right?.discount_id);
|
|
459
|
+
if (!leftResourceId || !rightResourceId) return false;
|
|
460
|
+
return leftResourceId === rightResourceId && getReceiptDiscountBundleMapId(left) === getReceiptDiscountBundleMapId(right) && String(left?.amount ?? left?.value ?? left?.discount_amount ?? '') === String(right?.amount ?? right?.value ?? right?.discount_amount ?? '');
|
|
461
|
+
}
|
|
364
462
|
function resolveProductBasePrice(product) {
|
|
365
463
|
const metadata = product.metadata || {};
|
|
366
464
|
const origin = product._origin || metadata.origin || {};
|
|
@@ -376,18 +474,31 @@ function normalizeProductDiscounts(params) {
|
|
|
376
474
|
currencySymbol,
|
|
377
475
|
quantity
|
|
378
476
|
} = params;
|
|
379
|
-
|
|
477
|
+
const rows = normalizeArray(product.discount_list).filter(discount => {
|
|
380
478
|
const amount = discount.amount ?? discount.value ?? discount.discount_amount;
|
|
381
479
|
return isNonZero(amount);
|
|
382
480
|
}).map(discount => {
|
|
383
481
|
const title = resolveDiscountTitleWithPercent(discount, locale);
|
|
384
482
|
const amount = discount.amount ?? discount.value ?? discount.discount_amount;
|
|
385
|
-
const
|
|
483
|
+
const isMarkup = isManualMarkup(discount);
|
|
484
|
+
const totalAmount = new _decimal.default(toMoneyNumber(amount)).mul(quantity);
|
|
485
|
+
const formattedAmount = isMarkup ? formatMoney(totalAmount.abs(), currencySymbol) : formatNegativeMoney(totalAmount, currencySymbol);
|
|
386
486
|
return {
|
|
387
487
|
item: title,
|
|
388
|
-
value: formattedAmount
|
|
488
|
+
value: formattedAmount,
|
|
489
|
+
isMarkup
|
|
389
490
|
};
|
|
390
491
|
});
|
|
492
|
+
return rows.sort((left, right) => Number(right.isMarkup) - Number(left.isMarkup)).map(({
|
|
493
|
+
isMarkup: _isMarkup,
|
|
494
|
+
...row
|
|
495
|
+
}) => row);
|
|
496
|
+
}
|
|
497
|
+
function isManualMarkup(discount) {
|
|
498
|
+
if (discount.type !== 'product') return false;
|
|
499
|
+
if (discount.discount?.discount_type === 'change_price') return true;
|
|
500
|
+
const amount = discount.amount ?? discount.value ?? discount.discount_amount;
|
|
501
|
+
return new _decimal.default(toMoneyNumber(amount)).isNegative();
|
|
391
502
|
}
|
|
392
503
|
function hasProductDiscount(product, extensionList = []) {
|
|
393
504
|
const hasDiscountListAmount = normalizeArray(product.discount_list).some(discount => isNonZero(discount.amount ?? discount.value ?? discount.discount_amount));
|
|
@@ -474,7 +585,9 @@ function resolveDiscountTitleWithPercent(discount, locale) {
|
|
|
474
585
|
function resolveDiscountTitle(discount, locale) {
|
|
475
586
|
const title = getLocalizedValue(discount.discount?.name ?? discount.discount?.title ?? discount.discount?.message ?? discount.name ?? discount.title ?? discount.message, locale);
|
|
476
587
|
if (title) return title;
|
|
477
|
-
if (discount.type === 'product')
|
|
588
|
+
if (discount.type === 'product') {
|
|
589
|
+
return label(locale, isManualMarkup(discount) ? 'manualMarkup' : 'manualDiscount');
|
|
590
|
+
}
|
|
478
591
|
return '';
|
|
479
592
|
}
|
|
480
593
|
function resolveProductTitle(params) {
|
|
@@ -550,9 +663,16 @@ function normalizeProductCombinations(params) {
|
|
|
550
663
|
const quantity = toNumber(bundle.num ?? bundle.quantity ?? 1, 1);
|
|
551
664
|
const formattedPrice = formatBundlePrice(bundle, currencySymbol);
|
|
552
665
|
const optionsTotal = sumNormalizedOptionTotals(combinationsOptions, currencySymbol);
|
|
553
|
-
const explicitBasePrice =
|
|
666
|
+
const explicitBasePrice = resolveCombinationBasePrice(bundle);
|
|
554
667
|
const basePrice = isPresentId(explicitBasePrice) ? explicitBasePrice : 0;
|
|
555
|
-
const
|
|
668
|
+
const extensionList = buildProductExtensionList({
|
|
669
|
+
product: bundle,
|
|
670
|
+
locale,
|
|
671
|
+
currencySymbol,
|
|
672
|
+
quantity,
|
|
673
|
+
originalPrice: formattedPrice
|
|
674
|
+
});
|
|
675
|
+
const shouldIncludeBasePrice = combinationsOptions.length > 0 || nestedCombinations.length > 0 || hasProductDiscount(bundle, extensionList);
|
|
556
676
|
return {
|
|
557
677
|
product_title: baseTitle,
|
|
558
678
|
product_quantity: quantity,
|
|
@@ -571,6 +691,9 @@ function normalizeProductCombinations(params) {
|
|
|
571
691
|
...(productNote ? {
|
|
572
692
|
product_note: productNote
|
|
573
693
|
} : {}),
|
|
694
|
+
...(extensionList.length ? {
|
|
695
|
+
extension_list: extensionList
|
|
696
|
+
} : {}),
|
|
574
697
|
...(nestedCombinations.length ? {
|
|
575
698
|
combinations: nestedCombinations
|
|
576
699
|
} : {})
|
|
@@ -597,12 +720,20 @@ function normalizeExistingCombinations(combinations, locale, currencySymbol) {
|
|
|
597
720
|
delete snapshot.product_note;
|
|
598
721
|
delete snapshot.note;
|
|
599
722
|
delete snapshot.combinations;
|
|
723
|
+
delete snapshot.discount_list;
|
|
600
724
|
const quantity = toNumber(combination.product_quantity ?? combination.num ?? combination.quantity ?? 1, 1);
|
|
601
725
|
const unitPrice = firstPresentValue(combination.selling_price, combination.price, combination.original_price) ?? 0;
|
|
602
726
|
const optionsAmount = sumMoneyValues(normalizedOptions.map(option => option.total_price));
|
|
603
|
-
const explicitBasePrice =
|
|
727
|
+
const explicitBasePrice = resolveCombinationBasePrice(combination, true);
|
|
604
728
|
const basePrice = isPresentId(explicitBasePrice) ? explicitBasePrice : 0;
|
|
605
|
-
const
|
|
729
|
+
const extensionList = buildProductExtensionList({
|
|
730
|
+
product: combination,
|
|
731
|
+
locale,
|
|
732
|
+
currencySymbol,
|
|
733
|
+
quantity,
|
|
734
|
+
originalPrice: unitPrice
|
|
735
|
+
});
|
|
736
|
+
const shouldIncludeBasePrice = normalizedOptions.length > 0 || nestedCombinations.length > 0 || hasProductDiscount(combination, extensionList);
|
|
606
737
|
return {
|
|
607
738
|
...snapshot,
|
|
608
739
|
product_quantity: quantity,
|
|
@@ -624,6 +755,9 @@ function normalizeExistingCombinations(combinations, locale, currencySymbol) {
|
|
|
624
755
|
...(productNote ? {
|
|
625
756
|
product_note: productNote
|
|
626
757
|
} : {}),
|
|
758
|
+
...(extensionList.length ? {
|
|
759
|
+
extension_list: extensionList
|
|
760
|
+
} : {}),
|
|
627
761
|
...(nestedCombinations.length ? {
|
|
628
762
|
combinations: nestedCombinations
|
|
629
763
|
} : {})
|
|
@@ -633,6 +767,10 @@ function normalizeExistingCombinations(combinations, locale, currencySymbol) {
|
|
|
633
767
|
function sumMoneyValues(values) {
|
|
634
768
|
return values.reduce((sum, value) => sum.plus(toMoneyNumber(value)), new _decimal.default(0));
|
|
635
769
|
}
|
|
770
|
+
function resolveCombinationBasePrice(combination, preferOriginalPrice = false) {
|
|
771
|
+
const fallbackPrices = preferOriginalPrice ? [combination.original_price, combination.product_price, combination.price] : [combination.price, combination.original_price, combination.product_price];
|
|
772
|
+
return firstPresentValue(combination.base_price, combination.metadata?.source_product_price, ...fallbackPrices);
|
|
773
|
+
}
|
|
636
774
|
function sumNormalizedOptionTotals(options, currencySymbol) {
|
|
637
775
|
return formatCalculatedMoney(sumMoneyValues(options.map(option => option.total_price)), currencySymbol);
|
|
638
776
|
}
|
|
@@ -869,12 +1007,6 @@ function buildFees(params) {
|
|
|
869
1007
|
});
|
|
870
1008
|
const isPriceIncludeTax = firstPresentValue(summary.is_price_include_tax, order.is_price_include_tax, shopInfo.is_price_include_tax);
|
|
871
1009
|
const roundingAmount = firstPresentValue(summary.rounding_amount, order.rounding_amount);
|
|
872
|
-
if (isNonZero(roundingAmount)) {
|
|
873
|
-
fees.push({
|
|
874
|
-
item: label(locale, 'rounding'),
|
|
875
|
-
value: formatMoney(roundingAmount, currencySymbol)
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
1010
|
const payProcessingFee = financials.payProcessingFee;
|
|
879
1011
|
if (isNonZero(payProcessingFee)) {
|
|
880
1012
|
fees.push({
|
|
@@ -998,24 +1130,13 @@ function buildDelivery(order, locale) {
|
|
|
998
1130
|
function buildAppointment(order, locale) {
|
|
999
1131
|
const bookings = sortBookings(normalizeArray(order.bookings));
|
|
1000
1132
|
if (bookings.length) {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
const bookingUid = firstPresentValue(booking.metadata?.unique_identification_number, booking.booking_uid);
|
|
1009
|
-
return {
|
|
1010
|
-
...snapshot,
|
|
1011
|
-
...(isPresentId(productUid) ? {
|
|
1012
|
-
product_uid: productUid
|
|
1013
|
-
} : {}),
|
|
1014
|
-
...(isPresentId(bookingUid) ? {
|
|
1015
|
-
booking_uid: bookingUid
|
|
1016
|
-
} : {})
|
|
1017
|
-
};
|
|
1018
|
-
});
|
|
1133
|
+
const allResources = normalizeArray(order.resources || order.resource_list);
|
|
1134
|
+
return bookings.flatMap(booking => buildAppointmentRows({
|
|
1135
|
+
booking,
|
|
1136
|
+
allResources,
|
|
1137
|
+
bookingCount: bookings.length,
|
|
1138
|
+
locale
|
|
1139
|
+
}));
|
|
1019
1140
|
}
|
|
1020
1141
|
const tableName = order.table_name || order.metadata?.table_name || order.metadata?.table?.name || order.form_data?.table_name;
|
|
1021
1142
|
if (!tableName) return [];
|
|
@@ -1024,6 +1145,82 @@ function buildAppointment(order, locale) {
|
|
|
1024
1145
|
value: getLocalizedValue(tableName, locale)
|
|
1025
1146
|
}];
|
|
1026
1147
|
}
|
|
1148
|
+
function buildAppointmentRows(params) {
|
|
1149
|
+
const {
|
|
1150
|
+
booking,
|
|
1151
|
+
allResources,
|
|
1152
|
+
bookingCount,
|
|
1153
|
+
locale
|
|
1154
|
+
} = params;
|
|
1155
|
+
const productUid = stringify(firstPresentValue(booking.product_uid, booking.metadata?.product_uid)).trim();
|
|
1156
|
+
const bookingUid = stringify(firstPresentValue(booking.metadata?.unique_identification_number, booking.booking_uid)).trim();
|
|
1157
|
+
const association = {
|
|
1158
|
+
product_uid: productUid,
|
|
1159
|
+
booking_uid: bookingUid
|
|
1160
|
+
};
|
|
1161
|
+
const bookingId = stringify(firstPresentValue(booking.booking_id, booking.schedule_event_id, bookingUid)).trim();
|
|
1162
|
+
const bookingTime = formatBookingRange(booking, locale);
|
|
1163
|
+
const resourceText = formatBookingResources(resolveBookingResourcesForReceipt(booking, allResources, bookingCount), locale);
|
|
1164
|
+
const holder = getLocalizedValue(booking.holder?.name ?? booking.metadata?.holder?.name, locale).trim();
|
|
1165
|
+
return [{
|
|
1166
|
+
item: label(locale, 'bookingId'),
|
|
1167
|
+
value: bookingId,
|
|
1168
|
+
...association
|
|
1169
|
+
}, {
|
|
1170
|
+
item: label(locale, 'bookingDateTime'),
|
|
1171
|
+
value: bookingTime,
|
|
1172
|
+
...association
|
|
1173
|
+
}, {
|
|
1174
|
+
item: label(locale, 'bookingResource'),
|
|
1175
|
+
value: resourceText,
|
|
1176
|
+
...association
|
|
1177
|
+
}, {
|
|
1178
|
+
item: label(locale, 'bookingHolder'),
|
|
1179
|
+
value: holder,
|
|
1180
|
+
...association
|
|
1181
|
+
}].filter(row => Boolean(row.value));
|
|
1182
|
+
}
|
|
1183
|
+
function formatBookingRange(booking, locale) {
|
|
1184
|
+
const startDate = stringify(booking.start_date);
|
|
1185
|
+
const startTime = stringify(booking.start_time);
|
|
1186
|
+
const endDate = stringify(booking.end_date || startDate);
|
|
1187
|
+
const endTime = stringify(booking.end_time);
|
|
1188
|
+
const start = firstValidBookingDate(booking.start_at, booking.start_datetime, booking.start_date_time, `${startDate} ${startTime}`.trim());
|
|
1189
|
+
const end = firstValidBookingDate(booking.end_at, booking.end_datetime, booking.end_date_time, endTime ? `${endDate} ${endTime}`.trim() : '');
|
|
1190
|
+
if (!start) {
|
|
1191
|
+
return [startDate, startTime, endTime ? `- ${endDate} ${endTime}` : ''].filter(Boolean).join(' ');
|
|
1192
|
+
}
|
|
1193
|
+
const dateFormat = locale === 'en' ? 'DD/MM/YYYY' : 'YYYY/MM/DD';
|
|
1194
|
+
const startText = start.format(`${dateFormat} HH:mm`);
|
|
1195
|
+
if (!end) return startText;
|
|
1196
|
+
return `${startText} - ${end.isSame(start, 'day') ? end.format('HH:mm') : end.format(`${dateFormat} HH:mm`)}`;
|
|
1197
|
+
}
|
|
1198
|
+
function firstValidBookingDate(...values) {
|
|
1199
|
+
for (const value of values) {
|
|
1200
|
+
if (!isPresentId(value)) continue;
|
|
1201
|
+
const parsed = (0, _dayjs.default)(value);
|
|
1202
|
+
if (parsed.isValid()) return parsed;
|
|
1203
|
+
}
|
|
1204
|
+
return null;
|
|
1205
|
+
}
|
|
1206
|
+
function resolveBookingResourcesForReceipt(booking, allResources, bookingCount) {
|
|
1207
|
+
const embedded = normalizeArray(booking.resources).filter(resource => Boolean(resource) && typeof resource === 'object' && !Array.isArray(resource));
|
|
1208
|
+
const resourceIds = new Set([booking.resource_id, booking.relation_id, ...normalizeArray(booking.resource_ids), ...normalizeArray(booking.metadata?.resource_ids)].map(value => stringify(value).trim()).filter(Boolean));
|
|
1209
|
+
for (const resource of embedded) {
|
|
1210
|
+
const id = stringify(resource.id ?? resource.resource_id ?? resource.relation_id).trim();
|
|
1211
|
+
if (id) resourceIds.add(id);
|
|
1212
|
+
}
|
|
1213
|
+
if (resourceIds.size) {
|
|
1214
|
+
const matched = allResources.filter(resource => resourceIds.has(stringify(resource.id ?? resource.resource_id ?? resource.relation_id).trim()));
|
|
1215
|
+
if (matched.length) return matched;
|
|
1216
|
+
}
|
|
1217
|
+
if (embedded.length) return embedded;
|
|
1218
|
+
if (bookingCount === 1 && allResources.length) return allResources;
|
|
1219
|
+
return [];
|
|
1220
|
+
}
|
|
1221
|
+
function formatBookingResources(resources, locale) {
|
|
1222
|
+
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(', ');
|
|
1223
|
+
}
|
|
1027
1224
|
function sortBookings(bookings) {
|
|
1028
1225
|
return bookings.map((booking, index) => ({
|
|
1029
1226
|
booking,
|
|
@@ -1234,18 +1431,34 @@ function getPointCardSnapshotRemainingPoints(snapshot) {
|
|
|
1234
1431
|
}
|
|
1235
1432
|
function buildVoucherAssets(params) {
|
|
1236
1433
|
const seenVoucherKeys = new Set();
|
|
1434
|
+
const fallbackAssetTime = firstPresentValue(params.order.updated_at, params.order.created_at);
|
|
1237
1435
|
return normalizeArray(params.order.vouchers).flatMap((voucher, index) => {
|
|
1238
1436
|
if (!voucher || typeof voucher !== 'object' || Array.isArray(voucher)) return [];
|
|
1239
1437
|
const voucherKey = getVoucherAssetKey(voucher, index);
|
|
1240
1438
|
if (seenVoucherKeys.has(voucherKey)) return [];
|
|
1241
1439
|
seenVoucherKeys.add(voucherKey);
|
|
1242
1440
|
const name = getVoucherAssetName(voucher, params.locale);
|
|
1441
|
+
const assetTime = firstPresentValue(voucher.updated_at, voucher.created_at, voucher.collection_time, fallbackAssetTime);
|
|
1442
|
+
const timeRow = isPresentId(assetTime) ? {
|
|
1443
|
+
item: formatDateTime(assetTime, params.locale),
|
|
1444
|
+
value: ''
|
|
1445
|
+
} : null;
|
|
1446
|
+
if (isTicketVoucher(voucher)) {
|
|
1447
|
+
const validity = getVoucherValidity(voucher, params.locale);
|
|
1448
|
+
return [[{
|
|
1449
|
+
item: name,
|
|
1450
|
+
value: ''
|
|
1451
|
+
}, ...(validity ? [{
|
|
1452
|
+
item: label(params.locale, 'validity'),
|
|
1453
|
+
value: validity
|
|
1454
|
+
}] : [])]];
|
|
1455
|
+
}
|
|
1243
1456
|
const percent = getVoucherDiscountPercent(voucher);
|
|
1244
1457
|
if (percent) {
|
|
1245
1458
|
return [[{
|
|
1246
1459
|
item: appendPercentToVoucherName(name, percent),
|
|
1247
1460
|
value: formatVoucherDiscount(percent, params.locale)
|
|
1248
|
-
}]];
|
|
1461
|
+
}, ...(timeRow ? [timeRow] : [])]];
|
|
1249
1462
|
}
|
|
1250
1463
|
const balance = getVoucherBalance(voucher);
|
|
1251
1464
|
return [[{
|
|
@@ -1254,9 +1467,67 @@ function buildVoucherAssets(params) {
|
|
|
1254
1467
|
}, {
|
|
1255
1468
|
item: label(params.locale, 'balance'),
|
|
1256
1469
|
value: formatMoney(balance, params.currencySymbol)
|
|
1257
|
-
}]];
|
|
1470
|
+
}, ...(timeRow ? [timeRow] : [])]];
|
|
1258
1471
|
});
|
|
1259
1472
|
}
|
|
1473
|
+
function isTicketVoucher(voucher) {
|
|
1474
|
+
return stringify(voucher.tag).trim().toLowerCase() === 'ticket';
|
|
1475
|
+
}
|
|
1476
|
+
function getVoucherValidity(voucher, locale) {
|
|
1477
|
+
const availableTime = firstPresentValue(voucher.available_time, voucher.avaliable_time, voucher.metadata?.available_time, voucher.metadata?.avaliable_time);
|
|
1478
|
+
const availableTimeText = formatVoucherAvailableTime(availableTime, locale);
|
|
1479
|
+
if (availableTimeText) return availableTimeText;
|
|
1480
|
+
const startDate = firstPresentValue(voucher.valid_from, voucher.start_at, voucher.start_date_time, voucher.start_date, voucher.metadata?.valid_from, voucher.metadata?.start_at, voucher.metadata?.start_date);
|
|
1481
|
+
const endDate = firstPresentValue(voucher.valid_to, voucher.end_at, voucher.end_date_time, voucher.expire_date, voucher.end_date, voucher.expire_time, voucher.metadata?.valid_to, voucher.metadata?.end_at, voucher.metadata?.expire_date, voucher.metadata?.end_date, voucher.metadata?.expire_time);
|
|
1482
|
+
return formatVoucherValidityRange(startDate, endDate, locale, {
|
|
1483
|
+
startTime: voucher.start_time ?? voucher.metadata?.start_time,
|
|
1484
|
+
endTime: voucher.end_time ?? voucher.metadata?.end_time
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1487
|
+
function formatVoucherAvailableTime(value, locale) {
|
|
1488
|
+
if (!isPresentId(value)) return '';
|
|
1489
|
+
if (Array.isArray(value)) {
|
|
1490
|
+
return value.map(item => formatVoucherAvailableTime(item, locale)).filter(Boolean).join(', ');
|
|
1491
|
+
}
|
|
1492
|
+
if (typeof value !== 'object') return stringify(value).trim();
|
|
1493
|
+
const start = firstPresentValue(value.start, value.start_at, value.start_date_time, value.start_date);
|
|
1494
|
+
const end = firstPresentValue(value.end, value.end_at, value.end_date_time, value.expire_time, value.expire_date, value.end_date);
|
|
1495
|
+
if (isPresentId(start) || isPresentId(end)) {
|
|
1496
|
+
return formatVoucherValidityRange(start, end, locale, {
|
|
1497
|
+
startTime: value.start_time,
|
|
1498
|
+
endTime: value.end_time
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
return getLocalizedValue(value, locale).trim();
|
|
1502
|
+
}
|
|
1503
|
+
function formatVoucherValidityRange(startValue, endValue, locale, times = {}) {
|
|
1504
|
+
const start = combineVoucherDateAndTime(startValue, times.startTime);
|
|
1505
|
+
const end = combineVoucherDateAndTime(endValue, times.endTime);
|
|
1506
|
+
const startText = formatVoucherValidityDate(start, locale);
|
|
1507
|
+
const endText = formatVoucherValidityDate(end, locale);
|
|
1508
|
+
if (startText && endText) {
|
|
1509
|
+
const parsedStart = (0, _dayjs.default)(start);
|
|
1510
|
+
const parsedEnd = (0, _dayjs.default)(end);
|
|
1511
|
+
const sameDay = parsedStart.isValid() && parsedEnd.isValid() && parsedStart.isSame(parsedEnd, 'day');
|
|
1512
|
+
return `${startText} - ${sameDay && /\d{1,2}:\d{2}/.test(stringify(end)) ? parsedEnd.format('HH:mm') : endText}`;
|
|
1513
|
+
}
|
|
1514
|
+
return startText || endText;
|
|
1515
|
+
}
|
|
1516
|
+
function combineVoucherDateAndTime(dateValue, timeValue) {
|
|
1517
|
+
if (!isPresentId(dateValue) || !isPresentId(timeValue)) return dateValue;
|
|
1518
|
+
const dateText = stringify(dateValue).trim();
|
|
1519
|
+
if (/\d{1,2}:\d{2}/.test(dateText)) return dateValue;
|
|
1520
|
+
return `${dateText} ${stringify(timeValue).trim()}`;
|
|
1521
|
+
}
|
|
1522
|
+
function formatVoucherValidityDate(value, locale) {
|
|
1523
|
+
if (!isPresentId(value)) return '';
|
|
1524
|
+
const source = stringify(value).trim();
|
|
1525
|
+
const parsed = (0, _dayjs.default)(source);
|
|
1526
|
+
if (!parsed.isValid()) return source;
|
|
1527
|
+
const hasTime = /\d{1,2}:\d{2}/.test(source);
|
|
1528
|
+
if (locale === 'en') return parsed.format(hasTime ? 'DD/MM/YYYY HH:mm' : 'DD/MM/YYYY');
|
|
1529
|
+
return parsed.format(hasTime ? 'YYYY/MM/DD HH:mm' : 'YYYY/MM/DD');
|
|
1530
|
+
}
|
|
1260
1531
|
function getVoucherAssetKey(voucher, index) {
|
|
1261
1532
|
const identity = firstPresentValue(voucher.voucher_id, voucher.id, voucher.code, voucher.encoded);
|
|
1262
1533
|
return identity === undefined ? `voucher-index:${index}` : `voucher:${String(identity)}`;
|