@extends('layouts.app') @section('title', __('Billing')) @section('content') @php use Illuminate\Support\Carbon; use App\Models\FileAnalysis; use App\Models\TenantMonthlyInvoice; $tenantId = $tenant?->id ?? 0; // --- Period filter --------------------------------------------------- $monthParam = request('month'); try { $period = $monthParam ? Carbon::createFromFormat('Y-m', $monthParam)->startOfMonth() : Carbon::now()->startOfMonth(); } catch (\Throwable $e) { $period = Carbon::now()->startOfMonth(); } $periodStart = $period->copy()->startOfMonth(); $periodEnd = $period->copy()->endOfMonth(); // --- Approved analyses for this period (admin-reviewed, sent to client) $hasBillable = \Illuminate\Support\Facades\Schema::hasColumn('file_analyses', 'billable_amount'); $hasReviewed = \Illuminate\Support\Facades\Schema::hasColumn('file_analyses', 'reviewed_at'); $approvedCol = $hasReviewed ? 'reviewed_at' : 'finished_at'; $analyses = FileAnalysis::query() ->with(['submission.uploader', 'reviewer']) ->where('tenant_id', $tenantId) ->where('status', FileAnalysis::STATUS_DONE) ->whereBetween($approvedCol, [$periodStart, $periodEnd]) ->orderByDesc($approvedCol) ->paginate(25) ->withQueryString(); // --- Period stats ---------------------------------------------------- $totalApproved = FileAnalysis::query() ->where('tenant_id', $tenantId) ->where('status', FileAnalysis::STATUS_DONE) ->whereBetween($approvedCol, [$periodStart, $periodEnd]) ->count(); $billable = $hasBillable ? (float) FileAnalysis::query() ->where('tenant_id', $tenantId) ->where('status', FileAnalysis::STATUS_DONE) ->whereBetween($approvedCol, [$periodStart, $periodEnd]) ->sum('billable_amount') : 0.0; $billedCount = $hasBillable ? FileAnalysis::query() ->where('tenant_id', $tenantId) ->where('status', FileAnalysis::STATUS_DONE) ->whereBetween($approvedCol, [$periodStart, $periodEnd]) ->whereNotNull('billed_invoice_id') ->count() : 0; $unbilledCount = max(0, $totalApproved - $billedCount); $freeAllowance = \Illuminate\Support\Facades\Schema::hasColumn('tenants', 'free_analyses_per_month') ? (int) ($tenant?->free_analyses_per_month ?? config('subscriptions.usage.free_analyses_per_month', 0)) : (int) config('subscriptions.usage.free_analyses_per_month', 0); $freeRemaining = max(0, $freeAllowance - $totalApproved); // --- Month picker options -------------------------------------------- $monthOptions = []; $cursor = Carbon::now()->startOfMonth(); for ($i = 0; $i < 12; $i++) { $monthOptions[$cursor->format('Y-m')] = $cursor->translatedFormat('F Y'); $cursor->subMonthNoOverflow(); } // --- Invoice for this period (if any) -------------------------------- $invoice = TenantMonthlyInvoice::query() ->where('tenant_id', $tenantId) ->whereDate('period_start', $periodStart->toDateString()) ->first(); @endphp {{-- Header --}}
{{ __('Pay-per-analysis') }}

{{ __('Billing') }}

{{ __('Every approved analysis adds a line to the monthly invoice.') }}

{{-- KPI cards --}}
{{ __('Approved') }}
{{ $totalApproved }}
@if($freeAllowance > 0)
{{ __(':n free remaining', ['n' => $freeRemaining]) }} / {{ $freeAllowance }}
@endif
{{ __('Billed') }}
{{ $billedCount }}
{{ __('On an invoice') }}
{{ __('Pending') }}
{{ $unbilledCount }}
{{ __('To be invoiced') }}
{{ __('Estimated') }}
{{ number_format($billable, 2, ',', ' ') }} €
{{ __('Excl. VAT') }}
{{-- Invoice banner --}} @if($invoice)
{{ __('Invoice for this period') }}
{{ $invoice->number }}
{{ number_format((float) $invoice->total, 2, ',', ' ') }} {{ $invoice->currency }}
$invoice->status === 'paid', 'bg-amber-50 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300' => $invoice->status === 'issued', 'bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-300' => $invoice->status === 'draft', 'bg-rose-50 text-rose-700 dark:bg-rose-900/40 dark:text-rose-300' => $invoice->status === 'void', ])>{{ ucfirst($invoice->status) }}
@endif {{-- Approved analyses list --}}

{{ __('Approved analyses') }}

{{ $period->translatedFormat('F Y') }} — {{ __('reviewed and sent to clients.') }}

@if($analyses->isEmpty())

{{ __('No approved analyses in this period.') }}

@else
@foreach($analyses as $a) @php $approvedAt = $a->reviewed_at ?? $a->finished_at; $billed = $hasBillable && ! is_null($a->billed_invoice_id); $amount = $hasBillable ? (float) $a->billable_amount : null; $currency = $a->billable_currency ?? 'EUR'; @endphp @endforeach
{{ __('File') }} {{ __('Approved at') }} {{ __('Amount') }} {{ __('Status') }}
{{ $a->submission?->original_name ?? '—' }}
{{ optional($a->submission?->uploader)->name ?? '—' }}
{{ $approvedAt?->format('Y-m-d H:i') ?? '—' }} @if(! is_null($amount)) {{ number_format($amount, 2, ',', ' ') }} {{ $currency }} @else @endif @if($billed) {{ __('Billed') }} @else {{ __('Pending') }} @endif @if($a->submission) {{ __('View') }} → @endif
{{ $analyses->links() }}
@endif

{{ __('Only approved analyses sent to clients are billable. Failed runs and re-runs are excluded.') }}

@endsection