@extends('layouts.app', ['title' => term('nav_finance','Control financiero')]) @section('header_actions') Historial de cortes @endsection @push('styles') @endpush @section('content') @php // Defensivo: si un despliegue parcial dejara variables sin pasar, la vista no truena. $filters = $filters ?? []; $f = array_merge(['from'=>'','to'=>'','all_dates'=>null,'type'=>null,'category'=>null,'project_id'=>null,'client_id'=>null,'lote_id'=>null,'method'=>null,'movement_type'=>null,'search'=>null], $filters); $c = is_array($corte ?? null) ? $corte : []; $corte = [ 'apartados' => ['total' => $c['apartados']['total'] ?? 0, 'count' => $c['apartados']['count'] ?? 0], 'aportaciones' => ['total' => $c['aportaciones']['total'] ?? 0, 'count' => $c['aportaciones']['count'] ?? 0], 'salidas' => ['total' => $c['salidas']['total'] ?? 0, 'count' => $c['salidas']['count'] ?? 0], 'total_ingresos' => $c['total_ingresos'] ?? 0, 'total_salidas' => $c['total_salidas'] ?? 0, 'saldo_esperado' => $c['saldo_esperado'] ?? 0, 'efectivo_esperado'=> $c['efectivo_esperado'] ?? 0, 'by_method' => is_array($c['by_method'] ?? null) ? $c['by_method'] : [], ]; $movimientos = $movimientos ?? collect(); $movementsCount = $movementsCount ?? $movimientos->count(); $existingCut = $existingCut ?? null; $cutDate = $cutDate ?? null; $singleDay = $singleDay ?? false; $activePreset = $activePreset ?? null; $period = $period ?? ''; $user = $user ?? auth()->user(); $movementType = $movementType ?? (fn ($tx) => 'Aportación'); $methodLabel = $methodLabel ?? (fn ($tx) => 'Otro'); $mvMeta = [ 'Apartado' => ['cls' => 'ap', 'ico' => 'fa-bookmark'], 'Aportación' => ['cls' => 'apo', 'ico' => 'fa-hand-holding-dollar'], 'Salida' => ['cls' => 'sa', 'ico' => 'fa-circle-minus'], ]; $methodIcon = fn ($m) => match (trim((string) $m)) { 'Efectivo' => 'fa-coins', 'Transferencia' => 'fa-building-columns', 'Tarjeta' => 'fa-credit-card', 'SPEI' => 'fa-bolt', default => 'fa-money-bill-wave', }; $keepAll = array_filter([ 'from'=>$f['from'],'to'=>$f['to'],'all_dates'=>$f['all_dates'], 'type'=>$f['type'],'category'=>$f['category'], 'project_id'=>$f['project_id'],'client_id'=>$f['client_id'],'lote_id'=>$f['lote_id'], 'method'=>$f['method'],'movement_type'=>$f['movement_type'],'search'=>$f['search'], ], fn ($v) => $v !== null && $v !== ''); $presetBase = collect($keepAll)->except(['from','to','all_dates'])->all(); $typeBase = collect($keepAll)->except(['movement_type'])->all(); $corteUrl = route('finance.corte', $keepAll); $presetList = ['hoy' => 'Hoy', 'ayer' => 'Ayer', 'semana' => 'Semana', 'mes' => 'Mes']; $typeList = ['apartado'=>['Apartados','ap','fa-bookmark'],'aportacion'=>['Aportaciones','apo','fa-hand-holding-dollar'],'salida'=>['Salidas','sa','fa-circle-minus']]; $ti = $corte['total_ingresos'] ?: 0; $cutClosed = (bool) $existingCut; $diffInit = $cutClosed ? (float) $existingCut->diferencia : (0 - $corte['efectivo_esperado']); $diffCuadra = $cutClosed && abs($diffInit) < 0.005; // Tarjeta de diferencia: antes de cerrar muestra el efectivo esperado ("Por contar"); ya cerrada muestra la diferencia real. $cardVal = $cutClosed ? $diffInit : (float) $corte['efectivo_esperado']; $cardState = $cutClosed ? ($diffCuadra ? '¡Todo cuadra!' : ($diffInit < 0 ? 'Falta' : 'Sobra')) : 'Por contar'; $cardIcoBg = $cutClosed ? ($diffCuadra ? '#16a34a' : '#ef4444') : '#c9a84c'; $hasAdvancedFilters = (bool) array_filter([$f['search'],$f['type'],$f['category'],$f['project_id'],$f['client_id'],$f['lote_id'],$f['method']]); @endphp
@if(session('success'))
{{ session('success') }}
@endif @if($errors->any())
{{ $errors->first() }}
@endif
@foreach($presetList as $k => $label) {{ $label }} @endforeach Todo {{ $period }}
Filtros y búsqueda
@if($f['movement_type'])@endif
@if(array_filter($f))@endif
{{-- ===== Tarjetas superiores ===== --}}
Apartados
${{ number_format($corte['apartados']['total'],2) }}
{{ $corte['apartados']['count'] }} movimientos
Aportaciones
${{ number_format($corte['aportaciones']['total'],2) }}
{{ $corte['aportaciones']['count'] }} movimientos
Salidas de caja
${{ number_format($corte['salidas']['total'],2) }}
{{ $corte['salidas']['count'] }} movimientos
Diferencia de caja
${{ number_format($cardVal,2) }}
{{ $cardState }}
{{-- ===== Columna izquierda: movimientos ===== --}}
Movimientos del día
{{ $movementsCount }} registro(s)
Todos @foreach($typeList as $mk => $mv) {{ $mv[0] }} @endforeach
@forelse($movimientos as $tx) @php $tipo = $movementType($tx); $meta = $mvMeta[$tipo] ?? $mvMeta['Aportación']; $metodo = $methodLabel($tx); $isIn = $tx->type === 'income'; @endphp @empty @endforelse
Fecha / HoraTipo movimientoClienteLoteMétodo de pagoImporte
{{ optional($tx->date)->format('d/m/Y') }}
{{ optional($tx->created_at)->format('h:i A') }}
{{ $tipo }}@if($tx->description)
{{ \Illuminate\Support\Str::limit(\Illuminate\Support\Str::beforeLast($tx->description, ' - '), 42) }}
@endif
{{ $tx->client?->name ?? '—' }} {{ $tx->lote?->label ?? '—' }} {{ $metodo }} {{ $isIn ? '' : '−' }}${{ number_format($tx->amount,2) }} @if($tx->pettyCashMovement) @else

