Baustein 7: Berichtsheft mit Vorschlaegen, Pruefung, Abzeichnen und PDF
- Taegliche Stichworte, je Tag ein eigenes Formular - Wochenblatt mit Betrieb, Berufsschule und Sonstiges, je mit Stunden - Vorschlaege aus Tagesnotizen, Aufgaben, Checklisten und Handouts - Pruefung mitlaufend beim Tippen: fehlende Stunden, Floskeln, IP- und MAC-Adressen, E-Mail, Zugangsdaten, Kundennamen - Warnung, keine Sperre - Einreichen ist immer moeglich - Formulierungshilfe mit Taetigkeitsverben und Gegenueberstellungen - Einreichen, zurueckgeben mit Anmerkung, abzeichnen; danach gesperrt - Fehlende Wochen werden benannt und lassen sich nachtragen; die laufende Nummer richtet sich nach dem Zeitraum, nicht nach dem Anlegen - PDF im IHK-Layout, einzeln oder als ganzer Zeitraum Behoben: Im PDF-Kopffeld liefen die Spalten auseinander (dokument.y als Bezugspunkt in der Schleife). Das Abzeichnen-Formular hatte keine action - gefunden ueber die ESLint-Meldung zur ungenutzten Serveraktion. 55 neue Tests, insgesamt 241. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b9d6041f3b
commit
7c4a051347
@@ -0,0 +1,77 @@
|
||||
import { erfordereBenutzer, ausbildungVonAzubi } from '@/lib/auth/rechte';
|
||||
import { darfAzubiDatenSehen } from '@/lib/auth/regeln';
|
||||
import { benutzerNachId } from '@/lib/repos/benutzer';
|
||||
import { alsWochenblatt, berichteVon } from '@/lib/repos/berichtsheft';
|
||||
import { berichtsheftPdf, type Blattangaben } from '@/lib/pdf/berichtsheft';
|
||||
import { ausbildungsjahr } from '@/lib/berichtsheft/jahr';
|
||||
import { alsDatumText, alsZeitpunktText } from '@/lib/zeit';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
const datumMuster = /^\d{4}-\d{2}-\d{2}$/;
|
||||
|
||||
/**
|
||||
* Ein ganzer Zeitraum als PDF, ein Blatt je Seite.
|
||||
*
|
||||
* /berichtsheft/pdf?von=2026-08-01&bis=2026-12-31[&azubi=…]
|
||||
*
|
||||
* Ohne `azubi` das eigene Berichtsheft. Der Ausbilder gibt die Kennung an –
|
||||
* geprüft wird sie serverseitig gegen das Ausbildungsverhältnis.
|
||||
*/
|
||||
export async function GET(anfrage: Request) {
|
||||
const benutzer = await erfordereBenutzer();
|
||||
const adresse = new URL(anfrage.url);
|
||||
|
||||
const azubiId = benutzer.rolle === 'azubi' ? benutzer.id : (adresse.searchParams.get('azubi') ?? '');
|
||||
if (!azubiId) return new Response('Nicht gefunden.', { status: 404 });
|
||||
|
||||
const bezug = ausbildungVonAzubi(azubiId);
|
||||
if (!darfAzubiDatenSehen(benutzer, azubiId, bezug)) {
|
||||
return new Response('Nicht gefunden.', { status: 404 });
|
||||
}
|
||||
|
||||
const von = adresse.searchParams.get('von') ?? '';
|
||||
const bis = adresse.searchParams.get('bis') ?? '';
|
||||
if (!datumMuster.test(von) || !datumMuster.test(bis) || von > bis) {
|
||||
return new Response('Ungültiger Zeitraum.', { status: 400 });
|
||||
}
|
||||
|
||||
const azubi = benutzerNachId(azubiId);
|
||||
const ausbilder = bezug ? benutzerNachId(bezug.ausbilderId) : null;
|
||||
|
||||
// Aufsteigend, damit das Heft in der richtigen Reihenfolge ausgedruckt wird.
|
||||
const berichte = berichteVon(azubiId)
|
||||
.filter((b) => b.bis >= von && b.von <= bis)
|
||||
.sort((a, b) => a.nr - b.nr);
|
||||
|
||||
const blaetter: Blattangaben[] = berichte.map((bericht) => {
|
||||
const abzeichner = bericht.abgezeichnetVon ? benutzerNachId(bericht.abgezeichnetVon) : null;
|
||||
return {
|
||||
nr: bericht.nr,
|
||||
von: alsDatumText(bericht.von),
|
||||
bis: alsDatumText(bericht.bis),
|
||||
azubi: azubi?.name ?? 'unbekannt',
|
||||
ausbilder: ausbilder?.name ?? '',
|
||||
beruf: bezug?.beruf ?? 'Fachinformatiker/-in für Systemintegration',
|
||||
ausbildungsjahr: bezug ? ausbildungsjahr(bezug.beginn, bericht.von) : null,
|
||||
status: bericht.status,
|
||||
anmerkung: bericht.anmerkung,
|
||||
abgezeichnetVon: abzeichner?.name ?? null,
|
||||
abgezeichnetAm: bericht.abgezeichnetAm ? alsZeitpunktText(bericht.abgezeichnetAm) : null,
|
||||
blatt: alsWochenblatt(bericht),
|
||||
};
|
||||
});
|
||||
|
||||
const pdf = await berichtsheftPdf(blaetter, `${alsDatumText(von)} bis ${alsDatumText(bis)}`);
|
||||
const dateiname = `Ausbildungsnachweis_${von}_bis_${bis}.pdf`;
|
||||
|
||||
return new Response(new Uint8Array(pdf), {
|
||||
headers: {
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': String(pdf.byteLength),
|
||||
'Content-Disposition': `inline; filename="${dateiname}"`,
|
||||
'Cache-Control': 'private, no-store',
|
||||
'X-Content-Type-Options': 'nosniff',
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user