From 1872cc04c287cf7452e9a243bef3993a18c8349f Mon Sep 17 00:00:00 2001 From: adeliaala Date: Wed, 4 Jun 2025 20:17:00 +0700 Subject: [PATCH] done hosting --- .../Resources/views/create.blade.php | 2 + .../Controllers/StockTransferController.php | 107 +++--- .../Resources/views/create.blade.php | 319 +++++------------- .../StockTransfer/CreateStockTransfer.php | 78 +++++ app/Livewire/StockTransfer/ProductTable.php | 79 +++++ app/Models/ProductBatch.php | 27 ++ .../create-stock-transfer.blade.php | 3 + .../stock-transfer/product-table.blade.php | 106 ++++++ 8 files changed, 429 insertions(+), 292 deletions(-) create mode 100644 app/Livewire/StockTransfer/CreateStockTransfer.php create mode 100644 app/Livewire/StockTransfer/ProductTable.php create mode 100644 resources/views/livewire/stock-transfer/create-stock-transfer.blade.php create mode 100644 resources/views/livewire/stock-transfer/product-table.blade.php diff --git a/Modules/Adjustment/Resources/views/create.blade.php b/Modules/Adjustment/Resources/views/create.blade.php index d692df8a..1cc4f827 100644 --- a/Modules/Adjustment/Resources/views/create.blade.php +++ b/Modules/Adjustment/Resources/views/create.blade.php @@ -11,7 +11,9 @@ @endsection @section('content') +
+

Sesuaikan Stok

