@extends('layouts.dashboard') @section('content') @php /** * Vendor → Inventory: countable stock with a movement ledger. * @var array $items stock rows (+ linked_count) * @var array $dishes restaurant dishes (id, name, stock_item_id) for the link picker * @var array|null $historyItem stock row whose ledger is open (?item=) * @var array $movements ledger rows for $historyItem (newest first) */ $fmtQty = static fn ($v): string => (rtrim(rtrim(number_format((float) $v, 2, '.', ''), '0'), '.') ?: '0'); $reasonLabels = ['sale' => t('vendor.inventory.reason_sale'), 'restock' => t('vendor.inventory.reason_restock'), 'waste' => t('vendor.inventory.reason_waste'), 'adjust' => t('vendor.inventory.reason_adjust'), 'void' => t('vendor.inventory.reason_void')]; $reasonPills = ['sale' => 'qm-pill-blue', 'restock' => 'qm-pill-green', 'waste' => 'qm-pill-red', 'adjust' => 'qm-pill-grey', 'void' => 'qm-pill-amber']; $kpis = $kpis ?? ['items' => 0, 'out' => 0, 'low' => 0, 'value' => 0.0, 'valued' => 0, 'waste_qty' => 0.0, 'waste_events' => 0]; $waste = $waste ?? []; $countSheet = $countSheet ?? []; // The stock list takes no filters beyond the restaurant scope, so the export needs no query string. $qs = ''; @endphp @php /* .qm-d2: the dashboard density standard (app.css §35) */ @endphp
@php // Page primary action → top-bar subheader slot (leaves the table card header for the // title + any table-scoped utility). ob_start captures the button HTML for the layout slot. ob_start(); @endphp @php \App\Support\View::slot('subhead', ob_get_clean()); @endphp @php /* Stock answers one question — is anything about to run out — and the list alone could never answer it. These four say it before a single row is read. */ @endphp @php $kpiSparks = $kpiSparks ?? []; @endphp
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'blue', 'icon' => 'fa-boxes', 'label' => t_raw('vendor.inventory.kpi_items'), 'value' => number_format((int) $kpis['items']), 'spark' => $kpiSparks['items'] ?? [], 'meta' => '' . t('vendor.inventory.kpi_meta_items') . '']) @partial('kpi-card', ['variant' => 'panel', 'accent' => 'rose', 'icon' => 'fa-exclamation-circle', 'label' => t_raw('vendor.inventory.kpi_out'), 'value' => number_format((int) $kpis['out']), 'spark' => $kpiSparks['out'] ?? [], 'meta' => '' . t('vendor.inventory.kpi_meta_out') . '']) @partial('kpi-card', ['variant' => 'panel', 'accent' => 'amber', 'icon' => 'fa-exclamation-triangle', 'label' => t_raw('vendor.inventory.kpi_low'), 'value' => number_format((int) $kpis['low']), 'spark' => $kpiSparks['low'] ?? [], 'meta' => '' . t('vendor.inventory.kpi_meta_low') . '']) @php /* Stock value only means something where a unit cost has been entered, so the caption always says how much of the shelf the figure actually covers. */ @endphp @partial('kpi-card', ['variant' => 'panel', 'accent' => 'violet', 'icon' => 'fa-coins', 'label' => t_raw('vendor.inventory.kpi_value'), 'value' => $kpis['valued'] > 0 ? money($kpis['value']) : "—", 'spark' => $kpis['valued'] > 0 ? ($kpiSparks['value'] ?? []) : [], 'meta' => '' . ($kpis['valued'] > 0 ? t('vendor.inventory.kpi_meta_value', [':valued' => number_format((int) $kpis['valued']), ':total' => number_format((int) $kpis['items'])]) : t('vendor.inventory.kpi_meta_value_none')) . '']) @partial('kpi-card', ['variant' => 'panel', 'accent' => 'orange', 'icon' => 'fa-trash-alt', 'label' => t_raw('vendor.inventory.kpi_waste'), 'value' => number_format((int) $kpis['waste_events']), 'spark' => $kpiSparks['waste'] ?? [], 'meta' => '' . t('vendor.inventory.kpi_meta_waste') . ''])

