Files
garagemanager/api.php
T
Luca ec81babb8f feat: GarageManager v1.0 - Gestion entretiens vehicules
- CRUD vehicules (nom, marque, modele, annee, immat, VIN, carburant, couleur, km, photo)
- CRUD entretiens (type, date, km, cout, mecanicien, garage, rappels km/date)
- CRUD pieces (marque, reference, prix, quantite, photo, fournisseur, categorie)
- Dashboard avec stats globales et rappels entretiens
- Vue detail par vehicule (tabs Entretiens/Pieces)
- Vue globale toutes pieces
- Upload photos vehicules et pieces
- Dark theme Tailwind-inspired
- SQLite via Docker PHP 8.3 Apache
- Traefik : garage.nas.percolouco.com
2026-03-14 23:40:35 +01:00

128 lines
9.9 KiB
PHP

<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; }
require_once __DIR__ . '/db.php';
$method = $_SERVER['REQUEST_METHOD'];
$action = $_GET['action'] ?? '';
function jsonOk($data): void { echo json_encode(['ok' => true, 'data' => $data]); exit; }
function jsonErr(string $msg, int $code = 400): void { http_response_code($code); echo json_encode(['ok' => false, 'error' => $msg]); exit; }
function getBody(): array { return json_decode(file_get_contents('php://input'), true) ?? []; }
function handleUpload(string $field): ?string {
if (!isset($_FILES[$field]) || $_FILES[$field]['error'] !== 0) return null;
$ext = strtolower(pathinfo($_FILES[$field]['name'], PATHINFO_EXTENSION));
$allowed = ['jpg','jpeg','png','gif','webp'];
if (!in_array($ext, $allowed)) return null;
$filename = uniqid('', true) . '.' . $ext;
$dest = UPLOADS_DIR . $filename;
if (move_uploaded_file($_FILES[$field]['tmp_name'], $dest)) return $filename;
return null;
}
$db = getDB();
if ($action === 'vehicles') {
if ($method === 'GET') {
$id = $_GET['id'] ?? null;
if ($id) {
$v = $db->prepare("SELECT * FROM vehicles WHERE id = ?"); $v->execute([$id]);
$vehicle = $v->fetch(); if (!$vehicle) jsonErr('Vehicule introuvable', 404);
$s = $db->prepare("SELECT COUNT(*) as cnt, COALESCE(SUM(cost),0) as total FROM maintenances WHERE vehicle_id = ?"); $s->execute([$id]); $vehicle['stats'] = $s->fetch();
$p = $db->prepare("SELECT COUNT(*) as cnt, COALESCE(SUM(price*quantity),0) as total FROM parts WHERE vehicle_id = ?"); $p->execute([$id]); $vehicle['parts_stats'] = $p->fetch();
jsonOk($vehicle);
}
$stmt = $db->query("SELECT v.*, (SELECT COUNT(*) FROM maintenances m WHERE m.vehicle_id = v.id) as maintenance_count, (SELECT COALESCE(SUM(cost),0) FROM maintenances m WHERE m.vehicle_id = v.id) as total_cost, (SELECT date FROM maintenances m WHERE m.vehicle_id = v.id ORDER BY date DESC LIMIT 1) as last_maintenance FROM vehicles v ORDER BY v.created_at DESC");
jsonOk($stmt->fetchAll());
}
if ($method === 'POST') {
$photo = handleUpload('photo'); $d = $_POST ?: getBody();
$stmt = $db->prepare("INSERT INTO vehicles (name,brand,model,year,license_plate,vin,fuel_type,color,purchase_date,purchase_price,current_km,photo,notes) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)");
$stmt->execute([$d['name']??'', $d['brand']??'', $d['model']??'', $d['year']??null, $d['license_plate']??null, $d['vin']??null, $d['fuel_type']??'Essence', $d['color']??null, $d['purchase_date']??null, $d['purchase_price']??null, $d['current_km']??0, $photo, $d['notes']??null]);
jsonOk(['id' => $db->lastInsertId()]);
}
if ($method === 'PUT') {
$id = $_GET['id'] ?? null; if (!$id) jsonErr('ID manquant'); $d = getBody();
$fields = ['name','brand','model','year','license_plate','vin','fuel_type','color','purchase_date','purchase_price','current_km','notes'];
$sets = array_map(fn($f) => "$f = ?", $fields); $vals = array_map(fn($f) => $d[$f] ?? null, $fields); $vals[] = $id;
$db->prepare("UPDATE vehicles SET " . implode(', ', $sets) . ", updated_at = datetime('now') WHERE id = ?")->execute($vals);
jsonOk(['updated' => true]);
}
if ($method === 'DELETE') {
$id = $_GET['id'] ?? null; if (!$id) jsonErr('ID manquant');
$v = $db->prepare("SELECT photo FROM vehicles WHERE id=?"); $v->execute([$id]); $row = $v->fetch(); if ($row['photo']) @unlink(UPLOADS_DIR . $row['photo']);
$db->prepare("DELETE FROM vehicles WHERE id = ?")->execute([$id]); jsonOk(['deleted' => true]);
}
}
if ($action === 'maintenances') {
$vid = $_GET['vehicle_id'] ?? null;
if ($method === 'GET') {
if ($vid) {
$stmt = $db->prepare("SELECT m.*, GROUP_CONCAT(p.name, ', ') as parts_names, COUNT(p.id) as parts_count, COALESCE(SUM(p.price*p.quantity),0) as parts_cost FROM maintenances m LEFT JOIN parts p ON p.maintenance_id = m.id WHERE m.vehicle_id = ? GROUP BY m.id ORDER BY m.date DESC, m.created_at DESC");
$stmt->execute([$vid]); jsonOk($stmt->fetchAll());
}
$stmt = $db->query("SELECT m.*, v.name as vehicle_name, v.license_plate, v.current_km FROM maintenances m JOIN vehicles v ON v.id = m.vehicle_id WHERE m.next_date IS NOT NULL OR m.next_km IS NOT NULL ORDER BY m.next_date ASC");
jsonOk($stmt->fetchAll());
}
if ($method === 'POST') {
$photo = handleUpload('invoice_photo'); $d = $_POST ?: getBody();
if (!($d['vehicle_id'] ?? null)) jsonErr('vehicle_id manquant');
$stmt = $db->prepare("INSERT INTO maintenances (vehicle_id,type,description,date,km,cost,mechanic,garage,next_km,next_date,invoice_photo,notes) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)");
$stmt->execute([$d['vehicle_id'], $d['type']??'', $d['description']??null, $d['date']??date('Y-m-d'), $d['km']??null, $d['cost']??0, $d['mechanic']??null, $d['garage']??null, $d['next_km']??null, $d['next_date']??null, $photo, $d['notes']??null]);
$mid = $db->lastInsertId();
if (!empty($d['km'])) { $db->prepare("UPDATE vehicles SET current_km = MAX(current_km, ?), updated_at = datetime('now') WHERE id = ?")->execute([$d['km'], $d['vehicle_id']]); }
jsonOk(['id' => $mid]);
}
if ($method === 'PUT') {
$id = $_GET['id'] ?? null; if (!$id) jsonErr('ID manquant'); $d = getBody();
$fields = ['type','description','date','km','cost','mechanic','garage','next_km','next_date','notes'];
$sets = array_map(fn($f) => "$f = ?", $fields); $vals = array_map(fn($f) => $d[$f] ?? null, $fields); $vals[] = $id;
$db->prepare("UPDATE maintenances SET " . implode(', ', $sets) . " WHERE id = ?")->execute($vals); jsonOk(['updated' => true]);
}
if ($method === 'DELETE') {
$id = $_GET['id'] ?? null; if (!$id) jsonErr('ID manquant');
$db->prepare("DELETE FROM maintenances WHERE id = ?")->execute([$id]); jsonOk(['deleted' => true]);
}
}
if ($action === 'parts') {
if ($method === 'GET') {
$vid = $_GET['vehicle_id'] ?? null; $mid = $_GET['maintenance_id'] ?? null;
if ($mid) { $stmt = $db->prepare("SELECT * FROM parts WHERE maintenance_id = ? ORDER BY created_at DESC"); $stmt->execute([$mid]); jsonOk($stmt->fetchAll()); }
if ($vid) { $stmt = $db->prepare("SELECT p.*, m.type as maintenance_type, m.date as maintenance_date FROM parts p LEFT JOIN maintenances m ON m.id = p.maintenance_id WHERE p.vehicle_id = ? ORDER BY p.created_at DESC"); $stmt->execute([$vid]); jsonOk($stmt->fetchAll()); }
$stmt = $db->query("SELECT p.*, v.name as vehicle_name, m.type as maintenance_type, m.date as maintenance_date FROM parts p LEFT JOIN vehicles v ON v.id = p.vehicle_id LEFT JOIN maintenances m ON m.id = p.maintenance_id ORDER BY p.created_at DESC");
jsonOk($stmt->fetchAll());
}
if ($method === 'POST') {
$photo = handleUpload('photo'); $d = $_POST ?: getBody();
$stmt = $db->prepare("INSERT INTO parts (vehicle_id,maintenance_id,brand,reference,name,category,price,quantity,unit,supplier,purchase_date,photo,notes) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)");
$stmt->execute([$d['vehicle_id']??null, $d['maintenance_id']??null, $d['brand']??null, $d['reference']??null, $d['name']??'', $d['category']??'Autre', $d['price']??0, $d['quantity']??1, $d['unit']??'piece', $d['supplier']??null, $d['purchase_date']??null, $photo, $d['notes']??null]);
jsonOk(['id' => $db->lastInsertId()]);
}
if ($method === 'PUT') {
$id = $_GET['id'] ?? null; if (!$id) jsonErr('ID manquant'); $d = getBody();
$fields = ['brand','reference','name','category','price','quantity','unit','supplier','purchase_date','notes'];
$sets = array_map(fn($f) => "$f = ?", $fields); $vals = array_map(fn($f) => $d[$f] ?? null, $fields); $vals[] = $id;
$db->prepare("UPDATE parts SET " . implode(', ', $sets) . " WHERE id = ?")->execute($vals); jsonOk(['updated' => true]);
}
if ($method === 'DELETE') {
$id = $_GET['id'] ?? null; if (!$id) jsonErr('ID manquant');
$r = $db->prepare("SELECT photo FROM parts WHERE id=?"); $r->execute([$id]); $row = $r->fetch(); if ($row['photo']) @unlink(UPLOADS_DIR . $row['photo']);
$db->prepare("DELETE FROM parts WHERE id = ?")->execute([$id]); jsonOk(['deleted' => true]);
}
}
if ($action === 'stats') {
$stats = ['vehicles' => $db->query("SELECT COUNT(*) as c FROM vehicles")->fetch()['c'], 'maintenances' => $db->query("SELECT COUNT(*) as c FROM maintenances")->fetch()['c'], 'parts' => $db->query("SELECT COUNT(*) as c FROM parts")->fetch()['c'], 'total_cost' => $db->query("SELECT COALESCE(SUM(cost),0) as s FROM maintenances")->fetch()['s'], 'total_parts_cost' => $db->query("SELECT COALESCE(SUM(price*quantity),0) as s FROM parts")->fetch()['s'], 'upcoming_reminders' => $db->query("SELECT COUNT(*) as c FROM maintenances WHERE next_date >= date('now') OR next_km IS NOT NULL")->fetch()['c']];
jsonOk($stats);
}
if ($action === 'upload_vehicle_photo') {
$id = $_GET['id'] ?? null; if (!$id) jsonErr('ID manquant');
$photo = handleUpload('photo'); if (!$photo) jsonErr('Upload echoue');
$old = $db->prepare("SELECT photo FROM vehicles WHERE id=?"); $old->execute([$id]); $row = $old->fetch(); if ($row['photo']) @unlink(UPLOADS_DIR . $row['photo']);
$db->prepare("UPDATE vehicles SET photo = ?, updated_at = datetime('now') WHERE id = ?")->execute([$photo, $id]); jsonOk(['photo' => $photo]);
}
if ($action === 'upload_part_photo') {
$id = $_GET['id'] ?? null; if (!$id) jsonErr('ID manquant');
$photo = handleUpload('photo'); if (!$photo) jsonErr('Upload echoue');
$db->prepare("UPDATE parts SET photo = ? WHERE id = ?")->execute([$photo, $id]); jsonOk(['photo' => $photo]);
}
jsonErr('Action inconnue', 404);