25.06.2026 09:52 Commit Lastenheft 1.1 Pflichtenheft 1.0 new versions
parent
576220c381
commit
469acd2060
|
|
@ -72,6 +72,16 @@ public class DokumentenService {
|
||||||
return repository.findByNummer(nummer);
|
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<PositionCommand> positionen) {
|
||||||
|
return service.erstelleAngebot(kundenId, positionen);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dokument löschen. Festgeschriebene Rechnungen können nicht gelöscht werden.
|
* Dokument löschen. Festgeschriebene Rechnungen können nicht gelöscht werden.
|
||||||
* @return true wenn erfolgreich gelöscht
|
* @return true wenn erfolgreich gelöscht
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,17 @@ package de.hsmannheim.faktura.gui;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.table.*;
|
import javax.swing.table.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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.Dokument;
|
||||||
import de.hsmannheim.faktura.dokumente.domain.DokumentStatus;
|
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.DokumentenService;
|
||||||
|
import de.hsmannheim.faktura.dokumente.service.PositionCommand;
|
||||||
import de.hsmannheim.faktura.dokumente.repository.InMemoryDokumentRepository;
|
import de.hsmannheim.faktura.dokumente.repository.InMemoryDokumentRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -61,9 +67,48 @@ public class DokumentPanel extends JPanel {
|
||||||
setLayout(new BorderLayout(8, 8));
|
setLayout(new BorderLayout(8, 8));
|
||||||
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
||||||
initComponents();
|
initComponents();
|
||||||
|
initialisiereDemoDaten();
|
||||||
ladeDokumente();
|
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() {
|
private void initComponents() {
|
||||||
// --- Linke Seite: Dokumentenliste ---
|
// --- Linke Seite: Dokumentenliste ---
|
||||||
JPanel leftPanel = new JPanel(new BorderLayout(4, 4));
|
JPanel leftPanel = new JPanel(new BorderLayout(4, 4));
|
||||||
|
|
@ -192,7 +237,8 @@ public class DokumentPanel extends JPanel {
|
||||||
rightPanel.add(aktionenPanel, BorderLayout.SOUTH);
|
rightPanel.add(aktionenPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
// Button-Actions
|
// Button-Actions
|
||||||
btnNeu.addActionListener(e -> neuesFormular());
|
btnNeu.addActionListener(e -> neuesAngebotDialog());
|
||||||
|
btnSpeichern.addActionListener(e -> neuesAngebotDialog());
|
||||||
btnAbbrechen.addActionListener(e -> { neuesFormular(); ladeDokumente(); });
|
btnAbbrechen.addActionListener(e -> { neuesFormular(); ladeDokumente(); });
|
||||||
btnPdfExport.addActionListener(e -> pdfExport());
|
btnPdfExport.addActionListener(e -> pdfExport());
|
||||||
btnLoeschen.addActionListener(e -> loeschen());
|
btnLoeschen.addActionListener(e -> loeschen());
|
||||||
|
|
@ -375,4 +421,140 @@ public class DokumentPanel extends JPanel {
|
||||||
f.setBackground(new Color(230, 230, 230));
|
f.setBackground(new Color(230, 230, 230));
|
||||||
return f;
|
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<PositionCommand> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue