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