Publier Globinours 1.0.0-rc.3

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

View file

@ -0,0 +1,106 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
$root = dirname(__DIR__);
require $root . '/app/Services/DB.php';
require $root . '/app/Services/BackupService.php';
$apply = in_array('--apply', $argv, true);
$db = DB::pdo();
$backup = null;
$user =
(int) ($db->query("SELECT id FROM users WHERE role='admin' AND active=1 ORDER BY id LIMIT 1")->fetchColumn() ?:
0) ?:
null;
$latest = $db
->query(
"WITH ranked AS (SELECT ap.*,ROW_NUMBER() OVER(PARTITION BY animal_id ORDER BY date(event_date) DESC,id DESC) rn FROM animal_placements ap) SELECT r.*,a.status current_status,a.archived_at current_archived,dc.name contact_name,dc.address contact_address,dc.postal_code contact_postal,dc.city contact_city,ad.adopter_address,ad.adopter_postal_code,ad.adopter_city FROM ranked r JOIN animals a ON a.id=r.animal_id LEFT JOIN animal_deaths d ON d.animal_id=a.id LEFT JOIN directory_contacts dc ON dc.id=r.contact_id LEFT JOIN adoptions ad ON ad.id=r.adoption_id WHERE r.rn=1 AND r.event_type='adoption' AND d.animal_id IS NULL AND a.deleted_at IS NULL ORDER BY r.event_date,r.animal_id",
)
->fetchAll(PDO::FETCH_ASSOC);
$summary = [
'adopted_states_checked' => count($latest),
'statuses_fixed' => 0,
'locations_fixed' => 0,
'movements_created' => 0,
'movements_updated' => 0,
'history_created' => 0,
];
if ($apply) {
$backup = BackupService::create('avant-reconciliation-etat-animaux');
}
$db->beginTransaction();
try {
foreach ($latest as $row) {
$animal = (int) $row['animal_id'];
$date = (string) $row['event_date'];
$contact = trim((string) ($row['contact_name'] ?? '')) ?: 'Adoptant non renseigné';
$address = implode(
' ',
array_filter([
trim((string) ($row['contact_address'] ?? '' ?: $row['adopter_address'] ?? '')),
trim((string) ($row['contact_postal'] ?? '' ?: $row['adopter_postal_code'] ?? '')),
trim((string) ($row['contact_city'] ?? '' ?: $row['adopter_city'] ?? '')),
]),
);
if ($address === '') {
$address = 'Adopté par ' . $contact;
}
if ($row['current_status'] !== 'adopte' || empty($row['current_archived'])) {
$summary['statuses_fixed']++;
}
$current = $db->prepare('SELECT current_address FROM animals WHERE id=?');
$current->execute([$animal]);
if (trim((string) $current->fetchColumn()) !== $address) {
$summary['locations_fixed']++;
}
$db->prepare(
"UPDATE animals SET status='adopte',is_archived=1,archived_at=?,refuge_room=NULL,care_box_key=NULL,quarantine_until=NULL,current_address=?,current_lat=NULL,current_lng=NULL,updated_at=datetime('now') WHERE id=?",
)->execute([$date . ' 12:00:00', $address, $animal]);
$movement = $db->prepare(
"SELECT id FROM animal_movements WHERE animal_id=? AND kind='exit' AND lower(COALESCE(lieu,'')) LIKE 'adopt%' ORDER BY created_at DESC,id DESC LIMIT 1",
);
$movement->execute([$animal]);
$movementId = (int) $movement->fetchColumn();
if ($movementId) {
$db->prepare(
"UPDATE animal_movements SET place=?,lieu='Adoption',contact_name=?,note=CASE WHEN note IS NULL OR note='' THEN 'Sortie synchronisée avec le parcours dadoption.' ELSE note END,created_at=? WHERE id=?",
)->execute([$address, $contact, $date . ' 12:00:00', $movementId]);
$summary['movements_updated']++;
} else {
$db->prepare(
"INSERT INTO animal_movements(animal_id,kind,place,lieu,contact_name,note,created_at) VALUES(?,'exit',?,'Adoption',?,'Sortie synchronisée avec le parcours dadoption.',?)",
)->execute([$animal, $address, $contact, $date . ' 12:00:00']);
$summary['movements_created']++;
}
$history = $db->prepare(
"SELECT 1 FROM animal_history WHERE animal_id=? AND date(created_at)=date(?) AND (lower(label) LIKE '%adopt%' OR lower(type)='adoption') LIMIT 1",
);
$history->execute([$animal, $date]);
if (!$history->fetchColumn()) {
$db->prepare(
"INSERT INTO animal_history(animal_id,type,label,details,user_id,created_at) VALUES(?,'status','Adoption de lanimal',?,?,?)",
)->execute([$animal, 'Adopté par ' . $contact . '.', $user, $date . ' 12:00:00']);
$summary['history_created']++;
}
}
$fk = $db->query('PRAGMA foreign_key_check')->fetchAll();
if ($fk) {
throw new RuntimeException('Violation de clé étrangère après synchronisation.');
}
if ($apply) {
$db->commit();
} else {
$db->rollBack();
}
echo json_encode(
['mode' => $apply ? 'APPLIQUE' : 'SIMULATION', 'backup' => $backup['name'] ?? null, 'summary' => $summary],
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
),
"\n";
} catch (Throwable $error) {
if ($db->inTransaction()) {
$db->rollBack();
}
fwrite(STDERR, 'Synchronisation interrompue : ' . $error->getMessage() . "\n");
exit(1);
}