Ansichtsausweis

This commit is contained in:
Moritz Utcke
2025-01-02 21:08:18 +07:00
parent d5578feb46
commit 9e7987758b
11 changed files with 575 additions and 322 deletions

View File

@@ -26,7 +26,7 @@
"@ibcornelsen/ui": "^0.0.2", "@ibcornelsen/ui": "^0.0.2",
"@mollie/api-client": "^3.7.0", "@mollie/api-client": "^3.7.0",
"@pdfme/common": "^5.1.7", "@pdfme/common": "^5.1.7",
"@pdfme/generator": "^5.1.7", "@pdfme/generator": "^5.2.11",
"@pdfme/ui": "^5.1.7", "@pdfme/ui": "^5.1.7",
"@trpc/client": "^10.45.2", "@trpc/client": "^10.45.2",
"@trpc/server": "^10.45.2", "@trpc/server": "^10.45.2",
@@ -44,10 +44,11 @@
"i18next-fs-backend": "^2.3.2", "i18next-fs-backend": "^2.3.2",
"i18next-http-backend": "^2.6.2", "i18next-http-backend": "^2.6.2",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"js-interpolate": "^1.1.0", "js-interpolate": "^1.3.1",
"katex": "^0.16.11", "katex": "^0.16.11",
"moment": "^2.30.1", "moment": "^2.30.1",
"moment-timezone": "^0.5.46", "moment-timezone": "^0.5.46",
"pdf-lib": "^1.17.1",
"postcss-nested": "^7.0.2", "postcss-nested": "^7.0.2",
"radix-svelte-icons": "^1.0.0", "radix-svelte-icons": "^1.0.0",
"sass": "^1.80.6", "sass": "^1.80.6",

Binary file not shown.

After

Width:  |  Height:  |  Size: 777 B

View File

@@ -0,0 +1,10 @@
<svg width="70" height="96" viewBox="0 0 70 96" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_0_3)">
<path d="M0 62.6526L12 56.5895V96H35H57.5V56.5895L70 62.6526L35 0L0 62.6526Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_0_3">
<rect width="70" height="96" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

View File

@@ -0,0 +1,10 @@
<svg width="70" height="96" viewBox="0 0 70 96" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_0_3)">
<path d="M0 33.3474L12 39.4105V0H35H57.5V39.4105L70 33.3474L35 96L0 33.3474Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_0_3">
<rect width="70" height="96" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 330 B

View File

