@extends('templates.adminlte') @section('content-header')

Compras

@endsection @section('main-content') @php $formatCurrency = fn($amount) => '$' . number_format((float) $amount, 2); $formatQuantity = function ($amount) { $amount = (float) $amount; return fmod($amount, 1.0) === 0.0 ? number_format($amount, 0) : number_format($amount, 2); }; $purchaseRows = $purchases ->map(function ($purchase) { $productItems = $purchase->products ->map(function ($product) { $quantity = (float) ($product->pivot->quantity ?? 0); $price = (float) ($product->pivot->price ?? 0); return [ 'name' => $product->name, 'quantity' => $quantity, 'price' => $price, 'total' => $quantity * $price, ]; }) ->values(); $equipmentItems = $purchase->equipments ->map(function ($equipment) { $quantity = (float) ($equipment->pivot->quantity ?? 0); $price = (float) ($equipment->pivot->price ?? 0); return [ 'name' => $equipment->name, 'quantity' => $quantity, 'price' => $price, 'total' => $quantity * $price, ]; }) ->values(); $lineItems = collect($productItems->all())->merge($equipmentItems)->values(); $summary = $lineItems ->take(2) ->map(function ($item) { return $item['name'] . ' x' . ((float) $item['quantity'] === floor((float) $item['quantity']) ? number_format((float) $item['quantity'], 0) : number_format((float) $item['quantity'], 2)); }) ->implode(', '); $subtotal = (float) $lineItems->sum('total'); $discount = (bool) ($purchase->discount_enabled ?? false) ? $subtotal * ((float) ($purchase->discount_rate ?? 0) / 100) : 0; $tax = (bool) ($purchase->iva_enabled ?? false) ? ($subtotal - $discount) * ((float) ($purchase->iva_rate ?? 0) / 100) : 0; return [ 'id' => $purchase->id, 'folio' => 'CMP-' . str_pad((string) $purchase->id, 4, '0', STR_PAD_LEFT), 'date' => \Illuminate\Support\Carbon::parse($purchase->date), 'line_items' => $lineItems, 'product_count' => $lineItems->count(), 'units' => (float) $lineItems->sum('quantity'), 'total' => max($subtotal - $discount + $tax, 0), 'summary' => $summary !== '' ? $summary : 'Sin conceptos registrados', 'remaining_products' => max($lineItems->count() - 2, 0), ]; }) ->values(); $currentMonthStart = now()->copy()->startOfMonth(); $currentMonthEnd = now()->copy()->endOfMonth(); $currentMonthPurchases = $purchaseRows->filter( fn($purchase) => $purchase['date']->between($currentMonthStart, $currentMonthEnd), ); $groupedProducts = $purchaseRows ->flatMap(fn($purchase) => $purchase['line_items']) ->groupBy('name') ->map(function ($items, $name) { return [ 'name' => $name, 'quantity' => (float) $items->sum('quantity'), 'total' => (float) $items->sum('total'), ]; }) ->values(); $distinctProductCount = $groupedProducts->count(); $topProducts = $groupedProducts->sortByDesc('quantity')->take(4)->values(); $latestPurchase = $purchaseRows->first(); $totals = [ 'count' => $purchaseRows->count(), 'month_count' => $currentMonthPurchases->count(), 'month_spend' => (float) $currentMonthPurchases->sum('total'), 'total_spend' => (float) $purchaseRows->sum('total'), 'units' => (float) $purchaseRows->sum('units'), 'avg_ticket' => $purchaseRows->isNotEmpty() ? (float) $purchaseRows->avg('total') : 0, ]; $kpis = [ [ 'label' => 'Compras del mes', 'value' => number_format($totals['month_count']), 'helper' => $formatCurrency($totals['month_spend']) . ' comprometidos este mes', 'icon' => 'shopping_cart', 'tone' => 'secondary', ], [ 'label' => 'Gasto acumulado', 'value' => $formatCurrency($totals['total_spend']), 'helper' => number_format($totals['count']) . ' compras registradas en el historico', 'icon' => 'payments', 'tone' => 'accent', ], [ 'label' => 'Unidades adquiridas', 'value' => $formatQuantity($totals['units']), 'helper' => 'Suma de productos y materiales comprados', 'icon' => 'inventory_2', 'tone' => 'warning', ], [ 'label' => 'Ticket promedio', 'value' => $formatCurrency($totals['avg_ticket']), 'helper' => $latestPurchase ? 'Ultima compra: ' . $latestPurchase['date']->format('d M Y') : 'Sin registros para calcular promedio', 'icon' => 'monitoring', 'tone' => 'secondary', ], ]; $toneClasses = [ 'secondary' => 'border-secondary/30 bg-secondary/10 text-secondary', 'accent' => 'border-accent/30 bg-accent/10 text-accent', 'warning' => 'border-warning/30 bg-warning/10 text-warning', 'error' => 'border-error/30 bg-error/10 text-error', ]; $toneIconClasses = [ 'secondary' => 'text-secondary', 'accent' => 'text-accent', 'warning' => 'text-warning', 'error' => 'text-error', ]; @endphp
Centro de Compras {{ number_format($totals['count']) }} movimientos registrados

Abastecimiento Operativo

Historial, volumen y detalle de compras del almacen.

Consulta el gasto acumulado, los productos adquiridos y el detalle reciente de cada compra sin salir del panel operativo.

calendar_month {{ number_format($totals['month_count']) }} compras durante el mes actual
payments {{ $formatCurrency($totals['total_spend']) }} de gasto historico
inventory_2 {{ $formatQuantity($totals['units']) }} unidades incorporadas

Ticket promedio

{{ $formatCurrency($totals['avg_ticket']) }}

Productos distintos

{{ $distinctProductCount }}

Ultimo registro

{{ $latestPurchase ? $latestPurchase['date']->format('d M') : '--' }}

@foreach ($kpis as $kpi)
{{ $kpi['icon'] }}

{{ $kpi['label'] }}

{{ $kpi['value'] }}

{{ $kpi['helper'] }}

@endforeach

Historial de Compras

Adquisiciones registradas

Monto total, unidades y resumen de productos incorporados por compra.

receipt_long {{ number_format($totals['count']) }} filas visibles
@if ($purchaseRows->isEmpty())
shopping_cart

Sin compras registradas

Registra la primera compra para comenzar a ver gasto, productos adquiridos y resumen operativo.

add_shopping_cart Registrar compra
@else
@foreach ($purchaseRows as $purchase) @endforeach
Folio Fecha Detalle Productos Unidades Monto
{{ $purchase['folio'] }}
{{ $purchase['date']->format('d/m/Y') }}

{{ $purchase['summary'] }}

@if ($purchase['remaining_products'] > 0)

+{{ $purchase['remaining_products'] }} productos adicionales en esta compra

@endif
{{ number_format($purchase['product_count']) }} registros {{ $formatQuantity($purchase['units']) }} {{ $formatCurrency($purchase['total']) }}
@endif
@endsection