menu csv full operationnel

This commit is contained in:
2026-07-02 14:15:39 +00:00
parent 2675598eee
commit 236ae4ab8c
6 changed files with 394 additions and 0 deletions
+125
View File
@@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/session.php';
require_once __DIR__ . '/db.php';
use App\Database\Database;
use App\Database\ActionsManager;
if (empty($_SESSION['user_id'])) {
header('Location: ../index.php');
exit;
}
$pageStylesheets = ['/css/csv.css'];
$pageScripts = ['/js/csv.js'];
$bodyClass = 'csv-page';
$database = new Database();
$actionsManager = new ActionsManager($database);
$csvFilePath = __DIR__ . '/../assets/csv/actions.csv';
$csvRecords = [];
$csvReadError = null;
$csvExists = is_readable($csvFilePath);
if ($csvExists) {
$handle = fopen($csvFilePath, 'r');
if ($handle === false) {
$csvExists = false;
$csvReadError = 'Impossible de lire le fichier CSV.';
} else {
$header = fgetcsv($handle);
while (($row = fgetcsv($handle)) !== false) {
if (count($row) < 4) {
continue;
}
$isin = trim((string) ($row[0] ?? ''));
if ($isin === '') {
continue;
}
$csvRecords[$isin] = [
'ticker' => trim((string) ($row[1] ?? '')),
'yahoo_code' => trim((string) ($row[2] ?? '')),
'company_name' => trim((string) ($row[3] ?? '')),
];
}
fclose($handle);
}
}
$actions = $actionsManager->listActions();
$differences = [];
foreach ($actions as $action) {
$isin = trim((string) ($action['isin'] ?? ''));
if ($isin === '') {
continue;
}
$expected = [
'ticker' => trim((string) ($action['ticker'] ?? '')),
'yahoo_code' => trim((string) ($action['yahoo_code'] ?? '')),
'company_name' => trim((string) ($action['company_name'] ?? '')),
];
if (!isset($csvRecords[$isin])) {
$differences[] = ['isin' => $isin, 'reason' => 'absent'];
continue;
}
$csvRow = $csvRecords[$isin];
foreach ($expected as $field => $value) {
if ($csvRow[$field] !== $value) {
$differences[] = ['isin' => $isin, 'reason' => $field];
break;
}
}
}
$csvReady = $csvExists && empty($differences);
if ($csvReadError !== null) {
$statusMessage = 'Impossible de vérifier le fichier actions.csv : ' . $csvReadError;
} elseif ($csvReady) {
$statusMessage = 'Le fichier actions.csv est à jour.';
} else {
$statusMessage = 'Le fichier actions.csv peut être mis à jour.';
if ($csvExists) {
$statusMessage .= ' ' . count($differences) . ' différence(s) détectée(s).';
}
}
require_once __DIR__ . '/header.php';
?>
<section class="csv-shell">
<div class="csv-card">
<div class="csv-content">
<h1>Synchronisation CSV</h1>
<p class="csv-status <?= $csvReady ? 'status-ok' : 'status-warning' ?>"><?= htmlspecialchars($statusMessage, ENT_QUOTES, 'UTF-8') ?></p>
</div>
<div class="csv-actions">
<?php if ($csvReady): ?>
<a href="/php/menu.php" class="btn-action btn-primary">Retour au menu</a>
<?php else: ?>
<button id="csv-update-yes" type="button" class="btn-action btn-primary">Mettre à jour</button>
<button id="csv-update-no" type="button" class="btn-action btn-secondary">Retour au menu</button>
<?php endif; ?>
<?php if ($csvExists): ?>
<a id="csv-download-link" href="/php/download_csv.php" class="btn-action btn-secondary">Télécharger CSV</a>
<?php endif; ?>
</div>
<div id="csv-feedback" class="csv-feedback" role="status" aria-live="polite"></div>
</div>
</section>
<script src="/js/csv.js"></script>
<?php require_once __DIR__ . '/footer.php';
+43
View File
@@ -395,6 +395,49 @@ class ActionsManager
return $statement->fetchAll();
}
/**
* Exporte toutes les actions vers un fichier CSV.
*
* @param string $filePath
* @return bool
*/
public function exportActionsToCsv(string $filePath): bool
{
$actions = $this->listActions();
$directory = dirname($filePath);
if (!is_dir($directory) && !mkdir($directory, 0755, true) && !is_dir($directory)) {
return false;
}
$handle = fopen($filePath, 'w');
if ($handle === false) {
return false;
}
$header = ['isin', 'ticker', 'yahoo_code', 'company_name', 'updated_at'];
if (fputcsv($handle, $header) === false) {
fclose($handle);
return false;
}
foreach ($actions as $action) {
if (fputcsv($handle, [
$action['isin'],
$action['ticker'],
$action['yahoo_code'],
$action['company_name'],
$action['updated_at'],
]) === false) {
fclose($handle);
return false;
}
}
fclose($handle);
return true;
}
/**
* Retourne une action par son ISIN.
*
+27
View File
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/session.php';
if (empty($_SESSION['user_id'])) {
header('Location: ../index.php');
exit;
}
$csvFilePath = __DIR__ . '/../assets/csv/actions.csv';
if (!is_readable($csvFilePath)) {
http_response_code(404);
echo 'Fichier CSV introuvable.';
exit;
}
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="actions.csv"');
header('Content-Length: ' . (string) filesize($csvFilePath));
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');
readfile($csvFilePath);
exit;
+43
View File
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/session.php';
require_once __DIR__ . '/db.php';
use App\Database\Database;
use App\Database\ActionsManager;
header('Content-Type: application/json; charset=UTF-8');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['success' => false, 'message' => 'Méthode non autorisée.']);
exit;
}
if (empty($_SESSION['user_id'])) {
http_response_code(401);
echo json_encode(['success' => false, 'message' => 'Authentification requise.']);
exit;
}
$database = new Database();
$actionsManager = new ActionsManager($database);
$csvFilePath = __DIR__ . '/../assets/csv/actions.csv';
try {
$success = $actionsManager->exportActionsToCsv($csvFilePath);
if (!$success) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Impossible de générer le fichier CSV.']);
exit;
}
echo json_encode(['success' => true, 'message' => 'Fichier CSV mis à jour avec succès.']);
exit;
} catch (Throwable $exception) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Erreur interne : ' . $exception->getMessage()]);
exit;
}