mirror of
https://github.com/open-reception/appointment-booking-software.git
synced 2026-08-17 21:25:52 +02:00
213 calendar when staff creates appointment for a channel with reservation the email still should be for a successfull appointment booking (#218)
* If staff creates an appointment, treat it as being already confirmed * Fixed tests
This commit is contained in:
@@ -489,21 +489,24 @@ export class AppointmentService {
|
||||
/**
|
||||
* Add appointment to existing client tunnel
|
||||
*/
|
||||
public async addAppointmentToTunnel(appointmentData: {
|
||||
emailHash: string;
|
||||
tunnelId: string;
|
||||
channelId: string;
|
||||
agentId: string;
|
||||
appointmentDate: string;
|
||||
duration: number;
|
||||
clientEmail: string;
|
||||
clientLanguage?: string;
|
||||
encryptedAppointment: {
|
||||
encryptedPayload: string;
|
||||
iv: string;
|
||||
authTag: string;
|
||||
};
|
||||
}): Promise<AppointmentResponse> {
|
||||
public async addAppointmentToTunnel(
|
||||
appointmentData: {
|
||||
emailHash: string;
|
||||
tunnelId: string;
|
||||
channelId: string;
|
||||
agentId: string;
|
||||
appointmentDate: string;
|
||||
duration: number;
|
||||
clientEmail: string;
|
||||
clientLanguage?: string;
|
||||
encryptedAppointment: {
|
||||
encryptedPayload: string;
|
||||
iv: string;
|
||||
authTag: string;
|
||||
};
|
||||
},
|
||||
staffCreated = false,
|
||||
): Promise<AppointmentResponse> {
|
||||
const log = logger.setContext("AppointmentService");
|
||||
|
||||
log.info("Adding appointment to existing tunnel", {
|
||||
@@ -547,8 +550,11 @@ export class AppointmentService {
|
||||
throw new NotFoundError("Active channel not found");
|
||||
}
|
||||
|
||||
const initialStatus = channelResult[0].requiresConfirmation ? "NEW" : "CONFIRMED";
|
||||
const requiresConfirmation = channelResult[0].requiresConfirmation || false;
|
||||
const initialStatus =
|
||||
channelResult[0].requiresConfirmation && !staffCreated ? "NEW" : "CONFIRMED";
|
||||
const requiresConfirmation = staffCreated
|
||||
? false
|
||||
: channelResult[0].requiresConfirmation || false;
|
||||
|
||||
// Create encrypted appointment
|
||||
const appointmentResult = await db
|
||||
@@ -597,6 +603,7 @@ export class AppointmentService {
|
||||
*/
|
||||
public async createNewClientWithAppointment(
|
||||
clientData: ClientTunnelData,
|
||||
staffCreated = false,
|
||||
): Promise<AppointmentResponse> {
|
||||
const log = logger.setContext("AppointmentService");
|
||||
|
||||
@@ -693,8 +700,11 @@ export class AppointmentService {
|
||||
throw new NotFoundError("Channel not found");
|
||||
}
|
||||
|
||||
const initialStatus = channelResult[0].requiresConfirmation ? "NEW" : "CONFIRMED";
|
||||
const requiresConfirmation = channelResult[0].requiresConfirmation || false;
|
||||
const initialStatus =
|
||||
channelResult[0].requiresConfirmation && !staffCreated ? "NEW" : "CONFIRMED";
|
||||
const requiresConfirmation = staffCreated
|
||||
? false
|
||||
: channelResult[0].requiresConfirmation || false;
|
||||
|
||||
// 4. Create encrypted appointment
|
||||
const appointmentResult = await tx
|
||||
|
||||
@@ -321,7 +321,7 @@ export const POST: RequestHandler = async ({ params, request, locals }) => {
|
||||
encryptedAppointment: validatedData.encryptedAppointment,
|
||||
};
|
||||
|
||||
result = await appointmentService.addAppointmentToTunnel(appointmentData);
|
||||
result = await appointmentService.addAppointmentToTunnel(appointmentData, true);
|
||||
} else {
|
||||
// New client - create tunnel and appointment
|
||||
isNewClient = true;
|
||||
@@ -360,7 +360,7 @@ export const POST: RequestHandler = async ({ params, request, locals }) => {
|
||||
clientEncryptedTunnelKey: validatedData.clientEncryptedTunnelKey,
|
||||
};
|
||||
|
||||
result = await appointmentService.createNewClientWithAppointment(clientData);
|
||||
result = await appointmentService.createNewClientWithAppointment(clientData, true);
|
||||
|
||||
// For new clients with email: initiate PIN reset flow
|
||||
if (validatedData.clientEmail && !validatedData.hasNoEmail) {
|
||||
@@ -397,7 +397,7 @@ export const POST: RequestHandler = async ({ params, request, locals }) => {
|
||||
validatedData.channelId,
|
||||
validatedData.clientEmail,
|
||||
validatedData.clientLanguage,
|
||||
!!result.requiresConfirmation,
|
||||
false,
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error("Failed to send appointment notification", {
|
||||
|
||||
+44
-38
@@ -102,30 +102,33 @@ describe("POST /api/tenants/[id]/appointments/staff-create", () => {
|
||||
expect(data.id).toBe("appointment-123");
|
||||
expect(data.isNewClient).toBe(true);
|
||||
expect(data.pinResetToken).toBe("reset-token-123");
|
||||
expect(mockAppointmentService.createNewClientWithAppointment).toHaveBeenCalledWith({
|
||||
tunnelId: "tunnel-123",
|
||||
channelId: "channel-123",
|
||||
agentId: "agent-123",
|
||||
appointmentDate: "2026-01-15T14:00:00.000Z",
|
||||
duration: 30,
|
||||
emailHash,
|
||||
clientEmail: "test@example.com",
|
||||
clientLanguage: "de",
|
||||
clientPublicKey: "public-key",
|
||||
privateKeyShare: "private-key-share",
|
||||
encryptedAppointment: {
|
||||
encryptedPayload: "encrypted-payload",
|
||||
iv: "iv",
|
||||
authTag: "auth-tag",
|
||||
},
|
||||
staffKeyShares: [
|
||||
{
|
||||
userId: "staff-123",
|
||||
encryptedTunnelKey: "encrypted-for-staff",
|
||||
expect(mockAppointmentService.createNewClientWithAppointment).toHaveBeenCalledWith(
|
||||
{
|
||||
tunnelId: "tunnel-123",
|
||||
channelId: "channel-123",
|
||||
agentId: "agent-123",
|
||||
appointmentDate: "2026-01-15T14:00:00.000Z",
|
||||
duration: 30,
|
||||
emailHash,
|
||||
clientEmail: "test@example.com",
|
||||
clientLanguage: "de",
|
||||
clientPublicKey: "public-key",
|
||||
privateKeyShare: "private-key-share",
|
||||
encryptedAppointment: {
|
||||
encryptedPayload: "encrypted-payload",
|
||||
iv: "iv",
|
||||
authTag: "auth-tag",
|
||||
},
|
||||
],
|
||||
clientEncryptedTunnelKey: "encrypted-tunnel-key",
|
||||
});
|
||||
staffKeyShares: [
|
||||
{
|
||||
userId: "staff-123",
|
||||
encryptedTunnelKey: "encrypted-for-staff",
|
||||
},
|
||||
],
|
||||
clientEncryptedTunnelKey: "encrypted-tunnel-key",
|
||||
},
|
||||
true,
|
||||
);
|
||||
expect(mockPinResetService.createResetToken).toHaveBeenCalledWith(emailHash, 60);
|
||||
});
|
||||
});
|
||||
@@ -235,21 +238,24 @@ describe("POST /api/tenants/[id]/appointments/staff-create", () => {
|
||||
expect(data.id).toBe("appointment-789");
|
||||
expect(data.isNewClient).toBe(false);
|
||||
expect(data.pinResetToken).toBeUndefined();
|
||||
expect(mockAppointmentService.addAppointmentToTunnel).toHaveBeenCalledWith({
|
||||
emailHash,
|
||||
tunnelId: "tunnel-789",
|
||||
channelId: "channel-123",
|
||||
agentId: "agent-123",
|
||||
appointmentDate: "2026-01-15T14:00:00.000Z",
|
||||
duration: 30,
|
||||
clientEmail: "existing@example.com",
|
||||
clientLanguage: "de",
|
||||
encryptedAppointment: {
|
||||
encryptedPayload: "encrypted-payload",
|
||||
iv: "iv",
|
||||
authTag: "auth-tag",
|
||||
expect(mockAppointmentService.addAppointmentToTunnel).toHaveBeenCalledWith(
|
||||
{
|
||||
emailHash,
|
||||
tunnelId: "tunnel-789",
|
||||
channelId: "channel-123",
|
||||
agentId: "agent-123",
|
||||
appointmentDate: "2026-01-15T14:00:00.000Z",
|
||||
duration: 30,
|
||||
clientEmail: "existing@example.com",
|
||||
clientLanguage: "de",
|
||||
encryptedAppointment: {
|
||||
encryptedPayload: "encrypted-payload",
|
||||
iv: "iv",
|
||||
authTag: "auth-tag",
|
||||
},
|
||||
},
|
||||
});
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -468,7 +474,7 @@ describe("POST /api/tenants/[id]/appointments/staff-create", () => {
|
||||
"channel-123",
|
||||
"test@example.com",
|
||||
"de",
|
||||
true,
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user