Publier Globinours 1.0.0-rc.3

This commit is contained in:
Alexandre NOEL 2026-09-03 12:39:15 +02:00
commit 9a2b4068da
325 changed files with 38230 additions and 20 deletions

View file

@ -0,0 +1,172 @@
<?php
declare(strict_types=1);
final class PlacementsController
{
private const TYPES = [
'reservation' => 'Réservation',
'foster' => 'Placement en famille daccueil',
'return' => 'Retour au refuge',
'cancellation' => 'Annulation',
];
public static function save(): void
{
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
http_response_code(405);
return;
}
$db = DB::pdo();
$animalId = (int) ($_POST['animal_id'] ?? 0);
$type = (string) ($_POST['event_type'] ?? '');
$date = (string) ($_POST['event_date'] ?? '');
if (!$animalId || !isset(self::TYPES[$type]) || !self::validDate($date)) {
http_response_code(400);
echo h(t('error.invalid_placement'));
return;
}
$reason = trim((string) ($_POST['reason'] ?? ''));
if (in_array($type, ['return', 'cancellation'], true) && $reason === '') {
http_response_code(400);
echo h(t('error.placement_reason_required'));
return;
}
$s = $db->prepare('SELECT * FROM animals WHERE id=:id AND deleted_at IS NULL AND archived_at IS NULL');
$s->execute([':id' => $animalId]);
$animal = $s->fetch(PDO::FETCH_ASSOC);
if (!$animal) {
http_response_code(404);
return;
}
$contactId = (int) ($_POST['contact_id'] ?? 0) ?: null;
$contact = null;
if ($contactId) {
$s = $db->prepare('SELECT * FROM directory_contacts WHERE id=:id AND deleted_at IS NULL');
$s->execute([':id' => $contactId]);
$contact = $s->fetch(PDO::FETCH_ASSOC) ?: null;
if (!$contact) {
$contactId = null;
}
}
$status = match ($type) {
'reservation' => 'reserve',
'foster' => 'fa',
'return' => 'refuge',
'cancellation' => in_array($animal['status'], ['reserve', 'réservé'], true) ? 'refuge' : $animal['status'],
};
$currentAddress = (string) ($animal['current_address'] ?? '');
if ($type === 'foster' && $contact) {
$currentAddress = implode(
' ',
array_filter([$contact['address'] ?? '', $contact['postal_code'] ?? '', $contact['city'] ?? '']),
);
}
if ($type === 'return') {
$currentAddress = implode(
' ',
array_filter([
AppSettings::get('association_address'),
AppSettings::get('association_postal_code'),
AppSettings::get('association_city'),
]),
);
}
$notes = trim((string) ($_POST['notes'] ?? ''));
$db->beginTransaction();
try {
$db->prepare(
'INSERT INTO animal_placements(animal_id,event_type,event_date,contact_id,reason,notes,created_by) VALUES(:animal,:type,:date,:contact,:reason,:notes,:user)',
)->execute([
':animal' => $animalId,
':type' => $type,
':date' => $date,
':contact' => $contactId,
':reason' => $reason ?: null,
':notes' => $notes ?: null,
':user' => Auth::id(),
]);
$db->prepare(
'UPDATE animals SET status=:status,refuge_room=NULL,care_box_key=NULL,current_address=:address,current_lat=NULL,current_lng=NULL,updated_at=datetime(\'now\') WHERE id=:id',
)->execute([':status' => $status, ':address' => $currentAddress ?: null, ':id' => $animalId]);
$after = $animal;
$after['status'] = $status;
$after['refuge_room'] = null;
$after['care_box_key'] = null;
$after['current_address'] = $currentAddress;
LocationHistoryService::record(
$db,
$animalId,
$animal,
$after,
'placement',
self::TYPES[$type] . ($reason !== '' ? ' — ' . $reason : ''),
$date . ' 12:00:00',
);
$details = [];
if ($reason !== '') {
$details[] = 'Motif : ' . $reason;
}
if ($notes !== '') {
$details[] = 'Notes : ' . $notes;
}
$db->prepare(
'INSERT INTO animal_history(animal_id,type,label,details,user_id,created_at) VALUES(:animal,\'placement\',:label,:details,:user,:date)',
)->execute([
':animal' => $animalId,
':label' => self::TYPES[$type],
':details' => $details ? implode("\n", $details) : null,
':user' => Auth::id(),
':date' => $date . ' 12:00:00',
]);
if ($type === 'foster' || $type === 'return') {
self::movement($db, $animalId, $type, $contactId, $reason, $notes, $date);
}
$db->commit();
header('Location: /animal?id=' . $animalId . '#tab-placements');
exit();
} catch (Throwable $e) {
if ($db->inTransaction()) {
$db->rollBack();
}
throw $e;
}
}
private static function movement(
PDO $db,
int $animalId,
string $type,
?int $contactId,
string $reason,
string $notes,
string $date,
): void {
$contact = null;
if ($contactId) {
$s = $db->prepare('SELECT name,phone,email,city FROM directory_contacts WHERE id=:id');
$s->execute([':id' => $contactId]);
$contact = $s->fetch(PDO::FETCH_ASSOC) ?: null;
}
$db->prepare(
'INSERT INTO animal_movements(animal_id,kind,place,lieu,contact_name,contact_phone,contact_email,note,created_at) VALUES(:animal,:kind,:place,:lieu,:name,:phone,:email,:note,:date)',
)->execute([
':animal' => $animalId,
':kind' => $type === 'return' ? 'entry' : 'exit',
':place' => $contact['city'] ?? null,
':lieu' => $type === 'return' ? 'Retour de placement' : 'Placement en FA',
':name' => $contact['name'] ?? null,
':phone' => $contact['phone'] ?? null,
':email' => $contact['email'] ?? null,
':note' =>
trim(($reason !== '' ? 'Motif : ' . $reason : '') . ($notes !== '' ? "\nNotes : " . $notes : '')) ?:
null,
':date' => $date . ' 12:00:00',
]);
}
private static function validDate(string $date): bool
{
$d = DateTimeImmutable::createFromFormat('!Y-m-d', $date);
return $d !== false && $d->format('Y-m-d') === $date;
}
}