feat: Czech public holidays in work fund calculation
- Created czech-holidays.ts with 11 fixed + 2 Easter-based holidays - Fund now automatically excludes public holidays (no manual records needed) - covered = worked + vacation + sick (NOT holidays — already in fund) - Renamed "Odpracováno" to "Pokryto" (worked + leave = what counts) - Removed dependency on holiday attendance records per employee Matches PHP CzechHolidays::getMonthlyWorkFund() logic exactly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { attendance_leave_type } from '@prisma/client';
|
||||
import prisma from '../config/database';
|
||||
import { getBusinessDaysInMonth } from '../utils/czech-holidays';
|
||||
|
||||
const VALID_LEAVE_TYPES = ['work', 'vacation', 'sick', 'holiday', 'unpaid'] as const;
|
||||
|
||||
@@ -10,16 +11,6 @@ const MONTH_NAMES = [
|
||||
|
||||
// ── Helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
function countWorkingDays(year: number, month: number, upToDay?: number): number {
|
||||
let count = 0;
|
||||
const cur = new Date(year, month, 1);
|
||||
while (cur.getMonth() === month && (!upToDay || cur.getDate() <= upToDay)) {
|
||||
const dow = cur.getDay();
|
||||
if (dow !== 0 && dow !== 6) count++;
|
||||
cur.setDate(cur.getDate() + 1);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function calcWorkedHours(arrival: Date, departure: Date, breakStart: Date | null, breakEnd: Date | null): number {
|
||||
let mins = (departure.getTime() - arrival.getTime()) / 60000;
|
||||
@@ -162,7 +153,7 @@ export async function getStatus(userId: number) {
|
||||
where: { user_id: userId, shift_date: { gte: monthStart, lte: monthEnd } },
|
||||
});
|
||||
|
||||
const workingDays = countWorkingDays(y, m);
|
||||
const workingDays = getBusinessDaysInMonth(y, m);
|
||||
const fund = workingDays * 8;
|
||||
|
||||
let workedHours = 0;
|
||||
@@ -375,8 +366,8 @@ export async function getWorkfund(year: number) {
|
||||
|
||||
for (let m = 0; m <= maxMonth; m++) {
|
||||
const isCurrentMonth = year === currentYear && m === currentMonth;
|
||||
const bizDays = countWorkingDays(year, m);
|
||||
const bizDaysToDate = isCurrentMonth ? countWorkingDays(year, m, now.getDate()) : bizDays;
|
||||
const bizDays = getBusinessDaysInMonth(year, m);
|
||||
const bizDaysToDate = isCurrentMonth ? getBusinessDaysInMonth(year, m, now.getDate()) : bizDays;
|
||||
const fund = bizDays * 8;
|
||||
const fundToDate = bizDaysToDate * 8;
|
||||
const monthStart = new Date(year, m, 1);
|
||||
@@ -406,10 +397,9 @@ export async function getWorkfund(year: number) {
|
||||
}
|
||||
}
|
||||
|
||||
const userFund = bizDaysToDate * 8;
|
||||
const userFund = fundToDate;
|
||||
const workedRound = Math.round(worked * 10) / 10;
|
||||
const holidayHours = holidayDays * 8;
|
||||
const leaveHours = vacationHours + sickHours + holidayHours;
|
||||
const leaveHours = vacationHours + sickHours;
|
||||
const covered = Math.round((worked + leaveHours) * 10) / 10;
|
||||
const missing = Math.max(0, Math.round((userFund - covered) * 10) / 10);
|
||||
const overtime = Math.max(0, Math.round((covered - userFund) * 10) / 10);
|
||||
@@ -552,7 +542,7 @@ export async function getPrintData(monthStr: string, filterUserId: number | null
|
||||
orderBy: [{ users: { last_name: 'asc' } }, { shift_date: 'asc' }],
|
||||
});
|
||||
|
||||
const fundHours = countWorkingDays(yr, mo - 1) * 8;
|
||||
const fundHours = getBusinessDaysInMonth(yr, mo - 1) * 8;
|
||||
|
||||
// Load project names for enrichment
|
||||
const projectIds = [...new Set(records.flatMap(r => (r as any).attendance_project_logs?.map((l: any) => l.project_id) || []).filter(Boolean))];
|
||||
@@ -656,7 +646,7 @@ export async function getPrintData(monthStr: string, filterUserId: number | null
|
||||
selected_user: filterUserId,
|
||||
selected_user_name: selectedUserName,
|
||||
year: yr,
|
||||
fund: { business_days: countWorkingDays(yr, mo - 1), hours: fundHours },
|
||||
fund: { business_days: getBusinessDaysInMonth(yr, mo - 1), hours: fundHours },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user