commit 7699d8860d5efa1c87c4636401def2d435494ad9 Author: Hymmel Date: Thu Dec 11 09:56:42 2025 +0100 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..d59ad37 --- /dev/null +++ b/README.md @@ -0,0 +1,106 @@ +# Projektaufgabe – Entwicklung einer Werkstattverwaltungssoftware + +Sie arbeiten als Softwareentwicklungs-Team in einem Unternehmen, das digitale Lösungen für kleine Betriebe entwickelt. Eine regionale Kfz-Werkstatt hat Sie beauftragt, eine Konsolenanwendung zu erstellen, mit der sie ihre täglichen Arbeitsabläufe digital verwalten kann. Die Daten sollen in einer Datenbank gespeichert werden. + +## Auftrag des Kunden + +### 1. Verwaltung der Personen, die Leistungen in Anspruch nehmen +Die Werkstatt möchte Informationen über ihre Kundschaft erfassen und abrufen können. Das System soll ermöglichen, neue Kunden anzulegen, vorhandene anzuzeigen und eine Übersicht aller erfassten Personen auszugeben. + +### 2. Verwaltung der Fahrzeuge +Zu jeder Person können mehrere Fahrzeuge gehören. Das System soll das Anlegen neuer Fahrzeuge, das Anzeigen einzelner Fahrzeuge sowie eine Übersicht über alle Fahrzeuge einer bestimmten Person ermöglichen. + +### 3. Dokumentation der Werkstattarbeiten +Für jedes Fahrzeug sollen Werkstattvorgänge angelegt und angezeigt werden können. Ein Werkstattvorgang besteht aus mehreren einzelnen Positionen, welche aus einem vordefinierten Katalog zu wählen sind + +#### Vorgabekatalog für Leistungen und Preise + +Die Werkstatt verwendet einen festen Leistungskatalog mit einheitlichen Preisen. Diese werden vorab in der Datenbank hinterlegt. + +Der Katalog enthält folgende Positionen: + +| Leistung | Preis pro Einheit | +|------------------------------------|-------------------| +| Ölwechsel | 89.00 € | +| Bremsflüssigkeit wechseln | 59.00 € | +| Reifen wechseln (pro Stück) | 18.00 € | +| Klimaanlagen-Service | 99.00 € | +| Bremsbeläge erneuern vorne | 149.00 € | +| Zündkerzen austauschen | 79.00 € | +| Diagnose / Fehlerspeicher auslesen | 25.00 € | +| Arbeitszeit pro Stunde | 75.00 € | + + +### 4. Einsicht in Werkstattvorgänge +Alle erfassten Vorgänge sollen einzeln einsehbar sein. Dabei sollen folgende Informationen angezeigt werden können: +- alle erfassten Positionen eines Vorgangs inkl. Preis, +- die automatisch berechnete Gesamtsumme, +- das zugehörige Fahrzeug und die zugehörige Person. + +## Datenhaltung + +Alle Daten sollen dauerhaft in einer relationalen Datenbank gespeichert werden. Die Struktur der Datenbank (Tabellen, Beziehungen) ist auf Basis der fachlichen Anforderungen selbst zu entwickeln. + +## Bedienoberfläche + +Alle Funktionen sollen über ein verständliches, klar strukturiertes Konsolenmenü steuerbar sein. + +## Technische Anforderungen + +- Java-Konsolenanwendung +- Objektorientierte Programmierung +- Verwendung einer relationalen Datenbank +- Datenbankzugriff mit JDBC +- Verwendung eines festen Leistungskatalogs +- Sinnvolle Trennung von Logik und Datenzugriff +- Fehlerbehandlung für Nutzereingaben +- Übersichtlicher, gut strukturierter Code + +## Liefergegenstände +Für die Colloboration und die Abgabe ist ein git Repository anzulegen. Die Lehrkraft ist dem Repository als Bearbeiter hinzuzufügen. Follgendes ist hierbei abzugeben: + +- Vollständiger Source-Code als Maven-Projekt +- SQL-Skript zum Erstellen der Datenbanktabellen +- ER-Modell +- UML-Diagramm +- Kunden Domumentation als README +- Javadoc als HTML-Dokumentation + +## Zusatzaufgaben (für Notenverbesserung über 1,5 hinaus) + +Durch die vollständige und korrekte Umsetzung der regulären Anforderungen kann eine maximale Note von **1,5** erreicht werden. +Für eine darüber hinausgehende Bewertung können zusätzlich folgende Erweiterungen umgesetzt werden: + +- **Laden des Leistungskatalogs aus einer Datei** + (z. B. JSON oder CSV statt statischer Daten) + +- **Rechnungserstellung** + Erzeugung eines strukturierten, druckfähigen Dokuments + (z. B. Textdatei, Markdown oder einfache PDF-Ausgabe) + +- **Zugangsbeschränkung für Mitarbeiter** + Implementierung eines einfachen Login-Systems + (Nutzerverwaltung + Passwortprüfung) + +- **Unit-Tests** + Testabdeckung von **> 90 %** der zentralen Funktionen + +- **Testen mit gemockter Datenbank** + Nutzung von Mocks oder In-Memory-Datenbank zur Simulation der Datenhaltung + +- **Datenexport** + Export von Vorgängen oder Kundendaten in JSON-, CSV- oder Textdateien + +- **Logging** + Protokollierung aller Aktionen in einer Logdatei (z. B. mit einfachem File-Logger) + + +## Bewertungskriterien + +- Qualität und Vollständigkeit des Datenmodells +- Saubere objektorientierte Struktur +- Funktionierende Datenbankanbindung +- Umsetzung des Leistungskatalogs +- Bedienbarkeit und Menüführung +- Code-Stil und Robustheit +- Qualität der Dokumentation diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..96de98b --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash +set -e +mvn -q -DskipTests package diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0f5ac98 --- /dev/null +++ b/pom.xml @@ -0,0 +1,55 @@ + + 4.0.0 + de.berufsschule + jdbc-konsolen-projekt + 1.0-SNAPSHOT + WerkstattKonsole + + 17 + 17 + UTF-8 + + + + org.postgresql + postgresql + 42.7.3 + + + + + + maven-compiler-plugin + 3.11.0 + + ${maven.compiler.source} + ${maven.compiler.target} + + + + maven-assembly-plugin + 3.6.0 + + + jar-with-dependencies + + + + de.berufsschule.workshop.App + + + + + + package + + single + + + + + + + diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..e4bfdc7 --- /dev/null +++ b/run.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e +./build.sh +java -jar target/jdbc-konsolen-projekt-1.0-SNAPSHOT-jar-with-dependencies.jar diff --git a/sql/schema.sql b/sql/schema.sql new file mode 100644 index 0000000..8b46cca --- /dev/null +++ b/sql/schema.sql @@ -0,0 +1,44 @@ +CREATE TABLE IF NOT EXISTS person ( + id SERIAL PRIMARY KEY, + name VARCHAR(200) NOT NULL, + phone VARCHAR(100) +); + +CREATE TABLE IF NOT EXISTS vehicle ( + id SERIAL PRIMARY KEY, + person_id INTEGER NOT NULL REFERENCES person(id) ON DELETE CASCADE, + brand VARCHAR(120) NOT NULL, + model VARCHAR(120) NOT NULL, + plate VARCHAR(50) NOT NULL +); + +CREATE TABLE IF NOT EXISTS service_item ( + id SERIAL PRIMARY KEY, + title VARCHAR(200) NOT NULL, + price NUMERIC(10,2) NOT NULL +); + +CREATE TABLE IF NOT EXISTS work_order ( + id SERIAL PRIMARY KEY, + vehicle_id INTEGER NOT NULL REFERENCES vehicle(id) ON DELETE CASCADE, + note TEXT, + created_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +CREATE TABLE IF NOT EXISTS work_order_item ( + id SERIAL PRIMARY KEY, + work_order_id INTEGER NOT NULL REFERENCES work_order(id) ON DELETE CASCADE, + service_item_id INTEGER NOT NULL REFERENCES service_item(id), + quantity INTEGER NOT NULL +); + +INSERT INTO service_item(title, price) VALUES + ('Ölwechsel', 89.00), + ('Bremsflüssigkeit wechseln', 59.00), + ('Reifen wechseln (pro Stück)', 18.00), + ('Klimaanlagen-Service', 99.00), + ('Bremsbeläge erneuern vorne', 149.00), + ('Zündkerzen austauschen', 79.00), + ('Diagnose / Fehlerspeicher auslesen', 25.00), + ('Arbeitszeit pro Stunde', 75.00) +ON CONFLICT DO NOTHING; diff --git a/src/main/java/de/berufsschule/workshop/App.java b/src/main/java/de/berufsschule/workshop/App.java new file mode 100644 index 0000000..5bdb5c0 --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/App.java @@ -0,0 +1,14 @@ +package de.berufsschule.workshop; + +import de.berufsschule.workshop.service.WorkshopService; + +public class App { + public static void main(String[] args) { + try { + WorkshopService service = new WorkshopService(); + new ConsoleUi(service).start(); + } catch (RuntimeException e) { + System.out.println("Start fehlgeschlagen: " + e.getMessage()); + } + } +} diff --git a/src/main/java/de/berufsschule/workshop/ConsoleUi.java b/src/main/java/de/berufsschule/workshop/ConsoleUi.java new file mode 100644 index 0000000..06830a4 --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/ConsoleUi.java @@ -0,0 +1,190 @@ +package de.berufsschule.workshop; + +import de.berufsschule.workshop.model.Person; +import de.berufsschule.workshop.model.ServiceItem; +import de.berufsschule.workshop.model.Vehicle; +import de.berufsschule.workshop.model.WorkOrderDetails; +import de.berufsschule.workshop.model.WorkOrderItem; +import de.berufsschule.workshop.service.WorkshopService; + +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; + +public class ConsoleUi { + private final WorkshopService service; + private final Scanner scanner = new Scanner(System.in); + + public ConsoleUi(WorkshopService service) { + this.service = service; + } + + public void start() { + boolean running = true; + while (running) { + printMenu(); + String input = scanner.nextLine(); + try { + switch (input) { + case "1" -> addPerson(); + case "2" -> listPersons(); + case "3" -> addVehicle(); + case "4" -> listVehiclesOfPerson(); + case "5" -> createWorkOrder(); + case "6" -> listWorkOrders(); + case "0" -> running = false; + default -> System.out.println("Unbekannte Auswahl"); + } + } catch (SQLException e) { + System.out.println("Fehler: " + e.getMessage()); + } + } + System.out.println("Bis dann"); + } + + private void printMenu() { + System.out.println(); + System.out.println("==== Werkstatt Menü ===="); + System.out.println("1) Kunde anlegen"); + System.out.println("2) Kunden anzeigen"); + System.out.println("3) Fahrzeug anlegen"); + System.out.println("4) Fahrzeuge einer Person"); + System.out.println("5) Werkstattvorgang anlegen"); + System.out.println("6) Vorgänge anzeigen"); + System.out.println("0) Ende"); + System.out.print("Auswahl: "); + } + + private void addPerson() throws SQLException { + System.out.print("Name: "); + String name = scanner.nextLine(); + System.out.print("Telefon: "); + String phone = scanner.nextLine(); + Person person = service.createPerson(name, phone); + System.out.println("Kunde gespeichert mit ID " + person.getId()); + } + + private void listPersons() throws SQLException { + List persons = service.listPersons(); + if (persons.isEmpty()) { + System.out.println("Keine Personen da"); + return; + } + persons.forEach(p -> System.out.println(p.toString())); + } + + private void addVehicle() throws SQLException { + System.out.print("Personen ID: "); + int personId = parseInt(scanner.nextLine()); + Person owner = service.findPerson(personId); + if (owner == null) { + System.out.println("Person nicht gefunden"); + return; + } + System.out.print("Hersteller: "); + String brand = scanner.nextLine(); + System.out.print("Modell: "); + String model = scanner.nextLine(); + System.out.print("Kennzeichen: "); + String plate = scanner.nextLine(); + Vehicle vehicle = service.createVehicle(personId, brand, model, plate); + System.out.println("Fahrzeug gespeichert mit ID " + vehicle.getId()); + } + + private void listVehiclesOfPerson() throws SQLException { + System.out.print("Personen ID: "); + int personId = parseInt(scanner.nextLine()); + Person owner = service.findPerson(personId); + if (owner == null) { + System.out.println("Person nicht gefunden"); + return; + } + List vehicles = service.listVehicles(personId); + System.out.println("Fahrzeuge von " + owner.getName() + ":"); + if (vehicles.isEmpty()) { + System.out.println("Keine Einträge"); + return; + } + vehicles.forEach(v -> System.out.println(v.toString())); + } + + private void createWorkOrder() throws SQLException { + System.out.print("Fahrzeug ID: "); + int vehicleId = parseInt(scanner.nextLine()); + Vehicle vehicle = service.findVehicle(vehicleId); + if (vehicle == null) { + System.out.println("Fahrzeug nicht gefunden"); + return; + } + System.out.print("Notiz: "); + String note = scanner.nextLine(); + List catalog = service.listServiceItems(); + Map selected = new LinkedHashMap<>(); + boolean adding = true; + while (adding) { + showCatalog(catalog); + System.out.print("Service ID (leer = fertig): "); + String input = scanner.nextLine(); + if (input.isBlank()) { + adding = false; + } else { + int serviceId = parseInt(input); + ServiceItem item = catalog.stream().filter(s -> s.getId() == serviceId).findFirst().orElse(null); + if (item == null) { + System.out.println("Service gibt es nicht"); + continue; + } + System.out.print("Menge: "); + int qty = parseInt(scanner.nextLine()); + if (qty <= 0) { + System.out.println("Menge muss > 0 sein"); + continue; + } + selected.put(serviceId, selected.getOrDefault(serviceId, 0) + qty); + } + } + if (selected.isEmpty()) { + System.out.println("Keine Positionen gespeichert"); + return; + } + int orderId = service.createWorkOrder(vehicleId, note, selected); + System.out.println("Vorgang angelegt mit ID " + orderId); + } + + private void showCatalog(List items) { + System.out.println("Katalog:"); + items.forEach(item -> System.out.println(item.toString())); + } + + private void listWorkOrders() throws SQLException { + List orders = service.listWorkOrders(); + if (orders.isEmpty()) { + System.out.println("Keine Vorgänge gespeichert"); + return; + } + for (WorkOrderDetails order : orders) { + System.out.println("--- Vorgang " + order.getOrderId() + " ---"); + System.out.println("Kunde: " + order.getPersonName()); + System.out.println("Fahrzeug: " + order.getVehicleText()); + System.out.println("Datum: " + order.getCreatedAt()); + if (order.getNote() != null && !order.getNote().isBlank()) { + System.out.println("Notiz: " + order.getNote()); + } + for (WorkOrderItem item : order.getItems()) { + System.out.println(" * " + item.getServiceItem().getTitle() + " x" + item.getQuantity() + + " = " + item.getTotalPrice() + " €"); + } + System.out.println("Summe: " + order.getTotal() + " €"); + } + } + + private int parseInt(String input) { + try { + return Integer.parseInt(input.trim()); + } catch (NumberFormatException e) { + return -1; + } + } +} diff --git a/src/main/java/de/berufsschule/workshop/Database.java b/src/main/java/de/berufsschule/workshop/Database.java new file mode 100644 index 0000000..ef4ed3c --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/Database.java @@ -0,0 +1,15 @@ +package de.berufsschule.workshop; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class Database { + private static final String URL = "jdbc:postgresql://38.242.130.81:5555/postgres"; + private static final String USER = "postgres"; + private static final String PASSWORD = "tgewptivkpv7zlk5"; + + public static Connection getConnection() throws SQLException { + return DriverManager.getConnection(URL, USER, PASSWORD); + } +} diff --git a/src/main/java/de/berufsschule/workshop/dao/PersonDao.java b/src/main/java/de/berufsschule/workshop/dao/PersonDao.java new file mode 100644 index 0000000..06d78ec --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/dao/PersonDao.java @@ -0,0 +1,50 @@ +package de.berufsschule.workshop.dao; + +import de.berufsschule.workshop.Database; +import de.berufsschule.workshop.model.Person; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +public class PersonDao { + public Person create(String name, String phone) throws SQLException { + String sql = "INSERT INTO person(name, phone) VALUES (?, ?) RETURNING id"; + try (Connection con = Database.getConnection(); + PreparedStatement ps = con.prepareStatement(sql)) { + ps.setString(1, name); + ps.setString(2, phone); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + return new Person(rs.getInt("id"), name, phone); + } + throw new SQLException("Konnte Person nicht speichern"); + } + } + + public List findAll() throws SQLException { + List result = new ArrayList<>(); + String sql = "SELECT id, name, phone FROM person ORDER BY name"; + try (Connection con = Database.getConnection(); + PreparedStatement ps = con.prepareStatement(sql); + ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + result.add(new Person(rs.getInt("id"), rs.getString("name"), rs.getString("phone"))); + } + } + return result; + } + + public Person findById(int id) throws SQLException { + String sql = "SELECT id, name, phone FROM person WHERE id = ?"; + try (Connection con = Database.getConnection(); + PreparedStatement ps = con.prepareStatement(sql)) { + ps.setInt(1, id); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + return new Person(rs.getInt("id"), rs.getString("name"), rs.getString("phone")); + } + return null; + } + } +} diff --git a/src/main/java/de/berufsschule/workshop/dao/SchemaCreator.java b/src/main/java/de/berufsschule/workshop/dao/SchemaCreator.java new file mode 100644 index 0000000..03fbce1 --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/dao/SchemaCreator.java @@ -0,0 +1,43 @@ +package de.berufsschule.workshop.dao; + +import de.berufsschule.workshop.Database; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +public class SchemaCreator { + public void ensureSchema() throws SQLException { + try (Connection con = Database.getConnection(); Statement st = con.createStatement()) { + st.execute("CREATE TABLE IF NOT EXISTS person (" + + "id SERIAL PRIMARY KEY, " + + "name VARCHAR(200) NOT NULL, " + + "phone VARCHAR(100)" + + ")"); + st.execute("CREATE TABLE IF NOT EXISTS vehicle (" + + "id SERIAL PRIMARY KEY, " + + "person_id INTEGER NOT NULL REFERENCES person(id) ON DELETE CASCADE, " + + "brand VARCHAR(120) NOT NULL, " + + "model VARCHAR(120) NOT NULL, " + + "plate VARCHAR(50) NOT NULL" + + ")"); + st.execute("CREATE TABLE IF NOT EXISTS service_item (" + + "id SERIAL PRIMARY KEY, " + + "title VARCHAR(200) NOT NULL, " + + "price NUMERIC(10,2) NOT NULL" + + ")"); + st.execute("CREATE TABLE IF NOT EXISTS work_order (" + + "id SERIAL PRIMARY KEY, " + + "vehicle_id INTEGER NOT NULL REFERENCES vehicle(id) ON DELETE CASCADE, " + + "note TEXT, " + + "created_at TIMESTAMP NOT NULL DEFAULT NOW()" + + ")"); + st.execute("CREATE TABLE IF NOT EXISTS work_order_item (" + + "id SERIAL PRIMARY KEY, " + + "work_order_id INTEGER NOT NULL REFERENCES work_order(id) ON DELETE CASCADE, " + + "service_item_id INTEGER NOT NULL REFERENCES service_item(id), " + + "quantity INTEGER NOT NULL" + + ")"); + } + } +} diff --git a/src/main/java/de/berufsschule/workshop/dao/ServiceItemDao.java b/src/main/java/de/berufsschule/workshop/dao/ServiceItemDao.java new file mode 100644 index 0000000..7bdc45b --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/dao/ServiceItemDao.java @@ -0,0 +1,70 @@ +package de.berufsschule.workshop.dao; + +import de.berufsschule.workshop.Database; +import de.berufsschule.workshop.model.ServiceItem; + +import java.math.BigDecimal; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +public class ServiceItemDao { + public List findAll() throws SQLException { + List result = new ArrayList<>(); + String sql = "SELECT id, title, price FROM service_item ORDER BY id"; + try (Connection con = Database.getConnection(); + PreparedStatement ps = con.prepareStatement(sql); + ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + result.add(map(rs)); + } + } + return result; + } + + public ServiceItem findById(int id) throws SQLException { + String sql = "SELECT id, title, price FROM service_item WHERE id = ?"; + try (Connection con = Database.getConnection(); + PreparedStatement ps = con.prepareStatement(sql)) { + ps.setInt(1, id); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + return map(rs); + } + return null; + } + } + + public void seedDefaultItems() throws SQLException { + String countSql = "SELECT COUNT(*) FROM service_item"; + try (Connection con = Database.getConnection(); + PreparedStatement countStmt = con.prepareStatement(countSql)) { + ResultSet rs = countStmt.executeQuery(); + if (rs.next() && rs.getLong(1) > 0) { + return; + } + } + insert("Ölwechsel", new BigDecimal("89.00")); + insert("Bremsflüssigkeit wechseln", new BigDecimal("59.00")); + insert("Reifen wechseln (pro Stück)", new BigDecimal("18.00")); + insert("Klimaanlagen-Service", new BigDecimal("99.00")); + insert("Bremsbeläge erneuern vorne", new BigDecimal("149.00")); + insert("Zündkerzen austauschen", new BigDecimal("79.00")); + insert("Diagnose / Fehlerspeicher auslesen", new BigDecimal("25.00")); + insert("Arbeitszeit pro Stunde", new BigDecimal("75.00")); + } + + private void insert(String title, BigDecimal price) throws SQLException { + String sql = "INSERT INTO service_item(title, price) VALUES (?, ?)"; + try (Connection con = Database.getConnection(); + PreparedStatement ps = con.prepareStatement(sql)) { + ps.setString(1, title); + ps.setBigDecimal(2, price); + ps.executeUpdate(); + } + } + + private ServiceItem map(ResultSet rs) throws SQLException { + return new ServiceItem(rs.getInt("id"), rs.getString("title"), rs.getBigDecimal("price")); + } +} diff --git a/src/main/java/de/berufsschule/workshop/dao/VehicleDao.java b/src/main/java/de/berufsschule/workshop/dao/VehicleDao.java new file mode 100644 index 0000000..1e18097 --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/dao/VehicleDao.java @@ -0,0 +1,63 @@ +package de.berufsschule.workshop.dao; + +import de.berufsschule.workshop.Database; +import de.berufsschule.workshop.model.Vehicle; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +public class VehicleDao { + public Vehicle create(int personId, String brand, String model, String plate) throws SQLException { + String sql = "INSERT INTO vehicle(person_id, brand, model, plate) VALUES (?, ?, ?, ?) RETURNING id"; + try (Connection con = Database.getConnection(); + PreparedStatement ps = con.prepareStatement(sql)) { + ps.setInt(1, personId); + ps.setString(2, brand); + ps.setString(3, model); + ps.setString(4, plate); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + return new Vehicle(rs.getInt("id"), personId, brand, model, plate); + } + throw new SQLException("Konnte Fahrzeug nicht speichern"); + } + } + + public List findByPerson(int personId) throws SQLException { + List result = new ArrayList<>(); + String sql = "SELECT id, person_id, brand, model, plate FROM vehicle WHERE person_id = ? ORDER BY id"; + try (Connection con = Database.getConnection(); + PreparedStatement ps = con.prepareStatement(sql)) { + ps.setInt(1, personId); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + result.add(map(rs)); + } + } + return result; + } + + public Vehicle findById(int id) throws SQLException { + String sql = "SELECT id, person_id, brand, model, plate FROM vehicle WHERE id = ?"; + try (Connection con = Database.getConnection(); + PreparedStatement ps = con.prepareStatement(sql)) { + ps.setInt(1, id); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + return map(rs); + } + return null; + } + } + + private Vehicle map(ResultSet rs) throws SQLException { + return new Vehicle( + rs.getInt("id"), + rs.getInt("person_id"), + rs.getString("brand"), + rs.getString("model"), + rs.getString("plate") + ); + } +} diff --git a/src/main/java/de/berufsschule/workshop/dao/WorkOrderDao.java b/src/main/java/de/berufsschule/workshop/dao/WorkOrderDao.java new file mode 100644 index 0000000..29ce2ac --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/dao/WorkOrderDao.java @@ -0,0 +1,108 @@ +package de.berufsschule.workshop.dao; + +import de.berufsschule.workshop.Database; +import de.berufsschule.workshop.model.ServiceItem; +import de.berufsschule.workshop.model.WorkOrderDetails; +import de.berufsschule.workshop.model.WorkOrderItem; + +import java.math.BigDecimal; +import java.sql.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class WorkOrderDao { + public int createWorkOrder(int vehicleId, String note, Map items) throws SQLException { + if (items.isEmpty()) { + throw new IllegalArgumentException("Keine Positionen gewählt"); + } + try (Connection con = Database.getConnection()) { + con.setAutoCommit(false); + try { + int orderId = insertOrder(con, vehicleId, note); + insertItems(con, orderId, items); + con.commit(); + return orderId; + } catch (SQLException e) { + con.rollback(); + throw e; + } finally { + con.setAutoCommit(true); + } + } + } + + private int insertOrder(Connection con, int vehicleId, String note) throws SQLException { + String sql = "INSERT INTO work_order(vehicle_id, note) VALUES (?, ?) RETURNING id"; + try (PreparedStatement ps = con.prepareStatement(sql)) { + ps.setInt(1, vehicleId); + ps.setString(2, note); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + return rs.getInt("id"); + } + throw new SQLException("Vorgang konnte nicht gespeichert werden"); + } + } + + private void insertItems(Connection con, int orderId, Map items) throws SQLException { + String sql = "INSERT INTO work_order_item(work_order_id, service_item_id, quantity) VALUES (?, ?, ?)"; + try (PreparedStatement ps = con.prepareStatement(sql)) { + for (Map.Entry entry : items.entrySet()) { + ps.setInt(1, orderId); + ps.setInt(2, entry.getKey()); + ps.setInt(3, entry.getValue()); + ps.addBatch(); + } + ps.executeBatch(); + } + } + + public List fetchAllDetails() throws SQLException { + Map> itemMap = loadItems(); + List result = new ArrayList<>(); + String sql = "SELECT wo.id, wo.note, wo.created_at, p.name AS person_name, " + + "v.brand, v.model, v.plate FROM work_order wo " + + "JOIN vehicle v ON wo.vehicle_id = v.id " + + "JOIN person p ON v.person_id = p.id ORDER BY wo.created_at DESC"; + try (Connection con = Database.getConnection(); + PreparedStatement ps = con.prepareStatement(sql); + ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + int id = rs.getInt("id"); + List items = itemMap.getOrDefault(id, new ArrayList<>()); + BigDecimal total = items.stream() + .map(WorkOrderItem::getTotalPrice) + .reduce(BigDecimal.ZERO, BigDecimal::add); + result.add(new WorkOrderDetails( + id, + rs.getString("person_name"), + rs.getString("brand") + " " + rs.getString("model") + " (" + rs.getString("plate") + ")", + rs.getTimestamp("created_at").toLocalDateTime(), + rs.getString("note"), + items, + total + )); + } + } + return result; + } + + private Map> loadItems() throws SQLException { + Map> map = new HashMap<>(); + String sql = "SELECT woi.id, woi.work_order_id, woi.quantity, si.id AS service_id, si.title, si.price " + + "FROM work_order_item woi JOIN service_item si ON woi.service_item_id = si.id"; + try (Connection con = Database.getConnection(); + PreparedStatement ps = con.prepareStatement(sql); + ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + ServiceItem item = new ServiceItem(rs.getInt("service_id"), rs.getString("title"), rs.getBigDecimal("price")); + WorkOrderItem orderItem = new WorkOrderItem(rs.getInt("id"), rs.getInt("work_order_id"), item, rs.getInt("quantity")); + map.computeIfAbsent(orderItem.getWorkOrderId(), id -> new ArrayList<>()).add(orderItem); + } + } + return map; + } +} diff --git a/src/main/java/de/berufsschule/workshop/model/Person.java b/src/main/java/de/berufsschule/workshop/model/Person.java new file mode 100644 index 0000000..5ca0964 --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/model/Person.java @@ -0,0 +1,30 @@ +package de.berufsschule.workshop.model; + +public class Person { + private final int id; + private final String name; + private final String phone; + + public Person(int id, String name, String phone) { + this.id = id; + this.name = name; + this.phone = phone; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public String getPhone() { + return phone; + } + + @Override + public String toString() { + return id + ": " + name + " (" + phone + ")"; + } +} diff --git a/src/main/java/de/berufsschule/workshop/model/ServiceItem.java b/src/main/java/de/berufsschule/workshop/model/ServiceItem.java new file mode 100644 index 0000000..dbb1a28 --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/model/ServiceItem.java @@ -0,0 +1,32 @@ +package de.berufsschule.workshop.model; + +import java.math.BigDecimal; + +public class ServiceItem { + private final int id; + private final String title; + private final BigDecimal price; + + public ServiceItem(int id, String title, BigDecimal price) { + this.id = id; + this.title = title; + this.price = price; + } + + public int getId() { + return id; + } + + public String getTitle() { + return title; + } + + public BigDecimal getPrice() { + return price; + } + + @Override + public String toString() { + return id + " - " + title + " (" + price + " €)"; + } +} diff --git a/src/main/java/de/berufsschule/workshop/model/Vehicle.java b/src/main/java/de/berufsschule/workshop/model/Vehicle.java new file mode 100644 index 0000000..e730a82 --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/model/Vehicle.java @@ -0,0 +1,42 @@ +package de.berufsschule.workshop.model; + +public class Vehicle { + private final int id; + private final int personId; + private final String brand; + private final String model; + private final String plate; + + public Vehicle(int id, int personId, String brand, String model, String plate) { + this.id = id; + this.personId = personId; + this.brand = brand; + this.model = model; + this.plate = plate; + } + + public int getId() { + return id; + } + + public int getPersonId() { + return personId; + } + + public String getBrand() { + return brand; + } + + public String getModel() { + return model; + } + + public String getPlate() { + return plate; + } + + @Override + public String toString() { + return id + " - " + brand + " " + model + " | " + plate; + } +} diff --git a/src/main/java/de/berufsschule/workshop/model/WorkOrder.java b/src/main/java/de/berufsschule/workshop/model/WorkOrder.java new file mode 100644 index 0000000..e4003a6 --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/model/WorkOrder.java @@ -0,0 +1,33 @@ +package de.berufsschule.workshop.model; + +import java.time.LocalDateTime; + +public class WorkOrder { + private final int id; + private final int vehicleId; + private final String note; + private final LocalDateTime createdAt; + + public WorkOrder(int id, int vehicleId, String note, LocalDateTime createdAt) { + this.id = id; + this.vehicleId = vehicleId; + this.note = note; + this.createdAt = createdAt; + } + + public int getId() { + return id; + } + + public int getVehicleId() { + return vehicleId; + } + + public String getNote() { + return note; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } +} diff --git a/src/main/java/de/berufsschule/workshop/model/WorkOrderDetails.java b/src/main/java/de/berufsschule/workshop/model/WorkOrderDetails.java new file mode 100644 index 0000000..a78434d --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/model/WorkOrderDetails.java @@ -0,0 +1,54 @@ +package de.berufsschule.workshop.model; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +public class WorkOrderDetails { + private final int orderId; + private final String personName; + private final String vehicleText; + private final LocalDateTime createdAt; + private final String note; + private final List items; + private final BigDecimal total; + + public WorkOrderDetails(int orderId, String personName, String vehicleText, + LocalDateTime createdAt, String note, List items, BigDecimal total) { + this.orderId = orderId; + this.personName = personName; + this.vehicleText = vehicleText; + this.createdAt = createdAt; + this.note = note; + this.items = items; + this.total = total; + } + + public int getOrderId() { + return orderId; + } + + public String getPersonName() { + return personName; + } + + public String getVehicleText() { + return vehicleText; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public String getNote() { + return note; + } + + public List getItems() { + return items; + } + + public BigDecimal getTotal() { + return total; + } +} diff --git a/src/main/java/de/berufsschule/workshop/model/WorkOrderItem.java b/src/main/java/de/berufsschule/workshop/model/WorkOrderItem.java new file mode 100644 index 0000000..906efb6 --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/model/WorkOrderItem.java @@ -0,0 +1,37 @@ +package de.berufsschule.workshop.model; + +import java.math.BigDecimal; + +public class WorkOrderItem { + private final int id; + private final int workOrderId; + private final ServiceItem serviceItem; + private final int quantity; + + public WorkOrderItem(int id, int workOrderId, ServiceItem serviceItem, int quantity) { + this.id = id; + this.workOrderId = workOrderId; + this.serviceItem = serviceItem; + this.quantity = quantity; + } + + public int getId() { + return id; + } + + public int getWorkOrderId() { + return workOrderId; + } + + public ServiceItem getServiceItem() { + return serviceItem; + } + + public int getQuantity() { + return quantity; + } + + public BigDecimal getTotalPrice() { + return serviceItem.getPrice().multiply(BigDecimal.valueOf(quantity)); + } +} diff --git a/src/main/java/de/berufsschule/workshop/service/WorkshopService.java b/src/main/java/de/berufsschule/workshop/service/WorkshopService.java new file mode 100644 index 0000000..05054db --- /dev/null +++ b/src/main/java/de/berufsschule/workshop/service/WorkshopService.java @@ -0,0 +1,67 @@ +package de.berufsschule.workshop.service; + +import de.berufsschule.workshop.dao.PersonDao; +import de.berufsschule.workshop.dao.SchemaCreator; +import de.berufsschule.workshop.dao.ServiceItemDao; +import de.berufsschule.workshop.dao.VehicleDao; +import de.berufsschule.workshop.dao.WorkOrderDao; +import de.berufsschule.workshop.model.Person; +import de.berufsschule.workshop.model.ServiceItem; +import de.berufsschule.workshop.model.Vehicle; +import de.berufsschule.workshop.model.WorkOrderDetails; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +public class WorkshopService { + private final PersonDao personDao = new PersonDao(); + private final VehicleDao vehicleDao = new VehicleDao(); + private final ServiceItemDao serviceItemDao = new ServiceItemDao(); + private final WorkOrderDao workOrderDao = new WorkOrderDao(); + + public WorkshopService() { + try { + new SchemaCreator().ensureSchema(); + serviceItemDao.seedDefaultItems(); + } catch (SQLException e) { + throw new RuntimeException("Setup fehlgeschlagen: " + e.getMessage(), e); + } + } + + public Person createPerson(String name, String phone) throws SQLException { + return personDao.create(name, phone); + } + + public List listPersons() throws SQLException { + return personDao.findAll(); + } + + public Vehicle createVehicle(int personId, String brand, String model, String plate) throws SQLException { + return vehicleDao.create(personId, brand, model, plate); + } + + public List listVehicles(int personId) throws SQLException { + return vehicleDao.findByPerson(personId); + } + + public List listServiceItems() throws SQLException { + return serviceItemDao.findAll(); + } + + public int createWorkOrder(int vehicleId, String note, Map items) throws SQLException { + return workOrderDao.createWorkOrder(vehicleId, note, items); + } + + public List listWorkOrders() throws SQLException { + return workOrderDao.fetchAllDetails(); + } + + public Person findPerson(int id) throws SQLException { + return personDao.findById(id); + } + + public Vehicle findVehicle(int id) throws SQLException { + return vehicleDao.findById(id); + } +} diff --git a/target/classes/de/berufsschule/workshop/App.class b/target/classes/de/berufsschule/workshop/App.class new file mode 100644 index 0000000..7d25b72 Binary files /dev/null and b/target/classes/de/berufsschule/workshop/App.class differ diff --git a/target/classes/de/berufsschule/workshop/ConsoleUi.class b/target/classes/de/berufsschule/workshop/ConsoleUi.class new file mode 100644 index 0000000..502735c Binary files /dev/null and b/target/classes/de/berufsschule/workshop/ConsoleUi.class differ diff --git a/target/classes/de/berufsschule/workshop/Database.class b/target/classes/de/berufsschule/workshop/Database.class new file mode 100644 index 0000000..388580f Binary files /dev/null and b/target/classes/de/berufsschule/workshop/Database.class differ diff --git a/target/classes/de/berufsschule/workshop/dao/PersonDao.class b/target/classes/de/berufsschule/workshop/dao/PersonDao.class new file mode 100644 index 0000000..cc4462d Binary files /dev/null and b/target/classes/de/berufsschule/workshop/dao/PersonDao.class differ diff --git a/target/classes/de/berufsschule/workshop/dao/SchemaCreator.class b/target/classes/de/berufsschule/workshop/dao/SchemaCreator.class new file mode 100644 index 0000000..55109b1 Binary files /dev/null and b/target/classes/de/berufsschule/workshop/dao/SchemaCreator.class differ diff --git a/target/classes/de/berufsschule/workshop/dao/ServiceItemDao.class b/target/classes/de/berufsschule/workshop/dao/ServiceItemDao.class new file mode 100644 index 0000000..a2264cc Binary files /dev/null and b/target/classes/de/berufsschule/workshop/dao/ServiceItemDao.class differ diff --git a/target/classes/de/berufsschule/workshop/dao/VehicleDao.class b/target/classes/de/berufsschule/workshop/dao/VehicleDao.class new file mode 100644 index 0000000..c06af04 Binary files /dev/null and b/target/classes/de/berufsschule/workshop/dao/VehicleDao.class differ diff --git a/target/classes/de/berufsschule/workshop/dao/WorkOrderDao.class b/target/classes/de/berufsschule/workshop/dao/WorkOrderDao.class new file mode 100644 index 0000000..bab0bfb Binary files /dev/null and b/target/classes/de/berufsschule/workshop/dao/WorkOrderDao.class differ diff --git a/target/classes/de/berufsschule/workshop/model/Person.class b/target/classes/de/berufsschule/workshop/model/Person.class new file mode 100644 index 0000000..37405c3 Binary files /dev/null and b/target/classes/de/berufsschule/workshop/model/Person.class differ diff --git a/target/classes/de/berufsschule/workshop/model/ServiceItem.class b/target/classes/de/berufsschule/workshop/model/ServiceItem.class new file mode 100644 index 0000000..71ad9fa Binary files /dev/null and b/target/classes/de/berufsschule/workshop/model/ServiceItem.class differ diff --git a/target/classes/de/berufsschule/workshop/model/Vehicle.class b/target/classes/de/berufsschule/workshop/model/Vehicle.class new file mode 100644 index 0000000..9dfc747 Binary files /dev/null and b/target/classes/de/berufsschule/workshop/model/Vehicle.class differ diff --git a/target/classes/de/berufsschule/workshop/model/WorkOrder.class b/target/classes/de/berufsschule/workshop/model/WorkOrder.class new file mode 100644 index 0000000..69cfbf4 Binary files /dev/null and b/target/classes/de/berufsschule/workshop/model/WorkOrder.class differ diff --git a/target/classes/de/berufsschule/workshop/model/WorkOrderDetails.class b/target/classes/de/berufsschule/workshop/model/WorkOrderDetails.class new file mode 100644 index 0000000..52998cc Binary files /dev/null and b/target/classes/de/berufsschule/workshop/model/WorkOrderDetails.class differ diff --git a/target/classes/de/berufsschule/workshop/model/WorkOrderItem.class b/target/classes/de/berufsschule/workshop/model/WorkOrderItem.class new file mode 100644 index 0000000..217e003 Binary files /dev/null and b/target/classes/de/berufsschule/workshop/model/WorkOrderItem.class differ diff --git a/target/classes/de/berufsschule/workshop/service/WorkshopService.class b/target/classes/de/berufsschule/workshop/service/WorkshopService.class new file mode 100644 index 0000000..23d9d21 Binary files /dev/null and b/target/classes/de/berufsschule/workshop/service/WorkshopService.class differ diff --git a/target/jdbc-konsolen-projekt-1.0-SNAPSHOT-jar-with-dependencies.jar b/target/jdbc-konsolen-projekt-1.0-SNAPSHOT-jar-with-dependencies.jar new file mode 100644 index 0000000..171ce52 Binary files /dev/null and b/target/jdbc-konsolen-projekt-1.0-SNAPSHOT-jar-with-dependencies.jar differ diff --git a/target/jdbc-konsolen-projekt-1.0-SNAPSHOT.jar b/target/jdbc-konsolen-projekt-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..8d6c50c Binary files /dev/null and b/target/jdbc-konsolen-projekt-1.0-SNAPSHOT.jar differ diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties new file mode 100644 index 0000000..aec08fb --- /dev/null +++ b/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Thu Dec 11 09:51:52 CET 2025 +artifactId=jdbc-konsolen-projekt +groupId=de.berufsschule +version=1.0-SNAPSHOT diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..1dd313d --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,15 @@ +de/berufsschule/workshop/model/ServiceItem.class +de/berufsschule/workshop/ConsoleUi.class +de/berufsschule/workshop/dao/WorkOrderDao.class +de/berufsschule/workshop/dao/VehicleDao.class +de/berufsschule/workshop/model/WorkOrder.class +de/berufsschule/workshop/App.class +de/berufsschule/workshop/dao/PersonDao.class +de/berufsschule/workshop/Database.class +de/berufsschule/workshop/service/WorkshopService.class +de/berufsschule/workshop/model/WorkOrderDetails.class +de/berufsschule/workshop/model/Person.class +de/berufsschule/workshop/model/Vehicle.class +de/berufsschule/workshop/model/WorkOrderItem.class +de/berufsschule/workshop/dao/SchemaCreator.class +de/berufsschule/workshop/dao/ServiceItemDao.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..6f98950 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,15 @@ +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/dao/VehicleDao.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/App.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/model/WorkOrderItem.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/model/Vehicle.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/ConsoleUi.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/model/WorkOrder.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/model/WorkOrderDetails.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/dao/ServiceItemDao.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/dao/WorkOrderDao.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/model/Person.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/Database.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/dao/PersonDao.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/dao/SchemaCreator.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/service/WorkshopService.java +/config/workspace/Schule/jdbc_konsolen_projekt/src/main/java/de/berufsschule/workshop/model/ServiceItem.java