latest() ->paginate(20); return Inertia::render('MaterialLogistics::Requisitions/Index', [ 'requisitions' => $requisitions, ]); } public function create() { $materials = Material::select('id', 'ulid', 'name', 'unit', 'unit_cost', 'sku', 'category', 'status') ->where('status', 'active') ->orderBy('category') ->orderBy('name') ->get(); $materialGroups = Material::with('components.component:id,ulid,name,sku,category,unit,unit_cost') ->whereIn('type', ['kit', 'assembly']) ->where('status', 'active') ->orderBy('name') ->get() ->map(function ($kit) { return [ 'id' => $kit->id, 'ulid' => $kit->ulid, 'name' => $kit->name, 'description' => $kit->description ?? '', 'status' => $kit->status, 'materials' => $kit->components->map(function ($comp) { return array_merge($comp->component->toArray(), [ 'pivot' => ['quantity' => $comp->quantity] ]); })->toArray() ]; }); return Inertia::render('MaterialLogistics::Requisitions/Form', [ 'materials' => $materials, 'materialGroups' => $materialGroups, ]); } public function store(Request $request) { $validated = $request->validate([ 'notes' => 'nullable|string|max:1000', 'items' => 'required|array|min:1', 'items.*.material_ulid' => 'required|string', 'items.*.quantity' => 'required|numeric|min:0.01', 'items.*.unit_cost' => 'required|numeric|min:0', ]); $docService = new DocumentNumberService(); $mr = DB::transaction(function () use ($validated, $docService) { $mr = MaterialRequisition::create([ 'document_number' => $docService->nextMrNumber(), 'status' => 'draft', 'requested_by' => auth()->id(), 'notes' => $validated['notes'] ?? null, ]); foreach ($validated['items'] as $item) { $material = Material::where('ulid', $item['material_ulid'])->firstOrFail(); $mr->items()->create([ 'material_id' => $material->id, 'quantity' => $item['quantity'], 'unit_cost' => $item['unit_cost'], ]); } return $mr; }); return redirect()->route('requisitions.show', $mr)->with('success', 'Material Requisition created.'); } public function edit(MaterialRequisition $requisition) { if ($requisition->status !== 'draft') { return redirect()->back()->with('error', 'Only draft requisitions can be edited.'); } $requisition->load('items.material:id,ulid,name,unit,unit_cost'); $materials = Material::select('id', 'ulid', 'name', 'sku', 'category', 'unit', 'unit_cost', 'status') ->where('status', 'active') ->orderBy('category') ->orderBy('name') ->get(); $materialGroups = Material::with('components.component:id,ulid,name,sku,category,unit,unit_cost') ->whereIn('type', ['kit', 'assembly']) ->where('status', 'active') ->orderBy('name') ->get() ->map(function ($kit) { return [ 'id' => $kit->id, 'ulid' => $kit->ulid, 'name' => $kit->name, 'description' => $kit->description ?? '', 'status' => $kit->status, 'materials' => $kit->components->map(function ($comp) { return array_merge($comp->component->toArray(), [ 'pivot' => ['quantity' => $comp->quantity] ]); })->toArray() ]; }); return Inertia::render('MaterialLogistics::Requisitions/Form', [ 'requisition' => $requisition, 'materials' => $materials, 'materialGroups' => $materialGroups, ]); } public function update(Request $request, MaterialRequisition $requisition) { if ($requisition->status !== 'draft') { return redirect()->back()->with('error', 'Only draft requisitions can be updated.'); } $validated = $request->validate([ 'notes' => 'nullable|string|max:1000', 'items' => 'required|array|min:1', 'items.*.material_ulid' => 'required|string', 'items.*.quantity' => 'required|numeric|min:0.01', 'items.*.unit_cost' => 'required|numeric|min:0', ]); DB::transaction(function () use ($validated, $requisition) { $requisition->update([ 'notes' => $validated['notes'] ?? null, ]); $requisition->items()->delete(); // remove old items foreach ($validated['items'] as $item) { $material = Material::where('ulid', $item['material_ulid'])->firstOrFail(); $requisition->items()->create([ 'material_id' => $material->id, 'quantity' => $item['quantity'], 'unit_cost' => $item['unit_cost'], ]); } }); return redirect()->route('requisitions.show', $requisition)->with('success', 'Material Requisition updated.'); } public function show(MaterialRequisition $requisition) { $requisition->load([ 'requester:id,ulid,name', 'approver:id,ulid,name', 'items.material' => function ($query) { $query->select('id', 'ulid', 'name', 'unit', 'unit_cost', 'type') ->with('components.component:id,name,unit'); }, 'approvalChains.steps.approver:id,ulid,name', ]); return Inertia::render('MaterialLogistics::Requisitions/Show', [ 'requisition' => $requisition, ]); } public function submitForApproval(MaterialRequisition $requisition) { if ($requisition->status !== 'draft') { return back()->with('error', 'Only draft requisitions can be submitted.'); } // Find personnel with approve_mr permission across the whole system $approverIds = \App\Models\User::permission('approve_mr') ->pluck('id') ->values() ->toArray(); if (empty($approverIds)) { return back()->with('error', 'No approvers found in the system for Material Requests.'); } $approvalService = app(ApprovalService::class); $approvalService->createChain( $requisition, $approverIds, 'material_requisition', auth()->id(), ); $requisition->update(['status' => 'submitted']); return back()->with('success', 'Material Requisition submitted for approval.'); } public function downloadPdf(MaterialRequisition $requisition) { $requisition->load([ 'requester:id,name', 'approver:id,name', 'items.material:id,name,unit', 'approvalChains.steps.approver:id,name', ]); // Global approver roles $approverDetails = []; if ($requisition->approvalChains->isNotEmpty()) { $chain = $requisition->approvalChains->first(); foreach ($chain->steps as $step) { if ($step->status === 'approved' && $step->approver) { $approverDetails[] = [ 'name' => $step->approver->name, 'role' => 'Approver', 'date' => $step->acted_at?->format('M d, Y'), ]; } } } if (empty($approverDetails) && $requisition->status === 'approved' && $requisition->approver) { $approverDetails[] = [ 'name' => $requisition->approver->name, 'role' => 'Approver', 'date' => $requisition->approved_at ? $requisition->approved_at->format('M d, Y') : null, ]; } $pdf = Pdf::loadView('materiallogistics::pdf.material-requisition', [ 'requisition' => $requisition, 'approverDetails' => $approverDetails, ]); return $pdf->download("MR-{$requisition->document_number}.pdf"); } }