Files
kita-planer/src/lib/notdienst-planning.ts
T

57 lines
1.8 KiB
TypeScript

import { Prisma } from "@prisma/client";
import { UserFacingError } from "./action-error";
import { getNotdienstDays } from "./notdienst-days";
import { todayKey } from "./staff/calculations";
export async function loadNotdienstDays(
db: Pick<Prisma.TransactionClient, "termin">,
kitaId: string,
year: number,
month: number,
) {
const closures = await db.termin.findMany({
where: {
kitaId,
status: "CONFIRMED",
type: { in: ["SCHLIESSTAG", "TEAMTAG"] },
// Include closures crossing the month boundary and Berlin midnight.
startDate: { lt: new Date(Date.UTC(year, month, 1)) },
endDate: { gte: new Date(Date.UTC(year, month - 1, 1) - 86400000) },
},
select: { startDate: true, endDate: true },
});
return getNotdienstDays(year, month, closures);
}
export async function assertChildrenRemovable(
tx: Prisma.TransactionClient,
kitaId: string,
familyId: string,
childIds: string[],
) {
if (childIds.length === 0) return;
// Coordinate with the FK locks taken by concurrent assignment writes.
await tx.$queryRaw`SELECT id FROM children WHERE "kitaId" = ${kitaId}
AND "familyId" = ${familyId} AND id IN (${Prisma.join(childIds)}) ORDER BY id FOR UPDATE`;
const obligation = await tx.notdienstAssignment.findFirst({
where: {
kitaId,
childId: { in: childIds },
child: { kitaId, familyId },
OR: [
{
date: { gte: new Date(`${todayKey()}T00:00:00Z`) },
plan: { kitaId, status: "PUBLISHED" },
},
{ alarms: { some: { kitaId, activeAssignmentId: { not: null } } } },
],
},
select: { id: true },
});
if (obligation) {
throw new UserFacingError(
"Das Kind kann nicht entfernt werden, solange veröffentlichte Notdienste oder aktive Alarme bestehen. Bitte zuerst die Einteilung mit der Koordination klären.",
);
}
}