feat: infrastructure assets management with warranty tracking and EAN lookup integration
This commit is contained in:
148
app/Http/Controllers/AssetController.php
Normal file
148
app/Http/Controllers/AssetController.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\Commune;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class AssetController extends Controller
|
||||
{
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$this->authorize('viewAny', Asset::class);
|
||||
|
||||
$query = Asset::with('commune');
|
||||
|
||||
if ($request->search) {
|
||||
$query->where(function ($q) use ($request) {
|
||||
$q->where('nom', 'like', "%{$request->search}%")
|
||||
->orWhere('numero_serie', 'like', "%{$request->search}%")
|
||||
->orWhere('type', 'like', "%{$request->search}%");
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->type) {
|
||||
$query->where('type', $request->type);
|
||||
}
|
||||
|
||||
if ($request->commune_id) {
|
||||
$query->where('commune_id', $request->commune_id);
|
||||
}
|
||||
|
||||
$assets = $query->latest()->paginate(20)->withQueryString();
|
||||
|
||||
return Inertia::render('Assets/Index', [
|
||||
'assets' => $assets,
|
||||
'filters' => $request->only(['search', 'type', 'commune_id']),
|
||||
'communes' => Commune::orderBy('nom')->get(),
|
||||
'types' => Asset::distinct()->pluck('type'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(): Response
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
|
||||
return Inertia::render('Assets/Form', [
|
||||
'communes' => Commune::orderBy('nom')->get(),
|
||||
'commandes' => \App\Models\Commande::select('id', 'numero_commande', 'objet')->latest()->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
|
||||
$validated = $request->validate([
|
||||
'nom' => 'required|string|max:255',
|
||||
'type' => 'required|string|max:100',
|
||||
'code_ean' => 'nullable|string|max:50',
|
||||
'marque' => 'nullable|string|max:100',
|
||||
'modele' => 'nullable|string|max:100',
|
||||
'numero_serie' => 'nullable|string|max:100',
|
||||
'emplacement' => 'nullable|string|max:255',
|
||||
'commune_id' => 'nullable|exists:communes,id',
|
||||
'commande_id' => 'nullable|exists:commandes,id',
|
||||
'date_achat' => 'nullable|date',
|
||||
'date_fin_garantie' => 'nullable|date',
|
||||
'statut' => 'required|in:en_service,hors_service,en_reparation,stock',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
|
||||
Asset::create($validated);
|
||||
|
||||
return redirect()->route('assets.index')
|
||||
->with('success', 'Asset ajouté avec succès.');
|
||||
}
|
||||
|
||||
public function show(Asset $asset): Response
|
||||
{
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
$asset->load(['commune', 'commande']);
|
||||
|
||||
return Inertia::render('Assets/Show', [
|
||||
'asset' => $asset,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(Asset $asset): Response
|
||||
{
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
return Inertia::render('Assets/Form', [
|
||||
'asset' => $asset,
|
||||
'communes' => Commune::orderBy('nom')->get(),
|
||||
'commandes' => \App\Models\Commande::select('id', 'numero_commande', 'objet')->latest()->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, Asset $asset): RedirectResponse
|
||||
{
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
$validated = $request->validate([
|
||||
'nom' => 'required|string|max:255',
|
||||
'type' => 'required|string|max:100',
|
||||
'code_ean' => 'nullable|string|max:50',
|
||||
'marque' => 'nullable|string|max:100',
|
||||
'modele' => 'nullable|string|max:100',
|
||||
'numero_serie' => 'nullable|string|max:100',
|
||||
'emplacement' => 'nullable|string|max:255',
|
||||
'commune_id' => 'nullable|exists:communes,id',
|
||||
'commande_id' => 'nullable|exists:commandes,id',
|
||||
'date_achat' => 'nullable|date',
|
||||
'date_fin_garantie' => 'nullable|date',
|
||||
'statut' => 'required|in:en_service,hors_service,en_reparation,stock',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$asset->update($validated);
|
||||
|
||||
return redirect()->route('assets.index')
|
||||
->with('success', 'Asset mis à jour.');
|
||||
}
|
||||
|
||||
public function destroy(Asset $asset): RedirectResponse
|
||||
{
|
||||
$this->authorize('delete', $asset);
|
||||
|
||||
$asset->delete();
|
||||
|
||||
return redirect()->route('assets.index')
|
||||
->with('success', 'Asset supprimé.');
|
||||
}
|
||||
|
||||
public function lookupEan($ean)
|
||||
{
|
||||
$response = \Illuminate\Support\Facades\Http::get("https://api.upcitemdb.com/prod/trial/lookup", [
|
||||
'upc' => $ean
|
||||
]);
|
||||
|
||||
return response()->json($response->json(), $response->status());
|
||||
}
|
||||
}
|
||||
104
app/Http/Controllers/CalendarController.php
Normal file
104
app/Http/Controllers/CalendarController.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Contrat;
|
||||
use App\Models\Licence;
|
||||
use App\Models\Domaine;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class CalendarController extends Controller
|
||||
{
|
||||
public function index(): Response
|
||||
{
|
||||
return Inertia::render('Calendar/Index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch events for FullCalendar.
|
||||
*/
|
||||
public function events(Request $request)
|
||||
{
|
||||
$events = [];
|
||||
|
||||
// 1. Contracts expirations
|
||||
$contrats = Contrat::with(['fournisseur', 'service'])
|
||||
->whereNotNull('date_echeance')
|
||||
->get();
|
||||
|
||||
foreach ($contrats as $contrat) {
|
||||
$events[] = [
|
||||
'id' => 'contrat-' . $contrat->id,
|
||||
'title' => '📑 ' . $contrat->titre . ' (' . ($contrat->fournisseur->nom ?? 'N/A') . ')',
|
||||
'start' => $contrat->date_echeance->toDateString(),
|
||||
'url' => route('contrats.show', $contrat->id),
|
||||
'backgroundColor' => $this->getContratColor($contrat),
|
||||
'extendedProps' => [
|
||||
'type' => 'Contrat',
|
||||
'service' => $contrat->service->nom ?? 'N/A',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// 2. Licenses expirations
|
||||
$licences = Licence::with(['fournisseur'])
|
||||
->whereNotNull('date_expiration')
|
||||
->get();
|
||||
|
||||
foreach ($licences as $licence) {
|
||||
$events[] = [
|
||||
'id' => 'licence-' . $licence->id,
|
||||
'title' => '🔑 ' . $licence->nom . ' (' . ($licence->fournisseur->nom ?? 'N/A') . ')',
|
||||
'start' => $licence->date_expiration->toDateString(),
|
||||
'url' => route('licences.index'), // Link to index or show if implemented
|
||||
'backgroundColor' => '#3498db',
|
||||
'extendedProps' => [
|
||||
'type' => 'Licence',
|
||||
'usage' => $licence->nombre_sieges_utilises . '/' . $licence->nombre_sieges_total,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// 3. Domain expirations
|
||||
$domaines = Domaine::whereNotNull('date_echeance')->get();
|
||||
foreach ($domaines as $domaine) {
|
||||
$events[] = [
|
||||
'id' => 'domaine-' . $domaine->id,
|
||||
'title' => '🌐 ' . $domaine->nom,
|
||||
'start' => $domaine->date_echeance->toDateString(),
|
||||
'url' => route('domaines.index'),
|
||||
'backgroundColor' => '#9b59b6',
|
||||
'extendedProps' => [
|
||||
'type' => 'Domaine',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// 4. Asset warranties
|
||||
$assets = \App\Models\Asset::whereNotNull('date_fin_garantie')->get();
|
||||
foreach ($assets as $asset) {
|
||||
$events[] = [
|
||||
'id' => 'asset-' . $asset->id,
|
||||
'title' => '🛠️ Garantie : ' . $asset->nom . ' (' . $asset->type . ')',
|
||||
'start' => $asset->date_fin_garantie->toDateString(),
|
||||
'url' => route('assets.show', $asset->id),
|
||||
'backgroundColor' => $asset->garantie_expiree ? '#e74c3c' : '#f1c40f',
|
||||
'extendedProps' => [
|
||||
'type' => 'Asset (Garantie)',
|
||||
'statut' => $asset->statut,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return response()->json($events);
|
||||
}
|
||||
|
||||
private function getContratColor($contrat): string
|
||||
{
|
||||
if ($contrat->statut === 'expire') return '#e74c3c';
|
||||
if ($contrat->est_proche_echeance) return '#f39c12';
|
||||
return '#27ae60';
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ class CommandeController extends Controller
|
||||
$query = Commande::with(['service', 'fournisseur', 'demandeur'])
|
||||
->when($request->service_id, fn ($q) => $q->parService($request->service_id))
|
||||
->when($request->fournisseur_id, fn ($q) => $q->parFournisseur($request->fournisseur_id))
|
||||
->when($request->commune_id, fn ($q) => $q->where('commune_id', $request->commune_id))
|
||||
->when($request->statut, fn ($q) => $q->parStatut($request->statut))
|
||||
->when($request->priorite, fn ($q) => $q->where('priorite', $request->priorite))
|
||||
->when($request->date_from, fn ($q) => $q->whereDate('date_demande', '>=', $request->date_from))
|
||||
@@ -38,6 +39,7 @@ class CommandeController extends Controller
|
||||
'commandes' => $commandes,
|
||||
'services' => Service::all(),
|
||||
'fournisseurs' => Fournisseur::active()->orderBy('nom')->get(),
|
||||
'communes' => \App\Models\Commune::orderBy('nom')->get(),
|
||||
'filters' => $request->only(['search', 'service_id', 'fournisseur_id', 'statut', 'priorite', 'date_from', 'date_to']),
|
||||
]);
|
||||
}
|
||||
@@ -49,6 +51,7 @@ class CommandeController extends Controller
|
||||
return Inertia::render('Commandes/Create', [
|
||||
'services' => Service::all(),
|
||||
'fournisseurs' => Fournisseur::active()->orderBy('nom')->get(),
|
||||
'communes' => \App\Models\Commune::orderBy('nom')->get(),
|
||||
'categories' => Categorie::active()->orderBy('ordre')->get(),
|
||||
'articles' => Article::active()->with('categorie')->orderBy('designation')->get(),
|
||||
]);
|
||||
@@ -61,6 +64,7 @@ class CommandeController extends Controller
|
||||
$validated = $request->validate([
|
||||
'service_id' => 'required|exists:services,id',
|
||||
'fournisseur_id' => 'nullable|exists:fournisseurs,id',
|
||||
'commune_id' => 'nullable|exists:communes,id',
|
||||
'objet' => 'required|string|max:255',
|
||||
'description' => 'nullable|string',
|
||||
'justification' => 'nullable|string',
|
||||
@@ -106,10 +110,11 @@ class CommandeController extends Controller
|
||||
$this->authorize('view', $commande);
|
||||
|
||||
$commande->load([
|
||||
'service', 'fournisseur', 'demandeur', 'validateur', 'acheteur',
|
||||
'service', 'fournisseur', 'demandeur', 'validateur', 'acheteur', 'commune',
|
||||
'lignes.categorie',
|
||||
'historique.user',
|
||||
'piecesJointes.user',
|
||||
'assets',
|
||||
]);
|
||||
|
||||
$transitionsDisponibles = collect(Commande::STATUT_TRANSITIONS[$commande->statut] ?? [])
|
||||
@@ -132,6 +137,7 @@ class CommandeController extends Controller
|
||||
'commande' => $commande,
|
||||
'services' => Service::all(),
|
||||
'fournisseurs' => Fournisseur::active()->orderBy('nom')->get(),
|
||||
'communes' => \App\Models\Commune::orderBy('nom')->get(),
|
||||
'categories' => Categorie::active()->orderBy('ordre')->get(),
|
||||
'articles' => Article::active()->with('categorie')->orderBy('designation')->get(),
|
||||
]);
|
||||
@@ -144,6 +150,7 @@ class CommandeController extends Controller
|
||||
$validated = $request->validate([
|
||||
'service_id' => 'required|exists:services,id',
|
||||
'fournisseur_id' => 'nullable|exists:fournisseurs,id',
|
||||
'commune_id' => 'nullable|exists:communes,id',
|
||||
'objet' => 'required|string|max:255',
|
||||
'description' => 'nullable|string',
|
||||
'justification' => 'nullable|string',
|
||||
@@ -234,7 +241,7 @@ class CommandeController extends Controller
|
||||
$this->authorize('view', $commande);
|
||||
|
||||
$commande->load([
|
||||
'service', 'fournisseur', 'demandeur', 'validateur', 'acheteur',
|
||||
'service', 'fournisseur', 'demandeur', 'validateur', 'acheteur', 'commune',
|
||||
'lignes.categorie',
|
||||
]);
|
||||
|
||||
|
||||
54
app/Http/Controllers/CommuneController.php
Normal file
54
app/Http/Controllers/CommuneController.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Commune;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class CommuneController extends Controller
|
||||
{
|
||||
public function index(): Response
|
||||
{
|
||||
return Inertia::render('Communes/Index', [
|
||||
'communes' => Commune::withCount('commandes', 'contrats')->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'nom' => 'required|string|max:100|unique:communes,nom',
|
||||
'code_postal' => 'nullable|string|max:10',
|
||||
]);
|
||||
|
||||
Commune::create($validated);
|
||||
|
||||
return back()->with('success', 'Commune créée.');
|
||||
}
|
||||
|
||||
public function update(Request $request, Commune $commune): RedirectResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'nom' => 'required|string|max:100|unique:communes,nom,' . $commune->id,
|
||||
'code_postal' => 'nullable|string|max:10',
|
||||
]);
|
||||
|
||||
$commune->update($validated);
|
||||
|
||||
return back()->with('success', 'Commune mise à jour.');
|
||||
}
|
||||
|
||||
public function destroy(Commune $commune): RedirectResponse
|
||||
{
|
||||
if ($commune->commandes()->exists() || $commune->contrats()->exists()) {
|
||||
return back()->with('error', 'Impossible de supprimer une commune liée à des commandes ou des contrats.');
|
||||
}
|
||||
|
||||
$commune->delete();
|
||||
|
||||
return back()->with('success', 'Commune supprimée.');
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,8 @@ class ContratController extends Controller
|
||||
}
|
||||
})->when($request->fournisseur_id, function ($q, $fournisseurId) {
|
||||
$q->where('fournisseur_id', $fournisseurId);
|
||||
})->when($request->commune_id, function ($q, $communeId) {
|
||||
$q->where('commune_id', $communeId);
|
||||
})->when($request->statut, function ($q, $statut) {
|
||||
$q->where('statut', $statut);
|
||||
});
|
||||
@@ -50,6 +52,7 @@ class ContratController extends Controller
|
||||
'contrats' => $contrats,
|
||||
'services' => $request->user()->hasRole('admin') ? Service::all() : Service::where('id', $request->user()->service_id)->get(),
|
||||
'fournisseurs' => Fournisseur::active()->orderBy('nom')->get(),
|
||||
'communes' => \App\Models\Commune::orderBy('nom')->get(),
|
||||
'filters' => $request->only(['search', 'service_id', 'fournisseur_id', 'statut']),
|
||||
'statuts' => Contrat::STATUTS_LABELS,
|
||||
]);
|
||||
@@ -62,6 +65,7 @@ class ContratController extends Controller
|
||||
return Inertia::render('Contrats/Create', [
|
||||
'services' => $request->user()->hasRole('admin') ? Service::all() : Service::where('id', $request->user()->service_id)->get(),
|
||||
'fournisseurs' => Fournisseur::active()->orderBy('nom')->get(),
|
||||
'communes' => \App\Models\Commune::orderBy('nom')->get(),
|
||||
'statuts' => Contrat::STATUTS_LABELS,
|
||||
]);
|
||||
}
|
||||
@@ -102,7 +106,7 @@ class ContratController extends Controller
|
||||
{
|
||||
$this->authorize('view', $contrat);
|
||||
|
||||
$contrat->load(['fournisseur', 'service', 'piecesJointes.user']);
|
||||
$contrat->load(['fournisseur', 'service', 'commune', 'piecesJointes.user']);
|
||||
$contrat->append(['est_proche_echeance', 'est_en_retard']);
|
||||
|
||||
return Inertia::render('Contrats/Show', [
|
||||
@@ -118,6 +122,7 @@ class ContratController extends Controller
|
||||
'contrat' => $contrat,
|
||||
'services' => $request->user()->hasRole('admin') ? Service::all() : Service::where('id', $request->user()->service_id)->get(),
|
||||
'fournisseurs' => Fournisseur::active()->orderBy('nom')->get(),
|
||||
'communes' => \App\Models\Commune::orderBy('nom')->get(),
|
||||
'statuts' => Contrat::STATUTS_LABELS,
|
||||
]);
|
||||
}
|
||||
|
||||
118
app/Http/Controllers/LicenceController.php
Normal file
118
app/Http/Controllers/LicenceController.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Commune;
|
||||
use App\Models\Contrat;
|
||||
use App\Models\Fournisseur;
|
||||
use App\Models\Licence;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class LicenceController extends Controller
|
||||
{
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$this->authorize('viewAny', Licence::class);
|
||||
|
||||
$query = Licence::with(['fournisseur', 'contrat', 'commune']);
|
||||
|
||||
if (!$request->user()->hasRole('admin')) {
|
||||
$query->where('commune_id', $request->user()->commune_id);
|
||||
}
|
||||
|
||||
$licences = $query->orderBy('date_expiration', 'asc')->paginate(20)->withQueryString();
|
||||
|
||||
return Inertia::render('Licences/Index', [
|
||||
'licences' => $licences,
|
||||
'filters' => $request->only(['search', 'commune_id', 'fournisseur_id']),
|
||||
'communes' => Commune::orderBy('nom')->get(),
|
||||
'fournisseurs' => Fournisseur::active()->orderBy('nom')->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(): Response
|
||||
{
|
||||
$this->authorize('create', Licence::class);
|
||||
|
||||
return Inertia::render('Licences/Form', [
|
||||
'contrats' => Contrat::orderBy('titre')->get(),
|
||||
'fournisseurs' => Fournisseur::active()->orderBy('nom')->get(),
|
||||
'communes' => Commune::orderBy('nom')->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$this->authorize('create', Licence::class);
|
||||
|
||||
$validated = $request->validate([
|
||||
'nom' => 'required|string|max:255',
|
||||
'contrat_id' => 'nullable|exists:contrats,id',
|
||||
'fournisseur_id' => 'required|exists:fournisseurs,id',
|
||||
'commune_id' => 'nullable|exists:communes,id',
|
||||
'cle_licence' => 'nullable|string',
|
||||
'nombre_sieges_total' => 'required|integer|min:1',
|
||||
'nombre_sieges_utilises' => 'required|integer|min:0',
|
||||
'date_acquisition' => 'nullable|date',
|
||||
'date_expiration' => 'nullable|date',
|
||||
'type_licence' => 'required|in:perpétuelle,abonnement',
|
||||
'statut' => 'required|in:active,expirée,résiliée',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
|
||||
Licence::create($validated);
|
||||
|
||||
return redirect()->route('licences.index')
|
||||
->with('success', 'Licence créée avec succès.');
|
||||
}
|
||||
|
||||
public function edit(Licence $licence): Response
|
||||
{
|
||||
$this->authorize('update', $licence);
|
||||
|
||||
return Inertia::render('Licences/Form', [
|
||||
'licence' => $licence,
|
||||
'contrats' => Contrat::orderBy('titre')->get(),
|
||||
'fournisseurs' => Fournisseur::active()->orderBy('nom')->get(),
|
||||
'communes' => Commune::orderBy('nom')->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, Licence $licence): RedirectResponse
|
||||
{
|
||||
$this->authorize('update', $licence);
|
||||
|
||||
$validated = $request->validate([
|
||||
'nom' => 'required|string|max:255',
|
||||
'contrat_id' => 'nullable|exists:contrats,id',
|
||||
'fournisseur_id' => 'required|exists:fournisseurs,id',
|
||||
'commune_id' => 'nullable|exists:communes,id',
|
||||
'cle_licence' => 'nullable|string',
|
||||
'nombre_sieges_total' => 'required|integer|min:1',
|
||||
'nombre_sieges_utilises' => 'required|integer|min:0',
|
||||
'date_acquisition' => 'nullable|date',
|
||||
'date_expiration' => 'nullable|date',
|
||||
'type_licence' => 'required|in:perpétuelle,abonnement',
|
||||
'statut' => 'required|in:active,expirée,résiliée',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$licence->update($validated);
|
||||
|
||||
return redirect()->route('licences.index')
|
||||
->with('success', 'Licence mise à jour.');
|
||||
}
|
||||
|
||||
public function destroy(Licence $licence): RedirectResponse
|
||||
{
|
||||
$this->authorize('delete', $licence);
|
||||
|
||||
$licence->delete();
|
||||
|
||||
return redirect()->route('licences.index')
|
||||
->with('success', 'Licence supprimée.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user