tripstorekrabi-shared 1.23.7 → 1.23.9
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 +81 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tripstorekrabi-shared",
|
|
3
|
-
"version": "1.23.
|
|
3
|
+
"version": "1.23.9",
|
|
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,49 @@ 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
|
+
if (!activeRevision) {
|
|
64
|
+
return pricing;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
...pricing,
|
|
69
|
+
...activeRevision
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
30
73
|
export function calculateProductSellingPrice(booking, pricing) {
|
|
31
74
|
return booking.customerPaymentStatus === "Refunded" ? 0 : calculateProductPrice(booking, pricing, { mode: SELLING_MODE });
|
|
32
75
|
}
|
|
@@ -70,22 +113,35 @@ export function calculateInflatedSellingPrice(price, config, forceInflation = fa
|
|
|
70
113
|
}
|
|
71
114
|
|
|
72
115
|
function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE }) {
|
|
73
|
-
|
|
116
|
+
const activeRevision = getActiveRevision(booking, pricing);
|
|
117
|
+
|
|
118
|
+
if (!activeRevision) {
|
|
119
|
+
throw new Error("No active pricing revision found for this booking date.");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let activeVolumes = activeRevision.volumes;
|
|
123
|
+
|
|
124
|
+
if (activeRevision.promotion) {
|
|
74
125
|
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 =
|
|
126
|
+
const startDate = activeRevision.promotion.startDate instanceof Date ? activeRevision.promotion.startDate : typeof activeRevision.promotion.startDate.toDate === 'function' ? activeRevision.promotion.startDate.toDate() : null;
|
|
127
|
+
const endDate = activeRevision.promotion.endDate instanceof Date ? activeRevision.promotion.endDate : typeof activeRevision.promotion.endDate.toDate === 'function' ? activeRevision.promotion.endDate.toDate() : null;
|
|
77
128
|
|
|
78
|
-
if (departureDate?.isBetween(startDate, endDate, "day", "[]")) {
|
|
79
|
-
|
|
129
|
+
if (departureDate?.isValid() && departureDate?.isBetween(dayjs(startDate), dayjs(endDate), "day", "[]")) {
|
|
130
|
+
activeVolumes = activeRevision.promotion.volumes;
|
|
80
131
|
}
|
|
81
132
|
}
|
|
82
133
|
|
|
83
|
-
const
|
|
134
|
+
const context = {
|
|
135
|
+
...pricing,
|
|
136
|
+
volumes: activeVolumes
|
|
137
|
+
};
|
|
138
|
+
|
|
84
139
|
let total = 0;
|
|
140
|
+
const type = context.type;
|
|
85
141
|
|
|
86
142
|
switch (type) {
|
|
87
143
|
case "person": {
|
|
88
|
-
const volume = findVolume(booking,
|
|
144
|
+
const volume = findVolume(booking, context);
|
|
89
145
|
if (!volume) {
|
|
90
146
|
console.error(`No volume price found for the total amount of ${booking.adults + booking.children} people`);
|
|
91
147
|
return undefined;
|
|
@@ -96,13 +152,13 @@ function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE
|
|
|
96
152
|
const customPricing = volume[bookingSourceKey];
|
|
97
153
|
|
|
98
154
|
if (options.mode === SELLING_MODE) {
|
|
99
|
-
const commissionRate =
|
|
155
|
+
const commissionRate = context.commissionRates?.[bookingSourceKey];
|
|
100
156
|
const baseNetPriceAdult = customPricing.adult.partnerNetPrice;
|
|
101
157
|
const baseRetailPriceAdult = calculateSellingPrice(commissionRate, baseNetPriceAdult);
|
|
102
158
|
const finalPriceAdult = calculateInflatedSellingPrice(baseRetailPriceAdult, customPricing);
|
|
103
159
|
|
|
104
160
|
let finalPriceChild = 0;
|
|
105
|
-
if (
|
|
161
|
+
if (context.ageBands.child.active && volume.child && booking.children) {
|
|
106
162
|
const baseNetPriceChild = customPricing.child.partnerNetPrice;
|
|
107
163
|
const baseRetailPriceChild = calculateSellingPrice(commissionRate, baseNetPriceChild);
|
|
108
164
|
finalPriceChild = calculateInflatedSellingPrice(baseRetailPriceChild, customPricing);
|
|
@@ -110,22 +166,22 @@ function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE
|
|
|
110
166
|
|
|
111
167
|
total = booking.adults * finalPriceAdult + (booking.children ? booking.children * finalPriceChild : 0);
|
|
112
168
|
} else if (options.mode === PARTNER_MODE) {
|
|
113
|
-
total = booking.adults * customPricing.adult.partnerNetPrice + (
|
|
169
|
+
total = booking.adults * customPricing.adult.partnerNetPrice + (context.ageBands.child.active && volume.child && booking.children ? booking.children * customPricing.child.partnerNetPrice : 0);
|
|
114
170
|
} else if (options.mode === OPERATOR_MODE) {
|
|
115
|
-
total = booking.adults * (customPricing.adult.operatorNetPrice ?? volume.adult.operatorNetPrice) + (
|
|
171
|
+
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
172
|
}
|
|
117
173
|
} else {
|
|
118
174
|
if (options.mode === SELLING_MODE || options.mode === PARTNER_MODE) {
|
|
119
|
-
total = booking.adults * volume.adult.sellingPrice + (
|
|
175
|
+
total = booking.adults * volume.adult.sellingPrice + (context.ageBands.child.active && volume.child && booking.children ? booking.children * volume.child.sellingPrice : 0);
|
|
120
176
|
} else if (options.mode === OPERATOR_MODE) {
|
|
121
|
-
total = booking.adults * volume.adult.operatorNetPrice + (
|
|
177
|
+
total = booking.adults * volume.adult.operatorNetPrice + (context.ageBands.child.active && volume.child && booking.children ? booking.children * volume.child.operatorNetPrice : 0);
|
|
122
178
|
}
|
|
123
179
|
}
|
|
124
180
|
break;
|
|
125
181
|
}
|
|
126
182
|
}
|
|
127
183
|
case "group": {
|
|
128
|
-
const volume = findVolume(booking,
|
|
184
|
+
const volume = findVolume(booking, context);
|
|
129
185
|
if (!volume) {
|
|
130
186
|
console.error(`No volume price found for the total amount of ${booking.adults + booking.children} people`);
|
|
131
187
|
return undefined;
|
|
@@ -136,7 +192,7 @@ function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE
|
|
|
136
192
|
const customPricing = volume[bookingSourceKey];
|
|
137
193
|
|
|
138
194
|
if (options.mode === SELLING_MODE) {
|
|
139
|
-
const commissionRate =
|
|
195
|
+
const commissionRate = context.commissionRates?.[bookingSourceKey];
|
|
140
196
|
const baseNetPrice = customPricing.partnerNetPrice;
|
|
141
197
|
const baseRetailPrice = calculateSellingPrice(commissionRate, baseNetPrice);
|
|
142
198
|
const finalPrice = calculateInflatedSellingPrice(baseRetailPrice, customPricing);
|
|
@@ -164,14 +220,21 @@ function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE
|
|
|
164
220
|
return total;
|
|
165
221
|
}
|
|
166
222
|
|
|
167
|
-
export function findVolume(people,
|
|
223
|
+
export function findVolume(people, context) {
|
|
168
224
|
let total = (people.adults ?? 0) + (people.children ?? 0);
|
|
169
|
-
|
|
225
|
+
|
|
226
|
+
if (!context?.volumes || context.volumes.length === 0) return undefined;
|
|
227
|
+
|
|
228
|
+
const sortedVolumes = [...context.volumes].sort((v1, v2) => v1.max - v2.max);
|
|
229
|
+
|
|
230
|
+
return sortedVolumes.find((item, i) => {
|
|
170
231
|
if (total <= item.max) {
|
|
171
232
|
return true;
|
|
172
|
-
}
|
|
233
|
+
}
|
|
234
|
+
else if (i === sortedVolumes.length - 1) {
|
|
173
235
|
return true;
|
|
174
236
|
}
|
|
237
|
+
return false;
|
|
175
238
|
});
|
|
176
239
|
}
|
|
177
240
|
|