## 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>
131 lines
4.1 KiB
PHP
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 . '%'
|
|
];
|
|
}
|
|
}
|