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 @@ + + + + + diff --git a/resources/js/Pages/Assets/Index.vue b/resources/js/Pages/Assets/Index.vue new file mode 100644 index 0000000..414060a --- /dev/null +++ b/resources/js/Pages/Assets/Index.vue @@ -0,0 +1,158 @@ + + + diff --git a/resources/js/Pages/Assets/Show.vue b/resources/js/Pages/Assets/Show.vue new file mode 100644 index 0000000..a371c45 --- /dev/null +++ b/resources/js/Pages/Assets/Show.vue @@ -0,0 +1,151 @@ + + + diff --git a/resources/js/Pages/Calendar/Index.vue b/resources/js/Pages/Calendar/Index.vue new file mode 100644 index 0000000..82f45b8 --- /dev/null +++ b/resources/js/Pages/Calendar/Index.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/resources/js/Pages/Commandes/Create.vue b/resources/js/Pages/Commandes/Create.vue index 86902ed..396710e 100644 --- a/resources/js/Pages/Commandes/Create.vue +++ b/resources/js/Pages/Commandes/Create.vue @@ -6,6 +6,7 @@ import { Head, Link, useForm } from '@inertiajs/vue3' const props = defineProps({ services: Array, fournisseurs: Array, + communes: Array, categories: Array, articles: Array, }) @@ -13,6 +14,7 @@ const props = defineProps({ const form = useForm({ service_id: '', fournisseur_id: '', + commune_id: '', objet: '', description: '', justification: '', @@ -77,6 +79,15 @@ function submit() { +
+ + +
+
+
+ + +
+ +
@@ -120,6 +137,7 @@ function formatCurrency(v) { N° Objet Service + Ville Fournisseur Statut Priorité @@ -141,6 +159,7 @@ function formatCurrency(v) {

{{ cmd.objet }}

{{ cmd.service?.nom }} + {{ cmd.commune?.nom ?? '—' }} {{ cmd.fournisseur?.nom ?? '—' }} diff --git a/resources/js/Pages/Commandes/Show.vue b/resources/js/Pages/Commandes/Show.vue index d9bbfec..1780244 100644 --- a/resources/js/Pages/Commandes/Show.vue +++ b/resources/js/Pages/Commandes/Show.vue @@ -96,6 +96,10 @@ const transitionColors = {

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)" />
+ +
+
+

Matériels livrés (Assets)

+ {{ commande.assets.length }} équipements +
+
+
+
+
+ + + +
+
+ + {{ asset.nom }} + +
{{ asset.numero_serie ?? 'Sans S/N' }} • {{ asset.marque }} {{ asset.modele }}
+
+
+
+ {{ asset.type }} + Garantie jusqu'au {{ formatDate(asset.date_fin_garantie) }} +
+
+
+
+
diff --git a/resources/js/Pages/Communes/Index.vue b/resources/js/Pages/Communes/Index.vue new file mode 100644 index 0000000..9fa7e5a --- /dev/null +++ b/resources/js/Pages/Communes/Index.vue @@ -0,0 +1,82 @@ + + + diff --git a/resources/js/Pages/Contrats/Create.vue b/resources/js/Pages/Contrats/Create.vue index d895612..9438579 100644 --- a/resources/js/Pages/Contrats/Create.vue +++ b/resources/js/Pages/Contrats/Create.vue @@ -5,6 +5,7 @@ import { Head, Link, useForm } from '@inertiajs/vue3' const props = defineProps({ services: Array, fournisseurs: Array, + communes: Array, statuts: Object, }) @@ -12,6 +13,7 @@ const form = useForm({ titre: '', description: '', fournisseur_id: '', + commune_id: '', service_id: props.services.length === 1 ? props.services[0].id : '', date_debut: '', date_echeance: '', @@ -79,6 +81,15 @@ function submit() { class="w-full rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 text-sm text-gray-500" />
+
+ + +
+
+
+ +
+ + +
+ +
+ + +
+ + + + +
+

Établissement & Dates

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ +
+ Annuler + +
+ + + + diff --git a/resources/js/Pages/Licences/Index.vue b/resources/js/Pages/Licences/Index.vue new file mode 100644 index 0000000..abeefad --- /dev/null +++ b/resources/js/Pages/Licences/Index.vue @@ -0,0 +1,141 @@ + + + diff --git a/resources/views/pdf/commande.blade.php b/resources/views/pdf/commande.blade.php index da49d1e..81ab203 100644 --- a/resources/views/pdf/commande.blade.php +++ b/resources/views/pdf/commande.blade.php @@ -2,142 +2,287 @@ - Commande {{ $commande->numero_commande }} + Proposition de commande {{ $commande->numero_commande }} -
-

BON DE COMMANDE : {{ $commande->numero_commande }}

-

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' }} +
+ +
+
+
+ +
+
+

Proposition de commande

+

N° {{ $commande->numero_commande }}

+
+ Date: {{ \Carbon\Carbon::parse($commande->date_demande)->format('d/m/Y') }}
+ Objet: {{ $commande->objet }}
-
-
- Informations Fournisseur - {{ $commande->fournisseur->nom ?? 'N/A' }}
- Réf : {{ $commande->reference_fournisseur ?? 'N/A' }}
-
-
-
+ + - - - @if($commande->imputation_budgetaire) - - @endif - @if($commande->date_souhaitee) - - @endif - -
-
- Imputation budgétaire : - {{ $commande->imputation_budgetaire }} -
-
-
- Date de livraison souhaitée : - {{ \Carbon\Carbon::parse($commande->date_souhaitee)->format('d/m/Y') }} -
-
- - - - - - - - - - - - - @foreach($commande->lignes as $ligne) - - - - - - - - @endforeach - -
DésignationRéf. ArticleQtéPU HTTotal 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 : -

{{ $commande->justification ?: $commande->description }}

-
- @endif -
- - - - - - - - - -
Total HT :{{ number_format($commande->montant_ht, 2, ',', ' ') }} €
Total TTC :{{ number_format($commande->montant_ttc, 2, ',', ' ') }} €
-
- - + +
+ DSICommander - Généré le {{ now()->format('d/m/Y H:i') }}
diff --git a/routes/web.php b/routes/web.php index edfc4f9..af86fce 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,6 +3,7 @@ use App\Http\Controllers\ArticleController; use App\Http\Controllers\CategorieController; use App\Http\Controllers\CommandeController; +use App\Http\Controllers\CommuneController; use App\Http\Controllers\ContratController; use App\Http\Controllers\DomaineController; use App\Http\Controllers\DashboardController; @@ -11,6 +12,9 @@ use App\Http\Controllers\PieceJointeController; use App\Http\Controllers\ProfileController; use App\Http\Controllers\ServiceController; use App\Http\Controllers\UserController; +use App\Http\Controllers\LicenceController; +use App\Http\Controllers\CalendarController; +use App\Http\Controllers\AssetController; use Illuminate\Support\Facades\Route; Route::get('/', fn () => redirect()->route('dashboard')); @@ -75,7 +79,20 @@ Route::middleware(['auth', 'verified'])->group(function () { Route::put('/users/{user}', [UserController::class, 'update'])->name('users.update'); Route::patch('/users/{user}/toggle-active', [UserController::class, 'toggleActive']) ->name('users.toggle-active'); + + Route::resource('communes', CommuneController::class)->except(['create', 'show', 'edit']); }); + + // Licences + Route::resource('licences', LicenceController::class); + + // Calendrier + Route::get('/calendar', [CalendarController::class, 'index'])->name('calendar.index'); + Route::get('/calendar/events', [CalendarController::class, 'events'])->name('calendar.events'); + + // Assets + Route::resource('assets', AssetController::class); + Route::get('/api/ean-lookup/{ean}', [AssetController::class, 'lookupEan'])->name('assets.ean-lookup'); }); require __DIR__ . '/auth.php';