From 00015b30cd6a30a594a1e50dc1046b9c01fa827c Mon Sep 17 00:00:00 2001 From: Moritz Utcke Date: Wed, 12 Mar 2025 18:14:13 -0300 Subject: [PATCH] Bedarfsausweis gewerbe --- dbml/schema.dbml | 18 +- .../migration.sql | 66 +++++ .../migrations/20250312210347_/migration.sql | 62 +++++ prisma/schema/BedarfsausweisGewerbe.prisma | 33 ++- prisma/schema/Bild.prisma | 1 + prisma/schema/GEGNachweisGewerbe.prisma | 2 +- prisma/schema/schema.prisma | 1 - src/astro-typesafe-api-caller.ts | 11 +- src/client/lib/helpers.ts | 7 +- src/client/lib/nachweisSpeichern.ts | 3 + src/components/Ausweis/AnlagenTechnik.svelte | 110 +++++++++ .../Ausweis/AnlagenTechnikImage.svelte | 39 +++ .../Ausweis/ButtonWeiterHilfe.svelte | 2 +- src/components/Ausweis/DaemmungImage.svelte | 2 +- .../Dashboard/DashboardAusweis.svelte | 5 +- .../Dashboard/DashboardNachweis.svelte | 2 +- .../GEGNachweis/GEGAusweisart.svelte | 2 +- src/generated/enums.ts | 2 +- src/generated/zod/bedarfsausweisgewerbe.ts | 17 ++ src/lib/pdf/pdfVerbrauchsausweisGewerbe.ts | 128 +++++----- src/lib/server/mail/invoice.ts | 9 +- src/lib/server/mail/payment-success.ts | 8 +- src/modules/KundendatenModule.svelte | 14 +- .../VerbrauchsausweisWohnenModule.svelte | 2 +- .../BedarfsausweisGewerbeModule.svelte | 233 ++++++++++++++++++ .../index.astro | 91 ++++++- src/pages/api/bedarfsausweis-gewerbe/[uid].ts | 212 ++++++++++++++++ src/pages/api/bedarfsausweis-gewerbe/index.ts | 144 +++++++++++ src/pages/api/rechnung/[uid].ts | 2 +- src/pages/api/rechnung/anfordern.ts | 23 ++ src/server/lib/hash.ts | 13 + src/types/fake-data.ts | 26 +- 32 files changed, 1182 insertions(+), 108 deletions(-) create mode 100644 prisma/migrations/20250312210235_bedarfsausweis_gewerbe/migration.sql create mode 100644 prisma/migrations/20250312210347_/migration.sql create mode 100644 src/components/Ausweis/AnlagenTechnik.svelte create mode 100644 src/components/Ausweis/AnlagenTechnikImage.svelte create mode 100644 src/modules/angebot-anfragen/BedarfsausweisGewerbeModule.svelte create mode 100644 src/pages/api/bedarfsausweis-gewerbe/[uid].ts create mode 100644 src/pages/api/bedarfsausweis-gewerbe/index.ts create mode 100644 src/server/lib/hash.ts diff --git a/dbml/schema.dbml b/dbml/schema.dbml index dfafbfd6..a7e5305a 100644 --- a/dbml/schema.dbml +++ b/dbml/schema.dbml @@ -108,6 +108,22 @@ Table Aufnahme { Table BedarfsausweisGewerbe { id Int [pk, increment] uid String [unique, not null] + ausstellgrund Ausstellgrund [note: '@zod.describe("Ausstellgrund wie z.B. Vermietung oder Verkauf")'] + keller_beheizt Boolean [note: '@zod.describe("Falls der Keller des Gebäudes beheizt wird, sollte dieser Wert auf true stehen")'] + storniert Boolean [default: false, note: '@zod.describe("Falls die Nachweisanfrage storniert wurde, sollte dieser Wert auf true stehen")'] + bestellt Boolean [default: false, note: '@zod.describe("Falls der Nachweis bestellt wurde, sollte dieser Wert auf true stehen")'] + zurueckgestellt Boolean [default: false, note: '@zod.describe("Falls der Nachweis vom Aussteller zurückgestellt wurde, sollte dieser Wert auf true stehen")'] + abluftanlage Boolean [default: false] + zu_abluftanlage Boolean [default: false] + konditionierung_der_zuluft Boolean [default: false] + luftheizung Boolean [default: false] + hallenheizung Boolean [default: false] + dunkelstrahler Boolean [default: false] + direktheizung Boolean [default: false] + infrarotstrahler Boolean [default: false] + fussbodenheizung Boolean [default: false] + bauteilaktivierung Boolean [default: false] + klimatisierung Boolean [default: false] benutzer_id Int [note: '@zod.describe("Die ID des Benutzers, welchem dieser Ausweis gehört")'] benutzer benutzer aufnahme_id Int [unique, not null] @@ -571,6 +587,7 @@ Enum BilderKategorie { Fenster Gebaeude Daemmung + AnlagenTechnik } Enum Einpreisungsstatus { @@ -631,7 +648,6 @@ Enum Ausweisart { BedarfsausweisWohnen BedarfsausweisGewerbe GEGNachweisWohnen - GEGNachweisBedarfsausweis GEGNachweisGewerbe } diff --git a/prisma/migrations/20250312210235_bedarfsausweis_gewerbe/migration.sql b/prisma/migrations/20250312210235_bedarfsausweis_gewerbe/migration.sql new file mode 100644 index 00000000..99b144c7 --- /dev/null +++ b/prisma/migrations/20250312210235_bedarfsausweis_gewerbe/migration.sql @@ -0,0 +1,66 @@ +-- AlterEnum +ALTER TYPE "BilderKategorie" ADD VALUE 'AnlagenTechnik'; + +-- AlterTable +ALTER TABLE "Anteilshaber" ALTER COLUMN "uid" SET DEFAULT 'ant-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Aufnahme" ALTER COLUMN "uid" SET DEFAULT 'auf-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "BedarfsausweisGewerbe" ADD COLUMN "abluftanlage" BOOLEAN DEFAULT false, +ADD COLUMN "ausstellgrund" "Ausstellgrund", +ADD COLUMN "bauteilaktivierung" BOOLEAN DEFAULT false, +ADD COLUMN "bestellt" BOOLEAN DEFAULT false, +ADD COLUMN "direktheizung" BOOLEAN DEFAULT false, +ADD COLUMN "dunkelstrahler" BOOLEAN DEFAULT false, +ADD COLUMN "fussbodenheizung" BOOLEAN DEFAULT false, +ADD COLUMN "hallenheizung" BOOLEAN DEFAULT false, +ADD COLUMN "infrarotstrahler" BOOLEAN DEFAULT false, +ADD COLUMN "keller_beheizt" BOOLEAN, +ADD COLUMN "klimatisierung" BOOLEAN DEFAULT false, +ADD COLUMN "konditionierung_der_zuluft" BOOLEAN DEFAULT false, +ADD COLUMN "luftheizung" BOOLEAN DEFAULT false, +ADD COLUMN "storniert" BOOLEAN DEFAULT false, +ADD COLUMN "zu_abluftanlage" BOOLEAN DEFAULT false, +ADD COLUMN "zurueckgestellt" BOOLEAN DEFAULT false, +ALTER COLUMN "uid" SET DEFAULT 'bag-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "BedarfsausweisWohnen" ALTER COLUMN "uid" SET DEFAULT 'baw-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Bild" ALTER COLUMN "uid" SET DEFAULT 'img-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Event" ALTER COLUMN "uid" SET DEFAULT 'evt-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "GEGEinpreisung" ALTER COLUMN "uid" SET DEFAULT 'gge-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "GEGNachweisGewerbe" ALTER COLUMN "uid" SET DEFAULT 'gng-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "GEGNachweisWohnen" ALTER COLUMN "uid" SET DEFAULT 'gnw-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Objekt" ALTER COLUMN "uid" SET DEFAULT 'obj-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Rechnung" ALTER COLUMN "uid" SET DEFAULT 'inv-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Tickets" ALTER COLUMN "uid" SET DEFAULT 'tkt-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Unterlage" ALTER COLUMN "uid" SET DEFAULT 'pln-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "VerbrauchsausweisGewerbe" ALTER COLUMN "uid" SET DEFAULT 'vag-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "VerbrauchsausweisWohnen" ALTER COLUMN "uid" SET DEFAULT 'vaw-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "benutzer" ALTER COLUMN "uid" SET DEFAULT 'usr-' || gen_random_uuid(); diff --git a/prisma/migrations/20250312210347_/migration.sql b/prisma/migrations/20250312210347_/migration.sql new file mode 100644 index 00000000..ffcf3ff6 --- /dev/null +++ b/prisma/migrations/20250312210347_/migration.sql @@ -0,0 +1,62 @@ +/* + Warnings: + + - The values [GEGNachweisBedarfsausweis] on the enum `Ausweisart` will be removed. If these variants are still used in the database, this will fail. + +*/ +-- AlterEnum +BEGIN; +CREATE TYPE "Ausweisart_new" AS ENUM ('VerbrauchsausweisWohnen', 'VerbrauchsausweisGewerbe', 'BedarfsausweisWohnen', 'BedarfsausweisGewerbe', 'GEGNachweisWohnen', 'GEGNachweisGewerbe'); +ALTER TABLE "Aufnahme" ALTER COLUMN "ausweisart" TYPE "Ausweisart_new" USING ("ausweisart"::text::"Ausweisart_new"); +ALTER TYPE "Ausweisart" RENAME TO "Ausweisart_old"; +ALTER TYPE "Ausweisart_new" RENAME TO "Ausweisart"; +DROP TYPE "Ausweisart_old"; +COMMIT; + +-- AlterTable +ALTER TABLE "Anteilshaber" ALTER COLUMN "uid" SET DEFAULT 'ant-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Aufnahme" ALTER COLUMN "uid" SET DEFAULT 'auf-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "BedarfsausweisGewerbe" ALTER COLUMN "uid" SET DEFAULT 'bag-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "BedarfsausweisWohnen" ALTER COLUMN "uid" SET DEFAULT 'baw-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Bild" ALTER COLUMN "uid" SET DEFAULT 'img-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Event" ALTER COLUMN "uid" SET DEFAULT 'evt-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "GEGEinpreisung" ALTER COLUMN "uid" SET DEFAULT 'gge-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "GEGNachweisGewerbe" ALTER COLUMN "uid" SET DEFAULT 'gng-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "GEGNachweisWohnen" ALTER COLUMN "uid" SET DEFAULT 'gnw-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Objekt" ALTER COLUMN "uid" SET DEFAULT 'obj-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Rechnung" ALTER COLUMN "uid" SET DEFAULT 'inv-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Tickets" ALTER COLUMN "uid" SET DEFAULT 'tkt-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "Unterlage" ALTER COLUMN "uid" SET DEFAULT 'pln-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "VerbrauchsausweisGewerbe" ALTER COLUMN "uid" SET DEFAULT 'vag-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "VerbrauchsausweisWohnen" ALTER COLUMN "uid" SET DEFAULT 'vaw-' || gen_random_uuid(); + +-- AlterTable +ALTER TABLE "benutzer" ALTER COLUMN "uid" SET DEFAULT 'usr-' || gen_random_uuid(); diff --git a/prisma/schema/BedarfsausweisGewerbe.prisma b/prisma/schema/BedarfsausweisGewerbe.prisma index 084827e4..f904331e 100644 --- a/prisma/schema/BedarfsausweisGewerbe.prisma +++ b/prisma/schema/BedarfsausweisGewerbe.prisma @@ -1,16 +1,39 @@ model BedarfsausweisGewerbe { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) uid String @unique @default(dbgenerated("'bag-' || gen_random_uuid()")) - /// @zod.describe("Die ID des Benutzers, welchem dieser Ausweis gehört") + /// @zod.describe("Ausstellgrund wie z.B. Vermietung oder Verkauf") + ausstellgrund Ausstellgrund? + /// @zod.describe("Falls der Keller des Gebäudes beheizt wird, sollte dieser Wert auf true stehen") + keller_beheizt Boolean? + + /// @zod.describe("Falls die Nachweisanfrage storniert wurde, sollte dieser Wert auf true stehen") + storniert Boolean? @default(false) + /// @zod.describe("Falls der Nachweis bestellt wurde, sollte dieser Wert auf true stehen") + bestellt Boolean? @default(false) + /// @zod.describe("Falls der Nachweis vom Aussteller zurückgestellt wurde, sollte dieser Wert auf true stehen") + zurueckgestellt Boolean? @default(false) + + abluftanlage Boolean? @default(false) + zu_abluftanlage Boolean? @default(false) + konditionierung_der_zuluft Boolean? @default(false) + luftheizung Boolean? @default(false) + hallenheizung Boolean? @default(false) + dunkelstrahler Boolean? @default(false) + direktheizung Boolean? @default(false) + infrarotstrahler Boolean? @default(false) + fussbodenheizung Boolean? @default(false) + bauteilaktivierung Boolean? @default(false) + klimatisierung Boolean? @default(false) + + /// @zod.describe("Die ID des Benutzers, welchem dieser Ausweis gehört") benutzer_id Int? benutzer Benutzer? @relation(fields: [benutzer_id], references: [id], onDelete: NoAction, onUpdate: NoAction) - aufnahme_id Int @unique + aufnahme_id Int @unique aufnahme Aufnahme @relation(fields: [aufnahme_id], references: [id], onDelete: NoAction, onUpdate: NoAction) - rechnung_id Int? @unique + rechnung_id Int? @unique rechnung Rechnung? @relation(fields: [rechnung_id], references: [id], onDelete: NoAction, onUpdate: NoAction) - geg_einpreisung_id Int? @unique geg_einpreisung GEGEinpreisung? @relation(fields: [geg_einpreisung_id], references: [id], onDelete: NoAction, onUpdate: NoAction) } \ No newline at end of file diff --git a/prisma/schema/Bild.prisma b/prisma/schema/Bild.prisma index c1910716..9d248380 100644 --- a/prisma/schema/Bild.prisma +++ b/prisma/schema/Bild.prisma @@ -4,6 +4,7 @@ enum BilderKategorie { Fenster Gebaeude Daemmung + AnlagenTechnik } model Bild { diff --git a/prisma/schema/GEGNachweisGewerbe.prisma b/prisma/schema/GEGNachweisGewerbe.prisma index cdcb9bdf..19a2d401 100644 --- a/prisma/schema/GEGNachweisGewerbe.prisma +++ b/prisma/schema/GEGNachweisGewerbe.prisma @@ -23,7 +23,7 @@ model GEGNachweisGewerbe { aufnahme_id Int @unique aufnahme Aufnahme @relation(fields: [aufnahme_id], references: [id], onDelete: NoAction, onUpdate: NoAction) - rechnung_id Int? @unique + rechnung_id Int? @unique rechnung Rechnung? @relation(fields: [rechnung_id], references: [id], onDelete: NoAction, onUpdate: NoAction) geg_einpreisung_id Int? @unique geg_einpreisung GEGEinpreisung? @relation(fields: [geg_einpreisung_id], references: [id], onDelete: NoAction, onUpdate: NoAction) diff --git a/prisma/schema/schema.prisma b/prisma/schema/schema.prisma index c3b8885c..1b46436d 100644 --- a/prisma/schema/schema.prisma +++ b/prisma/schema/schema.prisma @@ -53,7 +53,6 @@ enum Ausweisart { BedarfsausweisWohnen BedarfsausweisGewerbe GEGNachweisWohnen - GEGNachweisBedarfsausweis GEGNachweisGewerbe } diff --git a/src/astro-typesafe-api-caller.ts b/src/astro-typesafe-api-caller.ts index 38206f31..96865d20 100644 --- a/src/astro-typesafe-api-caller.ts +++ b/src/astro-typesafe-api-caller.ts @@ -5,7 +5,6 @@ export const createCaller = createCallerFactory({ "klimafaktoren": await import("../src/pages/api/klimafaktoren.ts"), "postleitzahlen": await import("../src/pages/api/postleitzahlen.ts"), "unterlage": await import("../src/pages/api/unterlage.ts"), - "aufnahme": await import("../src/pages/api/aufnahme/index.ts"), "admin/ausstellen": await import("../src/pages/api/admin/ausstellen.ts"), "admin/bestellbestaetigung": await import("../src/pages/api/admin/bestellbestaetigung.ts"), "admin/erinnern": await import("../src/pages/api/admin/erinnern.ts"), @@ -13,13 +12,15 @@ export const createCaller = createCallerFactory({ "admin/post-ausstellen": await import("../src/pages/api/admin/post-ausstellen.ts"), "admin/registriernummer": await import("../src/pages/api/admin/registriernummer.ts"), "admin/stornieren": await import("../src/pages/api/admin/stornieren.ts"), + "ausweise": await import("../src/pages/api/ausweise/index.ts"), + "aufnahme": await import("../src/pages/api/aufnahme/index.ts"), "auth/access-token": await import("../src/pages/api/auth/access-token.ts"), "auth/passwort-vergessen": await import("../src/pages/api/auth/passwort-vergessen.ts"), "auth/refresh-token": await import("../src/pages/api/auth/refresh-token.ts"), - "ausweise": await import("../src/pages/api/ausweise/index.ts"), + "bedarfsausweis-gewerbe/[uid]": await import("../src/pages/api/bedarfsausweis-gewerbe/[uid].ts"), + "bedarfsausweis-gewerbe": await import("../src/pages/api/bedarfsausweis-gewerbe/index.ts"), "bedarfsausweis-wohnen/[uid]": await import("../src/pages/api/bedarfsausweis-wohnen/[uid].ts"), "bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"), - "aufnahme": await import("../src/pages/api/aufnahme/index.ts"), "bilder/[uid]": await import("../src/pages/api/bilder/[uid].ts"), "geg-nachweis-gewerbe/[uid]": await import("../src/pages/api/geg-nachweis-gewerbe/[uid].ts"), "geg-nachweis-gewerbe": await import("../src/pages/api/geg-nachweis-gewerbe/index.ts"), @@ -30,12 +31,12 @@ export const createCaller = createCallerFactory({ "rechnung/anfordern": await import("../src/pages/api/rechnung/anfordern.ts"), "rechnung": await import("../src/pages/api/rechnung/index.ts"), "ticket": await import("../src/pages/api/ticket/index.ts"), + "verbrauchsausweis-gewerbe/[uid]": await import("../src/pages/api/verbrauchsausweis-gewerbe/[uid].ts"), + "verbrauchsausweis-gewerbe": await import("../src/pages/api/verbrauchsausweis-gewerbe/index.ts"), "user": await import("../src/pages/api/user/index.ts"), "user/self": await import("../src/pages/api/user/self.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-gewerbe/[uid]": await import("../src/pages/api/verbrauchsausweis-gewerbe/[uid].ts"), - "verbrauchsausweis-gewerbe": await import("../src/pages/api/verbrauchsausweis-gewerbe/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]": await import("../src/pages/api/aufnahme/[uid]/index.ts"), diff --git a/src/client/lib/helpers.ts b/src/client/lib/helpers.ts index 8017ff52..66dcc00e 100644 --- a/src/client/lib/helpers.ts +++ b/src/client/lib/helpers.ts @@ -58,9 +58,4 @@ export function getZodBaseType(schema: ZodType): ZodType { return getZodBaseType(schema._def.schema) } return schema; -} - -export const sqids = new Sqids({ - alphabet: "0123456789abcdefghijklmnopqrstuvw", - minLength: 8 - }) \ No newline at end of file +} \ No newline at end of file diff --git a/src/client/lib/nachweisSpeichern.ts b/src/client/lib/nachweisSpeichern.ts index 082383bf..d0ec3ca0 100644 --- a/src/client/lib/nachweisSpeichern.ts +++ b/src/client/lib/nachweisSpeichern.ts @@ -184,6 +184,9 @@ export async function nachweisSpeichern( } else if (ausweisart === Enums.Ausweisart.GEGNachweisGewerbe) { patchRoute = api["geg-nachweis-gewerbe"]._uid.PATCH putRoute = api["geg-nachweis-gewerbe"].PUT + } else if (ausweisart === Enums.Ausweisart.BedarfsausweisGewerbe) { + patchRoute = api["bedarfsausweis-gewerbe"]._uid.PATCH + putRoute = api["bedarfsausweis-gewerbe"].PUT } if (nachweis.uid) { diff --git a/src/components/Ausweis/AnlagenTechnik.svelte b/src/components/Ausweis/AnlagenTechnik.svelte new file mode 100644 index 00000000..757f9cc2 --- /dev/null +++ b/src/components/Ausweis/AnlagenTechnik.svelte @@ -0,0 +1,110 @@ + + +
+ + + + + + + + + + + +
+ + + diff --git a/src/components/Ausweis/AnlagenTechnikImage.svelte b/src/components/Ausweis/AnlagenTechnikImage.svelte new file mode 100644 index 00000000..a61fdaa9 --- /dev/null +++ b/src/components/Ausweis/AnlagenTechnikImage.svelte @@ -0,0 +1,39 @@ + + +
+ +
+ + +
+ WICHTIG: + Bild Upload - Anlagentechnik +
+
+ +
+
+ Diese Bilder erscheinen nicht auf Ihrem + Energieausweis!
+
+ + TEXT FEHLT + +
+
diff --git a/src/components/Ausweis/ButtonWeiterHilfe.svelte b/src/components/Ausweis/ButtonWeiterHilfe.svelte index 635c412e..42dff87e 100644 --- a/src/components/Ausweis/ButtonWeiterHilfe.svelte +++ b/src/components/Ausweis/ButtonWeiterHilfe.svelte @@ -70,7 +70,7 @@ loginOverlayHidden = true let result: Awaited> | Awaited> | null = null; - if (ausweisart === Enums.Ausweisart.GEGNachweisWohnen || ausweisart === Enums.Ausweisart.GEGNachweisGewerbe || ausweisart === Enums.Ausweisart.GEGNachweisBedarfsausweis) { + if (ausweisart === Enums.Ausweisart.GEGNachweisWohnen || ausweisart === Enums.Ausweisart.GEGNachweisGewerbe || ausweisart === Enums.Ausweisart.BedarfsausweisGewerbe) { result = await nachweisSpeichern(ausweis, objekt, aufnahme, bilder, unterlagen, ausweisart) } else { result = await ausweisSpeichern(ausweis, objekt, aufnahme, bilder, ausweisart) diff --git a/src/components/Ausweis/DaemmungImage.svelte b/src/components/Ausweis/DaemmungImage.svelte index 9b79ec51..76c74a04 100644 --- a/src/components/Ausweis/DaemmungImage.svelte +++ b/src/components/Ausweis/DaemmungImage.svelte @@ -1,6 +1,6 @@ + +
+ +
+ +
+
+ + + + + + + + + + + + + + + + + + + +
+
+
Pläne
+ +
+ Hier können sie Grundrisspläne, Ansichtspläne und + Schnitte hochladen. Die Dateien können entweder im PDF + Format oder als Bild hochgeladen werden. +
+
+ +
+
+ Bitte laden Sie hier mind. 1 Dokument hoch: +
+ +
+
+
+
+
Unterlagen
+ +
+ Hier können sie weitere Unterlagen wie z.B. + Baugenehmigungen, U-Wert Berechnungen, Anlagentechnik + oder ihren alten Energieausweis hochladen. Die Dateien + können entweder im PDF Format oder als Bild hochgeladen + werden. +
+
+ +
+ +
+
+
+
+ + +
diff --git a/src/pages/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/index.astro b/src/pages/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/index.astro index d2fcab26..68d56348 100644 --- a/src/pages/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/index.astro +++ b/src/pages/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/index.astro @@ -1 +1,90 @@ -bedarfsausweis Gewerbe anfragen \ No newline at end of file +--- +import AusweisLayout from "#layouts/AusweisLayoutDaten.astro"; +import { AufnahmeClient, BildClient, GEGNachweisWohnenClient, ObjektClient, UnterlageClient } from "#components/Ausweis/types"; +import { createCaller } from "src/astro-typesafe-api-caller"; +import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js"; +import { validateAccessTokenServer } from "#server/lib/validateAccessToken.js"; +import BedarfsausweisGewerbeModule from "#modules/angebot-anfragen/BedarfsausweisGewerbeModule.svelte"; + +const uid = Astro.url.searchParams.get("uid"); +let nachweis: GEGNachweisWohnenClient = {} as GEGNachweisWohnenClient; +let aufnahme: AufnahmeClient = {} as AufnahmeClient; +let objekt: ObjektClient = {} as ObjektClient; +let bilder: BildClient[] = [] +let unterlagen: UnterlageClient[] = [] + +const valid = validateAccessTokenServer(Astro); + +const caller = createCaller(Astro); + +if (uid) { + if (!valid) { + return Astro.redirect( + `/auth/login?redirect=${Astro.url.toString()}` + ); + } + + try { + nachweis = await caller["bedarfsausweis-gewerbe"]._uid.GET.fetch(null, { + headers: { + authorization: `Bearer ${Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value}` + }, + params: { + uid + } + }); + + aufnahme = await caller.aufnahme._uid.GET.fetch(null, { + headers: { + authorization: `Bearer ${Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value}` + }, + params: { + uid: nachweis.uid_aufnahme + } + }) + + objekt = await caller.objekt._uid.GET.fetch(null, { + headers: { + authorization: `Bearer ${Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value}` + }, + params: { + uid: nachweis.uid_objekt + } + }) + + bilder = await caller.aufnahme._uid.bilder.GET.fetch(null, { + headers: { + authorization: `Bearer ${Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value}` + }, + params: { + uid: nachweis.uid_aufnahme + } + }) + + unterlagen = await caller.aufnahme._uid.unterlagen.GET.fetch(null, { + headers: { + authorization: `Bearer ${Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value}` + }, + params: { + uid: nachweis.uid_aufnahme + } + }) + + if (!nachweis) { + // Der Ausweis scheint nicht zu existieren. + // Wir leiten auf die generische Ausweisseite ohne UID weiter. + return Astro.redirect( + "/angebot-anfragen/bedarfsausweis-gewerbe-anfragen" + ); + } + } catch(e) { + return Astro.redirect( + "/angebot-anfragen/bedarfsausweis-gewerbe-anfragen" + ); + } +} +--- + + + + diff --git a/src/pages/api/bedarfsausweis-gewerbe/[uid].ts b/src/pages/api/bedarfsausweis-gewerbe/[uid].ts new file mode 100644 index 00000000..526312af --- /dev/null +++ b/src/pages/api/bedarfsausweis-gewerbe/[uid].ts @@ -0,0 +1,212 @@ +import { GEGNachweisWohnenClient, OptionalNullable, UUidWithPrefix, ZodOverlap } from "#components/Ausweis/types.js"; +import { exclude } from "#lib/exclude.js"; +import { authorizationHeaders, authorizationMiddleware } from "#lib/middleware/authorization.js"; +import { prisma } from "#lib/server/prisma.js"; +import { APIError, defineApiRoute } from "astro-typesafe-api/server"; +import { BedarfsausweisGewerbeSchema } from "src/generated/zod/bedarfsausweisgewerbe.js"; +import { z } from "zod"; + +export const PATCH = defineApiRoute({ + input: BedarfsausweisGewerbeSchema.omit({ + uid: true, + id: true, + benutzer_id: true, + geg_einpreisung_id: true, + aufnahme_id: true, + }), + output: z.void(), + headers: { + "Authorization": z.string() + }, + middleware: authorizationMiddleware, + async fetch(input, ctx, user) { + const objekt = await prisma.bedarfsausweisGewerbe.findUnique({ + where: { + uid: ctx.params.uid, + benutzer: { + id: user.id + } + } + }) + + if (!objekt) { + throw new APIError({ + code: "NOT_FOUND", + message: "Nachweis konnte nicht gefunden werden oder gehört einem anderen Benutzer." + }) + } + + await prisma.bedarfsausweisGewerbe.update({ + where: { + uid: ctx.params.uid + }, + data: input + }) + }, +}) + +export const DELETE = defineApiRoute({ + meta: { + description: "Storniert einen Nachweis" + }, + headers: authorizationHeaders, + middleware: authorizationMiddleware, + async fetch(input, ctx, user) { + const { uid } = ctx.params; + + if (!UUidWithPrefix.safeParse(uid).success) { + throw new APIError({ + code: "BAD_REQUEST", + message: "UID konnte nicht verifiziert werden." + }) + } + + // Wir holen uns den BedarfsNachweis + // Dieser MUSS mit dem Nutzer verknüpft sein. + const nachweis = await prisma.bedarfsausweisGewerbe.findUnique({ + where: { + uid, + } + }); + + if (!nachweis) { + // Falls wir den Nachweis nicht finden können, werfen wir einen Fehler + throw new APIError({ + code: "NOT_FOUND", + message: "Nachweis konnte nicht gefunden werden.", + }); + } + + // Wir dürfen den Nachweis nur stornieren, wenn er noch nicht ausgestellt wurde + // Außerdem müssen wir schauen, ob wir Admin oder der Besitzer des Nachweises sind. + if ((nachweis.benutzer_id !== user.id) && user.rolle !== "ADMIN") { + // Falls der Nachweis nicht dem Nutzer gehört, werfen wir einen Fehler + throw new APIError({ + code: "FORBIDDEN", + message: "Nachweis gehört nicht dem Nutzer.", + }); + } + + // if (Nachweis.erledigt) { + // // Falls der Nachweis bereits ausgestellt wurde, werfen wir einen Fehler + // throw new TRPCError({ + // code: "BAD_REQUEST", + // message: "Nachweis wurde bereits ausgestellt.", + // }); + // } + + if (nachweis.storniert) { + // Falls der Nachweis bereits storniert ist, werfen wir einen Fehler + throw new APIError({ + code: "BAD_REQUEST", + message: "Nachweis wurde bereits storniert.", + }); + } + + await prisma.gEGNachweisWohnen.update({ + where: { + id: nachweis.id + }, + data: { + storniert: true + } + }) + + // Wir erstellen ein Event, dass der Nachweis storniert wurde + // Dann können wir das in der Historie anzeigen + await prisma.event.create({ + data: { + title: "Nachweis storniert", + description: ((user.rolle === "ADMIN") && (nachweis.benutzer_id !== user.id)) ? "Nachweis wurde von einem Administrator storniert." : "Nachweis wurde vom Besitzer storniert.", + benutzer: { + connect: { + id: user.id + } + }, + aufnahme: { + connect: { + id: nachweis.aufnahme_id + } + } + } + }) + }, +}) + +export const GET = defineApiRoute({ + meta: { + description: "Gibt ein spezifisches Gebäude des Benutzers zurück.", + tags: ["Gebäude"], + headers: { + "Authorization": { + description: "Ein gültiger Authentifizierungstoken", + required: true, + allowEmptyValue: false, + examples: { + Bearer: { + value: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } + } + } + } + }, + output: BedarfsausweisGewerbeSchema.merge(z.object({ + uid_aufnahme: UUidWithPrefix, + uid_objekt: UUidWithPrefix, + uid_benutzer: UUidWithPrefix.optional() + })).omit({ + id: true, + aufnahme_id: true, + benutzer_id: true + }), + middleware: authorizationMiddleware, + async fetch(input, context, user) { + const { uid } = context.params; + + if (!uid) { + throw new APIError({ + code: "BAD_REQUEST", + message: "Missing uid in request params" + }) + } + + const nachweis = await prisma.bedarfsausweisGewerbe.findUnique({ + where: { + uid, + benutzer_id: user.id + }, + include: { + benutzer: { + select: { + uid: true + } + }, + aufnahme: { + select: { + uid: true, + objekt: { + select: { + uid: true + } + } + } + } + } + }); + + if (!nachweis) { + // Falls wir den Nachweis nicht finden können, werfen wir einen Fehler + throw new APIError({ + code: "NOT_FOUND", + message: "Nachweis konnte nicht gefunden werden.", + }); + } + + return { + uid_aufnahme: nachweis.aufnahme.uid, + uid_objekt: nachweis.aufnahme.objekt.uid, + uid_benutzer: nachweis.benutzer?.uid, + ...exclude(nachweis, ["id", "aufnahme_id", "benutzer_id", "aufnahme"]) + } + }, +}); diff --git a/src/pages/api/bedarfsausweis-gewerbe/index.ts b/src/pages/api/bedarfsausweis-gewerbe/index.ts new file mode 100644 index 00000000..367a3d4f --- /dev/null +++ b/src/pages/api/bedarfsausweis-gewerbe/index.ts @@ -0,0 +1,144 @@ +import { UUidWithPrefix } from "#components/Ausweis/types.js"; +import { authorizationHeaders, authorizationMiddleware } from "#lib/middleware/authorization.js"; +import { prisma } from "#lib/server/prisma.js"; +import { APIError, defineApiRoute } from "astro-typesafe-api/server"; +import { BedarfsausweisGewerbeSchema } from "src/generated/zod/bedarfsausweisgewerbe.js"; +import { z } from "zod"; + +export const PUT = defineApiRoute({ + meta: { + contentTypes: ["application/json"], + description: + "Erstellt einen neuen Bedarfsausweis Gewerbe.", + tags: ["GEG Nachweis", "Bedarfsausweis Gewerbe"], + }, + input: z.object({ + nachweis: BedarfsausweisGewerbeSchema.omit({ + id: true, + benutzer_id: true, + uid: true, + aufnahme_id: true, + geg_einpreisung_id: true, + rechnung_id: true + }), + uid_aufnahme: UUidWithPrefix + }), + output: z.object({ + uid: UUidWithPrefix, + objekt_uid: UUidWithPrefix, + aufnahme_uid: UUidWithPrefix, + }), + headers: authorizationHeaders, + middleware: authorizationMiddleware, + async fetch(input, ctx, user) { + const aufnahme = await prisma.aufnahme.findUnique({ + where: { + uid: input.uid_aufnahme + } + }) + + if (!aufnahme || aufnahme.benutzer_id !== user.id) { + throw new APIError({ + code: "FORBIDDEN", + message: "Aufnahme konnte nicht gefunden werden oder gehört nicht zu diesem Benutzer." + }) + } + + const nachweis = await prisma.bedarfsausweisGewerbe.create({ + data: { + ...input.nachweis, + benutzer: { + connect: { + id: user.id, + }, + }, + aufnahme: { + connect: { + id: aufnahme.id, + }, + } + }, + select: { + uid: true, + aufnahme: { + select: { + uid: true, + objekt: { + select: { + uid: true, + }, + }, + }, + }, + }, + }); + + return { + uid: nachweis.uid, + objekt_uid: nachweis.aufnahme.objekt.uid, + aufnahme_uid: nachweis.aufnahme.uid, + }; + }, +}); + +export const GET = defineApiRoute({ + meta: { + description: "Gibt eine spezifische GEG Nachweis Anfrage des Benutzers zurück.", + tags: ["GEG Nachweis"], + headers: { + Authorization: { + description: "Ein gültiger Authentifizierungstoken", + required: true, + allowEmptyValue: false, + examples: { + Bearer: { + value: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + }, + }, + }, + }, + }, + headers: authorizationHeaders, + middleware: authorizationMiddleware, + async fetch(input, context, user) { + const { uid } = context.params; + + const nachweis = await prisma.bedarfsausweisGewerbe.findUnique({ + where: { + uid, + }, + include: { + benutzer: true, + aufnahme: { + include: { + events: { + include: { + benutzer: { + select: { + uid: true, + }, + }, + }, + orderBy: { + date: "asc", + }, + }, + }, + }, + }, + }); + + if ( + !nachweis || + (nachweis.benutzer_id !== null && nachweis.benutzer_id !== user.id) + ) { + // Falls wir den Ausweis nicht finden können, werfen wir einen Fehler + throw new APIError({ + code: "NOT_FOUND", + message: "GEG Nachweis konnte nicht gefunden werden.", + }); + } + + return nachweis; + }, +}); diff --git a/src/pages/api/rechnung/[uid].ts b/src/pages/api/rechnung/[uid].ts index 40c0f49c..dfd64b77 100644 --- a/src/pages/api/rechnung/[uid].ts +++ b/src/pages/api/rechnung/[uid].ts @@ -140,7 +140,7 @@ function getPaymentDescription(ausweisart: Enums.Ausweisart) { return "Verbrauchsausweis Gewerbegebäude" case "VerbrauchsausweisWohnen": return "Verbrauchsausweis Wohngebäude" - case "GEGNachweisBedarfsausweis": + case "BedarfsausweisGewerbe": case "GEGNachweisGewerbe": case "GEGNachweisWohnen": return "GEG Nachweis" diff --git a/src/pages/api/rechnung/anfordern.ts b/src/pages/api/rechnung/anfordern.ts index 21a50ce6..4ada44ff 100644 --- a/src/pages/api/rechnung/anfordern.ts +++ b/src/pages/api/rechnung/anfordern.ts @@ -35,6 +35,12 @@ export const PUT = defineApiRoute({ uid: input.nachweis_uid } }) + } else if (ausweisart === Enums.Ausweisart.BedarfsausweisGewerbe) { + nachweis = await prisma.bedarfsausweisGewerbe.findUnique({ + where: { + uid: input.nachweis_uid + } + }) } else { throw new APIError({ "code": "BAD_REQUEST", @@ -84,6 +90,23 @@ export const PUT = defineApiRoute({ } } }) + } else if (ausweisart === Enums.Ausweisart.BedarfsausweisGewerbe) { + einpreisung = await prisma.gEGEinpreisung.create({ + data: { + ...omit(input, ["nachweis_uid"]), + status: Enums.Einpreisungsstatus.open, + benutzer: { + connect: { + id: user.id + } + }, + bedarfsausweis_gewerbe: { + connect: { + uid: input.nachweis_uid + } + } + } + }) } await sendGEGAnforderungsMail(nachweis, user) diff --git a/src/server/lib/hash.ts b/src/server/lib/hash.ts new file mode 100644 index 00000000..3ec63504 --- /dev/null +++ b/src/server/lib/hash.ts @@ -0,0 +1,13 @@ +function murmurHash36(str: string) { + let h = 0xdeadbeef; + for (let i = 0; i < str.length; i++) { + h = Math.imul(h ^ str.charCodeAt(i), 2654435761); + } + return (h >>> 0).toString(36).toUpperCase().slice(0, 6); +} + +export function shortenUID(uid: string) { + const str = murmurHash36(uid); + const mid = Math.floor(str.length / 2); + return [str.slice(0, mid), str.slice(mid)].join("-"); +} diff --git a/src/types/fake-data.ts b/src/types/fake-data.ts index 77b9fa86..34f9171c 100644 --- a/src/types/fake-data.ts +++ b/src/types/fake-data.ts @@ -165,10 +165,32 @@ export function fakeAufnahmeComplete() { objekt_id: faker.number.int(), }; } +export function fakeBedarfsausweisGewerbe() { + return { + ausstellgrund: undefined, + keller_beheizt: undefined, + }; +} export function fakeBedarfsausweisGewerbeComplete() { return { id: faker.number.int({ max: 2147483647 }), uid: '[object Object]', + ausstellgrund: undefined, + keller_beheizt: undefined, + storniert: false, + bestellt: false, + zurueckgestellt: false, + abluftanlage: false, + zu_abluftanlage: false, + konditionierung_der_zuluft: false, + luftheizung: false, + hallenheizung: false, + dunkelstrahler: false, + direktheizung: false, + infrarotstrahler: false, + fussbodenheizung: false, + bauteilaktivierung: false, + klimatisierung: false, benutzer_id: undefined, aufnahme_id: faker.number.int(), rechnung_id: undefined, @@ -343,13 +365,13 @@ export function fakeBenutzerComplete() { } export function fakeBild() { return { - kategorie: faker.helpers.arrayElement([BilderKategorie.Heizung, BilderKategorie.Fenster, BilderKategorie.Gebaeude, BilderKategorie.Daemmung] as const), + kategorie: faker.helpers.arrayElement([BilderKategorie.Heizung, BilderKategorie.Fenster, BilderKategorie.Gebaeude, BilderKategorie.Daemmung, BilderKategorie.AnlagenTechnik] as const), }; } export function fakeBildComplete() { return { id: faker.number.int({ max: 2147483647 }), - kategorie: faker.helpers.arrayElement([BilderKategorie.Heizung, BilderKategorie.Fenster, BilderKategorie.Gebaeude, BilderKategorie.Daemmung] as const), + kategorie: faker.helpers.arrayElement([BilderKategorie.Heizung, BilderKategorie.Fenster, BilderKategorie.Gebaeude, BilderKategorie.Daemmung, BilderKategorie.AnlagenTechnik] as const), uid: '[object Object]', aufnahme_id: undefined, };