1 Commits

Author SHA1 Message Date
Moritz Utcke
dc0509cac2 Implementiert Nutzer Verifizierung
Fügt einen Mechanismus zur Nutzerverifizierung per E-Mail ein.

Nach der Registrierung wird eine E-Mail mit einem zeitbasierten Verifizierungscode versandt. Der Nutzer muss diesen Code eingeben, um sein Konto zu aktivieren.

Die Methode zur Erstellung des Codes ist zeitbasiert und ändert sich alle 15 Minuten.
2025-07-30 09:39:30 -05:00
35 changed files with 1574 additions and 843 deletions

View File

@@ -8,6 +8,8 @@ on:
jobs: jobs:
merge: merge:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs:
reason: ${{ steps.check.outputs.reason }}
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -28,9 +30,15 @@ jobs:
id: check id: check
run: | run: |
git fetch origin git fetch origin
if [ $(git rev-list --count origin/staging..origin/main) -gt 0 ]; then COUNT=$(git rev-list --count origin/staging..origin/main)
if [ "$COUNT" -gt 0 ]; then
echo "reason=ok" >> $GITHUB_OUTPUT
echo "❌ Staging is behind main and requires manual merging." echo "❌ Staging is behind main and requires manual merging."
exit 1 exit 1
elif [ "$COUNT" -eq 0 ]; then
echo "reason=identical" >> $GITHUB_OUTPUT
echo "✅ Staging and main are identical. Nothing to do."
exit 42
fi fi
- name: Create PR from staging to main - name: Create PR from staging to main
@@ -53,7 +61,7 @@ jobs:
notify_failure: notify_failure:
needs: merge needs: merge
if: failure() if: failure() && needs.merge.outputs.reason != 'identical'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Send Discord notification on failure - name: Send Discord notification on failure
@@ -61,16 +69,4 @@ jobs:
curl -H "Content-Type: application/json" \ curl -H "Content-Type: application/json" \
-X POST \ -X POST \
-d "{\"content\": \"🚨 Auto-Merge fehlgeschlagen! Bitte manuell prüfen: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" \ -d "{\"content\": \"🚨 Auto-Merge fehlgeschlagen! Bitte manuell prüfen: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" \
${{ secrets.DISCORD_WEBHOOK_URL }}
notify_success:
needs: merge
if: success()
runs-on: ubuntu-latest
steps:
- name: Send Discord notification on success
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d "{\"content\": \"✅ Auto-Merge ausgeführt! Ergebnis jetzt auf [GitHub](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) und [online-energieausweis.org](https://online-energieausweis.org) einsehen.\"}" \
${{ secrets.DISCORD_WEBHOOK_URL }} ${{ secrets.DISCORD_WEBHOOK_URL }}

View File

@@ -28,4 +28,4 @@ jobs:
git clean -f -d git clean -f -d
git pull origin dev git pull origin dev
git status git status
make prod-no-backup make prod

View File

@@ -13,15 +13,18 @@ jobs:
steps: steps:
- name: Prevent dev merges - name: Prevent dev merges
run: | run: |
if [[ "${{ github.head_ref }}" == "dev" && "${{ github.base_ref }}" == "main" ]]; then echo "${{ github.head_ref }}";
echo "ERROR: Merging 'dev' into 'main' is forbidden!" echo "${{ github.base_ref }}";
if [[ "${{ github.head_ref }}" == "dev" ]]; then
echo "ERROR: Merging 'dev' into '${{ github.base_ref }}' is forbidden!"
exit 1 exit 1
fi fi
- name: Allow only staging into main - name: Allow only staging into main
if: github.base_ref == 'main' if: github.base_ref == 'main'
run: | run: |
echo "${{ github.head_ref }}";
echo "${{ github.base_ref }}";
if [[ "${{ github.head_ref }}" != "staging" ]]; then if [[ "${{ github.head_ref }}" != "staging" ]]; then
echo "ERROR: Only 'staging' branch is allowed to merge into 'main'. Current: '${{ github.head_ref }}'" echo "ERROR: Only 'staging' branch is allowed to merge into 'main'. Current: '${{ github.head_ref }}'"
exit 1 exit 1

View File

@@ -59,11 +59,9 @@ all:
bun run dev 2>&1 | tee ~/logs/`date '+%d-%m-%Y_%H:%M:%S'`.log bun run dev 2>&1 | tee ~/logs/`date '+%d-%m-%Y_%H:%M:%S'`.log
update-dwd-klimafaktoren-cron: update-dwd-klimafaktoren-cron:
pm2 start bun --name "update-dwd-klimafaktoren-cron" --cron "0 12 28 * *" -- src/cronjobs/update-dwd-klimafaktoren.ts pm2 start bun --name "update-dwd-klimafaktoren-cron" --no-autorestart --cron "0 12 28 * *" -- src/cronjobs/update-dwd-klimafaktoren.ts
prod: prod-no-backup backup-database-cronjob prod: install-dependencies prisma-studio backup-database-cronjob update-dwd-klimafaktoren-cron
prod-no-backup: install-dependencies prisma-studio update-dwd-klimafaktoren-cron
bun run build bun run build
mkdir -p ~/logs mkdir -p ~/logs
mkdir -p ~/persistent/online-energieausweis mkdir -p ~/persistent/online-energieausweis

View File

@@ -15,7 +15,6 @@
"@pdfme/common": "^5.2.16", "@pdfme/common": "^5.2.16",
"@pdfme/generator": "^5.2.16", "@pdfme/generator": "^5.2.16",
"@pdfme/ui": "^5.2.16", "@pdfme/ui": "^5.2.16",
"@svelte-plugins/datepicker": "^1.0.11",
"@trpc/client": "^10.45.2", "@trpc/client": "^10.45.2",
"@trpc/server": "^10.45.2", "@trpc/server": "^10.45.2",
"astro": "^4.16.17", "astro": "^4.16.17",
@@ -712,8 +711,6 @@
"@smithy/util-waiter": ["@smithy/util-waiter@4.0.2", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ=="], "@smithy/util-waiter": ["@smithy/util-waiter@4.0.2", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ=="],
"@svelte-plugins/datepicker": ["@svelte-plugins/datepicker@1.0.11", "", {}, "sha512-Tqc07QLyRkCpc3Glg6oRLTUApLtCrOh52d6vJ7L32QI17HrwvcDDjaH3LF3X1SBm3CWdMrnqfJp3xjUZmB4wzw=="],
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@2.5.3", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^1.0.4", "debug": "^4.3.4", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.3", "svelte-hmr": "^0.15.3", "vitefu": "^0.2.4" }, "peerDependencies": { "svelte": "^3.54.0 || ^4.0.0 || ^5.0.0-next.0", "vite": "^4.0.0" } }, "sha512-erhNtXxE5/6xGZz/M9eXsmI7Pxa6MS7jyTy06zN3Ck++ldrppOnOlJwHHTsMC7DHDQdgUp4NAc4cDNQ9eGdB/w=="], "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@2.5.3", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^1.0.4", "debug": "^4.3.4", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.3", "svelte-hmr": "^0.15.3", "vitefu": "^0.2.4" }, "peerDependencies": { "svelte": "^3.54.0 || ^4.0.0 || ^5.0.0-next.0", "vite": "^4.0.0" } }, "sha512-erhNtXxE5/6xGZz/M9eXsmI7Pxa6MS7jyTy06zN3Ck++ldrppOnOlJwHHTsMC7DHDQdgUp4NAc4cDNQ9eGdB/w=="],
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@1.0.4", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^2.2.0", "svelte": "^3.54.0 || ^4.0.0", "vite": "^4.0.0" } }, "sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ=="], "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@1.0.4", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^2.2.0", "svelte": "^3.54.0 || ^4.0.0", "vite": "^4.0.0" } }, "sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ=="],

View File

@@ -1,4 +1,5 @@
version: '3' version: '3'
name: database
services: services:
database: database:
build: ./ build: ./

View File

@@ -29,7 +29,6 @@
"@pdfme/common": "^5.2.16", "@pdfme/common": "^5.2.16",
"@pdfme/generator": "^5.2.16", "@pdfme/generator": "^5.2.16",
"@pdfme/ui": "^5.2.16", "@pdfme/ui": "^5.2.16",
"@svelte-plugins/datepicker": "^1.0.11",
"@trpc/client": "^10.45.2", "@trpc/client": "^10.45.2",
"@trpc/server": "^10.45.2", "@trpc/server": "^10.45.2",
"astro": "^4.16.17", "astro": "^4.16.17",

View File