@@ -1,7 +1,6 @@
--- ---
import i18next from "i18next"; import i18next from "i18next";
import "../style/global.scss";
import { NotificationWrapper } from "@ibcornelsen/ui"; import { NotificationWrapper } from "@ibcornelsen/ui";
export interface Props { export interface Props {

View File

@@ -12,7 +12,7 @@ import { linearInterpolation } from "js-interpolate"
*/ */
export function berechnungTrinkwarmwasserWaermebedarf(ausweis: BedarfsausweisWohnenClient, gebaeude_aufnahme: GebaeudeAufnahmeClient) { export function berechnungTrinkwarmwasserWaermebedarf(ausweis: BedarfsausweisWohnenClient, gebaeude_aufnahme: GebaeudeAufnahmeClient) {
// A_NGF // A_NGF
const bezugsflaeche = gebaeude_aufnahme.flaeche ?? 0; const bezugsflaeche = gebaeude_aufnahme.nutzflaeche ?? 0;
const einheiten = gebaeude_aufnahme.einheiten ?? 1; const einheiten = gebaeude_aufnahme.einheiten ?? 1;

View File

@@ -1,18 +1,19 @@
// Funktion zur Berechnung des monatlichen Belastungsgrades aus Tabelle 9 EFH (Januar, Zeitkonstante 90) // Funktion zur Berechnung des monatlichen Belastungsgrades aus Tabelle 9 EFH (Januar, Zeitkonstante 90)
import { nevillePolynomialInterpolation, lagrangeInterpolation } from "js-interpolate";
function monatlicherBelastungsGradT90( function monatlicherBelastungsGradT90(
wertZwei: number, wertZwei: number,
wertEins: number, wertEins: number,
maxHeizlast: number, maxHeizlast: number,
maxHeizlastEins: number, maxHeizlastEins: number,
maxHeizlastZwei: number maxHeizlastZwei: number
): number { ): number {
return ( return (
wertZwei - wertZwei -
((wertZwei - wertEins) * (maxHeizlast - maxHeizlastEins)) / ((wertZwei - wertEins) * (maxHeizlast - maxHeizlastEins)) /
(maxHeizlastZwei - maxHeizlastEins) (maxHeizlastZwei - maxHeizlastEins)
); );
} }
// Beispielwerte // Beispielwerte
@@ -24,94 +25,157 @@ const maxHeizlastZwei = 50;
// Berechnung // Berechnung
const monatlicherBelastungsgradT90 = monatlicherBelastungsGradT90( const monatlicherBelastungsgradT90 = monatlicherBelastungsGradT90(
wertZwei, wertZwei,
wertEins, wertEins,
maxHeizlast, maxHeizlast,
maxHeizlastEins, maxHeizlastEins,
maxHeizlastZwei maxHeizlastZwei
); );
console.log("Monatlicher Belastungsgrad T90:", monatlicherBelastungsgradT90); console.log("Monatlicher Belastungsgrad T90:", monatlicherBelastungsgradT90);
// Funktion zur Berechnung des monatlichen Belastungsgrades aus Tabelle 9 EFH (Zeitkonstante 90,130) // Funktion zur Berechnung des monatlichen Belastungsgrades aus Tabelle 9 EFH (Zeitkonstante 90,130)
type MonthData = { type MonthData = {
month: string; month: string;
values: number[]; values: number[];
}; };
const datasetZeitkonstante90: MonthData[] = [ const datasetZeitkonstante90: MonthData[] = [
{ month: "Januar", values: [0.562, 0.559, 0.555, 0.548, 0.541, 0.538, 0.536, 0.535] }, {
{ month: "Februar", values: [0.536, 0.532, 0.529, 0.522, 0.515, 0.512, 0.510, 0.510] }, month: "Januar",
{ month: "März", values: [0.453, 0.450, 0.447, 0.441, 0.436, 0.433, 0.431, 0.431] }, values: [0.562, 0.559, 0.555, 0.548, 0.541, 0.538, 0.536, 0.535, 0.535],
{ month: "April", values: [0.320, 0.318, 0.316, 0.311, 0.307, 0.306, 0.305, 0.304] }, },
{ month: "Mai", values: [0.175, 0.173, 0.172, 0.170, 0.168, 0.167, 0.166, 0.166] }, {
{ month: "Juni", values: [0.098, 0.097, 0.096, 0.095, 0.094, 0.093, 0.093, 0.093] }, month: "Februar",
{ month: "Juli", values: [0.030, 0.029, 0.029, 0.029, 0.028, 0.028, 0.028, 0.028] }, values: [0.536, 0.532, 0.529, 0.522, 0.515, 0.512, 0.51, 0.51, 0.509],
{ month: "August", values: [0.041, 0.041, 0.041, 0.040, 0.040, 0.040, 0.039, 0.039] }, },
{ month: "September", values: [0.169, 0.168, 0.167, 0.164, 0.162, 0.161, 0.161, 0.161] }, {
{ month: "Oktober", values: [0.311, 0.309, 0.307, 0.303, 0.299, 0.297, 0.296, 0.296] }, month: "März",
{ month: "November", values: [0.471, 0.467, 0.465, 0.459, 0.453, 0.450, 0.448, 0.448] }, values: [0.453, 0.45, 0.447, 0.441, 0.436, 0.433, 0.431, 0.431, 0.43],
{ month: "Dezember", values: [0.565, 0.562, 0.558, 0.551, 0.544, 0.540, 0.539, 0.538] } },
{
month: "April",
values: [0.32, 0.318, 0.316, 0.311, 0.307, 0.306, 0.305, 0.304, 0.304],
},
{
month: "Mai",
values: [0.175, 0.173, 0.172, 0.17, 0.168, 0.167, 0.166, 0.166, 0.166],
},
{
month: "Juni",
values: [0.098, 0.097, 0.096, 0.095, 0.094, 0.093, 0.093, 0.093, 0.093],
},
{
month: "Juli",
values: [0.03, 0.029, 0.029, 0.029, 0.028, 0.028, 0.028, 0.028, 0.028],
},
{
month: "August",
values: [0.041, 0.041, 0.041, 0.04, 0.04, 0.04, 0.039, 0.039, 0.039],
},
{
month: "September",
values: [0.169, 0.168, 0.167, 0.164, 0.162, 0.161, 0.161, 0.161, 0.16],
},
{
month: "Oktober",
values: [0.311, 0.309, 0.307, 0.303, 0.299, 0.297, 0.296, 0.296, 0.295],
},
{
month: "November",
values: [0.471, 0.467, 0.465, 0.459, 0.453, 0.45, 0.448, 0.448, 0.447],
},
{
month: "Dezember",
values: [0.565, 0.562, 0.558, 0.551, 0.544, 0.54, 0.539, 0.538, 0.537],
},
]; ];
const datasetZeitkonstante130: MonthData[] = [ const datasetZeitkonstante130: MonthData[] = [
{ month: "Januar", values: [0.567, 0.563, 0.560, 0.553, 0.545, 0.542, 0.540, 0.539, 0.539] }, {
{ month: "Februar", values: [0.540, 0.537, 0.533, 0.526, 0.520, 0.516, 0.515, 0.514, 0.513] }, month: "Januar",
{ month: "März", values: [0.457, 0.454, 0.451, 0.445, 0.439, 0.435, 0.435, 0.434, 0.434] }, values: [0.567, 0.563, 0.56, 0.553, 0.545, 0.542, 0.54, 0.539, 0.539],
{ month: "April", values: [0.322, 0.320, 0.318, 0.314, 0.310, 0.308, 0.307, 0.307, 0.306] }, },
{ month: "Mai", values: [0.176, 0.175, 0.174, 0.172, 0.169, 0.168, 0.168, 0.168, 0.167] }, {
{ month: "Juni", values: [0.098, 0.097, 0.096, 0.095, 0.094, 0.094, 0.094, 0.094, 0.094] }, month: "Februar",
{ month: "Juli", values: [0.030, 0.030, 0.029, 0.029, 0.028, 0.028, 0.028, 0.028, 0.028] }, values: [0.54, 0.537, 0.533, 0.526, 0.52, 0.516, 0.515, 0.514, 0.513],
{ month: "August", values: [0.042, 0.041, 0.041, 0.040, 0.040, 0.040, 0.040, 0.040, 0.040] }, },
{ month: "September", values: [0.170, 0.169, 0.168, 0.166, 0.164, 0.163, 0.162, 0.162, 0.162] }, {
{ month: "Oktober", values: [0.313, 0.311, 0.309, 0.305, 0.301, 0.300, 0.299, 0.298, 0.298] }, month: "März",
{ month: "November", values: [0.474, 0.471, 0.469, 0.462, 0.456, 0.454, 0.453, 0.451, 0.451] }, values: [0.457, 0.454, 0.451, 0.445, 0.439, 0.435, 0.435, 0.434, 0.434],
{ month: "Dezember", values: [0.570, 0.566, 0.563, 0.555, 0.548, 0.545, 0.543, 0.542, 0.542] }, },
{
month: "April",
values: [0.322, 0.32, 0.318, 0.314, 0.31, 0.308, 0.307, 0.307, 0.306],
},
{
month: "Mai",
values: [0.176, 0.175, 0.174, 0.172, 0.169, 0.168, 0.168, 0.168, 0.167],
},
{
month: "Juni",
values: [0.098, 0.097, 0.096, 0.095, 0.094, 0.094, 0.094, 0.094, 0.094],
},
{
month: "Juli",
values: [0.03, 0.03, 0.029, 0.029, 0.028, 0.028, 0.028, 0.028, 0.028],
},
{
month: "August",
values: [0.042, 0.041, 0.041, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04],
},
{
month: "September",
values: [0.17, 0.169, 0.168, 0.166, 0.164, 0.163, 0.162, 0.162, 0.162],
},
{
month: "Oktober",
values: [0.313, 0.311, 0.309, 0.305, 0.301, 0.3, 0.299, 0.298, 0.298],
},
{
month: "November",
values: [0.474, 0.471, 0.469, 0.462, 0.456, 0.454, 0.453, 0.451, 0.451],
},
{
month: "Dezember",
values: [0.57, 0.566, 0.563, 0.555, 0.548, 0.545, 0.543, 0.542, 0.542],
},
]; ];
// Für "Ohne Teilbeheizung" habe ich hier einfach 0 eingesetzt: // Für "Ohne Teilbeheizung" habe ich hier einfach 0 eingesetzt:
const HeizLast = [0, 5, 10, 25, 50, 75, 100, 125, 150]; const HeizLast = [0, 5, 10, 25, 50, 75, 100, 125, 150];
/** const interpolatedValuesZeitkonstante90 = datasetZeitkonstante90.map((data) => {
* Linearly interpolates a value for the given x. const interpolate = nevillePolynomialInterpolation(
* @param x The heating load value (e.g., 82). data.values.map((value, i) => ({ x: HeizLast[i], y: value })),
* @param values The array of y-values corresponding to heating loads. data.values.length
* @param loads The array of heating load values (x-axis). );
* @returns The interpolated value.
*/
function interpolate(x: number, values: number[], loads: number[]): number {
if (x <= loads[0]) return values[0];
if (x >= loads[loads.length - 1]) return values[values.length - 1];
for (let i = 0; i < loads.length - 1; i++) { return {
if (x >= loads[i] && x <= loads[i + 1]) { month: data.month,
const x1 = loads[i]; interpolatedValue: interpolate(125),
const x2 = loads[i + 1]; };
const y1 = values[i]; });
const y2 = values[i + 1];
return y1 + ((x - x1) * (y2 - y1)) / (x2 - x1); console.log(
} datasetZeitkonstante90[0].values.map((value, i) => ({
} x: HeizLast[i],
throw new Error("Interpolation error: Value is out of bounds."); y: value,
} }))
);
// Example: Interpolate for x = 82 und Zeitkonstante zwischen 90-130 const interpolatedValuesZeitkonstante130 = datasetZeitkonstante130.map((data) => {
const x = 82; const interpolate = nevillePolynomialInterpolation(
const interpolatedValuesZeitkonstante90 = datasetZeitkonstante90.map(data => ({ data.values.map((value, i) => ({ x: HeizLast[i], y: value })),
month: data.month, data.values.length
interpolatedValue: interpolate(x, data.values, HeizLast) );
}));
// Example: Interpolate for x = 82 und Zeitkonstante >= 130 return {
const x = 82; month: data.month,
const interpolatedValuesZeitkonstante130 = datasetZeitkonstante130.map(data => ({ interpolatedValue: interpolate(125),
month: data.month, };
interpolatedValue: interpolate(x, data.values, HeizLast) });
}));
console.log(interpolatedValuesZeitkonstante90); console.log(interpolatedValuesZeitkonstante90);
console.log(interpolatedValuesZeitkonstante130); // console.log(interpolatedValuesZeitkonstante130);

