Website-Stand vom FTP-Server synchronisieren

This commit is contained in:
2026-09-02 19:24:09 +02:00
parent fd320ba0c6
commit 2c0e092e67
27 changed files with 267 additions and 721 deletions
+1
View File
@@ -0,0 +1 @@
00077aa09a2f4a21e4af14c732f37ef15feb6c38
View File
+5
View File
@@ -31,3 +31,8 @@
.vscode/ftp-sync.json
.vscode/sftp.json
sync_config.jsonc
# Patient uploads are runtime data, never source-control content.
/upload/*/
+25
View File
@@ -0,0 +1,25 @@
#php_value default_charset ?UTF-8?
#php_value mbstring.language UTF-8
#php_value mbstring.internal_encoding UTF-8
#php_value mbstring.http_output UTF-8
#php_value mbstring.http_output UTF-8
#Options +FollowSymlinks
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+)$ /$1.php
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>
+15
View File
@@ -0,0 +1,15 @@
<IfModule mod_rewrite.c>
RewriteEngine off
</IfModule>
# Require all granted
Satisfy Any
Order Deny,Allow
Allow from all
<FilesMatch "^\.">
# Require all denied
Order Allow,Deny
Deny from all
</FilesMatch>
@@ -0,0 +1 @@
_FSqA3-cKWlxMr6kSntJ4ylybPWmFbUu4bn2JmQ-eaI.vmGDXlVdcMc4EthdhP9VWKal6uwIIFdAaw45JAOlmK8
View File
-192
View File
@@ -1,192 +0,0 @@
-- Prueft die fuer den aktuellen Impf-Workflow relevanten Tabellen, Spalten,
-- Indizes und den Legacy-Migrationsstatus.
-- Das Skript veraendert keine Daten.
SELECT 'database' AS check_type, DATABASE() AS object_name, 'INFO' AS status;
SELECT
'table' AS check_type,
t.required_name AS object_name,
CASE
WHEN it.table_name IS NOT NULL THEN 'OK'
ELSE 'MISSING'
END AS status
FROM (
SELECT 'impf_workflow_meta' AS required_name
UNION ALL SELECT 'impfstoff_workflow'
UNION ALL SELECT 'impfstoff_wochenplan'
UNION ALL SELECT 'impf_zeitraum'
UNION ALL SELECT 'impf_zeitraum_impfstoff'
UNION ALL SELECT 'warteliste_zeitraum'
UNION ALL SELECT 'warteliste'
) t
LEFT JOIN information_schema.tables it
ON it.table_schema = DATABASE()
AND it.table_name = t.required_name
ORDER BY t.required_name;
SELECT
'column' AS check_type,
CONCAT(c.table_name, '.', c.column_name) AS object_name,
CASE
WHEN ic.column_name IS NOT NULL THEN 'OK'
ELSE 'MISSING'
END AS status
FROM (
SELECT 'impf_workflow_meta' AS table_name, 'meta_key' AS column_name
UNION ALL SELECT 'impf_workflow_meta', 'meta_value'
UNION ALL SELECT 'impf_workflow_meta', 'updated_at'
UNION ALL SELECT 'impfstoff_workflow', 'impfstoff_id'
UNION ALL SELECT 'impfstoff_workflow', 'dosen_pro_flasche'
UNION ALL SELECT 'impfstoff_wochenplan', 'plan_id'
UNION ALL SELECT 'impfstoff_wochenplan', 'impfstoff_id'
UNION ALL SELECT 'impfstoff_wochenplan', 'wochentag'
UNION ALL SELECT 'impfstoff_wochenplan', 'start'
UNION ALL SELECT 'impfstoff_wochenplan', 'ende'
UNION ALL SELECT 'impfstoff_wochenplan', 'impfortid'
UNION ALL SELECT 'impf_zeitraum', 'zeitraum_id'
UNION ALL SELECT 'impf_zeitraum', 'bezeichnung'
UNION ALL SELECT 'impf_zeitraum', 'wochentag'
UNION ALL SELECT 'impf_zeitraum', 'start'
UNION ALL SELECT 'impf_zeitraum', 'ende'
UNION ALL SELECT 'impf_zeitraum', 'impfortid'
UNION ALL SELECT 'impf_zeitraum_impfstoff', 'zeitraum_id'
UNION ALL SELECT 'impf_zeitraum_impfstoff', 'impfstoff_id'
UNION ALL SELECT 'warteliste_zeitraum', 'warteid'
UNION ALL SELECT 'warteliste_zeitraum', 'zeitraum_id'
UNION ALL SELECT 'warteliste_zeitraum', 'created_at'
UNION ALL SELECT 'warteliste', 'warteid'
UNION ALL SELECT 'warteliste', 'userid'
UNION ALL SELECT 'warteliste', 'impfenzeitraum'
UNION ALL SELECT 'warteliste', 'zeitraum_id'
) c
LEFT JOIN information_schema.columns ic
ON ic.table_schema = DATABASE()
AND ic.table_name = c.table_name
AND ic.column_name = c.column_name
ORDER BY c.table_name, c.column_name;
SELECT
'index' AS check_type,
CONCAT(i.table_name, '.', i.index_name) AS object_name,
CASE
WHEN isx.index_name IS NOT NULL THEN 'OK'
ELSE 'MISSING'
END AS status
FROM (
SELECT 'warteliste' AS table_name, 'idx_warteliste_zeitraum' AS index_name
UNION ALL SELECT 'warteliste_zeitraum', 'idx_warteliste_zeitraum_zeitraum'
UNION ALL SELECT 'impfstoff_wochenplan', 'idx_impfstoff_wochenplan_impfstoff'
UNION ALL SELECT 'impfstoff_wochenplan', 'idx_impfstoff_wochenplan_wochentag'
UNION ALL SELECT 'impf_zeitraum', 'idx_impf_zeitraum_wochentag'
UNION ALL SELECT 'impf_zeitraum', 'idx_impf_zeitraum_impfort'
UNION ALL SELECT 'impf_zeitraum_impfstoff', 'idx_impf_zeitraum_impfstoff_impfstoff'
) i
LEFT JOIN information_schema.statistics isx
ON isx.table_schema = DATABASE()
AND isx.table_name = i.table_name
AND isx.index_name = i.index_name
GROUP BY i.table_name, i.index_name, isx.index_name
ORDER BY i.table_name, i.index_name;
SELECT
'meta' AS check_type,
'impf_workflow_meta.legacy_wochenplan_migrated' AS object_name,
CASE
WHEN EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'impf_workflow_meta'
) THEN COALESCE((
SELECT CONCAT('VALUE=', meta_value)
FROM impf_workflow_meta
WHERE meta_key = 'legacy_wochenplan_migrated'
LIMIT 1
), 'MISSING')
ELSE 'TABLE_MISSING'
END AS status;
SELECT
'meta' AS check_type,
'impf_workflow_meta.legacy_warteliste_zeitraeume_migrated' AS object_name,
CASE
WHEN EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'impf_workflow_meta'
) THEN COALESCE((
SELECT CONCAT('VALUE=', meta_value)
FROM impf_workflow_meta
WHERE meta_key = 'legacy_warteliste_zeitraeume_migrated'
LIMIT 1
), 'MISSING')
ELSE 'TABLE_MISSING'
END AS status;
SELECT
'data' AS check_type,
'impfstoff_wochenplan rows' AS object_name,
CASE
WHEN EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'impfstoff_wochenplan'
) THEN CAST((SELECT COUNT(*) FROM impfstoff_wochenplan) AS CHAR)
ELSE 'TABLE_MISSING'
END AS status
UNION ALL
SELECT
'data' AS check_type,
'impf_zeitraum rows' AS object_name,
CASE
WHEN EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'impf_zeitraum'
) THEN CAST((SELECT COUNT(*) FROM impf_zeitraum) AS CHAR)
ELSE 'TABLE_MISSING'
END AS status
UNION ALL
SELECT
'data' AS check_type,
'impf_zeitraum_impfstoff rows' AS object_name,
CASE
WHEN EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'impf_zeitraum_impfstoff'
) THEN CAST((SELECT COUNT(*) FROM impf_zeitraum_impfstoff) AS CHAR)
ELSE 'TABLE_MISSING'
END AS status
UNION ALL
SELECT
'data' AS check_type,
'warteliste with zeitraum_id' AS object_name,
CASE
WHEN EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'warteliste'
AND column_name = 'zeitraum_id'
) THEN CAST((SELECT COUNT(*) FROM warteliste WHERE zeitraum_id IS NOT NULL) AS CHAR)
ELSE 'COLUMN_MISSING'
END AS status;
SELECT
'data' AS check_type,
'warteliste_zeitraum rows' AS object_name,
CASE
WHEN EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'warteliste_zeitraum'
) THEN CAST((SELECT COUNT(*) FROM warteliste_zeitraum) AS CHAR)
ELSE 'TABLE_MISSING'
END AS status;
-217
View File
@@ -1,217 +0,0 @@
-- Bringt den SQL-Dump auf den vom aktuellen Quellcode erwarteten Stand.
-- Die Migration ist idempotent und kann mehrfach ausgefuehrt werden.
CREATE TABLE IF NOT EXISTS `impf_workflow_meta` (
`meta_key` VARCHAR(100) NOT NULL,
`meta_value` VARCHAR(255) NOT NULL DEFAULT '',
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`meta_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `impfstoff_workflow` (
`impfstoff_id` INT NOT NULL,
`dosen_pro_flasche` INT NOT NULL,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`impfstoff_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `impfstoff_wochenplan` (
`plan_id` INT NOT NULL AUTO_INCREMENT,
`impfstoff_id` INT NOT NULL,
`wochentag` TINYINT NOT NULL,
`start` TIME NOT NULL,
`ende` TIME NOT NULL,
`impfortid` INT NOT NULL,
`aktiv` TINYINT NOT NULL DEFAULT 1,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`plan_id`),
INDEX `idx_impfstoff_wochenplan_impfstoff` (`impfstoff_id`),
INDEX `idx_impfstoff_wochenplan_wochentag` (`wochentag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `impf_zeitraum` (
`zeitraum_id` INT NOT NULL AUTO_INCREMENT,
`bezeichnung` VARCHAR(120) NOT NULL DEFAULT '',
`wochentag` TINYINT NOT NULL,
`start` TIME NOT NULL,
`ende` TIME NOT NULL,
`impfortid` INT NOT NULL,
`aktiv` TINYINT NOT NULL DEFAULT 1,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`zeitraum_id`),
INDEX `idx_impf_zeitraum_wochentag` (`wochentag`),
INDEX `idx_impf_zeitraum_impfort` (`impfortid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `impf_zeitraum_impfstoff` (
`zeitraum_id` INT NOT NULL,
`impfstoff_id` INT NOT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`zeitraum_id`, `impfstoff_id`),
INDEX `idx_impf_zeitraum_impfstoff_impfstoff` (`impfstoff_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `warteliste_zeitraum` (
`warteid` INT NOT NULL,
`zeitraum_id` INT NOT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`warteid`, `zeitraum_id`),
INDEX `idx_warteliste_zeitraum_zeitraum` (`zeitraum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
DROP PROCEDURE IF EXISTS `migrate_praxis_schema_20260320`;
DELIMITER $$
CREATE PROCEDURE `migrate_praxis_schema_20260320`()
BEGIN
DECLARE v_impf_zeitraum_exists INT DEFAULT 0;
DECLARE v_bezeichnung_exists INT DEFAULT 0;
DECLARE v_warteliste_exists INT DEFAULT 0;
DECLARE v_zeitraum_id_exists INT DEFAULT 0;
DECLARE v_warteliste_index_exists INT DEFAULT 0;
DECLARE v_warteliste_zeitraum_exists INT DEFAULT 0;
DECLARE v_warteliste_zeitraum_index_exists INT DEFAULT 0;
DECLARE v_legacy_plan_exists INT DEFAULT 0;
SELECT COUNT(*)
INTO v_impf_zeitraum_exists
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'impf_zeitraum';
IF v_impf_zeitraum_exists > 0 THEN
SELECT COUNT(*)
INTO v_bezeichnung_exists
FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'impf_zeitraum'
AND column_name = 'bezeichnung';
IF v_bezeichnung_exists = 0 THEN
ALTER TABLE `impf_zeitraum`
ADD COLUMN `bezeichnung` VARCHAR(120) NOT NULL DEFAULT '' AFTER `zeitraum_id`;
END IF;
END IF;
SELECT COUNT(*)
INTO v_warteliste_exists
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'warteliste';
IF v_warteliste_exists > 0 THEN
SELECT COUNT(*)
INTO v_zeitraum_id_exists
FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'warteliste'
AND column_name = 'zeitraum_id';
IF v_zeitraum_id_exists = 0 THEN
ALTER TABLE `warteliste`
ADD COLUMN `zeitraum_id` INT NULL AFTER `impfenzeitraum`;
END IF;
SELECT COUNT(*)
INTO v_warteliste_index_exists
FROM information_schema.statistics
WHERE table_schema = DATABASE()
AND table_name = 'warteliste'
AND index_name = 'idx_warteliste_zeitraum';
IF v_warteliste_index_exists = 0 THEN
ALTER TABLE `warteliste`
ADD INDEX `idx_warteliste_zeitraum` (`zeitraum_id`);
END IF;
END IF;
SELECT COUNT(*)
INTO v_warteliste_zeitraum_exists
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'warteliste_zeitraum';
IF v_warteliste_zeitraum_exists = 0 THEN
CREATE TABLE `warteliste_zeitraum` (
`warteid` INT NOT NULL,
`zeitraum_id` INT NOT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`warteid`, `zeitraum_id`),
INDEX `idx_warteliste_zeitraum_zeitraum` (`zeitraum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
SET v_warteliste_zeitraum_exists = 1;
ELSE
SELECT COUNT(*)
INTO v_warteliste_zeitraum_index_exists
FROM information_schema.statistics
WHERE table_schema = DATABASE()
AND table_name = 'warteliste_zeitraum'
AND index_name = 'idx_warteliste_zeitraum_zeitraum';
IF v_warteliste_zeitraum_index_exists = 0 THEN
ALTER TABLE `warteliste_zeitraum`
ADD INDEX `idx_warteliste_zeitraum_zeitraum` (`zeitraum_id`);
END IF;
END IF;
SELECT COUNT(*)
INTO v_legacy_plan_exists
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'impfstoff_wochenplan';
IF v_legacy_plan_exists > 0 AND v_impf_zeitraum_exists > 0 THEN
INSERT INTO `impf_zeitraum` (`bezeichnung`, `wochentag`, `start`, `ende`, `impfortid`, `aktiv`)
SELECT DISTINCT
'',
wp.`wochentag`,
wp.`start`,
wp.`ende`,
wp.`impfortid`,
wp.`aktiv`
FROM `impfstoff_wochenplan` wp
LEFT JOIN `impf_zeitraum` z
ON z.`wochentag` = wp.`wochentag`
AND z.`start` = wp.`start`
AND z.`ende` = wp.`ende`
AND z.`impfortid` = wp.`impfortid`
AND z.`aktiv` = wp.`aktiv`
WHERE z.`zeitraum_id` IS NULL;
INSERT INTO `impf_zeitraum_impfstoff` (`zeitraum_id`, `impfstoff_id`)
SELECT DISTINCT
z.`zeitraum_id`,
wp.`impfstoff_id`
FROM `impfstoff_wochenplan` wp
INNER JOIN `impf_zeitraum` z
ON z.`wochentag` = wp.`wochentag`
AND z.`start` = wp.`start`
AND z.`ende` = wp.`ende`
AND z.`impfortid` = wp.`impfortid`
AND z.`aktiv` = wp.`aktiv`
LEFT JOIN `impf_zeitraum_impfstoff` m
ON m.`zeitraum_id` = z.`zeitraum_id`
AND m.`impfstoff_id` = wp.`impfstoff_id`
WHERE wp.`impfstoff_id` > 0
AND m.`zeitraum_id` IS NULL;
INSERT INTO `impf_workflow_meta` (`meta_key`, `meta_value`)
VALUES ('legacy_wochenplan_migrated', '1') AS `incoming`
ON DUPLICATE KEY UPDATE `meta_value` = `incoming`.`meta_value`;
END IF;
IF v_warteliste_exists > 0 AND v_warteliste_zeitraum_exists > 0 THEN
INSERT IGNORE INTO `warteliste_zeitraum` (`warteid`, `zeitraum_id`)
SELECT `warteid`, `zeitraum_id`
FROM `warteliste`
WHERE `zeitraum_id` IS NOT NULL
AND `zeitraum_id` > 0;
INSERT INTO `impf_workflow_meta` (`meta_key`, `meta_value`)
VALUES ('legacy_warteliste_zeitraeume_migrated', '1') AS `incoming`
ON DUPLICATE KEY UPDATE `meta_value` = `incoming`.`meta_value`;
END IF;
END $$
DELIMITER ;
CALL `migrate_praxis_schema_20260320`();
DROP PROCEDURE IF EXISTS `migrate_praxis_schema_20260320`;
@@ -1,99 +0,0 @@
-- Migration fuer Mehrfach-Zeitfenster in der Impfwarteliste.
-- Idempotent: kann mehrfach ausgefuehrt werden.
-- Bestehende Tabellen werden nicht neu aufgebaut, sondern nur erweitert.
CREATE TABLE IF NOT EXISTS `impf_workflow_meta` (
`meta_key` VARCHAR(100) NOT NULL,
`meta_value` VARCHAR(255) NOT NULL DEFAULT '',
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`meta_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
DROP PROCEDURE IF EXISTS `migrate_warteliste_multi_zeitfenster_20260322`;
DELIMITER $$
CREATE PROCEDURE `migrate_warteliste_multi_zeitfenster_20260322`()
BEGIN
DECLARE v_warteliste_exists INT DEFAULT 0;
DECLARE v_zeitraum_id_exists INT DEFAULT 0;
DECLARE v_warteliste_index_exists INT DEFAULT 0;
DECLARE v_warteliste_zeitraum_exists INT DEFAULT 0;
DECLARE v_warteliste_zeitraum_index_exists INT DEFAULT 0;
SELECT COUNT(*)
INTO v_warteliste_exists
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'warteliste';
IF v_warteliste_exists = 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Tabelle warteliste wurde nicht gefunden.';
END IF;
SELECT COUNT(*)
INTO v_zeitraum_id_exists
FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'warteliste'
AND column_name = 'zeitraum_id';
IF v_zeitraum_id_exists = 0 THEN
ALTER TABLE `warteliste`
ADD COLUMN `zeitraum_id` INT NULL AFTER `impfenzeitraum`;
END IF;
SELECT COUNT(*)
INTO v_warteliste_index_exists
FROM information_schema.statistics
WHERE table_schema = DATABASE()
AND table_name = 'warteliste'
AND index_name = 'idx_warteliste_zeitraum';
IF v_warteliste_index_exists = 0 THEN
ALTER TABLE `warteliste`
ADD INDEX `idx_warteliste_zeitraum` (`zeitraum_id`);
END IF;
SELECT COUNT(*)
INTO v_warteliste_zeitraum_exists
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'warteliste_zeitraum';
IF v_warteliste_zeitraum_exists = 0 THEN
CREATE TABLE `warteliste_zeitraum` (
`warteid` INT NOT NULL,
`zeitraum_id` INT NOT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`warteid`, `zeitraum_id`),
INDEX `idx_warteliste_zeitraum_zeitraum` (`zeitraum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
SET v_warteliste_zeitraum_exists = 1;
END IF;
SELECT COUNT(*)
INTO v_warteliste_zeitraum_index_exists
FROM information_schema.statistics
WHERE table_schema = DATABASE()
AND table_name = 'warteliste_zeitraum'
AND index_name = 'idx_warteliste_zeitraum_zeitraum';
IF v_warteliste_zeitraum_index_exists = 0 THEN
ALTER TABLE `warteliste_zeitraum`
ADD INDEX `idx_warteliste_zeitraum_zeitraum` (`zeitraum_id`);
END IF;
INSERT IGNORE INTO `warteliste_zeitraum` (`warteid`, `zeitraum_id`)
SELECT `warteid`, `zeitraum_id`
FROM `warteliste`
WHERE `zeitraum_id` IS NOT NULL
AND `zeitraum_id` > 0;
INSERT INTO `impf_workflow_meta` (`meta_key`, `meta_value`)
VALUES ('legacy_warteliste_zeitraeume_migrated', '1') AS `incoming`
ON DUPLICATE KEY UPDATE `meta_value` = `incoming`.`meta_value`;
END $$
DELIMITER ;
CALL `migrate_warteliste_multi_zeitfenster_20260322`();
DROP PROCEDURE IF EXISTS `migrate_warteliste_multi_zeitfenster_20260322`;
@@ -1,24 +0,0 @@
CREATE TABLE IF NOT EXISTS time_error_notification_state (
employee_id INT NOT NULL,
cycle_started_on DATE NOT NULL,
first_error_date DATE NOT NULL,
last_notification_stage VARCHAR(50) DEFAULT NULL,
last_notification_sent_at DATETIME DEFAULT NULL,
employee_day_1_sent_at DATETIME DEFAULT NULL,
employee_day_3_sent_at DATETIME DEFAULT NULL,
admin_day_7_sent_at DATETIME DEFAULT NULL,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (employee_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS time_error_notifications (
id INT NOT NULL AUTO_INCREMENT,
employee_id INT NOT NULL,
cycle_started_on DATE NOT NULL,
notification_stage VARCHAR(50) NOT NULL,
recipient_email VARCHAR(255) NOT NULL,
sent_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uniq_time_error_notification (employee_id, cycle_started_on, notification_stage, recipient_email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-20
View File
@@ -1,20 +0,0 @@
CREATE TABLE IF NOT EXISTS impfstoff_workflow (
impfstoff_id INT NOT NULL,
dosen_pro_flasche INT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (impfstoff_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS impfstoff_wochenplan (
plan_id INT NOT NULL AUTO_INCREMENT,
impfstoff_id INT NOT NULL,
wochentag TINYINT NOT NULL,
start TIME NOT NULL,
ende TIME NOT NULL,
impfortid INT NOT NULL,
aktiv TINYINT NOT NULL DEFAULT 1,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (plan_id),
INDEX idx_impfstoff_wochenplan_impfstoff (impfstoff_id),
INDEX idx_impfstoff_wochenplan_wochentag (wochentag)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+1
View File
@@ -0,0 +1 @@
deny from all
+3
View File
@@ -0,0 +1,3 @@
{
"directory": "bower_components/"
}
+6
View File
@@ -0,0 +1,6 @@
# Auto detect text files and perform LF normalization
* text=auto
# SCSS and JS files must always use LF for tools to work
*.js eol=lf
*.scss eol=lf
+3
View File
@@ -0,0 +1,3 @@
github: Alex-D
patreon: AlexandreDemode
custom: https://paypal.me/demodealexandre
+20
View File
@@ -0,0 +1,20 @@
<!--- Provide a general summary of the issue in the Title above -->
### Description
<!--
Provide relevant details according to the context:
- OS
- Browser + version
- Screen resolution
- ...
-->
### How to reproduce?
<!--
If relevant, provide a link to a live example based on: https://jsfiddle.net/AlexandreDemode/p7hb3x6u/
- Edit
- Ctrl + S
- Copy/Paste the URL here
-->
+25
View File
@@ -0,0 +1,25 @@
# Others
~$*
/.sass-cache
/uploaded-files
/node_modules
/bower_components
/dist
/src/ui/sass/_sprite*
/plugins/**/ui/sass/_sprite*
# We use npm: ignore yarn
yarn.lock
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
# Mac crap
.DS_Store
# IDEA
.idea
/nbproject
+29
View File
@@ -0,0 +1,29 @@
{
"bitwise": true,
"camelcase": true,
"esnext": true,
"curly": true,
"eqeqeq": true,
"forin": true,
"indent": 4,
"latedef": false,
"newcap": false,
"noarg": true,
"noempty": true,
"plusplus": true,
"quotmark": "single",
"undef": true,
"unused": true,
"strict": true,
"trailing": true,
"maxparams": 5,
"maxdepth": 5,
"maxstatements": 40,
"maxcomplexity": 20,
"maxlen": 1200,
"eqnull": true,
"browser": true,
"globals": {
"jQuery": false
}
}
+15
View File
@@ -0,0 +1,15 @@
Gulpfile.js
banner.jpg
bower.json
src
docs
bower_components
.gitattributes
.gitignore
.jshintrc
.github
.idea
.bowerrc
sponsors
BACKERS.md
CONTRIBUTORS.md
+6
View File
@@ -0,0 +1,6 @@
/node_modules/
/bower_components
/dist
/src
/plugins
uploaded-files/
+31
View File
@@ -0,0 +1,31 @@
{
"bitwise": true,
"camelcase": true,
"esnext": true,
"curly": true,
"eqeqeq": true,
"forin": true,
"indent": 4,
"latedef": false,
"newcap": false,
"noarg": true,
"noempty": true,
"plusplus": true,
"quotmark": "single",
"undef": true,
"unused": true,
"strict": true,
"trailing": true,
"maxparams": 5,
"maxdepth": 5,
"maxstatements": 40,
"maxcomplexity": 20,
"maxlen": 1200,
"eqnull": true,
"browser": true,
"node": true,
"globals": {
"jQuery": false,
"hljs": false
}
}
+10 -30
View File
@@ -5,47 +5,27 @@ CALSCALE:GREGORIAN
METHOD:PUBLISH
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20251002
DTEND:20251005
DTSTART:20260420
DTEND:20260428
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20251020
DTEND:20251103
DTSTART:20260403
DTEND:20260407
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20251223
DTEND:20251228
DTSTART:20260612
DTEND:20260617
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20251230
DTEND:20260102
DTSTART:20260810
DTEND:20260822
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20251231
DTEND:20260102
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20251231
DTEND:20260101
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20260102
DTEND:20260111
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20260109
DTEND:20260110
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20260201
DTEND:20260204
DTSTART:20260911
DTEND:20260919
END:VEVENT
END:VCALENDAR
+10 -30
View File
@@ -5,47 +5,27 @@ CALSCALE:GREGORIAN
METHOD:PUBLISH
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20251002
DTEND:20251005
DTSTART:20260420
DTEND:20260428
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20251020
DTEND:20251103
DTSTART:20260403
DTEND:20260407
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20251223
DTEND:20251228
DTSTART:20260612
DTEND:20260617
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20251230
DTEND:20260102
DTSTART:20260810
DTEND:20260822
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20251231
DTEND:20260102
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20251231
DTEND:20260101
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20260102
DTEND:20260111
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20260109
DTEND:20260110
END:VEVENT
BEGIN:VEVENT
SUMMARY:Urlaub
DTSTART:20260201
DTEND:20260204
DTSTART:20260911
DTEND:20260919
END:VEVENT
END:VCALENDAR
+4
View File
@@ -0,0 +1,4 @@
RewriteEngine on
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+6 -66
View File
@@ -21,72 +21,12 @@ $pcmContent = websiteContentGetByTitle($pdo, 'PCM - Seiteninhalt');
<section class="box special features">
<span class="image featured"><img src="images/praxis1.jpg" alt="Praxis Creutzburg"></span>
<style>
.pcm-intro {
max-width: 860px;
margin: 0 auto 1.8rem auto;
text-align: center;
}
.pcm-intro .pcm-kicker {
display: inline-flex;
padding: 0.4rem 0.9rem;
margin-bottom: 0.9rem;
border-radius: 999px;
background: linear-gradient(135deg, rgba(233, 241, 255, 0.95), rgba(245, 248, 252, 0.95));
color: #29415f;
font-size: 0.8rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.pcm-intro h2 {
margin-bottom: 0.6rem;
}
.pcm-intro p {
font-size: 1.08rem;
line-height: 1.8;
color: #54626f;
}
.pcm-content {
max-width: 880px;
margin: 0 auto;
text-align: left;
}
.pcm-content h3 {
margin-top: 1.8rem;
color: #30404f;
}
.pcm-content ul {
margin: 0.8rem 0 0;
padding-left: 1.15rem;
line-height: 1.8;
color: #4e5860;
}
.pcm-callout {
margin: 1.3rem 0;
padding: 1rem 1.15rem;
border-left: 4px solid #6b8bb4;
background: #f7f9fc;
border-radius: 16px;
color: #44505d;
}
.pcm-content p {
line-height: 1.8;
color: #4e5860;
}
</style>
<div class="pcm-intro">
<span class="pcm-kicker">Primary Care Management</span>
<h2>PCM - die Qualifikation im Überblick</h2>
<p>PCM stärkt Medizinische Fachangestellte in medizinischen, organisatorischen und kommunikativen Aufgaben rund um die hausärztliche Versorgung. Die ausführliche Einordnung dazu steht im folgenden Inhalt.</p>
</div>
<div class="pcm-content">
<?php if (trim((string)($pcmContent['inhalt'] ?? '')) !== ''): ?>
<?php echo $pcmContent['inhalt']; ?>
<?php else: ?>
<p>Die Inhalte zur Qualifikation PCM werden derzeit vorbereitet.</p>
<?php endif; ?>
</div>
<h2>PCM - die Qualifikation im Überblick</h2>
<?php if (trim((string)($pcmContent['inhalt'] ?? '')) !== ''): ?>
<?php echo $pcmContent['inhalt']; ?>
<?php else: ?>
<p>Die Inhalte zur Qualifikation PCM werden derzeit vorbereitet.</p>
<?php endif; ?>
<p><a href="praxis.php#team" class="button alt">Zurück zum Team</a></p>
</section>
</section>
+51 -43
View File
@@ -192,15 +192,22 @@
<div class="team-profile__text">
<span class="team-profile__role">Medizinische Fachangestellte, Primary Care Management B.Sc.</span>
<h3 class="team-profile__name">Dalia Alayan-Ibrahim</h3>
<p class="team-profile__subtitle">Praxismanagerin, PCM B.Sc.</p>
<p>Dalia Alayan-Ibrahim übernimmt in unserer Praxis die Organisation der Abläufe und die Verantwortung für den Personalbereich. Sie sorgt mit dafür, dass Termine, Kommunikation und interne Abläufe gut ineinandergreifen und Patientinnen und Patienten sich jederzeit gut begleitet fühlen.</p>
<p>Mit ihrem abgeschlossenen Studium im Bereich Primary Care Management (Bachelor of Science) bringt sie zusätzlich vertiefte Kompetenzen in Praxisorganisation, koordinierter Versorgung und teamorientiertem Arbeiten mit. Darüber hinaus übernimmt sie alle delegierten Tätigkeiten, führt Patientengespräche und betreut eine eigene Sprechstunde im dafür vorgesehenen Rahmen. Das stärkt die Struktur im Alltag und schafft mehr Raum für eine verlässliche Betreuung.</p>
<p class="team-profile__subtitle">Praxismanagerin</p>
<p>Dalia Alayan-Ibrahim übernimmt in unserer Praxis als Praxisleitung die Organisation der Abläufe und die Verantwortung für den Personalbereich.
</p>
<p>Mit ihrem abgeschlossenen Studium im Bereich Primary Care Management (Bachelor of Science) bringt sie zusätzlich vertiefte Kompetenzen in Praxisorganisation,
koordinierter Versorgung und fachlicher Behandlung mit.
Sie übernimmt, über die normalen Tätigkeiten einer MFA hinaus, Patientengespräche und betreut Patienten in eine eigene Sprechstunde.
</p>
<ul class="team-profile__points">
<li>Praxisorganisation und Koordination der Abläufe</li>
<li>Personalverantwortung und strukturierte Teamarbeit</li>
<li>Delegierte Tätigkeiten, Patientengespräche und eigene Sprechstunde</li>
<li>Zusätzliche Stärke durch das PCM Studium</li>
<li>Jahrelange Erfahrung und verlässliche Begleitung</li>
<li>Patientengespräche und eigene Sprechstunde</li>
</ul>
<br>
<p><a href="pcm.php" class="button alt small">Mehr über PCM</a></p>
</div>
</div>
@@ -213,34 +220,14 @@
<div class="team-profile__text">
<span class="team-profile__role">Medizinische Fachangestellte</span>
<h3 class="team-profile__name">Finja Baruth</h3>
<p class="team-profile__subtitle">Anmeldung, Assistenz, Labortätigkeiten und Praxisorganisation</p>
<p>Finja Baruth unterstützt unser Team in der Patientenaufnahme, in der täglichen Assistenz sowie bei vielen organisatorischen Abläufen im Praxisalltag. So bleibt der Ablauf auch an lebhaften Tagen verlässlich, freundlich und gut strukturiert.</p>
<p class="team-profile__subtitle">Patientenaufnahme, Diagnostik, Labortätigkeiten und Praxisorganisation</p>
<p>Finja Baruth begleitet in unserer Praxis viele zentrale Abläufe von der Anmeldung bis zur Vorbereitung medizinischer Untersuchungen. Sie ist Ansprechperson für Patientinnen und Patienten, koordiniert Termine und Unterlagen, unterstützt bei diagnostischen Abläufen und trägt mit ihrer Arbeit dazu bei, dass Behandlungen strukturiert vorbereitet und im Alltag zuverlässig umgesetzt werden.</p>
<ul class="team-profile__points">
<li>Assistenz und Unterstützung im Behandlungsalltag</li>
<li>Organisation und Koordination an der Anmeldung</li>
<li>Labortätigkeiten und Vorbereitung diagnostischer Abläufe</li>
<li>Typische Standardaufgaben in einer modernen Arztpraxis</li>
<li>Verlässliche Begleitung für Patienten und Team</li>
</ul>
</div>
</div>
</section>
<section class="team-profile">
<div class="team-profile__body">
<div class="team-profile__media">
<img src="images/person-silhouette.svg" alt="Medizinische Fachangestellte">
</div>
<div class="team-profile__text">
<span class="team-profile__role">Medizinische Fachangestellte</span>
<h3 class="team-profile__name">Svenja Vespermann</h3>
<p class="team-profile__subtitle">Assistenz, Labortätigkeiten und verlässliche Praxisabläufe</p>
<p>Svenja Vespermann sorgt mit dafür, dass Vorbereitungen, organisatorische Aufgaben und die praktische Unterstützung im Alltag sauber ineinandergreifen. Das schafft Ruhe im Tagesgeschäft und hilft, Patientinnen und Patienten gut durch die Praxis zu begleiten.</p>
<ul class="team-profile__points">
<li>Unterstützung bei Praxisorganisation und Assistenz</li>
<li>Labortätigkeiten und Mitwirkung bei diagnostischen Standardabläufen</li>
<li>Sorgfältige Vor- und Nachbereitung im Alltag</li>
<li>Typische Aufgaben in Anmeldung, Organisation und Behandlungsablauf</li>
<li>Teamarbeit mit Blick auf einen freundlichen Ablauf</li>
<li>Empfang, Patientenaufnahme und Koordination an der Anmeldung</li>
<li>Vorbereitung und Begleitung diagnostischer Untersuchungen</li>
<li>Labortätigkeiten, Probenmanagement und organisatorische Nachbereitung</li>
<li>Unterstützung bei Behandlungsabläufen und medizinischen Routinen im Praxisalltag</li>
<li>Sorgfältige Abstimmung zwischen Patienten, Team und Terminorganisation</li>
</ul>
</div>
</div>
@@ -251,14 +238,34 @@
<img src="images/person-silhouette.svg" alt="Auszubildende zur MFA">
</div>
<div class="team-profile__text">
<span class="team-profile__role">Auszubildende zur Medizinische Fachangestellte</span>
<span class="team-profile__role">Auszubildende zur Medizinischen Fachangestellten</span>
<h3 class="team-profile__name">Promise Nnodim</h3>
<p class="team-profile__subtitle">Lernen, Mitwachsen und Praxisnähe</p>
<p>Unsere Auszubildende zur Medizinischen Fachangestellten lernt die Abläufe in der Praxis Schritt für Schritt kennen und wächst dabei in die vielfältigen Aufgaben einer modernen Hausarztpraxis hinein. Begleitung, Lernen und Praxisnähe stehen dabei im Vordergrund.</p>
<p class="team-profile__subtitle">Ausbildung mit Einblick in den gesamten Praxisalltag</p>
<p>Promise Nnodim lernt in ihrer Ausbildung die vielfältigen Abläufe unserer Hausarztpraxis Schritt für Schritt kennen. Dazu gehören der freundliche Umgang mit Patientinnen und Patienten, organisatorische Aufgaben an der Anmeldung sowie die praktische Unterstützung bei Untersuchungen und im täglichen Praxisbetrieb.</p>
<ul class="team-profile__points">
<li>Einblick in Anmeldung, Organisation und Assistenz</li>
<li>Begleitung durch erfahrene Kolleginnen und Kollegen</li>
<li>Frühe Verankerung im Team und in den Praxisabläufen</li>
<li>Einblicke in Anmeldung, Terminorganisation und Patientenbetreuung</li>
<li>Lernen und Mitarbeiten in Assistenz, Vorbereitung und Praxisorganisation</li>
<li>Begleitung durch erfahrene Kolleginnen im täglichen Ablauf</li>
</ul>
</div>
</div>
</section>
<section class="team-profile">
<div class="team-profile__body">
<div class="team-profile__media">
<img src="images/person-silhouette.svg" alt="Svenja Vespermann">
</div>
<div class="team-profile__text">
<span class="team-profile__role">Praxisassistentin</span>
<h3 class="team-profile__name">Svenja Vespermann</h3>
<p class="team-profile__subtitle">Anmeldung, Patientenmanagement und Unterstützung im Praxisablauf</p>
<p>Svenja Vespermann unterstützt unser Team in der Praxis bei der Anmeldung, im Patientenmanagement und bei weiteren organisatorischen Aufgaben im täglichen Ablauf. So trägt sie dazu bei, dass Patientinnen und Patienten freundlich begleitet werden und die Abläufe im Praxisalltag verlässlich funktionieren.</p>
<ul class="team-profile__points">
<li>Unterstützung an der Anmeldung und im direkten Patientenkontakt</li>
<li>Mitarbeit im Patientenmanagement und in organisatorischen Abläufen</li>
<li>Weitere Tätigkeiten im täglichen Praxisablauf</li>
<li>Sorgfältige Unterstützung bei Assistenz- und Routinetätigkeiten</li>
<li>Teamarbeit für einen freundlichen und verlässlichen Ablauf</li>
</ul>
</div>
</div>
@@ -271,12 +278,13 @@
<div class="team-profile__text">
<span class="team-profile__role">Praxisassistentin</span>
<h3 class="team-profile__name">Barbara Creutzburg</h3>
<p class="team-profile__subtitle">Unterstützung im Praxisalltag und organisatorische Begleitung</p>
<p>Barbara Creutzburg unterstützt die Praxis in organisatorischen und assistierenden Aufgaben und trägt dazu bei, dass Abläufe im Hintergrund zuverlässig vorbereitet und begleitet werden.</p>
<p class="team-profile__subtitle">Anmeldung, Patientenmanagement und organisatorische Unterstützung</p>
<p>Barbara Creutzburg hilft in der Praxis bei der Anmeldung, im Patientenmanagement und bei weiteren Tätigkeiten im Praxisablauf. Mit ihrer Unterstützung werden organisatorische Aufgaben zuverlässig begleitet und Patientinnen und Patienten im Alltag gut betreut.</p>
<ul class="team-profile__points">
<li>Unterstützung bei organisatorischen Praxisaufgaben</li>
<li>Assistierende Tätigkeiten im täglichen Ablauf</li>
<li>Verlässliche Begleitung im Hintergrund</li>
<li>Mitarbeit an der Anmeldung und Unterstützung im Empfangsbereich</li>
<li>Unterstützung im Patientenmanagement</li>
<li>Weitere organisatorische Tätigkeiten im Praxisablauf</li>
<li>Verlässliche Unterstützung im täglichen Praxisgeschehen</li>
</ul>
</div>
</div>