GEG Einpreisung

This commit is contained in:
Moritz Utcke
2025-02-21 12:25:28 +11:00
parent 685cb114f8
commit 1df144e5b2
24 changed files with 426 additions and 266 deletions

View File

@@ -4,9 +4,7 @@ export const createCaller = createCallerFactory({
"bild": await import("../src/pages/api/bild.ts"), "bild": await import("../src/pages/api/bild.ts"),
"klimafaktoren": await import("../src/pages/api/klimafaktoren.ts"), "klimafaktoren": await import("../src/pages/api/klimafaktoren.ts"),
"postleitzahlen": await import("../src/pages/api/postleitzahlen.ts"), "postleitzahlen": await import("../src/pages/api/postleitzahlen.ts"),
"auth/access-token": await import("../src/pages/api/auth/access-token.ts"), "unterlage": await import("../src/pages/api/unterlage.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"),
"admin/ausstellen": await import("../src/pages/api/admin/ausstellen.ts"), "admin/ausstellen": await import("../src/pages/api/admin/ausstellen.ts"),
"admin/bestellbestaetigung": await import("../src/pages/api/admin/bestellbestaetigung.ts"), "admin/bestellbestaetigung": await import("../src/pages/api/admin/bestellbestaetigung.ts"),
"admin/erinnern": await import("../src/pages/api/admin/erinnern.ts"), "admin/erinnern": await import("../src/pages/api/admin/erinnern.ts"),
@@ -14,6 +12,9 @@ export const createCaller = createCallerFactory({
"admin/post-ausstellen": await import("../src/pages/api/admin/post-ausstellen.ts"), "admin/post-ausstellen": await import("../src/pages/api/admin/post-ausstellen.ts"),
"admin/registriernummer": await import("../src/pages/api/admin/registriernummer.ts"), "admin/registriernummer": await import("../src/pages/api/admin/registriernummer.ts"),
"aufnahme": await import("../src/pages/api/aufnahme/index.ts"), "aufnahme": await import("../src/pages/api/aufnahme/index.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"),
"bedarfsausweis-wohnen/[uid]": await import("../src/pages/api/bedarfsausweis-wohnen/[uid].ts"), "bedarfsausweis-wohnen/[uid]": await import("../src/pages/api/bedarfsausweis-wohnen/[uid].ts"),
"bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"), "bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"),
"bilder/[uid]": await import("../src/pages/api/bilder/[uid].ts"), "bilder/[uid]": await import("../src/pages/api/bilder/[uid].ts"),
@@ -27,11 +28,10 @@ export const createCaller = createCallerFactory({
"user/self": await import("../src/pages/api/user/self.ts"), "user/self": await import("../src/pages/api/user/self.ts"),
"verbrauchsausweis-gewerbe/[uid]": await import("../src/pages/api/verbrauchsausweis-gewerbe/[uid].ts"), "verbrauchsausweis-gewerbe/[uid]": await import("../src/pages/api/verbrauchsausweis-gewerbe/[uid].ts"),
"verbrauchsausweis-gewerbe": await import("../src/pages/api/verbrauchsausweis-gewerbe/index.ts"), "verbrauchsausweis-gewerbe": await import("../src/pages/api/verbrauchsausweis-gewerbe/index.ts"),
"webhooks/mollie": await import("../src/pages/api/webhooks/mollie.ts"),
"verbrauchsausweis-wohnen/[uid]": await import("../src/pages/api/verbrauchsausweis-wohnen/[uid].ts"), "verbrauchsausweis-wohnen/[uid]": await import("../src/pages/api/verbrauchsausweis-wohnen/[uid].ts"),
"verbrauchsausweis-wohnen": await import("../src/pages/api/verbrauchsausweis-wohnen/index.ts"), "verbrauchsausweis-wohnen": await import("../src/pages/api/verbrauchsausweis-wohnen/index.ts"),
"webhooks/mollie": await import("../src/pages/api/webhooks/mollie.ts"),
"aufnahme/[uid]/bilder": await import("../src/pages/api/aufnahme/[uid]/bilder.ts"), "aufnahme/[uid]/bilder": await import("../src/pages/api/aufnahme/[uid]/bilder.ts"),
"aufnahme/[uid]": await import("../src/pages/api/aufnahme/[uid]/index.ts"), "aufnahme/[uid]": await import("../src/pages/api/aufnahme/[uid]/index.ts"),
"objekt/[uid]": await import("../src/pages/api/objekt/[uid]/index.ts"), "objekt/[uid]": await import("../src/pages/api/objekt/[uid]/index.ts"),
"objekt/[uid]/unterlagen": await import("../src/pages/api/objekt/[uid]/unterlagen.ts"),
}) })

18
src/client/lib/helpers.ts Normal file
View File

@@ -0,0 +1,18 @@
import { writable, Writable } from "svelte/store";
export function localStorageSync<T>(initial: T, name: string, modifier: (stored: any) => T = (stored) => JSON.parse(stored), reverseModifier: (value: T) => string = (value) => JSON.stringify(value)): Writable<T> {
const stored = localStorage.getItem(name) as T
let value = initial;
if (stored) {
value = modifier(stored)
}
const writableStore = writable(value)
writableStore.subscribe((value) => {
const reversed = reverseModifier(value);
localStorage.setItem(name, reversed)
})
return writableStore
}

View File

@@ -0,0 +1,135 @@
import { api } from "astro-typesafe-api/client"
import { exclude } from "#lib/exclude.js";
import Cookies from "js-cookie";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
import { AufnahmeClient, BedarfsausweisWohnenClient, BildClient, ObjektClient, UnterlageClient, UploadedGebaeudeBild, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient, } from "#components/Ausweis/types.js";
import { Enums } from "@ibcornelsen/database/client";
export async function nachweisSpeichern(
nachweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient,
objekt: ObjektClient,
aufnahme: AufnahmeClient,
bilder: BildClient[],
unterlagen: UnterlageClient[],
ausweisart: Enums.Ausweisart
) {
if (objekt.uid) {
await api.objekt._uid.PATCH.fetch({
...exclude(objekt, ["uid"])
}, {
params: {
uid: objekt.uid
},
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
} else {
const { uid } = await api.objekt.PUT.fetch({
...exclude(objekt, ["uid"])
}, {
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
objekt.uid = uid;
}
if (aufnahme.uid) {
await api.aufnahme._uid.PATCH.fetch({
...exclude(aufnahme, ["uid"]),
baujahr_gebaeude: aufnahme.baujahr_gebaeude || [],
baujahr_klima: aufnahme.baujahr_klima || [],
baujahr_heizung: aufnahme.baujahr_heizung || [],
}, {
params: {
uid: aufnahme.uid
},
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
} else {
const { uid } = await api.aufnahme.PUT.fetch({
aufnahme,
uid_objekt: objekt.uid
}, {
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
aufnahme.uid = uid
}
let patchRoute: any;
let putRoute: any;
if (ausweisart == Enums.Ausweisart.GEGNachweisWohnen) {
patchRoute = api["geg-nachweis-wohnen"]._uid.PATCH
putRoute = api["geg-nachweis-wohnen"].PUT
}
if (nachweis.uid) {
await patchRoute.fetch({
...exclude(nachweis, ["uid"])
}, {
params: {
uid: nachweis.uid
},
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
} else {
const { uid } = await putRoute.fetch({
nachweis,
uid_aufnahme: aufnahme.uid
}, {
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
nachweis.uid = uid;
}
await api.aufnahme._uid.bilder.PUT.fetch(bilder.map(bild => bild.uid), {
params: {
uid: aufnahme.uid
},
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
return {
uid_nachweis: nachweis.uid,
uid_aufnahme: aufnahme.uid,
uid_objekt: objekt.uid
}
// await client.v1.tickets.erstellen.mutate({
// titel: "Ausweis konnte nicht gespeichert werden",
// beschreibung: e.stack,
// email: user.email ?? "",
// metadata: JSON.stringify({
// ausweis,
// }),
// });
// addNotification({
// dismissable: false,
// message:
// "Ausweis konnte nicht gespeichert werden, bitte versuchen sie es erneut.",
// subtext:
// "Sollte das Problem weiterhin bestehen, kontaktieren sie bitte den Support.",
// timeout: 6000,
// type: "error",
// });
return null;
}

View File

@@ -48,7 +48,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required required
data-cy="ausstellgrund" data-cy="ausstellgrund"
> >
<option disabled selected value={false}>Bitte auswählen</option> <option disabled selected>Bitte auswählen</option>
{#each Object.entries(Enums.Ausstellgrund) as [name, ausstellgrund]} {#each Object.entries(Enums.Ausstellgrund) as [name, ausstellgrund]}
<option value={ausstellgrund}>{name}</option> <option value={ausstellgrund}>{name}</option>
{/each} {/each}

View File

@@ -1,16 +1,18 @@
<script lang="ts"> <script lang="ts">
import { ausweisSpeichern } from "#client/lib/ausweisSpeichern.js"; import { ausweisSpeichern } from "#client/lib/ausweisSpeichern.js";
import { validateAccessTokenClient } from "#client/lib/validateAccessToken.js"; import { validateAccessTokenClient } from "#client/lib/validateAccessToken.js";
import { AufnahmeClient, BedarfsausweisWohnenClient, BenutzerClient, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js"; import { AufnahmeClient, BedarfsausweisWohnenClient, BenutzerClient, GEGNachweisWohnenClient, ObjektClient, UnterlageClient, UploadedGebaeudeBild, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js";
import Overlay from "#components/Overlay.svelte"; import Overlay from "#components/Overlay.svelte";
import EmbeddedAuthFlowModule from "#modules/EmbeddedAuthFlowModule.svelte"; import EmbeddedAuthFlowModule from "#modules/EmbeddedAuthFlowModule.svelte";
import { AusweisTyp, Enums } from "@ibcornelsen/database/client"; import { AusweisTyp, Enums } from "@ibcornelsen/database/client";
import { openWindowWithPost } from "#lib/helpers/window.js"; import { openWindowWithPost } from "#lib/helpers/window.js";
import { PRICES } from "#lib/constants.js"; import { PRICES } from "#lib/constants.js";
import { nachweisSpeichern } from "#client/lib/nachweisSpeichern.js";
export let ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient; export let ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient | GEGNachweisWohnenClient;
export let bilder: UploadedGebaeudeBild[]; export let bilder: UploadedGebaeudeBild[];
export let unterlagen: UnterlageClient[];
export let user: BenutzerClient; export let user: BenutzerClient;
export let objekt: ObjektClient; export let objekt: ObjektClient;
export let aufnahme: AufnahmeClient; export let aufnahme: AufnahmeClient;
@@ -26,6 +28,7 @@
objekt, objekt,
aufnahme, aufnahme,
bilder, bilder,
unterlagen,
ausweisart, ausweisart,
ausweistyp ausweistyp
}, "") }, "")
@@ -42,7 +45,12 @@
loginOverlayHidden = true loginOverlayHidden = true
const result = await ausweisSpeichern(ausweis, objekt, aufnahme, bilder, ausweisart); let result: Awaited<ReturnType<typeof ausweisSpeichern>> | null = null;
if (ausweisart === Enums.Ausweisart.GEGNachweisWohnen) {
result = await nachweisSpeichern(ausweis, objekt, aufnahme, bilder, unterlagen, ausweisart)
} else {
result = await ausweisSpeichern(ausweis, objekt, aufnahme, bilder, ausweisart)
}
if (result !== null) { if (result !== null) {
window.history.pushState( window.history.pushState(

View File

@@ -121,7 +121,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
bind:value={aufnahme.dachgeschoss} bind:value={aufnahme.dachgeschoss}
required required
> >
<option disabled selected value={false}>Bitte auswählen</option> <option disabled selected>Bitte auswählen</option>
<option value={Enums.Heizungsstatus.NICHT_VORHANDEN}>nicht vorhanden</option> <option value={Enums.Heizungsstatus.NICHT_VORHANDEN}>nicht vorhanden</option>
<option value={Enums.Heizungsstatus.UNBEHEIZT}>unbeheizt</option> <option value={Enums.Heizungsstatus.UNBEHEIZT}>unbeheizt</option>
<option value={Enums.Heizungsstatus.BEHEIZT}>beheizt</option> <option value={Enums.Heizungsstatus.BEHEIZT}>beheizt</option>

View File

@@ -29,7 +29,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
bind:value={aufnahme.gebaeudeteil} bind:value={aufnahme.gebaeudeteil}
required required
> >
<option disabled selected value={false}>Bitte auswählen</option> <option disabled selected>Bitte auswählen</option>
<option value="Gesamtgebäude">Gesamtgebäude</option> <option value="Gesamtgebäude">Gesamtgebäude</option>
<option value="Wohnen">Wohnen</option> <option value="Wohnen">Wohnen</option>
</select> </select>
@@ -79,7 +79,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required required
bind:value={aufnahme.lueftung} bind:value={aufnahme.lueftung}
> >
<option disabled selected value={false}>Bitte auswählen</option> <option disabled selected>Bitte auswählen</option>
<option value="Fensterlueftung">Fensterlüftung</option> <option value="Fensterlueftung">Fensterlüftung</option>
<option value="Schachtlueftung">Schachtlüftung</option> <option value="Schachtlueftung">Schachtlüftung</option>
<option value="LueftungsanlageOhneWaermerueckgewinnung" <option value="LueftungsanlageOhneWaermerueckgewinnung"
@@ -112,7 +112,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required required
bind:value={aufnahme.kuehlung} bind:value={aufnahme.kuehlung}
> >
<option disabled selected value={false}>Bitte auswählen</option> <option disabled selected>Bitte auswählen</option>
<option value="1">vorhanden</option> <option value="1">vorhanden</option>
<option value="0">nicht vorhanden</option> <option value="0">nicht vorhanden</option>
</select> </select>

View File

@@ -223,7 +223,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
bind:value={aufnahme.brennstoff_1} bind:value={aufnahme.brennstoff_1}
required required
> >
<option disabled selected value={false}>Bitte auswählen</option> <option disabled selected>Bitte auswählen</option>
{#each Object.keys(fuelMap) as fuel} {#each Object.keys(fuelMap) as fuel}
<option value={fuel}>{fuel}</option> <option value={fuel}>{fuel}</option>
{/each} {/each}
@@ -256,7 +256,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
disabled={!aufnahme.brennstoff_1} disabled={!aufnahme.brennstoff_1}
required required
> >
<option disabled selected value={false}>Bitte auswählen</option> <option disabled selected >Bitte auswählen</option>
{#each fuelMap.hasOwnProperty(aufnahme.brennstoff_1) ? fuelMap[aufnahme.brennstoff_1] : [] as unit} {#each fuelMap.hasOwnProperty(aufnahme.brennstoff_1) ? fuelMap[aufnahme.brennstoff_1] : [] as unit}
<option value={unit}>{unit}</option> <option value={unit}>{unit}</option>
{/each} {/each}
@@ -405,7 +405,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
bind:value={aufnahme.brennstoff_2} bind:value={aufnahme.brennstoff_2}
required required
> >
<option disabled selected value={false} <option disabled selected
>Bitte auswählen</option >Bitte auswählen</option
> >
{#each Object.keys(fuelMap) as fuel} {#each Object.keys(fuelMap) as fuel}
@@ -440,7 +440,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
disabled={!aufnahme.brennstoff_2} disabled={!aufnahme.brennstoff_2}
required required
> >
<option disabled selected value={false} <option disabled selected
>Bitte auswählen</option >Bitte auswählen</option
> >
{#each fuelMap.hasOwnProperty(aufnahme.brennstoff_2) ? fuelMap[aufnahme.brennstoff_2] : [] as unit} {#each fuelMap.hasOwnProperty(aufnahme.brennstoff_2) ? fuelMap[aufnahme.brennstoff_2] : [] as unit}

View File

@@ -236,7 +236,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
bind:value={aufnahme.brennstoff_1} bind:value={aufnahme.brennstoff_1}
required required
> >
<option disabled selected value={false}>Bitte auswählen</option> <option disabled selected >Bitte auswählen</option>
{#each Object.keys(fuelMap) as fuel} {#each Object.keys(fuelMap) as fuel}
<option value={fuel}>{fuel}</option> <option value={fuel}>{fuel}</option>
{/each} {/each}
@@ -276,7 +276,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
disabled={!aufnahme.brennstoff_1} disabled={!aufnahme.brennstoff_1}
required required
> >
<option disabled selected value={false}>Bitte auswählen</option> <option disabled selected >Bitte auswählen</option>
{#each fuelMap.hasOwnProperty(aufnahme.brennstoff_1) ? fuelMap[aufnahme.brennstoff_1] : [] as unit} {#each fuelMap.hasOwnProperty(aufnahme.brennstoff_1) ? fuelMap[aufnahme.brennstoff_1] : [] as unit}
<option value={unit}>{unit}</option> <option value={unit}>{unit}</option>
{/each} {/each}
@@ -431,7 +431,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required required
data-cy="brennstoff_2" data-cy="brennstoff_2"
> >
<option disabled selected value={false} <option disabled selected
>Bitte auswählen</option >Bitte auswählen</option
> >
{#each Object.keys(fuelMap) as fuel} {#each Object.keys(fuelMap) as fuel}
@@ -474,7 +474,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
disabled={!aufnahme.brennstoff_2} disabled={!aufnahme.brennstoff_2}
required required
> >
<option disabled selected value={false} <option disabled selected
>Bitte auswählen</option >Bitte auswählen</option
> >
{#each fuelMap.hasOwnProperty(aufnahme.brennstoff_2) ? fuelMap[aufnahme.brennstoff_2] : [] as unit} {#each fuelMap.hasOwnProperty(aufnahme.brennstoff_2) ? fuelMap[aufnahme.brennstoff_2] : [] as unit}

View File

@@ -17,7 +17,7 @@ import { z, ZodSchema } from "zod";
export type OmitKeys<T, K extends keyof T> = Omit<T, K>; export type OmitKeys<T, K extends keyof T> = Omit<T, K>;
export type UploadedGebaeudeBild = OmitKeys<Bild, "id" | "objekt_id"> & { export type UploadedGebaeudeBild = OmitKeys<Bild, "id" | "aufnahme_id"> & {
data: string data: string
} }
@@ -148,6 +148,8 @@ export function getAusweisartFromUUID(uid: string): Enums.Ausweisart | null {
return Enums.Ausweisart.BedarfsausweisGewerbe return Enums.Ausweisart.BedarfsausweisGewerbe
} else if (uid.startsWith("gnw")) { } else if (uid.startsWith("gnw")) {
return Enums.Ausweisart.GEGNachweisWohnen return Enums.Ausweisart.GEGNachweisWohnen
} else if (uid.startsWith("gng")) {
return Enums.Ausweisart.GEGNachweisGewerbe
} }
return null; return null;

View File

@@ -4,13 +4,14 @@
import HelpLabel from "#components/labels/HelpLabel.svelte"; import HelpLabel from "#components/labels/HelpLabel.svelte";
export let kategorie: string = ""; export let kategorie: string = "";
export let files: (UnterlageClient & { data: string | ArrayBuffer })[] = []; export let files: UnterlageClient[] = [];
export let max: number = Infinity; export let max: number = Infinity;
export let min: number = 1; export let min: number = 1;
export let name: string = ""; export let name: string = "";
export let ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient | GEGNachweisWohnenClient; export let ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient | GEGNachweisWohnenClient;
export let objekt: ObjektClient; export let objekt: ObjektClient;
import mime from "mime-types"; import mime from "mime-types";
import { api } from "astro-typesafe-api/client";
function getAllFiles(this: HTMLInputElement) { function getAllFiles(this: HTMLInputElement) {
@@ -30,7 +31,7 @@
const reader = new FileReader(); const reader = new FileReader();
reader.onload = () => { reader.onload = async () => {
if (reader.readyState != reader.DONE) { if (reader.readyState != reader.DONE) {
return; return;
} }
@@ -41,7 +42,14 @@
const mimeType = mime.types[file.name.split(".").pop() as string] const mimeType = mime.types[file.name.split(".").pop() as string]
files.push({ data: reader.result, kategorie, name: file.name, mime: mimeType }); const { uid } = await api.unterlage.PUT.fetch({
data: reader.result as string,
kategorie,
mime: mimeType,
name: file.name
})
files.push({ uid, kategorie, name: file.name, mime: mimeType });
files = files; files = files;
@@ -50,7 +58,7 @@
} }
} }
reader.readAsArrayBuffer(file); reader.readAsDataURL(file);
} }
} }

View File

@@ -67,7 +67,7 @@
<div data-test="plz-container" class="absolute top-[calc(100%+4px)] flex flex-col left-0 bg-white py-1 shadow-md rounded-lg z-10" class:hidden={hideZipDropdown}> <div data-test="plz-container" class="absolute top-[calc(100%+4px)] flex flex-col left-0 bg-white py-1 shadow-md rounded-lg z-10" class:hidden={hideZipDropdown}>
{#each zipCodes as zipCode} {#each zipCodes as zipCode}
<button class="hover:bg-gray-100 cursor-pointer px-2 py-1 text-nowrap" tabindex="-1" on:click={() => { <button class="hover:bg-gray-100 cursor-pointer px-2 py-1 text-nowrap" type="button" tabindex="-1" on:click={() => {
zip = zipCode.plz; zip = zipCode.plz;
city = zipCode.stadt; city = zipCode.stadt;
hideZipDropdown = true; hideZipDropdown = true;

View File

@@ -18,7 +18,9 @@ export enum VALID_UUID_PREFIXES {
"inv" = "Rechnung", "inv" = "Rechnung",
"tkt" = "Ticket", "tkt" = "Ticket",
"pln" = "Gebäude Plan", "pln" = "Gebäude Plan",
"gnw" = "GEG Nachweis Wohnen" "gnw" = "GEG Nachweis Wohnen",
"gng" = "GEG Nachweis Gewerbe",
"gge" = "GEG Einpreisung",
} }
/** /**

View File

@@ -510,23 +510,23 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
// } // }
// } // }
if (aufnahme.kuehlung) { // if (aufnahme.kuehlung) {
/** // /**
* Kühlungszuschlag - Pauschale Erhöhung um 6kWh/m² // * Kühlungszuschlag - Pauschale Erhöhung um 6kWh/m²
* Primärenergiefaktor Strom // * Primärenergiefaktor Strom
* @link https://www.bundesanzeiger.de/pub/publication/MRYM4nI84Sdlr0EIvvW?2 // * @link https://www.bundesanzeiger.de/pub/publication/MRYM4nI84Sdlr0EIvvW?2
*/ // */
addVerbrauch( // addVerbrauch(
moment(ausweis.startdatum).format("MM.YYYY"), // moment(ausweis.startdatum).format("MM.YYYY"),
moment(ausweis.startdatum).add(3, "years").format("MM.YYYY"), // moment(ausweis.startdatum).add(3, "years").format("MM.YYYY"),
"Kühlungszuschlag", // "Kühlungszuschlag",
berechnungen?.primaerfaktorww.toString(), // berechnungen?.primaerfaktorww.toString(),
Math.round(berechnungen?.kuehlungsZuschlag || 0).toString(), // Math.round(berechnungen?.kuehlungsZuschlag || 0).toString(),
"0", // "0",
"0", // "0",
"" // ""
); // );
} // }
/* -------------------------------- Seite 4 -------------------------------- */ /* -------------------------------- Seite 4 -------------------------------- */

View File

@@ -17,9 +17,9 @@
AufnahmeClient, AufnahmeClient,
BenutzerClient, BenutzerClient,
BildClient, BildClient,
getAusweisartFromUUID,
ObjektClient, ObjektClient,
RechnungClient, RechnungClient,
UnterlageClient,
VerbrauchsausweisWohnenClient, VerbrauchsausweisWohnenClient,
} from "#components/Ausweis/types.js"; } from "#components/Ausweis/types.js";
import { validateAccessTokenClient } from "src/client/lib/validateAccessToken.js"; import { validateAccessTokenClient } from "src/client/lib/validateAccessToken.js";
@@ -30,12 +30,14 @@
import { ausweisSpeichern } from "#client/lib/ausweisSpeichern.js"; import { ausweisSpeichern } from "#client/lib/ausweisSpeichern.js";
import { addNotification } from "#components/Notifications/shared.js"; import { addNotification } from "#components/Notifications/shared.js";
import NotificationWrapper from "#components/Notifications/NotificationWrapper.svelte"; import NotificationWrapper from "#components/Notifications/NotificationWrapper.svelte";
import { nachweisSpeichern } from "#client/lib/nachweisSpeichern.js";
export let user: Partial<BenutzerClient>; export let user: Partial<BenutzerClient>;
export let ausweis: VerbrauchsausweisWohnenClient; export let ausweis: VerbrauchsausweisWohnenClient;
export let aufnahme: AufnahmeClient; export let aufnahme: AufnahmeClient;
export let objekt: ObjektClient; export let objekt: ObjektClient;
export let bilder: BildClient[]; export let bilder: BildClient[];
export let unterlagen: UnterlageClient[];
export let ausweisart: Enums.Ausweisart; export let ausweisart: Enums.Ausweisart;
export let aktiveBezahlmethode: Bezahlmethoden = Enums.Bezahlmethoden.paypal; export let aktiveBezahlmethode: Bezahlmethoden = Enums.Bezahlmethoden.paypal;
export let ausweistyp: Enums.AusweisTyp = Enums.AusweisTyp.Standard; export let ausweistyp: Enums.AusweisTyp = Enums.AusweisTyp.Standard;
@@ -137,7 +139,68 @@
} }
async function anfordern() { async function anfordern() {
// TODO Angebot anfordern if (!form.checkValidity()) {
addNotification({
dismissable: true,
message: "Fehlende Daten.",
subtext: "Nicht alle notwendigen Felder sind ausgefüllt, bitte füllen sie diese aus bevor sie es erneut versuchen.."
})
form.reportValidity();
return;
}
if (!await validateAccessTokenClient()) {
loginAction = bestellen
rechnung = rechnung
loginOverlayHidden = false;
return
}
loginOverlayHidden = true
let result: Awaited<ReturnType<typeof ausweisSpeichern>> | null = null;
if (ausweisart === Enums.Ausweisart.GEGNachweisWohnen) {
result = await nachweisSpeichern(ausweis, objekt, aufnahme, bilder, unterlagen, ausweisart)
} else {
result = await ausweisSpeichern(ausweis, objekt, aufnahme, bilder, ausweisart)
}
if (result === null) {
addNotification({
dismissable: true,
message: "Ups... Das hat nicht geklappt.",
subtext: "Der Nachweis konnte nicht gespeichert werden, bitte versuchen sie es erneut oder kontaktieren sie unseren Support."
})
}
try {
const { uid } = await api.rechnung.anfordern.PUT.fetch(
{
email: rechnung.email,
empfaenger: rechnung.empfaenger,
strasse: rechnung.strasse,
plz: rechnung.plz,
ort: rechnung.ort,
versand_empfaenger: rechnung.versand_empfaenger,
versand_strasse: rechnung.versand_strasse,
versand_plz: rechnung.versand_plz,
versand_ort: rechnung.versand_ort,
telefon: rechnung.telefon,
nachweis_uid: ausweis.uid,
},
{
headers: {
Authorization: `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`,
},
}
);
// Alle alten Ausweisdateien im localStorage löschen.
localStorage.clear();
window.location.href = `/einpreisung/success?e=${uid}&a=${ausweis.uid}`;
} catch (e) {}
} }
async function bestellen() { async function bestellen() {
@@ -161,7 +224,12 @@
loginOverlayHidden = true loginOverlayHidden = true
const result = await ausweisSpeichern(ausweis, objekt, aufnahme, bilder, ausweisart) let result: Awaited<ReturnType<typeof ausweisSpeichern>> | null = null;
if (ausweisart === Enums.Ausweisart.GEGNachweisWohnen) {
result = await nachweisSpeichern(ausweis, objekt, aufnahme, bilder, unterlagen, ausweisart)
} else {
result = await ausweisSpeichern(ausweis, objekt, aufnahme, bilder, ausweisart)
}
if (result === null) { if (result === null) {
addNotification({ addNotification({
@@ -363,8 +431,14 @@ grid-cols-5 justify-around justify-items-center items-center"
<div class="ProduktKostenTabelle"> <div class="ProduktKostenTabelle">
<div class="zeile betrag"> <div class="zeile betrag">
<div>Netto-Preis Energieausweis</div> {#if ausweistyp === Enums.AusweisTyp.Standard}
<div>:</div> <span>Netto-Preis Energieausweis</span>
{:else if ausweistyp === Enums.AusweisTyp.Beratung}
<span>Energieausweis inkl. Beratung</span>
{:else if ausweistyp === Enums.AusweisTyp.Offline}
<span>Energieausweis Offline</span>
{/if}
<span>:</span>
<div class="text-right"> <div class="text-right">
<b>{(price * 0.81).toFixed(2) + " €"}</b> <b>{(price * 0.81).toFixed(2) + " €"}</b>
</div> </div>

View File

@@ -2,7 +2,6 @@
import Ausweisart from "#components/Ausweis/Ausweisart.svelte"; import Ausweisart from "#components/Ausweis/Ausweisart.svelte";
import GebaeudeDaten from "#components/Ausweis/GebaeudeDaten.svelte"; import GebaeudeDaten from "#components/Ausweis/GebaeudeDaten.svelte";
import { import {
VerbrauchsausweisWohnenClient,
ObjektClient, ObjektClient,
AufnahmeClient, AufnahmeClient,
BenutzerClient, BenutzerClient,
@@ -16,167 +15,17 @@
import HelpLabel from "#components/labels/HelpLabel.svelte"; import HelpLabel from "#components/labels/HelpLabel.svelte";
import Progressbar from "#components/Ausweis/Progressbar.svelte"; import Progressbar from "#components/Ausweis/Progressbar.svelte";
import FileGrid from "#components/FileGrid.svelte"; import FileGrid from "#components/FileGrid.svelte";
import Hilfe from "#components/Ausweis/Hilfe.svelte"; import ButtonWeiterHilfe from "#components/Ausweis/ButtonWeiterHilfe.svelte";
import Overlay from "#components/Overlay.svelte";
import EmbeddedAuthFlowModule from "#modules/EmbeddedAuthFlowModule.svelte";
import { validateAccessTokenClient } from "#client/lib/validateAccessToken.js";
import { api } from "astro-typesafe-api/client";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
import { exclude } from "#lib/exclude.js";
import Cookies from "js-cookie";
export async function nachweisSpeichern(
nachweis: GEGNachweisWohnenClient,
objekt: ObjektClient,
aufnahme: AufnahmeClient,
unterlagen: (UnterlageClient & { data?: string })[]
) {
if (objekt.uid) {
await api.objekt._uid.PATCH.fetch({
...exclude(objekt, ["uid"])
}, {
params: {
uid: objekt.uid
},
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
} else {
const { uid } = await api.objekt.PUT.fetch({
...exclude(objekt, ["uid"])
}, {
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
objekt.uid = uid;
}
if (aufnahme.uid) {
await api.aufnahme._uid.PATCH.fetch({
...exclude({...aufnahme, baujahr_klima: []}, ["uid"])
}, {
params: {
uid: aufnahme.uid
},
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
} else {
const { uid } = await api.aufnahme.PUT.fetch({
aufnahme: {...aufnahme, baujahr_klima: []},
uid_objekt: objekt.uid
}, {
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
aufnahme.uid = uid
}
if (nachweis.uid) {
await api["geg-nachweis-wohnen"]._uid.PATCH.fetch({
...exclude(nachweis, ["uid"])
}, {
params: {
uid: nachweis.uid
},
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
} else {
const { uid } = await api["geg-nachweis-wohnen"].PUT.fetch({
nachweis: nachweis,
uid_aufnahme: aufnahme.uid
}, {
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
nachweis.uid = uid;
}
for (const unterlage of unterlagen) {
if (unterlage.uid || !unterlage.data) {
continue;
}
const response = await api.objekt._uid.unterlagen.PUT.fetch({
data: unterlage.data,
kategorie: unterlage.kategorie,
mime: unterlage.mime,
name: unterlage.name
}, {
params: {
uid: objekt.uid
},
headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
})
unterlage.uid = response.uid
}
return {
uid_ausweis: nachweis.uid,
uid_aufnahme: aufnahme.uid,
uid_objekt: objekt.uid
}
}
async function nachweisAbschicken() {
if (!(await validateAccessTokenClient())) {
loginOverlayHidden = false;
return;
}
const result = await nachweisSpeichern(nachweis, objekt, aufnahme, unterlagen)
if (result !== null) {
window.history.pushState(
{},
"",
`${location.pathname}?uid=${result.uid_ausweis}`
);
window.location.href = `/kundendaten?uid=${result.uid_ausweis}`;
}
loginOverlayHidden = true;
}
async function spaeterWeitermachen() {
if (!(await validateAccessTokenClient())) {
loginOverlayHidden = false;
return;
}
loginOverlayHidden = true;
}
let loginOverlayHidden = true;
export let nachweis: GEGNachweisWohnenClient; export let nachweis: GEGNachweisWohnenClient;
export let objekt: ObjektClient; export let objekt: ObjektClient;
export let aufnahme: AufnahmeClient; export let aufnahme: AufnahmeClient;
export let user: BenutzerClient = {} as BenutzerClient; export let user: BenutzerClient = {} as BenutzerClient;
export let bilder: UploadedGebaeudeBild[] = []; export let bilder: UploadedGebaeudeBild[] = [];
export let plaene: (UnterlageClient & { data: string })[] = export let plaene: UnterlageClient[] =
[]; [];
export let unterlagen: (UnterlageClient & { export let unterlagen: UnterlageClient[] = [];
data: string;
})[] = [];
if (Object.keys(nachweis).length === 0) { if (Object.keys(nachweis).length === 0) {
const localStorageAusweis = localStorage.getItem("ausweis"); const localStorageAusweis = localStorage.getItem("ausweis");
@@ -199,17 +48,27 @@
} }
} }
if (Object.keys(bilder).length === 0) {
const localStorageBilder = localStorage.getItem("bilder");
if (localStorageBilder) {
bilder = JSON.parse(localStorageBilder)
}
}
if (Object.keys(unterlagen).length === 0) {
const localStorageUnterlagen = localStorage.getItem("unterlagen");
if (localStorageUnterlagen) {
unterlagen = JSON.parse(localStorageUnterlagen)
}
}
$: { $: {
if (nachweis.uid && objekt.uid && aufnahme.uid) { localStorage.setItem("ausweis", JSON.stringify(nachweis))
localStorage.setItem(nachweis.uid, JSON.stringify(nachweis)) localStorage.setItem("aufnahme", JSON.stringify(aufnahme))
localStorage.setItem(objekt.uid, JSON.stringify(objekt)) localStorage.setItem("objekt", JSON.stringify(objekt))
localStorage.setItem(aufnahme.uid, JSON.stringify(aufnahme)) localStorage.setItem("bilder", JSON.stringify(bilder))
} else { localStorage.setItem("unterlagen", JSON.stringify(unterlagen))
localStorage.setItem("ausweis", JSON.stringify(nachweis))
localStorage.setItem("aufnahme", JSON.stringify(aufnahme))
localStorage.setItem("objekt", JSON.stringify(objekt))
}
} }
const ausweisart = Enums.Ausweisart.GEGNachweisWohnen; const ausweisart = Enums.Ausweisart.GEGNachweisWohnen;
@@ -234,7 +93,6 @@
<form <form
id="formInput-1" id="formInput-1"
on:submit={nachweisAbschicken}
name="ausweis" name="ausweis"
data-test="ausweis" data-test="ausweis"
> >
@@ -332,31 +190,15 @@
</Bereich> </Bereich>
</div> </div>
<div <ButtonWeiterHilfe
class="grid grid-cols-[1fr_min-content_min-content_min-content] grid-rows-[min_content_1fr] gap-x-2 self-start justify-self-end mt-8" bind:ausweis={nachweis}
bind:bilder
bind:unterlagen
bind:user
bind:objekt
bind:aufnahme
ausweisart={Enums.Ausweisart.GEGNachweisWohnen}
> >
<div></div> </ButtonWeiterHilfe>
<Hilfe />
<button class="button" type="button" on:click={spaeterWeitermachen}
>Später Weitermachen
</button>
<div>
<Overlay bind:hidden={loginOverlayHidden}>
<div class="bg-white w-full max-w-screen-sm py-8">
<EmbeddedAuthFlowModule onLogin={nachweisAbschicken}
></EmbeddedAuthFlowModule>
</div>
</Overlay>
<button
on:click={nachweisAbschicken}
type="button"
class="button"
data-cy="weiter">Weiter</button
>
</div>
</div>
</form> </form>

View File

@@ -1,9 +1,67 @@
import { defineApiRoute } from "astro-typesafe-api/server"; import { getAusweisartFromUUID, UUidWithPrefix } from "#components/Ausweis/types.js";
import { omit } from "#lib/helpers.js";
// TODO import { authorizationHeaders, authorizationMiddleware } from "#lib/middleware/authorization.js";
import { Enums, GEGEinpreisungSchema, prisma } from "@ibcornelsen/database/server";
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { z } from "zod";
export const PUT = defineApiRoute({ export const PUT = defineApiRoute({
async fetch(input, context, transfer) { input: GEGEinpreisungSchema.omit({
benutzer_id: true,
id: true,
uid: true,
status: true,
}).merge(z.object({
nachweis_uid: UUidWithPrefix
})),
headers: authorizationHeaders,
middleware: authorizationMiddleware,
async fetch(input, context, user) {
const ausweisart = getAusweisartFromUUID(input.nachweis_uid);
let einpreisung;
if (ausweisart === Enums.Ausweisart.GEGNachweisWohnen) {
const nachweis = await prisma.gEGNachweisWohnen.findUnique({
where: {
uid: input.nachweis_uid
}
})
if (!nachweis || nachweis.benutzer_id !== user.id) {
throw new APIError({
code: "BAD_REQUEST",
message: "Ausweis existiert nicht oder gehört einem anderen Benutzer."
})
}
einpreisung = await prisma.gEGEinpreisung.create({
data: {
...omit(input, ["nachweis_uid"]),
status: Enums.Einpreisungsstatus.open,
benutzer: {
connect: {
id: user.id
}
},
geg_nachweis_wohnen: {
connect: {
uid: input.nachweis_uid
}
}
}
})
} else {
throw new APIError({
"code": "BAD_REQUEST",
"message": `Ausweisart wird nicht unterstützt: ${ausweisart}`
})
}
return {
uid: einpreisung.uid
}
}, },
}) })

View File

@@ -16,7 +16,6 @@ export const PUT = defineApiRoute({
output: z.object({ output: z.object({
uid: z.string({ description: "Die UID der Unterlage." }) uid: z.string({ description: "Die UID der Unterlage." })
}), }),
middleware: authorizationMiddleware,
async fetch({ data, name, kategorie, mime }, ctx, user) { async fetch({ data, name, kategorie, mime }, ctx, user) {
if (mime !== "application/pdf" && mime !== "image/png" && mime !== "image/jpeg") { if (mime !== "application/pdf" && mime !== "image/png" && mime !== "image/jpeg") {
throw new APIError({ throw new APIError({
@@ -25,37 +24,20 @@ export const PUT = defineApiRoute({
}) })
} }
let aufnahme = await prisma.aufnahme.findUnique({
where: {
uid: ctx.params.uid,
benutzer_id: user.id
},
});
if (!aufnahme) {
throw new APIError({
code: "NOT_FOUND",
message: "Objekt nicht gefunden oder gehört einem anderen Benutzer.",
});
}
const buffer = Buffer.from(data, "base64"); const buffer = Buffer.from(data, "base64");
const unterlage = await prisma.unterlage.create({ const unterlage = await prisma.unterlage.create({
data: { data: {
kategorie: kategorie, kategorie: kategorie,
aufnahme: { mime,
connect: { name
id: aufnahme.id,
},
},
}, },
select: { select: {
uid: true, uid: true,
}, },
}); });
const filePath = fileURLToPath(new URL(`../../../../../persistent/unterlagen/${unterlage.uid}`, import.meta.url)); const filePath = fileURLToPath(new URL(`../../../persistent/unterlagen/${unterlage.uid}`, import.meta.url));
try { try {
writeFileSync(filePath, buffer) writeFileSync(filePath, buffer)
@@ -68,7 +50,7 @@ export const PUT = defineApiRoute({
}) })
// Und geben einen Fehler zurück // Und geben einen Fehler zurück
throw new APIError({ throw new APIError({
code: "INTERNAL_SERVER_ERROR", code: "UNPROCESSABLE_CONTENT",
message: "Unterlage konnte nicht gespeichert werden.", message: "Unterlage konnte nicht gespeichert werden.",
}); });
} }

View File

@@ -0,0 +1,31 @@
---
import Layout from "#layouts/Layout.astro";
import { getPrismaAusweisAdapter } from "#lib/server/ausweis";
import { getCurrentUser } from "#lib/server/user";
import { prisma } from "@ibcornelsen/database/server";
const uidEinpreisung = Astro.url.searchParams.get("e");
const uidAusweis = Astro.url.searchParams.get("a");
const user = await getCurrentUser(Astro)
if (!uidEinpreisung || !uidAusweis || !user) {
return Astro.redirect("/")
}
const einpreisung = await prisma.gEGEinpreisung.findUnique({
where: {
uid: uidEinpreisung,
benutzer: {
uid: user.uid
}
}
})
if (!einpreisung) {
return Astro.redirect("/")
}
---
<Layout title="GEG Anforderung erfolgreich">
<h1>GEG Anforderung erfolgreich</h1>
</Layout>