@@ -3,27 +3,17 @@
# === Configuration === # === Configuration ===
BUCKET_NAME="ibc-db-backup" BUCKET_NAME="ibc-db-backup"
ENDPOINT_URL="https://s3.eu-central-3.ionoscloud.com" ENDPOINT_URL="https://s3.eu-central-3.ionoscloud.com"
LOCAL_DOWNLOAD_DIR="./" # Where to save the file LOCAL_DOWNLOAD_DIR="./"
# === Check if a custom file is given as a command line argument === # === Use filename from argument if provided ===
if [ $# -eq 1 ]; then if [ -n "$1" ]; then
CUSTOM_FILE="$1" LATEST_FILE="$1"
echo "🔍 Using custom file: $CUSTOM_FILE"
# Check if the file exists
if [ ! -f "$CUSTOM_FILE" ]; then
echo "❌ Custom file does not exist: $CUSTOM_FILE"
exit 1
fi
LATEST_FILE="$CUSTOM_FILE"
FILENAME=$(basename "$LATEST_FILE")
SQL_FILE="${FILENAME%.br}" # Remove .br suffix
else else
echo "🔍 No custom file provided, searching for latest .sql.br file in S3" echo "📡 No filename provided, fetching latest..."
# === Get latest file from IONOS S3 bucket === # === Get latest file from IONOS S3 bucket ===
LATEST_FILE=$(aws s3api list-objects-v2 \ LATEST_FILE=$(aws --profile ionos s3api list-objects-v2 \
--bucket "$BUCKET_NAME" \ --bucket "$BUCKET_NAME" \
--prefix "data-dump" \ --prefix "full-dump" \
--endpoint-url "$ENDPOINT_URL" \ --endpoint-url "$ENDPOINT_URL" \
--query 'Contents | sort_by(@, &LastModified) | [-1].Key' \ --query 'Contents | sort_by(@, &LastModified) | [-1].Key' \
--output text) --output text)
@@ -33,22 +23,22 @@ else
echo "❌ No matching .sql.br file found." echo "❌ No matching .sql.br file found."
exit 1 exit 1
fi fi
echo "🔍 Latest file found: $LATEST_FILE"
FILENAME=$(basename "$LATEST_FILE")
SQL_FILE="${FILENAME%.br}" # Remove .br suffix
echo "📥 Downloading $LATEST_FILE"
aws s3 cp "s3://$BUCKET_NAME/$LATEST_FILE" "$LOCAL_DOWNLOAD_DIR" \
--endpoint-url "$ENDPOINT_URL"
fi fi
FILENAME=$(basename "$LATEST_FILE")
SQL_FILE="${FILENAME%.br}" # Remove .br suffix
echo "📥 Downloading $LATEST_FILE"
aws --profile ionos s3 cp "s3://$BUCKET_NAME/$LATEST_FILE" "$LOCAL_DOWNLOAD_DIR" \
--endpoint-url "$ENDPOINT_URL"
# === Decompress with Brotli === # === Decompress with Brotli ===
echo "🗜️ Decompressing $FILENAME -> $SQL_FILE" echo "🗜️ Decompressing $FILENAME -> $SQL_FILE"
brotli -d "$FILENAME" brotli -d "$FILENAME"
# === Import into Postgres inside Docker === # === Import into Postgres inside Docker ===
echo "🐘 Importing into PostgreSQL (online-energieausweis-database-1:main)" echo "🐘 Importing into PostgreSQL (database:main)"
docker exec -i "online-energieausweis-database-1" env PGPASSWORD="hHMP8cd^N3SnzGRR" \ docker exec -i "database" env PGPASSWORD="hHMP8cd^N3SnzGRR" \
psql -U "main" -d "main" < "$SQL_FILE" psql -U "main" -d "main" < "$SQL_FILE"
echo "✅ Import complete." echo "✅ Import complete."

View File

@@ -5,6 +5,7 @@ export const createCaller = createCallerFactory({
"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"),
"unterlage": await import("../src/pages/api/unterlage.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/ausstellen": await import("../src/pages/api/admin/ausstellen.ts"),
"admin/bedarfsausweis-ausstellen": await import("../src/pages/api/admin/bedarfsausweis-ausstellen.ts"), "admin/bedarfsausweis-ausstellen": await import("../src/pages/api/admin/bedarfsausweis-ausstellen.ts"),
"admin/bestellbestaetigung": await import("../src/pages/api/admin/bestellbestaetigung.ts"), "admin/bestellbestaetigung": await import("../src/pages/api/admin/bestellbestaetigung.ts"),
@@ -12,13 +13,15 @@ export const createCaller = createCallerFactory({
"admin/nicht-ausstellen": await import("../src/pages/api/admin/nicht-ausstellen.ts"), "admin/nicht-ausstellen": await import("../src/pages/api/admin/nicht-ausstellen.ts"),
"admin/registriernummer": await import("../src/pages/api/admin/registriernummer.ts"), "admin/registriernummer": await import("../src/pages/api/admin/registriernummer.ts"),
"admin/stornieren": await import("../src/pages/api/admin/stornieren.ts"), "admin/stornieren": await import("../src/pages/api/admin/stornieren.ts"),
"aufnahme": await import("../src/pages/api/aufnahme/index.ts"),
"ausweise": await import("../src/pages/api/ausweise/index.ts"), "ausweise": await import("../src/pages/api/ausweise/index.ts"),
"auth/access-token": await import("../src/pages/api/auth/access-token.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/passwort-vergessen": await import("../src/pages/api/auth/passwort-vergessen.ts"),
"auth/refresh-token": await import("../src/pages/api/auth/refresh-token.ts"), "auth/refresh-token": await import("../src/pages/api/auth/refresh-token.ts"),
"auth/verification-code": await import("../src/pages/api/auth/verification-code.ts"),
"aufnahme": await import("../src/pages/api/aufnahme/index.ts"),
"bedarfsausweis-gewerbe/[id]": await import("../src/pages/api/bedarfsausweis-gewerbe/[id].ts"), "bedarfsausweis-gewerbe/[id]": await import("../src/pages/api/bedarfsausweis-gewerbe/[id].ts"),
"bedarfsausweis-gewerbe": await import("../src/pages/api/bedarfsausweis-gewerbe/index.ts"), "bedarfsausweis-gewerbe": await import("../src/pages/api/bedarfsausweis-gewerbe/index.ts"),
"ausweise": await import("../src/pages/api/ausweise/index.ts"),
"bedarfsausweis-wohnen/[id]": await import("../src/pages/api/bedarfsausweis-wohnen/[id].ts"), "bedarfsausweis-wohnen/[id]": await import("../src/pages/api/bedarfsausweis-wohnen/[id].ts"),
"bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"), "bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"),
"bilder/[id]": await import("../src/pages/api/bilder/[id].ts"), "bilder/[id]": await import("../src/pages/api/bilder/[id].ts"),
@@ -26,20 +29,19 @@ export const createCaller = createCallerFactory({
"geg-nachweis-gewerbe": await import("../src/pages/api/geg-nachweis-gewerbe/index.ts"), "geg-nachweis-gewerbe": await import("../src/pages/api/geg-nachweis-gewerbe/index.ts"),
"geg-nachweis-wohnen/[id]": await import("../src/pages/api/geg-nachweis-wohnen/[id].ts"), "geg-nachweis-wohnen/[id]": await import("../src/pages/api/geg-nachweis-wohnen/[id].ts"),
"geg-nachweis-wohnen": await import("../src/pages/api/geg-nachweis-wohnen/index.ts"), "geg-nachweis-wohnen": await import("../src/pages/api/geg-nachweis-wohnen/index.ts"),
"objekt": await import("../src/pages/api/objekt/index.ts"),
"rechnung/[id]": await import("../src/pages/api/rechnung/[id].ts"), "rechnung/[id]": await import("../src/pages/api/rechnung/[id].ts"),
"rechnung/anfordern": await import("../src/pages/api/rechnung/anfordern.ts"), "rechnung/anfordern": await import("../src/pages/api/rechnung/anfordern.ts"),
"rechnung": await import("../src/pages/api/rechnung/index.ts"), "rechnung": await import("../src/pages/api/rechnung/index.ts"),
"objekt": await import("../src/pages/api/objekt/index.ts"),
"ticket": await import("../src/pages/api/ticket/index.ts"), "ticket": await import("../src/pages/api/ticket/index.ts"),
"user": await import("../src/pages/api/user/index.ts"),
"user/self": await import("../src/pages/api/user/self.ts"), "user/self": await import("../src/pages/api/user/self.ts"),
"verbrauchsausweis-gewerbe/[id]": await import("../src/pages/api/verbrauchsausweis-gewerbe/[id].ts"), "verbrauchsausweis-gewerbe/[id]": await import("../src/pages/api/verbrauchsausweis-gewerbe/[id].ts"),
"verbrauchsausweis-gewerbe": await import("../src/pages/api/verbrauchsausweis-gewerbe/index.ts"), "verbrauchsausweis-gewerbe": await import("../src/pages/api/verbrauchsausweis-gewerbe/index.ts"),
"verbrauchsausweis-wohnen/[id]": await import("../src/pages/api/verbrauchsausweis-wohnen/[id].ts"), "verbrauchsausweis-wohnen/[id]": await import("../src/pages/api/verbrauchsausweis-wohnen/[id].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"), "webhooks/mollie": await import("../src/pages/api/webhooks/mollie.ts"),
"objekt/[id]": await import("../src/pages/api/objekt/[id]/index.ts"),
"aufnahme/[id]/bilder": await import("../src/pages/api/aufnahme/[id]/bilder.ts"), "aufnahme/[id]/bilder": await import("../src/pages/api/aufnahme/[id]/bilder.ts"),
"aufnahme/[id]": await import("../src/pages/api/aufnahme/[id]/index.ts"), "aufnahme/[id]": await import("../src/pages/api/aufnahme/[id]/index.ts"),
"aufnahme/[id]/unterlagen": await import("../src/pages/api/aufnahme/[id]/unterlagen.ts"), "aufnahme/[id]/unterlagen": await import("../src/pages/api/aufnahme/[id]/unterlagen.ts"),
"objekt/[id]": await import("../src/pages/api/objekt/[id]/index.ts"),
}) })

View File

@@ -1,121 +0,0 @@
<script lang="ts">
import { Aufnahme, BedarfsausweisWohnen, Enums, Objekt, Rechnung, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "#lib/server/prisma.js";
import moment from "moment";
import { DatePicker } from "@svelte-plugins/datepicker"
export let bestellungen: (Rechnung & {
ausweis: (VerbrauchsausweisWohnen | BedarfsausweisWohnen | VerbrauchsausweisGewerbe) & { aufnahme: Aufnahme & { objekt: Objekt }}
})[];
export let provisionen: Record<Enums.Ausweisart, number>;
export let partnerCodeErstesMal: Date;
export let email: string;
const bestellungenNachMonat: Record<string, (typeof bestellungen)> = {};
for (const bestellung of bestellungen) {
const monat = moment(bestellung.created_at).format("Y-MM");
if (monat in bestellungenNachMonat) {
bestellungenNachMonat[monat].push(bestellung)
} else {
bestellungenNachMonat[monat] = [bestellung]
}
}
// Wir brauchen alle Monate zwischen dem ersten Mal, dass der partner_code benutzt wurde bis zum heutigen Zeitpunkt.
const months: Record<string, string> = {
"01": "Januar", "02": "Februar", "03": "März", "04": "April",
"05": "Mai", "06": "Juni", "07": "Juli", "08": "August",
"09": "September", "10": "Oktober", "11": "November", "12": "Dezember"
};
function getMonthlyPeriods(minTime?: Date): moment.Moment[] {
const min = minTime ? moment(minTime) : moment();
const start = min.clone().startOf('month');
const end = moment().add(1, 'month').startOf('month');
const monthsArray: moment.Moment[] = [];
const current = start.clone();
while (current.isBefore(end)) {
monthsArray.push(current.clone());
current.add(1, 'month');
}
return monthsArray.reverse(); // Most recent month first
}
const periods = getMonthlyPeriods(partnerCodeErstesMal)
let isOpen = false;
export let startDate = moment(partnerCodeErstesMal).startOf('month').toDate();
export let endDate = moment().endOf('month').toDate();
$: formattedStartDate = moment(startDate).format("DD.MM.YYYY");
$: formattedEndDate = moment(endDate).format("DD.MM.YYYY");
function toggleDatePicker() {
isOpen = !isOpen;
}
const onChange = ({ startDate, endDate }: { startDate: number, endDate: number }) => {
window.location.href = `/dashboard/abrechnung?start=${moment(startDate).format("YYYY-MM-DD")}&end=${moment(endDate).format("YYYY-MM-DD")}`;
};
</script>
<div class="fixed top-0 left-0 right-0 bg-white p-4 shadow z-10">
<div class="flex justify-between items-center">
<DatePicker bind:isOpen bind:startDate bind:endDate isRange={true} onDateChange={onChange} isMultipane={true}>
<input type="text" class="w-min" readonly value={`${formattedStartDate} - ${formattedEndDate}`} on:click={toggleDatePicker} />
</DatePicker>
<p>Abrechnungsübersicht für <strong>{email}</strong></p>
</div>
</div>
<main class="my-24 flex justify-center max-w-6xl mx-auto px-4">
{#if !bestellungen || bestellungen.length === 0}
<p class="text-center text-gray-500">Keine Bestellungen gefunden.</p>
{/if}
{#each periods as dt}
{@const jahrMonat = dt.format("Y-MM")}
{#if jahrMonat in bestellungenNachMonat && bestellungenNachMonat[jahrMonat].length > 0}
<!-- Echo dropdown foreach month. -->
{@const provisionMonat = bestellungenNachMonat[jahrMonat].reduce((acc, bestellung) => {
return acc + provisionen[bestellung.ausweis.ausweisart] || 0;
}, 0) * 1.19}
<!-- <div onclick="$(this).nextUntil('.dropdown_month').filter('table').toggle(); $('#betrag_gesamt').html('Abrechnungsbetrag $month_name: <b>$provision_month €</b>')" class='dropdown_month'>
<p>$month_name $year_name - Klicke, um Tabelle anzuzeigen</p>
<a target='_blank' rel='noreferrer noopener' href='/user/abrechnung/pdf.php?month={dt.format("m")}&year={dt.format("Y")}'>PDF Ansehen</a>
</div> -->
<table class="w-full mb-4 border-collapse border border-gray-300">
<thead>
<tr class="bg-primary text-white">
<td class="text-center font-bold">ID</td>
<td class="text-center font-bold">DATUM</td>
<td class="text-center font-bold">GEBÄUDEADRESSE </td>
<td class="text-center font-bold">PLZ </td>
<td class="text-center font-bold">ORT </td>
<td class="text-center font-bold">AUSWEIS</td>
<td class="text-center font-bold w-48">BETRAG NETTO</td>
</tr>
</thead>
<tbody class="text-sm">
<tr class="bg-secondary text-white">
<td class="text-center font-bold" colspan="6">{months[dt.format("MM")]} {dt.format("YYYY")}</td>
<td class="text-right font-bold w-48" style="font-family: monospace;">{provisionMonat.toFixed(2)}</td>
</tr>
</tbody>
{#each bestellungenNachMonat[jahrMonat] as bestellung}
{@const provisionBestellung = provisionen[bestellung.ausweis.ausweisart] || 0}
<tr>
<td class="text-center px-4 w-24" style="font-family: monospace;">{bestellung.id}</td>
<td class="text-center font-bold w-32">{moment(bestellung.created_at).format("DD.MM.YYYY")}</td>
<td class="text-left w-64">{bestellung.ausweis.aufnahme.objekt.adresse}</td>
<td class="text-center w-16">{bestellung.ausweis.aufnahme.objekt.plz}</td>
<td class="text-left w-64">{bestellung.ausweis.aufnahme.objekt.ort}</td>
<td class="text-center w-32">{bestellung.ausweis.ausweisart}</td>
<td class="text-right w-48" style="font-family: monospace;">{provisionBestellung.toFixed(2)}</td>
</tr>
{/each}
</table>
{/if}
{/each}
</main>

View File

@@ -0,0 +1,108 @@
<script lang="ts">
import { BedarfsausweisWohnen, Enums, Rechnung, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "#lib/server/prisma.js";
import moment from "moment";
export let bestellungen: (Rechnung & {
verbrauchsausweis_wohnen: VerbrauchsausweisWohnen | null,
verbrauchsausweis_gewerbe: VerbrauchsausweisGewerbe | null,
bedarfsausweis_wohnen: BedarfsausweisWohnen | null,
})[];
export let provisionen: Record<Enums.Ausweisart, number>;
export let partnerCodeErstesMal: Date;
const bestellungenNachMonat: Record<string, (typeof bestellungen)> = {};
for (const bestellung of bestellungen) {
const monat = moment(bestellung.created_at).format("Y-m");
if (monat in bestellungenNachMonat) {
bestellungenNachMonat[monat].push(bestellung)
} else {
bestellungenNachMonat[monat] = [bestellung]
}
}
// Wir brauchen alle Monate zwischen dem ersten Mal, dass der partner_code benutzt wurde bis zum heutigen Zeitpunkt.
const months: Record<string, string> = {
"01": "Januar", "02": "Februar", "03": "März", "04": "April",
"05": "Mai", "06": "Juni", "07": "Juli", "08": "August",
"09": "September", "10": "Oktober", "11": "November", "12": "Dezember"
};
function getMonthlyPeriods(minTime?: Date): moment.Moment[] {
const min = minTime ? moment(minTime) : moment();
const start = min.clone().startOf('month');
const end = moment().add(1, 'month').startOf('month');
const monthsArray: moment.Moment[] = [];
const current = start.clone();
while (current.isBefore(end)) {
monthsArray.push(current.clone());
current.add(1, 'month');
}
return monthsArray.reverse(); // Most recent month first
}
const periods = getMonthlyPeriods(partnerCodeErstesMal)
</script>
{#each periods as dt}
{@const jahrMonat = dt.format("Y-m")}
{#if jahrMonat in bestellungenNachMonat && bestellungenNachMonat[jahrMonat].length > 0}
<!-- Echo dropdown foreach month. -->
{@const provisionMonat = bestellungenNachMonat[jahrMonat].reduce((acc, bestellung) => {
if (bestellung.verbrauchsausweis_wohnen) {
return acc + provisionen[Enums.Ausweisart.VerbrauchsausweisWohnen];
}
if (bestellung.bedarfsausweis_wohnen) {
return acc + provisionen[Enums.Ausweisart.BedarfsausweisWohnen];
}
if (bestellung.verbrauchsausweis_gewerbe) {
return acc + provisionen[Enums.Ausweisart.VerbrauchsausweisGewerbe];
}
return acc;
}) * 1.19}
<div onclick="$(this).nextUntil('.dropdown_month').filter('table').toggle(); $('#betrag_gesamt').html('Abrechnungsbetrag $month_name: <b>$provision_month €</b>')" class='dropdown_month'>
<p>$month_name $year_name - Klicke, um Tabelle anzuzeigen</p>
<a target='_blank' rel='noreferrer noopener' href='/user/abrechnung/pdf.php?month={dt.format("m")}&year={dt.format("Y")}'>PDF Ansehen</a>
</div>
<table id='QTT' style='margin-top: 0 !important; display:none;'>
<thead>
<tr>
<td style='text-align:center;'>ID</td>
<td style='text-align:center;'>DATUM</td>
<td style='width:11em;text-align:center;'>GEBÄUDEADRESSE </td>
<td style='width:11em;text-align:center;'>PLZ </td>
<td style='width:11em;text-align:center;'>ORT </td>
<td style='text-align:center;'>AUSWEIS</td>
<td style='width:5em;text-align:center;'>BETRAG NETTO</td>
</tr>
</thead>
<tbody>
{#each bestellungenNachMonat[jahrMonat] as bestellung}
{@const provisionBestellung = bestellung.verbrauchsausweis_wohnen ? provisionen[Enums.Ausweisart.VerbrauchsausweisWohnen] : bestellung.verbrauchsausweis_gewerbe ? provisionen[Enums.Ausweisart.VerbrauchsausweisGewerbe] : provisionen[Enums.Ausweisart.BedarfsausweisWohnen]}
<tr>
<td style='width:1em;text-align:center;'>{bestellung.id}</td>
<td style='width:9em;text-align:center;font-weight:bold;'>{moment(bestellung.created_at).format("Y/m/d")}</td>
<td style='width:8em;text-align:left;'>{bestellung["objekt_strasse"]}</td>
<td style='width:5em;text-align:center;'>{bestellung["objekt_plz"]}</td>
<td style='width:6em;text-align:left;'>{bestellung["objekt_ort"]}</td>
<td style='width:3em;text-align:center;'>{bestellung['ausweisart']}</td>
<td style='width:8em;text-align:right;'>{provisionBestellung}</td>
</tr>
{/each}
</table>
{/if}
{/each}
<!-- foreach ($period as $dt) {
$year_month = $dt->format("Y-m");
$month_name = $months[$dt->format("m")];
if ((new DateTime(date("m/d/Y", strtotime($EEtimestamp))))->format("d") - (new DateTime(date("m/d/Y", strtotime($SStimestamp))))->format("d") == 1) {
$Pall = $dt->format("d/m/Y") . ' bis ' . (new DateTime($today))->format("d/m/Y");
} -->
<!-- } -->

View File

@@ -214,8 +214,8 @@ grid-cols-1 gap-x-2 gap-y-4
/> />
<div class="text-center xs:text-left justify-self-stretch"> <div class="text-center xs:text-left justify-self-stretch">
<b>Selbsteingabe online</b><br>inkl. ausführlicher&nbsp;telefonischer <b>Verbrauchsausweis online</b><br>inkl. ausführlicher&nbsp;telefonischer
Beratung! Beratung
</div> </div>
<div class="text-center xs:text-right"> <div class="text-center xs:text-right">
@@ -234,7 +234,7 @@ grid-cols-1 gap-x-2 gap-y-4
/> />
<div class="text-center xs:text-left justify-self-stretch"> <div class="text-center xs:text-left justify-self-stretch">
<b>Wir übernehmen die Eingabe</b><br>Sie übermitteln die nötigen Unterlagen per Upload oder E-Mail. <b>Verbrauchsausweis offline</b><br>Sie schicken uns 3&nbsp;Verbrauchsabrechnungen&nbsp;zu)
</div> </div>
<div class="text-center xs:text-right"> <div class="text-center xs:text-right">

View File

@@ -70,7 +70,7 @@
</div> </div>
</div> </div>
{/if} {/if}
<!-- <a href="/dashboard/abrechnung" class="button ">Monatliche Abrechnung</a> --> <a href="/dashboard/abrechnung" class="button ">Conversions</a>
</div> </div>
<hr class="border-gray-600" /> <hr class="border-gray-600" />

View File

@@ -2,7 +2,6 @@
export let hidden: boolean = true; export let hidden: boolean = true;
export let closeable: boolean = true; export let closeable: boolean = true;
import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock'; import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';
import { Cross1 } from 'radix-svelte-icons';
$: if (globalThis.window) { $: if (globalThis.window) {
if (hidden) { if (hidden) {
@@ -13,11 +12,13 @@
} }
</script> </script>
<div class="fixed top-0 left-0 w-[100vw] h-[100vh] flex items-center justify-center bg-[rgba(0,0,0,0.8)] z-50" class:hidden={hidden}> <div class="fixed top-0 left-0 w-[100vw] h-[100vh] flex items-center justify-center bg-[rgba(0,0,0,0.8)] z-50" class:hidden={hidden} on:click|self={() => {
hidden = closeable ? true : hidden;
}}>
{#if closeable} {#if closeable}
<button class="absolute top-4 right-4 text-white bg-gray-50 bg-opacity-25 px-4 py-2 rounded-lg" type="button" on:click={() => { <button class="absolute top-4 left-4 text-white" on:click={() => {
hidden = true; hidden = true;
}}><Cross1 size={20}></Cross1></button> }}>Schließen</button>
{/if} {/if}
<slot></slot> <slot></slot>
</div> </div>

View File

@@ -39,38 +39,63 @@ const isNET = pathname.includes("immonet");
</div> </div>
<div> <div>
<!-- Navigation als Liste (nur ab sm sichtbar) --> <ul class="navlist">
<ul class="navlist hidden xl:flex"> <li>
<li><a href={`/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/`}><button class={tab === 0 ? "glow" : ""}>Verbrauchsausweis</button></a></li> <a
<li><a href={`/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/`}><button class={tab === 1 ? "glow" : ""}>Verbrauchsausweis Gewerbe</button></a></li> href={`/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/`}
<li><a href={`/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/`}><button class={tab === 2 ? "glow" : ""}>Bedarfsausweis</button></a></li> ><button class={tab === 0 ? "glow" : ""}
<li><a href={`/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/`}><button class={tab === 3 ? "glow" : ""}>Bedarfsausweis Gewerbe</button></a></li> >Verbrauchsausweis</button
<li><a href={`/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/`}><button class={tab === 4 ? "glow" : ""}>GEG Nachweis Wohngebäude</button></a></li> ></a>
<li><a href={`/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/`}><button class={tab === 5 ? "glow" : ""}>GEG Nachweis Gewerbe</button></a></li> </li>
<li><a href={`/${partner}/welcher-ausweis/${partner}`}><button class={tab === 6 ? "glow" : ""}>Welcher Ausweis</button></a></li> <li>
</ul> <a
href={`/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/`}
<!-- Navigation als Dropdown (nur bis sm sichtbar) --> ><button class={tab === 1 ? "glow" : ""}
<select >Verbrauchsausweis Gewerbe</button
class="xl:hidden border rounded p-2 w-[calc(100%-2rem)]" ></a>
onchange="if (this.value) window.location.href=this.value" </li>
> <li>
<option value="">Auswahl treffen…</option> <a
<option value={`/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/`} selected={tab === 0}>Verbrauchsausweis</option> href={`/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/`}
<option value={`/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/`} selected={tab === 1}>Verbrauchsausweis Gewerbe</option> ><button class={tab === 2 ? "glow" : ""}
<option value={`/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/`} selected={tab === 2}>Bedarfsausweis</option> >Bedarfsausweis</button
<option value={`/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/`} selected={tab === 3}>Bedarfsausweis Gewerbe</option> ></a>
<option value={`/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/`} selected={tab === 4}>GEG Nachweis Wohngebäude</option> </li>
<option value={`/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/`} selected={tab === 5}>GEG Nachweis Gewerbe</option> <li>
<option value={`/${partner}/welcher-ausweis/${partner}`} selected={tab === 6}>Welcher Ausweis</option> <a
</select> href={`/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/`}
><button class={tab === 3 ? "glow" : ""}
>Bedarfsausweis Gewerbe</button
></a>
</li>
<li>
<a
href={`/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/`}
><button class={tab === 4 ? "glow" : ""}
>GEG Nachweis Wohngebäude</button
></a>
</li>
<li>
<a
href={`/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/`}
><button class={tab === 5 ? "glow" : ""}
>GEG Nachweis Gewerbe</button
></a>
</li>
<li>
<a href={`/${partner}/welcher-ausweis/${partner}`}
><button class={tab === 6 ? "glow" : ""}
>Welcher Ausweis</button
></a>
</li>
</ul>
</div> </div>
</div> </div>
</header> </header>
<div <div
id="titel" id="titel"
class="block w-full lg:h-[270px] bg-cover !px-8 md:px-32 !py-8 md:py-16" class="block w-full 2xl:h-[270px] lg:h-[148px] bg-cover px-24 py-20"
style={`background-image: url('/images/partner/${partner}/hero-energieausweis.jpg'); style={`background-image: url('/images/partner/${partner}/hero-energieausweis.jpg');
background-repeat:no-repeat; background-position:right;`} background-repeat:no-repeat; background-position:right;`}
> >
@@ -161,7 +186,7 @@ background-repeat:no-repeat; background-position:right;`}
font-family: "immo Sans"; font-family: "immo Sans";
font-weight:400; font-weight:400;
div{@apply w-fit bg-white/75 py-6 px-4 md:px-16 rounded-lg ring-2 ring-black/15 text-[1.45rem];box-shadow:8px 8px 16px rgba(0,0,0,0.5);} div{@apply w-fit bg-white/75 py-6 px-16 rounded-lg ring-2 ring-black/15 text-[1.45rem];box-shadow:8px 8px 16px rgba(0,0,0,0.5);}
} }
.header-button { .header-button {

View File

@@ -1,72 +1,68 @@
<script lang="ts"> <script lang="ts">
import { fade } from "svelte/transition";
import WidgetCardTemplate from "#components/widgets/immowelt/WidgetCardTemplate.svelte"; import WidgetCardTemplate from "#components/widgets/immowelt/WidgetCardTemplate.svelte";
import { PRICES } from "#lib/constants.js"; import { PRICES } from "#lib/constants.js";
import { Enums } from "#lib/client/prisma"; import { Enums } from "#lib/client/prisma.js";
let ausnahme: boolean = false; let ausnahme: boolean = false;
let oneBOX: boolean = false; let oneBOX: boolean = false;
let threeBOX: boolean = false; let threeBOX: boolean = false;
let gebaeudetyp: string = "bitte auswählen";
let anlass: string = "bitte auswählen";
let einheiten: string = "bitte auswählen";
let sanierungsstatus: string = "bitte auswählen";
let baujahr: string = "bitte auswählen";
let heizungsAlter: string = "bitte auswählen";
let leerStand: string = "bitte auswählen";
const partner:string = "immowelt"; let gebaeudetyp: string = "bitte auswählen";
let anlass: string = "bitte auswählen";
let einheiten: string = "bitte auswählen";
let sanierungsstatus: string = "bitte auswählen";
let baujahr: string = "bitte auswählen";
let heizungsAlter: string = "bitte auswählen";
let leerStand: string = "bitte auswählen";
const twoBoxReason = ["Vermietung/Verkauf", "Aushangpflicht", "Sonstiges"]; const partner: string = "immowelt";
const gewerbeHouse = ["Gewerbegebäude", "Mischgebäude"];
$: ausnahme = const twoBoxReason = ["Vermietung/Verkauf", "Aushangpflicht", "Sonstiges"];
leerStand === "mehr als 30" || const gewerbeHouse = ["Gewerbegebäude", "Mischgebäude"];
heizungsAlter === "< 3" ||
(baujahr === "vor 1978" && einheiten === "bis 4 Wohneinheiten" && sanierungsstatus === "unsaniert");
$: isTwoBoxReason = twoBoxReason.includes(anlass); $: ausnahme =
leerStand === "mehr als 30" ||
heizungsAlter === "< 3" ||
(baujahr === "vor 1978" &&
einheiten === "bis 4 Wohneinheiten" &&
sanierungsstatus === "unsaniert");
$: isGewerbe = gewerbeHouse.includes(gebaeudetyp); $: isTwoBoxReason = twoBoxReason.includes(anlass);
$: oneBOX = $: isGewerbe = gewerbeHouse.includes(gebaeudetyp);
(ausnahme && !isGewerbe) ||
(!isTwoBoxReason && gebaeudetyp !== "Mischgebäude") ||
(gebaeudetyp === "Gewerbegebäude" && leerStand === "mehr als 30");
$: threeBOX = $: oneBOX =
(ausnahme && gebaeudetyp === "Mischgebäude" && isTwoBoxReason && leerStand !== "mehr als 30"); (ausnahme && !isGewerbe) ||
(!isTwoBoxReason && gebaeudetyp !== "Mischgebäude") ||
(gebaeudetyp === "Gewerbegebäude" && leerStand === "mehr als 30");
$: standardXL = $: threeBOX =
(einheiten === "mehr als 4 Wohneinheiten") ausnahme &&
gebaeudetyp === "Mischgebäude" &&
isTwoBoxReason &&
leerStand !== "mehr als 30";
$: standardXL = einheiten === "mehr als 4 Wohneinheiten";
</script> </script>
<div id="IBC_app"> <div id="IBC_app">
<input id="recode" type="hidden" value="widgetvorlage" /> <input id="recode" type="hidden" value="widgetvorlage" />
<div id="OEA_input"> <div id="OEA_input">
<div
id="firstrow"
<div id="firstrow" class="firstrow" class="firstrow"
class:sm:grid-cols-3={isTwoBoxReason} class:sm:grid-cols-3={isTwoBoxReason}
class:sm:grid-cols-2={!isTwoBoxReason}> class:sm:grid-cols-2={!isTwoBoxReason}
>
<div class="auswahl"> <div class="auswahl">
<div class="titel">Anlass</div> <div class="titel">Anlass</div>
<select <select id="anlass" class="selectfeld" bind:value={anlass}>
id="anlass" >
class="selectfeld"
bind:value={anlass}>
>
<option selected disabled>bitte auswählen</option> <option selected disabled>bitte auswählen</option>
<option value="Vermietung/Verkauf">Vermietung/Verkauf</option> <option value="Vermietung/Verkauf"
>Vermietung/Verkauf</option
>
<option value="Modernisierung">Modernisierung</option> <option value="Modernisierung">Modernisierung</option>
<option value="Neubau">Neubau</option> <option value="Neubau">Neubau</option>
<option value="Erweiterung">Erweiterung</option> <option value="Erweiterung">Erweiterung</option>
@@ -77,10 +73,8 @@ $: standardXL =
<div class="auswahl"> <div class="auswahl">
<div class="titel">Gebäudetyp</div> <div class="titel">Gebäudetyp</div>
<select <select class="selectfeld" bind:value={gebaeudetyp}>
class="selectfeld" >
bind:value={gebaeudetyp}>
>
<option selected disabled>bitte auswählen</option> <option selected disabled>bitte auswählen</option>
<option value="Einfamilienhaus">Einfamilienhaus</option> <option value="Einfamilienhaus">Einfamilienhaus</option>
<option value="Zweifamilienhaus">Zweifamilienhaus</option> <option value="Zweifamilienhaus">Zweifamilienhaus</option>
@@ -91,97 +85,94 @@ $: standardXL =
</div> </div>
{#if isTwoBoxReason} {#if isTwoBoxReason}
<div class="auswahl"> <div class="auswahl">
<div class="titel">Sanierungsstand</div> <div class="titel">Sanierungsstand</div>
<select <select class="selectfeld" bind:value={sanierungsstatus}>
class="selectfeld" >
bind:value={sanierungsstatus}> <option selected disabled>bitte auswählen</option>
<option value="saniert">saniert</option>
> <option value="unsaniert">unsaniert</option>
<option selected disabled>bitte auswählen</option> </select>
<option value="saniert">saniert</option> </div>
<option value="unsaniert">unsaniert</option>
</select>
</div>
{/if} {/if}
</div> </div>
{#if isTwoBoxReason} {#if isTwoBoxReason}
<div id="secondrow" class="secondrow"> <div id="secondrow" class="secondrow">
<div class="auswahl"> <div class="auswahl">
<div class="titel">Baujahr</div> <div class="titel">Baujahr</div>
<select <select
id="baujahr" id="baujahr"
class="selectfeld" class="selectfeld"
bind:value={baujahr} bind:value={baujahr}
>
> <option selected disabled>bitte auswählen</option>
<option selected disabled>bitte auswählen</option> <option value="vor 1978">vor 1978</option>
<option value="vor 1978">vor 1978</option> <option value="nach 1977">nach 1977</option>
<option value="nach 1977">nach 1977</option> </select>
</select> </div>
</div>
<div class="auswahl"> <div class="auswahl">
<div class="titel">Heizungsalter</div> <div class="titel">Heizungsalter</div>
<select <select class="selectfeld" bind:value={heizungsAlter}>
class="selectfeld"
bind:value={heizungsAlter}
>
<option selected disabled>bitte auswählen</option> <option selected disabled>bitte auswählen</option>
<option value="< 3">jünger als 3 Jahre</option> <option value="< 3">jünger als 3 Jahre</option>
<option value=">= 3">3 Jahre oder älter</option> <option value=">= 3">3 Jahre oder älter</option>
</select> </select>
</div> </div>
<div class="auswahl"> <div class="auswahl">
<div class="titel">Wohneinheiten</div> <div class="titel">Wohneinheiten</div>
<select <select class="selectfeld" bind:value={einheiten}>
class="selectfeld" <option selected disabled>bitte auswählen</option>
bind:value={einheiten} <option value="bis 4 Wohneinheiten"
> >bis 4 Wohneinheiten</option
<option selected disabled>bitte auswählen</option> >
<option value="bis 4 Wohneinheiten" <option value="mehr als 4 Wohneinheiten"
>bis 4 Wohneinheiten</option >mehr als 4 Wohneinheiten</option
> >
<option value="mehr als 4 Wohneinheiten" </select>
>mehr als 4 Wohneinheiten</option </div>
>
</select>
</div>
<div class="OEA_item4"> <div class="OEA_item4">
<div class="titel">Leerstand</div> <div class="titel">Leerstand</div>
<select <select class="selectfeld ausnahmen" bind:value={leerStand}>
class="selectfeld ausnahmen" <option selected disabled>bitte auswählen</option>
bind:value={leerStand} <option value="bis 30">bis 30%</option>
> <option value="mehr als 30">mehr als 30%</option>
<option selected disabled>bitte auswählen</option> </select>
<option value="bis 30">bis 30%</option> </div>
<option value="mehr als 30">mehr als 30%</option>
</select>
</div> </div>
</div>
{/if} {/if}
<div id="thirdrow" class="thirdrow" <div
class:grid-cols-1={oneBOX} id="thirdrow"
class:md:grid-cols-6={threeBOX} class="thirdrow"
class:md:grid-cols-4={!oneBOX && !threeBOX} class:grid-cols-1={oneBOX}
> class:md:grid-cols-6={threeBOX}
class:md:grid-cols-4={!oneBOX && !threeBOX}
>
{#if isTwoBoxReason && (gebaeudetyp != "Gewerbegebäude") && (ausnahme === false)} {#if isTwoBoxReason && gebaeudetyp != "Gewerbegebäude" && ausnahme === false}
<WidgetCardTemplate <WidgetCardTemplate
name="Verbrauchsausweis Wohngebäude" name="Verbrauchsausweis Wohngebäude"
price = {PRICES.VerbrauchsausweisWohnen[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]} price={PRICES.VerbrauchsausweisWohnen[
price1 = {PRICES.VerbrauchsausweisWohnen[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]} standardXL
price2 = {PRICES.VerbrauchsausweisWohnen[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]} ? Enums.AusweisTyp.standardXL
: Enums.AusweisTyp.Standard
src={'https://online-energieausweis.org/images/partner/'+partner+'/wohngebaeude.svg'} ]}
price1={PRICES.VerbrauchsausweisWohnen[
standardXL
? Enums.AusweisTyp.BeratungXL
: Enums.AusweisTyp.Beratung
]}
price2={PRICES.VerbrauchsausweisWohnen[
standardXL
? Enums.AusweisTyp.OfflineXL
: Enums.AusweisTyp.Offline
]}
src={"https://online-energieausweis.org/images/partner/" +
partner +
"/wohngebaeude.svg"}
alt="Wohnhaus Verbrauchsausweis" alt="Wohnhaus Verbrauchsausweis"
variant="einfach" variant="einfach"
empfehlung="nein" empfehlung="nein"
@@ -189,27 +180,40 @@ $: standardXL =
services={[ services={[
["3&nbsp;Jahresverbräuche der Heizung benötigt.", true], ["3&nbsp;Jahresverbräuche der Heizung benötigt.", true],
["Zulässig bei Vermietung oder Verkauf.", true], ["Zulässig bei Vermietung oder Verkauf.", true],
["Unzulässig bei unsanierten Gebäuden vor 1978.", false], [
"Unzulässig bei unsanierten Gebäuden vor 1978.",
false,
],
["Ungenau durch individuelles Heizverhalten.", false], ["Ungenau durch individuelles Heizverhalten.", false],
["Wird nicht immer bei den Banken akzeptiert.", false] ["Wird nicht immer bei den Banken akzeptiert.", false],
]} ]}
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? "?ausweistyp=standardXL" : ""}`}
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? '?ausweistyp=standardXL' : ''}`} href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`} href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? "?ausweistyp=OfflineXL" : "?ausweistyp=Offline"}`}
href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? '?ausweistyp=OfflineXL' : '?ausweistyp=Offline'}`} ></WidgetCardTemplate>
></WidgetCardTemplate>
{/if} {/if}
{#if isTwoBoxReason && (gebaeudetyp != "Gewerbegebäude")} {#if isTwoBoxReason && gebaeudetyp != "Gewerbegebäude"}
<WidgetCardTemplate <WidgetCardTemplate
name="Bedarfsausweis Wohngebäude" name="Bedarfsausweis Wohngebäude"
price = {PRICES.BedarfsausweisWohnen[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]} price={PRICES.BedarfsausweisWohnen[
price1 = {PRICES.BedarfsausweisWohnen[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]} standardXL
price2 = {PRICES.BedarfsausweisWohnen[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]} ? Enums.AusweisTyp.standardXL
src={'https://online-energieausweis.org/images/partner/'+partner+'/wohngebaeude.svg'} : Enums.AusweisTyp.Standard
]}
price1={PRICES.BedarfsausweisWohnen[
standardXL
? Enums.AusweisTyp.BeratungXL
: Enums.AusweisTyp.Beratung
]}
price2={PRICES.BedarfsausweisWohnen[
standardXL
? Enums.AusweisTyp.OfflineXL
: Enums.AusweisTyp.Offline
]}
src={"https://online-energieausweis.org/images/partner/" +
partner +
"/wohngebaeude.svg"}
alt="Wohnhaus Bedarfsausweis" alt="Wohnhaus Bedarfsausweis"
variant="fundiert" variant="fundiert"
empfehlung="ja" empfehlung="ja"
@@ -219,200 +223,283 @@ $: standardXL =
["Für Vermietung, Verkauf und Finanzierung.", true], ["Für Vermietung, Verkauf und Finanzierung.", true],
["Zulässig auch für unsanierte Objekte.", true], ["Zulässig auch für unsanierte Objekte.", true],
["Kann als Grundlage für den ISFP dienen.", true], ["Kann als Grundlage für den ISFP dienen.", true],
["Objektivere Berechnungsmethode nach DIN 18599.", true], [
"Objektivere Berechnungsmethode nach DIN 18599.",
true,
],
]} ]}
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? "?ausweistyp=standardXL" : ""}`}
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? '?ausweistyp=standardXL' : ''}`} href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`} href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? "?ausweistyp=OfflineXL" : "?ausweistyp=Offline"}`}
href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? '?ausweistyp=OfflineXL' : '?ausweistyp=Offline'}`}
></WidgetCardTemplate> ></WidgetCardTemplate>
{/if} {/if}
{#if isTwoBoxReason && isGewerbe && (leerStand != "mehr als 30")} {#if isTwoBoxReason && isGewerbe && leerStand != "mehr als 30"}
<WidgetCardTemplate <WidgetCardTemplate
name="Verbrauchsausweis Gewerbegebäude" name="Verbrauchsausweis Gewerbegebäude"
price = {PRICES.VerbrauchsausweisGewerbe[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]} price={PRICES.VerbrauchsausweisGewerbe[
price1 = {PRICES.VerbrauchsausweisGewerbe[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]} standardXL
price2 = {PRICES.VerbrauchsausweisGewerbe[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]} ? Enums.AusweisTyp.standardXL
src={'https://online-energieausweis.org/images/partner/'+partner+'/gewerbegebaeude.svg'} : Enums.AusweisTyp.Standard
]}
price1={PRICES.VerbrauchsausweisGewerbe[
standardXL
? Enums.AusweisTyp.BeratungXL
: Enums.AusweisTyp.Beratung
]}
price2={PRICES.VerbrauchsausweisGewerbe[
standardXL
? Enums.AusweisTyp.OfflineXL
: Enums.AusweisTyp.Offline
]}
src={"https://online-energieausweis.org/images/partner/" +
partner +
"/gewerbegebaeude.svg"}
alt="Gewerbe Verbrauchsausweis" alt="Gewerbe Verbrauchsausweis"
variant="einfach" variant="einfach"
empfehlung="nein" empfehlung="nein"
cta="jetzt&nbsp;online erstellen" cta="jetzt&nbsp;online erstellen"
services={[ services={[
[
["3&nbsp;Jahresverbräuche von Heizung Gebäudestrom&nbsp;nötig.", true], "3&nbsp;Jahresverbräuche von Heizung Gebäudestrom&nbsp;nötig.",
true,
],
["Zulässig bei Vermietung oder Verkauf.", true], ["Zulässig bei Vermietung oder Verkauf.", true],
["Für bauliche und energetische Maßnahmen ungeeignet.", false], [
"Für bauliche und energetische Maßnahmen ungeeignet.",
false,
],
["Wird nicht immer bei den Banken akzeptiert.", false], ["Wird nicht immer bei den Banken akzeptiert.", false],
["Ungenau durch individuelles Heizverhalten", false], ["Ungenau durch individuelles Heizverhalten", false],
]} ]}
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? "?ausweistyp=standardXL" : ""}`}
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? '?ausweistyp=standardXL' : ''}`} href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`} href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? "?ausweistyp=OfflineXL" : "?ausweistyp=Offline"}`}
href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? '?ausweistyp=OfflineXL' : '?ausweistyp=Offline'}`}
></WidgetCardTemplate> ></WidgetCardTemplate>
{/if} {/if}
{#if isTwoBoxReason && isGewerbe} {#if isTwoBoxReason && isGewerbe}
<WidgetCardTemplate <WidgetCardTemplate
name="Bedarfsausweis Gewerbegebäude" name="Bedarfsausweis Gewerbegebäude"
price = {PRICES.BedarfsausweisGewerbe[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]} price={PRICES.BedarfsausweisGewerbe[
price1 = {PRICES.BedarfsausweisGewerbe[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]} standardXL
price2 = {PRICES.BedarfsausweisGewerbe[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]} ? Enums.AusweisTyp.standardXL
src={'https://online-energieausweis.org/images/partner/'+partner+'/gewerbegebaeude.svg'} : Enums.AusweisTyp.Standard
]}
price1={PRICES.BedarfsausweisGewerbe[
standardXL
? Enums.AusweisTyp.BeratungXL
: Enums.AusweisTyp.Beratung
]}
price2={PRICES.BedarfsausweisGewerbe[
standardXL
? Enums.AusweisTyp.OfflineXL
: Enums.AusweisTyp.Offline
]}
src={"https://online-energieausweis.org/images/partner/" +
partner +
"/gewerbegebaeude.svg"}
alt="Gewerbe Bedarfsausweis" alt="Gewerbe Bedarfsausweis"
variant="fundiert" variant="fundiert"
empfehlung="ja" empfehlung="ja"
cta="Angebot anfragen" cta="Angebot anfragen"
services={[ services={[
["Mehrzonenmodell nach DIN 18599.", true], ["Mehrzonenmodell nach DIN 18599.", true],
["Zulässig bei Vermietung oder Verkauf.", true], ["Zulässig bei Vermietung oder Verkauf.", true],
["Grundlage für Sanierung-Varianten.", true], ["Grundlage für Sanierung-Varianten.", true],
["Objektiveres, besser vergleichbares Ergebnis.", true], ["Objektiveres, besser vergleichbares Ergebnis.", true],
["Zulässig bei Leerstand oder fehlenden Verbräuchen", true], [
"Zulässig bei Leerstand oder fehlenden Verbräuchen",
true,
],
]} ]}
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/${standardXL ? "?ausweistyp=standardXL" : ""}`}
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/${standardXL ? '?ausweistyp=standardXL' : ''}`} href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`}
></WidgetCardTemplate> ></WidgetCardTemplate>
{/if} {/if}
{#if (anlass != "bitte auswählen") && !isTwoBoxReason && (gebaeudetyp != "Gewerbegebäude")} {#if anlass != "bitte auswählen" && !isTwoBoxReason && gebaeudetyp != "Gewerbegebäude"}
<WidgetCardTemplate
<WidgetCardTemplate name="GEG-Nachweis Wohngebäude"
name="GEG-Nachweis Wohngebäude" price={PRICES.GEGNachweisWohnen[
price = {PRICES.GEGNachweisWohnen[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]} standardXL
price1 = {PRICES.GEGNachweisWohnen[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]} ? Enums.AusweisTyp.standardXL
price2 = {PRICES.GEGNachweisWohnen[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]} : Enums.AusweisTyp.Standard
src={'https://online-energieausweis.org/images/partner/'+partner+'/wohngebaeude.svg'} ]}
alt="GEG-Nachweis-Wohnen" price1={PRICES.GEGNachweisWohnen[
variant="Bauvorlage" standardXL
empfehlung="ja" ? Enums.AusweisTyp.BeratungXL
cta="Angebot anfragen" : Enums.AusweisTyp.Beratung
services={[ ]}
price2={PRICES.GEGNachweisWohnen[
["Nachweis fürs Bauamt bei Neubau oder Modernisierung.", true], standardXL
["Beinhaltet die Effizienz der Bauteile und Anlagentechnik.", true], ? Enums.AusweisTyp.OfflineXL
["Erstellung des Energieausweises nach Abschluss inklusive.", true], : Enums.AusweisTyp.Offline
["Berechnung und Bilanzierung nach aktueller DIN 18599.", true], ]}
["Zonierung und Erstellung eines 3D Gebäudemodells.", true], src={"https://online-energieausweis.org/images/partner/" +
]} partner +
"/wohngebaeude.svg"}
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/${standardXL ? '?ausweistyp=standardXL' : ''}`} alt="GEG-Nachweis-Wohnen"
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`} variant="Bauvorlage"
empfehlung="ja"
cta="Angebot anfragen"
></WidgetCardTemplate> services={[
[
"Nachweis fürs Bauamt bei Neubau oder Modernisierung.",
true,
],
[
"Beinhaltet die Effizienz der Bauteile und Anlagentechnik.",
true,
],
[
"Erstellung des Energieausweises nach Abschluss inklusive.",
true,
],
[
"Berechnung und Bilanzierung nach aktueller DIN 18599.",
true,
],
[
"Zonierung und Erstellung eines 3D Gebäudemodells.",
true,
],
]}
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/${standardXL ? "?ausweistyp=standardXL" : ""}`}
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
></WidgetCardTemplate>
{/if} {/if}
{#if (anlass != "bitte auswählen") && !isTwoBoxReason && isGewerbe} {#if anlass != "bitte auswählen" && !isTwoBoxReason && isGewerbe}
<WidgetCardTemplate
<WidgetCardTemplate name="GEG-Nachweis Gewerbegebäude"
name="GEG-Nachweis Gewerbegebäude" price={PRICES.GEGNachweisGewerbe[
price = {PRICES.GEGNachweisGewerbe[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]} standardXL
price1 = {PRICES.GEGNachweisGewerbe[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]} ? Enums.AusweisTyp.standardXL
price2 = {PRICES.GEGNachweisGewerbe[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]} : Enums.AusweisTyp.Standard
src={'https://online-energieausweis.org/images/partner/'+partner+'/gewerbegebaeude.svg'} ]}
alt="GEG-Nachweis-Gewerbe" price1={PRICES.GEGNachweisGewerbe[
variant="Bauvorlage" standardXL
empfehlung="ja" ? Enums.AusweisTyp.BeratungXL
cta="Angebot anfragen" : Enums.AusweisTyp.Beratung
services={[ ]}
price2={PRICES.GEGNachweisGewerbe[
["Nachweis fürs Bauamt bei Neubau oder Modernisierung.", true], standardXL
["Beinhaltet die Effizienz der Bauteile und Anlagentechnik.", true], ? Enums.AusweisTyp.OfflineXL
["Erstellung des Energieausweises nach Abschluss inklusive.", true], : Enums.AusweisTyp.Offline
["Berechnung und Bilanzierung nach aktueller DIN 18599.", true], ]}
["Mehrzonenmodell inkl. Erstellung eines 3D Gebäudemodells.", true], src={"https://online-energieausweis.org/images/partner/" +
]} partner +
"/gewerbegebaeude.svg"}
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/${standardXL ? '?ausweistyp=standardXL' : ''}`} alt="GEG-Nachweis-Gewerbe"
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`} variant="Bauvorlage"
empfehlung="ja"
></WidgetCardTemplate> cta="Angebot anfragen"
services={[
[
"Nachweis fürs Bauamt bei Neubau oder Modernisierung.",
true,
],
[
"Beinhaltet die Effizienz der Bauteile und Anlagentechnik.",
true,
],
[
"Erstellung des Energieausweises nach Abschluss inklusive.",
true,
],
[
"Berechnung und Bilanzierung nach aktueller DIN 18599.",
true,
],
[
"Mehrzonenmodell inkl. Erstellung eines 3D Gebäudemodells.",
true,
],
]}
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/${standardXL ? "?ausweistyp=standardXL" : ""}`}
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
></WidgetCardTemplate>
{/if} {/if}
</div> </div>
</div> </div>
</div> </div>
<style lang="postcss"> <style lang="postcss">
#IBC_app {
@font-face {
font-family: "immo Sans";
src: url("/fonts/Immo-Sans/immoSans-Regular.eot");
src:
url("/fonts/Immo-Sans/immoSans-Regular.eot?#iefix")
format("embedded-opentype"),
url("/fonts/Immo-Sans/immoSans-Regular.woff2") format("woff2"),
url("/fonts/Immo-Sans/immoSans-Regular.woff") format("woff");
font-style: normal;
font-weight: 400;
}
#IBC_app { @font-face {
font-family: "immo Sans Bold";
src: url("/fonts/Immo-Sans/immoSans-Bold.eot");
src:
url("/fonts/Immo-Sans/immoSans-Bold.eot?#iefix")
format("embedded-opentype"),
url("../../Fonts/Immo-Sans/immoSans-Bold.woff2") format("woff2"),
url("../../Fonts/Immo-Sans/immoSans-Bold.woff") format("woff");
font-style: normal;
font-weight: 700;
}
@font-face { @font-face {
font-family: "immo Sans"; font-family: "Antique Olive Compact bold";
src: url('/fonts/Immo-Sans/immoSans-Regular.eot'); font-weight: 700;
src: url('/fonts/Immo-Sans/immoSans-Regular.eot?#iefix') format('embedded-opentype'), font-style: normal;
url('/fonts/Immo-Sans/immoSans-Regular.woff2') format('woff2'), font-display: swap;
url('/fonts/Immo-Sans/immoSans-Regular.woff') format('woff'); src: url("/fonts/Antique Olive Std Compact.woff2") format("woff2");
font-style: normal; }
font-weight: 400;
}
@font-face { @apply min-w-[320px] max-w-[1920px] p-[4px]
font-family: "immo Sans Bold";
src: url('/fonts/Immo-Sans/immoSans-Bold.eot');
src: url('/fonts/Immo-Sans/immoSans-Bold.eot?#iefix') format('embedded-opentype'), url('../../Fonts/Immo-Sans/immoSans-Bold.woff2') format('woff2'), url('../../Fonts/Immo-Sans/immoSans-Bold.woff') format('woff');
font-style: normal;
font-weight: 700;
}
@font-face {
font-family: 'Antique Olive Compact bold';
font-weight: 700;
font-style: normal;
font-display:swap;
src: url("/fonts/Antique Olive Std Compact.woff2") format('woff2');
}
@apply min-w-[320px] max-w-[1920px] p-[4px]
sm:p-[10px]; sm:p-[10px];
font-family: "immo Sans"; font-family: "immo Sans";
select option{ select option {
font-family: "immo Sans",sans-serif;} font-family: "immo Sans", sans-serif;
}
.firstrow{@apply grid grid-cols-1 gap-x-4 gap-y-2 .firstrow {
@apply grid grid-cols-1 gap-x-4 gap-y-2
sm:grid-cols-2 sm:gap-x-4 sm:gap-y-2; sm:grid-cols-2 sm:gap-x-4 sm:gap-y-2;
.titel{@apply text-black font-bold bg-[#ffcc00] px-2 py-1 rounded-[0.25rem];} .titel {
@apply text-black font-bold bg-[#ffcc00] px-2 py-1 rounded-[0.25rem];
}
.selectfeld{@apply w-full px-2 py-1 min-h-[38px] ring-1 ring-black/15} .selectfeld {
@apply w-full px-2 py-1 min-h-[38px] ring-1 ring-black/15;
}
}
} .secondrow {
@apply grid grid-cols-2 gap-x-4 gap-y-2 mt-4
.secondrow{@apply grid grid-cols-2 gap-x-4 gap-y-2 mt-4
sm:grid-cols-4 sm:gap-x-4 sm:gap-y-2; sm:grid-cols-4 sm:gap-x-4 sm:gap-y-2;
.titel{@apply text-black font-bold bg-[#cccccc] px-2 py-1 rounded-[0.25rem];} .titel {
@apply text-black font-bold bg-[#cccccc] px-2 py-1 rounded-[0.25rem];
.selectfeld{@apply w-full px-2 py-1 min-h-[38px] ring-1 ring-black/15} }
} .selectfeld {
@apply w-full px-2 py-1 min-h-[38px] ring-1 ring-black/15;
}
}
#OEA_input{@apply grid} #OEA_input {
@apply grid;
}
.thirdrow{@apply grid grid-cols-1 gap-x-4 gap-y-2 col-start-1 .thirdrow {
@apply grid grid-cols-1 gap-x-4 gap-y-2 col-start-1
md:grid-cols-4 md:gap-x-4 md:gap-y-2; md:grid-cols-4 md:gap-x-4 md:gap-y-2;
}
}
}
}
</style> </style>

View File

@@ -34,7 +34,6 @@
ab {price} ab {price}
</p> </p>
</div> </div>
</div> </div>
<hr class="col-span-2 w-full md:w-[50%] md:m-auto bg-[#ffcc00] h-[2px]" /> <hr class="col-span-2 w-full md:w-[50%] md:m-auto bg-[#ffcc00] h-[2px]" />
@@ -50,11 +49,13 @@
{/each} {/each}
</div> </div>
<hr class="col-span-2 w-full md:w-[50%] md:m-auto bg-[#ffcc00] h-[2px]" /> <hr class="col-span-2 w-full md:w-[50%] md:m-auto bg-[#ffcc00] h-[2px]" />
<div class="sumCent buttoncols" <div
class:md:grid-cols-3={href_buy3} class="sumCent buttoncols"
class:md:grid-cols-2={!href_buy3}> class:md:grid-cols-3={href_buy3}
class:md:grid-cols-2={!href_buy3}
>
<a <a
href={href_buy1} href={href_buy1}
class="buttoncol" class="buttoncol"

View File

@@ -38,13 +38,13 @@ export const BedarfsausweisWohnenSchema = z.object({
volumen: z.number().nullish(), volumen: z.number().nullish(),
dicht: z.boolean().nullish(), dicht: z.boolean().nullish(),
fenster_flaeche_1: z.number().nullish(), fenster_flaeche_1: z.number().nullish(),
fenster_art_1: z.string().nullish(), fenster_art_1: z.number().nullish(),
fenster_flaeche_2: z.number().nullish(), fenster_flaeche_2: z.number().nullish(),
fenster_art_2: z.string().nullish(), fenster_art_2: z.number().nullish(),
dachfenster_flaeche: z.number().nullish(), dachfenster_flaeche: z.number().nullish(),
dachfenster_art: z.string().nullish(), dachfenster_art: z.number().nullish(),
haustuer_flaeche: z.number().nullish(), haustuer_flaeche: z.number().nullish(),
haustuer_art: z.string().nullish(), haustuer_art: z.number().nullish(),
dach_bauart: z.string().nullish(), dach_bauart: z.string().nullish(),
decke_bauart: z.string().nullish(), decke_bauart: z.string().nullish(),
dach_daemmung: z.string().nullish(), dach_daemmung: z.string().nullish(),

View File

@@ -0,0 +1,29 @@
import { uptime } from "os"
import crypto from "crypto";
/**
* Generiert einen zeitbasierten Hash der sich alle 15 Minuten ändert und an die Uptime des servers gekoppelt ist.
* @param email - Die E-Mail-Adresse des Benutzers, die als Teil des Hashes verwendet wird.
* @param time - Die Zeit in Millisekunden, die seit dem Start des Servers vergangen ist (Standard ist die Uptime des Servers).
* @param length - Die Länge des zurückgegebenen Hashes (Standard ist 6 Zeichen).
* @returns Ein zeitbasierter Hash, der sich alle 15 Minuten ändert und auf der E-Mail-Adresse basiert.
*/
export function createTimeBasedHash(email: string, time: number = uptime(), length: number = 6): string {
const now = Date.now();
const elapsed = now - time;
const window = Math.floor(elapsed / (15 * 60 * 1000)); // 15 minute windows
const data = `${email}:${window}`;
// Use a cryptographic hash (you can also use HMAC with a secret if you want)
const hash = crypto.createHash('sha256').update(data).digest();
// Convert part of the hash to an integer
const int = hash.readUInt32BE(0); // take first 4 bytes
// Modulo to get 6 digits
const pin = (int % 1000000).toString().padStart(6, '0');
return pin;
}

View File

@@ -1,27 +1,11 @@
import { import { AufnahmeClient, BedarfsausweisWohnenClient, BenutzerClient, BildClient, getAusweisartFromId, ObjektClient, RechnungClient, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js";
AufnahmeClient,
BedarfsausweisWohnenClient,
BenutzerClient,
BildClient,
getAusweisartFromId,
ObjektClient,
RechnungClient,
VerbrauchsausweisGewerbeClient,
VerbrauchsausweisWohnenClient,
} from "#components/Ausweis/types.js";
import { pdfDatenblattVerbrauchsausweisGewerbe } from "#lib/pdf/pdfDatenblattVerbrauchsausweisGewerbe.js"; import { pdfDatenblattVerbrauchsausweisGewerbe } from "#lib/pdf/pdfDatenblattVerbrauchsausweisGewerbe.js";
import { pdfDatenblattVerbrauchsausweisWohnen } from "#lib/pdf/pdfDatenblattVerbrauchsausweisWohnen.js"; import { pdfDatenblattVerbrauchsausweisWohnen } from "#lib/pdf/pdfDatenblattVerbrauchsausweisWohnen.js";
import { pdfVerbrauchsausweisGewerbe } from "#lib/pdf/pdfVerbrauchsausweisGewerbe.js"; import { pdfVerbrauchsausweisGewerbe } from "#lib/pdf/pdfVerbrauchsausweisGewerbe.js";
import { pdfVerbrauchsausweisWohnen } from "#lib/pdf/pdfVerbrauchsausweisWohnen.js"; import { pdfVerbrauchsausweisWohnen } from "#lib/pdf/pdfVerbrauchsausweisWohnen.js";
import { pdfAushangVerbrauchsausweisGewerbe } from "#lib/pdf/pdfAushangVerbrauchsausweisGewerbe.js"; import { pdfAushangVerbrauchsausweisGewerbe } from "#lib/pdf/pdfAushangVerbrauchsausweisGewerbe.js";
import { import { Enums, prisma, Rechnung } from "#lib/server/prisma.js";
BedarfsausweisWohnen,
Enums,
prisma,
Rechnung,
VerbrauchsausweisGewerbe,
VerbrauchsausweisWohnen,
} from "#lib/server/prisma.js";
/** /**
* Gibt den richtigen Prisma Adapter für die Ausweisart basierend auf der UID zurück, oder null bei einer falschen UID. * Gibt den richtigen Prisma Adapter für die Ausweisart basierend auf der UID zurück, oder null bei einer falschen UID.
@@ -31,179 +15,68 @@ export function getPrismaAusweisAdapter(id: string) {
const ausweisart = getAusweisartFromId(id); const ausweisart = getAusweisartFromId(id);
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) { if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
return prisma.verbrauchsausweisWohnen; return prisma.verbrauchsausweisWohnen
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) { } else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
return prisma.verbrauchsausweisGewerbe; return prisma.verbrauchsausweisGewerbe
} else if (ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) { } else if (ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) {
return prisma.bedarfsausweisWohnen; return prisma.bedarfsausweisWohnen
} else if (ausweisart === Enums.Ausweisart.GEGNachweisWohnen) { } else if (ausweisart === Enums.Ausweisart.GEGNachweisWohnen) {
return prisma.gEGNachweisWohnen; return prisma.gEGNachweisWohnen
} else if (ausweisart === Enums.Ausweisart.GEGNachweisGewerbe) { } else if (ausweisart === Enums.Ausweisart.GEGNachweisGewerbe) {
return prisma.gEGNachweisGewerbe; return prisma.gEGNachweisGewerbe
} else if (ausweisart === Enums.Ausweisart.BedarfsausweisGewerbe) { } else if (ausweisart === Enums.Ausweisart.BedarfsausweisGewerbe) {
return prisma.bedarfsausweisGewerbe; return prisma.bedarfsausweisGewerbe
} }
} }
/** /**
* Gibt den richtigen Ansichtsausweis basierend auf der Ausweisart zurück. * Gibt den richtigen Ansichtsausweis basierend auf der Ausweisart zurück.
* @param ausweis * @param ausweis
*/ */
export async function getAnsichtsausweis( export async function getAnsichtsausweis(ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient, aufnahme: AufnahmeClient, objekt: ObjektClient, bilder: BildClient[], user: BenutzerClient, vorschau: boolean = true, ausweisart = getAusweisartFromId(ausweis.id)) {
ausweis:
| VerbrauchsausweisWohnenClient
| VerbrauchsausweisGewerbeClient
| BedarfsausweisWohnenClient,
aufnahme: AufnahmeClient,
objekt: ObjektClient,
bilder: BildClient[],
user: BenutzerClient,
vorschau: boolean = true,
ausweisart = getAusweisartFromId(ausweis.id)
) {
if (!ausweisart) { if (!ausweisart) {
return null; return null
} }
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) { if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
return await pdfVerbrauchsausweisWohnen( return await pdfVerbrauchsausweisWohnen(ausweis as VerbrauchsausweisWohnenClient, aufnahme, objekt, bilder, user, vorschau)
ausweis as VerbrauchsausweisWohnenClient,
aufnahme,
objekt,
bilder,
user,
vorschau
);
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) { } else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
return await pdfVerbrauchsausweisGewerbe( return await pdfVerbrauchsausweisGewerbe(ausweis as VerbrauchsausweisGewerbeClient, aufnahme, objekt, bilder, user, vorschau)
ausweis as VerbrauchsausweisGewerbeClient,
aufnahme,
objekt,
bilder,
user,
vorschau
);
} }
return null; return null
} }
/** /**
* Gibt das richtige Datenblatt basierend auf der Ausweisart zurück. * Gibt das richtige Datenblatt basierend auf der Ausweisart zurück.
* @param ausweis * @param ausweis
*/ */
export async function getDatenblatt( export async function getDatenblatt(ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient, aufnahme: AufnahmeClient, objekt: ObjektClient, bilder: BildClient[], user: BenutzerClient, rechnung: Rechnung, ausweisart = getAusweisartFromId(ausweis.id)) {
ausweis:
| VerbrauchsausweisWohnenClient
| VerbrauchsausweisGewerbeClient
| BedarfsausweisWohnenClient,
aufnahme: AufnahmeClient,
objekt: ObjektClient,
bilder: BildClient[],
user: BenutzerClient,
rechnung: Rechnung,
ausweisart = getAusweisartFromId(ausweis.id)
) {
if (!ausweisart) { if (!ausweisart) {
return null; return null
} }
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) { if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
return await pdfDatenblattVerbrauchsausweisWohnen( return await pdfDatenblattVerbrauchsausweisWohnen(ausweis as VerbrauchsausweisWohnenClient, aufnahme, objekt, rechnung, bilder)
ausweis as VerbrauchsausweisWohnenClient,
aufnahme,
objekt,
rechnung,
bilder
);
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) { } else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
return await pdfDatenblattVerbrauchsausweisGewerbe( return await pdfDatenblattVerbrauchsausweisGewerbe(ausweis as VerbrauchsausweisGewerbeClient, aufnahme, objekt, rechnung, bilder)
ausweis as VerbrauchsausweisGewerbeClient,
aufnahme,
objekt,
rechnung,
bilder
);
} }
return null; return null
} }
/** /**
* Gibt den richtigen Aushang basierend auf der Ausweisart zurück. * Gibt den richtigen Aushang basierend auf der Ausweisart zurück.
* @param ausweis * @param ausweis
*/ */
export async function getAushang( export async function getAushang(ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient, aufnahme: AufnahmeClient, objekt: ObjektClient, bilder: BildClient[], user: BenutzerClient, vorschau: boolean = true, rechnung: Rechnung, ausweisart = getAusweisartFromId(ausweis.id)) {
ausweis:
| VerbrauchsausweisWohnenClient
| VerbrauchsausweisGewerbeClient
| BedarfsausweisWohnenClient,
aufnahme: AufnahmeClient,
objekt: ObjektClient,
bilder: BildClient[],
user: BenutzerClient,
vorschau: boolean = true,
rechnung: Rechnung,
ausweisart = getAusweisartFromId(ausweis.id)
) {
if (!ausweisart || !rechnung.services.includes(Enums.Service.Aushang)) { if (!ausweisart || !rechnung.services.includes(Enums.Service.Aushang)) {
return null; return null
} }
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) { if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
return await pdfAushangVerbrauchsausweisGewerbe( return await pdfAushangVerbrauchsausweisGewerbe(ausweis as VerbrauchsausweisGewerbeClient, aufnahme, objekt, bilder, user, vorschau)
ausweis as VerbrauchsausweisGewerbeClient,
aufnahme,
objekt,
bilder,
user,
vorschau
);
} }
return null; return null
} }
/**
* Extrahiert die Ausweisfelder aus einem Objekt, das mehrere Ausweisarten enthält, und fasst sie in einem gemeinsamen Feld `ausweis` zusammen.
* @param row Ein Objekt, das die Ausweisfelder enthält.
* @returns Ein neues Objekt, das die Ausweisfelder extrahiert und in einem gemeinsamen Feld `ausweis` zusammenfasst.
*/
export function extrahiereAusweisAusFeldMitMehrerenAusweisen<T>(
row: T & {
bedarfsausweis_wohnen?: BedarfsausweisWohnen;
verbrauchsausweis_wohnen?: VerbrauchsausweisWohnen;
verbrauchsausweis_gewerbe?: VerbrauchsausweisGewerbe;
}
) {
const {
bedarfsausweis_wohnen,
verbrauchsausweis_wohnen,
verbrauchsausweis_gewerbe,
...rest
} = row;
return {
...rest,
ausweis: {
...(bedarfsausweis_wohnen ??
verbrauchsausweis_wohnen ??
verbrauchsausweis_gewerbe),
ausweisart: bedarfsausweis_wohnen
? Enums.Ausweisart.BedarfsausweisWohnen
: verbrauchsausweis_wohnen
? Enums.Ausweisart.VerbrauchsausweisWohnen
: Enums.Ausweisart.VerbrauchsausweisGewerbe,
},
} as {
ausweis: (
| BedarfsausweisWohnen
| VerbrauchsausweisWohnen
| VerbrauchsausweisGewerbe
) & { ausweisart: Enums.Ausweisart };
} & Omit<
T,
| "bedarfsausweis_wohnen"
| "verbrauchsausweis_wohnen"
| "verbrauchsausweis_gewerbe"
>;
}

View File

@@ -0,0 +1,41 @@
import { transport } from "#lib/mail.js";
import {
Benutzer,
} from "#lib/client/prisma.js";
import { createTimeBasedHash } from "#lib/auth/time-based-hash.js";
export async function sendVerificationCodeMail(
user: Benutzer
) {
const code = createTimeBasedHash(user.email)
await transport.sendMail({
from: `"IBCornelsen" <info@online-energieausweis.org>`,
to: user.email,
subject: `Ihre Registrierung bei IBCornelsen`,
bcc: "info@online-energieausweis.org",
html: `<p>Sehr geehrte*r ${user.vorname} ${user.name},</p>
<p>Um Ihre Registrierung abzuschließen, geben Sie folgenden Bestätigungscode auf der Website ein, um Ihre E-Mail-Adresse zu bestätigen:<br><br>
<b>${code}</b>
</p>
<p>
Mit freundlichen Grüßen,
<br>
Dipl.-Ing. Jens Cornelsen
<br>
<br>
<strong>IB Cornelsen</strong>
<br>
Katendeich 5A
<br>
21035 Hamburg
<br>
www.online-energieausweis.org
<br>
<br>
fon 040 · 209339850
<br>
fax 040 · 209339859
</p>`
});
}

View File

@@ -5,6 +5,7 @@ import {
} from "#lib/client/prisma.js"; } from "#lib/client/prisma.js";
import { encodeToken } from "#lib/auth/token.js"; import { encodeToken } from "#lib/auth/token.js";
import { TokenType } from "#lib/auth/types.js"; import { TokenType } from "#lib/auth/types.js";
import { createTimeBasedHash } from "#lib/auth/time-based-hash.js";
export async function sendRegisterMail( export async function sendRegisterMail(
user: Benutzer user: Benutzer
@@ -12,9 +13,11 @@ export async function sendRegisterMail(
const verificationJwt = encodeToken({ const verificationJwt = encodeToken({
typ: TokenType.Verify, typ: TokenType.Verify,
exp: Date.now() + (15 * 60 * 1000), exp: Date.now() + (15 * 60 * 1000),
uid: user.uid id: user.id
}) })
const code = createTimeBasedHash(user.email)
await transport.sendMail({ await transport.sendMail({
from: `"IBCornelsen" <info@online-energieausweis.org>`, from: `"IBCornelsen" <info@online-energieausweis.org>`,
to: user.email, to: user.email,
@@ -22,10 +25,11 @@ export async function sendRegisterMail(
bcc: "info@online-energieausweis.org", bcc: "info@online-energieausweis.org",
html: `<p>Sehr geehrte*r ${user.vorname} ${user.name},</p> html: `<p>Sehr geehrte*r ${user.vorname} ${user.name},</p>
<p>vielen Dank für Ihre Registrierung bei IBCornelsen. Ihr Benutzerkonto wurde erfolgreich erstellt.<br><br> <p>vielen Dank für Ihre Registrierung bei IBCornelsen. Ihr Benutzerkonto wurde erfolgreich erstellt.<br><br>
Um Ihre Registrierung abzuschließen, klicken Sie bitte auf den folgenden Link, um Ihre E-Mail-Adresse zu bestätigen:<br><br> Um Ihre Registrierung abzuschließen, klicken Sie bitte auf den folgenden Link, um Ihre E-Mail-Adresse zu bestätigen:<br><br>
<a href="${BASE_URI}/auth/verify?t=${verificationJwt}">E-Mail-Adresse bestätigen</a><br><br>
<a href="${BASE_URI}/auth/verify?t=${verificationJwt}">E-Mail-Adresse bestätigen</a><br></p> Oder geben Sie folgenden Bestätigungscode auf der Website ein:<br>
<b>${code}</b>
</p>
<p> <p>
Mit freundlichen Grüßen, Mit freundlichen Grüßen,
<br> <br>

View File

@@ -6,10 +6,11 @@
export let onLogin: (response: Awaited<ReturnType<typeof loginClient>>) => any; export let onLogin: (response: Awaited<ReturnType<typeof loginClient>>) => any;
export let email: string = ""; export let email: string = "";
export let password: string = ""; export let password: string = "";
export let route: "login" | "signup" = "login"
const navigate = (target: string) => { let route: "login" | "signup" = "login"
route = target as typeof route;
const navigate = (target: typeof route) => {
route = target
} }
const loginData = { const loginData = {

View File

@@ -0,0 +1,384 @@
<script lang="ts">
import ZipSearch from "../components/PlzSuche.svelte";
import Label from "../components/Label.svelte";
import type {
Bezahlmethoden,
} from "#lib/client/prisma";
import { Enums } from "#lib/client/prisma";
import PaymentOption from "#components/PaymentOption.svelte";
import CheckoutItem from "#components/CheckoutItem.svelte";
import { BenutzerClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js";
import { PRICES } from "#lib/constants.js";
import { RechnungClient } from "#components/Ausweis/types.js";
import { api } from "astro-typesafe-api/client";
export let user: BenutzerClient;
export let ausweis:
| VerbrauchsausweisWohnenClient;
// TODO: überarbeiten und zu inferProcedureOutput machen
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: "Qualitätsdruck per Post (zusätzlich zur PDF Version) für 9€ inkl. MwSt.",
id: Enums.Service.Qualitaetsdruck,
price: 9,
selected: false,
},
{
name: "Aushang (für öffentliche Gebäude gesetzlich vorgeschrieben) für 10€ inkl. MwSt.",
id: Enums.Service.Aushang,
price: 10,
selected: false,
},
{
name: "Same Day Service (Bestellung Werktags vor 12:00 Uhr - Ausstellung bis 18:00 Uhr am gleichen Tag) für 29€ inkl. MwSt.",
id: Enums.Service.SameDay,
price: 29,
selected: false,
},
{
name: "Telefonische Energieeffizienzberatung für 75€ inkl. MwSt.",
id: Enums.Service.Telefonberatung,
price: 75,
selected: false,
},
];
export let aktiveBezahlmethode: Bezahlmethoden =
Enums.Bezahlmethoden.paypal;
async function createPayment(e: SubmitEvent) {
e.preventDefault();
// TODO
const response = await api.rechnung.PUT.fetch({
...rechnung,
ausweisart: Enums.Ausweisart.VerbrauchsausweisWohnen,
ausweis_uid: ausweis.uid,
bezahlmethode: aktiveBezahlmethode,
services: services
.filter((service) => service.selected)
.map((service) => service.id),
});
if (aktiveBezahlmethode === Enums.Bezahlmethoden.rechnung) {
window.location.href = `/payment/success?r=${response.uid}&a=${ausweis.uid}`
} else {
window.location.href = response.checkout_url as string;
}
}
const priceTotal = services.reduce((acc, service) => {
if (service.selected) {
return acc + service.price;
}
return acc;
}, 0) + PRICES[Enums.Ausweisart.VerbrauchsausweisWohnen][0];
</script>
<form class="grid grid-cols-[2fr_1fr] gap-4 h-full" on:submit={createPayment}>
<div>
<h3 class="font-semibold">Ansprechpartner</h3>
<div class="rounded-lg border p-4 border-base-300 bg-base-100">
<div class="grid grid-cols-3 gap-4">
<!-- Anrede -->
<div>
<Label>Anrede *</Label>
<div>
<select name="anrede" bind:value={user.anrede}>
<option>bitte auswählen</option>
<option value="Herr">Herr</option>
<option value="Frau">Frau</option>
</select>
</div>
</div>
<!-- Vorname -->
<div>
<Label>Vorname *</Label>
<input
name="vorname"
type="text"
bind:value={user.vorname}
required
/>
</div>
<!-- Nachname -->
<div>
<Label>Nachname *</Label>
<input
name="name"
type="text"
bind:value={user.name}
required
/>
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<!-- Telefon -->
<div>
<Label>Telefon</Label>
<input
name="telefon"
bind:value={user.telefon}
type="text"
/>
</div>
<!-- Email -->
<div>
<Label>E-Mail *</Label>
<input
name="email"
type="email"
bind:value={user.email}
required
/>
</div>
</div>
</div>
<h3 class="mt-8 font-semibold">Rechnungsadresse</h3>
<div class="rounded-lg border p-4 border-base-300 bg-base-100">
<div class="grid grid-cols-2 gap-4">
<div>
<Label>Empfänger *</Label>
<input
name="rechnung_empfaenger"
type="text"
bind:value={rechnung.empfaenger}
required
data-rule-maxlength="100"
data-msg-maxlength="max. 100 Zeichen"
/>
</div>
<!-- Zusatzzeile -->
<div>
<Label>Zusatzzeile</Label>
<input
name="rechnung_zusatzzeile"
bind:value={rechnung.zusatzzeile}
type="text"
data-rule-maxlength="80"
data-msg-maxlength="max. 80 Zeichen"
/>
</div>
</div>
<div class="grid grid-cols-3 gap-4">
<!-- Strasse -->
<div>
<Label>Straße, Hausnummer *</Label>
<input
name="rechnung_strasse"
bind:value={rechnung.strasse}
type="text"
required
data-rule-maxlength="40"
data-msg-maxlength="max. 40 Zeichen"
/>
</div>
<!-- PLZ -->
<ZipSearch
name="rechnung_plz"
bind:zip={rechnung.plz}
bind:city={rechnung.ort}
/>
<!-- Ort -->
<div>
<Label>Ort *</Label>
<input
name="rechnung_ort"
readonly
type="text"
required
value={rechnung.ort}
/>
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<!-- Telefon -->
<div>
<Label>Telefon</Label>
<input
name="rechnung_telefon"
bind:value={rechnung.telefon}
type="text"
/>
</div>
<!-- Email -->
<div>
<Label>E-Mail</Label>
<input
name="rechnung_email"
bind:value={rechnung.email}
type="email"
/>
</div>
</div>
</div>
<h3 class="mt-8 font-semibold">Versandadresse</h3>
<div class="rounded-lg border p-4 border-base-300 bg-base-100">
<div class="flex flex-row gap-2 items-center">
<input
class="w-[15px] h-[15px]"
type="checkbox"
name="abweichende_versand_adresse"
bind:checked={rechnung.abweichende_versand_adresse}
/>
<Label>Abweichende Versandadresse</Label>
</div>
<div class="grid grid-cols-2 gap-4">
<!-- Empfänger -->
<div>
<Label>Empfänger *</Label>
<input
name="versand_empfaenger"
type="text"
readonly={!rechnung.abweichende_versand_adresse}
bind:value={rechnung.versand_empfaenger}
required
data-rule-maxlength="100"
data-msg-maxlength="max. 100 Zeichen"
/>
</div>
<!-- Zusatzzeile -->
<div>
<Label>Zusatzzeile</Label>
<input
name="versand_zusatzzeile"
type="text"
readonly={!rechnung.abweichende_versand_adresse}
bind:value={rechnung.versand_zusatzzeile}
data-rule-maxlength="80"
data-msg-maxlength="max. 80 Zeichen"
/>
</div>
</div>
<div class="grid grid-cols-3 gap-4">
<!-- Strasse -->
<div>
<Label>Straße, Hausnummer *</Label>
<input
name="versand_strasse"
type="text"
readonly={!rechnung.abweichende_versand_adresse}
bind:value={rechnung.versand_strasse}
required
data-rule-maxlength="40"
data-msg-maxlength="max. 40 Zeichen"
/>
</div>
<!-- PLZ -->
<ZipSearch
name="versand_plz"
readonly={!rechnung.abweichende_versand_adresse}
bind:zip={rechnung.versand_plz}
bind:city={rechnung.versand_ort}
/>
<!-- Ort -->
<div>
<Label>Ort *</Label>
<input
name="versand_ort"
type="text"
readonly
required
bind:value={rechnung.versand_ort}
/>
</div>
</div>
</div>
<h3 class="mt-8 font-semibold">Bezahlmethode</h3>
<div
class="rounded-lg border p-4 border-base-300 bg-base-100 flex flex-row gap-4 justify-between"
>
<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/mastercard.png"}
></PaymentOption>
<PaymentOption
bezahlmethode={Enums.Bezahlmethoden.rechnung}
bind:aktiveBezahlmethode
name={"Rechnung"}
icon={"/images/rechnung.png"}
></PaymentOption>
</div>
</div>
<div>
<h3 class="font-semibold">Zusammenfassung</h3>
<div class="rounded-lg border p-4 border-base-300 bg-base-100">
<CheckoutItem
image={"https://www.gih.de/wp-content/uploads/2015/11/EnergieausweisW-E.jpg"}
name={"Energieausweis"}
description={"Verbrauchsausweis Wohnen"}
price={45}
quantity={1}
removable={false}
maxQuantity={1}
/>
<div class="mt-auto">
<hr />
<div class="flex flex-row items-center justify-between">
<span class="opacity-75 text-sm">Netto</span>
<span class="font-semibold text-sm">{Math.round(priceTotal * 0.81 * 100) / 100}</span>
</div>
<div class="flex flex-row items-center justify-between">
<span class="opacity-75 text-sm">19% MwSt</span>
<span class="font-semibold text-sm">{Math.round(priceTotal * 0.19 * 100) / 100}}€</span>
</div>
<hr />
<div class="flex flex-row items-center justify-between">
<span class="opacity-75 text-sm">Gesamt</span>
<span class="font-semibold text-sm">{Math.round(priceTotal)}</span>
</div>
<p class="mt-8">Mit dem Klick auf "Bestellung Bestätigen" akzeptieren sie unsere <a href="/agb">AGB</a> und <a href="/impressum">Datenschutzbestimmungen</a>. Sie werden zu ihrem ausgewählten Bezahlprovider weitergeleitet, nach Bezahlung werden sie automatisch zu unserem Portal zurückgeleitet.</p>
<button class="btn btn-secondary w-full mt-4"
>Bestellung Bestätigen</button
>
</div>
</div>
</div>
</form>

View File

@@ -1156,7 +1156,7 @@ grid-cols-3 sm:grid-cols-5 justify-around justify-items-center items-center"
{/if} {/if}
<!-- Für alle --> <!-- Für alle -->
<div class="pruefpunkt"> <div class="pruefpunkt">
<input type="checkbox" required/> <input type="checkbox"/>
<div class="text-left"> <div class="text-left">
Ich habe die AGB und DSGVO im <a href="/impressum#agb" target="_blank" rel="noopener noreferrer">Impressum</a> gelesen und akzeptiert. Ich habe die AGB und DSGVO im <a href="/impressum#agb" target="_blank" rel="noopener noreferrer">Impressum</a> gelesen und akzeptiert.
</div> </div>
@@ -1215,7 +1215,7 @@ sm:grid-cols-[min-content_min-content_min-content] sm:justify-self-end sm:mt-8"
<Overlay bind:hidden={loginOverlayHidden}> <Overlay bind:hidden={loginOverlayHidden}>
<div class="bg-white w-full max-w-screen-sm py-8"> <div class="bg-white w-full max-w-screen-sm py-8">
<EmbeddedAuthFlowModule onLogin={loginAction} email={email} route="signup"></EmbeddedAuthFlowModule> <EmbeddedAuthFlowModule onLogin={loginAction} email={email}></EmbeddedAuthFlowModule>
</div> </div>
</Overlay> </Overlay>

View File

@@ -30,7 +30,7 @@
} }
try { try {
const { uid } = await api.user.PUT.fetch({ await api.user.PUT.fetch({
email, email,
passwort, passwort,
vorname, vorname,
@@ -42,7 +42,7 @@
return return
} }
window.location.href = "/auth/login"; window.location.href = `/auth/code?email=${email}`;
} catch (e) { } catch (e) {
errorHidden = false; errorHidden = false;
} }

View File

@@ -0,0 +1,89 @@
<script lang="ts">
import { addNotification } from "#components/Notifications/shared.js";
import { CrossCircled } from "radix-svelte-icons";
import { fade } from "svelte/transition";
import { api } from "astro-typesafe-api/client";
import NotificationWrapper from "#components/Notifications/NotificationWrapper.svelte";
export let redirect: string | null = null;
export let email: string;
function verify(e: SubmitEvent) {
e.preventDefault();
const code = numbers.join("");
if (code.length !== 6) {
addNotification({
message: "Bitte geben Sie einen gültigen Verifizierungscode ein.",
dismissable: true,
timeout: 3000,
type: "error"
});
return;
}
api.auth["verification-code"].POST.fetch({ code, email }).then(() => {
if (redirect) {
window.location.href = redirect;
} else {
window.location.href = "/";
}
}).catch(() => {
errorHidden = false;
});
}
// TODO
function codeErneutAnfordern() {
api.auth["verification-code"].GET.fetch(null, {
headers: {
"Authorization": "Bearer"
}
})
}
let numbers = new Array(6).fill("");
let errorHidden = true;
</script>
<div class="mx-auto w-1/3 bg-base-200 p-8 border border-base-300 rounded-lg">
<h1 class="text-3xl mb-4">Verifizierung</h1>
<p>Wir haben ihnen einen Verifizierungscode per Email geschickt, bitte geben sie diesen ein um ihre Registrierung fertigzustellen.</p>
<form on:submit={verify}>
<div class="flex flex-row gap-4 w-full justify-center my-12">
{#each { length: 6 } as _, i}
<input
type="text"
class="input input-bordered text-4xl text-base-content font-medium w-12 text-center"
bind:value={numbers[i]}
maxlength="1"
on:input={function(e) {
if (i !== 5) {
e.target.nextSibling.focus()
}
}}
required
/>
{/each}
</div>
<div class="flex justify-between">
<button type="submit" class="button"
>Abschicken</button
>
<!-- <button type="button" on:click={codeErneutAnfordern} class="button"
>Code erneut anfordern</button
> -->
</div>
{#if !errorHidden}
<div class="flex flex-row gap-4 mt-8" in:fade out:fade={{delay: 400}}>
<CrossCircled size={24} />
<span class="font-semibold"> Da ist wohl etwas schiefgelaufen. Der eingegebene Verifizierungscode ist ungültig.</span>
</div>
{/if}
</form>
<NotificationWrapper></NotificationWrapper>
</div>

View File

@@ -0,0 +1,70 @@
import { z } from "zod";
import { prisma } from "#lib/server/prisma.js";
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { sendVerificationCodeMail } from "#lib/server/mail/code.js";
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
import { createTimeBasedHash } from "#lib/auth/time-based-hash.js";
export const GET = defineApiRoute({
meta: {
description:
"Fragt einen neuen Verifizierungscode per Mail an.",
tags: ["Benutzer"],
summary: "Verifizierungscode anfragen.",
},
middleware: authorizationMiddleware,
output: z.void(),
async fetch(input, ctx) {
// Falls der Nutzer nicht existiert, wird eine Fehlermeldung zurückgegeben.
const user = await prisma.benutzer.findUnique({
where: {
email: input.email.toLowerCase(),
},
});
if (!user) {
throw new APIError({
code: "BAD_REQUEST",
message: "Benutzer konnte nicht gefunden werden.",
});
}
await sendVerificationCodeMail(user);
},
});
export const POST = defineApiRoute({
meta: {
description:
"Versucht den Nutzer mithilfe des abgeschickten Codes zu verifizieren.",
tags: ["Benutzer"],
summary: "Verifizieren.",
},
input: z.object({
code: z.string(),
email: z.string().email().toLowerCase(),
}),
output: z.void(),
async fetch({ code, email }, ctx) {
const generatedCode = createTimeBasedHash(email);
console.log(generatedCode, code);
if (code !== generatedCode) {
throw new APIError({
code: "BAD_REQUEST",
message: "Der eingegebene Verifizierungscode ist ungültig.",
});
}
await prisma.benutzer.update({
where: {
email: email.toLowerCase(),
},
data: {
verified: true,
},
});
},
});

View File

@@ -57,7 +57,7 @@ export const PUT = defineApiRoute({
labels[category as keyof typeof labels]) || labels[category as keyof typeof labels]) ||
"650e909fdc09629a4d6d495d") "650e909fdc09629a4d6d495d")
url.searchParams.append("key", "e057eb39018368ea96e456c753ac41b4") url.searchParams.append("key", "e057eb39018368ea96e456c753ac41b4")
url.searchParams.append("idList", "650303186e721b4bef0c3980") url.searchParams.append("idList", "67d75ca7403fd22c49bc7447")
url.searchParams.append("token", "ATTA8b65b3587ab627167038cc32a3460650973eb181cde01dabb208ca1e90ed5467AC06A4F2") url.searchParams.append("token", "ATTA8b65b3587ab627167038cc32a3460650973eb181cde01dabb208ca1e90ed5467AC06A4F2")
// Wir laden das Ticket zu Trello hoch. // Wir laden das Ticket zu Trello hoch.

24
src/pages/auth/code.astro Normal file
View File

@@ -0,0 +1,24 @@
---
import CodeModule from "../../modules/auth/CodeModule.svelte";
import MinimalLayout from "#layouts/MinimalLayout.astro";
import { validateAccessTokenServer } from "#server/lib/validateAccessToken";
const valid = await validateAccessTokenServer(Astro)
if (valid) {
return Astro.redirect("/dashboard")
}
const redirect = Astro.url.searchParams.get("redirect");
const email = Astro.url.searchParams.get("email");
if (!email) {
return Astro.redirect("/");
}
---
<MinimalLayout title="Verifizierung - IBCornelsen">
<CodeModule client:load {redirect} {email}></CodeModule>
</MinimalLayout>

View File

@@ -1,28 +1,27 @@
--- ---
import AbrechnungTable from "#components/Abrechnung/AbrechnungTable.svelte"; import AbrechungTable from "#components/Abrechnung/AbrechungTable.svelte";
import BlankLayout from "#layouts/BlankLayout.astro";
import { extrahiereAusweisAusFeldMitMehrerenAusweisen } from "#lib/server/ausweis";
import { Enums, prisma } from "#lib/server/prisma"; import { Enums, prisma } from "#lib/server/prisma";
import { getCurrentUser } from "#lib/server/user"; import { getCurrentUser } from "#lib/server/user";
import moment from "moment"; import moment from "moment";
const start = moment(Astro.url.searchParams.get("start"));
const end = moment(Astro.url.searchParams.get("end"));
let startdatum = start.isValid() ? start.toDate() : moment().startOf("month").toDate(); const start = moment(Astro.url.searchParams.get("start"))
let enddatum = end.isValid() ? end.toDate() : moment().endOf("month").toDate(); const end = moment(Astro.url.searchParams.get("end"))
const benutzer = await getCurrentUser(Astro); let startdatum = start.toDate();
let enddatum = end.toDate();
const benutzer = await getCurrentUser(Astro)
if (!benutzer) { if (!benutzer) {
return Astro.redirect("/404"); return Astro.redirect("/404")
} }
const provisionen = { const provisionen={
[Enums.Ausweisart.VerbrauchsausweisWohnen]: 10, [Enums.Ausweisart.VerbrauchsausweisWohnen]: 10,
[Enums.Ausweisart.BedarfsausweisWohnen]: 10, [Enums.Ausweisart.BedarfsausweisWohnen]: 10,
[Enums.Ausweisart.VerbrauchsausweisGewerbe]: 10, [Enums.Ausweisart.VerbrauchsausweisGewerbe]: 10,
}; }
// $kommission = db()->one("SELECT abr_va, abr_ba, abr_vanw FROM users WHERE resellercode = :resellercode", ["resellercode" => $resellercode]); // $kommission = db()->one("SELECT abr_va, abr_ba, abr_vanw FROM users WHERE resellercode = :resellercode", ["resellercode" => $resellercode]);
// Select every entry from database where user was involved. // Select every entry from database where user was involved.
@@ -31,206 +30,305 @@ if (start.isValid() && end.isValid()) {
bestellungen = await prisma.rechnung.findMany({ bestellungen = await prisma.rechnung.findMany({
where: { where: {
partner_code: "immowelt", partner_code: "immowelt",
OR: [ OR: [{
{ verbrauchsausweis_gewerbe: {
verbrauchsausweis_gewerbe: { ausgestellt: true
ausgestellt: true, }
}, },
{
bedarfsausweis_wohnen: {
ausgestellt: true
}
},
{
verbrauchsausweis_wohnen: {
ausgestellt: true
}
}],
AND: [{
created_at: {
gte: startdatum
}, },
{ }, {
bedarfsausweis_wohnen: { created_at: {
ausgestellt: true, lte: enddatum
},
}, },
{ }]
verbrauchsausweis_wohnen: {
ausgestellt: true,
},
},
],
AND: [
{
created_at: {
gte: startdatum,
},
},
{
created_at: {
lte: enddatum,
},
},
],
}, },
orderBy: { orderBy: {
created_at: "desc", created_at: "desc"
}, },
include: { include: {
bedarfsausweis_wohnen: { bedarfsausweis_wohnen: true,
include: { verbrauchsausweis_gewerbe: true,
aufnahme: { verbrauchsausweis_wohnen: true
include: { }
objekt: true,
},
},
},
},
verbrauchsausweis_gewerbe: {
include: {
aufnahme: {
include: {
objekt: true,
},
},
},
},
verbrauchsausweis_wohnen: {
include: {
aufnahme: {
include: {
objekt: true,
},
},
},
},
},
}); });
} else { } else {
bestellungen = await prisma.rechnung.findMany({ bestellungen = await prisma.rechnung.findMany({
where: { where: {
partner_code: "immowelt", partner_code: "immowelt",
OR: [ OR: [{
{ verbrauchsausweis_gewerbe: {
verbrauchsausweis_gewerbe: { ausgestellt: true
ausgestellt: true, }
}, },
}, {
{ bedarfsausweis_wohnen: {
bedarfsausweis_wohnen: { ausgestellt: true
ausgestellt: true, }
}, },
}, {
{ verbrauchsausweis_wohnen: {
verbrauchsausweis_wohnen: { ausgestellt: true
ausgestellt: true, }
}, }]
},
],
}, },
orderBy: { orderBy: {
created_at: "desc", created_at: "desc"
}, },
include: { include: {
bedarfsausweis_wohnen: { bedarfsausweis_wohnen: true,
include: { verbrauchsausweis_gewerbe: true,
aufnahme: { verbrauchsausweis_wohnen: true
include: { }
objekt: true,
},
},
},
},
verbrauchsausweis_gewerbe: {
include: {
aufnahme: {
include: {
objekt: true,
},
},
},
},
verbrauchsausweis_wohnen: {
include: {
aufnahme: {
include: {
objekt: true,
},
},
},
},
},
}); });
} }
// Wann wurde der partner_code zum ersten mal benutzt? // Wann wurde der partner_code zum ersten mal benutzt?
const partnerCodeErstesMal = ( const partnerCodeErstesMal = (await prisma.rechnung.findFirst({
await prisma.rechnung.findFirst({ select: {
select: { created_at: true
created_at: true, },
}, where: {
where: { partner_code: "immowelt",
partner_code: "immowelt", OR: [{
OR: [ verbrauchsausweis_gewerbe: {
{ ausgestellt: true
verbrauchsausweis_gewerbe: { }
ausgestellt: true,
},
},
{
bedarfsausweis_wohnen: {
ausgestellt: true,
},
},
{
verbrauchsausweis_wohnen: {
ausgestellt: true,
},
},
],
created_at: {
gte: moment().set("year", 2020).set("dayOfYear", 1).toDate(),
}, },
}, {
orderBy: { bedarfsausweis_wohnen: {
created_at: "asc", ausgestellt: true
}, }
}) },
)?.created_at; {
verbrauchsausweis_wohnen: {
ausgestellt: true
}
}],
created_at: {
gte: moment().set("year", 2020).set("dayOfYear", 1).toDate()
}
},
orderBy: {
created_at: "asc"
}
}))?.created_at
let provision = 0; let provision = 0;
const ausweisarten: string[] = []; const ausweisarten: string[] = [];
for (const bestellung of bestellungen) { for (const bestellung of bestellungen) {
if (bestellung.verbrauchsausweis_wohnen) { if (bestellung.verbrauchsausweis_wohnen) {
ausweisarten.push(Enums.Ausweisart.VerbrauchsausweisWohnen); ausweisarten.push(Enums.Ausweisart.VerbrauchsausweisWohnen)
provision += provisionen[Enums.Ausweisart.VerbrauchsausweisWohnen]; provision += provisionen[Enums.Ausweisart.VerbrauchsausweisWohnen]
} }
if (bestellung.bedarfsausweis_wohnen) { if (bestellung.bedarfsausweis_wohnen) {
ausweisarten.push(Enums.Ausweisart.BedarfsausweisWohnen); ausweisarten.push(Enums.Ausweisart.BedarfsausweisWohnen)
provision += provisionen[Enums.Ausweisart.BedarfsausweisWohnen]; provision += provisionen[Enums.Ausweisart.BedarfsausweisWohnen]
} }
if (bestellung.verbrauchsausweis_gewerbe) { if (bestellung.verbrauchsausweis_gewerbe) {
ausweisarten.push(Enums.Ausweisart.VerbrauchsausweisGewerbe); ausweisarten.push(Enums.Ausweisart.VerbrauchsausweisGewerbe)
provision += provisionen[Enums.Ausweisart.VerbrauchsausweisGewerbe]; provision += provisionen[Enums.Ausweisart.VerbrauchsausweisGewerbe]
} }
} }
--- ---
<BlankLayout title="Monatliche Abrechnung">
<AbrechnungTable
bestellungen={bestellungen.map((bestellung) =>
extrahiereAusweisAusFeldMitMehrerenAusweisen(bestellung)
)}
{provisionen}
{partnerCodeErstesMal}
startDate={startdatum}
endDate={enddatum}
email={benutzer.email}
client:load
/>
<div class="fixed bottom-0 left-0 right-0 bg-white p-4 shadow"> <!doctype html>
<div class="flex justify-between items-center"> <html lang="de">
<div>
<p id="betrag_gesamt"> <head>
Abrechnungsbetrag gesamt: <b>{provision} €</b> <meta charset="utf-8">
</p> <title>Reporting | online-energieausweis.org</title>
</div> <meta charset="utf-8">
<a <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
target="_blank" <script type="text/javascript" src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script>
rel="noreferrer noopener" <script type="text/javascript" src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
class="bg-secondary text-white px-4 py-2 rounded-lg hover:bg-secondary-focus" <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
href=`/user/abrechnung/pdf.php?month=${moment().subtract(1, "month").get("month")}&year=${moment().subtract(1, "month").get("year")}` <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css" />
>PDF für letzten Monat generieren.</a <link rel="stylesheet" href="./main.css">
> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</head>
<style>
body {
font-family: arial;
}
#inputwrap {
position: fixed;
top: 0;
left: 10%;
width: 70%;
padding-top: 10px;
padding-bottom: 10px;
background: #fff;
}
#cal {
margin-right: 10px !important;
}
#demo {
width: 76%;
display: inline-block;
}
table tr,
td {
border: 0.1em solid #000;
padding: 0;
margin: 0;
}
#QTT {
border-collapse: collapse;
width: 70%;
margin-top: 8em;
margin-left: 10%;
table-layout: auto;
}
#QTT thead td {
background: #ff7d26;
font-weight: bold;
}
#QTT tr:nth-child(even) {
background-color: #f2f2f2;
}
#QTT td {
padding: 0.4em 0.4em 0.4em 0.4em;
text-align: right;
}
#logo1 {
display: inline-block;
margin-right: 1em;
margin-top: -3px;
}
#logo2 {
width: 18%;
margin-bottom: 0.5em;
float: right;
padding-top: -1px;
margin-right: -5px;
}
</style>
<div id='inputwrap' class='form-group' >
<div style='display:flex; justify-content: space-between; align-items:center;'>
<img id='logo1' src='https://widget.ib-cornelsen.de/OEA_WIDGETS/img/IBC-logo.png' alt='IBCornelsen' />
<h5 style='margin-top: 10px;'><b>Erziehlte Conversions von {benutzer.email}</b></h5>
</div>
<input type='text' id='demo' class='form-control' name='demo' value='' placeholder='Bitte Zeitraum auswählen' />
</div>
<AbrechungTable bestellungen={bestellungen} {provisionen} {partnerCodeErstesMal}></AbrechungTable>
<div id="total" class="footer">
<div class="inner">
<div>
<p id="betrag_gesamt">Abrechnungsbetrag gesamt: <b>{provision} €</b></p>
</div> </div>
<a target='_blank' rel='noreferrer noopener' href=`/user/abrechnung/pdf.php?month=${moment().subtract(1, "month").get("month")}&year=${moment().subtract(1, "month").get("year")}`>PDF für letzten Monat generieren.</a>
</div> </div>
</BlankLayout> </div>
<script type="text/javascript">
$('#demo').daterangepicker({
"showDropdowns": true,
"minYear": 2019,
ranges: {
'Heute': [moment(), moment()],
'Gestern': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
'letzte 7 Tage': [moment().subtract(6, 'days'), moment()],
'letzte 30 Tage': [moment().subtract(29, 'days'), moment()],
'dieser Monat': [moment().startOf('month'), moment().endOf('month')],
'letzter Monat': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
},
"locale": {
"format": "DD/MM/YYYY",
"separator": " - ",
"applyLabel": "Übernehmen",
"cancelLabel": "Abrechen",
"fromLabel": "Von",
"toLabel": "Bis",
"customRangeLabel": "Benutzerdefiniert",
"weekLabel": "W",
"daysOfWeek": [
"So",
"Mo",
"Di",
"Mi",
"Do",
"Fr",
"Sa"
],
"monthNames": [
"Januar",
"Februar",
"März",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Dezember"
],
"firstDay": 1
},
"autoUpdateInput": false,
"alwaysShowCalendars": true,
"startDate": "<?php echo $day_start_display; ?>",
"endDate": "<?php echo $day_end_display; ?>",
"minDate": "01/10/2019"
}, function(start, end, label) {
var Pstart = start.format('MM/DD/YYYY');
var Pend = end.format('MM/DD/YYYY');
$("#start").val(Pstart);
$("#end").val(Pend);
$('#FO').submit();
});
// $("#demo").val(`Conversions im Zeitraum: <?php echo $Pall; ?>`);
</script>
<body>
<form id='FO' method='GET' action='index.php'>
<input type="hidden" id="start" name="start" value='' />
<input type="hidden" id="end" name="end" value='' />
</form>
<script>
</script>
</body>
</html>

View File

@@ -0,0 +1,31 @@
---
import KaufabschlussModule from "#modules/KaufabschlussModule.svelte";
import AusweisLayout from "#layouts/AusweisLayoutPruefung.astro";
import { Enums } from "#lib/client/prisma";
import { createCaller } from "src/astro-typesafe-api-caller";
// Man sollte nur auf diese Seite kommen, wenn ein Ausweis bereits vorliegt und in der Datenbank abgespeichert wurde.
const uid = Astro.url.searchParams.get("uid");
if (!uid) {
return Astro.redirect("/404");
}
const caller = createCaller(Astro);
const ausweis = await caller.v1.verbrauchsausweisWohnen.get({
uid
})
const user = await caller.v1.benutzer.self();
if (!ausweis || !user) {
return Astro.redirect("/404");
}
---
<AusweisLayout title="Kundendaten Aufnehmen - IBCornelsen">
<KaufabschlussModule user={user} ausweis={ausweis} aktiveBezahlmethode={Enums.Bezahlmethoden.paypal} client:load></KaufabschlussModule>
</AusweisLayout>

View File

@@ -3,7 +3,7 @@
set -e set -e
# Config # Config
CONTAINER_NAME="online-energieausweis-database-1" CONTAINER_NAME="database"
DB_USER="main" DB_USER="main"
DB_NAME="main" DB_NAME="main"
TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
@@ -58,7 +58,7 @@ BEGIN
FROM pg_tables FROM pg_tables
WHERE schemaname = 'public' WHERE schemaname = 'public'
LOOP LOOP
sql := sql || FORMAT('TRUNCATE TABLE public.%I CASCADE;', r.tablename); sql := sql || FORMAT('DROP TABLE public.%I CASCADE;', r.tablename);
END LOOP; END LOOP;
-- Drop all sequences -- Drop all sequences