#63 - permissions (#67)

* wip

* fix

* wip

* #63 - permissions

Co-authored-by: EwelinaLasowy <ewelina.lasowy@blumilk.pl>
This commit is contained in:
Adrian Hopek
2022-03-02 09:52:50 +01:00
committed by GitHub
parent 5cb46d2fc4
commit d825dd727f
47 changed files with 1027 additions and 411 deletions

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
namespace Toby\Domain;
use Carbon\CarbonPeriod;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Toby\Domain\Enums\VacationRequestState;
use Toby\Eloquent\Helpers\YearPeriodRetriever;
use Toby\Eloquent\Models\Vacation;
use Toby\Eloquent\Models\YearPeriod;
@@ -55,7 +55,7 @@ class CalendarGenerator
{
return Vacation::query()
->whereBetween("date", [$period->start, $period->end])
->whereRelation("vacationRequest", "state", VacationRequestState::Approved->value)
->whereRelation("vacationRequest", fn(Builder $query) => $query->states(VacationRequestStatesRetriever::successStates()))
->get()
->groupBy(fn(Vacation $vacation) => $vacation->date->toDateString());
}

View File

@@ -1,56 +0,0 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\Enums;
enum VacationRequestState: string
{
case Created = "created";
case Cancelled = "cancelled";
case Rejected = "rejected";
case Approved = "approved";
case WaitingForTechnical = "waiting_for_technical";
case WaitingForAdministrative = "waiting_for_administrative";
case AcceptedByTechnical = "accepted_by_technical";
case AcceptedByAdministrative = "accepted_by_administrative";
public function label(): string
{
return __($this->value);
}
public static function pendingStates(): array
{
return [
self::Created,
self::WaitingForTechnical,
self::WaitingForAdministrative,
self::AcceptedByTechnical,
self::AcceptedByAdministrative,
];
}
public static function successStates(): array
{
return [self::Approved];
}
public static function failedStates(): array
{
return [
self::Rejected,
self::Cancelled,
];
}
public static function filterByStatus(string $filter): array
{
return match ($filter) {
"pending" => VacationRequestState::pendingStates(),
"success" => VacationRequestState::successStates(),
"failed" => VacationRequestState::failedStates(),
default => VacationRequestState::cases(),
};
}
}

View File

