user(); $tab = $request->get('tab', 'pending'); $isAdmin = $user->user_type === 'admin' || $user->hasRole(['Super Admin', 'admin', 'Main Contractor Admin']); $isApprovingRole = $isAdmin || $user->hasRole(['Project Manager', 'Contractor Admin']); $query = ApprovalChain::query(); if ($tab === 'history') { if ($isApprovingRole) { $query->whereIn('status', ['approved', 'rejected']); } else { $query->whereHas('steps', function ($q) use ($user) { $q->where('approver_id', $user->id)->where('status', '!=', 'pending'); }); } } else { if ($isApprovingRole) { $query->whereIn('status', ['pending', 'in_review']); } else { $query->whereHas('steps', function ($q) use ($user) { $q->where('approver_id', $user->id)->where('status', 'pending'); })->whereIn('status', ['pending', 'in_review']); } } $chains = $query->with(['steps.approver:id,name', 'initiator:id,name']) ->latest() ->paginate(15) ->withQueryString(); // Cash advances intentionally use their own direct pending -> approved // workflow. They must still appear in the approvals workspace without // creating an ApprovalChain record. $cashAdvanceQuery = CashAdvance::withoutGlobalScopes() ->with(['project:id,name,code', 'requester:id,name,email']); if ($tab === 'history') { $cashAdvanceQuery->whereIn('status', ['approved', 'rejected']); } else { $cashAdvanceQuery->where('status', 'pending'); } $cashAdvanceQuery->whereIn('project_id', Project::query()->select('projects.id')); if (! $isAdmin) { $cashAdvanceQuery->where('requested_by', '!=', $user->id); } return Inertia::render('ApprovalWorkflow::Approvals/Index', [ 'approvals' => $chains, 'tab' => $tab, 'cashAdvances' => $cashAdvanceQuery->latest()->get(), ]); } /** * Show a specific approval chain. */ public function show(ApprovalChain $approvalChain) { $approvalChain->load(['steps.approver:id,name,email', 'initiator:id,name,email', 'approvable']); $breakdownData = null; if ($approvalChain->approvable) { if (method_exists($approvalChain->approvable, 'items')) { $approvalChain->approvable->load('items'); } $totalCost = $approvalChain->approvable->total_cost ?? 0; if ($approvalChain->approvable instanceof \Modules\ProjectManagement\Models\Project) { $project = $approvalChain->approvable; $project->load([ 'materialsEstimates', 'tasks.taskLabors.labor', 'tasks.taskEquipments.equipment', ]); $materialsCost = $project->materialsEstimates->reduce(function ($sum, $est) { return $sum + ($est->estimated_qty * $est->unit_cost); }, 0); $laborCost = 0; foreach ($project->tasks as $task) { foreach ($task->taskLabors as $tl) { $laborCost += ($tl->estimated_hours * ($tl->labor->hourly_rate ?? 0)); } } $equipmentCost = 0; foreach ($project->tasks as $task) { foreach ($task->taskEquipments as $te) { $equipmentCost += ($te->estimated_hours * ($te->equipment->hourly_rate ?? 0)); } } $totalCost = $materialsCost + $laborCost + $equipmentCost; } $breakdownData = [ 'document_number' => $approvalChain->approvable->document_number ?? $approvalChain->approvable->po_number ?? ($approvalChain->approvable instanceof \Modules\FinancialManagement\Models\FinancialInvoice ? $approvalChain->approvable->invoice_number : null) ?? ($approvalChain->approvable instanceof \Modules\ProjectManagement\Models\Project ? $approvalChain->approvable->code : null), 'total_cost' => $approvalChain->approvable instanceof \Modules\FinancialManagement\Models\FinancialInvoice ? $approvalChain->approvable->total_amount : $totalCost, 'retention_amount' => $approvalChain->approvable instanceof \Modules\FinancialManagement\Models\FinancialInvoice ? $approvalChain->approvable->retention_amount : null, 'notes' => $approvalChain->approvable->notes ?? ($approvalChain->approvable instanceof \Modules\FinancialManagement\Models\FinancialInvoice ? $approvalChain->approvable->notes : null) ?? ($approvalChain->approvable instanceof \Modules\ProjectManagement\Models\Project ? $approvalChain->approvable->description : null), ]; } return Inertia::render('ApprovalWorkflow::Approvals/Show', [ 'chain' => $approvalChain, 'breakdownData' => $breakdownData, ]); } /** * Approve the current step. */ public function approve(Request $request, ApprovalChain $approvalChain) { $request->validate(['notes' => 'nullable|string|max:1000']); try { $this->approvalService->approve($approvalChain, $request->user(), $request->notes); } catch (\InvalidArgumentException $e) { return back()->with('error', $e->getMessage()); } return back()->with('success', 'Step approved successfully.'); } /** * Reject the current step (rejects entire chain). */ public function reject(Request $request, ApprovalChain $approvalChain) { $request->validate(['notes' => 'required|string|max:1000']); try { $this->approvalService->reject($approvalChain, $request->user(), $request->notes); } catch (\InvalidArgumentException $e) { return back()->with('error', $e->getMessage()); } return back()->with('success', 'Approval rejected.'); } }