@php // Difference tone for the KPI card + summary ring. Both counts have a say: // the day's takings being out is a difference even when the drawer totals. $diff = (float) ($summary['difference'] ?? 0); $takingsDiff = (float) ($summary['takingsDifference'] ?? 0); if (! ($summary['anyDeclared'] ?? false)) { $diffColor = '#cbd5e1'; $diffText = 'text-slate-400'; } elseif (abs($diff) < 0.01) { $diffColor = '#10b981'; $diffText = 'text-emerald-600'; } else { $diffColor = '#f59e0b'; $diffText = 'text-amber-600'; } $takingsText = ! ($summary['anyDeclared'] ?? false) ? 'text-slate-400' : (abs($takingsDiff) < 0.01 ? 'text-emerald-600' : 'text-amber-600'); @endphp
{{-- Header --}}
@error('business_date')

{{ $message }}

@enderror
{{ __('History') }}
{{-- Needs your attention: differences sent back to me, and (for a director) other days' differences still waiting on a decision. One slim strip, the detail lives in a dialog so the count itself stays at the top of the page. --}} @php $hasReviews = $canReview && $pendingApprovals->isNotEmpty(); $awaitingMe = $hasReviews ? $pendingApprovals->where('reason_status', 'pending')->count() : 0; $sentBack = $hasReviews ? $pendingApprovals->where('reason_status', 'queried')->count() : 0; @endphp @if ($myQueries->isNotEmpty() || $hasReviews)
@if ($myQueries->isNotEmpty()) @endif @if ($hasReviews) @endif
@endif @if ($existing) @include('livewire.treasury.partials.cash-up-summary', ['existing' => $existing, 'baseCode' => $baseCode]) @elseif (! $cashAccount && ! $mobileAccount) @else {{-- KPI cards, Treasury card layout: a trio per account (opening / received / value on hand today), each with a "vs yesterday" delta. --}}
@foreach ($kpiCards as $kpi) @php $tone = $kpi['delta'] === null ? 'muted' : ($kpi['delta'] >= 0 ? 'up' : 'down'); $pill = ['up' => 'bg-emerald-50 text-emerald-700', 'down' => 'bg-rose-50 text-rose-700', 'muted' => 'bg-slate-100 text-slate-500'][$tone]; @endphp @php // A card only earns a hover when there is something to // itemise: the opening cards carry a single carried-over // figure and have nothing to break down. $cur = $kpi['currency']; $hasPop = ! empty($kpi['items']); $popTotal = $kpi['metric'] === 'expected' ? round((float) $kpi['opening'] + (float) $kpi['inflow'] - (float) $kpi['outflow'], 2) : (float) $kpi['inflow']; @endphp

{{ $kpi['label'] }}

{{ $baseCode }} {{ number_format((float) $kpi['value'], 2) }}

@if ($kpi['delta'] !== null) {{ number_format(abs($kpi['delta']), 0) }}% {{ __('vs yesterday') }} @else {{ __('No prior day') }} @endif @if ($hasPop) @endif
@if ($hasPop) {{-- Teleported: the card strip scrolls horizontally, so a panel positioned inside it would be clipped by that overflow. Fixed to the viewport off the body instead. --}} @endif
@endforeach
{{-- Reconciliation form. When the user isn't clocked in a transparent overlay lets them read the figures but intercepts any interaction, popping the "clock in first" dialog instead. --}}
@unless ($isClockedIn)
@endunless @php $countedPct = ($summary['accountCount'] ?? 0) > 0 ? (int) round(($summary['declaredCount'] / $summary['accountCount']) * 100) : 0; @endphp
@endif {{-- History --}}

{{ __('Recent cash-ups') }}

{{-- Filter toolbar --}}
@if ($performers->count() > 1) @endif
@if ($history->isEmpty())
@else
{{-- Two-tier header: each account gets a "day" and a "total" column, mirroring the two counts the cashier enters. A difference shows as a small figure under the count in the cell it belongs to. --}} {{-- Same words as the count itself, so a row reads as the two figures the cashier actually entered. --}} @foreach ($history as $row) @php $cashLine = $row->lines->first(fn ($l) => $l->account?->type === 'cash'); $mobileLine = $row->lines->first(fn ($l) => $l->account?->type === 'mobile_money'); $approvalState = null; if ($row->has_variance) { $statuses = $row->lines->pluck('reason_status')->filter(); // Most-open state wins: still with the cashier, then still // with the director, then whatever was actually decided. $approvalState = $statuses->contains('queried') ? 'queried' : ($statuses->contains('pending') ? 'pending' : ($statuses->contains('rejected') ? 'rejected' : ($statuses->isNotEmpty() ? 'approved' : null))); } @endphp @endforeach
{{ __('Date') }} {{ __('Cash') }} {{ __('EcoCash') }} {{ __('Status') }} {{ __('Approval') }} {{ __('By') }} {{ __('Actions') }}
{{ __('Came in') }} {{ __('In total') }} {{ __('Came in') }} {{ __('In total') }}
{{ $row->business_date->format('d M Y') }} @if ($row->has_variance) {{ __('Variance') }} ({{ $row->variance_lines_count }}) @else {{ __('Balanced') }} @endif @switch($approvalState) @case('pending') {{ __('Pending') }} @break @case('queried') {{ __('Sent back') }} @break @case('approved') {{ __('Approved') }} @break @case('rejected') {{ __('Rejected') }} @break @default — @endswitch {{ $row->performer?->name ?? '—' }}
@php // Only explain the mark when a row on this page carries it, // so the note retires itself once the old rows scroll out. $hasLegacy = $history->contains(fn ($r) => $r->lines->contains->takings_is_legacy_basis); @endphp
@if ($hasLegacy)

† {{ __('Counted before the day figure meant money in. On those rows it is the day\'s net, after what was paid out or moved on, and the difference beside it was decided on that basis.') }}

@endif {{ $history->links() }}
@endif
{{-- Attention dialog: the list behind whichever strip was clicked. Closes itself once the list is empty (last query answered, last difference decided). Deliberately not x-trap'd: the quick-fix overlay it opens lives outside it and must stay clickable. --}} @php $attentionOpen = ($attention === 'queries' && $myQueries->isNotEmpty()) || ($attention === 'reviews' && $hasReviews); @endphp @if ($attentionOpen)
$attention === 'queries', 'max-w-5xl' => $attention === 'reviews', ])>
$attention === 'queries', 'border-amber-100 bg-amber-50/60' => $attention === 'reviews', ])>
@if ($attention === 'queries')

{{ __('Sent back to you') }}

{{ __('Nothing has been charged to you. Fix what is wrong, then send it back.') }}

@else

{{ __('Differences from other days') }}

{{ __('Still waiting on you. The day on screen is decided on the record itself.') }}

@endif
@if ($attention === 'queries') {{ $myQueries->count() }} {{ __('to answer') }} @else @if ($awaitingMe) {{ $awaitingMe }} {{ __('pending') }} @endif @if ($sentBack) {{ $sentBack }} {{ __('sent back') }} @endif @endif
@if ($attention === 'queries') @include('livewire.treasury.partials.cash-up-queries', [ 'myQueries' => $myQueries, 'answering' => $answering, 'sweepTargets' => $sweepTargets, ]) @else @include('livewire.treasury.partials.cash-up-reviews') @endif
@endif {{-- Quick-fix overlay, rendered for every state of the page: it is reached from a fresh count AND from a queried day re-opened inside the dialog above, so it cannot live in the fresh-count branch only. --}} @include('livewire.treasury.partials.cash-up-fix-modal', [ 'expenseCategories' => $expenseCategories, 'incomeCategories' => $incomeCategories, 'openInvoices' => $openInvoices, ]) {{-- Clock-in gate dialog (pure Alpine, shown when a clocked-out user tries to act) --}}

{{ __('Clock in first') }}

{{ __('You need to be on the clock before cashing up. Use the clock button in the top bar to start your shift, then come back here.') }}