Adjustments

This commit is contained in:
adeliaala 2025-05-25 16:26:18 +07:00
parent 9e649ea5b8
commit 64e9140db7
5 changed files with 200 additions and 91 deletions

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('adjustments', function (Blueprint $table) {
$table->unsignedBigInteger('branches_id')->after('id');
$table->foreign('branches_id')->references('id')->on('branches')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('adjustments', function (Blueprint $table) {
$table->dropForeign(['branches_id']);
$table->dropColumn('branches_id');
});
}
};

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('adjusted_products', function (Blueprint $table) {
$table->unsignedBigInteger('product_batch_id')->after('product_id');
$table->foreign('product_batch_id')->references('id')->on('product_batches')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('adjusted_products', function (Blueprint $table) {
$table->dropForeign(['product_batch_id']);
$table->dropColumn('product_batch_id');
});
}
};

View File

@ -36,13 +36,14 @@ class AdjustmentController extends Controller
'reference' => 'required|string|max:255', 'reference' => 'required|string|max:255',
'date' => 'required|date', 'date' => 'required|date',
'note' => 'nullable|string|max:1000', 'note' => 'nullable|string|max:1000',
'product_ids' => 'required', 'product_batch_ids' => 'required',
'quantities' => 'required', 'quantities' => 'required',
'types' => 'required' 'types' => 'required'
]); ]);
DB::transaction(function () use ($request) { DB::transaction(function () use ($request) {
$adjustment = Adjustment::create([ $adjustment = Adjustment::create([
'branches_id' => session('branch_id'),
'date' => $request->date, 'date' => $request->date,
'note' => $request->note 'note' => $request->note
]); ]);
@ -51,22 +52,34 @@ class AdjustmentController extends Controller
AdjustedProduct::create([ AdjustedProduct::create([
'adjustment_id' => $adjustment->id, 'adjustment_id' => $adjustment->id,
'product_id' => $id, 'product_id' => $id,
'product_batch_id' => $request->product_batch_ids[$key],
'quantity' => $request->quantities[$key], 'quantity' => $request->quantities[$key],
'type' => $request->types[$key] 'type' => $request->types[$key]
]); ]);
$product = Product::findOrFail($id); $batchId = $request->product_batch_ids[$key]; // batch yang dipilih oleh user
$batch = DB::table('product_batches')->where('id', $batchId)->lockForUpdate()->first();
if ($batch) {
if ($request->types[$key] == 'add') { if ($request->types[$key] == 'add') {
$product->update([ DB::table('product_batches')->where('id', $batchId)->update([
'product_quantity' => $product->product_quantity + $request->quantities[$key] 'qty' => $batch->qty + $request->quantities[$key]
]); ]);
} elseif ($request->types[$key] == 'sub') { } elseif ($request->types[$key] == 'sub') {
$product->update([ $newQty = $batch->qty - $request->quantities[$key];
'product_quantity' => $product->product_quantity - $request->quantities[$key] if ($newQty < 0) {
throw new \Exception("Stock untuk batch {$batch->batch_code} tidak mencukupi.");
}
DB::table('product_batches')->where('id', $batchId)->update([
'qty' => $newQty
]); ]);
} }
} else {
throw new \Exception("Batch tidak ditemukan.");
} }
}
}); });
toast('Adjustment Created!', 'success'); toast('Adjustment Created!', 'success');
@ -128,6 +141,7 @@ class AdjustmentController extends Controller
AdjustedProduct::create([ AdjustedProduct::create([
'adjustment_id' => $adjustment->id, 'adjustment_id' => $adjustment->id,
'product_id' => $id, 'product_id' => $id,
'product_batch_id' => $request->batch_ids[$key],
'quantity' => $request->quantities[$key], 'quantity' => $request->quantities[$key],
'type' => $request->types[$key] 'type' => $request->types[$key]
]); ]);

View File

