Merge pull request 'Programmoberfläche' (#5) from Programmoberfläche into main
Reviewed-on: #5feature/produktverwaltung
commit
f4940a0068
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,382 @@
|
|||
package de.hsmannheim.faktura.gui;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.*;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
import de.hsmannheim.faktura.dokumente.domain.Dokument;
|
||||
import de.hsmannheim.faktura.dokumente.domain.DokumentStatus;
|
||||
import de.hsmannheim.faktura.dokumente.service.DokumentenService;
|
||||
import de.hsmannheim.faktura.dokumente.repository.DokumentRepository;
|
||||
|
||||
/**
|
||||
* Panel für den Dokumentenprozess (PHGUI10, PHGUI11, PHGUI12).
|
||||
* Zeigt Status und zulässige Folgeaktionen an. Deaktiviert unzulässige Aktionen (PHGUI10).
|
||||
* Festgeschriebene Rechnungen sind schreibgeschützt (PHGUI11).
|
||||
*/
|
||||
public class DokumentPanel extends JPanel {
|
||||
|
||||
private final MainFrame mainFrame;
|
||||
private final DokumentenService dokumentenService;
|
||||
|
||||
// Tabelle
|
||||
private DefaultTableModel tableModel;
|
||||
private JTable dokumentTable;
|
||||
|
||||
// Detailfelder (schreibgeschützt)
|
||||
private JTextField txtDokumentnummer;
|
||||
private JTextField txtKundenId;
|
||||
private JTextField txtDatum;
|
||||
private JTextField txtStatus;
|
||||
private JCheckBox chkFestgeschrieben;
|
||||
|
||||
// Positionstabelle
|
||||
private DefaultTableModel posTableModel;
|
||||
private JTable posTable;
|
||||
|
||||
// Summenfelder
|
||||
private JLabel lblNetto;
|
||||
private JLabel lblUst;
|
||||
private JLabel lblBrutto;
|
||||
|
||||
// Aktions-Buttons (PHGUI10)
|
||||
private JButton btnNeu;
|
||||
private JButton btnSpeichern;
|
||||
private JButton btnLoeschen;
|
||||
private JButton btnAbbrechen;
|
||||
private JButton btnPdfExport;
|
||||
|
||||
// Folgeaktions-Buttons
|
||||
private JButton btnAngebotZuAuftrag;
|
||||
private JButton btnAuftragZuLieferschein;
|
||||
private JButton btnLieferscheinZuRechnung;
|
||||
|
||||
private Dokument aktuellesDokument = null;
|
||||
|
||||
public DokumentPanel(MainFrame mainFrame) {
|
||||
this.mainFrame = mainFrame;
|
||||
this.dokumentenService = new DokumentenService(new DokumentRepository());
|
||||
setLayout(new BorderLayout(8, 8));
|
||||
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
||||
initComponents();
|
||||
ladeDokumente();
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
// --- Linke Seite: Dokumentenliste ---
|
||||
JPanel leftPanel = new JPanel(new BorderLayout(4, 4));
|
||||
leftPanel.setPreferredSize(new Dimension(300, 0));
|
||||
|
||||
String[] cols = {"Nr.", "Typ ▲", "Status"};
|
||||
tableModel = new DefaultTableModel(cols, 0) {
|
||||
public boolean isCellEditable(int r, int c) { return false; }
|
||||
};
|
||||
dokumentTable = new JTable(tableModel) {
|
||||
// Status-Spalte farbig (PHGUI10)
|
||||
public Component prepareRenderer(TableCellRenderer r, int row, int col) {
|
||||
Component c = super.prepareRenderer(r, row, col);
|
||||
if (col == 2) {
|
||||
String status = (String) getValueAt(row, col);
|
||||
if (status != null) {
|
||||
if (status.contains("Offen")) c.setForeground(new Color(41, 128, 185));
|
||||
else if (status.contains("Erstellt")) c.setForeground(new Color(39, 174, 96));
|
||||
else if (status.contains("Festgeschr")) c.setForeground(new Color(192, 57, 43));
|
||||
else c.setForeground(Color.BLACK);
|
||||
}
|
||||
} else {
|
||||
c.setForeground(Color.BLACK);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
};
|
||||
dokumentTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
// Sortierung (PHGUI08)
|
||||
dokumentTable.setAutoCreateRowSorter(true);
|
||||
dokumentTable.getSelectionModel().addListSelectionListener(e -> {
|
||||
if (!e.getValueIsAdjusting()) zeigeAusgewaehltes();
|
||||
});
|
||||
|
||||
leftPanel.add(new JLabel("Dokumente"), BorderLayout.NORTH);
|
||||
leftPanel.add(new JScrollPane(dokumentTable), BorderLayout.CENTER);
|
||||
|
||||
// --- Rechte Seite: Detailansicht ---
|
||||
JPanel rightPanel = new JPanel(new BorderLayout(4, 8));
|
||||
|
||||
// Kopfbereich
|
||||
JPanel headerPanel = new JPanel(new GridBagLayout());
|
||||
headerPanel.setBorder(BorderFactory.createTitledBorder("Dokumentdetail"));
|
||||
GridBagConstraints g = new GridBagConstraints();
|
||||
g.insets = new Insets(3, 4, 3, 4);
|
||||
g.fill = GridBagConstraints.HORIZONTAL;
|
||||
g.weightx = 0.5;
|
||||
|
||||
// Festgeschrieben-Checkbox (PHGUI11) oben rechts
|
||||
chkFestgeschrieben = new JCheckBox("Festgeschrieben");
|
||||
chkFestgeschrieben.setEnabled(false);
|
||||
g.gridx = 1; g.gridy = 0; g.anchor = GridBagConstraints.EAST;
|
||||
headerPanel.add(chkFestgeschrieben, g);
|
||||
|
||||
g.anchor = GridBagConstraints.WEST;
|
||||
g.gridx = 0; g.gridy = 1; headerPanel.add(new JLabel("Dokumentnummer"), g);
|
||||
g.gridx = 1; headerPanel.add(new JLabel("Kundennummer"), g);
|
||||
txtDokumentnummer = createReadOnlyField();
|
||||
txtKundenId = createReadOnlyField();
|
||||
g.gridx = 0; g.gridy = 2; headerPanel.add(txtDokumentnummer, g);
|
||||
g.gridx = 1; headerPanel.add(txtKundenId, g);
|
||||
|
||||
g.gridx = 0; g.gridy = 3; headerPanel.add(new JLabel("Datum"), g);
|
||||
g.gridx = 1; headerPanel.add(new JLabel("Status"), g);
|
||||
txtDatum = createReadOnlyField();
|
||||
txtStatus = createReadOnlyField();
|
||||
g.gridx = 0; g.gridy = 4; headerPanel.add(txtDatum, g);
|
||||
g.gridx = 1; headerPanel.add(txtStatus, g);
|
||||
|
||||
rightPanel.add(headerPanel, BorderLayout.NORTH);
|
||||
|
||||
// Positionstabelle
|
||||
String[] posCols = {"Pos", "Bezeichnung", "Menge", "Einzelpreis", "MwSt.", "Summe"};
|
||||
posTableModel = new DefaultTableModel(posCols, 0) {
|
||||
public boolean isCellEditable(int r, int c) { return false; }
|
||||
};
|
||||
posTable = new JTable(posTableModel);
|
||||
posTable.setEnabled(false);
|
||||
|
||||
JPanel posPanel = new JPanel(new BorderLayout());
|
||||
posPanel.setBorder(BorderFactory.createTitledBorder("Positionen:"));
|
||||
posPanel.add(new JScrollPane(posTable), BorderLayout.CENTER);
|
||||
|
||||
// Summen
|
||||
JPanel summenPanel = new JPanel(new GridLayout(3, 2, 4, 2));
|
||||
summenPanel.setBorder(BorderFactory.createEmptyBorder(4, 8, 4, 8));
|
||||
lblNetto = new JLabel("0,00 €"); lblNetto.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
lblUst = new JLabel("0,00 €"); lblUst.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
lblBrutto = new JLabel("0,00 €", SwingConstants.RIGHT);
|
||||
lblBrutto.setFont(new Font("SansSerif", Font.BOLD, 13));
|
||||
summenPanel.add(new JLabel("Netto:")); summenPanel.add(lblNetto);
|
||||
summenPanel.add(new JLabel("USt. 19%:")); summenPanel.add(lblUst);
|
||||
summenPanel.add(new JLabel("Brutto:")); summenPanel.add(lblBrutto);
|
||||
|
||||
JPanel mittelPanel = new JPanel(new BorderLayout());
|
||||
mittelPanel.add(posPanel, BorderLayout.CENTER);
|
||||
mittelPanel.add(summenPanel, BorderLayout.SOUTH);
|
||||
rightPanel.add(mittelPanel, BorderLayout.CENTER);
|
||||
|
||||
// --- Aktionen (PHGUI10: nur zulässige aktiv) ---
|
||||
JPanel aktionenPanel = new JPanel(new BorderLayout());
|
||||
aktionenPanel.setBorder(BorderFactory.createTitledBorder("Aktionen:"));
|
||||
|
||||
JPanel stdButtons = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
btnNeu = new JButton("Neu");
|
||||
btnSpeichern = new JButton("Speichern");
|
||||
btnLoeschen = new JButton("Löschen");
|
||||
btnAbbrechen = new JButton("Abbrechen");
|
||||
btnPdfExport = new JButton("PDF-Export");
|
||||
btnPdfExport.setBackground(new Color(41, 128, 185));
|
||||
btnPdfExport.setForeground(Color.WHITE);
|
||||
stdButtons.add(btnNeu);
|
||||
stdButtons.add(btnSpeichern);
|
||||
stdButtons.add(btnLoeschen);
|
||||
stdButtons.add(btnAbbrechen);
|
||||
stdButtons.add(btnPdfExport);
|
||||
|
||||
// Folgeaktionen
|
||||
JPanel folgeButtons = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
btnAngebotZuAuftrag = new JButton("→ Auftragsbestätigung erstellen");
|
||||
btnAuftragZuLieferschein = new JButton("→ Lieferschein erstellen");
|
||||
btnLieferscheinZuRechnung = new JButton("→ Rechnung erstellen");
|
||||
folgeButtons.add(btnAngebotZuAuftrag);
|
||||
folgeButtons.add(btnAuftragZuLieferschein);
|
||||
folgeButtons.add(btnLieferscheinZuRechnung);
|
||||
|
||||
aktionenPanel.add(stdButtons, BorderLayout.NORTH);
|
||||
aktionenPanel.add(folgeButtons, BorderLayout.SOUTH);
|
||||
rightPanel.add(aktionenPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Button-Actions
|
||||
btnNeu.addActionListener(e -> neuesFormular());
|
||||
btnAbbrechen.addActionListener(e -> { neuesFormular(); ladeDokumente(); });
|
||||
btnPdfExport.addActionListener(e -> pdfExport()); // PHGUI12
|
||||
btnLoeschen.addActionListener(e -> loeschen());
|
||||
|
||||
btnAngebotZuAuftrag.addActionListener(e -> folgeaktion("ANGEBOT_ZU_AUFTRAG"));
|
||||
btnAuftragZuLieferschein.addActionListener(e -> folgeaktion("AUFTRAG_ZU_LIEFERSCHEIN"));
|
||||
btnLieferscheinZuRechnung.addActionListener(e -> folgeaktion("LIEFERSCHEIN_ZU_RECHNUNG"));
|
||||
|
||||
// Initial alle deaktivieren (PHGUI10)
|
||||
setAktionenDeaktiviert();
|
||||
|
||||
JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel);
|
||||
split.setDividerLocation(300);
|
||||
add(split, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
// --- Daten ---
|
||||
private void ladeDokumente() {
|
||||
tableModel.setRowCount(0);
|
||||
List<Dokument> alle = dokumentenService.getAllDokumente();
|
||||
for (Dokument d : alle) {
|
||||
tableModel.addRow(new Object[]{
|
||||
d.getDokumentnummer(),
|
||||
d.getTypBezeichnung(),
|
||||
d.getStatus().getAnzeigename()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void zeigeAusgewaehltes() {
|
||||
int row = dokumentTable.getSelectedRow();
|
||||
if (row < 0) return;
|
||||
int modelRow = dokumentTable.convertRowIndexToModel(row);
|
||||
String nr = (String) tableModel.getValueAt(modelRow, 0);
|
||||
|
||||
dokumentenService.getDokumentByNummer(nr).ifPresent(d -> {
|
||||
aktuellesDokument = d;
|
||||
txtDokumentnummer.setText(d.getDokumentnummer());
|
||||
txtKundenId.setText(String.valueOf(d.getKundeId()));
|
||||
txtDatum.setText(d.getDatum().toString());
|
||||
txtStatus.setText(d.getStatus().getAnzeigename());
|
||||
|
||||
boolean festgeschrieben = d.isFestgeschrieben();
|
||||
chkFestgeschrieben.setSelected(festgeschrieben);
|
||||
|
||||
// Positionen laden
|
||||
posTableModel.setRowCount(0);
|
||||
int pos = 1;
|
||||
for (var position : d.getPositionen()) {
|
||||
posTableModel.addRow(new Object[]{
|
||||
pos++,
|
||||
position.getProduktbezeichnung(),
|
||||
position.getMenge(),
|
||||
position.getEinzelpreisNetto() + " €",
|
||||
position.getMehrwertsteuerSatz() + "%",
|
||||
position.getSumme() + " €"
|
||||
});
|
||||
}
|
||||
|
||||
// Summen
|
||||
if (d.getNettosumme() != null) {
|
||||
lblNetto.setText(d.getNettosumme().toPlainString() + " €");
|
||||
lblUst.setText(d.getUstSumme().toPlainString() + " €");
|
||||
lblBrutto.setText(d.getBruttosumme().toPlainString() + " €");
|
||||
}
|
||||
|
||||
// PHGUI10: Zulässige Folgeaktionen setzen
|
||||
aktualisiereAktionen(d);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktiviert nur die zulässigen Folgeaktionen basierend auf dem Dokumentstatus (PHGUI10).
|
||||
* Festgeschriebene Rechnungen: alle Bearbeitungsaktionen deaktiviert (PHGUI11).
|
||||
*/
|
||||
private void aktualisiereAktionen(Dokument d) {
|
||||
DokumentStatus status = d.getStatus();
|
||||
boolean festgeschrieben = d.isFestgeschrieben();
|
||||
|
||||
// PHGUI11: Festgeschriebene Rechnung → alles deaktiviert außer PDF-Export
|
||||
if (festgeschrieben) {
|
||||
btnNeu.setEnabled(true);
|
||||
btnSpeichern.setEnabled(false);
|
||||
btnLoeschen.setEnabled(false);
|
||||
btnAbbrechen.setEnabled(true);
|
||||
btnPdfExport.setEnabled(true); // PHGUI12
|
||||
btnAngebotZuAuftrag.setEnabled(false);
|
||||
btnAuftragZuLieferschein.setEnabled(false);
|
||||
btnLieferscheinZuRechnung.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// PHGUI10: Folgeaktionen je nach Status
|
||||
btnNeu.setEnabled(true);
|
||||
btnSpeichern.setEnabled(true);
|
||||
btnLoeschen.setEnabled(true);
|
||||
btnAbbrechen.setEnabled(true);
|
||||
btnPdfExport.setEnabled(status == DokumentStatus.RECHNUNG_ERSTELLT);
|
||||
|
||||
btnAngebotZuAuftrag.setEnabled(status == DokumentStatus.ANGEBOT_OFFEN);
|
||||
btnAuftragZuLieferschein.setEnabled(status == DokumentStatus.AUFTRAGSBESTAETIGUNG_ERSTELLT);
|
||||
btnLieferscheinZuRechnung.setEnabled(status == DokumentStatus.LIEFERSCHEIN_ERSTELLT);
|
||||
}
|
||||
|
||||
private void setAktionenDeaktiviert() {
|
||||
btnSpeichern.setEnabled(false);
|
||||
btnLoeschen.setEnabled(false);
|
||||
btnPdfExport.setEnabled(false);
|
||||
btnAngebotZuAuftrag.setEnabled(false);
|
||||
btnAuftragZuLieferschein.setEnabled(false);
|
||||
btnLieferscheinZuRechnung.setEnabled(false);
|
||||
}
|
||||
|
||||
private void neuesFormular() {
|
||||
aktuellesDokument = null;
|
||||
txtDokumentnummer.setText(""); txtKundenId.setText("");
|
||||
txtDatum.setText(""); txtStatus.setText("");
|
||||
chkFestgeschrieben.setSelected(false);
|
||||
posTableModel.setRowCount(0);
|
||||
lblNetto.setText("0,00 €"); lblUst.setText("0,00 €"); lblBrutto.setText("0,00 €");
|
||||
setAktionenDeaktiviert();
|
||||
btnNeu.setEnabled(true); btnAbbrechen.setEnabled(true);
|
||||
dokumentTable.clearSelection();
|
||||
}
|
||||
|
||||
private void loeschen() {
|
||||
if (aktuellesDokument == null) {
|
||||
mainFrame.showStatus("Kein Dokument ausgewählt.");
|
||||
return;
|
||||
}
|
||||
// PHGUI07: Bestätigungsdialog
|
||||
int confirm = JOptionPane.showConfirmDialog(
|
||||
this, "Dokument wirklich löschen?", "Löschen bestätigen",
|
||||
JOptionPane.YES_NO_OPTION
|
||||
);
|
||||
if (confirm != JOptionPane.YES_OPTION) return;
|
||||
|
||||
boolean deleted = dokumentenService.deleteDokument(aktuellesDokument.getDokumentnummer());
|
||||
if (deleted) {
|
||||
ladeDokumente(); neuesFormular();
|
||||
mainFrame.showStatus("Dokument gelöscht.");
|
||||
} else {
|
||||
mainFrame.showStatus("Dokument kann nicht gelöscht werden.");
|
||||
}
|
||||
}
|
||||
|
||||
private void folgeaktion(String typ) {
|
||||
if (aktuellesDokument == null) return;
|
||||
try {
|
||||
switch (typ) {
|
||||
case "ANGEBOT_ZU_AUFTRAG" ->
|
||||
dokumentenService.createAuftragsbestaetigung(aktuellesDokument.getDokumentnummer());
|
||||
case "AUFTRAG_ZU_LIEFERSCHEIN" ->
|
||||
dokumentenService.createLieferschein(aktuellesDokument.getDokumentnummer());
|
||||
case "LIEFERSCHEIN_ZU_RECHNUNG" ->
|
||||
dokumentenService.createRechnung(aktuellesDokument.getDokumentnummer());
|
||||
}
|
||||
ladeDokumente();
|
||||
mainFrame.showStatus("Folgedokument erfolgreich erstellt.");
|
||||
} catch (Exception ex) {
|
||||
mainFrame.showStatus("Fehler: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void pdfExport() {
|
||||
// PHGUI12: PDF-Export auslösen
|
||||
if (aktuellesDokument == null) {
|
||||
mainFrame.showStatus("Kein Dokument ausgewählt.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
dokumentenService.exportPdf(aktuellesDokument.getDokumentnummer(), aktuellesDokument.getTyp());
|
||||
mainFrame.showStatus("PDF-Export erfolgreich gestartet.");
|
||||
} catch (Exception ex) {
|
||||
mainFrame.showStatus("PDF-Export fehlgeschlagen: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private JTextField createReadOnlyField() {
|
||||
JTextField f = new JTextField();
|
||||
f.setEditable(false);
|
||||
f.setBackground(new Color(230, 230, 230));
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,351 @@
|
|||
package de.hsmannheim.faktura.gui;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.*;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
import de.hsmannheim.faktura.kunde.Kunde;
|
||||
import de.hsmannheim.faktura.kunde.service.KundenService;
|
||||
import de.hsmannheim.faktura.kunde.repository.KundenRepository;
|
||||
import de.hsmannheim.faktura.dokumente.repository.DokumentRepository;
|
||||
|
||||
/**
|
||||
* Panel für die Kundenverwaltung (PHGUI02, PHGUI03, PHGUI05–PHGUI09).
|
||||
* Einheitliches Layout mit Navigationsbereich, Inhaltsbereich und Statusbereich (PHGUI02).
|
||||
*/
|
||||
public class KundenPanel extends JPanel {
|
||||
|
||||
private final MainFrame mainFrame;
|
||||
private final KundenService kundenService;
|
||||
|
||||
// Tabelle
|
||||
private DefaultTableModel tableModel;
|
||||
private JTable kundenTable;
|
||||
|
||||
// Formularfelder
|
||||
private JTextField txtKundenId;
|
||||
private JTextField txtFirmenname;
|
||||
private JTextField txtStrasse;
|
||||
private JTextField txtPlz;
|
||||
private JTextField txtOrt;
|
||||
private JTextField txtTelefon;
|
||||
private JTextField txtEmail;
|
||||
private JTextField txtUstIdNr;
|
||||
|
||||
// Fehlerhinweise (PHGUI03, PHGUI05)
|
||||
private JLabel lblFirmenfehler;
|
||||
private JLabel lblStrasseFehler;
|
||||
private JLabel lblPlzFehler;
|
||||
private JLabel lblOrtFehler;
|
||||
private JLabel lblEmailFehler;
|
||||
|
||||
// Suche (PHGUI09)
|
||||
private JTextField txtSuche;
|
||||
|
||||
// Buttons (PHGUI02)
|
||||
private JButton btnNeu;
|
||||
private JButton btnSpeichern;
|
||||
private JButton btnLoeschen;
|
||||
private JButton btnAbbrechen;
|
||||
|
||||
public KundenPanel(MainFrame mainFrame) {
|
||||
this.mainFrame = mainFrame;
|
||||
this.kundenService = new KundenService(
|
||||
new KundenRepository(),
|
||||
createDummyDokumentRepository()
|
||||
);
|
||||
setLayout(new BorderLayout(8, 8));
|
||||
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
||||
initComponents();
|
||||
ladeKunden();
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
// --- Linke Seite: Liste + Suche ---
|
||||
JPanel leftPanel = new JPanel(new BorderLayout(4, 4));
|
||||
leftPanel.setPreferredSize(new Dimension(320, 0));
|
||||
|
||||
String[] columns = {"ID", "Name ▲", "Ort"};
|
||||
tableModel = new DefaultTableModel(columns, 0) {
|
||||
public boolean isCellEditable(int r, int c) { return false; }
|
||||
};
|
||||
kundenTable = new JTable(tableModel);
|
||||
kundenTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
// Sortierung (PHGUI08)
|
||||
kundenTable.setAutoCreateRowSorter(true);
|
||||
|
||||
kundenTable.getSelectionModel().addListSelectionListener(e -> {
|
||||
if (!e.getValueIsAdjusting()) zeigeAusgewaehlten();
|
||||
});
|
||||
|
||||
leftPanel.add(new JLabel("Kundenliste"), BorderLayout.NORTH);
|
||||
leftPanel.add(new JScrollPane(kundenTable), BorderLayout.CENTER);
|
||||
|
||||
// Suche (PHGUI09: nach Name oder Kunden-ID)
|
||||
JPanel suchePanel = new JPanel(new BorderLayout(4, 0));
|
||||
suchePanel.setBorder(BorderFactory.createTitledBorder("↑ Suche (Name / Kunden-ID)"));
|
||||
txtSuche = new JTextField();
|
||||
txtSuche.setToolTipText("Name oder ID eingeben...");
|
||||
txtSuche.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
|
||||
public void insertUpdate(javax.swing.event.DocumentEvent e) { sucheAktualisieren(); }
|
||||
public void removeUpdate(javax.swing.event.DocumentEvent e) { sucheAktualisieren(); }
|
||||
public void changedUpdate(javax.swing.event.DocumentEvent e) { sucheAktualisieren(); }
|
||||
});
|
||||
suchePanel.add(txtSuche, BorderLayout.CENTER);
|
||||
leftPanel.add(suchePanel, BorderLayout.SOUTH);
|
||||
|
||||
// --- Rechte Seite: Formular ---
|
||||
JPanel rightPanel = new JPanel(new BorderLayout(4, 8));
|
||||
rightPanel.setBorder(BorderFactory.createTitledBorder("Kundendetails Pflichtfelder sind mit * markiert"));
|
||||
|
||||
JPanel formPanel = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(3, 4, 1, 4);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.weightx = 0.5;
|
||||
|
||||
// Zeile 0: KundenId + Telefon
|
||||
addLabel(formPanel, gbc, "Kunden-ID (autom.)", 0, 0);
|
||||
addLabel(formPanel, gbc, "Telefonnummer", 1, 0);
|
||||
txtKundenId = new JTextField(); txtKundenId.setEditable(false);
|
||||
txtKundenId.setBackground(new Color(230, 230, 230));
|
||||
addField(formPanel, gbc, txtKundenId, 0, 1);
|
||||
txtTelefon = new JTextField();
|
||||
addField(formPanel, gbc, txtTelefon, 1, 1);
|
||||
|
||||
// Zeile 2: Firmenname + E-Mail
|
||||
addLabel(formPanel, gbc, "Firmenname / Nachname *", 0, 2);
|
||||
addLabel(formPanel, gbc, "E-Mail", 1, 2);
|
||||
txtFirmenname = new JTextField();
|
||||
addField(formPanel, gbc, txtFirmenname, 0, 3);
|
||||
txtEmail = new JTextField();
|
||||
addField(formPanel, gbc, txtEmail, 1, 3);
|
||||
|
||||
// Fehler Firmenname + E-Mail
|
||||
lblFirmenfehler = createFehlerLabel();
|
||||
addField(formPanel, gbc, lblFirmenfehler, 0, 4);
|
||||
lblEmailFehler = createFehlerLabel();
|
||||
lblEmailFehler.setText("▲ Format: name@domain.tld");
|
||||
lblEmailFehler.setForeground(new Color(120, 120, 120));
|
||||
addField(formPanel, gbc, lblEmailFehler, 1, 4);
|
||||
|
||||
// Zeile 5: Straße + UStIdNr
|
||||
addLabel(formPanel, gbc, "Straße *", 0, 5);
|
||||
addLabel(formPanel, gbc, "UStIdNr.", 1, 5);
|
||||
txtStrasse = new JTextField();
|
||||
addField(formPanel, gbc, txtStrasse, 0, 6);
|
||||
txtUstIdNr = new JTextField();
|
||||
addField(formPanel, gbc, txtUstIdNr, 1, 6);
|
||||
lblStrasseFehler = createFehlerLabel();
|
||||
addField(formPanel, gbc, lblStrasseFehler, 0, 7);
|
||||
|
||||
// Zeile 8: PLZ
|
||||
addLabel(formPanel, gbc, "PLZ *", 0, 8);
|
||||
txtPlz = new JTextField();
|
||||
addField(formPanel, gbc, txtPlz, 0, 9);
|
||||
lblPlzFehler = createFehlerLabel();
|
||||
addField(formPanel, gbc, lblPlzFehler, 0, 10);
|
||||
|
||||
// Zeile 11: Ort
|
||||
addLabel(formPanel, gbc, "Ort *", 0, 11);
|
||||
txtOrt = new JTextField();
|
||||
addField(formPanel, gbc, txtOrt, 0, 12);
|
||||
lblOrtFehler = createFehlerLabel();
|
||||
addField(formPanel, gbc, lblOrtFehler, 0, 13);
|
||||
|
||||
rightPanel.add(new JScrollPane(formPanel), BorderLayout.CENTER);
|
||||
|
||||
// --- Buttons (PHGUI02) ---
|
||||
JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
btnNeu = new JButton("Neu");
|
||||
btnSpeichern = new JButton("Speichern");
|
||||
btnLoeschen = new JButton("Löschen");
|
||||
btnAbbrechen = new JButton("Abbrechen");
|
||||
btnPanel.add(btnNeu);
|
||||
btnPanel.add(btnSpeichern);
|
||||
btnPanel.add(btnLoeschen);
|
||||
btnPanel.add(btnAbbrechen);
|
||||
rightPanel.add(btnPanel, BorderLayout.SOUTH);
|
||||
|
||||
btnNeu.addActionListener(e -> neuesFormular());
|
||||
btnSpeichern.addActionListener(e -> speichern());
|
||||
btnLoeschen.addActionListener(e -> loeschen());
|
||||
btnAbbrechen.addActionListener(e -> { neuesFormular(); ladeKunden(); });
|
||||
|
||||
JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel);
|
||||
split.setDividerLocation(320);
|
||||
add(split, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
// --- Hilfsmethoden Layout ---
|
||||
private void addLabel(JPanel p, GridBagConstraints g, String text, int col, int row) {
|
||||
g.gridx = col; g.gridy = row; g.weighty = 0;
|
||||
p.add(new JLabel(text), g);
|
||||
}
|
||||
private void addField(JPanel p, GridBagConstraints g, JComponent c, int col, int row) {
|
||||
g.gridx = col; g.gridy = row; g.weighty = 0;
|
||||
p.add(c, g);
|
||||
}
|
||||
|
||||
// --- Daten ---
|
||||
private void ladeKunden() {
|
||||
ladeKundenListe(kundenService.getAllKunden());
|
||||
}
|
||||
|
||||
private void ladeKundenListe(List<Kunde> liste) {
|
||||
tableModel.setRowCount(0);
|
||||
for (Kunde k : liste) {
|
||||
tableModel.addRow(new Object[]{
|
||||
String.valueOf(k.getKundenId()),
|
||||
k.getFirmennameNachname(),
|
||||
k.getOrt()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void sucheAktualisieren() {
|
||||
// PHGUI09: Groß-/Kleinschreibung ignorieren, nach Name oder ID suchen
|
||||
String query = txtSuche.getText();
|
||||
ladeKundenListe(kundenService.searchKunden(query));
|
||||
}
|
||||
|
||||
private void zeigeAusgewaehlten() {
|
||||
int row = kundenTable.getSelectedRow();
|
||||
if (row < 0) return;
|
||||
int modelRow = kundenTable.convertRowIndexToModel(row);
|
||||
String idStr = (String) tableModel.getValueAt(modelRow, 0);
|
||||
kundenService.getKundeById(Integer.parseInt(idStr)).ifPresent(k -> {
|
||||
txtKundenId.setText(String.valueOf(k.getKundenId()));
|
||||
txtFirmenname.setText(k.getFirmennameNachname());
|
||||
txtStrasse.setText(k.getStrasse());
|
||||
txtPlz.setText(k.getPlz());
|
||||
txtOrt.setText(k.getOrt());
|
||||
txtTelefon.setText(k.getTelefonnummer());
|
||||
txtEmail.setText(k.getEmail());
|
||||
txtUstIdNr.setText(k.getUstIdNr());
|
||||
clearFehler();
|
||||
});
|
||||
}
|
||||
|
||||
// --- Aktionen ---
|
||||
private void neuesFormular() {
|
||||
txtKundenId.setText(""); txtFirmenname.setText(""); txtStrasse.setText("");
|
||||
txtPlz.setText(""); txtOrt.setText(""); txtTelefon.setText("");
|
||||
txtEmail.setText(""); txtUstIdNr.setText("");
|
||||
clearFehler();
|
||||
kundenTable.clearSelection();
|
||||
}
|
||||
|
||||
private void speichern() {
|
||||
clearFehler();
|
||||
boolean valid = true;
|
||||
|
||||
// PHGUI03: Pflichtfelder
|
||||
if (txtFirmenname.getText().trim().isEmpty()) {
|
||||
lblFirmenfehler.setText("⚠ Pflichtfeld darf nicht leer sein");
|
||||
valid = false;
|
||||
}
|
||||
if (txtStrasse.getText().trim().isEmpty()) {
|
||||
lblStrasseFehler.setText("⚠ Pflichtfeld darf nicht leer sein");
|
||||
valid = false;
|
||||
}
|
||||
if (txtPlz.getText().trim().isEmpty()) {
|
||||
lblPlzFehler.setText("⚠ Pflichtfeld darf nicht leer sein");
|
||||
valid = false;
|
||||
}
|
||||
if (txtOrt.getText().trim().isEmpty()) {
|
||||
lblOrtFehler.setText("⚠ Pflichtfeld darf nicht leer sein");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
// PHGUI05: E-Mail-Validierung (Format: name@domain.tld)
|
||||
String email = txtEmail.getText().trim();
|
||||
if (!email.isEmpty() && !email.matches("^[^@]+@[^@]+\\.[^@]+$")) {
|
||||
lblEmailFehler.setText("⚠ Format: name@domain.tld");
|
||||
lblEmailFehler.setForeground(new Color(192, 57, 43));
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (!valid) return;
|
||||
|
||||
int kundenId = txtKundenId.getText().trim().isEmpty() ? 0
|
||||
: Integer.parseInt(txtKundenId.getText().trim());
|
||||
|
||||
Kunde k = new Kunde(
|
||||
kundenId,
|
||||
txtFirmenname.getText().trim(),
|
||||
txtStrasse.getText().trim(),
|
||||
txtPlz.getText().trim(),
|
||||
txtOrt.getText().trim(),
|
||||
txtTelefon.getText().trim(),
|
||||
email,
|
||||
txtUstIdNr.getText().trim()
|
||||
);
|
||||
|
||||
try {
|
||||
kundenService.saveKunde(k);
|
||||
ladeKunden();
|
||||
neuesFormular();
|
||||
// PHGUI06: Bestätigung < 1 Sekunde
|
||||
mainFrame.showStatus("Kunde erfolgreich gespeichert.");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
mainFrame.showStatus("Fehler: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void loeschen() {
|
||||
int row = kundenTable.getSelectedRow();
|
||||
if (row < 0) {
|
||||
mainFrame.showStatus("Kein Kunde ausgewählt.");
|
||||
return;
|
||||
}
|
||||
|
||||
// PHGUI07: Bestätigungsdialog
|
||||
int confirm = JOptionPane.showConfirmDialog(
|
||||
this, "Kunden wirklich löschen?", "Löschen bestätigen",
|
||||
JOptionPane.YES_NO_OPTION
|
||||
);
|
||||
if (confirm != JOptionPane.YES_OPTION) return;
|
||||
|
||||
int modelRow = kundenTable.convertRowIndexToModel(row);
|
||||
String idStr = (String) tableModel.getValueAt(modelRow, 0);
|
||||
boolean deleted = kundenService.deleteKunde(Integer.parseInt(idStr));
|
||||
|
||||
if (deleted) {
|
||||
ladeKunden();
|
||||
neuesFormular();
|
||||
mainFrame.showStatus("Kunde gelöscht.");
|
||||
} else {
|
||||
mainFrame.showStatus("Löschen nicht möglich – Kunde wird in Dokumenten verwendet.");
|
||||
}
|
||||
}
|
||||
|
||||
private void clearFehler() {
|
||||
lblFirmenfehler.setText("");
|
||||
lblStrasseFehler.setText("");
|
||||
lblPlzFehler.setText("");
|
||||
lblOrtFehler.setText("");
|
||||
lblEmailFehler.setText("▲ Format: name@domain.tld");
|
||||
lblEmailFehler.setForeground(new Color(120, 120, 120));
|
||||
}
|
||||
|
||||
private JLabel createFehlerLabel() {
|
||||
JLabel lbl = new JLabel("");
|
||||
lbl.setForeground(new Color(192, 57, 43));
|
||||
lbl.setFont(new Font("SansSerif", Font.PLAIN, 11));
|
||||
return lbl;
|
||||
}
|
||||
|
||||
private DokumentRepository createDummyDokumentRepository() {
|
||||
return new DokumentRepository() {
|
||||
public boolean existsReferencedProdukt(long id) { return false; }
|
||||
public boolean existsReferencedKunde(long id) { return false; }
|
||||
public int findMaxLaufendeNummer(String p, int j) { return 0; }
|
||||
public void save(de.hsmannheim.faktura.dokumente.domain.Dokument d) {}
|
||||
public java.util.Optional<de.hsmannheim.faktura.dokumente.domain.Dokument> findByNummer(String n) { return java.util.Optional.empty(); }
|
||||
public List<de.hsmannheim.faktura.dokumente.domain.Dokument> findByKunde(long id) { return List.of(); }
|
||||
public boolean existsFolgedokument(String q) { return false; }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
package de.hsmannheim.faktura.gui;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Hauptfenster der Anwendung (PHGUI01, PHGUI02).
|
||||
* Stellt die dauerhaft sichtbare Hauptnavigation bereit und lädt die Modulpanels.
|
||||
*/
|
||||
public class MainFrame extends JFrame {
|
||||
|
||||
private final ProduktPanel produktPanel;
|
||||
private final KundenPanel kundenPanel;
|
||||
private final DokumentPanel dokumentPanel;
|
||||
private final JLabel statusLabel;
|
||||
|
||||
public MainFrame() {
|
||||
setTitle("Fakturierungssystem");
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setSize(1100, 700);
|
||||
setLocationRelativeTo(null);
|
||||
|
||||
// Hauptlayout
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
// --- Navigation (PHGUI01) ---
|
||||
JPanel navPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
||||
navPanel.setBackground(new Color(52, 73, 94));
|
||||
|
||||
JLabel title = new JLabel(" Fakturierungssystem ");
|
||||
title.setForeground(Color.WHITE);
|
||||
title.setFont(new Font("SansSerif", Font.BOLD, 14));
|
||||
navPanel.add(title);
|
||||
|
||||
JButton btnProdukt = createNavButton("Produktverwaltung");
|
||||
JButton btnKunde = createNavButton("Kundenverwaltung");
|
||||
JButton btnDokument = createNavButton("Dokumentenprozess");
|
||||
navPanel.add(btnProdukt);
|
||||
navPanel.add(btnKunde);
|
||||
navPanel.add(btnDokument);
|
||||
add(navPanel, BorderLayout.NORTH);
|
||||
|
||||
// --- Inhaltsbereich ---
|
||||
JPanel contentArea = new JPanel(new CardLayout());
|
||||
produktPanel = new ProduktPanel(this);
|
||||
kundenPanel = new KundenPanel(this);
|
||||
dokumentPanel = new DokumentPanel(this);
|
||||
|
||||
contentArea.add(produktPanel, "produkt");
|
||||
contentArea.add(kundenPanel, "kunde");
|
||||
contentArea.add(dokumentPanel, "dokument");
|
||||
add(contentArea, BorderLayout.CENTER);
|
||||
|
||||
// --- Statusbereich (PHGUI06) ---
|
||||
statusLabel = new JLabel("Status: Bereit");
|
||||
statusLabel.setBorder(BorderFactory.createEmptyBorder(4, 8, 4, 8));
|
||||
statusLabel.setFont(new Font("SansSerif", Font.PLAIN, 12));
|
||||
add(statusLabel, BorderLayout.SOUTH);
|
||||
|
||||
// Navigation-Actions (PHGUI01: 1 Klick)
|
||||
CardLayout cl = (CardLayout) contentArea.getLayout();
|
||||
btnProdukt.addActionListener(e -> cl.show(contentArea, "produkt"));
|
||||
btnKunde.addActionListener(e -> cl.show(contentArea, "kunde"));
|
||||
btnDokument.addActionListener(e -> cl.show(contentArea, "dokument"));
|
||||
|
||||
// Standard: Produktverwaltung
|
||||
cl.show(contentArea, "produkt");
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt eine Statusmeldung im Statusbereich an (PHGUI06: innerhalb 1 Sekunde).
|
||||
*/
|
||||
public void showStatus(String message) {
|
||||
statusLabel.setText("Status: " + message);
|
||||
// Reset nach 3 Sekunden
|
||||
Timer timer = new Timer(3000, e -> statusLabel.setText("Status: Bereit"));
|
||||
timer.setRepeats(false);
|
||||
timer.start();
|
||||
}
|
||||
|
||||
private JButton createNavButton(String text) {
|
||||
JButton btn = new JButton(text);
|
||||
btn.setBackground(new Color(52, 73, 94));
|
||||
btn.setForeground(Color.WHITE);
|
||||
btn.setFocusPainted(false);
|
||||
btn.setBorderPainted(false);
|
||||
btn.setFont(new Font("SansSerif", Font.BOLD, 13));
|
||||
btn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
btn.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
public void mouseEntered(java.awt.event.MouseEvent e) {
|
||||
btn.setBackground(new Color(41, 128, 185));
|
||||
}
|
||||
public void mouseExited(java.awt.event.MouseEvent e) {
|
||||
btn.setBackground(new Color(52, 73, 94));
|
||||
}
|
||||
});
|
||||
return btn;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SwingUtilities.invokeLater(MainFrame::new);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,342 @@
|
|||
package de.hsmannheim.faktura.gui;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.*;
|
||||
import java.awt.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import de.hsmannheim.faktura.product.MwstSatz;
|
||||
import de.hsmannheim.faktura.product.Product;
|
||||
import de.hsmannheim.faktura.product.service.ProductService;
|
||||
import de.hsmannheim.faktura.product.repository.ProductRepository;
|
||||
import de.hsmannheim.faktura.dokumente.repository.DokumentRepository;
|
||||
|
||||
/**
|
||||
* Panel für die Produktverwaltung (PHGUI01–PHGUI04, PHGUI06–PHGUI09).
|
||||
* Einheitliches Layout mit Navigationsbereich, Inhaltsbereich und Statusbereich (PHGUI02).
|
||||
*/
|
||||
public class ProduktPanel extends JPanel {
|
||||
|
||||
private final MainFrame mainFrame;
|
||||
private final ProductService productService;
|
||||
|
||||
// Tabelle
|
||||
private DefaultTableModel tableModel;
|
||||
private JTable produktTable;
|
||||
|
||||
// Formularfelder
|
||||
private JTextField txtProduktId;
|
||||
private JTextField txtBezeichnung;
|
||||
private JTextField txtPreis;
|
||||
private JComboBox<MwstSatz> cmbMwst;
|
||||
private JTextArea txtBeschreibung;
|
||||
private JTextField txtKategorie;
|
||||
|
||||
// Fehlerhinweise (PHGUI03)
|
||||
private JLabel lblBezeichnungFehler;
|
||||
private JLabel lblPreisFehler;
|
||||
|
||||
// Suche (PHGUI09)
|
||||
private JTextField txtSuche;
|
||||
|
||||
// Buttons (PHGUI02: einheitlich beschriftet)
|
||||
private JButton btnNeu;
|
||||
private JButton btnSpeichern;
|
||||
private JButton btnLoeschen;
|
||||
private JButton btnAbbrechen;
|
||||
|
||||
public ProduktPanel(MainFrame mainFrame) {
|
||||
this.mainFrame = mainFrame;
|
||||
this.productService = new ProductService(
|
||||
new ProductRepository(),
|
||||
createDummyDokumentRepository()
|
||||
);
|
||||
setLayout(new BorderLayout(8, 8));
|
||||
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
||||
initComponents();
|
||||
ladeProdukte();
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
// --- Linke Seite: Liste + Suche ---
|
||||
JPanel leftPanel = new JPanel(new BorderLayout(4, 4));
|
||||
leftPanel.setPreferredSize(new Dimension(320, 0));
|
||||
|
||||
String[] columns = {"ID", "Bezeichnung ▲", "Preis"};
|
||||
tableModel = new DefaultTableModel(columns, 0) {
|
||||
public boolean isCellEditable(int r, int c) { return false; }
|
||||
};
|
||||
produktTable = new JTable(tableModel);
|
||||
produktTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
produktTable.getTableHeader().setReorderingAllowed(false);
|
||||
|
||||
// Sortierung (PHGUI08)
|
||||
produktTable.setAutoCreateRowSorter(true);
|
||||
|
||||
produktTable.getSelectionModel().addListSelectionListener(e -> {
|
||||
if (!e.getValueIsAdjusting()) zeigeAusgewaehltes();
|
||||
});
|
||||
|
||||
leftPanel.add(new JLabel("Produktliste"), BorderLayout.NORTH);
|
||||
leftPanel.add(new JScrollPane(produktTable), BorderLayout.CENTER);
|
||||
|
||||
// Suche (PHGUI09)
|
||||
JPanel suchePanel = new JPanel(new BorderLayout(4, 0));
|
||||
suchePanel.setBorder(BorderFactory.createTitledBorder("↑ Suche"));
|
||||
txtSuche = new JTextField();
|
||||
txtSuche.setToolTipText("Bezeichnung eingeben...");
|
||||
txtSuche.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
|
||||
public void insertUpdate(javax.swing.event.DocumentEvent e) { sucheAktualisieren(); }
|
||||
public void removeUpdate(javax.swing.event.DocumentEvent e) { sucheAktualisieren(); }
|
||||
public void changedUpdate(javax.swing.event.DocumentEvent e) { sucheAktualisieren(); }
|
||||
});
|
||||
suchePanel.add(new JLabel("Bezeichnung:"), BorderLayout.WEST);
|
||||
suchePanel.add(txtSuche, BorderLayout.CENTER);
|
||||
leftPanel.add(suchePanel, BorderLayout.SOUTH);
|
||||
|
||||
// --- Rechte Seite: Formular ---
|
||||
JPanel rightPanel = new JPanel(new BorderLayout(4, 8));
|
||||
rightPanel.setBorder(BorderFactory.createTitledBorder("Produktdetails Pflichtfelder sind mit * markiert"));
|
||||
|
||||
JPanel formPanel = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(4, 4, 2, 4);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
// Zeile 0: ProduktId + Einzelpreis
|
||||
gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 0.5;
|
||||
formPanel.add(new JLabel("Produkt-ID (autom. vergeben)"), gbc);
|
||||
gbc.gridx = 1;
|
||||
formPanel.add(new JLabel("Einzelpreis Netto (€) *"), gbc);
|
||||
|
||||
gbc.gridx = 0; gbc.gridy = 1;
|
||||
txtProduktId = new JTextField();
|
||||
txtProduktId.setEditable(false);
|
||||
txtProduktId.setBackground(new Color(230, 230, 230));
|
||||
formPanel.add(txtProduktId, gbc);
|
||||
gbc.gridx = 1;
|
||||
txtPreis = new JTextField();
|
||||
formPanel.add(txtPreis, gbc);
|
||||
|
||||
// Fehlerhinweis Preis
|
||||
gbc.gridx = 1; gbc.gridy = 2;
|
||||
lblPreisFehler = createFehlerLabel();
|
||||
formPanel.add(lblPreisFehler, gbc);
|
||||
|
||||
// Zeile 3: Bezeichnung + MwSt
|
||||
gbc.gridx = 0; gbc.gridy = 3;
|
||||
formPanel.add(new JLabel("Bezeichnung *"), gbc);
|
||||
gbc.gridx = 1;
|
||||
formPanel.add(new JLabel("MwSt.-Satz (%) *"), gbc);
|
||||
|
||||
gbc.gridx = 0; gbc.gridy = 4;
|
||||
txtBezeichnung = new JTextField();
|
||||
formPanel.add(txtBezeichnung, gbc);
|
||||
gbc.gridx = 1;
|
||||
cmbMwst = new JComboBox<>(MwstSatz.values());
|
||||
formPanel.add(cmbMwst, gbc);
|
||||
|
||||
// Fehlerhinweis Bezeichnung
|
||||
gbc.gridx = 0; gbc.gridy = 5;
|
||||
lblBezeichnungFehler = createFehlerLabel();
|
||||
formPanel.add(lblBezeichnungFehler, gbc);
|
||||
|
||||
// Zeile 6: Kategorie
|
||||
gbc.gridx = 0; gbc.gridy = 6; gbc.gridwidth = 2;
|
||||
formPanel.add(new JLabel("Kategorie"), gbc);
|
||||
gbc.gridy = 7;
|
||||
txtKategorie = new JTextField();
|
||||
formPanel.add(txtKategorie, gbc);
|
||||
|
||||
// Zeile 8: Beschreibung
|
||||
gbc.gridy = 8;
|
||||
formPanel.add(new JLabel("Beschreibung"), gbc);
|
||||
gbc.gridy = 9; gbc.weighty = 1.0; gbc.fill = GridBagConstraints.BOTH;
|
||||
txtBeschreibung = new JTextArea(4, 20);
|
||||
txtBeschreibung.setLineWrap(true);
|
||||
formPanel.add(new JScrollPane(txtBeschreibung), gbc);
|
||||
|
||||
rightPanel.add(formPanel, BorderLayout.CENTER);
|
||||
|
||||
// --- Buttons (PHGUI02: einheitlich) ---
|
||||
JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
btnNeu = new JButton("Neu");
|
||||
btnSpeichern = new JButton("Speichern");
|
||||
btnLoeschen = new JButton("Löschen");
|
||||
btnAbbrechen = new JButton("Abbrechen");
|
||||
btnPanel.add(btnNeu);
|
||||
btnPanel.add(btnSpeichern);
|
||||
btnPanel.add(btnLoeschen);
|
||||
btnPanel.add(btnAbbrechen);
|
||||
rightPanel.add(btnPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Button-Actions
|
||||
btnNeu.addActionListener(e -> neuesFormular());
|
||||
btnSpeichern.addActionListener(e -> speichern());
|
||||
btnLoeschen.addActionListener(e -> loeschen());
|
||||
btnAbbrechen.addActionListener(e -> { neuesFormular(); ladeProdukte(); });
|
||||
|
||||
// Split
|
||||
JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel);
|
||||
split.setDividerLocation(320);
|
||||
add(split, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
// --- Daten laden ---
|
||||
private void ladeProdukte() {
|
||||
ladeProdukteListe(productService.getAllProducts());
|
||||
}
|
||||
|
||||
private void ladeProdukteListe(List<Product> liste) {
|
||||
tableModel.setRowCount(0);
|
||||
for (Product p : liste) {
|
||||
tableModel.addRow(new Object[]{
|
||||
p.getProductId(),
|
||||
p.getBezeichnung(),
|
||||
p.getEinzelpreisNetto() + " €"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void sucheAktualisieren() {
|
||||
String query = txtSuche.getText();
|
||||
// PHGUI09: Groß-/Kleinschreibung ignorieren
|
||||
ladeProdukteListe(productService.searchProducts(query));
|
||||
}
|
||||
|
||||
private void zeigeAusgewaehltes() {
|
||||
int row = produktTable.getSelectedRow();
|
||||
if (row < 0) return;
|
||||
int modelRow = produktTable.convertRowIndexToModel(row);
|
||||
String id = (String) tableModel.getValueAt(modelRow, 0);
|
||||
productService.getProductById(id).ifPresent(p -> {
|
||||
txtProduktId.setText(p.getProductId());
|
||||
txtBezeichnung.setText(p.getBezeichnung());
|
||||
txtPreis.setText(p.getEinzelpreisNetto().toPlainString());
|
||||
cmbMwst.setSelectedItem(p.getMwstSatz());
|
||||
txtBeschreibung.setText(p.getBeschreibung());
|
||||
txtKategorie.setText(p.getKategorie());
|
||||
clearFehler();
|
||||
});
|
||||
}
|
||||
|
||||
// --- Aktionen ---
|
||||
private void neuesFormular() {
|
||||
txtProduktId.setText("");
|
||||
txtBezeichnung.setText("");
|
||||
txtPreis.setText("");
|
||||
txtBeschreibung.setText("");
|
||||
txtKategorie.setText("");
|
||||
cmbMwst.setSelectedIndex(0);
|
||||
clearFehler();
|
||||
produktTable.clearSelection();
|
||||
}
|
||||
|
||||
private void speichern() {
|
||||
clearFehler();
|
||||
boolean valid = true;
|
||||
|
||||
String bezeichnung = txtBezeichnung.getText().trim();
|
||||
String preisText = txtPreis.getText().trim();
|
||||
|
||||
// PHGUI03: Pflichtfeldprüfung
|
||||
if (bezeichnung.isEmpty()) {
|
||||
lblBezeichnungFehler.setText("⚠ Pflichtfeld darf nicht leer sein");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
BigDecimal preis = null;
|
||||
if (preisText.isEmpty()) {
|
||||
lblPreisFehler.setText("⚠ Pflichtfeld darf nicht leer sein");
|
||||
valid = false;
|
||||
} else {
|
||||
try {
|
||||
preis = new BigDecimal(preisText.replace(",", "."));
|
||||
// PHGUI04: Negativpreis ablehnen
|
||||
if (preis.compareTo(BigDecimal.ZERO) < 0) {
|
||||
lblPreisFehler.setText("⚠ Preis darf nicht negativ sein");
|
||||
valid = false;
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
lblPreisFehler.setText("⚠ Ungültiger Zahlenwert");
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid) return;
|
||||
|
||||
Product p = new Product(
|
||||
txtProduktId.getText().trim(),
|
||||
bezeichnung,
|
||||
preis,
|
||||
(MwstSatz) cmbMwst.getSelectedItem(),
|
||||
txtBeschreibung.getText().trim(),
|
||||
txtKategorie.getText().trim()
|
||||
);
|
||||
|
||||
try {
|
||||
productService.saveProduct(p);
|
||||
ladeProdukte();
|
||||
neuesFormular();
|
||||
// PHGUI06: Sichtbare Bestätigung < 1 Sekunde
|
||||
mainFrame.showStatus("Produkt erfolgreich gespeichert.");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
mainFrame.showStatus("Fehler: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void loeschen() {
|
||||
int row = produktTable.getSelectedRow();
|
||||
if (row < 0) {
|
||||
mainFrame.showStatus("Kein Produkt ausgewählt.");
|
||||
return;
|
||||
}
|
||||
|
||||
// PHGUI07: Bestätigungsdialog
|
||||
int confirm = JOptionPane.showConfirmDialog(
|
||||
this,
|
||||
"Produkt wirklich löschen?",
|
||||
"Löschen bestätigen",
|
||||
JOptionPane.YES_NO_OPTION
|
||||
);
|
||||
if (confirm != JOptionPane.YES_OPTION) return;
|
||||
|
||||
int modelRow = produktTable.convertRowIndexToModel(row);
|
||||
String id = (String) tableModel.getValueAt(modelRow, 0);
|
||||
boolean deleted = productService.deleteProduct(id);
|
||||
|
||||
if (deleted) {
|
||||
ladeProdukte();
|
||||
neuesFormular();
|
||||
// PHGUI06
|
||||
mainFrame.showStatus("Produkt gelöscht.");
|
||||
} else {
|
||||
mainFrame.showStatus("Löschen nicht möglich – Produkt wird in Dokumenten verwendet.");
|
||||
}
|
||||
}
|
||||
|
||||
private void clearFehler() {
|
||||
lblBezeichnungFehler.setText("");
|
||||
lblPreisFehler.setText("");
|
||||
}
|
||||
|
||||
private JLabel createFehlerLabel() {
|
||||
JLabel lbl = new JLabel("");
|
||||
lbl.setForeground(new Color(192, 57, 43));
|
||||
lbl.setFont(new Font("SansSerif", Font.PLAIN, 11));
|
||||
return lbl;
|
||||
}
|
||||
|
||||
private DokumentRepository createDummyDokumentRepository() {
|
||||
return new DokumentRepository() {
|
||||
public boolean existsReferencedProdukt(long id) { return false; }
|
||||
public boolean existsReferencedKunde(long id) { return false; }
|
||||
public int findMaxLaufendeNummer(String p, int j) { return 0; }
|
||||
public void save(de.hsmannheim.faktura.dokumente.domain.Dokument d) {}
|
||||
public java.util.Optional<de.hsmannheim.faktura.dokumente.domain.Dokument> findByNummer(String n) { return java.util.Optional.empty(); }
|
||||
public List<de.hsmannheim.faktura.dokumente.domain.Dokument> findByKunde(long id) { return List.of(); }
|
||||
public boolean existsFolgedokument(String q) { return false; }
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue