Fixing count and redundant checks for recurring (#5426)
Co-authored-by: Alex van Andel <me@alexvanandel.com>possible-fix/sentry^2
parent
97b99deb5d
commit
7d1fb7c659
|
@ -3,10 +3,18 @@ import { AppsStatus } from "@calcom/types/Calendar";
|
||||||
import * as fetch from "@lib/core/http/fetch-wrapper";
|
import * as fetch from "@lib/core/http/fetch-wrapper";
|
||||||
import { BookingCreateBody, BookingResponse } from "@lib/types/booking";
|
import { BookingCreateBody, BookingResponse } from "@lib/types/booking";
|
||||||
|
|
||||||
type ExtendedBookingCreateBody = BookingCreateBody & { noEmail?: boolean; recurringCount?: number };
|
type ExtendedBookingCreateBody = BookingCreateBody & {
|
||||||
|
noEmail?: boolean;
|
||||||
|
recurringCount?: number;
|
||||||
|
appsStatus?: AppsStatus[] | undefined;
|
||||||
|
allRecurringDates?: string[];
|
||||||
|
currentRecurringIndex?: number;
|
||||||
|
};
|
||||||
|
|
||||||
const createRecurringBooking = async (data: ExtendedBookingCreateBody[]) => {
|
const createRecurringBooking = async (data: ExtendedBookingCreateBody[]) => {
|
||||||
const createdBookings: BookingResponse[] = [];
|
const createdBookings: BookingResponse[] = [];
|
||||||
|
const allRecurringDates: string[] = data.map((booking) => booking.start);
|
||||||
|
let appsStatus: AppsStatus[] | undefined = undefined;
|
||||||
// Reversing to accumulate results for noEmail instances first, to then lastly, create the
|
// Reversing to accumulate results for noEmail instances first, to then lastly, create the
|
||||||
// emailed booking taking into account accumulated results to send app status accurately
|
// emailed booking taking into account accumulated results to send app status accurately
|
||||||
for (let key = 0; key < data.length; key++) {
|
for (let key = 0; key < data.length; key++) {
|
||||||
|
@ -23,22 +31,16 @@ const createRecurringBooking = async (data: ExtendedBookingCreateBody[]) => {
|
||||||
}
|
}
|
||||||
return prev;
|
return prev;
|
||||||
}, {} as { [key: string]: AppsStatus });
|
}, {} as { [key: string]: AppsStatus });
|
||||||
const appsStatus = Object.values(calcAppsStatus);
|
appsStatus = Object.values(calcAppsStatus);
|
||||||
const response = await fetch.post<
|
|
||||||
ExtendedBookingCreateBody & { appsStatus: AppsStatus[] },
|
|
||||||
BookingResponse
|
|
||||||
>("/api/book/event", {
|
|
||||||
...booking,
|
|
||||||
appsStatus,
|
|
||||||
});
|
|
||||||
createdBookings.push(response);
|
|
||||||
} else {
|
|
||||||
const response = await fetch.post<ExtendedBookingCreateBody, BookingResponse>("/api/book/event", {
|
|
||||||
...booking,
|
|
||||||
noEmail: true,
|
|
||||||
});
|
|
||||||
createdBookings.push(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const response = await fetch.post<ExtendedBookingCreateBody, BookingResponse>("/api/book/event", {
|
||||||
|
...booking,
|
||||||
|
appsStatus,
|
||||||
|
allRecurringDates,
|
||||||
|
currentRecurringIndex: key,
|
||||||
|
});
|
||||||
|
createdBookings.push(response);
|
||||||
}
|
}
|
||||||
return createdBookings;
|
return createdBookings;
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,6 @@ import {
|
||||||
import async from "async";
|
import async from "async";
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
import type { NextApiRequest } from "next";
|
import type { NextApiRequest } from "next";
|
||||||
import { RRule } from "rrule";
|
|
||||||
import short from "short-uuid";
|
import short from "short-uuid";
|
||||||
import { v5 as uuidv5 } from "uuid";
|
import { v5 as uuidv5 } from "uuid";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
@ -212,7 +211,11 @@ async function ensureAvailableUsers(
|
||||||
eventType: Awaited<ReturnType<typeof getEventTypesFromDB>> & {
|
eventType: Awaited<ReturnType<typeof getEventTypesFromDB>> & {
|
||||||
users: User[];
|
users: User[];
|
||||||
},
|
},
|
||||||
input: { dateFrom: string; dateTo: string }
|
input: { dateFrom: string; dateTo: string },
|
||||||
|
recurringDatesInfo?: {
|
||||||
|
allRecurringDates: string[] | undefined;
|
||||||
|
currentRecurringIndex: number | undefined;
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
const availableUsers: typeof eventType.users = [];
|
const availableUsers: typeof eventType.users = [];
|
||||||
/** Let's start checking for availability */
|
/** Let's start checking for availability */
|
||||||
|
@ -243,9 +246,12 @@ async function ensureAvailableUsers(
|
||||||
|
|
||||||
let foundConflict = false;
|
let foundConflict = false;
|
||||||
try {
|
try {
|
||||||
if (eventType.recurringEvent) {
|
if (
|
||||||
const recurringEvent = parseRecurringEvent(eventType.recurringEvent);
|
eventType.recurringEvent &&
|
||||||
const allBookingDates = new RRule({ dtstart: new Date(input.dateFrom), ...recurringEvent }).all();
|
recurringDatesInfo?.currentRecurringIndex === 0 &&
|
||||||
|
recurringDatesInfo.allRecurringDates
|
||||||
|
) {
|
||||||
|
const allBookingDates = recurringDatesInfo.allRecurringDates.map((strDate) => new Date(strDate));
|
||||||
// Go through each date for the recurring event and check if each one's availability
|
// Go through each date for the recurring event and check if each one's availability
|
||||||
// DONE: Decreased computational complexity from O(2^n) to O(n) by refactoring this loop to stop
|
// DONE: Decreased computational complexity from O(2^n) to O(n) by refactoring this loop to stop
|
||||||
// running at the first unavailable time.
|
// running at the first unavailable time.
|
||||||
|
@ -277,6 +283,8 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
recurringCount,
|
recurringCount,
|
||||||
|
allRecurringDates,
|
||||||
|
currentRecurringIndex,
|
||||||
noEmail,
|
noEmail,
|
||||||
eventTypeSlug,
|
eventTypeSlug,
|
||||||
eventTypeId,
|
eventTypeId,
|
||||||
|
@ -376,10 +384,20 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) {
|
||||||
{
|
{
|
||||||
...eventType,
|
...eventType,
|
||||||
users,
|
users,
|
||||||
|
...(eventType.recurringEvent && {
|
||||||
|
recurringEvent: {
|
||||||
|
...eventType.recurringEvent,
|
||||||
|
count: recurringCount || eventType.recurringEvent.count,
|
||||||
|
},
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
dateFrom: reqBody.start,
|
dateFrom: reqBody.start,
|
||||||
dateTo: reqBody.end,
|
dateTo: reqBody.end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
allRecurringDates,
|
||||||
|
currentRecurringIndex,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -513,7 +513,6 @@ export default class CloseCom {
|
||||||
description: "Generic Lead for Contacts created by Cal.com",
|
description: "Generic Lead for Contacts created by Cal.com",
|
||||||
}
|
}
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
debugger;
|
|
||||||
const closeComLeadNames = await this.lead.list({ query: { _fields: ["name", "id"] } });
|
const closeComLeadNames = await this.lead.list({ query: { _fields: ["name", "id"] } });
|
||||||
const searchLeadFromCalCom = closeComLeadNames.data.filter((lead) => lead.name === leadInfo.companyName);
|
const searchLeadFromCalCom = closeComLeadNames.data.filter((lead) => lead.name === leadInfo.companyName);
|
||||||
if (searchLeadFromCalCom.length === 0) {
|
if (searchLeadFromCalCom.length === 0) {
|
||||||
|
|
|
@ -150,6 +150,8 @@ export const extendedBookingCreateBody = bookingCreateBodySchema.merge(
|
||||||
z.object({
|
z.object({
|
||||||
noEmail: z.boolean().optional(),
|
noEmail: z.boolean().optional(),
|
||||||
recurringCount: z.number().optional(),
|
recurringCount: z.number().optional(),
|
||||||
|
allRecurringDates: z.string().array().optional(),
|
||||||
|
currentRecurringIndex: z.number().optional(),
|
||||||
rescheduleReason: z.string().optional(),
|
rescheduleReason: z.string().optional(),
|
||||||
smsReminderNumber: z.string().optional(),
|
smsReminderNumber: z.string().optional(),
|
||||||
appsStatus: z
|
appsStatus: z
|
||||||
|
|
Loading…
Reference in New Issue