zeiterfassung
This commit is contained in:
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"sqltools.connections": [
|
||||
{
|
||||
"mysqlOptions": {
|
||||
"authProtocol": "default",
|
||||
"enableSsl": "Disabled"
|
||||
},
|
||||
"ssh": "Disabled",
|
||||
"previewLimit": 50,
|
||||
"server": "mysql2fda.netcup.net",
|
||||
"port": 3306,
|
||||
"driver": "MySQL",
|
||||
"name": "Praxis Creutzburg",
|
||||
"database": "k25330_pracreutz",
|
||||
"username": "k25330_pracreutz"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
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;
|
||||
@@ -0,0 +1,24 @@
|
||||
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;
|
||||
@@ -0,0 +1,501 @@
|
||||
<?php
|
||||
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
|
||||
require_once __DIR__ . '/../../inc/PHPMailer/src/Exception.php';
|
||||
require_once __DIR__ . '/../../inc/PHPMailer/src/PHPMailer.php';
|
||||
require_once __DIR__ . '/../../inc/PHPMailer/src/SMTP.php';
|
||||
|
||||
if (!function_exists('timeErrorNotificationsEnsureTables')) {
|
||||
function timeErrorNotificationsEnsureTables(PDO $pdo): void
|
||||
{
|
||||
$legacyColumnStmt = $pdo->prepare(
|
||||
"SELECT COUNT(*)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'time_error_notifications'
|
||||
AND COLUMN_NAME = 'error_date'"
|
||||
);
|
||||
$legacyColumnStmt->execute();
|
||||
if ((int)$legacyColumnStmt->fetchColumn() > 0) {
|
||||
$pdo->exec("DROP TABLE IF EXISTS time_error_notifications");
|
||||
}
|
||||
|
||||
$pdo->exec(
|
||||
"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"
|
||||
);
|
||||
|
||||
$pdo->exec(
|
||||
"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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeErrorNotificationsSendMail')) {
|
||||
function timeErrorNotificationsSendMail(PDO $pdo, string $recipient, string $subject, string $body): bool
|
||||
{
|
||||
$stmt = $pdo->prepare("SELECT * FROM config LIMIT 1");
|
||||
$stmt->execute();
|
||||
$config = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mail = new PHPMailer(true);
|
||||
|
||||
try {
|
||||
$mail->SMTPDebug = 0;
|
||||
$mail->isSMTP();
|
||||
$mail->Host = (string)$config['mailserver'];
|
||||
$mail->SMTPAuth = true;
|
||||
$mail->Username = (string)$config['mailUsername'];
|
||||
$mail->Password = (string)$config['mailPassword'];
|
||||
|
||||
if (!empty($config['mailSMTPSecure'])) {
|
||||
$mail->SMTPSecure = (string)$config['mailSMTPSecure'];
|
||||
}
|
||||
|
||||
$mail->Port = (int)$config['mailPort'];
|
||||
$mail->CharSet = 'UTF-8';
|
||||
$mail->setFrom((string)$config['mailFrom'], (string)$config['mailFromName']);
|
||||
$mail->addAddress($recipient);
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = $subject;
|
||||
$mail->Body = $body;
|
||||
$mail->AltBody = trim(html_entity_decode(strip_tags(str_replace(["<br>", "<br/>", "<br />"], "\n", $body)), ENT_QUOTES, 'UTF-8'));
|
||||
|
||||
$mail->send();
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeErrorNotificationsFetchInvalidEntries')) {
|
||||
function timeErrorNotificationsFetchInvalidEntries(PDO $pdo): array
|
||||
{
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT
|
||||
u.id AS employee_id,
|
||||
u.email,
|
||||
u.vorname,
|
||||
u.nachname,
|
||||
DATE(t.timestamp_datetime) AS error_date,
|
||||
GROUP_CONCAT(t.timestamp_type ORDER BY t.timestamp_datetime) AS day_sequence
|
||||
FROM users u
|
||||
INNER JOIN timestamps t ON t.employee_id = u.id
|
||||
WHERE u.zeiterfassung = '1'
|
||||
AND DATE(t.timestamp_datetime) < CURDATE()
|
||||
GROUP BY u.id, u.email, u.vorname, u.nachname, DATE(t.timestamp_datetime)
|
||||
ORDER BY error_date ASC, u.nachname ASC, u.vorname ASC"
|
||||
);
|
||||
$stmt->execute();
|
||||
|
||||
$entries = [];
|
||||
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
if (!isValidSequence((string)$row['day_sequence'])) {
|
||||
$entries[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeErrorNotificationsGroupEntriesByEmployee')) {
|
||||
function timeErrorNotificationsGroupEntriesByEmployee(array $entries): array
|
||||
{
|
||||
$grouped = [];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$employeeId = (int)$entry['employee_id'];
|
||||
if (!isset($grouped[$employeeId])) {
|
||||
$grouped[$employeeId] = [
|
||||
'employee_id' => $employeeId,
|
||||
'email' => (string)$entry['email'],
|
||||
'vorname' => (string)$entry['vorname'],
|
||||
'nachname' => (string)$entry['nachname'],
|
||||
'first_error_date' => (string)$entry['error_date'],
|
||||
'error_dates' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$grouped[$employeeId]['error_dates'][] = (string)$entry['error_date'];
|
||||
if ((string)$entry['error_date'] < $grouped[$employeeId]['first_error_date']) {
|
||||
$grouped[$employeeId]['first_error_date'] = (string)$entry['error_date'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($grouped as &$employee) {
|
||||
$employee['error_dates'] = array_values(array_unique($employee['error_dates']));
|
||||
sort($employee['error_dates']);
|
||||
}
|
||||
unset($employee);
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeErrorNotificationsFetchAdminRecipients')) {
|
||||
function timeErrorNotificationsFetchAdminRecipients(PDO $pdo): array
|
||||
{
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT DISTINCT
|
||||
u.id,
|
||||
u.email,
|
||||
u.vorname,
|
||||
u.nachname
|
||||
FROM users u
|
||||
LEFT JOIN users_admin ua ON ua.userid = u.id
|
||||
WHERE (u.admin = 1 OR ua.userid IS NOT NULL)
|
||||
AND u.email IS NOT NULL
|
||||
AND u.email <> ''
|
||||
ORDER BY u.nachname ASC, u.vorname ASC"
|
||||
);
|
||||
$stmt->execute();
|
||||
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeErrorNotificationsFetchStateByEmployee')) {
|
||||
function timeErrorNotificationsFetchStateByEmployee(PDO $pdo): array
|
||||
{
|
||||
timeErrorNotificationsEnsureTables($pdo);
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM time_error_notification_state");
|
||||
$stmt->execute();
|
||||
|
||||
$states = [];
|
||||
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
$states[(int)$row['employee_id']] = $row;
|
||||
}
|
||||
|
||||
return $states;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeErrorNotificationsUpsertState')) {
|
||||
function timeErrorNotificationsUpsertState(PDO $pdo, array $employee, ?array $existingState): array
|
||||
{
|
||||
timeErrorNotificationsEnsureTables($pdo);
|
||||
|
||||
$cycleStartedOn = $existingState['cycle_started_on'] ?? $employee['first_error_date'];
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO time_error_notification_state (
|
||||
employee_id,
|
||||
cycle_started_on,
|
||||
first_error_date,
|
||||
last_notification_stage,
|
||||
last_notification_sent_at,
|
||||
employee_day_1_sent_at,
|
||||
employee_day_3_sent_at,
|
||||
admin_day_7_sent_at
|
||||
) VALUES (
|
||||
:employee_id,
|
||||
:cycle_started_on,
|
||||
:first_error_date,
|
||||
:last_notification_stage,
|
||||
:last_notification_sent_at,
|
||||
:employee_day_1_sent_at,
|
||||
:employee_day_3_sent_at,
|
||||
:admin_day_7_sent_at
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
cycle_started_on = VALUES(cycle_started_on),
|
||||
first_error_date = VALUES(first_error_date),
|
||||
last_notification_stage = VALUES(last_notification_stage),
|
||||
last_notification_sent_at = VALUES(last_notification_sent_at),
|
||||
employee_day_1_sent_at = VALUES(employee_day_1_sent_at),
|
||||
employee_day_3_sent_at = VALUES(employee_day_3_sent_at),
|
||||
admin_day_7_sent_at = VALUES(admin_day_7_sent_at)"
|
||||
);
|
||||
$stmt->execute([
|
||||
'employee_id' => $employee['employee_id'],
|
||||
'cycle_started_on' => $cycleStartedOn,
|
||||
'first_error_date' => $employee['first_error_date'],
|
||||
'last_notification_stage' => $existingState['last_notification_stage'] ?? null,
|
||||
'last_notification_sent_at' => $existingState['last_notification_sent_at'] ?? null,
|
||||
'employee_day_1_sent_at' => $existingState['employee_day_1_sent_at'] ?? null,
|
||||
'employee_day_3_sent_at' => $existingState['employee_day_3_sent_at'] ?? null,
|
||||
'admin_day_7_sent_at' => $existingState['admin_day_7_sent_at'] ?? null,
|
||||
]);
|
||||
|
||||
return [
|
||||
'employee_id' => $employee['employee_id'],
|
||||
'cycle_started_on' => $cycleStartedOn,
|
||||
'first_error_date' => $employee['first_error_date'],
|
||||
'last_notification_stage' => $existingState['last_notification_stage'] ?? null,
|
||||
'last_notification_sent_at' => $existingState['last_notification_sent_at'] ?? null,
|
||||
'employee_day_1_sent_at' => $existingState['employee_day_1_sent_at'] ?? null,
|
||||
'employee_day_3_sent_at' => $existingState['employee_day_3_sent_at'] ?? null,
|
||||
'admin_day_7_sent_at' => $existingState['admin_day_7_sent_at'] ?? null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeErrorNotificationsClearResolvedStates')) {
|
||||
function timeErrorNotificationsClearResolvedStates(PDO $pdo, array $openEmployeeIds): int
|
||||
{
|
||||
timeErrorNotificationsEnsureTables($pdo);
|
||||
|
||||
if (empty($openEmployeeIds)) {
|
||||
$stmt = $pdo->prepare("DELETE FROM time_error_notification_state");
|
||||
$stmt->execute();
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
$placeholders = implode(',', array_fill(0, count($openEmployeeIds), '?'));
|
||||
$stmt = $pdo->prepare("DELETE FROM time_error_notification_state WHERE employee_id NOT IN ($placeholders)");
|
||||
$stmt->execute(array_values($openEmployeeIds));
|
||||
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeErrorNotificationsMarkStageSent')) {
|
||||
function timeErrorNotificationsMarkStageSent(PDO $pdo, array $state, string $stage, string $recipientEmail): array
|
||||
{
|
||||
timeErrorNotificationsEnsureTables($pdo);
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO time_error_notifications (employee_id, cycle_started_on, notification_stage, recipient_email)
|
||||
VALUES (:employee_id, :cycle_started_on, :notification_stage, :recipient_email)
|
||||
ON DUPLICATE KEY UPDATE sent_at = sent_at"
|
||||
);
|
||||
$stmt->execute([
|
||||
'employee_id' => $state['employee_id'],
|
||||
'cycle_started_on' => $state['cycle_started_on'],
|
||||
'notification_stage' => $stage,
|
||||
'recipient_email' => $recipientEmail,
|
||||
]);
|
||||
|
||||
$now = (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');
|
||||
$state['last_notification_stage'] = $stage;
|
||||
$state['last_notification_sent_at'] = $now;
|
||||
|
||||
if ($stage === 'employee_day_1') {
|
||||
$state['employee_day_1_sent_at'] = $now;
|
||||
} elseif ($stage === 'employee_day_3') {
|
||||
$state['employee_day_3_sent_at'] = $now;
|
||||
} elseif ($stage === 'admin_day_7') {
|
||||
$state['admin_day_7_sent_at'] = $now;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"UPDATE time_error_notification_state
|
||||
SET last_notification_stage = :last_notification_stage,
|
||||
last_notification_sent_at = :last_notification_sent_at,
|
||||
employee_day_1_sent_at = :employee_day_1_sent_at,
|
||||
employee_day_3_sent_at = :employee_day_3_sent_at,
|
||||
admin_day_7_sent_at = :admin_day_7_sent_at
|
||||
WHERE employee_id = :employee_id"
|
||||
);
|
||||
$stmt->execute([
|
||||
'last_notification_stage' => $state['last_notification_stage'],
|
||||
'last_notification_sent_at' => $state['last_notification_sent_at'],
|
||||
'employee_day_1_sent_at' => $state['employee_day_1_sent_at'],
|
||||
'employee_day_3_sent_at' => $state['employee_day_3_sent_at'],
|
||||
'admin_day_7_sent_at' => $state['admin_day_7_sent_at'],
|
||||
'employee_id' => $state['employee_id'],
|
||||
]);
|
||||
|
||||
return $state;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeErrorNotificationsDaysSinceDate')) {
|
||||
function timeErrorNotificationsDaysSinceDate(string $date, ?DateTimeImmutable $today = null): int
|
||||
{
|
||||
$today = $today ?: new DateTimeImmutable('today');
|
||||
$reference = new DateTimeImmutable(substr($date, 0, 10));
|
||||
|
||||
return (int)$reference->diff($today)->days;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeErrorNotificationsFormatDateList')) {
|
||||
function timeErrorNotificationsFormatDateList(array $errorDates): string
|
||||
{
|
||||
$labels = array_map(static function (string $date): string {
|
||||
return date('d.m.Y', strtotime($date));
|
||||
}, $errorDates);
|
||||
|
||||
return implode(', ', $labels);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeErrorNotificationsBuildEmployeeMail')) {
|
||||
function timeErrorNotificationsBuildEmployeeMail(array $employee, string $stage): array
|
||||
{
|
||||
$name = trim($employee['vorname'] . ' ' . $employee['nachname']);
|
||||
$dateList = timeErrorNotificationsFormatDateList($employee['error_dates']);
|
||||
$subject = ($stage === 'employee_day_3')
|
||||
? 'Erneute Erinnerung zu offenen Zeitfehlern'
|
||||
: 'Erinnerung zu offenen Zeitfehlern';
|
||||
|
||||
$body = '<p>Hallo ' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . ',</p>'
|
||||
. '<p>in deiner Zeiterfassung gibt es weiterhin offene Buchungsfehler.</p>'
|
||||
. '<p><strong>Betroffene Tage:</strong> ' . htmlspecialchars($dateList, ENT_QUOTES, 'UTF-8') . '</p>'
|
||||
. '<p>Bitte korrigiere die Eintraege in der Zeiterfassung. Solange die Fehler offen bleiben, wird der Vorgang weiter verfolgt.</p>';
|
||||
|
||||
return [
|
||||
'subject' => $subject,
|
||||
'body' => $body,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeErrorNotificationsBuildAdminMail')) {
|
||||
function timeErrorNotificationsBuildAdminMail(array $employee): array
|
||||
{
|
||||
$employeeName = trim($employee['vorname'] . ' ' . $employee['nachname']);
|
||||
$dateList = timeErrorNotificationsFormatDateList($employee['error_dates']);
|
||||
$subject = 'Eskalation: Offene Zeitfehler von ' . $employeeName;
|
||||
$body = '<p>Bei einem Mitarbeiter bestehen weiterhin offene Zeitfehler.</p>'
|
||||
. '<p><strong>Mitarbeiter:</strong> ' . htmlspecialchars($employeeName, ENT_QUOTES, 'UTF-8') . '<br>'
|
||||
. '<strong>E-Mail:</strong> ' . htmlspecialchars($employee['email'], ENT_QUOTES, 'UTF-8') . '<br>'
|
||||
. '<strong>Betroffene Tage:</strong> ' . htmlspecialchars($dateList, ENT_QUOTES, 'UTF-8') . '</p>'
|
||||
. '<p>Bitte pruefen Sie die Zeiterfassung und stimmen Sie die Korrektur mit dem Mitarbeiter ab.</p>';
|
||||
|
||||
return [
|
||||
'subject' => $subject,
|
||||
'body' => $body,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('timeErrorNotificationsProcess')) {
|
||||
function timeErrorNotificationsProcess(PDO $pdo, ?DateTimeImmutable $today = null): array
|
||||
{
|
||||
$today = $today ?: new DateTimeImmutable('today');
|
||||
timeErrorNotificationsEnsureTables($pdo);
|
||||
|
||||
$results = [
|
||||
'processed_errors' => 0,
|
||||
'affected_employees' => 0,
|
||||
'employee_day_1_sent' => 0,
|
||||
'employee_day_3_sent' => 0,
|
||||
'admin_day_7_sent' => 0,
|
||||
'resolved_states_cleared' => 0,
|
||||
'skipped_missing_employee_email' => 0,
|
||||
'skipped_missing_admin_email' => 0,
|
||||
'failed' => [],
|
||||
];
|
||||
|
||||
$entries = timeErrorNotificationsFetchInvalidEntries($pdo);
|
||||
$employees = timeErrorNotificationsGroupEntriesByEmployee($entries);
|
||||
$adminRecipients = timeErrorNotificationsFetchAdminRecipients($pdo);
|
||||
$states = timeErrorNotificationsFetchStateByEmployee($pdo);
|
||||
|
||||
$results['processed_errors'] = count($entries);
|
||||
$results['affected_employees'] = count($employees);
|
||||
$results['resolved_states_cleared'] = timeErrorNotificationsClearResolvedStates($pdo, array_keys($employees));
|
||||
|
||||
foreach ($employees as $employeeId => $employee) {
|
||||
$state = timeErrorNotificationsUpsertState($pdo, $employee, $states[$employeeId] ?? null);
|
||||
$daysSinceCycleStart = timeErrorNotificationsDaysSinceDate($state['cycle_started_on'], $today);
|
||||
$employeeEmail = trim($employee['email']);
|
||||
|
||||
if ($state['employee_day_1_sent_at'] === null && $daysSinceCycleStart >= 1) {
|
||||
if ($employeeEmail === '') {
|
||||
$results['skipped_missing_employee_email']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$mail = timeErrorNotificationsBuildEmployeeMail($employee, 'employee_day_1');
|
||||
if (timeErrorNotificationsSendMail($pdo, $employeeEmail, $mail['subject'], $mail['body'])) {
|
||||
$state = timeErrorNotificationsMarkStageSent($pdo, $state, 'employee_day_1', $employeeEmail);
|
||||
$results['employee_day_1_sent']++;
|
||||
} else {
|
||||
$results['failed'][] = [
|
||||
'stage' => 'employee_day_1',
|
||||
'employee_id' => $employeeId,
|
||||
'recipient_email' => $employeeEmail,
|
||||
];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state['employee_day_3_sent_at'] === null && $daysSinceCycleStart >= 3) {
|
||||
if ($employeeEmail === '') {
|
||||
$results['skipped_missing_employee_email']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$mail = timeErrorNotificationsBuildEmployeeMail($employee, 'employee_day_3');
|
||||
if (timeErrorNotificationsSendMail($pdo, $employeeEmail, $mail['subject'], $mail['body'])) {
|
||||
$state = timeErrorNotificationsMarkStageSent($pdo, $state, 'employee_day_3', $employeeEmail);
|
||||
$results['employee_day_3_sent']++;
|
||||
} else {
|
||||
$results['failed'][] = [
|
||||
'stage' => 'employee_day_3',
|
||||
'employee_id' => $employeeId,
|
||||
'recipient_email' => $employeeEmail,
|
||||
];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state['admin_day_7_sent_at'] !== null || empty($state['last_notification_sent_at'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$daysSinceLastNotification = timeErrorNotificationsDaysSinceDate($state['last_notification_sent_at'], $today);
|
||||
if ($daysSinceLastNotification < 7) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($adminRecipients)) {
|
||||
$results['skipped_missing_admin_email']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($adminRecipients as $adminRecipient) {
|
||||
$adminEmail = trim((string)$adminRecipient['email']);
|
||||
if ($adminEmail === '') {
|
||||
$results['skipped_missing_admin_email']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$mail = timeErrorNotificationsBuildAdminMail($employee);
|
||||
if (timeErrorNotificationsSendMail($pdo, $adminEmail, $mail['subject'], $mail['body'])) {
|
||||
$state = timeErrorNotificationsMarkStageSent($pdo, $state, 'admin_day_7', $adminEmail);
|
||||
$results['admin_day_7_sent']++;
|
||||
} else {
|
||||
$results['failed'][] = [
|
||||
'stage' => 'admin_day_7',
|
||||
'employee_id' => $employeeId,
|
||||
'recipient_email' => $adminEmail,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
require_once __DIR__ . '/inc/config.inc.php';
|
||||
require_once __DIR__ . '/inc/functions.inc.php';
|
||||
require_once __DIR__ . '/inc/time_error_notifications.inc.php';
|
||||
|
||||
$isCli = PHP_SAPI === 'cli';
|
||||
|
||||
if (!$isCli) {
|
||||
$user = check_user();
|
||||
if (!$user || !is_admin_user()) {
|
||||
http_response_code(403);
|
||||
exit('Keine Berechtigung.');
|
||||
}
|
||||
}
|
||||
|
||||
$result = timeErrorNotificationsProcess($pdo);
|
||||
|
||||
if ($isCli) {
|
||||
echo "Zeiterfassungs-Benachrichtigungen ausgefuehrt\n";
|
||||
echo "Fehlerhafte Tage geprueft: " . $result['processed_errors'] . "\n";
|
||||
echo "Betroffene Mitarbeiter: " . $result['affected_employees'] . "\n";
|
||||
echo "Mitarbeiter-Erinnerungen Tag 1: " . $result['employee_day_1_sent'] . "\n";
|
||||
echo "Mitarbeiter-Erinnerungen Tag 3: " . $result['employee_day_3_sent'] . "\n";
|
||||
echo "Admin-Benachrichtigungen Tag 7: " . $result['admin_day_7_sent'] . "\n";
|
||||
echo "Bereinigte geloeste Status: " . $result['resolved_states_cleared'] . "\n";
|
||||
echo "Ohne Mitarbeiter-E-Mail uebersprungen: " . $result['skipped_missing_employee_email'] . "\n";
|
||||
echo "Ohne Admin-E-Mail uebersprungen: " . $result['skipped_missing_admin_email'] . "\n";
|
||||
echo "Fehlgeschlagene Sendungen: " . count($result['failed']) . "\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
include __DIR__ . '/header.php';
|
||||
?>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>Zeiterfassungs-Benachrichtigungen</h2>
|
||||
<div class="alert alert-info" role="alert">
|
||||
<strong>Gepruefte fehlerhafte Tage:</strong> <?php echo (int)$result['processed_errors']; ?><br>
|
||||
<strong>Betroffene Mitarbeiter:</strong> <?php echo (int)$result['affected_employees']; ?><br>
|
||||
<strong>Mitarbeiter-Erinnerungen Tag 1:</strong> <?php echo (int)$result['employee_day_1_sent']; ?><br>
|
||||
<strong>Mitarbeiter-Erinnerungen Tag 3:</strong> <?php echo (int)$result['employee_day_3_sent']; ?><br>
|
||||
<strong>Admin-Benachrichtigungen Tag 7:</strong> <?php echo (int)$result['admin_day_7_sent']; ?><br>
|
||||
<strong>Bereinigte geloeste Status:</strong> <?php echo (int)$result['resolved_states_cleared']; ?><br>
|
||||
<strong>Ohne Mitarbeiter-E-Mail uebersprungen:</strong> <?php echo (int)$result['skipped_missing_employee_email']; ?><br>
|
||||
<strong>Ohne Admin-E-Mail uebersprungen:</strong> <?php echo (int)$result['skipped_missing_admin_email']; ?><br>
|
||||
<strong>Fehlgeschlagene Sendungen:</strong> <?php echo count($result['failed']); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php include __DIR__ . '/footer.php'; ?>
|
||||
Reference in New Issue
Block a user