@php $cols = collect(explode(',', request('columns', ''))); $showHeader = request('show_header') === '1'; $showSummary = request('show_summary') === '1'; $hasCol = fn(string $k) => $cols->contains($k); // count visible columns for empty-state colspan $allColKeys = ['category_name','location_name','current_qty','expiry_date','days_until_expiry', 'daily_rate','projected_sold','at_risk_qty','at_risk_value','at_risk_revenue', 'at_risk_profit','sell_through_bar','risk_badge']; $colCount = 2 + collect($allColKeys)->filter(fn($k) => $hasCol($k))->count(); @endphp

⏱ Expiry Analyzer — Print Preview

@if($showHeader)

Expiry Analyzer Report

Generated: {{ now()->format('d M Y, h:i A') }} Risk Filter: {{ $riskLabel }} Velocity Period: {{ $velocityDays }} days @if($expiryWindow)Expiry Window: Next {{ $expiryWindow }} days@endif @if($search)Search: "{{ $search }}"@endif Batches: {{ $summary['total_batches'] }}
@endif @if($showSummary)
Total Batches
{{ number_format($summary['total_batches']) }}
Expired
{{ number_format($summary['expired']) }}
At-Risk Qty
{{ number_format($summary['total_at_risk_qty'], 2) }}
Cost Loss
{{ $currency }} {{ number_format($summary['total_at_risk_value'], 2) }}
Revenue Loss
{{ $currency }} {{ number_format($summary['total_at_risk_revenue'], 2) }}
Profit Loss
@php $pl = $summary['total_at_risk_profit']; @endphp
{{ $pl >= 0 ? '▼' : '▲' }} {{ $currency }} {{ number_format(abs($pl), 2) }}
@endif @if($hasCol('category_name')) @endif @if($hasCol('location_name')) @endif @if($hasCol('current_qty')) @endif @if($hasCol('expiry_date')) @endif @if($hasCol('days_until_expiry')) @endif @if($hasCol('daily_rate')) @endif @if($hasCol('projected_sold')) @endif @if($hasCol('at_risk_qty')) @endif @if($hasCol('at_risk_value')) @endif @if($hasCol('at_risk_revenue')) @endif @if($hasCol('at_risk_profit')) @endif @if($hasCol('sell_through_bar')) @endif @if($hasCol('risk_badge')) @endif @forelse($batches as $i => $b) @php $days = (int) $b->days_until_expiry; $atRisk = (float) $b->at_risk_qty; $qty = (float) $b->current_qty; $rate = (float) $b->daily_rate; $profit = (float) $b->at_risk_profit; if ($days < 0) { $risk = 'expired'; $riskBadge = '💀 Expired'; } elseif ($rate == 0) { $risk = 'nodata'; $riskBadge = '⚪ No Data'; } elseif ($atRisk == 0) { $risk = 'safe'; $riskBadge = '🟢 Safe'; } elseif ($atRisk / max(1,$qty) >= 0.30) { $risk = 'danger'; $riskBadge = '🔴 High Risk'; } else { $risk = 'warning'; $riskBadge = '🟡 At Risk'; } $daysClass = $days < 0 ? 'days-expired' : ($days <= 7 ? 'days-urgent' : ($days <= 30 ? 'days-warning' : 'days-ok')); $daysLabel = $days < 0 ? abs($days).'d ago' : ($days === 0 ? 'Today' : $days.' days'); $pct = $qty > 0 ? min(100, round(($b->projected_sold / $qty) * 100)) : 0; $barColor = $pct >= 100 ? '#16a34a' : ($pct >= 70 ? '#d97706' : '#dc2626'); @endphp @if($hasCol('category_name')) @endif @if($hasCol('location_name')) @endif @if($hasCol('current_qty')) @endif @if($hasCol('expiry_date')) @endif @if($hasCol('days_until_expiry')) @endif @if($hasCol('daily_rate')) @endif @if($hasCol('projected_sold')) @endif @if($hasCol('at_risk_qty')) @endif @if($hasCol('at_risk_value')) @endif @if($hasCol('at_risk_revenue')) @endif @if($hasCol('at_risk_profit')) @endif @if($hasCol('sell_through_bar')) @endif @if($hasCol('risk_badge')) @endif @empty @endforelse @if($batches->count() > 0) @if($hasCol('category_name')) @endif @if($hasCol('location_name')) @endif @if($hasCol('current_qty')) @endif @if($hasCol('expiry_date')) @endif @if($hasCol('days_until_expiry')) @endif @if($hasCol('daily_rate')) @endif @if($hasCol('projected_sold')) @endif @if($hasCol('at_risk_qty')) @endif @if($hasCol('at_risk_value')) @endif @if($hasCol('at_risk_revenue')) @endif @if($hasCol('at_risk_profit')) @endif @if($hasCol('sell_through_bar')) @endif @if($hasCol('risk_badge')) @endif @endif
# Item / BatchCategoryLocationStockExpiry DateDays LeftDaily RateProj. SoldAt-Risk QtyCost LossRevenue LossProfit / LossSell-ThroughRisk
{{ $i + 1 }}
{{ $b->item_name }}
@if($b->sku)
{{ $b->sku }}
@endif @if($b->batch_number)
Batch: {{ $b->batch_number }}
@endif
{{ $b->category_name ?? '—' }}{{ $b->location_name ?? '—' }}{{ number_format($qty, 0) }}{{ $b->expiry_date ? \Carbon\Carbon::parse($b->expiry_date)->format('d M Y') : '—' }}{{ $daysLabel }}{{ $rate > 0 ? number_format($rate, 2).'/d' : '—' }}{{ number_format($b->projected_sold, 0) }} {{ $atRisk > 0 ? number_format($atRisk, 0) : '—' }} {{ $atRisk > 0 ? $currency.' '.number_format($b->at_risk_value, 2) : '—' }} {{ $atRisk > 0 ? $currency.' '.number_format($b->at_risk_revenue, 2) : '—' }} @if($atRisk > 0){{ $profit >= 0 ? '▼' : '▲' }} {{ $currency }} {{ number_format(abs($profit), 2) }} @else — @endif
{{ $pct }}%
{{ $riskBadge }}
No batches match the selected filters.
Total — {{ $summary['total_batches'] }} batches{{ number_format($batches->sum('current_qty'), 0) }}{{ number_format($batches->sum('projected_sold'), 0) }}{{ number_format($summary['total_at_risk_qty'], 0) }}{{ $currency }} {{ number_format($summary['total_at_risk_value'], 2) }}{{ $currency }} {{ number_format($summary['total_at_risk_revenue'], 2) }}{{ $summary['total_at_risk_profit'] >= 0 ? '▼' : '▲' }} {{ $currency }} {{ number_format(abs($summary['total_at_risk_profit']), 2) }}