UniformFlow/app/Http/Controllers/HandoverController.php

98 lines
4.7 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Employee;
use App\Models\Handover;
use App\Models\StockMovement;
use App\Models\UniformIssue;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
class HandoverController extends Controller
{
public function index()
{
return view('handovers.index', [
'employees' => Employee::where('status', 'active')->withCount(['issues as outstanding_issues_count' => fn ($query) => $query->where('status', 'issued')])->orderBy('name')->get(),
'handovers' => Handover::with(['employee', 'items.issue.variant.item'])->latest('handover_date')->get(),
]);
}
public function create(Employee $employee)
{
$employee->load(['issues' => fn ($q) => $q->where('status', 'issued')->with('variant.item')]);
abort_if($employee->status !== 'active', 422, 'This employee has already left.');
return view('handovers.create', compact('employee'));
}
public function store(Request $request, Employee $employee)
{
$data = $request->validate([
'handover_date' => ['required', 'date'],
'notes' => ['nullable', 'string', 'max:1000'],
'items' => ['nullable', 'array'],
'items.*.issue_id' => ['required', 'integer', 'distinct', 'exists:uniform_issues,id'],
'items.*.condition' => ['required', Rule::in(['reusable', 'damaged', 'not_returned'])],
]);
DB::transaction(function () use ($data, $employee) {
$lockedEmployee = Employee::lockForUpdate()->findOrFail($employee->id);
if ($lockedEmployee->status !== 'active') {
throw ValidationException::withMessages(['employee' => 'This employee has already completed handover.']);
}
$openIssues = UniformIssue::where('employee_id', $employee->id)->where('status', 'issued')->lockForUpdate()->get()->keyBy('id');
$submittedItems = $data['items'] ?? [];
if ($openIssues->count() !== count($submittedItems)) {
throw ValidationException::withMessages(['items' => 'Every outstanding uniform must be accounted for before handover is completed.']);
}
$handover = Handover::create(['employee_id' => $employee->id, 'handover_date' => $data['handover_date'], 'notes' => $data['notes'] ?? null]);
foreach ($submittedItems as $itemData) {
$issue = $openIssues->get($itemData['issue_id']);
if (! $issue) {
throw ValidationException::withMessages(['items' => 'An invalid issue was submitted.']);
}
$quantity = $issue->outstanding_quantity;
$handover->items()->create(['uniform_issue_id' => $issue->id, 'quantity' => $quantity, 'condition' => $itemData['condition']]);
$variant = $issue->variant()->lockForUpdate()->first();
if ($itemData['condition'] === 'reusable') {
$variant->increment('quantity', $quantity);
$variant->increment('returned_quantity', $quantity);
}
StockMovement::create([
'stock_variant_id' => $issue->stock_variant_id,
'employee_id' => $employee->id,
'uniform_issue_id' => $issue->id,
'type' => match ($itemData['condition']) {
'reusable' => 'handover_reusable',
'damaged' => 'handover_damaged',
default => 'handover_not_returned',
},
'quantity_change' => $itemData['condition'] === 'reusable' ? $quantity : 0,
'stock_category' => $itemData['condition'] === 'reusable' ? 'returned' : null,
'unit_value' => $variant->unit_value,
'notes' => match ($itemData['condition']) {
'reusable' => 'Reusable return during employee exit handover',
'damaged' => "{$quantity} damaged unit(s) recorded during employee exit handover; stock unchanged",
default => "{$quantity} unit(s) not returned during employee exit handover; stock unchanged",
},
]);
$issue->update(['returned_quantity' => $issue->quantity, 'status' => $itemData['condition'] === 'not_returned' ? 'not_returned' : 'returned']);
}
$lockedEmployee->update(['status' => 'left', 'left_at' => $data['handover_date']]);
});
return redirect()->route('handovers.index')->with('success', 'Handover completed and employee marked as left.');
}
}