Files
CapaKraken/packages/api/src/router/vacation-read-support.ts
T

114 lines
3.6 KiB
TypeScript

import { VacationStatus, VacationType } from "@capakraken/db";
import { RESOURCE_BRIEF_SELECT } from "../db/selects.js";
import { type ResourceHolidayContext } from "../lib/resource-holiday-context.js";
import { VACATION_BALANCE_TYPES } from "../lib/vacation-deduction-snapshot.js";
import { countCalendarDaysInPeriod, countVacationChargeableDays } from "../lib/vacation-day-count.js";
import type { TRPCContext } from "../trpc.js";
type VacationReadDb = TRPCContext["db"];
type VacationPreviewInput = {
type: VacationType;
startDate: Date;
endDate: Date;
isHalfDay: boolean;
holidayContext: ResourceHolidayContext;
};
function resolveHolidayDetailSource(
isoDate: string,
holidayContext: ResourceHolidayContext,
): "CALENDAR" | "LEGACY_PUBLIC_HOLIDAY" | "CALENDAR_AND_LEGACY" {
const inCalendar = holidayContext.calendarHolidayStrings.includes(isoDate);
const inLegacy = holidayContext.publicHolidayStrings.includes(isoDate);
if (inCalendar && inLegacy) {
return "CALENDAR_AND_LEGACY";
}
return inCalendar ? "CALENDAR" : "LEGACY_PUBLIC_HOLIDAY";
}
export function buildVacationPreview(
input: VacationPreviewInput,
) {
const vacation = {
startDate: input.startDate,
endDate: input.endDate,
isHalfDay: input.isHalfDay,
};
const requestedDays = countCalendarDaysInPeriod(vacation);
const effectiveDays = VACATION_BALANCE_TYPES.has(input.type)
? countVacationChargeableDays({
vacation,
countryCode: input.holidayContext.countryCode,
federalState: input.holidayContext.federalState,
metroCityName: input.holidayContext.metroCityName,
calendarHolidayStrings: input.holidayContext.calendarHolidayStrings,
publicHolidayStrings: input.holidayContext.publicHolidayStrings,
})
: requestedDays;
const publicHolidayDates = [...new Set([
...input.holidayContext.calendarHolidayStrings,
...input.holidayContext.publicHolidayStrings,
])].sort();
return {
requestedDays,
effectiveDays,
deductedDays: VACATION_BALANCE_TYPES.has(input.type) ? effectiveDays : 0,
publicHolidayDates,
holidayDetails: publicHolidayDates.map((date) => ({
date,
source: resolveHolidayDetailSource(date, input.holidayContext),
})),
holidayContext: {
countryCode: input.holidayContext.countryCode ?? null,
countryName: input.holidayContext.countryName ?? null,
federalState: input.holidayContext.federalState ?? null,
metroCityName: input.holidayContext.metroCityName ?? null,
sources: {
hasCalendarHolidays: input.holidayContext.calendarHolidayStrings.length > 0,
hasLegacyPublicHolidayEntries: input.holidayContext.publicHolidayStrings.length > 0,
},
},
};
}
export async function findVacationResourceChapter(
db: VacationReadDb,
resourceId: string,
): Promise<string | null> {
const resource = await db.resource.findUnique({
where: { id: resourceId },
select: { chapter: true },
});
return resource?.chapter ?? null;
}
export async function listChapterVacationOverlaps(
db: VacationReadDb,
input: {
chapter: string;
resourceId: string;
startDate: Date;
endDate: Date;
},
) {
return db.vacation.findMany({
where: {
resource: { chapter: input.chapter },
resourceId: { not: input.resourceId },
status: { in: [VacationStatus.APPROVED, VacationStatus.PENDING] },
startDate: { lte: input.endDate },
endDate: { gte: input.startDate },
},
include: {
resource: { select: RESOURCE_BRIEF_SELECT },
},
orderBy: { startDate: "asc" },
take: 20,
});
}