feat(api): explain holiday-aware vacation deductions
This commit is contained in:
@@ -5,12 +5,12 @@ import { findUniqueOrThrow } from "../db/helpers.js";
|
||||
import { RESOURCE_BRIEF_SELECT } from "../db/selects.js";
|
||||
import { anonymizeResource, anonymizeUser, getAnonymizationDirectory } from "../lib/anonymization.js";
|
||||
import { loadResourceHolidayContext } from "../lib/resource-holiday-context.js";
|
||||
import {
|
||||
VACATION_BALANCE_TYPES,
|
||||
type VacationChargeableInput,
|
||||
} from "../lib/vacation-deduction-snapshot.js";
|
||||
import { countCalendarDaysInPeriod, countVacationChargeableDays } from "../lib/vacation-day-count.js";
|
||||
import { protectedProcedure, type TRPCContext } from "../trpc.js";
|
||||
import {
|
||||
buildVacationPreview,
|
||||
findVacationResourceChapter,
|
||||
listChapterVacationOverlaps,
|
||||
} from "./vacation-read-support.js";
|
||||
|
||||
type VacationReadContext = Pick<TRPCContext, "db" | "dbUser">;
|
||||
|
||||
@@ -138,53 +138,13 @@ export const vacationReadProcedures = {
|
||||
input.startDate,
|
||||
input.endDate,
|
||||
);
|
||||
const vacation: Pick<VacationChargeableInput, "startDate" | "endDate" | "isHalfDay"> = {
|
||||
return buildVacationPreview({
|
||||
type: input.type,
|
||||
startDate: input.startDate,
|
||||
endDate: input.endDate,
|
||||
isHalfDay: input.isHalfDay ?? false,
|
||||
};
|
||||
const requestedDays = countCalendarDaysInPeriod(vacation);
|
||||
const effectiveDays = VACATION_BALANCE_TYPES.has(input.type)
|
||||
? countVacationChargeableDays({
|
||||
vacation,
|
||||
countryCode: holidayContext.countryCode,
|
||||
federalState: holidayContext.federalState,
|
||||
metroCityName: holidayContext.metroCityName,
|
||||
calendarHolidayStrings: holidayContext.calendarHolidayStrings,
|
||||
publicHolidayStrings: holidayContext.publicHolidayStrings,
|
||||
})
|
||||
: requestedDays;
|
||||
const publicHolidayDates = [...new Set([
|
||||
...holidayContext.calendarHolidayStrings,
|
||||
...holidayContext.publicHolidayStrings,
|
||||
])].sort();
|
||||
const holidayDetails = publicHolidayDates.map((date) => ({
|
||||
date,
|
||||
source:
|
||||
holidayContext.calendarHolidayStrings.includes(date) && holidayContext.publicHolidayStrings.includes(date)
|
||||
? "CALENDAR_AND_LEGACY"
|
||||
: holidayContext.calendarHolidayStrings.includes(date)
|
||||
? "CALENDAR"
|
||||
: "LEGACY_PUBLIC_HOLIDAY",
|
||||
}));
|
||||
|
||||
return {
|
||||
requestedDays,
|
||||
effectiveDays,
|
||||
deductedDays: VACATION_BALANCE_TYPES.has(input.type) ? effectiveDays : 0,
|
||||
publicHolidayDates,
|
||||
holidayDetails,
|
||||
holidayContext: {
|
||||
countryCode: holidayContext.countryCode ?? null,
|
||||
countryName: holidayContext.countryName ?? null,
|
||||
federalState: holidayContext.federalState ?? null,
|
||||
metroCityName: holidayContext.metroCityName ?? null,
|
||||
sources: {
|
||||
hasCalendarHolidays: holidayContext.calendarHolidayStrings.length > 0,
|
||||
hasLegacyPublicHolidayEntries: holidayContext.publicHolidayStrings.length > 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
holidayContext,
|
||||
});
|
||||
}),
|
||||
|
||||
list: protectedProcedure
|
||||
@@ -316,27 +276,16 @@ export const vacationReadProcedures = {
|
||||
.query(async ({ ctx, input }) => {
|
||||
await assertCanReadVacationResource(ctx, input.resourceId);
|
||||
|
||||
const resource = await ctx.db.resource.findUnique({
|
||||
where: { id: input.resourceId },
|
||||
select: { chapter: true },
|
||||
});
|
||||
if (!resource?.chapter) {
|
||||
const chapter = await findVacationResourceChapter(ctx.db, input.resourceId);
|
||||
if (!chapter) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return ctx.db.vacation.findMany({
|
||||
where: {
|
||||
resource: { chapter: resource.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,
|
||||
return listChapterVacationOverlaps(ctx.db, {
|
||||
chapter,
|
||||
resourceId: input.resourceId,
|
||||
startDate: input.startDate,
|
||||
endDate: input.endDate,
|
||||
});
|
||||
}),
|
||||
|
||||
@@ -372,19 +321,11 @@ export const vacationReadProcedures = {
|
||||
});
|
||||
}
|
||||
|
||||
const overlaps = await ctx.db.vacation.findMany({
|
||||
where: {
|
||||
resource: { chapter: resource.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,
|
||||
const overlaps = await listChapterVacationOverlaps(ctx.db, {
|
||||
chapter: resource.chapter,
|
||||
resourceId: input.resourceId,
|
||||
startDate: input.startDate,
|
||||
endDate: input.endDate,
|
||||
});
|
||||
|
||||
return mapTeamOverlapDetail({
|
||||
|
||||
Reference in New Issue
Block a user