@bash-app/bash-common 30.57.0 → 30.58.0
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/dist/extendedSchemas.d.ts +44 -0
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js +6 -0
- package/dist/extendedSchemas.js.map +1 -1
- package/dist/utils/dateTimeUtils.d.ts +1 -17
- package/dist/utils/dateTimeUtils.d.ts.map +1 -1
- package/dist/utils/dateTimeUtils.js +1 -114
- package/dist/utils/dateTimeUtils.js.map +1 -1
- package/dist/utils/luxonUtils.d.ts +17 -6
- package/dist/utils/luxonUtils.d.ts.map +1 -1
- package/dist/utils/luxonUtils.js +31 -3
- package/dist/utils/luxonUtils.js.map +1 -1
- package/dist/utils/recurrenceUtils.d.ts +1 -1
- package/dist/utils/recurrenceUtils.d.ts.map +1 -1
- package/dist/utils/recurrenceUtils.js +24 -49
- package/dist/utils/recurrenceUtils.js.map +1 -1
- package/dist/utils/service/apiServiceBookingApiUtils.d.ts +14 -2
- package/dist/utils/service/apiServiceBookingApiUtils.d.ts.map +1 -1
- package/dist/utils/service/apiServiceBookingApiUtils.js +36 -2
- package/dist/utils/service/apiServiceBookingApiUtils.js.map +1 -1
- package/dist/utils/service/frontendServiceBookingUtils.d.ts +10 -3
- package/dist/utils/service/frontendServiceBookingUtils.d.ts.map +1 -1
- package/dist/utils/service/frontendServiceBookingUtils.js +86 -40
- package/dist/utils/service/frontendServiceBookingUtils.js.map +1 -1
- package/dist/utils/service/serviceBookingTypes.d.ts +4 -2
- package/dist/utils/service/serviceBookingTypes.d.ts.map +1 -1
- package/dist/utils/ticketListUtils.d.ts.map +1 -1
- package/dist/utils/ticketListUtils.js +10 -13
- package/dist/utils/ticketListUtils.js.map +1 -1
- package/dist/utils/typeUtils.d.ts +5 -0
- package/dist/utils/typeUtils.d.ts.map +1 -1
- package/dist/utils/typeUtils.js.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +31 -41
- package/src/extendedSchemas.ts +9 -0
- package/src/utils/dateTimeUtils.ts +7 -190
- package/src/utils/luxonUtils.ts +56 -10
- package/src/utils/recurrenceUtils.ts +97 -77
- package/src/utils/service/apiServiceBookingApiUtils.ts +64 -7
- package/src/utils/service/frontendServiceBookingUtils.ts +120 -49
- package/src/utils/service/serviceBookingTypes.ts +30 -2
- package/src/utils/ticketListUtils.ts +40 -27
- package/src/utils/typeUtils.ts +15 -8
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { ServiceBookingStatus, ServiceBookingType } from "@prisma/client";
|
|
1
2
|
import {
|
|
2
3
|
ApiBookingParamsLuxon,
|
|
3
|
-
ApiServiceBookingParams,
|
|
4
|
-
ApiServiceBookedDayParamsLuxon,
|
|
5
4
|
ApiServiceAddonParams,
|
|
6
5
|
ApiServiceBookedDayParams,
|
|
6
|
+
ApiServiceBookedDayParamsLuxon,
|
|
7
|
+
ApiServiceBookingParams,
|
|
7
8
|
ApiServicePriceToBookResult,
|
|
8
9
|
} from "../../definitions";
|
|
9
10
|
import {
|
|
@@ -17,12 +18,15 @@ import {
|
|
|
17
18
|
} from "../../extendedSchemas";
|
|
18
19
|
import { dateTimeFromString, dateTimeRangeFromDates } from "../luxonUtils";
|
|
19
20
|
import { serviceAttendeeOptions } from "./attendeeOptionUtils";
|
|
20
|
-
import { ServiceBookingPriceBreakdownBaseT } from "./serviceBookingTypes";
|
|
21
21
|
import {
|
|
22
|
-
ServiceAddonInput,
|
|
23
|
-
FrontendServiceGetPriceToBookResult,
|
|
24
22
|
FrontendServiceBookingDayInfo,
|
|
23
|
+
FrontendServiceGetPriceToBookResult,
|
|
24
|
+
ServiceAddonInput,
|
|
25
25
|
} from "./frontendServiceBookingUtils";
|
|
26
|
+
import {
|
|
27
|
+
ServiceBookingPriceBreakdownBaseT,
|
|
28
|
+
ServiceBookingRequiredData,
|
|
29
|
+
} from "./serviceBookingTypes";
|
|
26
30
|
|
|
27
31
|
export type ApiServiceCantBookReason = {
|
|
28
32
|
type: string;
|
|
@@ -30,6 +34,9 @@ export type ApiServiceCantBookReason = {
|
|
|
30
34
|
};
|
|
31
35
|
|
|
32
36
|
export const ApiServiceCantBookReasons = {
|
|
37
|
+
notPublished: {
|
|
38
|
+
msg: "The requested service must be published..",
|
|
39
|
+
} as ApiServiceCantBookReason,
|
|
33
40
|
notInBusinessHours: {
|
|
34
41
|
msg: "Time not within normal Business Hours",
|
|
35
42
|
} as ApiServiceCantBookReason,
|
|
@@ -104,6 +111,7 @@ export function apiTransformBookedDays(
|
|
|
104
111
|
packages: [],
|
|
105
112
|
fees: day.fees as ServiceBookingFeeExt[],
|
|
106
113
|
priceBreakdown: priceBreakdown,
|
|
114
|
+
subtotalBeforeTaxesCents: day.subtotalBeforeTaxesCents,
|
|
107
115
|
totalBeforeTaxesCents: day.totalBeforeTaxesCents,
|
|
108
116
|
} as Partial<ServiceBookingDayExt>;
|
|
109
117
|
});
|
|
@@ -137,6 +145,7 @@ export function serviceGetPriceToBookFromBooking(
|
|
|
137
145
|
addOns: day.addOns,
|
|
138
146
|
priceBreakdown: priceBreakdown,
|
|
139
147
|
fees: day.fees,
|
|
148
|
+
subtotalBeforeTaxesCents: day.subtotalBeforeTaxesCents,
|
|
140
149
|
totalBeforeTaxesCents: day.totalBeforeTaxesCents,
|
|
141
150
|
timezone: booking.timezone,
|
|
142
151
|
} as FrontendServiceBookingDayInfo;
|
|
@@ -148,11 +157,15 @@ export function serviceGetPriceToBookFromBooking(
|
|
|
148
157
|
|
|
149
158
|
return {
|
|
150
159
|
serviceId: service.id,
|
|
151
|
-
|
|
160
|
+
timezone: booking.timezone,
|
|
152
161
|
selectedAttendeeOption: serviceAttendeeOptions[0],
|
|
162
|
+
daysToBook: daysToBook,
|
|
163
|
+
rateOption: booking.rateOption,
|
|
164
|
+
flatRateCents: booking.flatRateCents,
|
|
153
165
|
additionalFees: booking.additionalFees,
|
|
154
|
-
|
|
166
|
+
addOns: booking.addOns,
|
|
155
167
|
daysTotalBeforeTaxesCents: booking.daysTotalATBCents,
|
|
168
|
+
subtotalBeforeTaxesCents: booking.subtotalATBCents,
|
|
156
169
|
totalBeforeTaxesCents: booking.totalATBCents,
|
|
157
170
|
} as FrontendServiceGetPriceToBookResult;
|
|
158
171
|
}
|
|
@@ -220,3 +233,47 @@ export function servicePriceToBookToApiDays(
|
|
|
220
233
|
|
|
221
234
|
return bookedDays;
|
|
222
235
|
}
|
|
236
|
+
export interface ServicePriceToBookToApiBookingBaseArgs {
|
|
237
|
+
bookingType: ServiceBookingType;
|
|
238
|
+
status: ServiceBookingStatus;
|
|
239
|
+
timezone: string;
|
|
240
|
+
priceToBook: FrontendServiceGetPriceToBookResult;
|
|
241
|
+
isFreeGuest?: boolean;
|
|
242
|
+
allowPromiseToPay?: boolean;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function servicePriceToBookToApiBookingBase({
|
|
246
|
+
bookingType,
|
|
247
|
+
status,
|
|
248
|
+
timezone,
|
|
249
|
+
priceToBook,
|
|
250
|
+
allowPromiseToPay = false,
|
|
251
|
+
isFreeGuest = false,
|
|
252
|
+
}: ServicePriceToBookToApiBookingBaseArgs): ServiceBookingRequiredData {
|
|
253
|
+
const bookedDays = apiTransformBookedDays(priceToBook);
|
|
254
|
+
const addOns = priceToBook.addOns.map(
|
|
255
|
+
(addOn): Partial<ServiceBookingAddOnExt> => ({
|
|
256
|
+
addOnId: addOn.addOnId,
|
|
257
|
+
addOn: addOn.addOn,
|
|
258
|
+
chosenQuantity: addOn.chosenQuantity,
|
|
259
|
+
costPQCents: addOn.costPQCents,
|
|
260
|
+
})
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
bookingType: bookingType,
|
|
265
|
+
status: status,
|
|
266
|
+
timezone: timezone,
|
|
267
|
+
rateOption: priceToBook.rateOption,
|
|
268
|
+
flatRateCents: priceToBook.flatRateCents ?? null,
|
|
269
|
+
bookedDays: bookedDays as ServiceBookingDayExt[],
|
|
270
|
+
additionalFees: priceToBook.additionalFees as ServiceBookingFeeExt[],
|
|
271
|
+
daysTotalATBCents: priceToBook.daysTotalBeforeTaxesCents,
|
|
272
|
+
subtotalATBCents: priceToBook.subtotalBeforeTaxesCents,
|
|
273
|
+
totalATBCents: priceToBook.totalBeforeTaxesCents,
|
|
274
|
+
addOns: addOns as ServiceBookingAddOnExt[],
|
|
275
|
+
packages: [],
|
|
276
|
+
allowPromiseToPay: allowPromiseToPay,
|
|
277
|
+
isFreeGuest: isFreeGuest,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
ServiceAddon,
|
|
3
3
|
ServiceBookingFee,
|
|
4
4
|
ServiceBookingFeeType,
|
|
5
|
+
ServiceBookingRateOption,
|
|
5
6
|
} from "@prisma/client";
|
|
6
7
|
import {
|
|
7
8
|
ServiceBookingAddOnExt,
|
|
@@ -54,6 +55,7 @@ export interface FrontendServiceBookingDayInfo {
|
|
|
54
55
|
dateTimeRange: LuxonDateRange;
|
|
55
56
|
timezone: string;
|
|
56
57
|
|
|
58
|
+
subtotalBeforeTaxesCents: number; //sum of all daysTotalBeforeTaxesCents, plus addOns/packages
|
|
57
59
|
totalBeforeTaxesCents: number;
|
|
58
60
|
}
|
|
59
61
|
|
|
@@ -63,13 +65,20 @@ export interface FrontendServiceGetPriceToBookResult {
|
|
|
63
65
|
daysToBook: FrontendServiceBookingDayInfo[];
|
|
64
66
|
additionalFees: ServiceBookingFeeBase[];
|
|
65
67
|
|
|
68
|
+
rateOption: ServiceBookingRateOption;
|
|
69
|
+
flatRateCents?: number; //only valid on flatRate
|
|
70
|
+
|
|
71
|
+
addOns: ServiceBookingAddOnBase[]; //for bookings that do not bill by the time. e.g. flat rate bookings. For time based bookings, addOns are part of the day
|
|
72
|
+
|
|
66
73
|
daysTotalBeforeTaxesCents: number;
|
|
74
|
+
subtotalBeforeTaxesCents: number; //sum of all daysTotalBeforeTaxesCents, plus addOns/packages
|
|
67
75
|
totalBeforeTaxesCents: number;
|
|
68
76
|
|
|
69
77
|
minimumTimeBlockHours?: number; //only valid on generalRate
|
|
70
78
|
}
|
|
71
79
|
|
|
72
80
|
export interface FrontendServiceGetBookingDayInfoFrontParams {
|
|
81
|
+
rateOption: ServiceBookingRateOption; //if true, then no special rates, daily rates, or general rates are used
|
|
73
82
|
filteredRates: ServiceRatesLuxon;
|
|
74
83
|
// overlappingBusinessHours: LuxonDateRange[] | null;
|
|
75
84
|
|
|
@@ -78,6 +87,7 @@ export interface FrontendServiceGetBookingDayInfoFrontParams {
|
|
|
78
87
|
}
|
|
79
88
|
|
|
80
89
|
export function frontendServiceGetBookingDayInfo({
|
|
90
|
+
rateOption,
|
|
81
91
|
filteredRates,
|
|
82
92
|
daysToBookParams,
|
|
83
93
|
// overlappingBusinessHours,
|
|
@@ -99,49 +109,55 @@ export function frontendServiceGetBookingDayInfo({
|
|
|
99
109
|
let currentRange = { ...day.dateTimeRange };
|
|
100
110
|
let baseCostDiscountedCents = 0;
|
|
101
111
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// currentRange.start = currentRange.start.plus({
|
|
122
|
-
// hours: pricingInfo.hours,
|
|
123
|
-
// });
|
|
124
|
-
currentRange.start = pricingInfo.dateTimeRange.end;
|
|
125
|
-
hoursRemaining -= pricingInfo.hours;
|
|
126
|
-
baseCostDiscountedCents += pricingInfo.piTotalCents;
|
|
112
|
+
if (rateOption === ServiceBookingRateOption.FlatRate) {
|
|
113
|
+
} else if (rateOption === ServiceBookingRateOption.TimeBased) {
|
|
114
|
+
const rates = serviceRatesFilter(filteredRates, [day.dateTimeRange]);
|
|
115
|
+
const { generalRate, specialRates, dailyRates } = rates;
|
|
116
|
+
while (serviceRatesHasRates(rates) && hoursRemaining > 0) {
|
|
117
|
+
const pricingInfo = getServiceRatePricingInfo(
|
|
118
|
+
currentRange,
|
|
119
|
+
generalRate,
|
|
120
|
+
dailyRates,
|
|
121
|
+
specialRates,
|
|
122
|
+
// overlappingBusinessHours,
|
|
123
|
+
timezone,
|
|
124
|
+
hoursRemaining
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
if (pricingInfo.hours == 0 || pricingInfo.unitCount == 0) {
|
|
128
|
+
throw new Error(`Invalid pricingInfo`);
|
|
129
|
+
}
|
|
127
130
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
priceBreakdown.push(pricingInfo);
|
|
132
|
+
|
|
133
|
+
// currentRange.start = currentRange.start.plus({
|
|
134
|
+
// hours: pricingInfo.hours,
|
|
135
|
+
// });
|
|
136
|
+
currentRange.start = pricingInfo.dateTimeRange.end;
|
|
137
|
+
hoursRemaining -= pricingInfo.hours;
|
|
138
|
+
baseCostDiscountedCents += pricingInfo.piTotalCents;
|
|
139
|
+
|
|
140
|
+
if (pricingInfo.hours < SERVICE_DAILY_RATE_HOURS_MIN) {
|
|
141
|
+
if (pricingInfo.rateType == "Special") {
|
|
142
|
+
specialRates.pop();
|
|
143
|
+
} else if (pricingInfo.rateType == "Weekday") {
|
|
144
|
+
dailyRates.pop();
|
|
145
|
+
}
|
|
133
146
|
}
|
|
134
147
|
}
|
|
135
148
|
}
|
|
136
149
|
|
|
137
|
-
const addOns =
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
150
|
+
const addOns =
|
|
151
|
+
rateOption === "FlatRate"
|
|
152
|
+
? []
|
|
153
|
+
: day.addOns.map((addOn): ServiceBookingAddOnBase => {
|
|
154
|
+
return {
|
|
155
|
+
addOn: addOn,
|
|
156
|
+
addOnId: addOn.id,
|
|
157
|
+
chosenQuantity: addOn.chosenQuantity ?? 0,
|
|
158
|
+
costPQCents: addOn.priceCents,
|
|
159
|
+
};
|
|
160
|
+
});
|
|
145
161
|
|
|
146
162
|
const feesTotalCents = day.fees.reduce(
|
|
147
163
|
(sofar, fee) => sofar + fee.costPQCents * fee.quantity,
|
|
@@ -153,15 +169,19 @@ export function frontendServiceGetBookingDayInfo({
|
|
|
153
169
|
0
|
|
154
170
|
);
|
|
155
171
|
|
|
156
|
-
const
|
|
157
|
-
baseCostDiscountedCents + addOnsTotalCents
|
|
172
|
+
const subtotalBeforeTaxesCents =
|
|
173
|
+
baseCostDiscountedCents + addOnsTotalCents;
|
|
174
|
+
|
|
175
|
+
const totalBeforeTaxesCents = subtotalBeforeTaxesCents + feesTotalCents;
|
|
158
176
|
|
|
159
177
|
return {
|
|
160
178
|
priceBreakdown: priceBreakdown,
|
|
161
179
|
dateTimeRange: day.dateTimeRange,
|
|
180
|
+
rateOption: rateOption,
|
|
162
181
|
timezone: day.dateTimeRange.start.zoneName,
|
|
163
182
|
fees: day.fees,
|
|
164
183
|
addOns: addOns,
|
|
184
|
+
subtotalBeforeTaxesCents: subtotalBeforeTaxesCents,
|
|
165
185
|
totalBeforeTaxesCents: totalBeforeTaxesCents,
|
|
166
186
|
} as FrontendServiceBookingDayInfo;
|
|
167
187
|
});
|
|
@@ -174,6 +194,7 @@ export interface FrontendServiceGetPriceToBookFeesParams {
|
|
|
174
194
|
selectedAttendeeOption: ServiceAttendeeOption;
|
|
175
195
|
|
|
176
196
|
additionalFees: ServiceBookingFeeBase[];
|
|
197
|
+
addOns: ServiceAddonInput[]; //used for bookings that do not bill by the time. e.g. flat rate bookings
|
|
177
198
|
|
|
178
199
|
// overlappingBusinessHours: LuxonDateRange[] | null;
|
|
179
200
|
timezone: string | null | undefined;
|
|
@@ -184,6 +205,7 @@ export function frontendServiceGetPriceToBookFees(
|
|
|
184
205
|
{
|
|
185
206
|
daysToBookParams,
|
|
186
207
|
selectedAttendeeOption,
|
|
208
|
+
addOns,
|
|
187
209
|
additionalFees,
|
|
188
210
|
// overlappingBusinessHours,
|
|
189
211
|
timezone,
|
|
@@ -198,33 +220,76 @@ export function frontendServiceGetPriceToBookFees(
|
|
|
198
220
|
timezone
|
|
199
221
|
);
|
|
200
222
|
|
|
223
|
+
const isFlatRate = (filteredRates.generalRate?.flatRateCents ?? 0) > 0;
|
|
224
|
+
const rateOption = isFlatRate
|
|
225
|
+
? ServiceBookingRateOption.FlatRate
|
|
226
|
+
: ServiceBookingRateOption.TimeBased;
|
|
227
|
+
|
|
228
|
+
//only add addOns if flat rate booking (addOns are part of the day for time based bookings)
|
|
229
|
+
const usedAddons =
|
|
230
|
+
rateOption === ServiceBookingRateOption.FlatRate
|
|
231
|
+
? addOns.map((addOn): ServiceBookingAddOnBase => {
|
|
232
|
+
return {
|
|
233
|
+
addOn: addOn,
|
|
234
|
+
addOnId: addOn.id,
|
|
235
|
+
chosenQuantity: addOn.chosenQuantity ?? 0,
|
|
236
|
+
costPQCents: addOn.priceCents,
|
|
237
|
+
};
|
|
238
|
+
})
|
|
239
|
+
: [];
|
|
240
|
+
|
|
201
241
|
const bookingDayResults: FrontendServiceBookingDayInfo[] =
|
|
202
242
|
frontendServiceGetBookingDayInfo({
|
|
243
|
+
rateOption: rateOption,
|
|
203
244
|
filteredRates: filteredRates,
|
|
204
245
|
daysToBookParams: daysToBookParams,
|
|
205
246
|
// overlappingBusinessHours: overlappingBusinessHours,
|
|
206
247
|
timezone: timezone,
|
|
207
248
|
});
|
|
208
249
|
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
250
|
+
const flatRateCents = filteredRates.generalRate?.flatRateCents ?? 0;
|
|
251
|
+
|
|
252
|
+
const daysTotalBeforeTaxesCents =
|
|
253
|
+
rateOption === ServiceBookingRateOption.FlatRate
|
|
254
|
+
? flatRateCents
|
|
255
|
+
: bookingDayResults.reduce((sofar, curr) => {
|
|
256
|
+
return sofar + curr.totalBeforeTaxesCents;
|
|
257
|
+
}, 0);
|
|
212
258
|
|
|
213
259
|
const minimumTimeBlockHours =
|
|
214
260
|
serviceRatesAssociation?.serviceGeneralRates?.minimumTimeBlockHours;
|
|
215
261
|
|
|
216
262
|
// const attendeeRate = selectedAttendeeOption.rate * durationHours;
|
|
217
263
|
|
|
218
|
-
|
|
219
|
-
|
|
264
|
+
let allAdditionalFees = [...additionalFees];
|
|
265
|
+
|
|
266
|
+
// if (rateOption === ServiceBookingRateOption.FlatRate) {
|
|
267
|
+
// allAdditionalFees.push({
|
|
268
|
+
// feeType: ServiceBookingFeeType.FlatRate,
|
|
269
|
+
// name: "Flat Rate",
|
|
270
|
+
// description: "Flat Rate",
|
|
271
|
+
// costPQCents: filteredRates.generalRate?.flatRateCents ?? 0,
|
|
272
|
+
// quantity: 1,
|
|
273
|
+
// });
|
|
274
|
+
// }
|
|
220
275
|
|
|
221
276
|
const hasCustomProcessingFee = additionalFees.some(
|
|
222
277
|
(fee) => fee.feeType == ServiceBookingFeeType.ProcessingFee
|
|
223
278
|
);
|
|
224
279
|
|
|
225
|
-
|
|
280
|
+
allAdditionalFees = allAdditionalFees.filter((fee) => fee.costPQCents > 0);
|
|
281
|
+
|
|
282
|
+
const addOnsTotalCents = usedAddons.reduce(
|
|
283
|
+
(sofar, addOn) => sofar + addOn.costPQCents * addOn.chosenQuantity,
|
|
284
|
+
0
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
const subtotalBeforeTaxesCents = daysTotalBeforeTaxesCents + addOnsTotalCents;
|
|
226
288
|
|
|
227
289
|
if (!hasCustomProcessingFee) {
|
|
290
|
+
const processingFeeCents =
|
|
291
|
+
SERVICE_BOOKING_PROCESSING_FEE_PERCENT * subtotalBeforeTaxesCents;
|
|
292
|
+
|
|
228
293
|
allAdditionalFees.push({
|
|
229
294
|
feeType: ServiceBookingFeeType.ProcessingFee,
|
|
230
295
|
name: "Processing Fee",
|
|
@@ -234,23 +299,28 @@ export function frontendServiceGetPriceToBookFees(
|
|
|
234
299
|
});
|
|
235
300
|
}
|
|
236
301
|
|
|
237
|
-
allAdditionalFees = allAdditionalFees.filter((fee) => fee.costPQCents > 0);
|
|
238
|
-
|
|
239
302
|
const allAdditionalFeesCents = allAdditionalFees.reduce(
|
|
240
303
|
(sofar, fee) => sofar + fee.costPQCents * fee.quantity,
|
|
241
304
|
0
|
|
242
305
|
);
|
|
243
306
|
|
|
244
307
|
const totalBeforeTaxesCents =
|
|
245
|
-
|
|
308
|
+
subtotalBeforeTaxesCents + allAdditionalFeesCents;
|
|
246
309
|
|
|
247
310
|
return {
|
|
248
311
|
serviceId: serviceRatesAssociation?.serviceId,
|
|
249
312
|
daysToBook: bookingDayResults,
|
|
250
313
|
daysTotalBeforeTaxesCents: daysTotalBeforeTaxesCents,
|
|
314
|
+
subtotalBeforeTaxesCents: subtotalBeforeTaxesCents,
|
|
251
315
|
totalBeforeTaxesCents: totalBeforeTaxesCents,
|
|
252
316
|
additionalFees: allAdditionalFees,
|
|
317
|
+
addOns: usedAddons,
|
|
253
318
|
minimumTimeBlockHours: minimumTimeBlockHours,
|
|
319
|
+
rateOption: rateOption,
|
|
320
|
+
flatRateCents:
|
|
321
|
+
(filteredRates.generalRate?.flatRateCents ?? 0) > 0
|
|
322
|
+
? filteredRates.generalRate?.flatRateCents
|
|
323
|
+
: undefined,
|
|
254
324
|
} as FrontendServiceGetPriceToBookResult;
|
|
255
325
|
}
|
|
256
326
|
|
|
@@ -305,6 +375,7 @@ export function frontendServiceGetPriceToBook(
|
|
|
305
375
|
daysToBookParams: daysToBookParams,
|
|
306
376
|
selectedAttendeeOption: selectedAttendeeOption,
|
|
307
377
|
additionalFees: [],
|
|
378
|
+
addOns: addOns,
|
|
308
379
|
timezone: timezone,
|
|
309
380
|
});
|
|
310
381
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ServiceBookingType, ServiceBookingStatus } from "@prisma/client";
|
|
1
2
|
import {
|
|
2
3
|
ServiceBookingAddOnExt,
|
|
3
4
|
ServiceBookingFeeExt,
|
|
@@ -8,14 +9,24 @@ import {
|
|
|
8
9
|
ServiceBookingExt,
|
|
9
10
|
} from "../../extendedSchemas";
|
|
10
11
|
import { LuxonDateRange } from "../luxonUtils";
|
|
12
|
+
import { FrontendServiceGetPriceToBookResult } from "./frontendServiceBookingUtils";
|
|
13
|
+
import { MakeNullablePropsOptional, MakeOptional } from "../typeUtils";
|
|
11
14
|
|
|
12
15
|
export type ServiceBookingAddOnBase = Omit<
|
|
13
16
|
ServiceBookingAddOnExt,
|
|
14
|
-
|
|
17
|
+
| "id"
|
|
18
|
+
| "serviceBookingDayId"
|
|
19
|
+
| "serviceBookingDay"
|
|
20
|
+
| "serviceBookingId"
|
|
21
|
+
| "serviceBooking"
|
|
15
22
|
>;
|
|
16
23
|
export type ServiceBookingPackageBase = Omit<
|
|
17
24
|
ServiceBookingAddOnExt,
|
|
18
|
-
|
|
25
|
+
| "id"
|
|
26
|
+
| "serviceBookingDayId"
|
|
27
|
+
| "serviceBookingDay"
|
|
28
|
+
| "serviceBookingId"
|
|
29
|
+
| "serviceBooking"
|
|
19
30
|
>;
|
|
20
31
|
export type ServiceBookingFeeBase = Omit<
|
|
21
32
|
ServiceBookingFeeExt,
|
|
@@ -38,6 +49,23 @@ export type ServiceBookingBase = Pick<
|
|
|
38
49
|
"bookedDays" | "timezone" | "additionalFees"
|
|
39
50
|
>;
|
|
40
51
|
|
|
52
|
+
export type ServiceBookingRequiredData = MakeNullablePropsOptional<
|
|
53
|
+
MakeOptional<
|
|
54
|
+
ServiceBookingExt,
|
|
55
|
+
| "id"
|
|
56
|
+
| "isVendorBid"
|
|
57
|
+
| "bashEventId"
|
|
58
|
+
| "vendorEventDetails"
|
|
59
|
+
| "serviceId"
|
|
60
|
+
| "service"
|
|
61
|
+
| "forUserId"
|
|
62
|
+
| "forUser"
|
|
63
|
+
| "creatorId"
|
|
64
|
+
| "creator"
|
|
65
|
+
| "checkout"
|
|
66
|
+
>
|
|
67
|
+
>;
|
|
68
|
+
|
|
41
69
|
export type ServiceBookingAddonBaseT = ServiceBookingAddOnBase; //T for Transformed/Processed aka used for logic
|
|
42
70
|
export type ServiceBookingPackageBaseT = ServiceBookingPackageBase;
|
|
43
71
|
export type ServiceBookingFeeBaseT = ServiceBookingFeeBase;
|
|
@@ -4,51 +4,54 @@ import {
|
|
|
4
4
|
URL_PARAMS_NUMBER_OF_TICKETS_TICKETS_DATE_DELIM,
|
|
5
5
|
URL_PARAMS_TICKET_LIST_DELIM,
|
|
6
6
|
URL_PARAMS_TICKET_TIER_ID_NUMBER_OF_TICKETS_DATE_DELIM,
|
|
7
|
-
URL_PARAMS_TICKETS_DATE_DELIM
|
|
7
|
+
URL_PARAMS_TICKETS_DATE_DELIM,
|
|
8
8
|
} from "../definitions";
|
|
9
|
-
import {
|
|
10
|
-
|
|
9
|
+
import {
|
|
10
|
+
dateTimeFormat,
|
|
11
|
+
dateTimeFromDate,
|
|
12
|
+
dateTimeFromString,
|
|
13
|
+
LUXON_DATETIME_FORMAT_ISO_LIKE,
|
|
14
|
+
} from "./luxonUtils";
|
|
11
15
|
|
|
12
16
|
/**
|
|
13
17
|
* Returns A string structured like: [ticketTierId]__[numberOfTickets];;[date]~~[numberOfTickets];;[date]~~,...
|
|
14
18
|
* @param ticketList A map where the key is the ticketTierId
|
|
15
19
|
*/
|
|
16
|
-
export function ticketListToString(
|
|
20
|
+
export function ticketListToString(
|
|
21
|
+
ticketList: Map<string, NumberOfTicketsForDate[]>
|
|
22
|
+
): string {
|
|
17
23
|
const ticketListArr: string[] = [];
|
|
18
24
|
|
|
19
25
|
for (const [ticketTierId, ticketListArgs] of ticketList.entries()) {
|
|
20
26
|
let ticketsExist = true;
|
|
21
|
-
const ticketArgs: string[] = [
|
|
27
|
+
const ticketArgs: string[] = [
|
|
28
|
+
`${ticketTierId}${URL_PARAMS_TICKET_TIER_ID_NUMBER_OF_TICKETS_DATE_DELIM}`,
|
|
29
|
+
];
|
|
22
30
|
for (const ticketNumAndDates of ticketListArgs) {
|
|
23
31
|
if (ticketNumAndDates.numberOfTickets > 0) {
|
|
24
32
|
ticketsExist = true;
|
|
25
|
-
|
|
33
|
+
|
|
26
34
|
// Ensure ticketDateTime is in ISO format
|
|
27
35
|
let dateTimeStr = ticketNumAndDates.ticketDateTime;
|
|
28
36
|
if (dateTimeStr) {
|
|
29
37
|
// If it's already a string, try to parse it as DateTime and convert to ISO
|
|
30
|
-
const dateTime =
|
|
31
|
-
? DateTime.fromFormat(dateTimeStr, DATETIME_FORMAT_ISO_LIKE).isValid
|
|
32
|
-
? DateTime.fromFormat(dateTimeStr, DATETIME_FORMAT_ISO_LIKE)
|
|
33
|
-
: DateTime.fromISO(dateTimeStr).isValid
|
|
34
|
-
? DateTime.fromISO(dateTimeStr)
|
|
35
|
-
: DateTime.fromJSDate(new Date(dateTimeStr))
|
|
36
|
-
: DateTime.fromJSDate(dateTimeStr as any);
|
|
37
|
-
|
|
38
|
+
const dateTime = dateTimeFromDate(dateTimeStr);
|
|
38
39
|
if (dateTime.isValid) {
|
|
39
|
-
dateTimeStr = dateTime
|
|
40
|
+
dateTimeStr = dateTimeFormat(dateTime) || dateTimeStr;
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
+
|
|
43
44
|
ticketArgs.push(
|
|
44
|
-
`${ticketNumAndDates.numberOfTickets}${URL_PARAMS_TICKETS_DATE_DELIM}${dateTimeStr}`
|
|
45
|
-
|
|
46
|
-
else {
|
|
45
|
+
`${ticketNumAndDates.numberOfTickets}${URL_PARAMS_TICKETS_DATE_DELIM}${dateTimeStr}`
|
|
46
|
+
);
|
|
47
|
+
} else {
|
|
47
48
|
ticketsExist = false;
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
if (ticketsExist) {
|
|
51
|
-
ticketListArr.push(
|
|
52
|
+
ticketListArr.push(
|
|
53
|
+
ticketArgs.join(URL_PARAMS_NUMBER_OF_TICKETS_TICKETS_DATE_DELIM)
|
|
54
|
+
);
|
|
52
55
|
}
|
|
53
56
|
}
|
|
54
57
|
return ticketListArr.join(URL_PARAMS_TICKET_LIST_DELIM);
|
|
@@ -58,7 +61,9 @@ export function ticketListToString(ticketList: Map<string, NumberOfTicketsForDat
|
|
|
58
61
|
* Returns map where the key is the ticketTierId
|
|
59
62
|
* @param ticketListStr A string structured like: [ticketTierId]__[numberOfTickets];;[date]~~[numberOfTickets];;[date]~~...,...
|
|
60
63
|
*/
|
|
61
|
-
export function ticketListStrToTicketList(
|
|
64
|
+
export function ticketListStrToTicketList(
|
|
65
|
+
ticketListStr: string
|
|
66
|
+
): Map<string, NumberOfTicketsForDate[]> {
|
|
62
67
|
const ticketList: Map<string, NumberOfTicketsForDate[]> = new Map();
|
|
63
68
|
|
|
64
69
|
// [ticketTierId]__~~[numberOfTickets];;[date]~~[numberOfTickets];;[date]~~...,...
|
|
@@ -67,22 +72,30 @@ export function ticketListStrToTicketList(ticketListStr: string): Map<string, Nu
|
|
|
67
72
|
ticketListArgs.forEach((tierIdAndTicketNumbersAndDates: string): void => {
|
|
68
73
|
const ticketNumAndDatesArr: NumberOfTicketsForDate[] = [];
|
|
69
74
|
// [ticketTierId]__~~[numberOfTickets];;[date]~~[numberOfTickets];;[date]~~...
|
|
70
|
-
const [ticketTierId, ticketNumAndDatesStr] =
|
|
75
|
+
const [ticketTierId, ticketNumAndDatesStr] =
|
|
76
|
+
tierIdAndTicketNumbersAndDates.split(
|
|
77
|
+
URL_PARAMS_TICKET_TIER_ID_NUMBER_OF_TICKETS_DATE_DELIM
|
|
78
|
+
);
|
|
71
79
|
if (!ticketNumAndDatesArr) {
|
|
72
|
-
throw new Error(
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Could not parse ticketListStr. Maybe it is just an amount?`
|
|
82
|
+
);
|
|
73
83
|
}
|
|
74
84
|
// ~~[numberOfTickets];;[date]~~[numberOfTickets];;[date]~~...
|
|
75
|
-
const ticketNumAndDates = ticketNumAndDatesStr
|
|
76
|
-
|
|
85
|
+
const ticketNumAndDates = ticketNumAndDatesStr
|
|
86
|
+
.split(URL_PARAMS_NUMBER_OF_TICKETS_TICKETS_DATE_DELIM)
|
|
87
|
+
.filter((ticketNumAndDateStr): boolean => !!ticketNumAndDateStr);
|
|
77
88
|
|
|
78
89
|
for (const ticketNumAndDateStr of ticketNumAndDates) {
|
|
79
90
|
// [numberOfTickets];;[date]
|
|
80
|
-
const [numberOfTickets, ticketDateTimeStr] = ticketNumAndDateStr.split(
|
|
91
|
+
const [numberOfTickets, ticketDateTimeStr] = ticketNumAndDateStr.split(
|
|
92
|
+
URL_PARAMS_TICKETS_DATE_DELIM
|
|
93
|
+
);
|
|
81
94
|
const ticketDateTime = DateTime.fromISO(ticketDateTimeStr);
|
|
82
95
|
let ticketDateTimeFormattedStr: string | undefined = undefined;
|
|
83
96
|
|
|
84
97
|
if (ticketDateTime.isValid) {
|
|
85
|
-
ticketDateTimeFormattedStr = ticketDateTime
|
|
98
|
+
ticketDateTimeFormattedStr = dateTimeFormat(ticketDateTime);
|
|
86
99
|
}
|
|
87
100
|
|
|
88
101
|
ticketNumAndDatesArr.push({
|
package/src/utils/typeUtils.ts
CHANGED
|
@@ -6,17 +6,24 @@ export type RemoveCommonProperties<T, U> = keyof (Omit<T, keyof U> &
|
|
|
6
6
|
export type MakeOptional<T, K extends keyof T> = Omit<T, K> &
|
|
7
7
|
Partial<Pick<T, K>>;
|
|
8
8
|
|
|
9
|
+
export type MakeNullablePropsOptional<T> = {
|
|
10
|
+
[K in keyof T as null extends T[K] ? K : never]?:
|
|
11
|
+
| Exclude<T[K], undefined>
|
|
12
|
+
| undefined;
|
|
13
|
+
} & {
|
|
14
|
+
[K in keyof T as null extends T[K] ? never : K]: T[K];
|
|
15
|
+
};
|
|
16
|
+
|
|
9
17
|
export type MakeRequired<T, K extends keyof T> = Omit<T, K> &
|
|
10
18
|
Required<Pick<T, K>>;
|
|
11
19
|
|
|
12
|
-
export type DeepPartial<T> =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
: T;
|
|
20
|
+
export type DeepPartial<T> = T extends Function
|
|
21
|
+
? T
|
|
22
|
+
: T extends Array<infer U>
|
|
23
|
+
? Array<DeepPartial<U>>
|
|
24
|
+
: T extends object
|
|
25
|
+
? { [P in keyof T]?: DeepPartial<T[P]> }
|
|
26
|
+
: T;
|
|
20
27
|
|
|
21
28
|
export type Override<T, U> = Omit<T, keyof U> & U;
|
|
22
29
|
|