import { Prisma, VacationType } from "@capakraken/db"; import type { TRPCContext } from "../trpc.js"; import { loadResourceHolidayContext } from "./resource-holiday-context.js"; import { countVacationChargeableDays } from "./vacation-day-count.js"; export const VACATION_BALANCE_TYPES = new Set([ VacationType.ANNUAL, VacationType.OTHER, ]); export type VacationChargeableInput = { resourceId: string; type: VacationType; startDate: Date; endDate: Date; isHalfDay: boolean; }; export type VacationDeductionSnapshot = { deductedDays: number; holidayCountryCode: string | null; holidayCountryName: string | null; holidayFederalState: string | null; holidayMetroCityName: string | null; holidayCalendarDates: string[]; holidayLegacyPublicHolidayDates: string[]; }; type VacationSnapshotCarrier = { deductedDays?: number | null; holidayCountryCode?: string | null; holidayFederalState?: string | null; holidayMetroCityName?: string | null; holidayCalendarDates?: Prisma.JsonValue | null; holidayLegacyPublicHolidayDates?: Prisma.JsonValue | null; startDate: Date; endDate: Date; isHalfDay: boolean; }; function isStringArray(value: unknown): value is string[] { return Array.isArray(value) && value.every((entry) => typeof entry === "string"); } export function parseVacationSnapshotDateList(value: Prisma.JsonValue | null | undefined): string[] { return isStringArray(value) ? [...value].sort() : []; } export async function calculateVacationDeductionSnapshot( db: TRPCContext["db"], vacation: VacationChargeableInput, ): Promise { const holidayContext = await loadResourceHolidayContext( db, vacation.resourceId, vacation.startDate, vacation.endDate, ); return { deductedDays: countVacationChargeableDays({ vacation, countryCode: holidayContext.countryCode, federalState: holidayContext.federalState, metroCityName: holidayContext.metroCityName, calendarHolidayStrings: holidayContext.calendarHolidayStrings, publicHolidayStrings: holidayContext.publicHolidayStrings, }), holidayCountryCode: holidayContext.countryCode ?? null, holidayCountryName: holidayContext.countryName ?? null, holidayFederalState: holidayContext.federalState ?? null, holidayMetroCityName: holidayContext.metroCityName ?? null, holidayCalendarDates: [...holidayContext.calendarHolidayStrings].sort(), holidayLegacyPublicHolidayDates: [...holidayContext.publicHolidayStrings].sort(), }; } export function buildVacationDeductionSnapshotWriteData( snapshot: VacationDeductionSnapshot, ): { deductedDays: number; holidayCountryCode: string | null; holidayCountryName: string | null; holidayFederalState: string | null; holidayMetroCityName: string | null; holidayCalendarDates: Prisma.InputJsonValue; holidayLegacyPublicHolidayDates: Prisma.InputJsonValue; } { return { deductedDays: snapshot.deductedDays, holidayCountryCode: snapshot.holidayCountryCode, holidayCountryName: snapshot.holidayCountryName, holidayFederalState: snapshot.holidayFederalState, holidayMetroCityName: snapshot.holidayMetroCityName, holidayCalendarDates: snapshot.holidayCalendarDates as unknown as Prisma.InputJsonValue, holidayLegacyPublicHolidayDates: snapshot.holidayLegacyPublicHolidayDates as unknown as Prisma.InputJsonValue, }; } export function countVacationChargeableDaysFromSnapshot( vacation: VacationSnapshotCarrier, periodStart?: Date, periodEnd?: Date, ): number | null { if (vacation.deductedDays == null) { return null; } return countVacationChargeableDays({ vacation, periodStart, periodEnd, countryCode: vacation.holidayCountryCode, federalState: vacation.holidayFederalState, metroCityName: vacation.holidayMetroCityName, calendarHolidayStrings: parseVacationSnapshotDateList(vacation.holidayCalendarDates), publicHolidayStrings: parseVacationSnapshotDateList(vacation.holidayLegacyPublicHolidayDates), }); }