Add kilometer-based Fahrtkosten billing, self-service distance entry, role deletion
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
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>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
09dce2ab98
commit
ffa2c4a9e7
@@ -0,0 +1,193 @@
|
||||
<?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();
|
||||
Reference in New Issue
Block a user