Rend les salles et tournées configurables

Refs #10 #21
This commit is contained in:
Alexandre NOEL 2026-09-08 00:33:25 +02:00
commit e7176b6371
17 changed files with 189 additions and 25 deletions

View file

@ -617,6 +617,15 @@ trait AnimalRecordActions
$fiv = $fiv ?? (int) ($old['fiv'] ?? 0);
$felv = $felv ?? (int) ($old['felv'] ?? 0);
if ($refugeRoom !== null && !ShelterRoomService::accepts($availableRooms[$refugeRoom], [
'species' => $species,
'sex' => $sex,
'fiv' => $fiv,
])) {
http_response_code(400);
echo h(t('rooms.animal_not_allowed'));
return;
}
$careBox = in_array($status, ['soin', 'quarantaine'], true) ? $old['care_box_key'] ?? null : null;
if ($isArchivedCorrection) {
$refugeRoom = $old['refuge_room'] ?? null;

View file

@ -4,10 +4,16 @@ declare(strict_types=1);
final class CareRoundsController
{
private static function period(mixed $value): string
{
$value = (string) $value;
return in_array($value, ShelterRoomService::PERIODS, true) ? $value : 'morning';
}
public static function index(): void
{
$db = DB::pdo();
$period = ($_GET['period'] ?? 'morning') === 'evening' ? 'evening' : 'morning';
$period = self::period($_GET['period'] ?? 'morning');
$date = preg_match('/^\d{4}-\d{2}-\d{2}$/', (string) ($_GET['date'] ?? ''))
? (string) $_GET['date']
: date('Y-m-d');
@ -65,6 +71,21 @@ final class CareRoundsController
foreach ($rows as $r) {
$unconfigured[(int) $r['animal_id']][] = $r;
}
$allRooms = ShelterRoomService::all(true);
$rooms = array_values(array_filter(
$allRooms,
static fn(array $room): bool => in_array($period, ShelterRoomService::periods($room), true),
));
$allRoomsByCode = array_column($allRooms, null, 'code');
$visibleRoomCodes = array_column($rooms, 'code');
$animals = array_values(array_filter(
$animals,
static fn(array $animal): bool => in_array(
ShelterRoomService::resolveAnimal($animal, $allRoomsByCode),
$visibleRoomCodes,
true,
),
));
render('care_round.php', [
'title' => t('care.title'),
'pageDescription' => t('care.description'),
@ -72,7 +93,7 @@ final class CareRoundsController
'date' => $date,
'round' => $round,
'layouts' => $layouts,
'rooms' => ShelterRoomService::all(true),
'rooms' => $rooms,
'animals' => $animals,
'treatmentsByAnimal' => $treatments,
'unconfiguredTreatments' => $unconfigured,
@ -95,10 +116,14 @@ final class CareRoundsController
return;
}
$animalCheck = $db->prepare(
"SELECT 1 FROM animals WHERE id=? AND deleted_at IS NULL AND archived_at IS NULL AND lower(status) IN ('refuge','soin','quarantaine')",
"SELECT * FROM animals WHERE id=? AND deleted_at IS NULL AND archived_at IS NULL AND lower(status) IN ('refuge','soin','quarantaine')",
);
$animalCheck->execute([$animalId]);
if (!$animalCheck->fetchColumn()) {
$animal = $animalCheck->fetch(PDO::FETCH_ASSOC);
$roomsByCode = ShelterRoomService::byCode(true);
$roomCode = $animal ? ShelterRoomService::resolveAnimal($animal, $roomsByCode) : '';
$period = self::period($_POST['period'] ?? 'morning');
if (!$animal || !isset($roomsByCode[$roomCode]) || !in_array($period, ShelterRoomService::periods($roomsByCode[$roomCode]), true)) {
http_response_code(404);
return;
}
@ -255,7 +280,7 @@ final class CareRoundsController
$roundStmt->execute([':id' => $roundId]);
$roundInfo = $roundStmt->fetch();
$roundDate = $roundInfo ? date('d/m/Y', strtotime((string) $roundInfo['round_date'])) : date('d/m/Y');
$roundPeriod = ($roundInfo['period'] ?? 'morning') === 'evening' ? 'soir' : 'matin';
$roundPeriod = t('care.' . self::period($roundInfo['period'] ?? 'morning'));
$history = $db->prepare(
"INSERT INTO animal_history(animal_id,type,label,details,user_id) VALUES(:animal,'care_round',:label,:details,:user)",
);
@ -277,7 +302,7 @@ final class CareRoundsController
}
throw $e;
}
$period = ($_POST['period'] ?? 'morning') === 'evening' ? 'evening' : 'morning';
$period = self::period($_POST['period'] ?? 'morning');
$date = (string) ($_POST['date'] ?? date('Y-m-d'));
header('Location: /care-round?period=' . $period . '&date=' . rawurlencode($date) . '#animal-' . $animalId);
exit();
@ -293,7 +318,8 @@ final class CareRoundsController
$roundId = self::ensureRound($db);
$room = (string) ($_POST['room'] ?? '');
$rooms = ShelterRoomService::byCode(true);
if (!$roundId || !isset($rooms[$room]) || empty($rooms[$room]['bulk_validation'])) {
$period = self::period($_POST['period'] ?? 'morning');
if (!$roundId || !isset($rooms[$room]) || empty($rooms[$room]['bulk_validation']) || !in_array($period, ShelterRoomService::periods($rooms[$room]), true)) {
http_response_code(400);
return;
}
@ -312,7 +338,6 @@ final class CareRoundsController
}
}
$period = ($_POST['period'] ?? 'morning') === 'evening' ? 'evening' : 'morning';
$date = preg_match('/^\d{4}-\d{2}-\d{2}$/', (string) ($_POST['date'] ?? ''))
? (string) $_POST['date']
: date('Y-m-d');
@ -329,7 +354,7 @@ final class CareRoundsController
}
$animals = $db
->query(
"SELECT id,name,status,refuge_room,care_box_key,current_address FROM animals WHERE deleted_at IS NULL AND archived_at IS NULL AND lower(status) IN ('refuge','soin','quarantaine') ORDER BY name COLLATE NOCASE",
"SELECT id,name,species,sex,fiv,fiv_status,status,refuge_room,care_box_key,current_address FROM animals WHERE deleted_at IS NULL AND archived_at IS NULL AND lower(status) IN ('refuge','soin','quarantaine') ORDER BY name COLLATE NOCASE",
)
->fetchAll();
render('care_setup.php', [
@ -384,6 +409,11 @@ final class CareRoundsController
continue;
}
$room = $rooms[$roomCode];
$animalRules = $db->prepare('SELECT species,sex,fiv,fiv_status FROM animals WHERE id=:id');
$animalRules->execute([':id' => $animalId]);
if (!ShelterRoomService::accepts($room, $animalRules->fetch(PDO::FETCH_ASSOC) ?: [])) {
continue;
}
$allowedBoxes = array_column(
array_filter($room['boxes'], static fn($box) => (int) $box['active'] === 1),
'code',
@ -434,7 +464,7 @@ final class CareRoundsController
DB::pdo()
->prepare('UPDATE treatments SET give_morning=?,give_evening=?,give_as_needed=? WHERE id=?')
->execute([$values[0], $values[1], $values[2], $id]);
$period = ($_POST['period'] ?? 'morning') === 'evening' ? 'evening' : 'morning';
$period = self::period($_POST['period'] ?? 'morning');
header('Location: /care-round?period=' . $period);
exit();
}
@ -444,7 +474,7 @@ final class CareRoundsController
$date = preg_match('/^\d{4}-\d{2}-\d{2}$/', (string) ($_POST['date'] ?? ''))
? (string) $_POST['date']
: date('Y-m-d');
$period = ($_POST['period'] ?? 'morning') === 'evening' ? 'evening' : 'morning';
$period = self::period($_POST['period'] ?? 'morning');
$requested = (int) ($_POST['round_id'] ?? 0);
if ($requested) {
$s = $db->prepare('SELECT id FROM care_rounds WHERE id=? AND round_date=? AND period=?');

View file

@ -215,6 +215,7 @@ final class SettingsController
render('settings_rooms.php', [
'title' => t('rooms.title'),
'rooms' => ShelterRoomService::all(),
'species' => SpeciesService::all(),
'saved' => isset($_GET['saved']),
'error' => (string) ($_GET['error'] ?? ''),
]);
@ -236,6 +237,18 @@ final class SettingsController
$type = (string) ($row['room_type'] ?? 'collective');
$status = (string) ($row['status_code'] ?? 'refuge');
$color = (string) ($row['color'] ?? '#6c757d');
$allowedSpecies = SpeciesService::normalize((string) ($row['allowed_species'] ?? ''));
if (!SpeciesService::exists($allowedSpecies, false)) {
$allowedSpecies = null;
}
$allowedSex = in_array($row['allowed_sex'] ?? '', ['M', 'F'], true) ? (string) $row['allowed_sex'] : null;
$requiredFiv = in_array($row['required_fiv'] ?? '', ['positive', 'negative'], true)
? (string) $row['required_fiv']
: 'any';
$periods = array_values(array_intersect(ShelterRoomService::PERIODS, (array) ($row['care_periods'] ?? [])));
if (!$periods) {
throw new RuntimeException(t('rooms.period_required'));
}
if (
$name === '' ||
mb_strlen($name) > 100 ||
@ -246,7 +259,7 @@ final class SettingsController
throw new RuntimeException(t('rooms.invalid'));
}
$db->prepare(
"UPDATE shelter_rooms SET name=?,room_type=?,status_code=?,color=?,bulk_validation=?,active=?,sort_order=?,updated_at=datetime('now') WHERE id=?",
"UPDATE shelter_rooms SET name=?,room_type=?,status_code=?,color=?,bulk_validation=?,active=?,sort_order=?,allowed_species=?,allowed_sex=?,required_fiv=?,care_periods=?,updated_at=datetime('now') WHERE id=?",
)->execute([
$name,
$type,
@ -255,6 +268,10 @@ final class SettingsController
isset($row['bulk_validation']) ? 1 : 0,
isset($row['active']) ? 1 : 0,
max(0, (int) ($row['sort_order'] ?? 100)),
$allowedSpecies,
$allowedSex,
$requiredFiv,
implode(',', $periods),
$id,
]);
self::syncBoxes($db, $id, (string) ($row['boxes'] ?? ''));
@ -278,14 +295,24 @@ final class SettingsController
$color = preg_match('/^#[0-9a-fA-F]{6}$/', (string) ($new['color'] ?? ''))
? (string) $new['color']
: '#6c757d';
$allowedSpecies = SpeciesService::normalize((string) ($new['allowed_species'] ?? ''));
if (!SpeciesService::exists($allowedSpecies, false)) {
$allowedSpecies = null;
}
$allowedSex = in_array($new['allowed_sex'] ?? '', ['M', 'F'], true) ? (string) $new['allowed_sex'] : null;
$requiredFiv = in_array($new['required_fiv'] ?? '', ['positive', 'negative'], true)
? (string) $new['required_fiv']
: 'any';
$periods = array_values(array_intersect(ShelterRoomService::PERIODS, (array) ($new['care_periods'] ?? [])));
$periods = $periods ?: ['morning', 'evening'];
$duplicate = $db->prepare('SELECT 1 FROM shelter_rooms WHERE code=? OR name=? COLLATE NOCASE');
$duplicate->execute([$code, $newName]);
if ($duplicate->fetchColumn()) {
throw new RuntimeException(t('rooms.duplicate'));
}
$db->prepare(
'INSERT INTO shelter_rooms(code,name,room_type,status_code,color,bulk_validation,sort_order) VALUES(?,?,?,?,?,?,?)',
)->execute([$code, $newName, $type, $status, $color, isset($new['bulk_validation']) ? 1 : 0, 100]);
'INSERT INTO shelter_rooms(code,name,room_type,status_code,color,bulk_validation,sort_order,allowed_species,allowed_sex,required_fiv,care_periods) VALUES(?,?,?,?,?,?,?,?,?,?,?)',
)->execute([$code, $newName, $type, $status, $color, isset($new['bulk_validation']) ? 1 : 0, 100, $allowedSpecies, $allowedSex, $requiredFiv, implode(',', $periods)]);
self::syncBoxes($db, (int) $db->lastInsertId(), (string) ($new['boxes'] ?? ''));
}
$db->commit();