diff --git a/controllers/admin.controller.js b/controllers/admin.controller.js
index 1801c20..ad2d5f8 100644
--- a/controllers/admin.controller.js
+++ b/controllers/admin.controller.js
@@ -195,6 +195,76 @@ function deactivateUser(req, res) {
});
}
+async function showInvoiceOverview(req, res) {
+ const search = req.query.q || "";
+ const view = req.query.view || "year";
+ const currentYear = new Date().getFullYear();
+ const fromYear = req.query.fromYear || currentYear;
+ const toYear = req.query.toYear || currentYear;
+
+ try {
+ const [yearly] = await db.promise().query(`
+ SELECT
+ YEAR(invoice_date) AS year,
+ SUM(total_amount) AS total
+ FROM invoices
+ WHERE status IN ('paid','open')
+ GROUP BY YEAR(invoice_date)
+ ORDER BY year DESC
+ `);
+
+ const [quarterly] = await db.promise().query(`
+ SELECT
+ YEAR(invoice_date) AS year,
+ QUARTER(invoice_date) AS quarter,
+ SUM(total_amount) AS total
+ FROM invoices
+ WHERE status IN ('paid','open')
+ GROUP BY YEAR(invoice_date), QUARTER(invoice_date)
+ ORDER BY year DESC, quarter DESC
+ `);
+
+ const [monthly] = await db.promise().query(`
+ SELECT
+ DATE_FORMAT(invoice_date, '%Y-%m') AS month,
+ SUM(total_amount) AS total
+ FROM invoices
+ WHERE status IN ('paid','open')
+ GROUP BY month
+ ORDER BY month DESC
+ `);
+
+ const [patients] = await db.promise().query(
+ `
+ SELECT
+ CONCAT(p.firstname, ' ', p.lastname) AS patient,
+ SUM(i.total_amount) AS total
+ FROM invoices i
+ JOIN patients p ON p.id = i.patient_id
+ WHERE i.status IN ('paid','open')
+ AND CONCAT(p.firstname, ' ', p.lastname) LIKE ?
+ GROUP BY p.id
+ ORDER BY total DESC
+ `,
+ [`%${search}%`]
+ );
+
+ res.render("admin/admin_invoice_overview", {
+ user: req.session.user,
+ yearly,
+ quarterly,
+ monthly,
+ patients,
+ search,
+ fromYear,
+ toYear,
+ });
+ } catch (err) {
+ console.error(err);
+ res.status(500).send("Fehler beim Laden der Rechnungsübersicht");
+ }
+}
+
module.exports = {
listUsers,
showCreateUser,
@@ -203,4 +273,5 @@ module.exports = {
resetUserPassword,
activateUser,
deactivateUser,
+ showInvoiceOverview,
};
diff --git a/public/invoices/2026/invoice-2026-0039.pdf b/public/invoices/2026/invoice-2026-0039.pdf
new file mode 100644
index 0000000..4540647
Binary files /dev/null and b/public/invoices/2026/invoice-2026-0039.pdf differ
diff --git a/routes/admin.routes.js b/routes/admin.routes.js
index a39dad3..e868f73 100644
--- a/routes/admin.routes.js
+++ b/routes/admin.routes.js
@@ -9,6 +9,7 @@ const {
resetUserPassword,
activateUser,
deactivateUser,
+ showInvoiceOverview,
} = require("../controllers/admin.controller");
const { requireAdmin } = require("../middleware/auth.middleware");
@@ -21,5 +22,6 @@ router.post("/users/change-role/:id", requireAdmin, changeUserRole);
router.post("/users/reset-password/:id", requireAdmin, resetUserPassword);
router.post("/users/activate/:id", requireAdmin, activateUser);
router.post("/users/deactivate/:id", requireAdmin, deactivateUser);
+router.get("/invoices", requireAdmin, showInvoiceOverview);
module.exports = router;
diff --git a/views/admin/admin_invoice_overview.ejs b/views/admin/admin_invoice_overview.ejs
new file mode 100644
index 0000000..80ed2c1
--- /dev/null
+++ b/views/admin/admin_invoice_overview.ejs
@@ -0,0 +1,233 @@
+
+
+
+
+ Rechnungsübersicht
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Jahr |
+ € |
+
+
+
+ <% if (yearly.length === 0) { %>
+
+ |
+ Keine Daten
+ |
+
+ <% } %> <% yearly.forEach(y => { %>
+
+ | <%= y.year %> |
+
+ <%= Number(y.total).toFixed(2) %>
+ |
+
+ <% }) %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Jahr |
+ Q |
+ € |
+
+
+
+ <% if (quarterly.length === 0) { %>
+
+ |
+ Keine Daten
+ |
+
+ <% } %> <% quarterly.forEach(q => { %>
+
+ | <%= q.year %> |
+ Q<%= q.quarter %> |
+
+ <%= Number(q.total).toFixed(2) %>
+ |
+
+ <% }) %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Monat |
+ € |
+
+
+
+ <% if (monthly.length === 0) { %>
+
+ |
+ Keine Daten
+ |
+
+ <% } %> <% monthly.forEach(m => { %>
+
+ | <%= m.month %> |
+
+ <%= Number(m.total).toFixed(2) %>
+ |
+
+ <% }) %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Patient |
+ € |
+
+
+
+ <% if (patients.length === 0) { %>
+
+ |
+ Keine Daten
+ |
+
+ <% } %> <% patients.forEach(p => { %>
+
+ | <%= p.patient %> |
+
+ <%= Number(p.total).toFixed(2) %>
+ |
+
+ <% }) %>
+
+
+
+
+
+
+
+
+
diff --git a/views/dashboard.ejs b/views/dashboard.ejs
index c1d81d8..d8855ed 100644
--- a/views/dashboard.ejs
+++ b/views/dashboard.ejs
@@ -1,128 +1,110 @@
-
-
+
+
Dashboard
-
-
-
-
-
-
-
-
-
-
+
+
+ <%- include("partials/flash") %>
-
-
-
-
- <%- include("partials/flash") %>
-
-
-
+
Willkommen, <%= user.username %>
-
+
-
-
-
+
🪑 Wartezimmer-Monitor
- <%
- const maxSlots = 21; // 3 Reihen × 7 Plätze
- for (let i = 0; i < maxSlots; i++) {
- const p = waitingPatients && waitingPatients[i];
- %>
-
-
- <% if (p) { %>
-
- <%= p.firstname %> <%= p.lastname %>
-
-
- <%= new Date(p.birthdate).toLocaleDateString("de-DE") %>
-
- <% } else { %>
-
-

-
- <% } %>
-
+ <% const maxSlots = 21; for (let i = 0; i < maxSlots; i++) { const p =
+ waitingPatients && waitingPatients[i]; %>
+
+ <% if (p) { %>
+
<%= p.firstname %> <%= p.lastname %>
+
+ <%= new Date(p.birthdate).toLocaleDateString("de-DE") %>
+
+ <% } else { %>
+
+

+
<% } %>
+
+
+ <% } %>
-
+
-
-
-
-
+