71 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
Jens Cornelsen
056cbfa144 Fernwärme Hamburg hinzugefügt 2025-07-08 16:44:43 +02:00
Carl Mahnke
e50f54eedc Merge remote-tracking branch 'origin/UMBE' into Dev-Carl 2025-07-08 09:32:30 +02:00
Carl Mahnke
4eee179d37 Registration Email Force Lower Case 2025-07-08 09:32:08 +02:00
Robert Jagtiani
1e3b5dff60 Merge branch 'dev' into UMBE 2025-07-08 01:06:23 +02:00
Robert Jagtiani
7b894ffd00 widget 2025-07-08 00:52:22 +02:00
Robert Jagtiani
dbd725797c widget 2025-07-08 00:50:41 +02:00
Robert Jagtiani
d9b0d6ac95 widget 2025-07-08 00:33:46 +02:00
Moritz Utcke
5bb5fb9401 Datenbank Backup und Wipe 2025-06-16 15:09:09 -05:00
Moritz Utcke
c0d546fb9d Auto merge Fehler repariert. 2025-06-16 15:03:35 -05:00
Moritz Utcke
9a2359a993 Merge remote-tracking branch 'origin/dev' into dev 2025-06-16 14:43:30 -05:00
Moritz Utcke
ed20bb1c76 Automatisches löschen der Autoincrement Sequenzen 2025-06-16 14:43:25 -05:00
Carl Mahnke
fc6462c653 Refresh Token Create Logging 2025-06-16 17:19:52 +02:00
Carl Mahnke
df7d80e2e4 Versandadresse Ort Remove Read Only 2025-06-14 15:18:48 +02:00
Carl Mahnke
89e71c4641 Defaulting Versandadresse 2025-06-14 14:54:30 +02:00
Carl Mahnke
2d0e27075b Merge remote-tracking branch 'origin/dev' into Dev-Carl 2025-06-14 14:54:11 +02:00
Moritz Utcke
bd758e3793 Merge pull request #551 from IBCornelsen/staging
Auto-merge staging into main
2025-06-13 22:37:51 -04:00
Moritz Utcke
3e73203c94 dach_daemmung 2025-06-13 09:10:26 -05:00
Carl Mahnke
e9f51560f6 Rechnungsadresse ändern 2025-06-13 10:23:17 +02:00
Moritz Utcke
1e7fd7afe0 Fenster Art Float 2025-06-11 13:53:49 -05:00
Moritz Utcke
3aa3349d72 Merge branch 'main' into staging 2025-06-11 13:50:49 -05:00
Moritz Utcke
042bad25e0 Remove old check 2025-06-11 13:42:46 -05:00
Moritz Utcke
50f36dbc6c Workflow 2025-06-11 13:38:54 -05:00
Moritz Utcke
6894468e58 Merge pull request #546 from IBCornelsen/test-2
Backup Database Skript
2025-06-11 14:21:10 -04:00
Moritz Utcke
51257bbde8 Backup Database Skript 2025-06-11 13:20:29 -05:00
Moritz Utcke
5bd06fa0ef Merge remote-tracking branch 'origin/main' 2025-06-11 13:15:49 -05:00
Moritz Utcke
9a1625fa1b Enforce PR Source 2025-06-11 13:15:42 -05:00
Moritz Utcke
d98a5065ef Abrechnung und Workflow 2025-06-11 09:50:41 -05:00
Moritz Utcke
54d8fb5d1b no-start existiert nicht 2025-06-09 12:11:59 -03:00
Moritz Utcke
feef273c8c Disables autorestart for database backup cronjob
Prevents the daily database backup cronjob from restarting automatically.
This ensures that the backup process only runs according to the defined schedule,
avoiding unintended backups outside of the scheduled time.
2025-06-09 12:09:50 -03:00
Moritz Utcke
e48cd6acde Prozentzahl rausgenommen 2025-06-09 10:52:39 -03:00
Moritz Utcke
8791516498 Merge remote-tracking branch 'origin/dev' into dev 2025-06-09 10:43:51 -03:00
Moritz Utcke
19136d475d Makefile 2025-06-09 10:43:46 -03:00
Carl Mahnke
3983aabcb2 Merge remote-tracking branch 'origin/dev' into Dev-Carl 2025-06-03 11:07:14 +02:00
Carl Mahnke
4d6b8283ff Kundendatenmodule - Daten speichern wenn nicht eingeloggt 2025-06-03 11:06:53 +02:00
Moritz Utcke
5f5e3f4bed Heap Snapshot 2025-06-02 21:49:26 -03:00
Moritz Utcke
85591431eb Heap Snapshot 2025-06-02 21:46:30 -03:00
Moritz Utcke
4d9bae412c Merge remote-tracking branch 'origin/dev' into dev 2025-06-02 21:35:08 -03:00
Moritz Utcke
9fa8ee2251 Heap Snapshot Generator 2025-06-02 21:35:00 -03:00
Moritz Utcke
744c9c3f52 Merge pull request #544 from IBCornelsen/staging
test MERGE
2025-06-02 15:15:37 -04:00
Moritz Utcke
429ce4c4e5 Merge pull request #543 from IBCornelsen/UMBE
GTM
2025-06-02 15:11:22 -04:00
Robert Jagtiani
9d58502f29 GTM 2025-06-02 21:09:14 +02:00
Moritz Utcke
fcf12db850 Merge pull request #538 from IBCornelsen/staging
Merge remote-tracking branch 'origin/main' into staging
2025-06-01 17:43:51 -04:00
UMBENOMENA
5d0ef29272 Merge pull request #539 from IBCornelsen/UMBE
GTM
2025-06-01 23:28:24 +02:00
Robert Jagtiani
36bcacd8a2 Google Tag Manager 2025-06-01 23:27:07 +02:00
Robert Jagtiani
d6fc6be6d0 Merge remote-tracking branch 'origin/dev' into UMBE 2025-06-01 22:49:54 +02:00
Carl Mahnke
5869b282c4 Fixing Kundendatenmodule Datenvorbelegen bei neuem Ausweis 2025-05-28 14:15:01 +02:00
Moritz Utcke
e2f230e240 Notify Success entfernt weil es übel nervt 2025-05-20 12:50:07 -03:00
Moritz Utcke
4db097b544 Merge remote-tracking branch 'origin/main' into staging 2025-05-20 12:45:02 -03:00
Carl Mahnke
b8bb16b0ba Merge remote-tracking branch 'origin/dev' into Dev-Carl 2025-05-20 16:57:46 +02:00
Carl Mahnke
42b2767c70 Einstellungen Passwort ändern 2025-05-20 16:55:18 +02:00
Moritz Utcke
7e047125a7 Databse Neustarten nach Server Neustart 2025-05-18 11:57:26 -03:00
Carl Mahnke
058a84025c Benutzerdaten beim Speichern/Bestellen als Admin in Tabelle Benutzer speichern 2025-05-16 13:48:15 +02:00
Carl Mahnke
d76b8b4e14 Benutzerdaten beim Speichern/Bestellen in Tabelle Benutzer speichern 2025-05-14 13:53:19 +02:00
Jens Cornelsen
4bf104fc76 Ausstellliste 2025-05-08 23:44:22 +02:00
Jens Cornelsen
58a8136479 Ausstellliste 2025-05-08 23:40:16 +02:00
Jens Cornelsen
7371fa8765 . 2025-05-05 14:21:15 +02:00
Jens Cornelsen
22a88599d1 Pfeilposition im PDF korrigiert 2025-05-05 14:20:26 +02:00
Jens Cornelsen
c6d7cbe661 Pfeilposition im PDF korrigiert 2025-05-05 14:12:49 +02:00
Jens Cornelsen
c7f18dd7ae Pfeilposition im PDF korrigiert 2025-05-05 14:05:18 +02:00
Jens Cornelsen
235969ccda . 2025-05-05 13:55:11 +02:00
Jens Cornelsen
fe06c09336 Pfeilposition im PDF korrigiert 2025-05-05 13:17:49 +02:00
Jens Cornelsen
01d3824514 Neue Bankverbindung 2025-05-02 17:31:28 +02:00
Jens Cornelsen
c67355ace7 Neue Bankverbindung 2025-05-02 17:28:15 +02:00
Carl Mahnke
39e91dae7b Ausgestellte Ausweis PDFs aus der Cloud laden und nicht neu generieren 2025-05-02 11:44:24 +02:00
Carl Mahnke
8d7d59bb4e Bedarfsausweis Wohnen PDF nach Austellung im Dashboard downloaden 2025-05-02 11:16:46 +02:00
Moritz Utcke
ed37399c3c Merge pull request #533 from IBCornelsen/staging
Formular
2025-04-29 16:52:33 -03:00
Moritz Utcke
76943d8ef4 Merge branch 'dev' into staging 2025-04-29 16:51:47 -03:00
Moritz Utcke
ebabb8d667 Merge pull request #532 from IBCornelsen/dev
update staging
2025-04-29 15:20:05 -03:00
Moritz Utcke
a603c5d202 Update prevent-wrong-pr.yml 2025-04-29 14:16:02 -03:00
Moritz Utcke
75c4e3fa2b Merge pull request #530 from IBCornelsen/staging
Ausweis erstellen Fixes
2025-04-29 12:22:17 -03:00
52 changed files with 1895 additions and 874 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
@@ -62,15 +70,3 @@ jobs:
-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 }} ${{ 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 }}

18
.github/workflows/enforce-pr-source.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Enforce PR Source
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
branches:
- main
jobs:
check-pr-source:
runs-on: ubuntu-latest
steps:
- name: Enforce only staging → main
run: |
if [[ "${{ github.head_ref }}" != "staging" ]]; then
echo "ERROR: Only 'staging' branch may create PRs into 'main'!"
exit 1
fi

View File

@@ -13,6 +13,8 @@ jobs:
steps: steps:
- name: Prevent dev merges - name: Prevent dev merges
run: | run: |
echo "${{ github.head_ref }}";
echo "${{ github.base_ref }}";
if [[ "${{ github.head_ref }}" == "dev" ]]; then if [[ "${{ github.head_ref }}" == "dev" ]]; then
echo "ERROR: Merging 'dev' into '${{ github.base_ref }}' is forbidden!" echo "ERROR: Merging 'dev' into '${{ github.base_ref }}' is forbidden!"
exit 1 exit 1
@@ -21,6 +23,8 @@ jobs:
- 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

@@ -10,7 +10,7 @@ PERSISTENT_DIR := $(HOME)/persistent/$(APP_NAME)
BACKUP_FILENAME := $(HOME)/backups/$(shell date +"%Y-%m-%d_%H-%M-%S").sql.gz BACKUP_FILENAME := $(HOME)/backups/$(shell date +"%Y-%m-%d_%H-%M-%S").sql.gz
online-energieausweis: online-energieausweis:
bun run dev --host NODE_ENV="development" bun run dev --host
dev: database online-energieausweis dev: database online-energieausweis
@@ -30,6 +30,7 @@ run-database: stop-database
docker volume create $(DB_VOLUME) docker volume create $(DB_VOLUME)
docker build -t $(DB_CONTAINER_NAME) . docker build -t $(DB_CONTAINER_NAME) .
docker run -d --name $(DB_CONTAINER_NAME) \ docker run -d --name $(DB_CONTAINER_NAME) \
--restart=always \
-e POSTGRES_USER=$(DB_USER) \ -e POSTGRES_USER=$(DB_USER) \
-e POSTGRES_PASSWORD=$(DB_PASSWORD) \ -e POSTGRES_PASSWORD=$(DB_PASSWORD) \
-p $(DB_PORT):5432 \ -p $(DB_PORT):5432 \
@@ -58,7 +59,7 @@ 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: install-dependencies prisma-studio backup-database-cronjob update-dwd-klimafaktoren-cron prod: install-dependencies prisma-studio backup-database-cronjob update-dwd-klimafaktoren-cron
bun run build bun run build
@@ -71,4 +72,4 @@ prod: install-dependencies prisma-studio backup-database-cronjob update-dwd-klim
backup-database-cronjob: backup-database-cronjob:
- pm2 delete daily-db-backup - pm2 delete daily-db-backup
pm2 start bash --name "daily-db-backup" --cron "0 0 * * *" -- backup-database.bash pm2 start bash --name "daily-db-backup" --no-autorestart --cron "0 0 * * *" -- backup-database.bash

View File

@@ -13,13 +13,17 @@ export AWS_RESPONSE_CHECKSUM_VALIDATION=when_required
# Den Key dafür findet man auf https://dcd.ionos.com/latest/?lang=en#/key-management # Den Key dafür findet man auf https://dcd.ionos.com/latest/?lang=en#/key-management
docker exec -t online-energieausweis-database-1 pg_dump --data-only -U main main | brotli --best > $FILE_NAME docker exec -t online-energieausweis-database-1 pg_dump --data-only -U main main | brotli --best > $FILE_NAME
aws s3 cp $FILE_NAME s3://ibc-db-backup/ --profile ionos --endpoint-url https://s3-eu-central-1.ionoscloud.com --storage-class STANDARD aws s3 cp $FILE_NAME s3://ibc-db-backup/ --profile ionos --endpoint-url https://s3.eu-central-3.ionoscloud.com --storage-class STANDARD
echo "Uploaded $FILE_NAME" echo "Uploaded $FILE_NAME"
docker exec -t online-energieausweis-database-1 pg_dumpall -c -U main | brotli --best > $FILE_NAME_COMPLETE docker exec -t online-energieausweis-database-1 pg_dumpall -c -U main | brotli --best > $FILE_NAME_COMPLETE
aws s3 cp $FILE_NAME_COMPLETE s3://ibc-db-backup/ --profile ionos --endpoint-url https://s3-eu-central-1.ionoscloud.com --storage-class STANDARD <<<<<<< HEAD
aws s3 cp $FILE_NAME_COMPLETE s3://ibc-db-backup/ --profile ionos --endpoint-url https://s3-eu-central-3.ionoscloud.com --storage-class STANDARD
=======
aws s3 cp $FILE_NAME_COMPLETE s3://ibc-db-backup/ --profile ionos --endpoint-url https://s3.eu-central-3.ionoscloud.com --storage-class STANDARD
>>>>>>> dev
echo "Uploaded $FILE_NAME_COMPLETE" echo "Uploaded $FILE_NAME_COMPLETE"

View File

@@ -1,7 +1,9 @@
version: '3' version: '3'
name: database
services: services:
database: database:
build: ./ build: ./
restart: always
env_file: env_file:
- .env - .env
ports: ports:

View File