@csrf @method('DELETE')
@endif
Sin movimientos en el periodo seleccionado.

* Los importes consideran los filtros seleccionados y la fecha del corte.

{{-- ===== Columna derecha: resumen + concentrado + cierre ===== --}}
Resumen del corte diario
Fecha del corte{{ $cutDate ? \Illuminate\Support\Carbon::parse($cutDate)->format('d/m/Y') : $period }}
Usuario{{ $user?->name ?? '—' }}
Total Apartados${{ number_format($corte['apartados']['total'],2) }}
Total Aportaciones${{ number_format($corte['aportaciones']['total'],2) }}
Total Ingresos${{ number_format($corte['total_ingresos'],2) }}
Salidas de Caja−${{ number_format($corte['total_salidas'],2) }}
Saldo Esperado en Caja${{ number_format($corte['saldo_esperado'],2) }}
Concentrado por método de pago
@forelse($corte['by_method'] as $bucket => $row)
{{ $bucket }} ${{ number_format($row['total'],2) }} {{ $ti > 0 ? number_format($row['total'] / $ti * 100, 1) : '0.0' }}%
@empty

Sin ingresos en el periodo.

@endforelse @if(! empty($corte['by_method']))
TOTAL${{ number_format($corte['total_ingresos'],2) }}
@endif
Caja física y cierre
@if($existingCut)
Corte cerrado el {{ optional($existingCut->closed_at)->format('d/m/Y H:i') }}.
Efectivo esperado${{ number_format($existingCut->efectivo_esperado,2) }}
Efectivo capturado${{ number_format($existingCut->efectivo_capturado,2) }}
Diferencia${{ number_format($existingCut->diferencia,2) }}
Ver cierre
@elseif($singleDay)
@csrf @if($f['project_id'])@endif
Efectivo esperado (sistema)${{ number_format($corte['efectivo_esperado'],2) }}
Diferencia
Imprimir corte
@else

El cierre de caja es por día. Selecciona un solo día (o un preset como “Hoy”) para poder cerrar el corte.

Imprimir corte del periodo @endif
Análisis y balances (año en curso)
@if($selectedProjectBalance)
${{ number_format($selectedProjectBalance['total'],0) }}
Total del proyecto
${{ number_format($selectedProjectBalance['ingresado'],0) }}
Ingresado
${{ number_format($selectedProjectBalance['gastos'],0) }}
Gastos
${{ number_format($selectedProjectBalance['capital'],0) }}
Capital
${{ number_format($selectedProjectBalance['activo'],0) }}
Activo
@endif
Balances por proyecto
Total del Proyecto, Ingresado, Gastos, Capital y Activo por proyecto.
@forelse($projectBalances as $balance) @php $projectMovementsUrl = route('finance.index', ['project_id' => $balance['project_id'], 'all_dates' => 1]); @endphp @empty @endforelse
ProyectoTotal del ProyectoIngresadoGastosCapitalActivo
{{ $balance['project']->name }} Ver movimientos
{{ $balance['project']->location ?: 'Sin ubicación' }}
${{ number_format($balance['total'],0) }} ${{ number_format($balance['ingresado'],0) }} ${{ number_format($balance['gastos'],0) }} ${{ number_format($balance['capital'],0) }} ${{ number_format($balance['activo'],0) }}
Sin proyectos para calcular balance.
Flujo mensual
Ingresos vs egresos del año
@endsection @section('modals') @foreach($movimientos as $tx) @continue($tx->pettyCashMovement) @endforeach @endsection @push('scripts') @endpush