user('web'); abort_unless($actor instanceof User, 403); abort_unless($actor->hasFunctions([PurchasingRole::Admin, PurchasingRole::Procurement, PurchasingRole::Finance, PurchasingRole::DepartmentHead]), 403); return $actor; } public function award(Request $request, Requisition $requisition, ProposalComparison $comparison, PurchaseWorkflow $workflow): Response { $actor = $this->access($request); Requisition::query()->visibleTo($actor)->findOrFail($requisition->id); $award = PurchaseAward::query()->where('requisition_id', $requisition->id)->with('approvals')->first(); $frozen = $award && AwardCancellation::query()->where('purchase_award_id', $award->id)->where('status', 'pending')->exists(); $recommendation = app(FormalRecommendation::class); $adoption = $recommendation->adoption($requisition); $candidate = null; if ($adoption) { $latest = RecommendationAttempt::query()->where('recommendation_adoption_id', $adoption->id)->latest('revision')->first(); if ($latest) { try { $recommendation->selection($requisition, $latest->quotation_id); $candidate = $latest->quotation_id; } catch (ValidationException) { // The POST revalidates again; stale recommendations cannot enable the form. } } } return Inertia::render('purchases/award', [ 'requisition' => $requisition, 'comparison' => $comparison->inspect($requisition), 'award' => $award, 'canSelect' => ! app(OrganizationAccess::class)->maintenanceOnly($actor) && $actor->hasFunctions([PurchasingRole::Procurement]) && $award === null && $actor->id !== $requisition->requester_id && (! $adoption || $candidate !== null), 'recommendation' => ['adopted' => $adoption !== null, 'quotation_id' => $candidate], 'canDecide' => ! $frozen && $award !== null && $workflow->canDecide($actor, $award, $requisition), 'canIssue' => $award !== null && $workflow->canIssue($actor, $award, $requisition), 'order' => PurchaseOrder::query()->where('requisition_id', $requisition->id)->first(['id', 'number']), ]); } public function select(Request $request, Requisition $requisition, PurchaseWorkflow $workflow): RedirectResponse { $actor = $this->access($request); $data = $request->validate([ 'quotation_id' => 'required|integer', 'justification' => 'required|string|min:10|max:5000', 'delivery_date' => 'required|date_format:Y-m-d|after_or_equal:today', 'delivery_location' => 'required|string|min:3|max:500', 'status' => 'prohibited', 'total_minor' => 'prohibited', 'subtotal_minor' => 'prohibited', 'tax_minor' => 'prohibited', 'approval_snapshot' => 'prohibited', 'decision_matrix' => 'prohibited', 'created_by' => 'prohibited', 'tenant_id' => 'prohibited', 'override' => 'prohibited', 'exception' => 'prohibited', ]); $workflow->select($actor, $requisition, $data); return to_route('purchases.award', $requisition); } public function decide(Request $request, PurchaseAward $award, PurchaseWorkflow $workflow): RedirectResponse { $actor = $this->access($request); $data = $request->validate(['decision' => ['required', Rule::in(['approved', 'rejected'])], 'comment' => 'nullable|required_if:decision,rejected|string|max:2000']); $workflow->decide($actor, $award, $data['decision'], $data['comment'] ?? null); return to_route('purchases.award', $award->requisition_id); } public function issue(Request $request, PurchaseAward $award, PurchaseWorkflow $workflow): RedirectResponse { $actor = $this->access($request); $request->validate([ 'status' => 'prohibited', 'snapshot' => 'prohibited', 'total_minor' => 'prohibited', 'subtotal_minor' => 'prohibited', 'tax_minor' => 'prohibited', 'issued_by' => 'prohibited', 'tenant_id' => 'prohibited', 'quotation_id' => 'prohibited', 'override' => 'prohibited', 'exception' => 'prohibited', ]); $order = $workflow->issue($actor, $award); return to_route('orders.show', $order); } public function index(Request $request): Response { $actor = $this->access($request); return Inertia::render('purchases/index', [ 'orders' => PurchaseOrder::query()->visibleTo($actor)->latest('id')->paginate(15), 'awards' => PurchaseAward::query()->whereIn('requisition_id', Requisition::query()->visibleTo($actor)->select('id'))->latest()->paginate(15, ['*'], 'awards_page'), ]); } public function show(Request $request, PurchaseOrder $order): Response { $actor = $this->access($request); PurchaseOrder::query()->visibleTo($actor)->findOrFail($order->id); return Inertia::render('purchases/show', [ 'order' => $order->load('items'), 'history' => AuditLog::query()->where('requisition_id', $order->requisition_id)->whereIn('action', ['award.created', 'award.approved', 'award.rejected', 'purchase_order.issued'])->orderBy('id')->get(['id', 'user_id', 'action', 'created_at']), ]); } public function print(Request $request, PurchaseOrder $order): View { $actor = $this->access($request); PurchaseOrder::query()->visibleTo($actor)->findOrFail($order->id); return view('purchases.print', ['order' => $order->load('items'), 'snapshot' => $order->snapshot]); } }