From d825dd727fc9ddb4b1483b5ff89b0020cff4fea5 Mon Sep 17 00:00:00 2001 From: Adrian Hopek Date: Wed, 2 Mar 2022 09:52:50 +0100 Subject: [PATCH] #63 - permissions (#67) * wip * fix * wip * #63 - permissions Co-authored-by: EwelinaLasowy --- .../Providers/AuthServiceProvider.php | 20 +- app/Domain/CalendarGenerator.php | 4 +- app/Domain/Enums/VacationRequestState.php | 56 -- .../Events/VacationRequestStateChanged.php | 2 +- app/Domain/Policies/VacationRequestPolicy.php | 51 ++ .../AcceptedByAdministrative.php | 10 + .../VacationRequest/AcceptedByTechnical.php | 10 + .../States/VacationRequest/Approved.php | 10 + .../States/VacationRequest/Cancelled.php | 10 + app/Domain/States/VacationRequest/Created.php | 10 + .../States/VacationRequest/Rejected.php | 10 + .../VacationRequest/VacationRequestState.php | 39 ++ .../WaitingForAdministrative.php | 10 + .../VacationRequest/WaitingForTechnical.php | 10 + app/Domain/VacationRequestStateManager.php | 62 +- app/Domain/VacationRequestStatesRetriever.php | 60 ++ .../Rules/DoesNotExceedLimitRule.php | 4 +- .../NoApprovedVacationRequestsInRange.php | 4 +- .../Rules/NoPendingVacationRequestInRange.php | 4 +- app/Eloquent/Models/VacationRequest.php | 29 +- .../Models/VacationRequestActivity.php | 2 +- .../Observers/VacationRequestObserver.php | 30 - .../Http/Controllers/HolidayController.php | 32 +- .../Http/Controllers/TimesheetController.php | 2 + .../Http/Controllers/UserController.php | 36 ++ .../VacationCalendarController.php | 5 + .../Controllers/VacationLimitController.php | 4 + .../Controllers/VacationRequestController.php | 85 ++- .../Http/Middleware/HandleInertiaRequests.php | 4 + .../Http/Requests/VacationRequestRequest.php | 21 +- .../VacationRequestActivityResource.php | 1 - composer.json | 5 +- composer.lock | 592 ++++++++++++------ config/model-states.php | 7 + database/factories/UserFactory.php | 21 + database/factories/VacationRequestFactory.php | 4 +- resources/js/Pages/Calendar.vue | 6 +- resources/js/Pages/Holidays/Index.vue | 7 +- resources/js/Pages/VacationRequest/Create.vue | 14 +- resources/js/Pages/VacationRequest/Show.vue | 24 +- resources/js/Shared/MainMenu.vue | 23 +- tests/Feature/HolidayTest.php | 15 +- tests/Feature/UserTest.php | 20 +- tests/Feature/VacationLimitTest.php | 4 +- tests/Feature/VacationRequestTest.php | 39 +- .../Unit/VacationRequestNotificationTest.php | 4 +- tests/Unit/VacationRequestStatesTest.php | 16 +- 47 files changed, 1027 insertions(+), 411 deletions(-) delete mode 100644 app/Domain/Enums/VacationRequestState.php create mode 100644 app/Domain/Policies/VacationRequestPolicy.php create mode 100644 app/Domain/States/VacationRequest/AcceptedByAdministrative.php create mode 100644 app/Domain/States/VacationRequest/AcceptedByTechnical.php create mode 100644 app/Domain/States/VacationRequest/Approved.php create mode 100644 app/Domain/States/VacationRequest/Cancelled.php create mode 100644 app/Domain/States/VacationRequest/Created.php create mode 100644 app/Domain/States/VacationRequest/Rejected.php create mode 100644 app/Domain/States/VacationRequest/VacationRequestState.php create mode 100644 app/Domain/States/VacationRequest/WaitingForAdministrative.php create mode 100644 app/Domain/States/VacationRequest/WaitingForTechnical.php create mode 100644 app/Domain/VacationRequestStatesRetriever.php create mode 100644 config/model-states.php diff --git a/app/Architecture/Providers/AuthServiceProvider.php b/app/Architecture/Providers/AuthServiceProvider.php index 88f3b1c..f2c8a5f 100644 --- a/app/Architecture/Providers/AuthServiceProvider.php +++ b/app/Architecture/Providers/AuthServiceProvider.php @@ -5,13 +5,31 @@ declare(strict_types=1); namespace Toby\Architecture\Providers; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; +use Illuminate\Support\Facades\Gate; +use Toby\Domain\Enums\Role; +use Toby\Domain\Policies\VacationRequestPolicy; +use Toby\Eloquent\Models\User; +use Toby\Eloquent\Models\VacationRequest; class AuthServiceProvider extends ServiceProvider { - protected $policies = []; + protected $policies = [ + VacationRequest::class => VacationRequestPolicy::class, + ]; public function boot(): void { $this->registerPolicies(); + + Gate::before(function (User $user) { + if ($user->role === Role::Administrator) { + return true; + } + }); + + Gate::define("manageUsers", fn(User $user) => $user->role === Role::AdministrativeApprover); + Gate::define("manageHolidays", fn(User $user) => $user->role === Role::AdministrativeApprover); + Gate::define("manageVacationLimits", fn(User $user) => $user->role === Role::AdministrativeApprover); + Gate::define("generateTimesheet", fn(User $user) => $user->role === Role::AdministrativeApprover); } } diff --git a/app/Domain/CalendarGenerator.php b/app/Domain/CalendarGenerator.php index 052e10c..559269c 100644 --- a/app/Domain/CalendarGenerator.php +++ b/app/Domain/CalendarGenerator.php @@ -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()); } diff --git a/app/Domain/Enums/VacationRequestState.php b/app/Domain/Enums/VacationRequestState.php deleted file mode 100644 index cde2e2f..0000000 --- a/app/Domain/Enums/VacationRequestState.php +++ /dev/null @@ -1,56 +0,0 @@ -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(), - }; - } -} diff --git a/app/Domain/Events/VacationRequestStateChanged.php b/app/Domain/Events/VacationRequestStateChanged.php index 7779917..d521f16 100644 --- a/app/Domain/Events/VacationRequestStateChanged.php +++ b/app/Domain/Events/VacationRequestStateChanged.php @@ -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; diff --git a/app/Domain/Policies/VacationRequestPolicy.php b/app/Domain/Policies/VacationRequestPolicy.php new file mode 100644 index 0000000..3807180 --- /dev/null +++ b/app/Domain/Policies/VacationRequestPolicy.php @@ -0,0 +1,51 @@ +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); + } +} diff --git a/app/Domain/States/VacationRequest/AcceptedByAdministrative.php b/app/Domain/States/VacationRequest/AcceptedByAdministrative.php new file mode 100644 index 0000000..c0b70ab --- /dev/null +++ b/app/Domain/States/VacationRequest/AcceptedByAdministrative.php @@ -0,0 +1,10 @@ +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); + } +} diff --git a/app/Domain/States/VacationRequest/WaitingForAdministrative.php b/app/Domain/States/VacationRequest/WaitingForAdministrative.php new file mode 100644 index 0000000..dd57906 --- /dev/null +++ b/app/Domain/States/VacationRequest/WaitingForAdministrative.php @@ -0,0 +1,10 @@ +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); } } diff --git a/app/Domain/VacationRequestStatesRetriever.php b/app/Domain/VacationRequestStatesRetriever.php new file mode 100644 index 0000000..ebe66fa --- /dev/null +++ b/app/Domain/VacationRequestStatesRetriever.php @@ -0,0 +1,60 @@ + self::pendingStates(), + "success" => self::successStates(), + "failed" => self::failedStates(), + default => self::all(), + }; + } +} diff --git a/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php b/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php index c8c9ab5..a21dfa8 100644 --- a/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php +++ b/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php @@ -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(); } diff --git a/app/Domain/Validation/Rules/NoApprovedVacationRequestsInRange.php b/app/Domain/Validation/Rules/NoApprovedVacationRequestsInRange.php index d06fd95..5758724 100644 --- a/app/Domain/Validation/Rules/NoApprovedVacationRequestsInRange.php +++ b/app/Domain/Validation/Rules/NoApprovedVacationRequestsInRange.php @@ -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(); } diff --git a/app/Domain/Validation/Rules/NoPendingVacationRequestInRange.php b/app/Domain/Validation/Rules/NoPendingVacationRequestInRange.php index 3031b56..1fbc7a5 100644 --- a/app/Domain/Validation/Rules/NoPendingVacationRequestInRange.php +++ b/app/Domain/Validation/Rules/NoPendingVacationRequestInRange.php @@ -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(); } diff --git a/app/Eloquent/Models/VacationRequest.php b/app/Eloquent/Models/VacationRequest.php index da28c1a..0b36764 100644 --- a/app/Eloquent/Models/VacationRequest.php +++ b/app/Eloquent/Models/VacationRequest.php @@ -12,8 +12,9 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Carbon; use Illuminate\Support\Collection; -use Toby\Domain\Enums\VacationRequestState; +use Spatie\ModelStates\HasStates; use Toby\Domain\Enums\VacationType; +use Toby\Domain\States\VacationRequest\VacationRequestState; /** * @property int $id @@ -34,6 +35,7 @@ use Toby\Domain\Enums\VacationType; class VacationRequest extends Model { use HasFactory; + use HasStates; protected $guarded = []; @@ -69,26 +71,14 @@ class VacationRequest extends Model return $this->hasMany(Vacation::class); } - public function changeStateTo(VacationRequestState $state): void + public function scopeStates(Builder $query, VacationRequestState|array $states): Builder { - $this->state = $state; - - $this->save(); + return $query->whereState("state", $states); } - public function hasFlowSkipped(): bool + public function scopeNoStates(Builder $query, VacationRequestState|array $states): Builder { - return $this->flow_skipped; - } - - public function scopeStates(Builder $query, array $states): Builder - { - return $query->whereIn("state", $states); - } - - public function scopeNoStates(Builder $query, array $states): Builder - { - return $query->whereNotIn("state", $states); + return $query->whereNotState("state", $states); } public function scopeOverlapsWith(Builder $query, self $vacationRequest): Builder @@ -97,6 +87,11 @@ class VacationRequest extends Model ->where("to", ">=", $vacationRequest->from); } + public function hasFlowSkipped(): bool + { + return $this->flow_skipped; + } + protected static function newFactory(): VacationRequestFactory { return VacationRequestFactory::new(); diff --git a/app/Eloquent/Models/VacationRequestActivity.php b/app/Eloquent/Models/VacationRequestActivity.php index 8425d99..178254a 100644 --- a/app/Eloquent/Models/VacationRequestActivity.php +++ b/app/Eloquent/Models/VacationRequestActivity.php @@ -6,7 +6,7 @@ namespace Toby\Eloquent\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Toby\Domain\Enums\VacationRequestState; +use Toby\Domain\States\VacationRequest\VacationRequestState; /** * @property int $id diff --git a/app/Eloquent/Observers/VacationRequestObserver.php b/app/Eloquent/Observers/VacationRequestObserver.php index ecbc5b1..4095ce2 100644 --- a/app/Eloquent/Observers/VacationRequestObserver.php +++ b/app/Eloquent/Observers/VacationRequestObserver.php @@ -6,9 +6,6 @@ namespace Toby\Eloquent\Observers; use Illuminate\Contracts\Auth\Factory as Auth; use Illuminate\Contracts\Events\Dispatcher; -use Toby\Domain\Enums\VacationRequestState; -use Toby\Domain\Events\VacationRequestStateChanged; -use Toby\Eloquent\Models\User; use Toby\Eloquent\Models\VacationRequest; class VacationRequestObserver @@ -29,31 +26,4 @@ class VacationRequestObserver $vacationRequest->name = "{$vacationRequestNumber}/${year}"; } - - public function saved(VacationRequest $vacationRequest): void - { - if ($vacationRequest->isDirty("state")) { - $previousState = $vacationRequest->getOriginal("state"); - - $this->fireStateChangedEvent($vacationRequest, $previousState, $vacationRequest->state); - } - } - - protected function fireStateChangedEvent( - VacationRequest $vacationRequest, - ?VacationRequestState $from, - VacationRequestState $to, - ): void { - $event = new VacationRequestStateChanged($vacationRequest, $from, $to, $this->getAuthUser()); - - $this->dispatcher->dispatch($event); - } - - protected function getAuthUser(): ?User - { - /** @var User $user */ - $user = $this->auth->guard()->user(); - - return $user; - } } diff --git a/app/Infrastructure/Http/Controllers/HolidayController.php b/app/Infrastructure/Http/Controllers/HolidayController.php index fe256dd..11da9f8 100644 --- a/app/Infrastructure/Http/Controllers/HolidayController.php +++ b/app/Infrastructure/Http/Controllers/HolidayController.php @@ -4,7 +4,9 @@ declare(strict_types=1); namespace Toby\Infrastructure\Http\Controllers; +use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; use Inertia\Response; use Toby\Eloquent\Models\Holiday; use Toby\Infrastructure\Http\Requests\HolidayRequest; @@ -13,7 +15,7 @@ use Toby\Infrastructure\Http\Resources\HolidayResource; class HolidayController extends Controller { - public function index(): Response + public function index(Request $request): Response { $holidays = Holiday::query() ->orderBy("date") @@ -21,16 +23,29 @@ class HolidayController extends Controller return inertia("Holidays/Index", [ "holidays" => HolidayResource::collection($holidays), + "can" => [ + "manageHolidays" => $request->user()->can("manageHolidays"), + ], ]); } + /** + * @throws AuthorizationException + */ public function create(): Response { + $this->authorize("manageHolidays"); + return inertia("Holidays/Create"); } + /** + * @throws AuthorizationException + */ public function store(HolidayRequest $request): RedirectResponse { + $this->authorize("manageHolidays"); + Holiday::query()->create($request->data()); return redirect() @@ -38,15 +53,25 @@ class HolidayController extends Controller ->with("success", __("Holiday has been created.")); } + /** + * @throws AuthorizationException + */ public function edit(Holiday $holiday): Response { + $this->authorize("manageHolidays"); + return inertia("Holidays/Edit", [ "holiday" => new HolidayFormDataResource($holiday), ]); } + /** + * @throws AuthorizationException + */ public function update(HolidayRequest $request, Holiday $holiday): RedirectResponse { + $this->authorize("manageHolidays"); + $holiday->update($request->data()); return redirect() @@ -54,8 +79,13 @@ class HolidayController extends Controller ->with("success", __("Holiday has been updated.")); } + /** + * @throws AuthorizationException + */ public function destroy(Holiday $holiday): RedirectResponse { + $this->authorize("manageHolidays"); + $holiday->delete(); return redirect() diff --git a/app/Infrastructure/Http/Controllers/TimesheetController.php b/app/Infrastructure/Http/Controllers/TimesheetController.php index 9f7fcd7..db26dfa 100644 --- a/app/Infrastructure/Http/Controllers/TimesheetController.php +++ b/app/Infrastructure/Http/Controllers/TimesheetController.php @@ -16,6 +16,8 @@ class TimesheetController extends Controller { public function __invoke(Month $month, YearPeriodRetriever $yearPeriodRetriever): BinaryFileResponse { + $this->authorize("generateTimesheet"); + $yearPeriod = $yearPeriodRetriever->selected(); $carbonMonth = Carbon::create($yearPeriod->year, $month->toCarbonNumber()); diff --git a/app/Infrastructure/Http/Controllers/UserController.php b/app/Infrastructure/Http/Controllers/UserController.php index a49e9ee..46557da 100644 --- a/app/Infrastructure/Http/Controllers/UserController.php +++ b/app/Infrastructure/Http/Controllers/UserController.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Toby\Infrastructure\Http\Controllers; +use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Inertia\Response; @@ -16,8 +17,13 @@ use Toby\Infrastructure\Http\Resources\UserResource; class UserController extends Controller { + /** + * @throws AuthorizationException + */ public function index(Request $request): Response { + $this->authorize("manageUsers"); + $users = User::query() ->withTrashed() ->search($request->query("search")) @@ -32,16 +38,26 @@ class UserController extends Controller ]); } + /** + * @throws AuthorizationException + */ public function create(): Response { + $this->authorize("manageUsers"); + return inertia("Users/Create", [ "employmentForms" => EmploymentForm::casesToSelect(), "roles" => Role::casesToSelect(), ]); } + /** + * @throws AuthorizationException + */ public function store(UserRequest $request): RedirectResponse { + $this->authorize("manageUsers"); + User::query()->create($request->data()); return redirect() @@ -49,8 +65,13 @@ class UserController extends Controller ->with("success", __("User has been created.")); } + /** + * @throws AuthorizationException + */ public function edit(User $user): Response { + $this->authorize("manageUsers"); + return inertia("Users/Edit", [ "user" => new UserFormDataResource($user), "employmentForms" => EmploymentForm::casesToSelect(), @@ -58,8 +79,13 @@ class UserController extends Controller ]); } + /** + * @throws AuthorizationException + */ public function update(UserRequest $request, User $user): RedirectResponse { + $this->authorize("manageUsers"); + $user->update($request->data()); return redirect() @@ -67,8 +93,13 @@ class UserController extends Controller ->with("success", __("User has been updated.")); } + /** + * @throws AuthorizationException + */ public function destroy(User $user): RedirectResponse { + $this->authorize("manageUsers"); + $user->delete(); return redirect() @@ -76,8 +107,13 @@ class UserController extends Controller ->with("success", __("User has been deleted.")); } + /** + * @throws AuthorizationException + */ public function restore(User $user): RedirectResponse { + $this->authorize("manageUsers"); + $user->restore(); return redirect() diff --git a/app/Infrastructure/Http/Controllers/VacationCalendarController.php b/app/Infrastructure/Http/Controllers/VacationCalendarController.php index ec7a33a..95353b3 100644 --- a/app/Infrastructure/Http/Controllers/VacationCalendarController.php +++ b/app/Infrastructure/Http/Controllers/VacationCalendarController.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Toby\Infrastructure\Http\Controllers; +use Illuminate\Http\Request; use Illuminate\Support\Carbon; use Inertia\Response; use Toby\Domain\CalendarGenerator; @@ -15,6 +16,7 @@ use Toby\Infrastructure\Http\Resources\UserResource; class VacationCalendarController extends Controller { public function index( + Request $request, YearPeriodRetriever $yearPeriodRetriever, CalendarGenerator $calendarGenerator, ?string $month = null, @@ -35,6 +37,9 @@ class VacationCalendarController extends Controller "calendar" => $calendar, "currentMonth" => $month->value, "users" => UserResource::collection($users), + "can" => [ + "generateTimesheet" => $request->user()->can("generateTimesheet"), + ], ]); } } diff --git a/app/Infrastructure/Http/Controllers/VacationLimitController.php b/app/Infrastructure/Http/Controllers/VacationLimitController.php index 2762b7d..6d30b7a 100644 --- a/app/Infrastructure/Http/Controllers/VacationLimitController.php +++ b/app/Infrastructure/Http/Controllers/VacationLimitController.php @@ -14,6 +14,8 @@ class VacationLimitController extends Controller { public function edit(): Response { + $this->authorize("manageVacationLimits"); + $limits = VacationLimit::query() ->with("user") ->orderByUserField("last_name") @@ -27,6 +29,8 @@ class VacationLimitController extends Controller public function update(VacationLimitRequest $request): RedirectResponse { + $this->authorize("manageVacationLimits"); + $data = $request->data(); foreach ($request->vacationLimits() as $limit) { diff --git a/app/Infrastructure/Http/Controllers/VacationRequestController.php b/app/Infrastructure/Http/Controllers/VacationRequestController.php index 6f1c8c6..6ba7034 100644 --- a/app/Infrastructure/Http/Controllers/VacationRequestController.php +++ b/app/Infrastructure/Http/Controllers/VacationRequestController.php @@ -5,14 +5,20 @@ declare(strict_types=1); namespace Toby\Infrastructure\Http\Controllers; use Barryvdh\DomPDF\Facade\Pdf; +use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response as LaravelResponse; +use Illuminate\Validation\ValidationException; use Inertia\Response; -use Toby\Domain\Enums\VacationRequestState; use Toby\Domain\Enums\VacationType; +use Toby\Domain\States\VacationRequest\AcceptedByAdministrative; +use Toby\Domain\States\VacationRequest\AcceptedByTechnical; +use Toby\Domain\States\VacationRequest\Cancelled; +use Toby\Domain\States\VacationRequest\Rejected; use Toby\Domain\VacationDaysCalculator; use Toby\Domain\VacationRequestStateManager; +use Toby\Domain\VacationRequestStatesRetriever; use Toby\Domain\Validation\VacationRequestValidator; use Toby\Eloquent\Helpers\YearPeriodRetriever; use Toby\Eloquent\Models\User; @@ -33,7 +39,7 @@ class VacationRequestController extends Controller ->with("vacations") ->where("year_period_id", $yearPeriodRetriever->selected()->id) ->latest() - ->states(VacationRequestState::filterByStatus($status)) + ->states(VacationRequestStatesRetriever::filterByStatus($status)) ->paginate(); return inertia("VacationRequest/Index", [ @@ -44,16 +50,37 @@ class VacationRequestController extends Controller ]); } - public function show(VacationRequest $vacationRequest): Response + /** + * @throws AuthorizationException + */ + public function show(Request $request, VacationRequest $vacationRequest): Response { + $this->authorize("show", $vacationRequest); + $user = $request->user(); + return inertia("VacationRequest/Show", [ "request" => new VacationRequestResource($vacationRequest), "activities" => VacationRequestActivityResource::collection($vacationRequest->activities), + "can" => [ + "acceptAsTechnical" => $vacationRequest->state->canTransitionTo(AcceptedByTechnical::class) + && $user->can("acceptAsTechApprover", $vacationRequest), + "acceptAsAdministrative" => $vacationRequest->state->canTransitionTo(AcceptedByAdministrative::class) + && $user->can("acceptAsAdminApprover", $vacationRequest), + "reject" => $vacationRequest->state->canTransitionTo(Rejected::class) + && $user->can("reject", $vacationRequest), + "cancel" => $vacationRequest->state->canTransitionTo(Cancelled::class) + && $user->can("cancel", $vacationRequest), + ], ]); } + /** + * @throws AuthorizationException + */ public function download(VacationRequest $vacationRequest): LaravelResponse { + $this->authorize("show", $vacationRequest); + $pdf = PDF::loadView("pdf.vacation-request", [ "vacationRequest" => $vacationRequest, ]); @@ -61,7 +88,7 @@ class VacationRequestController extends Controller return $pdf->stream(); } - public function create(): Response + public function create(Request $request): Response { $users = User::query() ->orderBy("last_name") @@ -71,15 +98,31 @@ class VacationRequestController extends Controller return inertia("VacationRequest/Create", [ "vacationTypes" => VacationType::casesToSelect(), "users" => UserResource::collection($users), + "can" => [ + "createOnBehalfOfEmployee" => $request->user()->can("createOnBehalfOfEmployee", VacationRequest::class), + "skipFlow" => $request->user()->can("skipFlow", VacationRequest::class), + ], ]); } + /** + * @throws AuthorizationException + * @throws ValidationException + */ public function store( VacationRequestRequest $request, VacationRequestValidator $vacationRequestValidator, VacationRequestStateManager $stateManager, VacationDaysCalculator $vacationDaysCalculator, ): RedirectResponse { + if ($request->createsOnBehalfOfEmployee()) { + $this->authorize("createOnBehalfOfEmployee", VacationRequest::class); + } + + if ($request->wantsSkipFlow()) { + $this->authorize("skipFlow", VacationRequest::class); + } + /** @var VacationRequest $vacationRequest */ $vacationRequest = $request->user()->createdVacationRequests()->make($request->data()); $vacationRequestValidator->validate($vacationRequest); @@ -100,48 +143,72 @@ class VacationRequestController extends Controller ]); } - $stateManager->markAsCreated($vacationRequest); + $stateManager->markAsCreated($vacationRequest, $request->user()); return redirect() ->route("vacation.requests.show", $vacationRequest) ->with("success", __("Vacation request has been created.")); } + /** + * @throws AuthorizationException + */ public function reject( + Request $request, VacationRequest $vacationRequest, VacationRequestStateManager $stateManager, ): RedirectResponse { - $stateManager->reject($vacationRequest); + $this->authorize("reject", $vacationRequest); + + $stateManager->reject($vacationRequest, $request->user()); return redirect()->back() ->with("success", __("Vacation request has been rejected.")); } + /** + * @throws AuthorizationException + */ public function cancel( + Request $request, VacationRequest $vacationRequest, VacationRequestStateManager $stateManager, ): RedirectResponse { - $stateManager->cancel($vacationRequest); + $this->authorize("cancel", $vacationRequest); + + $stateManager->cancel($vacationRequest, $request->user()); return redirect()->back() ->with("success", __("Vacation request has been cancelled.")); } + /** + * @throws AuthorizationException + */ public function acceptAsTechnical( + Request $request, VacationRequest $vacationRequest, VacationRequestStateManager $stateManager, ): RedirectResponse { - $stateManager->acceptAsTechnical($vacationRequest); + $this->authorize("acceptAsTechApprover", $vacationRequest); + + $stateManager->acceptAsTechnical($vacationRequest, $request->user()); return redirect()->back() ->with("success", __("Vacation request has been accepted.")); } + /** + * @throws AuthorizationException + */ public function acceptAsAdministrative( + Request $request, VacationRequest $vacationRequest, VacationRequestStateManager $stateManager, ): RedirectResponse { - $stateManager->acceptAsAdministrative($vacationRequest); + $this->authorize("acceptAsAdminApprover", $vacationRequest); + + $stateManager->acceptAsAdministrative($vacationRequest, $request->user()); return redirect()->back() ->with("success", __("Vacation request has been accepted.")); diff --git a/app/Infrastructure/Http/Middleware/HandleInertiaRequests.php b/app/Infrastructure/Http/Middleware/HandleInertiaRequests.php index 24e9e9f..d0c871f 100644 --- a/app/Infrastructure/Http/Middleware/HandleInertiaRequests.php +++ b/app/Infrastructure/Http/Middleware/HandleInertiaRequests.php @@ -23,6 +23,10 @@ class HandleInertiaRequests extends Middleware return array_merge(parent::share($request), [ "auth" => fn() => [ "user" => $user ? new UserResource($user) : null, + "can" => [ + "manageVacationLimits" => $user ? $user->can("manageVacationLimits") : false, + "manageUsers" => $user ? $user->can("manageUsers") : false, + ], ], "flash" => fn() => [ "success" => $request->session()->get("success"), diff --git a/app/Infrastructure/Http/Requests/VacationRequestRequest.php b/app/Infrastructure/Http/Requests/VacationRequestRequest.php index f69ef10..7d1a73d 100644 --- a/app/Infrastructure/Http/Requests/VacationRequestRequest.php +++ b/app/Infrastructure/Http/Requests/VacationRequestRequest.php @@ -27,16 +27,29 @@ class VacationRequestRequest extends FormRequest public function data(): array { - $from = $this->get("from"); - return [ "user_id" => $this->get("user"), "type" => $this->get("type"), - "from" => $from, + "from" => $this->get("from"), "to" => $this->get("to"), - "year_period_id" => YearPeriod::findByYear(Carbon::create($from)->year)->id, + "year_period_id" => $this->yearPeriod()->id, "comment" => $this->get("comment"), "flow_skipped" => $this->boolean("flowSkipped"), ]; } + + public function yearPeriod(): YearPeriod + { + return YearPeriod::findByYear(Carbon::create($this->get("from"))->year); + } + + public function createsOnBehalfOfEmployee(): bool + { + return $this->user()->id !== $this->get("user"); + } + + public function wantsSkipFlow(): bool + { + return $this->boolean("flowSkipped"); + } } diff --git a/app/Infrastructure/Http/Resources/VacationRequestActivityResource.php b/app/Infrastructure/Http/Resources/VacationRequestActivityResource.php index e751f89..976050e 100644 --- a/app/Infrastructure/Http/Resources/VacationRequestActivityResource.php +++ b/app/Infrastructure/Http/Resources/VacationRequestActivityResource.php @@ -17,7 +17,6 @@ class VacationRequestActivityResource extends JsonResource "time" => $this->created_at->format("H:i"), "user" => $this->user ? $this->user->fullName : __("System"), "state" => $this->to, - "text" => $this->to->label(), ]; } } diff --git a/composer.json b/composer.json index 5c28597..2abe7c9 100644 --- a/composer.json +++ b/composer.json @@ -18,8 +18,9 @@ "laravel/telescope": "^4.6", "laravel/tinker": "^2.5", "lasserafn/php-initial-avatar-generator": "^4.2", - "maatwebsite/excel": "^3.1", - "spatie/laravel-google-calendar": "^3.5" + "spatie/laravel-google-calendar": "^3.5", + "spatie/laravel-model-states": "^2.1", + "maatwebsite/excel": "^3.1" }, "require-dev": { "blumilksoftware/codestyle": "^0.9.0", diff --git a/composer.lock b/composer.lock index bf41929..04fdb95 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,64 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "09609461b05d589abb8bc0cbac9c653c", + "content-hash": "c090431972dc8bfbe198ce9fc9f816f3", "packages": [ + { + "name": "asm89/stack-cors", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/asm89/stack-cors.git", + "reference": "73e5b88775c64ccc0b84fb60836b30dc9d92ac4a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/asm89/stack-cors/zipball/73e5b88775c64ccc0b84fb60836b30dc9d92ac4a", + "reference": "73e5b88775c64ccc0b84fb60836b30dc9d92ac4a", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "symfony/http-foundation": "^4|^5|^6", + "symfony/http-kernel": "^4|^5|^6" + }, + "require-dev": { + "phpunit/phpunit": "^7|^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "psr-4": { + "Asm89\\Stack\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alexander", + "email": "iam.asm89@gmail.com" + } + ], + "description": "Cross-origin resource sharing library and stack middleware", + "homepage": "https://github.com/asm89/stack-cors", + "keywords": [ + "cors", + "stack" + ], + "support": { + "issues": "https://github.com/asm89/stack-cors/issues", + "source": "https://github.com/asm89/stack-cors/tree/v2.1.1" + }, + "time": "2022-01-18T09:12:03+00:00" + }, { "name": "azuyalabs/yasumi", "version": "2.5.0", @@ -383,16 +439,16 @@ }, { "name": "doctrine/lexer", - "version": "1.2.2", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "9c50f840f257bbb941e6f4a0e94ccf5db5c3f76c" + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/9c50f840f257bbb941e6f4a0e94ccf5db5c3f76c", - "reference": "9c50f840f257bbb941e6f4a0e94ccf5db5c3f76c", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229", "shasum": "" }, "require": { @@ -400,7 +456,7 @@ }, "require-dev": { "doctrine/coding-standard": "^9.0", - "phpstan/phpstan": "1.3", + "phpstan/phpstan": "^1.3", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "vimeo/psalm": "^4.11" }, @@ -439,7 +495,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/1.2.2" + "source": "https://github.com/doctrine/lexer/tree/1.2.3" }, "funding": [ { @@ -455,7 +511,7 @@ "type": "tidelift" } ], - "time": "2022-01-12T08:27:12+00:00" + "time": "2022-02-28T11:07:21+00:00" }, { "name": "dompdf/dompdf", @@ -672,12 +728,12 @@ }, "type": "library", "autoload": { - "psr-0": { - "HTMLPurifier": "library/" - }, "files": [ "library/HTMLPurifier.composer.php" ], + "psr-0": { + "HTMLPurifier": "library/" + }, "exclude-from-classmap": [ "/library/HTMLPurifier/Language/" ] @@ -704,6 +760,59 @@ }, "time": "2021-12-25T01:21:49+00:00" }, + { + "name": "facade/ignition-contracts", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/facade/ignition-contracts.git", + "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/3c921a1cdba35b68a7f0ccffc6dffc1995b18267", + "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^v2.15.8", + "phpunit/phpunit": "^9.3.11", + "vimeo/psalm": "^3.17.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Facade\\IgnitionContracts\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://flareapp.io", + "role": "Developer" + } + ], + "description": "Solution contracts for Ignition", + "homepage": "https://github.com/facade/ignition-contracts", + "keywords": [ + "contracts", + "flare", + "ignition" + ], + "support": { + "issues": "https://github.com/facade/ignition-contracts/issues", + "source": "https://github.com/facade/ignition-contracts/tree/1.0.2" + }, + "time": "2020-10-16T08:27:54+00:00" + }, { "name": "firebase/php-jwt", "version": "v5.5.1", @@ -763,20 +872,20 @@ }, { "name": "fruitcake/laravel-cors", - "version": "v2.1.0", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/fruitcake/laravel-cors.git", - "reference": "361d71f00a0eea8b74da26ae75d0d207c53aa5b3" + "reference": "783a74f5e3431d7b9805be8afb60fd0a8f743534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/laravel-cors/zipball/361d71f00a0eea8b74da26ae75d0d207c53aa5b3", - "reference": "361d71f00a0eea8b74da26ae75d0d207c53aa5b3", + "url": "https://api.github.com/repos/fruitcake/laravel-cors/zipball/783a74f5e3431d7b9805be8afb60fd0a8f743534", + "reference": "783a74f5e3431d7b9805be8afb60fd0a8f743534", "shasum": "" }, "require": { - "fruitcake/php-cors": "^1", + "asm89/stack-cors": "^2.0.1", "illuminate/contracts": "^6|^7|^8|^9", "illuminate/support": "^6|^7|^8|^9", "php": ">=7.2" @@ -826,7 +935,7 @@ ], "support": { "issues": "https://github.com/fruitcake/laravel-cors/issues", - "source": "https://github.com/fruitcake/laravel-cors/tree/v2.1.0" + "source": "https://github.com/fruitcake/laravel-cors/tree/v2.2.0" }, "funding": [ { @@ -838,7 +947,7 @@ "type": "github" } ], - "time": "2022-02-19T14:17:28+00:00" + "time": "2022-02-23T14:25:13+00:00" }, { "name": "fruitcake/php-cors", @@ -984,16 +1093,16 @@ }, { "name": "google/apiclient-services", - "version": "v0.236.0", + "version": "v0.237.0", "source": { "type": "git", "url": "https://github.com/googleapis/google-api-php-client-services.git", - "reference": "3aefd2914d9025a881ee93145de16f9e0f82443e" + "reference": "c10652adc29b4242237075acde318e83f88ab918" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/3aefd2914d9025a881ee93145de16f9e0f82443e", - "reference": "3aefd2914d9025a881ee93145de16f9e0f82443e", + "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/c10652adc29b4242237075acde318e83f88ab918", + "reference": "c10652adc29b4242237075acde318e83f88ab918", "shasum": "" }, "require": { @@ -1022,9 +1131,9 @@ ], "support": { "issues": "https://github.com/googleapis/google-api-php-client-services/issues", - "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.236.0" + "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.237.0" }, - "time": "2022-02-07T14:04:26+00:00" + "time": "2022-02-23T22:58:02+00:00" }, { "name": "google/auth", @@ -1295,12 +1404,12 @@ } }, "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, "files": [ "src/functions_include.php" - ] + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1622,16 +1731,16 @@ }, { "name": "laravel/framework", - "version": "v9.1.0", + "version": "v9.2.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "ca7ddd4782f120ae50569d04eb9e40e52f67a9d9" + "reference": "13372872bed31ae75df8709b9de5cde01d50646e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/ca7ddd4782f120ae50569d04eb9e40e52f67a9d9", - "reference": "ca7ddd4782f120ae50569d04eb9e40e52f67a9d9", + "url": "https://api.github.com/repos/laravel/framework/zipball/13372872bed31ae75df8709b9de5cde01d50646e", + "reference": "13372872bed31ae75df8709b9de5cde01d50646e", "shasum": "" }, "require": { @@ -1640,6 +1749,7 @@ "egulias/email-validator": "^3.1", "ext-mbstring": "*", "ext-openssl": "*", + "fruitcake/php-cors": "^1.2", "laravel/serializable-closure": "^1.0", "league/commonmark": "^2.2", "league/flysystem": "^3.0", @@ -1714,7 +1824,7 @@ "league/flysystem-ftp": "^3.0", "league/flysystem-sftp-v3": "^3.0", "mockery/mockery": "^1.4.4", - "orchestra/testbench-core": "^7.0", + "orchestra/testbench-core": "^7.1", "pda/pheanstalk": "^4.0", "phpstan/phpstan": "^1.0", "phpunit/phpunit": "^9.5.8", @@ -1796,20 +1906,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2022-02-15T15:06:44+00:00" + "time": "2022-02-22T15:30:23+00:00" }, { "name": "laravel/sanctum", - "version": "v2.14.1", + "version": "v2.14.2", "source": { "type": "git", "url": "https://github.com/laravel/sanctum.git", - "reference": "89937617fa144ddb759a740861a47c4f2fd2245b" + "reference": "dc5d749ba9bfcfd68d8f5c272238f88bea223e66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/89937617fa144ddb759a740861a47c4f2fd2245b", - "reference": "89937617fa144ddb759a740861a47c4f2fd2245b", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/dc5d749ba9bfcfd68d8f5c272238f88bea223e66", + "reference": "dc5d749ba9bfcfd68d8f5c272238f88bea223e66", "shasum": "" }, "require": { @@ -1860,7 +1970,7 @@ "issues": "https://github.com/laravel/sanctum/issues", "source": "https://github.com/laravel/sanctum" }, - "time": "2022-02-15T08:08:57+00:00" + "time": "2022-02-16T14:40:23+00:00" }, { "name": "laravel/serializable-closure", @@ -2291,16 +2401,16 @@ }, { "name": "league/commonmark", - "version": "2.2.2", + "version": "2.2.3", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "13d7751377732637814f0cda0e3f6d3243f9f769" + "reference": "47b015bc4e50fd4438c1ffef6139a1fb65d2ab71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/13d7751377732637814f0cda0e3f6d3243f9f769", - "reference": "13d7751377732637814f0cda0e3f6d3243f9f769", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/47b015bc4e50fd4438c1ffef6139a1fb65d2ab71", + "reference": "47b015bc4e50fd4438c1ffef6139a1fb65d2ab71", "shasum": "" }, "require": { @@ -2391,7 +2501,7 @@ "type": "tidelift" } ], - "time": "2022-02-13T15:00:57+00:00" + "time": "2022-02-26T21:24:45+00:00" }, { "name": "league/config", @@ -2477,16 +2587,16 @@ }, { "name": "league/flysystem", - "version": "3.0.8", + "version": "3.0.10", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "30f2c7069b2625da5b126ac66cbea7618a3db8b6" + "reference": "bbc5026adb5a423dfcdcecec74c7e15943ff6115" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/30f2c7069b2625da5b126ac66cbea7618a3db8b6", - "reference": "30f2c7069b2625da5b126ac66cbea7618a3db8b6", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/bbc5026adb5a423dfcdcecec74c7e15943ff6115", + "reference": "bbc5026adb5a423dfcdcecec74c7e15943ff6115", "shasum": "" }, "require": { @@ -2494,6 +2604,7 @@ "php": "^8.0.2" }, "conflict": { + "aws/aws-sdk-php": "3.209.31 || 3.210.0", "guzzlehttp/guzzle": "<7.0", "guzzlehttp/ringphp": "<1.1.1", "symfony/http-client": "<5.2" @@ -2546,7 +2657,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.0.8" + "source": "https://github.com/thephpleague/flysystem/tree/3.0.10" }, "funding": [ { @@ -2562,7 +2673,7 @@ "type": "tidelift" } ], - "time": "2022-02-16T18:51:54+00:00" + "time": "2022-02-26T11:09:13+00:00" }, { "name": "league/mime-type-detection", @@ -2698,16 +2809,16 @@ }, { "name": "maatwebsite/excel", - "version": "3.1.36", + "version": "3.1.37", "source": { "type": "git", "url": "https://github.com/SpartnerNL/Laravel-Excel.git", - "reference": "eb31f30d72c51c3fb11644b636945accbe50404f" + "reference": "49ccd4142d3d7bce492d6bfb9dd9a27b12935408" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/eb31f30d72c51c3fb11644b636945accbe50404f", - "reference": "eb31f30d72c51c3fb11644b636945accbe50404f", + "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/49ccd4142d3d7bce492d6bfb9dd9a27b12935408", + "reference": "49ccd4142d3d7bce492d6bfb9dd9a27b12935408", "shasum": "" }, "require": { @@ -2760,7 +2871,7 @@ ], "support": { "issues": "https://github.com/SpartnerNL/Laravel-Excel/issues", - "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.36" + "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.37" }, "funding": [ { @@ -2772,7 +2883,7 @@ "type": "github" } ], - "time": "2022-01-27T18:34:20+00:00" + "time": "2022-02-28T10:20:04+00:00" }, { "name": "maennchen/zipstream-php", @@ -4441,16 +4552,16 @@ }, { "name": "psy/psysh", - "version": "v0.11.1", + "version": "v0.11.2", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "570292577277f06f590635381a7f761a6cf4f026" + "reference": "7f7da640d68b9c9fec819caae7c744a213df6514" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/570292577277f06f590635381a7f761a6cf4f026", - "reference": "570292577277f06f590635381a7f761a6cf4f026", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/7f7da640d68b9c9fec819caae7c744a213df6514", + "reference": "7f7da640d68b9c9fec819caae7c744a213df6514", "shasum": "" }, "require": { @@ -4461,6 +4572,9 @@ "symfony/console": "^6.0 || ^5.0 || ^4.0 || ^3.4", "symfony/var-dumper": "^6.0 || ^5.0 || ^4.0 || ^3.4" }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, "require-dev": { "bamarni/composer-bin-plugin": "^1.2", "hoa/console": "3.17.05.02" @@ -4510,9 +4624,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.11.1" + "source": "https://github.com/bobthecow/psysh/tree/v0.11.2" }, - "time": "2022-01-03T13:58:38+00:00" + "time": "2022-02-28T15:28:54+00:00" }, { "name": "ralouphie/getallheaders", @@ -4702,12 +4816,12 @@ } }, "autoload": { - "psr-4": { - "Ramsey\\Uuid\\": "src/" - }, "files": [ "src/functions.php" - ] + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4864,17 +4978,150 @@ "time": "2022-01-13T09:43:27+00:00" }, { - "name": "symfony/console", - "version": "v6.0.3", + "name": "spatie/laravel-model-states", + "version": "2.1.4", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "22e8efd019c3270c4f79376234a3f8752cd25490" + "url": "https://github.com/spatie/laravel-model-states.git", + "reference": "c9c4865abd2b5ec534214aede784631366bed7d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/22e8efd019c3270c4f79376234a3f8752cd25490", - "reference": "22e8efd019c3270c4f79376234a3f8752cd25490", + "url": "https://api.github.com/repos/spatie/laravel-model-states/zipball/c9c4865abd2b5ec534214aede784631366bed7d4", + "reference": "c9c4865abd2b5ec534214aede784631366bed7d4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "facade/ignition-contracts": "^1.0", + "illuminate/contracts": "^8.73 | ^9.0", + "illuminate/database": "^8.73 | ^9.0", + "illuminate/support": "^8.73 | ^9.0", + "php": "^7.4|^8.0", + "spatie/laravel-package-tools": "^1.9" + }, + "require-dev": { + "orchestra/testbench": "^6.23 | ^7.0", + "phpunit/phpunit": "^9.4", + "symfony/var-dumper": "^5.3 | ^6.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\ModelStates\\ModelStatesServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\ModelStates\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brent Roose", + "email": "brent@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "State support for Eloquent models", + "homepage": "https://github.com/spatie/laravel-model-states", + "keywords": [ + "spatie", + "state" + ], + "support": { + "source": "https://github.com/spatie/laravel-model-states/tree/2.1.4" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-01-19T19:57:35+00:00" + }, + { + "name": "spatie/laravel-package-tools", + "version": "1.11.2", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-package-tools.git", + "reference": "16a8de828e7f1f32d580c667e1de5bf2943abd6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/16a8de828e7f1f32d580c667e1de5bf2943abd6b", + "reference": "16a8de828e7f1f32d580c667e1de5bf2943abd6b", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^7.0|^8.0|^9.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.4", + "orchestra/testbench": "^5.0|^6.23|^7.0", + "phpunit/phpunit": "^9.4", + "spatie/test-time": "^1.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\LaravelPackageTools\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Tools for creating Laravel packages", + "homepage": "https://github.com/spatie/laravel-package-tools", + "keywords": [ + "laravel-package-tools", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-package-tools/issues", + "source": "https://github.com/spatie/laravel-package-tools/tree/1.11.2" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-02-22T08:55:13+00:00" + }, + { + "name": "symfony/console", + "version": "v6.0.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "3bebf4108b9e07492a2a4057d207aa5a77d146b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/3bebf4108b9e07492a2a4057d207aa5a77d146b1", + "reference": "3bebf4108b9e07492a2a4057d207aa5a77d146b1", "shasum": "" }, "require": { @@ -4940,7 +5187,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.0.3" + "source": "https://github.com/symfony/console/tree/v6.0.5" }, "funding": [ { @@ -4956,7 +5203,7 @@ "type": "tidelift" } ], - "time": "2022-01-26T17:23:29+00:00" + "time": "2022-02-25T10:48:52+00:00" }, { "name": "symfony/css-selector", @@ -5386,16 +5633,16 @@ }, { "name": "symfony/http-foundation", - "version": "v6.0.3", + "version": "v6.0.5", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "ad157299ced81a637fade1efcadd688d6deba5c1" + "reference": "b460fb15905eef449c4c43a4f0c113eccee103b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ad157299ced81a637fade1efcadd688d6deba5c1", - "reference": "ad157299ced81a637fade1efcadd688d6deba5c1", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/b460fb15905eef449c4c43a4f0c113eccee103b9", + "reference": "b460fb15905eef449c4c43a4f0c113eccee103b9", "shasum": "" }, "require": { @@ -5438,7 +5685,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.0.3" + "source": "https://github.com/symfony/http-foundation/tree/v6.0.5" }, "funding": [ { @@ -5454,20 +5701,20 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2022-02-21T17:15:17+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.0.4", + "version": "v6.0.5", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "9dce179ce52b0f4f669c07fd5e465e5d809a5d3b" + "reference": "5ad3f5e5fa772a8b5c6bb217f8379b533afac2ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9dce179ce52b0f4f669c07fd5e465e5d809a5d3b", - "reference": "9dce179ce52b0f4f669c07fd5e465e5d809a5d3b", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/5ad3f5e5fa772a8b5c6bb217f8379b533afac2ba", + "reference": "5ad3f5e5fa772a8b5c6bb217f8379b533afac2ba", "shasum": "" }, "require": { @@ -5547,7 +5794,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.0.4" + "source": "https://github.com/symfony/http-kernel/tree/v6.0.5" }, "funding": [ { @@ -5563,20 +5810,20 @@ "type": "tidelift" } ], - "time": "2022-01-29T18:12:46+00:00" + "time": "2022-02-28T08:05:03+00:00" }, { "name": "symfony/mailer", - "version": "v6.0.3", + "version": "v6.0.5", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "d958befe7dbee9d2b2157ef6dfa9b103efa94f82" + "reference": "0f4772db6521a1beb44529aa2c0c1e56f671be8f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/d958befe7dbee9d2b2157ef6dfa9b103efa94f82", - "reference": "d958befe7dbee9d2b2157ef6dfa9b103efa94f82", + "url": "https://api.github.com/repos/symfony/mailer/zipball/0f4772db6521a1beb44529aa2c0c1e56f671be8f", + "reference": "0f4772db6521a1beb44529aa2c0c1e56f671be8f", "shasum": "" }, "require": { @@ -5621,7 +5868,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v6.0.3" + "source": "https://github.com/symfony/mailer/tree/v6.0.5" }, "funding": [ { @@ -5637,7 +5884,7 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2022-02-25T10:48:52+00:00" }, { "name": "symfony/mime", @@ -5754,12 +6001,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6377,16 +6624,16 @@ }, { "name": "symfony/process", - "version": "v6.0.3", + "version": "v6.0.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "298ed357274c1868c20a0061df256a1250a6c4af" + "reference": "1ccceccc6497e96f4f646218f04b97ae7d9fa7a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/298ed357274c1868c20a0061df256a1250a6c4af", - "reference": "298ed357274c1868c20a0061df256a1250a6c4af", + "url": "https://api.github.com/repos/symfony/process/zipball/1ccceccc6497e96f4f646218f04b97ae7d9fa7a1", + "reference": "1ccceccc6497e96f4f646218f04b97ae7d9fa7a1", "shasum": "" }, "require": { @@ -6418,7 +6665,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.0.3" + "source": "https://github.com/symfony/process/tree/v6.0.5" }, "funding": [ { @@ -6434,20 +6681,20 @@ "type": "tidelift" } ], - "time": "2022-01-26T17:23:29+00:00" + "time": "2022-01-30T18:19:12+00:00" }, { "name": "symfony/routing", - "version": "v6.0.3", + "version": "v6.0.5", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "b1debdf7a40e6bc7eee0f363ab9dd667fe04f099" + "reference": "a738b152426ac7fcb94bdab8188264652238bef1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/b1debdf7a40e6bc7eee0f363ab9dd667fe04f099", - "reference": "b1debdf7a40e6bc7eee0f363ab9dd667fe04f099", + "url": "https://api.github.com/repos/symfony/routing/zipball/a738b152426ac7fcb94bdab8188264652238bef1", + "reference": "a738b152426ac7fcb94bdab8188264652238bef1", "shasum": "" }, "require": { @@ -6506,7 +6753,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.0.3" + "source": "https://github.com/symfony/routing/tree/v6.0.5" }, "funding": [ { @@ -6522,7 +6769,7 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2022-01-31T19:46:53+00:00" }, { "name": "symfony/service-contracts", @@ -6638,12 +6885,12 @@ }, "type": "library", "autoload": { - "psr-4": { - "Symfony\\Component\\String\\": "" - }, "files": [ "Resources/functions.php" ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, "exclude-from-classmap": [ "/Tests/" ] @@ -6693,16 +6940,16 @@ }, { "name": "symfony/translation", - "version": "v6.0.3", + "version": "v6.0.5", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "71bb15335798f8c4da110911bcf2d2fead7a430d" + "reference": "e69501c71107cc3146b32aaa45f4edd0c3427875" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/71bb15335798f8c4da110911bcf2d2fead7a430d", - "reference": "71bb15335798f8c4da110911bcf2d2fead7a430d", + "url": "https://api.github.com/repos/symfony/translation/zipball/e69501c71107cc3146b32aaa45f4edd0c3427875", + "reference": "e69501c71107cc3146b32aaa45f4edd0c3427875", "shasum": "" }, "require": { @@ -6768,7 +7015,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.0.3" + "source": "https://github.com/symfony/translation/tree/v6.0.5" }, "funding": [ { @@ -6784,7 +7031,7 @@ "type": "tidelift" } ], - "time": "2022-01-07T00:29:03+00:00" + "time": "2022-02-09T15:52:48+00:00" }, { "name": "symfony/translation-contracts", @@ -6866,16 +7113,16 @@ }, { "name": "symfony/var-dumper", - "version": "v6.0.3", + "version": "v6.0.5", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "7b701676fc64f9ef11f9b4870f16b48f66be4834" + "reference": "60d6a756d5f485df5e6e40b337334848f79f61ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/7b701676fc64f9ef11f9b4870f16b48f66be4834", - "reference": "7b701676fc64f9ef11f9b4870f16b48f66be4834", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/60d6a756d5f485df5e6e40b337334848f79f61ce", + "reference": "60d6a756d5f485df5e6e40b337334848f79f61ce", "shasum": "" }, "require": { @@ -6934,7 +7181,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.0.3" + "source": "https://github.com/symfony/var-dumper/tree/v6.0.5" }, "funding": [ { @@ -6950,7 +7197,7 @@ "type": "tidelift" } ], - "time": "2022-01-17T16:30:44+00:00" + "time": "2022-02-21T17:15:17+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -7335,59 +7582,6 @@ ], "time": "2020-11-10T18:47:58+00:00" }, - { - "name": "facade/ignition-contracts", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/facade/ignition-contracts.git", - "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/3c921a1cdba35b68a7f0ccffc6dffc1995b18267", - "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267", - "shasum": "" - }, - "require": { - "php": "^7.3|^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^v2.15.8", - "phpunit/phpunit": "^9.3.11", - "vimeo/psalm": "^3.17.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Facade\\IgnitionContracts\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Freek Van der Herten", - "email": "freek@spatie.be", - "homepage": "https://flareapp.io", - "role": "Developer" - } - ], - "description": "Solution contracts for Ignition", - "homepage": "https://github.com/facade/ignition-contracts", - "keywords": [ - "contracts", - "flare", - "ignition" - ], - "support": { - "issues": "https://github.com/facade/ignition-contracts/issues", - "source": "https://github.com/facade/ignition-contracts/tree/1.0.2" - }, - "time": "2020-10-16T08:27:54+00:00" - }, { "name": "fakerphp/faker", "version": "v1.19.0", @@ -8017,12 +8211,12 @@ }, "type": "library", "autoload": { - "psr-4": { - "Facebook\\WebDriver\\": "lib/" - }, "files": [ "lib/Exception/TimeoutException.php" - ] + ], + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -8272,16 +8466,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.11", + "version": "9.2.14", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "665a1ac0a763c51afc30d6d130dac0813092b17f" + "reference": "9f4d60b6afe5546421462b76cd4e633ebc364ab4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/665a1ac0a763c51afc30d6d130dac0813092b17f", - "reference": "665a1ac0a763c51afc30d6d130dac0813092b17f", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/9f4d60b6afe5546421462b76cd4e633ebc364ab4", + "reference": "9f4d60b6afe5546421462b76cd4e633ebc364ab4", "shasum": "" }, "require": { @@ -8337,7 +8531,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.11" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.14" }, "funding": [ { @@ -8345,7 +8539,7 @@ "type": "github" } ], - "time": "2022-02-18T12:46:09+00:00" + "time": "2022-02-28T12:38:02+00:00" }, { "name": "phpunit/php-file-iterator", @@ -8590,16 +8784,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.14", + "version": "9.5.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1883687169c017d6ae37c58883ca3994cfc34189" + "reference": "5ff8c545a50226c569310a35f4fa89d79f1ddfdc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1883687169c017d6ae37c58883ca3994cfc34189", - "reference": "1883687169c017d6ae37c58883ca3994cfc34189", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5ff8c545a50226c569310a35f4fa89d79f1ddfdc", + "reference": "5ff8c545a50226c569310a35f4fa89d79f1ddfdc", "shasum": "" }, "require": { @@ -8615,7 +8809,7 @@ "phar-io/version": "^3.0.2", "php": ">=7.3", "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.7", + "phpunit/php-code-coverage": "^9.2.13", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.3", @@ -8677,7 +8871,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.14" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.16" }, "funding": [ { @@ -8689,7 +8883,7 @@ "type": "github" } ], - "time": "2022-02-18T12:54:07+00:00" + "time": "2022-02-23T17:10:58+00:00" }, { "name": "sebastian/cli-parser", @@ -9719,16 +9913,16 @@ }, { "name": "spatie/flare-client-php", - "version": "1.0.2", + "version": "1.0.5", "source": { "type": "git", "url": "https://github.com/spatie/flare-client-php.git", - "reference": "5d48e00716e3bab813cafffe223bc85c5732a410" + "reference": "8ada1e5f4d7a2869f491c5e75d1f689b69db423e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/5d48e00716e3bab813cafffe223bc85c5732a410", - "reference": "5d48e00716e3bab813cafffe223bc85c5732a410", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/8ada1e5f4d7a2869f491c5e75d1f689b69db423e", + "reference": "8ada1e5f4d7a2869f491c5e75d1f689b69db423e", "shasum": "" }, "require": { @@ -9771,7 +9965,7 @@ ], "support": { "issues": "https://github.com/spatie/flare-client-php/issues", - "source": "https://github.com/spatie/flare-client-php/tree/1.0.2" + "source": "https://github.com/spatie/flare-client-php/tree/1.0.5" }, "funding": [ { @@ -9779,7 +9973,7 @@ "type": "github" } ], - "time": "2022-02-16T16:14:24+00:00" + "time": "2022-03-01T10:52:59+00:00" }, { "name": "spatie/ignition", diff --git a/config/model-states.php b/config/model-states.php new file mode 100644 index 0000000..8d058b4 --- /dev/null +++ b/config/model-states.php @@ -0,0 +1,7 @@ + Spatie\ModelStates\DefaultTransition::class, +]; diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 23f0ea4..0b7d9d6 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -28,4 +28,25 @@ class UserFactory extends Factory "remember_token" => Str::random(10), ]; } + + public function admin(): static + { + return $this->state([ + "role" => Role::Administrator, + ]); + } + + public function technicalApprover(): static + { + return $this->state([ + "role" => Role::TechnicalApprover, + ]); + } + + public function administrativeApprover(): static + { + return $this->state([ + "role" => Role::AdministrativeApprover, + ]); + } } diff --git a/database/factories/VacationRequestFactory.php b/database/factories/VacationRequestFactory.php index a0fb00c..f9689b7 100644 --- a/database/factories/VacationRequestFactory.php +++ b/database/factories/VacationRequestFactory.php @@ -6,8 +6,8 @@ namespace Database\Factories; use Carbon\CarbonImmutable; use Illuminate\Database\Eloquent\Factories\Factory; -use Toby\Domain\Enums\VacationRequestState; use Toby\Domain\Enums\VacationType; +use Toby\Domain\VacationRequestStatesRetriever; use Toby\Eloquent\Models\User; use Toby\Eloquent\Models\VacationRequest; use Toby\Eloquent\Models\YearPeriod; @@ -28,7 +28,7 @@ class VacationRequestFactory extends Factory "year_period_id" => YearPeriod::factory(), "name" => fn(array $attributes): string => $this->generateName($attributes), "type" => $this->faker->randomElement(VacationType::cases()), - "state" => $this->faker->randomElement(VacationRequestState::cases()), + "state" => $this->faker->randomElement(VacationRequestStatesRetriever::all()), "from" => $from, "to" => $from->addDays($days), "comment" => $this->faker->boolean ? $this->faker->paragraph() : null, diff --git a/resources/js/Pages/Calendar.vue b/resources/js/Pages/Calendar.vue index 1a8e095..b0432f7 100644 --- a/resources/js/Pages/Calendar.vue +++ b/resources/js/Pages/Calendar.vue @@ -7,7 +7,7 @@ Kalendarz urlopów -
+
null, }, + can: { + type: Object, + default: () => null, + }, }, setup(props) { const {getMonths, findMonth} = useMonthInfo() diff --git a/resources/js/Pages/Holidays/Index.vue b/resources/js/Pages/Holidays/Index.vue index bd71b55..be5d0bb 100644 --- a/resources/js/Pages/Holidays/Index.vue +++ b/resources/js/Pages/Holidays/Index.vue @@ -10,7 +10,7 @@ Lista dni wolnych od pracy w danym roku

-
+
@@ -159,6 +160,10 @@ export default { type: Object, default: () => null, }, + can: { + type: Object, + default: () => null, + }, }, setup() { return {} diff --git a/resources/js/Pages/VacationRequest/Create.vue b/resources/js/Pages/VacationRequest/Create.vue index d36105c..1993b87 100644 --- a/resources/js/Pages/VacationRequest/Create.vue +++ b/resources/js/Pages/VacationRequest/Create.vue @@ -29,6 +29,7 @@
-
+
-
+

Zaakceptuj wniosek jako osoba techniczna @@ -134,7 +137,10 @@

-
+

Zaakceptuj wniosek jako osoba administracyjna @@ -157,7 +163,10 @@

-
+

Odrzuć wniosek @@ -180,7 +189,10 @@

-
+

Anuluj wniosek @@ -246,6 +258,10 @@ export default { type: Object, default: () => null, }, + can: { + type: Object, + default: () => null, + }, activities: { type: Object, default: () => null, diff --git a/resources/js/Shared/MainMenu.vue b/resources/js/Shared/MainMenu.vue index 28f501c..1201ff3 100644 --- a/resources/js/Shared/MainMenu.vue +++ b/resources/js/Shared/MainMenu.vue @@ -213,12 +213,12 @@ > Avatar