@@ -41,19 +41,19 @@ model BedarfsausweisWohnen {
volumen Float? volumen Float?
dicht Boolean? dicht Boolean?
fenster_flaeche_1 Float? fenster_flaeche_1 Float?
fenster_art_1 String? fenster_art_1 Float?
fenster_flaeche_2 Float? fenster_flaeche_2 Float?
fenster_art_2 String? fenster_art_2 Float?
dachfenster_flaeche Float? dachfenster_flaeche Float?
dachfenster_art String? dachfenster_art Float?
haustuer_flaeche Float? haustuer_flaeche Float?
haustuer_art String? haustuer_art Float?
dach_bauart String? @db.VarChar dach_bauart String? @db.VarChar
decke_bauart String? @db.VarChar decke_bauart String? @db.VarChar
dach_daemmung String? dach_daemmung Float?
decke_daemmung String? decke_daemmung Float?
aussenwand_daemmung String? aussenwand_daemmung Float?
boden_daemmung String? boden_daemmung Float?
aussenwand_bauart String? @db.VarChar aussenwand_bauart String? @db.VarChar
boden_bauart String? @db.VarChar boden_bauart String? @db.VarChar
warmwasser_verteilung String? @db.VarChar warmwasser_verteilung String? @db.VarChar

View File

@@ -2,28 +2,34 @@
# === Configuration === # === Configuration ===
BUCKET_NAME="ibc-db-backup" BUCKET_NAME="ibc-db-backup"
ENDPOINT_URL="https://s3-eu-central-1.ionoscloud.com" ENDPOINT_URL="https://s3.eu-central-3.ionoscloud.com"
LOCAL_DOWNLOAD_DIR="./" # Where to save the file LOCAL_DOWNLOAD_DIR="./"
# === Get latest file from IONOS S3 bucket === # === Use filename from argument if provided ===
LATEST_FILE=$(aws s3api list-objects-v2 \ if [ -n "$1" ]; then
LATEST_FILE="$1"
else
echo "📡 No filename provided, fetching latest..."
# === Get latest file from IONOS S3 bucket ===
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)
# === Check if file was found === # === Check if file was found ===
if [ "$LATEST_FILE" == "None" ] || [ -z "$LATEST_FILE" ]; then if [ "$LATEST_FILE" == "None" ] || [ -z "$LATEST_FILE" ]; then
echo "❌ No matching .sql.br file found." echo "❌ No matching .sql.br file found."
exit 1 exit 1
fi
fi fi
FILENAME=$(basename "$LATEST_FILE") FILENAME=$(basename "$LATEST_FILE")
SQL_FILE="${FILENAME%.br}" # Remove .br suffix SQL_FILE="${FILENAME%.br}" # Remove .br suffix
echo "📥 Downloading $LATEST_FILE" echo "📥 Downloading $LATEST_FILE"
aws s3 cp "s3://$BUCKET_NAME/$LATEST_FILE" "$LOCAL_DOWNLOAD_DIR" \ aws --profile ionos s3 cp "s3://$BUCKET_NAME/$LATEST_FILE" "$LOCAL_DOWNLOAD_DIR" \
--endpoint-url "$ENDPOINT_URL" --endpoint-url "$ENDPOINT_URL"
# === Decompress with Brotli === # === Decompress with Brotli ===
@@ -31,8 +37,8 @@ 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,34 +13,35 @@ 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"),
"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"),
"ausweise": await import("../src/pages/api/ausweise/index.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"),
"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-gewerbe/[id]": await import("../src/pages/api/geg-nachweis-gewerbe/[id].ts"), "geg-nachweis-gewerbe/[id]": await import("../src/pages/api/geg-nachweis-gewerbe/[id].ts"),
"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": await import("../src/pages/api/geg-nachweis-wohnen/index.ts"),
"objekt": await import("../src/pages/api/objekt/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"),
"user": await import("../src/pages/api/user/index.ts"),
"user/self": await import("../src/pages/api/user/self.ts"),
"ticket": await import("../src/pages/api/ticket/index.ts"), "ticket": await import("../src/pages/api/ticket/index.ts"),
"verbrauchsausweis-wohnen/[id]": await import("../src/pages/api/verbrauchsausweis-wohnen/[id].ts"), "user/self": await import("../src/pages/api/user/self.ts"),
"verbrauchsausweis-wohnen": await import("../src/pages/api/verbrauchsausweis-wohnen/index.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": 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"),
}) })

15
src/client/lib/lesen.ts Normal file
View File

@@ -0,0 +1,15 @@
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
import { Benutzer } from "#lib/client/prisma.js";
import { api } from "astro-typesafe-api/client";
import Cookies from "js-cookie";
export async function benutzerLesen(benutzerId: string): Promise<Benutzer> {
const benutzer = await api.user.GET.fetch({ id: benutzerId }
, {
headers: {
Authorization: `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
});
return benutzer[0];
}

View File

@@ -1,5 +1,5 @@
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js"; import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
import { Aufnahme, Objekt } from "#lib/client/prisma.js"; import { Aufnahme, Benutzer, Objekt } from "#lib/client/prisma.js";
import { api } from "astro-typesafe-api/client"; import { api } from "astro-typesafe-api/client";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
@@ -162,3 +162,31 @@ export async function objektSpeichern(objekt: Objekt & { id?: string }): Promise
return id; return id;
} }
} }
export async function benutzerSpeichern(benutzer: Partial<Benutzer>): Promise<string> {
const completeBenutzer: Benutzer = {
id: benutzer.id,
name: benutzer.name ?? null,
email: benutzer.email,
passwort: "",
adresse: benutzer.adresse ?? null,
anrede: benutzer.anrede ?? null,
firma: benutzer.firma ?? null,
vorname: benutzer.vorname ?? null,
ort: benutzer.ort ?? null,
plz: benutzer.plz ?? null,
profilbild: benutzer.profilbild ?? null,
telefon: benutzer.telefon ?? null,
updated_at: new Date(),
verified: benutzer.verified ?? false,
};
await api.user.POST.fetch(completeBenutzer
, {
headers: {
Authorization: `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
}
});
return benutzer.id;
}

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

