Gruppe K: Belegworkflow hinzugefuegt

main
Christian Khazanovych 2026-06-24 17:34:16 +02:00
parent a88ad8ec1b
commit 3d74423278
18 changed files with 555 additions and 10 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,33 @@
package belegworkflow;
import java.time.LocalDate;
import java.util.List;
public class Angebot extends Dokument {
private LocalDate gueltigBis;
private AngebotStatus status;
public Angebot(String belegnummer, LocalDate datum, String kundenReferenz,
List<Dokumentposition> positionen, LocalDate gueltigBis) {
super(belegnummer, datum, kundenReferenz, positionen);
this.gueltigBis = gueltigBis;
this.status = AngebotStatus.OFFEN;
}
public LocalDate getGueltigBis() { return gueltigBis; }
public AngebotStatus getStatus() { return status; }
public void setStatus(AngebotStatus status) {
this.status = status;
}
@Override
public String toString() {
return String.format("[%s] Angebot | Kunde: %s | Datum: %s | Gueltig bis: %s | Status: %s\n%s\n%s",
belegnummer, kundenReferenz, datum, gueltigBis, status,
positionen.stream().map(Dokumentposition::toString)
.reduce("", (a, b) -> a + "\n" + b),
getSummenAnzeige());
}
}

View File

@ -0,0 +1,7 @@
package belegworkflow;
public enum AngebotStatus {
OFFEN,
UEBERFUEHRT,
VERWORFEN
}

View File

@ -0,0 +1,53 @@
package belegworkflow;
import java.io.*;
import java.nio.file.*;
public class BelegnummernGenerator {
private static final String ANGEBOT_COUNTER = "data/angebot_counter.txt";
private static final String RECHNUNG_COUNTER = "data/rechnung_counter.txt";
private int angebotCounter;
private int rechnungCounter;
public BelegnummernGenerator() {
this.angebotCounter = ladeCounter(ANGEBOT_COUNTER);
this.rechnungCounter = ladeCounter(RECHNUNG_COUNTER);
}
// Nächste lückenlose Angebotsnummer (GR-01)
public String naechsteAngebotsnummer(int jahr) {
angebotCounter++;
speichereCounter(ANGEBOT_COUNTER, angebotCounter);
return String.format("AN-%d-%06d", jahr, angebotCounter);
}
// Nächste lückenlose Rechnungsnummer (GR-01, F-07)
public String naechsteRechnungsnummer(int jahr) {
rechnungCounter++;
speichereCounter(RECHNUNG_COUNTER, rechnungCounter);
return String.format("R-%d-%06d", jahr, rechnungCounter);
}
private int ladeCounter(String datei) {
try {
Path path = Path.of(datei);
if (Files.exists(path)) {
return Integer.parseInt(Files.readString(path).trim());
}
} catch (IOException e) {
System.err.println("Counter konnte nicht geladen werden: " + datei);
}
return 0;
}
private void speichereCounter(String datei, int wert) {
try {
Files.createDirectories(Path.of("data"));
Files.writeString(Path.of(datei), String.valueOf(wert));
} catch (IOException e) {
System.err.println("Counter konnte nicht gespeichert werden: " + datei);
}
}
}

View File

