tripstorekrabi-shared 1.23.7 → 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.
Files changed (2) hide show
  1. package/package.json +4 -4
  2. package/price.js +77 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tripstorekrabi-shared",
3
- "version": "1.23.7",
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.19",
21
- "libphonenumber-js": "^1.12.38",
22
- "nanoid": "^5.1.6"
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
- if (pricing.promotion) {
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 = pricing.promotion.startDate instanceof Date ? pricing.promotion.startDate : typeof pricing.promotion.startDate.toDate === 'function' ? pricing.promotion.startDate.toDate() : null;
76
- const endDate = pricing.promotion.endDate instanceof Date ? pricing.promotion.endDate : typeof pricing.promotion.endDate.toDate === 'function' ? pricing.promotion.endDate.toDate() : null;
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
- pricing = { ...pricing, volumes: pricing.promotion.volumes };
125
+ if (departureDate?.isValid() && departureDate?.isBetween(dayjs(startDate), dayjs(endDate), "day", "[]")) {
126
+ activeVolumes = activeRevision.promotion.volumes;
80
127
  }
81
128
  }
82
129
 
83
- const type = pricing.type;
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, pricing);
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 = pricing.commissionRates?.[bookingSourceKey];
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 (pricing.ageBands.child.active && volume.child && booking.children) {
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 + (pricing.ageBands.child.active && volume.child && booking.children ? booking.children * customPricing.child.partnerNetPrice : 0);
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) + (pricing.ageBands.child.active && volume.child && booking.children ? (booking.children * (customPricing.child.operatorNetPrice ?? volume.child.operatorNetPrice)) : 0);
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 + (pricing.ageBands.child.active && volume.child && booking.children ? booking.children * volume.child.sellingPrice : 0);
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 + (pricing.ageBands.child.active && volume.child && booking.children ? booking.children * volume.child.operatorNetPrice : 0);
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, pricing);
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 = pricing.commissionRates?.[bookingSourceKey];
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,14 +216,21 @@ function calculateProductPrice(booking, pricing, options = { mode: SELLING_MODE
164
216
  return total;
165
217
  }
166
218
 
167
- export function findVolume(people, pricing) {
219
+ export function findVolume(people, context) {
168
220
  let total = (people.adults ?? 0) + (people.children ?? 0);
169
- return [...pricing.volumes].sort((v1, v2) => v1.max - v2.max).find((item, i) => {
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
- } else if (i === pricing.volumes.length - 1) {
229
+ }
230
+ else if (i === sortedVolumes.length - 1) {
173
231
  return true;
174
232
  }
233
+ return false;
175
234
  });
176
235
  }
177
236