@@ -6,7 +6,7 @@ namespace Toby\Domain\Events;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Toby\Domain\Enums\VacationRequestState;
use Toby\Domain\States\VacationRequest\VacationRequestState;
use Toby\Eloquent\Models\User;
use Toby\Eloquent\Models\VacationRequest;

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\Policies;
use Toby\Domain\Enums\Role;
use Toby\Eloquent\Models\User;
use Toby\Eloquent\Models\VacationRequest;
class VacationRequestPolicy
{
public function createOnBehalfOfEmployee(User $user): bool
{
return $user->role === Role::AdministrativeApprover;
}
public function acceptAsAdminApprover(User $user): bool
{
return $user->role === Role::AdministrativeApprover;
}
public function acceptAsTechApprover(User $user): bool
{
return $user->role === Role::TechnicalApprover;
}
public function skipFlow(User $user): bool
{
return $user->role === Role::AdministrativeApprover;
}
public function reject(User $user): bool
{
return in_array($user->role, [Role::AdministrativeApprover, Role::TechnicalApprover], true);
}
public function cancel(User $user): bool
{
return $user->role === Role::AdministrativeApprover;
}
public function show(User $user, VacationRequest $vacationRequest): bool
{
if ($vacationRequest->user->is($user)) {
return true;
}
return in_array($user->role, [Role::TechnicalApprover, Role::AdministrativeApprover], true);
}
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\States\VacationRequest;
class AcceptedByAdministrative extends VacationRequestState
{
public static string $name = "accepted_by_administrative";
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\States\VacationRequest;
class AcceptedByTechnical extends VacationRequestState
{
public static string $name = "accepted_by_technical";
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\States\VacationRequest;
class Approved extends VacationRequestState
{
public static string $name = "approved";
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\States\VacationRequest;
class Cancelled extends VacationRequestState
{
public static string $name = "cancelled";
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\States\VacationRequest;
class Created extends VacationRequestState
{
public static string $name = "created";
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\States\VacationRequest;
class Rejected extends VacationRequestState
{
public static string $name = "rejected";
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\States\VacationRequest;
use Spatie\ModelStates\Exceptions\InvalidConfig;
use Spatie\ModelStates\State;
use Spatie\ModelStates\StateConfig;
abstract class VacationRequestState extends State
{
/**
* @throws InvalidConfig
*/
public static function config(): StateConfig
{
return parent::config()
->default(Created::class)
->allowTransition(Created::class, Approved::class)
->allowTransition(Created::class, WaitingForTechnical::class)
->allowTransition(Created::class, WaitingForAdministrative::class)
->allowTransition(WaitingForTechnical::class, Rejected::class)
->allowTransition(WaitingForTechnical::class, AcceptedByTechnical::class)
->allowTransition(WaitingForAdministrative::class, Rejected::class)
->allowTransition(WaitingForAdministrative::class, AcceptedByAdministrative::class)
->allowTransition(AcceptedByTechnical::class, WaitingForAdministrative::class)
->allowTransition(AcceptedByTechnical::class, Approved::class)
->allowTransition(AcceptedByAdministrative::class, Approved::class)
->allowTransition([
Created::class,
WaitingForTechnical::class,
WaitingForAdministrative::class,
AcceptedByTechnical::class,
AcceptedByAdministrative::class,
Approved::class,
], Cancelled::class);
}
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\States\VacationRequest;
class WaitingForAdministrative extends VacationRequestState
{
public static string $name = "waiting_for_administrative";
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\States\VacationRequest;
class WaitingForTechnical extends VacationRequestState
{
public static string $name = "waiting_for_technical";
}

View File

@@ -6,15 +6,24 @@ namespace Toby\Domain;
use Illuminate\Contracts\Auth\Factory as Auth;
use Illuminate\Contracts\Events\Dispatcher;
use Toby\Domain\Enums\VacationRequestState;
use Toby\Domain\Events\VacationRequestAcceptedByAdministrative;
use Toby\Domain\Events\VacationRequestAcceptedByTechnical;
use Toby\Domain\Events\VacationRequestApproved;
use Toby\Domain\Events\VacationRequestCancelled;
use Toby\Domain\Events\VacationRequestCreated;
use Toby\Domain\Events\VacationRequestRejected;
use Toby\Domain\Events\VacationRequestStateChanged;
use Toby\Domain\Events\VacationRequestWaitsForAdminApproval;
use Toby\Domain\Events\VacationRequestWaitsForTechApproval;
use Toby\Domain\States\VacationRequest\AcceptedByAdministrative;
use Toby\Domain\States\VacationRequest\AcceptedByTechnical;
use Toby\Domain\States\VacationRequest\Approved;
use Toby\Domain\States\VacationRequest\Cancelled;
use Toby\Domain\States\VacationRequest\Rejected;
use Toby\Domain\States\VacationRequest\VacationRequestState;
use Toby\Domain\States\VacationRequest\WaitingForAdministrative;
use Toby\Domain\States\VacationRequest\WaitingForTechnical;
use Toby\Eloquent\Models\User;
use Toby\Eloquent\Models\VacationRequest;
class VacationRequestStateManager
@@ -25,64 +34,77 @@ class VacationRequestStateManager
) {
}
public function markAsCreated(VacationRequest $vacationRequest): void
public function markAsCreated(VacationRequest $vacationRequest, ?User $user = null): void
{
$this->changeState($vacationRequest, VacationRequestState::Created);
$this->fireStateChangedEvent($vacationRequest, null, $vacationRequest->state, $user);
$this->dispatcher->dispatch(new VacationRequestCreated($vacationRequest));
}
public function approve(VacationRequest $vacationRequest): void
public function approve(VacationRequest $vacationRequest, ?User $user = null): void
{
$this->changeState($vacationRequest, VacationRequestState::Approved);
$this->changeState($vacationRequest, Approved::class, $user);
$this->dispatcher->dispatch(new VacationRequestApproved($vacationRequest));
}
public function reject(VacationRequest $vacationRequest): void
public function reject(VacationRequest $vacationRequest, ?User $user = null): void
{
$this->changeState($vacationRequest, VacationRequestState::Rejected);
$this->changeState($vacationRequest, Rejected::class, $user);
$this->dispatcher->dispatch(new VacationRequestRejected($vacationRequest));
}
public function cancel(VacationRequest $vacationRequest): void
public function cancel(VacationRequest $vacationRequest, ?User $user = null): void
{
$this->changeState($vacationRequest, VacationRequestState::Cancelled);
$this->changeState($vacationRequest, Cancelled::class, $user);
$this->dispatcher->dispatch(new VacationRequestCancelled($vacationRequest));
}
public function acceptAsTechnical(VacationRequest $vacationRequest): void
public function acceptAsTechnical(VacationRequest $vacationRequest, ?User $user = null): void
{
$this->changeState($vacationRequest, VacationRequestState::AcceptedByTechnical);
$this->changeState($vacationRequest, AcceptedByTechnical::class, $user);
$this->dispatcher->dispatch(new VacationRequestAcceptedByTechnical($vacationRequest));
}
public function acceptAsAdministrative(VacationRequest $vacationRequest): void
public function acceptAsAdministrative(VacationRequest $vacationRequest, ?User $user = null): void
{
$this->changeState($vacationRequest, VacationRequestState::AcceptedByAdministrative);
$this->changeState($vacationRequest, AcceptedByAdministrative::class, $user);
$this->dispatcher->dispatch(new VacationRequestAcceptedByAdministrative($vacationRequest));
}
public function waitForTechnical(VacationRequest $vacationRequest): void
public function waitForTechnical(VacationRequest $vacationRequest, ?User $user = null): void
{
$this->changeState($vacationRequest, VacationRequestState::WaitingForTechnical);
$this->changeState($vacationRequest, WaitingForTechnical::class, $user);
$this->dispatcher->dispatch(new VacationRequestWaitsForTechApproval($vacationRequest));
}
public function waitForAdministrative(VacationRequest $vacationRequest): void
public function waitForAdministrative(VacationRequest $vacationRequest, ?User $user = null): void
{
$this->changeState($vacationRequest, VacationRequestState::WaitingForAdministrative);
$this->changeState($vacationRequest, WaitingForAdministrative::class, $user);
$this->dispatcher->dispatch(new VacationRequestWaitsForAdminApproval($vacationRequest));
}
protected function changeState(VacationRequest $vacationRequest, VacationRequestState $state): void
protected function changeState(VacationRequest $vacationRequest, string $state, ?User $user = null): void
{
$vacationRequest->changeStateTo($state);
$previousState = $vacationRequest->state;
$vacationRequest->state->transitionTo($state);
$vacationRequest->save();
$this->fireStateChangedEvent($vacationRequest, $previousState, $vacationRequest->state, $user);
}
protected function fireStateChangedEvent(
VacationRequest $vacationRequest,
?VacationRequestState $from,
VacationRequestState $to,
?User $user = null,
): void {
$event = new VacationRequestStateChanged($vacationRequest, $from, $to, $user);
$this->dispatcher->dispatch($event);
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace Toby\Domain;
use Toby\Domain\States\VacationRequest\AcceptedByAdministrative;
use Toby\Domain\States\VacationRequest\AcceptedByTechnical;
use Toby\Domain\States\VacationRequest\Approved;
use Toby\Domain\States\VacationRequest\Cancelled;
use Toby\Domain\States\VacationRequest\Created;
use Toby\Domain\States\VacationRequest\Rejected;
use Toby\Domain\States\VacationRequest\WaitingForAdministrative;
use Toby\Domain\States\VacationRequest\WaitingForTechnical;
class VacationRequestStatesRetriever
{
public static function pendingStates(): array
{
return [
Created::class,
WaitingForTechnical::class,
WaitingForAdministrative::class,
AcceptedByTechnical::class,
AcceptedByAdministrative::class,
];
}
public static function successStates(): array
{
return [Approved::class];
}
public static function failedStates(): array
{
return [
Rejected::class,
Cancelled::class,
];
}
public static function all(): array
{
return [
...self::pendingStates(),
...self::successStates(),
...self::failedStates(),
];
}
public static function filterByStatus(string $filter): array
{
return match ($filter) {
"pending" => self::pendingStates(),
"success" => self::successStates(),
"failed" => self::failedStates(),
default => self::all(),
};
}
}

View File

@@ -6,9 +6,9 @@ namespace Toby\Domain\Validation\Rules;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Toby\Domain\Enums\VacationRequestState;
use Toby\Domain\Enums\VacationType;
use Toby\Domain\VacationDaysCalculator;
use Toby\Domain\VacationRequestStatesRetriever;
use Toby\Domain\VacationTypeConfigRetriever;
use Toby\Eloquent\Models\User;
use Toby\Eloquent\Models\VacationRequest;
@@ -53,7 +53,7 @@ class DoesNotExceedLimitRule implements VacationRequestRule
"vacationRequest",
fn(Builder $query) => $query
->whereIn("type", $this->getLimitableVacationTypes())
->noStates(VacationRequestState::failedStates()),
->noStates(VacationRequestStatesRetriever::failedStates()),
)
->count();
}

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Toby\Domain\Validation\Rules;
use Toby\Domain\Enums\VacationRequestState;
use Toby\Domain\VacationRequestStatesRetriever;
use Toby\Eloquent\Models\VacationRequest;
class NoApprovedVacationRequestsInRange implements VacationRequestRule
@@ -15,7 +15,7 @@ class NoApprovedVacationRequestsInRange implements VacationRequestRule
->user
->vacationRequests()
->overlapsWith($vacationRequest)
->states(VacationRequestState::successStates())
->states(VacationRequestStatesRetriever::successStates())
->exists();
}

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Toby\Domain\Validation\Rules;
use Toby\Domain\Enums\VacationRequestState;
use Toby\Domain\VacationRequestStatesRetriever;
use Toby\Eloquent\Models\VacationRequest;
class NoPendingVacationRequestInRange implements VacationRequestRule
@@ -15,7 +15,7 @@ class NoPendingVacationRequestInRange implements VacationRequestRule
->user
->vacationRequests()
->overlapsWith($vacationRequest)
->states(VacationRequestState::pendingStates())
->states(VacationRequestStatesRetriever::pendingStates())
->exists();
}