@extends('layouts.app') @section('title', 'Gross Profit Report') @section('breadcrumb') Reports / Gross Profit @endsection @section('content')

Gross Profit Report

Revenue vs costs across all jobs — sorted by profitability

Export CSV
Reset
Total Revenue
${{ number_format($totalRevenue) }}
{{ $rows->count() }} jobs
Total Costs
${{ number_format($totalCosts) }}
Mat ${{ number_format($totalMaterials) }} + Lab ${{ number_format($totalLabour) }}
Total Gross Profit
${{ number_format($totalProfit) }}
{{ $profitableJobs }} profitable / {{ $unprofitableJobs }} at loss
Avg Gross Margin
{{ number_format($avgMargin, 1) }}%
across jobs with revenue
@if($rows->count() > 0) @php $total = max(1, $rows->count()); @endphp
Margin distribution across {{ $rows->count() }} jobs
Loss <15% 15–30% >30%
@if($marginBuckets['loss'] > 0)
@endif @if($marginBuckets['low'] > 0)
@endif @if($marginBuckets['ok'] > 0)
@endif @if($marginBuckets['healthy'] > 0)
@endif
Loss: {{ $marginBuckets['loss'] }} Low (0–15%): {{ $marginBuckets['low'] }} OK (15–30%): {{ $marginBuckets['ok'] }} Healthy (>30%): {{ $marginBuckets['healthy'] }}
@endif @php function gpSortUrl(string $col, string $currentSort, string $currentDir, array $params): string { $dir = ($currentSort === $col && $currentDir === 'desc') ? 'asc' : 'desc'; return route('reports.gross-profit', array_merge($params, ['sort_by' => $col, 'sort_dir' => $dir])); } $filterParams = array_filter([ 'from' => request('from'), 'to' => request('to'), 'pm_filter' => request('pm_filter'), 'stage_filter' => request('stage_filter'), 'job_number' => request('job_number'), ]); @endphp

Job Profitability

Click column headers to sort
@if($rows->count() > 0)
@foreach($rows as $row) @php $gpColor = $row['gross_profit'] >= 0 ? 'text-green-400' : 'text-red-400'; $barColor = $row['gross_margin'] < 0 ? 'bg-red-500' : ($row['gross_margin'] < 15 ? 'bg-orange-400' : ($row['gross_margin'] < 30 ? 'bg-yellow-400' : 'bg-green-500')); $barWidth = min(100, max(0, abs($row['gross_margin']))); @endphp @endforeach
Job # @if($sortBy==='job_number')@endif Client / PM Stage Detail
{{ $row['job_number'] }}
{{ $row['client'] }}
{{ $row['pm'] }} · {{ $row['created_at'] }}
{{ ucfirst($row['stage']) }}  
@else

No jobs found for the selected filters.

Clear filters
@endif
@endsection