Docker-Images bauen und veröffentlichen / build (, omsorgCore/Dockerfile, omsorgcore) (push) Successful in 16s
Docker-Images bauen und veröffentlichen / build (, omsorgWeb/Dockerfile, omsorgweb) (push) Successful in 5s
Docker-Images bauen und veröffentlichen / build (, omsorgapp/Dockerfile, omsorgapp) (push) Successful in 19s
- Facility: TravelCostMode (Pauschale/ProKilometer) + TravelCostPerKm, alongside the existing flat rate; fixes FacilityService.UpdateAsync silently dropping all Konditionen fields on update. - New EmployeeFacilityDistance (Mitarbeiter x Einrichtung -> km) with full office-side CRUD in omsorgapp, plus a self-service endpoint/UI so field staff can maintain their own commute distance via OMSORG Connect (new ModuleType.EmployeeFacilityDistances, Own-scope, no Facilities access needed). - Roles can now be deleted (blocked with a 409 while still assigned to a user). - Fix employeesApi.js missing the Date-object conversion for dateOfBirth/entryDate/ exitDate that crashed employee creation whenever a date was filled in; add a clear/remove control for those date fields. - Requirements: flesh out FR-REC-3 with a staged Akquise cadence, add FR-REC-5/6 for CRM feature scope and success metrics. - Regenerate api-client-ts and api-client-php for all of the above. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
194 lines
7.5 KiB
PHP
194 lines
7.5 KiB
PHP
<?php
|
||
require_once __DIR__ . '/../lib/auth.php';
|
||
require_once __DIR__ . '/../lib/layout.php';
|
||
require_login();
|
||
|
||
if (!has_permission('EmployeeFacilityDistances', 'View')) {
|
||
header('Location: dashboard.php');
|
||
exit;
|
||
}
|
||
|
||
$canCreate = has_permission('EmployeeFacilityDistances', 'Create');
|
||
$canEdit = has_permission('EmployeeFacilityDistances', 'Edit');
|
||
$error = '';
|
||
$success = false;
|
||
$editId = null;
|
||
|
||
$facilitiesResult = omsorgcore_my_facility_distances_facility_options(_omsorgcore_config(), $_SESSION['omsorgcore_access_token']);
|
||
$facilities = $facilitiesResult['ok'] ? $facilitiesResult['data'] : [];
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
verify_csrf();
|
||
|
||
$mode = $_POST['mode'] ?? 'create';
|
||
$distanceKm = trim($_POST['distance_km'] ?? '');
|
||
|
||
if ($distanceKm === '' || !is_numeric($distanceKm) || (float) $distanceKm < 0) {
|
||
$error = 'Entfernung (km) ist erforderlich und darf nicht negativ sein.';
|
||
$editId = $mode === 'edit' ? ($_POST['distance_id'] ?? null) : null;
|
||
} elseif ($mode === 'edit') {
|
||
if (!$canEdit) {
|
||
// Server lehnt das ohnehin über [RequirePermission(EmployeeFacilityDistances, Edit)] ab -
|
||
// dieser Zweig greift nur, wenn jemand das Formular manuell postet, obwohl es clientseitig
|
||
// gar nicht angezeigt wurde.
|
||
$error = 'Keine Berechtigung, diese Entfernung zu bearbeiten.';
|
||
} else {
|
||
$distanceId = $_POST['distance_id'] ?? '';
|
||
$result = omsorgcore_my_facility_distances_update(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], $distanceId, ['distanceKm' => (float) $distanceKm]);
|
||
|
||
if ($result['ok']) {
|
||
header('Location: fahrtstrecken.php?updated=1');
|
||
exit;
|
||
}
|
||
|
||
$editId = $distanceId;
|
||
$error = $result['status'] === 403
|
||
? 'Keine Berechtigung, diese Entfernung zu bearbeiten.'
|
||
: (is_string($result['data'] ?? null) ? $result['data'] : 'Entfernung konnte nicht gespeichert werden.');
|
||
}
|
||
} elseif (!$canCreate) {
|
||
$error = 'Keine Berechtigung, eine Entfernung anzulegen.';
|
||
} else {
|
||
$facilityId = $_POST['facility_id'] ?? '';
|
||
|
||
if ($facilityId === '') {
|
||
$error = 'Einrichtung ist erforderlich.';
|
||
} else {
|
||
$result = omsorgcore_my_facility_distances_create(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], [
|
||
'facilityId' => $facilityId,
|
||
'distanceKm' => (float) $distanceKm,
|
||
]);
|
||
|
||
if ($result['ok']) {
|
||
$success = true;
|
||
} elseif ($result['status'] === 403) {
|
||
$error = 'Keine Berechtigung, eine Entfernung anzulegen.';
|
||
} else {
|
||
$error = is_string($result['data'] ?? null) ? $result['data'] : 'Entfernung konnte nicht angelegt werden.';
|
||
}
|
||
}
|
||
}
|
||
} elseif (isset($_GET['edit'])) {
|
||
$editId = $_GET['edit'];
|
||
}
|
||
|
||
$listResult = omsorgcore_my_facility_distances_list(_omsorgcore_config(), $_SESSION['omsorgcore_access_token']);
|
||
$distances = $listResult['ok'] ? $listResult['data'] : [];
|
||
|
||
$editDistance = null;
|
||
if ($editId !== null) {
|
||
foreach ($distances as $distance) {
|
||
if ($distance['id'] === $editId) {
|
||
$editDistance = $distance;
|
||
break;
|
||
}
|
||
}
|
||
// Fremde/ungültige Entfernung - Own-Scope-Filterung passiert serverseitig in
|
||
// omsorgcore_my_facility_distances_list, hier nur zusätzlich zurück zu "Neue Entfernung".
|
||
if ($editDistance === null) {
|
||
$editId = null;
|
||
}
|
||
}
|
||
|
||
$facilitiesWithoutDistance = array_values(array_filter($facilities, function ($facility) use ($distances) {
|
||
foreach ($distances as $distance) {
|
||
if ($distance['facilityId'] === $facility['id']) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}));
|
||
|
||
layout_start('Fahrtstrecken – Mitarbeiter-App', 'fahrtstrecken');
|
||
?>
|
||
<h1 class="main-title">Fahrtstrecken</h1>
|
||
<p class="hint" style="margin:-8px 0 20px">
|
||
Trage hier die Entfernung (einfache Strecke, km) von deinem Wohnort zu den Einrichtungen ein, bei denen du im Einsatz bist. Diese Angabe ist Grundlage für die kilometerbasierte Fahrtkostenabrechnung.
|
||
</p>
|
||
|
||
<div class="split-layout">
|
||
<div class="glass" style="padding:24px">
|
||
<h2 style="font-family:'KindelSerif',Georgia,serif;font-size:1.3rem;margin:0 0 18px">Meine Fahrtstrecken</h2>
|
||
|
||
<?php if (empty($distances)): ?>
|
||
<p>Noch keine Entfernung erfasst.</p>
|
||
<?php else: ?>
|
||
<?php foreach ($distances as $distance): ?>
|
||
<div style="padding:12px 0;border-bottom:1px solid rgba(0,0,0,.08)">
|
||
<b><?= e($distance['facilityName']) ?></b> — <?= e(number_format((float) $distance['distanceKm'], 1, ',', '.')) ?> km
|
||
<?php if ($canEdit): ?>
|
||
<div style="margin-top:6px">
|
||
<a href="fahrtstrecken.php?edit=<?= e($distance['id']) ?>">Bearbeiten</a>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<div class="glass" style="padding:24px">
|
||
<h2 style="font-family:'KindelSerif',Georgia,serif;font-size:1.3rem;margin:0 0 18px">
|
||
<?= $editDistance ? 'Entfernung bearbeiten' : 'Neue Entfernung' ?>
|
||
</h2>
|
||
|
||
<?php if ($success): ?>
|
||
<div class="hint" style="margin-bottom:16px">Entfernung wurde angelegt.</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (isset($_GET['updated'])): ?>
|
||
<div class="hint" style="margin-bottom:16px">Entfernung wurde aktualisiert.</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($error): ?>
|
||
<div class="error" style="margin-bottom:16px"><?= e($error) ?></div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($editDistance): ?>
|
||
<form method="post">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="mode" value="edit">
|
||
<input type="hidden" name="distance_id" value="<?= e($editDistance['id']) ?>">
|
||
<label>
|
||
Einrichtung
|
||
<input type="text" value="<?= e($editDistance['facilityName']) ?>" disabled>
|
||
</label>
|
||
<label>
|
||
Entfernung (km, einfache Strecke)
|
||
<input type="number" step="0.1" min="0" name="distance_km" value="<?= e($editDistance['distanceKm']) ?>" required>
|
||
</label>
|
||
<button type="submit" class="btn" style="width:100%;margin-top:6px">Änderungen speichern</button>
|
||
</form>
|
||
<p class="hint" style="text-align:center;margin-top:14px">
|
||
<a href="fahrtstrecken.php">Abbrechen</a>
|
||
</p>
|
||
<?php elseif ($canCreate): ?>
|
||
<?php if (empty($facilitiesWithoutDistance)): ?>
|
||
<p class="hint">Für alle Einrichtungen ist bereits eine Entfernung erfasst.</p>
|
||
<?php else: ?>
|
||
<form method="post">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="mode" value="create">
|
||
<label>
|
||
Einrichtung
|
||
<select name="facility_id" required>
|
||
<option value="">— auswählen —</option>
|
||
<?php foreach ($facilitiesWithoutDistance as $facility): ?>
|
||
<option value="<?= e($facility['id']) ?>"><?= e($facility['name']) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</label>
|
||
<label>
|
||
Entfernung (km, einfache Strecke)
|
||
<input type="number" step="0.1" min="0" name="distance_km" required>
|
||
</label>
|
||
<button type="submit" class="btn" style="width:100%;margin-top:6px">Entfernung anlegen</button>
|
||
</form>
|
||
<?php endif; ?>
|
||
<?php else: ?>
|
||
<p class="hint">Keine Berechtigung, eine Entfernung anzulegen.</p>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
layout_end();
|