diff --git a/app/Http/Controllers/AssetController.php b/app/Http/Controllers/AssetController.php
new file mode 100644
index 0000000..36033cd
--- /dev/null
+++ b/app/Http/Controllers/AssetController.php
@@ -0,0 +1,148 @@
+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());
+ }
+}
diff --git a/app/Http/Controllers/CalendarController.php b/app/Http/Controllers/CalendarController.php
new file mode 100644
index 0000000..0c87d2c
--- /dev/null
+++ b/app/Http/Controllers/CalendarController.php
@@ -0,0 +1,104 @@
+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';
+ }
+}
diff --git a/app/Http/Controllers/CommandeController.php b/app/Http/Controllers/CommandeController.php
index ce689d3..138cde0 100644
--- a/app/Http/Controllers/CommandeController.php
+++ b/app/Http/Controllers/CommandeController.php
@@ -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',
]);
diff --git a/app/Http/Controllers/CommuneController.php b/app/Http/Controllers/CommuneController.php
new file mode 100644
index 0000000..34f0240
--- /dev/null
+++ b/app/Http/Controllers/CommuneController.php
@@ -0,0 +1,54 @@
+ 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.');
+ }
+}
diff --git a/app/Http/Controllers/ContratController.php b/app/Http/Controllers/ContratController.php
index 4d4c755..2e09ab3 100644
--- a/app/Http/Controllers/ContratController.php
+++ b/app/Http/Controllers/ContratController.php
@@ -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,
]);
}
diff --git a/app/Http/Controllers/LicenceController.php b/app/Http/Controllers/LicenceController.php
new file mode 100644
index 0000000..2fa5dfe
--- /dev/null
+++ b/app/Http/Controllers/LicenceController.php
@@ -0,0 +1,118 @@
+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.');
+ }
+}
diff --git a/app/Models/Asset.php b/app/Models/Asset.php
new file mode 100644
index 0000000..2b13f2b
--- /dev/null
+++ b/app/Models/Asset.php
@@ -0,0 +1,71 @@
+ 'date',
+ 'date_fin_garantie' => 'date',
+ ];
+
+ public function commune(): BelongsTo
+ {
+ return $this->belongsTo(Commune::class);
+ }
+
+ public function commande(): BelongsTo
+ {
+ return $this->belongsTo(Commande::class);
+ }
+
+ public function getEstSousGarantieAttribute(): bool
+ {
+ if (!$this->date_fin_garantie) {
+ return false;
+ }
+
+ return Carbon::now()->isBefore($this->date_fin_garantie);
+ }
+
+ public function getGarantieExpireeAttribute(): bool
+ {
+ if (!$this->date_fin_garantie) {
+ return false;
+ }
+
+ return Carbon::now()->isAfter($this->date_fin_garantie);
+ }
+
+ public function getEstProcheExpirationGarantieAttribute(): bool
+ {
+ if (!$this->date_fin_garantie || $this->garantie_expiree) {
+ return false;
+ }
+
+ return Carbon::now()->diffInDays($this->date_fin_garantie, false) <= 30;
+ }
+}
diff --git a/app/Models/Commande.php b/app/Models/Commande.php
index 8819e04..116afa4 100644
--- a/app/Models/Commande.php
+++ b/app/Models/Commande.php
@@ -14,7 +14,7 @@ class Commande extends Model
use SoftDeletes;
protected $fillable = [
- 'numero_commande', 'service_id', 'fournisseur_id', 'user_id',
+ 'numero_commande', 'service_id', 'fournisseur_id', 'user_id', 'commune_id',
'validateur_id', 'acheteur_id', 'objet', 'description', 'justification',
'statut', 'priorite', 'reference_fournisseur', 'imputation_budgetaire',
'montant_ht', 'montant_ttc',
@@ -98,11 +98,21 @@ class Commande extends Model
return $this->belongsTo(User::class, 'user_id');
}
+ public function commune(): BelongsTo
+ {
+ return $this->belongsTo(Commune::class);
+ }
+
public function validateur(): BelongsTo
{
return $this->belongsTo(User::class, 'validateur_id');
}
+ public function assets(): HasMany
+ {
+ return $this->hasMany(Asset::class);
+ }
+
public function acheteur(): BelongsTo
{
return $this->belongsTo(User::class, 'acheteur_id');
diff --git a/app/Models/Commune.php b/app/Models/Commune.php
new file mode 100644
index 0000000..bcad813
--- /dev/null
+++ b/app/Models/Commune.php
@@ -0,0 +1,21 @@
+hasMany(Commande::class);
+ }
+
+ public function contrats(): HasMany
+ {
+ return $this->hasMany(Contrat::class);
+ }
+}
diff --git a/app/Models/Contrat.php b/app/Models/Contrat.php
index 242a2b9..e8a42a6 100644
--- a/app/Models/Contrat.php
+++ b/app/Models/Contrat.php
@@ -17,6 +17,7 @@ class Contrat extends Model
'description',
'fournisseur_id',
'service_id',
+ 'commune_id',
'date_debut',
'date_echeance',
'statut',
@@ -47,11 +48,21 @@ class Contrat extends Model
return $this->belongsTo(Service::class);
}
+ public function commune(): BelongsTo
+ {
+ return $this->belongsTo(Commune::class);
+ }
+
public function piecesJointes(): HasMany
{
return $this->hasMany(PieceJointe::class)->orderByDesc('created_at');
}
+ public function licences(): HasMany
+ {
+ return $this->hasMany(Licence::class);
+ }
+
// Un contrat est considéré "proche d'expiration" si l'échéance est dans moins de 30 jours (ou selon son préavis)
public function getEstProcheEcheanceAttribute(): bool
{
diff --git a/app/Models/Licence.php b/app/Models/Licence.php
new file mode 100644
index 0000000..0f1a497
--- /dev/null
+++ b/app/Models/Licence.php
@@ -0,0 +1,60 @@
+ 'date',
+ 'date_expiration' => 'date',
+ 'cle_licence' => 'encrypted', // Encrypted storage for license keys
+ ];
+
+ public function contrat(): BelongsTo
+ {
+ return $this->belongsTo(Contrat::class);
+ }
+
+ public function fournisseur(): BelongsTo
+ {
+ return $this->belongsTo(Fournisseur::class);
+ }
+
+ public function commune(): BelongsTo
+ {
+ return $this->belongsTo(Commune::class);
+ }
+
+ /**
+ * Get the usage rate as a percentage.
+ */
+ public function getTauxUtilisationAttribute(): float
+ {
+ if ($this->nombre_sieges_total <= 0) {
+ return 0;
+ }
+
+ return round(($this->nombre_sieges_utilises / $this->nombre_sieges_total) * 100, 2);
+ }
+}
diff --git a/app/Policies/AssetPolicy.php b/app/Policies/AssetPolicy.php
new file mode 100644
index 0000000..12c7d6c
--- /dev/null
+++ b/app/Policies/AssetPolicy.php
@@ -0,0 +1,53 @@
+hasRole('admin')) return true;
+ return $user->commune_id === $asset->commune_id;
+ }
+
+ /**
+ * Determine whether the user can create models.
+ */
+ public function create(User $user): bool
+ {
+ return true;
+ }
+
+ /**
+ * Determine whether the user can update the model.
+ */
+ public function update(User $user, Asset $asset): bool
+ {
+ if ($user->hasRole('admin')) return true;
+ return $user->commune_id === $asset->commune_id;
+ }
+
+ /**
+ * Determine whether the user can delete the model.
+ */
+ public function delete(User $user, Asset $asset): bool
+ {
+ if ($user->hasRole('admin')) return true;
+ return $user->commune_id === $asset->commune_id;
+ }
+}
diff --git a/app/Policies/LicencePolicy.php b/app/Policies/LicencePolicy.php
new file mode 100644
index 0000000..9998ebe
--- /dev/null
+++ b/app/Policies/LicencePolicy.php
@@ -0,0 +1,53 @@
+hasRole('admin')) return true;
+ return $user->commune_id === $licence->commune_id;
+ }
+
+ /**
+ * Determine whether the user can create models.
+ */
+ public function create(User $user): bool
+ {
+ return true; // Simple users can create
+ }
+
+ /**
+ * Determine whether the user can update the model.
+ */
+ public function update(User $user, Licence $licence): bool
+ {
+ if ($user->hasRole('admin')) return true;
+ return $user->commune_id === $licence->commune_id;
+ }
+
+ /**
+ * Determine whether the user can delete the model.
+ */
+ public function delete(User $user, Licence $licence): bool
+ {
+ if ($user->hasRole('admin')) return true;
+ return $user->commune_id === $licence->commune_id;
+ }
+}
diff --git a/composer.lock b/composer.lock
index da25a37..9a43eaf 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1287,16 +1287,16 @@
},
{
"name": "inertiajs/inertia-laravel",
- "version": "v2.0.22",
+ "version": "v2.0.23",
"source": {
"type": "git",
"url": "https://github.com/inertiajs/inertia-laravel.git",
- "reference": "4d5849328d4c64231f886d1422fdc945882f9094"
+ "reference": "20438dc5a0f3008965ccaa96e990d03116102d80"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/4d5849328d4c64231f886d1422fdc945882f9094",
- "reference": "4d5849328d4c64231f886d1422fdc945882f9094",
+ "url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/20438dc5a0f3008965ccaa96e990d03116102d80",
+ "reference": "20438dc5a0f3008965ccaa96e990d03116102d80",
"shasum": ""
},
"require": {
@@ -1354,9 +1354,9 @@
],
"support": {
"issues": "https://github.com/inertiajs/inertia-laravel/issues",
- "source": "https://github.com/inertiajs/inertia-laravel/tree/v2.0.22"
+ "source": "https://github.com/inertiajs/inertia-laravel/tree/v2.0.23"
},
- "time": "2026-03-11T15:51:16+00:00"
+ "time": "2026-04-07T14:01:31+00:00"
},
{
"name": "io-developer/php-whois",
@@ -1422,16 +1422,16 @@
},
{
"name": "laravel/framework",
- "version": "v13.3.0",
+ "version": "v13.4.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
- "reference": "118b7063c44a2f3421d1646f5ddf08defcfd1db3"
+ "reference": "912de244f88a69742b76e8a2807f6765947776da"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/framework/zipball/118b7063c44a2f3421d1646f5ddf08defcfd1db3",
- "reference": "118b7063c44a2f3421d1646f5ddf08defcfd1db3",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/912de244f88a69742b76e8a2807f6765947776da",
+ "reference": "912de244f88a69742b76e8a2807f6765947776da",
"shasum": ""
},
"require": {
@@ -1640,7 +1640,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-04-01T15:39:53+00:00"
+ "time": "2026-04-07T13:38:26+00:00"
},
{
"name": "laravel/prompts",
@@ -1766,16 +1766,16 @@
},
{
"name": "laravel/serializable-closure",
- "version": "v2.0.10",
+ "version": "v2.0.11",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
- "reference": "870fc81d2f879903dfc5b60bf8a0f94a1609e669"
+ "reference": "d1af40ac4a6ccc12bd062a7184f63c9995a63bdd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/870fc81d2f879903dfc5b60bf8a0f94a1609e669",
- "reference": "870fc81d2f879903dfc5b60bf8a0f94a1609e669",
+ "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/d1af40ac4a6ccc12bd062a7184f63c9995a63bdd",
+ "reference": "d1af40ac4a6ccc12bd062a7184f63c9995a63bdd",
"shasum": ""
},
"require": {
@@ -1823,7 +1823,7 @@
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
- "time": "2026-02-20T19:59:49+00:00"
+ "time": "2026-04-07T13:32:18+00:00"
},
{
"name": "laravel/tinker",
@@ -2625,16 +2625,16 @@
},
{
"name": "nesbot/carbon",
- "version": "3.11.3",
+ "version": "3.11.4",
"source": {
"type": "git",
"url": "https://github.com/CarbonPHP/carbon.git",
- "reference": "6a7e652845bb018c668220c2a545aded8594fbbf"
+ "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/6a7e652845bb018c668220c2a545aded8594fbbf",
- "reference": "6a7e652845bb018c668220c2a545aded8594fbbf",
+ "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/e890471a3494740f7d9326d72ce6a8c559ffee60",
+ "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60",
"shasum": ""
},
"require": {
@@ -2726,7 +2726,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-11T17:23:39+00:00"
+ "time": "2026-04-07T09:57:54+00:00"
},
{
"name": "nette/schema",
@@ -4029,16 +4029,16 @@
},
{
"name": "spatie/laravel-permission",
- "version": "7.2.4",
+ "version": "7.3.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-permission.git",
- "reference": "0a8ab4b84dc5efe23be9ddcd77951e10030c452d"
+ "reference": "5272955119759cd217e84e8bbac19443c305ebc3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/0a8ab4b84dc5efe23be9ddcd77951e10030c452d",
- "reference": "0a8ab4b84dc5efe23be9ddcd77951e10030c452d",
+ "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/5272955119759cd217e84e8bbac19443c305ebc3",
+ "reference": "5272955119759cd217e84e8bbac19443c305ebc3",
"shasum": ""
},
"require": {
@@ -4046,7 +4046,7 @@
"illuminate/container": "^12.0|^13.0",
"illuminate/contracts": "^12.0|^13.0",
"illuminate/database": "^12.0|^13.0",
- "php": "^8.4",
+ "php": "^8.3",
"spatie/laravel-package-tools": "^1.0"
},
"require-dev": {
@@ -4104,7 +4104,7 @@
],
"support": {
"issues": "https://github.com/spatie/laravel-permission/issues",
- "source": "https://github.com/spatie/laravel-permission/tree/7.2.4"
+ "source": "https://github.com/spatie/laravel-permission/tree/7.3.0"
},
"funding": [
{
@@ -4112,25 +4112,26 @@
"type": "github"
}
],
- "time": "2026-03-17T22:57:08+00:00"
+ "time": "2026-04-07T15:19:42+00:00"
},
{
"name": "symfony/clock",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/clock.git",
- "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3"
+ "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/clock/zipball/b55a638b189a6faa875e0ccdb00908fb87af95b3",
- "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3",
+ "url": "https://api.github.com/repos/symfony/clock/zipball/674fa3b98e21531dd040e613479f5f6fa8f32111",
+ "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111",
"shasum": ""
},
"require": {
- "php": ">=8.4",
- "psr/clock": "^1.0"
+ "php": ">=8.2",
+ "psr/clock": "^1.0",
+ "symfony/polyfill-php83": "^1.28"
},
"provide": {
"psr/clock-implementation": "1.0"
@@ -4169,7 +4170,7 @@
"time"
],
"support": {
- "source": "https://github.com/symfony/clock/tree/v8.0.8"
+ "source": "https://github.com/symfony/clock/tree/v7.4.8"
},
"funding": [
{
@@ -4189,43 +4190,51 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
"name": "symfony/console",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7"
+ "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/5b66d385dc58f69652e56f78a4184615e3f2b7f7",
- "reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7",
+ "url": "https://api.github.com/repos/symfony/console/zipball/1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707",
+ "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707",
"shasum": ""
},
"require": {
- "php": ">=8.4",
- "symfony/polyfill-mbstring": "^1.0",
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0",
"symfony/service-contracts": "^2.5|^3",
- "symfony/string": "^7.4|^8.0"
+ "symfony/string": "^7.2|^8.0"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/dotenv": "<6.4",
+ "symfony/event-dispatcher": "<6.4",
+ "symfony/lock": "<6.4",
+ "symfony/process": "<6.4"
},
"provide": {
"psr/log-implementation": "1.0|2.0|3.0"
},
"require-dev": {
"psr/log": "^1|^2|^3",
- "symfony/config": "^7.4|^8.0",
- "symfony/dependency-injection": "^7.4|^8.0",
- "symfony/event-dispatcher": "^7.4|^8.0",
- "symfony/http-foundation": "^7.4|^8.0",
- "symfony/http-kernel": "^7.4|^8.0",
- "symfony/lock": "^7.4|^8.0",
- "symfony/messenger": "^7.4|^8.0",
- "symfony/process": "^7.4|^8.0",
- "symfony/stopwatch": "^7.4|^8.0",
- "symfony/var-dumper": "^7.4|^8.0"
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/event-dispatcher": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/lock": "^6.4|^7.0|^8.0",
+ "symfony/messenger": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/stopwatch": "^6.4|^7.0|^8.0",
+ "symfony/var-dumper": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -4259,7 +4268,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v8.0.8"
+ "source": "https://github.com/symfony/console/tree/v7.4.8"
},
"funding": [
{
@@ -4279,24 +4288,24 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-30T13:54:39+00:00"
},
{
"name": "symfony/css-selector",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
- "reference": "8db1c00226a94d8ab6aa89d9224eeee91e2ea2ed"
+ "reference": "b055f228a4178a1d6774909903905e3475f3eac8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/css-selector/zipball/8db1c00226a94d8ab6aa89d9224eeee91e2ea2ed",
- "reference": "8db1c00226a94d8ab6aa89d9224eeee91e2ea2ed",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/b055f228a4178a1d6774909903905e3475f3eac8",
+ "reference": "b055f228a4178a1d6774909903905e3475f3eac8",
"shasum": ""
},
"require": {
- "php": ">=8.4"
+ "php": ">=8.2"
},
"type": "library",
"autoload": {
@@ -4328,7 +4337,7 @@
"description": "Converts CSS selectors to XPath expressions",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/css-selector/tree/v8.0.8"
+ "source": "https://github.com/symfony/css-selector/tree/v7.4.8"
},
"funding": [
{
@@ -4348,7 +4357,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -4419,32 +4428,33 @@
},
{
"name": "symfony/error-handler",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
- "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517"
+ "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/error-handler/zipball/c1119fe8dcfc3825ec74ec061b96ef0c8f281517",
- "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa",
+ "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa",
"shasum": ""
},
"require": {
- "php": ">=8.4",
+ "php": ">=8.2",
"psr/log": "^1|^2|^3",
"symfony/polyfill-php85": "^1.32",
- "symfony/var-dumper": "^7.4|^8.0"
+ "symfony/var-dumper": "^6.4|^7.0|^8.0"
},
"conflict": {
- "symfony/deprecation-contracts": "<2.5"
+ "symfony/deprecation-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4"
},
"require-dev": {
- "symfony/console": "^7.4|^8.0",
+ "symfony/console": "^6.4|^7.0|^8.0",
"symfony/deprecation-contracts": "^2.5|^3",
- "symfony/http-kernel": "^7.4|^8.0",
- "symfony/serializer": "^7.4|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/serializer": "^6.4|^7.0|^8.0",
"symfony/webpack-encore-bundle": "^1.0|^2.0"
},
"bin": [
@@ -4476,7 +4486,7 @@
"description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/error-handler/tree/v8.0.8"
+ "source": "https://github.com/symfony/error-handler/tree/v7.4.8"
},
"funding": [
{
@@ -4496,28 +4506,28 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "f662acc6ab22a3d6d716dcb44c381c6002940df6"
+ "reference": "f57b899fa736fd71121168ef268f23c206083f0a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f662acc6ab22a3d6d716dcb44c381c6002940df6",
- "reference": "f662acc6ab22a3d6d716dcb44c381c6002940df6",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f57b899fa736fd71121168ef268f23c206083f0a",
+ "reference": "f57b899fa736fd71121168ef268f23c206083f0a",
"shasum": ""
},
"require": {
- "php": ">=8.4",
+ "php": ">=8.2",
"symfony/event-dispatcher-contracts": "^2.5|^3"
},
"conflict": {
- "symfony/security-http": "<7.4",
+ "symfony/dependency-injection": "<6.4",
"symfony/service-contracts": "<2.5"
},
"provide": {
@@ -4526,14 +4536,14 @@
},
"require-dev": {
"psr/log": "^1|^2|^3",
- "symfony/config": "^7.4|^8.0",
- "symfony/dependency-injection": "^7.4|^8.0",
- "symfony/error-handler": "^7.4|^8.0",
- "symfony/expression-language": "^7.4|^8.0",
- "symfony/framework-bundle": "^7.4|^8.0",
- "symfony/http-foundation": "^7.4|^8.0",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/error-handler": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/framework-bundle": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^6.4|^7.0|^8.0",
"symfony/service-contracts": "^2.5|^3",
- "symfony/stopwatch": "^7.4|^8.0"
+ "symfony/stopwatch": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -4561,7 +4571,7 @@
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.8"
+ "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.8"
},
"funding": [
{
@@ -4581,7 +4591,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-30T13:54:39+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
@@ -4661,23 +4671,23 @@
},
{
"name": "symfony/finder",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "8da41214757b87d97f181e3d14a4179286151007"
+ "reference": "e0be088d22278583a82da281886e8c3592fbf149"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/8da41214757b87d97f181e3d14a4179286151007",
- "reference": "8da41214757b87d97f181e3d14a4179286151007",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/e0be088d22278583a82da281886e8c3592fbf149",
+ "reference": "e0be088d22278583a82da281886e8c3592fbf149",
"shasum": ""
},
"require": {
- "php": ">=8.4"
+ "php": ">=8.2"
},
"require-dev": {
- "symfony/filesystem": "^7.4|^8.0"
+ "symfony/filesystem": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -4705,7 +4715,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/finder/tree/v8.0.8"
+ "source": "https://github.com/symfony/finder/tree/v7.4.8"
},
"funding": [
{
@@ -4725,39 +4735,41 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
"name": "symfony/http-foundation",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "02656f7ebeae5c155d659e946f6b3a33df24051b"
+ "reference": "9381209597ec66c25be154cbf2289076e64d1eab"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/02656f7ebeae5c155d659e946f6b3a33df24051b",
- "reference": "02656f7ebeae5c155d659e946f6b3a33df24051b",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9381209597ec66c25be154cbf2289076e64d1eab",
+ "reference": "9381209597ec66c25be154cbf2289076e64d1eab",
"shasum": ""
},
"require": {
- "php": ">=8.4",
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-mbstring": "^1.1"
},
"conflict": {
- "doctrine/dbal": "<4.3"
+ "doctrine/dbal": "<3.6",
+ "symfony/cache": "<6.4.12|>=7.0,<7.1.5"
},
"require-dev": {
- "doctrine/dbal": "^4.3",
+ "doctrine/dbal": "^3.6|^4",
"predis/predis": "^1.1|^2.0",
- "symfony/cache": "^7.4|^8.0",
- "symfony/clock": "^7.4|^8.0",
- "symfony/dependency-injection": "^7.4|^8.0",
- "symfony/expression-language": "^7.4|^8.0",
- "symfony/http-kernel": "^7.4|^8.0",
- "symfony/mime": "^7.4|^8.0",
- "symfony/rate-limiter": "^7.4|^8.0"
+ "symfony/cache": "^6.4.12|^7.1.5|^8.0",
+ "symfony/clock": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/mime": "^6.4|^7.0|^8.0",
+ "symfony/rate-limiter": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -4785,7 +4797,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-foundation/tree/v8.0.8"
+ "source": "https://github.com/symfony/http-foundation/tree/v7.4.8"
},
"funding": [
{
@@ -4805,63 +4817,78 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
"name": "symfony/http-kernel",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "1770f6818d83b2fddc12185025b93f39a90cb628"
+ "reference": "017e76ad089bac281553389269e259e155935e1a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/1770f6818d83b2fddc12185025b93f39a90cb628",
- "reference": "1770f6818d83b2fddc12185025b93f39a90cb628",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/017e76ad089bac281553389269e259e155935e1a",
+ "reference": "017e76ad089bac281553389269e259e155935e1a",
"shasum": ""
},
"require": {
- "php": ">=8.4",
+ "php": ">=8.2",
"psr/log": "^1|^2|^3",
- "symfony/error-handler": "^7.4|^8.0",
- "symfony/event-dispatcher": "^7.4|^8.0",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/error-handler": "^6.4|^7.0|^8.0",
+ "symfony/event-dispatcher": "^7.3|^8.0",
"symfony/http-foundation": "^7.4|^8.0",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
+ "symfony/browser-kit": "<6.4",
+ "symfony/cache": "<6.4",
+ "symfony/config": "<6.4",
+ "symfony/console": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/doctrine-bridge": "<6.4",
"symfony/flex": "<2.10",
+ "symfony/form": "<6.4",
+ "symfony/http-client": "<6.4",
"symfony/http-client-contracts": "<2.5",
+ "symfony/mailer": "<6.4",
+ "symfony/messenger": "<6.4",
+ "symfony/translation": "<6.4",
"symfony/translation-contracts": "<2.5",
- "twig/twig": "<3.21"
+ "symfony/twig-bridge": "<6.4",
+ "symfony/validator": "<6.4",
+ "symfony/var-dumper": "<6.4",
+ "twig/twig": "<3.12"
},
"provide": {
"psr/log-implementation": "1.0|2.0|3.0"
},
"require-dev": {
"psr/cache": "^1.0|^2.0|^3.0",
- "symfony/browser-kit": "^7.4|^8.0",
- "symfony/clock": "^7.4|^8.0",
- "symfony/config": "^7.4|^8.0",
- "symfony/console": "^7.4|^8.0",
- "symfony/css-selector": "^7.4|^8.0",
- "symfony/dependency-injection": "^7.4|^8.0",
- "symfony/dom-crawler": "^7.4|^8.0",
- "symfony/expression-language": "^7.4|^8.0",
- "symfony/finder": "^7.4|^8.0",
+ "symfony/browser-kit": "^6.4|^7.0|^8.0",
+ "symfony/clock": "^6.4|^7.0|^8.0",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/css-selector": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4.1|^7.0.1|^8.0",
+ "symfony/dom-crawler": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/finder": "^6.4|^7.0|^8.0",
"symfony/http-client-contracts": "^2.5|^3",
- "symfony/process": "^7.4|^8.0",
- "symfony/property-access": "^7.4|^8.0",
- "symfony/routing": "^7.4|^8.0",
- "symfony/serializer": "^7.4|^8.0",
- "symfony/stopwatch": "^7.4|^8.0",
- "symfony/translation": "^7.4|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/property-access": "^7.1|^8.0",
+ "symfony/routing": "^6.4|^7.0|^8.0",
+ "symfony/serializer": "^7.1|^8.0",
+ "symfony/stopwatch": "^6.4|^7.0|^8.0",
+ "symfony/translation": "^6.4|^7.0|^8.0",
"symfony/translation-contracts": "^2.5|^3",
- "symfony/uid": "^7.4|^8.0",
- "symfony/validator": "^7.4|^8.0",
- "symfony/var-dumper": "^7.4|^8.0",
- "symfony/var-exporter": "^7.4|^8.0",
- "twig/twig": "^3.21"
+ "symfony/uid": "^6.4|^7.0|^8.0",
+ "symfony/validator": "^6.4|^7.0|^8.0",
+ "symfony/var-dumper": "^6.4|^7.0|^8.0",
+ "symfony/var-exporter": "^6.4|^7.0|^8.0",
+ "twig/twig": "^3.12"
},
"type": "library",
"autoload": {
@@ -4889,7 +4916,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-kernel/tree/v8.0.8"
+ "source": "https://github.com/symfony/http-kernel/tree/v7.4.8"
},
"funding": [
{
@@ -4909,39 +4936,43 @@
"type": "tidelift"
}
],
- "time": "2026-03-31T21:14:05+00:00"
+ "time": "2026-03-31T20:57:01+00:00"
},
{
"name": "symfony/mailer",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/mailer.git",
- "reference": "ca5f6edaf8780ece814404b58a4482b22b509c56"
+ "reference": "f6ea532250b476bfc1b56699b388a1bdbf168f62"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mailer/zipball/ca5f6edaf8780ece814404b58a4482b22b509c56",
- "reference": "ca5f6edaf8780ece814404b58a4482b22b509c56",
+ "url": "https://api.github.com/repos/symfony/mailer/zipball/f6ea532250b476bfc1b56699b388a1bdbf168f62",
+ "reference": "f6ea532250b476bfc1b56699b388a1bdbf168f62",
"shasum": ""
},
"require": {
"egulias/email-validator": "^2.1.10|^3|^4",
- "php": ">=8.4",
+ "php": ">=8.2",
"psr/event-dispatcher": "^1",
"psr/log": "^1|^2|^3",
- "symfony/event-dispatcher": "^7.4|^8.0",
- "symfony/mime": "^7.4|^8.0",
+ "symfony/event-dispatcher": "^6.4|^7.0|^8.0",
+ "symfony/mime": "^7.2|^8.0",
"symfony/service-contracts": "^2.5|^3"
},
"conflict": {
- "symfony/http-client-contracts": "<2.5"
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4",
+ "symfony/messenger": "<6.4",
+ "symfony/mime": "<6.4",
+ "symfony/twig-bridge": "<6.4"
},
"require-dev": {
- "symfony/console": "^7.4|^8.0",
- "symfony/http-client": "^7.4|^8.0",
- "symfony/messenger": "^7.4|^8.0",
- "symfony/twig-bridge": "^7.4|^8.0"
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/messenger": "^6.4|^7.0|^8.0",
+ "symfony/twig-bridge": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -4969,7 +5000,7 @@
"description": "Helps sending emails",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/mailer/tree/v8.0.8"
+ "source": "https://github.com/symfony/mailer/tree/v7.4.8"
},
"funding": [
{
@@ -4989,41 +5020,44 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
"name": "symfony/mime",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
- "reference": "ddff21f14c7ce04b98101b399a9463dce8b0ce66"
+ "reference": "6df02f99998081032da3407a8d6c4e1dcb5d4379"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mime/zipball/ddff21f14c7ce04b98101b399a9463dce8b0ce66",
- "reference": "ddff21f14c7ce04b98101b399a9463dce8b0ce66",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/6df02f99998081032da3407a8d6c4e1dcb5d4379",
+ "reference": "6df02f99998081032da3407a8d6c4e1dcb5d4379",
"shasum": ""
},
"require": {
- "php": ">=8.4",
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-intl-idn": "^1.10",
"symfony/polyfill-mbstring": "^1.0"
},
"conflict": {
"egulias/email-validator": "~3.0.0",
"phpdocumentor/reflection-docblock": "<5.2|>=7",
- "phpdocumentor/type-resolver": "<1.5.1"
+ "phpdocumentor/type-resolver": "<1.5.1",
+ "symfony/mailer": "<6.4",
+ "symfony/serializer": "<6.4.3|>7.0,<7.0.3"
},
"require-dev": {
"egulias/email-validator": "^2.1.10|^3.1|^4",
"league/html-to-markdown": "^5.0",
"phpdocumentor/reflection-docblock": "^5.2|^6.0",
- "symfony/dependency-injection": "^7.4|^8.0",
- "symfony/process": "^7.4|^8.0",
- "symfony/property-access": "^7.4|^8.0",
- "symfony/property-info": "^7.4|^8.0",
- "symfony/serializer": "^7.4|^8.0"
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/property-access": "^6.4|^7.0|^8.0",
+ "symfony/property-info": "^6.4|^7.0|^8.0",
+ "symfony/serializer": "^6.4.3|^7.0.3|^8.0"
},
"type": "library",
"autoload": {
@@ -5055,7 +5089,7 @@
"mime-type"
],
"support": {
- "source": "https://github.com/symfony/mime/tree/v8.0.8"
+ "source": "https://github.com/symfony/mime/tree/v7.4.8"
},
"funding": [
{
@@ -5075,7 +5109,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-30T14:11:46+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -5583,6 +5617,86 @@
],
"time": "2025-01-02T08:10:11+00:00"
},
+ {
+ "name": "symfony/polyfill-php83",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php83.git",
+ "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5",
+ "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php83\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-07-08T02:45:35+00:00"
+ },
{
"name": "symfony/polyfill-php84",
"version": "v1.33.0",
@@ -5828,20 +5942,20 @@
},
{
"name": "symfony/process",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc"
+ "reference": "60f19cd3badc8de688421e21e4305eba50f8089a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc",
- "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc",
+ "url": "https://api.github.com/repos/symfony/process/zipball/60f19cd3badc8de688421e21e4305eba50f8089a",
+ "reference": "60f19cd3badc8de688421e21e4305eba50f8089a",
"shasum": ""
},
"require": {
- "php": ">=8.4"
+ "php": ">=8.2"
},
"type": "library",
"autoload": {
@@ -5869,7 +5983,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/process/tree/v8.0.8"
+ "source": "https://github.com/symfony/process/tree/v7.4.8"
},
"funding": [
{
@@ -5889,33 +6003,38 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
"name": "symfony/routing",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
- "reference": "0de330ec2ea922a7b08ec45615bd51179de7fda4"
+ "reference": "9608de9873ec86e754fb6c0a0fa7e5f1a960eb6b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/routing/zipball/0de330ec2ea922a7b08ec45615bd51179de7fda4",
- "reference": "0de330ec2ea922a7b08ec45615bd51179de7fda4",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/9608de9873ec86e754fb6c0a0fa7e5f1a960eb6b",
+ "reference": "9608de9873ec86e754fb6c0a0fa7e5f1a960eb6b",
"shasum": ""
},
"require": {
- "php": ">=8.4",
+ "php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3"
},
+ "conflict": {
+ "symfony/config": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/yaml": "<6.4"
+ },
"require-dev": {
"psr/log": "^1|^2|^3",
- "symfony/config": "^7.4|^8.0",
- "symfony/dependency-injection": "^7.4|^8.0",
- "symfony/expression-language": "^7.4|^8.0",
- "symfony/http-foundation": "^7.4|^8.0",
- "symfony/yaml": "^7.4|^8.0"
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^6.4|^7.0|^8.0",
+ "symfony/yaml": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -5949,7 +6068,7 @@
"url"
],
"support": {
- "source": "https://github.com/symfony/routing/tree/v8.0.8"
+ "source": "https://github.com/symfony/routing/tree/v7.4.8"
},
"funding": [
{
@@ -5969,7 +6088,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
"name": "symfony/service-contracts",
@@ -6060,34 +6179,35 @@
},
{
"name": "symfony/string",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "ae9488f874d7603f9d2dfbf120203882b645d963"
+ "reference": "114ac57257d75df748eda23dd003878080b8e688"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963",
- "reference": "ae9488f874d7603f9d2dfbf120203882b645d963",
+ "url": "https://api.github.com/repos/symfony/string/zipball/114ac57257d75df748eda23dd003878080b8e688",
+ "reference": "114ac57257d75df748eda23dd003878080b8e688",
"shasum": ""
},
"require": {
- "php": ">=8.4",
- "symfony/polyfill-ctype": "^1.8",
- "symfony/polyfill-intl-grapheme": "^1.33",
- "symfony/polyfill-intl-normalizer": "^1.0",
- "symfony/polyfill-mbstring": "^1.0"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3.0",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.33",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
"symfony/translation-contracts": "<2.5"
},
"require-dev": {
- "symfony/emoji": "^7.4|^8.0",
- "symfony/http-client": "^7.4|^8.0",
- "symfony/intl": "^7.4|^8.0",
+ "symfony/emoji": "^7.1|^8.0",
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/intl": "^6.4|^7.0|^8.0",
"symfony/translation-contracts": "^2.5|^3.0",
- "symfony/var-exporter": "^7.4|^8.0"
+ "symfony/var-exporter": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -6126,7 +6246,7 @@
"utf8"
],
"support": {
- "source": "https://github.com/symfony/string/tree/v8.0.8"
+ "source": "https://github.com/symfony/string/tree/v7.4.8"
},
"funding": [
{
@@ -6146,31 +6266,38 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
"name": "symfony/translation",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
- "reference": "27c03ae3940de24ba2f71cfdbac824f2aa1fdf2f"
+ "reference": "33600f8489485425bfcddd0d983391038d3422e7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation/zipball/27c03ae3940de24ba2f71cfdbac824f2aa1fdf2f",
- "reference": "27c03ae3940de24ba2f71cfdbac824f2aa1fdf2f",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/33600f8489485425bfcddd0d983391038d3422e7",
+ "reference": "33600f8489485425bfcddd0d983391038d3422e7",
"shasum": ""
},
"require": {
- "php": ">=8.4",
- "symfony/polyfill-mbstring": "^1.0",
- "symfony/translation-contracts": "^3.6.1"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/translation-contracts": "^2.5.3|^3.3"
},
"conflict": {
"nikic/php-parser": "<5.0",
+ "symfony/config": "<6.4",
+ "symfony/console": "<6.4",
+ "symfony/dependency-injection": "<6.4",
"symfony/http-client-contracts": "<2.5",
- "symfony/service-contracts": "<2.5"
+ "symfony/http-kernel": "<6.4",
+ "symfony/service-contracts": "<2.5",
+ "symfony/twig-bundle": "<6.4",
+ "symfony/yaml": "<6.4"
},
"provide": {
"symfony/translation-implementation": "2.3|3.0"
@@ -6178,17 +6305,17 @@
"require-dev": {
"nikic/php-parser": "^5.0",
"psr/log": "^1|^2|^3",
- "symfony/config": "^7.4|^8.0",
- "symfony/console": "^7.4|^8.0",
- "symfony/dependency-injection": "^7.4|^8.0",
- "symfony/finder": "^7.4|^8.0",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/finder": "^6.4|^7.0|^8.0",
"symfony/http-client-contracts": "^2.5|^3.0",
- "symfony/http-kernel": "^7.4|^8.0",
- "symfony/intl": "^7.4|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/intl": "^6.4|^7.0|^8.0",
"symfony/polyfill-intl-icu": "^1.21",
- "symfony/routing": "^7.4|^8.0",
+ "symfony/routing": "^6.4|^7.0|^8.0",
"symfony/service-contracts": "^2.5|^3",
- "symfony/yaml": "^7.4|^8.0"
+ "symfony/yaml": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -6219,7 +6346,7 @@
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/translation/tree/v8.0.8"
+ "source": "https://github.com/symfony/translation/tree/v7.4.8"
},
"funding": [
{
@@ -6239,7 +6366,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
"name": "symfony/translation-contracts",
@@ -6325,24 +6452,24 @@
},
{
"name": "symfony/uid",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/uid.git",
- "reference": "f63fa6096a24147283bce4d29327d285326438e0"
+ "reference": "6883ebdf7bf6a12b37519dbc0df62b0222401b56"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/uid/zipball/f63fa6096a24147283bce4d29327d285326438e0",
- "reference": "f63fa6096a24147283bce4d29327d285326438e0",
+ "url": "https://api.github.com/repos/symfony/uid/zipball/6883ebdf7bf6a12b37519dbc0df62b0222401b56",
+ "reference": "6883ebdf7bf6a12b37519dbc0df62b0222401b56",
"shasum": ""
},
"require": {
- "php": ">=8.4",
+ "php": ">=8.2",
"symfony/polyfill-uuid": "^1.15"
},
"require-dev": {
- "symfony/console": "^7.4|^8.0"
+ "symfony/console": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -6379,7 +6506,7 @@
"uuid"
],
"support": {
- "source": "https://github.com/symfony/uid/tree/v8.0.8"
+ "source": "https://github.com/symfony/uid/tree/v7.4.8"
},
"funding": [
{
@@ -6399,35 +6526,35 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T15:14:47+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
"name": "symfony/var-dumper",
- "version": "v8.0.8",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
- "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1"
+ "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfb7badd53bf4177f6e9416cfbbccc13c0e773a1",
- "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9510c3966f749a1d1ff0059e1eabef6cc621e7fd",
+ "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd",
"shasum": ""
},
"require": {
- "php": ">=8.4",
- "symfony/polyfill-mbstring": "^1.0"
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
- "symfony/console": "<7.4",
- "symfony/error-handler": "<7.4"
+ "symfony/console": "<6.4"
},
"require-dev": {
- "symfony/console": "^7.4|^8.0",
- "symfony/http-kernel": "^7.4|^8.0",
- "symfony/process": "^7.4|^8.0",
- "symfony/uid": "^7.4|^8.0",
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/uid": "^6.4|^7.0|^8.0",
"twig/twig": "^3.12"
},
"bin": [
@@ -6466,7 +6593,7 @@
"dump"
],
"support": {
- "source": "https://github.com/symfony/var-dumper/tree/v8.0.8"
+ "source": "https://github.com/symfony/var-dumper/tree/v7.4.8"
},
"funding": [
{
@@ -6486,7 +6613,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-31T07:15:36+00:00"
+ "time": "2026-03-30T13:44:50+00:00"
},
{
"name": "thecodingmachine/safe",
@@ -7455,16 +7582,16 @@
},
{
"name": "nunomaduro/collision",
- "version": "v8.9.2",
+ "version": "v8.9.3",
"source": {
"type": "git",
"url": "https://github.com/nunomaduro/collision.git",
- "reference": "6eb16883e74fd725ac64dbe81544c961ab448ba5"
+ "reference": "b0d8ab95b29c3189aeeb902d81215231df4c1b64"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nunomaduro/collision/zipball/6eb16883e74fd725ac64dbe81544c961ab448ba5",
- "reference": "6eb16883e74fd725ac64dbe81544c961ab448ba5",
+ "url": "https://api.github.com/repos/nunomaduro/collision/zipball/b0d8ab95b29c3189aeeb902d81215231df4c1b64",
+ "reference": "b0d8ab95b29c3189aeeb902d81215231df4c1b64",
"shasum": ""
},
"require": {
@@ -7547,7 +7674,7 @@
"type": "patreon"
}
],
- "time": "2026-03-31T21:51:27+00:00"
+ "time": "2026-04-06T19:25:53+00:00"
},
{
"name": "phar-io/manifest",
@@ -8015,16 +8142,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "12.5.15",
+ "version": "12.5.17",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "aeb6899ffdbbf4b4ff5e6b6ebb77b35c51bb6d9a"
+ "reference": "85b62adab1a340982df64e66daa4a4435eb5723b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/aeb6899ffdbbf4b4ff5e6b6ebb77b35c51bb6d9a",
- "reference": "aeb6899ffdbbf4b4ff5e6b6ebb77b35c51bb6d9a",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/85b62adab1a340982df64e66daa4a4435eb5723b",
+ "reference": "85b62adab1a340982df64e66daa4a4435eb5723b",
"shasum": ""
},
"require": {
@@ -8093,7 +8220,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.15"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.17"
},
"funding": [
{
@@ -8101,7 +8228,7 @@
"type": "other"
}
],
- "time": "2026-03-31T06:41:33+00:00"
+ "time": "2026-04-08T03:04:19+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -8174,16 +8301,16 @@
},
{
"name": "sebastian/comparator",
- "version": "7.1.4",
+ "version": "7.1.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6"
+ "reference": "c284f55811f43d555e51e8e5c166ac40d3e33c63"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a7de5df2e094f9a80b40a522391a7e6022df5f6",
- "reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c284f55811f43d555e51e8e5c166ac40d3e33c63",
+ "reference": "c284f55811f43d555e51e8e5c166ac40d3e33c63",
"shasum": ""
},
"require": {
@@ -8242,7 +8369,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues",
"security": "https://github.com/sebastianbergmann/comparator/security/policy",
- "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.4"
+ "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.5"
},
"funding": [
{
@@ -8262,7 +8389,7 @@
"type": "tidelift"
}
],
- "time": "2026-01-24T09:28:48+00:00"
+ "time": "2026-04-08T04:43:00+00:00"
},
{
"name": "sebastian/complexity",
diff --git a/database/migrations/2026_04_09_065808_update_type_enum_in_pieces_jointes_table.php b/database/migrations/2026_04_09_065808_update_type_enum_in_pieces_jointes_table.php
index b11d99d..6bcb7e4 100644
--- a/database/migrations/2026_04_09_065808_update_type_enum_in_pieces_jointes_table.php
+++ b/database/migrations/2026_04_09_065808_update_type_enum_in_pieces_jointes_table.php
@@ -11,7 +11,13 @@ return new class extends Migration
*/
public function up(): void
{
- \Illuminate\Support\Facades\DB::statement("ALTER TABLE pieces_jointes MODIFY type ENUM('devis', 'bon_commande', 'bon_livraison', 'facture', 'autre', 'contrat', 'avenant') NOT NULL;");
+ if (Schema::getConnection()->getDriverName() === 'mysql') {
+ \Illuminate\Support\Facades\DB::statement("ALTER TABLE pieces_jointes MODIFY type ENUM('devis', 'bon_commande', 'bon_livraison', 'facture', 'autre', 'contrat', 'avenant') NOT NULL;");
+ } else {
+ Schema::table('pieces_jointes', function (Blueprint $table) {
+ $table->string('type')->change();
+ });
+ }
}
/**
@@ -19,6 +25,12 @@ return new class extends Migration
*/
public function down(): void
{
- \Illuminate\Support\Facades\DB::statement("ALTER TABLE pieces_jointes MODIFY type ENUM('devis', 'bon_commande', 'bon_livraison', 'facture', 'autre') NOT NULL;");
+ if (Schema::getConnection()->getDriverName() === 'mysql') {
+ \Illuminate\Support\Facades\DB::statement("ALTER TABLE pieces_jointes MODIFY type ENUM('devis', 'bon_commande', 'bon_livraison', 'facture', 'autre') NOT NULL;");
+ } else {
+ Schema::table('pieces_jointes', function (Blueprint $table) {
+ $table->string('type')->change();
+ });
+ }
}
};
diff --git a/database/migrations/2026_04_09_174018_create_communes_table.php b/database/migrations/2026_04_09_174018_create_communes_table.php
new file mode 100644
index 0000000..923d8ad
--- /dev/null
+++ b/database/migrations/2026_04_09_174018_create_communes_table.php
@@ -0,0 +1,29 @@
+id();
+ $table->string('nom')->unique();
+ $table->string('code_postal')->nullable(); // Optionnel mais utile
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('communes');
+ }
+};
diff --git a/database/migrations/2026_04_09_174031_add_commune_id_to_commandes_and_contrats_tables.php b/database/migrations/2026_04_09_174031_add_commune_id_to_commandes_and_contrats_tables.php
new file mode 100644
index 0000000..f514c41
--- /dev/null
+++ b/database/migrations/2026_04_09_174031_add_commune_id_to_commandes_and_contrats_tables.php
@@ -0,0 +1,28 @@
+foreignId('commune_id')->nullable()->after('id')->constrained('communes')->nullOnDelete();
+ });
+
+ Schema::table('contrats', function (Blueprint $table) {
+ $table->foreignId('commune_id')->nullable()->after('id')->constrained('communes')->nullOnDelete();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('commandes', function (Blueprint $table) {
+ $table->dropConstrainedForeignId('commune_id');
+ });
+
+ Schema::table('contrats', function (Blueprint $table) {
+ $table->dropConstrainedForeignId('commune_id');
+ });
+ }
+};
diff --git a/database/migrations/2026_04_09_185857_create_licences_table.php b/database/migrations/2026_04_09_185857_create_licences_table.php
new file mode 100644
index 0000000..d2b5287
--- /dev/null
+++ b/database/migrations/2026_04_09_185857_create_licences_table.php
@@ -0,0 +1,39 @@
+id();
+ $table->foreignId('contrat_id')->nullable()->constrained()->nullOnDelete();
+ $table->foreignId('fournisseur_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('commune_id')->nullable()->constrained()->nullOnDelete();
+ $table->string('nom');
+ $table->text('cle_licence')->nullable();
+ $table->integer('nombre_sieges_total')->default(1);
+ $table->integer('nombre_sieges_utilises')->default(0);
+ $table->date('date_acquisition')->nullable();
+ $table->date('date_expiration')->nullable();
+ $table->enum('type_licence', ['perpétuelle', 'abonnement'])->default('abonnement');
+ $table->enum('statut', ['active', 'expirée', 'résiliée'])->default('active');
+ $table->text('notes')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('licences');
+ }
+};
diff --git a/database/migrations/2026_04_09_192108_create_assets_table.php b/database/migrations/2026_04_09_192108_create_assets_table.php
new file mode 100644
index 0000000..c69f7cd
--- /dev/null
+++ b/database/migrations/2026_04_09_192108_create_assets_table.php
@@ -0,0 +1,38 @@
+id();
+ $table->string('nom');
+ $table->string('type'); // Serveur, Switch, NAS, Baie, etc.
+ $table->string('marque')->nullable();
+ $table->string('modele')->nullable();
+ $table->string('numero_serie')->nullable();
+ $table->string('emplacement')->nullable();
+ $table->foreignId('commune_id')->nullable()->constrained()->nullOnDelete();
+ $table->date('date_achat')->nullable();
+ $table->date('date_fin_garantie')->nullable();
+ $table->enum('statut', ['en_service', 'hors_service', 'en_reparation', 'stock'])->default('en_service');
+ $table->text('notes')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('assets');
+ }
+};
diff --git a/database/migrations/2026_04_09_192904_add_commande_id_to_assets_table.php b/database/migrations/2026_04_09_192904_add_commande_id_to_assets_table.php
new file mode 100644
index 0000000..47cd9cf
--- /dev/null
+++ b/database/migrations/2026_04_09_192904_add_commande_id_to_assets_table.php
@@ -0,0 +1,28 @@
+foreignId('commande_id')->nullable()->after('commune_id')->constrained()->nullOnDelete();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('assets', function (Blueprint $table) {
+ $table->dropConstrainedForeignId('commande_id');
+ });
+ }
+};
diff --git a/database/migrations/2026_04_09_194501_add_code_ean_to_assets_table.php b/database/migrations/2026_04_09_194501_add_code_ean_to_assets_table.php
new file mode 100644
index 0000000..089498e
--- /dev/null
+++ b/database/migrations/2026_04_09_194501_add_code_ean_to_assets_table.php
@@ -0,0 +1,28 @@
+string('code_ean')->nullable()->after('type');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('assets', function (Blueprint $table) {
+ $table->dropColumn('code_ean');
+ });
+ }
+};
diff --git a/database/seeders/CommuneSeeder.php b/database/seeders/CommuneSeeder.php
new file mode 100644
index 0000000..303de74
--- /dev/null
+++ b/database/seeders/CommuneSeeder.php
@@ -0,0 +1,43 @@
+updateOrInsert(
+ ['nom' => $commune],
+ ['created_at' => now(), 'updated_at' => now()]
+ );
+ }
+ }
+}
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
index 0726fa4..76583d9 100644
--- a/database/seeders/DatabaseSeeder.php
+++ b/database/seeders/DatabaseSeeder.php
@@ -13,6 +13,7 @@ class DatabaseSeeder extends Seeder
CategorieSeeder::class,
RolesPermissionsSeeder::class,
AdminUserSeeder::class,
+ CommuneSeeder::class,
]);
}
}
diff --git a/package-lock.json b/package-lock.json
index ca3c55d..0e04495 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,9 +1,15 @@
{
- "name": "Commandes",
+ "name": "DSICommander",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
+ "dependencies": {
+ "@fullcalendar/core": "^6.1.20",
+ "@fullcalendar/daygrid": "^6.1.20",
+ "@fullcalendar/interaction": "^6.1.20",
+ "@fullcalendar/vue3": "^6.1.20"
+ },
"devDependencies": {
"@inertiajs/vue3": "^2.0.0",
"@tailwindcss/forms": "^0.5.3",
@@ -36,7 +42,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -46,7 +51,6 @@
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -56,7 +60,6 @@
"version": "7.29.2",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
"integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.29.0"
@@ -72,7 +75,6 @@
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
@@ -116,6 +118,43 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@fullcalendar/core": {
+ "version": "6.1.20",
+ "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.20.tgz",
+ "integrity": "sha512-1cukXLlePFiJ8YKXn/4tMKsy0etxYLCkXk8nUCFi11nRONF2Ba2CD5b21/ovtOO2tL6afTJfwmc1ed3HG7eB1g==",
+ "license": "MIT",
+ "dependencies": {
+ "preact": "~10.12.1"
+ }
+ },
+ "node_modules/@fullcalendar/daygrid": {
+ "version": "6.1.20",
+ "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.20.tgz",
+ "integrity": "sha512-AO9vqhkLP77EesmJzuU+IGXgxNulsA8mgQHynclJ8U70vSwAVnbcLG9qftiTAFSlZjiY/NvhE7sflve6cJelyQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@fullcalendar/core": "~6.1.20"
+ }
+ },
+ "node_modules/@fullcalendar/interaction": {
+ "version": "6.1.20",
+ "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.20.tgz",
+ "integrity": "sha512-p6txmc5txL0bMiPaJxe2ip6o0T384TyoD2KGdsU6UjZ5yoBlaY+dg7kxfnYKpYMzEJLG58n+URrHr2PgNL2fyA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@fullcalendar/core": "~6.1.20"
+ }
+ },
+ "node_modules/@fullcalendar/vue3": {
+ "version": "6.1.20",
+ "resolved": "https://registry.npmjs.org/@fullcalendar/vue3/-/vue3-6.1.20.tgz",
+ "integrity": "sha512-8qg6pS27II9QBwFkkJC+7SfflMpWqOe7i3ii5ODq9KpLAjwQAd/zjfq8RvKR1Yryoh5UmMCmvRbMB7i4RGtqog==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@fullcalendar/core": "~6.1.20",
+ "vue": "^3.0.11"
+ }
+ },
"node_modules/@inertiajs/core": {
"version": "2.3.18",
"resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-2.3.18.tgz",
@@ -182,7 +221,6 @@
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
@@ -873,7 +911,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.31.tgz",
"integrity": "sha512-k/ueL14aNIEy5Onf0OVzR8kiqF/WThgLdFhxwa4e/KF/0qe38IwIdofoSWBTvvxQOesaz6riAFAUaYjoF9fLLQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.29.2",
@@ -887,7 +924,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.31.tgz",
"integrity": "sha512-BMY/ozS/xxjYqRFL+tKdRpATJYDTTgWSo0+AJvJNg4ig+Hgb0dOsHPXvloHQ5hmlivUqw1Yt2pPIqp4e0v1GUw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.5.31",
@@ -898,7 +934,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.31.tgz",
"integrity": "sha512-M8wpPgR9UJ8MiRGjppvx9uWJfLV7A/T+/rL8s/y3QG3u0c2/YZgff3d6SuimKRIhcYnWg5fTfDMlz2E6seUW8Q==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.29.2",
@@ -916,7 +951,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.31.tgz",
"integrity": "sha512-h0xIMxrt/LHOvJKMri+vdYT92BrK3HFLtDqq9Pr/lVVfE4IyKZKvWf0vJFW10Yr6nX02OR4MkJwI0c1HDa1hog==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.31",
@@ -927,7 +961,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.31.tgz",
"integrity": "sha512-DtKXxk9E/KuVvt8VxWu+6Luc9I9ETNcqR1T1oW1gf02nXaZ1kuAx58oVu7uX9XxJR0iJCro6fqBLw9oSBELo5g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/shared": "3.5.31"
@@ -937,7 +970,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.31.tgz",
"integrity": "sha512-AZPmIHXEAyhpkmN7aWlqjSfYynmkWlluDNPHMCZKFHH+lLtxP/30UJmoVhXmbDoP1Ng0jG0fyY2zCj1PnSSA6Q==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.31",
@@ -948,7 +980,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.31.tgz",
"integrity": "sha512-xQJsNRmGPeDCJq/u813tyonNgWBFjzfVkBwDREdEWndBnGdHLHgkwNBQxLtg4zDrzKTEcnikUy1UUNecb3lJ6g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.31",
@@ -961,7 +992,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.31.tgz",
"integrity": "sha512-GJuwRvMcdZX/CriUnyIIOGkx3rMV3H6sOu0JhdKbduaeCji6zb60iOGMY7tFoN24NfsUYoFBhshZtGxGpxO4iA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-ssr": "3.5.31",
@@ -975,7 +1005,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.31.tgz",
"integrity": "sha512-nBxuiuS9Lj5bPkPbWogPUnjxxWpkRniX7e5UBQDWl6Fsf4roq9wwV+cR7ezQ4zXswNvPIlsdj1slcLB7XCsRAw==",
- "dev": true,
"license": "MIT"
},
"node_modules/ansi-regex": {
@@ -1391,7 +1420,6 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/delayed-stream": {
@@ -1475,7 +1503,6 @@
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
@@ -1547,7 +1574,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
- "dev": true,
"license": "MIT"
},
"node_modules/fast-glob": {
@@ -2220,7 +2246,6 @@
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
@@ -2309,7 +2334,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -2385,7 +2409,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
@@ -2425,7 +2448,6 @@
"version": "8.5.8",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -2584,6 +2606,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/preact": {
+ "version": "10.12.1",
+ "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz",
+ "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/preact"
+ }
+ },
"node_modules/proxy-from-env": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
@@ -2864,7 +2896,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -3264,7 +3295,6 @@
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz",
"integrity": "sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.31",
diff --git a/package.json b/package.json
index 976a8d0..f523395 100644
--- a/package.json
+++ b/package.json
@@ -19,5 +19,11 @@
"tailwindcss": "^3.2.1",
"vite": "^8.0.0",
"vue": "^3.4.0"
+ },
+ "dependencies": {
+ "@fullcalendar/core": "^6.1.20",
+ "@fullcalendar/daygrid": "^6.1.20",
+ "@fullcalendar/interaction": "^6.1.20",
+ "@fullcalendar/vue3": "^6.1.20"
}
}
diff --git a/public/images/logo_agglo.png b/public/images/logo_agglo.png
new file mode 100644
index 0000000..80c0c25
Binary files /dev/null and b/public/images/logo_agglo.png differ
diff --git a/public/images/logo_agglo_20-bleu.png b/public/images/logo_agglo_20-bleu.png
new file mode 100644
index 0000000..2d2db40
Binary files /dev/null and b/public/images/logo_agglo_20-bleu.png differ
diff --git a/resources/js/Layouts/AuthenticatedLayout.vue b/resources/js/Layouts/AuthenticatedLayout.vue
index 8ce225d..63f2838 100644
--- a/resources/js/Layouts/AuthenticatedLayout.vue
+++ b/resources/js/Layouts/AuthenticatedLayout.vue
@@ -105,6 +105,14 @@ function isActive(...names) {
Noms de domaine
+
+
+ Assets (Matériels)
+
@@ -164,6 +172,33 @@ function isActive(...names) {
Services
+
+
+ Licences
+
+
+
+ Calendrier
+
+
+
+ Communes
+
diff --git a/resources/js/Pages/Assets/Form.vue b/resources/js/Pages/Assets/Form.vue
new file mode 100644
index 0000000..bebf93f
--- /dev/null
+++ b/resources/js/Pages/Assets/Form.vue
@@ -0,0 +1,267 @@
+
+
+
+
+ {{ asset.type }} - {{ asset.marque }} {{ asset.modele }} Nom / Hostname {{ asset.nom }} Type de matériel {{ asset.type }} Marque & Modèle {{ asset.marque || '—' }} {{ asset.modele || '' }} Code EAN/UPC {{ asset.code_ean || '—' }} Numéro de série / SN {{ asset.numero_serie || '—' }} Statut actuel Ville / Commune {{ asset.commune?.nom || 'Infrastructure Globale (Agglo)' }} Emplacement précis {{ asset.emplacement || '—' }} Date d'achat {{ formatDate(asset.date_achat) }} Fin de garantie (constructeur)
+ {{ formatDate(asset.date_fin_garantie) }}
+ ⚠️ Expirée
+ ⏳ À renouveler
+ {{ isEdit ? 'Modifier l\'équipement' : 'Ajouter un équipement' }}
+ Assets (Hardware & Infra)
+
+ + Nouvel Asset
+
+
+
+
+
+
+
+
+ Matériel
+ Type
+ S/N
+ Ville / Site
+ Fin Garantie
+ Statut
+ Actions
+
+
+
+
+
+
+
+ {{ asset.type }}
+
+
+ {{ asset.numero_serie ?? '—' }}
+
+
+
+ {{ formatDate(asset.date_fin_garantie) }}
+ ⚠️
+ ⏳
+
+
+
+ {{ statutLabels[asset.statut] }}
+
+
+
+
+ {{ asset.nom }}
+ Détails du matériel
+ Notes & Configuration
+ Localisation
+ Cycle de vie
+ Commande associée
+
+ Calendrier des échéances
+
+
+
{{ cmd.objet }}
Service demandeur
{{ commande.service?.nom ?? '—' }}
Ville / Commune
+{{ commande.commune?.nom ?? '—' }}
+Fournisseur
{{ commande.fournisseur?.nom ?? '—' }}
@@ -160,6 +164,35 @@ const transitionColors = { :show-received="['commandee','partiellement_recue','recue_complete','cloturee'].includes(commande.statut)" />{{ c.code_postal }}
+Service concerné
{{ contrat.service?.nom ?? '—' }}
+Ville / Commune
+{{ contrat.commune?.nom ?? '—' }}
+Fournisseur
{{ contrat.fournisseur?.nom ?? '—' }}
@@ -101,6 +105,20 @@ const statutColors = {| Produit / Logiciel | +Commune | +Fournisseur / Contrat | +Utilisation Sièges | +Expiration | +Actions | +
|---|---|---|---|---|---|
|
+ {{ lic.nom }}
+ {{ lic.type_licence }}
+ |
+ {{ lic.commune?.nom ?? '—' }} | +
+ {{ lic.fournisseur?.nom }}
+
+ {{ lic.contrat.titre }}
+
+ |
+
+
+
+
+
+ {{ lic.nombre_sieges_utilises }} / {{ lic.nombre_sieges_total }}
+
+
+ |
+
+
+ {{ lic.statut }}
+
+
+ Éxpire le {{ formatDate(lic.date_expiration) }}
+
+ |
+
+
+
+
+
+
+ |
+
Objet : {{ $commande->objet }}
-|
-
- Service Demandeur
- {{ $commande->service->nom ?? 'N/A' }} - Demandé par : {{ $commande->demandeur->name ?? 'N/A' }} - Le : {{ $commande->date_demande ? \Carbon\Carbon::parse($commande->date_demande)->format('d/m/Y') : 'N/A' }} + + +
+
+
+
+
+ |
-
-
- Informations Fournisseur
- {{ $commande->fournisseur->nom ?? 'N/A' }}
- - Réf : {{ $commande->reference_fournisseur ?? 'N/A' }} - - |
-
|
-
- Imputation budgétaire :
- {{ $commande->imputation_budgetaire }}
-
- |
- @endif
- @if($commande->date_souhaitee)
-
-
- Date de livraison souhaitée :
- {{ \Carbon\Carbon::parse($commande->date_souhaitee)->format('d/m/Y') }}
-
- |
- @endif
-
| Désignation | -Réf. Article | -Qté | -PU HT | -Total HT | -
|---|---|---|---|---|
| {{ $ligne->designation }} | -{{ $ligne->reference ?? '-' }} | -{{ number_format($ligne->quantite, 2, ',', ' ') }} {{ $ligne->unite }} | -{{ number_format($ligne->prix_unitaire_ht, 2, ',', ' ') }} € | -{{ number_format($ligne->quantite * $ligne->prix_unitaire_ht, 2, ',', ' ') }} € | -
|
- @if($commande->justification || $commande->description)
-
- Notes / Justification :
-
- @endif
- {{ $commande->justification ?: $commande->description }} - |
-
-
|
-