refactor(api): extract vacation management support
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
import type { Prisma } from "@capakraken/db";
|
||||
import { VacationStatus } from "@capakraken/db";
|
||||
import type { UpdateVacationStatusInput } from "@capakraken/shared";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
type VacationApprovalWriteData = Record<string, unknown>;
|
||||
|
||||
export const approvableVacationStatuses: readonly VacationStatus[] = [
|
||||
VacationStatus.PENDING,
|
||||
VacationStatus.CANCELLED,
|
||||
VacationStatus.REJECTED,
|
||||
];
|
||||
|
||||
export function assertVacationApprovable(status: VacationStatus): void {
|
||||
if (!approvableVacationStatuses.includes(status)) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Only PENDING, CANCELLED, or REJECTED vacations can be approved",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function assertVacationRejectable(status: VacationStatus): void {
|
||||
if (status !== VacationStatus.PENDING) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Only PENDING vacations can be rejected",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function assertVacationCancelable(status: VacationStatus): void {
|
||||
if (status === VacationStatus.CANCELLED) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Already cancelled",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function isVacationManagerRole(role: string | null | undefined): boolean {
|
||||
return role === "ADMIN" || role === "MANAGER";
|
||||
}
|
||||
|
||||
export function canActorCancelVacation(input: {
|
||||
actorId: string;
|
||||
actorRole: string | null | undefined;
|
||||
requestedById: string | null | undefined;
|
||||
resourceUserId: string | null | undefined;
|
||||
}): boolean {
|
||||
if (isVacationManagerRole(input.actorRole)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return input.requestedById === input.actorId || input.resourceUserId === input.actorId;
|
||||
}
|
||||
|
||||
export function buildApprovedVacationUpdateData(input: {
|
||||
deductionSnapshotWriteData: VacationApprovalWriteData;
|
||||
approvedById?: string | undefined;
|
||||
approvedAt: Date;
|
||||
}): Prisma.VacationUncheckedUpdateInput {
|
||||
return {
|
||||
status: VacationStatus.APPROVED,
|
||||
rejectionReason: null,
|
||||
...input.deductionSnapshotWriteData,
|
||||
...(input.approvedById ? { approvedById: input.approvedById } : {}),
|
||||
approvedAt: input.approvedAt,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildRejectedVacationUpdateData(input: {
|
||||
rejectionReason?: string | undefined;
|
||||
}): Prisma.VacationUncheckedUpdateInput {
|
||||
return {
|
||||
status: VacationStatus.REJECTED,
|
||||
...(input.rejectionReason !== undefined ? { rejectionReason: input.rejectionReason } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildVacationStatusUpdateData(input: {
|
||||
status: UpdateVacationStatusInput["status"];
|
||||
note?: string | undefined;
|
||||
approvedById: string;
|
||||
approvedAt: Date;
|
||||
}): Prisma.VacationUncheckedUpdateInput {
|
||||
const data: Prisma.VacationUncheckedUpdateInput = { status: input.status };
|
||||
|
||||
if (input.status === VacationStatus.APPROVED) {
|
||||
data.approvedById = input.approvedById;
|
||||
data.approvedAt = input.approvedAt;
|
||||
data.rejectionReason = null;
|
||||
}
|
||||
if (input.note !== undefined) {
|
||||
data.note = input.note;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
Reference in New Issue
Block a user