@@ -41,6 +41,7 @@ const brennstoffe: [
["Fernwärme KWK EB", "kWh", 1.0, 0.0, 0.04], ["Fernwärme KWK EB", "kWh", 1.0, 0.0, 0.04],
["Fernwärme HKW FB", "kWh", 1.0, 1.3, 0.4], ["Fernwärme HKW FB", "kWh", 1.0, 1.3, 0.4],
["Fernwärme HKW EB", "kWh", 1.0, 0.1, 0.06], ["Fernwärme HKW EB", "kWh", 1.0, 0.1, 0.06],
["Fernwärme Hamburg", "kWh", 1.0, 0.33, 0.064],
["Erdgas", "kWh", 1.0, 1.1, 0.24], ["Erdgas", "kWh", 1.0, 1.1, 0.24],
["Heizöl", "kWh", 1.0, 1.1, 0.31], ["Heizöl", "kWh", 1.0, 1.1, 0.31],
["Heizöl", "l", 10.0, 1.1, 0.31], ["Heizöl", "l", 10.0, 1.1, 0.31],

View File

@@ -257,6 +257,12 @@
let bedarfsausweisFileInput: HTMLInputElement; let bedarfsausweisFileInput: HTMLInputElement;
let bedarfsausweisAdditionalInput: HTMLInputElement; let bedarfsausweisAdditionalInput: HTMLInputElement;
let dropdownOpen = false;
function toggleDropdown() {
dropdownOpen = !dropdownOpen;
}
</script> </script>
<div class="relative bg-base-200 border border-base-300 rounded-lg p-4 mx-2"> <div class="relative bg-base-200 border border-base-300 rounded-lg p-4 mx-2">
@@ -273,30 +279,41 @@
{/if} {/if}
<div class="card-body"> <div class="card-body">
<div <div
class="flex justify-end mb-2 dropdown dropdown-bottom absolute top-4 right-4" class="mb-2 dropdown dropdown-bottom absolute top-4 right-4 bg-base-100"
> >
<button class="rounded-full p-2.5 hover:bg-base-100"> <button class="rounded-full p-2.5 hover:bg-base-100" on:click={toggleDropdown}>
<DotsVertical size={15} /> <DotsVertical size={15} />
</button> </button>
</div>
{#if dropdownOpen}
<ul <ul
tabindex="-1" tabindex="-1"
class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-64 gap-2" class="z-[1] menu p-2 shadow bg-base-100 rounded-box w-64 gap-2 border"
> >
<li> <!-- <div>
<button on:click={ausweisStornieren} <button on:click={ausweisStornieren} class="flex items-center gap-2" disabled
><CrossCircled size={15} />Ausweis Stornieren</button ><CrossCircled size={15} />Stornieren</button
> >
</li> </div>
<li> <div>
<button><Pencil2 size={15} /> Als Vorlage benutzen</button> <button class="flex items-center gap-2" disabled
</li> ><Pencil2 size={15} /> Als Vorlage</button>
<li> </div>
<button on:click={() => hilfeModal.showModal()} <div>
<button on:click={() => hilfeModal.showModal()} class="flex items-center gap-2" disabled
><QuestionMarkCircled size={15} /> Hilfe Erhalten</button ><QuestionMarkCircled size={15} /> Hilfe Erhalten</button
> >
</li> </div> -->
</ul> {#if ausweis.bestellt && rechnung}
<div>
<a href="/dashboard/rechnung/aendern?rechnungid={rechnung.id}"><button> Adresse ändern</button
></a>
</div> </div>
{/if}
</ul>
{/if}
<div class="flex flex-row flex-wrap items-center gap-2"> <div class="flex flex-row flex-wrap items-center gap-2">
{#if ausweis.ausgestellt} {#if ausweis.ausgestellt}
<span class="bg-green-600 px-2 py-0.5 text-sm font-semibold rounded-lg text-white">Ausgestellt</span> <span class="bg-green-600 px-2 py-0.5 text-sm font-semibold rounded-lg text-white">Ausgestellt</span>
@@ -341,9 +358,6 @@
<div class="w-full border rounded-lg my-2"> <div class="w-full border rounded-lg my-2">
<div class="bg-green-600 h-4 rounded-lg" class:bg-red-600={progress == 33} class:bg-primary={progress == 66} style="width: {progress}%;"></div> <div class="bg-green-600 h-4 rounded-lg" class:bg-red-600={progress == 33} class:bg-primary={progress == 66} style="width: {progress}%;"></div>
</div> </div>
<span class="text-sm font-semibold text-base-content"
>{progress}%</span
>
</div> </div>
{#await calculations then calculations} {#await calculations then calculations}
<div class="flex flex-col mb-4"> <div class="flex flex-col mb-4">

View File

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

View File

@@ -1,9 +1,7 @@
<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;
@@ -17,56 +15,54 @@
let heizungsAlter: string = "bitte auswählen"; let heizungsAlter: string = "bitte auswählen";
let leerStand: string = "bitte auswählen"; let leerStand: string = "bitte auswählen";
const partner:string = "immowelt"; const partner: string = "immowelt";
const twoBoxReason = ["Vermietung/Verkauf", "Aushangpflicht", "Sonstiges"]; const twoBoxReason = ["Vermietung/Verkauf", "Aushangpflicht", "Sonstiges"];
const gewerbeHouse = ["Gewerbegebäude", "Mischgebäude"]; const gewerbeHouse = ["Gewerbegebäude", "Mischgebäude"];
$: ausnahme = $: ausnahme =
leerStand === "mehr als 30" || leerStand === "mehr als 30" ||
heizungsAlter === "< 3" || heizungsAlter === "< 3" ||
(baujahr === "vor 1978" && einheiten === "bis 4 Wohneinheiten" && sanierungsstatus === "unsaniert"); (baujahr === "vor 1978" &&
einheiten === "bis 4 Wohneinheiten" &&
sanierungsstatus === "unsaniert");
$: isTwoBoxReason = twoBoxReason.includes(anlass); $: isTwoBoxReason = twoBoxReason.includes(anlass);
$: isGewerbe = gewerbeHouse.includes(gebaeudetyp); $: isGewerbe = gewerbeHouse.includes(gebaeudetyp);
$: oneBOX = $: oneBOX =
(ausnahme && !isGewerbe) || (ausnahme && !isGewerbe) ||
(!isTwoBoxReason && gebaeudetyp !== "Mischgebäude") || (!isTwoBoxReason && gebaeudetyp !== "Mischgebäude") ||
(gebaeudetyp === "Gewerbegebäude" && leerStand === "mehr als 30"); (gebaeudetyp === "Gewerbegebäude" && leerStand === "mehr als 30");
$: threeBOX = $: threeBOX =
(ausnahme && gebaeudetyp === "Mischgebäude" && isTwoBoxReason && leerStand !== "mehr als 30"); ausnahme &&
gebaeudetyp === "Mischgebäude" &&
$: standardXL = isTwoBoxReason &&
(einheiten === "mehr als 4 Wohneinheiten") 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,9 +73,7 @@ $: 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>
@@ -93,10 +87,7 @@ $: standardXL =
{#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 selected disabled>bitte auswählen</option>
<option value="saniert">saniert</option> <option value="saniert">saniert</option>
@@ -104,7 +95,6 @@ $: standardXL =
</select> </select>
</div> </div>
{/if} {/if}
</div> </div>
{#if isTwoBoxReason} {#if isTwoBoxReason}
@@ -115,7 +105,6 @@ $: standardXL =
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>
@@ -125,10 +114,7 @@ $: standardXL =
<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>
@@ -137,10 +123,7 @@ $: standardXL =
<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"
bind:value={einheiten}
>
<option selected disabled>bitte auswählen</option> <option selected disabled>bitte auswählen</option>
<option value="bis 4 Wohneinheiten" <option value="bis 4 Wohneinheiten"
>bis 4 Wohneinheiten</option >bis 4 Wohneinheiten</option
@@ -153,10 +136,7 @@ $: standardXL =
<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"
bind:value={leerStand}
>
<option selected disabled>bitte auswählen</option> <option selected disabled>bitte auswählen</option>
<option value="bis 30">bis 30%</option> <option value="bis 30">bis 30%</option>
<option value="mehr als 30">mehr als 30%</option> <option value="mehr als 30">mehr als 30%</option>
@@ -165,20 +145,34 @@ $: standardXL =
</div> </div>
{/if} {/if}
<div id="thirdrow" class="thirdrow" <div
id="thirdrow"
class="thirdrow"
class:grid-cols-1={oneBOX} class:grid-cols-1={oneBOX}
class:md:grid-cols-6={threeBOX} class:md:grid-cols-6={threeBOX}
class:md:grid-cols-4={!oneBOX && !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[Enums.AusweisTyp.Standard] + (standardXL ? 10 : 0)} price={PRICES.VerbrauchsausweisWohnen[
src={'https://online-energieausweis.org/images/partner/'+partner+'/wohngebaeude.svg'} standardXL
? Enums.AusweisTyp.standardXL
: Enums.AusweisTyp.Standard
]}
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"
@@ -186,22 +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_buy={"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_overview={"https://online-energieausweis.org/"+partner+"/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/produkt-uebersicht/"}{standardXL ? '?ausweistyp=standardXL' : ''} 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"}`}
></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[Enums.AusweisTyp.Standard] + (standardXL ? 25 : 0)} price={PRICES.BedarfsausweisWohnen[
src={'https://online-energieausweis.org/images/partner/'+partner+'/wohngebaeude.svg'} standardXL
? Enums.AusweisTyp.standardXL
: 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"
@@ -211,181 +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_buy={"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_overview={"https://online-energieausweis.org/"+partner+"/energieausweis-erstellen/bedarfsausweis-wohngebaeude/produkt-uebersicht"}{standardXL ? '?ausweistyp=standardXL' : ''} 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"}`}
></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[Enums.AusweisTyp.Standard] + (standardXL ? 15 : 0)} price={PRICES.VerbrauchsausweisGewerbe[
src={'https://online-energieausweis.org/images/partner/'+partner+'/gewerbegebaeude.svg'} standardXL
? Enums.AusweisTyp.standardXL
: 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_buy={"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_overview={"https://online-energieausweis.org/"+partner+"/energieausweis-erstellen/verbrauchsausweis-gewerbe/produkt-uebersicht/"}{standardXL ? '?ausweistyp=standardXL' : ''} 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"}`}
></WidgetCardTemplate> ></WidgetCardTemplate>
{/if} {/if}
{#if isTwoBoxReason && isGewerbe} {#if isTwoBoxReason && isGewerbe}
<WidgetCardTemplate <WidgetCardTemplate
name="Bedarfsausweis Gewerbegebäude" name="Bedarfsausweis Gewerbegebäude"
price={PRICES.BedarfsausweisGewerbe[Enums.AusweisTyp.Standard]} price={PRICES.BedarfsausweisGewerbe[
src={'https://online-energieausweis.org/images/partner/'+partner+'/gewerbegebaeude.svg'} standardXL
? Enums.AusweisTyp.standardXL
: 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_buy={"https://online-energieausweis.org/"+partner+"/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/"} href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/${standardXL ? "?ausweistyp=standardXL" : ""}`}
href_overview={"https://online-energieausweis.org/"+partner+"/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/produkt-uebersicht/"} 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[Enums.AusweisTyp.Standard]} price={PRICES.GEGNachweisWohnen[
src={'https://online-energieausweis.org/images/partner/'+partner+'/wohngebaeude.svg'} standardXL
? Enums.AusweisTyp.standardXL
: Enums.AusweisTyp.Standard
]}
price1={PRICES.GEGNachweisWohnen[
standardXL
? Enums.AusweisTyp.BeratungXL
: Enums.AusweisTyp.Beratung
]}
price2={PRICES.GEGNachweisWohnen[
standardXL
? Enums.AusweisTyp.OfflineXL
: Enums.AusweisTyp.Offline
]}
src={"https://online-energieausweis.org/images/partner/" +
partner +
"/wohngebaeude.svg"}
alt="GEG-Nachweis-Wohnen" alt="GEG-Nachweis-Wohnen"
variant="Bauvorlage" variant="Bauvorlage"
empfehlung="ja" empfehlung="ja"
cta="Angebot anfragen" cta="Angebot anfragen"
services={[ services={[
[
["Nachweis fürs Bauamt bei Neubau oder Modernisierung.", true], "Nachweis fürs Bauamt bei Neubau oder Modernisierung.",
["Beinhaltet die Effizienz der Bauteile und Anlagentechnik.", true], true,
["Erstellung des Energieausweises nach Abschluss inklusive.", true], ],
["Berechnung und Bilanzierung nach aktueller DIN 18599.", true], [
["Zonierung und Erstellung eines 3D Gebäudemodells.", 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_buy={"https://online-energieausweis.org/"+partner+"/angebot-anfragen/geg-nachweis-wohnen-anfragen/"} href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/${standardXL ? "?ausweistyp=standardXL" : ""}`}
href_overview={"https://online-energieausweis.org/"+partner+"/angebot-anfragen/geg-nachweis-wohnen-anfragen/produkt-uebersicht/"} href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
></WidgetCardTemplate> ></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[Enums.AusweisTyp.Standard]} price={PRICES.GEGNachweisGewerbe[
src={'https://online-energieausweis.org/images/partner/'+partner+'/gewerbegebaeude.svg'} standardXL
? Enums.AusweisTyp.standardXL
: Enums.AusweisTyp.Standard
]}
price1={PRICES.GEGNachweisGewerbe[
standardXL
? Enums.AusweisTyp.BeratungXL
: Enums.AusweisTyp.Beratung
]}
price2={PRICES.GEGNachweisGewerbe[
standardXL
? Enums.AusweisTyp.OfflineXL
: Enums.AusweisTyp.Offline
]}
src={"https://online-energieausweis.org/images/partner/" +
partner +
"/gewerbegebaeude.svg"}
alt="GEG-Nachweis-Gewerbe" alt="GEG-Nachweis-Gewerbe"
variant="Bauvorlage" variant="Bauvorlage"
empfehlung="ja" empfehlung="ja"
cta="Angebot anfragen" cta="Angebot anfragen"
services={[ services={[
[
["Nachweis fürs Bauamt bei Neubau oder Modernisierung.", true], "Nachweis fürs Bauamt bei Neubau oder Modernisierung.",
["Beinhaltet die Effizienz der Bauteile und Anlagentechnik.", true], true,
["Erstellung des Energieausweises nach Abschluss inklusive.", true], ],
["Berechnung und Bilanzierung nach aktueller DIN 18599.", true], [
["Mehrzonenmodell inkl. Erstellung eines 3D Gebäudemodells.", 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_buy={"https://online-energieausweis.org/"+partner+"/angebot-anfragen/geg-nachweis-gewerbe-anfragen/"} href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/${standardXL ? "?ausweistyp=standardXL" : ""}`}
href_overview={"https://online-energieausweis.org/"+partner+"/angebot-anfragen/geg-nachweis-gewerbe-anfragen/produkt-uebersicht/"} href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/${standardXL ? "?ausweistyp=BeratungXL" : "?ausweistyp=Beratung"}`}
></WidgetCardTemplate> ></WidgetCardTemplate>
{/if} {/if}
</div> </div>
</div> </div>
</div> </div>
<style lang="postcss"> <style lang="postcss">
#IBC_app {
#IBC_app {
@font-face { @font-face {
font-family: "immo Sans"; font-family: "immo Sans";
src: url('/fonts/Immo-Sans/immoSans-Regular.eot'); src: url("/fonts/Immo-Sans/immoSans-Regular.eot");
src: url('/fonts/Immo-Sans/immoSans-Regular.eot?#iefix') format('embedded-opentype'), src:
url('/fonts/Immo-Sans/immoSans-Regular.woff2') format('woff2'), url("/fonts/Immo-Sans/immoSans-Regular.eot?#iefix")
url('/fonts/Immo-Sans/immoSans-Regular.woff') format('woff'); 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-style: normal;
font-weight: 400; font-weight: 400;
} }
@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: 'Antique Olive Compact bold'; 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-weight: 700;
font-style: normal; font-style: normal;
font-display:swap; font-display: swap;
src: url("/fonts/Antique Olive Std Compact.woff2") format('woff2'); src: url("/fonts/Antique Olive Std Compact.woff2") format("woff2");
} }
@apply min-w-[320px] max-w-[1920px] p-[4px] @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

@@ -1,104 +1,140 @@
<script lang="ts"> <script lang="ts">
import { fade } from "svelte/transition"; import { fade } from "svelte/transition";
export let price: number; export let price: number;
export let price1: number;
export let price2: number;
export let name: string; export let name: string;
export let variant: string; export let variant: string;
export let services: [string, boolean][]; export let services: [string, boolean][];
export let href_buy: string; export let href_buy1: string;
export let href_overview: string; export let href_buy2: string;
export let href_buy3: string = "";
export let src: string; export let src: string;
export let alt: string; export let alt: string;
export let empfehlung: string; export let empfehlung: string;
export let cta: string; export let cta: string;
</script> </script>
<div <div class="produktbox" transition:fade={{ duration: 0 }}>
class="produktbox" {#if empfehlung === "ja"}
transition:fade={{ duration: 0 }} <div class="empfehlung" aria-label="Empfohlenes Produkt">
> Empfehlung
</div>
{#if empfehlung === "ja"} {/if}
<div class="empfehlung" aria-label="Empfohlenes Produkt">Empfehlung</div>
{/if}
<h2 class="titel sm:mb-2">{name}</h2> <h2 class="titel sm:mb-2">{name}</h2>
<div class="sumCent"> <div class="sumCent">
<div class="variante">{variant}</div> <div class="variante">{variant}</div>
<img <img class="image" {src} {alt} />
class="image"
{src}
{alt}
/>
<div class=""> <div class="">
<p class="price"> <p class="price">
ab {price} ab {price}
</p> </p>
</div> </div>
</div> </div>
<div class="sumCent buttoncols"> <hr class="col-span-2 w-full md:w-[50%] md:m-auto bg-[#ffcc00] h-[2px]" />
<a
href={href_buy}
class="buttoncol"
aria-label="Jetzt {name} kaufen"
target="_blank"
>{cta}
</a>
<a
href={href_overview}
class="buttoncol"
aria-label="{name} Produkt-Übersicht"
target="_blank"
>Produkt-Übersicht</a
>
</div>
<div class="sumRows forServices"> <div class="sumRows forServices">
{#each services as [service, check]} {#each services as [service, check]}
<div class="services"> <div class="services">
<span>{@html service}</span> <span>{@html service}</span>
<span class={check ? "check" : "check-no"}>{check ? "✔" : "✘"}</span> <span class={check ? "check" : "check-no"}
>{check ? "✔" : "✘"}</span
>
</div> </div>
{/each} {/each}
</div>
<hr class="col-span-2 w-full md:w-[50%] md:m-auto bg-[#ffcc00] h-[2px]" />
<div
class="sumCent buttoncols"
class:md:grid-cols-3={href_buy3}
class:md:grid-cols-2={!href_buy3}
>
<a
href={href_buy1}
class="buttoncol"
aria-label="Jetzt {name} kaufen"
target="_blank"
>mach selbst (<span class="inside-price">{price}</span>&nbsp;€)
</a>
<a
href={href_buy2}
class="buttoncol"
aria-label="{name} Produkt-Übersicht"
target="_blank"
>wir helfen (<span class="inside-price">{price1}</span>&nbsp;€)
</a>
{#if href_buy3}
<a
href={href_buy3}
class="buttoncol"
aria-label="{name} Produkt-Übersicht"
target="_blank"
>wir machen (<span class="inside-price">{price2}</span>&nbsp;€)
</a>
{/if}
</div> </div>
</div> </div>
<style lang="postcss"> <style lang="postcss">
.produktbox {
.produktbox{@apply grid grid-cols-subgrid col-span-2 grid-rows-subgrid row-span-3 md:row-span-12 bg-black/5 rounded-lg @apply grid grid-cols-subgrid col-span-2 grid-rows-subgrid row-span-3 md:row-span-12 bg-black/5 rounded-lg
px-2 py-2 mt-5; px-2 py-2 mt-5;
box-shadow:2px 2px 8px rgba(0,0,0,0.25); box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.25);
.sumCent{@apply justify-self-center col-span-2} .sumCent {
.sumRows{@apply hidden sm:grid grid-rows-subgrid row-span-5 items-center} @apply justify-self-center col-span-2;
.forServices{@apply grid-rows-subgrid row-span-5 items-center col-span-2 justify-center px-6} }
.sumRows {
@apply hidden sm:grid grid-rows-subgrid row-span-5 items-center;
}
.forServices {
@apply grid-rows-subgrid row-span-5 items-center col-span-2 justify-center px-6;
}
.image{@apply w-[75%] mx-auto .image {
md:w-[75%] md:pl-0} @apply w-[75%] mx-auto
md:w-[75%] md:pl-0;
}
.buttoncols{@apply grid grid-cols-1 gap-x-4 w-full mb-4 .buttoncols {
md:grid-cols-2 md:w-[auto]} @apply grid grid-cols-1 gap-x-4 w-full my-4
md:grid-cols-3 md:w-[auto];
}
.buttoncol{@apply mt-2 md:mt-0 text-center text-black bg-[#ffcc00] rounded-md px-3 py-1 no-underline .buttoncol {
hover:bg-[#222222] hover:text-white} @apply mt-2 md:mt-0 text-center text-black bg-[#ffcc00] rounded-md px-3 py-1 no-underline
hover:bg-[#222222] hover:text-white;
}
.inside-price {
@apply font-bold;
}
.price{@apply tracking-tighter text-[2rem] text-[#222222] pl-12 m-0 -mt-7 text-nowrap text-left; .price {
font-family: "Antique Olive Compact bold";} @apply tracking-tighter text-[2rem] text-[#222222] pl-12 m-0 -mt-7 text-nowrap text-left;
font-family: "Antique Olive Compact bold";
}
.titel {@apply col-span-2 text-center [font-size:_clamp(20px,2.5vw,28px)]} .titel {
.empfehlung{@apply -mt-4 absolute justify-self-end rounded-md bg-red-700 text-white w-fit h-fit px-2 py-1 rotate-1 text-[0.65rem] ring-4 ring-white mr-6} @apply col-span-2 text-center [font-size:_clamp(20px,2.5vw,28px)];
}
.empfehlung {
@apply -mt-4 absolute justify-self-end rounded-md bg-red-700 text-white w-fit h-fit px-2 py-1 rotate-1 text-[0.65rem] ring-4 ring-white mr-6;
}
.variante { .variante {
@apply w-fit italic col-span-2 -mt-2 -mb-4 text-[1rem] text-[#222222] justify-self-start ring-2 ring-[#ffcc00] rounded-md pl-[4px] pr-[6px] py-[0px]; @apply w-fit italic col-span-2 -mt-2 -mb-4 text-[1rem] text-[#222222] justify-self-start ring-2 ring-[#ffcc00] rounded-md pl-[4px] pr-[6px] py-[0px];
} }
.services { .services {
@apply hidden text-start py-1 md:grid grid-rows-subgrid row-span-1 items-center md:grid-cols-[1fr_50px] @apply hidden text-start py-1 md:grid grid-rows-subgrid row-span-1 items-center md:grid-cols-[1fr_50px];
} }
.services:not(:last-child) { .services:not(:last-child) {
@apply border-b-[1px] border-gray-200; @apply border-b-[1px] border-gray-200;
@@ -109,7 +145,5 @@ hover:bg-[#222222] hover:text-white}
.check-no { .check-no {
@apply justify-self-end self-center font-bold text-red-700; @apply justify-self-end self-center font-bold text-red-700;
} }
}
}
</style> </style>

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

@@ -102,6 +102,13 @@ const { title } = Astro.props;
<html lang="de"> <html lang="de">
<head> <head>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-59QKHH8');</script>
<!-- End Google Tag Manager -->
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.jpg" /> <link rel="icon" type="image/svg+xml" href="/favicon.jpg" />
@@ -147,6 +154,11 @@ const { title } = Astro.props;
</head> </head>
<body> <body>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-59QKHH8"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<Header {user} /> <Header {user} />
<main <main

View File

@@ -1,157 +0,0 @@
---
import "../style/global.css";
import "../style/formular.css";
import "../../svelte-dialogs.config";
import Header from "#components/design/header/AusweisHeaderImmowelt.astro";
import Footer from "#components/design/footer/Footer.astro";
import { NotificationWrapper } from "@ibcornelsen/ui";
export interface Props {
title: string;
}
const { title } = Astro.props;
---
<script>
window.addEventListener("scroll", (event) => {
let scroll = window.scrollY;
console.log(scroll);
if (scroll >= 400) {
document
.getElementById("skala")
?.classList.add(
"2xl:fixed",
"2xl:py-4",
"2xl:top-0",
"2xl:z-20"
);
document.getElementById("skala")?.classList.remove("w-full");
document.getElementById("skala").style.borderBottom =
"3px solid #e6e6e6";
document.getElementById("performance-box").style.maxWidth =
"688.5px";
document.getElementById("progress-box").style.maxWidth = "688.5px";
document
.getElementById("formInput-1")
?.classList.add("2xl:mt-[370px]");
} else {
document
.getElementById("skala")
?.classList.remove(
"2xl:fixed",
"2xl:py-4",
"2xl:top-0",
"2xl:z-20"
);
document.getElementById("skala")?.classList.add("w-full");
document.getElementById("skala").style.borderBottom = "none";
document
.getElementById("formInput-1")
?.classList.remove("2xl:mt-[370px]");
}
});
</script>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.jpg" />
<meta
name="description"
content="✅ Jetzt Ihren Energieausweis online erstellen. Erhalten Sie Ihren online Energieausweis rechtssicher und nach aktueller GEG (vormals EnEV) vom Diplom Ingenieur geprüft."
/>
<link rel="canonical" href="https://online-energieausweis.org/" />
<meta property="og:locale" content="de_DE" />
<meta property="og:type" content="website" />
<meta
property="og:title"
content="Energieausweis online erstellen - Online Energieausweis"
/>
<meta
property="og:description"
content="✅ Jetzt Ihren Energieausweis online erstellen. Erhalten Sie Ihren online Energieausweis rechtssicher und nach aktueller GEG (vormals EnEV) vom Diplom Ingenieur geprüft."
/>
<meta property="og:url" content="https://online-energieausweis.org/" />
<meta
property="og:site_name"
content="Energieausweis online erstellen"
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:description"
content="✅ Jetzt Ihren Energieausweis online erstellen. Erhalten Sie Ihren online Energieausweis rechtssicher und nach aktueller GEG (vormals EnEV) vom Diplom Ingenieur geprüft."
/>
<meta
name="twitter:title"
content="Energieausweis online erstellen - Online Energieausweis"
/>
<meta
name="twitter:image"
content="https://online-energieausweis.org/images/energieausweis-online-erstellen.jpg"
/>
<title>
{title || "Energieausweis online erstellen - Online Energieausweis"}
</title>
</head>
<body>
<Header />
<main
class="w-full p-0 grid
xs:grid-cols-[minmax(1fr)] xs:gap-1 xs:p-0
sm:grid-cols-[minmax(1fr)] sm:gap-1 sm:p-0
md:grid-cols-[minmax(1fr)] md:gap-2 md:p-0
lg:grid-cols-[minmax(1fr)] lg:gap-3 lg:p-4
xl:grid-cols-[minmax(1fr)] xl:gap-4 xl:p-6
2xl:grid-cols-[minmax(1fr)] 2xl:gap-5 2xl:p-6"
>
<article class="box rounded-tl-none p-2 lg:p-12">
<slot />
</article>
</main>
<Footer />
<NotificationWrapper client:load />
</body>
</html>
<style is:global lang="postcss">
body {
min-height: 100vh;
width:100%;
}
article {
p, h1, h2, h3, h4, h5, h6 {
@apply text-base-content;
}
}
.headline {
@apply text-lg;
}
.radio-inline {
@apply flex flex-row gap-2;
}
.checkbox-inline {
@apply flex flex-row gap-2;
}
</style>

View File

@@ -1,156 +0,0 @@
---
import "../style/global.css";
import "../style/formular.css";
import "../../svelte-dialogs.config"
import Header from "#components/design/header/AusweisHeaderImmowelt2.astro";
import Footer from "#components/design/footer/Footer.astro";
import { NotificationWrapper } from "@ibcornelsen/ui";
export interface Props {
title: string;
}
const { title } = Astro.props;
---
<script>
window.addEventListener("scroll", (event) => {
let scroll = window.scrollY;
console.log(scroll);
if(scroll>=400){
document.getElementById('skala')?.classList.add('2xl:fixed','2xl:py-4','2xl:top-0','2xl:z-20');
document.getElementById('skala')?.classList.remove('w-full');
document.getElementById('skala').style.borderBottom = "3px solid #e6e6e6";
document.getElementById('performance-box').style.maxWidth = "688.5px";
document.getElementById('progress-box').style.maxWidth = "688.5px";
document.getElementById('formInput-1')?.classList.add('2xl:mt-[370px]');
}else{
document.getElementById('skala')?.classList.remove('2xl:fixed','2xl:py-4','2xl:top-0','2xl:z-20');
document.getElementById('skala')?.classList.add('w-full');
document.getElementById('skala').style.borderBottom = "none";
document.getElementById('formInput-1')?.classList.remove('2xl:mt-[370px]');
}
});
</script>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.jpg" />
<meta
name="description"
content="✅ Jetzt Ihren Energieausweis online erstellen. Erhalten Sie Ihren online Energieausweis rechtssicher und nach aktueller GEG (vormals EnEV) vom Diplom Ingenieur geprüft."
/>
<link rel="canonical" href="https://online-energieausweis.org/" />
<meta property="og:locale" content="de_DE" />
<meta property="og:type" content="website" />
<meta
property="og:title"
content="Energieausweis online erstellen - Online Energieausweis"
/>
<meta
property="og:description"
content="✅ Jetzt Ihren Energieausweis online erstellen. Erhalten Sie Ihren online Energieausweis rechtssicher und nach aktueller GEG (vormals EnEV) vom Diplom Ingenieur geprüft."
/>
<meta property="og:url" content="https://online-energieausweis.org/" />
<meta property="og:site_name" content="Energieausweis online erstellen" />
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:description"
content="✅ Jetzt Ihren Energieausweis online erstellen. Erhalten Sie Ihren online Energieausweis rechtssicher und nach aktueller GEG (vormals EnEV) vom Diplom Ingenieur geprüft."
/>
<meta
name="twitter:title"
content="Energieausweis online erstellen - Online Energieausweis"
/>
<meta
name="twitter:image"
content="https://online-energieausweis.org/images/energieausweis-online-erstellen.jpg"
/>
<title>
{title || 'Energieausweis online erstellen - Online Energieausweis'}
</title>
</head>
<body>
<Header />
<main
class="w-full p-0 grid
xs:grid-cols-[minmax(1fr)] xs:gap-1 xs:p-0
sm:grid-cols-[minmax(1fr)] sm:gap-1 sm:p-0
md:grid-cols-[minmax(1fr)] md:gap-2 md:p-0
lg:grid-cols-[minmax(1fr)] lg:gap-3 lg:p-4
xl:grid-cols-[minmax(1fr)] xl:gap-4 xl:p-6
2xl:grid-cols-[minmax(1fr)] 2xl:gap-5 2xl:p-6
">
<article class="box rounded-tl-none p-2 lg:p-12">
<slot />
</article>
</main>
<Footer />
<NotificationWrapper client:load />
</body>
</html>
<style is:global lang="postcss">
body {
min-height: 100vh;
width:100%;
}
article {
p, h1, h2, h3, h4, h5, h6 {
@apply text-base-content;
}
}
.headline {
@apply text-lg;
}
.radio-inline {
@apply flex flex-row gap-2;
}
.checkbox-inline {
@apply flex flex-row gap-2;
}
</style>

View File

@@ -85,6 +85,14 @@ window.addEventListener("scroll", () => {
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de"> <html lang="de">
<head> <head>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-59QKHH8');</script>
<!-- End Google Tag Manager -->
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.jpg" /> <link rel="icon" type="image/svg+xml" href="/favicon.jpg" />
@@ -127,6 +135,10 @@ window.addEventListener("scroll", () => {
</head> </head>
<body> <body>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-59QKHH8"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<AusweisHeaderPartner {tab}/> <AusweisHeaderPartner {tab}/>
@@ -136,7 +148,6 @@ window.addEventListener("scroll", () => {
<article class="p-0 lg:px-20 lg:py-12"> <article class="p-0 lg:px-20 lg:py-12">
<slot /> <slot />
<div style="height: 450px;"></div>
</article> </article>
</main> </main>

View File

@@ -37,6 +37,14 @@ const { title } = Astro.props;
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de"> <html lang="de">
<head> <head>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-59QKHH8');</script>
<!-- End Google Tag Manager -->
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.jpg" /> <link rel="icon" type="image/svg+xml" href="/favicon.jpg" />
@@ -79,6 +87,10 @@ const { title } = Astro.props;
</head> </head>
<body> <body>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-59QKHH8"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<Header {user} /> <Header {user} />

View File

@@ -98,6 +98,15 @@ window.addEventListener("scroll", (event) => {
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de"> <html lang="de">
<head> <head>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-59QKHH8');</script>
<!-- End Google Tag Manager -->
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.jpg" /> <link rel="icon" type="image/svg+xml" href="/favicon.jpg" />
@@ -111,6 +120,10 @@ window.addEventListener("scroll", (event) => {
</head> </head>
<body> <body>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-59QKHH8"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<Header {user} /> <Header {user} />

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

@@ -369,25 +369,25 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
if (endenergieverbrauchTranslationPercentage > 0.5) { if (endenergieverbrauchTranslationPercentage > 0.5) {
page.drawText("Endenergieverbrauch Wärme", { page.drawText("Endenergieverbrauch Wärme", {
x: endenergieverbrauchTranslationX - margin - font.widthOfTextAtSize("Endenergieverbrauch Wärme", 10), x: endenergieverbrauchTranslationX - margin - font.widthOfTextAtSize("Endenergieverbrauch Wärme", 10) - (pfeilWidth / 2),
y: height - 191, y: height - 191,
size: 10 size: 10
}) })
page.drawText(endEnergieVerbrauchGesamtText, { page.drawText(endEnergieVerbrauchGesamtText, {
x: endenergieverbrauchTranslationX - margin - bold.widthOfTextAtSize(endEnergieVerbrauchGesamtText, 10), x: endenergieverbrauchTranslationX - margin - bold.widthOfTextAtSize(endEnergieVerbrauchGesamtText, 10) - (pfeilWidth / 2),
y: height - 205, y: height - 205,
size: 10, size: 10,
font: bold font: bold
}) })
} else { } else {
page.drawText("Endenergieverbrauch Wärme", { page.drawText("Endenergieverbrauch Wärme", {
x: endenergieverbrauchTranslationX + pfeilWidth + margin, x: endenergieverbrauchTranslationX + pfeilWidth + margin + (pfeilWidth / 2),
y: height - 191, y: height - 191,
size: 10 size: 10
}) })
page.drawText(endEnergieVerbrauchGesamtText, { page.drawText(endEnergieVerbrauchGesamtText, {
x: endenergieverbrauchTranslationX + pfeilWidth + margin, x: endenergieverbrauchTranslationX + pfeilWidth + margin + (pfeilWidth / 2),
y: height - 205, y: height - 205,
size: 10, size: 10,
font: bold font: bold
@@ -398,13 +398,13 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
if (vergleichsWertWaermeTranslationPercentage > 0.5) { if (vergleichsWertWaermeTranslationPercentage > 0.5) {
page.drawText("Vergleichswert Wärme", { page.drawText("Vergleichswert Wärme", {
x: vergleichsWertWaermeTranslationX - margin - font.widthOfTextAtSize("Vergleichswert Wärme", 10), x: vergleichsWertWaermeTranslationX - margin - font.widthOfTextAtSize("Vergleichswert Wärme", 10) - (pfeilWidth / 2),
y: height - 275, y: height - 275,
size: 10 size: 10
}) })
page.drawText(vergleichswertWaermeText, { page.drawText(vergleichswertWaermeText, {
x: vergleichsWertWaermeTranslationX - margin - bold.widthOfTextAtSize(vergleichswertWaermeText, 10), x: vergleichsWertWaermeTranslationX - margin - bold.widthOfTextAtSize(vergleichswertWaermeText, 10) - (pfeilWidth / 2),
y: height - 289, y: height - 289,
size: 10, size: 10,
font: bold font: bold
@@ -441,25 +441,25 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
if (stromVerbrauchTranslationPercentage > 0.5) { if (stromVerbrauchTranslationPercentage > 0.5) {
page.drawText("Endenergieverbrauch Strom", { page.drawText("Endenergieverbrauch Strom", {
x: stromVerbrauchTranslationX - margin - font.widthOfTextAtSize("Endenergieverbrauch Strom", 10), x: stromVerbrauchTranslationX - margin - font.widthOfTextAtSize("Endenergieverbrauch Strom", 10) - (pfeilWidth / 2),
y: height - 335, y: height - 335,
size: 10 size: 10
}) })
page.drawText(stromVerbrauchGesamtText, { page.drawText(stromVerbrauchGesamtText, {
x: stromVerbrauchTranslationX - margin - bold.widthOfTextAtSize(stromVerbrauchGesamtText, 10), x: stromVerbrauchTranslationX - margin - bold.widthOfTextAtSize(stromVerbrauchGesamtText, 10) - (pfeilWidth / 2),
y: height - 349, y: height - 349,
size: 10, size: 10,
font: bold font: bold
}) })
} else { } else {
page.drawText("Endenergieverbrauch Strom", { page.drawText("Endenergieverbrauch Strom", {
x: stromVerbrauchTranslationX + pfeilWidth + margin, x: stromVerbrauchTranslationX + pfeilWidth + margin + (pfeilWidth / 2),
y: height - 335, y: height - 335,
size: 10 size: 10
}) })
page.drawText(stromVerbrauchGesamtText, { page.drawText(stromVerbrauchGesamtText, {
x: stromVerbrauchTranslationX + pfeilWidth + margin, x: stromVerbrauchTranslationX + pfeilWidth + margin + (pfeilWidth / 2),
y: height - 349, y: height - 349,
size: 10, size: 10,
font: bold font: bold
@@ -484,12 +484,12 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
}) })
} else { } else {
page.drawText("Vergleichswert Strom", { page.drawText("Vergleichswert Strom", {
x: vergleichsWertStromTranslationX + pfeilWidth + margin, x: vergleichsWertStromTranslationX + pfeilWidth + margin + (pfeilWidth / 2),
y: height - 420, y: height - 420,
size: 10 size: 10
}) })
page.drawText(vergleichswertStromText, { page.drawText(vergleichswertStromText, {
x: vergleichsWertStromTranslationX + pfeilWidth + margin, x: vergleichsWertStromTranslationX + pfeilWidth + margin + (pfeilWidth / 2),
y: height - 434, y: height - 434,
size: 10, size: 10,
font: bold font: bold

View File

@@ -343,7 +343,7 @@ export async function pdfVerbrauchsausweisWohnen(ausweis: VerbrauchsausweisWohne
} }
page.drawImage(pfeilNachUnten, { page.drawImage(pfeilNachUnten, {
x: endenergieverbrauchTranslationX, x: endenergieverbrauchTranslationX - (pfeilWidth / 2),
y: height - 212, y: height - 212,
width: pfeilWidth, width: pfeilWidth,
height: 30 height: 30
@@ -380,7 +380,7 @@ export async function pdfVerbrauchsausweisWohnen(ausweis: VerbrauchsausweisWohne
} }
page.drawImage(pfeilNachOben, { page.drawImage(pfeilNachOben, {
x: primaerenergieverbrauchTranslationX, x: primaerenergieverbrauchTranslationX - (pfeilWidth / 2),
y: height - 297, y: height - 297,
width: pfeilWidth, width: pfeilWidth,
height: 30 height: 30
@@ -505,7 +505,7 @@ export async function pdfVerbrauchsausweisWohnen(ausweis: VerbrauchsausweisWohne
const addVerbrauch = addVerbrauchGenerator(); const addVerbrauch = addVerbrauchGenerator();
if (!ausweis.warmwasser_enthalten) { if (ausweis.warmwasser_enthalten !== true) {
// Mit Warmwasserzuschlag // Mit Warmwasserzuschlag
addVerbrauch( addVerbrauch(
moment(ausweis.startdatum).format("MM.YYYY"), moment(ausweis.startdatum).format("MM.YYYY"),

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

@@ -1,9 +1,27 @@
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js"; import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
import { checkAuthorizationHeader, checkAuthorizationHeaderNoThrow } from "#lib/middleware/authorization.js"; import { checkAuthorizationHeader, checkAuthorizationHeaderNoThrow } from "#lib/middleware/authorization.js";
import { AstroGlobal } from "astro"; import { AstroGlobal } from "astro";
import { Enums } from "#lib/client/prisma.js";
import { prisma } from "#lib/server/prisma.js";
export function getCurrentUser(Astro: AstroGlobal) { export function getCurrentUser(Astro: AstroGlobal) {
const accessToken = Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value; const accessToken = Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value;
return checkAuthorizationHeaderNoThrow(`Bearer ${accessToken}`) return checkAuthorizationHeaderNoThrow(`Bearer ${accessToken}`)
} }
export async function getOtherUser(Astro: AstroGlobal, userId : string) {
const accessToken = Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value;
let currentUser = await checkAuthorizationHeaderNoThrow(`Bearer ${accessToken}`)
if (currentUser?.rolle == Enums.BenutzerRolle.ADMIN) {
const user = await prisma.benutzer.findUnique({
where: {
id: userId
}
})
return user;
}
return null;
}

View File

@@ -32,8 +32,12 @@
import { getMaximumDevitationInPercent } from "#client/lib/helpers.js"; import { getMaximumDevitationInPercent } from "#client/lib/helpers.js";
import { endEnergieVerbrauchVerbrauchsausweis_2016_Client } from "#lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016_Client.js"; import { endEnergieVerbrauchVerbrauchsausweis_2016_Client } from "#lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016_Client.js";
import { endEnergieVerbrauchVerbrauchsausweisGewerbe_2016_Client } from "#lib/Berechnungen/VerbrauchsausweisGewerbe/VerbrauchsausweisGewerbe_2016_Client.js"; import { endEnergieVerbrauchVerbrauchsausweisGewerbe_2016_Client } from "#lib/Berechnungen/VerbrauchsausweisGewerbe/VerbrauchsausweisGewerbe_2016_Client.js";
import { benutzerSpeichern } from "#client/lib/speichern.js";
import { benutzerLesen } from "#client/lib/lesen.js";
import { exclude } from "#lib/exclude.js";
export let user: Partial<BenutzerClient>; export let user: Partial<BenutzerClient>;
export let impersonatedUser: Partial<BenutzerClient> | null = null;
export let ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbe | BedarfsausweisWohnen; export let ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbe | BedarfsausweisWohnen;
export let aufnahme: AufnahmeClient; export let aufnahme: AufnahmeClient;
export let objekt: ObjektClient; export let objekt: ObjektClient;
@@ -42,9 +46,8 @@
export let rechnung: RechnungClient | null = null; export let rechnung: RechnungClient | null = null;
export let ausweisart: Enums.Ausweisart; export let ausweisart: Enums.Ausweisart;
export let aktiveBezahlmethode: Bezahlmethoden = Enums.Bezahlmethoden.paypal; export let aktiveBezahlmethode: Bezahlmethoden = Enums.Bezahlmethoden.paypal;
export let partner_code: string; export let partner_code: string;
export let nurRechnungsadresseUpdate: Boolean | null = false;
let email: string, vorname: string, name: string, empfaenger: string, strasse: string, plz: string, ort: string, zusatzzeile: string, telefon: string; let email: string, vorname: string, name: string, empfaenger: string, strasse: string, plz: string, ort: string, zusatzzeile: string, telefon: string;
@@ -59,9 +62,22 @@
ort = rechnung?.ort || localStorage.getItem("kundendaten.ort") || user.ort || ""; ort = rechnung?.ort || localStorage.getItem("kundendaten.ort") || user.ort || "";
zusatzzeile = rechnung?.zusatzzeile || localStorage.getItem("kundendaten.zusatzzeile") || "" zusatzzeile = rechnung?.zusatzzeile || localStorage.getItem("kundendaten.zusatzzeile") || ""
telefon = rechnung?.telefon || localStorage.getItem("kundendaten.telefon") || user.telefon || ""; telefon = rechnung?.telefon || localStorage.getItem("kundendaten.telefon") || user.telefon || "";
} else if (impersonatedUser) {
vorname = impersonatedUser.vorname || "";
name = impersonatedUser.name || "";
telefon = impersonatedUser.telefon || "";
email = impersonatedUser.email || "";
if (rechnung){
empfaenger = rechnung?.empfaenger || "";
strasse = rechnung?.strasse || "";
plz = rechnung?.plz || "";
ort = rechnung?.ort || "";
zusatzzeile = rechnung?.zusatzzeile || "";
email = rechnung?.email || "";
}
} }
let abweichende_versand_adresse = JSON.parse(localStorage.getItem("kundendaten.abweichende_versand_adresse") || "false") let abweichende_versand_adresse = rechnung?.abweichende_versand_adresse ?? JSON.parse(localStorage.getItem("kundendaten.abweichende_versand_adresse") || "false")
let versand_email: string | undefined, let versand_email: string | undefined,
versand_zusatzzeile: string | undefined, versand_zusatzzeile: string | undefined,
@@ -69,16 +85,13 @@
versand_strasse: string | undefined, versand_strasse: string | undefined,
versand_plz: string | undefined, versand_plz: string | undefined,
versand_ort: string | undefined; versand_ort: string | undefined;
$: {
if (!abweichende_versand_adresse) { versand_email = "";//Todo Datenbankfeld fehlt noch
versand_email = email versand_zusatzzeile = rechnung?.versand_zusatzzeile ?? zusatzzeile;
versand_zusatzzeile = zusatzzeile versand_empfaenger = rechnung?.versand_empfaenger ?? empfaenger;
versand_empfaenger = empfaenger versand_strasse = rechnung?.versand_strasse ?? strasse;
versand_strasse = strasse versand_plz = rechnung?.versand_plz ?? plz;
versand_plz = plz versand_ort = rechnung?.versand_ort ?? ort;
versand_ort = ort
}
}
$: { $: {
// Wir speichern jede Änderung an den Kundendaten im localStorage ab. // Wir speichern jede Änderung an den Kundendaten im localStorage ab.
@@ -199,6 +212,12 @@
} }
try { try {
const merged_versand_empfaenger = versand_empfaenger || empfaenger;
const merged_versand_strasse = versand_strasse || strasse;
const merged_versand_plz = versand_plz || plz;
const merged_versand_ort = versand_ort || ort;
const merged_versand_zusatzzeile = versand_zusatzzeile || zusatzzeile;
const { id } = await api.rechnung.anfordern.PUT.fetch( const { id } = await api.rechnung.anfordern.PUT.fetch(
{ {
email: email, email: email,
@@ -206,10 +225,11 @@
strasse: strasse, strasse: strasse,
plz: plz, plz: plz,
ort: ort, ort: ort,
versand_empfaenger: versand_empfaenger, versand_empfaenger: merged_versand_empfaenger,
versand_strasse: versand_strasse, versand_strasse: merged_versand_strasse,
versand_plz: versand_plz, versand_plz: merged_versand_plz,
versand_ort: versand_ort, versand_ort: merged_versand_ort,
versand_zusatzzeile: merged_versand_zusatzzeile,
telefon: telefon, telefon: telefon,
nachweis_id: result.nachweis_id nachweis_id: result.nachweis_id
}, },
@@ -233,7 +253,7 @@
} }
} }
async function speichern() { async function speichern(authuser = null) {
loginAction = speichern; loginAction = speichern;
if (!await validateAccessTokenClient()) { if (!await validateAccessTokenClient()) {
loginOverlayHidden = false; loginOverlayHidden = false;
@@ -249,6 +269,62 @@
} else { } else {
result = await ausweisSpeichern(ausweis, objekt, aufnahme, bilder, unterlagen, ausweisart) result = await ausweisSpeichern(ausweis, objekt, aufnahme, bilder, unterlagen, ausweisart)
} }
if (authuser) {
user = await benutzerLesen(authuser.id);
}
let resultUser: Awaited<ReturnType<typeof benutzerSpeichern>> | Awaited<ReturnType<typeof benutzerSpeichern>> | null = null;
const { passwort, ...baseUser } = impersonatedUser ?? user;
const benutzerObjekt = {
...baseUser,
name,
vorname,
telefon
};
resultUser = await benutzerSpeichern(benutzerObjekt);
let id: string, checkout_url: string | undefined;
if (rechnung) {
const merged_versand_empfaenger = versand_empfaenger || empfaenger;
const merged_versand_strasse = versand_strasse || strasse;
const merged_versand_plz = versand_plz || plz;
const merged_versand_ort = versand_ort || ort;
const merged_versand_zusatzzeile = versand_zusatzzeile || zusatzzeile;
const result = await api.rechnung._id.PATCH.fetch({
bezahlmethode: aktiveBezahlmethode,
abweichende_versand_adresse: abweichende_versand_adresse,
empfaenger,
strasse,
plz,
ort,
telefon,
email,
zusatzzeile,
versand_empfaenger: merged_versand_empfaenger,
versand_strasse: merged_versand_strasse,
versand_plz: merged_versand_plz,
versand_ort: merged_versand_ort,
versand_zusatzzeile: merged_versand_zusatzzeile
}, {
params: {
id: rechnung.id
},
headers: {
Authorization: `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`,
},
});
id = result.id;
checkout_url = result.checkout_url;
}
} catch(e) { } catch(e) {
addNotification({ addNotification({
dismissable: true, dismissable: true,
@@ -269,7 +345,7 @@
} }
} }
async function bestellen() { async function bestellen(authuser = null) {
if (!form.checkValidity()) { if (!form.checkValidity()) {
addNotification({ addNotification({
dismissable: true, dismissable: true,
@@ -312,6 +388,22 @@
} }
} }
if (authuser) {
user = await benutzerLesen(authuser.id);
}
let resultUser: Awaited<ReturnType<typeof benutzerSpeichern>> | Awaited<ReturnType<typeof benutzerSpeichern>> | null = null;
const { passwort, ...baseUser } = impersonatedUser ?? user;
const benutzerObjekt = {
...baseUser,
name,
vorname,
telefon
};
resultUser = await benutzerSpeichern(benutzerObjekt);
if (rechnung && rechnung.status === "paid") { if (rechnung && rechnung.status === "paid") {
window.location.href = "/dashboard" window.location.href = "/dashboard"
return; return;
@@ -320,6 +412,12 @@
try { try {
let id: string, checkout_url: string | undefined; let id: string, checkout_url: string | undefined;
const merged_versand_empfaenger = versand_empfaenger || empfaenger;
const merged_versand_strasse = versand_strasse || strasse;
const merged_versand_plz = versand_plz || plz;
const merged_versand_ort = versand_ort || ort;
const merged_versand_zusatzzeile = versand_zusatzzeile || zusatzzeile;
if (rechnung) { if (rechnung) {
const result = await api.rechnung._id.PATCH.fetch({ const result = await api.rechnung._id.PATCH.fetch({
bezahlmethode: aktiveBezahlmethode, bezahlmethode: aktiveBezahlmethode,
@@ -328,12 +426,13 @@
strasse: strasse, strasse: strasse,
plz: plz, plz: plz,
ort: ort, ort: ort,
versand_empfaenger: versand_empfaenger,
versand_strasse: versand_strasse,
versand_plz: versand_plz,
versand_ort: versand_ort,
telefon: telefon, telefon: telefon,
versand_zusatzzeile: versand_zusatzzeile zusatzzeile: zusatzzeile,
versand_empfaenger: merged_versand_empfaenger,
versand_strasse: merged_versand_strasse,
versand_plz: merged_versand_plz,
versand_ort: merged_versand_ort,
versand_zusatzzeile: merged_versand_zusatzzeile
}, { }, {
params: { params: {
id: rechnung.id id: rechnung.id
@@ -358,13 +457,16 @@
strasse: strasse, strasse: strasse,
plz: plz, plz: plz,
ort: ort, ort: ort,
versand_empfaenger: versand_empfaenger, zusatzzeile: zusatzzeile,
versand_strasse: versand_strasse, versand_empfaenger: merged_versand_empfaenger,
versand_plz: versand_plz, versand_strasse: merged_versand_strasse,
versand_ort: versand_ort, versand_plz: merged_versand_plz,
versand_ort: merged_versand_ort,
versand_zusatzzeile: merged_versand_zusatzzeile,
telefon: telefon, telefon: telefon,
ausweis_id: ausweis.id, ausweis_id: ausweis.id,
partner_code partner_code,
abweichende_versand_adresse: abweichende_versand_adresse
}, },
{ {
headers: { headers: {
@@ -404,6 +506,7 @@
let form: HTMLFormElement; let form: HTMLFormElement;
</script> </script>
{#if !nurRechnungsadresseUpdate}
<div <div
id="skala" id="skala"
class="bg-white grid grid-cols-1 gap-x-8 gap-y-4 px-0 sm:p-4 class="bg-white grid grid-cols-1 gap-x-8 gap-y-4 px-0 sm:p-4
@@ -434,7 +537,7 @@
<Progressbar active={1} {ausweisart} ausweistyp={ausweis.ausweistyp} anliegen={"Energieausweis erstellen"} /> <Progressbar active={1} {ausweisart} ausweistyp={ausweis.ausweistyp} anliegen={"Energieausweis erstellen"} />
{/if} {/if}
</div> </div>
{/if}
<form id="formInput-2" bind:this={form}> <form id="formInput-2" bind:this={form}>
<div id="formular-box" class="formular-boxen ring-0"> <div id="formular-box" class="formular-boxen ring-0">
@@ -702,7 +805,6 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
<input <input
name="versand_ort" name="versand_ort"
type="text" type="text"
readonly
required required
bind:value={versand_ort} bind:value={versand_ort}
/> />
@@ -739,7 +841,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
</div> </div>
<!-- E-mail --> <!-- E-mail -->
<!-- Versand Email wird derzeit nicht verwendet
<div class="input-standard order-12 md:order-12 xl:order-12"> <div class="input-standard order-12 md:order-12 xl:order-12">
<InputLabel title="E-mail *"></InputLabel> <InputLabel title="E-mail *"></InputLabel>
@@ -756,7 +858,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
</HelpLabel> </HelpLabel>
</div> </div>
</div> </div>
-->
<!-- Telefon <!-- Telefon
<div class="input-standard order-[13] md:order-[13] xl:order-[13]"> <div class="input-standard order-[13] md:order-[13] xl:order-[13]">
@@ -780,7 +882,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
</Bereich </Bereich
> >
{#if !gegAnfrage} {#if !gegAnfrage && !nurRechnungsadresseUpdate}
<Bereich bereich="3" title="Bezahlmethode"> <Bereich bereich="3" title="Bezahlmethode">
<div <div
id="bezahlung" id="bezahlung"
@@ -891,7 +993,7 @@ grid-cols-3 sm:grid-cols-5 justify-around justify-items-center items-center"
{/if} {/if}
<!-- Falls wir es mit einem Ausweis zu tun haben und der Nutzer keine Hilfe bestellt hat dann zeigen wir eine Box mit sachen an die vielleicht unklar sind. --> <!-- Falls wir es mit einem Ausweis zu tun haben und der Nutzer keine Hilfe bestellt hat dann zeigen wir eine Box mit sachen an die vielleicht unklar sind. -->
{#if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe || ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) && (ausweis.ausweistyp === Enums.AusweisTyp.Standard || ausweis.ausweistyp === Enums.AusweisTyp.standardXL)} {#if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe || ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) && (ausweis.ausweistyp === Enums.AusweisTyp.Standard || ausweis.ausweistyp === Enums.AusweisTyp.standardXL) && (!nurRechnungsadresseUpdate)}
<div class="grid grid-cols-1 sm:grid-cols-1 gap-x-6 my-6"> <div class="grid grid-cols-1 sm:grid-cols-1 gap-x-6 my-6">
<div class="pruefpunkte bereich-box bg-white"> <div class="pruefpunkte bereich-box bg-white">
<h3>Bitte untenstehende Punkte bestätigen. Bitte gehen Sie gegebenenfalls zurück zum Formular und überprüfen bzw. korrigieren Ihre Eingaben.</h3> <h3>Bitte untenstehende Punkte bestätigen. Bitte gehen Sie gegebenenfalls zurück zum Formular und überprüfen bzw. korrigieren Ihre Eingaben.</h3>
@@ -1072,15 +1174,16 @@ sm:grid-cols-[min-content_min-content_min-content] sm:justify-self-end sm:mt-8"
<button class="order-2 button" type="button" on:click={speichern}>Speichern</button> <button class="order-2 button" type="button" on:click={() => speichern()}>Speichern</button>
{#if !nurRechnungsadresseUpdate}
{#if rechnung && rechnung.status === "paid"} {#if rechnung && rechnung.status === "paid"}
<!-- Von einer GEG Anfrage sollte man sowieso nicht noch mal auf die Kundendaten Seite gelangen, also brauchen wir das hier nicht. --> <!-- Von einer GEG Anfrage sollte man sowieso nicht noch mal auf die Kundendaten Seite gelangen, also brauchen wir das hier nicht. -->
<button <button
class="order-1 sm:order-2 button cursor-pointer" class="order-1 sm:order-2 button cursor-pointer"
data-cy="bestellen" data-cy="bestellen"
type="button" type="button"
on:click={bestellen}>Absenden</button on:click={() => bestellen()}>Absenden</button
> >
{:else} {:else}
{#if gegAnfrage} {#if gegAnfrage}
@@ -1095,10 +1198,11 @@ sm:grid-cols-[min-content_min-content_min-content] sm:justify-self-end sm:mt-8"
class="order-1 sm:order-2 button cursor-pointer" class="order-1 sm:order-2 button cursor-pointer"
data-cy="bestellen" data-cy="bestellen"
type="button" type="button"
on:click={bestellen}>Kostenpflichtig bestellen</button on:click={() => bestellen()}>Kostenpflichtig bestellen</button
> >
{/if} {/if}
{/if} {/if}
{/if}
{#if user.rolle === Enums.BenutzerRolle.ADMIN} {#if user.rolle === Enums.BenutzerRolle.ADMIN}
<button class="button text-sm" on:click={() => { <button class="button text-sm" on:click={() => {

View File

@@ -13,6 +13,10 @@
export let redirect: string | null = null; export let redirect: string | null = null;
function handleInput(event) {
email = event.target.value.toLowerCase();
}
async function login(e: SubmitEvent) { async function login(e: SubmitEvent) {
e.preventDefault() e.preventDefault()
if (passwort.length < 8) { if (passwort.length < 8) {
@@ -26,7 +30,7 @@
} }
try { try {
const { uid } = await api.user.PUT.fetch({ await api.user.PUT.fetch({
email, email,
passwort, passwort,
vorname, vorname,
@@ -38,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;
} }
@@ -83,6 +87,7 @@
name="email" name="email"
class="input input-bordered text-base text-base-content font-medium" class="input input-bordered text-base text-base-content font-medium"
bind:value={email} bind:value={email}
on:input={handleInput}
required required
/> />
</div> </div>

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

@@ -1,7 +1,5 @@
--- ---
import AusweisLayout from "#layouts/AusweisLayoutDatenPartner.astro"; import AusweisLayout from "#layouts/AusweisLayoutDatenPartner.astro";
import VerbrauchsausweisWohnenModule from "#modules/VerbrauchsausweisWohnen/VerbrauchsausweisWohnenModule.svelte";
import { AufnahmeClient, BildClient, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types";
import { Aufnahme, BedarfsausweisGewerbe, Bild, Enums, Objekt, Unterlage, VerbrauchsausweisWohnen } from "#lib/server/prisma"; import { Aufnahme, BedarfsausweisGewerbe, Bild, Enums, Objekt, Unterlage, VerbrauchsausweisWohnen } from "#lib/server/prisma";
import { getAufnahme, getBedarfsausweisGewerbe, getBilder, getObjekt, getUnterlagen, getVerbrauchsausweisWohnen } from "#lib/server/db"; import { getAufnahme, getBedarfsausweisGewerbe, getBilder, getObjekt, getUnterlagen, getVerbrauchsausweisWohnen } from "#lib/server/db";
import { getCurrentUser } from "#lib/server/user"; import { getCurrentUser } from "#lib/server/user";
@@ -9,7 +7,7 @@ import BedarfsausweisGewerbeModule from "#modules/angebot-anfragen/Bedarfsauswei
const id = Astro.url.searchParams.get("id"); const id = Astro.url.searchParams.get("id");
const aufnahme_id = Astro.url.searchParams.get("aufnahme") const aufnahme_id = Astro.url.searchParams.get("aufnahme")
let nachweistyp = Astro.url.searchParams.get("nachweistyp") as Enums.AusweisTyp || Enums.AusweisTyp.Standard; let nachweistyp = Astro.url.searchParams.get("ausweistyp") as Enums.AusweisTyp || Enums.AusweisTyp.Standard;
let nachweis: BedarfsausweisGewerbe = {} as BedarfsausweisGewerbe; let nachweis: BedarfsausweisGewerbe = {} as BedarfsausweisGewerbe;
let aufnahme: Aufnahme = {} as Aufnahme; let aufnahme: Aufnahme = {} as Aufnahme;

View File

@@ -7,7 +7,7 @@ import GEGNachweisGewerbeModule from "#modules/angebot-anfragen/GEGNachweisGewer
const id = Astro.url.searchParams.get("id"); const id = Astro.url.searchParams.get("id");
const aufnahme_id = Astro.url.searchParams.get("aufnahme") const aufnahme_id = Astro.url.searchParams.get("aufnahme")
let nachweistyp = Astro.url.searchParams.get("nachweistyp") as Enums.AusweisTyp || Enums.AusweisTyp.Standard; let nachweistyp = Astro.url.searchParams.get("ausweistyp") as Enums.AusweisTyp || Enums.AusweisTyp.Standard;
let nachweis: GEGNachweisGewerbe = {} as GEGNachweisGewerbe; let nachweis: GEGNachweisGewerbe = {} as GEGNachweisGewerbe;
let aufnahme: Aufnahme = {} as Aufnahme; let aufnahme: Aufnahme = {} as Aufnahme;

View File

@@ -17,7 +17,6 @@ import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { z } from "astro:content"; import { z } from "astro:content";
import { transport } from "#lib/mail.js"; import { transport } from "#lib/mail.js";
import {Attachment} from "nodemailer/lib/mailer/index.js"; import {Attachment} from "nodemailer/lib/mailer/index.js";
import { BASE_URI } from "#lib/constants.js";
import { getAnsichtsausweis, getDatenblatt, getAushang } from "#lib/server/ausweis.js"; import { getAnsichtsausweis, getDatenblatt, getAushang } from "#lib/server/ausweis.js";
import { PutObjectCommand } from "@aws-sdk/client-s3"; import { PutObjectCommand } from "@aws-sdk/client-s3";
import { s3Client } from "#lib/s3.js"; import { s3Client } from "#lib/s3.js";
@@ -34,7 +33,6 @@ import {
getVerbrauchsausweisWohnenKomplett, getVerbrauchsausweisWohnenKomplett,
} from "#lib/server/db.js"; } from "#lib/server/db.js";
import { PDFDocument } from "pdf-lib"; import { PDFDocument } from "pdf-lib";
import * as fs from 'fs';
export const GET = defineApiRoute({ export const GET = defineApiRoute({
input: z.object({ input: z.object({
@@ -329,14 +327,14 @@ export const GET = defineApiRoute({
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${ <p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${
post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : "" post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : ""
} Nachfolgend finden Sie unsere Bankverbindung. Bitte geben Sie als Verwendungszweck die Rechnungsnummer an (siehe unten). Vielen Dank.</p> } <b>Bitte beachten Sie unsere neue Bankverbindung.</b> Bitte geben Sie als Verwendungszweck die Rechnungsnummer an (siehe unten). Vielen Dank.</p>
<br> <br>
<table> <table>
<tr><td>Kreditinstitut</td><td>:</td><td>\t Commerzbank AG</td> <tr><td>Kreditinstitut</td><td>:</td><td>\t Volksbank eG</td>
<tr><td>Empfänger</td><td>:</td><td>\t IB Cornelsen</td> <tr><td>Empfänger</td><td>:</td><td>\t IB Cornelsen</td>
<tr><td>IBAN</td><td>:<td>\t DE81 2004 0000 0348 6008 00</td> <tr><td>IBAN</td><td>:<td>\t DE13 2519 3331 7209 0731 00</td>
<tr><td>BIC</td><td>:</td><td>\t COBADEFFXXX</td> <tr><td>BIC</td><td>:</td><td>\t GENODEF1PAT</td>
<tr><td>Betrag</td><td>:</td><td>\t <b>${rechnung.betrag}€</b></td> <tr><td>Betrag</td><td>:</td><td>\t <b>${rechnung.betrag}€</b></td>
<tr><td>Verwendungszweck</td><td>:</td><td>\t <b>${voucherNumber}</b></td> <tr><td>Verwendungszweck</td><td>:</td><td>\t <b>${voucherNumber}</b></td>
</table> </table>

View File

@@ -197,7 +197,7 @@ export const POST = defineApiRoute({
let filename: string; let filename: string;
if (type === "Ausweis") { if (type === "Ausweis") {
filename = `ID_${ausweis.id}_Ausweis.pdf` filename = `ID_${ausweis.id}_Energieausweis.pdf`
} else { } else {
filename = `ID_${ausweis.id}_${name}`; filename = `ID_${ausweis.id}_${name}`;
} }
@@ -212,7 +212,7 @@ export const POST = defineApiRoute({
const command = new PutObjectCommand({ const command = new PutObjectCommand({
Bucket: "ibc-pdfs", Bucket: "ibc-pdfs",
Key: name, Key: filename,
Body: buffer, Body: buffer,
ACL: "private", ACL: "private",
}); });
@@ -336,14 +336,14 @@ export const POST = defineApiRoute({
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${ <p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${
post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : "" post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : ""
} Nachfolgend finden Sie unsere Bankverbindung. Bitte geben Sie als Verwendungszweck die Rechnungsnummer an (siehe unten). Vielen Dank.</p> } <b>Bitte beachten Sie unsere neue Bankverbindung.</b> Bitte geben Sie als Verwendungszweck die Rechnungsnummer an (siehe unten). Vielen Dank.</p>
<br> <br>
<table> <table>
<tr><td>Kreditinstitut</td><td>:</td><td>\t Commerzbank AG</td> <tr><td>Kreditinstitut</td><td>:</td><td>\t Volksbank eG</td>
<tr><td>Empfänger</td><td>:</td><td>\t IB Cornelsen</td> <tr><td>Empfänger</td><td>:</td><td>\t IB Cornelsen</td>
<tr><td>IBAN</td><td>:<td>\t DE81 2004 0000 0348 6008 00</td> <tr><td>IBAN</td><td>:<td>\t DE13 2519 3331 7209 0731 00</td>
<tr><td>BIC</td><td>:</td><td>\t COBADEFFXXX</td> <tr><td>BIC</td><td>:</td><td>\t GENODEF1PAT</td>
<tr><td>Betrag</td><td>:</td><td>\t <b>${rechnung.betrag}€</b></td> <tr><td>Betrag</td><td>:</td><td>\t <b>${rechnung.betrag}€</b></td>
<tr><td>Verwendungszweck</td><td>:</td><td>\t <b>${voucherNumber}</b></td> <tr><td>Verwendungszweck</td><td>:</td><td>\t <b>${voucherNumber}</b></td>
</table> </table>

View File

@@ -62,6 +62,7 @@ export const GET = defineApiRoute({
exp: refreshTokenExpiry.valueOf(), exp: refreshTokenExpiry.valueOf(),
}); });
console.log("Creating refresh token for user ID:", user.id);
const { id } = await prisma.refreshTokens.create({ const { id } = await prisma.refreshTokens.create({
data: { data: {
token: refreshToken, token: refreshToken,

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

@@ -16,12 +16,14 @@ export const PATCH = defineApiRoute({
strasse: true, strasse: true,
telefon: true, telefon: true,
empfaenger: true, empfaenger: true,
zusatzzeile: true,
versand_empfaenger: true, versand_empfaenger: true,
versand_ort: true, versand_ort: true,
versand_plz: true, versand_plz: true,
versand_strasse: true, versand_strasse: true,
versand_zusatzzeile: true, versand_zusatzzeile: true,
abweichende_versand_adresse: true abweichende_versand_adresse: true,
email: true
}), }),
output: z.object({ output: z.object({
checkout_url: z.string().optional(), checkout_url: z.string().optional(),
@@ -31,7 +33,9 @@ export const PATCH = defineApiRoute({
headers: authorizationHeaders, headers: authorizationHeaders,
async fetch(input, context, user) { async fetch(input, context, user) {
// Wir holen uns die Rechnung // Wir holen uns die Rechnung
const rechnung = await prisma.rechnung.findUnique({ let rechnung;
if (user.rolle !== Enums.BenutzerRolle.ADMIN) {
rechnung = await prisma.rechnung.findUnique({
where: { where: {
id: context.params.id, id: context.params.id,
benutzer: { benutzer: {
@@ -47,6 +51,21 @@ export const PATCH = defineApiRoute({
verbrauchsausweis_wohnen: true verbrauchsausweis_wohnen: true
} }
}) })
} else {
rechnung = await prisma.rechnung.findUnique({
where: {
id: context.params.id
},
include: {
bedarfsausweis_gewerbe: true,
bedarfsausweis_wohnen: true,
geg_nachweis_gewerbe: true,
geg_nachweis_wohnen: true,
verbrauchsausweis_gewerbe: true,
verbrauchsausweis_wohnen: true
}
})
}
if (!rechnung) { if (!rechnung) {
throw new APIError({ throw new APIError({
@@ -66,12 +85,14 @@ export const PATCH = defineApiRoute({
strasse: input.strasse, strasse: input.strasse,
telefon: input.telefon, telefon: input.telefon,
empfaenger: input.empfaenger, empfaenger: input.empfaenger,
zusatzzeile: input.zusatzzeile,
versand_empfaenger: input.versand_empfaenger, versand_empfaenger: input.versand_empfaenger,
versand_ort: input.versand_ort, versand_ort: input.versand_ort,
versand_plz: input.versand_plz, versand_plz: input.versand_plz,
versand_strasse: input.versand_strasse, versand_strasse: input.versand_strasse,
versand_zusatzzeile: input.versand_zusatzzeile, versand_zusatzzeile: input.versand_zusatzzeile,
abweichende_versand_adresse: input.abweichende_versand_adresse, abweichende_versand_adresse: input.abweichende_versand_adresse,
email: input.email
} }
}) })

View File

@@ -5,18 +5,17 @@ import { adminMiddleware, authorizationMiddleware } from "#lib/middleware/author
import { hashPassword } from "#lib/password.js"; import { hashPassword } from "#lib/password.js";
import { createLexOfficeCustomer } from "#lib/server/lexoffice.js"; import { createLexOfficeCustomer } from "#lib/server/lexoffice.js";
import { sendRegisterMail } from "#lib/server/mail/registrierung.js"; import { sendRegisterMail } from "#lib/server/mail/registrierung.js";
import { prisma } from "#lib/server/prisma.js"; import { Benutzer, prisma } from "#lib/server/prisma.js";
import { APIError, defineApiRoute } from "astro-typesafe-api/server"; import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { BenutzerSchema } from "src/generated/zod/benutzer.js"; import { BenutzerSchema } from "src/generated/zod/benutzer.js";
import { z } from "zod"; import { z } from "zod";
import { Enums } from "#lib/client/prisma.js";
export const POST = defineApiRoute({ export const POST = defineApiRoute({
input: BenutzerSchema.omit({ input: BenutzerSchema.omit({
id: true,
lex_office_id: true, lex_office_id: true,
rolle: true, rolle: true,
created_at: true, created_at: true
updated_at: true
}), }),
middleware: authorizationMiddleware, middleware: authorizationMiddleware,
async fetch(input, context, user) { async fetch(input, context, user) {
@@ -24,24 +23,36 @@ export const POST = defineApiRoute({
// TODO: Email wurde geändert, neue Bestätigunsmail schicken. // TODO: Email wurde geändert, neue Bestätigunsmail schicken.
} }
const updateData: any = {};
updateData.id = user.id;
if (input.adresse) updateData.adresse = input.adresse;
if (input.anrede) updateData.anrede = input.anrede;
if (input.email) updateData.email = input.email;
if (input.firma) updateData.firma = input.firma;
if (input.name) updateData.name = input.name;
if (input.vorname) updateData.vorname = input.vorname;
if (input.ort) updateData.ort = input.ort;
if (input.passwort.length != 0) updateData.passwort = hashPassword(input.passwort);
if (input.plz) updateData.plz = input.plz;
if (input.profilbild) updateData.profilbild = input.profilbild;
if (input.telefon) updateData.telefon = input.telefon;
if (input.verified) updateData.telefon = input.verified;
//Only Admin can update other users
if (user.rolle == Enums.BenutzerRolle.ADMIN && input.id != user.id) {
updateData.id = input.id;
} else if(user.rolle != Enums.BenutzerRolle.ADMIN && input.id != user.id){
return;
}
await prisma.benutzer.update({ await prisma.benutzer.update({
where: { where: {
id: user.id id: updateData.id
}, },
data: { data: updateData
adresse: input.adresse, });
anrede: input.anrede,
email: input.email,
firma: input.firma,
name: input.name,
vorname: input.vorname,
ort: input.ort,
passwort: hashPassword(input.passwort),
plz: input.plz,
profilbild: input.profilbild,
telefon: input.telefon,
}
})
}, },
}) })
@@ -53,9 +64,14 @@ export const GET = defineApiRoute({
email: z.string() email: z.string()
})), })),
output: z.array(BenutzerSchema), output: z.array(BenutzerSchema),
middleware: adminMiddleware, middleware: authorizationMiddleware,
async fetch(input, context, admin) { async fetch(input, context, admin) {
if ("id" in input) { if ("id" in input) {
//Only Admin can read other users
if (admin.rolle != Enums.BenutzerRolle.ADMIN && input.id != admin.id) {
return;
}
const user = await prisma.benutzer.findUnique({ const user = await prisma.benutzer.findUnique({
where: { where: {
id: input.id id: input.id
@@ -68,6 +84,11 @@ export const GET = defineApiRoute({
return [user]; return [user];
} else { } else {
//Only admin can read many users
if (admin.rolle != Enums.BenutzerRolle.ADMIN ) {
return;
}
const users = await prisma.benutzer.findMany({ const users = await prisma.benutzer.findMany({
where: { where: {
email: { email: {

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

@@ -0,0 +1,334 @@
---
import AbrechungTable from "#components/Abrechnung/AbrechungTable.svelte";
import { Enums, prisma } from "#lib/server/prisma";
import { getCurrentUser } from "#lib/server/user";
import moment from "moment";
const start = moment(Astro.url.searchParams.get("start"))
const end = moment(Astro.url.searchParams.get("end"))
let startdatum = start.toDate();
let enddatum = end.toDate();
const benutzer = await getCurrentUser(Astro)
if (!benutzer) {
return Astro.redirect("/404")
}
const provisionen={
[Enums.Ausweisart.VerbrauchsausweisWohnen]: 10,
[Enums.Ausweisart.BedarfsausweisWohnen]: 10,
[Enums.Ausweisart.VerbrauchsausweisGewerbe]: 10,
}
// $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.
let bestellungen;
if (start.isValid() && end.isValid()) {
bestellungen = await prisma.rechnung.findMany({
where: {
partner_code: "immowelt",
OR: [{
verbrauchsausweis_gewerbe: {
ausgestellt: true
}
},
{
bedarfsausweis_wohnen: {
ausgestellt: true
}
},
{
verbrauchsausweis_wohnen: {
ausgestellt: true
}
}],
AND: [{
created_at: {
gte: startdatum
},
}, {
created_at: {
lte: enddatum
},
}]
},
orderBy: {
created_at: "desc"
},
include: {
bedarfsausweis_wohnen: true,
verbrauchsausweis_gewerbe: true,
verbrauchsausweis_wohnen: true
}
});
} else {
bestellungen = await prisma.rechnung.findMany({
where: {
partner_code: "immowelt",
OR: [{
verbrauchsausweis_gewerbe: {
ausgestellt: true
}
},
{
bedarfsausweis_wohnen: {
ausgestellt: true
}
},
{
verbrauchsausweis_wohnen: {
ausgestellt: true
}
}]
},
orderBy: {
created_at: "desc"
},
include: {
bedarfsausweis_wohnen: true,
verbrauchsausweis_gewerbe: true,
verbrauchsausweis_wohnen: true
}
});
}
// Wann wurde der partner_code zum ersten mal benutzt?
const partnerCodeErstesMal = (await prisma.rechnung.findFirst({
select: {
created_at: true
},
where: {
partner_code: "immowelt",
OR: [{
verbrauchsausweis_gewerbe: {
ausgestellt: true
}
},
{
bedarfsausweis_wohnen: {
ausgestellt: true
}
},
{
verbrauchsausweis_wohnen: {
ausgestellt: true
}
}],
created_at: {
gte: moment().set("year", 2020).set("dayOfYear", 1).toDate()
}
},
orderBy: {
created_at: "asc"
}
}))?.created_at
let provision = 0;
const ausweisarten: string[] = [];
for (const bestellung of bestellungen) {
if (bestellung.verbrauchsausweis_wohnen) {
ausweisarten.push(Enums.Ausweisart.VerbrauchsausweisWohnen)
provision += provisionen[Enums.Ausweisart.VerbrauchsausweisWohnen]
}
if (bestellung.bedarfsausweis_wohnen) {
ausweisarten.push(Enums.Ausweisart.BedarfsausweisWohnen)
provision += provisionen[Enums.Ausweisart.BedarfsausweisWohnen]
}
if (bestellung.verbrauchsausweis_gewerbe) {
ausweisarten.push(Enums.Ausweisart.VerbrauchsausweisGewerbe)
provision += provisionen[Enums.Ausweisart.VerbrauchsausweisGewerbe]
}
}
---
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Reporting | online-energieausweis.org</title>
<meta charset="utf-8">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script type="text/javascript" src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css" />
<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>
<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>
<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

@@ -50,9 +50,9 @@ if (user.rolle === Enums.BenutzerRolle.USER) {
// SELECT id, updated_at FROM "GEGNachweisGewerbe" WHERE created_at >= ${date} AND bestellt = ${true} // SELECT id, updated_at FROM "GEGNachweisGewerbe" WHERE created_at >= ${date} AND bestellt = ${true}
result = result =
await prisma.$queryRaw`SELECT id, updated_at FROM "VerbrauchsausweisWohnen" UNION ALL await prisma.$queryRaw`SELECT id, updated_at FROM "VerbrauchsausweisWohnen" WHERE ausgestellt = ${false} AND bestellt = ${true} UNION ALL
SELECT id, updated_at FROM "VerbrauchsausweisGewerbe" UNION ALL SELECT id, updated_at FROM "VerbrauchsausweisGewerbe" WHERE ausgestellt = ${false} AND bestellt = ${true} UNION ALL
SELECT id, updated_at FROM "BedarfsausweisWohnen" UNION ALL SELECT id, updated_at FROM "BedarfsausweisWohnen" WHERE ausgestellt = ${false} AND bestellt = ${true} UNION ALL
SELECT id, updated_at FROM "BedarfsausweisGewerbe" UNION ALL SELECT id, updated_at FROM "BedarfsausweisGewerbe" UNION ALL
SELECT id, updated_at FROM "GEGNachweisWohnen" UNION ALL SELECT id, updated_at FROM "GEGNachweisWohnen" UNION ALL
SELECT id, updated_at FROM "GEGNachweisGewerbe" SELECT id, updated_at FROM "GEGNachweisGewerbe"

View File

@@ -0,0 +1,108 @@
---
import { encodeToken } from "#lib/auth/token";
import { TokenType } from "#lib/auth/types";
import { API_ACCESS_TOKEN_COOKIE_NAME, API_REFRESH_TOKEN_COOKIE_NAME } from "#lib/constants";
import { Enums, prisma } from "#lib/server/prisma";
import moment from "moment";
import { createCaller } from "src/astro-typesafe-api-caller";
import KundendatenModule from "#modules/KundendatenModule.svelte";
import AusweisLayout from "#layouts/AusweisLayoutPruefung.astro";
import { getCurrentUser, getOtherUser } from "#lib/server/user";
import { getAusweisartFromId } from "#components/Ausweis/types";
import { getAufnahme, getBedarfsausweisWohnen, getBilder, getObjekt, getRechnung, getUnterlagen, getVerbrauchsausweisGewerbe, getVerbrauchsausweisWohnen } from "#lib/server/db";
import { BenutzerClient } from "#components/Ausweis/types.js";
function getExistingAusweis(rechnung) {
if (rechnung.verbrauchsausweis_wohnen) {
return rechnung.verbrauchsausweis_wohnen;
}
if (rechnung.verbrauchsausweis_gewerbe) {
return rechnung.verbrauchsausweis_gewerbe;
}
if (rechnung.bedarfsausweis_wohnen) {
return rechnung.bedarfsausweis_wohnen ;
}
if (rechnung.bedarfsausweis_gewerbe) {
return rechnung.bedarfsausweis_gewerbe;
}
if (rechnung.geg_nachweis_gewerbe) {
return rechnung.geg_nachweis_gewerbe;
}
if (rechnung.geg_nachweis_wohnen) {
return rechnung.geg_nachweis_wohnen;
}
return null;
}
const user = await getCurrentUser(Astro);
if (!user) {
return Astro.redirect("/auth/login")
}
const rechnungid = Astro.url.searchParams.get("rechnungid")
if (!rechnungid) {
return Astro.redirect("/404")
}
let rechnung;
//Only Admin can read foreign invoices
if (user.rolle !== Enums.BenutzerRolle.ADMIN) {
rechnung = await prisma.rechnung.findUnique({
where: {
id: rechnungid,
benutzer_id: user.id
},
include: {
verbrauchsausweis_wohnen: true,
verbrauchsausweis_gewerbe: true,
bedarfsausweis_wohnen: true,
bedarfsausweis_gewerbe: true,
geg_nachweis_gewerbe: true,
geg_nachweis_wohnen: true
}
})
} else {
rechnung = await prisma.rechnung.findUnique({
where: {
id: rechnungid,
},
include: {
verbrauchsausweis_wohnen: true,
verbrauchsausweis_gewerbe: true,
bedarfsausweis_wohnen: true,
bedarfsausweis_gewerbe: true,
geg_nachweis_gewerbe: true,
geg_nachweis_wohnen: true
}
})
}
if (!rechnung) {
return Astro.redirect("/404")
}
const ausweis = getExistingAusweis(rechnung);
const ausweisart = getAusweisartFromId(ausweis.id)
let aufnahme, objekt, bilder, unterlagen, partner_code;
aufnahme = await getAufnahme(ausweis.aufnahme_id)
objekt = await getObjekt(aufnahme?.objekt_id)
bilder = await getBilder(ausweis.aufnahme_id)
unterlagen = await getUnterlagen(ausweis.aufnahme_id)
let impersonatedUser: Partial<BenutzerClient> | null = null;
if (user){
if (user.id !== ausweis.benutzer_id && ausweis.benutzer_id !== undefined){
impersonatedUser = await getOtherUser(Astro, ausweis.benutzer_id) || {}
}
}
---
<AusweisLayout title="Kundendaten Aufnehmen - IBCornelsen">
<KundendatenModule {user} {impersonatedUser} {ausweis} {objekt} {aufnahme} {bilder} {rechnung} {ausweisart} {unterlagen} {partner_code} aktiveBezahlmethode={Enums.Bezahlmethoden.paypal} nurRechnungsadresseUpdate={true} client:only ></KundendatenModule>
</AusweisLayout>

View File

@@ -3,10 +3,11 @@ import { getAusweisartFromId } from "#components/Ausweis/types";
import AusweisLayoutPruefung from "#layouts/AusweisLayoutPruefung.astro"; import AusweisLayoutPruefung from "#layouts/AusweisLayoutPruefung.astro";
import { getPrismaAusweisAdapter } from "#lib/server/ausweis"; import { getPrismaAusweisAdapter } from "#lib/server/ausweis";
import { Enums } from "#lib/server/prisma"; import { Enums } from "#lib/server/prisma";
import { getCurrentUser } from "#lib/server/user"; import { getCurrentUser, getOtherUser } from "#lib/server/user";
import KundendatenModule from "#modules/KundendatenModule.svelte"; import KundendatenModule from "#modules/KundendatenModule.svelte";
import { PaymentStatus } from "@mollie/api-client"; import { PaymentStatus } from "@mollie/api-client";
import { AusweisTyp } from "src/generated/enums"; import { AusweisTyp } from "src/generated/enums";
import { BenutzerClient } from "#components/Ausweis/types.js";
const uid = Astro.url.searchParams.get("uid") const uid = Astro.url.searchParams.get("uid")
@@ -45,8 +46,16 @@ if (!ausweis) {
if (ausweis.rechnung.status === PaymentStatus.paid) { if (ausweis.rechnung.status === PaymentStatus.paid) {
return Astro.redirect("/dashboard") return Astro.redirect("/dashboard")
} }
let impersonatedUser: Partial<BenutzerClient> | null = null;
if (user){
if (user.id !== ausweis.benutzer_id && ausweis.benutzer_id !== undefined){
impersonatedUser = await getOtherUser(Astro, ausweis.benutzer_id) || {}
}
}
--- ---
<AusweisLayoutPruefung title="Energieausweis Bezahlung"> <AusweisLayoutPruefung title="Energieausweis Bezahlung">
<KundendatenModule {user} {ausweis} objekt={ausweis.aufnahme.objekt} rechnung={ausweis.rechnung} aufnahme={ausweis.aufnahme} bilder={ausweis.aufnahme.bilder} {ausweisart} ausweistyp={AusweisTyp.Standard} aktiveBezahlmethode={Enums.Bezahlmethoden.paypal} client:only></KundendatenModule> <KundendatenModule {user} {impersonatedUser} {ausweis} objekt={ausweis.aufnahme.objekt} rechnung={ausweis.rechnung} aufnahme={ausweis.aufnahme} bilder={ausweis.aufnahme.bilder} {ausweisart} ausweistyp={AusweisTyp.Standard} aktiveBezahlmethode={Enums.Bezahlmethoden.paypal} client:only></KundendatenModule>
</AusweisLayoutPruefung> </AusweisLayoutPruefung>

View File

@@ -0,0 +1,19 @@
---
import { getHeapSnapshot } from "v8";
import * as fs from "fs";
// Create a named heap snapshot
const snapshotStream = getHeapSnapshot();
const fileName = `heap-${Date.now()}.heapsnapshot`;
const fileStream = fs.createWriteStream(fileName);
snapshotStream.pipe(fileStream);
fileStream.on("finish", () => {
console.log(`Heap snapshot saved to ${fileName}`);
});
fileStream.on("error", (err) => {
console.error("Error writing heap snapshot:", err);
});
---

View File

@@ -3,9 +3,10 @@
import KundendatenModule from "#modules/KundendatenModule.svelte"; import KundendatenModule from "#modules/KundendatenModule.svelte";
import AusweisLayout from "#layouts/AusweisLayoutPruefung.astro"; import AusweisLayout from "#layouts/AusweisLayoutPruefung.astro";
import { Enums } from "#lib/client/prisma"; import { Enums } from "#lib/client/prisma";
import { getCurrentUser } from "#lib/server/user"; import { getCurrentUser, getOtherUser } from "#lib/server/user";
import { getAusweisartFromId } from "#components/Ausweis/types"; import { getAusweisartFromId } from "#components/Ausweis/types";
import { getAufnahme, getBedarfsausweisWohnen, getBilder, getObjekt, getRechnung, getUnterlagen, getVerbrauchsausweisGewerbe, getVerbrauchsausweisWohnen } from "#lib/server/db"; import { getAufnahme, getBedarfsausweisWohnen, getBilder, getObjekt, getRechnung, getUnterlagen, getVerbrauchsausweisGewerbe, getVerbrauchsausweisWohnen } from "#lib/server/db";
import { BenutzerClient } from "#components/Ausweis/types.js";
// Man sollte nur auf diese Seite kommen, wenn ein Ausweis bereits vorliegt und in der Datenbank abgespeichert wurde. // Man sollte nur auf diese Seite kommen, wenn ein Ausweis bereits vorliegt und in der Datenbank abgespeichert wurde.
@@ -70,9 +71,16 @@ if (!params.has("ausweis") || !params.has("aufnahme") || !params.has("objekt") |
} }
} }
let impersonatedUser: Partial<BenutzerClient> | null = null;
if (user){
if (user.id !== ausweis.benutzer_id && ausweis.benutzer_id !== undefined){
impersonatedUser = await getOtherUser(Astro, ausweis.benutzer_id) || {}
}
}
--- ---
<AusweisLayout title="Kundendaten Aufnehmen - IBCornelsen"> <AusweisLayout title="Kundendaten Aufnehmen - IBCornelsen">
<KundendatenModule {user} {ausweis} {objekt} {aufnahme} {bilder} {rechnung} {ausweisart} {unterlagen} {partner_code} aktiveBezahlmethode={Enums.Bezahlmethoden.paypal} client:only ></KundendatenModule> <KundendatenModule {user} {impersonatedUser} {ausweis} {objekt} {aufnahme} {bilder} {rechnung} {ausweisart} {unterlagen} {partner_code} aktiveBezahlmethode={Enums.Bezahlmethoden.paypal} client:only ></KundendatenModule>
</AusweisLayout> </AusweisLayout>

View File

@@ -45,14 +45,23 @@ export const GET: APIRoute = async (Astro) => {
user = await getCurrentUser(Astro) user = await getCurrentUser(Astro)
let pdf: Uint8Array<ArrayBufferLike> | null = null; let pdf: Uint8Array<ArrayBufferLike> | null = null;
if (ausweis.ausgestellt) {
if (/[A-Z]{2}[0-9]{8}/.test(ausweis.id)) { if (/[A-Z]{2}[0-9]{8}/.test(ausweis.id)) {
const id = ausweis.id.match(/[A-Z]{2}([0-9]{8})/) as RegExpMatchArray const id = ausweis.id.match(/[A-Z]{2}([0-9]{8})/) as RegExpMatchArray
// Dieser Ausweis wurde mit der alten Version erstellt, das PDF sollte bereits existieren. // Dieser Ausweis wurde mit der alten Version erstellt, das PDF sollte bereits existieren.
pdf = await getS3File("ibc-pdfs", `ID_${id[1]}_Energieausweis.pdf`) pdf = await getS3File("ibc-pdfs", `ID_${id[1]}_Energieausweis.pdf`)
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) { } else {
pdf = await getS3File("ibc-pdfs", `ID_${ausweis.id}_Energieausweis.pdf`)
}
} else {
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
pdf = await pdfVerbrauchsausweisWohnen(ausweis as VerbrauchsausweisWohnenClient, aufnahme, objekt, bilder, user, !ausweis.ausgestellt); pdf = await pdfVerbrauchsausweisWohnen(ausweis as VerbrauchsausweisWohnenClient, aufnahme, objekt, bilder, user, !ausweis.ausgestellt);
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) { } else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
pdf = await pdfVerbrauchsausweisGewerbe(ausweis as VerbrauchsausweisGewerbeClient, aufnahme, objekt, bilder, user, !ausweis.ausgestellt); pdf = await pdfVerbrauchsausweisGewerbe(ausweis as VerbrauchsausweisGewerbeClient, aufnahme, objekt, bilder, user, !ausweis.ausgestellt);
} else if (ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) {
//todo
}
} }
return new Response(pdf, { return new Response(pdf, {

View File

@@ -3,12 +3,28 @@
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")
FILE_NAME="backup-$TIMESTAMP.sql.br" FILE_NAME="backup-$TIMESTAMP.sql.br"
# Wir holen uns den parameter --skip-backup, um zu entscheiden, ob wir ein Backup machen wollen
SKIP_BACKUP=false
while [[ $# -gt 0 ]]; do
case $1 in
--skip-backup)
SKIP_BACKUP=true
shift # Remove the argument from the list
;;
*)
echo "Unbekannter Parameter: $1"
exit 1
;;
esac
done
REQUIRED_CONFIRMATION='Ja, ich möchte alle Daten unwiderruflich löschen.' REQUIRED_CONFIRMATION='Ja, ich möchte alle Daten unwiderruflich löschen.'
echo "⚠️ WARNUNG: Diese Aktion wird alle Tabellen und Einträge in der Datenbank vollständig löschen!" echo "⚠️ WARNUNG: Diese Aktion wird alle Tabellen und Einträge in der Datenbank vollständig löschen!"
@@ -21,9 +37,11 @@ if [[ "$USER_CONFIRMATION" != "$REQUIRED_CONFIRMATION" ]]; then
exit 1 exit 1
fi fi
echo "📦 Backup wird erstellt..." if [[ "$SKIP_BACKUP" == false ]]; then
docker exec -t "$CONTAINER_NAME" pg_dumpall -c -U "$DB_USER" | brotli > "$FILE_NAME" echo "📦 Backup wird erstellt..."
echo "✅ Backup abgeschlossen: $FILE_NAME" docker exec -t "$CONTAINER_NAME" pg_dumpall -c -U "$DB_USER" | brotli > "$FILE_NAME"
echo "✅ Backup abgeschlossen: $FILE_NAME"
fi
echo "🧨 Alle Daten aus allen Tabellen werden gelöscht..." echo "🧨 Alle Daten aus allen Tabellen werden gelöscht..."
@@ -34,12 +52,22 @@ DECLARE
r RECORD; r RECORD;
sql TEXT := ''; sql TEXT := '';
BEGIN BEGIN
-- Truncate all tables
FOR r IN FOR r IN
SELECT tablename SELECT tablename
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;
-- Drop all sequences
FOR r IN
SELECT sequence_name
FROM information_schema.sequences
WHERE sequence_schema = 'public'
LOOP
sql := sql || FORMAT('DROP SEQUENCE IF EXISTS public.%I CASCADE;', r.sequence_name);
END LOOP; END LOOP;
EXECUTE sql; EXECUTE sql;