@bash-app/bash-common 29.67.0 → 29.69.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.
@@ -0,0 +1,391 @@
1
+ import { ServiceAddon } from "@prisma/client";
2
+ import { ServiceRatesAssociationExt } from "../../extendedSchemas";
3
+ import { ServiceAttendeeOption } from "./attendeeOptionUtils";
4
+ import {
5
+ getServiceRatePricingInfo,
6
+ serviceGetFilteredRates,
7
+ ServiceRatePricingInfo,
8
+ serviceRatesFilter,
9
+ serviceRatesHasRates,
10
+ ServiceRatesLuxon,
11
+ } from "./serviceRateUtils";
12
+ import {
13
+ dateTimeDiffHours,
14
+ dateTimeRangeHours,
15
+ LuxonDateRange,
16
+ } from "../luxonUtils";
17
+
18
+ export const SERVICE_BOOKING_PROCESSING_FEE_PERCENT = 0.15;
19
+
20
+ export interface ServiceAddonInput extends ServiceAddon {
21
+ chosenQuantity?: number;
22
+ }
23
+
24
+ export type ServiceBookingFeeType =
25
+ | "addOnFees"
26
+ | "cleaningFee"
27
+ | "processingFee";
28
+
29
+ export const ServiceBookingFeeType = {
30
+ addonFees: "addOnFees",
31
+ cleaningFee: "cleaningFee",
32
+ processingFee: "processingFee",
33
+ } as const;
34
+
35
+ export interface ServiceBookingFee {
36
+ name: string;
37
+ type: ServiceBookingFeeType;
38
+ fee: number;
39
+ }
40
+
41
+ export interface ServiceBookingDayInfoParams {
42
+ dateTimeRange: LuxonDateRange;
43
+ fees: ServiceBookingFee[];
44
+ addOns: ServiceAddonInput[];
45
+ }
46
+
47
+ export interface ServiceBookingDayInfo {
48
+ priceBreakdown: ServiceRatePricingInfo[];
49
+ discounts: ServiceRatePricingInfo[];
50
+
51
+ dateTimeRange: LuxonDateRange;
52
+
53
+ baseCostUndiscounted: number; //rate purely based on generalHourly
54
+ baseCostDiscounted: number;
55
+ baseCostDiscount: number;
56
+
57
+ fees: ServiceBookingFee[];
58
+ totalBeforeTaxes: number;
59
+
60
+ addOns: ServiceAddonInput[];
61
+ }
62
+
63
+ export interface ServiceGetPriceToBookResult {
64
+ serviceId: string;
65
+
66
+ daysToBook: ServiceBookingDayInfo[];
67
+
68
+ baseCostUndiscounted: number; //rate purely based on generalHourly
69
+ baseCostDiscounted: number;
70
+ baseCostDiscount: number;
71
+
72
+ additionalFees: ServiceBookingFee[];
73
+ totalBeforeTaxes: number;
74
+
75
+ minimumTimeBlockHours?: number; //only valid on generalRate
76
+ totalDurationHours: number;
77
+ durationHoursPerDay: number;
78
+
79
+ baseHourlyRate: number;
80
+ }
81
+
82
+ export interface ServiceGetBookingDayInfoParams {
83
+ filteredRates: ServiceRatesLuxon;
84
+ // overlappingBusinessHours: LuxonDateRange[] | null;
85
+
86
+ timezone: string | null | undefined;
87
+ daysToBookParams: ServiceBookingDayInfoParams[];
88
+ }
89
+
90
+ export function serviceGetBookingDayInfo({
91
+ filteredRates,
92
+ daysToBookParams,
93
+ // overlappingBusinessHours,
94
+ timezone,
95
+ }: ServiceGetBookingDayInfoParams): ServiceBookingDayInfo[] {
96
+ const bookingDayResults: ServiceBookingDayInfo[] = daysToBookParams.map(
97
+ (day) => {
98
+ const rates = serviceRatesFilter(filteredRates, [day.dateTimeRange]);
99
+ const { generalRate, specialRates, dailyRates } = rates;
100
+
101
+ let priceBreakdown: ServiceRatePricingInfo[] = [];
102
+ const durationHours = dateTimeDiffHours(
103
+ day.dateTimeRange.start,
104
+ day.dateTimeRange.end
105
+ );
106
+ // console.log(
107
+ // `serviceGetBookingDayInfo: durationHours: ${JSON.stringify(
108
+ // durationHours
109
+ // )}, dtRange: ${JSON.stringify(day.dateTimeRange)}`
110
+ // );
111
+ let hoursRemaining = durationHours;
112
+ let currentRange = { ...day.dateTimeRange };
113
+ let baseCostDiscounted = 0;
114
+ while (serviceRatesHasRates(rates) && hoursRemaining > 0) {
115
+ const pricingInfo = getServiceRatePricingInfo(
116
+ currentRange,
117
+ generalRate,
118
+ dailyRates,
119
+ specialRates,
120
+ // overlappingBusinessHours,
121
+ timezone,
122
+ hoursRemaining
123
+ );
124
+
125
+ if (pricingInfo.hours == 0 || pricingInfo.unitCount == 0) {
126
+ throw new Error(`Invalid pricingInfo`);
127
+ }
128
+
129
+ priceBreakdown.push(pricingInfo);
130
+
131
+ // currentRange.start = currentRange.start.plus({
132
+ // hours: pricingInfo.hours,
133
+ // });
134
+ currentRange.start = pricingInfo.dateTimeRange.end;
135
+ hoursRemaining -= pricingInfo.hours;
136
+ baseCostDiscounted += pricingInfo.piTotal;
137
+
138
+ if (pricingInfo.rateType == "Special") {
139
+ specialRates.pop();
140
+ } else if (pricingInfo.rateType == "Weekday") {
141
+ dailyRates.pop();
142
+ }
143
+ }
144
+
145
+ const baseHourlyRate = generalRate?.hourlyRate ?? 0;
146
+
147
+ const discounts = priceBreakdown
148
+ .filter(
149
+ (pi) => !(pi.pricingType == "Hourly" && pi.rateType == "General")
150
+ )
151
+ .map((pi) => {
152
+ return {
153
+ ...pi,
154
+ piTotal: pi.hours * baseHourlyRate - pi.piTotal,
155
+ };
156
+ })
157
+ .filter((pi) => pi.piTotal > 0);
158
+
159
+ const baseCostUndiscounted = baseHourlyRate * durationHours;
160
+ const baseCostDiscount =
161
+ baseCostUndiscounted == 0
162
+ ? 0
163
+ : 1 - baseCostDiscounted / baseCostUndiscounted;
164
+
165
+ const totalBeforeTaxes =
166
+ baseCostDiscounted +
167
+ day.fees.reduce((sofar, fee) => sofar + fee.fee, 0);
168
+
169
+ return {
170
+ baseCostDiscount: baseCostDiscount,
171
+ baseCostDiscounted: baseCostDiscounted,
172
+ baseCostUndiscounted: baseCostUndiscounted,
173
+ discounts: discounts,
174
+ priceBreakdown: priceBreakdown,
175
+ dateTimeRange: day.dateTimeRange,
176
+ fees: day.fees,
177
+ addOns: day.addOns,
178
+ totalBeforeTaxes: totalBeforeTaxes,
179
+ } as ServiceBookingDayInfo;
180
+ }
181
+ );
182
+
183
+ return bookingDayResults;
184
+ }
185
+
186
+ export interface ServiceGetPriceToBookFeesParams {
187
+ daysToBookParams: ServiceBookingDayInfoParams[];
188
+
189
+ selectedAttendeeOption: ServiceAttendeeOption;
190
+
191
+ // overlappingBusinessHours: LuxonDateRange[] | null;
192
+ timezone: string | null | undefined;
193
+ }
194
+
195
+ export function serviceGetPriceToBookFees(
196
+ serviceRatesAssociation: ServiceRatesAssociationExt | null | undefined,
197
+ {
198
+ daysToBookParams,
199
+ selectedAttendeeOption,
200
+ // overlappingBusinessHours,
201
+ timezone,
202
+ }: ServiceGetPriceToBookFeesParams
203
+ ): ServiceGetPriceToBookResult {
204
+ const bookingDateTimeRanges = daysToBookParams.map(
205
+ (daysToBookParams) => daysToBookParams.dateTimeRange
206
+ );
207
+ const filteredRates = serviceGetFilteredRates(
208
+ serviceRatesAssociation,
209
+ bookingDateTimeRanges,
210
+ timezone
211
+ );
212
+
213
+ const bookingDayResults: ServiceBookingDayInfo[] = serviceGetBookingDayInfo({
214
+ filteredRates: filteredRates,
215
+ daysToBookParams: daysToBookParams,
216
+ // overlappingBusinessHours: overlappingBusinessHours,
217
+ timezone: timezone,
218
+ });
219
+
220
+ const durationHours = bookingDateTimeRanges.reduce((sofar, curr) => {
221
+ return sofar + dateTimeRangeHours(curr);
222
+ }, 0);
223
+ const bookingsTotalBeforeTaxes = bookingDayResults.reduce((sofar, curr) => {
224
+ return sofar + curr.totalBeforeTaxes;
225
+ }, 0);
226
+ const baseHourlyRate =
227
+ serviceRatesAssociation?.serviceGeneralRates?.hourlyRate ?? 0;
228
+
229
+ const baseCostUndiscounted = baseHourlyRate * durationHours;
230
+ const baseCostDiscount =
231
+ baseCostUndiscounted == 0
232
+ ? 0
233
+ : 1 - bookingsTotalBeforeTaxes / baseCostUndiscounted;
234
+
235
+ const minimumTimeBlockHours =
236
+ serviceRatesAssociation?.serviceGeneralRates?.minimumTimeBlockHours;
237
+
238
+ const attendeeRate = selectedAttendeeOption.rate * durationHours;
239
+
240
+ const processingFee =
241
+ SERVICE_BOOKING_PROCESSING_FEE_PERCENT * bookingsTotalBeforeTaxes;
242
+ const additionalFees = (
243
+ [
244
+ {
245
+ name: "Processing Fee",
246
+ type: "processingFee",
247
+ fee: processingFee,
248
+ },
249
+ ] as ServiceBookingFee[]
250
+ ).filter((fee) => fee.fee > 0);
251
+
252
+ const totalBeforeTaxes =
253
+ bookingsTotalBeforeTaxes +
254
+ additionalFees.reduce((sofar, fee) => sofar + fee.fee, 0);
255
+
256
+ return {
257
+ serviceId: serviceRatesAssociation?.serviceId,
258
+ daysToBook: bookingDayResults,
259
+ baseCostUndiscounted: baseCostUndiscounted,
260
+ baseCostDiscounted: bookingsTotalBeforeTaxes,
261
+ baseCostDiscount: baseCostDiscount,
262
+ totalBeforeTaxes: totalBeforeTaxes,
263
+ additionalFees: additionalFees,
264
+ baseHourlyRate: baseHourlyRate,
265
+ totalDurationHours: durationHours,
266
+ minimumTimeBlockHours: minimumTimeBlockHours,
267
+ durationHoursPerDay: dateTimeRangeHours(bookingDateTimeRanges[0]),
268
+ } as ServiceGetPriceToBookResult;
269
+ }
270
+
271
+ export interface ServiceGetPriceToBookParams {
272
+ addOns: ServiceAddonInput[];
273
+
274
+ selectedAttendeeOption: ServiceAttendeeOption;
275
+
276
+ bookingDateTimeRanges: LuxonDateRange[];
277
+
278
+ // overlappingBusinessHours: LuxonDateRange[] | null;
279
+ timezone: string | null | undefined;
280
+ }
281
+
282
+ export function serviceGetPriceToBook(
283
+ serviceRatesAssociation: ServiceRatesAssociationExt | null | undefined,
284
+ {
285
+ addOns,
286
+ selectedAttendeeOption,
287
+ bookingDateTimeRanges, //list of dateTime ranges to book
288
+ // overlappingBusinessHours,
289
+ timezone,
290
+ }: ServiceGetPriceToBookParams
291
+ ): ServiceGetPriceToBookResult {
292
+ const cleaningFee =
293
+ serviceRatesAssociation?.serviceGeneralRates?.cleaningFeePerBooking ?? 0;
294
+
295
+ const addOnTotal = addOns.reduce((sum, addOn) => {
296
+ return sum + (addOn.chosenQuantity || 0) * addOn.price;
297
+ }, 0);
298
+
299
+ const feesPerBooking = (
300
+ [
301
+ {
302
+ name: "AddOn Total",
303
+ type: "addOnFees",
304
+ fee: addOnTotal,
305
+ },
306
+ {
307
+ name: "Cleaning Fee",
308
+ type: "cleaningFee",
309
+ fee: cleaningFee,
310
+ },
311
+ ] as ServiceBookingFee[]
312
+ ).filter((fee) => fee.fee > 0);
313
+
314
+ const daysToBookParams = bookingDateTimeRanges.map(
315
+ (dtRange): ServiceBookingDayInfoParams => {
316
+ return {
317
+ dateTimeRange: dtRange,
318
+ fees: feesPerBooking,
319
+ addOns: addOns,
320
+ };
321
+ }
322
+ );
323
+
324
+ return serviceGetPriceToBookFees(serviceRatesAssociation, {
325
+ daysToBookParams: daysToBookParams,
326
+ selectedAttendeeOption: selectedAttendeeOption,
327
+ timezone: timezone,
328
+ });
329
+ }
330
+
331
+ export interface ServiceGetPriceToBookExtDay {
332
+ dateRange: LuxonDateRange;
333
+ addOns: ServiceAddonInput[];
334
+ }
335
+
336
+ export interface ServiceGetPriceToBookExtParams {
337
+ selectedAttendeeOption: ServiceAttendeeOption;
338
+
339
+ bookedDays: ServiceGetPriceToBookExtDay[];
340
+
341
+ // overlappingBusinessHours: LuxonDateRange[] | null;
342
+ timezone: string | null | undefined;
343
+ }
344
+
345
+ export function serviceGetPriceToBookExt(
346
+ serviceRatesAssociation: ServiceRatesAssociationExt | null | undefined,
347
+ {
348
+ selectedAttendeeOption,
349
+ bookedDays, //list of dateTime ranges to book
350
+ // overlappingBusinessHours,
351
+ timezone,
352
+ }: ServiceGetPriceToBookExtParams
353
+ ): ServiceGetPriceToBookResult {
354
+ const cleaningFee =
355
+ serviceRatesAssociation?.serviceGeneralRates?.cleaningFeePerBooking ?? 0;
356
+
357
+ const daysToBookParams = bookedDays.map(
358
+ (day): ServiceBookingDayInfoParams => {
359
+ const addOnTotal = day.addOns.reduce((sum, addOn) => {
360
+ return sum + (addOn.chosenQuantity || 0) * addOn.price;
361
+ }, 0);
362
+
363
+ const feesPerBooking = (
364
+ [
365
+ {
366
+ name: "AddOn Total",
367
+ type: "addOnFees",
368
+ fee: addOnTotal,
369
+ },
370
+ {
371
+ name: "Cleaning Fee",
372
+ type: "cleaningFee",
373
+ fee: cleaningFee,
374
+ },
375
+ ] as ServiceBookingFee[]
376
+ ).filter((fee) => fee.fee > 0);
377
+
378
+ return {
379
+ dateTimeRange: day.dateRange,
380
+ fees: feesPerBooking,
381
+ addOns: day.addOns,
382
+ };
383
+ }
384
+ );
385
+
386
+ return serviceGetPriceToBookFees(serviceRatesAssociation, {
387
+ daysToBookParams: daysToBookParams,
388
+ selectedAttendeeOption: selectedAttendeeOption,
389
+ timezone: timezone,
390
+ });
391
+ }
@@ -0,0 +1,51 @@
1
+ import { ServiceRate } from "@prisma/client";
2
+ import { ServiceExt, ServiceSpecialRatesExt } from "../../extendedSchemas";
3
+ import {
4
+ specialRateConvertToDb,
5
+ generalRatesConvertRatesToDB,
6
+ specialRateConvertFromDb,
7
+ generalRatesConvertRatesFromDB,
8
+ } from "./serviceRateDBUtils";
9
+
10
+ export function serviceToDb(service: Partial<ServiceExt>): Partial<ServiceExt> {
11
+ if (service.serviceRatesAssociation) {
12
+ service.serviceRatesAssociation.serviceSpecialRates =
13
+ service.serviceRatesAssociation.serviceSpecialRates?.map(
14
+ (
15
+ serviceSpecialRate: Partial<ServiceSpecialRatesExt>
16
+ ): ServiceSpecialRatesExt => {
17
+ return specialRateConvertToDb(
18
+ serviceSpecialRate
19
+ ) as ServiceSpecialRatesExt;
20
+ }
21
+ );
22
+ if (service.serviceRatesAssociation.serviceGeneralRates) {
23
+ service.serviceRatesAssociation.serviceGeneralRates =
24
+ generalRatesConvertRatesToDB(
25
+ service.serviceRatesAssociation.serviceGeneralRates
26
+ ) as ServiceRate;
27
+ }
28
+ }
29
+ return service;
30
+ }
31
+
32
+ export function serviceFromDb(service: ServiceExt): ServiceExt {
33
+ if (service.serviceRatesAssociation) {
34
+ service.serviceRatesAssociation.serviceSpecialRates =
35
+ service.serviceRatesAssociation.serviceSpecialRates?.map(
36
+ (
37
+ serviceSpecialRate: ServiceSpecialRatesExt
38
+ ): ServiceSpecialRatesExt => {
39
+ return specialRateConvertFromDb(serviceSpecialRate);
40
+ }
41
+ );
42
+ if (service.serviceRatesAssociation.serviceGeneralRates) {
43
+ console.log("updating general rates from db");
44
+ service.serviceRatesAssociation.serviceGeneralRates =
45
+ generalRatesConvertRatesFromDB(
46
+ service.serviceRatesAssociation.serviceGeneralRates
47
+ );
48
+ }
49
+ }
50
+ return service;
51
+ }
@@ -0,0 +1,179 @@
1
+ import { ServiceRate } from "@prisma/client";
2
+ import { ApiResult } from "../../definitions";
3
+ import { ServiceSpecialRatesExt } from "../../extendedSchemas";
4
+ import { convertDollarsToCents, convertCentsToDollars } from "../paymentUtils";
5
+
6
+ export function specialRateConvertToDb(
7
+ specialRate: Partial<ServiceSpecialRatesExt>
8
+ ): Partial<ServiceSpecialRatesExt> {
9
+ return specialRateConvertRatesToDB(
10
+ specialRateConvertDateStringsToDb(structuredClone(specialRate))
11
+ );
12
+ }
13
+
14
+ export function specialRateConvertFromDb(
15
+ specialRate: ServiceSpecialRatesExt
16
+ ): ServiceSpecialRatesExt {
17
+ // console.log(`specialRateConvertFromDb`);
18
+ return specialRateConvertRatesFromDB(
19
+ specialRateConvertDateStringsFromDb(specialRate)
20
+ );
21
+ }
22
+
23
+ function specialRateConvertDateStringsToDb(
24
+ specialRate: Partial<ServiceSpecialRatesExt>
25
+ ): Partial<ServiceSpecialRatesExt> {
26
+ // const oldStart = new Date(specialRate.startDate!);
27
+ // const oldEnd = new Date(specialRate.endDate!);
28
+
29
+ // specialRate.startDate = dateTimeLocalToUTCDate(specialRate.startDate!);
30
+ // specialRate.endDate = dateTimeLocalToUTCDate(specialRate.endDate!);
31
+ // specialRate.startDate = normalizeDate(specialRate.startDate!);
32
+ // specialRate.endDate = normalizeDate(specialRate.endDate!);
33
+
34
+ // const localStart = dateTimeLocalFromUTCDate(new Date(specialRate.startDate!));
35
+ // const localEnd = dateTimeLocalFromUTCDate(new Date(specialRate.endDate!));
36
+
37
+ // console.log(
38
+ // `specialRateConvertDateStringsToDb oldStart: ${oldStart?.toString()}`
39
+ // );
40
+ // console.log(
41
+ // `specialRateConvertDateStringsToDb oldEnd: ${oldEnd?.toString()}`
42
+ // );
43
+ // console.log(
44
+ // `specialRateConvertDateStringsToDb specialRate.startDate: ${specialRate.startDate?.toString()}`
45
+ // );
46
+ // console.log(
47
+ // `specialRateConvertDateStringsToDb specialRate.endDate: ${specialRate.endDate?.toString()}`
48
+ // );
49
+ // console.log(
50
+ // `specialRateConvertDateStringsToDb localStart: ${localStart?.toString()}`
51
+ // );
52
+ // console.log(
53
+ // `specialRateConvertDateStringsToDb localEnd: ${localEnd?.toString()}`
54
+ // );
55
+
56
+ // const a = 3 + 4;
57
+
58
+ // specialRate.startDate = specialRate.startDate.toISOString();
59
+ // specialRate.endDate = specialRate.endDate.toISOString();
60
+ // return specialRate;
61
+ return specialRate;
62
+ }
63
+
64
+ function specialRateConvertDateStringsFromDb(
65
+ specialRate: ServiceSpecialRatesExt
66
+ ): ServiceSpecialRatesExt {
67
+ // specialRate.startDate = localDateToUTC(new Date(specialRate.startDate));
68
+ // specialRate.endDate = localDateToUTC(new Date(specialRate.endDate));
69
+
70
+ //this shouldnt even be necessary as javascript dates are always stored in UTC timezone (they lack timezone information)
71
+ // specialRate.startDate = ensureDateTimeIsLocalDateTime(
72
+ // new Date(specialRate.startDate)
73
+ // ).toDate();
74
+ // specialRate.endDate = ensureDateTimeIsLocalDateTime(
75
+ // new Date(specialRate.endDate)
76
+ // ).toDate();
77
+
78
+ specialRate.startDate = new Date(specialRate.startDate);
79
+ specialRate.endDate = new Date(specialRate.endDate);
80
+
81
+ // const oldStart = new Date(specialRate.startDate!);
82
+ // const oldEnd = new Date(specialRate.endDate!);
83
+
84
+ // specialRate.startDate = dateTimeLocalFromUTCDate(
85
+ // new Date(specialRate.startDate)
86
+ // );
87
+ // specialRate.endDate = dateTimeLocalFromUTCDate(new Date(specialRate.endDate));
88
+
89
+ // // specialRate.startDate = ensureDateTimeIsLocalDateTime(
90
+ // // new Date(specialRate.startDate)
91
+ // // ).toDate();
92
+ // // specialRate.endDate = ensureDateTimeIsLocalDateTime(
93
+ // // new Date(specialRate.endDate)
94
+ // // ).toDate();
95
+
96
+ // // specialRate.startDate = localDateToUTC(new Date(specialRate.startDate));
97
+ // // specialRate.endDate = localDateToUTC(new Date(specialRate.endDate));
98
+
99
+ // const date2 = normalizeDate(specialRate.startDate)!;
100
+ // const date3 = normalizeDate(specialRate.endDate)!;
101
+
102
+ // const a = 3 + 4;
103
+ return specialRate;
104
+ }
105
+
106
+ function specialRateConvertRatesToDB(
107
+ specialRate: Partial<ServiceSpecialRatesExt>
108
+ ): Partial<ServiceSpecialRatesExt> {
109
+ if (specialRate.serviceRate) {
110
+ specialRate.serviceRate = generalRatesConvertRatesToDB(
111
+ specialRate.serviceRate
112
+ ) as ServiceRate;
113
+ }
114
+ return specialRate;
115
+ }
116
+
117
+ function specialRateConvertRatesFromDB(
118
+ specialRate: ServiceSpecialRatesExt
119
+ ): ServiceSpecialRatesExt {
120
+ if (specialRate.serviceRate) {
121
+ specialRate.serviceRate = generalRatesConvertRatesFromDB(
122
+ specialRate.serviceRate
123
+ );
124
+ }
125
+ return specialRate;
126
+ }
127
+
128
+ export function generalRatesConvertRatesToDB(
129
+ generalRate: ServiceRate
130
+ ): ServiceRate {
131
+ return generalRatesConvertRatesToDBHelper(structuredClone(generalRate));
132
+ }
133
+
134
+ export function generalRatesConvertRatesToDBHelper(
135
+ generalRate: ServiceRate
136
+ ): ServiceRate {
137
+ // console.log(
138
+ // `generalRatesConvertRatesToDBHelper A: ${JSON.stringify(generalRate)}`
139
+ // );
140
+ if (generalRate?.dailyRate) {
141
+ generalRate.dailyRate = convertDollarsToCents(generalRate.dailyRate);
142
+ }
143
+ if (generalRate?.hourlyRate) {
144
+ generalRate.hourlyRate = convertDollarsToCents(generalRate.hourlyRate);
145
+ }
146
+ if (generalRate?.cleaningFeePerBooking) {
147
+ generalRate.cleaningFeePerBooking = convertDollarsToCents(
148
+ generalRate.cleaningFeePerBooking
149
+ );
150
+ }
151
+ // console.log(
152
+ // `generalRatesConvertRatesToDBHelper B: ${JSON.stringify(generalRate)}`
153
+ // );
154
+ return generalRate;
155
+ }
156
+
157
+ export function generalRatesConvertRatesFromDB(
158
+ generalRate: ServiceRate
159
+ ): ServiceRate {
160
+ // console.log(
161
+ // `generalRatesConvertRatesFromDB A: ${JSON.stringify(generalRate)}`
162
+ // );
163
+ // console.log(`specialRateConvertRatesFromDB`);
164
+ if (generalRate?.dailyRate) {
165
+ generalRate.dailyRate = convertCentsToDollars(generalRate.dailyRate);
166
+ }
167
+ if (generalRate?.hourlyRate) {
168
+ generalRate.hourlyRate = convertCentsToDollars(generalRate.hourlyRate);
169
+ }
170
+ if (generalRate?.cleaningFeePerBooking) {
171
+ generalRate.cleaningFeePerBooking = convertCentsToDollars(
172
+ generalRate.cleaningFeePerBooking
173
+ );
174
+ }
175
+ // console.log(
176
+ // `generalRatesConvertRatesFromDB B: ${JSON.stringify(generalRate)}`
177
+ // );
178
+ return generalRate;
179
+ }