View File

@@ -1,256 +1,416 @@
import { VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js"; import { VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js";
import { generate } from "@pdfme/generator"; import { endEnergieVerbrauchVerbrauchsausweis_2016 } from "#lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016.js";
import VerbrauchsausweisWohnen2016Template from "../../lib/pdf/templates/GEG24_Verbrauchsausweis.json"; import { Enums } from "@ibcornelsen/database/server";
import { convertAusweisData } from "#lib/AusweisData.js"; import * as fs from "fs"
import { PDFDocument, PDFName, PDFNumber, StandardFonts } from "pdf-lib";
/* -------------------------------- Pdf Tools ------------------------------- */ /* -------------------------------- Pdf Tools ------------------------------- */
import { variable } from "#lib/pdf/plugins/variables/index.js";
import { text, image } from "@pdfme/schemas";
import { Schema, Template } from "@pdfme/common";
import { Moment } from "moment";
import { endEnergieVerbrauchVerbrauchsausweis_2016 } from "#lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016.js";
import moment from "moment";
import { getEmpfehlungen } from "#lib/XML/getEmpfehlungen.js";
export async function pdfVerbrauchsausweisWohnen(ausweis: VerbrauchsausweisWohnenClient) { export async function pdfVerbrauchsausweisWohnen(ausweis: VerbrauchsausweisWohnenClient) {
const template = VerbrauchsausweisWohnen2016Template as Template; const VerbrauchsausweisWohnenGEG2024PDF = fs.readFileSync(new URL("./templates/GEG24_Wohngebaeude_ohne_pfeile_form.pdf", import.meta.url), "base64");
const pdf = await PDFDocument.load(VerbrauchsausweisWohnenGEG2024PDF)
const pages = pdf.getPages()
// const template = VerbrauchsausweisWohnen2016Template as Template;
const berechnungen = await endEnergieVerbrauchVerbrauchsausweis_2016(ausweis); const berechnungen = await endEnergieVerbrauchVerbrauchsausweis_2016(ausweis);
let inputs: Record<string, string> = {};
// Verbrauchszeiträume
const addVerbrauchszeitraum = (() => {
let row = 0;
let columnWidth = [16, 16, 80, 12, 18, 18, 18, 12];
const getPosition = (column: number) => {
return {
position: {
x:
15 +
columnWidth.reduce(
(a, c, i) => (i >= column ? a : a + c),
0
),
y: 141 + row * 5,
},
height: 5,
width: 14,
type: "text",
fontSize: 8,
verticalAlign: "middle",
};
};
return (
schema: Schema,
start: Moment,
end: Moment,
energietraeger: string,
primaerenergiefaktor: number,
energieverbrauch: number,
anteilWarmwasser: number,
klimafaktor: number
) => {
inputs[`verbrauchszeitraum_${row}_start`] = start.format("MM.YYYY");
inputs[`verbrauchszeitraum_${row}_end`] = end.format("MM.YYYY");
inputs[`verbrauchszeitraum_${row}_energietraeger`] = energietraeger;
inputs[`verbrauchszeitraum_${row}_primaerenergiefaktor`] =
primaerenergiefaktor.toString();
inputs[`verbrauchszeitraum_${row}_energieverbrauch`] =
energieverbrauch.toString();
inputs[`verbrauchszeitraum_${row}_anteil_warmwasser`] = Math.round(
anteilWarmwasser * energieverbrauch
).toString();
inputs[`verbrauchszeitraum_${row}_anteil_heizung`] = Math.round(
(1 - anteilWarmwasser) *
energieverbrauch
).toString();
inputs[`verbrauchszeitraum_${row}_klimafaktor`] =
klimafaktor.toString();
schema[`verbrauchszeitraum_${row}_start`] = getPosition(0);
schema[`verbrauchszeitraum_${row}_end`] = getPosition(1);
schema[`verbrauchszeitraum_${row}_energietraeger`] = getPosition(2);
schema[`verbrauchszeitraum_${row}_primaerenergiefaktor`] =
getPosition(3);
schema[`verbrauchszeitraum_${row}_energieverbrauch`] = getPosition(4);
schema[`verbrauchszeitraum_${row}_anteil_warmwasser`] = getPosition(5);
schema[`verbrauchszeitraum_${row}_anteil_heizung`] = getPosition(6);
schema[`verbrauchszeitraum_${row}_klimafaktor`] = getPosition(7);
row++;
};
})();
addVerbrauchszeitraum(
template.schemas[2],
moment(ausweis.startdatum),
moment(ausweis.startdatum).add("1", "year"),
ausweis.gebaeude_aufnahme_allgemein?.brennstoff_1,
berechnungen?.primaerfaktorww,
berechnungen?.verbrauch_1_kwh,
ausweis.anteil_warmwasser_1 / 100,
berechnungen?.klimafaktoren[0].klimafaktor
);
addVerbrauchszeitraum(
template.schemas[2],
moment(ausweis.startdatum).add("1", "year"),
moment(ausweis.startdatum).add("2", "years"),
ausweis.gebaeude_aufnahme_allgemein?.brennstoff_1,
berechnungen?.primaerfaktorww,
berechnungen?.verbrauch_2_kwh,
ausweis.anteil_warmwasser_1 / 100,
berechnungen?.klimafaktoren[1].klimafaktor
);
addVerbrauchszeitraum(
template.schemas[2],
moment(ausweis.startdatum).add("2", "years"),
moment(ausweis.startdatum).add("3", "years"),
ausweis.gebaeude_aufnahme_allgemein?.brennstoff_1,
berechnungen?.primaerfaktorww,
berechnungen?.verbrauch_3_kwh,
ausweis.anteil_warmwasser_1 / 100,
berechnungen?.klimafaktoren[2].klimafaktor
);
if (ausweis.zusaetzliche_heizquelle) {
addVerbrauchszeitraum(
template.schemas[2],
moment(ausweis.startdatum),
moment(ausweis.startdatum).add("1", "year"),
ausweis.gebaeude_aufnahme_allgemein?.brennstoff_2,
berechnungen?.primaerfaktorww_1,
berechnungen?.verbrauch_4_kwh,
ausweis.anteil_warmwasser_2 / 100,
berechnungen?.klimafaktoren[0].klimafaktor
);
addVerbrauchszeitraum(
template.schemas[2],
moment(ausweis.startdatum).add("1", "year"),
moment(ausweis.startdatum).add("2", "years"),
ausweis.gebaeude_aufnahme_allgemein?.brennstoff_2,
berechnungen?.primaerfaktorww_1,
berechnungen?.verbrauch_5_kwh,
ausweis.anteil_warmwasser_2 / 100,
berechnungen?.klimafaktoren[1].klimafaktor
);
addVerbrauchszeitraum(
template.schemas[2],
moment(ausweis.startdatum).add("2", "years"),
moment(ausweis.startdatum).add("3", "years"),
ausweis.gebaeude_aufnahme_allgemein?.brennstoff_2,
berechnungen?.primaerfaktorww_1,
berechnungen?.verbrauch_6_kwh,
ausweis.anteil_warmwasser_2 / 100,
berechnungen?.klimafaktoren[1].klimafaktor
);
}
const convertedInputs = { ...convertAusweisData(ausweis), ...inputs };
const empfehlungen = getEmpfehlungen(ausweis);
const addEmpfehlung = (() => {
let i = 0;
let columnWidth = [9, 25, 81, 13, 10, 17, 31];
const getPosition = (column: number) => {
return {
position: {
x:
13 +
columnWidth.reduce(
(a, c, i) => (i >= column ? a : a + c),
0
),
y: 94 + i * 15,
},
height: 15,
width: columnWidth[column],
type: "text",
fontSize: 8,
verticalAlign: "middle",
};
};
return (empfehlung: ReturnType<typeof getEmpfehlungen>[0]) => {
template.schemas[3][`empfehlung_${i}_nr`] = getPosition(0)
template.schemas[3][`empfehlung_${i}_bauteil`] = getPosition(1)
template.schemas[3][`empfehlung_${i}_beschreibung`] = getPosition(2)
template.schemas[3][`empfehlung_${i}_amortisationszeit`] = getPosition(5)
template.schemas[3][`empfehlung_${i}_kosten`] = getPosition(6)
convertedInputs[`empfehlung_${i}_nr`] = (i + 1).toString();
convertedInputs[`empfehlung_${i}_bauteil`] = empfehlung.anlagenteil;
convertedInputs[`empfehlung_${i}_beschreibung`] = empfehlung.description;
convertedInputs[`empfehlung_${i}_amortisationszeit`] = "";
convertedInputs[`empfehlung_${i}_kosten`] = "";
i++;
}
})()
for (const empfehlung of empfehlungen) {
addEmpfehlung(empfehlung);
}
// Fügt den Header mit Registriernummer und GEG Datum hinzu. const height = pages[0].getHeight()
const addHeader = (page: number, includeRegistriernummer = true) => {
if (includeRegistriernummer) {
template.schemas[page]["ausweis.registriernummer"] = {
position: { x: 153, y: 37 },
height: 5,
width: 35,
type: "text",
fontSize: 8,
verticalAlign: "middle",
};
}
template.schemas[page]["geg_datum"] = { const font = await pdf.embedFont(StandardFonts.Helvetica)
position: { x: 108, y: 21.7 }, const bold = await pdf.embedFont(StandardFonts.HelveticaBold)
height: 5,
width: 25, const form = pdf.getForm()
type: "text", form.updateFieldAppearances(font)
fontSize: 8,
verticalAlign: "middle", const fillFormField = (name: string, value: string, fontSize: number = 8) => {
}; const field = form.getTextField(name)
field.setFontSize(fontSize)
field.setText(value)
} }
for (let i = 0; i < template.schemas.length; i++) { const toggleCheck = (name: string, checked: boolean = true) => {
if (i == template.schemas.length - 1) { const field = form.getCheckBox(name)
// Die letzte Seite ist nur ein Anhang mit Erläuterungen, darauf ist keine Registriernummer vermerkt.
addHeader(i, false); if (checked) {
field.check()
} else { } else {
addHeader(i); field.uncheck()
} }
} }
convertedInputs["ausweis.registriernummer"] = "1231234"; const gebaeudetyp = fillFormField("gebaeudetyp", ausweis.gebaeude_aufnahme_allgemein.gebaeudetyp)
convertedInputs["geg_datum"] = "01.01.2024";
const pdf = await generate({
template,
plugins: { text, image, variable: { ...variable, pdf: variable.pdf.bind(convertedInputs), ui: variable.ui.bind(convertedInputs) } },
inputs: [convertedInputs],
options: {
author: "IB Cornelsen",
creationDate: new Date(),
creator: "IB Cornelsen",
language: "de",
title: "Verbrauchsausweis Wohnen GEG 2024",
},
});
return pdf; const adresse = fillFormField("adresse", ausweis.gebaeude_aufnahme_allgemein.adresse)
const gebaeudeteil = fillFormField("gebaeudeteil", ausweis.gebaeude_aufnahme_allgemein.gebaeudeteil)
const baujahr_gebaeude = fillFormField("baujahr_gebaeude", ausweis.gebaeude_aufnahme_allgemein.baujahr_gebaeude.toString())
const baujahr_heizung = fillFormField("baujahr_heizung", ausweis.gebaeude_aufnahme_allgemein.baujahr_heizung.toString())
const einheiten = fillFormField("einheiten", ausweis.gebaeude_aufnahme_allgemein.einheiten.toString())
const nutzflaeche = fillFormField("nutzflaeche", `${ausweis.gebaeude_aufnahme_allgemein.nutzflaeche.toString()}`)
fillFormField("energietraeger_heizung", `${ausweis.gebaeude_aufnahme_allgemein.brennstoff_1}, ${ausweis.gebaeude_aufnahme_allgemein.brennstoff_2 || ""}`)
if (ausweis.warmwasser_enthalten) {
fillFormField("energietraeger_warmwasser", `${ausweis.gebaeude_aufnahme_allgemein.brennstoff_1}, ${ausweis.gebaeude_aufnahme_allgemein.brennstoff_2 || ""}`)
}
toggleCheck("fensterlueftung", ausweis.gebaeude_aufnahme_allgemein.lueftung == Enums.Lueftungskonzept.Fensterlueftung)
toggleCheck("schachtlueftung", ausweis.gebaeude_aufnahme_allgemein.lueftung == Enums.Lueftungskonzept.Schachtlueftung)
toggleCheck("lueftungsanlage_ohne_waermerueckgewinnung", ausweis.gebaeude_aufnahme_allgemein.lueftung == Enums.Lueftungskonzept.LueftungsanlageOhneWaermerueckgewinnung)
toggleCheck("lueftungsanlage_waermerueckgewinnung", ausweis.gebaeude_aufnahme_allgemein.lueftung == Enums.Lueftungskonzept.LueftungsanlageMitWaermerueckgewinnung)
toggleCheck("anlass_neubau", ausweis.ausstellgrund == "Neubau")
toggleCheck("anlass_vermietung", ausweis.ausstellgrund == "Vermietung" || ausweis.ausstellgrund == "Verkauf")
toggleCheck("anlass_modernisierung", ausweis.ausstellgrund == "Modernisierung")
toggleCheck("anlass_sonstiges", ausweis.ausstellgrund == "Sonstiges")
const gebaeudeBild = ausweis.gebaeude_aufnahme_allgemein.gebaeude_stammdaten.gebaeude_bilder?.find(image => image.kategorie === "Gebaeude");
/* -------------------------------- Seite 2 -------------------------------- */
pages[1].drawText(berechnungen.co2EmissionenGesamt.toString(), {
x: 392,
y: height - 170,
size: 10
})
const pfeilNachUnten = await pdf.embedPng(fs.readFileSync(new URL("../../../public/images/pfeil-nach-unten.png", import.meta.url), "base64"))
const pfeilNachOben = await pdf.embedPng(fs.readFileSync(new URL("../../../public/images/pfeil-nach-oben.png", import.meta.url), "base64"))
// Wir müssen den berechneten Wert zwischen 0 und 250 als Wert zwischen 0 und 1 festlegen
const endenergieverbrauchTranslationPercentage = Math.min(250, Math.max(0, berechnungen?.endEnergieVerbrauchGesamt)) / 250
const primaerenergieverbrauchTranslationPercentage = Math.min(250, Math.max(0, berechnungen?.primaerEnergieVerbrauchGesamt)) / 250
const minTranslation = 68
const maxTranslation = 504
const endenergieverbrauchTranslationX = minTranslation + (maxTranslation - minTranslation) * endenergieverbrauchTranslationPercentage;
const primaerenergieverbrauchTranslationX = minTranslation + (maxTranslation - minTranslation) * primaerenergieverbrauchTranslationPercentage;
const pfeilWidth = 20
const margin = 5;
pages[1].drawImage(pfeilNachUnten, {
x: endenergieverbrauchTranslationX,
y: height - 215,
width: pfeilWidth,
height: 30
})
const endEnergieVerbrauchGesamtText = `${berechnungen?.endEnergieVerbrauchGesamt.toString()}kWh/(m²a)`;
const primaerEnergieVerbrauchGesamtText = `${berechnungen?.primaerEnergieVerbrauchGesamt.toString()}kWh/(m²a)`;
if (endenergieverbrauchTranslationPercentage > 0.5) {
pages[1].drawText("Endenergieverbrauch", {
x: endenergieverbrauchTranslationX - margin - font.widthOfTextAtSize("Endenergieverbrauch", 10),
y: height - 193,
size: 10
})
pages[1].drawText(endEnergieVerbrauchGesamtText, {
x: endenergieverbrauchTranslationX - margin - bold.widthOfTextAtSize(endEnergieVerbrauchGesamtText, 10),
y: height - 207,
size: 10,
font: bold
})
} else {
pages[1].drawText("Endenergieverbrauch", {
x: endenergieverbrauchTranslationX + pfeilWidth + margin,
y: height - 193,
size: 10
})
pages[1].drawText(endEnergieVerbrauchGesamtText, {
x: endenergieverbrauchTranslationX + pfeilWidth + margin,
y: height - 207,
size: 10,
font: bold
})
}
pages[1].drawImage(pfeilNachOben, {
x: primaerenergieverbrauchTranslationX,
y: height - 298,
width: pfeilWidth,
height: 30
})
if (endenergieverbrauchTranslationPercentage > 0.5) {
pages[1].drawText("Primärenergieverbrauch", {
x: primaerenergieverbrauchTranslationX - margin - font.widthOfTextAtSize("Primärenergieverbrauch", 10),
y: height - 280,
size: 10
})
pages[1].drawText(primaerEnergieVerbrauchGesamtText, {
x: primaerenergieverbrauchTranslationX - margin - bold.widthOfTextAtSize(primaerEnergieVerbrauchGesamtText, 10),
y: height - 294,
size: 10,
font: bold
})
} else {
pages[1].drawText("Primärenergieverbrauch", {
x: primaerenergieverbrauchTranslationX + pfeilWidth + margin,
y: height - 280,
size: 10
})
pages[1].drawText(primaerEnergieVerbrauchGesamtText, {
x: primaerenergieverbrauchTranslationX + pfeilWidth + margin,
y: height - 294,
size: 10,
font: bold
})
}
pages[1].drawText(berechnungen?.primaerEnergieVerbrauchGesamt.toString(), {
x: 80,
y: height - 352,
font,
size: 8
})
// let inputs: Record<string, string> = {};
// // Verbrauchszeiträume
// const addVerbrauchszeitraum = (() => {
// let row = 0;
// let columnWidth = [16, 16, 80, 12, 18, 18, 18, 12];
// const getPosition = (column: number) => {
// return {
// position: {
// x:
// 15 +
// columnWidth.reduce(
// (a, c, i) => (i >= column ? a : a + c),
// 0
// ),
// y: 141 + row * 5,
// },
// height: 5,
// width: 14,
// type: "text",
// fontSize: 8,
// verticalAlign: "middle",
// };
// };
// return (
// schema: Schema,
// start: Moment,
// end: Moment,
// energietraeger: string,
// primaerenergiefaktor: number,
// energieverbrauch: number,
// anteilWarmwasser: number,
// klimafaktor: number
// ) => {
// inputs[`verbrauchszeitraum_${row}_start`] = start.format("MM.YYYY");
// inputs[`verbrauchszeitraum_${row}_end`] = end.format("MM.YYYY");
// inputs[`verbrauchszeitraum_${row}_energietraeger`] = energietraeger;
// inputs[`verbrauchszeitraum_${row}_primaerenergiefaktor`] =
// primaerenergiefaktor.toString();
// inputs[`verbrauchszeitraum_${row}_energieverbrauch`] =
// energieverbrauch.toString();
// inputs[`verbrauchszeitraum_${row}_anteil_warmwasser`] = Math.round(
// anteilWarmwasser * energieverbrauch
// ).toString();
// inputs[`verbrauchszeitraum_${row}_anteil_heizung`] = Math.round(
// (1 - anteilWarmwasser) *
// energieverbrauch
// ).toString();
// inputs[`verbrauchszeitraum_${row}_klimafaktor`] =
// klimafaktor.toString();
// schema[`verbrauchszeitraum_${row}_start`] = getPosition(0);
// schema[`verbrauchszeitraum_${row}_end`] = getPosition(1);
// schema[`verbrauchszeitraum_${row}_energietraeger`] = getPosition(2);
// schema[`verbrauchszeitraum_${row}_primaerenergiefaktor`] =
// getPosition(3);
// schema[`verbrauchszeitraum_${row}_energieverbrauch`] = getPosition(4);
// schema[`verbrauchszeitraum_${row}_anteil_warmwasser`] = getPosition(5);
// schema[`verbrauchszeitraum_${row}_anteil_heizung`] = getPosition(6);
// schema[`verbrauchszeitraum_${row}_klimafaktor`] = getPosition(7);
// row++;
// };
// })();
// addVerbrauchszeitraum(
// template.schemas[2],
// moment(ausweis.startdatum),
// moment(ausweis.startdatum).add("1", "year"),
// ausweis.gebaeude_aufnahme_allgemein?.brennstoff_1,
// berechnungen?.primaerfaktorww,
// berechnungen?.verbrauch_1_kwh,
// ausweis.anteil_warmwasser_1 / 100,
// berechnungen?.klimafaktoren[0].klimafaktor
// );
// addVerbrauchszeitraum(
// template.schemas[2],
// moment(ausweis.startdatum).add("1", "year"),
// moment(ausweis.startdatum).add("2", "years"),
// ausweis.gebaeude_aufnahme_allgemein?.brennstoff_1,
// berechnungen?.primaerfaktorww,
// berechnungen?.verbrauch_2_kwh,
// ausweis.anteil_warmwasser_1 / 100,
// berechnungen?.klimafaktoren[1].klimafaktor
// );
// addVerbrauchszeitraum(
// template.schemas[2],
// moment(ausweis.startdatum).add("2", "years"),
// moment(ausweis.startdatum).add("3", "years"),
// ausweis.gebaeude_aufnahme_allgemein?.brennstoff_1,
// berechnungen?.primaerfaktorww,
// berechnungen?.verbrauch_3_kwh,
// ausweis.anteil_warmwasser_1 / 100,
// berechnungen?.klimafaktoren[2].klimafaktor
// );
// if (ausweis.zusaetzliche_heizquelle) {
// addVerbrauchszeitraum(
// template.schemas[2],
// moment(ausweis.startdatum),
// moment(ausweis.startdatum).add("1", "year"),
// ausweis.gebaeude_aufnahme_allgemein?.brennstoff_2,
// berechnungen?.primaerfaktorww_1,
// berechnungen?.verbrauch_4_kwh,
// ausweis.anteil_warmwasser_2 / 100,
// berechnungen?.klimafaktoren[0].klimafaktor
// );
// addVerbrauchszeitraum(
// template.schemas[2],
// moment(ausweis.startdatum).add("1", "year"),
// moment(ausweis.startdatum).add("2", "years"),
// ausweis.gebaeude_aufnahme_allgemein?.brennstoff_2,
// berechnungen?.primaerfaktorww_1,
// berechnungen?.verbrauch_5_kwh,
// ausweis.anteil_warmwasser_2 / 100,
// berechnungen?.klimafaktoren[1].klimafaktor
// );
// addVerbrauchszeitraum(
// template.schemas[2],
// moment(ausweis.startdatum).add("2", "years"),
// moment(ausweis.startdatum).add("3", "years"),
// ausweis.gebaeude_aufnahme_allgemein?.brennstoff_2,
// berechnungen?.primaerfaktorww_1,
// berechnungen?.verbrauch_6_kwh,
// ausweis.anteil_warmwasser_2 / 100,
// berechnungen?.klimafaktoren[1].klimafaktor
// );
// }
// const convertedInputs = { ...convertAusweisData(ausweis), ...inputs };
// const empfehlungen = getEmpfehlungen(ausweis);
// const addEmpfehlung = (() => {
// let i = 0;
// let columnWidth = [9, 25, 81, 13, 10, 17, 31];
// const getPosition = (column: number) => {
// return {
// position: {
// x:
// 13 +
// columnWidth.reduce(
// (a, c, i) => (i >= column ? a : a + c),
// 0
// ),
// y: 94 + i * 15,
// },
// height: 15,
// width: columnWidth[column],
// type: "text",
// fontSize: 8,
// verticalAlign: "middle",
// };
// };
// return (empfehlung: ReturnType<typeof getEmpfehlungen>[0]) => {
// template.schemas[3][`empfehlung_${i}_nr`] = getPosition(0)
// template.schemas[3][`empfehlung_${i}_bauteil`] = getPosition(1)
// template.schemas[3][`empfehlung_${i}_beschreibung`] = getPosition(2)
// template.schemas[3][`empfehlung_${i}_amortisationszeit`] = getPosition(5)
// template.schemas[3][`empfehlung_${i}_kosten`] = getPosition(6)
// convertedInputs[`empfehlung_${i}_nr`] = (i + 1).toString();
// convertedInputs[`empfehlung_${i}_bauteil`] = empfehlung.anlagenteil;
// convertedInputs[`empfehlung_${i}_beschreibung`] = empfehlung.description;
// convertedInputs[`empfehlung_${i}_amortisationszeit`] = "";
// convertedInputs[`empfehlung_${i}_kosten`] = "";
// i++;
// }
// })()
// for (const empfehlung of empfehlungen) {
// addEmpfehlung(empfehlung);
// }
// // Fügt den Header mit Registriernummer und GEG Datum hinzu.
// const addHeader = (page: number, includeRegistriernummer = true) => {
// if (includeRegistriernummer) {
// template.schemas[page]["ausweis.registriernummer"] = {
// position: { x: 153, y: 37 },
// height: 5,
// width: 35,
// type: "text",
// fontSize: 8,
// verticalAlign: "middle",
// };
// }
// template.schemas[page]["geg_datum"] = {
// position: { x: 108, y: 21.7 },
// height: 5,
// width: 25,
// type: "text",
// fontSize: 8,
// verticalAlign: "middle",
// };
// }
// for (let i = 0; i < template.schemas.length; i++) {
// if (i == template.schemas.length - 1) {
// // Die letzte Seite ist nur ein Anhang mit Erläuterungen, darauf ist keine Registriernummer vermerkt.
// addHeader(i, false);
// } else {
// addHeader(i);
// }
// }
// convertedInputs["ausweis.registriernummer"] = "1231234";
// convertedInputs["geg_datum"] = "01.01.2024";
// const pdf = await generate({
// template,
// plugins: { text, image, variable: { ...variable, pdf: variable.pdf.bind(convertedInputs), ui: variable.ui.bind(convertedInputs) } },
// inputs: [convertedInputs],
// options: {
// author: "IB Cornelsen",
// creationDate: new Date(),
// creator: "IB Cornelsen",
// language: "de",
// title: "Verbrauchsausweis Wohnen GEG 2024",
// },
// });
pdf.getForm().flatten()
return pdf.save();
} }

View File

@@ -0,0 +1,9 @@
---
import BlankLayout from "#layouts/BlankLayout.astro";
---
<BlankLayout title="PDF Viewer">
<iframe class="h-screen w-screen" src="http://localhost:3000/pdf/ansichtsausweis?base64=eyJnZWJhZXVkZV9hdWZuYWhtZV9hbGxnZW1laW4iOnsiZ2ViYWV1ZGVfc3RhbW1kYXRlbiI6e30sImJhdWphaHJfZ2ViYWV1ZGUiOlsxOTYyXSwiYmF1amFocl9oZWl6dW5nIjpbMTk1Ml0sInBseiI6IjIxMDM5IiwiZ2ViYWV1ZGV0eXAiOiJFaW5mYW1pbGllbmhhdXMiLCJzYW5pZXJ0Ijp0cnVlLCJkYWNoZ2VzY2hvc3MiOiJOSUNIVF9WT1JIQU5ERU4iLCJrZWxsZXIiOiJOSUNIVF9WT1JIQU5ERU4iLCJicmVubnN0b2ZmXzEiOiJFcmRnYXMgSCIsImdlYmFldWRldGVpbCI6Ikdlc2FtdGdlYsOkdWRlIiwibHVlZnR1bmciOiJGZW5zdGVybMO8ZnR1bmciLCJrdWVobHVuZyI6IjEiLCJvYmVyc3RlX2dlc2Nob3NzZGVja2VfZ2VkYWVtbXQiOnRydWUsImRhY2hnZXNjaG9zc19nZWRhZW1tdCI6dHJ1ZSwiZWluaGVpdGVuIjoxLCJmbGFlY2hlIjoxNTIsIm51dHpmbGFlY2hlIjoxNzIsIm9ydCI6IkhhbWJ1cmciLCJhZHJlc3NlIjoiQ3Vyc2xhY2tlciBEZWljaCAxNzAifSwid2FybXdhc3Nlcl9hbnRlaWxfYmVrYW5udCI6ZmFsc2UsImF1c3N0ZWxsZ3J1bmQiOiJWZXJtaWV0dW5nIiwiZWluaGVpdF8xIjoia1doIiwidmVyYnJhdWNoXzEiOjE1MDAwLCJ2ZXJicmF1Y2hfMiI6MTQwMDAsInZlcmJyYXVjaF8zIjoxNjAwMCwia2VsbGVyX2JlaGVpenQiOnRydWUsImFudGVpbF93YXJtd2Fzc2VyXzEiOjE4LCJzdGFydGRhdHVtIjoiMjAyMC0xMi0zMVQxNzowMDowMC4wMDBaIn0="></iframe>
</BlankLayout>