@ -0,0 +1,191 @@
package belegworkflow;
import java.io.*;
import java.nio.file.*;
import java.time.LocalDate;
import java.util.*;
public class BelegworkflowService {
private static final String ANGEBOTE_FILE = "data/angebote.csv";
private static final String RECHNUNGEN_FILE = "data/rechnungen.csv";
private List<Angebot> angebote;
private List<Rechnung> rechnungen;
private BelegnummernGenerator generator;
public BelegworkflowService() throws IOException {
this.angebote = new ArrayList<>();
this.rechnungen = new ArrayList<>();
this.generator = new BelegnummernGenerator();
Files.createDirectories(Path.of("data"));
ladeAngebote();
ladeRechnungen();
}
// Erstellt ein neues Angebot (F-01, F-02, F-03, F-04)
public Angebot erstelleAngebot(String kundenId, List<Dokumentposition> positionen,
LocalDate gueltigBis) {
if (kundenId == null || kundenId.isBlank()) {
System.out.println("Fehler: Kunden-ID darf nicht leer sein.");
return null;
}
if (positionen == null || positionen.isEmpty()) {
System.out.println("Fehler: Mindestens eine Position erforderlich.");
return null;
}
String nr = generator.naechsteAngebotsnummer(LocalDate.now().getYear());
Angebot angebot = new Angebot(nr, LocalDate.now(), kundenId, positionen, gueltigBis);
angebote.add(angebot);
speichereAngebote();
System.out.println("Angebot erfolgreich erstellt: " + nr);
return angebot;
}
// Erstellt eine Rechnung aus einem Angebot (F-05, F-06, F-07, F-08)
public Rechnung erstelleRechnungAusAngebot(String angebotsNr, LocalDate rechnungsdatum,
LocalDate leistungsdatum, LocalDate zahlungsziel) {
Angebot angebot = findeAngebot(angebotsNr);
if (angebot == null) {
System.out.println("Fehler: Angebot " + angebotsNr + " nicht gefunden.");
return null;
}
if (angebot.getStatus() != AngebotStatus.OFFEN) {
System.out.println("Fehler: Angebot ist nicht mehr offen.");
return null;
}
String nr = generator.naechsteRechnungsnummer(LocalDate.now().getYear());
Rechnung rechnung = new Rechnung(nr, LocalDate.now(), angebot.getKundenReferenz(),
angebot.getPositionen(), rechnungsdatum, leistungsdatum, zahlungsziel, angebotsNr);
// Angebot als überführt markieren (F-05)
angebot.setStatus(AngebotStatus.UEBERFUEHRT);
rechnungen.add(rechnung);
speichereAngebote();
speichereRechnungen();
System.out.println("Rechnung erfolgreich erstellt: " + nr);
return rechnung;
}
// Festschreiben einer Rechnung (F-10)
public void finalisiereRechnung(String rechnungsNr) {
Rechnung rechnung = findeRechnung(rechnungsNr);
if (rechnung == null) {
System.out.println("Fehler: Rechnung " + rechnungsNr + " nicht gefunden.");
return;
}
rechnung.festschreiben();
speichereRechnungen();
System.out.println("Rechnung " + rechnungsNr + " wurde festgeschrieben.");
}
// Prüft ob Kunde in einem Beleg referenziert wird (F-12)
public boolean istKundeReferenziert(String kundenId) {
return angebote.stream().anyMatch(a -> a.getKundenReferenz().equals(kundenId)) ||
rechnungen.stream().anyMatch(r -> r.getKundenReferenz().equals(kundenId));
}
// Prüft ob Produkt in einem Beleg referenziert wird (F-12)
public boolean istProduktReferenziert(String produktId) {
return angebote.stream().anyMatch(a -> a.getPositionen().stream()
.anyMatch(p -> p.getProduktReferenz().equals(produktId))) ||
rechnungen.stream().anyMatch(r -> r.getPositionen().stream()
.anyMatch(p -> p.getProduktReferenz().equals(produktId)));
}
public List<Angebot> alleAngebote() { return new ArrayList<>(angebote); }
public List<Rechnung> alleRechnungen() { return new ArrayList<>(rechnungen); }
public Angebot findeAngebot(String nr) {
return angebote.stream().filter(a -> a.getBelegnummer().equals(nr)).findFirst().orElse(null);
}
public Rechnung findeRechnung(String nr) {
return rechnungen.stream().filter(r -> r.getBelegnummer().equals(nr)).findFirst().orElse(null);
}
// Einfache CSV Persistenz für Angebote
private void speichereAngebote() {
try {
StringBuilder sb = new StringBuilder();
for (Angebot a : angebote) {
sb.append(a.getBelegnummer()).append(";")
.append(a.getDatum()).append(";")
.append(a.getKundenReferenz()).append(";")
.append(a.getGueltigBis()).append(";")
.append(a.getStatus()).append(";")
.append(a.getSummeNetto()).append(";")
.append(a.getSummeSteuer()).append(";")
.append(a.getSummeBrutto()).append("\n");
}
Files.writeString(Path.of(ANGEBOTE_FILE), sb.toString());
} catch (IOException e) {
System.err.println("Fehler beim Speichern der Angebote: " + e.getMessage());
}
}
private void ladeAngebote() {
try {
Path path = Path.of(ANGEBOTE_FILE);
if (!Files.exists(path)) return;
// Vereinfachtes Laden - nur Metadaten ohne Positionen
List<String> zeilen = Files.readAllLines(path);
for (String zeile : zeilen) {
if (zeile.isBlank()) continue;
String[] t = zeile.split(";", -1);
if (t.length < 8) continue;
Angebot a = new Angebot(t[0], LocalDate.parse(t[1]), t[2],
new ArrayList<>(), LocalDate.parse(t[3]));
a.setStatus(AngebotStatus.valueOf(t[4]));
angebote.add(a);
}
} catch (IOException e) {
System.err.println("Fehler beim Laden der Angebote: " + e.getMessage());
}
}
private void speichereRechnungen() {
try {
StringBuilder sb = new StringBuilder();
for (Rechnung r : rechnungen) {
sb.append(r.getBelegnummer()).append(";")
.append(r.getDatum()).append(";")
.append(r.getKundenReferenz()).append(";")
.append(r.getRechnungsdatum()).append(";")
.append(r.getLeistungsdatum()).append(";")
.append(r.getZahlungsziel()).append(";")
.append(r.getAngebotsNr()).append(";")
.append(r.getStatus()).append(";")
.append(r.isFestgeschrieben()).append(";")
.append(r.getSummeNetto()).append(";")
.append(r.getSummeSteuer()).append(";")
.append(r.getSummeBrutto()).append("\n");
}
Files.writeString(Path.of(RECHNUNGEN_FILE), sb.toString());
} catch (IOException e) {
System.err.println("Fehler beim Speichern der Rechnungen: " + e.getMessage());
}
}
private void ladeRechnungen() {
try {
Path path = Path.of(RECHNUNGEN_FILE);
if (!Files.exists(path)) return;
List<String> zeilen = Files.readAllLines(path);
for (String zeile : zeilen) {
if (zeile.isBlank()) continue;
String[] t = zeile.split(";", -1);
if (t.length < 12) continue;
Rechnung r = new Rechnung(t[0], LocalDate.parse(t[1]), t[2],
new ArrayList<>(), LocalDate.parse(t[3]), LocalDate.parse(t[4]),
LocalDate.parse(t[5]), t[6]);
r.setStatus(RechnungStatus.valueOf(t[7]));
if (Boolean.parseBoolean(t[8])) r.festschreiben();
rechnungen.add(r);
}
} catch (IOException e) {
System.err.println("Fehler beim Laden der Rechnungen: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,63 @@
package belegworkflow;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
public abstract class Dokument {
protected String belegnummer;
protected LocalDate datum;
protected String kundenReferenz;
protected List<Dokumentposition> positionen;
protected BigDecimal summeNetto;
protected BigDecimal summeSteuer;
protected BigDecimal summeBrutto;
protected boolean festgeschrieben;
public Dokument(String belegnummer, LocalDate datum, String kundenReferenz,
List<Dokumentposition> positionen) {
this.belegnummer = belegnummer;
this.datum = datum;
this.kundenReferenz = kundenReferenz;
this.positionen = positionen;
this.festgeschrieben = false;
berechnesSummen();
}
// Berechnet Netto-, Steuer- und Bruttosumme (F-03, F-09, GR-06)
protected void berechnesSummen() {
summeNetto = BigDecimal.ZERO;
summeSteuer = BigDecimal.ZERO;
for (Dokumentposition pos : positionen) {
summeNetto = summeNetto.add(pos.getPositionssummeNetto());
BigDecimal steuer = pos.getPositionssummeNetto()
.multiply(pos.getSteuersatz())
.setScale(2, RoundingMode.HALF_UP);
summeSteuer = summeSteuer.add(steuer);
}
summeNetto = summeNetto.setScale(2, RoundingMode.HALF_UP);
summeSteuer = summeSteuer.setScale(2, RoundingMode.HALF_UP);
summeBrutto = summeNetto.add(summeSteuer).setScale(2, RoundingMode.HALF_UP);
}
public String getBelegnummer() { return belegnummer; }
public LocalDate getDatum() { return datum; }
public String getKundenReferenz() { return kundenReferenz; }
public List<Dokumentposition> getPositionen() { return positionen; }
public BigDecimal getSummeNetto() { return summeNetto; }
public BigDecimal getSummeSteuer() { return summeSteuer; }
public BigDecimal getSummeBrutto() { return summeBrutto; }
public boolean isFestgeschrieben() { return festgeschrieben; }
public void festschreiben() {
this.festgeschrieben = true;
}
public String getSummenAnzeige() {
return String.format("Netto: %.2f EUR | MwSt: %.2f EUR | Brutto: %.2f EUR",
summeNetto, summeSteuer, summeBrutto);
}
}

View File

@ -0,0 +1,39 @@
package belegworkflow;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Dokumentposition {
private String produktReferenz;
private String bezeichnung;
private int menge;
private BigDecimal einzelpreisNetto;
private BigDecimal steuersatz;
private BigDecimal positionssummeNetto;
public Dokumentposition(String produktReferenz, String bezeichnung,
int menge, BigDecimal einzelpreisNetto, BigDecimal steuersatz) {
this.produktReferenz = produktReferenz;
this.bezeichnung = bezeichnung;
this.menge = menge;
this.einzelpreisNetto = einzelpreisNetto.setScale(2, RoundingMode.HALF_UP);
this.steuersatz = steuersatz.setScale(2, RoundingMode.HALF_UP);
this.positionssummeNetto = einzelpreisNetto
.multiply(new BigDecimal(menge))
.setScale(2, RoundingMode.HALF_UP);
}
public String getProduktReferenz() { return produktReferenz; }
public String getBezeichnung() { return bezeichnung; }
public int getMenge() { return menge; }
public BigDecimal getEinzelpreisNetto() { return einzelpreisNetto; }
public BigDecimal getSteuersatz() { return steuersatz; }
public BigDecimal getPositionssummeNetto(){ return positionssummeNetto; }
@Override
public String toString() {
return String.format(" %s x%d | %.2f EUR (netto) | MwSt: %.0f%%",
bezeichnung, menge, einzelpreisNetto,
steuersatz.multiply(new BigDecimal("100")));
}
}

View File

@ -0,0 +1,50 @@
package belegworkflow;
import java.time.LocalDate;
import java.util.List;
public class Rechnung extends Dokument {
private LocalDate rechnungsdatum;
private LocalDate leistungsdatum;
private LocalDate zahlungsziel;
private String angebotsNr;
private RechnungStatus status;
public Rechnung(String belegnummer, LocalDate datum, String kundenReferenz,
List<Dokumentposition> positionen, LocalDate rechnungsdatum,
LocalDate leistungsdatum, LocalDate zahlungsziel, String angebotsNr) {
super(belegnummer, datum, kundenReferenz, positionen);
this.rechnungsdatum = rechnungsdatum;
this.leistungsdatum = leistungsdatum;
this.zahlungsziel = zahlungsziel;
this.angebotsNr = angebotsNr;
this.status = RechnungStatus.OFFEN;
}
public LocalDate getRechnungsdatum() { return rechnungsdatum; }
public LocalDate getLeistungsdatum() { return leistungsdatum; }
public LocalDate getZahlungsziel() { return zahlungsziel; }
public String getAngebotsNr() { return angebotsNr; }
public RechnungStatus getStatus() { return status; }
public void setStatus(RechnungStatus status) {
// Schreibschutz prüfen (F-10)
if (this.festgeschrieben) {
System.out.println("Fehler: Festgeschriebene Rechnung kann nicht geaendert werden.");
return;
}
this.status = status;
}
@Override
public String toString() {
return String.format(
"[%s] Rechnung | Kunde: %s | Datum: %s | Leistung: %s | Zahlung bis: %s | Angebot: %s | Status: %s\n%s\n%s",
belegnummer, kundenReferenz, rechnungsdatum, leistungsdatum,
zahlungsziel, angebotsNr, status,
positionen.stream().map(Dokumentposition::toString)
.reduce("", (a, b) -> a + "\n" + b),
getSummenAnzeige());
}
}

View File

@ -0,0 +1,6 @@
package belegworkflow;
public enum RechnungStatus {
OFFEN,
BEZAHLT
}

View File

@ -2,9 +2,12 @@ package kundenverwaltung;
import bestandsmanagement.Produkt; import bestandsmanagement.Produkt;
import bestandsmanagement.ProduktServiceImpl; import bestandsmanagement.ProduktServiceImpl;
import belegworkflow.*;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Scanner; import java.util.Scanner;
@ -12,12 +15,14 @@ public class Main {
private static KundenServiceImpl kundenService; private static KundenServiceImpl kundenService;
private static ProduktServiceImpl produktService; private static ProduktServiceImpl produktService;
private static BelegworkflowService belegService;
private static Scanner scanner = new Scanner(System.in); private static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) { public static void main(String[] args) {
try { try {
kundenService = new KundenServiceImpl(); kundenService = new KundenServiceImpl();
produktService = new ProduktServiceImpl(); produktService = new ProduktServiceImpl();
belegService = new BelegworkflowService();
System.out.println("==========================================="); System.out.println("===========================================");
System.out.println(" Fakturierungsanwendung - Team 3"); System.out.println(" Fakturierungsanwendung - Team 3");
System.out.println("==========================================="); System.out.println("===========================================");
@ -32,18 +37,15 @@ public class Main {
System.out.println("\n=== HAUPTMENUE ==="); System.out.println("\n=== HAUPTMENUE ===");
System.out.println("1. Kundenverwaltung"); System.out.println("1. Kundenverwaltung");
System.out.println("2. Bestandsmanagement"); System.out.println("2. Bestandsmanagement");
System.out.println("3. Belegworkflow");
System.out.println("0. Beenden"); System.out.println("0. Beenden");
System.out.print("Auswahl: "); System.out.print("Auswahl: ");
String eingabe = scanner.nextLine().trim(); switch (scanner.nextLine().trim()) {
switch (eingabe) {
case "1" -> kundenmenue(); case "1" -> kundenmenue();
case "2" -> produktmenue(); case "2" -> produktmenue();
case "0" -> { case "3" -> belegmenue();
System.out.println("Programm beendet."); case "0" -> { System.out.println("Programm beendet."); return; }
return;
}
default -> System.out.println("Ungueltige Eingabe."); default -> System.out.println("Ungueltige Eingabe.");
} }
} }
@ -253,4 +255,105 @@ public class Main {
System.out.print("Artikel-ID: "); System.out.print("Artikel-ID: ");
produktService.produktLoeschen(scanner.nextLine()); produktService.produktLoeschen(scanner.nextLine());
} }
// ─── BELEGWORKFLOW ──────────────────────────────────────────────
private static void belegmenue() {
while (true) {
System.out.println("\n--- BELEGWORKFLOW ---");
System.out.println("1. Angebot erstellen");
System.out.println("2. Alle Angebote anzeigen");
System.out.println("3. Rechnung aus Angebot erstellen");
System.out.println("4. Alle Rechnungen anzeigen");
System.out.println("5. Rechnung festschreiben");
System.out.println("0. Zurueck");
System.out.print("Auswahl: ");
switch (scanner.nextLine().trim()) {
case "1" -> angebotErstellen();
case "2" -> alleAngeboteAnzeigen();
case "3" -> rechnungAusAngebot();
case "4" -> alleRechnungenAnzeigen();
case "5" -> rechnungFestschreiben();
case "0" -> { return; }
default -> System.out.println("Ungueltige Eingabe.");
}
}
}
private static void angebotErstellen() {
System.out.println("\n--- ANGEBOT ERSTELLEN ---");
System.out.print("Kunden-ID: ");
String kundenId = scanner.nextLine();
List<Dokumentposition> positionen = new ArrayList<>();
while (true) {
System.out.println("Position hinzufuegen (Enter zum Beenden):");
System.out.print("Produkt-ID (oder Enter zum Beenden): ");
String produktId = scanner.nextLine();
if (produktId.isBlank()) break;
Produkt produkt = produktService.findeProdukt(produktId);
if (produkt == null) {
System.out.println("Produkt nicht gefunden.");
continue;
}
System.out.print("Menge: ");
int menge = Integer.parseInt(scanner.nextLine().trim());
positionen.add(new Dokumentposition(
produkt.getProduktId(),
produkt.getBezeichnung(),
menge,
produkt.getNettoEinzelpreis(),
produkt.getMehrwertsteuersatz()
));
System.out.println("Position hinzugefuegt: " + produkt.getBezeichnung());
}
if (positionen.isEmpty()) {
System.out.println("Fehler: Mindestens eine Position erforderlich.");
return;
}
System.out.print("Gueltig bis (YYYY-MM-DD): ");
LocalDate gueltigBis = LocalDate.parse(scanner.nextLine());
belegService.erstelleAngebot(kundenId, positionen, gueltigBis);
}
private static void alleAngeboteAnzeigen() {
System.out.println("\n--- ALLE ANGEBOTE ---");
List<Angebot> angebote = belegService.alleAngebote();
if (angebote.isEmpty()) { System.out.println("Keine Angebote vorhanden."); return; }
angebote.forEach(a -> System.out.println(a.getBelegnummer() + " | Kunde: " +
a.getKundenReferenz() + " | Status: " + a.getStatus()));
}
private static void rechnungAusAngebot() {
System.out.println("\n--- RECHNUNG AUS ANGEBOT ---");
System.out.print("Angebotsnummer: ");
String angebotsNr = scanner.nextLine();
System.out.print("Rechnungsdatum (YYYY-MM-DD): ");
LocalDate rechnungsdatum = LocalDate.parse(scanner.nextLine());
System.out.print("Leistungsdatum (YYYY-MM-DD): ");
LocalDate leistungsdatum = LocalDate.parse(scanner.nextLine());
System.out.print("Zahlungsziel (YYYY-MM-DD): ");
LocalDate zahlungsziel = LocalDate.parse(scanner.nextLine());
belegService.erstelleRechnungAusAngebot(angebotsNr, rechnungsdatum, leistungsdatum, zahlungsziel);
}
private static void alleRechnungenAnzeigen() {
System.out.println("\n--- ALLE RECHNUNGEN ---");
List<Rechnung> rechnungen = belegService.alleRechnungen();
if (rechnungen.isEmpty()) { System.out.println("Keine Rechnungen vorhanden."); return; }
rechnungen.forEach(r -> System.out.println(r.getBelegnummer() + " | Kunde: " +
r.getKundenReferenz() + " | Status: " + r.getStatus() +
" | Festgeschrieben: " + r.isFestgeschrieben()));
}
private static void rechnungFestschreiben() {
System.out.println("\n--- RECHNUNG FESTSCHREIBEN ---");
System.out.print("Rechnungsnummer: ");
belegService.finalisiereRechnung(scanner.nextLine());
}
} }