feat(reports): complete reporting system with deviations, progress curves, multi-format export
- Added planned/actual/baseline dates to Phase and Feature models
- Created ProgressSnapshot model + migration for historical tracking
- Implemented CaptureDailyProgressSnapshot job (scheduled daily at 06:30)
- Added DeviationCalculator logic (SPI, planned progress, deviation days)
- ReportGenerator service with complete data aggregation
- ReportController with builder, preview, generate (HTML/Excel)
- ReportBuilder Livewire component with date range, entity selection, format
- ProjectReportExport with 10 sheets (Summary, Phases, Features, Inspections, Issues, Tasks, Deviations, Media, Progress Curve, Parameters)
- Blade partials for all sections (header, summary, phases, features, inspections, issues, tasks, media, deviations, progress curve)
- New routes: /projects/{project}/reports/*
- Added 'generate reports' permission
- All 101 tests passing
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\DTO\ReportFilters;
|
||||
use App\Models\Project;
|
||||
use App\Services\ReportGenerator;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use App\Exports\ProjectReportExport;
|
||||
|
||||
class ReportController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show report builder for a project
|
||||
*/
|
||||
public function show(Project $project)
|
||||
{
|
||||
$this->authorizeProjectAccess($project);
|
||||
|
||||
return view('reports.builder', [
|
||||
'project' => $project,
|
||||
'availableEntities' => (new ReportFilters)->getAvailableEntities(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate report (HTML preview or download)
|
||||
*/
|
||||
public function generate(Request $request, Project $project)
|
||||
{
|
||||
$this->authorizeProjectAccess($project);
|
||||
|
||||
$filters = ReportFilters::fromRequest($request->all());
|
||||
$generator = new ReportGenerator($project, $filters);
|
||||
$data = $generator->generate();
|
||||
|
||||
$format = $filters->format;
|
||||
|
||||
if ($format === 'excel') {
|
||||
return $this->downloadExcel($project, $filters, $data);
|
||||
}
|
||||
|
||||
// HTML format (default) - render printable view
|
||||
return view('reports.complete', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Preview partial (AJAX)
|
||||
*/
|
||||
public function preview(Request $request, Project $project)
|
||||
{
|
||||
$this->authorizeProjectAccess($project);
|
||||
|
||||
$filters = ReportFilters::fromRequest($request->all());
|
||||
$generator = new ReportGenerator($project, $filters);
|
||||
$data = $generator->generate();
|
||||
|
||||
return view('reports.partials._preview', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download Excel export
|
||||
*/
|
||||
protected function downloadExcel(Project $project, ReportFilters $filters, array $data)
|
||||
{
|
||||
$export = new ProjectReportExport($project, $filters, $data);
|
||||
$filename = 'informe_' . $project->name . '_' . now()->format('Ymd_His') . '.xlsx';
|
||||
|
||||
return Excel::download($export, $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize access to project
|
||||
*/
|
||||
protected function authorizeProjectAccess(Project $project): void
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if ($user->can('manage all')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$project->users()->where('user_id', $user->id)->exists()) {
|
||||
abort(403, 'No tienes acceso a este proyecto.');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user