feat: module budgets complet avec sécurité, performance et métier
## Fonctionnalités - Module Budgets : enveloppes, lignes budgétaires, arbitrage DSI/Direction - Suivi de l'exécution budgétaire avec alertes visuelles (dépassement, seuil 80%) - Blocage des commandes si budget insuffisant (store + update) - Audit trail complet des arbitrages via HistoriqueBudget - Page d'index budgets refaite en tableau avec filtres et tri côté client - Page Services avec sélecteur d'icônes FontAwesome (solid + regular + brands) ## Sécurité - BudgetPolicy centralisée (viewAny, view, create, update, addLigne, updateLigne, deleteLigne, arbitrerLigne) - Autorisation sur tous les endpoints LigneBudget et Budget - Protection XSS : remplacement v-html par classes dynamiques - Validation des paramètres d'export (type, envelope) - Validation montant_arbitre ≤ montant_propose côté serveur ## Performance - Eager loading lignes.commandes.commune dans execution() et exportPdf() - Calculs montant_consomme/engage en mémoire sur collections déjà chargées - Null-safety sur montant_arbitre dans getMontantDisponibleAttribute ## Technique - Migration historique_budgets, budgets, ligne_budgets, rôle raf - SearchableSelect avec affichage du disponible budgétaire - FontAwesome enregistré globalement (fas, far, fab) - 33 tests Feature (sécurité, performance, métier) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
98
app/Http/Controllers/LigneBudgetController.php
Normal file
98
app/Http/Controllers/LigneBudgetController.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Budget;
|
||||
use App\Models\HistoriqueBudget;
|
||||
use App\Models\LigneBudget;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LigneBudgetController extends Controller
|
||||
{
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'budget_id' => 'required|exists:budgets,id',
|
||||
'nom' => 'required|string|max:255',
|
||||
'description' => 'nullable|string',
|
||||
'type_depense' => 'required|in:investissement,fonctionnement',
|
||||
'montant_propose' => 'required|numeric|min:0',
|
||||
'commune_id' => 'nullable|exists:communes,id',
|
||||
]);
|
||||
|
||||
$budget = Budget::findOrFail($request->budget_id);
|
||||
|
||||
$this->authorize('addLigne', $budget);
|
||||
|
||||
LigneBudget::create($request->only([
|
||||
'budget_id', 'nom', 'description', 'type_depense', 'montant_propose', 'commune_id',
|
||||
]));
|
||||
|
||||
return redirect()->back()->with('success', 'Ligne budgétaire ajoutée.');
|
||||
}
|
||||
|
||||
public function update(Request $request, LigneBudget $lignes_budget)
|
||||
{
|
||||
$this->authorize('updateLigne', $lignes_budget);
|
||||
|
||||
$request->validate([
|
||||
'nom' => 'required|string|max:255',
|
||||
'description' => 'nullable|string',
|
||||
'type_depense' => 'required|in:investissement,fonctionnement',
|
||||
'montant_propose' => 'required|numeric|min:0',
|
||||
'commune_id' => 'nullable|exists:communes,id',
|
||||
]);
|
||||
|
||||
$lignes_budget->update($request->only([
|
||||
'nom', 'description', 'type_depense', 'montant_propose', 'commune_id',
|
||||
]));
|
||||
|
||||
return redirect()->back()->with('success', 'Ligne budgétaire mise à jour.');
|
||||
}
|
||||
|
||||
public function arbitrer(Request $request, LigneBudget $ligneBudget)
|
||||
{
|
||||
$this->authorize('arbitrerLigne', $ligneBudget);
|
||||
|
||||
$request->validate([
|
||||
'statut_arbitrage' => 'required|in:propose,accepte_dsi,accepte_direction,valide_definitif,refuse,reporte',
|
||||
'commentaire' => 'nullable|string|max:1000',
|
||||
'montant_arbitre' => [
|
||||
'nullable',
|
||||
'numeric',
|
||||
'min:0',
|
||||
function ($attribute, $value, $fail) use ($ligneBudget) {
|
||||
if ($value !== null && $value > $ligneBudget->montant_propose) {
|
||||
$fail("Le montant arbitré ({$value} €) ne peut pas dépasser le montant proposé ({$ligneBudget->montant_propose} €).");
|
||||
}
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
HistoriqueBudget::create([
|
||||
'ligne_budget_id' => $ligneBudget->id,
|
||||
'user_id' => $request->user()->id,
|
||||
'ancien_statut' => $ligneBudget->statut_arbitrage,
|
||||
'nouveau_statut' => $request->statut_arbitrage,
|
||||
'ancien_montant_arbitre' => $ligneBudget->montant_arbitre,
|
||||
'nouveau_montant_arbitre'=> $request->montant_arbitre,
|
||||
'commentaire' => $request->commentaire,
|
||||
]);
|
||||
|
||||
$ligneBudget->update([
|
||||
'statut_arbitrage' => $request->statut_arbitrage,
|
||||
'montant_arbitre' => $request->montant_arbitre,
|
||||
]);
|
||||
|
||||
return redirect()->back()->with('success', 'Arbitrage enregistré.');
|
||||
}
|
||||
|
||||
public function destroy(LigneBudget $lignes_budget)
|
||||
{
|
||||
$this->authorize('deleteLigne', $lignes_budget);
|
||||
|
||||
$lignes_budget->delete();
|
||||
|
||||
return redirect()->back()->with('success', 'Ligne budgétaire supprimée.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user