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,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('phases', function (Blueprint $table) {
|
||||
// actual_start and actual_end already exist
|
||||
$table->date('baseline_start')->nullable()->after('actual_end');
|
||||
$table->date('baseline_end')->nullable()->after('baseline_start');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('phases', function (Blueprint $table) {
|
||||
$table->dropColumn(['baseline_start', 'baseline_end']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('features', function (Blueprint $table) {
|
||||
$table->date('planned_start')->nullable()->after('responsible');
|
||||
$table->date('planned_end')->nullable()->after('planned_start');
|
||||
$table->date('actual_start')->nullable()->after('planned_end');
|
||||
$table->date('actual_end')->nullable()->after('actual_start');
|
||||
$table->date('baseline_start')->nullable()->after('actual_end');
|
||||
$table->date('baseline_end')->nullable()->after('baseline_start');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('features', function (Blueprint $table) {
|
||||
$table->dropColumn([
|
||||
'planned_start', 'planned_end',
|
||||
'actual_start', 'actual_end',
|
||||
'baseline_start', 'baseline_end'
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('progress_snapshots', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('trackable'); // Phase, Feature, Task
|
||||
$table->date('snapshot_date');
|
||||
$table->decimal('progress', 5, 2)->default(0); // 0-100
|
||||
$table->date('planned_start')->nullable();
|
||||
$table->date('planned_end')->nullable();
|
||||
$table->date('actual_start')->nullable();
|
||||
$table->date('actual_end')->nullable();
|
||||
$table->string('method')->default('manual'); // manual|inspection|task|geometry|hybrid
|
||||
$table->json('metadata')->nullable(); // {earned_value, planned_value, actual_cost, spi, cpi, ...}
|
||||
$table->timestamps();
|
||||
|
||||
// Short index name for MySQL 64-char limit
|
||||
$table->unique(['trackable_type', 'trackable_id', 'snapshot_date'], 'ps_trackable_date_unique');
|
||||
$table->index(['trackable_type', 'trackable_id', 'snapshot_date'], 'ps_trackable_date_idx');
|
||||
$table->index(['snapshot_date'], 'ps_snapshot_date_idx');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('progress_snapshots');
|
||||
}
|
||||
};
|
||||
@@ -18,7 +18,7 @@ class RolesAndPermissionsSeeder extends Seeder
|
||||
// Create permissions
|
||||
$permissions = [
|
||||
'view projects', 'create projects', 'edit projects', 'delete projects',
|
||||
'assign users', 'upload layers', 'update progress', 'view reports', 'manage all',
|
||||
'assign users', 'upload layers', 'update progress', 'view reports', 'generate reports', 'manage all',
|
||||
'view inspections', 'create inspections', 'edit inspections', 'delete inspections', 'manage templates',
|
||||
'view tasks', 'create tasks', 'edit tasks', 'delete tasks', 'assign tasks', 'manage all tasks',
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user