Merge pull request #515 from IBCornelsen/dev

Datum Fetch, Rechnung, Mollie, etc
This commit is contained in:
Jens Cornelsen
2025-04-21 16:33:14 +02:00
committed by GitHub
28 changed files with 475 additions and 310 deletions

View File

@@ -41,7 +41,7 @@ stop-database:
- docker stop $(DB_CONTAINER_NAME) - docker stop $(DB_CONTAINER_NAME)
- docker rm $(DB_CONTAINER_NAME) - docker rm $(DB_CONTAINER_NAME)
wait-fordatabase: wait-for-database:
@while ! docker exec $(DB_CONTAINER_NAME) pg_isready -U $(DB_USER) -h localhost -p $(DB_PORT) > /dev/null 2>&1; do \ @while ! docker exec $(DB_CONTAINER_NAME) pg_isready -U $(DB_USER) -h localhost -p $(DB_PORT) > /dev/null 2>&1; do \
sleep 1; \ sleep 1; \
done done
@@ -68,4 +68,4 @@ prod: install-dependencies prisma-studio backup-database-cronjob
backup-database-cronjob: backup-database-cronjob:
- pm2 delete daily-db-backup - pm2 delete daily-db-backup
pm2 start backup-database.bash --name "daily-db-backup" --cron "0 0 * * *" pm2 start bash --name "daily-db-backup" --cron "0 0 * * *" -- backup-database.bash

View File

