Praxissofttware/controllers/invoicePdf.controller.js
2026-01-08 13:05:56 +00:00

199 lines
5.0 KiB
JavaScript

const db = require("../db");
const ejs = require("ejs");
const path = require("path");
const htmlToPdf = require("html-pdf-node");
const fs = require("fs");
async function createInvoicePdf(req, res) {
const patientId = req.params.id;
const connection = await db.promise().getConnection();
try {
await connection.beginTransaction();
const year = new Date().getFullYear();
// 🔒 Rechnungszähler sperren
const [[counterRow]] = await connection.query(
"SELECT counter FROM invoice_counters WHERE year = ? FOR UPDATE",
[year]
);
let counter;
if (!counterRow) {
counter = 1;
await connection.query(
"INSERT INTO invoice_counters (year, counter) VALUES (?, ?)",
[year, counter]
);
} else {
counter = counterRow.counter + 1;
await connection.query(
"UPDATE invoice_counters SET counter = ? WHERE year = ?",
[counter, year]
);
}
const invoiceNumber = `${year}-${String(counter).padStart(4, "0")}`;
// 🔹 Patient
const [[patient]] = await connection.query(
"SELECT * FROM patients WHERE id = ?",
[patientId]
);
if (!patient) throw new Error("Patient nicht gefunden");
// 🔹 Leistungen
const [rows] = await connection.query(
`
SELECT
ps.quantity,
COALESCE(ps.price_override, s.price) AS price,
s.name_de AS name
FROM patient_services ps
JOIN services s ON ps.service_id = s.id
WHERE ps.patient_id = ?
AND ps.invoice_id IS NULL
`,
[patientId]
);
if (!rows.length) throw new Error("Keine Leistungen vorhanden");
const services = rows.map((s) => ({
quantity: Number(s.quantity),
name: s.name,
price: Number(s.price),
total: Number(s.price) * Number(s.quantity),
}));
const total = services.reduce((sum, s) => sum + s.total, 0);
// 🔹 Arzt
const [[doctor]] = await connection.query(
`
SELECT first_name, last_name, fachrichtung, arztnummer
FROM users
WHERE id = (
SELECT created_by
FROM patient_services
WHERE patient_id = ?
ORDER BY service_date DESC
LIMIT 1
)
`,
[patientId]
);
// 🔹 Firma
const [[company]] = await connection.query(
"SELECT * FROM company_settings LIMIT 1"
);
// 🖼 Logo als Base64
let logoBase64 = null;
if (company && company.invoice_logo_path) {
const logoPath = path.join(
__dirname,
"..",
"public",
company.invoice_logo_path
);
if (fs.existsSync(logoPath)) {
const buffer = fs.readFileSync(logoPath);
const ext = path.extname(logoPath).toLowerCase();
const mime =
ext === ".jpg" || ext === ".jpeg" ? "image/jpeg" : "image/png";
logoBase64 = `data:${mime};base64,${buffer.toString("base64")}`;
}
}
// 📁 PDF-Pfad vorbereiten
const invoiceDir = path.join(
__dirname,
"..",
"public",
"invoices",
String(year)
);
if (!fs.existsSync(invoiceDir)) {
fs.mkdirSync(invoiceDir, { recursive: true });
}
const fileName = `invoice-${invoiceNumber}.pdf`;
const absoluteFilePath = path.join(invoiceDir, fileName);
const dbFilePath = `/invoices/${year}/${fileName}`;
// 🔹 Rechnung speichern
const [result] = await connection.query(
`
INSERT INTO invoices
(patient_id, invoice_date, file_path, total_amount, created_by, status)
VALUES (?, CURDATE(), ?, ?, ?, 'open')
`,
[patientId, dbFilePath, total, req.session.user.id]
);
const invoiceId = result.insertId;
const invoice = {
number: invoiceNumber,
date: new Date().toLocaleDateString("de-DE"),
};
// 🔹 HTML rendern
const html = await ejs.renderFile(
path.join(__dirname, "../views/invoices/invoice.ejs"),
{
patient,
services,
total,
invoice,
doctor,
company,
logoBase64,
}
);
// 🔹 PDF erzeugen
const pdfBuffer = await htmlToPdf.generatePdf(
{ content: html },
{ format: "A4", printBackground: true }
);
// 💾 PDF speichern
fs.writeFileSync(absoluteFilePath, pdfBuffer);
// 🔗 Leistungen mit Rechnung verknüpfen
const [updateResult] = await connection.query(
`
UPDATE patient_services
SET invoice_id = ?
WHERE patient_id = ?
AND invoice_id IS NULL
`,
[invoiceId, patientId]
);
const [[cid]] = await connection.query("SELECT CONNECTION_ID() AS cid");
console.log("🔌 INVOICE CID:", cid.cid);
await connection.commit();
console.log("🔌 INVOICE CID:", cid.cid);
// 📤 PDF anzeigen
res.render("invoice_preview", {
pdfUrl: dbFilePath,
});
} catch (err) {
await connection.rollback();
console.error("❌ INVOICE ERROR:", err);
res.status(500).send(err.message || "Fehler beim Erstellen der Rechnung");
} finally {
connection.release();
}
}
module.exports = { createInvoicePdf };