feat(api): explain holiday-aware vacation deductions
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
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,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user