@@ -18,7 +18,7 @@
"@trpc/client": "^10.45.2", "@trpc/client": "^10.45.2",
"@trpc/server": "^10.45.2", "@trpc/server": "^10.45.2",
"astro": "^4.16.17", "astro": "^4.16.17",
"astro-typesafe-api": "^0.2.2", "astro-typesafe-api": "^0.2.4",
"body-scroll-lock": "^4.0.0-beta.0", "body-scroll-lock": "^4.0.0-beta.0",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"bun": "^1.2.5", "bun": "^1.2.5",
@@ -44,6 +44,7 @@
"siema": "^1.5.1", "siema": "^1.5.1",
"soap": "^1.1.8", "soap": "^1.1.8",
"sqids": "^0.3.0", "sqids": "^0.3.0",
"ssh2-sftp-client": "^12.0.0",
"svelte": "^3.59.2", "svelte": "^3.59.2",
"svelte-dialogs": "^1.2.2", "svelte-dialogs": "^1.2.2",
"svelte-preprocess": "^5.1.4", "svelte-preprocess": "^5.1.4",
@@ -67,6 +68,7 @@
"@types/nodemailer": "^6.4.17", "@types/nodemailer": "^6.4.17",
"@types/papaparse": "^5.3.15", "@types/papaparse": "^5.3.15",
"@types/siema": "^1.4.11", "@types/siema": "^1.4.11",
"@types/ssh2-sftp-client": "^9.0.4",
"@types/uuid": "^9.0.8", "@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0", "@typescript-eslint/parser": "^5.62.0",
@@ -82,11 +84,11 @@
"postcss-import": "^16.1.0", "postcss-import": "^16.1.0",
"postcss-nesting": "^13.0.1", "postcss-nesting": "^13.0.1",
"prettier": "^2.8.8", "prettier": "^2.8.8",
"prisma": "^6.4.1", "prisma": "6.4.1",
"prisma-dbml-generator": "^0.12.0", "prisma-dbml-generator": "^0.12.0",
"prisma-generator-fake-data": "^0.14.3", "prisma-generator-fake-data": "^0.14.3",
"tsx": "^4.19.3", "tsx": "^4.19.3",
"typescript": "^5", "typescript": "^5.8.3",
"zod-prisma": "^0.5.4", "zod-prisma": "^0.5.4",
}, },
}, },
@@ -807,6 +809,10 @@
"@types/sizzle": ["@types/sizzle@2.3.9", "", {}, "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w=="], "@types/sizzle": ["@types/sizzle@2.3.9", "", {}, "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w=="],
"@types/ssh2": ["@types/ssh2@1.15.5", "", { "dependencies": { "@types/node": "^18.11.18" } }, "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ=="],
"@types/ssh2-sftp-client": ["@types/ssh2-sftp-client@9.0.4", "", { "dependencies": { "@types/ssh2": "^1.0.0" } }, "sha512-gnIn56MTB9W3A3hPL/1sHI23t8YwcE3eVYa1O2XjT9vaqimFdtNHxyQiy5Y78+ociQTKazMSD8YyMEO4QjNMrg=="],
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
"@types/uuid": ["@types/uuid@9.0.8", "", {}, "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA=="], "@types/uuid": ["@types/uuid@9.0.8", "", {}, "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA=="],
@@ -909,7 +915,7 @@
"astro": ["astro@4.16.18", "", { "dependencies": { "@astrojs/compiler": "^2.10.3", "@astrojs/internal-helpers": "0.4.1", "@astrojs/markdown-remark": "5.3.0", "@astrojs/telemetry": "3.1.0", "@babel/core": "^7.26.0", "@babel/plugin-transform-react-jsx": "^7.25.9", "@babel/types": "^7.26.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.3", "@types/babel__core": "^7.20.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.1.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^0.7.2", "cssesc": "^3.0.0", "debug": "^4.3.7", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.5.4", "esbuild": "^0.21.5", "estree-walker": "^3.0.3", "fast-glob": "^3.3.2", "flattie": "^1.1.1", "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", "html-escaper": "^3.0.3", "http-cache-semantics": "^4.1.1", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.14", "magicast": "^0.3.5", "micromatch": "^4.0.8", "mrmime": "^2.0.0", "neotraverse": "^0.6.18", "ora": "^8.1.1", "p-limit": "^6.1.0", "p-queue": "^8.0.1", "preferred-pm": "^4.0.0", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.6.3", "shiki": "^1.23.1", "tinyexec": "^0.3.1", "tsconfck": "^3.1.4", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3", "vite": "^5.4.11", "vitefu": "^1.0.4", "which-pm": "^3.0.0", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-G7zfwJt9BDHEZwlaLNvjbInIw2hPryyD654314KV/XT34pJU6SfN1S+mWa8RAkALcZNJnJXCJmT3JXLQStD3Lw=="], "astro": ["astro@4.16.18", "", { "dependencies": { "@astrojs/compiler": "^2.10.3", "@astrojs/internal-helpers": "0.4.1", "@astrojs/markdown-remark": "5.3.0", "@astrojs/telemetry": "3.1.0", "@babel/core": "^7.26.0", "@babel/plugin-transform-react-jsx": "^7.25.9", "@babel/types": "^7.26.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.3", "@types/babel__core": "^7.20.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.1.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^0.7.2", "cssesc": "^3.0.0", "debug": "^4.3.7", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.5.4", "esbuild": "^0.21.5", "estree-walker": "^3.0.3", "fast-glob": "^3.3.2", "flattie": "^1.1.1", "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", "html-escaper": "^3.0.3", "http-cache-semantics": "^4.1.1", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.14", "magicast": "^0.3.5", "micromatch": "^4.0.8", "mrmime": "^2.0.0", "neotraverse": "^0.6.18", "ora": "^8.1.1", "p-limit": "^6.1.0", "p-queue": "^8.0.1", "preferred-pm": "^4.0.0", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.6.3", "shiki": "^1.23.1", "tinyexec": "^0.3.1", "tsconfck": "^3.1.4", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3", "vite": "^5.4.11", "vitefu": "^1.0.4", "which-pm": "^3.0.0", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-G7zfwJt9BDHEZwlaLNvjbInIw2hPryyD654314KV/XT34pJU6SfN1S+mWa8RAkALcZNJnJXCJmT3JXLQStD3Lw=="],
"astro-typesafe-api": ["astro-typesafe-api@0.2.2", "", { "dependencies": { "es-codec": "^0.5.0", "globby": "^14.0.2" }, "peerDependencies": { "astro": "^4.16.17", "typescript": "^5.0.0", "zod": "^3.24.1" }, "bin": { "astro-typesafe-api": "src/cli.ts" } }, "sha512-SEHV2iPyIrdpYdYb0mIN1WmcvC61bvsCQqb/X+R4EOcFjuozJ9fJhSiFGxJMvNoxJ9S3P3GKLyDnxXvFlKq0mw=="], "astro-typesafe-api": ["astro-typesafe-api@0.2.4", "", { "dependencies": { "es-codec": "^0.5.0", "globby": "^14.0.2" }, "peerDependencies": { "astro": "^4.16.17", "typescript": "^5.0.0", "zod": "^3.24.1" }, "bin": { "astro-typesafe-api": "src/cli.ts" } }, "sha512-KiAw7+QJyuzz606GSkeaTdav8vttDUEYVaFAdVRlDuSvUdhcYsJB14zHkMe6ZSMfRNBQRxaMZBgPgEtWb1mf1w=="],
"async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
@@ -975,6 +981,10 @@
"buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
"buildcheck": ["buildcheck@0.0.6", "", {}, "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A=="],
"bun": ["bun@1.2.5", "", { "optionalDependencies": { "@oven/bun-darwin-aarch64": "1.2.5", "@oven/bun-darwin-x64": "1.2.5", "@oven/bun-darwin-x64-baseline": "1.2.5", "@oven/bun-linux-aarch64": "1.2.5", "@oven/bun-linux-aarch64-musl": "1.2.5", "@oven/bun-linux-x64": "1.2.5", "@oven/bun-linux-x64-baseline": "1.2.5", "@oven/bun-linux-x64-musl": "1.2.5", "@oven/bun-linux-x64-musl-baseline": "1.2.5", "@oven/bun-windows-x64": "1.2.5", "@oven/bun-windows-x64-baseline": "1.2.5" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "bun": "bin/bun.exe", "bunx": "bin/bun.exe" } }, "sha512-fbQLt+DPiGUrPKdmsHRRT7cQAlfjdxPVFvLZrsUPmKiTdv+pU50ypdx9yRJluknSbyaZchFVV7Lx2KXikXKX2Q=="], "bun": ["bun@1.2.5", "", { "optionalDependencies": { "@oven/bun-darwin-aarch64": "1.2.5", "@oven/bun-darwin-x64": "1.2.5", "@oven/bun-darwin-x64-baseline": "1.2.5", "@oven/bun-linux-aarch64": "1.2.5", "@oven/bun-linux-aarch64-musl": "1.2.5", "@oven/bun-linux-x64": "1.2.5", "@oven/bun-linux-x64-baseline": "1.2.5", "@oven/bun-linux-x64-musl": "1.2.5", "@oven/bun-linux-x64-musl-baseline": "1.2.5", "@oven/bun-windows-x64": "1.2.5", "@oven/bun-windows-x64-baseline": "1.2.5" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "bun": "bin/bun.exe", "bunx": "bin/bun.exe" } }, "sha512-fbQLt+DPiGUrPKdmsHRRT7cQAlfjdxPVFvLZrsUPmKiTdv+pU50ypdx9yRJluknSbyaZchFVV7Lx2KXikXKX2Q=="],
"bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="], "bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="],
@@ -1083,6 +1093,8 @@
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"concat-stream": ["concat-stream@2.0.0", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A=="],
"consola": ["consola@3.4.0", "", {}, "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA=="], "consola": ["consola@3.4.0", "", {}, "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA=="],
"console-control-strings": ["console-control-strings@1.1.0", "", {}, "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="], "console-control-strings": ["console-control-strings@1.1.0", "", {}, "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="],
@@ -1107,6 +1119,8 @@
"core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="], "core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
"cpu-features": ["cpu-features@0.0.10", "", { "dependencies": { "buildcheck": "~0.0.6", "nan": "^2.19.0" } }, "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA=="],
"crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="], "crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="],
"crc32-stream": ["crc32-stream@4.0.3", "", { "dependencies": { "crc-32": "^1.2.0", "readable-stream": "^3.4.0" } }, "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw=="], "crc32-stream": ["crc32-stream@4.0.3", "", { "dependencies": { "crc-32": "^1.2.0", "readable-stream": "^3.4.0" } }, "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw=="],
@@ -2395,6 +2409,10 @@
"sqids": ["sqids@0.3.0", "", {}, "sha512-lOQK1ucVg+W6n3FhRwwSeUijxe93b51Bfz5PMRMihVf1iVkl82ePQG7V5vwrhzB11v0NtsR25PSZRGiSomJaJw=="], "sqids": ["sqids@0.3.0", "", {}, "sha512-lOQK1ucVg+W6n3FhRwwSeUijxe93b51Bfz5PMRMihVf1iVkl82ePQG7V5vwrhzB11v0NtsR25PSZRGiSomJaJw=="],
"ssh2": ["ssh2@1.16.0", "", { "dependencies": { "asn1": "^0.2.6", "bcrypt-pbkdf": "^1.0.2" }, "optionalDependencies": { "cpu-features": "~0.0.10", "nan": "^2.20.0" } }, "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg=="],
"ssh2-sftp-client": ["ssh2-sftp-client@12.0.0", "", { "dependencies": { "concat-stream": "^2.0.0", "ssh2": "^1.16.0" } }, "sha512-k+ocDsx6N2eDwQlIRwJFa0I1bkQpFPhIc+cv1iplaQaIPXFt9YM1ZnXCJOW4OILS5dzE+12OlhYIF5g0AzgVfg=="],
"sshpk": ["sshpk@1.18.0", "", { "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "bcrypt-pbkdf": "^1.0.0", "dashdash": "^1.12.0", "ecc-jsbn": "~0.1.1", "getpass": "^0.1.1", "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" }, "bin": { "sshpk-conv": "bin/sshpk-conv", "sshpk-sign": "bin/sshpk-sign", "sshpk-verify": "bin/sshpk-verify" } }, "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ=="], "sshpk": ["sshpk@1.18.0", "", { "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "bcrypt-pbkdf": "^1.0.0", "dashdash": "^1.12.0", "ecc-jsbn": "~0.1.1", "getpass": "^0.1.1", "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" }, "bin": { "sshpk-conv": "bin/sshpk-conv", "sshpk-sign": "bin/sshpk-sign", "sshpk-verify": "bin/sshpk-verify" } }, "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ=="],
"statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
@@ -2539,6 +2557,8 @@
"type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
"typedarray": ["typedarray@0.0.6", "", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="],
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
"ufo": ["ufo@1.5.4", "", {}, "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ=="], "ufo": ["ufo@1.5.4", "", {}, "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ=="],
@@ -2741,6 +2761,8 @@
"@sveltejs/vite-plugin-svelte/vitefu": ["vitefu@0.2.5", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["vite"] }, "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q=="], "@sveltejs/vite-plugin-svelte/vitefu": ["vitefu@0.2.5", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["vite"] }, "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q=="],
"@types/ssh2/@types/node": ["@types/node@18.19.86", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-fifKayi175wLyKyc5qUfyENhQ1dCNI1UNjp653d8kuYcPQN5JhX3dGuP/XmvPTg/xRBn1VTLpbmi+H/Mr7tLfQ=="],
"@typescript-eslint/typescript-estree/globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], "@typescript-eslint/typescript-estree/globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="],
"@typescript-eslint/utils/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], "@typescript-eslint/utils/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="],
@@ -3001,6 +3023,8 @@
"@prisma/internals/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], "@prisma/internals/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"@types/ssh2/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
"@typescript-eslint/typescript-estree/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], "@typescript-eslint/typescript-estree/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"@typescript-eslint/utils/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], "@typescript-eslint/utils/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="],

View File

@@ -32,7 +32,7 @@
"@trpc/client": "^10.45.2", "@trpc/client": "^10.45.2",
"@trpc/server": "^10.45.2", "@trpc/server": "^10.45.2",
"astro": "^4.16.17", "astro": "^4.16.17",
"astro-typesafe-api": "^0.2.2", "astro-typesafe-api": "^0.2.4",
"body-scroll-lock": "^4.0.0-beta.0", "body-scroll-lock": "^4.0.0-beta.0",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"bun": "^1.2.5", "bun": "^1.2.5",
@@ -58,6 +58,7 @@
"siema": "^1.5.1", "siema": "^1.5.1",
"soap": "^1.1.8", "soap": "^1.1.8",
"sqids": "^0.3.0", "sqids": "^0.3.0",
"ssh2-sftp-client": "^12.0.0",
"svelte": "^3.59.2", "svelte": "^3.59.2",
"svelte-dialogs": "^1.2.2", "svelte-dialogs": "^1.2.2",
"svelte-preprocess": "^5.1.4", "svelte-preprocess": "^5.1.4",
@@ -81,6 +82,7 @@
"@types/nodemailer": "^6.4.17", "@types/nodemailer": "^6.4.17",
"@types/papaparse": "^5.3.15", "@types/papaparse": "^5.3.15",
"@types/siema": "^1.4.11", "@types/siema": "^1.4.11",
"@types/ssh2-sftp-client": "^9.0.4",
"@types/uuid": "^9.0.8", "@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0", "@typescript-eslint/parser": "^5.62.0",

View File

@@ -9,35 +9,34 @@ export const createCaller = createCallerFactory({
"admin/bestellbestaetigung": await import("../src/pages/api/admin/bestellbestaetigung.ts"), "admin/bestellbestaetigung": await import("../src/pages/api/admin/bestellbestaetigung.ts"),
"admin/erinnern": await import("../src/pages/api/admin/erinnern.ts"), "admin/erinnern": await import("../src/pages/api/admin/erinnern.ts"),
"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/post-ausstellen": await import("../src/pages/api/admin/post-ausstellen.ts"),
"admin/registriernummer": await import("../src/pages/api/admin/registriernummer.ts"), "admin/registriernummer": await import("../src/pages/api/admin/registriernummer.ts"),
"admin/stornieren": await import("../src/pages/api/admin/stornieren.ts"), "admin/stornieren": await import("../src/pages/api/admin/stornieren.ts"),
"bedarfsausweis-gewerbe/[id]": await import("../src/pages/api/bedarfsausweis-gewerbe/[id].ts"),
"bedarfsausweis-gewerbe": await import("../src/pages/api/bedarfsausweis-gewerbe/index.ts"),
"aufnahme": await import("../src/pages/api/aufnahme/index.ts"), "aufnahme": await import("../src/pages/api/aufnahme/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": await import("../src/pages/api/bedarfsausweis-wohnen/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"),
"bedarfsausweis-gewerbe/[id]": await import("../src/pages/api/bedarfsausweis-gewerbe/[id].ts"), "ausweise": await import("../src/pages/api/ausweise/index.ts"),
"bedarfsausweis-gewerbe": await import("../src/pages/api/bedarfsausweis-gewerbe/index.ts"),
"bedarfsausweis-wohnen/[id]": await import("../src/pages/api/bedarfsausweis-wohnen/[id].ts"),
"bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"),
"bilder/[id]": await import("../src/pages/api/bilder/[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-wohnen/[id]": await import("../src/pages/api/geg-nachweis-wohnen/[id].ts"), "geg-nachweis-wohnen/[id]": await import("../src/pages/api/geg-nachweis-wohnen/[id].ts"),
"geg-nachweis-wohnen": await import("../src/pages/api/geg-nachweis-wohnen/index.ts"), "geg-nachweis-wohnen": await import("../src/pages/api/geg-nachweis-wohnen/index.ts"),
"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"),
"bilder/[id]": await import("../src/pages/api/bilder/[id].ts"),
"ticket": await import("../src/pages/api/ticket/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"),
"ticket": await import("../src/pages/api/ticket/index.ts"),
"user": await import("../src/pages/api/user/index.ts"),
"user/self": await import("../src/pages/api/user/self.ts"),
"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"),
"webhooks/mollie": await import("../src/pages/api/webhooks/mollie.ts"), "user": await import("../src/pages/api/user/index.ts"),
"user/self": await import("../src/pages/api/user/self.ts"),
"verbrauchsausweis-wohnen/[id]": await import("../src/pages/api/verbrauchsausweis-wohnen/[id].ts"), "verbrauchsausweis-wohnen/[id]": await import("../src/pages/api/verbrauchsausweis-wohnen/[id].ts"),
"verbrauchsausweis-wohnen": await import("../src/pages/api/verbrauchsausweis-wohnen/index.ts"), "verbrauchsausweis-wohnen": await import("../src/pages/api/verbrauchsausweis-wohnen/index.ts"),
"webhooks/mollie": await import("../src/pages/api/webhooks/mollie.ts"),
"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"),

View File

@@ -63,8 +63,15 @@
fuelMap[fuel[0]].push(fuel[1]); fuelMap[fuel[0]].push(fuel[1]);
} }
let month = moment(ausweis.startdatum).month(); // Falls der Ausweis bereits einmal gespeichert wurde mit einem Startdatum sollten wir dieses benutzen.
let year = moment(ausweis.startdatum).year(); let month: number, year: number;
if (ausweis.startdatum) {
month = moment(ausweis.startdatum).get("month")
year = moment(ausweis.startdatum).get("year")
} else {
month = availableDates[availableDates.length - 1].month;
year = availableDates[availableDates.length - 1].year;
}
$: { $: {
if (typeof month === "number" && typeof year === "number") { if (typeof month === "number" && typeof year === "number") {

View File

@@ -40,15 +40,15 @@
"Dezember", "Dezember",
]; ];
const startDate = moment(aufnahme.erstellungsdatum || Date.now()) const earlistPossibleStartDate = moment(aufnahme.erstellungsdatum || Date.now())
.subtract(4, "years") .subtract(4, "years")
.subtract(6, "months"); .subtract(6, "months");
const endDate = moment(aufnahme.erstellungsdatum || Date.now()).subtract( const lastPossibleEndDate = moment(aufnahme.erstellungsdatum || Date.now()).subtract(
3, 3,
"years" "years"
); );
for (let m = moment(startDate); m.isBefore(endDate); m.add(1, "month")) { for (let m = moment(earlistPossibleStartDate); m.isBefore(lastPossibleEndDate); m.add(1, "month")) {
availableDates.push({ availableDates.push({
year: m.year(), year: m.year(),
month: m.month(), month: m.month(),
@@ -62,8 +62,15 @@
fuelMap[fuel[0]].push(fuel[1]); fuelMap[fuel[0]].push(fuel[1]);
} }
let month = availableDates[availableDates.length - 1].month; // Falls der Ausweis bereits einmal gespeichert wurde mit einem Startdatum sollten wir dieses benutzen.
let year = availableDates[availableDates.length - 1].year; let month: number, year: number;
if (ausweis.startdatum) {
month = moment(ausweis.startdatum).get("month")
year = moment(ausweis.startdatum).get("year")
} else {
month = availableDates[availableDates.length - 1].month;
year = availableDates[availableDates.length - 1].year;
}
$: { $: {
if (typeof month === "number" && typeof year === "number") { if (typeof month === "number" && typeof year === "number") {

View File

@@ -116,7 +116,7 @@
async function stornieren() { async function stornieren() {
try { try {
const response = await api.admin.stornieren.PUT.fetch({ const response = await api.admin.stornieren.PUT.fetch({
uid_ausweis: ausweis.id ausweis_id: ausweis.id
}, { }, {
headers: { headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}` "Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
@@ -143,7 +143,7 @@
} }
} }
async function ausstellen() { async function ausstellen(post = false) {
const notification = addNotification({ const notification = addNotification({
message: "Ausweis wird ausgestellt.", message: "Ausweis wird ausgestellt.",
subtext: "Der Ausweis wird nun ausgestellt, bitte habe einen Augenblick geduld..", subtext: "Der Ausweis wird nun ausgestellt, bitte habe einen Augenblick geduld..",
@@ -152,7 +152,8 @@
}) })
try { try {
await api.admin.ausstellen.GET.fetch({ await api.admin.ausstellen.GET.fetch({
id_ausweis: ausweis.id id_ausweis: ausweis.id,
post
}, { }, {
headers: { headers: {
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}` "Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
@@ -386,9 +387,10 @@
{/if} {/if}
{#if benutzer.rolle === Enums.BenutzerRolle.ADMIN} {#if benutzer.rolle === Enums.BenutzerRolle.ADMIN}
<button class="button text-sm" on:click={ausstellen}>A</button> <button class="button text-sm" title="Ausstellen" on:click={() => ausstellen(false)}>A</button>
<button class="button text-sm" on:click={stornieren}>S</button> <button class="button text-sm" title="Ausstellen mit Postversand" on:click={() => ausstellen(true)}>P</button>
<button class="button text-sm" on:click={registriernummer}>R</button> <button class="button text-sm" title="Stornieren" on:click={stornieren}>S</button>
<button class="button text-sm" title="Registriernummer anfordern" on:click={registriernummer}>R</button>
{/if} {/if}
<a <a

View File

@@ -3,13 +3,13 @@ import * as fs from "fs"
import { PDFDocument, StandardFonts } from "pdf-lib"; import { PDFDocument, StandardFonts } from "pdf-lib";
import { xml2pdf } from "./elements/xml2pdf.js"; import { xml2pdf } from "./elements/xml2pdf.js";
import moment from "moment"; import moment from "moment";
import { Enums, Heizungsstatus } from "#lib/server/prisma.js"; import { Enums, Heizungsstatus, Rechnung } from "#lib/server/prisma.js";
import { copyPage } from "./utils/copyPage.js"; import { copyPage } from "./utils/copyPage.js";
import { endEnergieVerbrauchVerbrauchsausweisGewerbe_2016_Server } from "#lib/Berechnungen/VerbrauchsausweisGewerbe/VerbrauchsausweisGewerbe_2016_Server.js"; import { endEnergieVerbrauchVerbrauchsausweisGewerbe_2016_Server } from "#lib/Berechnungen/VerbrauchsausweisGewerbe/VerbrauchsausweisGewerbe_2016_Server.js";
/* -------------------------------- Pdf Tools ------------------------------- */ /* -------------------------------- Pdf Tools ------------------------------- */
export async function pdfDatenblattVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewerbeClient, aufnahme: AufnahmeClient, objekt: ObjektClient, benutzer: BenutzerClient, bilder: BildClient[]) { export async function pdfDatenblattVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewerbeClient, aufnahme: AufnahmeClient, objekt: ObjektClient, rechnung: Rechnung | null, bilder: BildClient[]) {
const VerbrauchsausweisWohnenGEG2024PDF = fs.readFileSync(new URL("../../../public/pdf/templates/Leerseite_Datenblatt.pdf", import.meta.url), "base64"); const VerbrauchsausweisWohnenGEG2024PDF = fs.readFileSync(new URL("../../../public/pdf/templates/Leerseite_Datenblatt.pdf", import.meta.url), "base64");
const pdf = await PDFDocument.load(VerbrauchsausweisWohnenGEG2024PDF) const pdf = await PDFDocument.load(VerbrauchsausweisWohnenGEG2024PDF)
const page3 = copyPage(pdf.getPages()[0]); const page3 = copyPage(pdf.getPages()[0]);
@@ -32,14 +32,6 @@ export async function pdfDatenblattVerbrauchsausweisGewerbe(ausweis: Verbrauchsa
const marginX = 60; const marginX = 60;
const marginY = 150; const marginY = 150;
benutzer = benutzer || {
vorname: "Max",
name: "Mustermann",
adresse: "Musterstraße 123",
plz: "12345",
ort: "Beispielhausen"
};
const translateHeizungsstatus: Record<Heizungsstatus, string> = { const translateHeizungsstatus: Record<Heizungsstatus, string> = {
BEHEIZT: "beheizt", BEHEIZT: "beheizt",
NICHT_VORHANDEN: "nicht vorhanden", NICHT_VORHANDEN: "nicht vorhanden",
@@ -70,9 +62,10 @@ export async function pdfDatenblattVerbrauchsausweisGewerbe(ausweis: Verbrauchsa
} }
const layout = xml2pdf(`<layout height="${pages[0].getHeight()}" width="${pages[0].getWidth()}" marginTop="150" marginLeft="${marginX}" marginRight="${marginX}"> const layout = xml2pdf(`<layout height="${pages[0].getHeight()}" width="${pages[0].getWidth()}" marginTop="150" marginLeft="${marginX}" marginRight="${marginX}">
<text size="12" lineHeight="14">${benutzer.vorname} ${benutzer.name}</text> <text size="12" lineHeight="14">${rechnung?.versand_empfaenger || ""}</text>
<text size="12" lineHeight="14">${benutzer.adresse}</text> <text size="12" lineHeight="14">${rechnung?.versand_zusatzzeile || ""}</text>
<text size="12" lineHeight="14">${benutzer.plz} ${benutzer.ort}</text> <text size="12" lineHeight="14">${rechnung?.versand_strasse || ""}</text>
<text size="12" lineHeight="14">${rechnung?.versand_plz || ""} ${rechnung?.versand_ort || ""}</text>
<flex direction="row" justify="space-between" marginTop="55" width="${innerWidth}"> <flex direction="row" justify="space-between" marginTop="55" width="${innerWidth}">
<text size="12" font="bold">Datenblatt Energieausweis</text> <text size="12" font="bold">Datenblatt Energieausweis</text>
<text size="12">Ausweis ID: ${ausweis.id}</text> <text size="12">Ausweis ID: ${ausweis.id}</text>

View File

@@ -1,16 +1,15 @@
import { AufnahmeClient, BenutzerClient, BildClient, ObjektClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js"; import { AufnahmeClient, BildClient, ObjektClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js";
import { endEnergieVerbrauchVerbrauchsausweis_2016 } from "#lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016.js";
import * as fs from "fs" import * as fs from "fs"
import { PDFDocument, StandardFonts } from "pdf-lib"; import { PDFDocument, StandardFonts } from "pdf-lib";
import { xml2pdf } from "./elements/xml2pdf.js"; import { xml2pdf } from "./elements/xml2pdf.js";
import moment from "moment"; import moment from "moment";
import { Enums, Heizungsstatus } from "#lib/server/prisma.js"; import { Enums, Heizungsstatus, Rechnung } from "#lib/server/prisma.js";
import { copyPage } from "./utils/copyPage.js"; import { copyPage } from "./utils/copyPage.js";
import { endEnergieVerbrauchVerbrauchsausweis_2016_Server } from "#lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016_Server.js"; import { endEnergieVerbrauchVerbrauchsausweis_2016_Server } from "#lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016_Server.js";
/* -------------------------------- Pdf Tools ------------------------------- */ /* -------------------------------- Pdf Tools ------------------------------- */
export async function pdfDatenblattVerbrauchsausweisWohnen(ausweis: VerbrauchsausweisWohnenClient, aufnahme: AufnahmeClient, objekt: ObjektClient, benutzer: BenutzerClient, bilder: BildClient[]) { export async function pdfDatenblattVerbrauchsausweisWohnen(ausweis: VerbrauchsausweisWohnenClient, aufnahme: AufnahmeClient, objekt: ObjektClient, rechnung: Rechnung | null, bilder: BildClient[]) {
const VerbrauchsausweisWohnenGEG2024PDF = fs.readFileSync(new URL("../../../public/pdf/templates/Leerseite_Datenblatt.pdf", import.meta.url), "base64"); const VerbrauchsausweisWohnenGEG2024PDF = fs.readFileSync(new URL("../../../public/pdf/templates/Leerseite_Datenblatt.pdf", import.meta.url), "base64");
const pdf = await PDFDocument.load(VerbrauchsausweisWohnenGEG2024PDF) const pdf = await PDFDocument.load(VerbrauchsausweisWohnenGEG2024PDF)
const page3 = copyPage(pdf.getPages()[0]); const page3 = copyPage(pdf.getPages()[0]);
@@ -34,15 +33,6 @@ export async function pdfDatenblattVerbrauchsausweisWohnen(ausweis: Verbrauchsau
const marginX = 60; const marginX = 60;
const marginY = 150; const marginY = 150;
benutzer = benutzer || {
firma: "Max Mustermann GmbH",
vorname: "Max",
name: "Mustermann",
adresse: "Musterstraße 123",
plz: "12345",
ort: "Beispielhausen"
};
const translateHeizungsstatus: Record<Heizungsstatus, string> = { const translateHeizungsstatus: Record<Heizungsstatus, string> = {
BEHEIZT: "beheizt", BEHEIZT: "beheizt",
NICHT_VORHANDEN: "nicht vorhanden", NICHT_VORHANDEN: "nicht vorhanden",
@@ -54,9 +44,10 @@ export async function pdfDatenblattVerbrauchsausweisWohnen(ausweis: Verbrauchsau
const id = ausweis.id; const id = ausweis.id;
const layout = xml2pdf(`<layout height="${pages[0].getHeight()}" width="${pages[0].getWidth()}" marginTop="150" marginLeft="${marginX}" marginRight="${marginX}"> const layout = xml2pdf(`<layout height="${pages[0].getHeight()}" width="${pages[0].getWidth()}" marginTop="150" marginLeft="${marginX}" marginRight="${marginX}">
<text size="12" lineHeight="14">${benutzer.firma}</text> <text size="12" lineHeight="14">${rechnung?.versand_empfaenger || ""}</text>
<text size="12" lineHeight="14">${benutzer.adresse}</text> <text size="12" lineHeight="14">${rechnung?.versand_zusatzzeile || ""}</text>
<text size="12" lineHeight="14">${benutzer.plz} ${benutzer.ort}</text> <text size="12" lineHeight="14">${rechnung?.versand_strasse || ""}</text>
<text size="12" lineHeight="14">${rechnung?.versand_plz || ""} ${rechnung?.versand_ort || ""}</text>
<flex direction="row" justify="space-between" marginTop="55" width="${innerWidth}"> <flex direction="row" justify="space-between" marginTop="55" width="${innerWidth}">
<text size="12" font="bold">Datenblatt Energieausweis</text> <text size="12" font="bold">Datenblatt Energieausweis</text>
<text size="12">Ausweis ID: ${id}</text> <text size="12">Ausweis ID: ${id}</text>

View File

@@ -305,17 +305,6 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
width: pfeilWidth, width: pfeilWidth,
height: 30 height: 30
}) })
const endEnergieVerbrauchGesamt = Math.round(berechnungen?.endEnergieVerbrauchGesamt ?? 0);
const MaxvergleichsWertWaerme = Math.round(berechnungen?.vergleichsWertWaerme * 2);
const MaxvergleichsWertWaermeText = `> ${MaxvergleichsWertWaerme.toString()}`;
const MaxvergleichswertStrom = Math.round(berechnungen?.vergleichsWertStrom * 2);
const MaxvergleichswertStromText = `> ${Math.round(MaxvergleichswertStrom).toString()}`;
const endEnergieVerbrauchGesamtText = `${endEnergieVerbrauchGesamt.toString()}kWh/(m²a)`;
const stromVerbrauchGesamtText = `${Math.round(berechnungen?.endEnergieVerbrauchStrom ?? 0).toString()}kWh/(m²a)`;
const vergleichswertWaermeText = `${Math.round(berechnungen?.vergleichsWertWaerme).toString()}kWh/(m²a)`
const vergleichswertWaermeText2 = `${Math.round(berechnungen?.vergleichsWertWaerme).toString()}`
const vergleichswertStromText = `${Math.round(berechnungen?.vergleichsWertStrom).toString()}kWh/(m²a)`
const vergleichswertStromText2 = `${Math.round(berechnungen?.vergleichsWertStrom).toString()}`
page.drawText("0", { page.drawText("0", {
x: 85, x: 85,
@@ -324,6 +313,8 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
font: bold font: bold
}) })
const vergleichswertWaermeText2 = `${Math.round(berechnungen?.vergleichsWertWaerme).toString()}`
page.drawText(vergleichswertWaermeText2, { page.drawText(vergleichswertWaermeText2, {
x: 295, x: 295,
y: height - 241, y: height - 241,
@@ -331,7 +322,9 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
font: bold font: bold
}) })
page.drawText(MaxvergleichsWertWaermeText, { const maxVergleichswertWaermeText = `> ${Math.round(berechnungen?.vergleichsWertWaerme * 2).toString()}`;
page.drawText(maxVergleichswertWaermeText, {
x: vergleichsWertWaermeTranslationX * 2 - 78, x: vergleichsWertWaermeTranslationX * 2 - 78,
y: height - 241, y: height - 241,
size: 13, size: 13,
@@ -345,6 +338,8 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
font: bold font: bold
}) })
const vergleichswertStromText2 = `${Math.round(berechnungen?.vergleichsWertStrom).toString()}`
page.drawText(vergleichswertStromText2, { page.drawText(vergleichswertStromText2, {
x: 295, x: 295,
y: height - 385, y: height - 385,
@@ -352,13 +347,16 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
font: bold font: bold
}) })
page.drawText(MaxvergleichswertStromText, { const maxVergleichswertStromText = `> ${Math.round(berechnungen?.vergleichsWertStrom * 2).toString()}`;
page.drawText(maxVergleichswertStromText, {
x: vergleichsWertStromTranslationX * 2 - 78, x: vergleichsWertStromTranslationX * 2 - 78,
y: height - 385, y: height - 385,
size: 13, size: 13,
font: bold font: bold
}) })
const endEnergieVerbrauchGesamtText = `${Math.round(berechnungen?.endEnergieVerbrauchGesamt ?? 0).toString()}kWh/(m²a)`;
if (endenergieverbrauchTranslationPercentage > 0.5) { if (endenergieverbrauchTranslationPercentage > 0.5) {
page.drawText("Endenergieverbrauch Wärme", { page.drawText("Endenergieverbrauch Wärme", {
@@ -387,6 +385,8 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
}) })
} }
const vergleichswertWaermeText = `${Math.round(berechnungen?.vergleichsWertWaerme).toString()}kWh/(m²a)`
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),
@@ -428,6 +428,8 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
height: 30 height: 30
}) })
const stromVerbrauchGesamtText = `${Math.round(berechnungen?.endEnergieVerbrauchStrom ?? 0).toString()}kWh/(m²a)`;
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),
@@ -455,6 +457,9 @@ export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewe
}) })
} }
const vergleichswertStromText = `${Math.round(berechnungen?.vergleichsWertStrom).toString()}kWh/(m²a)`
if (vergleichsWertWaermeTranslationPercentage > 0.5) { if (vergleichsWertWaermeTranslationPercentage > 0.5) {
page.drawText("Vergleichswert Strom", { page.drawText("Vergleichswert Strom", {
x: vergleichsWertStromTranslationX - margin - font.widthOfTextAtSize("Vergleichswert Strom", 10), x: vergleichsWertStromTranslationX - margin - font.widthOfTextAtSize("Vergleichswert Strom", 10),

View File

@@ -3,7 +3,7 @@ import { pdfDatenblattVerbrauchsausweisGewerbe } from "#lib/pdf/pdfDatenblattVer
import { pdfDatenblattVerbrauchsausweisWohnen } from "#lib/pdf/pdfDatenblattVerbrauchsausweisWohnen.js"; import { pdfDatenblattVerbrauchsausweisWohnen } from "#lib/pdf/pdfDatenblattVerbrauchsausweisWohnen.js";
import { pdfVerbrauchsausweisGewerbe } from "#lib/pdf/pdfVerbrauchsausweisGewerbe.js"; import { pdfVerbrauchsausweisGewerbe } from "#lib/pdf/pdfVerbrauchsausweisGewerbe.js";
import { pdfVerbrauchsausweisWohnen } from "#lib/pdf/pdfVerbrauchsausweisWohnen.js"; import { pdfVerbrauchsausweisWohnen } from "#lib/pdf/pdfVerbrauchsausweisWohnen.js";
import { Enums, prisma } from "#lib/server/prisma.js"; import { Enums, prisma, Rechnung } from "#lib/server/prisma.js";
/** /**
* Gibt den richtigen Prisma Adapter für die Ausweisart basierend auf der UID zurück, oder null bei einer falschen UID. * Gibt den richtigen Prisma Adapter für die Ausweisart basierend auf der UID zurück, oder null bei einer falschen UID.
@@ -49,15 +49,15 @@ export async function getAnsichtsausweis(ausweis: VerbrauchsausweisWohnenClient
* Gibt das richtige Datenblatt basierend auf der Ausweisart zurück. * Gibt das richtige Datenblatt basierend auf der Ausweisart zurück.
* @param ausweis * @param ausweis
*/ */
export async function getDatenblatt(ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient, aufnahme: AufnahmeClient, objekt: ObjektClient, bilder: BildClient[], user: BenutzerClient, ausweisart = getAusweisartFromId(ausweis.id)) { export async function getDatenblatt(ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient, aufnahme: AufnahmeClient, objekt: ObjektClient, bilder: BildClient[], user: BenutzerClient, rechnung: Rechnung, ausweisart = getAusweisartFromId(ausweis.id)) {
if (!ausweisart) { if (!ausweisart) {
return null return null
} }
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) { if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
return await pdfDatenblattVerbrauchsausweisWohnen(ausweis as VerbrauchsausweisWohnenClient, aufnahme, objekt, user, bilder) return await pdfDatenblattVerbrauchsausweisWohnen(ausweis as VerbrauchsausweisWohnenClient, aufnahme, objekt, rechnung, bilder)
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) { } else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
return await pdfDatenblattVerbrauchsausweisGewerbe(ausweis as VerbrauchsausweisGewerbeClient, aufnahme, objekt, user, bilder) return await pdfDatenblattVerbrauchsausweisGewerbe(ausweis as VerbrauchsausweisGewerbeClient, aufnahme, objekt, rechnung, bilder)
} }
return null return null

View File

@@ -1,4 +1,4 @@
import { Aufnahme, BedarfsausweisGewerbe, BedarfsausweisWohnen, Bild, GEGNachweisGewerbe, GEGNachweisWohnen, Objekt, prisma, Unterlage, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "./prisma.js"; import { Aufnahme, BedarfsausweisGewerbe, BedarfsausweisWohnen, Bild, GEGNachweisGewerbe, GEGNachweisWohnen, Objekt, prisma, Rechnung, Unterlage, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "./prisma.js";
export async function getVerbrauchsausweisWohnen(id: string): Promise<VerbrauchsausweisWohnen | null> { export async function getVerbrauchsausweisWohnen(id: string): Promise<VerbrauchsausweisWohnen | null> {
return await prisma.verbrauchsausweisWohnen.findUnique({ return await prisma.verbrauchsausweisWohnen.findUnique({
@@ -24,6 +24,7 @@ export async function getVerbrauchsausweisWohnenKomplett(id: string) {
}, },
}, },
}, },
rechnung: true
}, },
}) })
} }
@@ -54,6 +55,7 @@ export async function getVerbrauchsausweisGewerbeKomplett(id: string) {
}, },
}, },
}, },
rechnung: true
}, },
}) })
} }
@@ -82,6 +84,7 @@ export async function getBedarfsausweisWohnenKomplett(id: string) {
}, },
}, },
}, },
rechnung: true
}, },
}) })
} }
@@ -141,3 +144,11 @@ export async function getUnterlagen(aufnahme_id: string): Promise<Unterlage[]> {
} }
}) })
} }
export async function getRechnung(rechnung_id: string) {
return await prisma.rechnung.findUnique({
where: {
id: rechnung_id
}
})
}

