1041 lines
49 KiB
PHP
1041 lines
49 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
trait AnimalRecordActions
|
||
{
|
||
use AnimalRelationshipActions;
|
||
public static function pdf(): void
|
||
{
|
||
$id = (int) ($_GET['id'] ?? 0);
|
||
if ($id <= 0) {
|
||
http_response_code(400);
|
||
echo h(t('error.bad_request'));
|
||
return;
|
||
}
|
||
|
||
$db = DB::pdo();
|
||
|
||
// Animal
|
||
$stmt = $db->prepare('SELECT * FROM animals WHERE id=:id');
|
||
$stmt->execute([':id' => $id]);
|
||
$animal = $stmt->fetch();
|
||
if (!$animal) {
|
||
http_response_code(404);
|
||
echo h(t('error.not_found'));
|
||
return;
|
||
}
|
||
|
||
$stmt = $db->prepare(
|
||
'SELECT ad.*,vet.name veterinarian_name,crem.name crematorium_name FROM animal_deaths ad LEFT JOIN directory_contacts vet ON vet.id=ad.veterinarian_contact_id LEFT JOIN directory_contacts crem ON crem.id=ad.crematorium_contact_id WHERE ad.animal_id=:id',
|
||
);
|
||
$stmt->execute([':id' => $id]);
|
||
$death = $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
|
||
|
||
// Notes médicales + clinique
|
||
$stmt = $db->prepare('
|
||
SELECT mn.*, COALESCE(dc.name, c.name) AS clinic_name
|
||
FROM medical_notes mn
|
||
LEFT JOIN clinics c ON c.id = mn.clinic_id
|
||
LEFT JOIN directory_contacts dc ON dc.id = mn.clinic_contact_id
|
||
WHERE mn.animal_id=:id
|
||
ORDER BY mn.noted_at DESC, mn.id DESC
|
||
');
|
||
$stmt->execute([':id' => $id]);
|
||
$medical_notes = $stmt->fetchAll();
|
||
|
||
// Photos médicales indexées par note
|
||
$stmt = $db->prepare('
|
||
SELECT id, medical_note_id, filename
|
||
FROM medical_photos
|
||
WHERE medical_note_id IN (
|
||
SELECT id FROM medical_notes WHERE animal_id = :id
|
||
)
|
||
ORDER BY medical_note_id ASC, id ASC
|
||
');
|
||
$stmt->execute([':id' => $id]);
|
||
$allMedicalPhotos = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
// Regroupe par note
|
||
$medicalPhotosByNote = [];
|
||
foreach ($allMedicalPhotos as $p) {
|
||
$medicalPhotosByNote[$p['medical_note_id']][] = $p;
|
||
}
|
||
|
||
// Traitements (tous, y compris terminés)
|
||
$stmt = $db->prepare('
|
||
SELECT t.*, m.name AS medication_name, m.molecule, m.form, COALESCE(dc.name, c.name) AS clinic_name
|
||
FROM treatments t
|
||
JOIN ref_medications m ON m.id = t.medication_id
|
||
LEFT JOIN clinics c ON c.id = t.clinic_id
|
||
LEFT JOIN directory_contacts dc ON dc.id = t.clinic_contact_id
|
||
WHERE t.animal_id=:id
|
||
ORDER BY t.ongoing DESC, t.start_date DESC, t.id DESC
|
||
');
|
||
$stmt->execute([':id' => $id]);
|
||
$treatments = $stmt->fetchAll();
|
||
|
||
// Vaccins
|
||
$stmt = $db->prepare('
|
||
SELECT v.*, rv.name AS vaccine_name, COALESCE(dc.name, c.name) AS clinic_name
|
||
FROM vaccinations v
|
||
JOIN ref_vaccines rv ON rv.id = v.vaccine_id
|
||
LEFT JOIN clinics c ON c.id = v.clinic_id
|
||
LEFT JOIN directory_contacts dc ON dc.id = v.clinic_contact_id
|
||
WHERE v.animal_id=:id
|
||
ORDER BY v.done_date DESC, v.id DESC
|
||
');
|
||
$stmt->execute([':id' => $id]);
|
||
$vaccinations = $stmt->fetchAll();
|
||
|
||
// Photos
|
||
$stmt = $db->prepare('SELECT * FROM animal_photos WHERE animal_id=:id ORDER BY is_primary DESC, id DESC');
|
||
$stmt->execute([':id' => $id]);
|
||
$photos = $stmt->fetchAll();
|
||
|
||
// Génère le HTML PDF via une vue dédiée
|
||
//$viewFile = __DIR__ . '/../Views/animal_pdf.php';
|
||
$viewFile = __DIR__ . '/../Views/animal_pdf_v2.php';
|
||
if (!file_exists($viewFile)) {
|
||
http_response_code(500);
|
||
echo h(t('error.missing_pdf_view'));
|
||
return;
|
||
}
|
||
|
||
// variables pour la vue
|
||
$title = 'Dossier - ' . ($animal['name'] ?? 'Animal #' . $id);
|
||
|
||
// Photo principale
|
||
$stmt = $db->prepare('
|
||
SELECT filename
|
||
FROM animal_photos
|
||
WHERE animal_id=:id AND is_primary=1
|
||
ORDER BY id DESC
|
||
LIMIT 1
|
||
');
|
||
$stmt->execute([':id' => $id]);
|
||
$primary_photo = $stmt->fetchColumn() ?: null;
|
||
|
||
// Dernière note médicale (résumé)
|
||
$stmt = $db->prepare('
|
||
SELECT noted_at, kind, reason, weight_kg, temperature_c, diagnosis, plan
|
||
FROM medical_notes
|
||
WHERE animal_id=:id
|
||
ORDER BY datetime(noted_at) DESC, id DESC
|
||
LIMIT 1
|
||
');
|
||
$stmt->execute([':id' => $id]);
|
||
$last_med = $stmt->fetch() ?: null;
|
||
|
||
// Prochain vaccin (due_date la plus proche)
|
||
$stmt = $db->prepare('
|
||
SELECT rv.name AS vaccine_name, v.due_date
|
||
FROM vaccinations v
|
||
JOIN ref_vaccines rv ON rv.id = v.vaccine_id
|
||
WHERE v.animal_id=:id AND v.due_date IS NOT NULL
|
||
ORDER BY date(v.due_date) ASC, v.id ASC
|
||
LIMIT 1
|
||
');
|
||
$stmt->execute([':id' => $id]);
|
||
$next_vaccine = $stmt->fetch() ?: null;
|
||
|
||
$generatedAt = new DateTimeImmutable('now', new DateTimeZone('Europe/Paris'));
|
||
$footerText =
|
||
'Généré par ' .
|
||
AppSettings::get('association_name') .
|
||
', le ' .
|
||
$generatedAt->format('d/m/Y') .
|
||
' à ' .
|
||
$generatedAt->format('H\\hi');
|
||
$internalCode = trim((string) ($animal['internal_code'] ?? ''));
|
||
if ($internalCode !== '') {
|
||
$footerText .= ' · Dossier ' . $internalCode;
|
||
}
|
||
header('Content-Type: text/html; charset=UTF-8');
|
||
require $viewFile;
|
||
}
|
||
|
||
public static function form(): void
|
||
{
|
||
$db = DB::pdo();
|
||
$id = (int) ($_GET['id'] ?? 0);
|
||
|
||
$animal = [
|
||
'id' => 0,
|
||
'name' => '',
|
||
'internal_code' => '',
|
||
'status' => 'refuge',
|
||
'species' => 'chat',
|
||
'sex' => 'U',
|
||
'sterilized' => 0,
|
||
'sterilization_status' => 'unknown',
|
||
'sterilization_date' => '',
|
||
'sterilization_vet_contact_id' => '',
|
||
'sterilization_clinic_contact_id' => '',
|
||
'refuge_room' => '',
|
||
'chip_id' => '',
|
||
'identification_type' => 'unknown',
|
||
'identification_date' => '',
|
||
'identification_registration_status' => 'unknown',
|
||
'adoption_availability' => 'unknown',
|
||
'adoption_available_from' => '',
|
||
'adoption_unavailability_reason' => '',
|
||
'website_published' => 0,
|
||
'website_description' => '',
|
||
'color' => '',
|
||
'hair_type' => '',
|
||
'birth_date' => '',
|
||
'notes' => '',
|
||
'intake_date' => '',
|
||
'intake_type' => 'unknown',
|
||
'intake_reason' => 'unknown',
|
||
'depositor_contact_id' => '',
|
||
'intake_circumstances' => '',
|
||
'intake_owner_care_home' => 0,
|
||
'quarantine_until' => '',
|
||
'compatibility_dogs' => 'unknown',
|
||
'compatibility_cats' => 'unknown',
|
||
'compatibility_children' => 'unknown',
|
||
'house_trained' => 'unknown',
|
||
'free_cat_site_name' => '',
|
||
'free_cat_city' => '',
|
||
'free_cat_address' => '',
|
||
'free_cat_caretaker_contact_id' => '',
|
||
'free_cat_captured_at' => '',
|
||
'free_cat_released_at' => '',
|
||
'free_cat_tracking_status' => 'present',
|
||
// adapte si tu as fiv/felv en colonnes
|
||
'fiv' => '',
|
||
'felv' => '',
|
||
];
|
||
|
||
if ($id > 0) {
|
||
$stmt = $db->prepare('SELECT * FROM animals WHERE id=:id');
|
||
$stmt->execute([':id' => $id]);
|
||
$row = $stmt->fetch();
|
||
if (!$row) {
|
||
http_response_code(404);
|
||
echo h(t('error.not_found'));
|
||
return;
|
||
}
|
||
$animal = array_merge($animal, $row);
|
||
if (!empty($animal['intake_owner_care_home'])) {
|
||
$animal['intake_reason'] = 'owner_care_home';
|
||
}
|
||
}
|
||
|
||
$fosterContacts = $db
|
||
->query(
|
||
"SELECT DISTINCT dc.id,dc.name,dc.address,dc.postal_code,dc.city FROM directory_contacts dc JOIN directory_contact_roles r ON r.contact_id=dc.id AND r.role='fa' WHERE dc.deleted_at IS NULL ORDER BY dc.name COLLATE NOCASE",
|
||
)
|
||
->fetchAll(PDO::FETCH_ASSOC);
|
||
$currentFosterId = 0;
|
||
if ($id > 0) {
|
||
$stmt = $db->prepare(
|
||
"SELECT contact_id FROM animal_placements WHERE animal_id=:id AND event_type='foster' ORDER BY date(event_date) DESC,id DESC LIMIT 1",
|
||
);
|
||
$stmt->execute([':id' => $id]);
|
||
$currentFosterId = (int) $stmt->fetchColumn();
|
||
}
|
||
|
||
$depositors = $db
|
||
->query(
|
||
"SELECT dc.id,dc.name,dc.phone,dc.city FROM directory_contacts dc JOIN directory_contact_roles r ON r.contact_id=dc.id AND r.role='deposant' WHERE dc.deleted_at IS NULL ORDER BY dc.name COLLATE NOCASE",
|
||
)
|
||
->fetchAll(PDO::FETCH_ASSOC);
|
||
$sterilizationVets = $db
|
||
->query(
|
||
"SELECT dc.id,dc.name FROM directory_contacts dc JOIN directory_contact_roles r ON r.contact_id=dc.id AND r.role='veterinaire' WHERE dc.deleted_at IS NULL ORDER BY dc.name COLLATE NOCASE",
|
||
)
|
||
->fetchAll(PDO::FETCH_ASSOC);
|
||
$sterilizationClinics = $db
|
||
->query(
|
||
"SELECT dc.id,dc.name FROM directory_contacts dc JOIN directory_contact_roles r ON r.contact_id=dc.id AND r.role='cabinet' WHERE dc.deleted_at IS NULL ORDER BY dc.name COLLATE NOCASE",
|
||
)
|
||
->fetchAll(PDO::FETCH_ASSOC);
|
||
$freeCatCaretakers = $db
|
||
->query(
|
||
"SELECT id,name,city FROM directory_contacts WHERE deleted_at IS NULL AND kind='person' ORDER BY name COLLATE NOCASE",
|
||
)
|
||
->fetchAll(PDO::FETCH_ASSOC);
|
||
render('animal_form.php', [
|
||
'title' => $id > 0 ? 'Modifier ' . $animal['name'] : 'Ajouter un animal',
|
||
'pageDescription' =>
|
||
$id > 0
|
||
? 'Modification de la fiche de ' . $animal['name'] . '.'
|
||
: 'Création d’une nouvelle fiche animal dans Globinours.',
|
||
'animal' => $animal,
|
||
'intakeTypes' => self::intakeTypes(),
|
||
'intakeReasons' => self::intakeReasons(),
|
||
'depositors' => $depositors,
|
||
'sterilizationVets' => $sterilizationVets,
|
||
'sterilizationClinics' => $sterilizationClinics,
|
||
'fosterContacts' => $fosterContacts,
|
||
'currentFosterId' => $currentFosterId,
|
||
'freeCatCaretakers' => $freeCatCaretakers,
|
||
'speciesOptions' => SpeciesService::activeWithCurrent((string) ($animal['species'] ?? 'chat')),
|
||
'shelterRooms' => ShelterRoomService::all(true),
|
||
]);
|
||
}
|
||
|
||
private static function nextInternalCode(string $name): string
|
||
{
|
||
$year = date('Y');
|
||
|
||
$base = strtoupper(trim($name));
|
||
$base = preg_replace('/[^A-Z0-9]+/u', '_', $base);
|
||
$base = trim($base, '_');
|
||
if ($base === '') {
|
||
$base = 'ANIMAL';
|
||
}
|
||
|
||
$db = DB::pdo();
|
||
$stmt = $db->prepare('
|
||
SELECT COUNT(*) FROM animals
|
||
WHERE internal_code LIKE :p
|
||
');
|
||
$stmt->execute([':p' => $year . '-%']);
|
||
$n = (int) $stmt->fetchColumn();
|
||
|
||
$seq = str_pad((string) ($n + 1), 3, '0', STR_PAD_LEFT);
|
||
return $year . '-' . $seq . '-' . $base;
|
||
}
|
||
|
||
public static function save(): void
|
||
{
|
||
require_once __DIR__ . '/../Services/GeocodeService.php';
|
||
require_once __DIR__ . '/../Services/GeoService.php';
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||
http_response_code(405);
|
||
return;
|
||
}
|
||
|
||
$db = DB::pdo();
|
||
|
||
$id = (int) ($_POST['id'] ?? 0);
|
||
$old = null;
|
||
if ($id > 0) {
|
||
$st = $db->prepare('SELECT * FROM animals WHERE id=:id');
|
||
$st->execute([':id' => $id]);
|
||
$old = $st->fetch(PDO::FETCH_ASSOC) ?: null;
|
||
}
|
||
$isArchivedCorrection = $old && !empty($old['archived_at']);
|
||
|
||
$name = trim((string) ($_POST['name'] ?? ''));
|
||
$status = trim((string) ($_POST['status'] ?? 'refuge'));
|
||
if ($isArchivedCorrection) {
|
||
$status = (string) $old['status'];
|
||
}
|
||
if (in_array(strtolower($status), ['decede', 'décédé'], true)) {
|
||
$deathCheck = $db->prepare('SELECT 1 FROM animal_deaths WHERE animal_id=:id');
|
||
$deathCheck->execute([':id' => $id]);
|
||
if (!$deathCheck->fetchColumn()) {
|
||
http_response_code(400);
|
||
echo h(t('error.use_death_action'));
|
||
return;
|
||
}
|
||
}
|
||
$refugeRoom = trim((string) ($_POST['refuge_room'] ?? ''));
|
||
$locationChangeReason = trim((string) ($_POST['location_change_reason'] ?? ''));
|
||
$availableRooms = ShelterRoomService::byCode(true);
|
||
if (!isset($availableRooms[$refugeRoom]) || (string) $availableRooms[$refugeRoom]['status_code'] !== $status) {
|
||
$refugeRoom = null;
|
||
}
|
||
|
||
$species = SpeciesService::normalize((string) ($_POST['species'] ?? 'chat'));
|
||
if (!SpeciesService::exists($species, $id <= 0)) {
|
||
http_response_code(400);
|
||
echo h(t('error.valid_species'));
|
||
return;
|
||
}
|
||
if ($status === 'chat_libre' && !SpeciesService::isCat($species)) {
|
||
http_response_code(400);
|
||
echo h(t('error.freecat_cats_only'));
|
||
return;
|
||
}
|
||
|
||
$sex = trim((string) ($_POST['sex'] ?? 'U'));
|
||
$sterilizationStatus = (string) ($_POST['sterilization_status'] ?? 'unknown');
|
||
if (!in_array($sterilizationStatus, ['unknown', 'no', 'yes'], true)) {
|
||
$sterilizationStatus = 'unknown';
|
||
}
|
||
$sterilized = $sterilizationStatus === 'yes' ? 1 : 0;
|
||
$sterilizationDate = trim((string) ($_POST['sterilization_date'] ?? ''));
|
||
if ($sterilizationDate !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $sterilizationDate)) {
|
||
http_response_code(400);
|
||
echo h(t('error.invalid_sterilization_date'));
|
||
return;
|
||
}
|
||
$sterilizationVetId = (int) ($_POST['sterilization_vet_contact_id'] ?? 0) ?: null;
|
||
$sterilizationClinicId = (int) ($_POST['sterilization_clinic_contact_id'] ?? 0) ?: null;
|
||
if ($sterilizationStatus !== 'yes') {
|
||
$sterilizationDate = '';
|
||
$sterilizationVetId = null;
|
||
$sterilizationClinicId = null;
|
||
}
|
||
$chip_id = trim((string) ($_POST['chip_id'] ?? ''));
|
||
$identificationType = (string) ($_POST['identification_type'] ?? 'unknown');
|
||
if (!in_array($identificationType, ['unknown', 'microchip', 'tattoo'], true)) {
|
||
$identificationType = 'unknown';
|
||
}
|
||
$identificationDate = trim((string) ($_POST['identification_date'] ?? ''));
|
||
$identificationRegistration = (string) ($_POST['identification_registration_status'] ?? 'unknown');
|
||
if (!in_array($identificationRegistration, ['unknown', 'pending', 'registered'], true)) {
|
||
$identificationRegistration = 'unknown';
|
||
}
|
||
if ($chip_id === '') {
|
||
$identificationType = 'unknown';
|
||
$identificationDate = '';
|
||
$identificationRegistration = 'unknown';
|
||
}
|
||
$adoptionAvailability = (string) ($_POST['adoption_availability'] ?? 'unknown');
|
||
if (!in_array($adoptionAvailability, ['unknown', 'not_available', 'available'], true)) {
|
||
$adoptionAvailability = 'unknown';
|
||
}
|
||
$adoptionAvailableFrom = trim((string) ($_POST['adoption_available_from'] ?? ''));
|
||
$adoptionUnavailabilityReason = trim((string) ($_POST['adoption_unavailability_reason'] ?? ''));
|
||
foreach ([$identificationDate, $adoptionAvailableFrom] as $dateValue) {
|
||
if ($dateValue !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $dateValue)) {
|
||
http_response_code(400);
|
||
echo h(t('error.invalid_date'));
|
||
return;
|
||
}
|
||
}
|
||
if ($adoptionAvailability !== 'available') {
|
||
$adoptionAvailableFrom = '';
|
||
}
|
||
if ($adoptionAvailability !== 'not_available') {
|
||
$adoptionUnavailabilityReason = '';
|
||
}
|
||
$websitePublished = isset($_POST['website_published']) ? 1 : 0;
|
||
$websiteDescription = mb_substr(trim((string) ($_POST['website_description'] ?? '')), 0, 5000);
|
||
$breed = trim((string) ($_POST['breed'] ?? ''));
|
||
$color = trim((string) ($_POST['color'] ?? ''));
|
||
$hairType = trim((string) ($_POST['hair_type'] ?? ''));
|
||
$profileValue = static function (string $field): string {
|
||
$value = (string) ($_POST[$field] ?? 'unknown');
|
||
return in_array($value, ['unknown', 'yes', 'no'], true) ? $value : 'unknown';
|
||
};
|
||
$compatibilityDogs = $profileValue('compatibility_dogs');
|
||
$compatibilityCats = $profileValue('compatibility_cats');
|
||
$compatibilityChildren = $profileValue('compatibility_children');
|
||
$houseTrained = $profileValue('house_trained');
|
||
$freeCatSiteName = trim((string) ($_POST['free_cat_site_name'] ?? ''));
|
||
$freeCatCity = trim((string) ($_POST['free_cat_city'] ?? ''));
|
||
$freeCatAddress = trim((string) ($_POST['free_cat_address'] ?? ''));
|
||
$freeCatCaretakerId = (int) ($_POST['free_cat_caretaker_contact_id'] ?? 0) ?: null;
|
||
$freeCatCapturedAt = trim((string) ($_POST['free_cat_captured_at'] ?? ''));
|
||
$freeCatReleasedAt = trim((string) ($_POST['free_cat_released_at'] ?? ''));
|
||
$freeCatTrackingStatus = (string) ($_POST['free_cat_tracking_status'] ?? 'present');
|
||
if (!in_array($freeCatTrackingStatus, ['present', 'missing', 'moved', 'deceased'], true)) {
|
||
$freeCatTrackingStatus = 'present';
|
||
}
|
||
foreach ([$freeCatCapturedAt, $freeCatReleasedAt] as $freeCatDate) {
|
||
if ($freeCatDate !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $freeCatDate)) {
|
||
http_response_code(400);
|
||
echo h(t('error.invalid_freecat_date'));
|
||
return;
|
||
}
|
||
}
|
||
if ($status !== 'chat_libre') {
|
||
$freeCatSiteName = '';
|
||
$freeCatCity = '';
|
||
$freeCatAddress = '';
|
||
$freeCatCaretakerId = null;
|
||
$freeCatCapturedAt = '';
|
||
$freeCatReleasedAt = '';
|
||
$freeCatTrackingStatus = 'present';
|
||
} elseif ($freeCatCaretakerId) {
|
||
$check = $db->prepare(
|
||
"SELECT 1 FROM directory_contacts WHERE id=:id AND deleted_at IS NULL AND kind='person'",
|
||
);
|
||
$check->execute([':id' => $freeCatCaretakerId]);
|
||
if (!$check->fetchColumn()) {
|
||
$freeCatCaretakerId = null;
|
||
}
|
||
}
|
||
|
||
$notes = trim((string) ($_POST['notes'] ?? ''));
|
||
|
||
$intake_date = trim((string) ($_POST['intake_date'] ?? '')); // YYYY-MM-DD
|
||
$intakeType = (string) ($_POST['intake_type'] ?? 'unknown');
|
||
if (!isset(self::INTAKE_TYPES[$intakeType])) {
|
||
$intakeType = 'unknown';
|
||
}
|
||
$submittedIntakeReason = (string) ($_POST['intake_reason'] ?? 'unknown');
|
||
if (!isset(self::INTAKE_REASONS[$submittedIntakeReason])) {
|
||
$submittedIntakeReason = 'unknown';
|
||
}
|
||
$intakeOwnerCareHome = $submittedIntakeReason === 'owner_care_home' ? 1 : 0;
|
||
$intakeReason = $intakeOwnerCareHome ? 'owner_health' : $submittedIntakeReason;
|
||
$intakeCircumstances = trim((string) ($_POST['intake_circumstances'] ?? ''));
|
||
|
||
// Naissance (date + estimation)
|
||
$birth_date = trim((string) ($_POST['birth_date'] ?? ''));
|
||
$birth_is_estimated = (int) ($_POST['birth_is_estimated'] ?? 0 ? 1 : 0);
|
||
|
||
$birth_est_years = trim((string) ($_POST['birth_est_years'] ?? ''));
|
||
$birth_est_months = trim((string) ($_POST['birth_est_months'] ?? ''));
|
||
|
||
$birth_est_years = $birth_est_years === '' ? 0 : (int) $birth_est_years;
|
||
$birth_est_months = $birth_est_months === '' ? 0 : (int) $birth_est_months;
|
||
|
||
$birth_estimated_months = $birth_est_years * 12 + $birth_est_months;
|
||
if ($birth_estimated_months === 0) {
|
||
$birth_estimated_months = null;
|
||
}
|
||
|
||
// FIV/FELV (selon ton stockage actuel)
|
||
$fiv = array_key_exists('fiv', $_POST) ? (isset($_POST['fiv']) ? 1 : 0) : null;
|
||
$felv = array_key_exists('felv', $_POST) ? (isset($_POST['felv']) ? 1 : 0) : null;
|
||
|
||
// Validations minimales
|
||
if ($name === '') {
|
||
http_response_code(400);
|
||
echo h(t('error.name_required'));
|
||
return;
|
||
}
|
||
if ($breed === '' && SpeciesService::isCat($species)) {
|
||
$breed = 'Chat Européen';
|
||
}
|
||
if ($sex === '') {
|
||
$sex = 'U';
|
||
}
|
||
|
||
// Si naissance estimée et date vide => calcul auto depuis "mois"
|
||
if ($birth_is_estimated === 1 && $birth_date === '' && $birth_estimated_months !== null) {
|
||
$dt = new DateTime('now');
|
||
$dt->modify('-' . $birth_estimated_months . ' months');
|
||
$birth_date = $dt->format('Y-m-d');
|
||
}
|
||
|
||
// Normaliser dates vides en NULL (SQLite aime mieux)
|
||
$birth_date = $birth_date === '' ? null : $birth_date;
|
||
$intake_date = $intake_date === '' ? null : $intake_date;
|
||
|
||
// Code interne : auto si vide (et idéalement readonly en edit)
|
||
$internal_code = trim((string) ($_POST['internal_code'] ?? ''));
|
||
if ($internal_code === '') {
|
||
$internal_code = self::nextInternalCode($name);
|
||
}
|
||
|
||
// Quarantaine : durée issue des paramètres généraux.
|
||
$quarantine_until = null;
|
||
if (strtolower($status) === 'quarantaine' && $intake_date !== null) {
|
||
$ts = strtotime($intake_date);
|
||
if ($ts !== false) {
|
||
$quarantine_until = date('Y-m-d', $ts + AppSettings::int('quarantine_days', 1, 90) * 86400);
|
||
}
|
||
} else {
|
||
// si pas en quarantaine, on vide la sortie (optionnel, mais logique)
|
||
$quarantine_until = null;
|
||
}
|
||
|
||
$rescueAddress = trim((string) ($_POST['rescue_address'] ?? ''));
|
||
$rescueLocationName = trim((string) ($_POST['rescue_location_name'] ?? ''));
|
||
$currentAddress = trim((string) ($_POST['current_address'] ?? ''));
|
||
$fosterContactId = (int) ($_POST['foster_contact_id'] ?? 0) ?: null;
|
||
$fosterContact = null;
|
||
if (!$isArchivedCorrection && in_array($status, ['fa', 'fa_permanente'], true) && $fosterContactId) {
|
||
$st = $db->prepare(
|
||
"SELECT dc.* FROM directory_contacts dc JOIN directory_contact_roles r ON r.contact_id=dc.id AND r.role='fa' WHERE dc.id=:id AND dc.deleted_at IS NULL",
|
||
);
|
||
$st->execute([':id' => $fosterContactId]);
|
||
$fosterContact = $st->fetch(PDO::FETCH_ASSOC) ?: null;
|
||
if (!$fosterContact) {
|
||
http_response_code(400);
|
||
echo h(t('error.invalid_foster'));
|
||
return;
|
||
}
|
||
$currentAddress =
|
||
implode(
|
||
' ',
|
||
array_filter([
|
||
$fosterContact['address'] ?? '',
|
||
$fosterContact['postal_code'] ?? '',
|
||
$fosterContact['city'] ?? '',
|
||
]),
|
||
) ?:
|
||
'Chez ' . $fosterContact['name'];
|
||
}
|
||
if ($status === 'chat_libre') {
|
||
$currentAddress =
|
||
trim(implode(' ', array_filter([$freeCatAddress, $freeCatCity]))) ?:
|
||
($freeCatSiteName ?:
|
||
$currentAddress);
|
||
}
|
||
|
||
$depositor = self::resolveDirectoryContact(
|
||
$db,
|
||
(int) ($_POST['depositor_contact_id'] ?? 0),
|
||
trim((string) ($_POST['new_depositor_name'] ?? '')),
|
||
'person',
|
||
'deposant',
|
||
);
|
||
$depositorId = $depositor ? (int) $depositor['id'] : null;
|
||
|
||
// Pour l’instant, tu bloques le lieu actuel
|
||
if ($isArchivedCorrection) {
|
||
$currentAddress = (string) ($old['current_address'] ?? '');
|
||
}
|
||
if ($currentAddress === '' && !$isArchivedCorrection) {
|
||
$currentAddress = '5 RUE NOTRE DAME 29260 LESNEVEN';
|
||
}
|
||
|
||
// Géocode rescue si adresse présente
|
||
$rescueLat = null;
|
||
$rescueLng = null;
|
||
if ($rescueAddress !== '') {
|
||
$geo = GeoService::geocode($db, $rescueAddress);
|
||
if ($geo) {
|
||
$rescueLat = $geo['lat'];
|
||
$rescueLng = $geo['lng'];
|
||
}
|
||
}
|
||
|
||
// Géocode current (toujours)
|
||
$currentLat = null;
|
||
$currentLng = null;
|
||
if ($currentAddress !== '' && !$isArchivedCorrection) {
|
||
$geo2 = GeoService::geocode($db, $currentAddress);
|
||
if ($geo2) {
|
||
$currentLat = $geo2['lat'];
|
||
$currentLng = $geo2['lng'];
|
||
}
|
||
}
|
||
|
||
$fiv = $fiv ?? (int) ($old['fiv'] ?? 0);
|
||
$felv = $felv ?? (int) ($old['felv'] ?? 0);
|
||
$careBox = in_array($status, ['soin', 'quarantaine'], true) ? $old['care_box_key'] ?? null : null;
|
||
if ($isArchivedCorrection) {
|
||
$refugeRoom = $old['refuge_room'] ?? null;
|
||
$careBox = $old['care_box_key'] ?? null;
|
||
$quarantine_until = $old['quarantine_until'] ?? null;
|
||
$currentLat = $old['current_lat'] ?? null;
|
||
$currentLng = $old['current_lng'] ?? null;
|
||
}
|
||
|
||
if ($id > 0) {
|
||
$stmt = $db->prepare("
|
||
UPDATE animals SET
|
||
name=:name,
|
||
internal_code=:internal_code,
|
||
status=:status,
|
||
refuge_room=:refuge_room,
|
||
care_box_key=:care_box_key,
|
||
species=:species,
|
||
sex=:sex,
|
||
sterilized=:sterilized,
|
||
sterilization_status=:sterilization_status,sterilization_date=:sterilization_date,sterilization_vet_contact_id=:sterilization_vet,sterilization_clinic_contact_id=:sterilization_clinic,
|
||
chip_id=:chip_id,
|
||
identification_type=:identification_type,identification_date=:identification_date,identification_registration_status=:identification_registration_status,
|
||
adoption_availability=:adoption_availability,adoption_available_from=:adoption_available_from,adoption_unavailability_reason=:adoption_unavailability_reason,
|
||
website_published=:website_published,website_description=:website_description,
|
||
breed=:breed,
|
||
color=:color,
|
||
hair_type=:hair_type,
|
||
compatibility_dogs=:compatibility_dogs,
|
||
compatibility_cats=:compatibility_cats,
|
||
compatibility_children=:compatibility_children,
|
||
house_trained=:house_trained,
|
||
free_cat_site_name=:free_cat_site_name,free_cat_city=:free_cat_city,free_cat_address=:free_cat_address,
|
||
free_cat_caretaker_contact_id=:free_cat_caretaker_contact_id,free_cat_captured_at=:free_cat_captured_at,
|
||
free_cat_released_at=:free_cat_released_at,free_cat_tracking_status=:free_cat_tracking_status,
|
||
birth_date=:birth_date,
|
||
birth_is_estimated=:birth_is_estimated,
|
||
birth_estimated_months=:birth_estimated_months,
|
||
notes=:notes,
|
||
intake_date=:intake_date,
|
||
intake_type=:intake_type,
|
||
intake_reason=:intake_reason,
|
||
intake_owner_care_home=:intake_owner_care_home,
|
||
depositor_contact_id=:depositor_contact_id,
|
||
intake_circumstances=:intake_circumstances,
|
||
quarantine_until=:quarantine_until,
|
||
fiv=:fiv,
|
||
felv=:felv,
|
||
rescue_address=:rescue_address,rescue_location_name=:rescue_location_name,
|
||
rescue_location_confidence=CASE WHEN COALESCE(:rescue_location_name,'')=COALESCE(rescue_location_name,'') AND COALESCE(:rescue_address,'')=COALESCE(rescue_address,'') THEN rescue_location_confidence WHEN TRIM(COALESCE(:rescue_location_name,''))<>'' OR TRIM(COALESCE(:rescue_address,''))<>'' THEN 'documented' ELSE NULL END,
|
||
rescue_location_evidence=CASE WHEN COALESCE(:rescue_location_name,'')=COALESCE(rescue_location_name,'') AND COALESCE(:rescue_address,'')=COALESCE(rescue_address,'') THEN rescue_location_evidence WHEN TRIM(COALESCE(:rescue_location_name,''))<>'' OR TRIM(COALESCE(:rescue_address,''))<>'' THEN 'Lieu renseigné depuis la fiche animale' ELSE NULL END,
|
||
rescue_lat=:rescue_lat, rescue_lng=:rescue_lng,
|
||
current_address=:current_address, current_lat=:current_lat, current_lng=:current_lng,
|
||
updated_at=datetime('now')
|
||
WHERE id=:id
|
||
");
|
||
|
||
$stmt->execute([
|
||
':id' => $id,
|
||
':name' => $name,
|
||
':internal_code' => $internal_code,
|
||
':status' => $status,
|
||
':refuge_room' => $refugeRoom,
|
||
':care_box_key' => $careBox,
|
||
':species' => $species,
|
||
':sex' => $sex,
|
||
':sterilized' => $sterilized,
|
||
':sterilization_status' => $sterilizationStatus,
|
||
':sterilization_date' => $sterilizationDate ?: null,
|
||
':sterilization_vet' => $sterilizationVetId,
|
||
':sterilization_clinic' => $sterilizationClinicId,
|
||
':chip_id' => $chip_id,
|
||
':identification_type' => $identificationType,
|
||
':identification_date' => $identificationDate ?: null,
|
||
':identification_registration_status' => $identificationRegistration,
|
||
':adoption_availability' => $adoptionAvailability,
|
||
':adoption_available_from' => $adoptionAvailableFrom ?: null,
|
||
':adoption_unavailability_reason' => $adoptionUnavailabilityReason ?: null,
|
||
':website_published' => $websitePublished,
|
||
':website_description' => $websiteDescription ?: null,
|
||
':breed' => $breed,
|
||
':color' => $color !== '' ? $color : null,
|
||
':hair_type' => $hairType !== '' ? $hairType : null,
|
||
':compatibility_dogs' => $compatibilityDogs,
|
||
':compatibility_cats' => $compatibilityCats,
|
||
':compatibility_children' => $compatibilityChildren,
|
||
':house_trained' => $houseTrained,
|
||
':free_cat_site_name' => $freeCatSiteName ?: null,
|
||
':free_cat_city' => $freeCatCity ?: null,
|
||
':free_cat_address' => $freeCatAddress ?: null,
|
||
':free_cat_caretaker_contact_id' => $freeCatCaretakerId,
|
||
':free_cat_captured_at' => $freeCatCapturedAt ?: null,
|
||
':free_cat_released_at' => $freeCatReleasedAt ?: null,
|
||
':free_cat_tracking_status' => $freeCatTrackingStatus,
|
||
':birth_date' => $birth_date,
|
||
':birth_is_estimated' => $birth_is_estimated,
|
||
':birth_estimated_months' => $birth_estimated_months,
|
||
':notes' => $notes,
|
||
':intake_date' => $intake_date,
|
||
':intake_type' => $intakeType,
|
||
':intake_reason' => $intakeReason,
|
||
':intake_owner_care_home' => $intakeOwnerCareHome,
|
||
':depositor_contact_id' => $depositorId,
|
||
':intake_circumstances' => $intakeCircumstances ?: null,
|
||
':quarantine_until' => $quarantine_until,
|
||
':fiv' => $fiv,
|
||
':felv' => $felv,
|
||
':rescue_address' => $rescueAddress,
|
||
':rescue_location_name' => $rescueLocationName ?: null,
|
||
':rescue_lat' => $rescueLat,
|
||
':rescue_lng' => $rescueLng,
|
||
':current_address' => $currentAddress,
|
||
':current_lat' => $currentLat,
|
||
':current_lng' => $currentLng,
|
||
]);
|
||
|
||
if ($old) {
|
||
LocationHistoryService::record(
|
||
$db,
|
||
$id,
|
||
$old,
|
||
[
|
||
'status' => $status,
|
||
'refuge_room' => $refugeRoom,
|
||
'care_box_key' => $careBox,
|
||
'current_address' => $currentAddress,
|
||
],
|
||
'animal_form',
|
||
$locationChangeReason ?: 'Emplacement modifié depuis la fiche animale',
|
||
);
|
||
}
|
||
|
||
//self::logHistory(
|
||
// $id,
|
||
// 'update',
|
||
// 'Fiche modifiée'
|
||
//);
|
||
|
||
// Code interne : en édition, on ne le regénère PAS
|
||
$internal_code = trim((string) ($_POST['internal_code'] ?? ''));
|
||
if ($id > 0) {
|
||
if ($internal_code === '' && $old && !empty($old['internal_code'])) {
|
||
$internal_code = (string) $old['internal_code'];
|
||
}
|
||
} else {
|
||
if ($internal_code === '') {
|
||
$internal_code = self::nextInternalCode($name);
|
||
}
|
||
}
|
||
|
||
if ($old) {
|
||
// Les nouvelles valeurs “connues” (ce que tu viens de sauver)
|
||
// Astuce: tu mets TOUT ce que tu passes à l’UPDATE ici, mais sans maintenance lourde :
|
||
// si tu ajoutes un champ au formulaire + UPDATE, tu l’ajoutes déjà ici de toute façon.
|
||
$new = [
|
||
'name' => $name,
|
||
'internal_code' => $internal_code,
|
||
'status' => $status,
|
||
'refuge_room' => $refugeRoom,
|
||
'species' => $species,
|
||
'sex' => $sex,
|
||
'sterilized' => $sterilized,
|
||
'sterilization_status' => $sterilizationStatus,
|
||
'sterilization_date' => $sterilizationDate,
|
||
'sterilization_vet_contact_id' => $sterilizationVetId,
|
||
'sterilization_clinic_contact_id' => $sterilizationClinicId,
|
||
'chip_id' => $chip_id,
|
||
'identification_type' => $identificationType,
|
||
'identification_date' => $identificationDate,
|
||
'identification_registration_status' => $identificationRegistration,
|
||
'adoption_availability' => $adoptionAvailability,
|
||
'adoption_available_from' => $adoptionAvailableFrom,
|
||
'adoption_unavailability_reason' => $adoptionUnavailabilityReason,
|
||
'website_published' => $websitePublished,
|
||
'website_description' => $websiteDescription,
|
||
'breed' => $breed,
|
||
'color' => $color,
|
||
'hair_type' => $hairType,
|
||
'compatibility_dogs' => $compatibilityDogs,
|
||
'compatibility_cats' => $compatibilityCats,
|
||
'compatibility_children' => $compatibilityChildren,
|
||
'house_trained' => $houseTrained,
|
||
'birth_date' => $birth_date,
|
||
'notes' => $notes,
|
||
'intake_date' => $intake_date,
|
||
'intake_type' => $intakeType,
|
||
'intake_reason' => $intakeReason,
|
||
'intake_owner_care_home' => $intakeOwnerCareHome,
|
||
'depositor_contact_id' => $depositorId,
|
||
'intake_circumstances' => $intakeCircumstances,
|
||
'rescue_location_name' => $rescueLocationName,
|
||
'quarantine_until' => $quarantine_until,
|
||
'fiv' => $fiv,
|
||
'felv' => $felv,
|
||
// ajoute ici uniquement les champs que tu SAUVEGARDES dans UPDATE
|
||
// (si demain tu ajoutes 3 champs au form, tu les ajouteras déjà à l’UPDATE -> copie/colle aussi ici)
|
||
];
|
||
|
||
// Champs à ignorer (bruit)
|
||
$ignore = ['id', 'created_at', 'updated_at', 'internal_code'];
|
||
|
||
$changes = [];
|
||
foreach ($new as $field => $newVal) {
|
||
if (in_array($field, $ignore, true)) {
|
||
continue;
|
||
}
|
||
|
||
$oldVal = $old[$field] ?? null;
|
||
|
||
// Normalisation légère pour éviter les faux positifs
|
||
$o = is_null($oldVal) ? '' : (string) $oldVal;
|
||
$n = is_null($newVal) ? '' : (string) $newVal;
|
||
|
||
if ($o !== $n) {
|
||
$label = match ($field) {
|
||
'name' => 'Nom',
|
||
'status' => 'Statut',
|
||
'refuge_room' => 'Salle au refuge',
|
||
'species' => 'Espèce',
|
||
'sex' => 'Sexe',
|
||
'sterilized' => 'Stérilisation',
|
||
'sterilization_status' => 'État de stérilisation',
|
||
'sterilization_date' => 'Date de stérilisation',
|
||
'sterilization_vet_contact_id' => 'Vétérinaire de stérilisation',
|
||
'sterilization_clinic_contact_id' => 'Cabinet de stérilisation',
|
||
'chip_id' => 'Puce',
|
||
'identification_type' => 'Type d’identification',
|
||
'identification_date' => 'Date d’identification',
|
||
'identification_registration_status' => 'Enregistrement I-CAD',
|
||
'adoption_availability' => 'Disponibilité à l’adoption',
|
||
'adoption_available_from' => 'Disponible à partir du',
|
||
'adoption_unavailability_reason' => 'Motif d’indisponibilité',
|
||
'website_published' => 'Publication sur le site',
|
||
'website_description' => 'Présentation publique',
|
||
'breed' => 'Race',
|
||
'color' => 'Couleur / robe',
|
||
'hair_type' => 'Type de poil',
|
||
'compatibility_dogs' => 'Compatibilité chiens',
|
||
'compatibility_cats' => 'Compatibilité chats',
|
||
'compatibility_children' => 'Compatibilité enfants',
|
||
'house_trained' => 'Propreté',
|
||
'birth_date' => 'Naissance',
|
||
'intake_date' => 'Arrivée',
|
||
'intake_type' => 'Mode d’entrée',
|
||
'intake_reason' => 'Motif d’entrée',
|
||
'intake_owner_care_home' => 'Entrée du propriétaire en établissement',
|
||
'depositor_contact_id' => 'Déposant',
|
||
'intake_circumstances' => 'Circonstances de l’entrée',
|
||
'rescue_location_name' => 'Commune d’origine',
|
||
'quarantine_until' => 'Fin quarantaine',
|
||
'fiv' => 'FIV',
|
||
'felv' => 'FELV',
|
||
'notes' => 'Notes',
|
||
default => $field,
|
||
};
|
||
|
||
// Evite d’exploser le log sur un gros champ notes
|
||
if (in_array($field, ['notes', 'website_description'], true)) {
|
||
$changes[] = "$label : (modifié)";
|
||
} else {
|
||
if (
|
||
in_array(
|
||
$field,
|
||
[
|
||
'compatibility_dogs',
|
||
'compatibility_cats',
|
||
'compatibility_children',
|
||
'house_trained',
|
||
],
|
||
true,
|
||
)
|
||
) {
|
||
$profileLabels = ['unknown' => 'Inconnu', 'yes' => 'Oui', 'no' => 'Non'];
|
||
$o = $profileLabels[$o] ?? $o;
|
||
$n = $profileLabels[$n] ?? $n;
|
||
}
|
||
$changes[] = "$label : " . ($o === '' ? '∅' : $o) . ' → ' . ($n === '' ? '∅' : $n);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!empty($changes)) {
|
||
$statusChanged = (string) ($old['status'] ?? '') !== (string) $status;
|
||
$type = $statusChanged ? 'status' : 'update';
|
||
$label = $statusChanged ? 'Changement de statut' : 'Fiche modifiée';
|
||
|
||
self::logHistory($id, $type, $label, implode("\n", $changes));
|
||
}
|
||
}
|
||
} else {
|
||
$stmt = $db->prepare("
|
||
INSERT INTO animals
|
||
(name, internal_code, status, refuge_room, species, sex, sterilized,sterilization_status,sterilization_date,sterilization_vet_contact_id,sterilization_clinic_contact_id, chip_id,identification_type,identification_date,identification_registration_status,adoption_availability,adoption_available_from,adoption_unavailability_reason,website_published,website_description, breed, color, hair_type,
|
||
compatibility_dogs,compatibility_cats,compatibility_children,house_trained,
|
||
free_cat_site_name,free_cat_city,free_cat_address,free_cat_caretaker_contact_id,free_cat_captured_at,free_cat_released_at,free_cat_tracking_status,
|
||
birth_date, birth_is_estimated, birth_estimated_months,
|
||
notes,intake_date,intake_type,intake_reason,intake_owner_care_home,depositor_contact_id,intake_circumstances,quarantine_until,fiv,felv,created_at,updated_at,rescue_address,rescue_location_name,rescue_location_confidence,rescue_location_evidence,rescue_lat,rescue_lng,current_address,current_lat,current_lng)
|
||
VALUES
|
||
(:name,:internal_code,:status,:refuge_room,:species,:sex,:sterilized,:sterilization_status,:sterilization_date,:sterilization_vet,:sterilization_clinic,:chip_id,:identification_type,:identification_date,:identification_registration_status,:adoption_availability,:adoption_available_from,:adoption_unavailability_reason,:website_published,:website_description,:breed,:color,:hair_type,
|
||
:compatibility_dogs,:compatibility_cats,:compatibility_children,:house_trained,
|
||
:free_cat_site_name,:free_cat_city,:free_cat_address,:free_cat_caretaker_contact_id,:free_cat_captured_at,:free_cat_released_at,:free_cat_tracking_status,
|
||
:birth_date,:birth_is_estimated,:birth_estimated_months,
|
||
:notes,:intake_date,:intake_type,:intake_reason,:intake_owner_care_home,:depositor_contact_id,:intake_circumstances,:quarantine_until,:fiv,:felv,datetime('now'),datetime('now'),
|
||
:rescue_address,:rescue_location_name,CASE WHEN TRIM(COALESCE(:rescue_location_name,''))<>'' OR TRIM(COALESCE(:rescue_address,''))<>'' THEN 'documented' ELSE NULL END,CASE WHEN TRIM(COALESCE(:rescue_location_name,''))<>'' OR TRIM(COALESCE(:rescue_address,''))<>'' THEN 'Lieu renseigné à la création de la fiche animale' ELSE NULL END,:rescue_lat,:rescue_lng,:current_address,:current_lat,:current_lng)
|
||
");
|
||
|
||
$stmt->execute([
|
||
':name' => $name,
|
||
':internal_code' => $internal_code,
|
||
':status' => $status,
|
||
':refuge_room' => $refugeRoom,
|
||
':species' => $species,
|
||
':sex' => $sex,
|
||
':sterilized' => $sterilized,
|
||
':sterilization_status' => $sterilizationStatus,
|
||
':sterilization_date' => $sterilizationDate ?: null,
|
||
':sterilization_vet' => $sterilizationVetId,
|
||
':sterilization_clinic' => $sterilizationClinicId,
|
||
':chip_id' => $chip_id,
|
||
':identification_type' => $identificationType,
|
||
':identification_date' => $identificationDate ?: null,
|
||
':identification_registration_status' => $identificationRegistration,
|
||
':adoption_availability' => $adoptionAvailability,
|
||
':adoption_available_from' => $adoptionAvailableFrom ?: null,
|
||
':adoption_unavailability_reason' => $adoptionUnavailabilityReason ?: null,
|
||
':website_published' => $websitePublished,
|
||
':website_description' => $websiteDescription ?: null,
|
||
':breed' => $breed,
|
||
':color' => $color !== '' ? $color : null,
|
||
':hair_type' => $hairType !== '' ? $hairType : null,
|
||
':compatibility_dogs' => $compatibilityDogs,
|
||
':compatibility_cats' => $compatibilityCats,
|
||
':compatibility_children' => $compatibilityChildren,
|
||
':house_trained' => $houseTrained,
|
||
':free_cat_site_name' => $freeCatSiteName ?: null,
|
||
':free_cat_city' => $freeCatCity ?: null,
|
||
':free_cat_address' => $freeCatAddress ?: null,
|
||
':free_cat_caretaker_contact_id' => $freeCatCaretakerId,
|
||
':free_cat_captured_at' => $freeCatCapturedAt ?: null,
|
||
':free_cat_released_at' => $freeCatReleasedAt ?: null,
|
||
':free_cat_tracking_status' => $freeCatTrackingStatus,
|
||
':birth_date' => $birth_date,
|
||
':birth_is_estimated' => $birth_is_estimated,
|
||
':birth_estimated_months' => $birth_estimated_months,
|
||
':notes' => $notes,
|
||
':intake_date' => $intake_date,
|
||
':intake_type' => $intakeType,
|
||
':intake_reason' => $intakeReason,
|
||
':intake_owner_care_home' => $intakeOwnerCareHome,
|
||
':depositor_contact_id' => $depositorId,
|
||
':intake_circumstances' => $intakeCircumstances ?: null,
|
||
':quarantine_until' => $quarantine_until,
|
||
':fiv' => $fiv,
|
||
':felv' => $felv,
|
||
':rescue_address' => $rescueAddress,
|
||
':rescue_location_name' => $rescueLocationName ?: null,
|
||
':rescue_lat' => $rescueLat,
|
||
':rescue_lng' => $rescueLng,
|
||
':current_address' => $currentAddress,
|
||
':current_lat' => $currentLat,
|
||
':current_lng' => $currentLng,
|
||
]);
|
||
|
||
$id = (int) $db->lastInsertId();
|
||
|
||
LocationHistoryService::record(
|
||
$db,
|
||
$id,
|
||
null,
|
||
[
|
||
'status' => $status,
|
||
'refuge_room' => $refugeRoom,
|
||
'care_box_key' => null,
|
||
'current_address' => $currentAddress,
|
||
],
|
||
'animal_creation',
|
||
'Emplacement lors de la création de la fiche',
|
||
);
|
||
|
||
self::logHistory($id, 'create', 'Animal ajouté', "Nom : $name\nStatut : $status");
|
||
}
|
||
|
||
self::syncIntakeMovement(
|
||
$db,
|
||
$id,
|
||
$intake_date,
|
||
$intakeType,
|
||
$submittedIntakeReason,
|
||
$depositorId,
|
||
$rescueLocationName,
|
||
$rescueAddress,
|
||
$intakeCircumstances,
|
||
);
|
||
|
||
if (!$isArchivedCorrection && in_array($status, ['fa', 'fa_permanente'], true) && $fosterContactId) {
|
||
$latest = $db->prepare(
|
||
"SELECT contact_id FROM animal_placements WHERE animal_id=:id AND event_type='foster' ORDER BY date(event_date) DESC,id DESC LIMIT 1",
|
||
);
|
||
$latest->execute([':id' => $id]);
|
||
$latestFosterId = (int) $latest->fetchColumn();
|
||
$enteredFoster = !$old || !in_array((string) ($old['status'] ?? ''), ['fa', 'fa_permanente'], true);
|
||
if ($enteredFoster || $latestFosterId !== $fosterContactId) {
|
||
$db->prepare(
|
||
"INSERT INTO animal_placements(animal_id,event_type,event_date,contact_id,reason,notes,created_by) VALUES(:animal,'foster',date('now'),:contact,:reason,:notes,:user)",
|
||
)->execute([
|
||
':animal' => $id,
|
||
':contact' => $fosterContactId,
|
||
':reason' => 'Changement d’emplacement depuis la fiche',
|
||
':notes' => $status === 'fa_permanente' ? 'Famille d’accueil permanente' : null,
|
||
':user' => Auth::id(),
|
||
]);
|
||
}
|
||
}
|
||
|
||
$initial_weight = trim((string) ($_POST['initial_weight'] ?? ''));
|
||
$initial_weight = $initial_weight === '' ? null : (float) $initial_weight;
|
||
|
||
if ($initial_weight !== null && $initial_weight > 0) {
|
||
$stmt = $db->prepare("
|
||
INSERT INTO measurements(animal_id, type, value, unit, measured_at)
|
||
VALUES(:aid,'weight',:v,'kg',datetime('now'))
|
||
");
|
||
$stmt->execute([
|
||
':aid' => $id,
|
||
':v' => $initial_weight,
|
||
]);
|
||
}
|
||
|
||
header('Location: /animal?id=' . $id);
|
||
exit();
|
||
}
|
||
}
|