tripstorekrabi-shared 1.23.6 → 1.23.8
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 -4
- package/price.js +83 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tripstorekrabi-shared",
|
|
3
|
-
"version": "1.23.
|
|
3
|
+
"version": "1.23.8",
|
|
4
4
|
"description": "Shared library for Trip Store Krabi",
|
|
5
5
|
"author": "Stefan Sommer <tonprince@gmail.com>",
|
|
6
6
|
"repository": {
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"./telegram": "./telegram.js"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"dayjs": "^1.11.
|
|
21
|
-
"libphonenumber-js": "^1.12.
|
|
22
|
-
"nanoid": "^5.1.
|
|
20
|
+
"dayjs": "^1.11.20",
|
|
21
|
+
"libphonenumber-js": "^1.12.41",
|
|
22
|
+
"nanoid": "^5.1.7"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"deploy": "npm publish"
|
package/price.js
CHANGED
|
@@ -27,6 +27,45 @@ const SELLING_MODE = 0;
|
|
|
27
27
|
const PARTNER_MODE = 1;
|
|
28
28
|
const OPERATOR_MODE = 2;
|
|
29
29
|
|
|
30
|
+
export function getActiveRevision(booking, pricing) {
|
|
31
|
+
if (!pricing?.revisions || pricing.revisions.length === 0) return null;
|
|
32
|
+
|
|
33
|
+
const getSafeDate = (dateField) => {
|
|
34
|
+
if (!dateField) return null;
|
|
35
|
+
if (typeof dateField.toDate === 'function') return dayjs(dateField.toDate());
|
|
36
|
+
return dayjs(dateField);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const bookingDate = getSafeDate(booking.date)?.startOf('day');
|
|
40
|
+
if (!bookingDate || !bookingDate.isValid()) return null;
|
|
41
|
+
|
|
42
|
+
const candidateRevisions = pricing.revisions.filter((rev) => {
|
|
43
|
+
if (rev.isActive === false) return false;
|
|
44
|
+
|
|
45
|
+
const revStart = getSafeDate(rev.startDate)?.startOf('day');
|
|
46
|
+
if (!revStart || !revStart.isValid()) return false;
|
|
47
|
+
|
|
48
|
+
return revStart.isSame(bookingDate) || revStart.isBefore(bookingDate);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (candidateRevisions.length === 0) return null;
|
|
52
|
+
|
|
53
|
+
return candidateRevisions.sort((a, b) => {
|
|
54
|
+
const aTime = getSafeDate(a.startDate)?.valueOf() || 0;
|
|
55
|
+
const bTime = getSafeDate(b.startDate)?.valueOf() || 0;
|
|
56
|
+
return bTime - aTime;
|
|
57
|
+
})[0];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function getActivePricing(booking, pricing) {
|
|
61
|
+
const activeRevision = getActiveRevision(booking, pricing);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
...pricing,
|
|
65
|
+
...activeRevision.pricing
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
30
69
|
export function calculateProductSellingPrice(booking, pricing) {
|
|
31
70
|
return booking.customerPaymentStatus === "Refunded" ? 0 : calculateProductPrice(booking, pricing, { mode: SELLING_MODE });
|
|
32
71
|
}
|
|
@@ -70,22 +109,35 @@ export function calculateInflatedSellingPrice(price, config, forceInflation = fa
|
|
|
70
109
|
}
|
|
71
110
|
|
|
72
111
|
function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE }) {
|
|
73
|
-
|
|
112
|
+
const activeRevision = getActiveRevision(booking, pricing);
|
|
113
|
+
|
|
114
|
+
if (!activeRevision) {
|
|
115
|
+
throw new Error("No active pricing revision found for this booking date.");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let activeVolumes = activeRevision.volumes;
|
|
119
|
+
|
|
120
|
+
if (activeRevision.promotion) {
|
|
74
121
|
const departureDate = booking.departureDate instanceof Date ? dayjs(booking.departureDate) : typeof booking.departureDate?.toDate === 'function' ? dayjs(booking.departureDate.toDate()) : null;
|
|
75
|
-
const startDate =
|
|
76
|
-
const endDate =
|
|
122
|
+
const startDate = activeRevision.promotion.startDate instanceof Date ? activeRevision.promotion.startDate : typeof activeRevision.promotion.startDate.toDate === 'function' ? activeRevision.promotion.startDate.toDate() : null;
|
|
123
|
+
const endDate = activeRevision.promotion.endDate instanceof Date ? activeRevision.promotion.endDate : typeof activeRevision.promotion.endDate.toDate === 'function' ? activeRevision.promotion.endDate.toDate() : null;
|
|
77
124
|
|
|
78
|
-
if (departureDate?.isBetween(startDate, endDate, "day", "[]")) {
|
|
79
|
-
|
|
125
|
+
if (departureDate?.isValid() && departureDate?.isBetween(dayjs(startDate), dayjs(endDate), "day", "[]")) {
|
|
126
|
+
activeVolumes = activeRevision.promotion.volumes;
|
|
80
127
|
}
|
|
81
128
|
}
|
|
82
129
|
|
|
83
|
-
const
|
|
130
|
+
const context = {
|
|
131
|
+
...pricing,
|
|
132
|
+
volumes: activeVolumes
|
|
133
|
+
};
|
|
134
|
+
|
|
84
135
|
let total = 0;
|
|
136
|
+
const type = context.type;
|
|
85
137
|
|
|
86
138
|
switch (type) {
|
|
87
139
|
case "person": {
|
|
88
|
-
const volume = findVolume(booking,
|
|
140
|
+
const volume = findVolume(booking, context);
|
|
89
141
|
if (!volume) {
|
|
90
142
|
console.error(`No volume price found for the total amount of ${booking.adults + booking.children} people`);
|
|
91
143
|
return undefined;
|
|
@@ -96,13 +148,13 @@ function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE
|
|
|
96
148
|
const customPricing = volume[bookingSourceKey];
|
|
97
149
|
|
|
98
150
|
if (options.mode === SELLING_MODE) {
|
|
99
|
-
const commissionRate =
|
|
151
|
+
const commissionRate = context.commissionRates?.[bookingSourceKey];
|
|
100
152
|
const baseNetPriceAdult = customPricing.adult.partnerNetPrice;
|
|
101
153
|
const baseRetailPriceAdult = calculateSellingPrice(commissionRate, baseNetPriceAdult);
|
|
102
154
|
const finalPriceAdult = calculateInflatedSellingPrice(baseRetailPriceAdult, customPricing);
|
|
103
155
|
|
|
104
156
|
let finalPriceChild = 0;
|
|
105
|
-
if (
|
|
157
|
+
if (context.ageBands.child.active && volume.child && booking.children) {
|
|
106
158
|
const baseNetPriceChild = customPricing.child.partnerNetPrice;
|
|
107
159
|
const baseRetailPriceChild = calculateSellingPrice(commissionRate, baseNetPriceChild);
|
|
108
160
|
finalPriceChild = calculateInflatedSellingPrice(baseRetailPriceChild, customPricing);
|
|
@@ -110,22 +162,22 @@ function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE
|
|
|
110
162
|
|
|
111
163
|
total = booking.adults * finalPriceAdult + (booking.children ? booking.children * finalPriceChild : 0);
|
|
112
164
|
} else if (options.mode === PARTNER_MODE) {
|
|
113
|
-
total = booking.adults * customPricing.adult.partnerNetPrice + (
|
|
165
|
+
total = booking.adults * customPricing.adult.partnerNetPrice + (context.ageBands.child.active && volume.child && booking.children ? booking.children * customPricing.child.partnerNetPrice : 0);
|
|
114
166
|
} else if (options.mode === OPERATOR_MODE) {
|
|
115
|
-
total = booking.adults * (customPricing.adult.operatorNetPrice ?? volume.adult.operatorNetPrice) + (
|
|
167
|
+
total = booking.adults * (customPricing.adult.operatorNetPrice ?? volume.adult.operatorNetPrice) + (context.ageBands.child.active && volume.child && booking.children ? (booking.children * (customPricing.child.operatorNetPrice ?? volume.child.operatorNetPrice)) : 0);
|
|
116
168
|
}
|
|
117
169
|
} else {
|
|
118
170
|
if (options.mode === SELLING_MODE || options.mode === PARTNER_MODE) {
|
|
119
|
-
total = booking.adults * volume.adult.sellingPrice + (
|
|
171
|
+
total = booking.adults * volume.adult.sellingPrice + (context.ageBands.child.active && volume.child && booking.children ? booking.children * volume.child.sellingPrice : 0);
|
|
120
172
|
} else if (options.mode === OPERATOR_MODE) {
|
|
121
|
-
total = booking.adults * volume.adult.operatorNetPrice + (
|
|
173
|
+
total = booking.adults * volume.adult.operatorNetPrice + (context.ageBands.child.active && volume.child && booking.children ? booking.children * volume.child.operatorNetPrice : 0);
|
|
122
174
|
}
|
|
123
175
|
}
|
|
124
176
|
break;
|
|
125
177
|
}
|
|
126
178
|
}
|
|
127
179
|
case "group": {
|
|
128
|
-
const volume = findVolume(booking,
|
|
180
|
+
const volume = findVolume(booking, context);
|
|
129
181
|
if (!volume) {
|
|
130
182
|
console.error(`No volume price found for the total amount of ${booking.adults + booking.children} people`);
|
|
131
183
|
return undefined;
|
|
@@ -136,7 +188,7 @@ function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE
|
|
|
136
188
|
const customPricing = volume[bookingSourceKey];
|
|
137
189
|
|
|
138
190
|
if (options.mode === SELLING_MODE) {
|
|
139
|
-
const commissionRate =
|
|
191
|
+
const commissionRate = context.commissionRates?.[bookingSourceKey];
|
|
140
192
|
const baseNetPrice = customPricing.partnerNetPrice;
|
|
141
193
|
const baseRetailPrice = calculateSellingPrice(commissionRate, baseNetPrice);
|
|
142
194
|
const finalPrice = calculateInflatedSellingPrice(baseRetailPrice, customPricing);
|
|
@@ -164,25 +216,34 @@ function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE
|
|
|
164
216
|
return total;
|
|
165
217
|
}
|
|
166
218
|
|
|
167
|
-
export function findVolume(people,
|
|
219
|
+
export function findVolume(people, context) {
|
|
168
220
|
let total = (people.adults ?? 0) + (people.children ?? 0);
|
|
169
|
-
|
|
221
|
+
|
|
222
|
+
if (!context?.volumes || context.volumes.length === 0) return undefined;
|
|
223
|
+
|
|
224
|
+
const sortedVolumes = [...context.volumes].sort((v1, v2) => v1.max - v2.max);
|
|
225
|
+
|
|
226
|
+
return sortedVolumes.find((item, i) => {
|
|
170
227
|
if (total <= item.max) {
|
|
171
228
|
return true;
|
|
172
|
-
}
|
|
229
|
+
}
|
|
230
|
+
else if (i === sortedVolumes.length - 1) {
|
|
173
231
|
return true;
|
|
174
232
|
}
|
|
233
|
+
return false;
|
|
175
234
|
});
|
|
176
235
|
}
|
|
177
236
|
|
|
178
237
|
export function countMinPeople(pricing) {
|
|
179
|
-
|
|
180
|
-
|
|
238
|
+
const volumes = pricing?.volumes?.map((item) => item.min) || [];
|
|
239
|
+
if (volumes.length === 0) return 1;
|
|
240
|
+
return Math.max(Math.min(...volumes), 1);
|
|
181
241
|
}
|
|
182
242
|
|
|
183
243
|
export function countMaxPeople(pricing) {
|
|
184
|
-
|
|
185
|
-
|
|
244
|
+
const volumes = pricing?.volumes?.map((item) => item.max) || [];
|
|
245
|
+
if (volumes.length === 0) return MAX_PAX;
|
|
246
|
+
return Math.max(...volumes);
|
|
186
247
|
}
|
|
187
248
|
|
|
188
249
|
export function getTotalAmountToBeReceived(booking, options = {}) {
|