* feat: Cal.ai Self Serve #2 * chore: fix import and remove logs * fix: update checkout session * fix: type errors and test * fix: imports * fix: type err * fix: type error * fix: tests * chore: save progress * fix: workflow flow * fix: workflow update bug * tests: add unit tests for retell ai webhoo * fix: status code * fix: test and delete bug * fix: add dynamic variables * fix: type err * chore: update unit test * fix: type error * chore: update default prompt * fix: type errors * fix: workflow permissions * fix: workflow page * fix: translations * feat: add call duration * chore: add booking uid * fix: button positioning * chore: update tests * chore: improvements * chore: some more improvements * refactor: improvements * refactor: code feedback * refactor: improvements * feat: enable credits for orgs (#23077) * Show credits UI for orgs * fix stripe callback url when buying credits * give orgs 20% credits * add test for calulating credits --------- Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> * fix: types * fix: types * chore: error * fix: type error * fix: type error * chore: mock env * feat: add idempotency key to prevent double charging * chore: add userId and teamId * fix: skip inbound calls * chore: update tests * feat: add feature flag for voice agent * feat: finish test call and other improvements * chore: add alert * chore: update .env.example * chore: improvements * fix: update tests * refactor: remove un necessary * feat: add setup badge * chore: improvements * fix: use referene id * chore: improvements * fix: type error * fix: type * refactor: change pricing logic * refactor: update tests * fix: conflicts * fix: billing link for orgs * fix: types * refactor: move feature flag up * fix: alert and test call credit check * fix: update unit tests * fix: feedback * refactor: improvements * refactor: move handlers to separate files * fix: types * fix: missing import * fix: type * refactor: change general tools functions handling * refactor: use repository * refactor: improvements * fix: types * fix: type errorr * fix: auth check * feat: add creditFor * fix: update defualt prompt * fix: throw error on frontend * fix: update unit tests * fix: use deleteAllWorkflowReminders * refactor: add connect phone number * refactor: improvements * chore: translation * chore: update message * chore: translation * design improvements buy number dialog * add translation for error message * use translation key in error message * refactor: improve connect phone number tab * feat: support un saved workflow to tests * chore: remove un used * fix: remove un used * fix: remove un used * refactor: similify billing --------- Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: Keith Williams <keithwillcode@gmail.com>
153 lines
4.2 KiB
TypeScript
153 lines
4.2 KiB
TypeScript
import { createDefaultAIPhoneServiceProvider } from "@calcom/features/calAIPhone";
|
|
import { WorkflowRepository } from "@calcom/lib/server/repository/workflow";
|
|
import { prisma } from "@calcom/prisma";
|
|
import { WorkflowActions } from "@calcom/prisma/enums";
|
|
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
|
|
|
import { TRPCError } from "@trpc/server";
|
|
|
|
import type { TDeleteInputSchema } from "./delete.schema";
|
|
import { isAuthorized, removeSmsReminderFieldForEventTypes } from "./util";
|
|
|
|
type DeleteOptions = {
|
|
ctx: {
|
|
user: NonNullable<TrpcSessionUser>;
|
|
};
|
|
input: TDeleteInputSchema;
|
|
};
|
|
|
|
export const deleteHandler = async ({ ctx, input }: DeleteOptions) => {
|
|
const { id } = input;
|
|
|
|
const workflowToDelete = await prisma.workflow.findUnique({
|
|
where: {
|
|
id,
|
|
},
|
|
select: {
|
|
id: true,
|
|
teamId: true,
|
|
userId: true,
|
|
activeOn: {
|
|
select: {
|
|
eventTypeId: true,
|
|
},
|
|
},
|
|
activeOnTeams: {
|
|
select: {
|
|
teamId: true,
|
|
},
|
|
},
|
|
steps: {
|
|
select: {
|
|
action: true,
|
|
agent: {
|
|
select: {
|
|
id: true,
|
|
outboundPhoneNumbers: {
|
|
select: {
|
|
id: true,
|
|
phoneNumber: true,
|
|
subscriptionStatus: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
team: {
|
|
select: {
|
|
isOrganization: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const isUserAuthorized = await isAuthorized(workflowToDelete, ctx.user.id, "workflow.delete");
|
|
|
|
if (!isUserAuthorized || !workflowToDelete) {
|
|
throw new TRPCError({ code: "UNAUTHORIZED" });
|
|
}
|
|
|
|
const calAISteps = workflowToDelete.steps?.filter(
|
|
(step) => step.action === WorkflowActions.CAL_AI_PHONE_CALL
|
|
);
|
|
|
|
if (calAISteps && calAISteps.length > 0) {
|
|
const aiPhoneService = createDefaultAIPhoneServiceProvider();
|
|
|
|
for (const step of calAISteps) {
|
|
if (step.agent?.outboundPhoneNumbers && step.agent.outboundPhoneNumbers.length > 0) {
|
|
for (const phoneNumber of step.agent.outboundPhoneNumbers) {
|
|
try {
|
|
// Check subscription status and handle accordingly
|
|
if (phoneNumber.subscriptionStatus === "ACTIVE") {
|
|
await aiPhoneService.cancelPhoneNumberSubscription({
|
|
phoneNumberId: phoneNumber.id,
|
|
userId: ctx.user.id,
|
|
});
|
|
} else if (
|
|
phoneNumber.subscriptionStatus === null ||
|
|
phoneNumber.subscriptionStatus === undefined
|
|
) {
|
|
await aiPhoneService.deletePhoneNumber({
|
|
phoneNumber: phoneNumber.phoneNumber,
|
|
userId: ctx.user.id,
|
|
deleteFromDB: true,
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error(`Failed to handle phone number ${phoneNumber.phoneNumber}:`, error);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (step.agent) {
|
|
try {
|
|
await aiPhoneService.deleteAgent({
|
|
id: step.agent.id,
|
|
userId: ctx.user.id,
|
|
teamId: workflowToDelete.teamId ?? undefined,
|
|
});
|
|
} catch (error) {
|
|
console.error(`Failed to delete agent ${step.agent.id}:`, error);
|
|
// Continue with deletion even if agent deletion fails
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const scheduledReminders = await prisma.workflowReminder.findMany({
|
|
where: {
|
|
workflowStep: {
|
|
workflowId: id,
|
|
},
|
|
scheduled: true,
|
|
NOT: {
|
|
referenceId: null,
|
|
},
|
|
},
|
|
});
|
|
|
|
//cancel workflow reminders of deleted workflow
|
|
await WorkflowRepository.deleteAllWorkflowReminders(scheduledReminders);
|
|
|
|
const isOrg = workflowToDelete.team?.isOrganization ?? false;
|
|
|
|
const activeOnToRemove = isOrg
|
|
? workflowToDelete.activeOnTeams.map((activeOn) => activeOn.teamId)
|
|
: workflowToDelete.activeOn.map((activeOn) => activeOn.eventTypeId);
|
|
|
|
await removeSmsReminderFieldForEventTypes({ activeOnToRemove, workflowId: workflowToDelete.id, isOrg });
|
|
|
|
// automatically deletes all steps and reminders connected to this workflow
|
|
await prisma.workflow.deleteMany({
|
|
where: {
|
|
id,
|
|
},
|
|
});
|
|
|
|
return {
|
|
id,
|
|
};
|
|
};
|