Compare commits
2 Commits
main
...
revert-115
Author | SHA1 | Date |
---|---|---|
Peer Richelsen | 56c2c81def | |
Omar López | 048912799e |
|
@ -73,8 +73,10 @@ function BookingListItem(booking: BookingItemProps) {
|
||||||
}
|
}
|
||||||
utils.viewer.bookings.invalidate();
|
utils.viewer.bookings.invalidate();
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: (e) => {
|
||||||
showToast(t("booking_confirmation_failed"), "error");
|
let message = t("booking_confirmation_failed");
|
||||||
|
if ("message" in e) message = e.message;
|
||||||
|
showToast(message, "error");
|
||||||
utils.viewer.bookings.invalidate();
|
utils.viewer.bookings.invalidate();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -137,6 +137,8 @@ test.describe("pro user", () => {
|
||||||
page.click('[data-testid="confirm"]'),
|
page.click('[data-testid="confirm"]'),
|
||||||
page.waitForResponse((response) => response.url().includes("/api/trpc/bookings/confirm")),
|
page.waitForResponse((response) => response.url().includes("/api/trpc/bookings/confirm")),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
await page.goto("/bookings/unconfirmed");
|
||||||
// This is the only booking in there that needed confirmation and now it should be empty screen
|
// This is the only booking in there that needed confirmation and now it should be empty screen
|
||||||
await expect(page.locator('[data-testid="empty-screen"]')).toBeVisible();
|
await expect(page.locator('[data-testid="empty-screen"]')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
@ -227,6 +229,45 @@ test.describe("pro user", () => {
|
||||||
const firstSlotAvailableText = await firstSlotAvailable.innerText();
|
const firstSlotAvailableText = await firstSlotAvailable.innerText();
|
||||||
expect(firstSlotAvailableText).toContain("9:00");
|
expect(firstSlotAvailableText).toContain("9:00");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Cannot confirm booking for a slot, if another confirmed booking already exists for same slot.", async ({
|
||||||
|
page,
|
||||||
|
users,
|
||||||
|
}) => {
|
||||||
|
// First booking done for first available time slot in next month
|
||||||
|
await bookOptinEvent(page);
|
||||||
|
|
||||||
|
const [pro] = users.get();
|
||||||
|
await page.goto(`/${pro.username}`);
|
||||||
|
|
||||||
|
// Second booking done for same time slot
|
||||||
|
await bookOptinEvent(page);
|
||||||
|
|
||||||
|
await pro.apiLogin();
|
||||||
|
|
||||||
|
await page.goto("/bookings/unconfirmed");
|
||||||
|
|
||||||
|
// Confirm first booking
|
||||||
|
await Promise.all([
|
||||||
|
page.click('[data-testid="confirm"]'),
|
||||||
|
page.waitForResponse((response) => response.url().includes("/api/trpc/bookings/confirm")),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
page.goto("/bookings/unconfirmed"),
|
||||||
|
page.waitForResponse((response) => response.url().includes("/api/trpc/bookings/get")),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Confirm second booking
|
||||||
|
await page.click('[data-testid="confirm"]');
|
||||||
|
const response = await page.waitForResponse(
|
||||||
|
(response) => response.url().includes("/api/trpc/bookings/confirm") && response.status() !== 200
|
||||||
|
);
|
||||||
|
const responseObj = await response.json();
|
||||||
|
|
||||||
|
expect(responseObj[0]?.error?.json?.data?.code).toEqual("BAD_REQUEST");
|
||||||
|
expect(responseObj[0]?.error?.json?.message).toEqual("Slot already confirmed for other booking");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("prefill", () => {
|
test.describe("prefill", () => {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { handleConfirmation } from "@calcom/features/bookings/lib/handleConfirma
|
||||||
import { handleWebhookTrigger } from "@calcom/features/bookings/lib/handleWebhookTrigger";
|
import { handleWebhookTrigger } from "@calcom/features/bookings/lib/handleWebhookTrigger";
|
||||||
import type { EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload";
|
import type { EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload";
|
||||||
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
|
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
|
||||||
|
import { getUsernameList } from "@calcom/lib/defaultEvents";
|
||||||
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
|
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
|
||||||
import { getTranslation } from "@calcom/lib/server";
|
import { getTranslation } from "@calcom/lib/server";
|
||||||
import { getUsersCredentials } from "@calcom/lib/server/getUsersCredentials";
|
import { getUsersCredentials } from "@calcom/lib/server/getUsersCredentials";
|
||||||
|
@ -19,6 +20,7 @@ import type { IAbstractPaymentService, PaymentApp } from "@calcom/types/PaymentS
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
import type { TrpcSessionUser } from "../../../trpc";
|
import type { TrpcSessionUser } from "../../../trpc";
|
||||||
|
import { getAvailableSlots } from "../slots/util";
|
||||||
import type { TConfirmInputSchema } from "./confirm.schema";
|
import type { TConfirmInputSchema } from "./confirm.schema";
|
||||||
import type { BookingsProcedureContext } from "./util";
|
import type { BookingsProcedureContext } from "./util";
|
||||||
|
|
||||||
|
@ -55,6 +57,7 @@ export const confirmHandler = async ({ ctx, input }: ConfirmOptions) => {
|
||||||
teamId: true,
|
teamId: true,
|
||||||
recurringEvent: true,
|
recurringEvent: true,
|
||||||
title: true,
|
title: true,
|
||||||
|
slug: true,
|
||||||
requiresConfirmation: true,
|
requiresConfirmation: true,
|
||||||
currency: true,
|
currency: true,
|
||||||
length: true,
|
length: true,
|
||||||
|
@ -113,6 +116,27 @@ export const confirmHandler = async ({ ctx, input }: ConfirmOptions) => {
|
||||||
throw new TRPCError({ code: "BAD_REQUEST", message: "Booking already confirmed" });
|
throw new TRPCError({ code: "BAD_REQUEST", message: "Booking already confirmed" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for user's slot availability for current booking start and end time
|
||||||
|
if (confirmed) {
|
||||||
|
const slotsAvailable = await getAvailableSlots({
|
||||||
|
input: {
|
||||||
|
startTime: booking?.startTime.toISOString(),
|
||||||
|
endTime: booking?.endTime.toISOString(),
|
||||||
|
eventTypeId: booking?.eventType?.id,
|
||||||
|
eventTypeSlug: booking?.eventType?.slug,
|
||||||
|
timeZone: user?.timeZone,
|
||||||
|
usernameList: getUsernameList(user?.username ?? ""),
|
||||||
|
isTeamEvent: !!booking?.eventType?.teamId || false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// If no free slot available with current booking request's start time and
|
||||||
|
// end time, then the slot is booked for other booking ,throw error
|
||||||
|
if (slotsAvailable?.slots && Object.keys(slotsAvailable.slots).length === 0) {
|
||||||
|
throw new TRPCError({ code: "BAD_REQUEST", message: "Slot already confirmed for other booking" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If booking requires payment and is not paid, we don't allow confirmation
|
// If booking requires payment and is not paid, we don't allow confirmation
|
||||||
if (confirmed && booking.payment.length > 0 && !booking.paid) {
|
if (confirmed && booking.payment.length > 0 && !booking.paid) {
|
||||||
await prisma.booking.update({
|
await prisma.booking.update({
|
||||||
|
|
Loading…
Reference in New Issue