tripstorekrabi-shared 1.13.66 → 1.13.68
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 +169 -132
- package/package.json +2 -2
- package/price.js +2 -1
package/booking.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import dayjs from
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
2
|
import advancedFormat from 'dayjs/plugin/advancedFormat.js';
|
|
3
|
-
import utc from
|
|
4
|
-
import timezone from
|
|
3
|
+
import utc from 'dayjs/plugin/utc.js';
|
|
4
|
+
import timezone from 'dayjs/plugin/timezone.js';
|
|
5
5
|
import { PAY_ON_TOUR, calculateTotalOperatorPrice } from './price.js';
|
|
6
6
|
|
|
7
7
|
dayjs.extend(advancedFormat);
|
|
@@ -10,131 +10,155 @@ dayjs.extend(timezone);
|
|
|
10
10
|
|
|
11
11
|
const PHONE_REGEXP = /[^\d+]/g;
|
|
12
12
|
|
|
13
|
-
export function addBookingFromInvoice(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
13
|
+
export function addBookingFromInvoice(
|
|
14
|
+
firestore,
|
|
15
|
+
admin,
|
|
16
|
+
invoice,
|
|
17
|
+
paymentData,
|
|
18
|
+
comment = ''
|
|
19
|
+
) {
|
|
20
|
+
let bookingNumberDoc = firestore.collection('meta').doc('BOOKING_NUMBER');
|
|
21
|
+
|
|
22
|
+
return firestore.runTransaction((transaction) => {
|
|
23
|
+
return bookingNumberDoc.get().then(async (snapshot) => {
|
|
24
|
+
let nextBookingNumber = (snapshot.get('last') ?? 0) + 1;
|
|
25
|
+
let processed = 0;
|
|
26
|
+
comment =
|
|
27
|
+
[comment, invoice.comment].filter((item) => item).join(', ') ?? '';
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < invoice.items.length; i++) {
|
|
30
|
+
let item = invoice.items[i];
|
|
31
|
+
|
|
32
|
+
// process only product items
|
|
33
|
+
let hasProduct = item.productId?.length > 0;
|
|
34
|
+
let productSnapshot = null;
|
|
35
|
+
let productDescription;
|
|
36
|
+
if (hasProduct) {
|
|
37
|
+
productSnapshot = await firestore
|
|
38
|
+
.collection('products')
|
|
39
|
+
.doc(item.productId)
|
|
40
|
+
.get();
|
|
41
|
+
productDescription = productSnapshot.get('shortName');
|
|
42
|
+
} else {
|
|
43
|
+
productDescription = item.description;
|
|
44
|
+
}
|
|
36
45
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
46
|
+
let data = {
|
|
47
|
+
referenceNumber: 'TSK' + (nextBookingNumber + processed),
|
|
48
|
+
hint: '',
|
|
49
|
+
specialRequirements: item.dietary?.toUpperCase() ?? '',
|
|
50
|
+
productDescription,
|
|
51
|
+
productRef: hasProduct
|
|
52
|
+
? firestore.collection('products').doc(item.productId)
|
|
53
|
+
: null,
|
|
54
|
+
operator: productSnapshot?.get('operator.default.name') ?? '',
|
|
55
|
+
departureDate: admin.firestore.Timestamp.fromDate(
|
|
56
|
+
dayjs(item.departureDate).toDate()
|
|
57
|
+
),
|
|
58
|
+
schedule: item.schedule ?? '',
|
|
59
|
+
pickupTime: '?',
|
|
60
|
+
adults: item.adults ?? 0,
|
|
61
|
+
children: item.children ?? 0,
|
|
62
|
+
infants: item.infants ?? 0,
|
|
63
|
+
leadTraveller: invoice.name,
|
|
64
|
+
email: invoice.email ?? '',
|
|
65
|
+
phoneNumber: invoice.phoneNumber ?? '',
|
|
66
|
+
cc: '',
|
|
67
|
+
customerBookingStatus: 'Pending',
|
|
68
|
+
operatorBookingStatus: 'Pending',
|
|
69
|
+
sentToOperator: false,
|
|
70
|
+
startingPointDescription: '',
|
|
71
|
+
pickupHotel: item.pickupHotel ?? null,
|
|
72
|
+
roomNumber: item.roomNumber ?? '',
|
|
73
|
+
flight: item.flight ?? null,
|
|
74
|
+
train: item.train ?? null,
|
|
75
|
+
comment,
|
|
76
|
+
customerPaymentStatus: 'Paid',
|
|
77
|
+
operatorPaymentStatus: 'Pending',
|
|
78
|
+
extraPayments: {},
|
|
79
|
+
addOns: item.addOns ?? null,
|
|
80
|
+
labels: ['NEW'],
|
|
81
|
+
paypalInvoiceId: '',
|
|
82
|
+
paypalTransactionId: '',
|
|
83
|
+
omiseChargeId: '',
|
|
84
|
+
invoiceNumber: '',
|
|
85
|
+
date: admin.firestore.FieldValue.serverTimestamp(),
|
|
86
|
+
createdBy: 'Trip Store Krabi Next',
|
|
87
|
+
lastModified: dayjs().toDate(),
|
|
88
|
+
lastModifiedBy: 'Trip Store Krabi Next',
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (item.startingPointId) {
|
|
92
|
+
data.startingPointId = item.startingPointId;
|
|
93
|
+
}
|
|
80
94
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
if (item.dropoffHotel && item.dropoffHotel.length > 0) {
|
|
96
|
+
data.dropoffHotel = item.dropoffHotel;
|
|
97
|
+
}
|
|
84
98
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
99
|
+
if (i > 0) {
|
|
100
|
+
data.partnerNetPrice = 0;
|
|
101
|
+
}
|
|
88
102
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
103
|
+
if (item.nationalParkFee) {
|
|
104
|
+
data.nationalParkFee = item.nationalParkFee;
|
|
105
|
+
}
|
|
92
106
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
107
|
+
if (item.tourGuideLanguage) {
|
|
108
|
+
data.tourGuideLanguage = item.tourGuideLanguage;
|
|
109
|
+
}
|
|
96
110
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (endPointId) {
|
|
105
|
-
data.endPointId = endPointId;
|
|
111
|
+
let endPointId = item.endPointId;
|
|
112
|
+
if (!endPointId) {
|
|
113
|
+
let endPoints = productSnapshot?.get('endPoints');
|
|
114
|
+
if (endPoints && endPoints.length === 1) {
|
|
115
|
+
endPointId = endPoints[0].id;
|
|
106
116
|
}
|
|
117
|
+
}
|
|
118
|
+
if (endPointId) {
|
|
119
|
+
data.endPointId = endPointId;
|
|
120
|
+
}
|
|
107
121
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
data
|
|
112
|
-
|
|
122
|
+
if (productSnapshot) {
|
|
123
|
+
data.operatorNetPrice = calculateTotalOperatorPrice(
|
|
124
|
+
data,
|
|
125
|
+
productSnapshot.data()
|
|
126
|
+
);
|
|
127
|
+
} else {
|
|
128
|
+
data.operatorNetPrice = 0;
|
|
129
|
+
}
|
|
113
130
|
|
|
114
|
-
|
|
131
|
+
data = { ...data, ...paymentData };
|
|
115
132
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
133
|
+
let doc = firestore.collection('bookings').doc();
|
|
134
|
+
transaction.set(doc, data);
|
|
135
|
+
processed++;
|
|
136
|
+
}
|
|
120
137
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
138
|
+
transaction.set(
|
|
139
|
+
bookingNumberDoc,
|
|
140
|
+
{
|
|
141
|
+
last: admin.firestore.FieldValue.increment(processed),
|
|
142
|
+
},
|
|
143
|
+
{ merge: true }
|
|
144
|
+
);
|
|
128
145
|
});
|
|
146
|
+
});
|
|
129
147
|
}
|
|
130
148
|
|
|
131
149
|
export function getOperatorSignature(agent, operatorName, contact) {
|
|
132
150
|
let signature =
|
|
133
|
-
'<br><br>Warm regards,<br><br>' +
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
signature +=
|
|
137
|
-
|
|
151
|
+
'<br><br>Warm regards,<br><br>' +
|
|
152
|
+
(agent ? agent + ' - ' : '') +
|
|
153
|
+
operatorName;
|
|
154
|
+
signature += `<br>WhatsApp chat: <a href='https://wa.me/${contact.respondNumber.replace(
|
|
155
|
+
/\D/g,
|
|
156
|
+
''
|
|
157
|
+
)}'>${contact.respondNumber}</a>`;
|
|
158
|
+
signature += `<br>WhatsApp call: <a href='https://wa.me/${contact.whatsappNumber.replace(
|
|
159
|
+
/\D/g,
|
|
160
|
+
''
|
|
161
|
+
)}'>${contact.whatsappNumber}</a>`;
|
|
138
162
|
signature +=
|
|
139
163
|
"<br>Telegram chat: <a href='https://t.me/tripstorekrabi_bot'>@tripstorekrabi_bot</a>";
|
|
140
164
|
return signature;
|
|
@@ -144,7 +168,7 @@ export function buildIndex(id, booking) {
|
|
|
144
168
|
let referenceNumbers = [booking.referenceNumber];
|
|
145
169
|
if (booking.expediaVoucherNumbers) {
|
|
146
170
|
booking.expediaVoucherNumbers.forEach((voucherNumber) => {
|
|
147
|
-
referenceNumbers.push(voucherNumber +
|
|
171
|
+
referenceNumbers.push(voucherNumber + '');
|
|
148
172
|
});
|
|
149
173
|
}
|
|
150
174
|
|
|
@@ -157,7 +181,7 @@ export function buildIndex(id, booking) {
|
|
|
157
181
|
|
|
158
182
|
let emails = [];
|
|
159
183
|
let email = booking.email;
|
|
160
|
-
if (email && email !==
|
|
184
|
+
if (email && email !== 'info@tripstorekrabi.com') {
|
|
161
185
|
emails.push(email);
|
|
162
186
|
}
|
|
163
187
|
|
|
@@ -165,7 +189,7 @@ export function buildIndex(id, booking) {
|
|
|
165
189
|
emails.push(booking.cc);
|
|
166
190
|
}
|
|
167
191
|
|
|
168
|
-
let pickupHotel = booking.pickupHotel ??
|
|
192
|
+
let pickupHotel = booking.pickupHotel ?? '';
|
|
169
193
|
|
|
170
194
|
let phoneNumbers = [];
|
|
171
195
|
let phoneNumber = booking.phoneNumber;
|
|
@@ -178,18 +202,25 @@ export function buildIndex(id, booking) {
|
|
|
178
202
|
}
|
|
179
203
|
phoneNumbers = [...new Set(phoneNumbers)];
|
|
180
204
|
|
|
181
|
-
return {
|
|
205
|
+
return {
|
|
206
|
+
id,
|
|
207
|
+
referenceNumbers,
|
|
208
|
+
travellers,
|
|
209
|
+
emails,
|
|
210
|
+
pickupHotel,
|
|
211
|
+
phoneNumbers,
|
|
212
|
+
};
|
|
182
213
|
}
|
|
183
214
|
|
|
184
215
|
function addPhoneNumbers(phoneNumbers, phoneNumber) {
|
|
185
|
-
phoneNumber = (phoneNumber +
|
|
186
|
-
phoneNumber = phoneNumber.replace(PHONE_REGEXP,
|
|
187
|
-
if (phoneNumber.startsWith(
|
|
216
|
+
phoneNumber = (phoneNumber + '').trim();
|
|
217
|
+
phoneNumber = phoneNumber.replace(PHONE_REGEXP, '');
|
|
218
|
+
if (phoneNumber.startsWith('+')) {
|
|
188
219
|
phoneNumbers.push(phoneNumber);
|
|
189
220
|
phoneNumbers.push(phoneNumber.substring(1));
|
|
190
221
|
} else {
|
|
191
222
|
phoneNumbers.push(phoneNumber);
|
|
192
|
-
phoneNumbers.push(
|
|
223
|
+
phoneNumbers.push('+' + phoneNumber);
|
|
193
224
|
}
|
|
194
225
|
}
|
|
195
226
|
|
|
@@ -203,13 +234,11 @@ export function getStartingPointMessage(booking, product) {
|
|
|
203
234
|
text += ' at ' + booking.pickupTime;
|
|
204
235
|
}
|
|
205
236
|
|
|
206
|
-
let now = dayjs().tz(
|
|
207
|
-
let dayjsDate = dayjs.tz(booking.departureDate.toDate(),
|
|
237
|
+
let now = dayjs().tz('Asia/Bangkok');
|
|
238
|
+
let dayjsDate = dayjs.tz(booking.departureDate.toDate(), 'Asia/Bangkok');
|
|
208
239
|
let date = dayjsDate.format('Do MMM');
|
|
209
240
|
|
|
210
|
-
if (
|
|
211
|
-
now.add(1, 'day').isSame(dayjsDate, 'day')
|
|
212
|
-
) {
|
|
241
|
+
if (now.add(1, 'day').isSame(dayjsDate, 'day')) {
|
|
213
242
|
text += ' tomorrow the ' + date;
|
|
214
243
|
} else if (now.isSame(dayjsDate, 'day')) {
|
|
215
244
|
text += ' today the ' + date;
|
|
@@ -219,10 +248,17 @@ export function getStartingPointMessage(booking, product) {
|
|
|
219
248
|
|
|
220
249
|
text += '. Please watch out for the staff of ' + booking.operator + '.';
|
|
221
250
|
|
|
251
|
+
if (product?.whatToBring?.length > 0) {
|
|
252
|
+
text += '\n\nWHAT TO BRING: ' + product.whatToBring;
|
|
253
|
+
}
|
|
254
|
+
|
|
222
255
|
if (booking.specialRequirements) {
|
|
223
256
|
text +=
|
|
224
|
-
'\n\nSPECIAL REQUIREMENTS: ' +
|
|
225
|
-
|
|
257
|
+
'\n\nSPECIAL REQUIREMENTS: ' + booking.specialRequirements.toUpperCase();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (product?.customerInformation?.length > 0) {
|
|
261
|
+
text += '\n\nIMPORTANT INFORMATION: ' + product.customerInformation;
|
|
226
262
|
}
|
|
227
263
|
|
|
228
264
|
let payOnTour = [];
|
|
@@ -233,8 +269,7 @@ export function getStartingPointMessage(booking, product) {
|
|
|
233
269
|
item += ' to ' + product?.nationalPark.title;
|
|
234
270
|
}
|
|
235
271
|
|
|
236
|
-
if (product?.nationalPark?.pricing
|
|
237
|
-
) {
|
|
272
|
+
if (product?.nationalPark?.pricing) {
|
|
238
273
|
item +=
|
|
239
274
|
' of ' +
|
|
240
275
|
product.nationalPark.pricing.adult +
|
|
@@ -247,12 +282,14 @@ export function getStartingPointMessage(booking, product) {
|
|
|
247
282
|
payOnTour.push(` • ${item}`);
|
|
248
283
|
}
|
|
249
284
|
|
|
250
|
-
let extraPayments = Object.values(booking.extraPayments)
|
|
285
|
+
let extraPayments = Object.values(booking.extraPayments)
|
|
286
|
+
.filter((item) => !item.disabled && item.paymentMethod === PAY_ON_TOUR)
|
|
287
|
+
.map((item) => ` • ${item.description}: ${item.price} THB`);
|
|
251
288
|
payOnTour = [...payOnTour, ...extraPayments];
|
|
252
289
|
if (payOnTour.length > 0) {
|
|
253
|
-
text += '\n\nTO BE PAID ON TOUR:\n' + payOnTour.join(
|
|
290
|
+
text += '\n\nTO BE PAID ON TOUR:\n' + payOnTour.join('\n');
|
|
254
291
|
text += '\n\nPlease bring the total amount in cash to the trip.';
|
|
255
292
|
}
|
|
256
293
|
|
|
257
294
|
return text;
|
|
258
|
-
}
|
|
295
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tripstorekrabi-shared",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.68",
|
|
4
4
|
"description": "Shared library for Trip Store Krabi",
|
|
5
5
|
"author": "Stefan Sommer <tonprince@gmail.com>",
|
|
6
6
|
"repository": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"./product": "./product.js"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"dayjs": "^1.11.
|
|
18
|
+
"dayjs": "^1.11.10"
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"deploy": "npm publish"
|
package/price.js
CHANGED
|
@@ -403,7 +403,8 @@ export function calculatePhayunPrices(booking, product) {
|
|
|
403
403
|
let productPrice;
|
|
404
404
|
let cashOnTourAmount = 0;
|
|
405
405
|
let activeExtraPayments = getActiveExtraPayments(Object.entries(booking.extraPayments));
|
|
406
|
-
|
|
406
|
+
let bookingType = getBookingType(booking.referenceNumber);
|
|
407
|
+
|
|
407
408
|
if (
|
|
408
409
|
product.operator.default.invoicing?.operator?.create &&
|
|
409
410
|
product.operator.default.invoicing?.operator?.partners?.[
|