@extends('layouts/base_view') @section('title') {!! $variables['header_routes'] !!} @endsection @section('content')

Stock Opname

@csrf
{{ auth()->user()->fullname }}
@if(auth()->user()->roles->isNotEmpty()) {{ auth()->user()->roles->pluck('name')->join(', ') }} @endif
@if(session('store_selected')) @php $stores = Cache::get('select_stores', []); $selectedStore = collect($stores)->firstWhere('id', session('store_selected')); @endphp
{{ $selectedStore ? $selectedStore->name : 'No Store Selected' }}
@else
No Store Selected
@endif
@if(isset($data->id) && $data->status) {{ $data->status }} @else Belum Disimpan @endif
@if(auth()->user()->canBackdateSO()) Backdate diizinkan @else Tanggal sistem (tidak bisa backdate) kecuali user tertentu @endif

@if($data->id > 0) @endif

Rumus Perhitungan SPB

Item DO (Delivery Order)

Item basah yang dikirim via DO. Ada 2 tipe: HARIAN dan PERIODIK

DO HARIAN:

SPB = (Forecast Besok + Forecast Lusa + TSM Lusa) - LSM Aktual - Open SPB
  • Forecast Besok: Forecast H+1 (1 hari setelah SO)
  • Forecast Lusa: Forecast H+2 (2 hari setelah SO)
  • TSM Lusa: TSM H+2 (target sisa malam di hari lusa)
  • LSM Aktual: Ending Stock dari SO hari ini
  • Open SPB: Total SPB yang belum CLOSED

Contoh DO HARIAN:

SO tanggal Jumat 5 Sept 2025:
• Forecast Besok (Sabtu 6 Sept) = 105.25
• Forecast Lusa (Minggu 7 Sept) = 98.75
• TSM Lusa (Minggu 7 Sept) = 50.25
• LSM Aktual = 150.00
SPB = (105.25 + 98.75 + 50.25) - 150.00 = 104.25 Pakai

DO PERIODIK:

SPB = Total Forecast (Coverage) - LSM Aktual - Open SPB
  • Coverage Logic: Sama seperti PR (H+2 hari kerja, check delivery day)
  • PERBEDAAN: TIDAK skip hari libur saat hitung forecast
  • TIDAK pakai TSM: Hanya forecast saja
  • Delivery Days: Dari Shipment Deliveries (filter by period_id)

Contoh DO PERIODIK:

SO: Selasa, 13 Jan 2025, Delivery days: ["Senin", "Kamis"]
• H+2 hari kerja = Kamis, 15 Jan (delivery day ✓)
• Next delivery = Senin, 19 Jan
• Coverage = Jumat 16 - Senin 19 Jan (4 hari)
• Total Forecast = 200.50 (termasuk libur!)
• LSM Aktual = 50.00, Open SPB = 10.00
SPB = 200.50 - 50.00 - 10.00 = 140.50 Pakai
* DO PERIODIK: Semua hari dihitung (TIDAK skip libur)
Item PR (Purchase Request)

Item kering periodik yang dikirim via PR

SPB = Total Forecast (Coverage Period) - LSM Aktual - Open SPB

Coverage Period Logic:

  • Shipment Date: H+2 HARI KERJA (skip libur)
  • Check Shift: Jika shipment date BUKAN hari shift → Tidak ada SPB
  • Coverage: Dari hari setelah shipment sampai sebelum shipment berikutnya
  • Shifts: Berdasarkan data shifts di Shipment Supplier (contoh: ["Senin","Kamis"])
  • Skip Libur: Hari libur tidak dihitung dalam forecast
* Coverage sampai hari pengiriman berikutnya (inclusive)

Contoh Perhitungan:

Scenario 1 - Tidak ada SPB:
• SO: Senin, 12 Jan 2025
• Shifts: ["Senin", "Kamis"]
• H+2 hari kerja = Rabu, 14 Jan
• Rabu BUKAN hari shift → Tidak ada SPB
Scenario 3 - Skip libur:
• SO: Rabu, 14 Jan 2025
• Libur: Jumat 16, Sabtu 17, Minggu 18 Jan
• Shifts: ["Senin", "Kamis"]
• H+2 hari kerja = Senin, 19 Jan (skip 3 hari libur)
• Senin ADALAH hari shift ✓ → Ada SPB
Scenario 2 - Ada SPB:
• SO: Selasa, 13 Jan 2025
• Shifts: ["Senin", "Kamis"]
• H+2 hari kerja = Kamis, 15 Jan
• Kamis ADALAH hari shift ✓
• Next Shipment = Senin, 19 Jan
• Coverage = Jumat 16 - Senin 19 Jan (4 hari, inclusive)
• Total Forecast (16-19 Jan) = 200.50
• LSM Aktual = 50.00, Open SPB = 10.00
SPB = 200.50 - 50.00 - 10.00 = 140.50 Pakai
Scenario 4 - Next shipment juga libur:
• SO: Kamis, 25 Des 2025
• Shifts: ["Senin", "Kamis"]
• H+2 hari kerja = Senin, 29 Des (skip Sabtu/Minggu)
• Senin ADALAH hari shift ✓
• Next Shipment = Kamis, 1 Jan 2026 (LIBUR!)
• Skip ke next valid shipment = Senin, 5 Jan 2026
• Coverage = Selasa 30 Des - Senin 5 Jan (7 hari)
Forecast: 30 Des, 31 Des, 1 Jan, 2 Jan, 3 Jan, 4 Jan, 5 Jan
• (Coverage INCLUSIVE sampai hari pengiriman berikutnya)
Catatan: Hasil perhitungan dalam UOM Pakai akan dikonversi ke UOM Kirim (jika ada). Konversi: Pakai → Utuh → Kirim. Hasil akhir UOM Kirim akan dibulatkan ke angka bulat terdekat.
@foreach($variables['raw_materials'] as $index => $rm) @php $existingDetail = null; if(isset($data->details)) { $existingDetail = $data->details->where('raw_material_id', $rm->id)->first(); } // Get conversion data $hasConversion = $rm->conversion_factor_utuh_pakai && $rm->conversion_factor_utuh_pakai > 0; $conversionFactor = $hasConversion ? $rm->conversion_factor_utuh_pakai : 1; $uomUtuh = $rm->uom_utuh ?? $rm->uom; $uomPakai = $rm->uom_pakai ?? $rm->uom; // Get store raw material mapping for SPB suggestions $mapping = $variables['mappings']->get($rm->id); $hasMapping = $mapping && $mapping->is_active; $showSPB = $hasMapping && ($mapping->spb_flag ?? false); @endphp {{-- FSTR Sub-lines: Display FSTR components (WIP items) for this RM --}} @php // Get all FSTR components (WIP items) that belong to this RM // Example: DRY0048 (parent) has WIP0029, WIP0030, WIP0031 (components) // Filter only components with input_lsm_flag = true to show in LSM input $fstrComponents = $rm->components() ->where('delete_flag', 0) ->where('input_lsm_flag', true) ->with('component') ->get() ->filter(function($comp) { return $comp->component !== null; // Remove components with null component }); @endphp @foreach($fstrComponents as $fstrComponent) @php $fstr = $fstrComponent->component; // Changed from parent to component // Skip if component is null (filtered out) if (!$fstr) continue; $componentQtyPerUnit = $fstrComponent->qty; // Check if this FSTR is also a FSTR (has components) $isFSTR = $fstr->components()->where('delete_flag', 0)->exists(); // Get existing FSTR qty_pakai from SO detail if exists $existingFstrQty = 0; if ($existingDetail && $existingDetail->fstr_qty_pakai) { $fstrQtyData = is_string($existingDetail->fstr_qty_pakai) ? json_decode($existingDetail->fstr_qty_pakai, true) : $existingDetail->fstr_qty_pakai; $existingFstrQty = $fstrQtyData[$fstr->id] ?? 0; } @endphp @endforeach @endforeach
No Code Description UOM Pakai Current Stock Sisa Utuh Sisa Pakai LSM Aktual DOI Status Stok Tipe Dok Saran Qty Final Qty Adjustments Action
{{ $index + 1 }} @if(!$hasConversion)
@endif
{{ $rm->code }} @if(!$hasConversion)
Tidak ada mapping @endif
{{ $rm->name }} @if(!$hasConversion)
⚠️ Menggunakan rasio 1:1 (tidak akurat!) @endif
{{ $uomPakai }} @if($hasConversion)
1 {{ $uomUtuh }} = {{ $conversionFactor }} {{ $uomPakai }} @endif
- + -
Tidak Diketahui
Belum Pernah
@if($variables['fstr_parents']->has($rm->id))
@endif
@if($showSPB) -
Open SPB: 0.00 @else - @endif
@if($showSPB && $mapping->tipe_dokumen) @if($mapping->tipe_dokumen == 'DO') {{ $mapping->period->code }} / {{ $mapping->tipe_dokumen }} @else {{ $rm->tipe_doi }} / {{ $mapping->tipe_dokumen }} @endif @else - @endif @if($showSPB) - @else - @endif @if($showSPB) @else - @endif @php $mapping = $variables['mappings']->get($rm->id); $hasInvMatrix = $mapping && $mapping->inv_matrix_flag; $hasRestan = $mapping && $mapping->adj_restan_flag; $hasExtras = $mapping && $mapping->adj_extras_flag; $hasRingkas = $mapping && $mapping->adj_ringkas_flag; $hasSelisih = $mapping && $mapping->adj_selisih_flag; $hasAnyAdjustment = $hasRestan || $hasExtras || $hasRingkas || $hasSelisih; @endphp @if($hasInvMatrix && $hasAnyAdjustment)

Adjustments

@elseif($hasInvMatrix && !$hasAnyAdjustment)
No adjustment columns
enabled for this item
@else
Adjustment not available
for this material
@endif
@if($data->id > 0) @else - @endif
FSTR: {{ $fstr->code }} {{ $fstr->name }} Menggunakan {{ number_format($componentQtyPerUnit, 4) }} {{ $rm->uom_pakai ?? $rm->uom }} per unit {{-- FSTR qty_pakai input --}} Input qty FSTR akan otomatis menambah qty_pakai bahan baku di atas
@if(!isset($data->id) || $data->status == 'Draft') @endif @if(isset($data->id) && $data->id > 0 && $data->status == 'Submitted') @endif Kembali
@endsection