{{ t_raw('vendor.inventory.stock_heading') }}

{{ t_raw('vendor.inventory.intro_sub') }}

@if ($countSheet) @endif {{ t_raw('common.export_csv') }}
@if ($items) @foreach ($items as $it) @php $onHand = (float) $it['on_hand']; $reserved = (float) ($it['reserved'] ?? 0); $physical = $onHand + $reserved; $low = (float) $it['low_threshold']; $state = $onHand <= 0 ? 'out' : (($low > 0 && $onHand <= $low) ? 'low' : 'ok'); $pill = ['out' => 'qm-pill-red', 'low' => 'qm-pill-amber', 'ok' => 'qm-pill-green'][$state]; $label = ['out' => t('vendor.inventory.state_out'), 'low' => t('vendor.inventory.state_low'), 'ok' => t('vendor.inventory.state_in_stock')][$state]; @endphp @php /* What this item actually consumes on a trading day, and therefore how long the shelf lasts. Cover is the number that decides whether an order goes in today; on-hand alone never could. */ @endphp @php $perDay = (float) ($it['burn_per_day'] ?? 0); $cover = $it['days_cover'] ?? null; @endphp @endforeach @else @partial('table-empty', ['colspan' => 9, 'icon' => 'fa-boxes', 'msg' => t('vendor.inventory.empty_items')]) @endif
{{ t_raw('vendor.inventory.col_item') }}{{ t_raw('vendor.inventory.col_on_hand') }}{{ t_raw('vendor.inventory.col_reserved') }}{{ t_raw('vendor.inventory.col_available') }}{{ t_raw('vendor.inventory.col_burn') }}{{ t_raw('vendor.inventory.col_low_at') }}{{ t_raw('vendor.inventory.col_tracked') }}{{ t_raw('vendor.inventory.col_dishes') }}{{ t_raw('common.actions') }}
{{ $it['name'] }}
{{ $it['unit'] }}
{!! $fmtQty($physical) !!} {{ $it['unit'] }} {!! $reserved > 0 ? '' . e($fmtQty($reserved)) . ' ' . e($it['unit']) . '' : dash() !!} {!! $label !!} {!! $fmtQty($it['on_hand']) !!} {{ $it['unit'] }} @if ($perDay <= 0) {!! dash() !!}
{{ t_raw('vendor.inventory.burn_no_movement') }}
@else @php $cTone = $cover === null ? 'grey' : ($cover < 2 ? 'red' : ($cover < 5 ? 'amber' : 'green')); @endphp {{ t_raw('vendor.inventory.cover_days', [':n' => $fmtQty($cover)]) }}
{{ t_raw('vendor.inventory.burn_per_day', [':qty' => $fmtQty($perDay) . ' ' . (string) $it['unit']]) }}
@if ((float) ($it['burn_wasted'] ?? 0) > 0)
{{ t_raw('vendor.inventory.burn_wasted', [':qty' => $fmtQty($it['burn_wasted']) . ' ' . (string) $it['unit']]) }}
@endif @endif
{!! $low > 0 ? e($fmtQty($low)) . ' ' . e($it['unit']) : dash() !!}
@csrf
{{ (int) $it['linked_count'] }} {{ t_raw('vendor.inventory.linked_count_suffix') }} @partial('row-actions', ['actions' => [ [ 'label' => t_raw('vendor.inventory.aria_record_movement') . ' ' . $it['name'], 'icon' => 'fa-exchange-alt', 'primary' => true, 'attrs' => [ 'data-bs-toggle' => 'modal', 'data-bs-target' => '#invMoveModal', 'data-inv-move' => '', 'data-id' => (int) $it['id'], 'data-name' => $it['name'], 'data-unit' => $it['unit'], 'data-onhand' => $fmtQty($it['on_hand']), ], ], [ 'label' => t_raw('vendor.inventory.aria_movement_history') . ' ' . $it['name'], 'icon' => 'fa-history', 'primary' => true, 'href' => '/vendor/inventory?item=' . (int) $it['id'] . '#inv-history', ], [ 'label' => t_raw('common.edit'), 'icon' => 'fa-pen', 'attrs' => [ 'data-bs-toggle' => 'modal', 'data-bs-target' => '#invModal', 'data-inv-edit' => '', 'data-id' => (int) $it['id'], 'data-name' => $it['name'], 'data-unit' => $it['unit'], 'data-low' => $fmtQty($low), 'data-cost' => $it['unit_cost'] !== null ? $fmtQty($it['unit_cost']) : '', 'data-track' => (int) $it['track'], ], ], [ 'label' => t_raw('common.delete'), 'icon' => 'fa-trash-alt', 'danger' => true, 'form' => [ 'action' => '/vendor/inventory/delete', 'fields' => ['id' => (int) $it['id']], 'confirm' => t_raw('vendor.inventory.confirm_delete_item'), ], ], ]])
@partial('table-toolbar', ['pager' => $pager, 'base' => '/vendor/inventory'])
@php /* Waste log. A write-off total nobody can break down is a total nobody acts on — this names what was thrown away, when, by whom and why. */ @endphp

