Files
online-energieausweis/src/modules/KundendatenModule.svelte
2025-02-23 22:58:17 +11:00

568 lines
16 KiB
Svelte

<script lang="ts">
import PerformanceScore from "#components/Ausweis/PerformanceScore.svelte";
import Progressbar from "#components/Ausweis/Progressbar.svelte";
import Bereich from "#components/labels/Bereich.svelte";
import Ansprechpartner from "#components/Ausweis/Ansprechpartner.svelte";
import Rechnungsadresse from "#components/Ausweis/Rechnungsadresse.svelte";
import type { Bezahlmethoden } from "#lib/client/prisma.js";
import { Enums } from "#lib/client/prisma.js";
import { dialogs } from "svelte-dialogs";
import LoginDialog from "#components/LoginDialog.svelte";
import {
API_ACCESS_TOKEN_COOKIE_NAME,
PRICES,
} from "#lib/constants.js";
import Cookies from "js-cookie";
import {
AufnahmeClient,
BenutzerClient,
BildClient,
ObjektClient,
RechnungClient,
UnterlageClient,
VerbrauchsausweisWohnenClient,
} from "#components/Ausweis/types.js";
import { validateAccessTokenClient } from "src/client/lib/validateAccessToken.js";
import { api } from "astro-typesafe-api/client";
import PaymentOption from "#components/PaymentOption.svelte";
import Overlay from "#components/Overlay.svelte";
import EmbeddedAuthFlowModule from "./EmbeddedAuthFlowModule.svelte";
import { ausweisSpeichern } from "#client/lib/ausweisSpeichern.js";
import { addNotification } from "#components/Notifications/shared.js";
import NotificationWrapper from "#components/Notifications/NotificationWrapper.svelte";
import { nachweisSpeichern } from "#client/lib/nachweisSpeichern.js";
import { EnterFullScreen } from "radix-svelte-icons";
export let user: Partial<BenutzerClient>;
export let ausweis: VerbrauchsausweisWohnenClient;
export let aufnahme: AufnahmeClient;
export let objekt: ObjektClient;
export let bilder: BildClient[];
export let unterlagen: UnterlageClient[];
export let ausweisart: Enums.Ausweisart;
export let aktiveBezahlmethode: Bezahlmethoden = Enums.Bezahlmethoden.paypal;
export let ausweistyp: Enums.AusweisTyp = Enums.AusweisTyp.Standard;
let rechnung: Partial<RechnungClient> = {
email: user.email,
empfaenger: user.vorname + " " + user.name,
strasse: user.adresse,
plz: user.plz,
ort: user.ort,
versand_empfaenger: user.vorname + " " + user.name,
versand_strasse: user.adresse,
versand_plz: user.plz,
versand_ort: user.ort,
telefon: user.telefon,
};
let services: {
name: string;
id: Enums.Service;
price: number;
selected: boolean;
}[] = [
{
name: "Qualitätsdruck per Post (zusätzlich zur PDF Version)",
id: Enums.Service.Qualitaetsdruck,
price: 9,
selected: false,
},
{
name: "Aushang (für öffentliche Gebäude gesetzlich vorgeschrieben)",
id: Enums.Service.Aushang,
price: 10,
selected: false,
},
{
name: "Same Day Service - Bestellung Werktags vor <b>12:00&nbsp;Uhr</b><br>Ausstellung bis <b>18:00&nbsp;Uhr</b> am gleichen Tag",
id: Enums.Service.SameDay,
price: 29,
selected: false,
},
{
name: "Telefonische Energie-Effizienzberatung",
id: Enums.Service.Telefonberatung,
price: 75,
selected: false,
},
];
let prices = PRICES[ausweisart];
let basePrice: number = prices[ausweistyp];
$: price =
basePrice +
services.reduce(
(acc, service) => (service.selected && acc + service.price) || acc,
0
);
const zurueck = {
[Enums.Ausweisart.VerbrauchsausweisWohnen]:
`/energieausweis-erstellen/verbrauchsausweis-wohngebaeude?uid=${ausweis.uid}`,
[Enums.Ausweisart.VerbrauchsausweisGewerbe]:
`/energieausweis-erstellen/verbrauchsausweis-gewerbe?uid=${ausweis.uid}`,
[Enums.Ausweisart.BedarfsausweisWohnen]:
`/energieausweis-erstellen/bedarfsausweis-wohnen?uid=${ausweis.uid}`,
[Enums.Ausweisart.BedarfsausweisGewerbe]:
`/energieausweis-erstellen/bedarfsausweis-gewerbe?uid=${ausweis.uid}`,
[Enums.Ausweisart.GEGNachweisWohnen]:
`/angebot-anfragen/geg-nachweis-wohnen-anfragen?uid=${ausweis.uid}`,
[Enums.Ausweisart.GEGNachweisGewerbe]:
`/angebot-anfragen/geg-nachweis-gewerbe-anfragen?uid=${ausweis.uid}`,
[Enums.Ausweisart.GEGNachweisBedarfsausweis]:
`/angebot-anfragen/bedarfsausweis-gewerbe-anfragen?uid=${ausweis.uid}`,
}[ausweisart];
async function speichern(e: SubmitEvent) {
e.preventDefault();
// Um einen Ausweis zu speichern müssen wir eingeloggt sein, andernfalls wird die API den call ablehnen.
// Wir prüfen also ob wir eingeloggt sind und leiten den Nutzer ggf. auf die Login Seite weiter.
if (!(await validateAccessTokenClient())) {
// TOOD: Auf Dialog umstellen.
const loggedIn = await dialogs.modal(LoginDialog);
if (!loggedIn) {
return false;
}
}
// Falls der Ausweis noch keine benutzer_id hat müssen wir ihn claimen, damit er dem jetzigen Nutzer zugewiesen wird...
// await client.v1.verbrauchsausweisWohnen.claim.mutate({
// uid: ausweis.uid,
// });
localStorage.clear();
window.location.href = `/kaufabschluss?uid=${ausweis.uid}`;
}
async function 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) {
addNotification({
dismissable: true,
message: "Ups... Das hat nicht geklappt.",
subtext: "Beim erstellen des Nachweises ist etwas schiefgelaufen, bitte versuchen sie es erneut oder kontaktieren sie unseren Support."
})
}
}
async function bestellen() {
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 || ausweisart === Enums.Ausweisart.GEGNachweisGewerbe || ausweisart === Enums.Ausweisart.GEGNachweisBedarfsausweis) {
result = await nachweisSpeichern(ausweis, objekt, aufnahme, bilder, unterlagen, ausweisart)
} else {
result = await ausweisSpeichern(ausweis, objekt, aufnahme, bilder, ausweisart)
}
if (result === null) {
if (ausweisart === Enums.Ausweisart.GEGNachweisWohnen || ausweisart === Enums.Ausweisart.GEGNachweisGewerbe || ausweisart === Enums.Ausweisart.GEGNachweisBedarfsausweis) {
addNotification({
dismissable: true,
message: "Ups... Das hat nicht geklappt.",
subtext: "Der Ausweis konnte nicht gespeichert werden, bitte versuchen sie es erneut oder kontaktieren sie unseren Support."
})
} else {
addNotification({
dismissable: true,
message: "Ups... Das hat nicht geklappt.",
subtext: "Ihre Anfrage für einen GEG Nachweis konnte nicht gespeichert werden, bitte versuchen sie es erneut oder kontaktieren sie unseren Support."
})
}
}
try {
const { uid, checkout_url } = await api.rechnung.PUT.fetch(
{
ausweisart,
bezahlmethode: aktiveBezahlmethode,
services: services
.filter((service) => service.selected)
.map((service) => service.id),
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,
ausweis_uid: ausweis.uid,
ausweistyp,
},
{
headers: {
Authorization: `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`,
},
}
);
// Alle alten Ausweisdateien im localStorage löschen.
localStorage.clear();
if (aktiveBezahlmethode === Enums.Bezahlmethoden.rechnung) {
window.location.href = `/payment/success?r=${uid}&a=${ausweis.uid}`;
} else {
window.location.href = checkout_url as string;
}
} catch (e) {
addNotification({
dismissable: true,
message: "Ups... Das hat nicht geklappt.",
subtext: "Beim erstellen des Ausweises ist etwas schiefgelaufen, bitte versuchen sie es erneut oder kontaktieren sie unseren Support."
})
}
}
/**
* TODO
* Ausschlusskriterien: Neubau, Modernisierung
Baujahr vor 1978, unsaniert und bis 4 Wohneinheiten /
Heizung jünger als 3 Jahre / Verbrauchsjahre unvollständig / Verbrauchsjahre nicht aktuell genug /
Klimafaktoren unvollständig /
Leerstand größer 30% /
Fehlende oder unvollständige Bilder (pro Abschnitt mindestens ein Bild) /
Fehlende Angaben zum Sanierungsstand (Bei F mindestens 2 Haken), (Bei G mindestens 1 Haken)
*/
let bestellenNichtMoeglich = false;
if (
((ausweis.ausstellgrund === "Neubau" ||
ausweis.ausstellgrund === "Modernisierung") &&
aufnahme.baujahr_gebaeude[0] < 1978 &&
aufnahme.saniert === false &&
aufnahme.einheiten &&
aufnahme.einheiten > 4) ||
new Date().getFullYear() - aufnahme.baujahr_heizung[0] < 3 ||
!ausweis.verbrauch_1 ||
!ausweis.verbrauch_2 ||
!ausweis.verbrauch_3 ||
(aufnahme.leerstand && aufnahme.leerstand > 30)
) {
bestellenNichtMoeglich = true;
}
const gegAnfrage =
ausweisart === Enums.Ausweisart.GEGNachweisWohnen ||
ausweisart === Enums.Ausweisart.GEGNachweisBedarfsausweis ||
ausweisart === Enums.Ausweisart.GEGNachweisGewerbe;
let loginOverlayHidden = true;
let loginAction = () => {};
let form: HTMLFormElement;
</script>
<div
id="skala"
class="bg-white grid grid-cols-1 gap-x-8 gap-y-4 px-0 sm:p-4
2xl:grid-cols-2 2xl:gap-x-8
"
>
{#if !gegAnfrage}
<PerformanceScore
bind:ausweis
bind:aufnahme
bind:objekt
{ausweisart}
/>
{/if}
{#if gegAnfrage}
<Progressbar
active={1}
steps={["Gebäudedaten", "Kundendaten", "Bestätigung"]}
{ausweisart}
anliegen={"erstellen"}
/>
{:else}
<Progressbar active={1} {ausweisart} anliegen={"anfragen"} />
{/if}
</div>
<form id="formInput-2" bind:this={form}>
<div id="formular-box" class="formular-boxen ring-0">
<Bereich
bereich="1"
title="Eingabe des Ansprechpartners -> {user.email}"
>
<Ansprechpartner bind:user /></Bereich
>
<Bereich bereich="2" title="Rechnungsadresse">
<Rechnungsadresse bind:rechnung /></Bereich
>
{#if !gegAnfrage}
<Bereich bereich="3" title="Bezahlmethode">
<div
id="bezahlung"
class="bereich-box grid
grid-cols-3 sm:grid-cols-5 justify-around justify-items-center items-center"
>
<PaymentOption
bezahlmethode={Enums.Bezahlmethoden.paypal}
bind:aktiveBezahlmethode
name={"PayPal"}
icon={"/images/paypal.png"}
></PaymentOption>
<PaymentOption
bezahlmethode={Enums.Bezahlmethoden.sofort}
bind:aktiveBezahlmethode
name={"Sofort"}
icon={"/images/sofort.png"}
></PaymentOption>
<PaymentOption
bezahlmethode={Enums.Bezahlmethoden.giropay}
bind:aktiveBezahlmethode
name={"Giropay"}
icon={"/images/giropay.png"}
></PaymentOption>
<PaymentOption
bezahlmethode={Enums.Bezahlmethoden.creditcard}
bind:aktiveBezahlmethode
name={"Kreditkarte"}
icon={"/images/creditcard.png"}
></PaymentOption>
<PaymentOption
bezahlmethode={Enums.Bezahlmethoden.rechnung}
bind:aktiveBezahlmethode
name={"Rechnung"}
icon={"/images/rechnung.png"}
></PaymentOption>
</div></Bereich
>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-x-6 my-6">
<div class="zusaetze bereich-box bg-white">
<h3>Zusatzleistungen:</h3>
{#each services as service}
<div class="zusatz">
<input
type="checkbox"
bind:checked={service.selected}
/>
<div class="text-right pr-6">
<b>{@html service.price}</b> inkl.&nbsp;Mwst
</div>
<div>{@html service.name}</div>
</div>
{/each}
</div>
<div class=" bereich-box bg-white ring-primary/50 ring-2">
<h3>Kosten:</h3>
<div class="ProduktKostenTabelle">
<div class="zeile betrag">
{#if ausweistyp === Enums.AusweisTyp.Standard}
<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">
<b>{(price * 0.81).toFixed(2) + " €"}</b>
</div>
</div>
<div class="zeile betrag">
<div>Zusatzleistung (Prüfung Mehraufwand)</div>
<div>:</div>
<div class="text-right"><b>0.00 €</b></div>
</div>
<div class="zeile betrag">
<div>19% gesetzl. MwSt.</div>
<div>:</div>
<div class="text-right">
<b>{(price * 0.19).toFixed(2) + " €"}</b>
</div>
</div>
<div class="zeile betrag mt-4">
<div><b>Preis inkl. MwSt.</b></div>
<div>:</div>
<div class="text-right">
<b>{price.toFixed(2) + " €"}</b>
</div>
</div>
<div class="zeile betrag mt-4 items-center">
<div><b>Bezahlmethode</b></div>
<div>:</div>
<div class="justify-self-end">
<img
src="images/{aktiveBezahlmethode}.png"
alt={aktiveBezahlmethode}
/>
</div>
</div>
</div>
</div>
</div>
{/if}
<div
class="grid grid-cols-1 gap-x-2 gap-y-1 self-start mt-0
sm:grid-cols-[min-content_min-content_min-content] sm:justify-self-end sm:mt-8"
>
<a class="order-3 sm:order-1 button cursor-pointer text-center sm:text-start" href={zurueck}>Zurück</a>
<button class="order-2 button" type="button">Speichern</button>
{#if gegAnfrage}
<button
class="order-1 sm:order-2 button cursor-pointer"
type="button"
data-cy="bestellen"
on:click={anfordern}>Angebot anfordern</button
>
{:else}
<button
class="order-1 sm:order-2 button cursor-pointer"
data-cy="bestellen"
type="button"
on:click={bestellen}>Kostenpflichtig bestellen</button
>
{/if}
</div>
</div>
<Overlay bind:hidden={loginOverlayHidden}>
<div class="bg-white w-full max-w-screen-sm py-8">
<EmbeddedAuthFlowModule onLogin={loginAction} email={rechnung.email}></EmbeddedAuthFlowModule>
</div>
</Overlay>
</form>
<NotificationWrapper></NotificationWrapper>
<style lang="postcss">
h3 {
@apply text-[1.25rem] ml-0 pl-0 font-bold mb-6;
}
.ProduktKostenTabelle {
@apply block mt-6 sm:mt-0;
.zeile {
@apply grid grid-cols-[9rem_1rem_5rem_1fr] sm:grid-cols-[19rem_1rem_5rem_1fr];
}
.betrag {
@apply font-bold text-[1.15rem];
}
}
.varianten {
@apply block;
.variante {
@apply grid grid-cols-[40px_8rem_1fr] items-start pb-4;
input {
@apply mt-[5px];
}
}
}
.zusaetze {
@apply block;
.zusatz {
@apply grid grid-cols-[10px_6rem_1fr] sm:grid-cols-[40px_8rem_1fr] items-start pb-3;
input {
@apply mt-[4px];
}
}
}
</style>