From e9f7a2bb2f4003cff17a2b9c898b37cb8c5cb46f Mon Sep 17 00:00:00 2001 From: Moritz Utcke Date: Mon, 10 Feb 2025 09:47:32 +0700 Subject: [PATCH] Ansichtsausweis --- src/astro-typesafe-api-caller.ts | 2 +- src/components/AnsichtsausweisButton.svelte | 33 ++++++-- src/components/Ausweis/Ausweisart.svelte | 2 +- src/lib/XML/getEmpfehlungen.ts | 48 ++++++----- src/lib/pdf/pdfVerbrauchsausweisWohnen.ts | 84 ++++++++++++++++++- src/pages/dev/pdf-viewer.astro | 2 +- ...sichtsausweis.astro => ansichtsausweis.ts} | 77 +++++++++++------ 7 files changed, 186 insertions(+), 62 deletions(-) rename src/pages/pdf/{ansichtsausweis.astro => ansichtsausweis.ts} (53%) diff --git a/src/astro-typesafe-api-caller.ts b/src/astro-typesafe-api-caller.ts index 7b0ed16d..4a683d52 100644 --- a/src/astro-typesafe-api-caller.ts +++ b/src/astro-typesafe-api-caller.ts @@ -5,8 +5,8 @@ export const createCaller = createCallerFactory({ "postleitzahlen": await import("../src/pages/api/postleitzahlen.ts"), "aufnahme/[uid]": await import("../src/pages/api/aufnahme/[uid].ts"), "aufnahme": await import("../src/pages/api/aufnahme/index.ts"), - "bilder/[uid]": await import("../src/pages/api/bilder/[uid].ts"), "bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"), + "bilder/[uid]": await import("../src/pages/api/bilder/[uid].ts"), "auth/access-token": await import("../src/pages/api/auth/access-token.ts"), "auth/forgot-password": await import("../src/pages/api/auth/forgot-password.ts"), "auth/refresh-token": await import("../src/pages/api/auth/refresh-token.ts"), diff --git a/src/components/AnsichtsausweisButton.svelte b/src/components/AnsichtsausweisButton.svelte index df939d4b..a111aa9a 100644 --- a/src/components/AnsichtsausweisButton.svelte +++ b/src/components/AnsichtsausweisButton.svelte @@ -7,13 +7,34 @@ export let aufnahme: AufnahmeClient; export let bilder: UploadedGebaeudeBild[]; - $: base64Ausweis = Buffer.from(JSON.stringify(ausweis), "utf-8").toString("base64") - $: base64Aufnahme = Buffer.from(JSON.stringify(aufnahme), "utf-8").toString("base64") - $: base64Objekt = Buffer.from(JSON.stringify(objekt), "utf-8").toString("base64") - $: base64Bilder = Buffer.from(JSON.stringify(bilder), "utf-8").toString("base64") + function openWindowWithPost(url: string, data: Record) { + var form = document.createElement("form"); + form.target = "_blank"; + form.method = "POST"; + form.action = url; + form.style.display = "none"; + + for (var key in data) { + var input = document.createElement("input"); + input.type = "hidden"; + input.name = key; + input.value = data[key]; + form.appendChild(input); + } + document.body.appendChild(form); + form.submit(); + document.body.removeChild(form); +} - + diff --git a/src/components/Ausweis/Ausweisart.svelte b/src/components/Ausweis/Ausweisart.svelte index 25092d90..986be682 100644 --- a/src/components/Ausweis/Ausweisart.svelte +++ b/src/components/Ausweis/Ausweisart.svelte @@ -70,7 +70,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8 required bind:value={aufnahme.gebaeudetyp} > - + {#if ausweisart=="VerbrauchsausweisWohnen"} diff --git a/src/lib/XML/getEmpfehlungen.ts b/src/lib/XML/getEmpfehlungen.ts index 658b2f91..5bb3e34b 100644 --- a/src/lib/XML/getEmpfehlungen.ts +++ b/src/lib/XML/getEmpfehlungen.ts @@ -1,34 +1,36 @@ -import { VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js"; +import { AufnahmeClient, ObjektClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js"; import { Enums } from "@ibcornelsen/database/client"; import moment from "moment"; -export function getEmpfehlungen(ausweis: VerbrauchsausweisWohnenClient): { +export function getEmpfehlungen(ausweis: VerbrauchsausweisWohnenClient, aufnahme: AufnahmeClient, objekt: ObjektClient): { title: string, description: string, - anlagenteil: string + anlagenteil: string, + amortisationszeit: string, + kosten: string }[] { - let Warmwasserrohre_gedaemmt = ausweis.aufnahme.warmwasser_rohre_gedaemmt; - let Heizungsrohre_gedaemmt = ausweis.aufnahme.heizungsrohre_gedaemmt; - let Waermepumpe = ausweis.aufnahme.waermepumpe; - let Kellerwand_gedaemmt = ausweis.aufnahme.keller_wand_gedaemmt; - let Keller = ausweis.aufnahme.keller; - let Kellerdecke_Kalraeume_gedaemmt = ausweis.aufnahme.keller_decke_gedaemmt; - let Brennwertkessel = ausweis.aufnahme.brennwert_kessel; - let baujahr_anlagesanlage = ausweis.aufnahme.baujahr_heizung[0]; - let Zentralheizung = ausweis.aufnahme.zentralheizung; - let photovoltaik = ausweis.aufnahme.photovoltaik; - let Brennstoff = ausweis.aufnahme.brennstoff_1; - let Aussenwand_gedaemmt = ausweis.aufnahme.aussenwand_gedaemmt; - let Dachgeschoss = ausweis.aufnahme.dachgeschoss; - let Dachgeschoss_gedaemmt = ausweis.aufnahme.dachgeschoss_gedaemmt; - let Oberste_Geschossdecke_gedaemmt = ausweis.aufnahme.oberste_geschossdecke_gedaemmt; - let Einfachglas = ausweis.aufnahme.einfach_verglasung; - let Doppelfenster = ausweis.aufnahme.doppel_verglasung; - let Fenster_teilw_undicht = ausweis.aufnahme.fenster_teilweise_undicht; + let Warmwasserrohre_gedaemmt = aufnahme.warmwasser_rohre_gedaemmt; + let Heizungsrohre_gedaemmt = aufnahme.heizungsrohre_gedaemmt; + let Waermepumpe = aufnahme.waermepumpe; + let Kellerwand_gedaemmt = aufnahme.keller_wand_gedaemmt; + let Keller = aufnahme.keller; + let Kellerdecke_Kalraeume_gedaemmt = aufnahme.keller_decke_gedaemmt; + let Brennwertkessel = aufnahme.brennwert_kessel; + let baujahr_anlagesanlage = aufnahme.baujahr_heizung[0]; + let Zentralheizung = aufnahme.zentralheizung; + let photovoltaik = aufnahme.photovoltaik; + let Brennstoff = aufnahme.brennstoff_1; + let Aussenwand_gedaemmt = aufnahme.aussenwand_gedaemmt; + let Dachgeschoss = aufnahme.dachgeschoss; + let Dachgeschoss_gedaemmt = aufnahme.dachgeschoss_gedaemmt; + let Oberste_Geschossdecke_gedaemmt = aufnahme.oberste_geschossdecke_gedaemmt; + let Einfachglas = aufnahme.einfach_verglasung; + let Doppelfenster = aufnahme.doppel_verglasung; + let Fenster_teilw_undicht = aufnahme.fenster_teilweise_undicht; let empfehlungen = []; - if (ausweis.aufnahme.einfach_verglasung || (Doppelfenster && Fenster_teilw_undicht)) { + if (aufnahme.einfach_verglasung || (Doppelfenster && Fenster_teilw_undicht)) { empfehlungen.push({ "title" : "Erneuerung der Fenster", "description" : "Alte und undichte Fenster mit Wärmeschutzfenstern auswechseln.", @@ -38,7 +40,7 @@ export function getEmpfehlungen(ausweis: VerbrauchsausweisWohnenClient): { }); } - if (ausweis.aufnahme.dachgeschoss == Enums.Heizungsstatus.UNBEHEIZT && !Oberste_Geschossdecke_gedaemmt) { + if (aufnahme.dachgeschoss == Enums.Heizungsstatus.UNBEHEIZT && !Oberste_Geschossdecke_gedaemmt) { empfehlungen.push({ "title" : "Zusätzliche Dämmung des Fußbodens des kalten Dachraumes", "description" : "Beim Einbringen sollten mindestens 16cm Dämmstoff verarbeitet werden. Das Einsparpotenzial ist für jeden zusätzlichen cm Dämmung sehr hoch.", diff --git a/src/lib/pdf/pdfVerbrauchsausweisWohnen.ts b/src/lib/pdf/pdfVerbrauchsausweisWohnen.ts index bf42987a..5b7d8240 100644 --- a/src/lib/pdf/pdfVerbrauchsausweisWohnen.ts +++ b/src/lib/pdf/pdfVerbrauchsausweisWohnen.ts @@ -1,9 +1,10 @@ import { AufnahmeClient, BenutzerClient, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js"; import { endEnergieVerbrauchVerbrauchsausweis_2016 } from "#lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016.js"; +import { getEmpfehlungen } from "#lib/XML/getEmpfehlungen.js"; import { Enums } from "@ibcornelsen/database/server"; import * as fs from "fs" import moment from "moment"; -import { PDFDocument, PDFName, PDFNumber, PDFPage, StandardFonts, TextAlignment } from "pdf-lib"; +import { PDFDocument, PDFFont, PDFName, PDFNumber, PDFPage, StandardFonts, TextAlignment } from "pdf-lib"; /* -------------------------------- Pdf Tools ------------------------------- */ @@ -15,6 +16,7 @@ export async function pdfVerbrauchsausweisWohnen(ausweis: VerbrauchsausweisWohne // const template = VerbrauchsausweisWohnen2016Template as Template; const berechnungen = await endEnergieVerbrauchVerbrauchsausweis_2016(ausweis, aufnahme, objekt); + const empfehlungen = getEmpfehlungen(ausweis, aufnahme, objekt) const height = pages[0].getHeight() @@ -41,12 +43,12 @@ export async function pdfVerbrauchsausweisWohnen(ausweis: VerbrauchsausweisWohne } } - const gebaeudetyp = fillFormField("gebaeudetyp", aufnahme.gebaeudetyp) + const gebaeudetyp = fillFormField("gebaeudetyp", aufnahme.gebaeudetyp || "") const adresse = fillFormField("adresse", objekt.adresse) - const gebaeudeteil = fillFormField("gebaeudeteil", aufnahme.gebaeudeteil) + const gebaeudeteil = fillFormField("gebaeudeteil", aufnahme.gebaeudeteil || "") const baujahr_gebaeude = fillFormField("baujahr_gebaeude", aufnahme.baujahr_gebaeude?.toString()) @@ -291,6 +293,82 @@ export async function pdfVerbrauchsausweisWohnen(ausweis: VerbrauchsausweisWohne ).toString(), berechnungen?.klimafaktoren[0].klimafaktor.toString() ); + + /* -------------------------------- Seite 4 -------------------------------- */ + + const splitToSize = (text: string, size: number, font: PDFFont, fontSize: number) => { + const lines = [] + + let currentLine = "" + for (const char of text) { + if (font.widthOfTextAtSize(currentLine + char, fontSize) <= size) { + currentLine += char; + } else { + lines.push(currentLine) + currentLine = char; + } + } + + lines.push(currentLine) + + return lines.join("\n") + } + + const addEmpfehlungenGenerator = () => { + let i = 0; + let yOffset = 43; + const initialHeight = 568 + const initialXOffset = 36; + return (bauteil?: string, beschreibung?: string, alsEinzelmassnahme?: boolean, amortisationszeit?: string, kosten?: string) => { + pages[3].drawText((i + 1).toString(), { + x: initialXOffset, + y: initialHeight - (i * yOffset), + size: 8, + font + }) + + pages[3].drawText(splitToSize(bauteil || "", 70, font, 8), { + x: initialXOffset + 25, + y: initialHeight - (i * yOffset), + size: 8, + font, + lineHeight: 10 + }) + + pages[3].drawText(splitToSize(beschreibung || "", 230, font, 8), { + x: initialXOffset + 98, + y: initialHeight - (i * yOffset), + size: 8, + font, + lineHeight: 10 + }) + + + + pages[3].drawText(amortisationszeit || "", { + x: initialXOffset + 403, + y: initialHeight - (i * yOffset), + size: 8, + font + }) + + pages[3].drawText(kosten || "", { + x: initialXOffset + 451, + y: initialHeight - (i * yOffset), + size: 8, + font + }) + i++; + } + } + + const addEmpfehlung = addEmpfehlungenGenerator() + + for (const empfehlung of empfehlungen) { + addEmpfehlung(empfehlung.anlagenteil, empfehlung.description, true, empfehlung.amortisationszeit, empfehlung.kosten) + } + + // pdf.getForm().flatten() diff --git a/src/pages/dev/pdf-viewer.astro b/src/pages/dev/pdf-viewer.astro index aa439e6e..1ba3ba6e 100644 --- a/src/pages/dev/pdf-viewer.astro +++ b/src/pages/dev/pdf-viewer.astro @@ -5,5 +5,5 @@ import BlankLayout from "#layouts/BlankLayout.astro"; --- - + \ No newline at end of file diff --git a/src/pages/pdf/ansichtsausweis.astro b/src/pages/pdf/ansichtsausweis.ts similarity index 53% rename from src/pages/pdf/ansichtsausweis.astro rename to src/pages/pdf/ansichtsausweis.ts index 979c016b..dc5cdc7e 100644 --- a/src/pages/pdf/ansichtsausweis.astro +++ b/src/pages/pdf/ansichtsausweis.ts @@ -1,35 +1,27 @@ ---- import { AufnahmeClient, BenutzerClient, getAusweisartFromUUID, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types"; -import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants"; -import { pdfVerbrauchsausweisWohnen } from "#lib/pdf/pdfVerbrauchsausweisWohnen"; +import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js"; +import { pdfVerbrauchsausweisWohnen } from "#lib/pdf/pdfVerbrauchsausweisWohnen.js"; import { Enums } from "@ibcornelsen/database/client"; -import { createCaller } from "src/astro-typesafe-api-caller"; +import { APIRoute } from "astro"; +import { createCaller } from "src/astro-typesafe-api-caller.js"; -const base64Ausweis = Astro.url.searchParams.get("ausweis"); -const base64Aufnahme = Astro.url.searchParams.get("aufnahme"); -const base64Objekt = Astro.url.searchParams.get("objekt"); -const base64Bilder = Astro.url.searchParams.get("bilder"); -let ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | null = null; -let aufnahme: AufnahmeClient = {} as AufnahmeClient; -let objekt: ObjektClient = {} as ObjektClient; -let user: BenutzerClient = {} as BenutzerClient; -let bilder: UploadedGebaeudeBild[] = [] -if (base64Ausweis && base64Aufnahme && base64Objekt && base64Bilder) { - ausweis = JSON.parse(Buffer.from(base64Ausweis, "base64").toString("utf-8")) as VerbrauchsausweisWohnenClient; - objekt = JSON.parse(Buffer.from(base64Objekt, "base64").toString("utf-8")) as ObjektClient; - aufnahme = JSON.parse(Buffer.from(base64Aufnahme, "base64").toString("utf-8")) as AufnahmeClient; - bilder = JSON.parse(Buffer.from(base64Bilder, "base64").toString("utf-8")) as UploadedGebaeudeBild[]; -} else { +export const GET: APIRoute = async (Astro) => { const uidAusweis = Astro.url.searchParams.get("uid"); if (!uidAusweis) { - return Astro.redirect("/404"); + return Astro.redirect("/404") } const ausweisart = getAusweisartFromUUID(uidAusweis) const caller = createCaller(Astro); + let ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | null = null; + let aufnahme: AufnahmeClient = {} as AufnahmeClient; + let objekt: ObjektClient = {} as ObjektClient; + let user: BenutzerClient = {} as BenutzerClient; + let bilder: UploadedGebaeudeBild[] = [] + if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) { ausweis = await caller["verbrauchsausweis-wohnen"]._uid.GET.fetch(undefined, { params: { @@ -78,13 +70,44 @@ if (base64Ausweis && base64Aufnahme && base64Objekt && base64Bilder) { Authorization: `Bearer ${Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value}` } }); + + const pdf = await pdfVerbrauchsausweisWohnen(ausweis, aufnahme, objekt, bilder, user); + + return new Response(pdf, { + headers: { + "Content-Type": "application/pdf", + }, + }); } -const pdf = await pdfVerbrauchsausweisWohnen(ausweis, aufnahme, objekt, bilder, user); +export const POST: APIRoute = async (Astro) => { + const body = await Astro.request.text(); + const params = new URLSearchParams(body); -return new Response(pdf, { - headers: { - "Content-Type": "application/pdf", - }, -}); ---- + const caller = createCaller(Astro); + + const ausweis = JSON.parse(params.get("ausweis")); + const aufnahme = JSON.parse(params.get("aufnahme")); + const objekt = JSON.parse(params.get("objekt")); + const bilder = JSON.parse(params.get("bilder")); + + let user: BenutzerClient = {}; + + try { + user = await caller.user.self.GET.fetch(undefined, { + headers: { + Authorization: `Bearer ${Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value}` + } + }); + } catch (e) { + + } + + const pdf = await pdfVerbrauchsausweisWohnen(ausweis, aufnahme, objekt, bilder, user); + + return new Response(pdf, { + headers: { + "Content-Type": "application/pdf", + }, + }); +} \ No newline at end of file