diff --git a/app.js b/app.js index e991b16..8f5caf5 100644 --- a/app.js +++ b/app.js @@ -73,7 +73,7 @@ app.use(express.json()); app.use( helmet({ contentSecurityPolicy: false, - }) + }), ); app.use( @@ -83,7 +83,7 @@ app.use( store: getSessionStore(), resave: false, saveUninitialized: false, - }) + }), ); // ✅ i18n Middleware (SAFE) @@ -156,7 +156,7 @@ app.use(async (req, res, next) => { `SELECT id, serial_number, trial_started_at FROM company_settings ORDER BY id ASC - LIMIT 1` + LIMIT 1`, ); const settings = rowsSettings?.[0]; @@ -170,7 +170,7 @@ app.use(async (req, res, next) => { .promise() .query( `UPDATE company_settings SET trial_started_at = NOW() WHERE id = ?`, - [settings.id] + [settings.id], ); return next(); } @@ -226,7 +226,7 @@ app.get("/serial-number", async (req, res) => { `SELECT id, serial_number, trial_started_at FROM company_settings ORDER BY id ASC - LIMIT 1` + LIMIT 1`, ); const settings = rowsSettings?.[0]; @@ -240,7 +240,7 @@ app.get("/serial-number", async (req, res) => { .promise() .query( `UPDATE company_settings SET trial_started_at = NOW() WHERE id = ?`, - [settings.id] + [settings.id], ); settings.trial_started_at = new Date(); } @@ -288,7 +288,7 @@ app.get("/admin/serial-number", async (req, res) => { const [rowsSettings] = await db .promise() .query( - `SELECT serial_number FROM company_settings ORDER BY id ASC LIMIT 1` + `SELECT serial_number FROM company_settings ORDER BY id ASC LIMIT 1`, ); const currentSerial = rowsSettings?.[0]?.serial_number || ""; @@ -399,7 +399,7 @@ app.use("/services", serviceRoutes); app.use("/", patientFileRoutes); app.use("/", waitingRoomRoutes); -app.use("/", invoiceRoutes); +app.use("/invoices", invoiceRoutes); app.get("/logout", (req, res) => { req.session.destroy(() => res.redirect("/")); diff --git a/controllers/invoice.controller.js b/controllers/invoice.controller.js new file mode 100644 index 0000000..0cb8382 --- /dev/null +++ b/controllers/invoice.controller.js @@ -0,0 +1,33 @@ +const db = require("../db"); + +exports.openInvoices = async (req, res) => { + try { + const [rows] = await db.promise().query(` + SELECT + i.id, + i.invoice_date, + i.total_amount, + i.status, + p.firstname, + p.lastname + FROM invoices i + JOIN patients p ON p.id = i.patient_id + WHERE i.status = 'open' + ORDER BY i.invoice_date DESC +`); + console.log("ROWS:", rows); + const invoices = rows.map((inv) => ({ + ...inv, + total_amount_formatted: Number(inv.total_amount).toFixed(2), + })); + + res.render("invoices/open-invoices", { + user: req.session.user, + invoices, + active: "open_invoices", + }); + } catch (err) { + console.error("❌ openInvoices Fehler:", err); + res.status(500).send("Fehler beim Laden der offenen Rechnungen"); + } +}; diff --git a/controllers/service.controller.js b/controllers/service.controller.js index 948a0c2..5f307ed 100644 --- a/controllers/service.controller.js +++ b/controllers/service.controller.js @@ -287,7 +287,7 @@ async function listOpenServices(req, res, next) { res.render("open_services", { title: "Offene Leistungen", - sidebarPartial: "partials/patient_sidebar", + sidebarPartial: "partials/sidebar-invoices", active: "services", rows, diff --git a/locales/de.json b/locales/de.json index 9be11da..7d8adca 100644 --- a/locales/de.json +++ b/locales/de.json @@ -6,55 +6,56 @@ "reset": "Reset", "dashboard": "Dashboard", "logout": "Logout", - "title":"Titel", - "firstname":"Vorname", - "lastname":"Nachname", - "username":"Username", - "role":"Rolle", - "action":"Aktionen", - "status":"Status", - "you":"Du Selbst", - "newuser":"Neuer benutzer", - "inactive":"inaktive", - "active":"aktive", - "closed":"gesperrt", - "filter":"Filtern", - "yearcash":"Jahresumsatz", - "monthcash":"Monatsumsatz", - "quartalcash":"Quartalsumsatz", - "year":"Jahr", - "nodata":"keine Daten", - "month":"Monat", - "patientcash":"Umsatz pro Patient", - "patient":"Patient", - "systeminfo":"Systeminformationen", - "table":"Tabelle", - "lines":"Zeilen", - "size":"Grösse", - "errordatabase":"Fehler beim Auslesen der Datenbankinfos:", - "welcome":"Willkommen", - "waitingroomtext":"Wartezimmer-Monitor", - "waitingroomtextnopatient":"Keine Patienten im Wartezimmer.", - "gender":"Geschlecht", - "birthday":"Geburtstag", - "email":"E-Mail", - "phone":"Telefon", - "address":"Adresse", - "country":"Land", - "notice":"Notizen", - "create":"Erstellt", - "change":"Geändert", - "reset2":"Zurücksetzen", - "edit":"Bearbeiten", - "selection":"Auswahl", - "waiting":"Wartet bereits", - "towaitingroom":"Ins Wartezimmer", - "overview":"Übersicht", - "upload":"Hochladen", - "lock":"Sperren", - "unlock":"Enrsperren", - "name":"Name" + "title": "Titel", + "firstname": "Vorname", + "lastname": "Nachname", + "username": "Username", + "role": "Rolle", + "action": "Aktionen", + "status": "Status", + "you": "Du Selbst", + "newuser": "Neuer benutzer", + "inactive": "inaktive", + "active": "aktive", + "closed": "gesperrt", + "filter": "Filtern", + "yearcash": "Jahresumsatz", + "monthcash": "Monatsumsatz", + "quartalcash": "Quartalsumsatz", + "year": "Jahr", + "nodata": "keine Daten", + "month": "Monat", + "patientcash": "Umsatz pro Patient", + "patient": "Patient", + "systeminfo": "Systeminformationen", + "table": "Tabelle", + "lines": "Zeilen", + "size": "Grösse", + "errordatabase": "Fehler beim Auslesen der Datenbankinfos:", + "welcome": "Willkommen", + "waitingroomtext": "Wartezimmer-Monitor", + "waitingroomtextnopatient": "Keine Patienten im Wartezimmer.", + "gender": "Geschlecht", + "birthday": "Geburtstag", + "email": "E-Mail", + "phone": "Telefon", + "address": "Adresse", + "country": "Land", + "notice": "Notizen", + "create": "Erstellt", + "change": "Geändert", + "reset2": "Zurücksetzen", + "edit": "Bearbeiten", + "selection": "Auswahl", + "waiting": "Wartet bereits", + "towaitingroom": "Ins Wartezimmer", + "overview": "Übersicht", + "upload": "Hochladen", + "lock": "Sperren", + "unlock": "Enrsperren", + "name": "Name" }, + "sidebar": { "patients": "Patienten", "medications": "Medikamente", @@ -63,56 +64,61 @@ "admin": "Verwaltung", "logout": "Logout" }, + "dashboard": { "welcome": "Willkommen", "waitingRoom": "Wartezimmer-Monitor", "noWaitingPatients": "Keine Patienten im Wartezimmer.", - "title":"Dashboard" + "title": "Dashboard" }, "adminSidebar": { "users": "Userverwaltung", "database": "Datenbankverwaltung", - "user":"Benutzer", - "invocieoverview":"Rechnungsübersicht", - "seriennumber":"Seriennummer", - "databasetable":"Datenbank", - "companysettings":"Firmendaten" + "user": "Benutzer", + "invocieoverview": "Rechnungsübersicht", + "seriennumber": "Seriennummer", + "databasetable": "Datenbank", + "companysettings": "Firmendaten" }, "adminuseroverview": { "useroverview": "Benutzerübersicht", "usermanagement": "Benutzer Verwaltung", - "user":"Benutzer", - "invocieoverview":"Rechnungsübersicht", - "seriennumber":"Seriennummer", - "databasetable":"Datenbank" + "user": "Benutzer", + "invocieoverview": "Rechnungsübersicht", + "seriennumber": "Seriennummer", + "databasetable": "Datenbank" }, "seriennumber": { "seriennumbertitle": "Seriennummer eingeben", "seriennumbertext": "Bitte gib deine Lizenz-Seriennummer ein um die Software dauerhaft freizuschalten.", - "seriennumbershort":"Seriennummer (AAAAA-AAAAA-AAAAA-AAAAA)", - "seriennumberdeclaration":"Nur Buchstaben + Zahlen. Format: 4×5 Zeichen, getrennt mit „-“. ", - "saveseriennumber":"Seriennummer Speichern" + "seriennumbershort": "Seriennummer (AAAAA-AAAAA-AAAAA-AAAAA)", + "seriennumberdeclaration": "Nur Buchstaben + Zahlen. Format: 4×5 Zeichen, getrennt mit „-“. ", + "saveseriennumber": "Seriennummer Speichern" }, "databaseoverview": { "title": "Datenbank Konfiguration", - "text":"Hier kannst du die DB-Verbindung testen und speichern. ", + "text": "Hier kannst du die DB-Verbindung testen und speichern. ", "host": "Host", - "port":"Port", - "database":"Datenbank", - "password":"Password", - "connectiontest":"Verbindung testen", - "tablecount":"Anzahl Tabellen", - "databasesize":"Datenbankgrösse", - "tableoverview":"Tabellenübersicht" + "port": "Port", + "database": "Datenbank", + "password": "Password", + "connectiontest": "Verbindung testen", + "tablecount": "Anzahl Tabellen", + "databasesize": "Datenbankgrösse", + "tableoverview": "Tabellenübersicht" }, "patienteoverview": { "patienttitle": "Patientenübersicht", - "newpatient":"Neuer Patient", - "nopatientfound":"Keine Patienten gefunden" + "newpatient": "Neuer Patient", + "nopatientfound": "Keine Patienten gefunden" + }, + + "openinvoices": { + "openinvoices": "Offene Rechnungen" } } diff --git a/locales/es.json b/locales/es.json index 2062297..7b5c2a0 100644 --- a/locales/es.json +++ b/locales/es.json @@ -6,54 +6,54 @@ "reset": "Resetear", "dashboard": "Panel", "logout": "cerrar sesión", - "title":"Título", - "firstname":"Nombre", - "lastname":"apellido", - "username":"Nombre de usuario", - "role":"desempeñar", - "action":"acción", - "status":"Estado", - "you":"su mismo", - "newuser":"Nuevo usuario", - "inactive":"inactivo", - "active":"activo", - "closed":"bloqueado", - "filter":"Filtro", - "yearcash":"volumen de negocios anual", - "monthcash":"volumen de negocios mensual", - "quartalcash":"volumen de negocios trimestral", - "year":"ano", - "nodata":"sin datos", - "month":"mes", - "patientcash":"Ingresos por paciente", - "patient":"paciente", - "systeminfo":"Información del sistema", - "table":"tablas", - "lines":"líneas", - "size":"Tamaño", - "errordatabase":"Error al leer la información de la base de datos:", - "welcome":"Bienvenido", - "waitingroomtext":"Monitor de sala de espera", - "waitingroomtextnopatient":"No hay pacientes en la sala de espera.", - "gender":"Sexo", - "birthday":"Fecha de nacimiento", - "email":"Correo electrónico", - "phone":"Teléfono", - "address":"Dirección", - "country":"País", - "notice":"Notas", - "create":"Creado", - "change":"Modificado", - "reset2":"Restablecer", - "edit":"editar", - "selection":"Selección", - "waiting":"Ya está esperando", - "towaitingroom":"A la sala de espera", - "overview":"Resumen", - "upload":"Cargar", - "lock":"bloquear", - "unlock":"desbloquear", - "name":"Nombre" + "title": "Título", + "firstname": "Nombre", + "lastname": "apellido", + "username": "Nombre de usuario", + "role": "desempeñar", + "action": "acción", + "status": "Estado", + "you": "su mismo", + "newuser": "Nuevo usuario", + "inactive": "inactivo", + "active": "activo", + "closed": "bloqueado", + "filter": "Filtro", + "yearcash": "volumen de negocios anual", + "monthcash": "volumen de negocios mensual", + "quartalcash": "volumen de negocios trimestral", + "year": "ano", + "nodata": "sin datos", + "month": "mes", + "patientcash": "Ingresos por paciente", + "patient": "paciente", + "systeminfo": "Información del sistema", + "table": "tablas", + "lines": "líneas", + "size": "Tamaño", + "errordatabase": "Error al leer la información de la base de datos:", + "welcome": "Bienvenido", + "waitingroomtext": "Monitor de sala de espera", + "waitingroomtextnopatient": "No hay pacientes en la sala de espera.", + "gender": "Sexo", + "birthday": "Fecha de nacimiento", + "email": "Correo electrónico", + "phone": "Teléfono", + "address": "Dirección", + "country": "País", + "notice": "Notas", + "create": "Creado", + "change": "Modificado", + "reset2": "Restablecer", + "edit": "editar", + "selection": "Selección", + "waiting": "Ya está esperando", + "towaitingroom": "A la sala de espera", + "overview": "Resumen", + "upload": "Cargar", + "lock": "bloquear", + "unlock": "desbloquear", + "name": "Nombre" }, "sidebar": { @@ -64,56 +64,61 @@ "admin": "Administración", "logout": "Cerrar sesión" }, + "dashboard": { "welcome": "Bienvenido", "waitingRoom": "Monitor sala de espera", "noWaitingPatients": "No hay pacientes en la sala de espera.", - "title":"Dashboard" + "title": "Dashboard" }, "adminSidebar": { "users": "Administración de usuarios", "database": "Administración de base de datos", - "user":"usuario", - "invocieoverview":"Resumen de facturas", - "seriennumber":"número de serie", - "databasetable":"base de datos", - "companysettings":"Datos de la empresa" + "user": "usuario", + "invocieoverview": "Resumen de facturas", + "seriennumber": "número de serie", + "databasetable": "base de datos", + "companysettings": "Datos de la empresa" }, "adminuseroverview": { "useroverview": "Resumen de usuarios", "usermanagement": "Administración de usuarios", - "user":"usuario", - "invocieoverview":"Resumen de facturas", - "seriennumber":"número de serie", - "databasetable":"base de datos" + "user": "usuario", + "invocieoverview": "Resumen de facturas", + "seriennumber": "número de serie", + "databasetable": "base de datos" }, "seriennumber": { "seriennumbertitle": "Introduce el número de serie", "seriennumbertext": "Introduce el número de serie de tu licencia para activar el software de forma permanente.", - "seriennumbershort":"Número de serie (AAAAA-AAAAA-AAAAA-AAAAA)", - "seriennumberdeclaration":"Solo letras y números. Formato: 4×5 caracteres, separados por «-». ", - "saveseriennumber":"Guardar número de serie" + "seriennumbershort": "Número de serie (AAAAA-AAAAA-AAAAA-AAAAA)", + "seriennumberdeclaration": "Solo letras y números. Formato: 4×5 caracteres, separados por «-». ", + "saveseriennumber": "Guardar número de serie" }, "databaseoverview": { "title": "Configuración de la base de datos", "host": "Host", - "port":"Puerto", - "database":"Base de datos", - "password":"Contraseña", - "connectiontest":"Probar conexión", - "text":"Aquí puedes probar y guardar la conexión a la base de datos. ", - "tablecount":"Número de tablas", - "databasesize":"Tamaño de la base de datos", - "tableoverview":"Resumen de tablas" + "port": "Puerto", + "database": "Base de datos", + "password": "Contraseña", + "connectiontest": "Probar conexión", + "text": "Aquí puedes probar y guardar la conexión a la base de datos. ", + "tablecount": "Número de tablas", + "databasesize": "Tamaño de la base de datos", + "tableoverview": "Resumen de tablas" }, - + "patienteoverview": { "patienttitle": "Resumen de pacientes", - "newpatient":"Paciente nuevo", - "nopatientfound":"No se han encontrado pacientes." + "newpatient": "Paciente nuevo", + "nopatientfound": "No se han encontrado pacientes." + }, + + "openinvoices": { + "openinvoices": "Facturas pendientes" } } diff --git a/routes/invoice.routes.js b/routes/invoice.routes.js index 62b2e15..ecc4d52 100644 --- a/routes/invoice.routes.js +++ b/routes/invoice.routes.js @@ -1,8 +1,14 @@ const express = require("express"); const router = express.Router(); + const { requireArzt } = require("../middleware/auth.middleware"); const { createInvoicePdf } = require("../controllers/invoicePdf.controller"); +const { openInvoices } = require("../controllers/invoice.controller"); +// ✅ NEU: Offene Rechnungen anzeigen +router.get("/open", requireArzt, openInvoices); + +// Bestehend router.post("/patients/:id/create-invoice", requireArzt, createInvoicePdf); module.exports = router; diff --git a/views/invoices/open-invoices.ejs b/views/invoices/open-invoices.ejs new file mode 100644 index 0000000..80eb730 --- /dev/null +++ b/views/invoices/open-invoices.ejs @@ -0,0 +1,28 @@ +
Keine offenen Rechnungen 🎉
+<% } else { %> +| # | +Patient | +Datum | +Betrag | +Status | +
|---|---|---|---|---|
| <%= inv.id %> | +<%= inv.firstname %> <%= inv.lastname %> | +<%= inv.invoice_date %> | +<%= inv.total_amount_formatted %> € | +offen | +