@pisell/pisellos 2.2.309 → 2.2.310
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
- package/dist/model/strategy/adapter/walletPass/evaluator.js +4 -2
- package/dist/modules/BookingContext/index.js +4 -4
- package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +58 -19
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +24 -12
- package/dist/modules/Customer/index.js +6 -3
- package/dist/modules/Customer/types.d.ts +2 -0
- package/dist/modules/Discount/availability.d.ts +7 -0
- package/dist/modules/Discount/availability.js +49 -0
- package/dist/modules/Discount/entitlementPass.d.ts +34 -0
- package/dist/modules/Discount/entitlementPass.js +147 -0
- package/dist/modules/Discount/index.d.ts +2 -2
- package/dist/modules/Discount/index.js +37 -151
- package/dist/modules/Discount/types.d.ts +36 -5
- package/dist/modules/OpenData/index.d.ts +1 -1
- package/dist/modules/OpenData/index.js +21 -12
- package/dist/modules/Order/index.d.ts +32 -4
- package/dist/modules/Order/index.js +733 -373
- package/dist/modules/Order/types.d.ts +17 -0
- package/dist/modules/Order/utils/orderCollectionIdentity.js +16 -12
- package/dist/modules/Order/utils.d.ts +14 -2
- package/dist/modules/Order/utils.js +275 -76
- package/dist/modules/Payment/index.d.ts +0 -5
- package/dist/modules/Payment/index.js +35 -85
- package/dist/modules/Payment/types.d.ts +21 -1
- package/dist/modules/Payment/types.js +2 -0
- package/dist/modules/Payment/walletpass.d.ts +1 -1
- package/dist/modules/Payment/walletpass.js +80 -47
- package/dist/modules/ProductList/index.js +18 -5
- package/dist/modules/Rules/index.d.ts +2 -2
- package/dist/modules/Rules/index.js +480 -186
- package/dist/modules/Rules/types.d.ts +4 -0
- package/dist/server/index.js +8 -5
- package/dist/server/modules/order/index.js +6 -0
- package/dist/server/modules/order/types.d.ts +12 -0
- package/dist/server/modules/payment/index.d.ts +25 -6
- package/dist/server/modules/payment/index.js +660 -133
- package/dist/server/utils/small-ticket.d.ts +8 -5
- package/dist/server/utils/small-ticket.js +1269 -199
- package/dist/solution/BaseSales/index.d.ts +11 -2
- package/dist/solution/BaseSales/index.js +1463 -1173
- package/dist/solution/BaseSales/types.d.ts +7 -3
- package/dist/solution/BaseSales/utils/cartPromotion.js +141 -126
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.d.ts +12 -3
- package/dist/solution/BookingTicket/index.js +663 -542
- package/dist/solution/BookingTicket/types.d.ts +5 -1
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +2 -0
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +338 -147
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +8 -4
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +6 -0
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +19 -6
- package/dist/solution/ShopDiscount/index.d.ts +2 -0
- package/dist/solution/ShopDiscount/index.js +99 -42
- package/dist/solution/ShopDiscount/utils.d.ts +4 -2
- package/dist/solution/ShopDiscount/utils.js +43 -23
- package/dist/solution/UnifiedBookingSales/index.d.ts +2 -2
- package/dist/solution/UnifiedBookingSales/index.js +5 -5
- package/dist/utils/os-private-payload.d.ts +5 -0
- package/dist/utils/os-private-payload.js +17 -0
- package/lib/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
- package/lib/model/strategy/adapter/walletPass/evaluator.js +2 -0
- package/lib/modules/BookingContext/index.js +3 -3
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +35 -6
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +13 -6
- package/lib/modules/Customer/index.js +5 -1
- package/lib/modules/Customer/types.d.ts +2 -0
- package/lib/modules/Discount/availability.d.ts +7 -0
- package/lib/modules/Discount/availability.js +43 -0
- package/lib/modules/Discount/entitlementPass.d.ts +34 -0
- package/lib/modules/Discount/entitlementPass.js +141 -0
- package/lib/modules/Discount/index.d.ts +2 -2
- package/lib/modules/Discount/index.js +23 -129
- package/lib/modules/Discount/types.d.ts +36 -5
- package/lib/modules/OpenData/index.d.ts +1 -1
- package/lib/modules/OpenData/index.js +11 -2
- package/lib/modules/Order/index.d.ts +32 -4
- package/lib/modules/Order/index.js +341 -71
- package/lib/modules/Order/types.d.ts +17 -0
- package/lib/modules/Order/utils/orderCollectionIdentity.js +14 -11
- package/lib/modules/Order/utils.d.ts +14 -2
- package/lib/modules/Order/utils.js +186 -3
- package/lib/modules/Payment/index.d.ts +0 -5
- package/lib/modules/Payment/index.js +3 -28
- package/lib/modules/Payment/types.d.ts +21 -1
- package/lib/modules/Payment/types.js +1 -0
- package/lib/modules/Payment/walletpass.d.ts +1 -1
- package/lib/modules/Payment/walletpass.js +29 -9
- package/lib/modules/ProductList/index.js +14 -1
- package/lib/modules/Rules/index.d.ts +2 -2
- package/lib/modules/Rules/index.js +357 -110
- package/lib/modules/Rules/types.d.ts +4 -0
- package/lib/server/index.js +8 -5
- package/lib/server/modules/order/index.js +6 -0
- package/lib/server/modules/order/types.d.ts +12 -0
- package/lib/server/modules/payment/index.d.ts +25 -6
- package/lib/server/modules/payment/index.js +350 -78
- package/lib/server/utils/small-ticket.d.ts +8 -5
- package/lib/server/utils/small-ticket.js +1009 -121
- package/lib/solution/BaseSales/index.d.ts +11 -2
- package/lib/solution/BaseSales/index.js +164 -30
- package/lib/solution/BaseSales/types.d.ts +7 -3
- package/lib/solution/BaseSales/utils/cartPromotion.js +12 -4
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.d.ts +12 -3
- package/lib/solution/BookingTicket/index.js +103 -41
- package/lib/solution/BookingTicket/types.d.ts +5 -1
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +2 -0
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +174 -42
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +8 -4
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +6 -0
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +11 -3
- package/lib/solution/ShopDiscount/index.d.ts +2 -0
- package/lib/solution/ShopDiscount/index.js +55 -9
- package/lib/solution/ShopDiscount/utils.d.ts +4 -2
- package/lib/solution/ShopDiscount/utils.js +42 -25
- package/lib/solution/UnifiedBookingSales/index.d.ts +2 -2
- package/lib/solution/UnifiedBookingSales/index.js +5 -5
- package/lib/utils/os-private-payload.d.ts +5 -0
- package/lib/utils/os-private-payload.js +22 -0
- package/package.json +1 -1
|
@@ -9,15 +9,31 @@ var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
|
9
9
|
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
10
10
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
11
|
const DEFAULT_LOCALES = ['en', 'zh_CN'];
|
|
12
|
+
const SERVICE_TYPE_LABEL_KEYS = {
|
|
13
|
+
takeaway: 'takeAway',
|
|
14
|
+
pick_up: 'pickUp',
|
|
15
|
+
table_service: 'tableService',
|
|
16
|
+
delivery: 'delivery',
|
|
17
|
+
room_service: 'roomService',
|
|
18
|
+
dine_in: 'dineIn',
|
|
19
|
+
delivery_by_uber: 'deliveryByUber',
|
|
20
|
+
delivery_by_panda: 'deliveryByPanda',
|
|
21
|
+
delivery_by_merchant: 'deliveryByMerchant'
|
|
22
|
+
};
|
|
12
23
|
const LABELS = {
|
|
13
24
|
en: {
|
|
14
25
|
contactName: 'Contact name',
|
|
15
26
|
contactMobile: 'Contact Mobile',
|
|
16
27
|
email: 'email',
|
|
17
|
-
dineIn: 'Dine
|
|
18
|
-
takeAway: '
|
|
28
|
+
dineIn: 'Dine in',
|
|
29
|
+
takeAway: 'Takeaway',
|
|
19
30
|
delivery: 'Delivery',
|
|
20
|
-
pickUp: '
|
|
31
|
+
pickUp: 'Pickup',
|
|
32
|
+
tableService: 'Table Service',
|
|
33
|
+
roomService: 'Room Service',
|
|
34
|
+
deliveryByUber: 'Uber Eats Delivery',
|
|
35
|
+
deliveryByPanda: 'HungryPanda Delivery',
|
|
36
|
+
deliveryByMerchant: 'Merchant Delivery',
|
|
21
37
|
shopService: 'Dine-in',
|
|
22
38
|
pickUpTime: 'Pick-up time',
|
|
23
39
|
asap: 'ASAP',
|
|
@@ -27,13 +43,21 @@ const LABELS = {
|
|
|
27
43
|
eftpos: 'Eftpos',
|
|
28
44
|
wallet: 'Wallet',
|
|
29
45
|
giftCard: 'Gift card',
|
|
46
|
+
pointCard: 'Point card',
|
|
30
47
|
balance: 'Balance',
|
|
48
|
+
validity: 'Validity',
|
|
31
49
|
remainingAmount: 'Remaining amount',
|
|
32
50
|
redeemPoints: 'Redeem points',
|
|
33
|
-
remainingPoints: '
|
|
51
|
+
remainingPoints: 'Points balance',
|
|
52
|
+
entitlementRemaining: 'Remaining',
|
|
34
53
|
table: 'Table',
|
|
54
|
+
bookingId: 'Booking ID',
|
|
55
|
+
bookingDateTime: 'Date & time',
|
|
56
|
+
bookingResource: 'Resource',
|
|
57
|
+
bookingHolder: 'Holder',
|
|
35
58
|
orderDiscount: 'Order discount',
|
|
36
59
|
manualDiscount: 'Manual discount',
|
|
60
|
+
manualMarkup: 'Manual markup',
|
|
37
61
|
payProcessingFee: 'Pay processing fee',
|
|
38
62
|
surchargeIncluded: 'Surcharge(Included)',
|
|
39
63
|
rounding: 'Rounding',
|
|
@@ -49,7 +73,12 @@ const LABELS = {
|
|
|
49
73
|
dineIn: '堂食',
|
|
50
74
|
takeAway: '外带',
|
|
51
75
|
delivery: '配送',
|
|
52
|
-
pickUp: '
|
|
76
|
+
pickUp: '自取',
|
|
77
|
+
tableService: '送餐到桌',
|
|
78
|
+
roomService: '客房服务',
|
|
79
|
+
deliveryByUber: 'Uber 配送',
|
|
80
|
+
deliveryByPanda: '熊猫外卖配送',
|
|
81
|
+
deliveryByMerchant: '商家配送',
|
|
53
82
|
shopService: '堂食',
|
|
54
83
|
pickUpTime: '取餐时间',
|
|
55
84
|
asap: '尽快',
|
|
@@ -59,13 +88,21 @@ const LABELS = {
|
|
|
59
88
|
eftpos: '刷卡支付',
|
|
60
89
|
wallet: '钱包支付',
|
|
61
90
|
giftCard: '礼品卡',
|
|
91
|
+
pointCard: '积分卡',
|
|
62
92
|
balance: '余额',
|
|
93
|
+
validity: '有效期',
|
|
63
94
|
remainingAmount: '剩余金额',
|
|
64
95
|
redeemPoints: '本次使用积分',
|
|
65
|
-
remainingPoints: '
|
|
96
|
+
remainingPoints: '积分余额',
|
|
97
|
+
entitlementRemaining: '剩余次数',
|
|
66
98
|
table: '桌台',
|
|
99
|
+
bookingId: '预约编号',
|
|
100
|
+
bookingDateTime: '日期和时间',
|
|
101
|
+
bookingResource: '资源',
|
|
102
|
+
bookingHolder: '预约人',
|
|
67
103
|
orderDiscount: '整单折扣',
|
|
68
104
|
manualDiscount: '手动折扣',
|
|
105
|
+
manualMarkup: '手动加价',
|
|
69
106
|
payProcessingFee: '支付手续费',
|
|
70
107
|
surchargeIncluded: '手续费(已含)',
|
|
71
108
|
rounding: '抹零',
|
|
@@ -82,6 +119,11 @@ const LABELS = {
|
|
|
82
119
|
takeAway: '外帶',
|
|
83
120
|
delivery: '配送',
|
|
84
121
|
pickUp: '自取',
|
|
122
|
+
tableService: '送餐到桌',
|
|
123
|
+
roomService: '客房服務',
|
|
124
|
+
deliveryByUber: 'Uber 配送',
|
|
125
|
+
deliveryByPanda: '熊貓外賣配送',
|
|
126
|
+
deliveryByMerchant: '商家配送',
|
|
85
127
|
shopService: '堂食',
|
|
86
128
|
pickUpTime: '取餐時間',
|
|
87
129
|
asap: '盡快',
|
|
@@ -91,13 +133,21 @@ const LABELS = {
|
|
|
91
133
|
eftpos: '刷卡支付',
|
|
92
134
|
wallet: '錢包支付',
|
|
93
135
|
giftCard: '禮品卡',
|
|
136
|
+
pointCard: '積分卡',
|
|
94
137
|
balance: '餘額',
|
|
138
|
+
validity: '有效期',
|
|
95
139
|
remainingAmount: '剩餘金額',
|
|
96
140
|
redeemPoints: '本次使用積分',
|
|
97
|
-
remainingPoints: '
|
|
141
|
+
remainingPoints: '積分餘額',
|
|
142
|
+
entitlementRemaining: '剩餘次數',
|
|
98
143
|
table: '桌台',
|
|
144
|
+
bookingId: '預約編號',
|
|
145
|
+
bookingDateTime: '日期和時間',
|
|
146
|
+
bookingResource: '資源',
|
|
147
|
+
bookingHolder: '預約人',
|
|
99
148
|
orderDiscount: '整單折扣',
|
|
100
149
|
manualDiscount: '手動折扣',
|
|
150
|
+
manualMarkup: '手動加價',
|
|
101
151
|
payProcessingFee: '支付手續費',
|
|
102
152
|
surchargeIncluded: '手續費(已含)',
|
|
103
153
|
rounding: '抹零',
|
|
@@ -114,6 +164,11 @@ const LABELS = {
|
|
|
114
164
|
takeAway: 'テイクアウト',
|
|
115
165
|
delivery: '配達',
|
|
116
166
|
pickUp: '受け取り',
|
|
167
|
+
tableService: 'テーブルサービス',
|
|
168
|
+
roomService: 'ルームサービス',
|
|
169
|
+
deliveryByUber: 'Uber Eats 配達',
|
|
170
|
+
deliveryByPanda: 'HungryPanda 配達',
|
|
171
|
+
deliveryByMerchant: '店舗による配達',
|
|
117
172
|
shopService: '店内飲食',
|
|
118
173
|
pickUpTime: '受取時間',
|
|
119
174
|
asap: 'できるだけ早く',
|
|
@@ -121,13 +176,21 @@ const LABELS = {
|
|
|
121
176
|
eftpos: 'カード',
|
|
122
177
|
wallet: 'ウォレット',
|
|
123
178
|
giftCard: 'ギフトカード',
|
|
179
|
+
pointCard: 'ポイントカード',
|
|
124
180
|
balance: '残高',
|
|
181
|
+
validity: '有効期限',
|
|
125
182
|
remainingAmount: '残高',
|
|
126
183
|
redeemPoints: '利用ポイント',
|
|
127
|
-
remainingPoints: '
|
|
184
|
+
remainingPoints: 'ポイント残高',
|
|
185
|
+
entitlementRemaining: '残り回数',
|
|
128
186
|
table: 'テーブル',
|
|
187
|
+
bookingId: '予約ID',
|
|
188
|
+
bookingDateTime: '日時',
|
|
189
|
+
bookingResource: 'リソース',
|
|
190
|
+
bookingHolder: '予約者',
|
|
129
191
|
orderDiscount: '注文割引',
|
|
130
192
|
manualDiscount: '手動割引',
|
|
193
|
+
manualMarkup: '手動値上げ',
|
|
131
194
|
payProcessingFee: '決済手数料',
|
|
132
195
|
rounding: '端数調整',
|
|
133
196
|
gross: '総重量',
|
|
@@ -145,6 +208,11 @@ const LABELS = {
|
|
|
145
208
|
takeAway: 'Para levar',
|
|
146
209
|
delivery: 'Entrega',
|
|
147
210
|
pickUp: 'Recolha',
|
|
211
|
+
tableService: 'Serviço à mesa',
|
|
212
|
+
roomService: 'Serviço de quarto',
|
|
213
|
+
deliveryByUber: 'Entrega pela Uber Eats',
|
|
214
|
+
deliveryByPanda: 'Entrega pela HungryPanda',
|
|
215
|
+
deliveryByMerchant: 'Entrega pelo comerciante',
|
|
148
216
|
shopService: 'Consumo no local',
|
|
149
217
|
pickUpTime: 'Hora de recolha',
|
|
150
218
|
asap: 'Assim que possível',
|
|
@@ -152,13 +220,21 @@ const LABELS = {
|
|
|
152
220
|
eftpos: 'Cartão',
|
|
153
221
|
wallet: 'Carteira',
|
|
154
222
|
giftCard: 'Cartão-presente',
|
|
223
|
+
pointCard: 'Cartão de pontos',
|
|
155
224
|
balance: 'Saldo',
|
|
225
|
+
validity: 'Validade',
|
|
156
226
|
remainingAmount: 'Saldo restante',
|
|
157
227
|
redeemPoints: 'Pontos utilizados',
|
|
158
|
-
remainingPoints: '
|
|
228
|
+
remainingPoints: 'Saldo de pontos',
|
|
229
|
+
entitlementRemaining: 'Utilizações restantes',
|
|
159
230
|
table: 'Mesa',
|
|
231
|
+
bookingId: 'ID da reserva',
|
|
232
|
+
bookingDateTime: 'Data e hora',
|
|
233
|
+
bookingResource: 'Recurso',
|
|
234
|
+
bookingHolder: 'Titular',
|
|
160
235
|
orderDiscount: 'Desconto do pedido',
|
|
161
236
|
manualDiscount: 'Desconto manual',
|
|
237
|
+
manualMarkup: 'Acréscimo manual',
|
|
162
238
|
payProcessingFee: 'Taxa de processamento',
|
|
163
239
|
rounding: 'Arredondamento',
|
|
164
240
|
gross: 'Peso bruto',
|
|
@@ -264,9 +340,7 @@ function buildBaseData(params) {
|
|
|
264
340
|
payment_status: financials.paymentStatus,
|
|
265
341
|
total_amount: formatMoney(financials.totalAmount, currencySymbol),
|
|
266
342
|
has_pay: formatMoney(financials.netPaymentApplied, currencySymbol),
|
|
267
|
-
|
|
268
|
-
amount_gap: formatMoney(financials.amountGap, currencySymbol)
|
|
269
|
-
} : {}),
|
|
343
|
+
amount_gap: formatCalculatedMoney(financials.amountGap ?? 0, currencySymbol),
|
|
270
344
|
is_repeat: 0,
|
|
271
345
|
buzzer: stringify(order.buzzer || order.pickup_number || order.metadata?.buzzer),
|
|
272
346
|
order_no: orderNumber,
|
|
@@ -278,7 +352,9 @@ function buildBaseData(params) {
|
|
|
278
352
|
}
|
|
279
353
|
function buildProducts(order, locale, productMap) {
|
|
280
354
|
const products = Array.isArray(order.products) ? order.products : [];
|
|
281
|
-
|
|
355
|
+
const allergyByProductUid = buildProductAllergyMap(order, locale);
|
|
356
|
+
return products.map(rawProduct => {
|
|
357
|
+
const product = restoreReceiptDiscountHierarchy(rawProduct);
|
|
282
358
|
const quantity = toNumber(product.product_quantity ?? product.quantity ?? product.num ?? 1, 1);
|
|
283
359
|
const originalPrice = product.original_price ?? product.selling_price ?? product.payment_price ?? product.price ?? 0;
|
|
284
360
|
const sellingPrice = product.selling_price ?? product.payment_price ?? product.price ?? originalPrice;
|
|
@@ -317,16 +393,26 @@ function buildProducts(order, locale, productMap) {
|
|
|
317
393
|
originalPrice
|
|
318
394
|
});
|
|
319
395
|
const productNote = resolveProductNote(product, locale);
|
|
396
|
+
const allergy = isPresentId(productUid) ? allergyByProductUid.get(String(productUid).trim()) || '' : '';
|
|
320
397
|
const basePrice = resolveProductBasePrice(product);
|
|
398
|
+
const resolvedCombinations = Array.isArray(product.combinations) && product.combinations.length ? normalizeExistingCombinations(product.combinations, locale, currencySymbol) : combinations;
|
|
399
|
+
const shouldIncludeBasePrice = options.length > 0 || resolvedCombinations.length > 0 || hasProductDiscount(product, extensionList);
|
|
321
400
|
return {
|
|
322
401
|
product_title: productTitle,
|
|
323
402
|
product_quantity: quantity,
|
|
324
403
|
selling_price: formatMoney(sellingPrice, currencySymbol),
|
|
404
|
+
total_selling_price: formatCalculatedMoney(new _decimal.default(toMoneyNumber(sellingPrice)).mul(quantity), currencySymbol),
|
|
325
405
|
original_price: formatMoney(originalPrice, currencySymbol),
|
|
326
|
-
...(
|
|
327
|
-
base_price:
|
|
406
|
+
...(shouldIncludeBasePrice ? {
|
|
407
|
+
base_price: formatCalculatedMoney(basePrice ?? 0, currencySymbol)
|
|
328
408
|
} : {}),
|
|
329
409
|
total_original_price: formatMoney(new _decimal.default(toMoneyNumber(originalPrice)).mul(quantity), currencySymbol),
|
|
410
|
+
...(options.length ? {
|
|
411
|
+
options_total_price: sumNormalizedOptionTotals(options, currencySymbol)
|
|
412
|
+
} : {}),
|
|
413
|
+
...(resolvedCombinations.length ? {
|
|
414
|
+
package_items_total_price: sumCombinationTotals(resolvedCombinations, currencySymbol)
|
|
415
|
+
} : {}),
|
|
330
416
|
extension_list: extensionList,
|
|
331
417
|
...(isPresentId(productUid) ? {
|
|
332
418
|
product_uid: productUid
|
|
@@ -337,27 +423,104 @@ function buildProducts(order, locale, productMap) {
|
|
|
337
423
|
...(productNote ? {
|
|
338
424
|
product_note: productNote
|
|
339
425
|
} : {}),
|
|
426
|
+
...(allergy ? {
|
|
427
|
+
allergy
|
|
428
|
+
} : {}),
|
|
340
429
|
...(options.length ? {
|
|
341
430
|
options
|
|
342
431
|
} : {}),
|
|
343
432
|
...(resolvedVariant ? {
|
|
344
433
|
variant: resolvedVariant
|
|
345
434
|
} : {}),
|
|
346
|
-
...(
|
|
347
|
-
combinations:
|
|
348
|
-
} : combinations.length ? {
|
|
349
|
-
combinations
|
|
435
|
+
...(resolvedCombinations.length ? {
|
|
436
|
+
combinations: resolvedCombinations
|
|
350
437
|
} : {})
|
|
351
438
|
};
|
|
352
439
|
});
|
|
353
440
|
}
|
|
441
|
+
const RECEIPT_BUNDLE_MAP_ID_KEY = 'custom_product_bundle_map_id';
|
|
442
|
+
function restoreReceiptDiscountHierarchy(source) {
|
|
443
|
+
if (!source || typeof source !== 'object' || Array.isArray(source)) {
|
|
444
|
+
return source;
|
|
445
|
+
}
|
|
446
|
+
const childKey = Array.isArray(source.combinations) && source.combinations.length ? 'combinations' : Array.isArray(source.product_bundle) && source.product_bundle.length ? 'product_bundle' : null;
|
|
447
|
+
if (!childKey) return {
|
|
448
|
+
...source
|
|
449
|
+
};
|
|
450
|
+
const children = source[childKey].map(child => child && typeof child === 'object' && !Array.isArray(child) ? restoreReceiptDiscountHierarchy(child) : child);
|
|
451
|
+
const restored = {
|
|
452
|
+
...source,
|
|
453
|
+
[childKey]: children
|
|
454
|
+
};
|
|
455
|
+
if (!Array.isArray(source.discount_list) || !source.discount_list.length) {
|
|
456
|
+
return restored;
|
|
457
|
+
}
|
|
458
|
+
const descendantsByMapId = new Map();
|
|
459
|
+
indexReceiptDiscountTargets(children, descendantsByMapId);
|
|
460
|
+
if (!descendantsByMapId.size) return restored;
|
|
461
|
+
const ownDiscounts = [];
|
|
462
|
+
for (const discount of source.discount_list) {
|
|
463
|
+
const mapId = getReceiptDiscountBundleMapId(discount);
|
|
464
|
+
const target = mapId ? descendantsByMapId.get(mapId) : undefined;
|
|
465
|
+
if (!target) {
|
|
466
|
+
ownDiscounts.push(discount);
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
const targetDiscounts = Array.isArray(target.discount_list) ? target.discount_list : [];
|
|
470
|
+
if (!targetDiscounts.some(item => isSameReceiptDiscount(item, discount))) {
|
|
471
|
+
target.discount_list = [...targetDiscounts, discount];
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
restored.discount_list = ownDiscounts;
|
|
475
|
+
return restored;
|
|
476
|
+
}
|
|
477
|
+
function indexReceiptDiscountTargets(nodes, targets) {
|
|
478
|
+
for (const node of nodes) {
|
|
479
|
+
if (!node || typeof node !== 'object' || Array.isArray(node)) continue;
|
|
480
|
+
const mapId = getReceiptBundleMapId(node);
|
|
481
|
+
if (mapId && !targets.has(mapId)) targets.set(mapId, node);
|
|
482
|
+
const children = Array.isArray(node.combinations) && node.combinations.length ? node.combinations : Array.isArray(node.product_bundle) ? node.product_bundle : [];
|
|
483
|
+
indexReceiptDiscountTargets(children, targets);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function normalizeReceiptId(value) {
|
|
487
|
+
if (value === undefined || value === null) return null;
|
|
488
|
+
const normalized = String(value).trim();
|
|
489
|
+
return normalized ? normalized : null;
|
|
490
|
+
}
|
|
491
|
+
function getReceiptBundleMapId(node) {
|
|
492
|
+
return normalizeReceiptId(node.metadata?.[RECEIPT_BUNDLE_MAP_ID_KEY] ?? node._id);
|
|
493
|
+
}
|
|
494
|
+
function getReceiptDiscountBundleMapId(discount) {
|
|
495
|
+
return normalizeReceiptId(discount?.metadata?.[RECEIPT_BUNDLE_MAP_ID_KEY]);
|
|
496
|
+
}
|
|
497
|
+
function isSameReceiptDiscount(left, right) {
|
|
498
|
+
const leftOrderDiscountId = normalizeReceiptId(left?.order_discount_id);
|
|
499
|
+
const rightOrderDiscountId = normalizeReceiptId(right?.order_discount_id);
|
|
500
|
+
if (leftOrderDiscountId && rightOrderDiscountId) {
|
|
501
|
+
return leftOrderDiscountId === rightOrderDiscountId;
|
|
502
|
+
}
|
|
503
|
+
const leftResourceId = normalizeReceiptId(left?.discount?.resource_id ?? left?.resource_id ?? left?.discount_id);
|
|
504
|
+
const rightResourceId = normalizeReceiptId(right?.discount?.resource_id ?? right?.resource_id ?? right?.discount_id);
|
|
505
|
+
if (!leftResourceId || !rightResourceId) return false;
|
|
506
|
+
return leftResourceId === rightResourceId && getReceiptDiscountBundleMapId(left) === getReceiptDiscountBundleMapId(right) && String(left?.amount ?? left?.value ?? left?.discount_amount ?? '') === String(right?.amount ?? right?.value ?? right?.discount_amount ?? '');
|
|
507
|
+
}
|
|
354
508
|
function resolveProductBasePrice(product) {
|
|
355
509
|
const metadata = product.metadata || {};
|
|
356
510
|
const origin = product._origin || metadata.origin || {};
|
|
357
511
|
const variantId = product.product_variant_id ?? product.variant_id;
|
|
358
512
|
const variant = Array.isArray(origin.variant) && isPresentId(variantId) ? origin.variant.find(item => isSameId(item?.id, variantId)) : undefined;
|
|
359
513
|
const isV2 = metadata.price_schema_version === 2 || metadata.price_schema_version === '2';
|
|
360
|
-
|
|
514
|
+
const quantity = toNumber(product.product_quantity ?? product.quantity ?? product.num ?? 1, 1);
|
|
515
|
+
const hasEntitlementPass = normalizeArray(product.discount_list).some(discount => getProductWalletDiscountType(discount) === 'entitlement_pass');
|
|
516
|
+
const hasOptions = normalizeArray(product.options).length > 0 || normalizeArray(product.product_sku?.option).length > 0;
|
|
517
|
+
const hasCombinations = normalizeArray(product.combinations).length > 0 || normalizeArray(product.product_bundle).length > 0 || normalizeArray(product.bundle).length > 0;
|
|
518
|
+
const aggregateSourcePrice = firstPresentValue(metadata.source_product_price, product.price);
|
|
519
|
+
const originalPrice = product.original_price;
|
|
520
|
+
// 部分旧 entitlement 商品把 source/price 持久化成了整行总价,但
|
|
521
|
+
// original_price 仍是单件价。仅在等式能明确证明该口径时做窄幅回退。
|
|
522
|
+
const useOriginalPriceAsLegacyUnitPrice = hasEntitlementPass && quantity > 1 && !hasOptions && !hasCombinations && isNumericAmount(aggregateSourcePrice) && isNumericAmount(originalPrice) && new _decimal.default(aggregateSourcePrice).eq(new _decimal.default(originalPrice).mul(quantity));
|
|
523
|
+
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);
|
|
361
524
|
}
|
|
362
525
|
function normalizeProductDiscounts(params) {
|
|
363
526
|
const {
|
|
@@ -366,15 +529,37 @@ function normalizeProductDiscounts(params) {
|
|
|
366
529
|
currencySymbol,
|
|
367
530
|
quantity
|
|
368
531
|
} = params;
|
|
369
|
-
|
|
532
|
+
const rows = normalizeArray(product.discount_list).filter(discount => {
|
|
533
|
+
const amount = discount.amount ?? discount.value ?? discount.discount_amount;
|
|
534
|
+
return isNonZero(amount);
|
|
535
|
+
}).map(discount => {
|
|
370
536
|
const title = resolveDiscountTitleWithPercent(discount, locale);
|
|
371
537
|
const amount = discount.amount ?? discount.value ?? discount.discount_amount;
|
|
372
|
-
const
|
|
538
|
+
const isMarkup = isManualMarkup(discount);
|
|
539
|
+
const isAggregatedBundleDiscount = Boolean(getReceiptDiscountBundleMapId(discount));
|
|
540
|
+
const isEntitlementPass = getProductWalletDiscountType(discount) === 'entitlement_pass';
|
|
541
|
+
const totalAmount = new _decimal.default(toMoneyNumber(amount)).mul(isAggregatedBundleDiscount || isEntitlementPass ? 1 : quantity);
|
|
542
|
+
const formattedAmount = isMarkup ? formatMoney(totalAmount.abs(), currencySymbol) : formatNegativeMoney(totalAmount, currencySymbol);
|
|
373
543
|
return {
|
|
374
544
|
item: title,
|
|
375
|
-
value: formattedAmount
|
|
545
|
+
value: formattedAmount,
|
|
546
|
+
isMarkup
|
|
376
547
|
};
|
|
377
548
|
});
|
|
549
|
+
return rows.sort((left, right) => Number(right.isMarkup) - Number(left.isMarkup)).map(({
|
|
550
|
+
isMarkup: _isMarkup,
|
|
551
|
+
...row
|
|
552
|
+
}) => row);
|
|
553
|
+
}
|
|
554
|
+
function isManualMarkup(discount) {
|
|
555
|
+
if (discount.type !== 'product') return false;
|
|
556
|
+
if (discount.discount?.discount_type === 'change_price') return true;
|
|
557
|
+
const amount = discount.amount ?? discount.value ?? discount.discount_amount;
|
|
558
|
+
return new _decimal.default(toMoneyNumber(amount)).isNegative();
|
|
559
|
+
}
|
|
560
|
+
function hasProductDiscount(product, extensionList = []) {
|
|
561
|
+
const hasDiscountListAmount = normalizeArray(product.discount_list).some(discount => isNonZero(discount.amount ?? discount.value ?? discount.discount_amount));
|
|
562
|
+
return hasDiscountListAmount || extensionList.some(row => /^\s*-/.test(stringify(row?.value)));
|
|
378
563
|
}
|
|
379
564
|
function buildProductExtensionList(params) {
|
|
380
565
|
const {
|
|
@@ -446,6 +631,9 @@ function buildProductQuantityPriceText(params) {
|
|
|
446
631
|
}
|
|
447
632
|
function resolveDiscountTitleWithPercent(discount, locale) {
|
|
448
633
|
const title = resolveDiscountTitle(discount, locale);
|
|
634
|
+
if (getProductWalletDiscountType(discount) === 'entitlement_pass') {
|
|
635
|
+
return title;
|
|
636
|
+
}
|
|
449
637
|
const percentRaw = discount.discount?.percent;
|
|
450
638
|
const cardType = discount.discount?.discount_card_type ?? 'percent';
|
|
451
639
|
const percent = percentRaw !== undefined && percentRaw !== null ? Math.trunc(Number(percentRaw)) : NaN;
|
|
@@ -457,7 +645,9 @@ function resolveDiscountTitleWithPercent(discount, locale) {
|
|
|
457
645
|
function resolveDiscountTitle(discount, locale) {
|
|
458
646
|
const title = getLocalizedValue(discount.discount?.name ?? discount.discount?.title ?? discount.discount?.message ?? discount.name ?? discount.title ?? discount.message, locale);
|
|
459
647
|
if (title) return title;
|
|
460
|
-
if (discount.type === 'product')
|
|
648
|
+
if (discount.type === 'product') {
|
|
649
|
+
return label(locale, isManualMarkup(discount) ? 'manualMarkup' : 'manualDiscount');
|
|
650
|
+
}
|
|
461
651
|
return '';
|
|
462
652
|
}
|
|
463
653
|
function resolveProductTitle(params) {
|
|
@@ -483,6 +673,7 @@ function normalizeProductOptions(params) {
|
|
|
483
673
|
const rawOptions = Array.isArray(params.options) ? params.options : Array.isArray(product.options) ? product.options : Array.isArray(product.product_sku?.option) ? product.product_sku.option : [];
|
|
484
674
|
return rawOptions.map(option => {
|
|
485
675
|
const price = option.original_price ?? option.add_price ?? option.price;
|
|
676
|
+
const quantity = toNumber(option.num ?? option.quantity ?? 1, 1);
|
|
486
677
|
const value = option.value ?? option.option ?? option.item;
|
|
487
678
|
const name = getProductTitleSource(option, locale) || resolveOptionTitleFromParentProduct({
|
|
488
679
|
option,
|
|
@@ -492,11 +683,12 @@ function normalizeProductOptions(params) {
|
|
|
492
683
|
return {
|
|
493
684
|
name,
|
|
494
685
|
...(option.num !== undefined || option.quantity !== undefined ? {
|
|
495
|
-
num:
|
|
686
|
+
num: quantity
|
|
496
687
|
} : {}),
|
|
497
688
|
...(price !== undefined && price !== null && price !== '' ? {
|
|
498
689
|
price: formatMoney(price, currencySymbol)
|
|
499
690
|
} : {}),
|
|
691
|
+
total_price: formatCalculatedMoney(new _decimal.default(toMoneyNumber(price ?? 0)).mul(quantity), currencySymbol),
|
|
500
692
|
...(value !== undefined && value !== null && value !== '' ? {
|
|
501
693
|
value: getLocalizedValue(value, locale)
|
|
502
694
|
} : {})
|
|
@@ -527,49 +719,149 @@ function normalizeProductCombinations(params) {
|
|
|
527
719
|
parentProduct
|
|
528
720
|
});
|
|
529
721
|
const productNote = resolveProductNote(bundle, locale);
|
|
530
|
-
const nestedCombinations = Array.isArray(bundle.combinations) ? normalizeExistingCombinations(bundle.combinations, locale) : [];
|
|
722
|
+
const nestedCombinations = Array.isArray(bundle.combinations) ? normalizeExistingCombinations(bundle.combinations, locale, currencySymbol) : [];
|
|
723
|
+
const quantity = toNumber(bundle.num ?? bundle.quantity ?? 1, 1);
|
|
724
|
+
const formattedPrice = formatBundlePrice(bundle, currencySymbol);
|
|
725
|
+
const optionsTotal = sumNormalizedOptionTotals(combinationsOptions, currencySymbol);
|
|
726
|
+
const explicitBasePrice = resolveCombinationBasePrice(bundle);
|
|
727
|
+
const basePrice = isPresentId(explicitBasePrice) ? explicitBasePrice : 0;
|
|
728
|
+
const extensionList = buildProductExtensionList({
|
|
729
|
+
product: bundle,
|
|
730
|
+
locale,
|
|
731
|
+
currencySymbol,
|
|
732
|
+
quantity,
|
|
733
|
+
originalPrice: formattedPrice
|
|
734
|
+
});
|
|
735
|
+
const shouldIncludeBasePrice = combinationsOptions.length > 0 || nestedCombinations.length > 0 || hasProductDiscount(bundle, extensionList);
|
|
531
736
|
return {
|
|
532
737
|
product_title: baseTitle,
|
|
533
|
-
product_quantity:
|
|
534
|
-
price:
|
|
738
|
+
product_quantity: quantity,
|
|
739
|
+
price: formattedPrice,
|
|
740
|
+
total_selling_price: formatCalculatedMoney(new _decimal.default(toMoneyNumber(formattedPrice)).mul(quantity), currencySymbol),
|
|
741
|
+
...(shouldIncludeBasePrice ? {
|
|
742
|
+
base_price: formatCalculatedMoney(basePrice, currencySymbol)
|
|
743
|
+
} : {}),
|
|
535
744
|
combinations_options: combinationsOptions,
|
|
745
|
+
...(combinationsOptions.length ? {
|
|
746
|
+
options_total_price: optionsTotal
|
|
747
|
+
} : {}),
|
|
748
|
+
...(nestedCombinations.length ? {
|
|
749
|
+
package_items_total_price: sumCombinationTotals(nestedCombinations, currencySymbol)
|
|
750
|
+
} : {}),
|
|
536
751
|
...(productNote ? {
|
|
537
752
|
product_note: productNote
|
|
538
753
|
} : {}),
|
|
754
|
+
...(extensionList.length ? {
|
|
755
|
+
extension_list: extensionList
|
|
756
|
+
} : {}),
|
|
539
757
|
...(nestedCombinations.length ? {
|
|
540
758
|
combinations: nestedCombinations
|
|
541
759
|
} : {})
|
|
542
760
|
};
|
|
543
761
|
});
|
|
544
762
|
}
|
|
545
|
-
function normalizeExistingCombinations(combinations, locale) {
|
|
763
|
+
function normalizeExistingCombinations(combinations, locale, currencySymbol) {
|
|
546
764
|
return combinations.map(combination => {
|
|
547
765
|
if (!combination || typeof combination !== 'object' || Array.isArray(combination)) {
|
|
548
766
|
return combination;
|
|
549
767
|
}
|
|
550
768
|
const productNote = resolveProductNote(combination, locale);
|
|
551
|
-
const
|
|
769
|
+
const rawOptions = Array.isArray(combination.combinations_options) ? combination.combinations_options : Array.isArray(combination.options) ? combination.options : [];
|
|
770
|
+
const normalizedOptions = normalizeProductOptions({
|
|
771
|
+
product: {},
|
|
772
|
+
locale,
|
|
773
|
+
currencySymbol,
|
|
774
|
+
options: rawOptions
|
|
775
|
+
});
|
|
776
|
+
const nestedCombinations = Array.isArray(combination.combinations) ? normalizeExistingCombinations(combination.combinations, locale, currencySymbol) : [];
|
|
777
|
+
const productTitle = getBundleProductTitleSource(combination, locale);
|
|
552
778
|
const snapshot = {
|
|
553
779
|
...combination
|
|
554
780
|
};
|
|
781
|
+
delete snapshot.product_title;
|
|
555
782
|
delete snapshot.product_note;
|
|
556
783
|
delete snapshot.note;
|
|
784
|
+
delete snapshot.combinations;
|
|
785
|
+
delete snapshot.discount_list;
|
|
786
|
+
const quantity = toNumber(combination.product_quantity ?? combination.num ?? combination.quantity ?? 1, 1);
|
|
787
|
+
const unitPrice = firstPresentValue(combination.selling_price, combination.price, combination.original_price) ?? 0;
|
|
788
|
+
const optionsAmount = sumMoneyValues(normalizedOptions.map(option => option.total_price));
|
|
789
|
+
const explicitBasePrice = resolveCombinationBasePrice(combination, true);
|
|
790
|
+
const basePrice = isPresentId(explicitBasePrice) ? explicitBasePrice : 0;
|
|
791
|
+
const extensionList = buildProductExtensionList({
|
|
792
|
+
product: combination,
|
|
793
|
+
locale,
|
|
794
|
+
currencySymbol,
|
|
795
|
+
quantity,
|
|
796
|
+
originalPrice: unitPrice
|
|
797
|
+
});
|
|
798
|
+
const shouldIncludeBasePrice = normalizedOptions.length > 0 || nestedCombinations.length > 0 || hasProductDiscount(combination, extensionList);
|
|
557
799
|
return {
|
|
558
800
|
...snapshot,
|
|
801
|
+
...(productTitle ? {
|
|
802
|
+
product_title: productTitle
|
|
803
|
+
} : {}),
|
|
804
|
+
product_quantity: quantity,
|
|
805
|
+
total_selling_price: formatCalculatedMoney(new _decimal.default(toMoneyNumber(unitPrice)).mul(quantity), currencySymbol),
|
|
806
|
+
...(shouldIncludeBasePrice ? {
|
|
807
|
+
base_price: formatCalculatedMoney(basePrice, currencySymbol)
|
|
808
|
+
} : {}),
|
|
809
|
+
...(Array.isArray(combination.combinations_options) ? {
|
|
810
|
+
combinations_options: normalizedOptions
|
|
811
|
+
} : Array.isArray(combination.options) ? {
|
|
812
|
+
options: normalizedOptions
|
|
813
|
+
} : {}),
|
|
814
|
+
...(normalizedOptions.length ? {
|
|
815
|
+
options_total_price: formatCalculatedMoney(optionsAmount, currencySymbol)
|
|
816
|
+
} : {}),
|
|
817
|
+
...(nestedCombinations.length ? {
|
|
818
|
+
package_items_total_price: sumCombinationTotals(nestedCombinations, currencySymbol)
|
|
819
|
+
} : {}),
|
|
559
820
|
...(productNote ? {
|
|
560
821
|
product_note: productNote
|
|
561
822
|
} : {}),
|
|
562
|
-
...(
|
|
823
|
+
...(extensionList.length ? {
|
|
824
|
+
extension_list: extensionList
|
|
825
|
+
} : {}),
|
|
826
|
+
...(nestedCombinations.length ? {
|
|
563
827
|
combinations: nestedCombinations
|
|
564
828
|
} : {})
|
|
565
829
|
};
|
|
566
830
|
});
|
|
567
831
|
}
|
|
832
|
+
function sumMoneyValues(values) {
|
|
833
|
+
return values.reduce((sum, value) => sum.plus(toMoneyNumber(value)), new _decimal.default(0));
|
|
834
|
+
}
|
|
835
|
+
function resolveCombinationBasePrice(combination, preferOriginalPrice = false) {
|
|
836
|
+
const fallbackPrices = preferOriginalPrice ? [combination.original_price, combination.product_price, combination.price] : [combination.price, combination.original_price, combination.product_price];
|
|
837
|
+
return firstPresentValue(combination.base_price, combination.metadata?.source_product_price, ...fallbackPrices);
|
|
838
|
+
}
|
|
839
|
+
function sumNormalizedOptionTotals(options, currencySymbol) {
|
|
840
|
+
return formatCalculatedMoney(sumMoneyValues(options.map(option => option.total_price)), currencySymbol);
|
|
841
|
+
}
|
|
842
|
+
function sumCombinationTotals(combinations, currencySymbol) {
|
|
843
|
+
return formatCalculatedMoney(sumMoneyValues(combinations.map(combination => combination.total_selling_price)), currencySymbol);
|
|
844
|
+
}
|
|
568
845
|
function resolveProductNote(product, locale) {
|
|
569
846
|
const productNote = getLocalizedValue(product.product_note, locale).trim();
|
|
570
847
|
if (productNote) return productNote;
|
|
571
848
|
return getLocalizedValue(product.note, locale).trim();
|
|
572
849
|
}
|
|
850
|
+
function buildProductAllergyMap(order, locale) {
|
|
851
|
+
const allergyByProductUid = new Map();
|
|
852
|
+
for (const note of normalizeArray(order.notes)) {
|
|
853
|
+
if (!note || typeof note !== 'object' || Array.isArray(note) || String(note.note_type || '').trim() !== 'allergy') {
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
const content = getLocalizedValue(note.content?.text, locale).trim();
|
|
857
|
+
if (!content) continue;
|
|
858
|
+
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);
|
|
859
|
+
const productUid = String(relation?.target_uuid ?? '').trim();
|
|
860
|
+
if (!productUid || allergyByProductUid.has(productUid)) continue;
|
|
861
|
+
allergyByProductUid.set(productUid, content);
|
|
862
|
+
}
|
|
863
|
+
return allergyByProductUid;
|
|
864
|
+
}
|
|
573
865
|
function resolveBundleProductTitle(params) {
|
|
574
866
|
const {
|
|
575
867
|
bundle,
|
|
@@ -577,18 +869,18 @@ function resolveBundleProductTitle(params) {
|
|
|
577
869
|
locale,
|
|
578
870
|
parentProduct
|
|
579
871
|
} = params;
|
|
580
|
-
const ownTitle =
|
|
872
|
+
const ownTitle = getBundleProductTitleSource(bundle, locale);
|
|
581
873
|
if (ownTitle) return ownTitle;
|
|
582
874
|
const parentBundleItem = resolveBundleItemFromParentProduct({
|
|
583
875
|
bundle,
|
|
584
876
|
parentProduct
|
|
585
877
|
});
|
|
586
|
-
const parentBundleTitle = parentBundleItem ?
|
|
878
|
+
const parentBundleTitle = parentBundleItem ? getBundleProductTitleSource(parentBundleItem, locale) : '';
|
|
587
879
|
if (parentBundleTitle) return parentBundleTitle;
|
|
588
880
|
const productId = bundle.bundle_product_id ?? bundle._bundle_product_id ?? bundle.product_id ?? bundle.id;
|
|
589
881
|
const fallbackProduct = resolveProductFromMap(productId, productMap);
|
|
590
882
|
if (!fallbackProduct) return '';
|
|
591
|
-
return
|
|
883
|
+
return getBundleProductTitleSource(fallbackProduct, locale);
|
|
592
884
|
}
|
|
593
885
|
function resolveProductFromMap(productId, productMap) {
|
|
594
886
|
if (productId === undefined || productId === null || productId === '') return null;
|
|
@@ -660,7 +952,11 @@ function resolveVariantItem(group, itemPositionId) {
|
|
|
660
952
|
}
|
|
661
953
|
function getProductTitleSource(source, locale) {
|
|
662
954
|
if (!source || typeof source !== 'object') return '';
|
|
663
|
-
return resolveTitleValue(source.product_title, locale) || resolveTitleValue(source.title, locale) || resolveTitleValue(source.name, locale);
|
|
955
|
+
return resolveTitleValue(source.product_title, locale) || resolveTitleValue(source.title_i18n, locale) || resolveTitleValue(source.title, locale) || resolveTitleValue(source.name_i18n, locale) || resolveTitleValue(source.name, locale);
|
|
956
|
+
}
|
|
957
|
+
function getBundleProductTitleSource(source, locale) {
|
|
958
|
+
if (!source || typeof source !== 'object') return '';
|
|
959
|
+
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);
|
|
664
960
|
}
|
|
665
961
|
function resolveTitleValue(value, locale) {
|
|
666
962
|
if (value === undefined || value === null) return '';
|
|
@@ -795,12 +1091,6 @@ function buildFees(params) {
|
|
|
795
1091
|
});
|
|
796
1092
|
const isPriceIncludeTax = firstPresentValue(summary.is_price_include_tax, order.is_price_include_tax, shopInfo.is_price_include_tax);
|
|
797
1093
|
const roundingAmount = firstPresentValue(summary.rounding_amount, order.rounding_amount);
|
|
798
|
-
if (isNonZero(roundingAmount)) {
|
|
799
|
-
fees.push({
|
|
800
|
-
item: label(locale, 'rounding'),
|
|
801
|
-
value: formatMoney(roundingAmount, currencySymbol)
|
|
802
|
-
});
|
|
803
|
-
}
|
|
804
1094
|
const payProcessingFee = financials.payProcessingFee;
|
|
805
1095
|
if (isNonZero(payProcessingFee)) {
|
|
806
1096
|
fees.push({
|
|
@@ -811,12 +1101,14 @@ function buildFees(params) {
|
|
|
811
1101
|
const productExpectAmount = firstPresentValue(summary.product_expect_amount, summary.product_amount, order.product_expect_amount);
|
|
812
1102
|
const productOriginalAmount = firstPresentValue(summary.product_original_amount, order.product_original_amount) ?? calculateProductOriginalAmount(order);
|
|
813
1103
|
const totalSavingsAmount = new _decimal.default(toMoneyNumber(productOriginalAmount)).minus(toMoneyNumber(productExpectAmount)).plus(toMoneyNumber(discountAmount));
|
|
1104
|
+
const itemDiscountAmount = new _decimal.default(toMoneyNumber(productExpectAmount ?? 0)).minus(toMoneyNumber(productOriginalAmount));
|
|
814
1105
|
return {
|
|
815
1106
|
fees,
|
|
816
1107
|
countFee: {
|
|
817
1108
|
total_amount: formatMoney(firstPresentValue(summary.total_amount, order.total_amount) ?? 0, currencySymbol),
|
|
818
1109
|
original_item_amount: formatMoney(productOriginalAmount, currencySymbol),
|
|
819
1110
|
product_expect_amount: formatMoney(productExpectAmount ?? 0, currencySymbol),
|
|
1111
|
+
item_discount_amount: formatCalculatedMoney(itemDiscountAmount, currencySymbol),
|
|
820
1112
|
expect_amount: formatMoney(financials.totalAmount, currencySymbol),
|
|
821
1113
|
product_quantity: toNumber(firstPresentValue(summary.product_quantity, products.reduce((sum, product) => sum + product.product_quantity, 0)), 0),
|
|
822
1114
|
has_pay: formatMoney(financials.orderPaidAmount, currencySymbol),
|
|
@@ -900,15 +1192,13 @@ function normalizeCustomerDisplayValue(value) {
|
|
|
900
1192
|
return text;
|
|
901
1193
|
}
|
|
902
1194
|
function buildDelivery(order, locale) {
|
|
903
|
-
const
|
|
904
|
-
const
|
|
905
|
-
const serviceTypeLabelKey = resolveServiceTypeLabelKey(serviceType, order);
|
|
1195
|
+
const serviceType = stringify(order.metadata?.shop_service_data?.service_type).trim();
|
|
1196
|
+
const serviceTypeLabelKey = resolveServiceTypeLabelKey(serviceType);
|
|
906
1197
|
if (!serviceTypeLabelKey) return [];
|
|
907
1198
|
const items = [{
|
|
908
|
-
item:
|
|
909
|
-
value:
|
|
910
|
-
size: 2
|
|
911
|
-
align: 'left'
|
|
1199
|
+
item: label(locale, serviceTypeLabelKey),
|
|
1200
|
+
value: '',
|
|
1201
|
+
size: 2
|
|
912
1202
|
}];
|
|
913
1203
|
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);
|
|
914
1204
|
if (isPresentId(handoverTime)) {
|
|
@@ -920,26 +1210,16 @@ function buildDelivery(order, locale) {
|
|
|
920
1210
|
return items;
|
|
921
1211
|
}
|
|
922
1212
|
function buildAppointment(order, locale) {
|
|
923
|
-
const
|
|
1213
|
+
const parentScheduleId = order.metadata?.appointment_booking_parent_schedule_id;
|
|
1214
|
+
const bookings = sortBookings(normalizeArray(order.bookings).filter(booking => isReceiptAppointmentBooking(booking, parentScheduleId)));
|
|
924
1215
|
if (bookings.length) {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
const bookingUid = firstPresentValue(booking.metadata?.unique_identification_number, booking.booking_uid);
|
|
933
|
-
return {
|
|
934
|
-
...snapshot,
|
|
935
|
-
...(isPresentId(productUid) ? {
|
|
936
|
-
product_uid: productUid
|
|
937
|
-
} : {}),
|
|
938
|
-
...(isPresentId(bookingUid) ? {
|
|
939
|
-
booking_uid: bookingUid
|
|
940
|
-
} : {})
|
|
941
|
-
};
|
|
942
|
-
});
|
|
1216
|
+
const allResources = normalizeArray(order.resources || order.resource_list);
|
|
1217
|
+
return bookings.flatMap(booking => buildAppointmentRows({
|
|
1218
|
+
booking,
|
|
1219
|
+
allResources,
|
|
1220
|
+
bookingCount: bookings.length,
|
|
1221
|
+
locale
|
|
1222
|
+
}));
|
|
943
1223
|
}
|
|
944
1224
|
const tableName = order.table_name || order.metadata?.table_name || order.metadata?.table?.name || order.form_data?.table_name;
|
|
945
1225
|
if (!tableName) return [];
|
|
@@ -948,6 +1228,95 @@ function buildAppointment(order, locale) {
|
|
|
948
1228
|
value: getLocalizedValue(tableName, locale)
|
|
949
1229
|
}];
|
|
950
1230
|
}
|
|
1231
|
+
function isReceiptAppointmentBooking(booking, parentScheduleId) {
|
|
1232
|
+
if (!booking || typeof booking !== 'object' || Array.isArray(booking)) {
|
|
1233
|
+
return false;
|
|
1234
|
+
}
|
|
1235
|
+
if (isPresentId(parentScheduleId) && isSameId(booking.schedule_event_id, parentScheduleId)) {
|
|
1236
|
+
return false;
|
|
1237
|
+
}
|
|
1238
|
+
const parentId = booking.parent_id;
|
|
1239
|
+
if (parentId === undefined || parentId === null || parentId === '') {
|
|
1240
|
+
return true;
|
|
1241
|
+
}
|
|
1242
|
+
return Number(parentId) !== 0;
|
|
1243
|
+
}
|
|
1244
|
+
function buildAppointmentRows(params) {
|
|
1245
|
+
const {
|
|
1246
|
+
booking,
|
|
1247
|
+
allResources,
|
|
1248
|
+
bookingCount,
|
|
1249
|
+
locale
|
|
1250
|
+
} = params;
|
|
1251
|
+
const productUid = stringify(firstPresentValue(booking.product_uid, booking.metadata?.product_uid)).trim();
|
|
1252
|
+
const bookingUid = stringify(firstPresentValue(booking.metadata?.unique_identification_number, booking.booking_uid)).trim();
|
|
1253
|
+
const association = {
|
|
1254
|
+
product_uid: productUid,
|
|
1255
|
+
booking_uid: bookingUid
|
|
1256
|
+
};
|
|
1257
|
+
const bookingId = stringify(firstPresentValue(booking.booking_id, booking.schedule_event_id, bookingUid)).trim();
|
|
1258
|
+
const bookingTime = formatBookingRange(booking, locale);
|
|
1259
|
+
const resourceText = formatBookingResources(resolveBookingResourcesForReceipt(booking, allResources, bookingCount), locale);
|
|
1260
|
+
const holder = getLocalizedValue(booking.holder?.name ?? booking.metadata?.holder?.name, locale).trim();
|
|
1261
|
+
return [{
|
|
1262
|
+
item: label(locale, 'bookingId'),
|
|
1263
|
+
value: bookingId,
|
|
1264
|
+
...association
|
|
1265
|
+
}, {
|
|
1266
|
+
item: label(locale, 'bookingDateTime'),
|
|
1267
|
+
value: bookingTime,
|
|
1268
|
+
...association
|
|
1269
|
+
}, {
|
|
1270
|
+
item: label(locale, 'bookingResource'),
|
|
1271
|
+
value: resourceText,
|
|
1272
|
+
...association
|
|
1273
|
+
}, {
|
|
1274
|
+
item: label(locale, 'bookingHolder'),
|
|
1275
|
+
value: holder,
|
|
1276
|
+
...association
|
|
1277
|
+
}].filter(row => Boolean(row.value));
|
|
1278
|
+
}
|
|
1279
|
+
function formatBookingRange(booking, locale) {
|
|
1280
|
+
const startDate = stringify(booking.start_date);
|
|
1281
|
+
const startTime = stringify(booking.start_time);
|
|
1282
|
+
const endDate = stringify(booking.end_date || startDate);
|
|
1283
|
+
const endTime = stringify(booking.end_time);
|
|
1284
|
+
const start = firstValidBookingDate(booking.start_at, booking.start_datetime, booking.start_date_time, `${startDate} ${startTime}`.trim());
|
|
1285
|
+
const end = firstValidBookingDate(booking.end_at, booking.end_datetime, booking.end_date_time, endTime ? `${endDate} ${endTime}`.trim() : '');
|
|
1286
|
+
if (!start) {
|
|
1287
|
+
return [startDate, startTime, endTime ? `- ${endDate} ${endTime}` : ''].filter(Boolean).join(' ');
|
|
1288
|
+
}
|
|
1289
|
+
const dateFormat = locale === 'en' ? 'DD/MM/YYYY' : 'YYYY/MM/DD';
|
|
1290
|
+
const startText = start.format(`${dateFormat} HH:mm`);
|
|
1291
|
+
if (!end) return startText;
|
|
1292
|
+
return `${startText} - ${end.isSame(start, 'day') ? end.format('HH:mm') : end.format(`${dateFormat} HH:mm`)}`;
|
|
1293
|
+
}
|
|
1294
|
+
function firstValidBookingDate(...values) {
|
|
1295
|
+
for (const value of values) {
|
|
1296
|
+
if (!isPresentId(value)) continue;
|
|
1297
|
+
const parsed = (0, _dayjs.default)(value);
|
|
1298
|
+
if (parsed.isValid()) return parsed;
|
|
1299
|
+
}
|
|
1300
|
+
return null;
|
|
1301
|
+
}
|
|
1302
|
+
function resolveBookingResourcesForReceipt(booking, allResources, bookingCount) {
|
|
1303
|
+
const embedded = normalizeArray(booking.resources).filter(resource => Boolean(resource) && typeof resource === 'object' && !Array.isArray(resource));
|
|
1304
|
+
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));
|
|
1305
|
+
for (const resource of embedded) {
|
|
1306
|
+
const id = stringify(resource.id ?? resource.resource_id ?? resource.relation_id).trim();
|
|
1307
|
+
if (id) resourceIds.add(id);
|
|
1308
|
+
}
|
|
1309
|
+
if (resourceIds.size) {
|
|
1310
|
+
const matched = allResources.filter(resource => resourceIds.has(stringify(resource.id ?? resource.resource_id ?? resource.relation_id).trim()));
|
|
1311
|
+
if (matched.length) return matched;
|
|
1312
|
+
}
|
|
1313
|
+
if (embedded.length) return embedded;
|
|
1314
|
+
if (bookingCount === 1 && allResources.length) return allResources;
|
|
1315
|
+
return [];
|
|
1316
|
+
}
|
|
1317
|
+
function formatBookingResources(resources, locale) {
|
|
1318
|
+
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(', ');
|
|
1319
|
+
}
|
|
951
1320
|
function sortBookings(bookings) {
|
|
952
1321
|
return bookings.map((booking, index) => ({
|
|
953
1322
|
booking,
|
|
@@ -999,7 +1368,9 @@ function buildPaymentData(params) {
|
|
|
999
1368
|
payment,
|
|
1000
1369
|
locale: params.locale,
|
|
1001
1370
|
currencySymbol: params.currencySymbol,
|
|
1002
|
-
includeFundRecordBalance: false
|
|
1371
|
+
includeFundRecordBalance: false,
|
|
1372
|
+
includeFundRecordUsage: true,
|
|
1373
|
+
includePaymentAmount: true
|
|
1003
1374
|
}));
|
|
1004
1375
|
}
|
|
1005
1376
|
function buildRefundData(params) {
|
|
@@ -1083,28 +1454,454 @@ function resolveRefundPayment(refund, payments) {
|
|
|
1083
1454
|
return matchedPayment || refund;
|
|
1084
1455
|
}
|
|
1085
1456
|
function buildWallets(params) {
|
|
1086
|
-
const
|
|
1087
|
-
|
|
1457
|
+
const virtualCurrencySnapshots = collectVirtualCurrencySnapshots(params.order);
|
|
1458
|
+
const displayedWalletPayments = normalizeArray(params.order.payments).filter(payment => isPrintablePayment(payment) && !!getFundRecord(payment));
|
|
1459
|
+
const paymentWallets = displayedWalletPayments.map(payment => {
|
|
1460
|
+
const virtualCurrencySnapshot = findVirtualCurrencySnapshotForPayment(payment, virtualCurrencySnapshots.byId);
|
|
1461
|
+
return buildPaymentItems({
|
|
1462
|
+
payment,
|
|
1463
|
+
locale: params.locale,
|
|
1464
|
+
currencySymbol: params.currencySymbol,
|
|
1465
|
+
includeFundRecordBalance: true,
|
|
1466
|
+
includeFundRecordUsage: false,
|
|
1467
|
+
includePaymentAmount: false,
|
|
1468
|
+
virtualCurrencyUnit: virtualCurrencySnapshot ? getVirtualCurrencySnapshotUnit(virtualCurrencySnapshot, params.locale) : undefined
|
|
1469
|
+
});
|
|
1470
|
+
});
|
|
1471
|
+
const displayedWalletAssetIdentities = collectDisplayedWalletAssetIdentities(displayedWalletPayments);
|
|
1472
|
+
const voucherWallets = buildVoucherAssets({
|
|
1473
|
+
...params,
|
|
1474
|
+
displayedWalletAssetIdentities
|
|
1475
|
+
});
|
|
1476
|
+
const productDiscountWallets = buildProductDiscountAssets({
|
|
1477
|
+
...params,
|
|
1478
|
+
displayedWalletAssetIdentities
|
|
1479
|
+
});
|
|
1480
|
+
return [...paymentWallets, ...voucherWallets, ...productDiscountWallets, ...buildPointCardSnapshotAssets({
|
|
1481
|
+
order: params.order,
|
|
1088
1482
|
locale: params.locale,
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1483
|
+
displayedWalletAssetIdentities
|
|
1484
|
+
}), ...buildVirtualCurrencySnapshotAssets({
|
|
1485
|
+
snapshots: virtualCurrencySnapshots.items,
|
|
1486
|
+
order: params.order,
|
|
1487
|
+
locale: params.locale,
|
|
1488
|
+
displayedWalletAssetIdentities
|
|
1489
|
+
})];
|
|
1490
|
+
}
|
|
1491
|
+
function collectDisplayedWalletAssetIdentities(payments) {
|
|
1492
|
+
const identities = [];
|
|
1493
|
+
payments.forEach(payment => {
|
|
1494
|
+
const fundRecord = getFundRecord(payment);
|
|
1495
|
+
const ids = Array.from(new Set([fundRecord?.voucher_id, payment.voucher_id].map(value => normalizeWalletPassId(value)).filter(value => !!value)));
|
|
1496
|
+
const codes = normalizeWalletAssetCodes(fundRecord?.code, fundRecord?.encoded);
|
|
1497
|
+
if (!ids.length) {
|
|
1498
|
+
registerWalletAssetIdentity(identities, {
|
|
1499
|
+
id: null,
|
|
1500
|
+
codes
|
|
1501
|
+
});
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
ids.forEach(id => {
|
|
1505
|
+
registerWalletAssetIdentity(identities, {
|
|
1506
|
+
id,
|
|
1507
|
+
codes
|
|
1508
|
+
});
|
|
1509
|
+
});
|
|
1510
|
+
});
|
|
1511
|
+
return identities;
|
|
1512
|
+
}
|
|
1513
|
+
const PRODUCT_WALLET_DISCOUNT_TYPES = new Set(['good_pass', 'discount_card', 'product_discount_card', 'entitlement_pass']);
|
|
1514
|
+
function buildProductDiscountAssets(params) {
|
|
1515
|
+
const fallbackAssetTime = firstPresentValue(params.order.updated_at, params.order.created_at);
|
|
1516
|
+
return collectProductWalletDiscountAssets(params.order).flatMap(asset => {
|
|
1517
|
+
if (!claimWalletAssetIdentity(params.displayedWalletAssetIdentities, {
|
|
1518
|
+
id: asset.id,
|
|
1519
|
+
codes: asset.codes
|
|
1520
|
+
})) return [];
|
|
1521
|
+
const name = getProductWalletDiscountName(asset, params.locale);
|
|
1522
|
+
const assetTime = getProductWalletDiscountTime(asset) ?? fallbackAssetTime;
|
|
1523
|
+
const timeRow = isPresentId(assetTime) ? {
|
|
1524
|
+
item: formatDateTime(assetTime, params.locale),
|
|
1525
|
+
value: ''
|
|
1526
|
+
} : null;
|
|
1527
|
+
if (asset.type === 'entitlement_pass') {
|
|
1528
|
+
const remaining = getProductEntitlementBalance(asset);
|
|
1529
|
+
return [[{
|
|
1530
|
+
item: name,
|
|
1531
|
+
value: ''
|
|
1532
|
+
}, ...(remaining ? [{
|
|
1533
|
+
item: label(params.locale, 'entitlementRemaining'),
|
|
1534
|
+
value: formatEntitlementUses(remaining, params.locale)
|
|
1535
|
+
}] : []), ...(timeRow ? [timeRow] : [])]];
|
|
1536
|
+
}
|
|
1537
|
+
const percent = getProductWalletDiscountPercent(asset);
|
|
1538
|
+
if (percent) {
|
|
1539
|
+
return [[{
|
|
1540
|
+
item: appendPercentToVoucherName(name, percent),
|
|
1541
|
+
value: formatVoucherDiscount(percent, params.locale)
|
|
1542
|
+
}, ...(timeRow ? [timeRow] : [])]];
|
|
1543
|
+
}
|
|
1544
|
+
const balance = getProductWalletMoneyBalance(asset);
|
|
1545
|
+
return [[{
|
|
1546
|
+
item: name,
|
|
1547
|
+
value: ''
|
|
1548
|
+
}, ...(balance ? [{
|
|
1549
|
+
item: label(params.locale, 'balance'),
|
|
1550
|
+
value: formatMoney(balance, params.currencySymbol)
|
|
1551
|
+
}] : []), ...(timeRow ? [timeRow] : [])]];
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
function collectProductWalletDiscountAssets(order) {
|
|
1555
|
+
const assets = [];
|
|
1556
|
+
const assetsById = new Map();
|
|
1557
|
+
normalizeArray(order.products).forEach(rawProduct => {
|
|
1558
|
+
if (!rawProduct || typeof rawProduct !== 'object') return;
|
|
1559
|
+
visitProductWalletDiscounts(restoreReceiptDiscountHierarchy(rawProduct), assets, assetsById);
|
|
1560
|
+
});
|
|
1561
|
+
return assets;
|
|
1562
|
+
}
|
|
1563
|
+
function visitProductWalletDiscounts(product, assets, assetsById) {
|
|
1564
|
+
normalizeArray(product.discount_list).forEach(record => {
|
|
1565
|
+
if (!record || typeof record !== 'object' || Array.isArray(record)) return;
|
|
1566
|
+
const type = getProductWalletDiscountType(record);
|
|
1567
|
+
if (!type) return;
|
|
1568
|
+
const id = getProductWalletDiscountAssetId(record);
|
|
1569
|
+
if (!id) return;
|
|
1570
|
+
const codes = getProductWalletDiscountCodes(record);
|
|
1571
|
+
const existing = assetsById.get(id);
|
|
1572
|
+
if (existing) {
|
|
1573
|
+
existing.records.push(record);
|
|
1574
|
+
existing.codes = Array.from(new Set([...existing.codes, ...codes]));
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1577
|
+
const asset = {
|
|
1578
|
+
id,
|
|
1579
|
+
type,
|
|
1580
|
+
codes,
|
|
1581
|
+
records: [record]
|
|
1582
|
+
};
|
|
1583
|
+
assetsById.set(id, asset);
|
|
1584
|
+
assets.push(asset);
|
|
1585
|
+
});
|
|
1586
|
+
for (const childKey of ['combinations', 'product_bundle', 'bundle']) {
|
|
1587
|
+
normalizeArray(product[childKey]).forEach(child => {
|
|
1588
|
+
if (!child || typeof child !== 'object' || Array.isArray(child)) return;
|
|
1589
|
+
visitProductWalletDiscounts(child, assets, assetsById);
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
function getProductWalletDiscountType(record) {
|
|
1594
|
+
for (const value of [record.tag, record.type, record.discount?.tag, record.discount?.type]) {
|
|
1595
|
+
const type = stringify(value).trim().toLowerCase();
|
|
1596
|
+
if (PRODUCT_WALLET_DISCOUNT_TYPES.has(type)) return type;
|
|
1597
|
+
}
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1600
|
+
function getProductWalletDiscountAssetId(record) {
|
|
1601
|
+
for (const value of [record.discount?.resource_id, record.resource_id, record.id, record.discount_id]) {
|
|
1602
|
+
const id = normalizeWalletPassId(value);
|
|
1603
|
+
if (id && id !== '0') return id;
|
|
1604
|
+
}
|
|
1605
|
+
return null;
|
|
1606
|
+
}
|
|
1607
|
+
function getProductWalletDiscountCodes(record) {
|
|
1608
|
+
return normalizeWalletAssetCodes(record.code, record.encoded, record.discount?.code, record.discount?.encoded);
|
|
1609
|
+
}
|
|
1610
|
+
function getProductWalletDiscountName(asset, locale) {
|
|
1611
|
+
for (const record of asset.records) {
|
|
1612
|
+
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]) {
|
|
1613
|
+
const name = getLocalizedValue(value, locale).trim();
|
|
1614
|
+
if (name) return name;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
return asset.codes[0] || label(locale, 'giftCard');
|
|
1618
|
+
}
|
|
1619
|
+
function getProductWalletDiscountTime(asset) {
|
|
1620
|
+
for (const field of ['updated_at', 'created_at', 'collection_time']) {
|
|
1621
|
+
for (const record of asset.records) {
|
|
1622
|
+
const value = firstPresentValue(record[field], record.discount?.[field]);
|
|
1623
|
+
if (isPresentId(value)) return value;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
return undefined;
|
|
1627
|
+
}
|
|
1628
|
+
function getProductEntitlementBalance(asset) {
|
|
1629
|
+
for (const record of asset.records) {
|
|
1630
|
+
const balance = getProductDiscountEntitlementBalance(record);
|
|
1631
|
+
if (balance) return balance;
|
|
1632
|
+
}
|
|
1633
|
+
return null;
|
|
1634
|
+
}
|
|
1635
|
+
function getProductDiscountEntitlementBalance(record) {
|
|
1636
|
+
return getEntitlementBalance(record.metadata?.balance, record.balance, record.discount?.metadata?.balance, record.discount?.balance);
|
|
1637
|
+
}
|
|
1638
|
+
function getEntitlementBalance(...values) {
|
|
1639
|
+
for (const value of values) {
|
|
1640
|
+
if (!isNumericAmount(value)) continue;
|
|
1641
|
+
const balance = new _decimal.default(value);
|
|
1642
|
+
if (balance.isFinite() && balance.isInteger() && balance.gte(0)) {
|
|
1643
|
+
return balance;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
return null;
|
|
1647
|
+
}
|
|
1648
|
+
function formatEntitlementUses(value, locale) {
|
|
1649
|
+
const count = value.toFixed(0);
|
|
1650
|
+
switch (normalizeLocaleKey(locale)) {
|
|
1651
|
+
case 'zh_CN':
|
|
1652
|
+
case 'zh_HK':
|
|
1653
|
+
return `${count}次`;
|
|
1654
|
+
case 'ja':
|
|
1655
|
+
return `${count}回`;
|
|
1656
|
+
case 'pt':
|
|
1657
|
+
return `${count} ${value.eq(1) ? 'utilização' : 'utilizações'}`;
|
|
1658
|
+
default:
|
|
1659
|
+
return `${count} ${value.eq(1) ? 'use' : 'uses'}`;
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
function getProductWalletDiscountPercent(asset) {
|
|
1663
|
+
if (asset.type !== 'discount_card' && asset.type !== 'product_discount_card') return null;
|
|
1664
|
+
for (const record of asset.records) {
|
|
1665
|
+
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();
|
|
1666
|
+
if (discountType && discountType !== 'percent' && discountType !== 'percentage') continue;
|
|
1667
|
+
const candidates = [record.percent, record.discount?.percent];
|
|
1668
|
+
if (discountType === 'percent' || discountType === 'percentage') {
|
|
1669
|
+
candidates.push(record.par_value, record.discount?.par_value);
|
|
1670
|
+
}
|
|
1671
|
+
for (const value of candidates) {
|
|
1672
|
+
if (!isNumericAmount(value)) continue;
|
|
1673
|
+
const percent = new _decimal.default(value);
|
|
1674
|
+
if (percent.isFinite() && percent.gt(0) && percent.lte(100)) {
|
|
1675
|
+
return percent;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
return null;
|
|
1680
|
+
}
|
|
1681
|
+
function getProductWalletMoneyBalance(asset) {
|
|
1682
|
+
for (const record of asset.records) {
|
|
1683
|
+
for (const value of [record.metadata?.balance, record.balance, record.discount?.metadata?.balance, record.discount?.balance]) {
|
|
1684
|
+
if (!isNumericAmount(value)) continue;
|
|
1685
|
+
return _decimal.default.max(new _decimal.default(value), 0);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
for (const record of asset.records) {
|
|
1689
|
+
const parValue = firstPresentValue(record.par_value, record.discount?.par_value);
|
|
1690
|
+
const usedParValue = firstPresentValue(record.used_par_value, record.discount?.used_par_value);
|
|
1691
|
+
if (!isNumericAmount(parValue) || !isNumericAmount(usedParValue)) continue;
|
|
1692
|
+
return _decimal.default.max(new _decimal.default(parValue).minus(new _decimal.default(usedParValue)), 0);
|
|
1693
|
+
}
|
|
1694
|
+
return null;
|
|
1695
|
+
}
|
|
1696
|
+
function buildPointCardSnapshotAssets(params) {
|
|
1697
|
+
const snapshots = params.order.order_meta_list?.point_cards_snapshot;
|
|
1698
|
+
if (!Array.isArray(snapshots)) return [];
|
|
1699
|
+
const snapshotTime = firstPresentValue(params.order.updated_at, params.order.created_at);
|
|
1700
|
+
return snapshots.flatMap(snapshot => {
|
|
1701
|
+
if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) return [];
|
|
1702
|
+
const pointCard = snapshot;
|
|
1703
|
+
if (stringify(pointCard.tag).trim().toLowerCase() !== 'point_card') return [];
|
|
1704
|
+
const assetId = normalizeWalletPassId(pointCard.id);
|
|
1705
|
+
if (!assetId || !claimWalletAssetIdentity(params.displayedWalletAssetIdentities, {
|
|
1706
|
+
id: assetId,
|
|
1707
|
+
codes: []
|
|
1708
|
+
})) return [];
|
|
1709
|
+
const rows = [{
|
|
1710
|
+
item: appendMaskedCardCode(stringify(pointCard.product_name).trim() || label(params.locale, 'pointCard'), pointCard.code),
|
|
1711
|
+
value: ''
|
|
1712
|
+
}];
|
|
1713
|
+
const remainingPoints = getPointCardSnapshotRemainingPoints(pointCard);
|
|
1714
|
+
if (remainingPoints) {
|
|
1715
|
+
rows.push({
|
|
1716
|
+
item: label(params.locale, 'remainingPoints'),
|
|
1717
|
+
value: trimDecimal(remainingPoints)
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
if (isPresentId(snapshotTime)) {
|
|
1721
|
+
rows.push({
|
|
1722
|
+
item: formatDateTime(snapshotTime, params.locale),
|
|
1723
|
+
value: ''
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
return [rows];
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
function collectVirtualCurrencySnapshots(order) {
|
|
1730
|
+
const items = [];
|
|
1731
|
+
const byId = new Map();
|
|
1732
|
+
const snapshots = order.order_meta_list?.virtual_currency_snapshot;
|
|
1733
|
+
if (!Array.isArray(snapshots)) return {
|
|
1734
|
+
items,
|
|
1735
|
+
byId
|
|
1736
|
+
};
|
|
1737
|
+
snapshots.forEach(snapshot => {
|
|
1738
|
+
if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) return;
|
|
1739
|
+
const virtualCurrency = snapshot;
|
|
1740
|
+
if (stringify(virtualCurrency.tag).trim().toLowerCase() !== 'virtual_currency') return;
|
|
1741
|
+
const id = normalizeWalletPassId(virtualCurrency.id);
|
|
1742
|
+
if (!id || byId.has(id)) return;
|
|
1743
|
+
byId.set(id, virtualCurrency);
|
|
1744
|
+
items.push(virtualCurrency);
|
|
1745
|
+
});
|
|
1746
|
+
return {
|
|
1747
|
+
items,
|
|
1748
|
+
byId
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
function findVirtualCurrencySnapshotForPayment(payment, snapshotsById) {
|
|
1752
|
+
const fundRecord = getFundRecord(payment);
|
|
1753
|
+
for (const value of [fundRecord?.voucher_id, payment.voucher_id]) {
|
|
1754
|
+
const id = normalizeWalletPassId(value);
|
|
1755
|
+
if (!id) continue;
|
|
1756
|
+
const snapshot = snapshotsById.get(id);
|
|
1757
|
+
if (snapshot) return snapshot;
|
|
1758
|
+
}
|
|
1759
|
+
return undefined;
|
|
1760
|
+
}
|
|
1761
|
+
function buildVirtualCurrencySnapshotAssets(params) {
|
|
1762
|
+
const snapshotTime = firstPresentValue(params.order.updated_at, params.order.created_at);
|
|
1763
|
+
return params.snapshots.flatMap(virtualCurrency => {
|
|
1764
|
+
const assetId = normalizeWalletPassId(virtualCurrency.id);
|
|
1765
|
+
if (!assetId || !claimWalletAssetIdentity(params.displayedWalletAssetIdentities, {
|
|
1766
|
+
id: assetId,
|
|
1767
|
+
codes: []
|
|
1768
|
+
})) return [];
|
|
1769
|
+
const rows = [{
|
|
1770
|
+
item: stringify(virtualCurrency.product_name).trim(),
|
|
1771
|
+
value: ''
|
|
1772
|
+
}];
|
|
1773
|
+
const balance = getVirtualCurrencySnapshotBalance(virtualCurrency);
|
|
1774
|
+
if (balance) {
|
|
1775
|
+
rows.push({
|
|
1776
|
+
item: label(params.locale, 'balance'),
|
|
1777
|
+
value: formatVirtualCurrencyBalance(balance, getVirtualCurrencySnapshotUnit(virtualCurrency, params.locale))
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1780
|
+
if (isPresentId(snapshotTime)) {
|
|
1781
|
+
rows.push({
|
|
1782
|
+
item: formatDateTime(snapshotTime, params.locale),
|
|
1783
|
+
value: ''
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
return [rows];
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
function getVirtualCurrencySnapshotBalance(snapshot) {
|
|
1790
|
+
if (!isNumericAmount(snapshot.par_value) || !isNumericAmount(snapshot.used_par_value)) {
|
|
1791
|
+
return null;
|
|
1792
|
+
}
|
|
1793
|
+
return _decimal.default.max(new _decimal.default(snapshot.par_value).minus(new _decimal.default(snapshot.used_par_value)), 0);
|
|
1794
|
+
}
|
|
1795
|
+
function getVirtualCurrencySnapshotUnit(snapshot, locale) {
|
|
1796
|
+
return getNonEmptyLocalizedValue(snapshot.name, locale);
|
|
1797
|
+
}
|
|
1798
|
+
function getNonEmptyLocalizedValue(value, locale) {
|
|
1799
|
+
if (value === undefined || value === null) return '';
|
|
1800
|
+
if (typeof value !== 'object' || Array.isArray(value)) return stringify(value).trim();
|
|
1801
|
+
const dashedLocale = locale.replace('_', '-');
|
|
1802
|
+
const underscoredLocale = locale.replace('-', '_');
|
|
1803
|
+
for (const candidate of [value[locale], value[dashedLocale], value[underscoredLocale], value.original, value.auto, value.default, value.en]) {
|
|
1804
|
+
const text = stringify(candidate).trim();
|
|
1805
|
+
if (text) return text;
|
|
1806
|
+
}
|
|
1807
|
+
return '';
|
|
1808
|
+
}
|
|
1809
|
+
function formatVirtualCurrencyBalance(value, unit) {
|
|
1810
|
+
const amount = trimDecimal(value);
|
|
1811
|
+
const normalizedUnit = unit.trim();
|
|
1812
|
+
return normalizedUnit ? `${amount} ${normalizedUnit}` : amount;
|
|
1813
|
+
}
|
|
1814
|
+
function normalizeWalletPassId(value) {
|
|
1815
|
+
if (typeof value === 'number') return Number.isFinite(value) ? String(value) : null;
|
|
1816
|
+
if (typeof value !== 'string') return null;
|
|
1817
|
+
return value.trim() || null;
|
|
1818
|
+
}
|
|
1819
|
+
function firstNormalizedWalletAssetId(...values) {
|
|
1820
|
+
for (const value of values) {
|
|
1821
|
+
const id = normalizeWalletPassId(value);
|
|
1822
|
+
if (id) return id;
|
|
1823
|
+
}
|
|
1824
|
+
return null;
|
|
1825
|
+
}
|
|
1826
|
+
function normalizeWalletAssetCodes(...values) {
|
|
1827
|
+
return Array.from(new Set(values.map(value => normalizeWalletPassId(value)).filter(value => !!value)));
|
|
1828
|
+
}
|
|
1829
|
+
function registerWalletAssetIdentity(identities, candidate) {
|
|
1830
|
+
if (!candidate.id && !candidate.codes.length) return;
|
|
1831
|
+
const existing = identities.find(identity => isSameWalletAssetIdentity(identity, candidate));
|
|
1832
|
+
if (existing) {
|
|
1833
|
+
mergeWalletAssetIdentity(existing, candidate);
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
identities.push({
|
|
1837
|
+
id: candidate.id,
|
|
1838
|
+
codes: [...candidate.codes]
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
function claimWalletAssetIdentity(identities, candidate) {
|
|
1842
|
+
if (!candidate.id && !candidate.codes.length) return true;
|
|
1843
|
+
const existing = identities.find(identity => isSameWalletAssetIdentity(identity, candidate));
|
|
1844
|
+
if (existing) {
|
|
1845
|
+
mergeWalletAssetIdentity(existing, candidate);
|
|
1846
|
+
return false;
|
|
1847
|
+
}
|
|
1848
|
+
identities.push({
|
|
1849
|
+
id: candidate.id,
|
|
1850
|
+
codes: [...candidate.codes]
|
|
1851
|
+
});
|
|
1852
|
+
return true;
|
|
1853
|
+
}
|
|
1854
|
+
function isSameWalletAssetIdentity(left, right) {
|
|
1855
|
+
if (left.id && right.id) return left.id === right.id;
|
|
1856
|
+
return left.codes.some(code => right.codes.includes(code));
|
|
1857
|
+
}
|
|
1858
|
+
function mergeWalletAssetIdentity(target, source) {
|
|
1859
|
+
if (!target.id && source.id) target.id = source.id;
|
|
1860
|
+
target.codes = Array.from(new Set([...target.codes, ...source.codes]));
|
|
1861
|
+
}
|
|
1862
|
+
function getPointCardSnapshotRemainingPoints(snapshot) {
|
|
1863
|
+
if (!isNumericAmount(snapshot.par_value) || !isNumericAmount(snapshot.used_par_value)) {
|
|
1864
|
+
return null;
|
|
1865
|
+
}
|
|
1866
|
+
return _decimal.default.max(new _decimal.default(snapshot.par_value).minus(new _decimal.default(snapshot.used_par_value)), 0);
|
|
1093
1867
|
}
|
|
1094
1868
|
function buildVoucherAssets(params) {
|
|
1095
|
-
const
|
|
1096
|
-
return normalizeArray(params.order.vouchers).flatMap(
|
|
1869
|
+
const fallbackAssetTime = firstPresentValue(params.order.updated_at, params.order.created_at);
|
|
1870
|
+
return normalizeArray(params.order.vouchers).flatMap(voucher => {
|
|
1097
1871
|
if (!voucher || typeof voucher !== 'object' || Array.isArray(voucher)) return [];
|
|
1098
|
-
|
|
1099
|
-
if (seenVoucherKeys.has(voucherKey)) return [];
|
|
1100
|
-
seenVoucherKeys.add(voucherKey);
|
|
1872
|
+
if (!claimWalletAssetIdentity(params.displayedWalletAssetIdentities, getVoucherAssetIdentity(voucher))) return [];
|
|
1101
1873
|
const name = getVoucherAssetName(voucher, params.locale);
|
|
1874
|
+
const assetTime = firstPresentValue(voucher.updated_at, voucher.created_at, voucher.collection_time, fallbackAssetTime);
|
|
1875
|
+
const timeRow = isPresentId(assetTime) ? {
|
|
1876
|
+
item: formatDateTime(assetTime, params.locale),
|
|
1877
|
+
value: ''
|
|
1878
|
+
} : null;
|
|
1879
|
+
if (isEntitlementVoucher(voucher)) {
|
|
1880
|
+
const remaining = getEntitlementBalance(voucher.metadata?.balance, voucher.balance);
|
|
1881
|
+
return [[{
|
|
1882
|
+
item: name,
|
|
1883
|
+
value: ''
|
|
1884
|
+
}, ...(remaining ? [{
|
|
1885
|
+
item: label(params.locale, 'entitlementRemaining'),
|
|
1886
|
+
value: formatEntitlementUses(remaining, params.locale)
|
|
1887
|
+
}] : []), ...(timeRow ? [timeRow] : [])]];
|
|
1888
|
+
}
|
|
1889
|
+
if (isTicketVoucher(voucher)) {
|
|
1890
|
+
const validity = getVoucherValidity(voucher, params.locale);
|
|
1891
|
+
return [[{
|
|
1892
|
+
item: name,
|
|
1893
|
+
value: ''
|
|
1894
|
+
}, ...(validity ? [{
|
|
1895
|
+
item: label(params.locale, 'validity'),
|
|
1896
|
+
value: validity
|
|
1897
|
+
}] : [])]];
|
|
1898
|
+
}
|
|
1102
1899
|
const percent = getVoucherDiscountPercent(voucher);
|
|
1103
1900
|
if (percent) {
|
|
1104
1901
|
return [[{
|
|
1105
1902
|
item: appendPercentToVoucherName(name, percent),
|
|
1106
1903
|
value: formatVoucherDiscount(percent, params.locale)
|
|
1107
|
-
}]];
|
|
1904
|
+
}, ...(timeRow ? [timeRow] : [])]];
|
|
1108
1905
|
}
|
|
1109
1906
|
const balance = getVoucherBalance(voucher);
|
|
1110
1907
|
return [[{
|
|
@@ -1113,12 +1910,92 @@ function buildVoucherAssets(params) {
|
|
|
1113
1910
|
}, {
|
|
1114
1911
|
item: label(params.locale, 'balance'),
|
|
1115
1912
|
value: formatMoney(balance, params.currencySymbol)
|
|
1116
|
-
}]];
|
|
1913
|
+
}, ...(timeRow ? [timeRow] : [])]];
|
|
1914
|
+
});
|
|
1915
|
+
}
|
|
1916
|
+
const TICKET_VOUCHER_TAGS = new Set(['ticket', 'product_package_ticket', 'appointment_ticket', 'session_ticket']);
|
|
1917
|
+
function isEntitlementVoucher(voucher) {
|
|
1918
|
+
return [voucher.tag, voucher.type].some(value => stringify(value).trim().toLowerCase() === 'entitlement_pass');
|
|
1919
|
+
}
|
|
1920
|
+
function isTicketVoucher(voucher) {
|
|
1921
|
+
return TICKET_VOUCHER_TAGS.has(stringify(voucher.tag).trim().toLowerCase());
|
|
1922
|
+
}
|
|
1923
|
+
function getVoucherValidity(voucher, locale) {
|
|
1924
|
+
const availableTime = firstPresentValue(voucher.available_time, voucher.avaliable_time, voucher.metadata?.available_time, voucher.metadata?.avaliable_time);
|
|
1925
|
+
const availableTimeText = formatVoucherAvailableTime(availableTime, locale);
|
|
1926
|
+
if (availableTimeText) return availableTimeText;
|
|
1927
|
+
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);
|
|
1928
|
+
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);
|
|
1929
|
+
return formatVoucherValidityRange(startDate, endDate, locale, {
|
|
1930
|
+
startTime: voucher.start_time ?? voucher.metadata?.start_time,
|
|
1931
|
+
endTime: voucher.end_time ?? voucher.metadata?.end_time
|
|
1117
1932
|
});
|
|
1118
1933
|
}
|
|
1119
|
-
function
|
|
1120
|
-
|
|
1121
|
-
|
|
1934
|
+
function formatVoucherAvailableTime(value, locale) {
|
|
1935
|
+
if (!isPresentId(value)) return '';
|
|
1936
|
+
if (Array.isArray(value)) {
|
|
1937
|
+
return value.map(item => formatVoucherAvailableTime(item, locale)).filter(Boolean).join(', ');
|
|
1938
|
+
}
|
|
1939
|
+
if (typeof value !== 'object') return stringify(value).trim();
|
|
1940
|
+
const start = firstPresentValue(value.start, value.start_at, value.start_date_time, value.start_date);
|
|
1941
|
+
const end = firstPresentValue(value.end, value.end_at, value.end_date_time, value.expire_time, value.expire_date, value.end_date);
|
|
1942
|
+
if (isPresentId(start) || isPresentId(end)) {
|
|
1943
|
+
return formatVoucherValidityRange(start, end, locale, {
|
|
1944
|
+
startTime: value.start_time,
|
|
1945
|
+
endTime: value.end_time
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
return getLocalizedValue(value, locale).trim();
|
|
1949
|
+
}
|
|
1950
|
+
function formatVoucherValidityRange(startValue, endValue, locale, times = {}) {
|
|
1951
|
+
const start = combineVoucherDateAndTime(startValue, times.startTime);
|
|
1952
|
+
const end = combineVoucherDateAndTime(endValue, times.endTime);
|
|
1953
|
+
const startPart = formatVoucherValidityDate(start, locale);
|
|
1954
|
+
const endPart = formatVoucherValidityDate(end, locale);
|
|
1955
|
+
if (startPart.text && endPart.text) {
|
|
1956
|
+
const parsedStart = (0, _dayjs.default)(start);
|
|
1957
|
+
const parsedEnd = (0, _dayjs.default)(end);
|
|
1958
|
+
const sameDay = parsedStart.isValid() && parsedEnd.isValid() && parsedStart.isSame(parsedEnd, 'day');
|
|
1959
|
+
if (sameDay && startPart.time && endPart.time) {
|
|
1960
|
+
return `${startPart.time} - ${endPart.time} ${startPart.date}`;
|
|
1961
|
+
}
|
|
1962
|
+
return `${startPart.text} - ${endPart.text}`;
|
|
1963
|
+
}
|
|
1964
|
+
return startPart.text || endPart.text;
|
|
1965
|
+
}
|
|
1966
|
+
function combineVoucherDateAndTime(dateValue, timeValue) {
|
|
1967
|
+
if (!isPresentId(dateValue) || !isPresentId(timeValue)) return dateValue;
|
|
1968
|
+
const dateText = stringify(dateValue).trim();
|
|
1969
|
+
if (/\d{1,2}:\d{2}/.test(dateText)) return dateValue;
|
|
1970
|
+
return `${dateText} ${stringify(timeValue).trim()}`;
|
|
1971
|
+
}
|
|
1972
|
+
function formatVoucherValidityDate(value, locale) {
|
|
1973
|
+
if (!isPresentId(value)) return {
|
|
1974
|
+
date: '',
|
|
1975
|
+
time: '',
|
|
1976
|
+
text: ''
|
|
1977
|
+
};
|
|
1978
|
+
const source = stringify(value).trim();
|
|
1979
|
+
const parsed = (0, _dayjs.default)(source);
|
|
1980
|
+
if (!parsed.isValid()) return {
|
|
1981
|
+
date: '',
|
|
1982
|
+
time: '',
|
|
1983
|
+
text: source
|
|
1984
|
+
};
|
|
1985
|
+
const hasTime = /\d{1,2}:\d{2}/.test(source);
|
|
1986
|
+
const date = parsed.format(locale === 'en' ? 'DD/MM/YYYY' : 'YYYY/MM/DD');
|
|
1987
|
+
const time = hasTime ? parsed.format(/\d{1,2}:\d{2}:\d{2}/.test(source) ? 'HH:mm:ss' : 'HH:mm') : '';
|
|
1988
|
+
return {
|
|
1989
|
+
date,
|
|
1990
|
+
time,
|
|
1991
|
+
text: time ? `${time} ${date}` : date
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
function getVoucherAssetIdentity(voucher) {
|
|
1995
|
+
return {
|
|
1996
|
+
id: firstNormalizedWalletAssetId(voucher.voucher_id, voucher.id),
|
|
1997
|
+
codes: normalizeWalletAssetCodes(voucher.code, voucher.encoded)
|
|
1998
|
+
};
|
|
1122
1999
|
}
|
|
1123
2000
|
function getVoucherAssetName(voucher, locale) {
|
|
1124
2001
|
return getLocalizedValue(voucher.product_name || voucher.name || voucher.title, locale) || stringify(voucher.encoded || voucher.code || label(locale, 'giftCard'));
|
|
@@ -1144,6 +2021,10 @@ function formatVoucherDiscount(percent, locale) {
|
|
|
1144
2021
|
return normalizedLocale === 'en' ? `Discount ${trimDecimal(percent)}% OFF` : `${trimDecimal(percent)}% OFF`;
|
|
1145
2022
|
}
|
|
1146
2023
|
function getVoucherBalance(voucher) {
|
|
2024
|
+
for (const value of [voucher.metadata?.balance, voucher.balance]) {
|
|
2025
|
+
if (!isNumericAmount(value)) continue;
|
|
2026
|
+
return _decimal.default.max(new _decimal.default(value), 0);
|
|
2027
|
+
}
|
|
1147
2028
|
const parValue = new _decimal.default(toMoneyNumber(voucher.par_value));
|
|
1148
2029
|
const usedParValue = new _decimal.default(toMoneyNumber(voucher.used_par_value));
|
|
1149
2030
|
return _decimal.default.max(parValue.minus(usedParValue), 0);
|
|
@@ -1153,12 +2034,15 @@ function buildPaymentItems(params) {
|
|
|
1153
2034
|
payment,
|
|
1154
2035
|
locale,
|
|
1155
2036
|
currencySymbol,
|
|
1156
|
-
includeFundRecordBalance
|
|
2037
|
+
includeFundRecordBalance,
|
|
2038
|
+
includeFundRecordUsage,
|
|
2039
|
+
includePaymentAmount,
|
|
2040
|
+
virtualCurrencyUnit
|
|
1157
2041
|
} = params;
|
|
1158
2042
|
const serviceFee = getPaymentServiceFee(payment);
|
|
1159
2043
|
const items = [{
|
|
1160
2044
|
item: paymentMethodName(payment, locale),
|
|
1161
|
-
value: formatMoney(resolveReceiptPaymentAmount(payment, serviceFee), currencySymbol)
|
|
2045
|
+
value: includePaymentAmount ? formatMoney(resolveReceiptPaymentAmount(payment, serviceFee), currencySymbol) : ''
|
|
1162
2046
|
}];
|
|
1163
2047
|
items.push(...buildCashChangePaymentItems({
|
|
1164
2048
|
payment,
|
|
@@ -1178,7 +2062,9 @@ function buildPaymentItems(params) {
|
|
|
1178
2062
|
fundRecord,
|
|
1179
2063
|
locale,
|
|
1180
2064
|
currencySymbol,
|
|
1181
|
-
includeBalance: includeFundRecordBalance
|
|
2065
|
+
includeBalance: includeFundRecordBalance,
|
|
2066
|
+
includeUsage: includeFundRecordUsage,
|
|
2067
|
+
virtualCurrencyUnit
|
|
1182
2068
|
}));
|
|
1183
2069
|
} else {
|
|
1184
2070
|
const remainingAmount = payment.remaining_amount ?? payment.balance ?? payment.metadata?.remaining_amount;
|
|
@@ -1242,17 +2128,21 @@ function buildFundRecordPaymentItems(params) {
|
|
|
1242
2128
|
fundRecord,
|
|
1243
2129
|
locale,
|
|
1244
2130
|
currencySymbol,
|
|
1245
|
-
includeBalance
|
|
2131
|
+
includeBalance,
|
|
2132
|
+
includeUsage,
|
|
2133
|
+
virtualCurrencyUnit
|
|
1246
2134
|
} = params;
|
|
1247
2135
|
const items = [];
|
|
1248
2136
|
const usingBeforeValue = fundRecord.using_before_value;
|
|
1249
2137
|
const usingAfterValue = fundRecord.using_after_value;
|
|
1250
2138
|
if (isPointCardPayment(payment) && usingBeforeValue !== undefined && usingAfterValue !== undefined) {
|
|
1251
2139
|
const usedPoints = new _decimal.default(toMoneyNumber(usingBeforeValue)).minus(toMoneyNumber(usingAfterValue));
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
2140
|
+
if (includeUsage) {
|
|
2141
|
+
items.push({
|
|
2142
|
+
item: label(locale, 'redeemPoints'),
|
|
2143
|
+
value: trimDecimal(usedPoints)
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
1256
2146
|
if (includeBalance) {
|
|
1257
2147
|
items.push({
|
|
1258
2148
|
item: label(locale, 'remainingPoints'),
|
|
@@ -1262,8 +2152,16 @@ function buildFundRecordPaymentItems(params) {
|
|
|
1262
2152
|
return items;
|
|
1263
2153
|
}
|
|
1264
2154
|
if (includeBalance && usingAfterValue !== undefined && usingAfterValue !== null) {
|
|
2155
|
+
if (virtualCurrencyUnit !== undefined) {
|
|
2156
|
+
if (!isNumericAmount(usingAfterValue)) return items;
|
|
2157
|
+
items.push({
|
|
2158
|
+
item: label(locale, 'balance'),
|
|
2159
|
+
value: formatVirtualCurrencyBalance(new _decimal.default(usingAfterValue), virtualCurrencyUnit)
|
|
2160
|
+
});
|
|
2161
|
+
return items;
|
|
2162
|
+
}
|
|
1265
2163
|
items.push({
|
|
1266
|
-
item: label(locale, '
|
|
2164
|
+
item: label(locale, 'balance'),
|
|
1267
2165
|
value: formatMoney(usingAfterValue, currencySymbol)
|
|
1268
2166
|
});
|
|
1269
2167
|
}
|
|
@@ -1281,9 +2179,14 @@ function isPointCardPayment(payment) {
|
|
|
1281
2179
|
return code === 'POINTCARD' || type === 'point_card';
|
|
1282
2180
|
}
|
|
1283
2181
|
function maskMachineCode(code) {
|
|
1284
|
-
const text = stringify(code);
|
|
1285
|
-
if (text
|
|
1286
|
-
|
|
2182
|
+
const text = stringify(code).trim();
|
|
2183
|
+
if (!text) return '';
|
|
2184
|
+
if (text.length <= 6) return '*'.repeat(text.length);
|
|
2185
|
+
return `${text.slice(0, 2)}****${text.slice(-4)}`;
|
|
2186
|
+
}
|
|
2187
|
+
function appendMaskedCardCode(name, code) {
|
|
2188
|
+
const maskedCode = maskMachineCode(code);
|
|
2189
|
+
return maskedCode ? `${name}(${maskedCode})` : name;
|
|
1287
2190
|
}
|
|
1288
2191
|
function trimDecimal(value) {
|
|
1289
2192
|
return value.toDecimalPlaces(2).toFixed(2).replace(/\.?0+$/, '');
|
|
@@ -1292,16 +2195,13 @@ function getFundRecordPaymentName(payment, locale) {
|
|
|
1292
2195
|
const fundRecord = getFundRecord(payment);
|
|
1293
2196
|
if (!fundRecord) return '';
|
|
1294
2197
|
const name = getLocalizedValue(fundRecord.wallet_pass_name, locale);
|
|
1295
|
-
|
|
1296
|
-
if (!name && !code) return '';
|
|
1297
|
-
if (!code) return name;
|
|
2198
|
+
if (!name && !maskMachineCode(fundRecord.code)) return '';
|
|
1298
2199
|
const fallbackName = getLocalizedValue(payment.name || payment.title || payment.payment_method_name || payment.method_name || payment.code, locale);
|
|
1299
|
-
return
|
|
2200
|
+
return appendMaskedCardCode(name || fallbackName, fundRecord.code);
|
|
1300
2201
|
}
|
|
1301
2202
|
function appendMachineCodeToName(name, payment) {
|
|
1302
2203
|
const fundRecord = getFundRecord(payment);
|
|
1303
|
-
|
|
1304
|
-
return `${name}(${maskMachineCode(fundRecord.code)})`;
|
|
2204
|
+
return appendMaskedCardCode(name, fundRecord?.code);
|
|
1305
2205
|
}
|
|
1306
2206
|
function paymentMethodName(payment, locale) {
|
|
1307
2207
|
const fundRecordName = getFundRecordPaymentName(payment, locale);
|
|
@@ -1338,25 +2238,8 @@ function getLocalizedValue(value, locale) {
|
|
|
1338
2238
|
const underscoredLocale = locale.replace('-', '_');
|
|
1339
2239
|
return stringify(value[locale] ?? value[dashedLocale] ?? value[underscoredLocale] ?? value.original ?? value.auto ?? value.default ?? value.en ?? '');
|
|
1340
2240
|
}
|
|
1341
|
-
function resolveServiceTypeLabelKey(serviceType
|
|
1342
|
-
|
|
1343
|
-
if (['', 'nil', 'none', 'unknown'].includes(normalized)) return null;
|
|
1344
|
-
if (['pickup', 'pick_up', 'self_pickup'].includes(normalized)) return 'pickUp';
|
|
1345
|
-
if (['delivery', 'shipping', 'local_delivery'].includes(normalized)) return 'delivery';
|
|
1346
|
-
if (['takeaway', 'take_away'].includes(normalized)) return 'takeAway';
|
|
1347
|
-
if (['dine_in', 'dinein'].includes(normalized)) return 'dineIn';
|
|
1348
|
-
if (['shop_service', 'shop_server'].includes(normalized)) {
|
|
1349
|
-
return resolveShopServiceLabelKey(order);
|
|
1350
|
-
}
|
|
1351
|
-
return null;
|
|
1352
|
-
}
|
|
1353
|
-
function resolveShopServiceLabelKey(order) {
|
|
1354
|
-
const rawShopServiceType = firstPresentValue(order.metadata?.shop_service_type?.server_type, order.shop_service_type?.server_type, typeof order.shop_service_type === 'string' ? order.shop_service_type : undefined);
|
|
1355
|
-
if (!isPresentId(rawShopServiceType)) return 'dineIn';
|
|
1356
|
-
const normalized = stringify(rawShopServiceType).trim().toLowerCase().replace(/[\s-]+/g, '_');
|
|
1357
|
-
if (['takeaway', 'take_away'].includes(normalized)) return 'takeAway';
|
|
1358
|
-
if (['dine_in', 'dinein'].includes(normalized)) return 'dineIn';
|
|
1359
|
-
return null;
|
|
2241
|
+
function resolveServiceTypeLabelKey(serviceType) {
|
|
2242
|
+
return SERVICE_TYPE_LABEL_KEYS[serviceType] || null;
|
|
1360
2243
|
}
|
|
1361
2244
|
function normalizeBooleanFlag(value) {
|
|
1362
2245
|
if (value === true || value === 1 || stringify(value).trim() === '1') return 1;
|
|
@@ -1377,6 +2260,11 @@ function formatMoney(value, currencySymbol) {
|
|
|
1377
2260
|
const amount = new _decimal.default(toMoneyNumber(value));
|
|
1378
2261
|
return `${currencySymbol}${amount.toFixed(2)}`;
|
|
1379
2262
|
}
|
|
2263
|
+
function formatCalculatedMoney(value, currencySymbol) {
|
|
2264
|
+
const amount = new _decimal.default(toMoneyNumber(value));
|
|
2265
|
+
const sign = amount.isNegative() ? '-' : '';
|
|
2266
|
+
return `${sign}${currencySymbol}${amount.abs().toFixed(2)}`;
|
|
2267
|
+
}
|
|
1380
2268
|
function formatNegativeMoney(value, currencySymbol) {
|
|
1381
2269
|
const amount = new _decimal.default(toMoneyNumber(value)).abs();
|
|
1382
2270
|
return `-${currencySymbol}${amount.toFixed(2)}`;
|