View File

@@ -190,7 +190,7 @@
> >
<!-- F Angaben zur Heizungsanlage --> <!-- F Angaben zur Heizungsanlage -->
<!-- <Bereich bereich="F" title="Angaben zur Heizunganlage" <Bereich bereich="D" title="Angaben zur Heizunganlage"
><SanierungszustandHeizungsanlage ><SanierungszustandHeizungsanlage
bind:images={bilder} bind:images={bilder}
bind:objekt bind:objekt
@@ -198,9 +198,9 @@
bind:ausweis bind:ausweis
{ausweisart} {ausweisart}
/></Bereich /></Bereich
> --> >
<Bereich bereich="D" title="Gebäudepläne & Unterlagen"> <Bereich bereich="E" title="Gebäudepläne & Unterlagen">
<div <div
class="bereich-box grid grid-cols-1 lg:grid-cols-2 gap-x-6 mt-6" class="bereich-box grid grid-cols-1 lg:grid-cols-2 gap-x-6 mt-6"
> >
@@ -239,7 +239,7 @@
<!-- G Angaben zur Fenster, Dachfenster und Türen --> <!-- G Angaben zur Fenster, Dachfenster und Türen -->
<Bereich bereich="E" title="Angaben zu Fenster, Dachfenster und Türen" <Bereich bereich="F" title="Angaben zu Fenster, Dachfenster und Türen"
><SanierungszustandFensterTueren ><SanierungszustandFensterTueren
bind:images={bilder} bind:images={bilder}
bind:objekt bind:objekt
@@ -249,7 +249,7 @@
> >
<!-- H Angaben zur Wärmedammung --> <!-- H Angaben zur Wärmedammung -->
<Bereich bereich="F" title="Angaben zur Wärmedämmung" <Bereich bereich="G" title="Angaben zur Wärmedämmung"
><SanierungszustandWaermedammung ><SanierungszustandWaermedammung
bind:images={bilder} bind:images={bilder}
bind:objekt bind:objekt
@@ -260,7 +260,7 @@
<!-- I Gebäudebild und Energieausweis PDF Vorschau --> <!-- I Gebäudebild und Energieausweis PDF Vorschau -->
<Bereich bereich="G" title="Gebäudebild und Energieausweis PDF Vorschau" <Bereich bereich="H" title="Gebäudebild und Energieausweis PDF Vorschau"
><AusweisPreviewContainer ><AusweisPreviewContainer
bind:images={bilder} bind:images={bilder}
bind:ausweis bind:ausweis

