import { getAusweisartFromId } from "#components/Ausweis/types.js"; import { adminMiddleware } from "#lib/middleware/authorization.js"; import { Aufnahme, BedarfsausweisWohnen, Benutzer, Bild, Enums, Objekt, prisma, Rechnung, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen, } from "#lib/server/prisma.js"; import { join } from "path"; import { APIError, defineApiRoute } from "astro-typesafe-api/server"; import { z } from "astro:content"; import { transport } from "#lib/mail.js"; import {Attachment} from "nodemailer/lib/mailer/index.js"; import { getAnsichtsausweis, getDatenblatt, getAushang } from "#lib/server/ausweis.js"; import { PutObjectCommand } from "@aws-sdk/client-s3"; import { s3Client } from "#lib/s3.js"; import { createInvoice, getLexOfficeRechnung, getLexOfficeVoucherNumber, } from "#lib/server/invoice.js"; import { tryCatch } from "#lib/tryCatch.js"; import SFTPClient from "ssh2-sftp-client"; import { getBedarfsausweisWohnenKomplett, getVerbrauchsausweisGewerbeKomplett, getVerbrauchsausweisWohnenKomplett, } from "#lib/server/db.js"; import { PDFDocument } from "pdf-lib"; export const GET = defineApiRoute({ input: z.object({ id_ausweis: z.string(), post: z .boolean() .describe("Ob der Ausweis auch per Post ausgestellt werden soll.") .optional() .default(false), }), output: z.void(), middleware: adminMiddleware, async fetch({ id_ausweis, post }, context) { const ausweisart = getAusweisartFromId(id_ausweis); let ausweis: | (( | VerbrauchsausweisGewerbe | VerbrauchsausweisWohnen | BedarfsausweisWohnen ) & { aufnahme: Aufnahme & { bilder: Bild[]; objekt: Objekt & { benutzer: Benutzer | null; }; }; rechnung: Rechnung; }) | null = null; if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) { ausweis = await getVerbrauchsausweisWohnenKomplett(id_ausweis); } else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) { ausweis = await getVerbrauchsausweisGewerbeKomplett(id_ausweis); } else if (ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) { ausweis = await getBedarfsausweisWohnenKomplett(id_ausweis); } if (!ausweis) { throw new APIError({ code: "BAD_REQUEST", message: "Ausweis existiert nicht.", }); } const rechnung = await prisma.rechnung.findFirst({ where: { OR: [ { bedarfsausweis_wohnen: { id: id_ausweis } }, { verbrauchsausweis_wohnen: { id: id_ausweis } }, { verbrauchsausweis_gewerbe: { id: id_ausweis } }, ], }, orderBy: { erstellt_am: "desc", }, include: { benutzer: true, }, }); if (!rechnung) { throw new APIError({ code: "BAD_REQUEST", message: "Die Rechnung wurde noch nicht erstellt, wir können nicht fortfahren.", }); } let voucherNumber: string = ""; if (!rechnung.lex_office_id) { const [result, error] = await tryCatch( createInvoice(ausweis, rechnung) ); if (error) { throw new APIError({ code: "BAD_REQUEST", message: "Die Rechnung konnte bei LexOffice nicht angelegt werden..", cause: error, }); } const { id, voucherNumber: lexOfficeVoucherNumber } = result; voucherNumber = lexOfficeVoucherNumber; await prisma.rechnung.update({ where: { id: rechnung.id, }, data: { lex_office_id: id, }, }); } else { voucherNumber = await getLexOfficeVoucherNumber(rechnung); } const pdfAusweis = await getAnsichtsausweis( ausweis, ausweis.aufnahme, ausweis.aufnahme.objekt, ausweis.aufnahme.bilder, ausweis.aufnahme.objekt.benutzer, false ); const pdfDatenblatt = await getDatenblatt( ausweis, ausweis.aufnahme, ausweis.aufnahme.objekt, ausweis.aufnahme.bilder, ausweis.aufnahme.objekt.benutzer, ausweis.rechnung ); const pdfAushang = await getAushang( ausweis, ausweis.aufnahme, ausweis.aufnahme.objekt, ausweis.aufnahme.bilder, ausweis.aufnahme.objekt.benutzer, false, ausweis.rechnung ); // TODO: Das ist immer noch scheiße, LexOffice ist doof // Hier müssen wir warten, damit wir sichergehen können, dass die Rechnung bei LexOffice existiert. setTimeout(async () => { const [pdfRechnung, pdfRechnungError] = await tryCatch( getLexOfficeRechnung(rechnung) ); if (pdfRechnungError) { throw new APIError({ code: "INTERNAL_SERVER_ERROR", message: "Rechnungs PDF konnte nicht generiert werden.", }); } if (!pdfAusweis) { throw new APIError({ code: "INTERNAL_SERVER_ERROR", message: "Ausweis PDF konnte nicht generiert werden.", }); } if (!pdfDatenblatt) { throw new APIError({ code: "INTERNAL_SERVER_ERROR", message: "Datenblatt PDF konnte nicht generiert werden.", }); } const ausweisCommand = new PutObjectCommand({ Bucket: "ibc-pdfs", Key: `ID_${ausweis.id}_Energieausweis.pdf`, Body: pdfAusweis, ACL: "private", }); await s3Client.send(ausweisCommand); const datenblattCommand = new PutObjectCommand({ Bucket: "ibc-pdfs", Key: `ID_${ausweis.id}_Datenblatt.pdf`, Body: pdfDatenblatt, ACL: "private", }); await s3Client.send(datenblattCommand); const rechnungsCommand = new PutObjectCommand({ Bucket: "ibc-pdfs", Key: `ID_${ausweis.id}_Rechnung.pdf`, Body: Buffer.from(pdfRechnung), ACL: "private", }); await s3Client.send(rechnungsCommand); if (pdfAushang){ const aushangCommand = new PutObjectCommand({ Bucket: "ibc-pdfs", Key: `ID_${ausweis.id}_Aushang.pdf`, Body: pdfDatenblatt, ACL: "private", }); await s3Client.send(rechnungsCommand); } // Falls Postversand angefragt wurde müssen wir die Dateien auf den Postserver hochladen if (post) { const dateiNameDruck = `1011000000000-AW_ID_${ausweis.id}.pdf`; const outputPdf = await PDFDocument.create(); async function appendPdf(buffer: Uint8Array) { const doc = await PDFDocument.load(buffer); const copiedPages = await outputPdf.copyPages( doc, doc.getPageIndices() ); for (const page of copiedPages) { outputPdf.addPage(page); } } await appendPdf(pdfDatenblatt); await appendPdf(pdfAusweis); if (pdfAushang){ await appendPdf(pdfAushang); } const pdfBytes = await outputPdf.save(); const pdfCommand = new PutObjectCommand({ Bucket: "ibc-pdfs", Key: dateiNameDruck, Body: pdfBytes, ACL: "private", }); await s3Client.send(pdfCommand); const sftp = new SFTPClient(); try { await sftp.connect({ host: "api.ppost.de", username: "jens.cornelsen@ib-cornelsen.de", password: "ANTQesWYjd", }); const cwd = await sftp.cwd(); await sftp.put( Buffer.from(pdfBytes), join(cwd, "upload/api", dateiNameDruck) ); } catch (err) { console.error("SFTP Upload failed:", err); throw new APIError({ code: "INTERNAL_SERVER_ERROR", message: "Login zum Postversand Server war nicht erfolgreich.", }); } finally { sftp.end(); } } let html: string; if (rechnung.status === Enums.Rechnungsstatus.paid) { html = `

Sehr geehrte/r ${rechnung.empfaenger},

im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${ post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : "" } Den Rechnungsbetrag haben Sie bereits bezahlt. Vielen Dank.

Mit freundlichen Grüßen,
Dipl.-Ing. Jens Cornelsen

IB Cornelsen
Katendeich 5A
21035 Hamburg
www.online-energieausweis.org

fon 040 · 209339850
fax 040 · 209339859

`; } else { html = `

Sehr geehrte/r ${rechnung.empfaenger},

im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${ post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : "" } Bitte beachten Sie unsere neue Bankverbindung. Bitte geben Sie als Verwendungszweck die Rechnungsnummer an (siehe unten). Vielen Dank.


Kreditinstitut:\t Volksbank eG
Empfänger:\t IB Cornelsen
IBAN:\t DE13 2519 3331 7209 0731 00
BIC:\t GENODEF1PAT
Betrag:\t ${rechnung.betrag}€
Verwendungszweck:\t ${voucherNumber}


Mit freundlichen Grüßen,
Dipl.-Ing. Jens Cornelsen

IB Cornelsen
Katendeich 5A
21035 Hamburg
www.online-energieausweis.org

fon 040 · 209339850
fax 040 · 209339859

`; } const attachments: Attachment[] = [ { filename: `ID_${ausweis.id}_Energieausweis.pdf`, encoding: "binary", content: Buffer.from(pdfAusweis), contentType: "application/pdf", }, { filename: `ID_${ausweis.id}_Datenblatt.pdf`, encoding: "binary", content: Buffer.from(pdfDatenblatt), contentType: "application/pdf", }, { filename: `ID_${ausweis.id}_Rechnung.pdf`, encoding: "binary", content: Buffer.from(pdfRechnung), contentType: "application/pdf", } ]; if (pdfAushang) { attachments.push({ filename: `ID_${ausweis.id}_Aushang.pdf`, encoding: "binary", content: Buffer.from(pdfAushang), contentType: "application/pdf", }); } await transport.sendMail({ from: `"IBCornelsen" `, to: rechnung.email || rechnung.benutzer.email, bcc: "info@online-energieausweis.org", subject: `Ihr Originalausweis vom Ingenieurbüro Cornelsen (ID: ${ausweis.id})`, html, attachments }); if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) { await prisma.verbrauchsausweisWohnen.update({ where: { id: ausweis.id, }, data: { ausgestellt: true, }, }); } else if ( ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe ) { await prisma.verbrauchsausweisGewerbe.update({ where: { id: ausweis.id, }, data: { ausgestellt: true, }, }); } else if (ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) { await prisma.bedarfsausweisWohnen.update({ where: { id: ausweis.id, }, data: { ausgestellt: true, }, }); } }, 3000); }, });