@ -30,24 +30,26 @@ class ProductTable extends Component
} }
public function productSelected($product) { public function productSelected($product) {
switch ($this->hasAdjustments) { $branch_id = session('branch_id'); // pastikan session branch_id sudah ada
case true: $productModel = Product::with(['batches' => function($q) use ($branch_id) {
if (in_array($product, array_map(function ($adjustment) { $q->where('branch_id', $branch_id)->where('qty', '>', 0);
return $adjustment['product']; }])->find($product['id']);
}, $this->products))) {
return session()->flash('message', 'Already exists in the product list!'); if (!$productModel) {
} return session()->flash('message', 'Produk tidak ditemukan!');
break;
case false:
if (in_array($product, $this->products)) {
return session()->flash('message', 'Already exists in the product list!');
}
break;
default:
return session()->flash('message', 'Something went wrong!');
} }
array_push($this->products, $product); // 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 removeProduct($key) { public function removeProduct($key) {

View File

@ -1,4 +1,5 @@
<div> <div>
{{-- Alert Message --}}
@if (session()->has('message')) @if (session()->has('message'))
<div class="alert alert-warning alert-dismissible fade show" role="alert"> <div class="alert alert-warning alert-dismissible fade show" role="alert">
<div class="alert-body"> <div class="alert-body">
@ -9,60 +10,86 @@
</div> </div>
</div> </div>
@endif @endif
<div class="table-responsive">
<div wire:loading.flex class="col-12 position-absolute justify-content-center align-items-center" style="top:0;right:0;left:0;bottom:0;background-color: rgba(255,255,255,0.5);z-index: 99;"> {{-- Loading Overlay --}}
<div wire:loading.flex class="col-12 position-absolute justify-content-center align-items-center"
style="top:0;right:0;left:0;bottom:0;background-color: rgba(255,255,255,0.5);z-index: 99;">
<div class="spinner-border text-primary" role="status"> <div class="spinner-border text-primary" role="status">
<span class="sr-only">Loading...</span> <span class="sr-only">Loading...</span>
</div> </div>
</div> </div>
{{-- Product Table --}}
<div class="table-responsive">
<table class="table table-bordered"> <table class="table table-bordered">
<thead> <thead>
<tr class="align-middle"> <tr class="align-middle text-center">
<th class="align-middle">#</th> <th>#</th>
<th class="align-middle">Product Name</th> <th>Product Name</th>
<th class="align-middle">Code</th> <th>Code</th>
<th class="align-middle">Stock</th> <th>Batch</th>
<th class="align-middle">Quantity</th> <th>Stock</th>
<th class="align-middle">Type</th> <th>Quantity</th>
<th class="align-middle">Action</th> <th>Type</th>
<th>Action</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@if (!empty($products)) @if (!empty($products))
@foreach ($products as $key => $product) @foreach ($products as $key => $product)
@php
$productData = $product['product'] ?? $product;
$batches = $product['batches'] ?? [];
@endphp
<tr> <tr>
<td class="align-middle">{{ $key + 1 }}</td> <td class="align-middle text-center">{{ $key + 1 }}</td>
<td class="align-middle">{{ $product['product_name'] ?? $product['product']['product_name'] }}</td>
<td class="align-middle">{{ $product['product_code'] ?? $product['product']['product_code'] }}</td> {{-- Product Name --}}
<td class="align-middle">{{ $productData['product_name'] }}</td>
{{-- Product Code --}}
<td class="align-middle">{{ $productData['product_code'] }}</td>
{{-- Batch Select --}}
<td class="align-middle">
<select name="product_batch_ids[]" class="form-control" required>
<option value="">Pilih Batch</option>
@foreach ($batches as $batch)
<option value="{{ $batch['id'] }}">
{{ $batch['batch_code'] ?? '-' }} | Qty: {{ $batch['qty'] }}
</option>
@endforeach
</select>
</td>
{{-- Stock (dari batch, default ambil batch pertama jika ada) --}}
<td class="align-middle text-center"> <td class="align-middle text-center">
@php
$firstBatch = $batches[0] ?? null;
@endphp
<span class="badge badge-info"> <span class="badge badge-info">
{{ $product['product_quantity'] ?? $product['product']['product_quantity'] }} {{ $product['product_unit'] ?? $product['product']['product_unit'] }} {{ $firstBatch['qty'] ?? '0' }} {{ $productData['product_unit'] }}
</span> </span>
</td> </td>
<input type="hidden" name="product_ids[]" value="{{ $product['product']['id'] ?? $product['id'] }}">
{{-- Quantity Input --}}
<td class="align-middle"> <td class="align-middle">
<input type="number" name="quantities[]" min="1" class="form-control" value="{{ $product['quantity'] ?? 1 }}"> <input type="number" name="quantities[]" min="1" class="form-control"
value="{{ $product['quantity'] ?? 1 }}">
</td> </td>
{{-- Type Select --}}
<td class="align-middle"> <td class="align-middle">
@if(isset($product['type']))
@if($product['type'] == 'add')
<select name="types[]" class="form-control"> <select name="types[]" class="form-control">
<option value="add" selected>(+) Addition</option> <option value="add" {{ ($product['type'] ?? '') === 'add' ? 'selected' : '' }}>(+) Addition</option>
<option value="sub">(-) Subtraction</option> <option value="sub" {{ ($product['type'] ?? '') === 'sub' ? 'selected' : '' }}>(-) Subtraction</option>
</select> </select>
@elseif($product['type'] == 'sub')
<select name="types[]" class="form-control">
<option value="sub" selected>(-) Subtraction</option>
<option value="add">(+) Addition</option>
</select>
@endif
@else
<select name="types[]" class="form-control">
<option value="add">(+) Addition</option>
<option value="sub">(-) Subtraction</option>
</select>
@endif
</td> </td>
{{-- Hidden Product ID --}}
<input type="hidden" name="product_ids[]" value="{{ $productData['id'] }}">
{{-- Action Button --}}
<td class="align-middle text-center"> <td class="align-middle text-center">
<button type="button" class="btn btn-danger" wire:click="removeProduct({{ $key }})"> <button type="button" class="btn btn-danger" wire:click="removeProduct({{ $key }})">
<i class="bi bi-trash"></i> <i class="bi bi-trash"></i>
@ -72,10 +99,8 @@
@endforeach @endforeach
@else @else
<tr> <tr>
<td colspan="7" class="text-center"> <td colspan="8" class="text-center">
<span class="text-danger"> <span class="text-danger">Please search & select products!</span>
Please search & select products!
</span>
</td> </td>
</tr> </tr>
@endif @endif