feat: NAS storage for invoices/offers, code cleanup, date/time fixes
- NAS storage for created invoices (PDF via puppeteer), received invoices, and offers with auto-save on create/edit - Deterministic file paths derived from DB fields (no file_path column needed) - Separate NAS mount points: NAS_FINANCIALS_PATH, NAS_OFFERS_PATH - Invoice language field (cs/en) stored per invoice, replaces lang modal - Invoices list filtered by month/year matching KPI card selection - Centralized date helpers (src/utils/date.ts) replacing all .toISOString() calls that returned UTC instead of local time - Attendance project switching uses exact time (not rounded) - Comment cleanup: removed ~100 unnecessary/Czech comments - Removed as-any casts in orders and attendance - Prisma migrations: add invoice language, drop received_invoices BLOB columns Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { attendance_leave_type, Prisma } from "@prisma/client";
|
||||
import prisma from "../config/database";
|
||||
import { getBusinessDaysInMonth } from "../utils/czech-holidays";
|
||||
import { localDateStr } from "../utils/date";
|
||||
|
||||
type AttendanceWithRelations = Prisma.attendanceGetPayload<{
|
||||
include: {
|
||||
@@ -254,7 +255,6 @@ export async function getStatus(userId: number) {
|
||||
};
|
||||
|
||||
// 5) Project logs for ongoing shift
|
||||
// Collect all project IDs from completed shifts for name lookup
|
||||
const completedProjectIds = new Set<number>();
|
||||
for (const shift of todayShiftsRaw) {
|
||||
for (const log of shift.attendance_project_logs) {
|
||||
@@ -262,7 +262,6 @@ export async function getStatus(userId: number) {
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch project names for completed shifts
|
||||
const completedProjectNames = new Map<number, string>();
|
||||
if (completedProjectIds.size > 0) {
|
||||
const projects = await prisma.projects.findMany({
|
||||
@@ -277,7 +276,6 @@ export async function getStatus(userId: number) {
|
||||
}
|
||||
}
|
||||
|
||||
// Enrich today's completed shifts with project names
|
||||
const todayShifts = todayShiftsRaw.map((shift) => ({
|
||||
...shift,
|
||||
project_logs: shift.attendance_project_logs.map((l) => ({
|
||||
@@ -337,7 +335,7 @@ export async function getStatus(userId: number) {
|
||||
today_shifts: todayShifts,
|
||||
leave_balance: leaveBalance,
|
||||
monthly_fund: monthlyFund,
|
||||
date: now.toISOString().split("T")[0],
|
||||
date: localDateStr(now),
|
||||
project_logs: projectLogs,
|
||||
active_project_id: activeProjectId,
|
||||
};
|
||||
@@ -759,7 +757,6 @@ export async function getPrintData(
|
||||
|
||||
const fundHours = getBusinessDaysInMonth(yr, mo - 1) * 8;
|
||||
|
||||
// Load project names for enrichment
|
||||
const typedRecords = records as AttendanceWithRelations[];
|
||||
|
||||
const projectIds = [
|
||||
@@ -784,7 +781,6 @@ export async function getPrintData(
|
||||
}
|
||||
}
|
||||
|
||||
// Group records by user and calculate totals
|
||||
const userTotals: Record<string, Record<string, unknown>> = {};
|
||||
for (const rec of typedRecords) {
|
||||
const uid = String(rec.user_id);
|
||||
@@ -806,7 +802,6 @@ export async function getPrintData(
|
||||
};
|
||||
}
|
||||
|
||||
// Build record with project_logs for frontend
|
||||
const projectLogs =
|
||||
rec.attendance_project_logs?.map((log) => ({
|
||||
project_id: log.project_id,
|
||||
@@ -843,7 +838,6 @@ export async function getPrintData(
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate fund coverage per user
|
||||
for (const uid of Object.keys(userTotals)) {
|
||||
const ut = userTotals[uid];
|
||||
const workedH = Math.round(((ut.minutes as number) / 60) * 10) / 10;
|
||||
@@ -864,7 +858,6 @@ export async function getPrintData(
|
||||
);
|
||||
}
|
||||
|
||||
// Leave balances
|
||||
const leaveBalances: Record<string, Record<string, number>> = {};
|
||||
const balanceRecords = await prisma.leave_balances.findMany({
|
||||
where: { year: yr },
|
||||
@@ -878,7 +871,6 @@ export async function getPrintData(
|
||||
};
|
||||
}
|
||||
|
||||
// Selected user name
|
||||
let selectedUserName = "";
|
||||
if (filterUserId) {
|
||||
const u = users.find((u) => u.id === filterUserId);
|
||||
@@ -912,7 +904,8 @@ export async function getActiveProjects() {
|
||||
});
|
||||
return activeProjects.map((p) => ({
|
||||
id: p.id,
|
||||
name: p.project_number ? `${p.project_number} – ${p.name}` : p.name,
|
||||
name: p.name,
|
||||
project_number: p.project_number ?? "",
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1069,9 +1062,7 @@ export async function bulkCreateAttendance(data: BulkAttendanceData) {
|
||||
select: { user_id: true, shift_date: true },
|
||||
});
|
||||
const existingSet = new Set(
|
||||
existing.map(
|
||||
(r) => `${r.user_id}:${r.shift_date.toISOString().split("T")[0]}`,
|
||||
),
|
||||
existing.map((r) => `${r.user_id}:${localDateStr(r.shift_date)}`),
|
||||
);
|
||||
|
||||
let inserted = 0;
|
||||
@@ -1080,7 +1071,7 @@ export async function bulkCreateAttendance(data: BulkAttendanceData) {
|
||||
for (const userId of data.user_ids.map(Number)) {
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
const date = new Date(yr, mo - 1, day);
|
||||
const dateStr = date.toISOString().split("T")[0];
|
||||
const dateStr = localDateStr(date);
|
||||
const dow = date.getDay();
|
||||
|
||||
if (dow === 0 || dow === 6) continue;
|
||||
@@ -1136,7 +1127,7 @@ export async function createLeave(data: LeaveData, authUserId: number) {
|
||||
while (current <= end) {
|
||||
const dow = current.getDay();
|
||||
if (dow !== 0 && dow !== 6) {
|
||||
const dateStr = current.toISOString().split("T")[0];
|
||||
const dateStr = localDateStr(current);
|
||||
const shiftDate = new Date(
|
||||
Date.UTC(
|
||||
current.getFullYear(),
|
||||
@@ -1161,7 +1152,6 @@ export async function createLeave(data: LeaveData, authUserId: number) {
|
||||
current.setDate(current.getDate() + 1);
|
||||
}
|
||||
|
||||
// Update leave balance for vacation/sick
|
||||
const totalLeaveHours =
|
||||
created * (data.leave_hours ? Number(data.leave_hours) : 8);
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user