107 lines
4.5 KiB
PHP
107 lines
4.5 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Employee;
|
|
use App\Models\StockMovement;
|
|
use App\Models\StockVariant;
|
|
use App\Models\UniformIssue;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class IssueController extends Controller
|
|
{
|
|
public function index()
|
|
{
|
|
return view('issues.index', [
|
|
'selectedEmployee' => old('employee_id') ? Employee::find(old('employee_id')) : null,
|
|
'selectedVariant' => old('stock_variant_id') ? StockVariant::with('item')->find(old('stock_variant_id')) : null,
|
|
'issues' => UniformIssue::with(['employee', 'variant.item'])->latest('issued_at')->paginate(15),
|
|
]);
|
|
}
|
|
|
|
public function employeeLookup(Request $request)
|
|
{
|
|
$term = trim((string) $request->query('q', ''));
|
|
|
|
return response()->json(Employee::query()
|
|
->where('status', 'active')
|
|
->when($term, fn ($query) => $query->where(fn ($query) => $query
|
|
->where('name', 'like', "%{$term}%")
|
|
->orWhere('employee_no', 'like', "%{$term}%")
|
|
->orWhere('department', 'like', "%{$term}%")))
|
|
->orderBy('name')->limit(20)->get()
|
|
->map(fn ($employee) => [
|
|
'id' => $employee->id,
|
|
'title' => $employee->name,
|
|
'subtitle' => "{$employee->employee_no} / {$employee->department}",
|
|
]));
|
|
}
|
|
|
|
public function variantLookup(Request $request)
|
|
{
|
|
$term = trim((string) $request->query('q', ''));
|
|
|
|
return response()->json(StockVariant::query()->with('item')
|
|
->where('quantity', '>', 0)
|
|
->when($term, fn ($query) => $query->where(fn ($query) => $query
|
|
->where('size', 'like', "%{$term}%")
|
|
->orWhereHas('item', fn ($item) => $item->where('name', 'like', "%{$term}%")->orWhere('code', 'like', "%{$term}%"))))
|
|
->join('uniform_items', 'uniform_items.id', '=', 'stock_variants.uniform_item_id')
|
|
->orderBy('uniform_items.name')->orderBy('stock_variants.size')
|
|
->select('stock_variants.*')->limit(20)->get()
|
|
->map(fn ($variant) => [
|
|
'id' => $variant->id,
|
|
'title' => "{$variant->item->name} / Size {$variant->size}",
|
|
'subtitle' => "{$variant->item->code} / {$variant->quantity} available",
|
|
]));
|
|
}
|
|
|
|
public function store(Request $request)
|
|
{
|
|
$data = $request->validate([
|
|
'employee_id' => ['required', 'exists:employees,id'],
|
|
'stock_variant_id' => ['required', 'exists:stock_variants,id'],
|
|
'quantity' => ['required', 'integer', 'min:1'],
|
|
'issued_at' => ['required', 'date'],
|
|
'notes' => ['nullable', 'string', 'max:500'],
|
|
]);
|
|
|
|
DB::transaction(function () use ($data) {
|
|
$employee = Employee::lockForUpdate()->findOrFail($data['employee_id']);
|
|
if ($employee->status !== 'active') {
|
|
throw ValidationException::withMessages(['employee_id' => 'Uniforms can only be issued to active employees.']);
|
|
}
|
|
|
|
$variant = StockVariant::lockForUpdate()->findOrFail($data['stock_variant_id']);
|
|
if ($variant->quantity < $data['quantity']) {
|
|
throw ValidationException::withMessages(['quantity' => "Only {$variant->quantity} item(s) are available in this size."]);
|
|
}
|
|
|
|
$issue = UniformIssue::create($data);
|
|
$returnedUsed = min($variant->returned_quantity, $data['quantity']);
|
|
$newUsed = $data['quantity'] - $returnedUsed;
|
|
$variant->decrement('quantity', $data['quantity']);
|
|
if ($returnedUsed) {
|
|
$variant->decrement('returned_quantity', $returnedUsed);
|
|
}
|
|
if ($newUsed) {
|
|
$variant->decrement('new_quantity', $newUsed);
|
|
}
|
|
StockMovement::create([
|
|
'stock_variant_id' => $variant->id,
|
|
'employee_id' => $employee->id,
|
|
'uniform_issue_id' => $issue->id,
|
|
'type' => 'issue',
|
|
'quantity_change' => -$data['quantity'],
|
|
'stock_category' => $returnedUsed && $newUsed ? 'mixed' : ($returnedUsed ? 'returned' : 'new'),
|
|
'unit_value' => $variant->unit_value,
|
|
'notes' => "Issued to {$employee->name}",
|
|
]);
|
|
});
|
|
|
|
return back()->with('success', 'Uniform issued and stock adjusted.');
|
|
}
|
|
}
|