{{ t_raw('vendor.inventory.waste_title') }}

{{ t_raw('vendor.inventory.waste_sub', [':events' => number_format((int) $kpis['waste_events'])]) }}

@if ($waste) @foreach ($waste as $w) @php $qty = -(float) $w['delta']; @endphp @endforeach @else @partial('table-empty', ['colspan' => 5, 'icon' => 'fa-trash-alt', 'msg' => t('vendor.inventory.empty_waste')]) @endif
{{ t_raw('vendor.inventory.waste_caption') }}
{{ t_raw('vendor.inventory.col_item') }}{{ t_raw('vendor.inventory.col_written_off') }}{{ t_raw('common.when') }}{{ t_raw('vendor.inventory.col_by') }}{{ t_raw('common.note') }}
{{ $w['item_name'] }} {!! $fmtQty($qty) !!} {{ $w['unit'] }} {!! fmt_datetime($w['created_at']) !!} {!! or_dash($w['user_name'] ?? null) !!} {!! or_dash($w['note'] ?? null) !!}
@partial('table-toolbar', ['pager' => $wastePager, 'base' => '/vendor/inventory'])
@if ($countSheet) @endif @if ($historyItem)

{{ t_raw('vendor.inventory.history_title_prefix') }} {{ $historyItem['name'] }}

{{ t_raw('vendor.inventory.history_sub') }} {!! $fmtQty($historyItem['on_hand']) !!} {{ $historyItem['unit'] }}

{{ t_raw('common.close') }}
@if ($movements) @foreach ($movements as $m) @php $delta = (float) $m['delta']; @endphp @endforeach @else @partial('table-empty', ['colspan' => 6, 'icon' => 'fa-history', 'msg' => t('vendor.inventory.empty_movements')]) @endif
{{ t_raw('common.when') }}{{ t_raw('common.type') }}{{ t_raw('vendor.inventory.col_change') }}{{ t_raw('common.order') }}{{ t_raw('vendor.inventory.col_by') }}{{ t_raw('common.note') }}
{!! fmt_datetime($m['created_at']) !!} {!! $reasonLabels[$m['reason']] ?? e(ucfirst((string) $m['reason'])) !!} {{ $delta > 0 ? '+' : '' }}{!! $fmtQty($delta) !!} {{ $historyItem['unit'] }} {!! or_dash($m['order_number'] ?? null) !!} {!! or_dash($m['user_name'] ?? null) !!} {!! or_dash($m['note'] ?? null) !!}
@partial('table-toolbar', ['pager' => $movePager, 'base' => '/vendor/inventory'])
@endif
@php /* /.qm-d2 */ @endphp @endsection