fix: corriger le suivi médical et les styles publics

This commit is contained in:
Alexandre NOEL 2026-09-22 18:05:38 +02:00
commit a55d27935f
15 changed files with 206 additions and 24 deletions

View file

@ -2,6 +2,16 @@
Toutes les modifications importantes du projet sont consignées ici. Le format s’inspire de [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le projet suit le [versionnage sémantique](https://semver.org/lang/fr/). Toutes les modifications importantes du projet sont consignées ici. Le format s’inspire de [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le projet suit le [versionnage sémantique](https://semver.org/lang/fr/).
## [1.0.0-rc.29] — 2026-09-22
### Corrections du suivi médical et du site public
- correction du formulaire d’édition des administrations de vermifuge / anti-puce ;
- modification des traitements déjà saisis et correction des intitulés de produits médicaux ;
- accès plus visible aux vermifuges dans l’onglet médical ;
- chargement du CSS personnalisé après les styles ordinaires du site public ;
- affichage de la date de naissance dans le résumé de la fiche animale.
## [1.0.0-rc.28] — 2026-09-15 ## [1.0.0-rc.28] — 2026-09-15
### Saisies médicales corrigibles ### Saisies médicales corrigibles

View file

@ -10,6 +10,10 @@ L’import depuis Animal Shelter Manager a été conçu comme un parcours pruden
Avant la version 1.0, le projet a fait l’objet d’un nettoyage structurel et d’un découpage de ses fichiers les plus volumineux. La recherche universelle, les salles configurables, les traductions française et anglaise et une suite de contrôles isolés ont complété cette phase de stabilisation. Avant la version 1.0, le projet a fait l’objet d’un nettoyage structurel et d’un découpage de ses fichiers les plus volumineux. La recherche universelle, les salles configurables, les traductions française et anglaise et une suite de contrôles isolés ont complété cette phase de stabilisation.
## `1.0.0-rc.29` — Suivi médical et présentation
Les traitements et les intitulés des produits médicaux peuvent être corrigés après leur saisie. Le formulaire des vermifuges s’ouvre de nouveau correctement. La fiche animale montre sa date de naissance et le CSS personnalisé du site public prend priorité sur les styles standard.
## `1.0.0-rc.28` — Corrections des saisies médicales ## `1.0.0-rc.28` — Corrections des saisies médicales
Les vaccinations et administrations de vermifuges ou d’anti-puces peuvent être corrigées sans provoquer une seconde sortie de stock ni une nouvelle dépense. Les nouvelles fiches de chats proposent également la race Chat Européen par défaut. Les vaccinations et administrations de vermifuges ou d’anti-puces peuvent être corrigées sans provoquer une seconde sortie de stock ni une nouvelle dépense. Les nouvelles fiches de chats proposent également la race Chat Européen par défaut.

View file

@ -200,7 +200,7 @@ L’option `--apply` ne doit être utilisée qu’après lecture des rapports. L
## État du projet ## État du projet
La candidate actuelle est `1.0.0-rc.28`. La branche 1.0 est gelée fonctionnellement : seuls les défauts bloquants découverts pendant les validations terrain sont corrigés avant `1.0.0`. La candidate actuelle est `1.0.0-rc.29`. La branche 1.0 est gelée fonctionnellement : seuls les défauts bloquants découverts pendant les validations terrain sont corrigés avant `1.0.0`.
Le contrôle de stabilisation est disponible dans [docs/AUDIT-1.0.md](docs/AUDIT-1.0.md). Le contrôle de stabilisation est disponible dans [docs/AUDIT-1.0.md](docs/AUDIT-1.0.md).

View file

@ -1 +1 @@
1.0.0-rc.28 1.0.0-rc.29

View file

@ -400,6 +400,7 @@ trait AnimalMedicalActions
} }
$id = (int) ($_POST['animal_id'] ?? 0); $id = (int) ($_POST['animal_id'] ?? 0);
$entryId = (int) ($_POST['entry_id'] ?? 0);
$medId = (int) ($_POST['medication_id'] ?? 0); $medId = (int) ($_POST['medication_id'] ?? 0);
$createMedication = !empty($_POST['create_medication']); $createMedication = !empty($_POST['create_medication']);
if ($id <= 0 || (!$createMedication && $medId <= 0)) { if ($id <= 0 || (!$createMedication && $medId <= 0)) {
@ -429,6 +430,15 @@ trait AnimalMedicalActions
} }
$db = DB::pdo(); $db = DB::pdo();
if ($entryId > 0) {
$existing = $db->prepare('SELECT 1 FROM treatments WHERE id=:entry AND animal_id=:animal');
$existing->execute([':entry' => $entryId, ':animal' => $id]);
if (!$existing->fetchColumn()) {
http_response_code(404);
echo h(t('error.not_found'));
return;
}
}
if ($createMedication) { if ($createMedication) {
$medicationName = trim((string) ($_POST['new_medication_name'] ?? '')); $medicationName = trim((string) ($_POST['new_medication_name'] ?? ''));
$medicationMolecule = trim((string) ($_POST['new_medication_molecule'] ?? '')); $medicationMolecule = trim((string) ($_POST['new_medication_molecule'] ?? ''));
@ -475,12 +485,7 @@ trait AnimalMedicalActions
return; return;
} }
$vet_name = $vetContact['name'] ?? $vet_name; $vet_name = $vetContact['name'] ?? $vet_name;
$stmt = $db->prepare(' $values = [
INSERT INTO treatments(animal_id, medication_id, route, dose_text, start_date, end_date, ongoing, notes, clinic_id, vet_name, clinic_contact_id, vet_contact_id, give_morning, give_evening, give_as_needed, created_by)
VALUES(:aid,:mid,:route,:dose,:start,:end,1,:notes,:clinic_id,:vet_name,:clinic_contact_id,:vet_contact_id,:give_morning,:give_evening,:give_as_needed,:created_by)
');
$stmt->execute([
':aid' => $id,
':mid' => $medId, ':mid' => $medId,
':route' => $route !== '' ? $route : null, ':route' => $route !== '' ? $route : null,
':dose' => $dose, ':dose' => $dose,
@ -494,16 +499,32 @@ trait AnimalMedicalActions
':give_morning' => $giveMorning, ':give_morning' => $giveMorning,
':give_evening' => $giveEvening, ':give_evening' => $giveEvening,
':give_as_needed' => $giveAsNeeded, ':give_as_needed' => $giveAsNeeded,
':created_by' => Auth::id(), ];
]); if ($entryId > 0) {
if ((int) ($_POST['tariff_id'] ?? 0) > 0) { $values[':entry'] = $entryId;
PricingService::record($db, $id, (int) $_POST['tariff_id'], 1, $start, 'Ajoutée avec le traitement'); $db->prepare('UPDATE treatments SET medication_id=:mid,route=:route,dose_text=:dose,start_date=:start,
end_date=:end,notes=:notes,clinic_id=:clinic_id,vet_name=:vet_name,
clinic_contact_id=:clinic_contact_id,vet_contact_id=:vet_contact_id,
give_morning=:give_morning,give_evening=:give_evening,give_as_needed=:give_as_needed
WHERE id=:entry')->execute($values);
} else {
$values[':aid'] = $id;
$values[':created_by'] = Auth::id();
$db->prepare('INSERT INTO treatments(animal_id, medication_id, route, dose_text, start_date, end_date,
ongoing, notes, clinic_id, vet_name, clinic_contact_id, vet_contact_id,
give_morning, give_evening, give_as_needed, created_by)
VALUES(:aid,:mid,:route,:dose,:start,:end,1,:notes,:clinic_id,:vet_name,
:clinic_contact_id,:vet_contact_id,:give_morning,:give_evening,:give_as_needed,:created_by)')
->execute($values);
if ((int) ($_POST['tariff_id'] ?? 0) > 0) {
PricingService::record($db, $id, (int) $_POST['tariff_id'], 1, $start, 'Ajoutée avec le traitement');
}
} }
self::logHistory( self::logHistory(
$id, $id,
'treatment', 'treatment',
'Traitement ajouté', $entryId > 0 ? 'Traitement corrigé' : 'Traitement ajouté',
"Médicament #$medId\nDose : $dose\nDébut : $start" . "Médicament #$medId\nDose : $dose\nDébut : $start" .
($end !== '' ? "\nFin : $end" : '') . ($end !== '' ? "\nFin : $end" : '') .
($notes !== '' ? "\nNotes : $notes" : ''), ($notes !== '' ? "\nNotes : $notes" : ''),

View file

@ -4,6 +4,53 @@ declare(strict_types=1);
final class DewormingController final class DewormingController
{ {
public static function renameProduct(): void
{
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
http_response_code(405);
return;
}
if (!PermissionService::can('medical', 'edit')) {
http_response_code(403);
return;
}
$tables = ['dewormer' => 'ref_dewormers', 'medication' => 'ref_medications', 'vaccine' => 'ref_vaccines'];
$category = (string) ($_POST['category'] ?? '');
$table = $tables[$category] ?? null;
$id = (int) ($_POST['product_id'] ?? 0);
$animalId = (int) ($_POST['animal_id'] ?? 0);
$name = trim((string) ($_POST['name'] ?? ''));
if ($table === null || $id <= 0 || $animalId <= 0 || $name === '' || mb_strlen($name) > 150) {
http_response_code(400);
echo h(t('error.bad_request'));
return;
}
$db = DB::pdo();
$stmt = $db->prepare("SELECT name FROM $table WHERE id=:id");
$stmt->execute([':id' => $id]);
$previous = $stmt->fetchColumn();
if ($previous === false) {
http_response_code(404);
echo h(t('error.not_found'));
return;
}
$duplicate = $db->prepare("SELECT 1 FROM $table WHERE name=:name COLLATE NOCASE AND id<>:id");
$duplicate->execute([':name' => $name, ':id' => $id]);
if ($duplicate->fetchColumn()) {
http_response_code(409);
echo h(t('catalog.name_exists'));
return;
}
$db->prepare("UPDATE $table SET name=:name WHERE id=:id")->execute([':name' => $name, ':id' => $id]);
AuditService::log('catalog_renamed', '/animal/product/rename', 'Intitulé de produit corrigé', 'animal', $animalId, [
'category' => $category,
'product_id' => $id,
'previous_name' => $previous,
'new_name' => $name,
]);
header('Location: /animal?id=' . $animalId . '#tab-med');
}
public static function save(): void public static function save(): void
{ {
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {

View file

@ -88,6 +88,9 @@ switch ($path) {
case '/animal/add-deworming': case '/animal/add-deworming':
DewormingController::save(); DewormingController::save();
break; break;
case '/animal/product/rename':
DewormingController::renameProduct();
break;
case '/animal/add-vaccine': case '/animal/add-vaccine':
AnimalsController::addVaccine(); AnimalsController::addVaccine();

View file

@ -91,6 +91,8 @@ final class PermissionService
'/animal/medical-status', '/animal/medical-status',
'/animal/add-treatment', '/animal/add-treatment',
'/animal/add-vaccine', '/animal/add-vaccine',
'/animal/add-deworming',
'/animal/product/rename',
'/animal/death', '/animal/death',
'/animal/prescription', '/animal/prescription',
'/animal/lab-report', '/animal/lab-report',

View file

@ -198,7 +198,7 @@
<div class="card mt-3"> <div class="card mt-3">
<div class="card-header d-flex align-items-center justify-content-between"> <div class="card-header d-flex align-items-center justify-content-between">
<div> <div>
<h3 class="card-title m-0"><?= h(t('animals.dewormer')) ?></h3> <h3 class="card-title m-0">🪱 <?= h(t('animals.dewormer')) ?></h3>
<div class="text-muted small"><?= h(t('animal.deworming_schedule')) ?></div> <div class="text-muted small"><?= h(t('animal.deworming_schedule')) ?></div>
</div><button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" </div><button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal"
data-bs-target="#modalDeworming"><?= h(t('common.add')) ?></button> data-bs-target="#modalDeworming"><?= h(t('common.add')) ?></button>
@ -400,7 +400,19 @@ ORDER BY created_at ASC
<td><?= h($t['start_date']) ?></td> <td><?= h($t['start_date']) ?></td>
<td><?= $t['end_date'] ? h($t['end_date']) : '—' ?></td> <td><?= $t['end_date'] ? h($t['end_date']) : '—' ?></td>
<td><?= $t['notes'] ? h($t['notes']) : '—' ?></td> <td><?= $t['notes'] ? h($t['notes']) : '—' ?></td>
<td class="text-end"><button type="button" class="btn btn-sm btn-ghost-primary edit-treatment"
data-bs-toggle="modal" data-bs-target="#modalTreatment"
data-entry-id="<?= (int)$t['id'] ?>"
data-medication-id="<?= (int)$t['medication_id'] ?>"
data-route="<?= h($t['route'] ?? '') ?>" data-dose-text="<?= h($t['dose_text']) ?>"
data-start-date="<?= h($t['start_date']) ?>" data-end-date="<?= h($t['end_date'] ?? '') ?>"
data-clinic-contact-id="<?= (int)($t['clinic_contact_id'] ?? 0) ?>"
data-vet-contact-id="<?= (int)($t['vet_contact_id'] ?? 0) ?>"
data-give-morning="<?= (int)$t['give_morning'] ?>"
data-give-evening="<?= (int)$t['give_evening'] ?>"
data-give-as-needed="<?= (int)$t['give_as_needed'] ?>"
data-notes="<?= h($t['notes'] ?? '') ?>"
aria-label="<?= h(t('common.edit')) ?>">✏️</button></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</tbody> </tbody>

View file

@ -22,6 +22,7 @@ $currentTime = date('H:i');
<div class="modal-body"> <div class="modal-body">
<input type="hidden" name="animal_id" value="<?= (int)$animal['id'] ?>"> <input type="hidden" name="animal_id" value="<?= (int)$animal['id'] ?>">
<input type="hidden" name="entry_id" value="">
<div class="row g-2"> <div class="row g-2">
<div class="col-md-8"> <div class="col-md-8">
@ -37,6 +38,8 @@ $currentTime = date('H:i');
<button class="btn btn-outline-primary" type="button" id="treatmentMedicationAdd" <button class="btn btn-outline-primary" type="button" id="treatmentMedicationAdd"
aria-expanded="false" aria-controls="treatmentMedicationNew">+ aria-expanded="false" aria-controls="treatmentMedicationNew">+
<?= h(t('common.add')) ?></button> <?= h(t('common.add')) ?></button>
<button class="btn btn-outline-secondary" type="button" data-bs-toggle="modal"
data-bs-target="#modalProductRename">✏️</button>
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
@ -105,7 +108,7 @@ $currentTime = date('H:i');
<?php $directoryFieldId = 'treatment'; <?php $directoryFieldId = 'treatment';
require __DIR__ . '/_directory_medical_fields.php'; ?> require __DIR__ . '/_directory_medical_fields.php'; ?>
<?php require __DIR__ . '/_expense_tariff_field.php'; ?> <div data-treatment-new-only style="display: contents"><?php require __DIR__ . '/_expense_tariff_field.php'; ?></div>
<div class="col-12"> <div class="col-12">
<label class="form-label"><?= h(t('common.notes')) ?></label> <label class="form-label"><?= h(t('common.notes')) ?></label>
@ -166,7 +169,8 @@ $currentTime = date('H:i');
<option value=""><?= h(t('common.not_provided')) ?></option> <option value=""><?= h(t('common.not_provided')) ?></option>
<?php foreach ($dewormers as $m): ?><option value="<?= (int)$m['id'] ?>"> <?php foreach ($dewormers as $m): ?><option value="<?= (int)$m['id'] ?>">
<?= h($m['name']) ?></option><?php endforeach; ?> <?= h($m['name']) ?></option><?php endforeach; ?>
</select></div> </select><button class="btn btn-sm btn-link px-0" type="button" data-bs-toggle="modal"
data-bs-target="#modalProductRename"><?= h(t('catalog.rename')) ?></button></div>
<div class="col-md-6"><label class="form-label"><?= h(t('animal.dose')) ?></label><input <div class="col-md-6"><label class="form-label"><?= h(t('animal.dose')) ?></label><input
class="form-control" name="dose_text" placeholder="<?= h(t('deworm.dose_example')) ?>"> class="form-control" name="dose_text" placeholder="<?= h(t('deworm.dose_example')) ?>">
</div> </div>
@ -269,7 +273,8 @@ $currentTime = date('H:i');
<option value="<?= (int)$v['id'] ?>"><?= htmlspecialchars($v['name'], ENT_QUOTES) ?> <option value="<?= (int)$v['id'] ?>"><?= htmlspecialchars($v['name'], ENT_QUOTES) ?>
</option> </option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select><button class="btn btn-sm btn-link px-0" type="button" data-bs-toggle="modal"
data-bs-target="#modalProductRename"><?= h(t('catalog.rename')) ?></button>
</div> </div>
@ -332,6 +337,33 @@ $currentTime = date('H:i');
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const treatmentModal = document.getElementById('modalTreatment');
treatmentModal?.addEventListener('show.bs.modal', event => {
const form = treatmentModal.querySelector('form');
const trigger = event.relatedTarget;
const editing = trigger?.classList.contains('edit-treatment') ?? false;
if (!form) return;
form.reset();
form.elements.entry_id.value = editing ? trigger.dataset.entryId : '';
if (editing) {
for (const name of ['medication_id', 'route', 'dose_text', 'start_date', 'end_date',
'clinic_contact_id', 'vet_contact_id', 'notes']) {
const key = name.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
if (form.elements[name]) form.elements[name].value = trigger.dataset[key] || '';
}
for (const name of ['give_morning', 'give_evening', 'give_as_needed']) {
const key = name.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
form.elements[name].checked = trigger.dataset[key] === '1';
}
}
treatmentModal.querySelectorAll('[data-treatment-new-only]').forEach(element => {
element.classList.toggle('d-none', editing);
element.querySelectorAll('input,select,textarea').forEach(input => input.disabled = editing);
});
treatmentModal.querySelectorAll('.directory-contact-select').forEach(select => {
select.dispatchEvent(new Event('change', {bubbles: true}));
});
});
const prepareEditor = (modalId, triggerClass, fields) => { const prepareEditor = (modalId, triggerClass, fields) => {
const modal = document.getElementById(modalId); const modal = document.getElementById(modalId);
if (!modal) return; if (!modal) return;
@ -371,6 +403,52 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
</script> </script>
<div class="modal modal-blur fade" id="modalProductRename" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<form class="modal-content" method="post" action="/animal/product/rename">
<div class="modal-header"><h5 class="modal-title"><?= h(t('catalog.rename')) ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="<?= h(t('common.close')) ?>"></button></div>
<div class="modal-body">
<input type="hidden" name="animal_id" value="<?= (int)$animal['id'] ?>">
<p class="text-muted small"><?= h(t('catalog.rename_hint')) ?></p>
<label class="form-label"><?= h(t('animal.product')) ?></label>
<select class="form-select mb-3" id="catalogRenameChoice" required>
<option value="">—</option>
<optgroup label="<?= h(t('animal.medication')) ?>">
<?php foreach ($medications as $product): ?><option value="medication:<?= (int)$product['id'] ?>"
data-name="<?= h($product['name']) ?>"><?= h($product['name']) ?></option><?php endforeach; ?>
</optgroup>
<optgroup label="<?= h(t('animals.dewormer')) ?>">
<?php foreach ($dewormers as $product): ?><option value="dewormer:<?= (int)$product['id'] ?>"
data-name="<?= h($product['name']) ?>"><?= h($product['name']) ?></option><?php endforeach; ?>
</optgroup>
<optgroup label="<?= h(t('animal.vaccine')) ?>">
<?php foreach ($vaccines as $product): ?><option value="vaccine:<?= (int)$product['id'] ?>"
data-name="<?= h($product['name']) ?>"><?= h($product['name']) ?></option><?php endforeach; ?>
</optgroup>
</select>
<input type="hidden" name="category" id="catalogRenameCategory">
<input type="hidden" name="product_id" id="catalogRenameId">
<label class="form-label"><?= h(t('common.name')) ?></label>
<input class="form-control" name="name" id="catalogRenameName" maxlength="150" required>
</div>
<div class="modal-footer"><button type="button" class="btn me-auto" data-bs-dismiss="modal"><?= h(t('common.cancel')) ?></button>
<button type="submit" class="btn btn-primary"><?= h(t('common.save')) ?></button></div>
</form>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const choice = document.getElementById('catalogRenameChoice');
choice?.addEventListener('change', () => {
const [category, id] = choice.value.split(':');
document.getElementById('catalogRenameCategory').value = category || '';
document.getElementById('catalogRenameId').value = id || '';
document.getElementById('catalogRenameName').value = choice.selectedOptions[0]?.dataset.name || '';
});
});
</script>
<div class="modal modal-blur fade" id="modalPrescription" tabindex="-1" aria-hidden="true"> <div class="modal modal-blur fade" id="modalPrescription" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered"> <div class="modal-dialog modal-lg modal-dialog-centered">
<form class="modal-content" method="post" action="/animal/prescription/save" enctype="multipart/form-data"> <form class="modal-content" method="post" action="/animal/prescription/save" enctype="multipart/form-data">

View file

@ -57,8 +57,6 @@
</div> </div>
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('[data-bs-target="#modalDeworming"]').forEach(b => b.setAttribute(
'data-bs-target', '#modalDewormingCatalog'));
const t = document.getElementById('dewormerNewToggle'), const t = document.getElementById('dewormerNewToggle'),
p = document.getElementById('dewormerNewFields'); p = document.getElementById('dewormerNewFields');
t?.addEventListener('change', () => p?.classList.toggle('d-none', !t.checked)); t?.addEventListener('change', () => p?.classList.toggle('d-none', !t.checked));

View file

@ -183,6 +183,8 @@ if ($felvPos) {
</div> </div>
<?php endif; ?> <?php endif; ?>
<div class="text-muted mt-1" style="font-size:.7rem"><?= h($animal['internal_code']) ?></div> <div class="text-muted mt-1" style="font-size:.7rem"><?= h($animal['internal_code']) ?></div>
<div class="text-muted small mt-1"><?= h(t('animal.form.birth_date')) ?> :
<?= !empty($animal['birth_date']) ? h(date('d/m/Y', strtotime((string)$animal['birth_date']))) : '—' ?></div>
</div> </div>
<div> <div>

View file

@ -99,10 +99,6 @@ if (!function_exists('public_age')) {
--line: #eadfd5 --line: #eadfd5
} }
<?php if ($theme === 'custom' && trim((string)($settings['public_site_custom_css'] ?? '')) !== ''): ?>
<?= $settings['public_site_custom_css'] ?>
<?php endif; ?>
* { * {
box-sizing: border-box box-sizing: border-box
} }
@ -476,6 +472,9 @@ if (!function_exists('public_age')) {
} }
</style> </style>
<?php if ($theme === 'custom' && trim((string)($settings['public_site_custom_css'] ?? '')) !== ''): ?>
<style id="public-site-custom-css"><?= $settings['public_site_custom_css'] ?></style>
<?php endif; ?>
</head> </head>
<body> <body>

View file

@ -228,6 +228,9 @@ return [
'agenda.deworming'=>'Deworming / flea treatment', 'agenda.deworming'=>'Deworming / flea treatment',
'agenda.deworming_reminder'=>'Deworming / flea treatment: :product', 'agenda.deworming_reminder'=>'Deworming / flea treatment: :product',
'agenda.type_deworming'=>'Next deworming / flea treatment', 'agenda.type_deworming'=>'Next deworming / flea treatment',
'catalog.rename'=>'Correct a product name',
'catalog.rename_hint'=>'This correction renames the product in every record that uses it. It does not change recorded administrations.',
'catalog.name_exists'=>'This name already exists in this catalogue.',
'animal.form.change_pending'=>'Ownership change pending', 'animal.form.change_pending'=>'Ownership change pending',
'animal.form.owner_changed'=>'Owner changed', 'animal.form.owner_changed'=>'Owner changed',
'animal.registration_change_pending'=>'I-CAD: ownership change pending', 'animal.registration_change_pending'=>'I-CAD: ownership change pending',

View file

@ -228,6 +228,9 @@ return [
'agenda.deworming'=>'Vermifuge / anti-puce', 'agenda.deworming'=>'Vermifuge / anti-puce',
'agenda.deworming_reminder'=>'Vermifuge / anti-puce : :product', 'agenda.deworming_reminder'=>'Vermifuge / anti-puce : :product',
'agenda.type_deworming'=>'Prochain vermifuge / anti-puce', 'agenda.type_deworming'=>'Prochain vermifuge / anti-puce',
'catalog.rename'=>'Corriger le nom d’un produit',
'catalog.rename_hint'=>'Cette correction renomme le produit dans toutes les fiches qui l’utilisent. Elle ne modifie pas les administrations enregistrées.',
'catalog.name_exists'=>'Ce nom existe déjà dans ce catalogue.',
'animal.form.change_pending'=>'En attente de changement', 'animal.form.change_pending'=>'En attente de changement',
'animal.form.owner_changed'=>'Propriétaire changé', 'animal.form.owner_changed'=>'Propriétaire changé',
'animal.registration_change_pending'=>'I-CAD : en attente de changement', 'animal.registration_change_pending'=>'I-CAD : en attente de changement',