tripstorekrabi-shared 1.24.4 → 1.24.6
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/booking.js +102 -129
- package/package.json +4 -4
- package/price.js +167 -285
package/booking.js
CHANGED
|
@@ -2,7 +2,6 @@ import dayjs from 'dayjs';
|
|
|
2
2
|
import advancedFormat from 'dayjs/plugin/advancedFormat.js';
|
|
3
3
|
import utc from 'dayjs/plugin/utc.js';
|
|
4
4
|
import timezone from 'dayjs/plugin/timezone.js';
|
|
5
|
-
import { FieldValue, Timestamp } from 'firebase-admin/firestore';
|
|
6
5
|
import { PAY_ON_TOUR, calculateTotalOperatorPrice } from './price.js';
|
|
7
6
|
import { nanoid } from 'nanoid';
|
|
8
7
|
import { CHECK_IN_LATE, IN_HOUSE, isAirportDropOff, isAirportPickup, isHotelPickup, isTrainStation, KEEP_PRIVATE, NO_PICKUP_HOTEL_YET, NO_ROOM, STAY_NEARBY } from './utils.js';
|
|
@@ -28,148 +27,122 @@ export function addBookingFromInvoice(
|
|
|
28
27
|
) {
|
|
29
28
|
let bookingNumberDoc = firestore.collection('meta').doc('BOOKING_NUMBER');
|
|
30
29
|
|
|
31
|
-
return firestore.runTransaction((transaction) => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
let processed = 0;
|
|
35
|
-
|
|
36
|
-
for (let i = 0; i < invoice.items.length; i++) {
|
|
37
|
-
let item = invoice.items[i];
|
|
38
|
-
|
|
39
|
-
// process only product items
|
|
40
|
-
let hasProduct = item.productId?.length > 0;
|
|
41
|
-
let productSnapshot = null;
|
|
42
|
-
let productDescription;
|
|
43
|
-
if (hasProduct) {
|
|
44
|
-
productSnapshot = await firestore
|
|
45
|
-
.collection('products')
|
|
46
|
-
.doc(item.productId)
|
|
47
|
-
.get();
|
|
48
|
-
productDescription = productSnapshot.get('shortName');
|
|
49
|
-
} else {
|
|
50
|
-
productDescription = item.description;
|
|
51
|
-
}
|
|
30
|
+
return firestore.runTransaction(async (transaction) => {
|
|
31
|
+
const snapshot = await transaction.get(bookingNumberDoc);
|
|
32
|
+
const currentLast = snapshot.get('last') ?? 0;
|
|
52
33
|
|
|
53
|
-
|
|
34
|
+
const productSnapshots = {};
|
|
35
|
+
for (const item of invoice.items) {
|
|
36
|
+
if (item.productId?.length > 0 && !productSnapshots[item.productId]) {
|
|
37
|
+
const productRef = firestore.collection('products').doc(item.productId);
|
|
38
|
+
productSnapshots[item.productId] = await transaction.get(productRef);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
54
41
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
description: comment.trim(),
|
|
58
|
-
visibility: [],
|
|
59
|
-
lastModified: dayjs().toDate(),
|
|
60
|
-
lastModifiedBy: 'Trip Store Krabi Next',
|
|
61
|
-
};
|
|
62
|
-
}
|
|
42
|
+
let nextBookingNumber = currentLast + 1;
|
|
43
|
+
let processed = 0;
|
|
63
44
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
};
|
|
71
|
-
}
|
|
45
|
+
for (let i = 0; i < invoice.items.length; i++) {
|
|
46
|
+
let item = invoice.items[i];
|
|
47
|
+
let hasProduct = item.productId?.length > 0;
|
|
48
|
+
|
|
49
|
+
let productSnapshot = hasProduct ? productSnapshots[item.productId] : null;
|
|
50
|
+
let productDescription = hasProduct ? productSnapshot.get('shortName') : item.description;
|
|
72
51
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
: null,
|
|
80
|
-
operator: productSnapshot?.get('operator.default.name') ?? '',
|
|
81
|
-
departureDate: Timestamp.fromDate(
|
|
82
|
-
dayjs(item.departureDate).toDate()
|
|
83
|
-
),
|
|
84
|
-
schedule: item.schedule ?? '',
|
|
85
|
-
pickupTime: '?',
|
|
86
|
-
adults: item.adults ?? 0,
|
|
87
|
-
children: item.children ?? 0,
|
|
88
|
-
infants: item.infants ?? 0,
|
|
89
|
-
leadTraveller: invoice.name,
|
|
90
|
-
email: invoice.email ?? '',
|
|
91
|
-
phoneNumber: invoice.phoneNumber ?? '',
|
|
92
|
-
cc: '',
|
|
93
|
-
customerBookingStatus: 'Pending',
|
|
94
|
-
operatorBookingStatus: 'Pending',
|
|
95
|
-
sentToOperator: false,
|
|
96
|
-
startingPointDescription: item.startingPointDescription ?? "",
|
|
97
|
-
pickupHotel: item.pickupHotel ?? null,
|
|
98
|
-
roomNumber: item.roomNumber ?? '',
|
|
99
|
-
googleMapsLocation: item.googleMapsLocation ?? '',
|
|
100
|
-
dietary: item.dietary ?? "",
|
|
101
|
-
flight: item.flight ?? null,
|
|
102
|
-
train: item.train ?? null,
|
|
103
|
-
customerPaymentStatus: 'Paid',
|
|
104
|
-
operatorPaymentStatus: 'Pending',
|
|
105
|
-
extraPayments: {},
|
|
106
|
-
addOns: item.addOns ?? null,
|
|
107
|
-
labels: ['NEW'],
|
|
108
|
-
paypalInvoiceId: '',
|
|
109
|
-
paypalTransactionId: '',
|
|
110
|
-
omiseChargeId: '',
|
|
111
|
-
invoiceNumber: '',
|
|
112
|
-
date: FieldValue.serverTimestamp(),
|
|
113
|
-
createdBy: 'Trip Store Krabi Next',
|
|
52
|
+
let notes = {};
|
|
53
|
+
|
|
54
|
+
if (comment) {
|
|
55
|
+
notes[nanoid()] = {
|
|
56
|
+
description: comment.trim(),
|
|
57
|
+
visibility: [],
|
|
114
58
|
lastModified: dayjs().toDate(),
|
|
115
59
|
lastModifiedBy: 'Trip Store Krabi Next',
|
|
116
60
|
};
|
|
61
|
+
}
|
|
117
62
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (i > 0) {
|
|
127
|
-
data.partnerNetPrice = 0;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (item.nationalParkFee) {
|
|
131
|
-
data.nationalParkFee = item.nationalParkFee;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (item.tourGuideLanguage) {
|
|
135
|
-
data.tourGuideLanguage = item.tourGuideLanguage;
|
|
136
|
-
}
|
|
63
|
+
if (invoice.comment) {
|
|
64
|
+
notes[nanoid()] = {
|
|
65
|
+
description: invoice.comment.trim(),
|
|
66
|
+
visibility: [],
|
|
67
|
+
lastModified: dayjs().toDate(),
|
|
68
|
+
lastModifiedBy: 'Trip Store Krabi Next',
|
|
69
|
+
};
|
|
70
|
+
}
|
|
137
71
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
72
|
+
let data = {
|
|
73
|
+
date: dayjs().toDate(),
|
|
74
|
+
departureDate: dayjs(item.departureDate).toDate(),
|
|
75
|
+
referenceNumber: 'TSK' + (nextBookingNumber + processed),
|
|
76
|
+
notes,
|
|
77
|
+
productDescription,
|
|
78
|
+
productRef: hasProduct ? firestore.collection('products').doc(item.productId) : null,
|
|
79
|
+
operator: productSnapshot?.get('operator.default.name') ?? '',
|
|
80
|
+
schedule: item.schedule ?? '',
|
|
81
|
+
pickupTime: '?',
|
|
82
|
+
adults: item.adults ?? 0,
|
|
83
|
+
children: item.children ?? 0,
|
|
84
|
+
infants: item.infants ?? 0,
|
|
85
|
+
leadTraveller: invoice.name,
|
|
86
|
+
email: invoice.email ?? '',
|
|
87
|
+
phoneNumber: invoice.phoneNumber ?? '',
|
|
88
|
+
cc: '',
|
|
89
|
+
customerBookingStatus: 'Pending',
|
|
90
|
+
operatorBookingStatus: 'Pending',
|
|
91
|
+
sentToOperator: false,
|
|
92
|
+
startingPointDescription: item.startingPointDescription ?? "",
|
|
93
|
+
pickupHotel: item.pickupHotel ?? null,
|
|
94
|
+
roomNumber: item.roomNumber ?? '',
|
|
95
|
+
googleMapsLocation: item.googleMapsLocation ?? '',
|
|
96
|
+
dietary: item.dietary ?? "",
|
|
97
|
+
flight: item.flight ?? null,
|
|
98
|
+
train: item.train ?? null,
|
|
99
|
+
customerPaymentStatus: 'Paid',
|
|
100
|
+
operatorPaymentStatus: 'Pending',
|
|
101
|
+
extraPayments: {},
|
|
102
|
+
addOns: item.addOns ?? null,
|
|
103
|
+
labels: ['NEW'],
|
|
104
|
+
paypalInvoiceId: '',
|
|
105
|
+
paypalTransactionId: '',
|
|
106
|
+
omiseChargeId: '',
|
|
107
|
+
invoiceNumber: '',
|
|
108
|
+
createdBy: 'Trip Store Krabi Next',
|
|
109
|
+
lastModified: dayjs().toDate(),
|
|
110
|
+
lastModifiedBy: 'Trip Store Krabi Next',
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
if (item.startingPointId) data.startingPointId = item.startingPointId;
|
|
114
|
+
if (item.dropoffHotel && item.dropoffHotel.length > 0) data.dropoffHotel = item.dropoffHotel;
|
|
115
|
+
if (i > 0) data.partnerNetPrice = 0;
|
|
116
|
+
if (item.nationalParkFee) data.nationalParkFee = item.nationalParkFee;
|
|
117
|
+
if (item.tourGuideLanguage) data.tourGuideLanguage = item.tourGuideLanguage;
|
|
118
|
+
|
|
119
|
+
let endPointId = item.endPointId;
|
|
120
|
+
if (!endPointId) {
|
|
121
|
+
let endPoints = productSnapshot?.get('endPoints');
|
|
122
|
+
if (endPoints && endPoints.length === 1) {
|
|
123
|
+
endPointId = endPoints[0].id;
|
|
147
124
|
}
|
|
125
|
+
}
|
|
126
|
+
if (endPointId) data.endPointId = endPointId;
|
|
148
127
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
} else {
|
|
155
|
-
data.operatorNetPrice = 0;
|
|
156
|
-
}
|
|
128
|
+
if (productSnapshot) {
|
|
129
|
+
data.operatorNetPrice = calculateTotalOperatorPrice(data, productSnapshot.data());
|
|
130
|
+
} else {
|
|
131
|
+
data.operatorNetPrice = 0;
|
|
132
|
+
}
|
|
157
133
|
|
|
158
|
-
|
|
134
|
+
data = { ...data, ...paymentData };
|
|
159
135
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
136
|
+
let doc = firestore.collection('bookings').doc();
|
|
137
|
+
transaction.set(doc, data);
|
|
138
|
+
processed++;
|
|
139
|
+
}
|
|
164
140
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
{ merge: true }
|
|
171
|
-
);
|
|
172
|
-
});
|
|
141
|
+
transaction.set(
|
|
142
|
+
bookingNumberDoc,
|
|
143
|
+
{ last: currentLast + processed },
|
|
144
|
+
{ merge: true }
|
|
145
|
+
);
|
|
173
146
|
});
|
|
174
147
|
}
|
|
175
148
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tripstorekrabi-shared",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.6",
|
|
4
4
|
"description": "Shared library for Trip Store Krabi",
|
|
5
5
|
"author": "Stefan Sommer <tonprince@gmail.com>",
|
|
6
6
|
"repository": {
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"dayjs": "^1.11.21",
|
|
21
|
-
"libphonenumber-js": "^1.13.
|
|
22
|
-
"nanoid": "^5.1.
|
|
23
|
-
"firebase-admin": "^14.
|
|
21
|
+
"libphonenumber-js": "^1.13.7",
|
|
22
|
+
"nanoid": "^5.1.16",
|
|
23
|
+
"firebase-admin": "^14.1.0"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"deploy": "npm publish"
|
package/price.js
CHANGED
|
@@ -15,67 +15,102 @@ export const PAX_EXTRA_PAYMENT = 'PAX';
|
|
|
15
15
|
export const ADD_ON_EXTRA_PAYMENT = 'ADD-ON';
|
|
16
16
|
export const PRODUCT_CHANGE_EXTRA_PAYMENT = 'PRODUCT CHANGE';
|
|
17
17
|
export const OTHER_EXTRA_PAYMENT = 'OTHER';
|
|
18
|
-
export const EXTRA_PAYMENT_CATEGORIES = [
|
|
18
|
+
export const EXTRA_PAYMENT_CATEGORIES = [
|
|
19
|
+
STARTING_POINT_EXTRA_PAYMENT,
|
|
19
20
|
END_POINT_EXTRA_PAYMENT,
|
|
20
21
|
NATIONAL_PARK_FEE_EXTRA_PAYMENT,
|
|
21
22
|
PAX_EXTRA_PAYMENT,
|
|
22
23
|
ADD_ON_EXTRA_PAYMENT,
|
|
23
24
|
PRODUCT_CHANGE_EXTRA_PAYMENT,
|
|
24
|
-
OTHER_EXTRA_PAYMENT
|
|
25
|
+
OTHER_EXTRA_PAYMENT
|
|
26
|
+
];
|
|
25
27
|
|
|
26
28
|
const SELLING_MODE = 0;
|
|
27
29
|
const PARTNER_MODE = 1;
|
|
28
30
|
const OPERATOR_MODE = 2;
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
if (!
|
|
32
|
+
function parseSafeDate(dateField) {
|
|
33
|
+
if (!dateField) return null;
|
|
34
|
+
if (typeof dateField.toDate === 'function') return dayjs(dateField.toDate());
|
|
35
|
+
if (dateField instanceof Date) return dayjs(dateField);
|
|
36
|
+
const parsed = dayjs(dateField);
|
|
37
|
+
return parsed.isValid() ? parsed : null;
|
|
38
|
+
}
|
|
32
39
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (typeof dateField.toDate === 'function') return dayjs(dateField.toDate());
|
|
36
|
-
return dayjs(dateField);
|
|
37
|
-
};
|
|
40
|
+
export function resolvePricingContext(pricing, bookingDate = dayjs(), departureDate = null) {
|
|
41
|
+
if (!pricing) return null;
|
|
38
42
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
.sort((a, b) => getSafeDate(a.startDate)?.valueOf() - getSafeDate(b.startDate)?.valueOf());
|
|
43
|
+
const safeBookingDate = parseSafeDate(bookingDate)?.startOf('day') || dayjs().startOf('day');
|
|
44
|
+
let activeRevision = pricing;
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
if (pricing.revisions?.length > 0) {
|
|
47
|
+
const validRevisions = pricing.revisions
|
|
48
|
+
.filter(r => r.isActive !== false)
|
|
49
|
+
.map(r => ({ ...r, parsedDate: parseSafeDate(r.startDate)?.startOf('day') }))
|
|
50
|
+
.filter(r => r.parsedDate && (r.parsedDate.isSame(safeBookingDate) || r.parsedDate.isBefore(safeBookingDate)))
|
|
51
|
+
.sort((a, b) => b.parsedDate.valueOf() - a.parsedDate.valueOf());
|
|
44
52
|
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
if (validRevisions.length > 0) {
|
|
54
|
+
activeRevision = { ...pricing, ...validRevisions[0] };
|
|
55
|
+
} else {
|
|
56
|
+
const sortedAll = [...pricing.revisions]
|
|
57
|
+
.filter(r => r.isActive !== false)
|
|
58
|
+
.sort((a, b) => (parseSafeDate(a.startDate)?.valueOf() || 0) - (parseSafeDate(b.startDate)?.valueOf() || 0));
|
|
59
|
+
activeRevision = { ...pricing, ...(sortedAll[0] || pricing.revisions[0]) };
|
|
60
|
+
}
|
|
47
61
|
}
|
|
48
62
|
|
|
49
|
-
|
|
50
|
-
if (rev.isActive === false) return false;
|
|
51
|
-
const revStart = getSafeDate(rev.startDate)?.startOf('day');
|
|
52
|
-
if (!revStart || !revStart.isValid()) return false;
|
|
63
|
+
let activeVolumes = activeRevision.volumes || [];
|
|
53
64
|
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
if (activeRevision.promotion && departureDate) {
|
|
66
|
+
const depDate = parseSafeDate(departureDate);
|
|
67
|
+
const promoStart = parseSafeDate(activeRevision.promotion.startDate);
|
|
68
|
+
const promoEnd = parseSafeDate(activeRevision.promotion.endDate);
|
|
56
69
|
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
if (depDate?.isValid() && promoStart?.isValid() && promoEnd?.isValid() &&
|
|
71
|
+
depDate.isBetween(promoStart, promoEnd, "day", "[]")) {
|
|
72
|
+
activeVolumes = activeRevision.promotion.volumes || activeVolumes;
|
|
73
|
+
}
|
|
59
74
|
}
|
|
60
75
|
|
|
61
|
-
return
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
76
|
+
return {
|
|
77
|
+
...activeRevision,
|
|
78
|
+
volumes: activeVolumes,
|
|
79
|
+
_isResolved: true
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getActiveRevision(date, pricing) {
|
|
84
|
+
return resolvePricingContext(pricing, date, null);
|
|
66
85
|
}
|
|
67
86
|
|
|
68
87
|
export function getActivePricing(date, pricing) {
|
|
69
|
-
|
|
88
|
+
return resolvePricingContext(pricing, date, null);
|
|
89
|
+
}
|
|
70
90
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
91
|
+
export function findVolume(people, context) {
|
|
92
|
+
const total = (people?.adults ?? 0) + (people?.children ?? 0);
|
|
74
93
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
const activeContext = context?._isResolved ? context : resolvePricingContext(context, people?.date, people?.departureDate);
|
|
95
|
+
const volumes = activeContext?.volumes;
|
|
96
|
+
|
|
97
|
+
if (!volumes || volumes.length === 0) return undefined;
|
|
98
|
+
|
|
99
|
+
const sortedVolumes = [...volumes].sort((v1, v2) => v1.max - v2.max);
|
|
100
|
+
|
|
101
|
+
return sortedVolumes.find(v => total <= v.max) || sortedVolumes[sortedVolumes.length - 1];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function countMinPeople(pricing, bookingDate = dayjs(), departureDate = null) {
|
|
105
|
+
const context = resolvePricingContext(pricing, bookingDate, departureDate);
|
|
106
|
+
const minVals = context?.volumes?.map(v => v.min ?? 1) || [];
|
|
107
|
+
return minVals.length > 0 ? Math.max(Math.min(...minVals), 1) : 1;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function countMaxPeople(pricing, bookingDate = dayjs(), departureDate = null) {
|
|
111
|
+
const context = resolvePricingContext(pricing, bookingDate, departureDate);
|
|
112
|
+
const maxVals = context?.volumes?.map(v => v.max ?? MAX_PAX) || [];
|
|
113
|
+
return maxVals.length > 0 ? Math.max(...maxVals) : MAX_PAX;
|
|
79
114
|
}
|
|
80
115
|
|
|
81
116
|
export function calculateProductSellingPrice(booking, pricing) {
|
|
@@ -91,26 +126,16 @@ export function calculateProductOperatorPrice(booking, pricing) {
|
|
|
91
126
|
}
|
|
92
127
|
|
|
93
128
|
function calculateSellingPrice(commissionRate = 0, price) {
|
|
94
|
-
return Math.floor(
|
|
95
|
-
(price * 100) /
|
|
96
|
-
(100 -
|
|
97
|
-
commissionRate)
|
|
98
|
-
);
|
|
129
|
+
return Math.floor((price * 100) / (100 - commissionRate));
|
|
99
130
|
}
|
|
100
131
|
|
|
101
132
|
export function calculateInflatedSellingPrice(price, config, forceInflation = false) {
|
|
102
|
-
if (!config || (!config.inflatedPrices && !forceInflation))
|
|
103
|
-
return price;
|
|
104
|
-
}
|
|
133
|
+
if (!config || (!config.inflatedPrices && !forceInflation)) return price;
|
|
105
134
|
|
|
106
135
|
const discountRate = Number(config.discount) || 0;
|
|
107
|
-
|
|
108
|
-
if (discountRate === 0 || !isFinite(discountRate)) {
|
|
109
|
-
return price;
|
|
110
|
-
}
|
|
136
|
+
if (discountRate === 0 || !isFinite(discountRate)) return price;
|
|
111
137
|
|
|
112
138
|
const denominator = 100 - discountRate;
|
|
113
|
-
|
|
114
139
|
if (denominator <= 0) {
|
|
115
140
|
console.warn("Invalid discount rate for inflation calculation:", discountRate);
|
|
116
141
|
return price;
|
|
@@ -121,163 +146,82 @@ export function calculateInflatedSellingPrice(price, config, forceInflation = fa
|
|
|
121
146
|
}
|
|
122
147
|
|
|
123
148
|
function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE }) {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
if (!activeRevision) {
|
|
127
|
-
throw new Error(`Pricing fail-safe triggered: No active or standard pricing found for booking date ${booking.date}.`);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
let activeVolumes = activeRevision.volumes || [];
|
|
149
|
+
const context = resolvePricingContext(pricing, booking.date, booking.departureDate);
|
|
131
150
|
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const endDate = activeRevision.promotion.endDate instanceof Date ? activeRevision.promotion.endDate : typeof activeRevision.promotion.endDate.toDate === 'function' ? activeRevision.promotion.endDate.toDate() : null;
|
|
136
|
-
|
|
137
|
-
if (departureDate?.isValid() && departureDate?.isBetween(dayjs(startDate), dayjs(endDate), "day", "[]")) {
|
|
138
|
-
activeVolumes = activeRevision.promotion.volumes || [];
|
|
139
|
-
}
|
|
151
|
+
if (!context || !context.volumes || context.volumes.length === 0) {
|
|
152
|
+
console.warn("Pricing fail-safe triggered: No valid context or volumes found.");
|
|
153
|
+
return 0;
|
|
140
154
|
}
|
|
141
155
|
|
|
142
|
-
|
|
143
|
-
|
|
156
|
+
const volume = findVolume(booking, context);
|
|
157
|
+
if (!volume) {
|
|
158
|
+
console.error(`No volume price found for ${booking.adults + booking.children} people`);
|
|
144
159
|
return 0;
|
|
145
160
|
}
|
|
146
161
|
|
|
147
|
-
const context = {
|
|
148
|
-
...pricing,
|
|
149
|
-
volumes: activeVolumes
|
|
150
|
-
};
|
|
151
|
-
|
|
152
162
|
let total = 0;
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (volume
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (options.mode === SELLING_MODE) {
|
|
168
|
-
const commissionRate = context.commissionRates?.[bookingSourceKey];
|
|
169
|
-
const baseNetPriceAdult = customPricing.adult.partnerNetPrice;
|
|
170
|
-
const baseRetailPriceAdult = calculateSellingPrice(commissionRate, baseNetPriceAdult);
|
|
171
|
-
const finalPriceAdult = calculateInflatedSellingPrice(baseRetailPriceAdult, customPricing);
|
|
172
|
-
|
|
173
|
-
let finalPriceChild = 0;
|
|
174
|
-
if (context.ageBands.child.active && volume.child && booking.children) {
|
|
175
|
-
const baseNetPriceChild = customPricing.child.partnerNetPrice;
|
|
176
|
-
const baseRetailPriceChild = calculateSellingPrice(commissionRate, baseNetPriceChild);
|
|
177
|
-
finalPriceChild = calculateInflatedSellingPrice(baseRetailPriceChild, customPricing);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
total = booking.adults * finalPriceAdult + (booking.children ? booking.children * finalPriceChild : 0);
|
|
181
|
-
} else if (options.mode === PARTNER_MODE) {
|
|
182
|
-
total = booking.adults * customPricing.adult.partnerNetPrice + (context.ageBands.child.active && volume.child && booking.children ? booking.children * customPricing.child.partnerNetPrice : 0);
|
|
183
|
-
} else if (options.mode === OPERATOR_MODE) {
|
|
184
|
-
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);
|
|
185
|
-
}
|
|
186
|
-
} else {
|
|
187
|
-
if (options.mode === SELLING_MODE || options.mode === PARTNER_MODE) {
|
|
188
|
-
total = booking.adults * volume.adult.sellingPrice + (context.ageBands.child.active && volume.child && booking.children ? booking.children * volume.child.sellingPrice : 0);
|
|
189
|
-
} else if (options.mode === OPERATOR_MODE) {
|
|
190
|
-
total = booking.adults * volume.adult.operatorNetPrice + (context.ageBands.child.active && volume.child && booking.children ? booking.children * volume.child.operatorNetPrice : 0);
|
|
191
|
-
}
|
|
163
|
+
const bookingSourceKey = getBookingSourceKey(booking.otaPartnerRef);
|
|
164
|
+
|
|
165
|
+
if (context.type === "person") {
|
|
166
|
+
if (volume[bookingSourceKey]) {
|
|
167
|
+
const customPricing = volume[bookingSourceKey];
|
|
168
|
+
|
|
169
|
+
if (options.mode === SELLING_MODE) {
|
|
170
|
+
const commRate = context.commissionRates?.[bookingSourceKey] || 0;
|
|
171
|
+
const finalPriceAdult = calculateInflatedSellingPrice(calculateSellingPrice(commRate, customPricing.adult.partnerNetPrice), customPricing);
|
|
172
|
+
|
|
173
|
+
let finalPriceChild = 0;
|
|
174
|
+
if (context.ageBands?.child?.active && volume.child && booking.children) {
|
|
175
|
+
finalPriceChild = calculateInflatedSellingPrice(calculateSellingPrice(commRate, customPricing.child.partnerNetPrice), customPricing);
|
|
192
176
|
}
|
|
193
|
-
|
|
177
|
+
total = (booking.adults * finalPriceAdult) + (booking.children ? booking.children * finalPriceChild : 0);
|
|
178
|
+
} else if (options.mode === PARTNER_MODE) {
|
|
179
|
+
total = (booking.adults * customPricing.adult.partnerNetPrice) + (context.ageBands?.child?.active && volume.child && booking.children ? booking.children * customPricing.child.partnerNetPrice : 0);
|
|
180
|
+
} else if (options.mode === OPERATOR_MODE) {
|
|
181
|
+
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);
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
if (options.mode === SELLING_MODE || options.mode === PARTNER_MODE) {
|
|
185
|
+
total = (booking.adults * volume.adult.sellingPrice) + (context.ageBands?.child?.active && volume.child && booking.children ? booking.children * volume.child.sellingPrice : 0);
|
|
186
|
+
} else if (options.mode === OPERATOR_MODE) {
|
|
187
|
+
total = (booking.adults * volume.adult.operatorNetPrice) + (context.ageBands?.child?.active && volume.child && booking.children ? booking.children * volume.child.operatorNetPrice : 0);
|
|
194
188
|
}
|
|
195
189
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
} else if (options.mode === PARTNER_MODE) {
|
|
214
|
-
total = customPricing.partnerNetPrice
|
|
215
|
-
} else if (options.mode === OPERATOR_MODE) {
|
|
216
|
-
total = customPricing.operatorNetPrice ?? volume.operatorNetPrice;
|
|
217
|
-
}
|
|
218
|
-
} else {
|
|
219
|
-
if (options.mode === SELLING_MODE || options.mode === PARTNER_MODE) {
|
|
220
|
-
total = volume.sellingPrice;
|
|
221
|
-
} else if (options.mode === OPERATOR_MODE) {
|
|
222
|
-
total = volume.operatorNetPrice;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
break;
|
|
190
|
+
} else if (context.type === "group") {
|
|
191
|
+
if (volume[bookingSourceKey]) {
|
|
192
|
+
const customPricing = volume[bookingSourceKey];
|
|
193
|
+
|
|
194
|
+
if (options.mode === SELLING_MODE) {
|
|
195
|
+
const commRate = context.commissionRates?.[bookingSourceKey] || 0;
|
|
196
|
+
total = calculateInflatedSellingPrice(calculateSellingPrice(commRate, customPricing.partnerNetPrice), customPricing);
|
|
197
|
+
} else if (options.mode === PARTNER_MODE) {
|
|
198
|
+
total = customPricing.partnerNetPrice;
|
|
199
|
+
} else if (options.mode === OPERATOR_MODE) {
|
|
200
|
+
total = customPricing.operatorNetPrice ?? volume.operatorNetPrice;
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
if (options.mode === SELLING_MODE || options.mode === PARTNER_MODE) {
|
|
204
|
+
total = volume.sellingPrice;
|
|
205
|
+
} else if (options.mode === OPERATOR_MODE) {
|
|
206
|
+
total = volume.operatorNetPrice;
|
|
227
207
|
}
|
|
228
208
|
}
|
|
229
|
-
|
|
230
|
-
|
|
209
|
+
} else {
|
|
210
|
+
throw new Error(`Pricing type ${context.type} is not supported`);
|
|
231
211
|
}
|
|
232
212
|
|
|
233
213
|
return total;
|
|
234
214
|
}
|
|
235
215
|
|
|
236
|
-
export function findVolume(people, context) {
|
|
237
|
-
let total = (people.adults ?? 0) + (people.children ?? 0);
|
|
238
|
-
|
|
239
|
-
if (!context?.volumes || context.volumes.length === 0) return undefined;
|
|
240
|
-
|
|
241
|
-
const sortedVolumes = [...context.volumes].sort((v1, v2) => v1.max - v2.max);
|
|
242
|
-
|
|
243
|
-
return sortedVolumes.find((item, i) => {
|
|
244
|
-
if (total <= item.max) {
|
|
245
|
-
return true;
|
|
246
|
-
}
|
|
247
|
-
else if (i === sortedVolumes.length - 1) {
|
|
248
|
-
return true;
|
|
249
|
-
}
|
|
250
|
-
return false;
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export function countMinPeople(pricing) {
|
|
255
|
-
const volumes = pricing?.volumes?.map((item) => item.min) || [];
|
|
256
|
-
if (volumes.length === 0) return 1;
|
|
257
|
-
return Math.max(Math.min(...volumes), 1);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
export function countMaxPeople(pricing) {
|
|
261
|
-
const volumes = pricing?.volumes?.map((item) => item.max) || [];
|
|
262
|
-
if (volumes.length === 0) return MAX_PAX;
|
|
263
|
-
return Math.max(...volumes);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
216
|
export function getTotalAmountToBeReceived(booking, options = {}) {
|
|
267
217
|
let filter = (item) => (!options.filter || options.filter(item)) && !item.disabled && item.paymentMethod !== PAY_ON_TOUR;
|
|
268
218
|
return booking.customerPaymentStatus === "Refunded" ? 0 : booking.partnerNetPrice + calculateExtraPayments(booking, { ...options, mode: SELLING_MODE, filter })
|
|
269
219
|
}
|
|
270
220
|
|
|
271
221
|
export function calculatePartnerNetPrice(booking, product) {
|
|
272
|
-
return getExtraPaymentsPaidWithPartner(
|
|
273
|
-
booking,
|
|
274
|
-
product
|
|
275
|
-
).reduce(
|
|
222
|
+
return getExtraPaymentsPaidWithPartner(booking, product).reduce(
|
|
276
223
|
(prev, curr) => (prev ?? 0) + curr.price,
|
|
277
|
-
calculateProductPartnerNetPrice(
|
|
278
|
-
booking,
|
|
279
|
-
product.pricing
|
|
280
|
-
)
|
|
224
|
+
calculateProductPartnerNetPrice(booking, product.pricing)
|
|
281
225
|
);
|
|
282
226
|
}
|
|
283
227
|
|
|
@@ -301,24 +245,15 @@ function calculateTotalPrice(booking, product, options = { mode: SELLING_MODE })
|
|
|
301
245
|
total += calculateExtraPayments(booking, {
|
|
302
246
|
...options,
|
|
303
247
|
filter: (item) => {
|
|
304
|
-
if (item.disabled === true)
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return true;
|
|
308
|
-
} else if (item.paymentMethod === PAY_BEFORE && item.category === OTHER_EXTRA_PAYMENT) {
|
|
309
|
-
return true;
|
|
310
|
-
}
|
|
248
|
+
if (item.disabled === true) return false;
|
|
249
|
+
if (item.paymentMethod === PAY_ON_TOUR) return true;
|
|
250
|
+
if (item.paymentMethod === PAY_BEFORE && item.category === OTHER_EXTRA_PAYMENT) return true;
|
|
311
251
|
},
|
|
312
252
|
getPrice: (item, options) => {
|
|
313
253
|
if (item.paymentMethod === PAY_ON_TOUR) {
|
|
314
|
-
|
|
315
|
-
return -getExtraPaymentPrice(item, options);
|
|
316
|
-
} else {
|
|
317
|
-
return 0;
|
|
318
|
-
}
|
|
319
|
-
} else {
|
|
320
|
-
return getExtraPaymentPrice(item, options);
|
|
254
|
+
return item.category !== OTHER_EXTRA_PAYMENT ? -getExtraPaymentPrice(item, options) : 0;
|
|
321
255
|
}
|
|
256
|
+
return getExtraPaymentPrice(item, options);
|
|
322
257
|
}
|
|
323
258
|
});
|
|
324
259
|
total += calculateAddOnsPrice(booking, product, options);
|
|
@@ -326,7 +261,9 @@ function calculateTotalPrice(booking, product, options = { mode: SELLING_MODE })
|
|
|
326
261
|
}
|
|
327
262
|
|
|
328
263
|
export function calculateNationalParkFee(booking, product) {
|
|
329
|
-
return booking.nationalParkFee === ALREADY_PAID && product.nationalPark
|
|
264
|
+
return booking.nationalParkFee === ALREADY_PAID && product.nationalPark
|
|
265
|
+
? (product.nationalPark.pricing.adult * booking.adults) + (product.nationalPark.pricing.child * booking.children)
|
|
266
|
+
: 0;
|
|
330
267
|
}
|
|
331
268
|
|
|
332
269
|
function getExtraPaymentPrice(item, options) {
|
|
@@ -350,19 +287,15 @@ export function calculateTotalExtraPaymentsOperatorPrice(booking) {
|
|
|
350
287
|
function calculateExtraPayments(booking, options = { mode: SELLING_MODE }) {
|
|
351
288
|
let filter = options.filter ?? ((item) => !item.disabled);
|
|
352
289
|
let getPrice = options.getPrice ?? getExtraPaymentPrice;
|
|
353
|
-
|
|
354
290
|
let total = 0;
|
|
355
|
-
for (const item of Object.values(booking.extraPayments).filter(filter)) {
|
|
356
|
-
total += getPrice(item, options);
|
|
357
291
|
|
|
292
|
+
for (const item of Object.values(booking.extraPayments ?? {}).filter(filter)) {
|
|
293
|
+
total += getPrice(item, options);
|
|
358
294
|
if (item.paymentMethod === PAY_ON_TOUR && options.mode === OPERATOR_MODE) {
|
|
359
295
|
let ourCommission = item.price - item.operatorPrice;
|
|
360
|
-
if (ourCommission > 0)
|
|
361
|
-
total -= ourCommission;
|
|
362
|
-
}
|
|
296
|
+
if (ourCommission > 0) total -= ourCommission;
|
|
363
297
|
}
|
|
364
298
|
}
|
|
365
|
-
|
|
366
299
|
return total;
|
|
367
300
|
}
|
|
368
301
|
|
|
@@ -370,56 +303,32 @@ export function getExtraPaymentsPaidWithPartner(booking, product) {
|
|
|
370
303
|
let startingPoint = findStartingPoint(product, booking.startingPointId);
|
|
371
304
|
let endPoint = findEndPoint(product, booking.endPointId);
|
|
372
305
|
let addOnDefs = {};
|
|
306
|
+
|
|
373
307
|
(product.addOns ?? []).forEach((addOn) => {
|
|
374
308
|
addOnDefs[addOn.id] = addOn;
|
|
375
309
|
});
|
|
310
|
+
|
|
376
311
|
let addOns = Object.entries(booking.addOns ?? {}).filter(([id]) => addOnDefs[id] != null).map(([id, addOn]) => {
|
|
377
312
|
let addOnDef = addOnDefs[id];
|
|
378
|
-
|
|
379
313
|
return {
|
|
380
314
|
category: ADD_ON_EXTRA_PAYMENT,
|
|
381
315
|
description: addOnDef.title,
|
|
382
|
-
price: addOn.adults * addOnDef.pricing.adult.sellingPrice +
|
|
383
|
-
|
|
384
|
-
operatorPrice: addOn.adults * addOnDef.pricing.adult.operatorNetPrice +
|
|
385
|
-
(addOn.children ?? 0) * (addOnDef.pricing.child?.operatorNetPrice ?? 0)
|
|
316
|
+
price: (addOn.adults * addOnDef.pricing.adult.sellingPrice) + ((addOn.children ?? 0) * (addOnDef.pricing.child?.sellingPrice ?? 0)),
|
|
317
|
+
operatorPrice: (addOn.adults * addOnDef.pricing.adult.operatorNetPrice) + ((addOn.children ?? 0) * (addOnDef.pricing.child?.operatorNetPrice ?? 0))
|
|
386
318
|
}
|
|
387
319
|
});
|
|
388
320
|
|
|
389
321
|
return [
|
|
390
|
-
{
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
price: calculateStartEndPointSellingPrice(booking, startingPoint),
|
|
394
|
-
operatorPrice: calculateStartEndPointOperatorPrice(booking, startingPoint),
|
|
395
|
-
},
|
|
396
|
-
{
|
|
397
|
-
category: END_POINT_EXTRA_PAYMENT,
|
|
398
|
-
description: endPoint.name,
|
|
399
|
-
price: calculateStartEndPointSellingPrice(booking, endPoint),
|
|
400
|
-
operatorPrice: calculateStartEndPointOperatorPrice(booking, endPoint),
|
|
401
|
-
},
|
|
402
|
-
{
|
|
403
|
-
category: NATIONAL_PARK_FEE_EXTRA_PAYMENT,
|
|
404
|
-
description: `Entrance fee to ${product.nationalPark?.title}`,
|
|
405
|
-
price: calculateNationalParkFee(booking, product),
|
|
406
|
-
operatorPrice: calculateNationalParkFee(booking, product),
|
|
407
|
-
},
|
|
322
|
+
{ category: STARTING_POINT_EXTRA_PAYMENT, description: startingPoint.name, price: calculateStartEndPointSellingPrice(booking, startingPoint), operatorPrice: calculateStartEndPointOperatorPrice(booking, startingPoint) },
|
|
323
|
+
{ category: END_POINT_EXTRA_PAYMENT, description: endPoint.name, price: calculateStartEndPointSellingPrice(booking, endPoint), operatorPrice: calculateStartEndPointOperatorPrice(booking, endPoint) },
|
|
324
|
+
{ category: NATIONAL_PARK_FEE_EXTRA_PAYMENT, description: `Entrance fee to ${product.nationalPark?.title}`, price: calculateNationalParkFee(booking, product), operatorPrice: calculateNationalParkFee(booking, product) },
|
|
408
325
|
...addOns
|
|
409
326
|
].filter(
|
|
410
|
-
(item) =>
|
|
411
|
-
(
|
|
412
|
-
Object.values(booking.extraPayments).find(
|
|
413
|
-
(extraPayment) =>
|
|
414
|
-
extraPayment.category === item.category && !extraPayment.disabled
|
|
415
|
-
) == null
|
|
327
|
+
(item) => (item.price > 0 || item.operatorPrice > 0) &&
|
|
328
|
+
!Object.values(booking.extraPayments ?? {}).find(ep => ep.category === item.category && !ep.disabled)
|
|
416
329
|
).map((item) => {
|
|
417
|
-
if (booking.customerPaymentStatus === "Refunded")
|
|
418
|
-
|
|
419
|
-
}
|
|
420
|
-
if (booking.operatorPaymentStatus === "No Charge") {
|
|
421
|
-
item.operatorPrice = 0;
|
|
422
|
-
}
|
|
330
|
+
if (booking.customerPaymentStatus === "Refunded") item.price = 0;
|
|
331
|
+
if (booking.operatorPaymentStatus === "No Charge") item.operatorPrice = 0;
|
|
423
332
|
return item;
|
|
424
333
|
});
|
|
425
334
|
}
|
|
@@ -427,31 +336,18 @@ export function getExtraPaymentsPaidWithPartner(booking, product) {
|
|
|
427
336
|
export function getActiveExtraPayments(extraPayments) {
|
|
428
337
|
return [...extraPayments]
|
|
429
338
|
.filter(([id, item]) => !item.disabled)
|
|
430
|
-
.sort(([id1, item1], [id2, item2]) =>
|
|
431
|
-
item1.description.localeCompare(item2.description)
|
|
432
|
-
);
|
|
339
|
+
.sort(([id1, item1], [id2, item2]) => item1.description.localeCompare(item2.description));
|
|
433
340
|
}
|
|
434
341
|
|
|
435
342
|
export function getExtraPaymentsOperatorPrice(extraPayments) {
|
|
436
|
-
return getActiveExtraPayments(extraPayments).reduce(
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
let
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
} else {
|
|
445
|
-
price = 0;
|
|
446
|
-
}
|
|
447
|
-
} else {
|
|
448
|
-
price = item.operatorPrice;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
return prev + price;
|
|
452
|
-
},
|
|
453
|
-
0
|
|
454
|
-
);
|
|
343
|
+
return getActiveExtraPayments(extraPayments).reduce((prev, curr) => {
|
|
344
|
+
let item = curr[1];
|
|
345
|
+
if (item.paymentMethod === PAY_ON_TOUR) {
|
|
346
|
+
let ourCommission = item.price - item.operatorPrice;
|
|
347
|
+
return prev + (ourCommission > 0 ? -ourCommission : 0);
|
|
348
|
+
}
|
|
349
|
+
return prev + item.operatorPrice;
|
|
350
|
+
}, 0);
|
|
455
351
|
}
|
|
456
352
|
|
|
457
353
|
export function calculateAddOnsPrice(booking, product, options = { mode: SELLING_MODE }) {
|
|
@@ -461,11 +357,8 @@ export function calculateAddOnsPrice(booking, product, options = { mode: SELLING
|
|
|
461
357
|
for (const [id, addOn] of Object.entries(booking.addOns ?? {})) {
|
|
462
358
|
let addOnDef = findAddOn(id, product.addOns ?? []);
|
|
463
359
|
if (addOnDef) {
|
|
464
|
-
total += addOn.adults * (isSelling ? addOnDef.pricing.adult
|
|
465
|
-
.sellingPrice : addOnDef.pricing.
|
|
466
|
-
.operatorNetPrice) + (addOn.children > 0 ? addOn.children * (isSelling ? addOnDef.pricing.child
|
|
467
|
-
.sellingPrice : addOnDef.pricing.child
|
|
468
|
-
.operatorNetPrice) : 0);
|
|
360
|
+
total += (addOn.adults * (isSelling ? addOnDef.pricing.adult.sellingPrice : addOnDef.pricing.adult.operatorNetPrice)) +
|
|
361
|
+
(addOn.children > 0 ? addOn.children * (isSelling ? addOnDef.pricing.child.sellingPrice : addOnDef.pricing.child.operatorNetPrice) : 0);
|
|
469
362
|
}
|
|
470
363
|
}
|
|
471
364
|
return total;
|
|
@@ -486,32 +379,23 @@ export function calculateStartEndPointPartnerNetPrice(people, startEndPoint) {
|
|
|
486
379
|
function calculateStartEndPointPrice(people, startEndPoint, options = { mode: SELLING_MODE }) {
|
|
487
380
|
let pricing = startEndPoint?.pricing;
|
|
488
381
|
let isSelling = [SELLING_MODE, PARTNER_MODE].includes(options.mode);
|
|
382
|
+
|
|
489
383
|
if (pricing) {
|
|
384
|
+
const priceAmount = isSelling ? (pricing.sellingPrice ?? pricing.amount) : pricing.operatorNetPrice;
|
|
490
385
|
if (pricing.type === "group") {
|
|
491
|
-
return
|
|
386
|
+
return priceAmount * Math.ceil((people.adults + people.children) / pricing.max);
|
|
492
387
|
} else if (pricing.type === "person") {
|
|
493
|
-
return
|
|
494
|
-
} else {
|
|
495
|
-
return 0;
|
|
388
|
+
return priceAmount * (people.adults + people.children);
|
|
496
389
|
}
|
|
497
|
-
} else {
|
|
498
|
-
return 0;
|
|
499
390
|
}
|
|
391
|
+
return 0;
|
|
500
392
|
}
|
|
501
393
|
|
|
502
394
|
export function findStartingPoint(product, startingPointId, useDefault = true) {
|
|
503
395
|
return [
|
|
504
396
|
...product.startingPoints,
|
|
505
|
-
{
|
|
506
|
-
|
|
507
|
-
name: TELL_HOTEL_LATER_STARTING_POINT.description,
|
|
508
|
-
value: '',
|
|
509
|
-
},
|
|
510
|
-
{
|
|
511
|
-
id: CUSTOM_MEETING_POINT.id,
|
|
512
|
-
name: CUSTOM_MEETING_POINT.description,
|
|
513
|
-
value: '',
|
|
514
|
-
},
|
|
397
|
+
{ id: TELL_HOTEL_LATER_STARTING_POINT.id, name: TELL_HOTEL_LATER_STARTING_POINT.description, value: '' },
|
|
398
|
+
{ id: CUSTOM_MEETING_POINT.id, name: CUSTOM_MEETING_POINT.description, value: '' },
|
|
515
399
|
].find((item) => item.id === startingPointId) ?? (useDefault ? product.startingPoints[0] : null);
|
|
516
400
|
}
|
|
517
401
|
|
|
@@ -520,9 +404,7 @@ export function findEndPoint(product, endPointId, useDefault = true) {
|
|
|
520
404
|
}
|
|
521
405
|
|
|
522
406
|
export function findAddOn(id, addOns) {
|
|
523
|
-
return addOns.find((item) =>
|
|
524
|
-
item.id === id
|
|
525
|
-
);
|
|
407
|
+
return addOns.find((item) => item.id === id);
|
|
526
408
|
}
|
|
527
409
|
|
|
528
410
|
export function inRange(expected, actual, boundary = 10) {
|