diff --git a/Modultestbericht_GruppeF_Dokumentenprozess.md b/Unterlagen/Modultestbericht_GruppeF_Dokumentenprozess.md similarity index 100% rename from Modultestbericht_GruppeF_Dokumentenprozess.md rename to Unterlagen/Modultestbericht_GruppeF_Dokumentenprozess.md diff --git a/src/main/java/de/hsmannheim/faktura/dokumente/service/DokumentenService.java b/src/main/java/de/hsmannheim/faktura/dokumente/service/DokumentenService.java index 814da65..6d4a6cb 100644 --- a/src/main/java/de/hsmannheim/faktura/dokumente/service/DokumentenService.java +++ b/src/main/java/de/hsmannheim/faktura/dokumente/service/DokumentenService.java @@ -50,8 +50,8 @@ public class DokumentenService { * Vollständiger Konstruktor – für Produktion wenn echte Lookups vorliegen. */ public DokumentenService(DokumentRepository repository, - ProductLookup produktLookup, - CustomerLookup kundeLookup) { + ProductLookup produktLookup, + CustomerLookup kundeLookup) { this.repository = repository; this.service = new DokumentServiceImpl( repository, produktLookup, kundeLookup, new SimplePdfExportService() @@ -72,6 +72,16 @@ public class DokumentenService { return repository.findByNummer(nummer); } + /** + * Neues Angebot anlegen (PH-DP-01). + * @param kundenId Kunde (muss in Kundenverwaltung existieren / vom CustomerLookup auflösbar sein) + * @param positionen mindestens eine Position (Produkt-ID + Menge) + * @return das gespeicherte Angebot mit Nummer und Status OFFEN + */ + public Angebot createAngebot(long kundenId, List positionen) { + return service.erstelleAngebot(kundenId, positionen); + } + /** * Dokument löschen. Festgeschriebene Rechnungen können nicht gelöscht werden. * @return true wenn erfolgreich gelöscht @@ -140,4 +150,4 @@ public class DokumentenService { )); } } -} +} \ No newline at end of file diff --git a/src/main/java/de/hsmannheim/faktura/gui/DokumentPanel.java b/src/main/java/de/hsmannheim/faktura/gui/DokumentPanel.java index eb218af..41aec23 100644 --- a/src/main/java/de/hsmannheim/faktura/gui/DokumentPanel.java +++ b/src/main/java/de/hsmannheim/faktura/gui/DokumentPanel.java @@ -3,11 +3,17 @@ package de.hsmannheim.faktura.gui; import javax.swing.*; import javax.swing.table.*; import java.awt.*; +import java.math.BigDecimal; +import java.util.ArrayList; import java.util.List; +import de.hsmannheim.faktura.dokumente.domain.Angebot; +import de.hsmannheim.faktura.dokumente.domain.Auftragsbestaetigung; import de.hsmannheim.faktura.dokumente.domain.Dokument; import de.hsmannheim.faktura.dokumente.domain.DokumentStatus; +import de.hsmannheim.faktura.dokumente.domain.Lieferschein; import de.hsmannheim.faktura.dokumente.service.DokumentenService; +import de.hsmannheim.faktura.dokumente.service.PositionCommand; import de.hsmannheim.faktura.dokumente.repository.InMemoryDokumentRepository; /** @@ -61,9 +67,48 @@ public class DokumentPanel extends JPanel { setLayout(new BorderLayout(8, 8)); setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); initComponents(); + initialisiereDemoDaten(); ladeDokumente(); } + /** + * Legt beim Start einige Beispieldokumente an, damit der gesamte + * Belegfluss (Angebot → AB → Lieferschein → Rechnung) sofort + * demonstriert werden kann. + */ + private void initialisiereDemoDaten() { + try { + // 1) Frisches Angebot (Status OFFEN) – Folgeaktion "→ AB" wird aktiv + dokumentenService.createAngebot(1001L, List.of( + new PositionCommand(1L, 5), + new PositionCommand(2L, 3))); + + // 2) Angebot überführt in AB (Status BESTAETIGT) – Folgeaktion "→ Lieferschein" wird aktiv + Angebot a2 = dokumentenService.createAngebot(1002L, + List.of(new PositionCommand(1L, 10))); + dokumentenService.createAuftragsbestaetigung(a2.getDokumentNummer()); + + // 3) Kette bis Lieferschein (Status GELIEFERT) – Folgeaktion "→ Rechnung" wird aktiv + Angebot a3 = dokumentenService.createAngebot(1003L, + List.of(new PositionCommand(3L, 2))); + Auftragsbestaetigung ab3 = + dokumentenService.createAuftragsbestaetigung(a3.getDokumentNummer()); + dokumentenService.createLieferschein(ab3.getDokumentNummer()); + + // 4) Komplette Kette bis Rechnung (Status FESTGESCHRIEBEN) – nur PDF-Export aktiv + Angebot a4 = dokumentenService.createAngebot(1004L, + List.of(new PositionCommand(1L, 1))); + Auftragsbestaetigung ab4 = + dokumentenService.createAuftragsbestaetigung(a4.getDokumentNummer()); + Lieferschein ls4 = + dokumentenService.createLieferschein(ab4.getDokumentNummer()); + dokumentenService.createRechnung(ls4.getDokumentNummer()); + } catch (Exception ex) { + System.err.println("Demo-Daten konnten nicht initialisiert werden: " + + ex.getMessage()); + } + } + private void initComponents() { // --- Linke Seite: Dokumentenliste --- JPanel leftPanel = new JPanel(new BorderLayout(4, 4)); @@ -192,7 +237,8 @@ public class DokumentPanel extends JPanel { rightPanel.add(aktionenPanel, BorderLayout.SOUTH); // Button-Actions - btnNeu.addActionListener(e -> neuesFormular()); + btnNeu.addActionListener(e -> neuesAngebotDialog()); + btnSpeichern.addActionListener(e -> neuesAngebotDialog()); btnAbbrechen.addActionListener(e -> { neuesFormular(); ladeDokumente(); }); btnPdfExport.addActionListener(e -> pdfExport()); btnLoeschen.addActionListener(e -> loeschen()); @@ -215,9 +261,9 @@ public class DokumentPanel extends JPanel { List alle = dokumentenService.getAllDokumente(); for (Dokument d : alle) { tableModel.addRow(new Object[]{ - d.getDokumentNummer(), // FIX: getDokumentNummer() statt getDokumentnummer() - d.getTypBezeichnung(), - d.getStatus().getAnzeigename() + d.getDokumentNummer(), // FIX: getDokumentNummer() statt getDokumentnummer() + d.getTypBezeichnung(), + d.getStatus().getAnzeigename() }); } } @@ -242,13 +288,13 @@ public class DokumentPanel extends JPanel { int pos = 1; for (var position : d.getPositionen()) { posTableModel.addRow(new Object[]{ - pos++, - position.getBezeichnungSnapshot(), // FIX - position.getMenge(), - position.getEinzelpreisNettoSnapshot() + " €", // FIX - position.getMwstSatzSnapshot().multiply(java.math.BigDecimal.valueOf(100)) - .setScale(0, java.math.RoundingMode.HALF_UP) + "%",// FIX - position.getNettoGesamt() + " €" // FIX + pos++, + position.getBezeichnungSnapshot(), // FIX + position.getMenge(), + position.getEinzelpreisNettoSnapshot() + " €", // FIX + position.getMwstSatzSnapshot().multiply(java.math.BigDecimal.valueOf(100)) + .setScale(0, java.math.RoundingMode.HALF_UP) + "%",// FIX + position.getNettoGesamt() + " €" // FIX }); } @@ -323,8 +369,8 @@ public class DokumentPanel extends JPanel { return; } int confirm = JOptionPane.showConfirmDialog( - this, "Dokument wirklich löschen?", "Löschen bestätigen", - JOptionPane.YES_NO_OPTION + this, "Dokument wirklich löschen?", "Löschen bestätigen", + JOptionPane.YES_NO_OPTION ); if (confirm != JOptionPane.YES_OPTION) return; @@ -342,11 +388,11 @@ public class DokumentPanel extends JPanel { try { switch (typ) { case "ANGEBOT_ZU_AUFTRAG" -> - dokumentenService.createAuftragsbestaetigung(aktuellesDokument.getDokumentNummer()); // FIX + dokumentenService.createAuftragsbestaetigung(aktuellesDokument.getDokumentNummer()); // FIX case "AUFTRAG_ZU_LIEFERSCHEIN" -> - dokumentenService.createLieferschein(aktuellesDokument.getDokumentNummer()); // FIX + dokumentenService.createLieferschein(aktuellesDokument.getDokumentNummer()); // FIX case "LIEFERSCHEIN_ZU_RECHNUNG" -> - dokumentenService.createRechnung(aktuellesDokument.getDokumentNummer()); // FIX + dokumentenService.createRechnung(aktuellesDokument.getDokumentNummer()); // FIX } ladeDokumente(); mainFrame.showStatus("Folgedokument erfolgreich erstellt."); @@ -362,7 +408,7 @@ public class DokumentPanel extends JPanel { } try { dokumentenService.exportPdf( - aktuellesDokument.getDokumentNummer(), aktuellesDokument.getTyp()); // FIX + aktuellesDokument.getDokumentNummer(), aktuellesDokument.getTyp()); // FIX mainFrame.showStatus("PDF-Export erfolgreich."); } catch (Exception ex) { mainFrame.showStatus("PDF-Export fehlgeschlagen: " + ex.getMessage()); @@ -375,4 +421,140 @@ public class DokumentPanel extends JPanel { f.setBackground(new Color(230, 230, 230)); return f; } -} + + // ------------------------------------------------------------------------- + // Dialog: Neues Angebot anlegen + // ------------------------------------------------------------------------- + + /** + * Öffnet einen modalen Dialog zum Anlegen eines neuen Angebots. + * Eingaben: Kunden-ID und mindestens eine Position (Produkt-ID + Menge). + * Bei erfolgreichem Anlegen wird das Angebot gespeichert und die + * Dokumentenliste neu geladen. + */ + private void neuesAngebotDialog() { + JDialog dialog = new JDialog( + (Frame) SwingUtilities.getWindowAncestor(this), + "Neues Angebot anlegen", true); + dialog.setLayout(new BorderLayout(8, 8)); + ((JComponent) dialog.getContentPane()) + .setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + + // --- Kopfbereich: Kunden-ID --- + JPanel headPanel = new JPanel(new GridBagLayout()); + headPanel.setBorder(BorderFactory.createTitledBorder("Kunde")); + GridBagConstraints g = new GridBagConstraints(); + g.insets = new Insets(3, 4, 3, 4); + g.fill = GridBagConstraints.HORIZONTAL; + g.weightx = 1.0; + + g.gridx = 0; g.gridy = 0; headPanel.add(new JLabel("Kunden-ID:"), g); + JTextField txtKundenIdInput = new JTextField("1001"); + g.gridx = 1; headPanel.add(txtKundenIdInput, g); + + // --- Positionsbereich --- + String[] cols = {"Produkt-ID", "Menge"}; + DefaultTableModel posModel = new DefaultTableModel(cols, 0) { + @Override + public Class getColumnClass(int column) { return String.class; } + @Override + public boolean isCellEditable(int r, int c) { return true; } + }; + JTable tblPositionen = new JTable(posModel); + tblPositionen.setRowHeight(22); + // Eine Beispielzeile zum Loslegen + posModel.addRow(new Object[]{"1", "1"}); + + JPanel posButtons = new JPanel(new FlowLayout(FlowLayout.LEFT)); + JButton btnPosHinzu = new JButton("Position hinzufügen"); + JButton btnPosEntfernen = new JButton("Position entfernen"); + posButtons.add(btnPosHinzu); + posButtons.add(btnPosEntfernen); + + btnPosHinzu.addActionListener(ev -> posModel.addRow(new Object[]{"1", "1"})); + btnPosEntfernen.addActionListener(ev -> { + int row = tblPositionen.getSelectedRow(); + if (row >= 0) posModel.removeRow(row); + }); + + JPanel posPanel = new JPanel(new BorderLayout(4, 4)); + posPanel.setBorder(BorderFactory.createTitledBorder("Positionen")); + posPanel.add(new JScrollPane(tblPositionen), BorderLayout.CENTER); + posPanel.add(posButtons, BorderLayout.SOUTH); + + // --- OK / Abbrechen --- + JButton btnOk = new JButton("Anlegen"); + JButton btnCancel = new JButton("Abbrechen"); + JPanel dlgButtons = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + dlgButtons.add(btnOk); + dlgButtons.add(btnCancel); + + JLabel lblFehler = new JLabel(" "); + lblFehler.setForeground(new Color(192, 57, 43)); + + btnCancel.addActionListener(ev -> dialog.dispose()); + btnOk.addActionListener(ev -> { + // Bei in der Tabelle laufender Bearbeitung: Wert übernehmen + if (tblPositionen.isEditing()) tblPositionen.getCellEditor().stopCellEditing(); + + // Kunden-ID parsen + long kundenId; + try { + kundenId = Long.parseLong(txtKundenIdInput.getText().trim()); + } catch (NumberFormatException ex) { + lblFehler.setText("Kunden-ID muss eine Zahl sein."); + return; + } + + // Positionen aufbauen + List positionen = new ArrayList<>(); + for (int r = 0; r < posModel.getRowCount(); r++) { + Object pidObj = posModel.getValueAt(r, 0); + Object mengeObj = posModel.getValueAt(r, 1); + if (pidObj == null || mengeObj == null + || pidObj.toString().isBlank() || mengeObj.toString().isBlank()) { + continue; + } + try { + long pid = Long.parseLong(pidObj.toString().trim()); + int menge = Integer.parseInt(mengeObj.toString().trim()); + positionen.add(new PositionCommand(pid, menge)); + } catch (NumberFormatException ex) { + lblFehler.setText("Position " + (r + 1) + + ": Produkt-ID und Menge müssen Zahlen sein."); + return; + } catch (IllegalArgumentException ex) { + lblFehler.setText("Position " + (r + 1) + ": " + ex.getMessage()); + return; + } + } + if (positionen.isEmpty()) { + lblFehler.setText("Mindestens eine Position erforderlich."); + return; + } + + // Angebot anlegen + try { + Angebot a = dokumentenService.createAngebot(kundenId, positionen); + ladeDokumente(); + mainFrame.showStatus("Angebot " + a.getDokumentNummer() + + " angelegt (Netto " + a.getNettoSumme() + " €)."); + dialog.dispose(); + } catch (Exception ex) { + lblFehler.setText("Fehler: " + ex.getMessage()); + } + }); + + JPanel southPanel = new JPanel(new BorderLayout()); + southPanel.add(lblFehler, BorderLayout.NORTH); + southPanel.add(dlgButtons, BorderLayout.SOUTH); + + dialog.add(headPanel, BorderLayout.NORTH); + dialog.add(posPanel, BorderLayout.CENTER); + dialog.add(southPanel, BorderLayout.SOUTH); + + dialog.setSize(480, 360); + dialog.setLocationRelativeTo(this); + dialog.setVisible(true); + } +} \ No newline at end of file