@extends('layout.main') @section('content') {{-- Reportes → Reportes de inventario (InventoryReportController): existencias por local, un producto, inventario bajo, valor del inventario y Kardex. Solo lee; mismos principios visuales que Ventas por fechas. --}} @php $money = function ($v) { return '$' . number_format((float) $v, 2); }; $n = function ($v) { $v = (float) $v; return $v == floor($v) ? number_format($v, 0) : number_format($v, 2); }; $keep = array_filter(['local' => $warehouse_id ?: null, 'categoria' => $category_id, 'q' => $text !== '' ? $text : null]); $tab = function ($key) use ($keep) { return route('report.inventory', ['vista' => $key] + $keep); }; $short = function ($name) { return preg_replace('/\s*-\s*Entre Tu y Yo$/i', '', $name); }; $needsProduct = in_array($vista, ['producto', 'kardex']); @endphp

Qué hay, dónde está, cuánto vale y cómo se ha movido. Solo consulta: no cambia nada.

@if($vista == 'producto' || $vista == 'kardex')@if(request('producto'))@endif @endif @if(count($warehouses) > 1)
@endif @if(!$needsProduct)
@endif
@if($vista == 'kardex')
@endif
{{-- ============ INVENTARIO POR LOCAL ============ --}} @if($vista == 'actual')
@if($rows->isEmpty())
No hay productos con existencia con ese filtro.
@else
@foreach($cols as $w)@endforeach @if($cols->count() > 1)@endif @foreach($rows as $r) @foreach($cols as $w)@php $v = $r->{'w' . $w->id}; @endphp@endforeach @if($cols->count() > 1)@endif @endforeach
ProductoCategoría{{ $short($w->name) }}Total
{{ $r->name }}
{{ $r->code }}
{{ $r->category ?: '—' }}{{ $n($v) }}{{ $n($r->total) }}
@endif
{{ $rows->links('pagination::bootstrap-4') }}
{{-- ============ UN PRODUCTO ============ --}} @elseif($vista == 'producto') @if(!isset($product))
@if($matches->count() > 1)

Elige el producto:

@foreach($matches as $m){{ $m->name }} {{ $m->code }}@endforeach
@elseif($text !== '')
No encontramos productos con «{{ $text }}».
@else
Escribe el nombre o el código de un producto para ver cuánto hay en cada local.
@endif
@else @php $totalQty = $per->sum('qty'); @endphp
Existencia total{{ $n($totalQty) }}
Costo unitario{{ $money($product->cost) }}
Valor al costo{{ $money($totalQty * $product->cost) }}
Mínimo (alerta){{ $product->alert_quantity ? $n($product->alert_quantity) : '—' }}
{{ $product->name }}
{{ $product->code }} · {{ $category_name ?: 'Sin categoría' }}
@foreach($per as $row) @php $low = $product->alert_quantity > 0 && $row->qty <= $product->alert_quantity; @endphp @endforeach
LocalExistenciaValor al costoEstado
{{ $short($row->name) }}{{ $n($row->qty) }}{{ $money($row->qty * $product->cost) }} @if($row->qty <= 0)Agotado@elseif($low)Bajo el mínimo@elseNormal@endif
@if($kardex_installed)
Últimos movimientos
Ver Kardex completo →
@if($last->isEmpty())
Todavía no hay movimientos anotados de este producto.
@else
@foreach($last as $m)@endforeach
FechaLocalMovimientoEntraSaleSaldo
{{ \Carbon\Carbon::parse($m->created_at)->format('d/m/Y H:i') }}{{ $short($m->warehouse) }}{{ $m->type }}@if($m->reference)
{{ $m->reference }}
@endif
{{ $m->qty_in > 0 ? '+' . $n($m->qty_in) : '' }}{{ $m->qty_out > 0 ? '−' . $n($m->qty_out) : '' }}{{ $n($m->balance) }}
@endif
@endif @endif {{-- ============ INVENTARIO BAJO ============ --}} @elseif($vista == 'bajo')

Productos cuya existencia en un local está en su mínimo (cantidad de alerta del producto) o por debajo. Los más urgentes primero.

@if($rows->isEmpty())
Nada por debajo del mínimo con este filtro.
@else
@foreach($rows as $r) @endforeach
ProductoLocalCategoríaExistenciaMínimoFaltan
{{ $r->name }}
{{ $r->code }}
{{ $short($r->warehouse) }}{{ $r->category ?: '—' }} {{ $n($r->qty) }}{{ $n($r->alert_quantity) }} {{ $n(max(0, $r->alert_quantity - $r->qty)) }} @if($r->qty <= 0)Agotado@elseBajo@endif
@endif
{{ $rows->links('pagination::bootstrap-4') }}
{{-- ============ VALOR DEL INVENTARIO ============ --}} @elseif($vista == 'valor')
Valor al costo{{ $money($total->cost_value ?? 0) }}
Valor a precio de venta{{ $money($total->sale_value ?? 0) }}
Unidades{{ $n($total->units ?? 0) }}
Productos distintos{{ $n($total->products ?? 0) }}

Costo = existencia × costo del producto. Precio de venta = existencia × precio del producto (tal como está guardado). Solo cuenta lo que hay en existencia.

@foreach(['by_warehouse' => ['Por local', 'Local'], 'by_category' => ['Por categoría', 'Categoría']] as $key => [$title, $col])
{{ $title }}
@if($$key->isEmpty())
Sin datos con este filtro.
@else
@foreach($$key as $r)@endforeach
{{ $col }}ProductosUnidadesValor al costoA precio de venta
{{ $key == 'by_warehouse' ? $short($r->name) : $r->name }}{{ $n($r->products) }}{{ $n($r->units) }}{{ $money($r->cost_value) }}{{ $money($r->sale_value) }}
@endif
@endforeach {{-- ============ KARDEX ============ --}} @else @if(!$kardex_installed)
El Kardex necesita una tabla nueva: ejecuta database/cambios/2026-10-21b_kardex.sql en phpMyAdmin (base de pruebas). Desde entonces, cada movimiento de inventario queda anotado solo.
@elseif(!$product)
@if($matches->count() > 1)

Elige el producto:

@foreach($matches as $m){{ $m->name }} {{ $m->code }}@endforeach
@elseif($text !== '')
No encontramos productos con «{{ $text }}».
@else
Escribe el nombre o el código de un producto para ver su historial de movimientos.
@endif
@else
{{ $product->name }}{{ $product->code }}
@if($opening !== null)
Saldo inicial{{ $n($opening) }}
@endif
Entradas{{ $n($sum_in) }}
Salidas{{ $n($sum_out) }}
@if($rows->isNotEmpty() && $warehouse_id)
Saldo final{{ $n($rows->last()->balance) }}
@endif
@if($rows->isEmpty())
No hay movimientos anotados en estas fechas{{ $warehouse_id ? ' en este local' : '' }}. El Kardex se llena desde que se instaló.
@else
@foreach($rows as $m)@endforeach
FechaLocalMovimientoReferenciaEntradaSalidaSaldoUsuario
{{ \Carbon\Carbon::parse($m->created_at)->format('d/m/Y H:i') }}{{ $short($m->warehouse) }}{{ $m->type }}{{ $m->reference ?: '—' }} {{ $m->qty_in > 0 ? '+' . $n($m->qty_in) : '' }}{{ $m->qty_out > 0 ? '−' . $n($m->qty_out) : '' }}{{ $n($m->balance) }}{{ $m->user ?: '—' }}
@if($truncated)

Se muestran los primeros 1000 movimientos: acorta las fechas para ver el resto.

@endif @endif
@endif @endif
@endsection