fix: fiabiliser les fiches animales et vaccinations
This commit is contained in:
parent
f45cfc40f2
commit
361bef8561
20 changed files with 157 additions and 65 deletions
|
|
@ -612,12 +612,11 @@ trait AnimalBrowseActions
|
|||
|
||||
// Vaccins
|
||||
$stmt = $db->prepare('
|
||||
SELECT v.*, rv.name AS vaccine_name, COALESCE(dc.name, c.name) AS clinic_name,COALESCE(v.administered_by_name,au.display_name,au.username) AS administrator_name
|
||||
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
|
||||
LEFT JOIN users au ON au.id=v.administered_by_user_id
|
||||
WHERE v.animal_id = :id
|
||||
ORDER BY v.done_date DESC, v.id DESC
|
||||
LIMIT 50
|
||||
|
|
@ -833,7 +832,7 @@ trait AnimalBrowseActions
|
|||
CASE WHEN COALESCE(v.lot,'')!='' THEN '\nLot : ' || v.lot ELSE '' END ||
|
||||
CASE WHEN COALESCE(v.manufacturer,'')!='' THEN '\nFabricant : ' || v.manufacturer ELSE '' END ||
|
||||
CASE WHEN COALESCE(v.batch_expires_on,'')!='' THEN '\nExpiration du lot : ' || v.batch_expires_on ELSE '' END ||
|
||||
CASE WHEN COALESCE(v.administered_by_name,'')!='' THEN '\nAdministré par : ' || v.administered_by_name WHEN v.administered_by_user_id IS NOT NULL THEN '\nAdministré par : ' || COALESCE((SELECT display_name FROM users WHERE id=v.administered_by_user_id),(SELECT username FROM users WHERE id=v.administered_by_user_id),'Compte inconnu') ELSE '' END ||
|
||||
CASE WHEN v.clinic_contact_id IS NOT NULL THEN '\nCabinet vétérinaire : ' || COALESCE((SELECT name FROM directory_contacts WHERE id=v.clinic_contact_id),'Cabinet inconnu') WHEN v.clinic_id IS NOT NULL THEN '\nCabinet vétérinaire : ' || COALESCE((SELECT name FROM clinics WHERE id=v.clinic_id),'Cabinet inconnu') ELSE '' END ||
|
||||
CASE WHEN COALESCE(v.notes,'')!='' THEN '\nNotes : ' || v.notes ELSE '' END
|
||||
) AS details,
|
||||
datetime(v.done_date || ' 12:00:00') AS at
|
||||
|
|
|
|||
|
|
@ -630,15 +630,6 @@ trait AnimalMedicalActions
|
|||
echo h(t('error.invalid_batch_expiry'));
|
||||
return;
|
||||
}
|
||||
$administratorUserId = (int) ($_POST['administered_by_user_id'] ?? 0) ?: null;
|
||||
$administratorName = trim((string) ($_POST['administered_by_name'] ?? ''));
|
||||
if ($administratorUserId) {
|
||||
$check = $db->prepare('SELECT 1 FROM users WHERE id=:id AND active=1');
|
||||
$check->execute([':id' => $administratorUserId]);
|
||||
if (!$check->fetchColumn()) {
|
||||
$administratorUserId = null;
|
||||
}
|
||||
}
|
||||
$notes = trim((string) ($_POST['notes'] ?? ''));
|
||||
$clinic_id = (int) ($_POST['clinic_id'] ?? 0);
|
||||
$clinic_id = $clinic_id > 0 ? $clinic_id : null;
|
||||
|
|
@ -668,8 +659,8 @@ trait AnimalMedicalActions
|
|||
$vet_name = $vetContact['name'] ?? $vet_name;
|
||||
|
||||
$stmt = $db->prepare('
|
||||
INSERT INTO vaccinations(animal_id, vaccine_id, done_date, due_date, lot,manufacturer,batch_expires_on,administered_by_user_id,administered_by_name, notes, clinic_id, vet_name, clinic_contact_id, vet_contact_id, created_by)
|
||||
VALUES(:aid,:vid,:done,:due,:lot,:manufacturer,:batch_expiry,:administrator_user,:administrator_name,:notes,:clinic_id,:vet_name,:clinic_contact_id,:vet_contact_id,:created_by)
|
||||
INSERT INTO vaccinations(animal_id, vaccine_id, done_date, due_date, lot,manufacturer,batch_expires_on, notes, clinic_id, vet_name, clinic_contact_id, vet_contact_id, created_by)
|
||||
VALUES(:aid,:vid,:done,:due,:lot,:manufacturer,:batch_expiry,:notes,:clinic_id,:vet_name,:clinic_contact_id,:vet_contact_id,:created_by)
|
||||
');
|
||||
$stmt->execute([
|
||||
':aid' => $id,
|
||||
|
|
@ -679,8 +670,6 @@ trait AnimalMedicalActions
|
|||
':lot' => $lot !== '' ? $lot : null,
|
||||
':manufacturer' => $manufacturer ?: null,
|
||||
':batch_expiry' => $batchExpiresOn ?: null,
|
||||
':administrator_user' => $administratorUserId,
|
||||
':administrator_name' => $administratorName ?: null,
|
||||
':notes' => $notes !== '' ? $notes : null,
|
||||
':clinic_id' => $clinic_id,
|
||||
':vet_name' => $vet_name,
|
||||
|
|
@ -707,10 +696,7 @@ trait AnimalMedicalActions
|
|||
($manufacturer !== '' ? "\nFabricant : $manufacturer" : '') .
|
||||
($lot !== '' ? "\nLot : $lot" : '') .
|
||||
($batchExpiresOn !== '' ? "\nExpiration du lot : $batchExpiresOn" : '') .
|
||||
($administratorName !== '' || $administratorUserId
|
||||
? "\nAdministré par : " .
|
||||
($administratorName !== '' ? $administratorName : 'utilisateur #' . $administratorUserId)
|
||||
: ''),
|
||||
"\nCabinet vétérinaire : " . ($clinicContact['name'] ?? ''),
|
||||
);
|
||||
|
||||
$db->prepare("UPDATE animals SET updated_at = datetime('now') WHERE id = :id")->execute([':id' => $id]);
|
||||
|
|
|
|||
|
|
@ -376,7 +376,10 @@ trait AnimalRecordActions
|
|||
}
|
||||
$identificationDate = trim((string) ($_POST['identification_date'] ?? ''));
|
||||
$identificationRegistration = (string) ($_POST['identification_registration_status'] ?? 'unknown');
|
||||
if (!in_array($identificationRegistration, ['unknown', 'pending', 'registered'], true)) {
|
||||
if ($identificationRegistration === 'pending') {
|
||||
$identificationRegistration = 'change_pending';
|
||||
}
|
||||
if (!in_array($identificationRegistration, ['unknown', 'registered', 'change_pending', 'owner_changed'], true)) {
|
||||
$identificationRegistration = 'unknown';
|
||||
}
|
||||
if ($chip_id === '') {
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ final class Asm3MigrationPlanner
|
|||
'identification_date' => $date($chip !== '' ? $r['IDENTICHIPDATE'] ?? null : $r['TATTOODATE'] ?? null),
|
||||
'identification_registration_status' => match ((string) ($r['IDENTICHIPSTATUS'] ?? '')) {
|
||||
'1' => 'registered',
|
||||
'0' => 'pending',
|
||||
'0' => 'change_pending',
|
||||
default => 'unknown',
|
||||
},
|
||||
'sterilization_status' => $yes($r['NEUTERED'] ?? 0) ? 'yes' : 'unknown',
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ final class ExportService
|
|||
'vaccinations' => [
|
||||
'Vaccinations',
|
||||
'vaccinations.csv',
|
||||
'SELECT v.id,a.internal_code code_animal,a.name animal,rv.name vaccin,v.done_date date_realisation,v.due_date prochain_rappel,v.manufacturer fabricant,v.lot,v.batch_expires_on expiration_lot,COALESCE(v.administered_by_name,au.display_name,au.username) administre_par,COALESCE(cu.display_name,cu.username) saisi_par,vet.name veterinaire,clinic.name cabinet,v.notes FROM vaccinations v JOIN animals a ON a.id=v.animal_id JOIN ref_vaccines rv ON rv.id=v.vaccine_id LEFT JOIN directory_contacts vet ON vet.id=v.vet_contact_id LEFT JOIN directory_contacts clinic ON clinic.id=v.clinic_contact_id LEFT JOIN users au ON au.id=v.administered_by_user_id LEFT JOIN users cu ON cu.id=v.created_by ORDER BY v.done_date,v.id',
|
||||
'SELECT v.id,a.internal_code code_animal,a.name animal,rv.name vaccin,v.done_date date_realisation,v.due_date prochain_rappel,v.manufacturer fabricant,v.lot,v.batch_expires_on expiration_lot,clinic.name administre_par,COALESCE(cu.display_name,cu.username) saisi_par,vet.name veterinaire,clinic.name cabinet,v.notes FROM vaccinations v JOIN animals a ON a.id=v.animal_id JOIN ref_vaccines rv ON rv.id=v.vaccine_id LEFT JOIN directory_contacts vet ON vet.id=v.vet_contact_id LEFT JOIN directory_contacts clinic ON clinic.id=v.clinic_contact_id LEFT JOIN users cu ON cu.id=v.created_by ORDER BY v.done_date,v.id',
|
||||
],
|
||||
'deaths' => [
|
||||
'Décès',
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ final class ImageService
|
|||
['/usr/bin/magick', '/usr/local/bin/magick', '/usr/bin/convert', '/usr/local/bin/convert']
|
||||
as $binary
|
||||
) {
|
||||
if (is_executable($binary)) {
|
||||
if (@is_executable($binary)) {
|
||||
return $binary;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,13 +91,21 @@ final class Migrations
|
|||
}
|
||||
}
|
||||
|
||||
$relaxesForeignKeys = $name === '093_room_rules_and_care_periods.sql';
|
||||
$relaxesForeignKeys = in_array(
|
||||
$name,
|
||||
['093_room_rules_and_care_periods.sql', '097_remove_legacy_room_constraint.sql'],
|
||||
true,
|
||||
);
|
||||
if ($relaxesForeignKeys) {
|
||||
$db->exec('PRAGMA foreign_keys = OFF');
|
||||
}
|
||||
$db->beginTransaction();
|
||||
try {
|
||||
$db->exec($sql);
|
||||
if ($name === '097_remove_legacy_room_constraint.sql') {
|
||||
self::modernizeAnimalConstraints($db);
|
||||
} else {
|
||||
$db->exec($sql);
|
||||
}
|
||||
$ins = $db->prepare("INSERT INTO _migrations(name, applied_at) VALUES(:n, datetime('now'))");
|
||||
$ins->execute([':n' => $name]);
|
||||
if ($relaxesForeignKeys && $db->query('PRAGMA foreign_key_check')->fetchColumn() !== false) {
|
||||
|
|
@ -116,4 +124,62 @@ final class Migrations
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function modernizeAnimalConstraints(PDO $db): void
|
||||
{
|
||||
$createSql = (string) $db
|
||||
->query("SELECT sql FROM sqlite_master WHERE type='table' AND name='animals'")
|
||||
->fetchColumn();
|
||||
$constraint = "/refuge_room\\s+TEXT\\s+CHECK\\s*\\(\\s*refuge_room\\s+IN\\s*\\(\\s*'silver'\\s*,\\s*'twix'\\s*\\)\\s+OR\\s+refuge_room\\s+IS\\s+NULL\\s*\\)/i";
|
||||
$registrationConstraint = "/identification_registration_status\\s+TEXT\\s+NOT\\s+NULL\\s+DEFAULT\\s+'unknown'\\s+CHECK\\s*\\(\\s*identification_registration_status\\s+IN\\s*\\(\\s*'unknown'\\s*,\\s*'pending'\\s*,\\s*'registered'\\s*\\)\\s*\\)/i";
|
||||
if (
|
||||
$createSql === ''
|
||||
|| (!preg_match($constraint, $createSql) && !preg_match($registrationConstraint, $createSql))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$newSql = preg_replace(
|
||||
'/^CREATE TABLE\\s+(?:IF NOT EXISTS\\s+)?["`\[]?animals["`\]]?/i',
|
||||
'CREATE TABLE animals_room_unrestricted',
|
||||
$createSql,
|
||||
1,
|
||||
);
|
||||
$newSql = preg_replace($constraint, 'refuge_room TEXT', (string) $newSql, 1);
|
||||
$newSql = preg_replace(
|
||||
$registrationConstraint,
|
||||
"identification_registration_status TEXT NOT NULL DEFAULT 'unknown' " .
|
||||
"CHECK(identification_registration_status IN ('unknown','registered','change_pending','owner_changed','pending'))",
|
||||
(string) $newSql,
|
||||
1,
|
||||
);
|
||||
if (!$newSql || preg_match($constraint, $newSql)) {
|
||||
throw new RuntimeException('Unable to remove the legacy shelter-room constraint.');
|
||||
}
|
||||
|
||||
$objects = $db
|
||||
->query("SELECT sql FROM sqlite_master WHERE tbl_name='animals' AND type IN ('index','trigger') AND sql IS NOT NULL")
|
||||
->fetchAll(PDO::FETCH_COLUMN);
|
||||
$views = $db
|
||||
->query("SELECT name,sql FROM sqlite_master WHERE type='view' AND sql IS NOT NULL ORDER BY rowid")
|
||||
->fetchAll(PDO::FETCH_ASSOC);
|
||||
$columns = array_column($db->query('PRAGMA table_info(animals)')->fetchAll(PDO::FETCH_ASSOC), 'name');
|
||||
$quotedColumns = implode(',', array_map(static fn(string $column): string => '"' . str_replace('"', '""', $column) . '"', $columns));
|
||||
|
||||
foreach ($views as $view) {
|
||||
$viewName = '"' . str_replace('"', '""', (string) $view['name']) . '"';
|
||||
$db->exec('DROP VIEW ' . $viewName);
|
||||
}
|
||||
$db->exec($newSql);
|
||||
$db->exec("INSERT INTO animals_room_unrestricted ($quotedColumns) SELECT $quotedColumns FROM animals");
|
||||
$db->exec("UPDATE animals_room_unrestricted SET identification_registration_status='change_pending' WHERE identification_registration_status='pending'");
|
||||
$db->exec('DROP TABLE animals');
|
||||
$db->exec('ALTER TABLE animals_room_unrestricted RENAME TO animals');
|
||||
foreach ($objects as $objectSql) {
|
||||
$db->exec((string) $objectSql);
|
||||
}
|
||||
foreach ($views as $view) {
|
||||
$db->exec((string) $view['sql']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -410,7 +410,7 @@ ORDER BY created_at ASC
|
|||
<th><?= h(t('animal.done_on')) ?></th>
|
||||
<th><?= h(t('animal.reminder_due')) ?></th>
|
||||
<th><?= h(t('animal.batch_traceability')) ?></th>
|
||||
<th><?= h(t('animal.administered_by')) ?></th>
|
||||
<th><?= h(t('animal.veterinary_clinic')) ?></th>
|
||||
<th><?= h(t('common.notes')) ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -424,7 +424,7 @@ ORDER BY created_at ASC
|
|||
<div class="text-muted small">
|
||||
<?= h(t('animal.expires_on', ['date' => date('d/m/Y', strtotime((string)$v['batch_expires_on']))])) ?>
|
||||
</div><?php endif; ?></td>
|
||||
<td><?= !empty($v['administrator_name']) ? h($v['administrator_name']) : '—' ?></td>
|
||||
<td><?= !empty($v['clinic_name']) ? h($v['clinic_name']) : '—' ?></td>
|
||||
<td><?= $v['notes'] ? h($v['notes']) : '—' ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
|
|
|||
|
|
@ -312,38 +312,12 @@ $currentTime = date('H:i');
|
|||
<div class="col-md-4"><label class="form-label"><?= h(t('inventory.quantity')) ?></label><input
|
||||
class="form-control" type="number" min="0" step="0.01" name="inventory_quantity"></div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label"><?= h(t('animal.administered_by')) ?></label>
|
||||
<select class="form-select" name="administered_by_user_id" id="vaccineAdministrator">
|
||||
<option value=""><?= h(t('common.not_provided')) ?></option>
|
||||
<?php foreach (($activeUsers ?? []) as $user): ?><option value="<?= (int)$user['id'] ?>">
|
||||
<?= h($user['name']) ?></option><?php endforeach; ?><option value="other">
|
||||
<?= h(t('animal.other_person')) ?></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 d-none" id="vaccineAdministratorOther">
|
||||
<label class="form-label"><?= h(t('animal.person_name')) ?></label>
|
||||
<input class="form-control" name="administered_by_name"
|
||||
placeholder="<?= h(t('animal.person_placeholder')) ?>">
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<label class="form-label"><?= h(t('common.notes')) ?></label>
|
||||
<input class="form-control" name="notes" placeholder="Optionnel">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const select = document.getElementById('vaccineAdministrator');
|
||||
const wrap = document.getElementById('vaccineAdministratorOther');
|
||||
const sync = () => wrap?.classList.toggle('d-none', select?.value !== 'other');
|
||||
select?.addEventListener('change', sync);
|
||||
sync();
|
||||
});
|
||||
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
|
|
|
|||
|
|
@ -370,12 +370,15 @@ $estMonths = $birth_estimated_months > 0 ? ($birth_estimated_months % 12) : '';
|
|||
<option value="unknown"
|
||||
<?= (($animal['identification_registration_status'] ?? 'unknown') === 'unknown') ? 'selected' : '' ?>>
|
||||
<?= h(t('common.unknown')) ?></option>
|
||||
<option value="pending"
|
||||
<?= (($animal['identification_registration_status'] ?? 'unknown') === 'pending') ? 'selected' : '' ?>>
|
||||
<?= h(t('animal.form.pending')) ?></option>
|
||||
<option value="registered"
|
||||
<?= (($animal['identification_registration_status'] ?? 'unknown') === 'registered') ? 'selected' : '' ?>>
|
||||
<?= h(t('animal.form.registered')) ?></option>
|
||||
<option value="change_pending"
|
||||
<?= in_array(($animal['identification_registration_status'] ?? 'unknown'), ['pending', 'change_pending'], true) ? 'selected' : '' ?>>
|
||||
<?= h(t('animal.form.change_pending')) ?></option>
|
||||
<option value="owner_changed"
|
||||
<?= (($animal['identification_registration_status'] ?? 'unknown') === 'owner_changed') ? 'selected' : '' ?>>
|
||||
<?= h(t('animal.form.owner_changed')) ?></option>
|
||||
</select></div>
|
||||
|
||||
<div class="col-12">
|
||||
|
|
|
|||
|
|
@ -296,9 +296,9 @@ if ($felvPos) {
|
|||
<?php $availability = $animal['adoption_availability'] ?? 'unknown';
|
||||
$availabilityMeta = ['unknown' => [t('animal.availability_define'),'bg-secondary-lt'],'not_available' => [t('animals.unavailable_title'),'bg-orange-lt'],'available' => [t('animals.available_title'),'bg-green-lt']];
|
||||
[$availabilityLabel,$availabilityClass] = $availabilityMeta[$availability] ?? $availabilityMeta['unknown'];
|
||||
$registrationLabels = ['unknown' => t('animal.registration_unknown'),'pending' => t('animal.registration_pending'),'registered' => t('animal.registration_done')];
|
||||
$registrationLabels = ['unknown' => t('animal.registration_unknown'),'pending' => t('animal.registration_change_pending'),'change_pending' => t('animal.registration_change_pending'),'registered' => t('animal.registration_done'),'owner_changed' => t('animal.registration_owner_changed')];
|
||||
$registration = $animal['identification_registration_status'] ?? 'unknown';
|
||||
$registrationClass = $registration === 'registered' ? 'bg-green-lt' : ($registration === 'pending' ? 'bg-yellow-lt' : 'bg-secondary-lt'); ?>
|
||||
$registrationClass = in_array($registration, ['registered', 'owner_changed'], true) ? 'bg-green-lt' : (in_array($registration, ['pending', 'change_pending'], true) ? 'bg-yellow-lt' : 'bg-secondary-lt'); ?>
|
||||
<div class="mt-2 d-flex flex-wrap align-items-center gap-1"><span
|
||||
class="badge <?= h($registrationClass) ?>"><?= h($registrationLabels[$registration] ?? $registrationLabels['unknown']) ?></span><span
|
||||
class="badge <?= h($availabilityClass) ?>"><?= h($availabilityLabel) ?></span><?php if (!empty($animal['website_published'])): ?><span
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@
|
|||
<div class="col-lg-6">
|
||||
<section class="card h-100">
|
||||
<div class="card-body">
|
||||
<span class="badge bg-blue-lt mb-2">1.0.0-rc.25</span>
|
||||
<h3>Portées modifiables et fiables</h3>
|
||||
<p class="text-muted">La création d’une fratrie ne dépend plus d’un ancien traitement de photo et
|
||||
son historique reste compatible avec les bases existantes.</p>
|
||||
<span class="badge bg-blue-lt mb-2">1.0.0-rc.26</span>
|
||||
<h3>Données animales fiabilisées</h3>
|
||||
<p class="text-muted">Les salles configurables, les états I-CAD et le suivi vaccinal restent
|
||||
cohérents sur les installations neuves comme sur les bases historiques.</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue