Corriger les dossiers animaux et le suivi médical
This commit is contained in:
parent
a55d27935f
commit
7b89fd5dfa
34 changed files with 440 additions and 39 deletions
|
|
@ -518,7 +518,7 @@ trait AnimalBrowseActions
|
|||
|
||||
// Adoptions
|
||||
$stmt = $db->prepare('
|
||||
SELECT adopter_name, adopter_address, adopter_postal_code, adopter_city, adopter_country, adopter_phone, adopter_email, adoption_date
|
||||
SELECT adopter_name, adopter_address, adopter_postal_code, adopter_city, adopter_country, adopter_phone, adopter_email, adoption_date, adopter_contact_id
|
||||
FROM adoptions
|
||||
WHERE animal_id = :id
|
||||
ORDER BY id DESC
|
||||
|
|
@ -650,7 +650,10 @@ trait AnimalBrowseActions
|
|||
$ref_med_administration = $db
|
||||
->query('SELECT id, name, accro FROM ref_med_administration ORDER BY id')
|
||||
->fetchAll();
|
||||
$vaccines = $db->query('SELECT id, name FROM ref_vaccines ORDER BY id')->fetchAll();
|
||||
$vaccines = $db->query('SELECT id, name FROM ref_vaccines ORDER BY name COLLATE NOCASE,id')->fetchAll();
|
||||
$vaccineManufacturers = $db
|
||||
->query('SELECT id,name FROM ref_vaccine_manufacturers WHERE active=1 ORDER BY name COLLATE NOCASE,id')
|
||||
->fetchAll(PDO::FETCH_ASSOC);
|
||||
$inventoryBatches = $db
|
||||
->query(
|
||||
'SELECT b.id,b.batch_number,b.expires_on,b.quantity,p.name,p.unit,p.category,p.reference_id FROM inventory_batches b JOIN inventory_products p ON p.id=b.product_id WHERE b.quantity>0 AND p.active=1 ORDER BY p.category,p.name COLLATE NOCASE,date(b.expires_on)',
|
||||
|
|
@ -958,6 +961,7 @@ trait AnimalBrowseActions
|
|||
'surgeries' => $surgeries,
|
||||
'ref_med_administration' => $ref_med_administration,
|
||||
'vaccines' => $vaccines,
|
||||
'vaccineManufacturers' => $vaccineManufacturers,
|
||||
'inventoryBatches' => $inventoryBatches,
|
||||
'activeUsers' => $activeUsers,
|
||||
'clinics' => $clinics,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ trait AnimalMedicalActions
|
|||
$testDate = date('Y-m-d');
|
||||
}
|
||||
$testNotes = trim((string) ($_POST['test_notes'] ?? ''));
|
||||
$testTariffId = (int) ($_POST['test_tariff_id'] ?? 0);
|
||||
$testClinicId = (int) ($_POST['clinic_contact_id'] ?? 0);
|
||||
$conditions = is_array($_POST['conditions'] ?? null) ? $_POST['conditions'] : [];
|
||||
|
||||
$db = DB::pdo();
|
||||
|
|
@ -53,6 +55,7 @@ trait AnimalMedicalActions
|
|||
UPDATE animals
|
||||
SET fiv_status = :fiv_status,
|
||||
felv_status = :felv_status,
|
||||
fiv_felv_test_date = :test_date,
|
||||
fiv = :fiv,
|
||||
felv = :felv,
|
||||
updated_at = datetime('now')
|
||||
|
|
@ -61,6 +64,7 @@ trait AnimalMedicalActions
|
|||
)->execute([
|
||||
':fiv_status' => $fivStatus,
|
||||
':felv_status' => $felvStatus,
|
||||
':test_date' => $testDate,
|
||||
':fiv' => $fivStatus === 'pos' ? 1 : 0,
|
||||
':felv' => $felvStatus === 'pos' ? 1 : 0,
|
||||
':id' => $animalId,
|
||||
|
|
@ -98,6 +102,15 @@ trait AnimalMedicalActions
|
|||
}
|
||||
}
|
||||
|
||||
if ($testTariffId > 0) {
|
||||
$tariffCheck = $db->prepare("SELECT 1 FROM clinic_tariffs WHERE id=? AND clinic_contact_id=? AND category='test' AND active=1");
|
||||
$tariffCheck->execute([$testTariffId, $testClinicId]);
|
||||
if (!$testClinicId || !$tariffCheck->fetchColumn()) {
|
||||
throw new RuntimeException(t('error.tariff_not_found'));
|
||||
}
|
||||
PricingService::record($db, $animalId, $testTariffId, 1, $testDate, $testNotes);
|
||||
}
|
||||
|
||||
$conditionSelect = $db->prepare(
|
||||
'SELECT status FROM animal_health_conditions WHERE id = :id AND animal_id = :animal_id',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -746,6 +746,18 @@ trait AnimalRecordActions
|
|||
':current_lng' => $currentLng,
|
||||
]);
|
||||
|
||||
// Un décès est une donnée structurée distincte du statut. Lors d'une correction
|
||||
// explicite d'un animal déclaré à tort décédé, ne pas laisser une ancienne fiche
|
||||
// de décès continuer à alimenter la bannière et les statistiques.
|
||||
$oldWasDeceased = in_array(strtolower((string) ($old['status'] ?? '')), ['decede', 'décédé'], true);
|
||||
$isNowDeceased = in_array(strtolower($status), ['decede', 'décédé'], true);
|
||||
if ($oldWasDeceased && !$isNowDeceased) {
|
||||
$db->prepare('DELETE FROM animal_deaths WHERE animal_id=:id')->execute([':id' => $id]);
|
||||
$db->prepare(
|
||||
"INSERT INTO animal_history(animal_id,action,type,label,details,user_id) VALUES(:id,'death_correction','update','Décès annulé','La fiche de décès a été retirée après correction du statut de l’animal.',:user)",
|
||||
)->execute([':id' => $id, ':user' => Auth::id()]);
|
||||
}
|
||||
|
||||
if ($old) {
|
||||
LocationHistoryService::record(
|
||||
$db,
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ final class DashboardController
|
|||
JOIN ref_vaccines rv ON rv.id = v.vaccine_id
|
||||
WHERE v.due_date IS NOT NULL
|
||||
AND a.deleted_at IS NULL AND a.archived_at IS NULL
|
||||
AND lower(a.status) NOT IN ('decede','décédé')
|
||||
AND lower(a.status) NOT IN ('decede','décédé','adopte','adopté')
|
||||
AND date(v.due_date) < date('now')
|
||||
ORDER BY date(v.due_date) ASC
|
||||
LIMIT 50
|
||||
|
|
@ -135,7 +135,7 @@ final class DashboardController
|
|||
JOIN ref_vaccines rv ON rv.id = v.vaccine_id
|
||||
WHERE v.due_date IS NOT NULL
|
||||
AND a.deleted_at IS NULL AND a.archived_at IS NULL
|
||||
AND lower(a.status) NOT IN ('decede','décédé')
|
||||
AND lower(a.status) NOT IN ('decede','décédé','adopte','adopté')
|
||||
AND date(v.due_date) >= date('now')
|
||||
AND date(v.due_date) <= date('now', '+7 day')
|
||||
ORDER BY date(v.due_date) ASC
|
||||
|
|
@ -152,7 +152,7 @@ final class DashboardController
|
|||
JOIN ref_vaccines rv ON rv.id = v.vaccine_id
|
||||
WHERE v.due_date IS NOT NULL
|
||||
AND a.deleted_at IS NULL AND a.archived_at IS NULL
|
||||
AND lower(a.status) NOT IN ('decede','décédé')
|
||||
AND lower(a.status) NOT IN ('decede','décédé','adopte','adopté')
|
||||
AND date(v.due_date) > date('now', '+7 day')
|
||||
AND date(v.due_date) <= date('now', '+30 day')
|
||||
ORDER BY date(v.due_date) ASC
|
||||
|
|
@ -162,7 +162,7 @@ final class DashboardController
|
|||
$vaccines_30d = $stmt->fetchAll();
|
||||
|
||||
// Traitements en cours
|
||||
$stmt = $db->prepare('
|
||||
$stmt = $db->prepare("
|
||||
SELECT a.id, a.name, a.internal_code AS code,
|
||||
m.name AS medication_name,
|
||||
t.start_date, t.end_date, t.dose_text, t.route
|
||||
|
|
@ -171,9 +171,10 @@ final class DashboardController
|
|||
JOIN ref_medications m ON m.id = t.medication_id
|
||||
WHERE t.ongoing = 1
|
||||
AND a.deleted_at IS NULL AND a.archived_at IS NULL
|
||||
AND lower(a.status) NOT IN ('decede','décédé','adopte','adopté')
|
||||
ORDER BY date(t.start_date) DESC, a.name ASC
|
||||
LIMIT 50
|
||||
');
|
||||
");
|
||||
$stmt->execute();
|
||||
$treatments = $stmt->fetchAll();
|
||||
|
||||
|
|
@ -185,6 +186,7 @@ final class DashboardController
|
|||
JOIN ref_medications m ON m.id = t.medication_id
|
||||
WHERE t.ongoing = 1
|
||||
AND a.deleted_at IS NULL AND a.archived_at IS NULL
|
||||
AND lower(a.status) NOT IN ('decede','décédé','adopte','adopté')
|
||||
AND t.end_date IS NOT NULL
|
||||
AND date(t.end_date) < date('now')
|
||||
ORDER BY date(t.end_date) ASC
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ final class DirectoryController
|
|||
'dirigeant' => t('role.dirigeant'),
|
||||
'fa' => t('role.fa'),
|
||||
'fourriere' => t('role.fourriere'),
|
||||
'pet_sitter' => t('role.pet_sitter'),
|
||||
'refuge_partenaire' => t('role.refuge_partenaire'),
|
||||
'toiletteur' => t('role.toiletteur'),
|
||||
'professionnel_autre' => t('role.professionnel_autre'),
|
||||
'veterinaire' => t('role.veterinaire'),
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1227,6 +1227,68 @@ final class SettingsController
|
|||
header('X-Content-Type-Options: nosniff');
|
||||
readfile($path);
|
||||
}
|
||||
public static function catalogs(): void
|
||||
{
|
||||
self::admin();
|
||||
$db = DB::pdo();
|
||||
render('settings_catalogs.php', [
|
||||
'title' => t('settings.catalogs'),
|
||||
'catalogs' => [
|
||||
'medication' => $db->query('SELECT id,name,molecule extra FROM ref_medications ORDER BY name COLLATE NOCASE')->fetchAll(PDO::FETCH_ASSOC),
|
||||
'vaccine' => $db->query('SELECT id,name,code extra FROM ref_vaccines ORDER BY name COLLATE NOCASE')->fetchAll(PDO::FETCH_ASSOC),
|
||||
'dewormer' => $db->query('SELECT id,name,active_ingredient extra FROM ref_dewormers ORDER BY name COLLATE NOCASE')->fetchAll(PDO::FETCH_ASSOC),
|
||||
'manufacturer' => $db->query('SELECT id,name,NULL extra FROM ref_vaccine_manufacturers ORDER BY name COLLATE NOCASE')->fetchAll(PDO::FETCH_ASSOC),
|
||||
],
|
||||
'saved' => isset($_GET['saved']),
|
||||
'error' => (string) ($_GET['error'] ?? ''),
|
||||
]);
|
||||
}
|
||||
public static function saveCatalogItem(): void
|
||||
{
|
||||
self::admin(); self::postOnly();
|
||||
$type = (string) ($_POST['type'] ?? '');
|
||||
$id = (int) ($_POST['id'] ?? 0);
|
||||
$name = trim((string) ($_POST['name'] ?? ''));
|
||||
$extra = trim((string) ($_POST['extra'] ?? '')) ?: null;
|
||||
$map = [
|
||||
'medication' => ['ref_medications','molecule'], 'vaccine' => ['ref_vaccines','code'],
|
||||
'dewormer' => ['ref_dewormers','active_ingredient'], 'manufacturer' => ['ref_vaccine_manufacturers',null],
|
||||
];
|
||||
if ($name === '' || !isset($map[$type])) {
|
||||
header('Location: /settings/catalogs?error=' . rawurlencode(t('catalog.invalid'))); exit();
|
||||
}
|
||||
[$table,$extraColumn] = $map[$type];
|
||||
try {
|
||||
if ($id) {
|
||||
$sql = "UPDATE $table SET name=?" . ($extraColumn ? ",$extraColumn=?" : '') . ' WHERE id=?';
|
||||
DB::pdo()->prepare($sql)->execute($extraColumn ? [$name,$extra,$id] : [$name,$id]);
|
||||
} else {
|
||||
$sql = "INSERT INTO $table(name" . ($extraColumn ? ",$extraColumn" : '') . ') VALUES(?' . ($extraColumn ? ',?' : '') . ')';
|
||||
DB::pdo()->prepare($sql)->execute($extraColumn ? [$name,$extra] : [$name]);
|
||||
$id = (int) DB::pdo()->lastInsertId();
|
||||
}
|
||||
AuditService::log('catalog_saved', '/settings/catalogs/save', 'Catalogue modifié : ' . $name, $type, $id);
|
||||
header('Location: /settings/catalogs?saved=1');
|
||||
} catch (Throwable $e) {
|
||||
header('Location: /settings/catalogs?error=' . rawurlencode(SecurityService::publicError($e)));
|
||||
}
|
||||
exit();
|
||||
}
|
||||
public static function deleteCatalogItem(): void
|
||||
{
|
||||
self::admin(); self::postOnly();
|
||||
$type = (string) ($_POST['type'] ?? ''); $id = (int) ($_POST['id'] ?? 0);
|
||||
$tables = ['medication'=>'ref_medications','vaccine'=>'ref_vaccines','dewormer'=>'ref_dewormers','manufacturer'=>'ref_vaccine_manufacturers'];
|
||||
if (!$id || !isset($tables[$type])) { http_response_code(400); return; }
|
||||
try {
|
||||
DB::pdo()->prepare('DELETE FROM ' . $tables[$type] . ' WHERE id=?')->execute([$id]);
|
||||
AuditService::log('catalog_deleted', '/settings/catalogs/delete', 'Élément de catalogue supprimé', $type, $id);
|
||||
header('Location: /settings/catalogs?saved=1');
|
||||
} catch (Throwable) {
|
||||
header('Location: /settings/catalogs?error=' . rawurlencode(t('catalog.in_use')));
|
||||
}
|
||||
exit();
|
||||
}
|
||||
private static function postOnly(): void
|
||||
{
|
||||
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,16 @@ final class SurgeriesController
|
|||
}
|
||||
$db = DB::pdo();
|
||||
$tariff = (int) ($_POST['tariff_id'] ?? 0) ?: null;
|
||||
$clinic = (int) ($_POST['clinic_contact_id'] ?? 0) ?: null;
|
||||
if ($tariff) {
|
||||
$validTariff = $db->prepare('SELECT 1 FROM clinic_tariffs WHERE id=? AND clinic_contact_id=? AND active=1');
|
||||
$validTariff->execute([$tariff, $clinic]);
|
||||
if (!$clinic || !$validTariff->fetchColumn()) {
|
||||
http_response_code(400);
|
||||
echo h(t('surgery.invalid_data'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
$amountRaw = str_replace(',', '.', trim((string) ($_POST['amount'] ?? '')));
|
||||
$amount = $amountRaw === '' ? null : (int) round(max(0, (float) $amountRaw) * 100);
|
||||
if ($amount === null && $tariff) {
|
||||
|
|
@ -77,7 +87,7 @@ final class SurgeriesController
|
|||
$name,
|
||||
$date,
|
||||
$status,
|
||||
(int) ($_POST['clinic_contact_id'] ?? 0) ?: null,
|
||||
$clinic,
|
||||
(int) ($_POST['veterinarian_contact_id'] ?? 0) ?: null,
|
||||
$tariff,
|
||||
$amount,
|
||||
|
|
@ -92,7 +102,7 @@ final class SurgeriesController
|
|||
'INSERT INTO animal_expenses(animal_id,clinic_contact_id,tariff_id,source_type,source_id,label,occurred_on,quantity,catalog_unit_cents,discount_percent,total_cents,notes,created_by) VALUES(?,?,?,\'surgery\',?,?,?,1,?,0,?,?,?)',
|
||||
)->execute([
|
||||
$animal,
|
||||
(int) ($_POST['clinic_contact_id'] ?? 0) ?: null,
|
||||
$clinic,
|
||||
$tariff,
|
||||
$id,
|
||||
$name,
|
||||
|
|
|
|||
Loading…
Reference in a new issue