View File

@@ -94,15 +94,10 @@
<hr /> <hr />
{#if user.rolle === Enums.BenutzerRolle.ADMIN}
<!-- <div class="flex flex-col mb-4">
<AusweisePruefenFilter bind:filters={filters}></AusweisePruefenFilter>
</div> -->
<form action="" class="flex flex-row gap-2 my-2"> <form action="" class="flex flex-row gap-2 my-2">
<input type="text" bind:value={id} name="id" placeholder="ID"> <input type="text" bind:value={id} name="id" placeholder="ID">
<button class="button text-sm">Suchen</button> <button class="button text-sm">Suchen</button>
</form> </form>
{/if}
<!-- <div class="relative mb-6"> <!-- <div class="relative mb-6">
<button class="button" on:click={() => { <button class="button" on:click={() => {

View File

@@ -30,8 +30,6 @@
import InputLabel from "#components/labels/InputLabel.svelte"; import InputLabel from "#components/labels/InputLabel.svelte";
import PlzSuche from "#components/PlzSuche.svelte"; import PlzSuche from "#components/PlzSuche.svelte";
import { getMaximumDevitationInPercent } from "#client/lib/helpers.js"; import { getMaximumDevitationInPercent } from "#client/lib/helpers.js";
import { endEnergieVerbrauchVerbrauchsausweis_2016 } from "#lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016.js";
import { endEnergieVerbrauchVerbrauchsausweisGewerbe_2016 } from "#lib/Berechnungen/VerbrauchsausweisGewerbe/VerbrauchsausweisGewerbe_2016.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";

View File

@@ -8,9 +8,11 @@ import {
Enums, Enums,
Objekt, Objekt,
prisma, prisma,
Rechnung,
VerbrauchsausweisGewerbe, VerbrauchsausweisGewerbe,
VerbrauchsausweisWohnen, VerbrauchsausweisWohnen,
} from "#lib/server/prisma.js"; } from "#lib/server/prisma.js";
import { join } from "path"
import { APIError, defineApiRoute } from "astro-typesafe-api/server"; 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";
@@ -20,19 +22,22 @@ import { PutObjectCommand } from "@aws-sdk/client-s3";
import { s3Client } from "#lib/s3.js"; import { s3Client } from "#lib/s3.js";
import { createInvoice, getLexOfficeRechnung } from "#lib/server/invoice.js"; import { createInvoice, getLexOfficeRechnung } from "#lib/server/invoice.js";
import { tryCatch } from "#lib/tryCatch.js"; import { tryCatch } from "#lib/tryCatch.js";
import SFTPClient from 'ssh2-sftp-client';
import { import {
getBedarfsausweisWohnenKomplett, getBedarfsausweisWohnenKomplett,
getVerbrauchsausweisGewerbeKomplett, getVerbrauchsausweisGewerbeKomplett,
getVerbrauchsausweisWohnenKomplett, getVerbrauchsausweisWohnenKomplett,
} from "#lib/server/db.js"; } from "#lib/server/db.js";
import { PDFDocument } from "pdf-lib";
export const GET = defineApiRoute({ export const GET = defineApiRoute({
input: z.object({ input: z.object({
id_ausweis: z.string(), id_ausweis: z.string(),
post: z.boolean().describe("Ob der Ausweis auch per Post ausgestellt werden soll.").optional().default(false)
}), }),
output: z.void(), output: z.void(),
middleware: adminMiddleware, middleware: adminMiddleware,
async fetch({ id_ausweis }, context) { async fetch({ id_ausweis, post }, context) {
const ausweisart = getAusweisartFromId(id_ausweis); const ausweisart = getAusweisartFromId(id_ausweis);
let ausweis: let ausweis:
@@ -46,7 +51,8 @@ export const GET = defineApiRoute({
objekt: Objekt & { objekt: Objekt & {
benutzer: Benutzer | null; benutzer: Benutzer | null;
}; };
}; },
rechnung: Rechnung
}) })
| null = null; | null = null;
@@ -95,7 +101,11 @@ export const GET = defineApiRoute({
); );
if (error) { if (error) {
return; throw new APIError({
code: "BAD_REQUEST",
message:
"Die Rechnung konnte bei LexOffice nicht angelegt werden..",
});
} }
const { id, voucherNumber } = result; const { id, voucherNumber } = result;
@@ -123,184 +133,239 @@ export const GET = defineApiRoute({
ausweis.aufnahme, ausweis.aufnahme,
ausweis.aufnahme.objekt, ausweis.aufnahme.objekt,
ausweis.aufnahme.bilder, ausweis.aufnahme.bilder,
ausweis.aufnahme.objekt.benutzer ausweis.aufnahme.objekt.benutzer,
ausweis.rechnung
); );
const [pdfRechnung, pdfRechnungError] = await tryCatch(getLexOfficeRechnung(rechnung)); // TODO: Das ist immer noch scheiße, LexOffice ist doof
// Hier müssen wir warten, damit wir sichergehen können, dass die Rechnung bei LexOffice existiert.
setTimeout(async () => {
const [pdfRechnung, pdfRechnungError] = await tryCatch(getLexOfficeRechnung(rechnung));
if (pdfRechnungError) { if (pdfRechnungError) {
throw new APIError({ throw new APIError({
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
message: "Rechnungs PDF konnte nicht generiert werden." message: "Rechnungs PDF konnte nicht generiert werden."
}) })
} }
if (!pdfAusweis) { if (!pdfAusweis) {
throw new APIError({ throw new APIError({
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
message: "Ausweis PDF konnte nicht generiert werden." message: "Ausweis PDF konnte nicht generiert werden."
}) })
} }
if (!pdfDatenblatt) { if (!pdfDatenblatt) {
throw new APIError({ throw new APIError({
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
message: "Datenblatt PDF konnte nicht generiert werden." message: "Datenblatt PDF konnte nicht generiert werden."
}) })
} }
const ausweisCommand = new PutObjectCommand({ const ausweisCommand = new PutObjectCommand({
Bucket: "ibc-pdfs", Bucket: "ibc-pdfs",
Key: `ID_${ausweis.id}_Energieausweis.pdf`, Key: `ID_${ausweis.id}_Energieausweis.pdf`,
Body: pdfAusweis, Body: pdfAusweis,
ACL: "private", ACL: "private",
});
await s3Client.send(ausweisCommand);
const datenblattCommand = new PutObjectCommand({
Bucket: "ibc-pdfs",
Key: `ID_${ausweis.id}_Datenblatt.pdf`,
Body: pdfDatenblatt,
ACL: "private",
});
await s3Client.send(datenblattCommand);
const rechnungsCommand = new PutObjectCommand({
Bucket: "ibc-pdfs",
Key: `ID_${ausweis.id}_Rechnung.pdf`,
Body: pdfDatenblatt,
ACL: "private",
});
await s3Client.send(rechnungsCommand);
let html: string;
if (rechnung.status === Enums.Rechnungsstatus.paid) {
html = `
<p>Sehr geehrte*r ${rechnung.empfaenger},</p>
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. Den Rechnungsbetrag haben Sie bereits bezahlt. Vielen Dank.</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>`;
} else {
html = `
<p>Sehr geehrte*r ${rechnung.empfaenger},</p>
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. Nachfolgend finden Sie unsere Bankverbindung. Bitte geben Sie als Verwendungszweck die Rechnungsnummer an (siehe unten). Vielen Dank.</p>
<br>
<table>
<tr><td>Kreditinstitut</td><td>:</td><td>\t Commerzbank AG</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>BIC</td><td>:</td><td>\t COBADEFFXXX</td>
<tr><td>Betrag</td><td>:</td><td>\t <b>${rechnung.betrag}€</b></td>
<tr><td>Verwendungszweck</td><td>:</td><td>\t <b>${rechnung.id}</b></td>
</table>
<br>
<br>
<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>`;
}
await transport.sendMail({
from: `"IBCornelsen" <info@online-energieausweis.org>`,
to: rechnung.email || rechnung.benutzer.email,
bcc: "info@online-energieausweis.org",
subject: `Ihr Originalausweis vom Ingenieurbüro Cornelsen (ID: ${ausweis.id})`,
html,
attachments: [{
filename: `ID_${ausweis.id}_Energieausweis.pdf`,
encoding: "binary",
content: Buffer.from(pdfAusweis),
contentType: "application/pdf",
contentDisposition: "attachment",
}, {
filename: `ID_${ausweis.id}_Datenblatt.pdf`,
encoding: "binary",
content: Buffer.from(pdfDatenblatt),
contentType: "application/pdf",
contentDisposition: "attachment",
}, {
filename: `ID_${ausweis.id}_Rechnung.pdf`,
encoding: "binary",
content: Buffer.from(pdfRechnung),
contentType: "application/pdf",
contentDisposition: "attachment",
}]
});
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
await prisma.verbrauchsausweisWohnen.update({
where: {
id: ausweis.id,
},
data: {
ausgestellt: true,
},
}); });
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
await prisma.verbrauchsausweisGewerbe.update({ await s3Client.send(ausweisCommand);
where: {
id: ausweis.id, const datenblattCommand = new PutObjectCommand({
}, Bucket: "ibc-pdfs",
data: { Key: `ID_${ausweis.id}_Datenblatt.pdf`,
ausgestellt: true, Body: pdfDatenblatt,
}, ACL: "private",
}); });
} else if (ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) {
await prisma.bedarfsausweisWohnen.update({ await s3Client.send(datenblattCommand);
where: {
id: ausweis.id, const rechnungsCommand = new PutObjectCommand({
}, Bucket: "ibc-pdfs",
data: { Key: `ID_${ausweis.id}_Rechnung.pdf`,
ausgestellt: true, Body: pdfDatenblatt,
}, ACL: "private",
}); });
}
await s3Client.send(rechnungsCommand);
// Falls Postversand angefragt wurde müssen wir die Dateien auf den Postserver hochladen
if (post) {
const dateiNameDruck = `1011000000000-AW_ID_${ausweis.id}.pdf`;
const outputPdf = await PDFDocument.create();
async function appendPdf(buffer: Uint8Array<ArrayBufferLike>) {
const doc = await PDFDocument.load(buffer);
const copiedPages = await outputPdf.copyPages(doc, doc.getPageIndices());
for (const page of copiedPages) {
outputPdf.addPage(page);
}
}
await appendPdf(pdfDatenblatt);
await appendPdf(pdfAusweis);
const pdfBytes = await outputPdf.save();
const pdfCommand = new PutObjectCommand({
Bucket: "ibc-pdfs",
Key: dateiNameDruck,
Body: pdfBytes,
ACL: "private",
});
await s3Client.send(pdfCommand);
const sftp = new SFTPClient();
try {
await sftp.connect({
host: 'api.ppost.de',
username: 'jens.cornelsen@ib-cornelsen.de',
password: 'ANTQesWYjd',
});
const cwd = await sftp.cwd();
await sftp.put(Buffer.from(pdfBytes), join(cwd, "upload/api", dateiNameDruck));
} catch (err) {
console.error('SFTP Upload failed:', err);
throw new APIError({
code: "INTERNAL_SERVER_ERROR",
message: "Login zum Postversand Server war nicht erfolgreich."
});
} finally {
sftp.end();
}
}
let html: string;
if (rechnung.status === Enums.Rechnungsstatus.paid) {
html = `
<p>Sehr geehrte*r ${rechnung.empfaenger},</p>
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : ""} Den Rechnungsbetrag haben Sie bereits bezahlt. Vielen Dank.</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>`;
} else {
html = `
<p>Sehr geehrte*r ${rechnung.empfaenger},</p>
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${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>
<br>
<table>
<tr><td>Kreditinstitut</td><td>:</td><td>\t Commerzbank AG</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>BIC</td><td>:</td><td>\t COBADEFFXXX</td>
<tr><td>Betrag</td><td>:</td><td>\t <b>${rechnung.betrag}€</b></td>
<tr><td>Verwendungszweck</td><td>:</td><td>\t <b>${rechnung.lex_office_id}</b></td>
</table>
<br>
<br>
<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>`;
}
await transport.sendMail({
from: `"IBCornelsen" <info@online-energieausweis.org>`,
to: rechnung.email || rechnung.benutzer.email,
bcc: "info@online-energieausweis.org",
subject: `Ihr Originalausweis vom Ingenieurbüro Cornelsen (ID: ${ausweis.id})`,
html,
attachments: [{
filename: `ID_${ausweis.id}_Energieausweis.pdf`,
encoding: "binary",
content: Buffer.from(pdfAusweis),
contentType: "application/pdf",
contentDisposition: "attachment",
}, {
filename: `ID_${ausweis.id}_Datenblatt.pdf`,
encoding: "binary",
content: Buffer.from(pdfDatenblatt),
contentType: "application/pdf",
contentDisposition: "attachment",
}, {
filename: `ID_${ausweis.id}_Rechnung.pdf`,
encoding: "binary",
content: Buffer.from(pdfRechnung),
contentType: "application/pdf",
contentDisposition: "attachment",
}]
});
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
await prisma.verbrauchsausweisWohnen.update({
where: {
id: ausweis.id,
},
data: {
ausgestellt: true,
},
});
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
await prisma.verbrauchsausweisGewerbe.update({
where: {
id: ausweis.id,
},
data: {
ausgestellt: true,
},
});
} else if (ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) {
await prisma.bedarfsausweisWohnen.update({
where: {
id: ausweis.id,
},
data: {
ausgestellt: true,
},
});
}
}, 3000)
}, },
}); });

View File

@@ -194,9 +194,7 @@ export const GET = defineApiRoute({
.Registriernummer, .Registriernummer,
kontrolldatei_angefragt: result kontrolldatei_angefragt: result
.DatenregistraturResult .DatenregistraturResult
.WEB_Service_Antwort.Datendatei .WEB_Service_Antwort.Datendatei == 0 ? false : true,
? true
: false,
}, },
}); });

View File

@@ -3,16 +3,17 @@ import { adminMiddleware } from "#lib/middleware/authorization.js";
import { mollieClient } from "#lib/mollie.js"; import { mollieClient } from "#lib/mollie.js";
import { getPrismaAusweisAdapter } from "#lib/server/ausweis.js"; import { getPrismaAusweisAdapter } from "#lib/server/ausweis.js";
import { Prisma, prisma } from "#lib/server/prisma.js"; import { Prisma, prisma } from "#lib/server/prisma.js";
import { RefundStatus } from "@mollie/api-client";
import { APIError, defineApiRoute } from "astro-typesafe-api/server"; import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { z } from "zod"; import { z } from "zod";
export const PUT = defineApiRoute({ export const PUT = defineApiRoute({
input: z.object({ input: z.object({
uid_ausweis: UUidWithPrefix ausweis_id: UUidWithPrefix
}), }),
middleware: adminMiddleware, middleware: adminMiddleware,
async fetch(input, context, transfer) { async fetch(input, context, transfer) {
const adapter = getPrismaAusweisAdapter(input.uid_ausweis) as Prisma.VerbrauchsausweisWohnenDelegate; const adapter = getPrismaAusweisAdapter(input.ausweis_id) as Prisma.VerbrauchsausweisWohnenDelegate;
if (!adapter) { if (!adapter) {
throw new APIError({ throw new APIError({
@@ -23,7 +24,7 @@ export const PUT = defineApiRoute({
const ausweis = await adapter.findUnique({ const ausweis = await adapter.findUnique({
where: { where: {
uid: input.uid_ausweis id: input.ausweis_id
} }
}) })
@@ -36,7 +37,7 @@ export const PUT = defineApiRoute({
const response = await adapter.findUnique({ const response = await adapter.findUnique({
where: { where: {
uid: input.uid_ausweis id: input.ausweis_id
}, },
select: { select: {
rechnung: true rechnung: true
@@ -46,7 +47,7 @@ export const PUT = defineApiRoute({
if (!response || !response.rechnung) { if (!response || !response.rechnung) {
await adapter.update({ await adapter.update({
where: { where: {
uid: input.uid_ausweis id: input.ausweis_id
}, },
data: { data: {
storniert: true storniert: true
@@ -63,7 +64,7 @@ export const PUT = defineApiRoute({
await adapter.update({ await adapter.update({
where: { where: {
uid: input.uid_ausweis id: input.ausweis_id
}, },
data: { data: {
storniert: true, storniert: true,
@@ -94,10 +95,17 @@ export const PUT = defineApiRoute({
value: rechnung.betrag.toFixed(2) value: rechnung.betrag.toFixed(2)
}, },
metadata: { metadata: {
rechnung_uid: rechnung.uid rechnung_id: rechnung.id
}, },
testmode: true testmode: true
}) })
if (refund.status === RefundStatus.failed) {
throw new APIError({
code: "INTERNAL_SERVER_ERROR",
message: "Rückerstattung konnte nicht durchgeführt werden, Mollie hat die Rückerstattung abgelehnt."
})
}
} }
}, },
}) })

View File

@@ -30,7 +30,7 @@ export const GET = defineApiRoute({
// Falls der Nutzer nicht existiert, wird eine Fehlermeldung zurückgegeben. // Falls der Nutzer nicht existiert, wird eine Fehlermeldung zurückgegeben.
const user = await prisma.benutzer.findUnique({ const user = await prisma.benutzer.findUnique({
where: { where: {
email: input.email, email: input.email.toLowerCase(),
}, },
}); });

View File

@@ -1,5 +1,5 @@
import { z } from "zod"; import { z } from "zod";
import { prisma } from "#lib/server/prisma"; import { prisma } from "#lib/server/prisma.js";
import { APIError, defineApiRoute } from "astro-typesafe-api/server"; import { APIError, defineApiRoute } from "astro-typesafe-api/server";
export const GET = defineApiRoute({ export const GET = defineApiRoute({

View File

@@ -65,6 +65,9 @@ export const PUT = defineApiRoute({
const ausweis = await adapter.findUnique({ const ausweis = await adapter.findUnique({
where: { where: {
id: ausweis_id id: ausweis_id
},
include: {
rechnung: true
} }
}) })
@@ -75,6 +78,13 @@ export const PUT = defineApiRoute({
}); });
} }
if (ausweis.rechnung) {
throw new APIError({
code: "BAD_REQUEST",
message: "Eine Rechnung für diesen Ausweis existiert bereits.",
});
}
if (ausweis.benutzer_id !== user.id) { if (ausweis.benutzer_id !== user.id) {
throw new APIError({ throw new APIError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",

View File

@@ -1,4 +1,5 @@
import { UUidWithPrefix } from "#components/Ausweis/types.js"; import { UUidWithPrefix } from "#components/Ausweis/types.js";
import { VALID_UUID_PREFIXES } from "#lib/constants.js";
import { generatePrefixedId } from "#lib/db.js"; import { generatePrefixedId } from "#lib/db.js";
import { authorizationHeaders, authorizationMiddleware } from "#lib/middleware/authorization.js"; import { authorizationHeaders, authorizationMiddleware } from "#lib/middleware/authorization.js";
import { prisma } from "#lib/server/prisma.js"; import { prisma } from "#lib/server/prisma.js";
@@ -52,7 +53,7 @@ export const PUT = defineApiRoute({
}) })
} }
const id = generatePrefixedId(9, "VW"); const id = generatePrefixedId(9, VALID_UUID_PREFIXES.VerbrauchsausweisWohnen);
const createdAusweis = await prisma.verbrauchsausweisWohnen.create({ const createdAusweis = await prisma.verbrauchsausweisWohnen.create({
data: { data: {

View File

@@ -20,7 +20,7 @@ export const GET: APIRoute = async (Astro) => {
const file = await getS3File("ibc-images", `${image.id}.jpg`); const file = await getS3File("ibc-images", `${image.id}.jpg`);
if (!file) { if (!file) {
const file = await getS3File("ibc-images", `${image.name}.jpg`) const file = await getS3File("ibc-images", image.name)
if (!file) { if (!file) {
return new Response(null, { status: 404 }) return new Response(null, { status: 404 })

View File

@@ -83,7 +83,10 @@ if (id) {
unterlagen: true, unterlagen: true,
bedarfsausweise_wohnen: true, bedarfsausweise_wohnen: true,
verbrauchsausweise_gewerbe: true, verbrauchsausweise_gewerbe: true,
verbrauchsausweise_wohnen: true verbrauchsausweise_wohnen: true,
bedarfsausweise_gewerbe: true,
geg_nachweise_gewerbe: true,
geg_nachweise_wohnen: true
} }
} }
}, },
@@ -145,7 +148,10 @@ if (id) {
unterlagen: true, unterlagen: true,
bedarfsausweise_wohnen: true, bedarfsausweise_wohnen: true,
verbrauchsausweise_gewerbe: true, verbrauchsausweise_gewerbe: true,
verbrauchsausweise_wohnen: true verbrauchsausweise_wohnen: true,
bedarfsausweise_gewerbe: true,
geg_nachweise_gewerbe: true,
geg_nachweise_wohnen: true
} }
} }
}, },

View File

@@ -5,7 +5,7 @@ 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 } from "#lib/server/user";
import { getAusweisartFromId } from "#components/Ausweis/types"; import { getAusweisartFromId } from "#components/Ausweis/types";
import { getAufnahme, getBedarfsausweisWohnen, getBilder, getObjekt, getUnterlagen, getVerbrauchsausweisGewerbe, getVerbrauchsausweisWohnen } from "#lib/server/db"; import { getAufnahme, getBedarfsausweisWohnen, getBilder, getObjekt, getRechnung, getUnterlagen, getVerbrauchsausweisGewerbe, getVerbrauchsausweisWohnen } from "#lib/server/db";
// 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.
@@ -15,7 +15,7 @@ const user = await getCurrentUser(Astro) || {}
const params = new URLSearchParams(await Astro.request.text()); const params = new URLSearchParams(await Astro.request.text());
const searchParams = Astro.url.searchParams; const searchParams = Astro.url.searchParams;
let ausweis, aufnahme, objekt, ausweisart, bilder, unterlagen, partner_code; let ausweis, aufnahme, objekt, ausweisart, bilder, unterlagen, partner_code, rechnung = null;
if (!params.has("ausweis") || !params.has("aufnahme") || !params.has("objekt") || !params.has("bilder") || !params.has("ausweisart")) { if (!params.has("ausweis") || !params.has("aufnahme") || !params.has("objekt") || !params.has("bilder") || !params.has("ausweisart")) {
// Rechnung und Ausweis als GET parameter // Rechnung und Ausweis als GET parameter
@@ -40,6 +40,9 @@ if (!params.has("ausweis") || !params.has("aufnahme") || !params.has("objekt") |
objekt = await getObjekt(aufnahme?.objekt_id) objekt = await getObjekt(aufnahme?.objekt_id)
bilder = await getBilder(ausweis.aufnahme_id) bilder = await getBilder(ausweis.aufnahme_id)
unterlagen = await getUnterlagen(ausweis.aufnahme_id) unterlagen = await getUnterlagen(ausweis.aufnahme_id)
if (ausweis.rechnung_id) {
rechnung = await getRechnung(ausweis.rechnung_id)
}
} else { } else {
// Nichts ist vorhanden // Nichts ist vorhanden
return Astro.redirect("/404") return Astro.redirect("/404")
@@ -65,6 +68,6 @@ if (!params.has("ausweis") || !params.has("aufnahme") || !params.has("objekt") |
--- ---
<AusweisLayout title="Kundendaten Aufnehmen - IBCornelsen"> <AusweisLayout title="Kundendaten Aufnehmen - IBCornelsen">
<KundendatenModule {user} {ausweis} {objekt} {aufnahme} {bilder} {ausweisart} {unterlagen} {partner_code} aktiveBezahlmethode={Enums.Bezahlmethoden.paypal} client:only ></KundendatenModule> <KundendatenModule {user} {ausweis} {objekt} {aufnahme} {bilder} {rechnung} {ausweisart} {unterlagen} {partner_code} aktiveBezahlmethode={Enums.Bezahlmethoden.paypal} client:only ></KundendatenModule>
</AusweisLayout> </AusweisLayout>

View File

@@ -2,11 +2,11 @@ import { BenutzerClient, getAusweisartFromId, VerbrauchsausweisGewerbeClient, Ve
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js"; import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
import { pdfDatenblattVerbrauchsausweisGewerbe } from "#lib/pdf/pdfDatenblattVerbrauchsausweisGewerbe.js"; import { pdfDatenblattVerbrauchsausweisGewerbe } from "#lib/pdf/pdfDatenblattVerbrauchsausweisGewerbe.js";
import { pdfDatenblattVerbrauchsausweisWohnen } from "#lib/pdf/pdfDatenblattVerbrauchsausweisWohnen.js"; import { pdfDatenblattVerbrauchsausweisWohnen } from "#lib/pdf/pdfDatenblattVerbrauchsausweisWohnen.js";
import { Aufnahme, Benutzer, Bild, Enums, Objekt, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "#lib/client/prisma.js"; import { Aufnahme, Benutzer, Bild, Enums, Objekt, Rechnung, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "#lib/client/prisma.js";
import { APIRoute } from "astro"; import { APIRoute } from "astro";
import { createCaller } from "src/astro-typesafe-api-caller.js"; import { createCaller } from "src/astro-typesafe-api-caller.js";
import { getS3File } from "#lib/s3.js"; import { getS3File } from "#lib/s3.js";
import { getAufnahme, getBilder, getObjekt, getVerbrauchsausweisGewerbe, getVerbrauchsausweisWohnen } from "#lib/server/db.js"; import { getAufnahme, getBilder, getObjekt, getRechnung, getVerbrauchsausweisGewerbe, getVerbrauchsausweisWohnen } from "#lib/server/db.js";
import { getCurrentUser } from "#lib/server/user.js"; import { getCurrentUser } from "#lib/server/user.js";
export const GET: APIRoute = async (Astro) => { export const GET: APIRoute = async (Astro) => {
@@ -21,8 +21,8 @@ export const GET: APIRoute = async (Astro) => {
let ausweis: VerbrauchsausweisWohnen | VerbrauchsausweisGewerbe | null = null; let ausweis: VerbrauchsausweisWohnen | VerbrauchsausweisGewerbe | null = null;
let aufnahme: Aufnahme = {} as Aufnahme; let aufnahme: Aufnahme = {} as Aufnahme;
let objekt: Objekt = {} as Objekt; let objekt: Objekt = {} as Objekt;
let user: Benutzer = {} as Benutzer;
let bilder: Bild[] = [] let bilder: Bild[] = []
let rechnung: Rechnung | null = null;
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) { if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
ausweis = await getVerbrauchsausweisWohnen(ausweis_id) ausweis = await getVerbrauchsausweisWohnen(ausweis_id)
@@ -41,7 +41,9 @@ export const GET: APIRoute = async (Astro) => {
bilder = await getBilder(ausweis.aufnahme_id) bilder = await getBilder(ausweis.aufnahme_id)
user = await getCurrentUser(Astro) if (ausweis.rechnung_id) {
rechnung = await getRechnung(ausweis.rechnung_id)
}
let pdf: Uint8Array<ArrayBufferLike> | null = null; let pdf: Uint8Array<ArrayBufferLike> | null = null;
if (/[A-Z]{2}[0-9]{8}/.test(ausweis.id)) { if (/[A-Z]{2}[0-9]{8}/.test(ausweis.id)) {
@@ -49,9 +51,9 @@ export const GET: APIRoute = async (Astro) => {
// 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]}_Datenblatt.pdf`) pdf = await getS3File("ibc-pdfs", `ID_${id[1]}_Datenblatt.pdf`)
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) { } else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
pdf = await pdfDatenblattVerbrauchsausweisWohnen(ausweis as VerbrauchsausweisWohnenClient, aufnahme, objekt, user, bilder); pdf = await pdfDatenblattVerbrauchsausweisWohnen(ausweis as VerbrauchsausweisWohnenClient, aufnahme, objekt, rechnung, bilder);
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) { } else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
pdf = await pdfDatenblattVerbrauchsausweisGewerbe(ausweis as VerbrauchsausweisGewerbeClient, aufnahme, objekt, user, bilder); pdf = await pdfDatenblattVerbrauchsausweisGewerbe(ausweis as VerbrauchsausweisGewerbeClient, aufnahme, objekt, rechnung, bilder);
} }
return new Response(pdf, { return new Response(pdf, {
@@ -65,31 +67,17 @@ export const POST: APIRoute = async (Astro) => {
const body = await Astro.request.text(); const body = await Astro.request.text();
const params = new URLSearchParams(body); const params = new URLSearchParams(body);
const caller = createCaller(Astro);
const ausweis = JSON.parse(params.get("ausweis") || "{}"); const ausweis = JSON.parse(params.get("ausweis") || "{}");
const aufnahme = JSON.parse(params.get("aufnahme") || "{}"); const aufnahme = JSON.parse(params.get("aufnahme") || "{}");
const objekt = JSON.parse(params.get("objekt") || "{}"); const objekt = JSON.parse(params.get("objekt") || "{}");
const bilder = JSON.parse(params.get("bilder") || "{}"); const bilder = JSON.parse(params.get("bilder") || "{}");
const ausweisart: Enums.Ausweisart = JSON.parse(params.get("ausweisart") || "") const ausweisart: Enums.Ausweisart = JSON.parse(params.get("ausweisart") || "")
let user: BenutzerClient = {};
try {
user = await caller.user.self.GET.fetch(undefined, {
headers: {
Authorization: `Bearer ${Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value}`
}
});
} catch (e) {
}
let pdf: Uint8Array<ArrayBufferLike> | null = null; let pdf: Uint8Array<ArrayBufferLike> | null = null;
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) { if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
pdf = await pdfDatenblattVerbrauchsausweisWohnen(ausweis, aufnahme, objekt, user, bilder); pdf = await pdfDatenblattVerbrauchsausweisWohnen(ausweis, aufnahme, objekt, null, bilder);
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) { } else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
pdf = await pdfDatenblattVerbrauchsausweisGewerbe(ausweis, aufnahme, objekt, user, bilder); pdf = await pdfDatenblattVerbrauchsausweisGewerbe(ausweis, aufnahme, objekt, null, bilder);
} }
return new Response(pdf, { return new Response(pdf, {

52
wipe-database.bash Normal file
View File

@@ -0,0 +1,52 @@
#!/bin/bash
set -e
# Config
CONTAINER_NAME="online-energieausweis-database-1"
DB_USER="main"
DB_NAME="main"
TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
FILE_NAME="backup-$TIMESTAMP.sql.br"
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 "Um fortzufahren, tippe exakt: \"$REQUIRED_CONFIRMATION\""
echo
read -p "> " USER_CONFIRMATION
if [[ "$USER_CONFIRMATION" != "$REQUIRED_CONFIRMATION" ]]; then
echo "❌ Falsche Eingabe. Abbruch."
exit 1
fi
echo "📦 Backup wird erstellt..."
docker exec -t "$CONTAINER_NAME" pg_dumpall -c -U "$DB_USER" | brotli > "$FILE_NAME"
echo "✅ Backup abgeschlossen: $FILE_NAME"
echo "🧨 Alle Daten aus allen Tabellen werden gelöscht..."
# Generate and run TRUNCATE statements for all tables in the public schema
docker exec -i "$CONTAINER_NAME" psql -U "$DB_USER" "$DB_NAME" <<'EOSQL'
DO $$
DECLARE
r RECORD;
sql TEXT := '';
BEGIN
FOR r IN
SELECT tablename
FROM pg_tables
WHERE schemaname = 'public'
LOOP
sql := sql || FORMAT('TRUNCATE TABLE public.%I CASCADE;', r.tablename);
END LOOP;
EXECUTE sql;
END
$$;
EOSQL
echo "✅ Alle Tabellen gelöscht und Schema zurückgesetzt."
echo "🚀 Datenbankbereinigung abgeschlossen."