feat: guard room availability writes

This commit is contained in:
2026-06-25 11:39:44 +01:00
parent 44b34c850c
commit 17f36e6378
2 changed files with 82 additions and 7 deletions
@@ -4,6 +4,7 @@ import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db';
import { contracts, contractRooms, rooms, services } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { contractConflictMessage, findActiveContractRoomConflict } from '$lib/server/scheduling';
import { archiveSchema } from '$lib/schemas/shared.schema';
import {
contractCreateSchema,
@@ -263,6 +264,34 @@ export const actions: Actions = {
return message(form, invalidSelection.message, { status: 400 });
}
const [existingContract] = await db
.select({ status: contracts.status })
.from(contracts)
.where(
and(
eq(contracts.id, form.data.id),
eq(contracts.clientId, params.id),
eq(contracts.organizationId, activeOrganizationId),
isNull(contracts.archivedAt)
)
)
.limit(1);
if (!existingContract) return message(form, 'Choose a valid contract.', { status: 400 });
if (existingContract.status === 'active') {
const conflict = await findActiveContractRoomConflict({
organizationId: activeOrganizationId,
roomIds: form.data.roomIds,
startDate: form.data.startDate,
endDate: form.data.endDate,
excludingContractId: form.data.id
});
if (conflict) {
return message(form, contractConflictMessage(conflict), { status: 400 });
}
}
try {
await db.transaction(async (tx) => {
await tx
@@ -280,7 +309,8 @@ export const actions: Actions = {
and(
eq(contracts.id, form.data.id),
eq(contracts.clientId, params.id),
eq(contracts.organizationId, activeOrganizationId)
eq(contracts.organizationId, activeOrganizationId),
isNull(contracts.archivedAt)
)
);
@@ -311,7 +341,11 @@ export const actions: Actions = {
if (!form.valid) return message(form, 'Choose a valid contract action.', { status: 400 });
const [contract] = await db
.select({ status: contracts.status })
.select({
status: contracts.status,
startDate: contracts.startDate,
endDate: contracts.endDate
})
.from(contracts)
.where(
and(
@@ -332,6 +366,23 @@ export const actions: Actions = {
return message(form, 'That contract status change is not allowed.', { status: 400 });
}
if (form.data.targetStatus === 'active') {
const roomLinks = await db
.select({ roomId: contractRooms.roomId })
.from(contractRooms)
.where(eq(contractRooms.contractId, form.data.id));
const conflict = await findActiveContractRoomConflict({
organizationId: activeOrganizationId,
roomIds: roomLinks.map((link) => link.roomId),
startDate: contract.startDate,
endDate: contract.endDate,
excludingContractId: form.data.id
});
if (conflict) {
return message(form, contractConflictMessage(conflict), { status: 400 });
}
}
await db
.update(contracts)
.set({ status: form.data.targetStatus, updatedAt: new Date() })
@@ -339,7 +390,8 @@ export const actions: Actions = {
and(
eq(contracts.id, form.data.id),
eq(contracts.clientId, params.id),
eq(contracts.organizationId, activeOrganizationId)
eq(contracts.organizationId, activeOrganizationId),
isNull(contracts.archivedAt)
)
);
@@ -361,7 +413,8 @@ export const actions: Actions = {
and(
eq(contracts.id, form.data.id),
eq(contracts.clientId, params.id),
eq(contracts.organizationId, activeOrganizationId)
eq(contracts.organizationId, activeOrganizationId),
isNull(contracts.archivedAt)
)
);