Offene Rechnungen anzeigen lassen.
This commit is contained in:
parent
fbe1b34b25
commit
57073ffc05
16
app.js
16
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("/"));
|
||||
|
||||
33
controllers/invoice.controller.js
Normal file
33
controllers/invoice.controller.js
Normal file
@ -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");
|
||||
}
|
||||
};
|
||||
@ -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,
|
||||
|
||||
@ -55,6 +55,7 @@
|
||||
"unlock": "Enrsperren",
|
||||
"name": "Name"
|
||||
},
|
||||
|
||||
"sidebar": {
|
||||
"patients": "Patienten",
|
||||
"medications": "Medikamente",
|
||||
@ -63,6 +64,7 @@
|
||||
"admin": "Verwaltung",
|
||||
"logout": "Logout"
|
||||
},
|
||||
|
||||
"dashboard": {
|
||||
"welcome": "Willkommen",
|
||||
"waitingRoom": "Wartezimmer-Monitor",
|
||||
@ -114,5 +116,9 @@
|
||||
"patienttitle": "Patientenübersicht",
|
||||
"newpatient": "Neuer Patient",
|
||||
"nopatientfound": "Keine Patienten gefunden"
|
||||
},
|
||||
|
||||
"openinvoices": {
|
||||
"openinvoices": "Offene Rechnungen"
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,6 +64,7 @@
|
||||
"admin": "Administración",
|
||||
"logout": "Cerrar sesión"
|
||||
},
|
||||
|
||||
"dashboard": {
|
||||
"welcome": "Bienvenido",
|
||||
"waitingRoom": "Monitor sala de espera",
|
||||
@ -115,5 +116,9 @@
|
||||
"patienttitle": "Resumen de pacientes",
|
||||
"newpatient": "Paciente nuevo",
|
||||
"nopatientfound": "No se han encontrado pacientes."
|
||||
},
|
||||
|
||||
"openinvoices": {
|
||||
"openinvoices": "Facturas pendientes"
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
28
views/invoices/open-invoices.ejs
Normal file
28
views/invoices/open-invoices.ejs
Normal file
@ -0,0 +1,28 @@
|
||||
<h1>🧾 Offene Rechnungen</h1>
|
||||
|
||||
<% if (invoices.length === 0) { %>
|
||||
<p>Keine offenen Rechnungen 🎉</p>
|
||||
<% } else { %>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Patient</th>
|
||||
<th>Datum</th>
|
||||
<th>Betrag</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% invoices.forEach(inv => { %>
|
||||
<tr>
|
||||
<td><%= inv.id %></td>
|
||||
<td><%= inv.firstname %> <%= inv.lastname %></td>
|
||||
<td><%= inv.invoice_date %></td>
|
||||
<td><%= inv.total_amount_formatted %> €</td>
|
||||
<td>offen</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% } %>
|
||||
@ -1,36 +0,0 @@
|
||||
<div class="sidebar">
|
||||
<div class="logo">
|
||||
<i class="bi bi-cash-coin"></i>
|
||||
Invoice Menü
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="/admin/invoices?view=year"
|
||||
class="nav-item <%= active === 'sales_year' ? 'active' : '' %>"
|
||||
>
|
||||
<i class="bi bi-calendar3"></i> <%= t.adminInvoice.annualSales %>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/admin/invoices?view=quarter"
|
||||
class="nav-item <%= active === 'sales_quarter' ? 'active' : '' %>"
|
||||
>
|
||||
<i class="bi bi-calendar2-week"></i> <%= t.adminInvoice.quarterlySales %>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/admin/invoices?view=month"
|
||||
class="nav-item <%= active === 'sales_month' ? 'active' : '' %>"
|
||||
>
|
||||
<i class="bi bi-calendar2"></i> <%= t.adminInvoice.monthSales %>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/admin/invoices?view=patient"
|
||||
class="nav-item <%= active === 'sales_patient' ? 'active' : '' %>"
|
||||
>
|
||||
<i class="bi bi-people"></i> <%= t.adminInvoice.patientsSales %>
|
||||
</a>
|
||||
|
||||
<div class="spacer"></div>
|
||||
</div>
|
||||
@ -167,10 +167,6 @@
|
||||
<% } %>
|
||||
</button>
|
||||
|
||||
<div class="sidebar-muted" style="margin-top: 6px">
|
||||
Nur aktiv nach Patientenauswahl
|
||||
</div>
|
||||
|
||||
<% if (canUsePatient) { %>
|
||||
</form>
|
||||
<% } %>
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
// BASISDATEN
|
||||
// =========================
|
||||
const role = user?.role || null;
|
||||
// ✅ Bereich 1: Arzt + Mitarbeiter
|
||||
const canDoctorAndStaff = role === "arzt" || role === "mitarbeiter";
|
||||
|
||||
// Arzt + Mitarbeiter dürfen Patienten bedienen
|
||||
const canPatientArea = role === "arzt" || role === "mitarbeiter";
|
||||
@ -38,28 +40,21 @@
|
||||
|
||||
<div style="margin:10px 0; border-top:1px solid rgba(255,255,255,0.12);"></div>
|
||||
|
||||
<!-- ✅ Kein Patient gewählt -->
|
||||
<% if (!pid) { %>
|
||||
<div class="nav-item locked" style="opacity:0.7;">
|
||||
<i class="bi bi-info-circle"></i> Bitte Patient auswählen
|
||||
<span style="margin-left:auto;"><i class="bi bi-lock-fill"></i></span>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<!-- =========================
|
||||
Rechnungen
|
||||
========================= -->
|
||||
<a
|
||||
href="<%= hrefIfAllowed(canDoctorAndStaff, '/patients') %>"
|
||||
class="nav-item <%= active === 'patients' ? 'active' : '' %> <%= lockClass(canDoctorAndStaff) %>"
|
||||
href="<%= hrefIfAllowed(canDoctorAndStaff, '/invoices/open') %>"
|
||||
class="nav-item <%= active === 'open_invoices' ? 'active' : '' %> <%= lockClass(canDoctorAndStaff) %>"
|
||||
title="<%= canDoctorAndStaff ? '' : 'Nur Arzt + Mitarbeiter' %>"
|
||||
>
|
||||
<i class="bi bi-people"></i> <%= t.sidebar.patients %>
|
||||
<i class="bi bi-receipt"></i> <%= t.openinvoices.openinvoices %>
|
||||
<% if (!canDoctorAndStaff) { %>
|
||||
<span style="margin-left:auto;"><i class="bi bi-lock-fill"></i></span>
|
||||
<% } %>
|
||||
</a>
|
||||
|
||||
|
||||
<a
|
||||
href="<%= hrefIfAllowed(canDoctorAndStaff, '/patients') %>"
|
||||
class="nav-item <%= active === 'patients' ? 'active' : '' %> <%= lockClass(canDoctorAndStaff) %>"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user