94 lines
3.6 KiB
PHP
94 lines
3.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Enums\PurchasingRole;
|
|
use App\Models\PurchaseAward;
|
|
use App\Models\Quotation;
|
|
use App\Models\Requisition;
|
|
use App\Models\User;
|
|
use Illuminate\Validation\ValidationException;
|
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
|
|
|
/** Shared read-only authority for quotation forms, tasks and commands. */
|
|
class QuotationParticipation
|
|
{
|
|
private function assertActor(User $actor, Requisition $request, bool $evaluation): void
|
|
{
|
|
$roles = $evaluation ? [PurchasingRole::Procurement, PurchasingRole::Finance, PurchasingRole::DepartmentHead] : [PurchasingRole::Procurement];
|
|
abort_unless($actor->tenant_id === $request->tenant_id && $request->status === 'approved' && $actor->hasFunctions($roles)
|
|
&& ! app(OrganizationAccess::class)->maintenanceOnly($actor)
|
|
&& (! $evaluation || $actor->id !== $request->requester_id)
|
|
&& Requisition::query()->visibleTo($actor)->whereKey($request->id)->exists(), 403);
|
|
}
|
|
|
|
private function assertOpen(Requisition $request, bool $evaluation): void
|
|
{
|
|
if (PurchaseAward::query()->where('requisition_id', $request->id)->exists()) {
|
|
throw ValidationException::withMessages(['award' => $evaluation
|
|
? 'As avaliações estão fechadas pela adjudicação deste processo.'
|
|
: 'As propostas estão fechadas pela adjudicação deste processo.']);
|
|
}
|
|
}
|
|
|
|
public function assertPrepare(User $actor, Requisition $request): void
|
|
{
|
|
$this->assertActor($actor, $request, false);
|
|
$this->assertOpen($request, false);
|
|
app(ConflictGuard::class)->assertParticipation($actor, $request);
|
|
}
|
|
|
|
public function assertEvaluateProcess(User $actor, Requisition $request): void
|
|
{
|
|
$this->assertActor($actor, $request, true);
|
|
$this->assertOpen($request, true);
|
|
app(ConflictGuard::class)->assertParticipation($actor, $request);
|
|
}
|
|
|
|
public function assertEvaluate(User $actor, Requisition $request, Quotation $quote): void
|
|
{
|
|
$this->assertEvaluateProcess($actor, $request);
|
|
abort_unless($quote->tenant_id === $request->tenant_id && $quote->requisition_id === $request->id, 403);
|
|
app(ConflictGuard::class)->assertProcess($request, $quote->supplier_id, $actor);
|
|
if ($quote->valid_until < today() || ! $quote->supplier?->eligible()) {
|
|
throw ValidationException::withMessages(['evaluation' => 'Proposta expirada ou fornecedor não elegível.']);
|
|
}
|
|
if ($quote->evaluations()->where('evaluator_id', $actor->id)->exists()) {
|
|
throw ValidationException::withMessages(['evaluation' => 'Já avaliou esta proposta.']);
|
|
}
|
|
}
|
|
|
|
/** @param callable():void $check */
|
|
private function eligible(callable $check): bool
|
|
{
|
|
try {
|
|
$check();
|
|
|
|
return true;
|
|
} catch (ValidationException) {
|
|
return false;
|
|
} catch (HttpException $error) {
|
|
if ($error->getStatusCode() !== 403) {
|
|
throw $error;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function canPrepare(User $actor, Requisition $request): bool
|
|
{
|
|
return $this->eligible(fn () => $this->assertPrepare($actor, $request));
|
|
}
|
|
|
|
public function canEvaluateProcess(User $actor, Requisition $request): bool
|
|
{
|
|
return $this->eligible(fn () => $this->assertEvaluateProcess($actor, $request));
|
|
}
|
|
|
|
public function canEvaluate(User $actor, Requisition $request, Quotation $quote): bool
|
|
{
|
|
return $this->eligible(fn () => $this->assertEvaluate($actor, $request, $quote));
|
|
}
|
|
}
|