Files
dsi-commander/app/Exports/BudgetExecutionExport.php
jeremy bayse 04fc56cd70 feat: dashboard amélioré, exports budgets, alertes expiration et correctifs
## Dashboard
- Refonte complète du tableau de bord avec widgets budgets, commandes, contrats
- Intégration des données d'exécution budgétaire en temps réel

## Exports & Rapports
- BudgetExecutionExport : export Excel de l'exécution budgétaire
- Template PDF budgets (budgets_pdf.blade.php)
- Routes d'export PDF et Excel

## Alertes & Notifications
- Commande CheckExpirations : détection des contrats/assets arrivant à échéance
- Mail ExpiringElementsMail avec template Blade
- Planification via routes/console.php

## Correctifs
- CommandePolicy et ContratPolicy : ajustements des règles d'autorisation
- ContratController : corrections mineures
- Commande model : ajustements relations/casts
- AuthenticatedLayout : refonte navigation avec icônes budgets
- Assets/Form.vue : corrections formulaire
- Seeder rôles/permissions mis à jour
- Dépendances composer mises à jour (barryvdh/laravel-dompdf, maatwebsite/excel)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 20:20:49 +02:00

131 lines
4.1 KiB
PHP

<?php
namespace App\Exports;
use App\Models\LigneBudget;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithTitle;
class BudgetExecutionExport implements FromCollection, WithHeadings, WithMapping, ShouldAutoSize, WithTitle
{
protected $user;
protected $annee;
protected $filters;
public function __construct($user, $annee, $filters = [])
{
$this->user = $user;
$this->annee = $annee;
$this->filters = $filters;
}
public function title(): string
{
$parts = ['Exécution Budgétaire', $this->annee];
if (!empty($this->filters['type'])) {
$parts[] = ucfirst($this->filters['type']);
}
if (!empty($this->filters['envelope'])) {
$parts[] = ucfirst($this->filters['envelope']);
}
if (!empty($this->filters['service'])) {
$parts[] = $this->filters['service'];
}
return substr(implode(' - ', $parts), 0, 31); // Max 31 chars for Excel sheet title
}
public function collection()
{
$query = LigneBudget::with(['budget.service', 'commandes'])
->whereHas('budget', function($q) {
$q->where('annee', $this->annee);
});
// Role based access
if (!$this->user->hasRole(['admin', 'directeur'])) {
$query->whereHas('budget', function($q) {
$q->where('service_id', $this->user->service_id);
});
}
// Dynamic filters
if (!empty($this->filters['type'])) {
$query->where('type_depense', $this->filters['type']);
}
if (!empty($this->filters['envelope'])) {
$query->whereHas('budget', function($q) {
$q->where('type_budget', $this->filters['envelope']);
});
}
if (!empty($this->filters['service'])) {
$query->whereHas('budget.service', function($q) {
$q->where('nom', $this->filters['service']);
});
}
$statutsConsommes = ['validee', 'commandee', 'partiellement_recue', 'recue_complete', 'cloturee'];
$statutsEngages = ['brouillon', 'en_attente_validation'];
return $query->get()->map(function($lb) use ($statutsConsommes, $statutsEngages) {
// Travail en mémoire sur la relation déjà eager-loadée — zéro requête supplémentaire
$commandes = $lb->commandes;
$consomme = (float) $commandes->whereIn('statut', $statutsConsommes)->sum('montant_ttc');
$engage = (float) $commandes->whereIn('statut', $statutsEngages)->sum('montant_ttc');
$lb->consomme = $consomme;
$lb->engage = $engage;
$lb->total_cumule = $consomme + $engage;
$lb->reste = (float) ($lb->montant_arbitre ?? 0) - $lb->total_cumule;
$lb->percent = ($lb->montant_arbitre ?? 0) > 0
? round(($lb->total_cumule / $lb->montant_arbitre) * 100, 2)
: 0;
return $lb;
});
}
public function headings(): array
{
return [
'ID',
'Service',
'Type Budget',
'Ligne Budgétaire',
'Type Dépense',
'Enveloppe Arbitrée (€)',
'Consommé (€)',
'Engagé (€)',
'Total Engagé (€)',
'Reste (€)',
'Taux d\'exécution (%)'
];
}
public function map($lb): array
{
return [
$lb->id,
$lb->budget->service->nom ?? 'Agglo',
$lb->budget->type_budget === 'agglo' ? 'Agglomération' : 'Mutualisé',
$lb->nom,
$lb->type_depense === 'fonctionnement' ? 'Fonctionnement' : 'Investissement',
$lb->montant_arbitre,
$lb->consomme,
$lb->engage,
$lb->total_cumule,
$lb->reste,
$lb->percent . '%'
];
}
}