@pisell/pisellos 0.10.28 → 0.10.30
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/core/index.d.ts +7 -0
- package/dist/core/index.js +108 -65
- package/dist/model/strategy/adapter/dataVariant/evaluator.js +13 -2
- package/dist/model/strategy/adapter/promotion/index.js +0 -9
- package/dist/modules/Discount/index.js +4 -1
- package/dist/modules/Discount/types.d.ts +14 -4
- package/dist/modules/Order/index.d.ts +3 -1
- package/dist/modules/Order/index.js +89 -16
- package/dist/modules/Order/types.d.ts +13 -0
- package/dist/modules/Order/utils.d.ts +17 -2
- package/dist/modules/Order/utils.js +133 -27
- package/dist/modules/Quotation/index.d.ts +6 -0
- package/dist/modules/Quotation/index.js +77 -25
- package/dist/modules/ResourcePlanner/planner.js +14 -4
- package/dist/modules/ResourcePlanner/types.d.ts +2 -0
- package/dist/plugins/request.d.ts +1 -0
- package/dist/server/index.d.ts +13 -2
- package/dist/server/index.js +1531 -1230
- package/dist/server/modules/floor-plan/index.d.ts +4 -0
- package/dist/server/modules/floor-plan/index.js +240 -98
- package/dist/server/modules/menu/index.js +48 -23
- package/dist/server/modules/order/index.d.ts +17 -7
- package/dist/server/modules/order/index.js +419 -207
- package/dist/server/modules/order/types.d.ts +50 -0
- package/dist/server/modules/order/types.js +2 -0
- package/dist/server/modules/products/index.d.ts +10 -4
- package/dist/server/modules/products/index.js +177 -84
- package/dist/server/modules/resource/index.js +21 -13
- package/dist/server/utils/product.d.ts +1 -1
- package/dist/server/utils/product.js +186 -22
- package/dist/server/utils/small-ticket.d.ts +4 -1
- package/dist/server/utils/small-ticket.js +543 -101
- package/dist/solution/BaseSales/index.d.ts +3 -3
- package/dist/solution/BaseSales/index.js +171 -198
- package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +2 -0
- package/dist/solution/BaseSales/utils/parseSalesResponse.js +1 -1
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +3 -2
- package/dist/solution/BookingByStep/index.d.ts +2 -2
- package/dist/solution/Sales/index.d.ts +7 -0
- package/dist/solution/Sales/index.js +87 -31
- package/dist/solution/UnifiedBookingSales/index.d.ts +2 -2
- package/dist/solution/UnifiedBookingSales/index.js +38 -21
- package/lib/core/index.d.ts +7 -0
- package/lib/core/index.js +20 -2
- package/lib/model/strategy/adapter/dataVariant/evaluator.js +12 -2
- package/lib/model/strategy/adapter/promotion/index.js +46 -1
- package/lib/modules/Discount/index.js +4 -1
- package/lib/modules/Discount/types.d.ts +14 -4
- package/lib/modules/Order/index.d.ts +3 -1
- package/lib/modules/Order/index.js +69 -1
- package/lib/modules/Order/types.d.ts +13 -0
- package/lib/modules/Order/utils.d.ts +17 -2
- package/lib/modules/Order/utils.js +131 -18
- package/lib/modules/Quotation/index.d.ts +6 -0
- package/lib/modules/Quotation/index.js +52 -8
- package/lib/modules/ResourcePlanner/planner.js +9 -1
- package/lib/modules/ResourcePlanner/types.d.ts +2 -0
- package/lib/plugins/request.d.ts +1 -0
- package/lib/server/index.d.ts +13 -2
- package/lib/server/index.js +210 -19
- package/lib/server/modules/floor-plan/index.d.ts +4 -0
- package/lib/server/modules/floor-plan/index.js +60 -7
- package/lib/server/modules/menu/index.js +26 -4
- package/lib/server/modules/order/index.d.ts +17 -7
- package/lib/server/modules/order/index.js +194 -57
- package/lib/server/modules/order/types.d.ts +50 -0
- package/lib/server/modules/order/types.js +1 -0
- package/lib/server/modules/products/index.d.ts +10 -4
- package/lib/server/modules/products/index.js +93 -23
- package/lib/server/modules/resource/index.js +12 -2
- package/lib/server/utils/product.d.ts +1 -1
- package/lib/server/utils/product.js +163 -3
- package/lib/server/utils/small-ticket.d.ts +4 -1
- package/lib/server/utils/small-ticket.js +507 -75
- package/lib/solution/BaseSales/index.d.ts +3 -3
- package/lib/solution/BaseSales/index.js +9 -14
- package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +2 -0
- package/lib/solution/BaseSales/utils/parseSalesResponse.js +1 -1
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +3 -2
- package/lib/solution/BookingByStep/index.d.ts +2 -2
- package/lib/solution/Sales/index.d.ts +7 -0
- package/lib/solution/Sales/index.js +48 -3
- package/lib/solution/UnifiedBookingSales/index.d.ts +2 -2
- package/lib/solution/UnifiedBookingSales/index.js +19 -3
- package/package.json +1 -1
|
@@ -22,9 +22,13 @@ const LABELS = {
|
|
|
22
22
|
pickUpTime: 'Pick-up time',
|
|
23
23
|
asap: 'ASAP',
|
|
24
24
|
cash: 'Cash',
|
|
25
|
+
cashReceived: 'Cash received',
|
|
26
|
+
changeDue: 'Change due',
|
|
25
27
|
eftpos: 'Eftpos',
|
|
26
28
|
wallet: 'Wallet',
|
|
27
29
|
giftCard: 'Gift card',
|
|
30
|
+
pointCard: 'Point card',
|
|
31
|
+
balance: 'Balance',
|
|
28
32
|
remainingAmount: 'Remaining amount',
|
|
29
33
|
redeemPoints: 'Redeem points',
|
|
30
34
|
remainingPoints: 'Remaining points',
|
|
@@ -32,9 +36,12 @@ const LABELS = {
|
|
|
32
36
|
orderDiscount: 'Order discount',
|
|
33
37
|
manualDiscount: 'Manual discount',
|
|
34
38
|
payProcessingFee: 'Pay processing fee',
|
|
39
|
+
surchargeIncluded: 'Surcharge(Included)',
|
|
35
40
|
rounding: 'Rounding',
|
|
36
41
|
gross: 'Gross',
|
|
37
|
-
tare: 'Tare'
|
|
42
|
+
tare: 'Tare',
|
|
43
|
+
handoverTime: 'Handover time',
|
|
44
|
+
refund: 'Refund'
|
|
38
45
|
},
|
|
39
46
|
zh_CN: {
|
|
40
47
|
contactName: '联系人姓名',
|
|
@@ -48,9 +55,13 @@ const LABELS = {
|
|
|
48
55
|
pickUpTime: '取餐时间',
|
|
49
56
|
asap: '尽快',
|
|
50
57
|
cash: '现金支付',
|
|
58
|
+
cashReceived: '收取现金',
|
|
59
|
+
changeDue: '找零',
|
|
51
60
|
eftpos: '刷卡支付',
|
|
52
61
|
wallet: '钱包支付',
|
|
53
62
|
giftCard: '礼品卡',
|
|
63
|
+
pointCard: '积分卡',
|
|
64
|
+
balance: '余额',
|
|
54
65
|
remainingAmount: '剩余金额',
|
|
55
66
|
redeemPoints: '本次使用积分',
|
|
56
67
|
remainingPoints: '剩余积分',
|
|
@@ -58,9 +69,12 @@ const LABELS = {
|
|
|
58
69
|
orderDiscount: '整单折扣',
|
|
59
70
|
manualDiscount: '手动折扣',
|
|
60
71
|
payProcessingFee: '支付手续费',
|
|
72
|
+
surchargeIncluded: '手续费(已含)',
|
|
61
73
|
rounding: '抹零',
|
|
62
74
|
gross: '重量',
|
|
63
|
-
tare: '去皮'
|
|
75
|
+
tare: '去皮',
|
|
76
|
+
handoverTime: '交付时间',
|
|
77
|
+
refund: '退款'
|
|
64
78
|
},
|
|
65
79
|
zh_HK: {
|
|
66
80
|
contactName: '聯絡人姓名',
|
|
@@ -74,9 +88,13 @@ const LABELS = {
|
|
|
74
88
|
pickUpTime: '取餐時間',
|
|
75
89
|
asap: '盡快',
|
|
76
90
|
cash: '現金支付',
|
|
91
|
+
cashReceived: '收取現金',
|
|
92
|
+
changeDue: '找零',
|
|
77
93
|
eftpos: '刷卡支付',
|
|
78
94
|
wallet: '錢包支付',
|
|
79
95
|
giftCard: '禮品卡',
|
|
96
|
+
pointCard: '積分卡',
|
|
97
|
+
balance: '餘額',
|
|
80
98
|
remainingAmount: '剩餘金額',
|
|
81
99
|
redeemPoints: '本次使用積分',
|
|
82
100
|
remainingPoints: '剩餘積分',
|
|
@@ -84,9 +102,12 @@ const LABELS = {
|
|
|
84
102
|
orderDiscount: '整單折扣',
|
|
85
103
|
manualDiscount: '手動折扣',
|
|
86
104
|
payProcessingFee: '支付手續費',
|
|
105
|
+
surchargeIncluded: '手續費(已含)',
|
|
87
106
|
rounding: '抹零',
|
|
88
107
|
gross: '重量',
|
|
89
|
-
tare: '去皮'
|
|
108
|
+
tare: '去皮',
|
|
109
|
+
handoverTime: '交付時間',
|
|
110
|
+
refund: '退款'
|
|
90
111
|
},
|
|
91
112
|
ja: {
|
|
92
113
|
contactName: '連絡先名',
|
|
@@ -103,6 +124,8 @@ const LABELS = {
|
|
|
103
124
|
eftpos: 'カード',
|
|
104
125
|
wallet: 'ウォレット',
|
|
105
126
|
giftCard: 'ギフトカード',
|
|
127
|
+
pointCard: 'ポイントカード',
|
|
128
|
+
balance: '残高',
|
|
106
129
|
remainingAmount: '残高',
|
|
107
130
|
redeemPoints: '利用ポイント',
|
|
108
131
|
remainingPoints: '残りポイント',
|
|
@@ -112,7 +135,11 @@ const LABELS = {
|
|
|
112
135
|
payProcessingFee: '決済手数料',
|
|
113
136
|
rounding: '端数調整',
|
|
114
137
|
gross: '総重量',
|
|
115
|
-
tare: '風袋'
|
|
138
|
+
tare: '風袋',
|
|
139
|
+
handoverTime: '受け渡し時間',
|
|
140
|
+
refund: '返金',
|
|
141
|
+
cashReceived: 'お預かり',
|
|
142
|
+
changeDue: 'お釣り'
|
|
116
143
|
},
|
|
117
144
|
pt: {
|
|
118
145
|
contactName: 'Nome de contato',
|
|
@@ -129,6 +156,8 @@ const LABELS = {
|
|
|
129
156
|
eftpos: 'Cartão',
|
|
130
157
|
wallet: 'Carteira',
|
|
131
158
|
giftCard: 'Cartão-presente',
|
|
159
|
+
pointCard: 'Cartão de pontos',
|
|
160
|
+
balance: 'Saldo',
|
|
132
161
|
remainingAmount: 'Saldo restante',
|
|
133
162
|
redeemPoints: 'Pontos utilizados',
|
|
134
163
|
remainingPoints: 'Pontos restantes',
|
|
@@ -138,7 +167,11 @@ const LABELS = {
|
|
|
138
167
|
payProcessingFee: 'Taxa de processamento',
|
|
139
168
|
rounding: 'Arredondamento',
|
|
140
169
|
gross: 'Peso bruto',
|
|
141
|
-
tare: 'Tara'
|
|
170
|
+
tare: 'Tara',
|
|
171
|
+
handoverTime: 'Hora de entrega',
|
|
172
|
+
refund: 'Reembolso',
|
|
173
|
+
cashReceived: 'Dinheiro recebido',
|
|
174
|
+
changeDue: 'Troco'
|
|
142
175
|
}
|
|
143
176
|
};
|
|
144
177
|
function buildSmallTicketData(params) {
|
|
@@ -169,11 +202,15 @@ function buildLocaleData(params) {
|
|
|
169
202
|
const products = buildProducts(order, locale, productMap);
|
|
170
203
|
const summary = getSummary(order);
|
|
171
204
|
const currencySymbol = getCurrencySymbol(order, shopInfo);
|
|
205
|
+
const financials = resolveReceiptFinancials(order, summary);
|
|
206
|
+
const delivery = buildDelivery(order, locale);
|
|
172
207
|
return {
|
|
173
208
|
base_data: buildBaseData({
|
|
174
209
|
order,
|
|
175
210
|
shopInfo,
|
|
176
|
-
locale
|
|
211
|
+
locale,
|
|
212
|
+
currencySymbol,
|
|
213
|
+
financials
|
|
177
214
|
}),
|
|
178
215
|
products,
|
|
179
216
|
fees: buildFees({
|
|
@@ -182,11 +219,14 @@ function buildLocaleData(params) {
|
|
|
182
219
|
locale,
|
|
183
220
|
products,
|
|
184
221
|
summary,
|
|
185
|
-
currencySymbol
|
|
222
|
+
currencySymbol,
|
|
223
|
+
financials
|
|
186
224
|
}),
|
|
187
225
|
customer: buildCustomer(order, locale),
|
|
188
226
|
appointment: buildAppointment(order, locale),
|
|
189
|
-
|
|
227
|
+
...(delivery.length ? {
|
|
228
|
+
delivery
|
|
229
|
+
} : {}),
|
|
190
230
|
resources: buildResources(order, locale),
|
|
191
231
|
payment_data: buildPaymentData({
|
|
192
232
|
order,
|
|
@@ -200,7 +240,11 @@ function buildLocaleData(params) {
|
|
|
200
240
|
}),
|
|
201
241
|
tickets: Array.isArray(order.tickets) ? order.tickets : [],
|
|
202
242
|
source_shop: [],
|
|
203
|
-
refund_data:
|
|
243
|
+
refund_data: buildRefundData({
|
|
244
|
+
order,
|
|
245
|
+
locale,
|
|
246
|
+
currencySymbol
|
|
247
|
+
}),
|
|
204
248
|
expend_data: []
|
|
205
249
|
};
|
|
206
250
|
}
|
|
@@ -208,7 +252,9 @@ function buildBaseData(params) {
|
|
|
208
252
|
const {
|
|
209
253
|
order,
|
|
210
254
|
shopInfo,
|
|
211
|
-
locale
|
|
255
|
+
locale,
|
|
256
|
+
currencySymbol,
|
|
257
|
+
financials
|
|
212
258
|
} = params;
|
|
213
259
|
const orderNumber = stringify(order.buzzer || order.shop_full_order_number);
|
|
214
260
|
return {
|
|
@@ -219,7 +265,13 @@ function buildBaseData(params) {
|
|
|
219
265
|
shop_tax_number: stringify(shopInfo.tax_number || order.shop_tax_number),
|
|
220
266
|
shop_phone: stringify(shopInfo.phone || order.shop_phone),
|
|
221
267
|
shop_address: stringify(shopInfo.receipt_address || shopInfo.address || buildShopAddress(shopInfo) || order.shop_address),
|
|
222
|
-
original_payment_status:
|
|
268
|
+
original_payment_status: financials.paymentStatus,
|
|
269
|
+
payment_status: financials.paymentStatus,
|
|
270
|
+
total_amount: formatMoney(financials.totalAmount, currencySymbol),
|
|
271
|
+
has_pay: formatMoney(financials.netPaymentApplied, currencySymbol),
|
|
272
|
+
...(isPresentId(financials.amountGap) ? {
|
|
273
|
+
amount_gap: formatMoney(financials.amountGap, currencySymbol)
|
|
274
|
+
} : {}),
|
|
223
275
|
is_repeat: 0,
|
|
224
276
|
buzzer: stringify(order.buzzer || order.pickup_number || order.metadata?.buzzer),
|
|
225
277
|
order_no: orderNumber,
|
|
@@ -269,11 +321,16 @@ function buildProducts(order, locale, productMap) {
|
|
|
269
321
|
quantity,
|
|
270
322
|
originalPrice
|
|
271
323
|
});
|
|
324
|
+
const productNote = resolveProductNote(product, locale);
|
|
325
|
+
const basePrice = resolveProductBasePrice(product);
|
|
272
326
|
return {
|
|
273
327
|
product_title: productTitle,
|
|
274
328
|
product_quantity: quantity,
|
|
275
329
|
selling_price: formatMoney(sellingPrice, currencySymbol),
|
|
276
330
|
original_price: formatMoney(originalPrice, currencySymbol),
|
|
331
|
+
...(isPresentId(basePrice) ? {
|
|
332
|
+
base_price: formatMoney(basePrice, currencySymbol)
|
|
333
|
+
} : {}),
|
|
277
334
|
total_original_price: formatMoney(new _decimal.default(toMoneyNumber(originalPrice)).mul(quantity), currencySymbol),
|
|
278
335
|
extension_list: extensionList,
|
|
279
336
|
...(isPresentId(productUid) ? {
|
|
@@ -282,20 +339,31 @@ function buildProducts(order, locale, productMap) {
|
|
|
282
339
|
...(isPresentId(bookingUid) ? {
|
|
283
340
|
booking_uid: bookingUid
|
|
284
341
|
} : {}),
|
|
342
|
+
...(productNote ? {
|
|
343
|
+
product_note: productNote
|
|
344
|
+
} : {}),
|
|
285
345
|
...(options.length ? {
|
|
286
346
|
options
|
|
287
347
|
} : {}),
|
|
288
348
|
...(resolvedVariant ? {
|
|
289
349
|
variant: resolvedVariant
|
|
290
350
|
} : {}),
|
|
291
|
-
...(Array.isArray(product.combinations) ? {
|
|
292
|
-
combinations: product.combinations
|
|
351
|
+
...(Array.isArray(product.combinations) && product.combinations.length ? {
|
|
352
|
+
combinations: normalizeExistingCombinations(product.combinations, locale)
|
|
293
353
|
} : combinations.length ? {
|
|
294
354
|
combinations
|
|
295
355
|
} : {})
|
|
296
356
|
};
|
|
297
357
|
});
|
|
298
358
|
}
|
|
359
|
+
function resolveProductBasePrice(product) {
|
|
360
|
+
const metadata = product.metadata || {};
|
|
361
|
+
const origin = product._origin || metadata.origin || {};
|
|
362
|
+
const variantId = product.product_variant_id ?? product.variant_id;
|
|
363
|
+
const variant = Array.isArray(origin.variant) && isPresentId(variantId) ? origin.variant.find(item => isSameId(item?.id, variantId)) : undefined;
|
|
364
|
+
const isV2 = metadata.price_schema_version === 2 || metadata.price_schema_version === '2';
|
|
365
|
+
return firstPresentValue(metadata.source_product_price, variant?.price, product._origin?.price, product._origin?.base_price, !isV2 ? metadata.main_product_original_price : undefined, product.base_price, product.price);
|
|
366
|
+
}
|
|
299
367
|
function normalizeProductDiscounts(params) {
|
|
300
368
|
const {
|
|
301
369
|
product,
|
|
@@ -463,15 +531,50 @@ function normalizeProductCombinations(params) {
|
|
|
463
531
|
locale,
|
|
464
532
|
parentProduct
|
|
465
533
|
});
|
|
466
|
-
const
|
|
534
|
+
const productNote = resolveProductNote(bundle, locale);
|
|
535
|
+
const nestedCombinations = Array.isArray(bundle.combinations) ? normalizeExistingCombinations(bundle.combinations, locale) : [];
|
|
467
536
|
return {
|
|
468
|
-
product_title:
|
|
537
|
+
product_title: baseTitle,
|
|
469
538
|
product_quantity: toNumber(bundle.num ?? bundle.quantity ?? 1, 1),
|
|
470
539
|
price: formatBundlePrice(bundle, currencySymbol),
|
|
471
|
-
combinations_options: combinationsOptions
|
|
540
|
+
combinations_options: combinationsOptions,
|
|
541
|
+
...(productNote ? {
|
|
542
|
+
product_note: productNote
|
|
543
|
+
} : {}),
|
|
544
|
+
...(nestedCombinations.length ? {
|
|
545
|
+
combinations: nestedCombinations
|
|
546
|
+
} : {})
|
|
472
547
|
};
|
|
473
548
|
});
|
|
474
549
|
}
|
|
550
|
+
function normalizeExistingCombinations(combinations, locale) {
|
|
551
|
+
return combinations.map(combination => {
|
|
552
|
+
if (!combination || typeof combination !== 'object' || Array.isArray(combination)) {
|
|
553
|
+
return combination;
|
|
554
|
+
}
|
|
555
|
+
const productNote = resolveProductNote(combination, locale);
|
|
556
|
+
const nestedCombinations = Array.isArray(combination.combinations) ? normalizeExistingCombinations(combination.combinations, locale) : undefined;
|
|
557
|
+
const snapshot = {
|
|
558
|
+
...combination
|
|
559
|
+
};
|
|
560
|
+
delete snapshot.product_note;
|
|
561
|
+
delete snapshot.note;
|
|
562
|
+
return {
|
|
563
|
+
...snapshot,
|
|
564
|
+
...(productNote ? {
|
|
565
|
+
product_note: productNote
|
|
566
|
+
} : {}),
|
|
567
|
+
...(nestedCombinations ? {
|
|
568
|
+
combinations: nestedCombinations
|
|
569
|
+
} : {})
|
|
570
|
+
};
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
function resolveProductNote(product, locale) {
|
|
574
|
+
const productNote = getLocalizedValue(product.product_note, locale).trim();
|
|
575
|
+
if (productNote) return productNote;
|
|
576
|
+
return getLocalizedValue(product.note, locale).trim();
|
|
577
|
+
}
|
|
475
578
|
function resolveBundleProductTitle(params) {
|
|
476
579
|
const {
|
|
477
580
|
bundle,
|
|
@@ -654,15 +757,6 @@ function isSameId(left, right) {
|
|
|
654
757
|
if (!isPresentId(left) || !isPresentId(right)) return false;
|
|
655
758
|
return String(left) === String(right);
|
|
656
759
|
}
|
|
657
|
-
function buildOptionTitleSuffix(options) {
|
|
658
|
-
const text = options.map(option => {
|
|
659
|
-
const name = stringify(option.name).trim();
|
|
660
|
-
if (!name) return '';
|
|
661
|
-
const quantity = option.num ?? 1;
|
|
662
|
-
return `${name} x ${quantity}`;
|
|
663
|
-
}).filter(Boolean).join(', ');
|
|
664
|
-
return text ? `(${text})` : '';
|
|
665
|
-
}
|
|
666
760
|
function formatBundlePrice(bundle, currencySymbol) {
|
|
667
761
|
const value = bundle.bundle_selling_price ?? bundle.bundle_sum_price ?? bundle.price ?? 0;
|
|
668
762
|
const amount = new _decimal.default(toMoneyNumber(value));
|
|
@@ -678,7 +772,8 @@ function buildFees(params) {
|
|
|
678
772
|
locale,
|
|
679
773
|
products,
|
|
680
774
|
summary,
|
|
681
|
-
currencySymbol
|
|
775
|
+
currencySymbol,
|
|
776
|
+
financials
|
|
682
777
|
} = params;
|
|
683
778
|
const fees = [];
|
|
684
779
|
for (const surcharge of normalizeArray(order.surcharges || summary.surcharges)) {
|
|
@@ -689,64 +784,101 @@ function buildFees(params) {
|
|
|
689
784
|
value: formatMoney(value, currencySymbol)
|
|
690
785
|
});
|
|
691
786
|
}
|
|
692
|
-
const discountAmount = order.shop_discount
|
|
787
|
+
const discountAmount = firstPresentValue(order.shop_discount, summary.discount_amount, order.discount_amount);
|
|
693
788
|
if (isNonZero(discountAmount)) {
|
|
694
789
|
fees.push({
|
|
695
790
|
item: label(locale, 'orderDiscount'),
|
|
696
791
|
value: formatNegativeMoney(discountAmount, currencySymbol)
|
|
697
792
|
});
|
|
698
793
|
}
|
|
699
|
-
const taxFee = summary.tax_fee
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
}
|
|
706
|
-
const
|
|
794
|
+
const taxFee = firstPresentValue(summary.tax_fee, order.tax_fee);
|
|
795
|
+
const taxTitle = resolveTaxTitle({
|
|
796
|
+
summary,
|
|
797
|
+
order,
|
|
798
|
+
shopInfo,
|
|
799
|
+
locale
|
|
800
|
+
});
|
|
801
|
+
const isPriceIncludeTax = firstPresentValue(summary.is_price_include_tax, order.is_price_include_tax, shopInfo.is_price_include_tax);
|
|
802
|
+
const roundingAmount = firstPresentValue(summary.rounding_amount, order.rounding_amount);
|
|
707
803
|
if (isNonZero(roundingAmount)) {
|
|
708
804
|
fees.push({
|
|
709
805
|
item: label(locale, 'rounding'),
|
|
710
806
|
value: formatMoney(roundingAmount, currencySymbol)
|
|
711
807
|
});
|
|
712
808
|
}
|
|
713
|
-
const payProcessingFee =
|
|
809
|
+
const payProcessingFee = financials.payProcessingFee;
|
|
714
810
|
if (isNonZero(payProcessingFee)) {
|
|
715
811
|
fees.push({
|
|
716
812
|
item: label(locale, 'payProcessingFee'),
|
|
717
813
|
value: formatMoney(payProcessingFee, currencySymbol)
|
|
718
814
|
});
|
|
719
815
|
}
|
|
720
|
-
const productExpectAmount = summary.product_expect_amount
|
|
816
|
+
const productExpectAmount = firstPresentValue(summary.product_expect_amount, summary.product_amount, order.product_expect_amount);
|
|
721
817
|
const productOriginalAmount = firstPresentValue(summary.product_original_amount, order.product_original_amount) ?? calculateProductOriginalAmount(order);
|
|
722
818
|
const totalSavingsAmount = new _decimal.default(toMoneyNumber(productOriginalAmount)).minus(toMoneyNumber(productExpectAmount)).plus(toMoneyNumber(discountAmount));
|
|
723
819
|
return {
|
|
724
820
|
fees,
|
|
725
821
|
countFee: {
|
|
726
|
-
total_amount: formatMoney(summary.total_amount
|
|
822
|
+
total_amount: formatMoney(firstPresentValue(summary.total_amount, order.total_amount) ?? 0, currencySymbol),
|
|
727
823
|
original_item_amount: formatMoney(productOriginalAmount, currencySymbol),
|
|
728
|
-
product_expect_amount: formatMoney(productExpectAmount, currencySymbol),
|
|
729
|
-
expect_amount: formatMoney(
|
|
730
|
-
product_quantity: toNumber(summary.product_quantity
|
|
731
|
-
has_pay: formatMoney(
|
|
824
|
+
product_expect_amount: formatMoney(productExpectAmount ?? 0, currencySymbol),
|
|
825
|
+
expect_amount: formatMoney(financials.totalAmount, currencySymbol),
|
|
826
|
+
product_quantity: toNumber(firstPresentValue(summary.product_quantity, products.reduce((sum, product) => sum + product.product_quantity, 0)), 0),
|
|
827
|
+
has_pay: formatMoney(financials.orderPaidAmount, currencySymbol),
|
|
732
828
|
payment_method: '',
|
|
733
|
-
tax_title:
|
|
734
|
-
total_refund_amount: formatMoney(
|
|
735
|
-
|
|
829
|
+
tax_title: taxTitle,
|
|
830
|
+
total_refund_amount: formatMoney(financials.totalRefundAmount, currencySymbol),
|
|
831
|
+
customer_paid_amount: formatMoney(financials.customerPaidAmount, currencySymbol),
|
|
832
|
+
net_payment_applied: formatMoney(financials.netPaymentApplied, currencySymbol),
|
|
833
|
+
...(isPresentId(roundingAmount) ? {
|
|
736
834
|
rounding_amount: formatMoney(roundingAmount, currencySymbol)
|
|
737
835
|
} : {}),
|
|
738
836
|
...(isNonZero(totalSavingsAmount) ? {
|
|
739
837
|
order_total_discount: formatMoney(totalSavingsAmount, currencySymbol)
|
|
740
838
|
} : {}),
|
|
741
|
-
...(
|
|
839
|
+
...(financials.hasPayProcessingFee ? {
|
|
742
840
|
pay_processing_fee: formatMoney(payProcessingFee, currencySymbol)
|
|
743
841
|
} : {}),
|
|
744
|
-
...(
|
|
842
|
+
...(isPresentId(taxFee) ? {
|
|
745
843
|
tax_fee: formatMoney(taxFee, currencySymbol)
|
|
844
|
+
} : {}),
|
|
845
|
+
...(isPresentId(isPriceIncludeTax) ? {
|
|
846
|
+
is_price_include_tax: normalizeBooleanFlag(isPriceIncludeTax)
|
|
746
847
|
} : {})
|
|
747
848
|
}
|
|
748
849
|
};
|
|
749
850
|
}
|
|
851
|
+
function resolveReceiptFinancials(order, summary) {
|
|
852
|
+
const totalAmount = firstPresentValue(summary.expect_amount, order.expect_amount, summary.total_amount, order.total_amount) ?? 0;
|
|
853
|
+
const rawPayProcessingFee = firstPresentValue(summary.pay_service_charge_amount, order.pay_service_charge_amount);
|
|
854
|
+
const payProcessingFee = rawPayProcessingFee ?? 0;
|
|
855
|
+
const orderPaidAmount = firstPresentValue(summary.order_paid_amount, order.order_paid_amount, sumAppliedPayments(order.payments)) ?? 0;
|
|
856
|
+
const totalRefundAmount = firstPresentValue(summary.total_refund_amount, order.total_refund_amount) ?? 0;
|
|
857
|
+
const netPaymentApplied = _decimal.default.max(new _decimal.default(toMoneyNumber(orderPaidAmount)).minus(toMoneyNumber(totalRefundAmount)), 0);
|
|
858
|
+
const fallbackCustomerPaidAmount = _decimal.default.max(sumCustomerPaidPayments(order.payments), new _decimal.default(toMoneyNumber(orderPaidAmount)).plus(toMoneyNumber(payProcessingFee)));
|
|
859
|
+
const customerPaidAmount = firstPresentValue(summary.customer_paid_amount, order.customer_paid_amount, fallbackCustomerPaidAmount) ?? 0;
|
|
860
|
+
const amountGap = firstPresentValue(summary.amount_gap, order.amount_gap);
|
|
861
|
+
return {
|
|
862
|
+
totalAmount,
|
|
863
|
+
orderPaidAmount,
|
|
864
|
+
payProcessingFee,
|
|
865
|
+
customerPaidAmount,
|
|
866
|
+
totalRefundAmount,
|
|
867
|
+
netPaymentApplied,
|
|
868
|
+
amountGap,
|
|
869
|
+
paymentStatus: resolveReceiptPaymentStatus({
|
|
870
|
+
order,
|
|
871
|
+
summary
|
|
872
|
+
}),
|
|
873
|
+
hasPayProcessingFee: isPresentId(rawPayProcessingFee)
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function resolveReceiptPaymentStatus(params) {
|
|
877
|
+
return [params.order.payment_status, params.summary.payment_status, params.order.original_payment_status].map(status => stringify(status).trim()).find(Boolean) || '';
|
|
878
|
+
}
|
|
879
|
+
function resolveTaxTitle(params) {
|
|
880
|
+
return [params.summary.tax_title, params.order.tax_title, params.shopInfo.tax_title].map(title => getLocalizedValue(title, params.locale).trim()).find(Boolean) || 'Tax';
|
|
881
|
+
}
|
|
750
882
|
function calculateProductOriginalAmount(order) {
|
|
751
883
|
return normalizeArray(order.products).reduce((total, product) => {
|
|
752
884
|
const quantity = toNumber(product.product_quantity ?? product.quantity ?? product.num ?? 1, 1);
|
|
@@ -773,25 +905,27 @@ function normalizeCustomerDisplayValue(value) {
|
|
|
773
905
|
return text;
|
|
774
906
|
}
|
|
775
907
|
function buildDelivery(order, locale) {
|
|
776
|
-
const
|
|
777
|
-
|
|
778
|
-
const serviceTypeLabelKey =
|
|
908
|
+
const deliveryType = stringify(order.delivery_type).trim();
|
|
909
|
+
const serviceType = deliveryType || stringify(firstPresentValue(order.metadata?.service_type, order.service_type)).trim();
|
|
910
|
+
const serviceTypeLabelKey = resolveServiceTypeLabelKey(serviceType, order);
|
|
911
|
+
if (!serviceTypeLabelKey) return [];
|
|
779
912
|
const items = [{
|
|
780
913
|
item: '',
|
|
781
914
|
value: label(locale, serviceTypeLabelKey),
|
|
782
915
|
size: 2,
|
|
783
916
|
align: 'left'
|
|
784
917
|
}];
|
|
785
|
-
|
|
918
|
+
const handoverTime = firstPresentValue(order.handover_time, order.handover_at, order.pickup_time, order.pickup_at, order.scheduled_pickup_time, order.delivery_time, order.delivery_at, order.scheduled_delivery_time, order.fulfillment_time, order.fulfillment_at, order.metadata?.handover_time, order.metadata?.handover_at, order.metadata?.pickup_time, order.metadata?.pickup_at, order.metadata?.scheduled_pickup_time, order.metadata?.delivery_time, order.metadata?.delivery_at, order.metadata?.scheduled_delivery_time, order.metadata?.fulfillment_time, order.metadata?.fulfillment_at);
|
|
919
|
+
if (isPresentId(handoverTime)) {
|
|
786
920
|
items.push({
|
|
787
|
-
item: label(locale, '
|
|
788
|
-
value:
|
|
921
|
+
item: label(locale, 'handoverTime'),
|
|
922
|
+
value: formatDateTime(handoverTime, locale)
|
|
789
923
|
});
|
|
790
924
|
}
|
|
791
925
|
return items;
|
|
792
926
|
}
|
|
793
927
|
function buildAppointment(order, locale) {
|
|
794
|
-
const bookings = normalizeArray(order.bookings);
|
|
928
|
+
const bookings = sortBookings(normalizeArray(order.bookings));
|
|
795
929
|
if (bookings.length) {
|
|
796
930
|
return bookings.map(booking => {
|
|
797
931
|
const snapshot = {
|
|
@@ -819,6 +953,43 @@ function buildAppointment(order, locale) {
|
|
|
819
953
|
value: getLocalizedValue(tableName, locale)
|
|
820
954
|
}];
|
|
821
955
|
}
|
|
956
|
+
function sortBookings(bookings) {
|
|
957
|
+
return bookings.map((booking, index) => ({
|
|
958
|
+
booking,
|
|
959
|
+
index
|
|
960
|
+
})).sort((left, right) => {
|
|
961
|
+
const leftStart = bookingStartTimestamp(left.booking);
|
|
962
|
+
const rightStart = bookingStartTimestamp(right.booking);
|
|
963
|
+
if (leftStart !== rightStart) return leftStart < rightStart ? -1 : 1;
|
|
964
|
+
for (const field of ['booking_id', 'schedule_event_id']) {
|
|
965
|
+
const fieldOrder = compareOptionalValues(left.booking?.[field], right.booking?.[field]);
|
|
966
|
+
if (fieldOrder !== 0) return fieldOrder;
|
|
967
|
+
}
|
|
968
|
+
const bookingUidOrder = compareOptionalValues(firstPresentValue(left.booking?.metadata?.unique_identification_number, left.booking?.booking_uid), firstPresentValue(right.booking?.metadata?.unique_identification_number, right.booking?.booking_uid));
|
|
969
|
+
return bookingUidOrder || left.index - right.index;
|
|
970
|
+
}).map(({
|
|
971
|
+
booking
|
|
972
|
+
}) => booking);
|
|
973
|
+
}
|
|
974
|
+
function bookingStartTimestamp(booking) {
|
|
975
|
+
const startValues = [booking?.start_at, booking?.start_datetime, booking?.booking_time, isPresentId(booking?.start_date) ? `${booking.start_date} ${stringify(booking.start_time)}`.trim() : undefined];
|
|
976
|
+
for (const startValue of startValues) {
|
|
977
|
+
if (!isPresentId(startValue)) continue;
|
|
978
|
+
const timestamp = (0, _dayjs.default)(startValue).valueOf();
|
|
979
|
+
if (Number.isFinite(timestamp)) return timestamp;
|
|
980
|
+
}
|
|
981
|
+
return Number.POSITIVE_INFINITY;
|
|
982
|
+
}
|
|
983
|
+
function compareOptionalValues(left, right) {
|
|
984
|
+
const hasLeft = isPresentId(left);
|
|
985
|
+
const hasRight = isPresentId(right);
|
|
986
|
+
if (!hasLeft && !hasRight) return 0;
|
|
987
|
+
if (!hasLeft) return 1;
|
|
988
|
+
if (!hasRight) return -1;
|
|
989
|
+
return String(left).localeCompare(String(right), undefined, {
|
|
990
|
+
numeric: true
|
|
991
|
+
});
|
|
992
|
+
}
|
|
822
993
|
function buildResources(order, locale) {
|
|
823
994
|
return normalizeArray(order.resources || order.resource_list).map(resource => ({
|
|
824
995
|
id: resource.id || resource.resource_id || '',
|
|
@@ -836,13 +1007,205 @@ function buildPaymentData(params) {
|
|
|
836
1007
|
includeFundRecordBalance: false
|
|
837
1008
|
}));
|
|
838
1009
|
}
|
|
1010
|
+
function buildRefundData(params) {
|
|
1011
|
+
const payments = normalizeArray(params.order.payments);
|
|
1012
|
+
const refunds = Array.from(normalizeArray(params.order.refunds).reduce((result, refund, index) => {
|
|
1013
|
+
const identity = refundIdentity(refund, index);
|
|
1014
|
+
const candidate = {
|
|
1015
|
+
refund,
|
|
1016
|
+
index
|
|
1017
|
+
};
|
|
1018
|
+
const existing = result.get(identity);
|
|
1019
|
+
if (!existing || shouldPreferRefundCandidate(candidate, existing)) {
|
|
1020
|
+
result.set(identity, candidate);
|
|
1021
|
+
}
|
|
1022
|
+
return result;
|
|
1023
|
+
}, new Map()).values()).filter(({
|
|
1024
|
+
refund
|
|
1025
|
+
}) => isPrintableRefund(refund)).sort((left, right) => {
|
|
1026
|
+
const leftTime = refundTimestamp(left.refund);
|
|
1027
|
+
const rightTime = refundTimestamp(right.refund);
|
|
1028
|
+
if (leftTime !== rightTime) return leftTime < rightTime ? -1 : 1;
|
|
1029
|
+
return left.index - right.index;
|
|
1030
|
+
});
|
|
1031
|
+
return refunds.map(({
|
|
1032
|
+
refund
|
|
1033
|
+
}) => {
|
|
1034
|
+
const payment = resolveRefundPayment(refund, payments);
|
|
1035
|
+
const methodName = paymentMethodName(payment, params.locale) || label(params.locale, 'refund');
|
|
1036
|
+
const amount = firstPresentValue(refund.amount, refund.refund_amount, refund.total_amount) ?? 0;
|
|
1037
|
+
const items = [{
|
|
1038
|
+
item: methodName,
|
|
1039
|
+
value: formatNegativeMoney(amount, params.currencySymbol)
|
|
1040
|
+
}];
|
|
1041
|
+
const occurredAt = firstPresentValue(refund.refund_time, refund.refunded_at, refund.created_at, refund.updated_at);
|
|
1042
|
+
if (isPresentId(occurredAt)) {
|
|
1043
|
+
items.push({
|
|
1044
|
+
item: formatDateTime(occurredAt, params.locale),
|
|
1045
|
+
value: ''
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
return items;
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
function refundIdentity(refund, index) {
|
|
1052
|
+
if (isPresentId(refund.order_refund_id)) return `refund-id:${refund.order_refund_id}`;
|
|
1053
|
+
if (isPresentId(refund.refund_number)) return `refund-number:${refund.refund_number}`;
|
|
1054
|
+
return `refund-index:${index}`;
|
|
1055
|
+
}
|
|
1056
|
+
function shouldPreferRefundCandidate(candidate, existing) {
|
|
1057
|
+
const candidatePrintable = isPrintableRefund(candidate.refund);
|
|
1058
|
+
const existingPrintable = isPrintableRefund(existing.refund);
|
|
1059
|
+
if (candidatePrintable !== existingPrintable) return candidatePrintable;
|
|
1060
|
+
const candidateTime = refundFreshnessTimestamp(candidate.refund);
|
|
1061
|
+
const existingTime = refundFreshnessTimestamp(existing.refund);
|
|
1062
|
+
if (candidateTime !== existingTime) return candidateTime > existingTime;
|
|
1063
|
+
return candidate.index > existing.index;
|
|
1064
|
+
}
|
|
1065
|
+
function refundFreshnessTimestamp(refund) {
|
|
1066
|
+
const timestampValue = firstPresentValue(refund.updated_at, refund.refunded_at, refund.refund_time, refund.created_at);
|
|
1067
|
+
if (!isPresentId(timestampValue)) return Number.NEGATIVE_INFINITY;
|
|
1068
|
+
const timestamp = (0, _dayjs.default)(timestampValue).valueOf();
|
|
1069
|
+
return Number.isFinite(timestamp) ? timestamp : Number.NEGATIVE_INFINITY;
|
|
1070
|
+
}
|
|
1071
|
+
function refundTimestamp(refund) {
|
|
1072
|
+
const occurredAt = firstPresentValue(refund.refund_time, refund.refunded_at, refund.created_at, refund.updated_at);
|
|
1073
|
+
if (!isPresentId(occurredAt)) return Number.POSITIVE_INFINITY;
|
|
1074
|
+
const timestamp = (0, _dayjs.default)(occurredAt).valueOf();
|
|
1075
|
+
return Number.isFinite(timestamp) ? timestamp : Number.POSITIVE_INFINITY;
|
|
1076
|
+
}
|
|
1077
|
+
function isPrintableRefund(refund) {
|
|
1078
|
+
const status = stringify(refund.status).trim().toLowerCase();
|
|
1079
|
+
if (!status) return true;
|
|
1080
|
+
return !['pending', 'payment_pending', 'payment_processing', 'refund_processing', 'processing', 'failed', 'error', 'cancelled', 'canceled', 'voided'].includes(status);
|
|
1081
|
+
}
|
|
1082
|
+
function resolveRefundPayment(refund, payments) {
|
|
1083
|
+
if (refund.payment && typeof refund.payment === 'object' && !Array.isArray(refund.payment)) {
|
|
1084
|
+
return refund.payment;
|
|
1085
|
+
}
|
|
1086
|
+
const orderPaymentId = firstPresentValue(refund.order_payment_id, refund.payment_id);
|
|
1087
|
+
const matchedPayment = payments.find(payment => isSameId(firstPresentValue(payment.order_payment_id, payment.id), orderPaymentId));
|
|
1088
|
+
return matchedPayment || refund;
|
|
1089
|
+
}
|
|
839
1090
|
function buildWallets(params) {
|
|
840
|
-
|
|
1091
|
+
const displayedWalletPayments = normalizeArray(params.order.payments).filter(payment => isPrintablePayment(payment) && !!getFundRecord(payment));
|
|
1092
|
+
const paymentWallets = displayedWalletPayments.map(payment => buildPaymentItems({
|
|
841
1093
|
payment,
|
|
842
1094
|
locale: params.locale,
|
|
843
1095
|
currencySymbol: params.currencySymbol,
|
|
844
1096
|
includeFundRecordBalance: true
|
|
845
1097
|
}));
|
|
1098
|
+
const displayedWalletPassIds = collectDisplayedWalletPassIds(displayedWalletPayments);
|
|
1099
|
+
return [...paymentWallets, ...buildVoucherAssets(params), ...buildPointCardSnapshotAssets({
|
|
1100
|
+
order: params.order,
|
|
1101
|
+
locale: params.locale,
|
|
1102
|
+
displayedWalletPassIds
|
|
1103
|
+
})];
|
|
1104
|
+
}
|
|
1105
|
+
function collectDisplayedWalletPassIds(payments) {
|
|
1106
|
+
const ids = new Set();
|
|
1107
|
+
payments.forEach(payment => {
|
|
1108
|
+
const fundRecord = getFundRecord(payment);
|
|
1109
|
+
[payment.voucher_id, fundRecord?.voucher_id].forEach(value => {
|
|
1110
|
+
const id = normalizeWalletPassId(value);
|
|
1111
|
+
if (id) ids.add(id);
|
|
1112
|
+
});
|
|
1113
|
+
});
|
|
1114
|
+
return ids;
|
|
1115
|
+
}
|
|
1116
|
+
function buildPointCardSnapshotAssets(params) {
|
|
1117
|
+
const snapshots = params.order.order_meta_list?.point_cards_snapshot;
|
|
1118
|
+
if (!Array.isArray(snapshots)) return [];
|
|
1119
|
+
const seenSnapshotIds = new Set();
|
|
1120
|
+
return snapshots.flatMap(snapshot => {
|
|
1121
|
+
if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) return [];
|
|
1122
|
+
const pointCard = snapshot;
|
|
1123
|
+
if (stringify(pointCard.tag).trim().toLowerCase() !== 'point_card') return [];
|
|
1124
|
+
const id = normalizeWalletPassId(pointCard.id);
|
|
1125
|
+
if (!id || seenSnapshotIds.has(id)) return [];
|
|
1126
|
+
seenSnapshotIds.add(id);
|
|
1127
|
+
if (params.displayedWalletPassIds.has(id)) return [];
|
|
1128
|
+
const rows = [{
|
|
1129
|
+
item: stringify(pointCard.product_name).trim() || label(params.locale, 'pointCard'),
|
|
1130
|
+
value: ''
|
|
1131
|
+
}];
|
|
1132
|
+
const remainingPoints = getPointCardSnapshotRemainingPoints(pointCard);
|
|
1133
|
+
if (remainingPoints) {
|
|
1134
|
+
rows.push({
|
|
1135
|
+
item: label(params.locale, 'remainingPoints'),
|
|
1136
|
+
value: trimDecimal(remainingPoints)
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
return [rows];
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
function normalizeWalletPassId(value) {
|
|
1143
|
+
if (typeof value === 'number') return Number.isFinite(value) ? String(value) : null;
|
|
1144
|
+
if (typeof value !== 'string') return null;
|
|
1145
|
+
return value.trim() || null;
|
|
1146
|
+
}
|
|
1147
|
+
function getPointCardSnapshotRemainingPoints(snapshot) {
|
|
1148
|
+
if (!isNumericAmount(snapshot.par_value) || !isNumericAmount(snapshot.used_par_value)) {
|
|
1149
|
+
return null;
|
|
1150
|
+
}
|
|
1151
|
+
return _decimal.default.max(new _decimal.default(snapshot.par_value).minus(new _decimal.default(snapshot.used_par_value)), 0);
|
|
1152
|
+
}
|
|
1153
|
+
function buildVoucherAssets(params) {
|
|
1154
|
+
const seenVoucherKeys = new Set();
|
|
1155
|
+
return normalizeArray(params.order.vouchers).flatMap((voucher, index) => {
|
|
1156
|
+
if (!voucher || typeof voucher !== 'object' || Array.isArray(voucher)) return [];
|
|
1157
|
+
const voucherKey = getVoucherAssetKey(voucher, index);
|
|
1158
|
+
if (seenVoucherKeys.has(voucherKey)) return [];
|
|
1159
|
+
seenVoucherKeys.add(voucherKey);
|
|
1160
|
+
const name = getVoucherAssetName(voucher, params.locale);
|
|
1161
|
+
const percent = getVoucherDiscountPercent(voucher);
|
|
1162
|
+
if (percent) {
|
|
1163
|
+
return [[{
|
|
1164
|
+
item: appendPercentToVoucherName(name, percent),
|
|
1165
|
+
value: formatVoucherDiscount(percent, params.locale)
|
|
1166
|
+
}]];
|
|
1167
|
+
}
|
|
1168
|
+
const balance = getVoucherBalance(voucher);
|
|
1169
|
+
return [[{
|
|
1170
|
+
item: name,
|
|
1171
|
+
value: ''
|
|
1172
|
+
}, {
|
|
1173
|
+
item: label(params.locale, 'balance'),
|
|
1174
|
+
value: formatMoney(balance, params.currencySymbol)
|
|
1175
|
+
}]];
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
function getVoucherAssetKey(voucher, index) {
|
|
1179
|
+
const identity = firstPresentValue(voucher.voucher_id, voucher.id, voucher.code, voucher.encoded);
|
|
1180
|
+
return identity === undefined ? `voucher-index:${index}` : `voucher:${String(identity)}`;
|
|
1181
|
+
}
|
|
1182
|
+
function getVoucherAssetName(voucher, locale) {
|
|
1183
|
+
return getLocalizedValue(voucher.product_name || voucher.name || voucher.title, locale) || stringify(voucher.encoded || voucher.code || label(locale, 'giftCard'));
|
|
1184
|
+
}
|
|
1185
|
+
function getVoucherDiscountPercent(voucher) {
|
|
1186
|
+
const discountType = stringify(voucher.metadata?.discount_card_type || voucher.discount_card_type).toLowerCase();
|
|
1187
|
+
if (discountType !== 'percent' && discountType !== 'percentage') return null;
|
|
1188
|
+
const percent = new _decimal.default(toMoneyNumber(voucher.par_value));
|
|
1189
|
+
if (!percent.isFinite() || percent.lte(0) || percent.gt(100)) return null;
|
|
1190
|
+
return percent;
|
|
1191
|
+
}
|
|
1192
|
+
function appendPercentToVoucherName(name, percent) {
|
|
1193
|
+
if (/\d+(?:\.\d+)?\s*(?:%|折)|\boff\b/i.test(name)) return name;
|
|
1194
|
+
const separator = /^[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]/u.test(name) ? '' : ' ';
|
|
1195
|
+
return `${trimDecimal(percent)}%${separator}${name}`;
|
|
1196
|
+
}
|
|
1197
|
+
function formatVoucherDiscount(percent, locale) {
|
|
1198
|
+
const normalizedLocale = normalizeLocaleKey(locale);
|
|
1199
|
+
if (normalizedLocale === 'zh_CN' || normalizedLocale === 'zh_HK') {
|
|
1200
|
+
return `${trimDecimal(percent)}% OFF`;
|
|
1201
|
+
}
|
|
1202
|
+
if (normalizedLocale === 'pt') return `Desconto ${trimDecimal(percent)}%`;
|
|
1203
|
+
return normalizedLocale === 'en' ? `Discount ${trimDecimal(percent)}% OFF` : `${trimDecimal(percent)}% OFF`;
|
|
1204
|
+
}
|
|
1205
|
+
function getVoucherBalance(voucher) {
|
|
1206
|
+
const parValue = new _decimal.default(toMoneyNumber(voucher.par_value));
|
|
1207
|
+
const usedParValue = new _decimal.default(toMoneyNumber(voucher.used_par_value));
|
|
1208
|
+
return _decimal.default.max(parValue.minus(usedParValue), 0);
|
|
846
1209
|
}
|
|
847
1210
|
function buildPaymentItems(params) {
|
|
848
1211
|
const {
|
|
@@ -851,10 +1214,22 @@ function buildPaymentItems(params) {
|
|
|
851
1214
|
currencySymbol,
|
|
852
1215
|
includeFundRecordBalance
|
|
853
1216
|
} = params;
|
|
1217
|
+
const serviceFee = getPaymentServiceFee(payment);
|
|
854
1218
|
const items = [{
|
|
855
1219
|
item: paymentMethodName(payment, locale),
|
|
856
|
-
value: formatMoney(payment
|
|
1220
|
+
value: formatMoney(resolveReceiptPaymentAmount(payment, serviceFee), currencySymbol)
|
|
857
1221
|
}];
|
|
1222
|
+
items.push(...buildCashChangePaymentItems({
|
|
1223
|
+
payment,
|
|
1224
|
+
locale,
|
|
1225
|
+
currencySymbol
|
|
1226
|
+
}));
|
|
1227
|
+
if (serviceFee.gt(0)) {
|
|
1228
|
+
items.push({
|
|
1229
|
+
item: label(locale, 'surchargeIncluded'),
|
|
1230
|
+
value: formatMoney(serviceFee, currencySymbol)
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
858
1233
|
const fundRecord = getFundRecord(payment);
|
|
859
1234
|
if (fundRecord) {
|
|
860
1235
|
items.push(...buildFundRecordPaymentItems({
|
|
@@ -879,6 +1254,47 @@ function buildPaymentItems(params) {
|
|
|
879
1254
|
});
|
|
880
1255
|
return items;
|
|
881
1256
|
}
|
|
1257
|
+
function getPaymentServiceFee(payment) {
|
|
1258
|
+
return new _decimal.default(toMoneyNumber(payment.service_fee));
|
|
1259
|
+
}
|
|
1260
|
+
function resolveReceiptPaymentAmount(payment, serviceFee) {
|
|
1261
|
+
const amount = new _decimal.default(toMoneyNumber(payment.amount ?? payment.origin_amount));
|
|
1262
|
+
const hasAmount = payment.amount !== undefined && payment.amount !== null && payment.amount !== '';
|
|
1263
|
+
const hasOriginAmount = payment.origin_amount !== undefined && payment.origin_amount !== null && payment.origin_amount !== '';
|
|
1264
|
+
if (!hasAmount || !hasOriginAmount || serviceFee.lte(0)) return amount;
|
|
1265
|
+
const originAmount = new _decimal.default(toMoneyNumber(payment.origin_amount));
|
|
1266
|
+
return amount.eq(originAmount) ? amount.plus(serviceFee) : amount;
|
|
1267
|
+
}
|
|
1268
|
+
function buildCashChangePaymentItems(params) {
|
|
1269
|
+
const {
|
|
1270
|
+
payment,
|
|
1271
|
+
locale,
|
|
1272
|
+
currencySymbol
|
|
1273
|
+
} = params;
|
|
1274
|
+
if (!shouldDisplayCashChange(payment)) return [];
|
|
1275
|
+
return [{
|
|
1276
|
+
item: label(locale, 'cashReceived'),
|
|
1277
|
+
value: formatMoney(payment.metadata.actual_paid_amount, currencySymbol)
|
|
1278
|
+
}, {
|
|
1279
|
+
item: label(locale, 'changeDue'),
|
|
1280
|
+
value: formatMoney(payment.metadata.change_given_amount, currencySymbol)
|
|
1281
|
+
}];
|
|
1282
|
+
}
|
|
1283
|
+
function shouldDisplayCashChange(payment) {
|
|
1284
|
+
if (stringify(payment.status).toLowerCase() !== 'paid') return false;
|
|
1285
|
+
const paymentMethod = stringify(payment.code ?? payment.payment_method).toUpperCase();
|
|
1286
|
+
if (paymentMethod !== 'CASHMANUAL') return false;
|
|
1287
|
+
const actualPaidAmount = payment.metadata?.actual_paid_amount;
|
|
1288
|
+
const changeGivenAmount = payment.metadata?.change_given_amount;
|
|
1289
|
+
if (!isNumericAmount(actualPaidAmount) || !isNumericAmount(changeGivenAmount)) return false;
|
|
1290
|
+
return new _decimal.default(changeGivenAmount).toDecimalPlaces(2).gt(0);
|
|
1291
|
+
}
|
|
1292
|
+
function isNumericAmount(value) {
|
|
1293
|
+
if (typeof value === 'number') return Number.isFinite(value);
|
|
1294
|
+
if (typeof value !== 'string') return false;
|
|
1295
|
+
const normalized = value.trim();
|
|
1296
|
+
return normalized !== '' && /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i.test(normalized);
|
|
1297
|
+
}
|
|
882
1298
|
function buildFundRecordPaymentItems(params) {
|
|
883
1299
|
const {
|
|
884
1300
|
payment,
|
|
@@ -981,13 +1397,29 @@ function getLocalizedValue(value, locale) {
|
|
|
981
1397
|
const underscoredLocale = locale.replace('-', '_');
|
|
982
1398
|
return stringify(value[locale] ?? value[dashedLocale] ?? value[underscoredLocale] ?? value.original ?? value.auto ?? value.default ?? value.en ?? '');
|
|
983
1399
|
}
|
|
984
|
-
function
|
|
985
|
-
const normalized = serviceType.toLowerCase();
|
|
986
|
-
if (
|
|
987
|
-
if (
|
|
988
|
-
if (
|
|
989
|
-
if (
|
|
990
|
-
return 'dineIn';
|
|
1400
|
+
function resolveServiceTypeLabelKey(serviceType, order) {
|
|
1401
|
+
const normalized = serviceType.trim().toLowerCase().replace(/[\s-]+/g, '_');
|
|
1402
|
+
if (['', 'nil', 'none', 'unknown'].includes(normalized)) return null;
|
|
1403
|
+
if (['pickup', 'pick_up', 'self_pickup'].includes(normalized)) return 'pickUp';
|
|
1404
|
+
if (['delivery', 'shipping', 'local_delivery'].includes(normalized)) return 'delivery';
|
|
1405
|
+
if (['takeaway', 'take_away'].includes(normalized)) return 'takeAway';
|
|
1406
|
+
if (['dine_in', 'dinein'].includes(normalized)) return 'dineIn';
|
|
1407
|
+
if (['shop_service', 'shop_server'].includes(normalized)) {
|
|
1408
|
+
return resolveShopServiceLabelKey(order);
|
|
1409
|
+
}
|
|
1410
|
+
return null;
|
|
1411
|
+
}
|
|
1412
|
+
function resolveShopServiceLabelKey(order) {
|
|
1413
|
+
const rawShopServiceType = firstPresentValue(order.metadata?.shop_service_type?.server_type, order.shop_service_type?.server_type, typeof order.shop_service_type === 'string' ? order.shop_service_type : undefined);
|
|
1414
|
+
if (!isPresentId(rawShopServiceType)) return 'dineIn';
|
|
1415
|
+
const normalized = stringify(rawShopServiceType).trim().toLowerCase().replace(/[\s-]+/g, '_');
|
|
1416
|
+
if (['takeaway', 'take_away'].includes(normalized)) return 'takeAway';
|
|
1417
|
+
if (['dine_in', 'dinein'].includes(normalized)) return 'dineIn';
|
|
1418
|
+
return null;
|
|
1419
|
+
}
|
|
1420
|
+
function normalizeBooleanFlag(value) {
|
|
1421
|
+
if (value === true || value === 1 || stringify(value).trim() === '1') return 1;
|
|
1422
|
+
return 0;
|
|
991
1423
|
}
|
|
992
1424
|
function buildShopAddress(shopInfo) {
|
|
993
1425
|
return [shopInfo.address_street, shopInfo.address_detail, shopInfo.city, shopInfo.province, shopInfo.country, shopInfo.zip].filter(Boolean).join(', ');
|
|
@@ -1008,17 +1440,17 @@ function formatNegativeMoney(value, currencySymbol) {
|
|
|
1008
1440
|
const amount = new _decimal.default(toMoneyNumber(value)).abs();
|
|
1009
1441
|
return `-${currencySymbol}${amount.toFixed(2)}`;
|
|
1010
1442
|
}
|
|
1011
|
-
function
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
}
|
|
1021
|
-
function
|
|
1443
|
+
function sumAppliedPayments(payments) {
|
|
1444
|
+
return normalizeArray(payments).filter(payment => isPrintablePayment(payment)).reduce((sum, payment) => {
|
|
1445
|
+
if (isPresentId(payment.origin_amount)) {
|
|
1446
|
+
return sum.plus(toMoneyNumber(payment.origin_amount));
|
|
1447
|
+
}
|
|
1448
|
+
const amount = new _decimal.default(toMoneyNumber(payment.amount));
|
|
1449
|
+
const serviceFee = _decimal.default.max(toMoneyNumber(payment.service_fee), 0);
|
|
1450
|
+
return sum.plus(_decimal.default.max(amount.minus(serviceFee), 0));
|
|
1451
|
+
}, new _decimal.default(0));
|
|
1452
|
+
}
|
|
1453
|
+
function sumCustomerPaidPayments(payments) {
|
|
1022
1454
|
return normalizeArray(payments).filter(payment => isPrintablePayment(payment)).reduce((sum, payment) => {
|
|
1023
1455
|
return sum.plus(toMoneyNumber(payment.amount ?? payment.origin_amount));
|
|
1024
1456
|
}, new _decimal.default(0));
|
|
@@ -1026,7 +1458,7 @@ function sumPayments(payments) {
|
|
|
1026
1458
|
function isPrintablePayment(payment) {
|
|
1027
1459
|
const status = stringify(payment.status).toLowerCase();
|
|
1028
1460
|
if (!status) return true;
|
|
1029
|
-
return ['paid', 'success', 'succeeded', 'completed'].includes(status);
|
|
1461
|
+
return ['paid', 'success', 'succeeded', 'completed', 'partially_refunded', 'refunded'].includes(status);
|
|
1030
1462
|
}
|
|
1031
1463
|
function normalizeArray(value) {
|
|
1032
1464
|
return Array.isArray(value) ? value : [];
|