$apply ? 'apply' : 'dry-run', 'source' => basename($path), 'source_sha256' => $hash, 'planned' => $plan['summary'], 'created' => 0, 'linked_matches' => 0, 'skipped_existing_import' => 0, 'skipped_match' => 0, 'skipped_review' => $plan['summary']['review'], 'backup' => null, 'read_only' => !$apply, ]; if (!$apply) { $result['would_create'] = $plan['summary']['create']; return $result; } if ($createBackup) { $backup = BackupService::create('pre-asm3-import'); $result['backup'] = $backup['name'] ?? null; } $ownsTransaction = !$db->inTransaction(); if ($ownsTransaction) { $db->beginTransaction(); } try { $run = $db->prepare( "INSERT INTO asm3_import_runs(source_name,source_sha256,status,summary_json,backup_name) VALUES(:name,:hash,'completed','{}',:backup)", ); $run->execute([':name' => basename($path), ':hash' => $hash, ':backup' => $result['backup']]); $runId = (int) $db->lastInsertId(); foreach ($plan['items'] as $item) { if ($item['action'] === 'review') { continue; } $check = $db->prepare( "SELECT target_id FROM asm3_import_links WHERE source_sha256=:hash AND entity_type='animal' AND source_id=:source", ); $check->execute([':hash' => $hash, ':source' => $item['asm3_id']]); if ($check->fetchColumn()) { $result['skipped_existing_import']++; continue; } if ($item['action'] === 'match') { $target = (int) ($item['globinours_candidates'][0]['id'] ?? 0); if ($target <= 0) { $result['skipped_match']++; continue; } $result['linked_matches']++; } else { $target = self::insert($db, $item['mapped'], $item['asm3_id']); $result['created']++; } $db->prepare( "INSERT INTO asm3_import_links(source_sha256,entity_type,source_id,target_id,import_run_id) VALUES(:hash,'animal',:source,:target,:run)", )->execute([':hash' => $hash, ':source' => $item['asm3_id'], ':target' => $target, ':run' => $runId]); } $db->prepare('UPDATE asm3_import_runs SET summary_json=:summary WHERE id=:id')->execute([ ':summary' => json_encode($result, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR), ':id' => $runId, ]); if ($ownsTransaction) { $db->commit(); } } catch (Throwable $e) { if ($ownsTransaction && $db->inTransaction()) { $db->rollBack(); } throw $e; } return $result; } private static function insert(PDO $db, array $a, int $sourceId): int { $code = trim((string) $a['internal_code']); if ($code === '') { $code = 'ASM3-' . $sourceId; } $exists = $db->prepare('SELECT 1 FROM animals WHERE internal_code=:code'); $exists->execute([':code' => $code]); if ($exists->fetchColumn()) { $code .= '-ASM3-' . $sourceId; } $stmt = $db->prepare( "INSERT INTO animals(internal_code,name,species,sex,birth_date,birth_is_estimated,chip_id,status,sterilized,sterilization_status,sterilization_date,color,breed,notes,intake_date,archived_at,identification_type,identification_date,identification_registration_status,adoption_availability,adoption_available_from,adoption_unavailability_reason,compatibility_dogs,compatibility_cats,compatibility_children,house_trained,created_at,updated_at) VALUES(:code,:name,:species,:sex,:birth,:estimated,:chip,:status,:sterilized,:sterilization_status,:sterilization_date,:color,:breed,:notes,:intake,:archived,:identification_type,:identification_date,:registration,:availability,:available_from,:unavailable_reason,:dogs,:cats,:children,:trained,datetime('now'),datetime('now'))", ); $stmt->execute([ ':code' => $code, ':name' => $a['name'] ?: 'Animal ASM3 ' . $sourceId, ':species' => $a['species'] ?: 'inconnu', ':sex' => $a['sex'], ':birth' => $a['birth_date'], ':estimated' => $a['birth_is_estimated'], ':chip' => $a['chip_id'] ?: null, ':status' => $a['status'], ':sterilized' => $a['sterilization_status'] === 'yes' ? 1 : 0, ':sterilization_status' => $a['sterilization_status'], ':sterilization_date' => $a['sterilization_date'], ':color' => $a['color'], ':breed' => $a['breed'] ?: null, ':notes' => $a['notes'] ?: null, ':intake' => $a['intake_date'], ':archived' => $a['archived'] ? date('Y-m-d H:i:s') : null, ':identification_type' => $a['identification_type'], ':identification_date' => $a['identification_date'], ':registration' => $a['identification_registration_status'], ':availability' => $a['adoption_availability'], ':available_from' => $a['adoption_available_from'], ':unavailable_reason' => $a['adoption_unavailability_reason'] ?: null, ':dogs' => $a['compatibility_dogs'], ':cats' => $a['compatibility_cats'], ':children' => $a['compatibility_children'], ':trained' => $a['house_trained'], ]); $id = (int) $db->lastInsertId(); if ($a['deceased_date']) { $db->prepare( "INSERT INTO animal_deaths(animal_id,deceased_date,cause_code,cause_details,place_type,body_disposition,notes) VALUES(:animal,:date,'unknown',NULL,'inconnu','pending',:notes)", )->execute([':animal' => $id, ':date' => $a['deceased_date'], ':notes' => 'Importé depuis ASM3']); } return $id; } }