From 4e46df813c75aaf2296b86f8f6db999c2b3ecae0 Mon Sep 17 00:00:00 2001 From: schakal00783 Date: Mon, 21 Sep 2026 18:08:05 +0200 Subject: [PATCH] fix(export): Stundennachweis nur fuer laufende Projekte auswaehlbar Die Auswahl im Exportdialog kam aus allen zugewiesenen Projekten - also auch aus archivierten und von stillgelegten Kunden. In der Liste stand damit etwa "Top IT Services", wo seit 2025 nichts mehr laeuft. Waehlbar sind jetzt nur laufende Projekte aktiver Kunden. Dafuer liefert loadProjects den Status des Kunden mit; ohne ihn liesse sich "nur aktive Kunden" in der Oberflaeche nicht entscheiden. Die Zeiten aus archivierten Projekten bleiben erreichbar: ohne Filter exportiert der Nachweis alles, was die Person im Monat gebucht hat, und der Projektreport hat seinen eigenen Export. Co-Authored-By: Claude Opus 5 (1M context) --- server/src/lib/projects.js | 3 ++- src/components/booking/ExportDialog.tsx | 14 +++++++++++--- src/types.ts | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/server/src/lib/projects.js b/server/src/lib/projects.js index da4170e..d056b28 100644 --- a/server/src/lib/projects.js +++ b/server/src/lib/projects.js @@ -175,6 +175,7 @@ export function isBillable(project, task) { export async function loadProjects(settings, { where = '1=1', params = [] } = {}) { const [projects] = await pool.query( `SELECT p.*, c.name AS customer_name, c.short_name AS customer_short, c.color AS customer_color, + c.is_active AS customer_active, u.name AS owner_name FROM pulse_projects p JOIN pulse_customers c ON c.id = p.customer_id @@ -270,7 +271,7 @@ export async function loadProjects(settings, { where = '1=1', params = [] } = {} return { ...project, display_name: displayName(settings, project, customer), - customer: { id: p.customer_id, name: p.customer_name, short_name: p.customer_short, color: p.customer_color }, + customer: { id: p.customer_id, name: p.customer_name, short_name: p.customer_short, color: p.customer_color, is_active: Boolean(p.customer_active) }, pots: projectPots, tasks: tasks .filter((t) => t.project_id === p.id) diff --git a/src/components/booking/ExportDialog.tsx b/src/components/booking/ExportDialog.tsx index 0a8ab40..fb23c6c 100644 --- a/src/components/booking/ExportDialog.tsx +++ b/src/components/booking/ExportDialog.tsx @@ -33,12 +33,20 @@ export const ExportDialog: React.FC<{ const [busy, setBusy] = useState(false); const isLead = atLeast(user, 'lead'); + /* + * Zur Auswahl stehen nur laufende Projekte aktiver Kunden. Archivierte + * Projekte und stillgelegte Kunden wuerden die Liste mit Aufträgen fuellen, + * fuer die niemand mehr einen Nachweis schreibt. Ihre Zeiten sind dadurch + * nicht verloren: ohne Filter exportiert der Nachweis alles, was die Person + * im Monat gebucht hat - und der Projektreport hat einen eigenen Export. + */ + const waehlbar = useMemo(() => projects.filter((p) => p.is_active && p.customer.is_active !== false), [projects]); const customers = useMemo(() => { const map = new Map(); - projects.forEach((p) => map.set(p.customer.id, p.customer.name)); + waehlbar.forEach((p) => map.set(p.customer.id, p.customer.name)); return [...map.entries()].sort((a, b) => a[1].localeCompare(b[1], 'de')); - }, [projects]); - const projectOptions = projects.filter((p) => !customerId || p.customer_id === customerId); + }, [waehlbar]); + const projectOptions = waehlbar.filter((p) => !customerId || p.customer_id === customerId); // Monate: 18 zurueck, 2 voraus. const months = useMemo(() => { diff --git a/src/types.ts b/src/types.ts index 32fe208..cfa8088 100644 --- a/src/types.ts +++ b/src/types.ts @@ -178,7 +178,7 @@ export interface Project { owner_id: number | null; owner_name: string | null; is_active: boolean; - customer: { id: number; name: string; short_name: string; color: string | null }; + customer: { id: number; name: string; short_name: string; color: string | null; is_active: boolean }; pots: BudgetPot[]; tasks: Task[]; members: MemberRef[];