@extends('layouts.app') @section('title', __('lang_v1.bin_locations')) @php $zone_labels = [ 'receiving' => 'Receiving', 'pick' => 'Pick Face', 'bulk' => 'Bulk', 'quarantine' => 'Quarantine', 'rejected' => 'Rejected', 'expired' => 'Expired', 'returns' => 'Returns', 'narcotic' => 'Narcotic (Secure)', 'dispatch' => 'Dispatch', ]; $temp_labels = [ 'cold_2_8' => 'Cold 2–8°C', 'cool_8_15' => 'Cool 8–15°C', 'crt_15_25' => 'CRT 15–25°C', 'ambient' => 'Ambient', ]; $status_colors = [ 'available' => 'bg-green', 'occupied' => 'bg-aqua', 'blocked' => 'bg-gray', 'quarantine' => 'bg-yellow', 'damaged' => 'bg-red', ]; @endphp @section('content')

@lang('lang_v1.bin_locations') @lang('lang_v1.bin_locations_desc')

{{-- Validation failures MUST be visible here. The app layout renders session('status') (via toastr) but never includes the $errors partial, so a rejected "Add Storage Location" used to redirect back silently — the modal closed, nothing was created, and the operator got no reason. --}} @include('layouts.partials.error') {{-- Create / update a storage location --}}
{{-- Put-away: assign un-binned batches to bins --}}

@lang('lang_v1.put_away') @lang('lang_v1.put_away_desc')

@if($stockable_bins->isEmpty() && $unbinned->isNotEmpty()) {{-- Nothing can be put away until a BIN-level location exists. Without this the operator sees populated rows, empty dropdowns and a non-empty location master, with no clue why. --}} @endif @forelse($unbinned as $u) @php $row_bins = $compatible_bins[$u->purchase_line_id] ?? []; $row_block = $incompatible_reason[$u->purchase_line_id] ?? null; $qc_badge = !empty($u->recalled_at) ? ['bg-red', __('lang_v1.batch_recalled')] : ($u->qc_status === 'quarantine' ? ['bg-yellow', __('lang_v1.batch_qc_quarantine')] : ($u->qc_status === 'rejected' ? ['bg-red', __('lang_v1.batch_qc_rejected')] : null)); @endphp @empty @endforelse
@lang('business.product') @lang('lang_v1.lot_number') @lang('lang_v1.unbinned') @lang('lang_v1.assign_to_bin')
@lang('lang_v1.no_stockable_bins')
{{ $u->product_name }} @if($u->drug_schedule === 'X')X@endif @if($qc_badge){{ $qc_badge[1] }}@endif @if($u->storage_condition)
{{ $u->storage_condition }}@endif
{{ $u->lot_number ?: '-' }} {{-- Full expiry + manufacturer: two deliveries routinely share a lot number and are only distinguishable by these. --}}
{{ $u->exp_date ? \Carbon\Carbon::parse($u->exp_date)->format('d-m-Y') : '-' }} @if($u->manufacturer_name)
{{ $u->manufacturer_name }}@endif
{{ number_format($u->unbinned_qty, 2) }} @if(empty($row_bins)) {{-- Every bin fails the placement rules for this batch, or none exists. Say why instead of showing an empty dropdown. --}} @if($row_block) {{ __('lang_v1.putaway_blocked_reason', ['reason' => $row_block]) }} @else @lang('lang_v1.no_compatible_bin') @endif @else {!! Form::open(['route' => 'supplier.bin.putaway', 'method' => 'post', 'class' => 'form-inline']) !!} {{-- Only bins this batch may legally occupy — same rules the server enforces in WarehouseBinUtil::putAway. --}} {!! Form::select('bin_id', $row_bins, null, ['class' => 'form-control input-sm', 'style' => 'width:45%', 'placeholder' => __('messages.please_select'), 'required' => true]) !!} {!! Form::close() !!} @endif
@lang('lang_v1.all_stock_binned')
{{-- Bin contents --}}

@lang('lang_v1.bin_contents')

@forelse($bin_contents as $c) @empty @endforelse
@lang('lang_v1.bin') @lang('business.product') @lang('lang_v1.lot_number') @lang('sale.quantity')
{{ $c->bin_code }} {{ $zone_labels[$c->zone] ?? $c->zone }} @if($c->temp_class)
{{ $temp_labels[$c->temp_class] ?? $c->temp_class }}@endif
{{ $c->product_name }} {{ $c->lot_number ?: '-' }}
{{ $c->exp_date ? \Carbon\Carbon::parse($c->exp_date)->format('m/Y') : '' }}
{{ number_format($c->qty, 2) }}
@lang('lang_v1.no_records_found')
{{-- Location master --}}

@lang('lang_v1.location_master')

@forelse($bins as $b) @empty @endforelse
@lang('lang_v1.location_code') @lang('lang_v1.location_type') @lang('lang_v1.zone') @lang('lang_v1.temp_class') @lang('lang_v1.capacity') @lang('lang_v1.status')
{{ $b->code }}@if($b->parent_id)
↳ {{ optional($bins->firstWhere('id',$b->parent_id))->code }}@endif
{{ ucfirst($b->location_type) }} {{ $zone_labels[$b->zone] ?? $b->zone }} {{ $b->temp_class ? ($temp_labels[$b->temp_class] ?? $b->temp_class) : '-' }}@if($b->humidity_controlled)
RH ctrl@endif
{{ $b->capacity ? number_format($b->capacity, 0) : '-' }} {{ ucfirst($b->status) }} {!! Form::open(['route' => ['supplier.bin.status', $b->id], 'method' => 'post', 'class' => 'form-inline']) !!} {!! Form::select('status', collect(\App\WarehouseBin::STATUSES)->mapWithKeys(fn($s)=>[$s=>ucfirst($s)])->toArray(), $b->status, ['class' => 'form-control input-sm', 'onchange' => 'this.form.submit()']) !!} {!! Form::close() !!}
@lang('lang_v1.no_records_found')
@endsection {{-- MUST be @section('javascript'): the layout yields it after layouts.partials.javascripts, where jQuery is loaded. Inside @section('content') this runs before jQuery exists and dies on `$ is not defined`, leaving the modal wiring dead. --}} @section('javascript') @endsection