tripstorekrabi-shared 1.22.2 → 1.22.4
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/package.json +4 -3
- package/price.js +47 -24
- package/telegram.js +57 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tripstorekrabi-shared",
|
|
3
|
-
"version": "1.22.
|
|
3
|
+
"version": "1.22.4",
|
|
4
4
|
"description": "Shared library for Trip Store Krabi",
|
|
5
5
|
"author": "Stefan Sommer <tonprince@gmail.com>",
|
|
6
6
|
"repository": {
|
|
@@ -13,11 +13,12 @@
|
|
|
13
13
|
"./availability": "./availability.js",
|
|
14
14
|
"./booking": "./booking.js",
|
|
15
15
|
"./product": "./product.js",
|
|
16
|
-
"./ota": "./ota.js"
|
|
16
|
+
"./ota": "./ota.js",
|
|
17
|
+
"./telegram": "./telegram.js"
|
|
17
18
|
},
|
|
18
19
|
"dependencies": {
|
|
19
20
|
"dayjs": "^1.11.19",
|
|
20
|
-
"libphonenumber-js": "^1.12.
|
|
21
|
+
"libphonenumber-js": "^1.12.31",
|
|
21
22
|
"nanoid": "^5.1.6"
|
|
22
23
|
},
|
|
23
24
|
"scripts": {
|
package/price.js
CHANGED
|
@@ -39,7 +39,7 @@ export function calculateProductOperatorPrice(booking, pricing) {
|
|
|
39
39
|
return booking.operatorPaymentStatus === "No Charge" ? 0 : calculateProductPrice(booking, pricing, { mode: OPERATOR_MODE });
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
function calculateSellingPrice(commissionRate, price) {
|
|
42
|
+
function calculateSellingPrice(commissionRate = 0, price) {
|
|
43
43
|
return Math.floor(
|
|
44
44
|
(price * 100) /
|
|
45
45
|
(100 -
|
|
@@ -47,40 +47,65 @@ function calculateSellingPrice(commissionRate, price) {
|
|
|
47
47
|
);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
function applyPromotionDiscount(price, config) {
|
|
51
|
+
if (!config || !config.inflatedPrices) {
|
|
52
|
+
return price;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const discountRate = Number(config.discount) || 0;
|
|
56
|
+
|
|
57
|
+
if (!isFinite(discountRate)) return price;
|
|
58
|
+
const denominator = 100 - discountRate;
|
|
59
|
+
|
|
60
|
+
if (denominator <= 0) {
|
|
61
|
+
console.warn("Invalid discount rate for inflation calculation:", discountRate);
|
|
62
|
+
return price;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const finalPrice = (price * 100) / denominator;
|
|
66
|
+
return Math.ceil(finalPrice * 100) / 100;
|
|
67
|
+
}
|
|
68
|
+
|
|
50
69
|
function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE }) {
|
|
51
70
|
if (pricing.promotion) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
71
|
+
const departureDate = booking.departureDate instanceof Date ? dayjs(booking.departureDate) : typeof booking.departureDate?.toDate === 'function' ? dayjs(booking.departureDate.toDate()) : null;
|
|
72
|
+
const startDate = pricing.promotion.startDate instanceof Date ? pricing.promotion.startDate : typeof pricing.promotion.startDate.toDate === 'function' ? pricing.promotion.startDate.toDate() : null;
|
|
73
|
+
const endDate = pricing.promotion.endDate instanceof Date ? pricing.promotion.endDate : typeof pricing.promotion.endDate.toDate === 'function' ? pricing.promotion.endDate.toDate() : null;
|
|
55
74
|
|
|
56
75
|
if (departureDate?.isBetween(startDate, endDate, "day", "[]")) {
|
|
57
76
|
pricing = { ...pricing, volumes: pricing.promotion.volumes };
|
|
58
77
|
}
|
|
59
78
|
}
|
|
60
79
|
|
|
61
|
-
|
|
80
|
+
const type = pricing.type;
|
|
62
81
|
let total = 0;
|
|
63
82
|
|
|
64
83
|
switch (type) {
|
|
65
84
|
case "person": {
|
|
66
|
-
|
|
85
|
+
const volume = findVolume(booking, pricing);
|
|
67
86
|
if (!volume) {
|
|
68
87
|
console.error(`No volume price found for the total amount of ${booking.adults + booking.children} people`);
|
|
69
88
|
return undefined;
|
|
70
89
|
} else {
|
|
71
|
-
|
|
90
|
+
const bookingSourceKey = getBookingSourceKey(booking.otaPartnerRef);
|
|
72
91
|
|
|
73
92
|
if (volume[bookingSourceKey]) {
|
|
74
|
-
|
|
93
|
+
const customPricing = volume[bookingSourceKey];
|
|
75
94
|
|
|
76
95
|
if (options.mode === SELLING_MODE) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
96
|
+
const commissionRate = pricing.commissionRates?.[bookingSourceKey];
|
|
97
|
+
const baseNetPriceAdult = customPricing.adult.partnerNetPrice;
|
|
98
|
+
const baseRetailPriceAdult = calculateSellingPrice(commissionRate, baseNetPriceAdult);
|
|
99
|
+
const finalPriceAdult = applyPromotionDiscount(baseRetailPriceAdult, customPricing);
|
|
100
|
+
|
|
101
|
+
let finalPriceChild = 0;
|
|
102
|
+
if (pricing.ageBands.child.active && volume.child && booking.children) {
|
|
103
|
+
const baseNetPriceChild = customPricing.child.partnerNetPrice;
|
|
104
|
+
const baseRetailPriceChild = calculateSellingPrice(commissionRate, baseNetPriceChild);
|
|
105
|
+
finalPriceChild = applyPromotionDiscount(baseRetailPriceChild, customPricing);
|
|
83
106
|
}
|
|
107
|
+
|
|
108
|
+
total = booking.adults * finalPriceAdult + (booking.children ? booking.children * finalPriceChild : 0);
|
|
84
109
|
} else if (options.mode === PARTNER_MODE) {
|
|
85
110
|
total = booking.adults * customPricing.adult.partnerNetPrice + (pricing.ageBands.child.active && volume.child && booking.children ? booking.children * customPricing.child.partnerNetPrice : 0);
|
|
86
111
|
} else if (options.mode === OPERATOR_MODE) {
|
|
@@ -97,24 +122,22 @@ function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE
|
|
|
97
122
|
}
|
|
98
123
|
}
|
|
99
124
|
case "group": {
|
|
100
|
-
|
|
125
|
+
const volume = findVolume(booking, pricing);
|
|
101
126
|
if (!volume) {
|
|
102
127
|
console.error(`No volume price found for the total amount of ${booking.adults + booking.children} people`);
|
|
103
128
|
return undefined;
|
|
104
129
|
} else {
|
|
105
|
-
|
|
130
|
+
const bookingSourceKey = getBookingSourceKey(booking.otaPartnerRef);
|
|
106
131
|
|
|
107
132
|
if (volume[bookingSourceKey]) {
|
|
108
|
-
|
|
133
|
+
const customPricing = volume[bookingSourceKey];
|
|
109
134
|
|
|
110
135
|
if (options.mode === SELLING_MODE) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
total = customPricing.retailPrice;
|
|
117
|
-
}
|
|
136
|
+
const commissionRate = pricing.commissionRates?.[bookingSourceKey];
|
|
137
|
+
const baseNetPrice = customPricing.partnerNetPrice;
|
|
138
|
+
const baseRetailPrice = calculateSellingPrice(commissionRate, baseNetPrice);
|
|
139
|
+
const finalPrice = applyPromotionDiscount(baseRetailPrice, customPricing);
|
|
140
|
+
total = finalPrice;
|
|
118
141
|
} else if (options.mode === PARTNER_MODE) {
|
|
119
142
|
total = customPricing.partnerNetPrice
|
|
120
143
|
} else if (options.mode === OPERATOR_MODE) {
|
package/telegram.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const ENV_BOT_TOKEN = process.env.TELEGRAM_API_TOKEN;
|
|
2
|
+
const ENV_CHAT_ID = process.env.TELEGRAM_CHAT_ID;
|
|
3
|
+
const TELEGRAM_MAX_CHARS = 4096;
|
|
4
|
+
|
|
5
|
+
export async function sendTelegramMessage(message, options = {}) {
|
|
6
|
+
const {
|
|
7
|
+
botToken,
|
|
8
|
+
chatId,
|
|
9
|
+
parseMode = 'HTML',
|
|
10
|
+
prefix = '',
|
|
11
|
+
truncate = true
|
|
12
|
+
} = options;
|
|
13
|
+
|
|
14
|
+
const BOT_TOKEN = botToken || ENV_BOT_TOKEN;
|
|
15
|
+
const CHAT_ID = chatId || ENV_CHAT_ID;
|
|
16
|
+
|
|
17
|
+
if (!BOT_TOKEN || !CHAT_ID) {
|
|
18
|
+
console.error("FATAL: Telegram secrets are not configured in options or the environment.");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let finalMessage = `${prefix} ${message}`.trim();
|
|
23
|
+
|
|
24
|
+
if (truncate && finalMessage.length > TELEGRAM_MAX_CHARS) {
|
|
25
|
+
const note = "\n\n... [Message Truncated]";
|
|
26
|
+
const usableLength = TELEGRAM_MAX_CHARS - note.length;
|
|
27
|
+
finalMessage = finalMessage.substring(0, usableLength) + note;
|
|
28
|
+
console.warn(`Message truncated from ${message.length} to ${TELEGRAM_MAX_CHARS} characters.`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const telegramApiUrl = `https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`;
|
|
33
|
+
const body = {
|
|
34
|
+
chat_id: CHAT_ID,
|
|
35
|
+
text: finalMessage,
|
|
36
|
+
parse_mode: parseMode.toUpperCase(),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const res = await fetch(telegramApiUrl, {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
headers: {
|
|
42
|
+
'Content-Type': 'application/json'
|
|
43
|
+
},
|
|
44
|
+
body: JSON.stringify(body)
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
const errorBody = await res.text();
|
|
49
|
+
console.error(`Failed to send Telegram message: HTTP Status ${res.status} ${res.statusText}`);
|
|
50
|
+
console.error('Telegram API Detailed Error:', errorBody);
|
|
51
|
+
} else {
|
|
52
|
+
console.log('Telegram message sent successfully.');
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.error('An error occurred while trying to send a Telegram message:', e);
|
|
56
|
+
}
|
|
57
|
+
}
|