feat: show planning availability and manage Kita access roles
This commit is contained in:
+48
-17
@@ -81,7 +81,11 @@ async function inviteStaff(
|
||||
if (
|
||||
existingUser &&
|
||||
(existingUser.kitaId !== actor.kitaId ||
|
||||
existingUser.role !== UserRole.ERZIEHER ||
|
||||
!(
|
||||
existingUser.role === UserRole.ERZIEHER ||
|
||||
(existingUser.role === UserRole.ADMIN &&
|
||||
existingUser.id === profile.userId)
|
||||
) ||
|
||||
(await tx.staffProfile.count({
|
||||
where: { userId: existingUser.id, id: { not: profile.id } },
|
||||
})))
|
||||
@@ -205,7 +209,9 @@ export function assertStaffAccess(
|
||||
throw new UserFacingError("Kein Zugriff auf dieses Personalprofil.");
|
||||
if (adminOnly) requireAdmin(actor);
|
||||
else if (actor.role !== UserRole.ADMIN && profile.userId !== actor.id)
|
||||
throw new UserFacingError("Du kannst nur dein eigenes Personalprofil verwalten.");
|
||||
throw new UserFacingError(
|
||||
"Du kannst nur dein eigenes Personalprofil verwalten.",
|
||||
);
|
||||
}
|
||||
|
||||
export async function getStaffProfile(actor: StaffActor, profileId?: string) {
|
||||
@@ -320,8 +326,8 @@ export async function executeStaffCommand(
|
||||
kitaId: actor.kitaId,
|
||||
role: actor.role,
|
||||
privacyPolicyAcceptedAt: { not: null },
|
||||
emailVerifiedAt: { not: null },
|
||||
disabledAt: null,
|
||||
emailVerifiedAt: { not: null },
|
||||
disabledAt: null,
|
||||
},
|
||||
});
|
||||
if (
|
||||
@@ -419,7 +425,9 @@ export async function executeStaffCommand(
|
||||
if (
|
||||
profile.user &&
|
||||
(profile.user.kitaId !== actor.kitaId ||
|
||||
profile.user.role !== UserRole.ERZIEHER)
|
||||
!([UserRole.ERZIEHER, UserRole.ADMIN] as UserRole[]).includes(
|
||||
profile.user.role,
|
||||
))
|
||||
)
|
||||
throw new UserFacingError(
|
||||
"Der verknüpfte Account gehört nicht zum Fachpersonal dieser Kita.",
|
||||
@@ -440,7 +448,11 @@ export async function executeStaffCommand(
|
||||
return { ...invitation, educatorId: educator.id };
|
||||
}
|
||||
if (command === "emailPreference") {
|
||||
if (actor.role !== UserRole.ERZIEHER)
|
||||
if (
|
||||
!(await tx.staffProfile.count({
|
||||
where: { userId: actor.id, kitaId: actor.kitaId },
|
||||
}))
|
||||
)
|
||||
throw new UserFacingError(
|
||||
"Diese Einstellung ist für Fachkräfte vorgesehen.",
|
||||
);
|
||||
@@ -534,7 +546,9 @@ export async function executeStaffCommand(
|
||||
input.employmentEnd &&
|
||||
input.employmentEnd < input.employmentStart
|
||||
)
|
||||
throw new UserFacingError("Das Beschäftigungsende liegt vor dem Beginn.");
|
||||
throw new UserFacingError(
|
||||
"Das Beschäftigungsende liegt vor dem Beginn.",
|
||||
);
|
||||
if (
|
||||
input.trackingStart < input.employmentStart ||
|
||||
(input.employmentEnd && input.trackingStart > input.employmentEnd)
|
||||
@@ -545,7 +559,8 @@ export async function executeStaffCommand(
|
||||
const educator = await tx.educator.findFirst({
|
||||
where: { id: input.educatorId, kitaId: actor.kitaId },
|
||||
});
|
||||
if (!educator) throw new UserFacingError("Fachkraft nicht gefunden.");
|
||||
if (!educator)
|
||||
throw new UserFacingError("Fachkraft nicht gefunden.");
|
||||
const existing = await tx.staffProfile.findFirst({
|
||||
where: { educatorId: educator.id, kitaId: actor.kitaId },
|
||||
include: profileInclude,
|
||||
@@ -591,7 +606,8 @@ export async function executeStaffCommand(
|
||||
where: { id: profileId, kitaId: actor.kitaId },
|
||||
include: profileInclude,
|
||||
});
|
||||
if (!profile) throw new UserFacingError("Personalprofil nicht gefunden.");
|
||||
if (!profile)
|
||||
throw new UserFacingError("Personalprofil nicht gefunden.");
|
||||
assertStaffAccess(actor, profile);
|
||||
const holidays = new Set(
|
||||
(
|
||||
@@ -631,7 +647,10 @@ export async function executeStaffCommand(
|
||||
for (const user of recipients) {
|
||||
if (
|
||||
!toAdmins &&
|
||||
(user.role !== UserRole.ERZIEHER || !user.staffDecisionEmails)
|
||||
(!(
|
||||
[UserRole.ERZIEHER, UserRole.ADMIN] as UserRole[]
|
||||
).includes(user.role) ||
|
||||
!user.staffDecisionEmails)
|
||||
)
|
||||
continue;
|
||||
const job = await tx.staffEmail.create({
|
||||
@@ -719,7 +738,9 @@ export async function executeStaffCommand(
|
||||
input.employmentEnd &&
|
||||
input.employmentEnd < dateKey(profile.employmentStart)
|
||||
)
|
||||
throw new UserFacingError("Das Beschäftigungsende liegt vor dem Beginn.");
|
||||
throw new UserFacingError(
|
||||
"Das Beschäftigungsende liegt vor dem Beginn.",
|
||||
);
|
||||
if (
|
||||
input.employmentEnd &&
|
||||
(input.employmentEnd < dateKey(profile.trackingStart) ||
|
||||
@@ -786,7 +807,9 @@ export async function executeStaffCommand(
|
||||
(profile.employmentEnd &&
|
||||
input.date > dateKey(profile.employmentEnd))
|
||||
)
|
||||
throw new UserFacingError("Der Tag liegt außerhalb der Beschäftigung.");
|
||||
throw new UserFacingError(
|
||||
"Der Tag liegt außerhalb der Beschäftigung.",
|
||||
);
|
||||
if (input.remove)
|
||||
await tx.staffDayOverride.deleteMany({
|
||||
where: { profileId, date: dateOnly(input.date) },
|
||||
@@ -859,7 +882,9 @@ export async function executeStaffCommand(
|
||||
})
|
||||
.parse(data);
|
||||
if (input.date < dateKey(profile.trackingStart))
|
||||
throw new UserFacingError("Korrekturen müssen ab Erfassungsbeginn liegen.");
|
||||
throw new UserFacingError(
|
||||
"Korrekturen müssen ab Erfassungsbeginn liegen.",
|
||||
);
|
||||
await tx.staffAdjustment.create({
|
||||
data: {
|
||||
profileId,
|
||||
@@ -887,7 +912,8 @@ export async function executeStaffCommand(
|
||||
throw new UserFacingError("Zeiteintrag nicht gefunden.");
|
||||
if (existing) ensureDayEditable(profile, dateKey(existing.date));
|
||||
if (command === "deleteTime") {
|
||||
if (!existing) throw new UserFacingError("Zeiteintrag nicht gefunden.");
|
||||
if (!existing)
|
||||
throw new UserFacingError("Zeiteintrag nicht gefunden.");
|
||||
await tx.staffTimeEntry.delete({ where: { id: existing.id } });
|
||||
await history(
|
||||
"Arbeitszeit gelöscht",
|
||||
@@ -1135,7 +1161,8 @@ export async function executeStaffCommand(
|
||||
const existing = data.id
|
||||
? profile.leaves.find((l) => l.id === idSchema.parse(data.id))
|
||||
: undefined;
|
||||
if (data.id && !existing) throw new UserFacingError("Antrag nicht gefunden.");
|
||||
if (data.id && !existing)
|
||||
throw new UserFacingError("Antrag nicht gefunden.");
|
||||
if (command === "leaveCancel") {
|
||||
if (
|
||||
!existing ||
|
||||
@@ -1182,7 +1209,9 @@ export async function executeStaffCommand(
|
||||
!existing ||
|
||||
!["PENDING", "CANCEL_REQUESTED"].includes(existing.status)
|
||||
)
|
||||
throw new UserFacingError("Dieser Antrag ist nicht mehr offen.");
|
||||
throw new UserFacingError(
|
||||
"Dieser Antrag ist nicht mehr offen.",
|
||||
);
|
||||
const cancelling = existing.status === "CANCEL_REQUESTED";
|
||||
if (input.approve)
|
||||
ensureRangeEditable(
|
||||
@@ -1284,7 +1313,9 @@ export async function executeStaffCommand(
|
||||
.parse(data);
|
||||
if (input.kind === "SICKNESS") requireAdmin(actor);
|
||||
if (existing && existing.status !== "PENDING")
|
||||
throw new UserFacingError("Nur offene Anträge können bearbeitet werden.");
|
||||
throw new UserFacingError(
|
||||
"Nur offene Anträge können bearbeitet werden.",
|
||||
);
|
||||
if (input.startDate < dateKey(profile.trackingStart))
|
||||
throw new UserFacingError(
|
||||
"Abwesenheiten bitte ab Erfassungsbeginn erfassen.",
|
||||
|
||||
Reference in New Issue
Block a user