diff --git a/Modules/StockTransfer/Http/Controllers/StockTransferController.php b/Modules/StockTransfer/Http/Controllers/StockTransferController.php index 434c2b3d..cdc4421b 100644 --- a/Modules/StockTransfer/Http/Controllers/StockTransferController.php +++ b/Modules/StockTransfer/Http/Controllers/StockTransferController.php @@ -7,10 +7,9 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Modules\StockTransfer\Entities\StockTransfer; use Modules\StockTransfer\Entities\StockTransferItem; -use Modules\StockTransfer\Http\Requests\StockTransferRequest; -use App\Models\ProductBatch; use Modules\Branch\Entities\Branch; use Modules\Product\Entities\Product; +use App\Models\ProductBatch; class StockTransferController extends Controller { @@ -27,72 +26,80 @@ class StockTransferController extends Controller { $branches = Branch::all(); $products = Product::all(); - return view('stocktransfer::create', compact('branches', 'products')); } - public function store(StockTransferRequest $request) + public function store(Request $request) { + $data = $request->validate([ + 'from_branch_id' => 'required|exists:branches,id', + 'to_branch_id' => 'required|exists:branches,id|different:from_branch_id', + 'items' => 'required|array|min:1', + 'items.*.product_id' => 'required|exists:products,id', + 'items.*.product_batch_id' => 'required|exists:product_batches,id', + 'items.*.quantity' => 'required|integer|min:1', + ]); + try { DB::beginTransaction(); - // Create stock transfer record $transfer = StockTransfer::create([ - 'source_branch_id' => $request->source_branch_id, - 'destination_branch_id' => $request->destination_branch_id, - 'transfer_date' => $request->transfer_date, - 'note' => $request->note, - 'status' => 'pending', + 'source_branch_id' => $data['from_branch_id'], + 'destination_branch_id' => $data['to_branch_id'], + 'transfer_date' => now(), + 'status' => 'completed', 'created_by' => auth()->id(), ]); - // Process each transfer item - foreach ($request->items as $item) { - // Get source batch - $sourceBatch = ProductBatch::findOrFail($item['product_batch_id']); + foreach ($data['items'] as $item) { + $sourceBatch = ProductBatch::where('id', $item['product_batch_id']) + ->where('branch_id', $data['from_branch_id']) + ->lockForUpdate() + ->firstOrFail(); - // Step 1: Deduct quantity from source branch - $sourceBatch->quantity -= $item['quantity']; + if ($sourceBatch->qty < $item['quantity']) { + throw new \Exception("Stok tidak mencukupi untuk batch {$sourceBatch->batch_code}"); + } + + // Kurangi stok dari batch asal + $sourceBatch->qty -= $item['quantity']; $sourceBatch->save(); - // Step 2: Create new batch in destination branch - ProductBatch::create([ - 'product_id' => $sourceBatch->product_id, - 'branch_id' => $request->destination_branch_id, - 'batch_code' => $sourceBatch->batch_code, - 'quantity' => $item['quantity'], - 'purchase_price' => $sourceBatch->purchase_price, - 'expired_date' => $sourceBatch->expired_date, - 'created_by' => auth()->id(), - 'updated_by' => auth()->id() - ]); + // Tambahkan ke batch cabang tujuan (dengan batch_code sama) + $targetBatch = ProductBatch::firstOrCreate( + [ + 'product_id' => $item['product_id'], + 'branch_id' => $data['to_branch_id'], + 'batch_code' => $sourceBatch->batch_code, + ], + [ + 'qty' => 0, + 'unit_price' => $sourceBatch->unit_price, + 'price' => $sourceBatch->price, + 'exp_date' => $sourceBatch->exp_date, + 'created_by' => auth()->id(), + ] + ); - // Step 3: Create transfer item record - StockTransferItem::create([ - 'stock_transfer_id' => $transfer->id, - 'product_id' => $sourceBatch->product_id, - 'product_batch_id' => $sourceBatch->id, - 'quantity' => $item['quantity'] + $targetBatch->qty += $item['quantity']; + $targetBatch->updated_by = auth()->id(); + $targetBatch->save(); + + // Simpan ke tabel stock_transfer_items + $transfer->items()->create([ + 'product_id' => $item['product_id'], + 'batch_id' => $sourceBatch->id, + 'qty' => $item['quantity'], + 'unit_price' => $sourceBatch->unit_price, + 'price' => $sourceBatch->price, ]); } - // Update transfer status to completed - $transfer->update([ - 'status' => 'completed', - 'updated_by' => auth()->id() - ]); - DB::commit(); - - return redirect() - ->route('stock-transfers.show', $transfer) - ->with('success', 'Stock transfer created successfully.'); + return redirect()->route('stock-transfers.index')->with('success', 'Transfer stok berhasil.'); } catch (\Exception $e) { DB::rollBack(); - return redirect() - ->back() - ->with('error', 'Error creating stock transfer: ' . $e->getMessage()) - ->withInput(); + return back()->with('error', 'Gagal transfer stok: ' . $e->getMessage())->withInput(); } } @@ -106,9 +113,9 @@ class StockTransferController extends Controller { $batches = ProductBatch::where('product_id', $productId) ->where('branch_id', $branchId) - ->where('quantity', '>', 0) - ->get(['id', 'batch_code', 'quantity']); + ->where('qty', '>', 0) + ->get(['id', 'batch_code', 'qty']); return response()->json($batches); } -} \ No newline at end of file +} diff --git a/Modules/StockTransfer/Resources/views/create.blade.php b/Modules/StockTransfer/Resources/views/create.blade.php index 29a94f7f..e0027b2c 100644 --- a/Modules/StockTransfer/Resources/views/create.blade.php +++ b/Modules/StockTransfer/Resources/views/create.blade.php @@ -1,252 +1,87 @@ @extends('layouts.app') -@section('title', 'Create Stock Transfer') - @section('content') -
-
-
-
-
-

Create Stock Transfer

+
+

Tambah Transfer Stok

+ + @php + use Modules\Branch\Entities\Branch; + $branch = Branch::find(session('branch_id')); + @endphp + +
+ @csrf +
+
+ +
+
+
+
+ +
+
+ +
-
- - @csrf -
-
-
- - -
-
-
-
- - -
-
-
- -
-
-
- - -
-
-
-
- - -
-
-
- -
-
-
- - - - - - - - - - - - - - - - - - - -
Product *Batch *Available QuantityTransfer Quantity *Action
- - - - - 0 - - - - -
-
- -
-
- -
-
- - Cancel -
-
- +
+
+
+ + +
+
+
+
+ +
-
+ + {{-- Komponen pencarian produk --}} + + +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+ +
+
+
@endsection -@push('scripts') - -@endpush \ No newline at end of file diff --git a/app/Livewire/StockTransfer/CreateStockTransfer.php b/app/Livewire/StockTransfer/CreateStockTransfer.php new file mode 100644 index 00000000..51f18e89 --- /dev/null +++ b/app/Livewire/StockTransfer/CreateStockTransfer.php @@ -0,0 +1,78 @@ + 1, 'qty' => 5], ...] + + public function render() + { + return view('StockTransfer::create'); + } + + public function handleSubmit() + { + $this->validate([ + 'source_branch_id' => 'required|exists:branches,id', + 'destination_branch_id' => 'required|exists:branches,id|different:source_branch_id', + 'transfer_date' => 'required|date', + 'products' => 'required|array|min:1', + 'products.*.batch_id' => 'required|exists:product_batches,id', + 'products.*.qty' => 'required|integer|min:1', + ]); + + try { + DB::beginTransaction(); + + // Buat transfer baru + $transfer = StockTransfer::create([ + 'reference_no' => 'TRF-' . strtoupper(Str::random(6)), + 'source_branch_id' => $this->source_branch_id, + 'destination_branch_id' => $this->destination_branch_id, + 'transfer_date' => $this->transfer_date, + 'note' => $this->note, + 'status' => $this->status, + 'created_by' => auth()->id(), + 'updated_by' => auth()->id(), + ]); + + foreach ($this->products as $product) { + $newBatch = ProductBatch::transferToBranch( + $product['batch_id'], + $this->destination_branch_id, + $product['qty'], + auth()->id() + ); + + // Simpan item transfer + StockTransferItem::create([ + 'stock_transfer_id' => $transfer->id, + 'product_id' => $newBatch->product_id, + 'product_batch_id' => $newBatch->id, + 'quantity' => $product['qty'], + ]); + } + + DB::commit(); + + session()->flash('success', 'Transfer stok berhasil disimpan.'); + return redirect()->route('stock-transfers.index'); + } catch (\Exception $e) { + DB::rollBack(); + session()->flash('error', 'Gagal menyimpan transfer: ' . $e->getMessage()); + } + } +} diff --git a/app/Livewire/StockTransfer/ProductTable.php b/app/Livewire/StockTransfer/ProductTable.php new file mode 100644 index 00000000..999cc581 --- /dev/null +++ b/app/Livewire/StockTransfer/ProductTable.php @@ -0,0 +1,79 @@ +fromBranchId = $fromBranchId; + $this->products = []; + } + + public function render() + { + return view('livewire.stock-transfer.product-table'); + } + + public function productSelected($product) { + $branch_id = session('branch_id'); // pastikan session branch_id sudah ada + $productModel = Product::with(['batches' => function($q) use ($branch_id) { + $q->where('branch_id', $branch_id)->where('qty', '>', 0); + }])->find($product['id']); + + if (!$productModel) { + return session()->flash('message', 'Produk tidak ditemukan!'); + } + + // Cek duplikasi + foreach ($this->products as $p) { + if ($p['id'] == $productModel->id) { + return session()->flash('message', 'Already exists in the product list!'); + } + } + + $productArr = $productModel->toArray(); + $productArr['batches'] = $productModel->batches->toArray(); + + $this->products[] = $productArr; + } + + public function updateBatch($key, $batchId) + { + $product = $this->products[$key]['product'] ?? $this->products[$key]; + + $batch = ProductBatch::where('id', $batchId) + ->where('branch_id', $this->fromBranchId) + ->first(); + if ($batch) { + $this->products[$key]['selected_batch_id'] = $batch->id; + $this->products[$key]['max_qty'] = $batch->qty; + // Reset quantity jika melebihi stok + if ($this->products[$key]['quantity'] > $batch->qty) { + $this->products[$key]['quantity'] = $batch->qty; + } + } + } + + public function updateQuantity($key, $qty) + { + $max = $this->products[$key]['max_qty'] ?? 0; + $this->products[$key]['quantity'] = min(max(1, (int)$qty), $max); + } + + public function removeProduct($key) + { + unset($this->products[$key]); + $this->products = array_values($this->products); // reindex + } +} diff --git a/app/Models/ProductBatch.php b/app/Models/ProductBatch.php index 5aa9215c..64607ef6 100644 --- a/app/Models/ProductBatch.php +++ b/app/Models/ProductBatch.php @@ -148,4 +148,31 @@ class ProductBatch extends Model return round($totalPrice / $qty, 2); } + + public static function transferToBranch(int $sourceBatchId, int $destinationBranchId, int $qty, int $userId): ProductBatch + { + $sourceBatch = self::lockForUpdate()->findOrFail($sourceBatchId); + + if ($sourceBatch->quantity < $qty) { + throw new \Exception("Stok tidak cukup pada batch {$sourceBatch->batch_code}"); + } + + // Kurangi stok dari batch asal + $sourceBatch->quantity -= $qty; + $sourceBatch->save(); + + // Buat batch baru di cabang tujuan + return self::create([ + 'product_id' => $sourceBatch->product_id, + 'batch_code' => $sourceBatch->batch_code, + 'unit_price' => $sourceBatch->unit_price, + 'price' => $sourceBatch->price, + 'expired_date' => $sourceBatch->expired_date, + 'purchase_id' => $sourceBatch->purchase_id, + 'branch_id' => $destinationBranchId, + 'quantity' => $qty, + 'created_by' => $userId, + 'updated_by' => $userId, + ]); + } } diff --git a/resources/views/livewire/stock-transfer/create-stock-transfer.blade.php b/resources/views/livewire/stock-transfer/create-stock-transfer.blade.php new file mode 100644 index 00000000..fd5ed6b9 --- /dev/null +++ b/resources/views/livewire/stock-transfer/create-stock-transfer.blade.php @@ -0,0 +1,3 @@ +
+ {{-- Nothing in the world is as soft and yielding as water. --}} +
diff --git a/resources/views/livewire/stock-transfer/product-table.blade.php b/resources/views/livewire/stock-transfer/product-table.blade.php new file mode 100644 index 00000000..24306030 --- /dev/null +++ b/resources/views/livewire/stock-transfer/product-table.blade.php @@ -0,0 +1,106 @@ +
+ {{-- Alert Message --}} + @if (session()->has('message')) + + @endif + + {{-- Loading Overlay --}} +
+
+ Loading... +
+
+ + {{-- Product Table --}} +
+ + + + + + + + + + + + + + @if (!empty($products)) + @foreach ($products as $key => $product) + @php + $productData = $product['product'] ?? $product; + $batches = $product['batches'] ?? []; + $selectedBatchId = $product['selected_batch_id'] ?? null; + $selectedBatch = collect($batches)->firstWhere('id', $selectedBatchId) ?? ($batches[0] ?? null); + @endphp + + + + {{-- Nama Produk --}} + + + {{-- Kode Produk --}} + + + {{-- Pilih Batch --}} + + + {{-- Stok Batch --}} + + + {{-- Input Jumlah Transfer --}} + + + {{-- Hidden Product ID --}} + + + {{-- Aksi --}} + + + @endforeach + @else + + + + @endif + +
#Nama ProdukKodeBatch (Cabang Pengirim)Stok BatchJumlah TransferAksi
{{ $key + 1 }}{{ $productData['product_name'] }}{{ $productData['product_code'] }} + + + + {{ $selectedBatch['qty'] ?? '0' }} {{ $productData['product_unit'] ?? '' }} + + + + + +
+ Silakan cari & pilih produk! +
+
+
+