@pisell/pisellos 0.10.53 → 0.10.55
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/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/model/strategy/adapter/promotion/index.js +9 -0
- package/dist/modules/BusinessAvailability/index.d.ts +30 -0
- package/dist/modules/BusinessAvailability/index.js +204 -0
- package/dist/modules/OpenData/index.js +1 -1
- package/dist/modules/OpenData/types.d.ts +2 -0
- package/dist/modules/Order/index.d.ts +5 -2
- package/dist/modules/Order/index.js +320 -247
- package/dist/modules/Order/types.d.ts +8 -0
- package/dist/modules/Payment/index.js +9 -10
- package/dist/plugins/request.d.ts +3 -0
- package/dist/server/utils/small-ticket.d.ts +1 -85
- package/dist/server/utils/small-ticket.js +2 -2576
- package/dist/shared/payment/loadPaymentMethods.d.ts +9 -0
- package/dist/shared/payment/loadPaymentMethods.js +66 -0
- package/dist/shared/receipt/small-ticket.d.ts +85 -0
- package/dist/shared/receipt/small-ticket.js +2576 -0
- package/dist/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
- package/dist/shared/sales/loadSalesOrderSnapshot.js +21 -0
- package/dist/solution/BaseSales/index.d.ts +4 -15
- package/dist/solution/BaseSales/index.js +131 -86
- package/dist/solution/BaseSales/types.d.ts +11 -0
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.js +41 -13
- package/dist/solution/OrderInvoicePreview/index.d.ts +56 -0
- package/dist/solution/OrderInvoicePreview/index.js +269 -0
- package/dist/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
- package/dist/solution/UnifiedBookingSales/businessAvailability.js +292 -0
- package/dist/solution/UnifiedBookingSales/index.d.ts +11 -0
- package/dist/solution/UnifiedBookingSales/index.js +154 -75
- package/dist/solution/UnifiedBookingSales/types.d.ts +2 -0
- package/dist/solution/index.d.ts +1 -0
- package/dist/solution/index.js +2 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.js +21 -1
- package/lib/modules/BusinessAvailability/index.d.ts +30 -0
- package/lib/modules/BusinessAvailability/index.js +146 -0
- package/lib/modules/OpenData/index.js +1 -1
- package/lib/modules/OpenData/types.d.ts +2 -0
- package/lib/modules/Order/index.d.ts +5 -2
- package/lib/modules/Order/index.js +67 -13
- package/lib/modules/Order/types.d.ts +8 -0
- package/lib/modules/Payment/index.js +3 -4
- package/lib/plugins/request.d.ts +3 -0
- package/lib/server/utils/small-ticket.d.ts +1 -85
- package/lib/server/utils/small-ticket.js +9 -2335
- package/lib/shared/payment/loadPaymentMethods.d.ts +9 -0
- package/lib/shared/payment/loadPaymentMethods.js +40 -0
- package/lib/shared/receipt/small-ticket.d.ts +85 -0
- package/lib/shared/receipt/small-ticket.js +2342 -0
- package/lib/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
- package/lib/shared/sales/loadSalesOrderSnapshot.js +22 -0
- package/lib/solution/BaseSales/index.d.ts +4 -15
- package/lib/solution/BaseSales/index.js +45 -24
- package/lib/solution/BaseSales/types.d.ts +11 -0
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.js +37 -4
- package/lib/solution/OrderInvoicePreview/index.d.ts +56 -0
- package/lib/solution/OrderInvoicePreview/index.js +194 -0
- package/lib/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
- package/lib/solution/UnifiedBookingSales/businessAvailability.js +138 -0
- package/lib/solution/UnifiedBookingSales/index.d.ts +11 -0
- package/lib/solution/UnifiedBookingSales/index.js +54 -0
- package/lib/solution/UnifiedBookingSales/types.d.ts +2 -0
- package/lib/solution/index.d.ts +1 -0
- package/lib/solution/index.js +11 -0
- package/package.json +1 -1
|
@@ -3,2340 +3,14 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
takeaway: 'takeAway',
|
|
15
|
-
pick_up: 'pickUp',
|
|
16
|
-
table_service: 'tableService',
|
|
17
|
-
delivery: 'delivery',
|
|
18
|
-
room_service: 'roomService',
|
|
19
|
-
dine_in: 'dineIn',
|
|
20
|
-
delivery_by_uber: 'deliveryByUber',
|
|
21
|
-
delivery_by_panda: 'deliveryByPanda',
|
|
22
|
-
delivery_by_merchant: 'deliveryByMerchant'
|
|
23
|
-
};
|
|
24
|
-
const LABELS = {
|
|
25
|
-
en: {
|
|
26
|
-
contactName: 'Contact name',
|
|
27
|
-
contactMobile: 'Contact Mobile',
|
|
28
|
-
email: 'email',
|
|
29
|
-
dineIn: 'Dine in',
|
|
30
|
-
takeAway: 'Takeaway',
|
|
31
|
-
delivery: 'Delivery',
|
|
32
|
-
pickUp: 'Pickup',
|
|
33
|
-
tableService: 'Table Service',
|
|
34
|
-
roomService: 'Room Service',
|
|
35
|
-
deliveryByUber: 'Uber Eats Delivery',
|
|
36
|
-
deliveryByPanda: 'HungryPanda Delivery',
|
|
37
|
-
deliveryByMerchant: 'Merchant Delivery',
|
|
38
|
-
shopService: 'Dine-in',
|
|
39
|
-
pickUpTime: 'Pick-up time',
|
|
40
|
-
asap: 'ASAP',
|
|
41
|
-
cash: 'Cash',
|
|
42
|
-
cashReceived: 'Cash received',
|
|
43
|
-
changeDue: 'Change due',
|
|
44
|
-
eftpos: 'Eftpos',
|
|
45
|
-
wallet: 'Wallet',
|
|
46
|
-
giftCard: 'Gift card',
|
|
47
|
-
pointCard: 'Point card',
|
|
48
|
-
balance: 'Balance',
|
|
49
|
-
validity: 'Validity',
|
|
50
|
-
remainingAmount: 'Remaining amount',
|
|
51
|
-
redeemPoints: 'Redeem points',
|
|
52
|
-
remainingPoints: 'Points balance',
|
|
53
|
-
entitlementRemaining: 'Remaining',
|
|
54
|
-
table: 'Table',
|
|
55
|
-
bookingId: 'Booking ID',
|
|
56
|
-
bookingDateTime: 'Date & time',
|
|
57
|
-
bookingResource: 'Resource',
|
|
58
|
-
bookingHolder: 'Holder',
|
|
59
|
-
orderDiscount: 'Order discount',
|
|
60
|
-
manualDiscount: 'Manual discount',
|
|
61
|
-
manualMarkup: 'Manual markup',
|
|
62
|
-
payProcessingFee: 'Pay processing fee',
|
|
63
|
-
surchargeIncluded: 'Surcharge(Included)',
|
|
64
|
-
rounding: 'Rounding',
|
|
65
|
-
gross: 'Gross',
|
|
66
|
-
tare: 'Tare',
|
|
67
|
-
handoverTime: 'Handover time',
|
|
68
|
-
refund: 'Refund'
|
|
69
|
-
},
|
|
70
|
-
zh_CN: {
|
|
71
|
-
contactName: '联系人姓名',
|
|
72
|
-
contactMobile: '联系电话',
|
|
73
|
-
email: '邮箱',
|
|
74
|
-
dineIn: '堂食',
|
|
75
|
-
takeAway: '外带',
|
|
76
|
-
delivery: '配送',
|
|
77
|
-
pickUp: '自取',
|
|
78
|
-
tableService: '送餐到桌',
|
|
79
|
-
roomService: '客房服务',
|
|
80
|
-
deliveryByUber: 'Uber 配送',
|
|
81
|
-
deliveryByPanda: '熊猫外卖配送',
|
|
82
|
-
deliveryByMerchant: '商家配送',
|
|
83
|
-
shopService: '堂食',
|
|
84
|
-
pickUpTime: '取餐时间',
|
|
85
|
-
asap: '尽快',
|
|
86
|
-
cash: '现金支付',
|
|
87
|
-
cashReceived: '收取现金',
|
|
88
|
-
changeDue: '找零',
|
|
89
|
-
eftpos: '刷卡支付',
|
|
90
|
-
wallet: '钱包支付',
|
|
91
|
-
giftCard: '礼品卡',
|
|
92
|
-
pointCard: '积分卡',
|
|
93
|
-
balance: '余额',
|
|
94
|
-
validity: '有效期',
|
|
95
|
-
remainingAmount: '剩余金额',
|
|
96
|
-
redeemPoints: '本次使用积分',
|
|
97
|
-
remainingPoints: '积分余额',
|
|
98
|
-
entitlementRemaining: '剩余次数',
|
|
99
|
-
table: '桌台',
|
|
100
|
-
bookingId: '预约编号',
|
|
101
|
-
bookingDateTime: '日期和时间',
|
|
102
|
-
bookingResource: '资源',
|
|
103
|
-
bookingHolder: '预约人',
|
|
104
|
-
orderDiscount: '整单折扣',
|
|
105
|
-
manualDiscount: '手动折扣',
|
|
106
|
-
manualMarkup: '手动加价',
|
|
107
|
-
payProcessingFee: '支付手续费',
|
|
108
|
-
surchargeIncluded: '手续费(已含)',
|
|
109
|
-
rounding: '抹零',
|
|
110
|
-
gross: '重量',
|
|
111
|
-
tare: '去皮',
|
|
112
|
-
handoverTime: '交付时间',
|
|
113
|
-
refund: '退款'
|
|
114
|
-
},
|
|
115
|
-
zh_HK: {
|
|
116
|
-
contactName: '聯絡人姓名',
|
|
117
|
-
contactMobile: '聯絡電話',
|
|
118
|
-
email: '電郵',
|
|
119
|
-
dineIn: '堂食',
|
|
120
|
-
takeAway: '外帶',
|
|
121
|
-
delivery: '配送',
|
|
122
|
-
pickUp: '自取',
|
|
123
|
-
tableService: '送餐到桌',
|
|
124
|
-
roomService: '客房服務',
|
|
125
|
-
deliveryByUber: 'Uber 配送',
|
|
126
|
-
deliveryByPanda: '熊貓外賣配送',
|
|
127
|
-
deliveryByMerchant: '商家配送',
|
|
128
|
-
shopService: '堂食',
|
|
129
|
-
pickUpTime: '取餐時間',
|
|
130
|
-
asap: '盡快',
|
|
131
|
-
cash: '現金支付',
|
|
132
|
-
cashReceived: '收取現金',
|
|
133
|
-
changeDue: '找零',
|
|
134
|
-
eftpos: '刷卡支付',
|
|
135
|
-
wallet: '錢包支付',
|
|
136
|
-
giftCard: '禮品卡',
|
|
137
|
-
pointCard: '積分卡',
|
|
138
|
-
balance: '餘額',
|
|
139
|
-
validity: '有效期',
|
|
140
|
-
remainingAmount: '剩餘金額',
|
|
141
|
-
redeemPoints: '本次使用積分',
|
|
142
|
-
remainingPoints: '積分餘額',
|
|
143
|
-
entitlementRemaining: '剩餘次數',
|
|
144
|
-
table: '桌台',
|
|
145
|
-
bookingId: '預約編號',
|
|
146
|
-
bookingDateTime: '日期和時間',
|
|
147
|
-
bookingResource: '資源',
|
|
148
|
-
bookingHolder: '預約人',
|
|
149
|
-
orderDiscount: '整單折扣',
|
|
150
|
-
manualDiscount: '手動折扣',
|
|
151
|
-
manualMarkup: '手動加價',
|
|
152
|
-
payProcessingFee: '支付手續費',
|
|
153
|
-
surchargeIncluded: '手續費(已含)',
|
|
154
|
-
rounding: '抹零',
|
|
155
|
-
gross: '重量',
|
|
156
|
-
tare: '去皮',
|
|
157
|
-
handoverTime: '交付時間',
|
|
158
|
-
refund: '退款'
|
|
159
|
-
},
|
|
160
|
-
ja: {
|
|
161
|
-
contactName: '連絡先名',
|
|
162
|
-
contactMobile: '連絡先電話',
|
|
163
|
-
email: 'メール',
|
|
164
|
-
dineIn: '店内飲食',
|
|
165
|
-
takeAway: 'テイクアウト',
|
|
166
|
-
delivery: '配達',
|
|
167
|
-
pickUp: '受け取り',
|
|
168
|
-
tableService: 'テーブルサービス',
|
|
169
|
-
roomService: 'ルームサービス',
|
|
170
|
-
deliveryByUber: 'Uber Eats 配達',
|
|
171
|
-
deliveryByPanda: 'HungryPanda 配達',
|
|
172
|
-
deliveryByMerchant: '店舗による配達',
|
|
173
|
-
shopService: '店内飲食',
|
|
174
|
-
pickUpTime: '受取時間',
|
|
175
|
-
asap: 'できるだけ早く',
|
|
176
|
-
cash: '現金',
|
|
177
|
-
eftpos: 'カード',
|
|
178
|
-
wallet: 'ウォレット',
|
|
179
|
-
giftCard: 'ギフトカード',
|
|
180
|
-
pointCard: 'ポイントカード',
|
|
181
|
-
balance: '残高',
|
|
182
|
-
validity: '有効期限',
|
|
183
|
-
remainingAmount: '残高',
|
|
184
|
-
redeemPoints: '利用ポイント',
|
|
185
|
-
remainingPoints: 'ポイント残高',
|
|
186
|
-
entitlementRemaining: '残り回数',
|
|
187
|
-
table: 'テーブル',
|
|
188
|
-
bookingId: '予約ID',
|
|
189
|
-
bookingDateTime: '日時',
|
|
190
|
-
bookingResource: 'リソース',
|
|
191
|
-
bookingHolder: '予約者',
|
|
192
|
-
orderDiscount: '注文割引',
|
|
193
|
-
manualDiscount: '手動割引',
|
|
194
|
-
manualMarkup: '手動値上げ',
|
|
195
|
-
payProcessingFee: '決済手数料',
|
|
196
|
-
rounding: '端数調整',
|
|
197
|
-
gross: '総重量',
|
|
198
|
-
tare: '風袋',
|
|
199
|
-
handoverTime: '受け渡し時間',
|
|
200
|
-
refund: '返金',
|
|
201
|
-
cashReceived: 'お預かり',
|
|
202
|
-
changeDue: 'お釣り'
|
|
203
|
-
},
|
|
204
|
-
pt: {
|
|
205
|
-
contactName: 'Nome de contato',
|
|
206
|
-
contactMobile: 'Celular de contato',
|
|
207
|
-
email: 'e-mail',
|
|
208
|
-
dineIn: 'Consumo no local',
|
|
209
|
-
takeAway: 'Para levar',
|
|
210
|
-
delivery: 'Entrega',
|
|
211
|
-
pickUp: 'Recolha',
|
|
212
|
-
tableService: 'Serviço à mesa',
|
|
213
|
-
roomService: 'Serviço de quarto',
|
|
214
|
-
deliveryByUber: 'Entrega pela Uber Eats',
|
|
215
|
-
deliveryByPanda: 'Entrega pela HungryPanda',
|
|
216
|
-
deliveryByMerchant: 'Entrega pelo comerciante',
|
|
217
|
-
shopService: 'Consumo no local',
|
|
218
|
-
pickUpTime: 'Hora de recolha',
|
|
219
|
-
asap: 'Assim que possível',
|
|
220
|
-
cash: 'Dinheiro',
|
|
221
|
-
eftpos: 'Cartão',
|
|
222
|
-
wallet: 'Carteira',
|
|
223
|
-
giftCard: 'Cartão-presente',
|
|
224
|
-
pointCard: 'Cartão de pontos',
|
|
225
|
-
balance: 'Saldo',
|
|
226
|
-
validity: 'Validade',
|
|
227
|
-
remainingAmount: 'Saldo restante',
|
|
228
|
-
redeemPoints: 'Pontos utilizados',
|
|
229
|
-
remainingPoints: 'Saldo de pontos',
|
|
230
|
-
entitlementRemaining: 'Utilizações restantes',
|
|
231
|
-
table: 'Mesa',
|
|
232
|
-
bookingId: 'ID da reserva',
|
|
233
|
-
bookingDateTime: 'Data e hora',
|
|
234
|
-
bookingResource: 'Recurso',
|
|
235
|
-
bookingHolder: 'Titular',
|
|
236
|
-
orderDiscount: 'Desconto do pedido',
|
|
237
|
-
manualDiscount: 'Desconto manual',
|
|
238
|
-
manualMarkup: 'Acréscimo manual',
|
|
239
|
-
payProcessingFee: 'Taxa de processamento',
|
|
240
|
-
rounding: 'Arredondamento',
|
|
241
|
-
gross: 'Peso bruto',
|
|
242
|
-
tare: 'Tara',
|
|
243
|
-
handoverTime: 'Hora de entrega',
|
|
244
|
-
refund: 'Reembolso',
|
|
245
|
-
cashReceived: 'Dinheiro recebido',
|
|
246
|
-
changeDue: 'Troco'
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
function buildSmallTicketData(params) {
|
|
250
|
-
const order = params.order || {};
|
|
251
|
-
const shopInfo = params.shopInfo || {};
|
|
252
|
-
const locales = resolveLocales(shopInfo, params.locales);
|
|
253
|
-
const productMap = params.productMap || {};
|
|
254
|
-
return locales.reduce((data, locale) => {
|
|
255
|
-
data[locale] = buildLocaleData({
|
|
256
|
-
order,
|
|
257
|
-
shopInfo,
|
|
258
|
-
locale,
|
|
259
|
-
productMap
|
|
260
|
-
});
|
|
261
|
-
return data;
|
|
262
|
-
}, {});
|
|
263
|
-
}
|
|
264
|
-
function hasSmallTicketData(value) {
|
|
265
|
-
return !!value && typeof value === 'object' && Object.keys(value).length > 0;
|
|
266
|
-
}
|
|
267
|
-
function buildLocaleData(params) {
|
|
268
|
-
const {
|
|
269
|
-
order,
|
|
270
|
-
shopInfo,
|
|
271
|
-
locale,
|
|
272
|
-
productMap
|
|
273
|
-
} = params;
|
|
274
|
-
const products = buildProducts(order, locale, productMap);
|
|
275
|
-
const summary = getSummary(order);
|
|
276
|
-
const currencySymbol = getCurrencySymbol(order, shopInfo);
|
|
277
|
-
const financials = resolveReceiptFinancials(order, summary);
|
|
278
|
-
const delivery = buildDelivery(order, locale);
|
|
279
|
-
return {
|
|
280
|
-
base_data: buildBaseData({
|
|
281
|
-
order,
|
|
282
|
-
shopInfo,
|
|
283
|
-
locale,
|
|
284
|
-
currencySymbol,
|
|
285
|
-
financials
|
|
286
|
-
}),
|
|
287
|
-
products,
|
|
288
|
-
fees: buildFees({
|
|
289
|
-
order,
|
|
290
|
-
shopInfo,
|
|
291
|
-
locale,
|
|
292
|
-
products,
|
|
293
|
-
summary,
|
|
294
|
-
currencySymbol,
|
|
295
|
-
financials
|
|
296
|
-
}),
|
|
297
|
-
customer: buildCustomer(order, locale),
|
|
298
|
-
appointment: buildAppointment(order, locale),
|
|
299
|
-
...(delivery.length ? {
|
|
300
|
-
delivery
|
|
301
|
-
} : {}),
|
|
302
|
-
resources: buildResources(order, locale),
|
|
303
|
-
payment_data: buildPaymentData({
|
|
304
|
-
order,
|
|
305
|
-
locale,
|
|
306
|
-
currencySymbol
|
|
307
|
-
}),
|
|
308
|
-
wallets: buildWallets({
|
|
309
|
-
order,
|
|
310
|
-
locale,
|
|
311
|
-
currencySymbol
|
|
312
|
-
}),
|
|
313
|
-
tickets: Array.isArray(order.tickets) ? order.tickets : [],
|
|
314
|
-
source_shop: [],
|
|
315
|
-
refund_data: buildRefundData({
|
|
316
|
-
order,
|
|
317
|
-
locale,
|
|
318
|
-
currencySymbol
|
|
319
|
-
}),
|
|
320
|
-
expend_data: []
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
function buildBaseData(params) {
|
|
324
|
-
const {
|
|
325
|
-
order,
|
|
326
|
-
shopInfo,
|
|
327
|
-
locale,
|
|
328
|
-
currencySymbol,
|
|
329
|
-
financials
|
|
330
|
-
} = params;
|
|
331
|
-
const orderNumber = stringify(order.buzzer || order.shop_full_order_number);
|
|
332
|
-
return {
|
|
333
|
-
shop_name: stringify(shopInfo.name || order.shop_name),
|
|
334
|
-
note: stringify(order.note),
|
|
335
|
-
shop_order_number: stringify(order.shop_order_number),
|
|
336
|
-
order_number: orderNumber,
|
|
337
|
-
shop_tax_number: stringify(shopInfo.tax_number || order.shop_tax_number),
|
|
338
|
-
shop_phone: stringify(shopInfo.phone || order.shop_phone),
|
|
339
|
-
shop_address: stringify(shopInfo.receipt_address || shopInfo.address || buildShopAddress(shopInfo) || order.shop_address),
|
|
340
|
-
original_payment_status: financials.paymentStatus,
|
|
341
|
-
payment_status: financials.paymentStatus,
|
|
342
|
-
total_amount: formatMoney(financials.totalAmount, currencySymbol),
|
|
343
|
-
has_pay: formatMoney(financials.netPaymentApplied, currencySymbol),
|
|
344
|
-
amount_gap: formatCalculatedMoney(financials.amountGap ?? 0, currencySymbol),
|
|
345
|
-
is_repeat: 0,
|
|
346
|
-
buzzer: stringify(order.buzzer || order.pickup_number || order.metadata?.buzzer),
|
|
347
|
-
order_no: orderNumber,
|
|
348
|
-
order_time: formatDateTime(order.created_at || order.sales_time || new Date(), locale),
|
|
349
|
-
...(isPresentId(order.updated_at) ? {
|
|
350
|
-
updated_at: formatDateTime(order.updated_at, locale)
|
|
351
|
-
} : {})
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
function buildProducts(order, locale, productMap) {
|
|
355
|
-
const products = Array.isArray(order.products) ? order.products : [];
|
|
356
|
-
const allergyByProductUid = buildProductAllergyMap(order, locale);
|
|
357
|
-
return products.map(rawProduct => {
|
|
358
|
-
const product = restoreReceiptDiscountHierarchy(rawProduct);
|
|
359
|
-
const quantity = toNumber(product.product_quantity ?? product.quantity ?? product.num ?? 1, 1);
|
|
360
|
-
const originalPrice = product.original_price ?? product.selling_price ?? product.payment_price ?? product.price ?? 0;
|
|
361
|
-
const sellingPrice = product.selling_price ?? product.payment_price ?? product.price ?? originalPrice;
|
|
362
|
-
const productTitle = resolveProductTitle({
|
|
363
|
-
product,
|
|
364
|
-
productMap,
|
|
365
|
-
locale
|
|
366
|
-
});
|
|
367
|
-
const parentProduct = resolveProductFromMap(product.product_id ?? product.id, productMap);
|
|
368
|
-
const currencySymbol = getCurrencySymbol(order, null);
|
|
369
|
-
const options = normalizeProductOptions({
|
|
370
|
-
product,
|
|
371
|
-
locale,
|
|
372
|
-
currencySymbol,
|
|
373
|
-
parentProduct
|
|
374
|
-
});
|
|
375
|
-
const combinations = normalizeProductCombinations({
|
|
376
|
-
product,
|
|
377
|
-
productMap,
|
|
378
|
-
locale,
|
|
379
|
-
currencySymbol,
|
|
380
|
-
parentProduct
|
|
381
|
-
});
|
|
382
|
-
const resolvedVariant = resolveProductVariantText({
|
|
383
|
-
product,
|
|
384
|
-
parentProduct,
|
|
385
|
-
locale
|
|
386
|
-
});
|
|
387
|
-
const productUid = firstPresentValue(product.metadata?.unique_identification_number, product.unique_identification_number);
|
|
388
|
-
const bookingUid = firstPresentValue(product.booking_uid, product.metadata?.booking_uid);
|
|
389
|
-
const extensionList = buildProductExtensionList({
|
|
390
|
-
product,
|
|
391
|
-
locale,
|
|
392
|
-
currencySymbol,
|
|
393
|
-
quantity,
|
|
394
|
-
originalPrice
|
|
395
|
-
});
|
|
396
|
-
const productNote = resolveProductNote(product, locale);
|
|
397
|
-
const allergy = isPresentId(productUid) ? allergyByProductUid.get(String(productUid).trim()) || '' : '';
|
|
398
|
-
const basePrice = resolveProductBasePrice(product);
|
|
399
|
-
const resolvedCombinations = Array.isArray(product.combinations) && product.combinations.length ? normalizeExistingCombinations(product.combinations, locale, currencySymbol) : combinations;
|
|
400
|
-
const shouldIncludeBasePrice = options.length > 0 || resolvedCombinations.length > 0 || hasProductDiscount(product, extensionList);
|
|
401
|
-
return {
|
|
402
|
-
product_title: productTitle,
|
|
403
|
-
product_quantity: quantity,
|
|
404
|
-
selling_price: formatMoney(sellingPrice, currencySymbol),
|
|
405
|
-
total_selling_price: formatCalculatedMoney(new _decimal.default(toMoneyNumber(sellingPrice)).mul(quantity), currencySymbol),
|
|
406
|
-
original_price: formatMoney(originalPrice, currencySymbol),
|
|
407
|
-
...(shouldIncludeBasePrice ? {
|
|
408
|
-
base_price: formatCalculatedMoney(basePrice ?? 0, currencySymbol)
|
|
409
|
-
} : {}),
|
|
410
|
-
total_original_price: formatMoney(new _decimal.default(toMoneyNumber(originalPrice)).mul(quantity), currencySymbol),
|
|
411
|
-
...(options.length ? {
|
|
412
|
-
options_total_price: sumNormalizedOptionTotals(options, currencySymbol)
|
|
413
|
-
} : {}),
|
|
414
|
-
...(resolvedCombinations.length ? {
|
|
415
|
-
package_items_total_price: sumCombinationTotals(resolvedCombinations, currencySymbol)
|
|
416
|
-
} : {}),
|
|
417
|
-
extension_list: extensionList,
|
|
418
|
-
...(isPresentId(productUid) ? {
|
|
419
|
-
product_uid: productUid
|
|
420
|
-
} : {}),
|
|
421
|
-
...(isPresentId(bookingUid) ? {
|
|
422
|
-
booking_uid: bookingUid
|
|
423
|
-
} : {}),
|
|
424
|
-
...(productNote ? {
|
|
425
|
-
product_note: productNote
|
|
426
|
-
} : {}),
|
|
427
|
-
...(allergy ? {
|
|
428
|
-
allergy
|
|
429
|
-
} : {}),
|
|
430
|
-
...(options.length ? {
|
|
431
|
-
options
|
|
432
|
-
} : {}),
|
|
433
|
-
...(resolvedVariant ? {
|
|
434
|
-
variant: resolvedVariant
|
|
435
|
-
} : {}),
|
|
436
|
-
...(resolvedCombinations.length ? {
|
|
437
|
-
combinations: resolvedCombinations
|
|
438
|
-
} : {})
|
|
439
|
-
};
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
const RECEIPT_BUNDLE_MAP_ID_KEY = 'custom_product_bundle_map_id';
|
|
443
|
-
function restoreReceiptDiscountHierarchy(source) {
|
|
444
|
-
if (!source || typeof source !== 'object' || Array.isArray(source)) {
|
|
445
|
-
return source;
|
|
446
|
-
}
|
|
447
|
-
const childKey = Array.isArray(source.combinations) && source.combinations.length ? 'combinations' : Array.isArray(source.product_bundle) && source.product_bundle.length ? 'product_bundle' : null;
|
|
448
|
-
if (!childKey) return {
|
|
449
|
-
...source
|
|
450
|
-
};
|
|
451
|
-
const children = source[childKey].map(child => child && typeof child === 'object' && !Array.isArray(child) ? restoreReceiptDiscountHierarchy(child) : child);
|
|
452
|
-
const restored = {
|
|
453
|
-
...source,
|
|
454
|
-
[childKey]: children
|
|
455
|
-
};
|
|
456
|
-
if (!Array.isArray(source.discount_list) || !source.discount_list.length) {
|
|
457
|
-
return restored;
|
|
458
|
-
}
|
|
459
|
-
const descendantsByMapId = new Map();
|
|
460
|
-
indexReceiptDiscountTargets(children, descendantsByMapId);
|
|
461
|
-
if (!descendantsByMapId.size) return restored;
|
|
462
|
-
const ownDiscounts = [];
|
|
463
|
-
for (const discount of source.discount_list) {
|
|
464
|
-
const mapId = getReceiptDiscountBundleMapId(discount);
|
|
465
|
-
const target = mapId ? descendantsByMapId.get(mapId) : undefined;
|
|
466
|
-
if (!target) {
|
|
467
|
-
ownDiscounts.push(discount);
|
|
468
|
-
continue;
|
|
469
|
-
}
|
|
470
|
-
const targetDiscounts = Array.isArray(target.discount_list) ? target.discount_list : [];
|
|
471
|
-
if (!targetDiscounts.some(item => isSameReceiptDiscount(item, discount))) {
|
|
472
|
-
target.discount_list = [...targetDiscounts, discount];
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
restored.discount_list = ownDiscounts;
|
|
476
|
-
return restored;
|
|
477
|
-
}
|
|
478
|
-
function indexReceiptDiscountTargets(nodes, targets) {
|
|
479
|
-
for (const node of nodes) {
|
|
480
|
-
if (!node || typeof node !== 'object' || Array.isArray(node)) continue;
|
|
481
|
-
const mapId = getReceiptBundleMapId(node);
|
|
482
|
-
if (mapId && !targets.has(mapId)) targets.set(mapId, node);
|
|
483
|
-
const children = Array.isArray(node.combinations) && node.combinations.length ? node.combinations : Array.isArray(node.product_bundle) ? node.product_bundle : [];
|
|
484
|
-
indexReceiptDiscountTargets(children, targets);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
function normalizeReceiptId(value) {
|
|
488
|
-
if (value === undefined || value === null) return null;
|
|
489
|
-
const normalized = String(value).trim();
|
|
490
|
-
return normalized ? normalized : null;
|
|
491
|
-
}
|
|
492
|
-
function getReceiptBundleMapId(node) {
|
|
493
|
-
return normalizeReceiptId(node.metadata?.[RECEIPT_BUNDLE_MAP_ID_KEY] ?? node._id);
|
|
494
|
-
}
|
|
495
|
-
function getReceiptDiscountBundleMapId(discount) {
|
|
496
|
-
return normalizeReceiptId(discount?.metadata?.[RECEIPT_BUNDLE_MAP_ID_KEY]);
|
|
497
|
-
}
|
|
498
|
-
function isSameReceiptDiscount(left, right) {
|
|
499
|
-
const leftOrderDiscountId = normalizeReceiptId(left?.order_discount_id);
|
|
500
|
-
const rightOrderDiscountId = normalizeReceiptId(right?.order_discount_id);
|
|
501
|
-
if (leftOrderDiscountId && rightOrderDiscountId) {
|
|
502
|
-
return leftOrderDiscountId === rightOrderDiscountId;
|
|
503
|
-
}
|
|
504
|
-
const leftResourceId = normalizeReceiptId(left?.discount?.resource_id ?? left?.resource_id ?? left?.discount_id);
|
|
505
|
-
const rightResourceId = normalizeReceiptId(right?.discount?.resource_id ?? right?.resource_id ?? right?.discount_id);
|
|
506
|
-
if (!leftResourceId || !rightResourceId) return false;
|
|
507
|
-
return leftResourceId === rightResourceId && getReceiptDiscountBundleMapId(left) === getReceiptDiscountBundleMapId(right) && String(left?.amount ?? left?.value ?? left?.discount_amount ?? '') === String(right?.amount ?? right?.value ?? right?.discount_amount ?? '');
|
|
508
|
-
}
|
|
509
|
-
function resolveProductBasePrice(product) {
|
|
510
|
-
const metadata = product.metadata || {};
|
|
511
|
-
const origin = product._origin || metadata.origin || {};
|
|
512
|
-
const variantId = product.product_variant_id ?? product.variant_id;
|
|
513
|
-
const variant = Array.isArray(origin.variant) && isPresentId(variantId) ? origin.variant.find(item => isSameId(item?.id, variantId)) : undefined;
|
|
514
|
-
const isV2 = metadata.price_schema_version === 2 || metadata.price_schema_version === '2';
|
|
515
|
-
const quantity = toNumber(product.product_quantity ?? product.quantity ?? product.num ?? 1, 1);
|
|
516
|
-
const hasEntitlementPass = normalizeArray(product.discount_list).some(discount => getProductWalletDiscountType(discount) === 'entitlement_pass');
|
|
517
|
-
const hasOptions = normalizeArray(product.options).length > 0 || normalizeArray(product.product_sku?.option).length > 0;
|
|
518
|
-
const hasCombinations = normalizeArray(product.combinations).length > 0 || normalizeArray(product.product_bundle).length > 0 || normalizeArray(product.bundle).length > 0;
|
|
519
|
-
const aggregateSourcePrice = firstPresentValue(metadata.source_product_price, product.price);
|
|
520
|
-
const originalPrice = product.original_price;
|
|
521
|
-
// 部分旧 entitlement 商品把 source/price 持久化成了整行总价,但
|
|
522
|
-
// original_price 仍是单件价。仅在等式能明确证明该口径时做窄幅回退。
|
|
523
|
-
const useOriginalPriceAsLegacyUnitPrice = hasEntitlementPass && quantity > 1 && !hasOptions && !hasCombinations && isNumericAmount(aggregateSourcePrice) && isNumericAmount(originalPrice) && new _decimal.default(aggregateSourcePrice).eq(new _decimal.default(originalPrice).mul(quantity));
|
|
524
|
-
return firstPresentValue(useOriginalPriceAsLegacyUnitPrice ? originalPrice : undefined, 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);
|
|
525
|
-
}
|
|
526
|
-
function normalizeProductDiscounts(params) {
|
|
527
|
-
const {
|
|
528
|
-
product,
|
|
529
|
-
locale,
|
|
530
|
-
currencySymbol,
|
|
531
|
-
quantity
|
|
532
|
-
} = params;
|
|
533
|
-
const rows = normalizeArray(product.discount_list).filter(discount => {
|
|
534
|
-
const amount = discount.amount ?? discount.value ?? discount.discount_amount;
|
|
535
|
-
return isNonZero(amount);
|
|
536
|
-
}).map(discount => {
|
|
537
|
-
const title = resolveDiscountTitleWithPercent(discount, locale);
|
|
538
|
-
const amount = discount.amount ?? discount.value ?? discount.discount_amount;
|
|
539
|
-
const isMarkup = isManualMarkup(discount);
|
|
540
|
-
const isAggregatedBundleDiscount = Boolean(getReceiptDiscountBundleMapId(discount));
|
|
541
|
-
const isEntitlementPass = getProductWalletDiscountType(discount) === 'entitlement_pass';
|
|
542
|
-
const totalAmount = new _decimal.default(toMoneyNumber(amount)).mul(isAggregatedBundleDiscount || isEntitlementPass ? 1 : quantity);
|
|
543
|
-
const formattedAmount = isMarkup ? formatMoney(totalAmount.abs(), currencySymbol) : formatNegativeMoney(totalAmount, currencySymbol);
|
|
544
|
-
return {
|
|
545
|
-
item: title,
|
|
546
|
-
value: formattedAmount,
|
|
547
|
-
isMarkup
|
|
548
|
-
};
|
|
549
|
-
});
|
|
550
|
-
return rows.sort((left, right) => Number(right.isMarkup) - Number(left.isMarkup)).map(({
|
|
551
|
-
isMarkup: _isMarkup,
|
|
552
|
-
...row
|
|
553
|
-
}) => row);
|
|
554
|
-
}
|
|
555
|
-
function isManualMarkup(discount) {
|
|
556
|
-
if (discount.type !== 'product') return false;
|
|
557
|
-
if (discount.discount?.discount_type === 'change_price') return true;
|
|
558
|
-
const amount = discount.amount ?? discount.value ?? discount.discount_amount;
|
|
559
|
-
return new _decimal.default(toMoneyNumber(amount)).isNegative();
|
|
560
|
-
}
|
|
561
|
-
function hasProductDiscount(product, extensionList = []) {
|
|
562
|
-
const hasDiscountListAmount = normalizeArray(product.discount_list).some(discount => isNonZero(discount.amount ?? discount.value ?? discount.discount_amount));
|
|
563
|
-
return hasDiscountListAmount || extensionList.some(row => /^\s*-/.test(stringify(row?.value)));
|
|
564
|
-
}
|
|
565
|
-
function buildProductExtensionList(params) {
|
|
566
|
-
const {
|
|
567
|
-
product,
|
|
568
|
-
locale,
|
|
569
|
-
currencySymbol,
|
|
570
|
-
quantity,
|
|
571
|
-
originalPrice
|
|
572
|
-
} = params;
|
|
573
|
-
const discountList = normalizeProductDiscounts({
|
|
574
|
-
product,
|
|
575
|
-
locale,
|
|
576
|
-
currencySymbol,
|
|
577
|
-
quantity
|
|
578
|
-
});
|
|
579
|
-
const extensionList = [...buildProductOpenWeightRows({
|
|
580
|
-
product,
|
|
581
|
-
locale,
|
|
582
|
-
currencySymbol
|
|
583
|
-
}), ...(quantity > 1 ? [{
|
|
584
|
-
item: buildProductQuantityPriceText({
|
|
585
|
-
quantity,
|
|
586
|
-
currencySymbol,
|
|
587
|
-
price: originalPrice
|
|
588
|
-
}),
|
|
589
|
-
value: ''
|
|
590
|
-
}] : [])];
|
|
591
|
-
const productExtensionList = extensionList.length ? extensionList : normalizeArray(product.extension_list);
|
|
592
|
-
return [...productExtensionList, ...discountList];
|
|
593
|
-
}
|
|
594
|
-
function buildProductOpenWeightRows(params) {
|
|
595
|
-
const {
|
|
596
|
-
product,
|
|
597
|
-
locale,
|
|
598
|
-
currencySymbol
|
|
599
|
-
} = params;
|
|
600
|
-
const productSku = product.product_sku || {};
|
|
601
|
-
if (productSku.open_sold_weight != 1) return [];
|
|
602
|
-
const unit = productSku.unit ?? '';
|
|
603
|
-
const manualFlag = productSku.weight_source === 'manual' ? '(M) ' : '';
|
|
604
|
-
const hasTare = toNumber(productSku.tare_weight, 0) > 0;
|
|
605
|
-
const rows = [];
|
|
606
|
-
if (hasTare) {
|
|
607
|
-
rows.push({
|
|
608
|
-
item: `${manualFlag}${label(locale, 'gross')}: ${productSku.weight ?? 0}${unit}`,
|
|
609
|
-
value: ''
|
|
610
|
-
});
|
|
611
|
-
rows.push({
|
|
612
|
-
item: `${label(locale, 'tare')}: ${productSku.tare_weight}${unit}`,
|
|
613
|
-
value: ''
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
const netWeight = productSku.net_weight ?? 0;
|
|
617
|
-
const unitPrice = productSku.unit_price ?? 0;
|
|
618
|
-
const netItem = `${netWeight} ${unit} NET @ ${currencySymbol}${unitPrice}/${unit}`;
|
|
619
|
-
rows.push({
|
|
620
|
-
item: hasTare ? netItem : `${manualFlag}${netItem}`,
|
|
621
|
-
value: ''
|
|
622
|
-
});
|
|
623
|
-
return rows;
|
|
624
|
-
}
|
|
625
|
-
function buildProductQuantityPriceText(params) {
|
|
626
|
-
const {
|
|
627
|
-
quantity,
|
|
628
|
-
currencySymbol,
|
|
629
|
-
price
|
|
630
|
-
} = params;
|
|
631
|
-
return `${quantity} @ ${currencySymbol}${stringify(price)} EACH`;
|
|
632
|
-
}
|
|
633
|
-
function resolveDiscountTitleWithPercent(discount, locale) {
|
|
634
|
-
const title = resolveDiscountTitle(discount, locale);
|
|
635
|
-
if (getProductWalletDiscountType(discount) === 'entitlement_pass') {
|
|
636
|
-
return title;
|
|
637
|
-
}
|
|
638
|
-
const percentRaw = discount.discount?.percent;
|
|
639
|
-
const cardType = discount.discount?.discount_card_type ?? 'percent';
|
|
640
|
-
const percent = percentRaw !== undefined && percentRaw !== null ? Math.trunc(Number(percentRaw)) : NaN;
|
|
641
|
-
if (percentRaw === undefined || percentRaw === null) return title;
|
|
642
|
-
if (cardType !== 'percent') return title;
|
|
643
|
-
if (Number.isNaN(percent)) return title;
|
|
644
|
-
return `${title} (${percent}%)`;
|
|
645
|
-
}
|
|
646
|
-
function resolveDiscountTitle(discount, locale) {
|
|
647
|
-
const title = getLocalizedValue(discount.discount?.name ?? discount.discount?.title ?? discount.discount?.message ?? discount.name ?? discount.title ?? discount.message, locale);
|
|
648
|
-
if (title) return title;
|
|
649
|
-
if (discount.type === 'product') {
|
|
650
|
-
return label(locale, isManualMarkup(discount) ? 'manualMarkup' : 'manualDiscount');
|
|
651
|
-
}
|
|
652
|
-
return '';
|
|
653
|
-
}
|
|
654
|
-
function resolveProductTitle(params) {
|
|
655
|
-
const {
|
|
656
|
-
product,
|
|
657
|
-
productMap,
|
|
658
|
-
locale
|
|
659
|
-
} = params;
|
|
660
|
-
const ownTitle = getProductTitleSource(product, locale);
|
|
661
|
-
if (ownTitle) return ownTitle;
|
|
662
|
-
const productId = product.product_id ?? product.id;
|
|
663
|
-
const fallbackProduct = resolveProductFromMap(productId, productMap);
|
|
664
|
-
if (!fallbackProduct) return ownTitle || '';
|
|
665
|
-
return getProductTitleSource(fallbackProduct, locale);
|
|
666
|
-
}
|
|
667
|
-
function normalizeProductOptions(params) {
|
|
668
|
-
const {
|
|
669
|
-
product,
|
|
670
|
-
locale,
|
|
671
|
-
currencySymbol,
|
|
672
|
-
parentProduct
|
|
673
|
-
} = params;
|
|
674
|
-
const rawOptions = Array.isArray(params.options) ? params.options : Array.isArray(product.options) ? product.options : Array.isArray(product.product_sku?.option) ? product.product_sku.option : [];
|
|
675
|
-
return rawOptions.map(option => {
|
|
676
|
-
const price = option.original_price ?? option.add_price ?? option.price;
|
|
677
|
-
const quantity = toNumber(option.num ?? option.quantity ?? 1, 1);
|
|
678
|
-
const value = option.value ?? option.option ?? option.item;
|
|
679
|
-
const name = getProductTitleSource(option, locale) || resolveOptionTitleFromParentProduct({
|
|
680
|
-
option,
|
|
681
|
-
parentProduct,
|
|
682
|
-
locale
|
|
683
|
-
});
|
|
684
|
-
return {
|
|
685
|
-
name,
|
|
686
|
-
...(option.num !== undefined || option.quantity !== undefined ? {
|
|
687
|
-
num: quantity
|
|
688
|
-
} : {}),
|
|
689
|
-
...(price !== undefined && price !== null && price !== '' ? {
|
|
690
|
-
price: formatMoney(price, currencySymbol)
|
|
691
|
-
} : {}),
|
|
692
|
-
total_price: formatCalculatedMoney(new _decimal.default(toMoneyNumber(price ?? 0)).mul(quantity), currencySymbol),
|
|
693
|
-
...(value !== undefined && value !== null && value !== '' ? {
|
|
694
|
-
value: getLocalizedValue(value, locale)
|
|
695
|
-
} : {})
|
|
696
|
-
};
|
|
697
|
-
});
|
|
698
|
-
}
|
|
699
|
-
function normalizeProductCombinations(params) {
|
|
700
|
-
const {
|
|
701
|
-
product,
|
|
702
|
-
productMap,
|
|
703
|
-
locale,
|
|
704
|
-
currencySymbol,
|
|
705
|
-
parentProduct
|
|
706
|
-
} = params;
|
|
707
|
-
const bundles = Array.isArray(product.product_bundle) ? product.product_bundle : [];
|
|
708
|
-
return bundles.map(bundle => {
|
|
709
|
-
const combinationsOptions = normalizeProductOptions({
|
|
710
|
-
product: {},
|
|
711
|
-
locale,
|
|
712
|
-
currencySymbol,
|
|
713
|
-
options: Array.isArray(bundle.option) ? bundle.option : [],
|
|
714
|
-
parentProduct
|
|
715
|
-
});
|
|
716
|
-
const baseTitle = resolveBundleProductTitle({
|
|
717
|
-
bundle,
|
|
718
|
-
productMap,
|
|
719
|
-
locale,
|
|
720
|
-
parentProduct
|
|
721
|
-
});
|
|
722
|
-
const productNote = resolveProductNote(bundle, locale);
|
|
723
|
-
const nestedCombinations = Array.isArray(bundle.combinations) ? normalizeExistingCombinations(bundle.combinations, locale, currencySymbol) : [];
|
|
724
|
-
const quantity = toNumber(bundle.num ?? bundle.quantity ?? 1, 1);
|
|
725
|
-
const formattedPrice = formatBundlePrice(bundle, currencySymbol);
|
|
726
|
-
const optionsTotal = sumNormalizedOptionTotals(combinationsOptions, currencySymbol);
|
|
727
|
-
const explicitBasePrice = resolveCombinationBasePrice(bundle);
|
|
728
|
-
const basePrice = isPresentId(explicitBasePrice) ? explicitBasePrice : 0;
|
|
729
|
-
const extensionList = buildProductExtensionList({
|
|
730
|
-
product: bundle,
|
|
731
|
-
locale,
|
|
732
|
-
currencySymbol,
|
|
733
|
-
quantity,
|
|
734
|
-
originalPrice: formattedPrice
|
|
735
|
-
});
|
|
736
|
-
const shouldIncludeBasePrice = combinationsOptions.length > 0 || nestedCombinations.length > 0 || hasProductDiscount(bundle, extensionList);
|
|
737
|
-
return {
|
|
738
|
-
product_title: baseTitle,
|
|
739
|
-
product_quantity: quantity,
|
|
740
|
-
price: formattedPrice,
|
|
741
|
-
total_selling_price: formatCalculatedMoney(new _decimal.default(toMoneyNumber(formattedPrice)).mul(quantity), currencySymbol),
|
|
742
|
-
...(shouldIncludeBasePrice ? {
|
|
743
|
-
base_price: formatCalculatedMoney(basePrice, currencySymbol)
|
|
744
|
-
} : {}),
|
|
745
|
-
combinations_options: combinationsOptions,
|
|
746
|
-
...(combinationsOptions.length ? {
|
|
747
|
-
options_total_price: optionsTotal
|
|
748
|
-
} : {}),
|
|
749
|
-
...(nestedCombinations.length ? {
|
|
750
|
-
package_items_total_price: sumCombinationTotals(nestedCombinations, currencySymbol)
|
|
751
|
-
} : {}),
|
|
752
|
-
...(productNote ? {
|
|
753
|
-
product_note: productNote
|
|
754
|
-
} : {}),
|
|
755
|
-
...(extensionList.length ? {
|
|
756
|
-
extension_list: extensionList
|
|
757
|
-
} : {}),
|
|
758
|
-
...(nestedCombinations.length ? {
|
|
759
|
-
combinations: nestedCombinations
|
|
760
|
-
} : {})
|
|
761
|
-
};
|
|
762
|
-
});
|
|
763
|
-
}
|
|
764
|
-
function normalizeExistingCombinations(combinations, locale, currencySymbol) {
|
|
765
|
-
return combinations.map(combination => {
|
|
766
|
-
if (!combination || typeof combination !== 'object' || Array.isArray(combination)) {
|
|
767
|
-
return combination;
|
|
768
|
-
}
|
|
769
|
-
const productNote = resolveProductNote(combination, locale);
|
|
770
|
-
const rawOptions = Array.isArray(combination.combinations_options) ? combination.combinations_options : Array.isArray(combination.options) ? combination.options : [];
|
|
771
|
-
const normalizedOptions = normalizeProductOptions({
|
|
772
|
-
product: {},
|
|
773
|
-
locale,
|
|
774
|
-
currencySymbol,
|
|
775
|
-
options: rawOptions
|
|
776
|
-
});
|
|
777
|
-
const nestedCombinations = Array.isArray(combination.combinations) ? normalizeExistingCombinations(combination.combinations, locale, currencySymbol) : [];
|
|
778
|
-
const productTitle = getBundleProductTitleSource(combination, locale);
|
|
779
|
-
const snapshot = {
|
|
780
|
-
...combination
|
|
781
|
-
};
|
|
782
|
-
delete snapshot.product_title;
|
|
783
|
-
delete snapshot.product_note;
|
|
784
|
-
delete snapshot.note;
|
|
785
|
-
delete snapshot.combinations;
|
|
786
|
-
delete snapshot.discount_list;
|
|
787
|
-
const quantity = toNumber(combination.product_quantity ?? combination.num ?? combination.quantity ?? 1, 1);
|
|
788
|
-
const unitPrice = firstPresentValue(combination.selling_price, combination.price, combination.original_price) ?? 0;
|
|
789
|
-
const optionsAmount = sumMoneyValues(normalizedOptions.map(option => option.total_price));
|
|
790
|
-
const explicitBasePrice = resolveCombinationBasePrice(combination, true);
|
|
791
|
-
const basePrice = isPresentId(explicitBasePrice) ? explicitBasePrice : 0;
|
|
792
|
-
const extensionList = buildProductExtensionList({
|
|
793
|
-
product: combination,
|
|
794
|
-
locale,
|
|
795
|
-
currencySymbol,
|
|
796
|
-
quantity,
|
|
797
|
-
originalPrice: unitPrice
|
|
798
|
-
});
|
|
799
|
-
const shouldIncludeBasePrice = normalizedOptions.length > 0 || nestedCombinations.length > 0 || hasProductDiscount(combination, extensionList);
|
|
800
|
-
return {
|
|
801
|
-
...snapshot,
|
|
802
|
-
...(productTitle ? {
|
|
803
|
-
product_title: productTitle
|
|
804
|
-
} : {}),
|
|
805
|
-
product_quantity: quantity,
|
|
806
|
-
total_selling_price: formatCalculatedMoney(new _decimal.default(toMoneyNumber(unitPrice)).mul(quantity), currencySymbol),
|
|
807
|
-
...(shouldIncludeBasePrice ? {
|
|
808
|
-
base_price: formatCalculatedMoney(basePrice, currencySymbol)
|
|
809
|
-
} : {}),
|
|
810
|
-
...(Array.isArray(combination.combinations_options) ? {
|
|
811
|
-
combinations_options: normalizedOptions
|
|
812
|
-
} : Array.isArray(combination.options) ? {
|
|
813
|
-
options: normalizedOptions
|
|
814
|
-
} : {}),
|
|
815
|
-
...(normalizedOptions.length ? {
|
|
816
|
-
options_total_price: formatCalculatedMoney(optionsAmount, currencySymbol)
|
|
817
|
-
} : {}),
|
|
818
|
-
...(nestedCombinations.length ? {
|
|
819
|
-
package_items_total_price: sumCombinationTotals(nestedCombinations, currencySymbol)
|
|
820
|
-
} : {}),
|
|
821
|
-
...(productNote ? {
|
|
822
|
-
product_note: productNote
|
|
823
|
-
} : {}),
|
|
824
|
-
...(extensionList.length ? {
|
|
825
|
-
extension_list: extensionList
|
|
826
|
-
} : {}),
|
|
827
|
-
...(nestedCombinations.length ? {
|
|
828
|
-
combinations: nestedCombinations
|
|
829
|
-
} : {})
|
|
830
|
-
};
|
|
831
|
-
});
|
|
832
|
-
}
|
|
833
|
-
function sumMoneyValues(values) {
|
|
834
|
-
return values.reduce((sum, value) => sum.plus(toMoneyNumber(value)), new _decimal.default(0));
|
|
835
|
-
}
|
|
836
|
-
function resolveCombinationBasePrice(combination, preferOriginalPrice = false) {
|
|
837
|
-
const fallbackPrices = preferOriginalPrice ? [combination.original_price, combination.product_price, combination.price] : [combination.price, combination.original_price, combination.product_price];
|
|
838
|
-
return firstPresentValue(combination.base_price, combination.metadata?.source_product_price, ...fallbackPrices);
|
|
839
|
-
}
|
|
840
|
-
function sumNormalizedOptionTotals(options, currencySymbol) {
|
|
841
|
-
return formatCalculatedMoney(sumMoneyValues(options.map(option => option.total_price)), currencySymbol);
|
|
842
|
-
}
|
|
843
|
-
function sumCombinationTotals(combinations, currencySymbol) {
|
|
844
|
-
return formatCalculatedMoney(sumMoneyValues(combinations.map(combination => combination.total_selling_price)), currencySymbol);
|
|
845
|
-
}
|
|
846
|
-
function resolveProductNote(product, locale) {
|
|
847
|
-
const productNote = getLocalizedValue(product.product_note, locale).trim();
|
|
848
|
-
if (productNote) return productNote;
|
|
849
|
-
return getLocalizedValue(product.note, locale).trim();
|
|
850
|
-
}
|
|
851
|
-
function buildProductAllergyMap(order, locale) {
|
|
852
|
-
const allergyByProductUid = new Map();
|
|
853
|
-
for (const note of normalizeArray(order.notes)) {
|
|
854
|
-
if (!note || typeof note !== 'object' || Array.isArray(note) || String(note.note_type || '').trim() !== 'allergy') {
|
|
855
|
-
continue;
|
|
856
|
-
}
|
|
857
|
-
const content = getLocalizedValue(note.content?.text, locale).trim();
|
|
858
|
-
if (!content) continue;
|
|
859
|
-
const relation = normalizeArray(note.note_relations).find(item => item && typeof item === 'object' && !Array.isArray(item) && item.relation_type === 'about' && item.target_type === 'order_detail' && Number(item.is_primary) === 1);
|
|
860
|
-
const productUid = String(relation?.target_uuid ?? '').trim();
|
|
861
|
-
if (!productUid || allergyByProductUid.has(productUid)) continue;
|
|
862
|
-
allergyByProductUid.set(productUid, content);
|
|
863
|
-
}
|
|
864
|
-
return allergyByProductUid;
|
|
865
|
-
}
|
|
866
|
-
function resolveBundleProductTitle(params) {
|
|
867
|
-
const {
|
|
868
|
-
bundle,
|
|
869
|
-
productMap,
|
|
870
|
-
locale,
|
|
871
|
-
parentProduct
|
|
872
|
-
} = params;
|
|
873
|
-
const ownTitle = getBundleProductTitleSource(bundle, locale);
|
|
874
|
-
if (ownTitle) return ownTitle;
|
|
875
|
-
const parentBundleItem = resolveBundleItemFromParentProduct({
|
|
876
|
-
bundle,
|
|
877
|
-
parentProduct
|
|
878
|
-
});
|
|
879
|
-
const parentBundleTitle = parentBundleItem ? getBundleProductTitleSource(parentBundleItem, locale) : '';
|
|
880
|
-
if (parentBundleTitle) return parentBundleTitle;
|
|
881
|
-
const productId = bundle.bundle_product_id ?? bundle._bundle_product_id ?? bundle.product_id ?? bundle.id;
|
|
882
|
-
const fallbackProduct = resolveProductFromMap(productId, productMap);
|
|
883
|
-
if (!fallbackProduct) return '';
|
|
884
|
-
return getBundleProductTitleSource(fallbackProduct, locale);
|
|
885
|
-
}
|
|
886
|
-
function resolveProductFromMap(productId, productMap) {
|
|
887
|
-
if (productId === undefined || productId === null || productId === '') return null;
|
|
888
|
-
return productMap[productId] || productMap[String(productId)] || null;
|
|
889
|
-
}
|
|
890
|
-
function resolveProductVariantText(params) {
|
|
891
|
-
const {
|
|
892
|
-
product,
|
|
893
|
-
parentProduct,
|
|
894
|
-
locale
|
|
895
|
-
} = params;
|
|
896
|
-
if (product.variant) return getLocalizedValue(product.variant, locale);
|
|
897
|
-
if (!parentProduct) return '';
|
|
898
|
-
const productVariantId = product.product_variant_id ?? product.variant_id;
|
|
899
|
-
if (!isNonZeroId(productVariantId)) return '';
|
|
900
|
-
const matchedVariant = normalizeArray(parentProduct.variant).find(variant => isSameId(variant.id, productVariantId));
|
|
901
|
-
if (!matchedVariant) return '';
|
|
902
|
-
const resolvedFromSkuKey = resolveVariantTextFromSkuKey({
|
|
903
|
-
skuKey: matchedVariant.sku_key,
|
|
904
|
-
parentProduct,
|
|
905
|
-
locale
|
|
906
|
-
});
|
|
907
|
-
if (resolvedFromSkuKey) return resolvedFromSkuKey;
|
|
908
|
-
return resolveVariantTextFromNameMap({
|
|
909
|
-
nameMap: matchedVariant.name || matchedVariant.title,
|
|
910
|
-
parentProduct,
|
|
911
|
-
locale
|
|
912
|
-
});
|
|
913
|
-
}
|
|
914
|
-
function resolveVariantTextFromSkuKey(params) {
|
|
915
|
-
const skuKey = stringify(params.skuKey);
|
|
916
|
-
if (!skuKey) return '';
|
|
917
|
-
return skuKey.split('_').map(item => {
|
|
918
|
-
const [groupPositionId, itemPositionId] = item.split(':');
|
|
919
|
-
return resolveVariantTextItem({
|
|
920
|
-
parentProduct: params.parentProduct,
|
|
921
|
-
groupPositionId,
|
|
922
|
-
itemPositionId,
|
|
923
|
-
locale: params.locale
|
|
924
|
-
});
|
|
925
|
-
}).filter(Boolean).join(',');
|
|
926
|
-
}
|
|
927
|
-
function resolveVariantTextFromNameMap(params) {
|
|
928
|
-
if (!params.nameMap || typeof params.nameMap !== 'object') return '';
|
|
929
|
-
return Object.entries(params.nameMap).map(([groupPositionId, value]) => {
|
|
930
|
-
const group = resolveVariantGroup(params.parentProduct, groupPositionId);
|
|
931
|
-
if (!group) return '';
|
|
932
|
-
const groupName = getLocalizedValue(group.name, params.locale);
|
|
933
|
-
const itemName = getLocalizedValue(value, params.locale);
|
|
934
|
-
if (!groupName || !itemName) return '';
|
|
935
|
-
return `${groupName}:${itemName}`;
|
|
936
|
-
}).filter(Boolean).join(',');
|
|
937
|
-
}
|
|
938
|
-
function resolveVariantTextItem(params) {
|
|
939
|
-
const group = resolveVariantGroup(params.parentProduct, params.groupPositionId);
|
|
940
|
-
const item = resolveVariantItem(group, params.itemPositionId);
|
|
941
|
-
if (!group || !item) return '';
|
|
942
|
-
const groupName = getLocalizedValue(group.name, params.locale);
|
|
943
|
-
const itemName = getLocalizedValue(item.name, params.locale);
|
|
944
|
-
if (!groupName || !itemName) return '';
|
|
945
|
-
return `${groupName}:${itemName}`;
|
|
946
|
-
}
|
|
947
|
-
function resolveVariantGroup(parentProduct, groupPositionId) {
|
|
948
|
-
return normalizeArray(parentProduct.variant_group).find(group => isSameId(group.position_id, groupPositionId) || isSameId(group.id, groupPositionId)) || null;
|
|
949
|
-
}
|
|
950
|
-
function resolveVariantItem(group, itemPositionId) {
|
|
951
|
-
if (!group) return null;
|
|
952
|
-
return normalizeArray(group.variant_item).find(item => isSameId(item.position_id, itemPositionId) || isSameId(item.id, itemPositionId)) || null;
|
|
953
|
-
}
|
|
954
|
-
function getProductTitleSource(source, locale) {
|
|
955
|
-
if (!source || typeof source !== 'object') return '';
|
|
956
|
-
return resolveTitleValue(source.product_title, locale) || resolveTitleValue(source.title_i18n, locale) || resolveTitleValue(source.title, locale) || resolveTitleValue(source.name, locale);
|
|
957
|
-
}
|
|
958
|
-
function getBundleProductTitleSource(source, locale) {
|
|
959
|
-
if (!source || typeof source !== 'object') return '';
|
|
960
|
-
return resolveTitleValue(source.bundle_title, locale) || resolveTitleValue(source.trans_title, locale) || resolveTitleValue(source.title_i18n, locale) || resolveTitleValue(source.product_title, locale) || resolveTitleValue(source.title, locale) || resolveTitleValue(source.name, locale);
|
|
961
|
-
}
|
|
962
|
-
function resolveTitleValue(value, locale) {
|
|
963
|
-
if (value === undefined || value === null) return '';
|
|
964
|
-
if (typeof value !== 'object') return stringify(value);
|
|
965
|
-
const dashedLocale = locale.replace('_', '-');
|
|
966
|
-
const underscoredLocale = locale.replace('-', '_');
|
|
967
|
-
return stringify(value[locale] ?? value[dashedLocale] ?? value[underscoredLocale] ?? value.original ?? value.auto ?? '');
|
|
968
|
-
}
|
|
969
|
-
function resolveOptionTitleFromParentProduct(params) {
|
|
970
|
-
const {
|
|
971
|
-
option,
|
|
972
|
-
parentProduct,
|
|
973
|
-
locale
|
|
974
|
-
} = params;
|
|
975
|
-
if (!parentProduct) return '';
|
|
976
|
-
const optionItems = flattenOptionItems(parentProduct);
|
|
977
|
-
const matchedOption = optionItems.find(item => isOptionItemMatch(option, item));
|
|
978
|
-
return getProductTitleSource(matchedOption, locale);
|
|
979
|
-
}
|
|
980
|
-
function flattenOptionItems(parentProduct) {
|
|
981
|
-
const optionGroups = Array.isArray(parentProduct.option_group) ? parentProduct.option_group : [];
|
|
982
|
-
return optionGroups.flatMap(group => {
|
|
983
|
-
const items = normalizeArray(group.option_item || group.option_items || group.option || group.options || group.items || group.children || group.product_option_item);
|
|
984
|
-
return items.map(item => ({
|
|
985
|
-
...item,
|
|
986
|
-
option_group_id: item.option_group_id ?? group.id ?? group.option_group_id
|
|
987
|
-
}));
|
|
988
|
-
});
|
|
989
|
-
}
|
|
990
|
-
function isOptionItemMatch(option, item) {
|
|
991
|
-
const optionItemId = option.option_group_item_id ?? option.product_option_item_id ?? option.id;
|
|
992
|
-
const itemId = item.option_group_item_id ?? item.product_option_item_id ?? item.id;
|
|
993
|
-
if (!isSameId(optionItemId, itemId)) return false;
|
|
994
|
-
const optionGroupId = option.option_group_id ?? option.group_id;
|
|
995
|
-
const itemGroupId = item.option_group_id ?? item.group_id;
|
|
996
|
-
if (isPresentId(optionGroupId) && isPresentId(itemGroupId)) {
|
|
997
|
-
return isSameId(optionGroupId, itemGroupId);
|
|
998
|
-
}
|
|
999
|
-
return true;
|
|
1000
|
-
}
|
|
1001
|
-
function resolveBundleItemFromParentProduct(params) {
|
|
1002
|
-
const {
|
|
1003
|
-
bundle,
|
|
1004
|
-
parentProduct
|
|
1005
|
-
} = params;
|
|
1006
|
-
if (!parentProduct) return null;
|
|
1007
|
-
const bundleItems = flattenBundleItems(parentProduct);
|
|
1008
|
-
const bundleVariantId = bundle.bundle_variant_id ?? bundle.variant_id ?? bundle.product_variant_id;
|
|
1009
|
-
if (isNonZeroId(bundleVariantId)) {
|
|
1010
|
-
const matchedByVariant = bundleItems.find(item => isSameId(item.bundle_variant_id ?? item.variant_id ?? item.product_variant_id, bundleVariantId));
|
|
1011
|
-
if (matchedByVariant) return matchedByVariant;
|
|
1012
|
-
}
|
|
1013
|
-
const bundleId = bundle.bundle_id ?? bundle.id;
|
|
1014
|
-
if (isPresentId(bundleId)) {
|
|
1015
|
-
const matchedByBundleId = bundleItems.find(item => isSameId(item.id ?? item.bundle_id, bundleId));
|
|
1016
|
-
if (matchedByBundleId) return matchedByBundleId;
|
|
1017
|
-
}
|
|
1018
|
-
const bundleGroupId = bundle.bundle_group_id ?? bundle.group_id;
|
|
1019
|
-
const bundleProductId = bundle.bundle_product_id ?? bundle._bundle_product_id ?? bundle.product_id;
|
|
1020
|
-
if (isPresentId(bundleGroupId) && isPresentId(bundleProductId)) {
|
|
1021
|
-
const matchedByGroupAndProduct = bundleItems.find(item => isSameId(item.group_id ?? item.bundle_group_id, bundleGroupId) && isSameId(item.bundle_product_id ?? item._bundle_product_id ?? item.product_id, bundleProductId));
|
|
1022
|
-
if (matchedByGroupAndProduct) return matchedByGroupAndProduct;
|
|
1023
|
-
}
|
|
1024
|
-
if (isPresentId(bundleProductId)) {
|
|
1025
|
-
return bundleItems.find(item => isSameId(item.bundle_product_id ?? item._bundle_product_id ?? item.product_id, bundleProductId)) || null;
|
|
1026
|
-
}
|
|
1027
|
-
return null;
|
|
1028
|
-
}
|
|
1029
|
-
function flattenBundleItems(parentProduct) {
|
|
1030
|
-
const bundleGroups = Array.isArray(parentProduct.bundle_group) ? parentProduct.bundle_group : [];
|
|
1031
|
-
return bundleGroups.flatMap(group => {
|
|
1032
|
-
const items = Array.isArray(group.bundle_item) ? group.bundle_item : [];
|
|
1033
|
-
return items.map(item => ({
|
|
1034
|
-
...item,
|
|
1035
|
-
group_id: item.group_id ?? group.id ?? group.group_id
|
|
1036
|
-
}));
|
|
1037
|
-
});
|
|
1038
|
-
}
|
|
1039
|
-
function isNonZeroId(value) {
|
|
1040
|
-
return isPresentId(value) && String(value) !== '0';
|
|
1041
|
-
}
|
|
1042
|
-
function isPresentId(value) {
|
|
1043
|
-
return value !== undefined && value !== null && value !== '';
|
|
1044
|
-
}
|
|
1045
|
-
function firstPresentValue(...values) {
|
|
1046
|
-
return values.find(value => isPresentId(value));
|
|
1047
|
-
}
|
|
1048
|
-
function isSameId(left, right) {
|
|
1049
|
-
if (!isPresentId(left) || !isPresentId(right)) return false;
|
|
1050
|
-
return String(left) === String(right);
|
|
1051
|
-
}
|
|
1052
|
-
function formatBundlePrice(bundle, currencySymbol) {
|
|
1053
|
-
const value = bundle.bundle_selling_price ?? bundle.bundle_sum_price ?? bundle.price ?? 0;
|
|
1054
|
-
const amount = new _decimal.default(toMoneyNumber(value));
|
|
1055
|
-
// 套餐价格协议:只有 markdown 是减价;加价与商品原价都保持正数。
|
|
1056
|
-
const isNegative = bundle.price_type === 'markdown';
|
|
1057
|
-
if (isNegative) return `-${currencySymbol}${amount.abs().toFixed(2)}`;
|
|
1058
|
-
return formatMoney(amount, currencySymbol);
|
|
1059
|
-
}
|
|
1060
|
-
function buildFees(params) {
|
|
1061
|
-
const {
|
|
1062
|
-
order,
|
|
1063
|
-
shopInfo,
|
|
1064
|
-
locale,
|
|
1065
|
-
products,
|
|
1066
|
-
summary,
|
|
1067
|
-
currencySymbol,
|
|
1068
|
-
financials
|
|
1069
|
-
} = params;
|
|
1070
|
-
const fees = [];
|
|
1071
|
-
for (const surcharge of normalizeArray(order.surcharges || summary.surcharges)) {
|
|
1072
|
-
const value = surcharge.amount ?? surcharge.value ?? surcharge.total_amount;
|
|
1073
|
-
if (!isNonZero(value)) continue;
|
|
1074
|
-
fees.push({
|
|
1075
|
-
item: getLocalizedValue(surcharge.name || surcharge.title || surcharge.item, locale),
|
|
1076
|
-
value: formatMoney(value, currencySymbol)
|
|
1077
|
-
});
|
|
1078
|
-
}
|
|
1079
|
-
const discountAmount = firstPresentValue(order.shop_discount, summary.discount_amount, order.discount_amount);
|
|
1080
|
-
if (isNonZero(discountAmount)) {
|
|
1081
|
-
fees.push({
|
|
1082
|
-
item: label(locale, 'orderDiscount'),
|
|
1083
|
-
value: formatNegativeMoney(discountAmount, currencySymbol)
|
|
1084
|
-
});
|
|
1085
|
-
}
|
|
1086
|
-
const taxFee = firstPresentValue(summary.tax_fee, order.tax_fee);
|
|
1087
|
-
const taxTitle = resolveTaxTitle({
|
|
1088
|
-
summary,
|
|
1089
|
-
order,
|
|
1090
|
-
shopInfo,
|
|
1091
|
-
locale
|
|
1092
|
-
});
|
|
1093
|
-
const isPriceIncludeTax = firstPresentValue(summary.is_price_include_tax, order.is_price_include_tax, shopInfo.is_price_include_tax);
|
|
1094
|
-
const roundingAmount = firstPresentValue(summary.rounding_amount, order.rounding_amount);
|
|
1095
|
-
const payProcessingFee = financials.payProcessingFee;
|
|
1096
|
-
if (isNonZero(payProcessingFee)) {
|
|
1097
|
-
fees.push({
|
|
1098
|
-
item: label(locale, 'payProcessingFee'),
|
|
1099
|
-
value: formatMoney(payProcessingFee, currencySymbol)
|
|
1100
|
-
});
|
|
1101
|
-
}
|
|
1102
|
-
const productExpectAmount = firstPresentValue(summary.product_expect_amount, summary.product_amount, order.product_expect_amount);
|
|
1103
|
-
const productOriginalAmount = firstPresentValue(summary.product_original_amount, order.product_original_amount) ?? calculateProductOriginalAmount(order);
|
|
1104
|
-
const totalSavingsAmount = new _decimal.default(toMoneyNumber(productOriginalAmount)).minus(toMoneyNumber(productExpectAmount)).plus(toMoneyNumber(discountAmount));
|
|
1105
|
-
const itemDiscountAmount = new _decimal.default(toMoneyNumber(productExpectAmount ?? 0)).minus(toMoneyNumber(productOriginalAmount));
|
|
1106
|
-
return {
|
|
1107
|
-
fees,
|
|
1108
|
-
countFee: {
|
|
1109
|
-
total_amount: formatMoney(firstPresentValue(summary.total_amount, order.total_amount) ?? 0, currencySymbol),
|
|
1110
|
-
original_item_amount: formatMoney(productOriginalAmount, currencySymbol),
|
|
1111
|
-
product_expect_amount: formatMoney(productExpectAmount ?? 0, currencySymbol),
|
|
1112
|
-
item_discount_amount: formatCalculatedMoney(itemDiscountAmount, currencySymbol),
|
|
1113
|
-
expect_amount: formatMoney(financials.totalAmount, currencySymbol),
|
|
1114
|
-
product_quantity: toNumber(firstPresentValue(summary.product_quantity, products.reduce((sum, product) => sum + product.product_quantity, 0)), 0),
|
|
1115
|
-
has_pay: formatMoney(financials.orderPaidAmount, currencySymbol),
|
|
1116
|
-
payment_method: '',
|
|
1117
|
-
tax_title: taxTitle,
|
|
1118
|
-
total_refund_amount: formatMoney(financials.totalRefundAmount, currencySymbol),
|
|
1119
|
-
customer_paid_amount: formatMoney(financials.customerPaidAmount, currencySymbol),
|
|
1120
|
-
net_payment_applied: formatMoney(financials.netPaymentApplied, currencySymbol),
|
|
1121
|
-
...(isPresentId(roundingAmount) ? {
|
|
1122
|
-
rounding_amount: formatMoney(roundingAmount, currencySymbol)
|
|
1123
|
-
} : {}),
|
|
1124
|
-
...(isNonZero(totalSavingsAmount) ? {
|
|
1125
|
-
order_total_discount: formatMoney(totalSavingsAmount, currencySymbol)
|
|
1126
|
-
} : {}),
|
|
1127
|
-
...(financials.hasPayProcessingFee ? {
|
|
1128
|
-
pay_processing_fee: formatMoney(payProcessingFee, currencySymbol)
|
|
1129
|
-
} : {}),
|
|
1130
|
-
...(isPresentId(taxFee) ? {
|
|
1131
|
-
tax_fee: formatMoney(taxFee, currencySymbol)
|
|
1132
|
-
} : {}),
|
|
1133
|
-
...(isPresentId(isPriceIncludeTax) ? {
|
|
1134
|
-
is_price_include_tax: normalizeBooleanFlag(isPriceIncludeTax)
|
|
1135
|
-
} : {})
|
|
1136
|
-
}
|
|
1137
|
-
};
|
|
1138
|
-
}
|
|
1139
|
-
function resolveReceiptFinancials(order, summary) {
|
|
1140
|
-
const totalAmount = firstPresentValue(summary.expect_amount, order.expect_amount, summary.total_amount, order.total_amount) ?? 0;
|
|
1141
|
-
const rawPayProcessingFee = firstPresentValue(summary.pay_service_charge_amount, order.pay_service_charge_amount);
|
|
1142
|
-
const payProcessingFee = rawPayProcessingFee ?? 0;
|
|
1143
|
-
const orderPaidAmount = firstPresentValue(summary.order_paid_amount, order.order_paid_amount, sumAppliedPayments(order.payments)) ?? 0;
|
|
1144
|
-
const totalRefundAmount = firstPresentValue(summary.total_refund_amount, order.total_refund_amount) ?? 0;
|
|
1145
|
-
const netPaymentApplied = _decimal.default.max(new _decimal.default(toMoneyNumber(orderPaidAmount)).minus(toMoneyNumber(totalRefundAmount)), 0);
|
|
1146
|
-
const fallbackCustomerPaidAmount = _decimal.default.max(sumCustomerPaidPayments(order.payments), new _decimal.default(toMoneyNumber(orderPaidAmount)).plus(toMoneyNumber(payProcessingFee)));
|
|
1147
|
-
const customerPaidAmount = firstPresentValue(summary.customer_paid_amount, order.customer_paid_amount, fallbackCustomerPaidAmount) ?? 0;
|
|
1148
|
-
const amountGap = firstPresentValue(summary.amount_gap, order.amount_gap);
|
|
1149
|
-
return {
|
|
1150
|
-
totalAmount,
|
|
1151
|
-
orderPaidAmount,
|
|
1152
|
-
payProcessingFee,
|
|
1153
|
-
customerPaidAmount,
|
|
1154
|
-
totalRefundAmount,
|
|
1155
|
-
netPaymentApplied,
|
|
1156
|
-
amountGap,
|
|
1157
|
-
paymentStatus: resolveReceiptPaymentStatus({
|
|
1158
|
-
order,
|
|
1159
|
-
summary
|
|
1160
|
-
}),
|
|
1161
|
-
hasPayProcessingFee: isPresentId(rawPayProcessingFee)
|
|
1162
|
-
};
|
|
1163
|
-
}
|
|
1164
|
-
function resolveReceiptPaymentStatus(params) {
|
|
1165
|
-
return [params.order.payment_status, params.summary.payment_status, params.order.original_payment_status].map(status => stringify(status).trim()).find(Boolean) || '';
|
|
1166
|
-
}
|
|
1167
|
-
function resolveTaxTitle(params) {
|
|
1168
|
-
return [params.summary.tax_title, params.order.tax_title, params.shopInfo.tax_title].map(title => getLocalizedValue(title, params.locale).trim()).find(Boolean) || 'Tax';
|
|
1169
|
-
}
|
|
1170
|
-
function calculateProductOriginalAmount(order) {
|
|
1171
|
-
return normalizeArray(order.products).reduce((total, product) => {
|
|
1172
|
-
const quantity = toNumber(product.product_quantity ?? product.quantity ?? product.num ?? 1, 1);
|
|
1173
|
-
const originalPrice = product.original_price ?? product.selling_price ?? product.payment_price ?? product.price ?? 0;
|
|
1174
|
-
return total.plus(new _decimal.default(toMoneyNumber(originalPrice)).mul(quantity));
|
|
1175
|
-
}, new _decimal.default(0));
|
|
1176
|
-
}
|
|
1177
|
-
function buildCustomer(order, locale) {
|
|
1178
|
-
return [{
|
|
1179
|
-
item: label(locale, 'contactName'),
|
|
1180
|
-
value: normalizeCustomerDisplayValue(order.customer_name || order.customer?.name || order.customer?.display_name)
|
|
1181
|
-
}, {
|
|
1182
|
-
item: label(locale, 'contactMobile'),
|
|
1183
|
-
value: stringify(order.phone || order.customer?.phone)
|
|
1184
|
-
}, {
|
|
1185
|
-
item: label(locale, 'email'),
|
|
1186
|
-
value: stringify(order.email || order.customer?.email)
|
|
1187
|
-
}];
|
|
1188
|
-
}
|
|
1189
|
-
function normalizeCustomerDisplayValue(value) {
|
|
1190
|
-
const text = stringify(value).trim();
|
|
1191
|
-
if (!text) return 'Walk-In';
|
|
1192
|
-
if (text.toLowerCase() === 'select a customer') return 'Walk-In';
|
|
1193
|
-
return text;
|
|
1194
|
-
}
|
|
1195
|
-
function buildDelivery(order, locale) {
|
|
1196
|
-
const serviceType = stringify(order.metadata?.shop_service_data?.service_type).trim();
|
|
1197
|
-
const serviceTypeLabelKey = resolveServiceTypeLabelKey(serviceType);
|
|
1198
|
-
if (!serviceTypeLabelKey) return [];
|
|
1199
|
-
const items = [{
|
|
1200
|
-
item: label(locale, serviceTypeLabelKey),
|
|
1201
|
-
value: '',
|
|
1202
|
-
size: 2
|
|
1203
|
-
}];
|
|
1204
|
-
if (serviceType === 'pick_up') {
|
|
1205
|
-
const pickupTime = formatPickupBookingStart(order.bookings, locale);
|
|
1206
|
-
if (pickupTime) {
|
|
1207
|
-
items.push({
|
|
1208
|
-
item: pickupTime,
|
|
1209
|
-
value: '',
|
|
1210
|
-
size: 2
|
|
1211
|
-
});
|
|
1212
|
-
return items;
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
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);
|
|
1216
|
-
if (isPresentId(handoverTime)) {
|
|
1217
|
-
items.push({
|
|
1218
|
-
item: label(locale, 'handoverTime'),
|
|
1219
|
-
value: formatDateTime(handoverTime, locale)
|
|
1220
|
-
});
|
|
1221
|
-
}
|
|
1222
|
-
return items;
|
|
1223
|
-
}
|
|
1224
|
-
function formatPickupBookingStart(bookings, locale) {
|
|
1225
|
-
const booking = normalizeArray(bookings).find(_pickupTiming.isFulfillmentTimingBooking);
|
|
1226
|
-
const date = stringify(booking?.start_date).trim();
|
|
1227
|
-
const time = stringify(booking?.start_time).trim();
|
|
1228
|
-
if (!/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/.test(date) || !/^([01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$/.test(time)) {
|
|
1229
|
-
return '';
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
// Validate only the calendar date in UTC; display the stored shop-local
|
|
1233
|
-
// fields verbatim so the printing device's timezone cannot shift pickup.
|
|
1234
|
-
const calendarDate = new Date(`${date}T00:00:00.000Z`);
|
|
1235
|
-
if (!Number.isFinite(calendarDate.getTime()) || calendarDate.toISOString().slice(0, 10) !== date) {
|
|
1236
|
-
return '';
|
|
1237
|
-
}
|
|
1238
|
-
const [year, month, day] = date.split('-');
|
|
1239
|
-
const dateText = locale === 'en' ? `${day}/${month}/${year}` : `${year}/${month}/${day}`;
|
|
1240
|
-
return `${time.slice(0, 5)} ${dateText}`;
|
|
1241
|
-
}
|
|
1242
|
-
function buildAppointment(order, locale) {
|
|
1243
|
-
const parentScheduleId = order.metadata?.appointment_booking_parent_schedule_id;
|
|
1244
|
-
const bookings = sortBookings(normalizeArray(order.bookings).filter(booking => !(0, _pickupTiming.isFulfillmentTimingBooking)(booking) && isReceiptAppointmentBooking(booking, parentScheduleId)));
|
|
1245
|
-
if (bookings.length) {
|
|
1246
|
-
const allResources = normalizeArray(order.resources || order.resource_list);
|
|
1247
|
-
return bookings.flatMap(booking => buildAppointmentRows({
|
|
1248
|
-
booking,
|
|
1249
|
-
allResources,
|
|
1250
|
-
bookingCount: bookings.length,
|
|
1251
|
-
locale
|
|
1252
|
-
}));
|
|
1253
|
-
}
|
|
1254
|
-
const tableName = order.table_name || order.metadata?.table_name || order.metadata?.table?.name || order.form_data?.table_name;
|
|
1255
|
-
if (!tableName) return [];
|
|
1256
|
-
return [{
|
|
1257
|
-
item: label(locale, 'table'),
|
|
1258
|
-
value: getLocalizedValue(tableName, locale)
|
|
1259
|
-
}];
|
|
1260
|
-
}
|
|
1261
|
-
function isReceiptAppointmentBooking(booking, parentScheduleId) {
|
|
1262
|
-
if (!booking || typeof booking !== 'object' || Array.isArray(booking)) {
|
|
1263
|
-
return false;
|
|
1264
|
-
}
|
|
1265
|
-
if (isPresentId(parentScheduleId) && isSameId(booking.schedule_event_id, parentScheduleId)) {
|
|
1266
|
-
return false;
|
|
1267
|
-
}
|
|
1268
|
-
const parentId = booking.parent_id;
|
|
1269
|
-
if (parentId === undefined || parentId === null || parentId === '') {
|
|
1270
|
-
return true;
|
|
1271
|
-
}
|
|
1272
|
-
return Number(parentId) !== 0;
|
|
1273
|
-
}
|
|
1274
|
-
function buildAppointmentRows(params) {
|
|
1275
|
-
const {
|
|
1276
|
-
booking,
|
|
1277
|
-
allResources,
|
|
1278
|
-
bookingCount,
|
|
1279
|
-
locale
|
|
1280
|
-
} = params;
|
|
1281
|
-
const productUid = stringify(firstPresentValue(booking.product_uid, booking.metadata?.product_uid)).trim();
|
|
1282
|
-
const bookingUid = stringify(firstPresentValue(booking.metadata?.unique_identification_number, booking.booking_uid)).trim();
|
|
1283
|
-
const association = {
|
|
1284
|
-
product_uid: productUid,
|
|
1285
|
-
booking_uid: bookingUid
|
|
1286
|
-
};
|
|
1287
|
-
const bookingId = stringify(firstPresentValue(booking.booking_id, booking.schedule_event_id, bookingUid)).trim();
|
|
1288
|
-
const bookingTime = formatBookingRange(booking, locale);
|
|
1289
|
-
const resourceText = formatBookingResources(resolveBookingResourcesForReceipt(booking, allResources, bookingCount), locale);
|
|
1290
|
-
const holder = getLocalizedValue(booking.holder?.name ?? booking.metadata?.holder?.name, locale).trim();
|
|
1291
|
-
return [{
|
|
1292
|
-
item: label(locale, 'bookingId'),
|
|
1293
|
-
value: bookingId,
|
|
1294
|
-
...association
|
|
1295
|
-
}, {
|
|
1296
|
-
item: label(locale, 'bookingDateTime'),
|
|
1297
|
-
value: bookingTime,
|
|
1298
|
-
...association
|
|
1299
|
-
}, {
|
|
1300
|
-
item: label(locale, 'bookingResource'),
|
|
1301
|
-
value: resourceText,
|
|
1302
|
-
...association
|
|
1303
|
-
}, {
|
|
1304
|
-
item: label(locale, 'bookingHolder'),
|
|
1305
|
-
value: holder,
|
|
1306
|
-
...association
|
|
1307
|
-
}].filter(row => Boolean(row.value));
|
|
1308
|
-
}
|
|
1309
|
-
function formatBookingRange(booking, locale) {
|
|
1310
|
-
const startDate = stringify(booking.start_date);
|
|
1311
|
-
const startTime = stringify(booking.start_time);
|
|
1312
|
-
const endDate = stringify(booking.end_date || startDate);
|
|
1313
|
-
const endTime = stringify(booking.end_time);
|
|
1314
|
-
const start = firstValidBookingDate(booking.start_at, booking.start_datetime, booking.start_date_time, `${startDate} ${startTime}`.trim());
|
|
1315
|
-
const end = firstValidBookingDate(booking.end_at, booking.end_datetime, booking.end_date_time, endTime ? `${endDate} ${endTime}`.trim() : '');
|
|
1316
|
-
if (!start) {
|
|
1317
|
-
return [startDate, startTime, endTime ? `- ${endDate} ${endTime}` : ''].filter(Boolean).join(' ');
|
|
1318
|
-
}
|
|
1319
|
-
const dateFormat = locale === 'en' ? 'DD/MM/YYYY' : 'YYYY/MM/DD';
|
|
1320
|
-
const startText = start.format(`${dateFormat} HH:mm`);
|
|
1321
|
-
if (!end) return startText;
|
|
1322
|
-
return `${startText} - ${end.isSame(start, 'day') ? end.format('HH:mm') : end.format(`${dateFormat} HH:mm`)}`;
|
|
1323
|
-
}
|
|
1324
|
-
function firstValidBookingDate(...values) {
|
|
1325
|
-
for (const value of values) {
|
|
1326
|
-
if (!isPresentId(value)) continue;
|
|
1327
|
-
const parsed = (0, _dayjs.default)(value);
|
|
1328
|
-
if (parsed.isValid()) return parsed;
|
|
1329
|
-
}
|
|
1330
|
-
return null;
|
|
1331
|
-
}
|
|
1332
|
-
function resolveBookingResourcesForReceipt(booking, allResources, bookingCount) {
|
|
1333
|
-
const embedded = normalizeArray(booking.resources).filter(resource => Boolean(resource) && typeof resource === 'object' && !Array.isArray(resource));
|
|
1334
|
-
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));
|
|
1335
|
-
for (const resource of embedded) {
|
|
1336
|
-
const id = stringify(resource.id ?? resource.resource_id ?? resource.relation_id).trim();
|
|
1337
|
-
if (id) resourceIds.add(id);
|
|
1338
|
-
}
|
|
1339
|
-
if (resourceIds.size) {
|
|
1340
|
-
const matched = allResources.filter(resource => resourceIds.has(stringify(resource.id ?? resource.resource_id ?? resource.relation_id).trim()));
|
|
1341
|
-
if (matched.length) return matched;
|
|
1342
|
-
}
|
|
1343
|
-
if (embedded.length) return embedded;
|
|
1344
|
-
if (bookingCount === 1 && allResources.length) return allResources;
|
|
1345
|
-
return [];
|
|
1346
|
-
}
|
|
1347
|
-
function formatBookingResources(resources, locale) {
|
|
1348
|
-
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(', ');
|
|
1349
|
-
}
|
|
1350
|
-
function sortBookings(bookings) {
|
|
1351
|
-
return bookings.map((booking, index) => ({
|
|
1352
|
-
booking,
|
|
1353
|
-
index
|
|
1354
|
-
})).sort((left, right) => {
|
|
1355
|
-
const leftStart = bookingStartTimestamp(left.booking);
|
|
1356
|
-
const rightStart = bookingStartTimestamp(right.booking);
|
|
1357
|
-
if (leftStart !== rightStart) return leftStart < rightStart ? -1 : 1;
|
|
1358
|
-
for (const field of ['booking_id', 'schedule_event_id']) {
|
|
1359
|
-
const fieldOrder = compareOptionalValues(left.booking?.[field], right.booking?.[field]);
|
|
1360
|
-
if (fieldOrder !== 0) return fieldOrder;
|
|
1361
|
-
}
|
|
1362
|
-
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));
|
|
1363
|
-
return bookingUidOrder || left.index - right.index;
|
|
1364
|
-
}).map(({
|
|
1365
|
-
booking
|
|
1366
|
-
}) => booking);
|
|
1367
|
-
}
|
|
1368
|
-
function bookingStartTimestamp(booking) {
|
|
1369
|
-
const startValues = [booking?.start_at, booking?.start_datetime, booking?.booking_time, isPresentId(booking?.start_date) ? `${booking.start_date} ${stringify(booking.start_time)}`.trim() : undefined];
|
|
1370
|
-
for (const startValue of startValues) {
|
|
1371
|
-
if (!isPresentId(startValue)) continue;
|
|
1372
|
-
const timestamp = (0, _dayjs.default)(startValue).valueOf();
|
|
1373
|
-
if (Number.isFinite(timestamp)) return timestamp;
|
|
1374
|
-
}
|
|
1375
|
-
return Number.POSITIVE_INFINITY;
|
|
1376
|
-
}
|
|
1377
|
-
function compareOptionalValues(left, right) {
|
|
1378
|
-
const hasLeft = isPresentId(left);
|
|
1379
|
-
const hasRight = isPresentId(right);
|
|
1380
|
-
if (!hasLeft && !hasRight) return 0;
|
|
1381
|
-
if (!hasLeft) return 1;
|
|
1382
|
-
if (!hasRight) return -1;
|
|
1383
|
-
return String(left).localeCompare(String(right), undefined, {
|
|
1384
|
-
numeric: true
|
|
1385
|
-
});
|
|
1386
|
-
}
|
|
1387
|
-
function buildResources(order, locale) {
|
|
1388
|
-
return normalizeArray(order.resources || order.resource_list).map(resource => ({
|
|
1389
|
-
id: resource.id || resource.resource_id || '',
|
|
1390
|
-
code: '',
|
|
1391
|
-
title: getLocalizedValue(resource.title || resource.name, locale),
|
|
1392
|
-
form_title: getLocalizedValue(resource.form_title || resource.type || '', locale)
|
|
1393
|
-
}));
|
|
1394
|
-
}
|
|
1395
|
-
function buildPaymentData(params) {
|
|
1396
|
-
const payments = normalizeArray(params.order.payments);
|
|
1397
|
-
return payments.filter(payment => isPrintablePayment(payment)).map(payment => buildPaymentItems({
|
|
1398
|
-
payment,
|
|
1399
|
-
locale: params.locale,
|
|
1400
|
-
currencySymbol: params.currencySymbol,
|
|
1401
|
-
includeFundRecordBalance: false,
|
|
1402
|
-
includeFundRecordUsage: true,
|
|
1403
|
-
includePaymentAmount: true
|
|
1404
|
-
}));
|
|
1405
|
-
}
|
|
1406
|
-
function buildRefundData(params) {
|
|
1407
|
-
const payments = normalizeArray(params.order.payments);
|
|
1408
|
-
const refunds = Array.from(normalizeArray(params.order.refunds).reduce((result, refund, index) => {
|
|
1409
|
-
const identity = refundIdentity(refund, index);
|
|
1410
|
-
const candidate = {
|
|
1411
|
-
refund,
|
|
1412
|
-
index
|
|
1413
|
-
};
|
|
1414
|
-
const existing = result.get(identity);
|
|
1415
|
-
if (!existing || shouldPreferRefundCandidate(candidate, existing)) {
|
|
1416
|
-
result.set(identity, candidate);
|
|
1417
|
-
}
|
|
1418
|
-
return result;
|
|
1419
|
-
}, new Map()).values()).filter(({
|
|
1420
|
-
refund
|
|
1421
|
-
}) => isPrintableRefund(refund)).sort((left, right) => {
|
|
1422
|
-
const leftTime = refundTimestamp(left.refund);
|
|
1423
|
-
const rightTime = refundTimestamp(right.refund);
|
|
1424
|
-
if (leftTime !== rightTime) return leftTime < rightTime ? -1 : 1;
|
|
1425
|
-
return left.index - right.index;
|
|
1426
|
-
});
|
|
1427
|
-
return refunds.map(({
|
|
1428
|
-
refund
|
|
1429
|
-
}) => {
|
|
1430
|
-
const payment = resolveRefundPayment(refund, payments);
|
|
1431
|
-
const methodName = paymentMethodName(payment, params.locale) || label(params.locale, 'refund');
|
|
1432
|
-
const amount = firstPresentValue(refund.amount, refund.refund_amount, refund.total_amount) ?? 0;
|
|
1433
|
-
const items = [{
|
|
1434
|
-
item: methodName,
|
|
1435
|
-
value: formatNegativeMoney(amount, params.currencySymbol)
|
|
1436
|
-
}];
|
|
1437
|
-
const occurredAt = firstPresentValue(refund.refund_time, refund.refunded_at, refund.created_at, refund.updated_at);
|
|
1438
|
-
if (isPresentId(occurredAt)) {
|
|
1439
|
-
items.push({
|
|
1440
|
-
item: formatDateTime(occurredAt, params.locale),
|
|
1441
|
-
value: ''
|
|
1442
|
-
});
|
|
1443
|
-
}
|
|
1444
|
-
return items;
|
|
1445
|
-
});
|
|
1446
|
-
}
|
|
1447
|
-
function refundIdentity(refund, index) {
|
|
1448
|
-
if (isPresentId(refund.order_refund_id)) return `refund-id:${refund.order_refund_id}`;
|
|
1449
|
-
if (isPresentId(refund.refund_number)) return `refund-number:${refund.refund_number}`;
|
|
1450
|
-
return `refund-index:${index}`;
|
|
1451
|
-
}
|
|
1452
|
-
function shouldPreferRefundCandidate(candidate, existing) {
|
|
1453
|
-
const candidatePrintable = isPrintableRefund(candidate.refund);
|
|
1454
|
-
const existingPrintable = isPrintableRefund(existing.refund);
|
|
1455
|
-
if (candidatePrintable !== existingPrintable) return candidatePrintable;
|
|
1456
|
-
const candidateTime = refundFreshnessTimestamp(candidate.refund);
|
|
1457
|
-
const existingTime = refundFreshnessTimestamp(existing.refund);
|
|
1458
|
-
if (candidateTime !== existingTime) return candidateTime > existingTime;
|
|
1459
|
-
return candidate.index > existing.index;
|
|
1460
|
-
}
|
|
1461
|
-
function refundFreshnessTimestamp(refund) {
|
|
1462
|
-
const timestampValue = firstPresentValue(refund.updated_at, refund.refunded_at, refund.refund_time, refund.created_at);
|
|
1463
|
-
if (!isPresentId(timestampValue)) return Number.NEGATIVE_INFINITY;
|
|
1464
|
-
const timestamp = (0, _dayjs.default)(timestampValue).valueOf();
|
|
1465
|
-
return Number.isFinite(timestamp) ? timestamp : Number.NEGATIVE_INFINITY;
|
|
1466
|
-
}
|
|
1467
|
-
function refundTimestamp(refund) {
|
|
1468
|
-
const occurredAt = firstPresentValue(refund.refund_time, refund.refunded_at, refund.created_at, refund.updated_at);
|
|
1469
|
-
if (!isPresentId(occurredAt)) return Number.POSITIVE_INFINITY;
|
|
1470
|
-
const timestamp = (0, _dayjs.default)(occurredAt).valueOf();
|
|
1471
|
-
return Number.isFinite(timestamp) ? timestamp : Number.POSITIVE_INFINITY;
|
|
1472
|
-
}
|
|
1473
|
-
function isPrintableRefund(refund) {
|
|
1474
|
-
const status = stringify(refund.status).trim().toLowerCase();
|
|
1475
|
-
if (!status) return true;
|
|
1476
|
-
return !['pending', 'payment_pending', 'payment_processing', 'refund_processing', 'processing', 'failed', 'error', 'cancelled', 'canceled', 'voided'].includes(status);
|
|
1477
|
-
}
|
|
1478
|
-
function resolveRefundPayment(refund, payments) {
|
|
1479
|
-
if (refund.payment && typeof refund.payment === 'object' && !Array.isArray(refund.payment)) {
|
|
1480
|
-
return refund.payment;
|
|
1481
|
-
}
|
|
1482
|
-
const orderPaymentId = firstPresentValue(refund.order_payment_id, refund.payment_id);
|
|
1483
|
-
const matchedPayment = payments.find(payment => isSameId(firstPresentValue(payment.order_payment_id, payment.id), orderPaymentId));
|
|
1484
|
-
return matchedPayment || refund;
|
|
1485
|
-
}
|
|
1486
|
-
function buildWallets(params) {
|
|
1487
|
-
const virtualCurrencySnapshots = collectVirtualCurrencySnapshots(params.order);
|
|
1488
|
-
const displayedWalletPayments = normalizeArray(params.order.payments).filter(payment => isPrintablePayment(payment) && !!getFundRecord(payment));
|
|
1489
|
-
const paymentWallets = displayedWalletPayments.map(payment => {
|
|
1490
|
-
const virtualCurrencySnapshot = findVirtualCurrencySnapshotForPayment(payment, virtualCurrencySnapshots.byId);
|
|
1491
|
-
return buildPaymentItems({
|
|
1492
|
-
payment,
|
|
1493
|
-
locale: params.locale,
|
|
1494
|
-
currencySymbol: params.currencySymbol,
|
|
1495
|
-
includeFundRecordBalance: true,
|
|
1496
|
-
includeFundRecordUsage: false,
|
|
1497
|
-
includePaymentAmount: false,
|
|
1498
|
-
virtualCurrencyUnit: virtualCurrencySnapshot ? getVirtualCurrencySnapshotUnit(virtualCurrencySnapshot, params.locale) : undefined
|
|
1499
|
-
});
|
|
1500
|
-
});
|
|
1501
|
-
const displayedWalletAssetIdentities = collectDisplayedWalletAssetIdentities(displayedWalletPayments);
|
|
1502
|
-
const voucherWallets = buildVoucherAssets({
|
|
1503
|
-
...params,
|
|
1504
|
-
displayedWalletAssetIdentities
|
|
1505
|
-
});
|
|
1506
|
-
const productDiscountWallets = buildProductDiscountAssets({
|
|
1507
|
-
...params,
|
|
1508
|
-
displayedWalletAssetIdentities
|
|
1509
|
-
});
|
|
1510
|
-
return [...paymentWallets, ...voucherWallets, ...productDiscountWallets, ...buildPointCardSnapshotAssets({
|
|
1511
|
-
order: params.order,
|
|
1512
|
-
locale: params.locale,
|
|
1513
|
-
displayedWalletAssetIdentities
|
|
1514
|
-
}), ...buildVirtualCurrencySnapshotAssets({
|
|
1515
|
-
snapshots: virtualCurrencySnapshots.items,
|
|
1516
|
-
order: params.order,
|
|
1517
|
-
locale: params.locale,
|
|
1518
|
-
displayedWalletAssetIdentities
|
|
1519
|
-
})];
|
|
1520
|
-
}
|
|
1521
|
-
function collectDisplayedWalletAssetIdentities(payments) {
|
|
1522
|
-
const identities = [];
|
|
1523
|
-
payments.forEach(payment => {
|
|
1524
|
-
const fundRecord = getFundRecord(payment);
|
|
1525
|
-
const ids = Array.from(new Set([fundRecord?.voucher_id, payment.voucher_id].map(value => normalizeWalletPassId(value)).filter(value => !!value)));
|
|
1526
|
-
const codes = normalizeWalletAssetCodes(fundRecord?.code, fundRecord?.encoded);
|
|
1527
|
-
if (!ids.length) {
|
|
1528
|
-
registerWalletAssetIdentity(identities, {
|
|
1529
|
-
id: null,
|
|
1530
|
-
codes
|
|
1531
|
-
});
|
|
1532
|
-
return;
|
|
1533
|
-
}
|
|
1534
|
-
ids.forEach(id => {
|
|
1535
|
-
registerWalletAssetIdentity(identities, {
|
|
1536
|
-
id,
|
|
1537
|
-
codes
|
|
1538
|
-
});
|
|
1539
|
-
});
|
|
1540
|
-
});
|
|
1541
|
-
return identities;
|
|
1542
|
-
}
|
|
1543
|
-
const PRODUCT_WALLET_DISCOUNT_TYPES = new Set(['good_pass', 'discount_card', 'product_discount_card', 'entitlement_pass']);
|
|
1544
|
-
function buildProductDiscountAssets(params) {
|
|
1545
|
-
const fallbackAssetTime = firstPresentValue(params.order.updated_at, params.order.created_at);
|
|
1546
|
-
return collectProductWalletDiscountAssets(params.order).flatMap(asset => {
|
|
1547
|
-
if (!claimWalletAssetIdentity(params.displayedWalletAssetIdentities, {
|
|
1548
|
-
id: asset.id,
|
|
1549
|
-
codes: asset.codes
|
|
1550
|
-
})) return [];
|
|
1551
|
-
const name = getProductWalletDiscountName(asset, params.locale);
|
|
1552
|
-
const assetTime = getProductWalletDiscountTime(asset) ?? fallbackAssetTime;
|
|
1553
|
-
const timeRow = isPresentId(assetTime) ? {
|
|
1554
|
-
item: formatDateTime(assetTime, params.locale),
|
|
1555
|
-
value: ''
|
|
1556
|
-
} : null;
|
|
1557
|
-
if (asset.type === 'entitlement_pass') {
|
|
1558
|
-
const remaining = getProductEntitlementBalance(asset);
|
|
1559
|
-
return [[{
|
|
1560
|
-
item: name,
|
|
1561
|
-
value: ''
|
|
1562
|
-
}, ...(remaining ? [{
|
|
1563
|
-
item: label(params.locale, 'entitlementRemaining'),
|
|
1564
|
-
value: formatEntitlementUses(remaining, params.locale)
|
|
1565
|
-
}] : []), ...(timeRow ? [timeRow] : [])]];
|
|
6
|
+
var _smallTicket = require("../../shared/receipt/small-ticket");
|
|
7
|
+
Object.keys(_smallTicket).forEach(function (key) {
|
|
8
|
+
if (key === "default" || key === "__esModule") return;
|
|
9
|
+
if (key in exports && exports[key] === _smallTicket[key]) return;
|
|
10
|
+
Object.defineProperty(exports, key, {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function () {
|
|
13
|
+
return _smallTicket[key];
|
|
1566
14
|
}
|
|
1567
|
-
const percent = getProductWalletDiscountPercent(asset);
|
|
1568
|
-
if (percent) {
|
|
1569
|
-
return [[{
|
|
1570
|
-
item: appendPercentToVoucherName(name, percent),
|
|
1571
|
-
value: formatVoucherDiscount(percent, params.locale)
|
|
1572
|
-
}, ...(timeRow ? [timeRow] : [])]];
|
|
1573
|
-
}
|
|
1574
|
-
const balance = getProductWalletMoneyBalance(asset);
|
|
1575
|
-
return [[{
|
|
1576
|
-
item: name,
|
|
1577
|
-
value: ''
|
|
1578
|
-
}, ...(balance ? [{
|
|
1579
|
-
item: label(params.locale, 'balance'),
|
|
1580
|
-
value: formatMoney(balance, params.currencySymbol)
|
|
1581
|
-
}] : []), ...(timeRow ? [timeRow] : [])]];
|
|
1582
|
-
});
|
|
1583
|
-
}
|
|
1584
|
-
function collectProductWalletDiscountAssets(order) {
|
|
1585
|
-
const assets = [];
|
|
1586
|
-
const assetsById = new Map();
|
|
1587
|
-
normalizeArray(order.products).forEach(rawProduct => {
|
|
1588
|
-
if (!rawProduct || typeof rawProduct !== 'object') return;
|
|
1589
|
-
visitProductWalletDiscounts(restoreReceiptDiscountHierarchy(rawProduct), assets, assetsById);
|
|
1590
|
-
});
|
|
1591
|
-
return assets;
|
|
1592
|
-
}
|
|
1593
|
-
function visitProductWalletDiscounts(product, assets, assetsById) {
|
|
1594
|
-
normalizeArray(product.discount_list).forEach(record => {
|
|
1595
|
-
if (!record || typeof record !== 'object' || Array.isArray(record)) return;
|
|
1596
|
-
const type = getProductWalletDiscountType(record);
|
|
1597
|
-
if (!type) return;
|
|
1598
|
-
const id = getProductWalletDiscountAssetId(record);
|
|
1599
|
-
if (!id) return;
|
|
1600
|
-
const codes = getProductWalletDiscountCodes(record);
|
|
1601
|
-
const existing = assetsById.get(id);
|
|
1602
|
-
if (existing) {
|
|
1603
|
-
existing.records.push(record);
|
|
1604
|
-
existing.codes = Array.from(new Set([...existing.codes, ...codes]));
|
|
1605
|
-
return;
|
|
1606
|
-
}
|
|
1607
|
-
const asset = {
|
|
1608
|
-
id,
|
|
1609
|
-
type,
|
|
1610
|
-
codes,
|
|
1611
|
-
records: [record]
|
|
1612
|
-
};
|
|
1613
|
-
assetsById.set(id, asset);
|
|
1614
|
-
assets.push(asset);
|
|
1615
|
-
});
|
|
1616
|
-
for (const childKey of ['combinations', 'product_bundle', 'bundle']) {
|
|
1617
|
-
normalizeArray(product[childKey]).forEach(child => {
|
|
1618
|
-
if (!child || typeof child !== 'object' || Array.isArray(child)) return;
|
|
1619
|
-
visitProductWalletDiscounts(child, assets, assetsById);
|
|
1620
|
-
});
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
function getProductWalletDiscountType(record) {
|
|
1624
|
-
for (const value of [record.tag, record.type, record.discount?.tag, record.discount?.type]) {
|
|
1625
|
-
const type = stringify(value).trim().toLowerCase();
|
|
1626
|
-
if (PRODUCT_WALLET_DISCOUNT_TYPES.has(type)) return type;
|
|
1627
|
-
}
|
|
1628
|
-
return null;
|
|
1629
|
-
}
|
|
1630
|
-
function getProductWalletDiscountAssetId(record) {
|
|
1631
|
-
for (const value of [record.discount?.resource_id, record.resource_id, record.id, record.discount_id]) {
|
|
1632
|
-
const id = normalizeWalletPassId(value);
|
|
1633
|
-
if (id && id !== '0') return id;
|
|
1634
|
-
}
|
|
1635
|
-
return null;
|
|
1636
|
-
}
|
|
1637
|
-
function getProductWalletDiscountCodes(record) {
|
|
1638
|
-
return normalizeWalletAssetCodes(record.code, record.encoded, record.discount?.code, record.discount?.encoded);
|
|
1639
|
-
}
|
|
1640
|
-
function getProductWalletDiscountName(asset, locale) {
|
|
1641
|
-
for (const record of asset.records) {
|
|
1642
|
-
for (const value of [record.discount?.title, record.discount?.name, record.discount?.message, record.product_name, record.format_title, record.title, record.name, record.message]) {
|
|
1643
|
-
const name = getLocalizedValue(value, locale).trim();
|
|
1644
|
-
if (name) return name;
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
return asset.codes[0] || label(locale, 'giftCard');
|
|
1648
|
-
}
|
|
1649
|
-
function getProductWalletDiscountTime(asset) {
|
|
1650
|
-
for (const field of ['updated_at', 'created_at', 'collection_time']) {
|
|
1651
|
-
for (const record of asset.records) {
|
|
1652
|
-
const value = firstPresentValue(record[field], record.discount?.[field]);
|
|
1653
|
-
if (isPresentId(value)) return value;
|
|
1654
|
-
}
|
|
1655
|
-
}
|
|
1656
|
-
return undefined;
|
|
1657
|
-
}
|
|
1658
|
-
function getProductEntitlementBalance(asset) {
|
|
1659
|
-
for (const record of asset.records) {
|
|
1660
|
-
const balance = getProductDiscountEntitlementBalance(record);
|
|
1661
|
-
if (balance) return balance;
|
|
1662
|
-
}
|
|
1663
|
-
return null;
|
|
1664
|
-
}
|
|
1665
|
-
function getProductDiscountEntitlementBalance(record) {
|
|
1666
|
-
return getEntitlementBalance(record.metadata?.balance, record.balance, record.discount?.metadata?.balance, record.discount?.balance);
|
|
1667
|
-
}
|
|
1668
|
-
function getEntitlementBalance(...values) {
|
|
1669
|
-
for (const value of values) {
|
|
1670
|
-
if (!isNumericAmount(value)) continue;
|
|
1671
|
-
const balance = new _decimal.default(value);
|
|
1672
|
-
if (balance.isFinite() && balance.isInteger() && balance.gte(0)) {
|
|
1673
|
-
return balance;
|
|
1674
|
-
}
|
|
1675
|
-
}
|
|
1676
|
-
return null;
|
|
1677
|
-
}
|
|
1678
|
-
function formatEntitlementUses(value, locale) {
|
|
1679
|
-
const count = value.toFixed(0);
|
|
1680
|
-
switch (normalizeLocaleKey(locale)) {
|
|
1681
|
-
case 'zh_CN':
|
|
1682
|
-
case 'zh_HK':
|
|
1683
|
-
return `${count}次`;
|
|
1684
|
-
case 'ja':
|
|
1685
|
-
return `${count}回`;
|
|
1686
|
-
case 'pt':
|
|
1687
|
-
return `${count} ${value.eq(1) ? 'utilização' : 'utilizações'}`;
|
|
1688
|
-
default:
|
|
1689
|
-
return `${count} ${value.eq(1) ? 'use' : 'uses'}`;
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
function getProductWalletDiscountPercent(asset) {
|
|
1693
|
-
if (asset.type !== 'discount_card' && asset.type !== 'product_discount_card') return null;
|
|
1694
|
-
for (const record of asset.records) {
|
|
1695
|
-
const discountType = stringify(firstPresentValue(record.discount_card_type, record.metadata?.discount_card_type, record.discount?.discount_card_type, record.discount?.metadata?.discount_card_type)).trim().toLowerCase();
|
|
1696
|
-
if (discountType && discountType !== 'percent' && discountType !== 'percentage') continue;
|
|
1697
|
-
const candidates = [record.percent, record.discount?.percent];
|
|
1698
|
-
if (discountType === 'percent' || discountType === 'percentage') {
|
|
1699
|
-
candidates.push(record.par_value, record.discount?.par_value);
|
|
1700
|
-
}
|
|
1701
|
-
for (const value of candidates) {
|
|
1702
|
-
if (!isNumericAmount(value)) continue;
|
|
1703
|
-
const percent = new _decimal.default(value);
|
|
1704
|
-
if (percent.isFinite() && percent.gt(0) && percent.lte(100)) {
|
|
1705
|
-
return percent;
|
|
1706
|
-
}
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
1709
|
-
return null;
|
|
1710
|
-
}
|
|
1711
|
-
function getProductWalletMoneyBalance(asset) {
|
|
1712
|
-
for (const record of asset.records) {
|
|
1713
|
-
for (const value of [record.metadata?.balance, record.balance, record.discount?.metadata?.balance, record.discount?.balance]) {
|
|
1714
|
-
if (!isNumericAmount(value)) continue;
|
|
1715
|
-
return _decimal.default.max(new _decimal.default(value), 0);
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
for (const record of asset.records) {
|
|
1719
|
-
const parValue = firstPresentValue(record.par_value, record.discount?.par_value);
|
|
1720
|
-
const usedParValue = firstPresentValue(record.used_par_value, record.discount?.used_par_value);
|
|
1721
|
-
if (!isNumericAmount(parValue) || !isNumericAmount(usedParValue)) continue;
|
|
1722
|
-
return _decimal.default.max(new _decimal.default(parValue).minus(new _decimal.default(usedParValue)), 0);
|
|
1723
|
-
}
|
|
1724
|
-
return null;
|
|
1725
|
-
}
|
|
1726
|
-
function buildPointCardSnapshotAssets(params) {
|
|
1727
|
-
const snapshots = params.order.order_meta_list?.point_cards_snapshot;
|
|
1728
|
-
if (!Array.isArray(snapshots)) return [];
|
|
1729
|
-
const snapshotTime = firstPresentValue(params.order.updated_at, params.order.created_at);
|
|
1730
|
-
return snapshots.flatMap(snapshot => {
|
|
1731
|
-
if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) return [];
|
|
1732
|
-
const pointCard = snapshot;
|
|
1733
|
-
if (stringify(pointCard.tag).trim().toLowerCase() !== 'point_card') return [];
|
|
1734
|
-
const assetId = normalizeWalletPassId(pointCard.id);
|
|
1735
|
-
if (!assetId || !claimWalletAssetIdentity(params.displayedWalletAssetIdentities, {
|
|
1736
|
-
id: assetId,
|
|
1737
|
-
codes: []
|
|
1738
|
-
})) return [];
|
|
1739
|
-
const rows = [{
|
|
1740
|
-
item: appendMaskedCardCode(stringify(pointCard.product_name).trim() || label(params.locale, 'pointCard'), pointCard.code),
|
|
1741
|
-
value: ''
|
|
1742
|
-
}];
|
|
1743
|
-
const remainingPoints = getPointCardSnapshotRemainingPoints(pointCard);
|
|
1744
|
-
if (remainingPoints) {
|
|
1745
|
-
rows.push({
|
|
1746
|
-
item: label(params.locale, 'remainingPoints'),
|
|
1747
|
-
value: trimDecimal(remainingPoints)
|
|
1748
|
-
});
|
|
1749
|
-
}
|
|
1750
|
-
if (isPresentId(snapshotTime)) {
|
|
1751
|
-
rows.push({
|
|
1752
|
-
item: formatDateTime(snapshotTime, params.locale),
|
|
1753
|
-
value: ''
|
|
1754
|
-
});
|
|
1755
|
-
}
|
|
1756
|
-
return [rows];
|
|
1757
|
-
});
|
|
1758
|
-
}
|
|
1759
|
-
function collectVirtualCurrencySnapshots(order) {
|
|
1760
|
-
const items = [];
|
|
1761
|
-
const byId = new Map();
|
|
1762
|
-
const snapshots = order.order_meta_list?.virtual_currency_snapshot;
|
|
1763
|
-
if (!Array.isArray(snapshots)) return {
|
|
1764
|
-
items,
|
|
1765
|
-
byId
|
|
1766
|
-
};
|
|
1767
|
-
snapshots.forEach(snapshot => {
|
|
1768
|
-
if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) return;
|
|
1769
|
-
const virtualCurrency = snapshot;
|
|
1770
|
-
if (stringify(virtualCurrency.tag).trim().toLowerCase() !== 'virtual_currency') return;
|
|
1771
|
-
const id = normalizeWalletPassId(virtualCurrency.id);
|
|
1772
|
-
if (!id || byId.has(id)) return;
|
|
1773
|
-
byId.set(id, virtualCurrency);
|
|
1774
|
-
items.push(virtualCurrency);
|
|
1775
15
|
});
|
|
1776
|
-
|
|
1777
|
-
items,
|
|
1778
|
-
byId
|
|
1779
|
-
};
|
|
1780
|
-
}
|
|
1781
|
-
function findVirtualCurrencySnapshotForPayment(payment, snapshotsById) {
|
|
1782
|
-
const fundRecord = getFundRecord(payment);
|
|
1783
|
-
for (const value of [fundRecord?.voucher_id, payment.voucher_id]) {
|
|
1784
|
-
const id = normalizeWalletPassId(value);
|
|
1785
|
-
if (!id) continue;
|
|
1786
|
-
const snapshot = snapshotsById.get(id);
|
|
1787
|
-
if (snapshot) return snapshot;
|
|
1788
|
-
}
|
|
1789
|
-
return undefined;
|
|
1790
|
-
}
|
|
1791
|
-
function buildVirtualCurrencySnapshotAssets(params) {
|
|
1792
|
-
const snapshotTime = firstPresentValue(params.order.updated_at, params.order.created_at);
|
|
1793
|
-
return params.snapshots.flatMap(virtualCurrency => {
|
|
1794
|
-
const assetId = normalizeWalletPassId(virtualCurrency.id);
|
|
1795
|
-
if (!assetId || !claimWalletAssetIdentity(params.displayedWalletAssetIdentities, {
|
|
1796
|
-
id: assetId,
|
|
1797
|
-
codes: []
|
|
1798
|
-
})) return [];
|
|
1799
|
-
const rows = [{
|
|
1800
|
-
item: stringify(virtualCurrency.product_name).trim(),
|
|
1801
|
-
value: ''
|
|
1802
|
-
}];
|
|
1803
|
-
const balance = getVirtualCurrencySnapshotBalance(virtualCurrency);
|
|
1804
|
-
if (balance) {
|
|
1805
|
-
rows.push({
|
|
1806
|
-
item: label(params.locale, 'balance'),
|
|
1807
|
-
value: formatVirtualCurrencyBalance(balance, getVirtualCurrencySnapshotUnit(virtualCurrency, params.locale))
|
|
1808
|
-
});
|
|
1809
|
-
}
|
|
1810
|
-
if (isPresentId(snapshotTime)) {
|
|
1811
|
-
rows.push({
|
|
1812
|
-
item: formatDateTime(snapshotTime, params.locale),
|
|
1813
|
-
value: ''
|
|
1814
|
-
});
|
|
1815
|
-
}
|
|
1816
|
-
return [rows];
|
|
1817
|
-
});
|
|
1818
|
-
}
|
|
1819
|
-
function getVirtualCurrencySnapshotBalance(snapshot) {
|
|
1820
|
-
if (!isNumericAmount(snapshot.par_value) || !isNumericAmount(snapshot.used_par_value)) {
|
|
1821
|
-
return null;
|
|
1822
|
-
}
|
|
1823
|
-
return _decimal.default.max(new _decimal.default(snapshot.par_value).minus(new _decimal.default(snapshot.used_par_value)), 0);
|
|
1824
|
-
}
|
|
1825
|
-
function getVirtualCurrencySnapshotUnit(snapshot, locale) {
|
|
1826
|
-
return getNonEmptyLocalizedValue(snapshot.name, locale);
|
|
1827
|
-
}
|
|
1828
|
-
function getNonEmptyLocalizedValue(value, locale) {
|
|
1829
|
-
if (value === undefined || value === null) return '';
|
|
1830
|
-
if (typeof value !== 'object' || Array.isArray(value)) return stringify(value).trim();
|
|
1831
|
-
const dashedLocale = locale.replace('_', '-');
|
|
1832
|
-
const underscoredLocale = locale.replace('-', '_');
|
|
1833
|
-
for (const candidate of [value[locale], value[dashedLocale], value[underscoredLocale], value.original, value.auto, value.default, value.en]) {
|
|
1834
|
-
const text = stringify(candidate).trim();
|
|
1835
|
-
if (text) return text;
|
|
1836
|
-
}
|
|
1837
|
-
return '';
|
|
1838
|
-
}
|
|
1839
|
-
function formatVirtualCurrencyBalance(value, unit) {
|
|
1840
|
-
const amount = trimDecimal(value);
|
|
1841
|
-
const normalizedUnit = unit.trim();
|
|
1842
|
-
return normalizedUnit ? `${amount} ${normalizedUnit}` : amount;
|
|
1843
|
-
}
|
|
1844
|
-
function normalizeWalletPassId(value) {
|
|
1845
|
-
if (typeof value === 'number') return Number.isFinite(value) ? String(value) : null;
|
|
1846
|
-
if (typeof value !== 'string') return null;
|
|
1847
|
-
return value.trim() || null;
|
|
1848
|
-
}
|
|
1849
|
-
function firstNormalizedWalletAssetId(...values) {
|
|
1850
|
-
for (const value of values) {
|
|
1851
|
-
const id = normalizeWalletPassId(value);
|
|
1852
|
-
if (id) return id;
|
|
1853
|
-
}
|
|
1854
|
-
return null;
|
|
1855
|
-
}
|
|
1856
|
-
function normalizeWalletAssetCodes(...values) {
|
|
1857
|
-
return Array.from(new Set(values.map(value => normalizeWalletPassId(value)).filter(value => !!value)));
|
|
1858
|
-
}
|
|
1859
|
-
function registerWalletAssetIdentity(identities, candidate) {
|
|
1860
|
-
if (!candidate.id && !candidate.codes.length) return;
|
|
1861
|
-
const existing = identities.find(identity => isSameWalletAssetIdentity(identity, candidate));
|
|
1862
|
-
if (existing) {
|
|
1863
|
-
mergeWalletAssetIdentity(existing, candidate);
|
|
1864
|
-
return;
|
|
1865
|
-
}
|
|
1866
|
-
identities.push({
|
|
1867
|
-
id: candidate.id,
|
|
1868
|
-
codes: [...candidate.codes]
|
|
1869
|
-
});
|
|
1870
|
-
}
|
|
1871
|
-
function claimWalletAssetIdentity(identities, candidate) {
|
|
1872
|
-
if (!candidate.id && !candidate.codes.length) return true;
|
|
1873
|
-
const existing = identities.find(identity => isSameWalletAssetIdentity(identity, candidate));
|
|
1874
|
-
if (existing) {
|
|
1875
|
-
mergeWalletAssetIdentity(existing, candidate);
|
|
1876
|
-
return false;
|
|
1877
|
-
}
|
|
1878
|
-
identities.push({
|
|
1879
|
-
id: candidate.id,
|
|
1880
|
-
codes: [...candidate.codes]
|
|
1881
|
-
});
|
|
1882
|
-
return true;
|
|
1883
|
-
}
|
|
1884
|
-
function isSameWalletAssetIdentity(left, right) {
|
|
1885
|
-
if (left.id && right.id) return left.id === right.id;
|
|
1886
|
-
return left.codes.some(code => right.codes.includes(code));
|
|
1887
|
-
}
|
|
1888
|
-
function mergeWalletAssetIdentity(target, source) {
|
|
1889
|
-
if (!target.id && source.id) target.id = source.id;
|
|
1890
|
-
target.codes = Array.from(new Set([...target.codes, ...source.codes]));
|
|
1891
|
-
}
|
|
1892
|
-
function getPointCardSnapshotRemainingPoints(snapshot) {
|
|
1893
|
-
if (!isNumericAmount(snapshot.par_value) || !isNumericAmount(snapshot.used_par_value)) {
|
|
1894
|
-
return null;
|
|
1895
|
-
}
|
|
1896
|
-
return _decimal.default.max(new _decimal.default(snapshot.par_value).minus(new _decimal.default(snapshot.used_par_value)), 0);
|
|
1897
|
-
}
|
|
1898
|
-
function buildVoucherAssets(params) {
|
|
1899
|
-
const fallbackAssetTime = firstPresentValue(params.order.updated_at, params.order.created_at);
|
|
1900
|
-
return normalizeArray(params.order.vouchers).flatMap(voucher => {
|
|
1901
|
-
if (!voucher || typeof voucher !== 'object' || Array.isArray(voucher)) return [];
|
|
1902
|
-
if (!claimWalletAssetIdentity(params.displayedWalletAssetIdentities, getVoucherAssetIdentity(voucher))) return [];
|
|
1903
|
-
const name = getVoucherAssetName(voucher, params.locale);
|
|
1904
|
-
const assetTime = firstPresentValue(voucher.updated_at, voucher.created_at, voucher.collection_time, fallbackAssetTime);
|
|
1905
|
-
const timeRow = isPresentId(assetTime) ? {
|
|
1906
|
-
item: formatDateTime(assetTime, params.locale),
|
|
1907
|
-
value: ''
|
|
1908
|
-
} : null;
|
|
1909
|
-
if (isEntitlementVoucher(voucher)) {
|
|
1910
|
-
const remaining = getEntitlementBalance(voucher.metadata?.balance, voucher.balance);
|
|
1911
|
-
return [[{
|
|
1912
|
-
item: name,
|
|
1913
|
-
value: ''
|
|
1914
|
-
}, ...(remaining ? [{
|
|
1915
|
-
item: label(params.locale, 'entitlementRemaining'),
|
|
1916
|
-
value: formatEntitlementUses(remaining, params.locale)
|
|
1917
|
-
}] : []), ...(timeRow ? [timeRow] : [])]];
|
|
1918
|
-
}
|
|
1919
|
-
if (isTicketVoucher(voucher)) {
|
|
1920
|
-
const validity = getVoucherValidity(voucher, params.locale);
|
|
1921
|
-
return [[{
|
|
1922
|
-
item: name,
|
|
1923
|
-
value: ''
|
|
1924
|
-
}, ...(validity ? [{
|
|
1925
|
-
item: label(params.locale, 'validity'),
|
|
1926
|
-
value: validity
|
|
1927
|
-
}] : [])]];
|
|
1928
|
-
}
|
|
1929
|
-
const percent = getVoucherDiscountPercent(voucher);
|
|
1930
|
-
if (percent) {
|
|
1931
|
-
return [[{
|
|
1932
|
-
item: appendPercentToVoucherName(name, percent),
|
|
1933
|
-
value: formatVoucherDiscount(percent, params.locale)
|
|
1934
|
-
}, ...(timeRow ? [timeRow] : [])]];
|
|
1935
|
-
}
|
|
1936
|
-
const balance = getVoucherBalance(voucher);
|
|
1937
|
-
return [[{
|
|
1938
|
-
item: name,
|
|
1939
|
-
value: ''
|
|
1940
|
-
}, {
|
|
1941
|
-
item: label(params.locale, 'balance'),
|
|
1942
|
-
value: formatMoney(balance, params.currencySymbol)
|
|
1943
|
-
}, ...(timeRow ? [timeRow] : [])]];
|
|
1944
|
-
});
|
|
1945
|
-
}
|
|
1946
|
-
const TICKET_VOUCHER_TAGS = new Set(['ticket', 'product_package_ticket', 'appointment_ticket', 'session_ticket']);
|
|
1947
|
-
function isEntitlementVoucher(voucher) {
|
|
1948
|
-
return [voucher.tag, voucher.type].some(value => stringify(value).trim().toLowerCase() === 'entitlement_pass');
|
|
1949
|
-
}
|
|
1950
|
-
function isTicketVoucher(voucher) {
|
|
1951
|
-
return TICKET_VOUCHER_TAGS.has(stringify(voucher.tag).trim().toLowerCase());
|
|
1952
|
-
}
|
|
1953
|
-
function getVoucherValidity(voucher, locale) {
|
|
1954
|
-
const availableTime = firstPresentValue(voucher.available_time, voucher.avaliable_time, voucher.metadata?.available_time, voucher.metadata?.avaliable_time);
|
|
1955
|
-
const availableTimeText = formatVoucherAvailableTime(availableTime, locale);
|
|
1956
|
-
if (availableTimeText) return availableTimeText;
|
|
1957
|
-
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);
|
|
1958
|
-
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);
|
|
1959
|
-
return formatVoucherValidityRange(startDate, endDate, locale, {
|
|
1960
|
-
startTime: voucher.start_time ?? voucher.metadata?.start_time,
|
|
1961
|
-
endTime: voucher.end_time ?? voucher.metadata?.end_time
|
|
1962
|
-
});
|
|
1963
|
-
}
|
|
1964
|
-
function formatVoucherAvailableTime(value, locale) {
|
|
1965
|
-
if (!isPresentId(value)) return '';
|
|
1966
|
-
if (Array.isArray(value)) {
|
|
1967
|
-
return value.map(item => formatVoucherAvailableTime(item, locale)).filter(Boolean).join(', ');
|
|
1968
|
-
}
|
|
1969
|
-
if (typeof value !== 'object') return stringify(value).trim();
|
|
1970
|
-
const start = firstPresentValue(value.start, value.start_at, value.start_date_time, value.start_date);
|
|
1971
|
-
const end = firstPresentValue(value.end, value.end_at, value.end_date_time, value.expire_time, value.expire_date, value.end_date);
|
|
1972
|
-
if (isPresentId(start) || isPresentId(end)) {
|
|
1973
|
-
return formatVoucherValidityRange(start, end, locale, {
|
|
1974
|
-
startTime: value.start_time,
|
|
1975
|
-
endTime: value.end_time
|
|
1976
|
-
});
|
|
1977
|
-
}
|
|
1978
|
-
return getLocalizedValue(value, locale).trim();
|
|
1979
|
-
}
|
|
1980
|
-
function formatVoucherValidityRange(startValue, endValue, locale, times = {}) {
|
|
1981
|
-
const start = combineVoucherDateAndTime(startValue, times.startTime);
|
|
1982
|
-
const end = combineVoucherDateAndTime(endValue, times.endTime);
|
|
1983
|
-
const startPart = formatVoucherValidityDate(start, locale);
|
|
1984
|
-
const endPart = formatVoucherValidityDate(end, locale);
|
|
1985
|
-
if (startPart.text && endPart.text) {
|
|
1986
|
-
const parsedStart = (0, _dayjs.default)(start);
|
|
1987
|
-
const parsedEnd = (0, _dayjs.default)(end);
|
|
1988
|
-
const sameDay = parsedStart.isValid() && parsedEnd.isValid() && parsedStart.isSame(parsedEnd, 'day');
|
|
1989
|
-
if (sameDay && startPart.time && endPart.time) {
|
|
1990
|
-
return `${startPart.time} - ${endPart.time} ${startPart.date}`;
|
|
1991
|
-
}
|
|
1992
|
-
return `${startPart.text} - ${endPart.text}`;
|
|
1993
|
-
}
|
|
1994
|
-
return startPart.text || endPart.text;
|
|
1995
|
-
}
|
|
1996
|
-
function combineVoucherDateAndTime(dateValue, timeValue) {
|
|
1997
|
-
if (!isPresentId(dateValue) || !isPresentId(timeValue)) return dateValue;
|
|
1998
|
-
const dateText = stringify(dateValue).trim();
|
|
1999
|
-
if (/\d{1,2}:\d{2}/.test(dateText)) return dateValue;
|
|
2000
|
-
return `${dateText} ${stringify(timeValue).trim()}`;
|
|
2001
|
-
}
|
|
2002
|
-
function formatVoucherValidityDate(value, locale) {
|
|
2003
|
-
if (!isPresentId(value)) return {
|
|
2004
|
-
date: '',
|
|
2005
|
-
time: '',
|
|
2006
|
-
text: ''
|
|
2007
|
-
};
|
|
2008
|
-
const source = stringify(value).trim();
|
|
2009
|
-
const parsed = (0, _dayjs.default)(source);
|
|
2010
|
-
if (!parsed.isValid()) return {
|
|
2011
|
-
date: '',
|
|
2012
|
-
time: '',
|
|
2013
|
-
text: source
|
|
2014
|
-
};
|
|
2015
|
-
const hasTime = /\d{1,2}:\d{2}/.test(source);
|
|
2016
|
-
const date = parsed.format(locale === 'en' ? 'DD/MM/YYYY' : 'YYYY/MM/DD');
|
|
2017
|
-
const time = hasTime ? parsed.format(/\d{1,2}:\d{2}:\d{2}/.test(source) ? 'HH:mm:ss' : 'HH:mm') : '';
|
|
2018
|
-
return {
|
|
2019
|
-
date,
|
|
2020
|
-
time,
|
|
2021
|
-
text: time ? `${time} ${date}` : date
|
|
2022
|
-
};
|
|
2023
|
-
}
|
|
2024
|
-
function getVoucherAssetIdentity(voucher) {
|
|
2025
|
-
return {
|
|
2026
|
-
id: firstNormalizedWalletAssetId(voucher.voucher_id, voucher.id),
|
|
2027
|
-
codes: normalizeWalletAssetCodes(voucher.code, voucher.encoded)
|
|
2028
|
-
};
|
|
2029
|
-
}
|
|
2030
|
-
function getVoucherAssetName(voucher, locale) {
|
|
2031
|
-
return getLocalizedValue(voucher.product_name || voucher.name || voucher.title, locale) || stringify(voucher.encoded || voucher.code || label(locale, 'giftCard'));
|
|
2032
|
-
}
|
|
2033
|
-
function getVoucherDiscountPercent(voucher) {
|
|
2034
|
-
const discountType = stringify(voucher.metadata?.discount_card_type || voucher.discount_card_type).toLowerCase();
|
|
2035
|
-
if (discountType !== 'percent' && discountType !== 'percentage') return null;
|
|
2036
|
-
const percent = new _decimal.default(toMoneyNumber(voucher.par_value));
|
|
2037
|
-
if (!percent.isFinite() || percent.lte(0) || percent.gt(100)) return null;
|
|
2038
|
-
return percent;
|
|
2039
|
-
}
|
|
2040
|
-
function appendPercentToVoucherName(name, percent) {
|
|
2041
|
-
if (/\d+(?:\.\d+)?\s*(?:%|折)|\boff\b/i.test(name)) return name;
|
|
2042
|
-
const separator = /^[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]/u.test(name) ? '' : ' ';
|
|
2043
|
-
return `${trimDecimal(percent)}%${separator}${name}`;
|
|
2044
|
-
}
|
|
2045
|
-
function formatVoucherDiscount(percent, locale) {
|
|
2046
|
-
const normalizedLocale = normalizeLocaleKey(locale);
|
|
2047
|
-
if (normalizedLocale === 'zh_CN' || normalizedLocale === 'zh_HK') {
|
|
2048
|
-
return `${trimDecimal(percent)}% OFF`;
|
|
2049
|
-
}
|
|
2050
|
-
if (normalizedLocale === 'pt') return `Desconto ${trimDecimal(percent)}%`;
|
|
2051
|
-
return normalizedLocale === 'en' ? `Discount ${trimDecimal(percent)}% OFF` : `${trimDecimal(percent)}% OFF`;
|
|
2052
|
-
}
|
|
2053
|
-
function getVoucherBalance(voucher) {
|
|
2054
|
-
for (const value of [voucher.metadata?.balance, voucher.balance]) {
|
|
2055
|
-
if (!isNumericAmount(value)) continue;
|
|
2056
|
-
return _decimal.default.max(new _decimal.default(value), 0);
|
|
2057
|
-
}
|
|
2058
|
-
const parValue = new _decimal.default(toMoneyNumber(voucher.par_value));
|
|
2059
|
-
const usedParValue = new _decimal.default(toMoneyNumber(voucher.used_par_value));
|
|
2060
|
-
return _decimal.default.max(parValue.minus(usedParValue), 0);
|
|
2061
|
-
}
|
|
2062
|
-
function buildPaymentItems(params) {
|
|
2063
|
-
const {
|
|
2064
|
-
payment,
|
|
2065
|
-
locale,
|
|
2066
|
-
currencySymbol,
|
|
2067
|
-
includeFundRecordBalance,
|
|
2068
|
-
includeFundRecordUsage,
|
|
2069
|
-
includePaymentAmount,
|
|
2070
|
-
virtualCurrencyUnit
|
|
2071
|
-
} = params;
|
|
2072
|
-
const serviceFee = getPaymentServiceFee(payment);
|
|
2073
|
-
const items = [{
|
|
2074
|
-
item: paymentMethodName(payment, locale),
|
|
2075
|
-
value: includePaymentAmount ? formatMoney(resolveReceiptPaymentAmount(payment, serviceFee), currencySymbol) : ''
|
|
2076
|
-
}];
|
|
2077
|
-
items.push(...buildCashChangePaymentItems({
|
|
2078
|
-
payment,
|
|
2079
|
-
locale,
|
|
2080
|
-
currencySymbol
|
|
2081
|
-
}));
|
|
2082
|
-
if (serviceFee.gt(0)) {
|
|
2083
|
-
items.push({
|
|
2084
|
-
item: label(locale, 'surchargeIncluded'),
|
|
2085
|
-
value: formatMoney(serviceFee, currencySymbol)
|
|
2086
|
-
});
|
|
2087
|
-
}
|
|
2088
|
-
const fundRecord = getFundRecord(payment);
|
|
2089
|
-
if (fundRecord) {
|
|
2090
|
-
items.push(...buildFundRecordPaymentItems({
|
|
2091
|
-
payment,
|
|
2092
|
-
fundRecord,
|
|
2093
|
-
locale,
|
|
2094
|
-
currencySymbol,
|
|
2095
|
-
includeBalance: includeFundRecordBalance,
|
|
2096
|
-
includeUsage: includeFundRecordUsage,
|
|
2097
|
-
virtualCurrencyUnit
|
|
2098
|
-
}));
|
|
2099
|
-
} else {
|
|
2100
|
-
const remainingAmount = payment.remaining_amount ?? payment.balance ?? payment.metadata?.remaining_amount;
|
|
2101
|
-
if (remainingAmount !== undefined && remainingAmount !== null) {
|
|
2102
|
-
items.push({
|
|
2103
|
-
item: label(locale, 'remainingAmount'),
|
|
2104
|
-
value: formatMoney(remainingAmount, currencySymbol)
|
|
2105
|
-
});
|
|
2106
|
-
}
|
|
2107
|
-
}
|
|
2108
|
-
items.push({
|
|
2109
|
-
item: formatDateTime(payment.paid_at || payment.created_at || payment.updated_at || new Date(), locale),
|
|
2110
|
-
value: ''
|
|
2111
|
-
});
|
|
2112
|
-
return items;
|
|
2113
|
-
}
|
|
2114
|
-
function getPaymentServiceFee(payment) {
|
|
2115
|
-
return new _decimal.default(toMoneyNumber(payment.service_fee));
|
|
2116
|
-
}
|
|
2117
|
-
function resolveReceiptPaymentAmount(payment, serviceFee) {
|
|
2118
|
-
const amount = new _decimal.default(toMoneyNumber(payment.amount ?? payment.origin_amount));
|
|
2119
|
-
const hasAmount = payment.amount !== undefined && payment.amount !== null && payment.amount !== '';
|
|
2120
|
-
const hasOriginAmount = payment.origin_amount !== undefined && payment.origin_amount !== null && payment.origin_amount !== '';
|
|
2121
|
-
if (!hasAmount || !hasOriginAmount || serviceFee.lte(0)) return amount;
|
|
2122
|
-
const originAmount = new _decimal.default(toMoneyNumber(payment.origin_amount));
|
|
2123
|
-
return amount.eq(originAmount) ? amount.plus(serviceFee) : amount;
|
|
2124
|
-
}
|
|
2125
|
-
function buildCashChangePaymentItems(params) {
|
|
2126
|
-
const {
|
|
2127
|
-
payment,
|
|
2128
|
-
locale,
|
|
2129
|
-
currencySymbol
|
|
2130
|
-
} = params;
|
|
2131
|
-
if (!shouldDisplayCashChange(payment)) return [];
|
|
2132
|
-
return [{
|
|
2133
|
-
item: label(locale, 'cashReceived'),
|
|
2134
|
-
value: formatMoney(payment.metadata.actual_paid_amount, currencySymbol)
|
|
2135
|
-
}, {
|
|
2136
|
-
item: label(locale, 'changeDue'),
|
|
2137
|
-
value: formatMoney(payment.metadata.change_given_amount, currencySymbol)
|
|
2138
|
-
}];
|
|
2139
|
-
}
|
|
2140
|
-
function shouldDisplayCashChange(payment) {
|
|
2141
|
-
if (stringify(payment.status).toLowerCase() !== 'paid') return false;
|
|
2142
|
-
const paymentMethod = stringify(payment.code ?? payment.payment_method).toUpperCase();
|
|
2143
|
-
if (paymentMethod !== 'CASHMANUAL') return false;
|
|
2144
|
-
const actualPaidAmount = payment.metadata?.actual_paid_amount;
|
|
2145
|
-
const changeGivenAmount = payment.metadata?.change_given_amount;
|
|
2146
|
-
if (!isNumericAmount(actualPaidAmount) || !isNumericAmount(changeGivenAmount)) return false;
|
|
2147
|
-
return new _decimal.default(changeGivenAmount).toDecimalPlaces(2).gt(0);
|
|
2148
|
-
}
|
|
2149
|
-
function isNumericAmount(value) {
|
|
2150
|
-
if (typeof value === 'number') return Number.isFinite(value);
|
|
2151
|
-
if (typeof value !== 'string') return false;
|
|
2152
|
-
const normalized = value.trim();
|
|
2153
|
-
return normalized !== '' && /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i.test(normalized);
|
|
2154
|
-
}
|
|
2155
|
-
function buildFundRecordPaymentItems(params) {
|
|
2156
|
-
const {
|
|
2157
|
-
payment,
|
|
2158
|
-
fundRecord,
|
|
2159
|
-
locale,
|
|
2160
|
-
currencySymbol,
|
|
2161
|
-
includeBalance,
|
|
2162
|
-
includeUsage,
|
|
2163
|
-
virtualCurrencyUnit
|
|
2164
|
-
} = params;
|
|
2165
|
-
const items = [];
|
|
2166
|
-
const usingBeforeValue = fundRecord.using_before_value;
|
|
2167
|
-
const usingAfterValue = fundRecord.using_after_value;
|
|
2168
|
-
if (isPointCardPayment(payment) && usingBeforeValue !== undefined && usingAfterValue !== undefined) {
|
|
2169
|
-
const usedPoints = new _decimal.default(toMoneyNumber(usingBeforeValue)).minus(toMoneyNumber(usingAfterValue));
|
|
2170
|
-
if (includeUsage) {
|
|
2171
|
-
items.push({
|
|
2172
|
-
item: label(locale, 'redeemPoints'),
|
|
2173
|
-
value: trimDecimal(usedPoints)
|
|
2174
|
-
});
|
|
2175
|
-
}
|
|
2176
|
-
if (includeBalance) {
|
|
2177
|
-
items.push({
|
|
2178
|
-
item: label(locale, 'remainingPoints'),
|
|
2179
|
-
value: trimDecimal(new _decimal.default(toMoneyNumber(usingAfterValue)))
|
|
2180
|
-
});
|
|
2181
|
-
}
|
|
2182
|
-
return items;
|
|
2183
|
-
}
|
|
2184
|
-
if (includeBalance && usingAfterValue !== undefined && usingAfterValue !== null) {
|
|
2185
|
-
if (virtualCurrencyUnit !== undefined) {
|
|
2186
|
-
if (!isNumericAmount(usingAfterValue)) return items;
|
|
2187
|
-
items.push({
|
|
2188
|
-
item: label(locale, 'balance'),
|
|
2189
|
-
value: formatVirtualCurrencyBalance(new _decimal.default(usingAfterValue), virtualCurrencyUnit)
|
|
2190
|
-
});
|
|
2191
|
-
return items;
|
|
2192
|
-
}
|
|
2193
|
-
items.push({
|
|
2194
|
-
item: label(locale, 'balance'),
|
|
2195
|
-
value: formatMoney(usingAfterValue, currencySymbol)
|
|
2196
|
-
});
|
|
2197
|
-
}
|
|
2198
|
-
return items;
|
|
2199
|
-
}
|
|
2200
|
-
function getFundRecord(payment) {
|
|
2201
|
-
const fundRecord = payment.fund_record;
|
|
2202
|
-
if (!fundRecord || typeof fundRecord !== 'object' || Array.isArray(fundRecord)) return null;
|
|
2203
|
-
if (!fundRecord.voucher_id && !fundRecord.code) return null;
|
|
2204
|
-
return fundRecord;
|
|
2205
|
-
}
|
|
2206
|
-
function isPointCardPayment(payment) {
|
|
2207
|
-
const code = stringify(payment.code || payment.payment_method).toUpperCase();
|
|
2208
|
-
const type = stringify(payment.type || payment.fund_record?.tag).toLowerCase();
|
|
2209
|
-
return code === 'POINTCARD' || type === 'point_card';
|
|
2210
|
-
}
|
|
2211
|
-
function maskMachineCode(code) {
|
|
2212
|
-
const text = stringify(code).trim();
|
|
2213
|
-
if (!text) return '';
|
|
2214
|
-
if (text.length <= 6) return '*'.repeat(text.length);
|
|
2215
|
-
return `${text.slice(0, 2)}****${text.slice(-4)}`;
|
|
2216
|
-
}
|
|
2217
|
-
function appendMaskedCardCode(name, code) {
|
|
2218
|
-
const maskedCode = maskMachineCode(code);
|
|
2219
|
-
return maskedCode ? `${name}(${maskedCode})` : name;
|
|
2220
|
-
}
|
|
2221
|
-
function trimDecimal(value) {
|
|
2222
|
-
return value.toDecimalPlaces(2).toFixed(2).replace(/\.?0+$/, '');
|
|
2223
|
-
}
|
|
2224
|
-
function getFundRecordPaymentName(payment, locale) {
|
|
2225
|
-
const fundRecord = getFundRecord(payment);
|
|
2226
|
-
if (!fundRecord) return '';
|
|
2227
|
-
const name = getLocalizedValue(fundRecord.wallet_pass_name, locale);
|
|
2228
|
-
if (!name && !maskMachineCode(fundRecord.code)) return '';
|
|
2229
|
-
const fallbackName = getLocalizedValue(payment.name || payment.title || payment.payment_method_name || payment.method_name || payment.code, locale);
|
|
2230
|
-
return appendMaskedCardCode(name || fallbackName, fundRecord.code);
|
|
2231
|
-
}
|
|
2232
|
-
function appendMachineCodeToName(name, payment) {
|
|
2233
|
-
const fundRecord = getFundRecord(payment);
|
|
2234
|
-
return appendMaskedCardCode(name, fundRecord?.code);
|
|
2235
|
-
}
|
|
2236
|
-
function paymentMethodName(payment, locale) {
|
|
2237
|
-
const fundRecordName = getFundRecordPaymentName(payment, locale);
|
|
2238
|
-
if (fundRecordName) return fundRecordName;
|
|
2239
|
-
const raw = payment.name || payment.title || payment.payment_method_name || payment.method_name;
|
|
2240
|
-
if (raw) return appendMachineCodeToName(getLocalizedValue(raw, locale), payment);
|
|
2241
|
-
const code = stringify(payment.code || payment.type || payment.payment_method || payment.method).toLowerCase();
|
|
2242
|
-
if (code.includes('cash')) return label(locale, 'cash');
|
|
2243
|
-
if (code.includes('eftpos') || code.includes('card')) return label(locale, 'eftpos');
|
|
2244
|
-
if (code.includes('wallet')) return label(locale, 'wallet');
|
|
2245
|
-
if (code.includes('gift')) return label(locale, 'giftCard');
|
|
2246
|
-
return stringify(payment.code || payment.type || payment.payment_method || payment.method);
|
|
2247
|
-
}
|
|
2248
|
-
function getSummary(order) {
|
|
2249
|
-
return order.summary && typeof order.summary === 'object' ? order.summary : order;
|
|
2250
|
-
}
|
|
2251
|
-
function getCurrencySymbol(order, shopInfo) {
|
|
2252
|
-
return stringify(order.currency_symbol || shopInfo?.currency_symbol || order.shop_symbol || '$') || '$';
|
|
2253
|
-
}
|
|
2254
|
-
function resolveLocales(shopInfo, locales) {
|
|
2255
|
-
const preferred = [...(locales || []), shopInfo.primary_locale, ...DEFAULT_LOCALES].filter(Boolean);
|
|
2256
|
-
return Array.from(new Set(preferred.map(normalizeLocaleKey)));
|
|
2257
|
-
}
|
|
2258
|
-
function normalizeLocaleKey(locale) {
|
|
2259
|
-
return locale.replace('-', '_');
|
|
2260
|
-
}
|
|
2261
|
-
function label(locale, key) {
|
|
2262
|
-
return LABELS[locale]?.[key] || LABELS.en[key] || key;
|
|
2263
|
-
}
|
|
2264
|
-
function getLocalizedValue(value, locale) {
|
|
2265
|
-
if (value === undefined || value === null) return '';
|
|
2266
|
-
if (typeof value !== 'object') return stringify(value);
|
|
2267
|
-
const dashedLocale = locale.replace('_', '-');
|
|
2268
|
-
const underscoredLocale = locale.replace('-', '_');
|
|
2269
|
-
return stringify(value[locale] ?? value[dashedLocale] ?? value[underscoredLocale] ?? value.original ?? value.auto ?? value.default ?? value.en ?? '');
|
|
2270
|
-
}
|
|
2271
|
-
function resolveServiceTypeLabelKey(serviceType) {
|
|
2272
|
-
return SERVICE_TYPE_LABEL_KEYS[serviceType] || null;
|
|
2273
|
-
}
|
|
2274
|
-
function normalizeBooleanFlag(value) {
|
|
2275
|
-
if (value === true || value === 1 || stringify(value).trim() === '1') return 1;
|
|
2276
|
-
return 0;
|
|
2277
|
-
}
|
|
2278
|
-
function buildShopAddress(shopInfo) {
|
|
2279
|
-
return [shopInfo.address_street, shopInfo.address_detail, shopInfo.city, shopInfo.province, shopInfo.country, shopInfo.zip].filter(Boolean).join(', ');
|
|
2280
|
-
}
|
|
2281
|
-
function formatDateTime(value, locale) {
|
|
2282
|
-
const date = (0, _dayjs.default)(value);
|
|
2283
|
-
if (!date.isValid()) return stringify(value);
|
|
2284
|
-
if (locale === 'en') return date.format('HH:mm:ss DD/MM/YYYY');
|
|
2285
|
-
return date.format('YYYY/MM/DD HH:mm:ss');
|
|
2286
|
-
}
|
|
2287
|
-
function formatMoney(value, currencySymbol) {
|
|
2288
|
-
if (value instanceof _decimal.default) return `${currencySymbol}${value.toFixed(2)}`;
|
|
2289
|
-
if (typeof value === 'string' && value.trim().startsWith(currencySymbol)) return value;
|
|
2290
|
-
const amount = new _decimal.default(toMoneyNumber(value));
|
|
2291
|
-
return `${currencySymbol}${amount.toFixed(2)}`;
|
|
2292
|
-
}
|
|
2293
|
-
function formatCalculatedMoney(value, currencySymbol) {
|
|
2294
|
-
const amount = new _decimal.default(toMoneyNumber(value));
|
|
2295
|
-
const sign = amount.isNegative() ? '-' : '';
|
|
2296
|
-
return `${sign}${currencySymbol}${amount.abs().toFixed(2)}`;
|
|
2297
|
-
}
|
|
2298
|
-
function formatNegativeMoney(value, currencySymbol) {
|
|
2299
|
-
const amount = new _decimal.default(toMoneyNumber(value)).abs();
|
|
2300
|
-
return `-${currencySymbol}${amount.toFixed(2)}`;
|
|
2301
|
-
}
|
|
2302
|
-
function sumAppliedPayments(payments) {
|
|
2303
|
-
return normalizeArray(payments).filter(payment => isPrintablePayment(payment)).reduce((sum, payment) => {
|
|
2304
|
-
if (isPresentId(payment.origin_amount)) {
|
|
2305
|
-
return sum.plus(toMoneyNumber(payment.origin_amount));
|
|
2306
|
-
}
|
|
2307
|
-
const amount = new _decimal.default(toMoneyNumber(payment.amount));
|
|
2308
|
-
const serviceFee = _decimal.default.max(toMoneyNumber(payment.service_fee), 0);
|
|
2309
|
-
return sum.plus(_decimal.default.max(amount.minus(serviceFee), 0));
|
|
2310
|
-
}, new _decimal.default(0));
|
|
2311
|
-
}
|
|
2312
|
-
function sumCustomerPaidPayments(payments) {
|
|
2313
|
-
return normalizeArray(payments).filter(payment => isPrintablePayment(payment)).reduce((sum, payment) => {
|
|
2314
|
-
return sum.plus(toMoneyNumber(payment.amount ?? payment.origin_amount));
|
|
2315
|
-
}, new _decimal.default(0));
|
|
2316
|
-
}
|
|
2317
|
-
function isPrintablePayment(payment) {
|
|
2318
|
-
const status = stringify(payment.status).toLowerCase();
|
|
2319
|
-
if (!status) return true;
|
|
2320
|
-
return ['paid', 'success', 'succeeded', 'completed', 'partially_refunded', 'refunded'].includes(status);
|
|
2321
|
-
}
|
|
2322
|
-
function normalizeArray(value) {
|
|
2323
|
-
return Array.isArray(value) ? value : [];
|
|
2324
|
-
}
|
|
2325
|
-
function isNonZero(value) {
|
|
2326
|
-
return !new _decimal.default(toMoneyNumber(value)).isZero();
|
|
2327
|
-
}
|
|
2328
|
-
function toMoneyNumber(value) {
|
|
2329
|
-
if (value instanceof _decimal.default) return value.toString();
|
|
2330
|
-
if (typeof value === 'number') return Number.isFinite(value) ? value : 0;
|
|
2331
|
-
if (typeof value !== 'string') return 0;
|
|
2332
|
-
const normalized = value.replace(/[^\d.-]/g, '');
|
|
2333
|
-
return normalized || 0;
|
|
2334
|
-
}
|
|
2335
|
-
function toNumber(value, fallback) {
|
|
2336
|
-
const numberValue = Number(value);
|
|
2337
|
-
return Number.isFinite(numberValue) ? numberValue : fallback;
|
|
2338
|
-
}
|
|
2339
|
-
function stringify(value) {
|
|
2340
|
-
if (value === undefined || value === null) return '';
|
|
2341
|
-
return String(value);
|
|
2342
|
-
}
|
|
16
|
+
});
|