diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..305f6cc --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,50 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Projekt + +Desktop-Fakturierungsanwendung (Einzelplatz) für ein Hochschulprojekt (SE1, Team 1). Java 21, Swing-GUI mit FlatLaf, Maven-Build. Alle Texte (Klassen, Methoden, Variablen, Kommentare, Spezifikationen) sind auf **Deutsch**. + +## Build & Run + +```bash +./mvnw compile # Kompilieren +./mvnw test # Alle Tests (JUnit 5) +./mvnw test -Dtest=KundenVerwaltungTest # Einzelne Testklasse +./mvnw test -Dtest=KundenVerwaltungTest#testMethode # Einzelne Testmethode +./mvnw package # Fat-JAR bauen (maven-shade-plugin) +java -jar target/fakturierung-1.0.0.jar # Anwendung starten +``` + +## PDF-Spezifikationen bauen + +Die `*.md`-Dateien im Wurzelverzeichnis (Lastenheft, Pflichtenheft_Gruppe{A-D}, Modultestplan) werden mit pandoc + XeLaTeX zu PDF konvertiert — es gibt kein Build-Skript, der Aufruf erfolgt manuell. + +## Diagramme + +PlantUML-Quelldateien liegen in `diagramme/`. Rendering via `tools/plantuml.jar`. + +## Architektur + +Vier fachliche Module, jeweils einer Projektgruppe zugeordnet: + +| Paket | Gruppe | Verantwortung | +|-------|--------|---------------| +| `dokumente` | A | Dokumentenzyklus: Angebot → Auftragsbestätigung → Lieferschein → Rechnung, Belegnummern, PDF-Export | +| `produkte` | B | Produktverwaltung (CRUD, Nummernvergabe, Löschsperre) | +| `kunden` | C | Kundenverwaltung (CRUD, Nummernvergabe, Löschsperre) | +| `gui` | D | Swing-Oberfläche (HauptFenster, Panels, Dialoge, Wizard) | +| `gemeinsam` | — | Querschnitt: EreignisBus, JsonPersistenz, CSV-Hilfe, Exceptions | + +### Wichtige Muster + +- **EreignisBus** (Observer): Services melden Datenänderungen (`DatenBereich.KUNDEN/PRODUKTE/DOKUMENTE`), GUI-Panels abonnieren und aktualisieren sich. Kein manueller Refresh nötig. +- **Repository-Interfaces** (`KundenRepository`, `ProduktRepository`, `DokumentRepository`) mit JSON-Implementierungen. Persistenz erfolgt atomar via `JsonPersistenz.schreibeAtomar()`. +- **NummernGenerator-Interfaces** (`KundennummernGenerator`, `ProduktnummernGenerator`, `BelegnummernGenerator`) mit `Einfacher*`-Implementierungen, die den nächsten Wert aus dem Repository-Bestand ableiten. +- **ReferenzPrüfung**: `DokumentReferenzPruefung` verhindert Löschung von Kunden/Produkten, die in Dokumenten referenziert werden. +- **Wiring** in `Main.java`: Manuelle Dependency Injection — kein Framework. + +### Persistenz + +Alle Daten liegen als JSON-Dateien im Verzeichnis `daten/` (kunden.json, produkte.json, dokumente.json). Dieses Verzeichnis ist in `.gitignore`. diff --git a/Praesentation.md b/Praesentation.md new file mode 100644 index 0000000..f09055f --- /dev/null +++ b/Praesentation.md @@ -0,0 +1,232 @@ +--- +marp: true +theme: default +paginate: true +lang: de +header: 'Team 1 · Desktop-Fakturierungsanwendung' +footer: 'Abschlusspräsentation · Software Engineering 1 · 29.06.2026' +style: | + section { + font-size: 26px; + padding: 60px 70px; + color: #1f2933; + } + h1 { color: #16466b; } + h2 { color: #16466b; border-bottom: 3px solid #d9a441; padding-bottom: 6px; } + h3 { color: #2a6f97; } + strong { color: #16466b; } + table { font-size: 22px; } + th { background: #16466b; color: #fff; } + tr:nth-child(even) { background: #f2f5f8; } + code { background: #eef2f6; color: #16466b; } + section.lead { justify-content: center; text-align: center; } + section.lead h1 { font-size: 52px; } + section.divider { + background: #16466b; color: #fff; justify-content: center; + } + section.divider h1, section.divider h2 { color: #fff; border: none; } + section.divider h3 { color: #d9a441; } + footer, header { color: #8a98a5; } +--- + + + + + + +# Desktop-Fakturierungsanwendung + +### Schlanke, lokal betriebene Fakturierung für Kleinstunternehmen + +**Abschlusspräsentation · Software Engineering 1 (SoSe 2026)** +**Team 1** + +29.06.2026 · Prof. Dr. Gerd Marmitt + +--- + +## Agenda + +1. **Team & Aufgabenverteilung** +2. **Kontext & Ziel** des Projekts +3. **Architektur** im Überblick +4. **Live-Demo** des Programms *(≈ 10 Min)* +5. **Entwicklungsprojekt** *(≈ 5 Min)* + - Ergebnisse der Modultestpläne + - Wo wurde KI eingesetzt – und wie gut? + - Was würden wir nächstes Mal anders machen? +6. **Fragen** *(5 Min)* + +--- + +## Team & Aufgabenverteilung + +12 Personen, vier fachliche Komponenten (V-Modell, Einzelplatzanwendung): + +| Gruppe | Komponente | Leitung | Mitglieder | +|---|---|---|---| +| **A** | Prozess / Dokumentenzyklus | Lucas Strubel | Luca Kaiser | +| **B** | Produktverwaltung | Meron Berhane | Jan-Micah SchulzeAmeling · Jessica Volz | +| **C** | Kundenverwaltung | Mahsuna Ahadyar | Kübra Kilic · Mara Weidmann | +| **D** | Programmoberfläche | Mirkan Güngör | Moritz König · Mohammed Bouhki | + +Querschnitt (`gemeinsam`): EreignisBus, JSON-Persistenz, CSV-Hilfe – von allen Gruppen genutzt. + +--- + +## Kontext & Ziel + +**Problem:** SaaS-Fakturierung kostet laufend Lizenzgebühren; Open-Source-Alternativen (z. B. *Fakturama*) sind im Betrieb anspruchsvoll. + +**Unsere Lösung:** schlanke Desktop-Anwendung mit voller Datensouveränität – ohne Cloud. + +| Ziel | Inhalt | +|---|---| +| **PZ-01** | Digitale Verwaltung von Kunden- & Produktstammdaten (CRUD) | +| **PZ-02** | Vollständiger Dokumentenzyklus: Angebot → AB → Lieferschein → Rechnung | +| **PZ-03** | Bedienbar ohne Vorkenntnisse (Rechnung in < 10 Min) | +| **PZ-04** | 100 % lokale, DSGVO-konforme Datenhaltung | + +*Nichtziele:* Mehrbenutzer/Netzwerk, vollständige Buchhaltung, E-Rechnung (ZUGFeRD/XRechnung). + +--- + +## Architektur im Überblick + +**Vier Fachmodule + gemeinsame Infrastruktur**, manuelle Dependency Injection in `Main.java` (kein Framework). + +- **Repository-Interfaces** je Domäne mit JSON-Implementierungen; atomare Schreibvorgänge (`JsonPersistenz.schreibeAtomar`) +- **EreignisBus** (Observer): Services melden Datenänderungen, GUI-Panels aktualisieren sich selbst +- **Snapshot-Prinzip** in Belegen + **Löschsperren** (referenzielle Integrität) + +**Technologie-Stack** + +`Java 21` · `Swing + FlatLaf 3.6` · `Maven` (Fat-JAR via shade) · `Jackson 2.17` (JSON) · `Apache PDFBox 3.0` (PDF) · `JUnit 5.10` + +--- + + + + +# Live-Demo +### Das entwickelte Programm + +--- + +## Demo 1 — Stammdaten + +**Kundenverwaltung (Gruppe C)** +- Anlegen mit eindeutiger Kundennummer (`K-000017`), Pflichtfeld-Validierung *(BA-01)* +- Suchen & sortierte Liste, Ändern *(BA-02, BA-04)* +- **Löschsperre**: Kunde mit verknüpften Dokumenten wird nicht gelöscht *(BA-03, GR-04)* + +**Produktverwaltung (Gruppe B)** +- Anlegen mit Nettopreis & zulässigem Steuersatz, Nummernvergabe `P-000042` *(BA-05)* +- Ändern, Suche, **Löschsperre** bei referenzierten Produkten *(BA-06–BA-08)* + +--- + +## Demo 2 — Dokumentenzyklus (Gruppe A) + +**Angebot → Auftragsbestätigung → Lieferschein → Rechnung** + +- Folgebeleg übernimmt Kunde, Positionen & Mengen und speichert eine **Rückreferenz** *(GR-05)* +- **Lückenlose** Belegnummern `R-2026-000124` (GoBD) *(GR-01)* +- Automatische **Steuerberechnung** netto / Steuer / brutto, Scale 2 *(GR-03)* +- Standard-**Zahlungsziel** +14 Tage, falls nicht angegeben *(GR-06)* +- Rechnung mit allen Pflichtangaben gem. **§ 14 UStG** *(BA-12)* + +--- + +## Demo 3 — Wizard, Export & Storno + +- **Geführte Rechnungserstellung** (Wizard): Kunde → Positionen → Bestätigen → Zusammenfassung → Speichern *(BA-13)* +- **PDF-Export** der Belege (PDFBox) ins lokale Dateisystem *(IF-01)* +- **CSV-Vollexport** von Stamm- **und** Bewegungsdaten – offenes Format *(Q-08)* +- **Stornierung** einer offenen Rechnung mit Datum & Benutzer; versendete Belege sind **unveränderlich** *(BA-14, GR-02)* + +--- + + + + +# Das Entwicklungsprojekt +### Modultests · KI-Einsatz · Lessons Learned + +--- + +## Ergebnisse der Modultestpläne + +**71 deterministische JUnit-5-Testfälle – alle grün.** Nachbarkomponenten durch Stubs/Mocks ersetzt. + +| Komponente | Testfälle | +|---|---| +| A — Dokumentenzyklus (Zyklus, Persistenz, PDF, CSV) | 18 | +| B — Produktverwaltung | 14 | +| C — Kundenverwaltung | 14 | +| D — Programmoberfläche (Controller) | 15 | +| Gemeinsame Infrastruktur (EreignisBus, JSON) | 6 | +| Performance/Last (Q-02/03/04/08) | 4 | +| **Summe** | **71** | + +Performance-Schranken eingehalten: Start ≤ 5 s, Suche ≤ 1 s, PDF ≤ 2 s, Export ≤ 30 s (5.000 Kunden/Produkte). + +--- + +## Traceability + +**Lückenlose Kette: Anforderung → Codebeleg → Testfall.** + +- Spezifikation der Testfälle: **`Modultestplan.md`** (Vorbedingung / Eingabe / erwartetes Ergebnis) +- Nachweismatrix: **`Anforderungsabgleich.md`** — BA-01…14, GR-01…06, Q-01…09, AC-01…11 +- Jede Testfall-ID ist auch im Code per `@DisplayName` auffindbar + (`TC-…`, `INF-…`, `Q-…`) → direkt grep-bar + +> Alle durch Code/Tests belegbaren Anforderungen sind ✅; offen bleiben nur organisatorische Usability-Tests (Q-05/AC-11). + +--- + +## Wo wurde KI eingesetzt – und wie gut? + +> *Entwurf — vom Team mit eigenen Erfahrungswerten zu ergänzen.* + +**Eingesetzt für** +- **Dokumentation:** Entwürfe für Lasten-/Pflichtenhefte, Modultestplan, Traceability-Matrix +- **Code:** Boilerplate der Repository-/Service-Schicht, JSON-Persistenz, CSV-Export, JUnit-Testgerüste +- **Diagramme & Tooling:** PlantUML-Quellen, Maven-Konfiguration, pandoc-/PDF-Workflow + +**Bewertung** +- 👍 Hohes Tempo bei wiederkehrendem Code, Tests & Doku; konsistente deutsche Fachsprache; schnelle Iteration nach Feedback +- 👎 Fachlogik (§ 14 UStG, GoBD, Steuer-/Snapshot-Regeln) und Gruppen-Schnittstellen mussten **manuell geprüft & nachgeschärft** werden; Vorschläge teils zu generisch + +--- + +## Was würden wir nächstes Mal anders machen? + +> *Entwurf — vom Team zu ergänzen.* + +- **Früher integrieren:** modulübergreifende Tests nicht erst spät im V-Modell +- **Schnittstellen-Verträge** zwischen Gruppen A–D verbindlich zu Projektbeginn festlegen +- **Kleinere, häufigere Commits** + konsequentere Pull-Request-Reviews +- **KI-Output gezielter prüfen** statt übernehmen – besonders bei Fachlogik +- Aufgaben-/Lastverteilung über die Git-History früh sichtbar machen + +--- + +## Zusammenfassung + +- Vollständiger **Dokumentenzyklus** + **Stammdatenverwaltung** lauffähig umgesetzt +- **Lokale, DSGVO-konforme** Einzelplatzanwendung (Java 21, Swing/FlatLaf) +- **71 grüne Modultests** mit vollständiger **Traceability** zu den Anforderungen +- Alle Performance-Anforderungen erfüllt + +--- + + + + +# Vielen Dank! + +### Fragen? + +**Team 1 · Desktop-Fakturierungsanwendung · SE1 SoSe 2026** diff --git a/Praesentation.pdf b/Praesentation.pdf new file mode 100644 index 0000000..8650f76 Binary files /dev/null and b/Praesentation.pdf differ diff --git a/src/test/java/de/team1/faktura/dokumente/DokumentCsvExportTest.java b/src/test/java/de/team1/faktura/dokumente/DokumentCsvExportTest.java index 72c981a..475a515 100644 --- a/src/test/java/de/team1/faktura/dokumente/DokumentCsvExportTest.java +++ b/src/test/java/de/team1/faktura/dokumente/DokumentCsvExportTest.java @@ -23,7 +23,7 @@ class DokumentCsvExportTest { Path tempDir; @Test - @DisplayName("Q-08: Export schreibt Kopfzeile und je eine Zeile pro Dokumentposition") + @DisplayName("TC-18 (Q-08): Export schreibt Kopfzeile und je eine Zeile pro Dokumentposition") void exportiertAlleBelegeMitPositionen() throws IOException { JsonDokumentRepository repository = new JsonDokumentRepository(tempDir.resolve("dokumente.json")); repository.speichere(TestBelege.rechnung("R-2026-000001", DokumentStatus.OFFEN)); diff --git a/src/test/java/de/team1/faktura/dokumente/JsonDokumentRepositoryTest.java b/src/test/java/de/team1/faktura/dokumente/JsonDokumentRepositoryTest.java index b8f4146..bc7ec19 100644 --- a/src/test/java/de/team1/faktura/dokumente/JsonDokumentRepositoryTest.java +++ b/src/test/java/de/team1/faktura/dokumente/JsonDokumentRepositoryTest.java @@ -21,7 +21,7 @@ class JsonDokumentRepositoryTest { Path tempDir; @Test - @DisplayName("Belege überleben den Neustart und behalten ihren Belegtyp") + @DisplayName("TC-14: Belege überleben den Neustart und behalten ihren Belegtyp") void belegeWerdenNachNeustartGeladen() { Path datei = tempDir.resolve("dokumente.json"); diff --git a/src/test/java/de/team1/faktura/dokumente/PdfBoxPdfExporterTest.java b/src/test/java/de/team1/faktura/dokumente/PdfBoxPdfExporterTest.java index cdce15e..d0343d0 100644 --- a/src/test/java/de/team1/faktura/dokumente/PdfBoxPdfExporterTest.java +++ b/src/test/java/de/team1/faktura/dokumente/PdfBoxPdfExporterTest.java @@ -24,7 +24,7 @@ class PdfBoxPdfExporterTest { Path tempDir; @Test - @DisplayName("Vollständiger Beleg wird als PDF-Datei exportiert") + @DisplayName("TC-15: Vollständiger Beleg wird als PDF-Datei exportiert") void exportiertVollstaendigenBeleg() { Path ziel = tempDir.resolve("rechnung.pdf"); Rechnung rechnung = TestBelege.rechnung("R-2026-000001", DokumentStatus.OFFEN); @@ -35,7 +35,7 @@ class PdfBoxPdfExporterTest { } @Test - @DisplayName("Altdaten mit null-Positionsfeldern werfen beim Export keinen Fehler") + @DisplayName("TC-16: Altdaten mit null-Positionsfeldern werfen beim Export keinen Fehler") void exportiertBelegMitNullPositionsfeldern() { Path ziel = tempDir.resolve("altdaten.pdf"); Rechnung rechnung = new Rechnung(); @@ -49,7 +49,7 @@ class PdfBoxPdfExporterTest { } @Test - @DisplayName("Summenberechnung behandelt null-Positionssummen als 0") + @DisplayName("TC-17: Summenberechnung behandelt null-Positionssummen als 0") void summenberechnungToleriertNullPositionen() { Rechnung rechnung = new Rechnung(); rechnung.setBelegnummer("R-2026-000098"); diff --git a/src/test/java/de/team1/faktura/gemeinsam/EreignisBusTest.java b/src/test/java/de/team1/faktura/gemeinsam/EreignisBusTest.java index 4eb774f..39cee0f 100644 --- a/src/test/java/de/team1/faktura/gemeinsam/EreignisBusTest.java +++ b/src/test/java/de/team1/faktura/gemeinsam/EreignisBusTest.java @@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; class EreignisBusTest { @Test - @DisplayName("melde benachrichtigt alle Beobachter des Bereichs") + @DisplayName("INF-01: melde benachrichtigt alle Beobachter des Bereichs") void benachrichtigtAlleBeobachter() { EreignisBus bus = new EreignisBus(); AtomicInteger ersterBeobachter = new AtomicInteger(); @@ -26,7 +26,7 @@ class EreignisBusTest { } @Test - @DisplayName("melde benachrichtigt nur Beobachter des betroffenen Bereichs") + @DisplayName("INF-02: melde benachrichtigt nur Beobachter des betroffenen Bereichs") void benachrichtigtNurBetroffenenBereich() { EreignisBus bus = new EreignisBus(); AtomicInteger kundenBeobachter = new AtomicInteger(); @@ -41,7 +41,7 @@ class EreignisBusTest { } @Test - @DisplayName("melde ohne Beobachter ist wirkungslos und wirft nicht") + @DisplayName("INF-03: melde ohne Beobachter ist wirkungslos und wirft nicht") void meldenOhneBeobachterIstWirkungslos() { new EreignisBus().melde(DatenBereich.PRODUKTE); } diff --git a/src/test/java/de/team1/faktura/gemeinsam/JsonPersistenzTest.java b/src/test/java/de/team1/faktura/gemeinsam/JsonPersistenzTest.java index fb30385..cf06f23 100644 --- a/src/test/java/de/team1/faktura/gemeinsam/JsonPersistenzTest.java +++ b/src/test/java/de/team1/faktura/gemeinsam/JsonPersistenzTest.java @@ -19,7 +19,7 @@ class JsonPersistenzTest { private final ObjectMapper mapper = JsonPersistenz.mapper(); @Test - @DisplayName("schreibeAtomar legt die Zieldatei an und lässt keine Temp-Datei zurück") + @DisplayName("INF-04: schreibeAtomar legt die Zieldatei an und lässt keine Temp-Datei zurück") void schreibtOhneTempDateiRueckstand(@TempDir Path verzeichnis) throws IOException { Path datei = verzeichnis.resolve("bestand.json"); @@ -33,7 +33,7 @@ class JsonPersistenzTest { } @Test - @DisplayName("schreibeAtomar ersetzt einen vorhandenen Bestand vollständig") + @DisplayName("INF-05: schreibeAtomar ersetzt einen vorhandenen Bestand vollständig") void ersetztVorhandenenBestand(@TempDir Path verzeichnis) throws IOException { Path datei = verzeichnis.resolve("bestand.json"); JsonPersistenz.schreibeAtomar(datei, mapper.writer(), List.of("alt")); @@ -45,7 +45,7 @@ class JsonPersistenzTest { } @Test - @DisplayName("schreibeAtomar legt fehlende Elternverzeichnisse an") + @DisplayName("INF-06: schreibeAtomar legt fehlende Elternverzeichnisse an") void legtElternverzeichnisseAn(@TempDir Path verzeichnis) throws IOException { Path datei = verzeichnis.resolve("unterordner").resolve("bestand.json");