Compare commits
1 Commits
main
...
#17-dusk-t
Author | SHA1 | Date | |
---|---|---|---|
![]() |
03f1295194 |
@ -19,8 +19,8 @@ DB_PASSWORD=password
|
|||||||
|
|
||||||
BROADCAST_DRIVER=log
|
BROADCAST_DRIVER=log
|
||||||
CACHE_DRIVER=array
|
CACHE_DRIVER=array
|
||||||
QUEUE_CONNECTION=redis
|
QUEUE_CONNECTION=sync
|
||||||
SESSION_DRIVER=redis
|
SESSION_DRIVER=file
|
||||||
SESSION_LIFETIME=120
|
SESSION_LIFETIME=120
|
||||||
FILESYSTEM_DISK=local
|
FILESYSTEM_DISK=local
|
||||||
MAIL_MAILER=array
|
MAIL_MAILER=array
|
||||||
|
@ -57,13 +57,6 @@ DOCKER_INSTALL_XDEBUG=false
|
|||||||
|
|
||||||
GOOGLE_CLIENT_ID=
|
GOOGLE_CLIENT_ID=
|
||||||
GOOGLE_CLIENT_SECRET=
|
GOOGLE_CLIENT_SECRET=
|
||||||
GOOGLE_CALENDAR_ENABLED=true
|
|
||||||
GOOGLE_REDIRECT=http://localhost/login/google/end
|
GOOGLE_REDIRECT=http://localhost/login/google/end
|
||||||
GOOGLE_CALENDAR_ID=
|
GOOGLE_CALENDAR_ID=
|
||||||
LOCAL_EMAIL_FOR_LOGIN_VIA_GOOGLE=
|
LOCAL_EMAIL_FOR_LOGIN_VIA_GOOGLE=
|
||||||
|
|
||||||
SLACK_URL=https://slack.com/api
|
|
||||||
SLACK_ENABLED=true
|
|
||||||
SLACK_CLIENT_TOKEN=
|
|
||||||
SLACK_SIGNING_SECRET=
|
|
||||||
SLACK_DEFAULT_CHANNEL="#general"
|
|
||||||
|
@ -13,8 +13,5 @@ module.exports = {
|
|||||||
indent: ['error', 2],
|
indent: ['error', 2],
|
||||||
'vue/html-indent': ['error', 2],
|
'vue/html-indent': ['error', 2],
|
||||||
'comma-dangle': ['error', 'always-multiline'],
|
'comma-dangle': ['error', 'always-multiline'],
|
||||||
'object-curly-spacing': ['error', 'always'],
|
|
||||||
'vue/require-default-prop': 0,
|
|
||||||
'vue/multi-word-component-names': 0,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -1 +0,0 @@
|
|||||||
* @blumilksoftware/toby
|
|
17
.github/workflows/deploy.yml
vendored
17
.github/workflows/deploy.yml
vendored
@ -1,17 +0,0 @@
|
|||||||
name: Deploy
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- v*
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: akhileshns/heroku-deploy@v3.12.12
|
|
||||||
with:
|
|
||||||
heroku_api_key: ${{secrets.HEROKU_API_KEY}}
|
|
||||||
heroku_app_name: ${{secrets.HEROKU_APP_NAME}}
|
|
||||||
heroku_email: ${{secrets.HEROKU_EMAIL}}
|
|
2
.github/workflows/test-and-lint-php.yml
vendored
2
.github/workflows/test-and-lint-php.yml
vendored
@ -45,7 +45,7 @@ jobs:
|
|||||||
run: composer install --prefer-dist --no-interaction --no-suggest
|
run: composer install --prefer-dist --no-interaction --no-suggest
|
||||||
|
|
||||||
- name: Run PHP linter
|
- name: Run PHP linter
|
||||||
run: composer cs
|
run: composer ecs
|
||||||
|
|
||||||
- name: Execute tests
|
- name: Execute tests
|
||||||
run: php artisan test --env=ci
|
run: php artisan test --env=ci
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,6 +15,5 @@ Homestead.json
|
|||||||
Homestead.yaml
|
Homestead.yaml
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
google-credentials.json
|
|
||||||
.idea/
|
.idea/
|
||||||
.composer
|
.composer
|
||||||
|
3
Procfile
3
Procfile
@ -1,3 +0,0 @@
|
|||||||
web: vendor/bin/heroku-php-nginx -C environment/prod/nginx.conf public/
|
|
||||||
release: php artisan config:cache && php artisan route:cache && php artisan migrate --force
|
|
||||||
worker: php artisan queue:work
|
|
@ -5,9 +5,6 @@ declare(strict_types=1);
|
|||||||
namespace Toby\Architecture;
|
namespace Toby\Architecture;
|
||||||
|
|
||||||
use Illuminate\Foundation\Exceptions\Handler;
|
use Illuminate\Foundation\Exceptions\Handler;
|
||||||
use Inertia\Inertia;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
class ExceptionHandler extends Handler
|
class ExceptionHandler extends Handler
|
||||||
{
|
{
|
||||||
@ -16,36 +13,4 @@ class ExceptionHandler extends Handler
|
|||||||
"password",
|
"password",
|
||||||
"password_confirmation",
|
"password_confirmation",
|
||||||
];
|
];
|
||||||
protected array $handleByInertia = [
|
|
||||||
Response::HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
Response::HTTP_SERVICE_UNAVAILABLE,
|
|
||||||
Response::HTTP_TOO_MANY_REQUESTS,
|
|
||||||
419, // CSRF
|
|
||||||
Response::HTTP_NOT_FOUND,
|
|
||||||
Response::HTTP_FORBIDDEN,
|
|
||||||
Response::HTTP_UNAUTHORIZED,
|
|
||||||
];
|
|
||||||
|
|
||||||
public function render($request, Throwable $e): Response
|
|
||||||
{
|
|
||||||
$response = parent::render($request, $e);
|
|
||||||
|
|
||||||
if (!app()->environment("production")) {
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($response->status() === Response::HTTP_METHOD_NOT_ALLOWED) {
|
|
||||||
$response->setStatusCode(Response::HTTP_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_array($response->status(), $this->handleByInertia, true)) {
|
|
||||||
return Inertia::render("Error", [
|
|
||||||
"status" => $response->status(),
|
|
||||||
])
|
|
||||||
->toResponse($request)
|
|
||||||
->setStatusCode($response->status());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,24 +4,13 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Toby\Architecture\Providers;
|
namespace Toby\Architecture\Providers;
|
||||||
|
|
||||||
use Illuminate\Contracts\Foundation\Application;
|
|
||||||
use Illuminate\Notifications\ChannelManager;
|
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\Notification;
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Toby\Infrastructure\Slack\Channels\SlackApiChannel;
|
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
public function register(): void
|
|
||||||
{
|
|
||||||
Notification::resolved(function (ChannelManager $service): void {
|
|
||||||
$service->extend("slack", fn(Application $app): SlackApiChannel => $app->make(SlackApiChannel::class));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
Carbon::macro("toDisplayString", fn(): string => $this->translatedFormat("d.m.Y"));
|
Carbon::macro("toDisplayString", fn() => $this->translatedFormat("d.m.Y"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,7 @@ namespace Toby\Architecture\Providers;
|
|||||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
use Toby\Domain\Enums\Role;
|
use Toby\Domain\Enums\Role;
|
||||||
use Toby\Domain\Policies\KeyPolicy;
|
|
||||||
use Toby\Domain\Policies\VacationRequestPolicy;
|
use Toby\Domain\Policies\VacationRequestPolicy;
|
||||||
use Toby\Eloquent\Models\Key;
|
|
||||||
use Toby\Eloquent\Models\User;
|
use Toby\Eloquent\Models\User;
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
@ -17,7 +15,6 @@ class AuthServiceProvider extends ServiceProvider
|
|||||||
{
|
{
|
||||||
protected $policies = [
|
protected $policies = [
|
||||||
VacationRequest::class => VacationRequestPolicy::class,
|
VacationRequest::class => VacationRequestPolicy::class,
|
||||||
Key::class => KeyPolicy::class,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
@ -30,11 +27,9 @@ class AuthServiceProvider extends ServiceProvider
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Gate::define("manageUsers", fn(User $user): bool => $user->role === Role::AdministrativeApprover);
|
Gate::define("manageUsers", fn(User $user) => $user->role === Role::AdministrativeApprover);
|
||||||
Gate::define("manageHolidays", fn(User $user): bool => $user->role === Role::AdministrativeApprover);
|
Gate::define("manageHolidays", fn(User $user) => $user->role === Role::AdministrativeApprover);
|
||||||
Gate::define("manageVacationLimits", fn(User $user): bool => $user->role === Role::AdministrativeApprover);
|
Gate::define("manageVacationLimits", fn(User $user) => $user->role === Role::AdministrativeApprover);
|
||||||
Gate::define("generateTimesheet", fn(User $user): bool => $user->role === Role::AdministrativeApprover);
|
Gate::define("generateTimesheet", fn(User $user) => $user->role === Role::AdministrativeApprover);
|
||||||
Gate::define("listMonthlyUsage", fn(User $user): bool => $user->role === Role::AdministrativeApprover);
|
|
||||||
Gate::define("manageResumes", fn(User $user): bool => $user->role === Role::TechnicalApprover);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
24
app/Architecture/Providers/DuskServiceProvider.php
Normal file
24
app/Architecture/Providers/DuskServiceProvider.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Architecture\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Laravel\Dusk\Browser;
|
||||||
|
|
||||||
|
class DuskServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
public function boot(): void
|
||||||
|
{
|
||||||
|
Browser::macro("fillMonth", function ($month) {
|
||||||
|
$this->select("div.flatpickr-calendar > div.flatpickr-months select", $month - 1);
|
||||||
|
return $this;
|
||||||
|
});
|
||||||
|
|
||||||
|
Browser::macro("fillDay", function ($day) {
|
||||||
|
$this->click("div.flatpickr-calendar.animate.arrowTop.arrowLeft.open > div.flatpickr-innerContainer > div > div.flatpickr-days > div > span:nth-child({$day})");
|
||||||
|
return $this;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -5,8 +5,39 @@ declare(strict_types=1);
|
|||||||
namespace Toby\Architecture\Providers;
|
namespace Toby\Architecture\Providers;
|
||||||
|
|
||||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||||
|
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\Listeners\CreateVacationRequestActivity;
|
||||||
|
use Toby\Domain\Listeners\HandleAcceptedByAdministrativeVacationRequest;
|
||||||
|
use Toby\Domain\Listeners\HandleAcceptedByTechnicalVacationRequest;
|
||||||
|
use Toby\Domain\Listeners\HandleApprovedVacationRequest;
|
||||||
|
use Toby\Domain\Listeners\HandleCancelledVacationRequest;
|
||||||
|
use Toby\Domain\Listeners\HandleCreatedVacationRequest;
|
||||||
|
use Toby\Domain\Listeners\SendApprovedVacationRequestNotification;
|
||||||
|
use Toby\Domain\Listeners\SendCancelledVacationRequestNotification;
|
||||||
|
use Toby\Domain\Listeners\SendCreatedVacationRequestNotification;
|
||||||
|
use Toby\Domain\Listeners\SendRejectedVacationRequestNotification;
|
||||||
|
use Toby\Domain\Listeners\SendWaitedForAdministrativeVacationRequestNotification;
|
||||||
|
use Toby\Domain\Listeners\SendWaitedForTechnicalVacationRequestNotification;
|
||||||
|
|
||||||
class EventServiceProvider extends ServiceProvider
|
class EventServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
protected $listen = [];
|
protected $listen = [
|
||||||
|
VacationRequestStateChanged::class => [CreateVacationRequestActivity::class],
|
||||||
|
VacationRequestCreated::class => [HandleCreatedVacationRequest::class, SendCreatedVacationRequestNotification::class],
|
||||||
|
VacationRequestAcceptedByTechnical::class => [HandleAcceptedByTechnicalVacationRequest::class],
|
||||||
|
VacationRequestAcceptedByAdministrative::class => [HandleAcceptedByAdministrativeVacationRequest::class],
|
||||||
|
VacationRequestApproved::class => [HandleApprovedVacationRequest::class, SendApprovedVacationRequestNotification::class],
|
||||||
|
VacationRequestRejected::class => [SendRejectedVacationRequestNotification::class],
|
||||||
|
VacationRequestCancelled::class => [HandleCancelledVacationRequest::class, SendCancelledVacationRequestNotification::class],
|
||||||
|
VacationRequestWaitsForTechApproval::class => [SendWaitedForTechnicalVacationRequestNotification::class],
|
||||||
|
VacationRequestWaitsForAdminApproval::class => [SendWaitedForAdministrativeVacationRequestNotification::class],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,17 @@ namespace Toby\Architecture\Providers;
|
|||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Toby\Eloquent\Models\User;
|
use Toby\Eloquent\Models\User;
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
use Toby\Eloquent\Models\YearPeriod;
|
||||||
use Toby\Eloquent\Observers\UserObserver;
|
use Toby\Eloquent\Observers\UserObserver;
|
||||||
use Toby\Eloquent\Observers\VacationRequestObserver;
|
use Toby\Eloquent\Observers\VacationRequestObserver;
|
||||||
|
use Toby\Eloquent\Observers\YearPeriodObserver;
|
||||||
|
|
||||||
class ObserverServiceProvider extends ServiceProvider
|
class ObserverServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
User::observe(UserObserver::class);
|
User::observe(UserObserver::class);
|
||||||
|
YearPeriod::observe(YearPeriodObserver::class);
|
||||||
VacationRequest::observe(VacationRequestObserver::class);
|
VacationRequest::observe(VacationRequestObserver::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,6 @@ class RouteServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
protected function configureRateLimiting(): void
|
protected function configureRateLimiting(): void
|
||||||
{
|
{
|
||||||
RateLimiter::for("api", fn(Request $request): Limit => Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()));
|
RateLimiter::for("api", fn(Request $request) => Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Actions;
|
|
||||||
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
use Toby\Eloquent\Models\YearPeriod;
|
|
||||||
|
|
||||||
class CreateUserAction
|
|
||||||
{
|
|
||||||
public function execute(array $userData, array $profileData): User
|
|
||||||
{
|
|
||||||
$user = new User($userData);
|
|
||||||
|
|
||||||
$user->save();
|
|
||||||
|
|
||||||
$user->profile()->create($profileData);
|
|
||||||
|
|
||||||
$this->createVacationLimitsFor($user);
|
|
||||||
|
|
||||||
return $user;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createVacationLimitsFor(User $user): void
|
|
||||||
{
|
|
||||||
$yearPeriods = YearPeriod::all();
|
|
||||||
|
|
||||||
foreach ($yearPeriods as $yearPeriod) {
|
|
||||||
$user->vacationLimits()->create([
|
|
||||||
"year_period_id" => $yearPeriod->id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Actions;
|
|
||||||
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
|
|
||||||
class UpdateUserAction
|
|
||||||
{
|
|
||||||
public function execute(User $user, array $userData, array $profileData): User
|
|
||||||
{
|
|
||||||
$user->update($userData);
|
|
||||||
|
|
||||||
$user->profile->update($profileData);
|
|
||||||
|
|
||||||
return $user;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Actions\VacationRequest;
|
|
||||||
|
|
||||||
use Toby\Domain\VacationRequestStateManager;
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
|
||||||
|
|
||||||
class AcceptAsAdministrativeAction
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected VacationRequestStateManager $stateManager,
|
|
||||||
protected ApproveAction $approveAction,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function execute(VacationRequest $vacationRequest, User $user): void
|
|
||||||
{
|
|
||||||
$this->stateManager->acceptAsAdministrative($vacationRequest, $user);
|
|
||||||
|
|
||||||
$this->approveAction->execute($vacationRequest);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Actions\VacationRequest;
|
|
||||||
|
|
||||||
use Toby\Domain\VacationRequestStateManager;
|
|
||||||
use Toby\Domain\VacationTypeConfigRetriever;
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
|
||||||
|
|
||||||
class AcceptAsTechnicalAction
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected VacationRequestStateManager $stateManager,
|
|
||||||
protected VacationTypeConfigRetriever $configRetriever,
|
|
||||||
protected WaitForAdminApprovalAction $waitForAdminApprovalAction,
|
|
||||||
protected ApproveAction $approveAction,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function execute(VacationRequest $vacationRequest, User $user): void
|
|
||||||
{
|
|
||||||
$this->stateManager->acceptAsTechnical($vacationRequest, $user);
|
|
||||||
|
|
||||||
if ($this->configRetriever->needsAdministrativeApproval($vacationRequest->type)) {
|
|
||||||
$this->waitForAdminApprovalAction->execute($vacationRequest);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->approveAction->execute($vacationRequest);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Actions\VacationRequest;
|
|
||||||
|
|
||||||
use Toby\Domain\Enums\Role;
|
|
||||||
use Toby\Domain\Notifications\VacationRequestStatusChangedNotification;
|
|
||||||
use Toby\Domain\VacationRequestStateManager;
|
|
||||||
use Toby\Domain\VacationTypeConfigRetriever;
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
|
||||||
use Toby\Infrastructure\Jobs\SendVacationRequestDaysToGoogleCalendar;
|
|
||||||
|
|
||||||
class ApproveAction
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected VacationRequestStateManager $stateManager,
|
|
||||||
protected VacationTypeConfigRetriever $configRetriever,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function execute(VacationRequest $vacationRequest, ?User $user = null): void
|
|
||||||
{
|
|
||||||
$this->stateManager->approve($vacationRequest, $user);
|
|
||||||
|
|
||||||
if ($this->configRetriever->isVacation($vacationRequest->type)) {
|
|
||||||
SendVacationRequestDaysToGoogleCalendar::dispatch($vacationRequest);
|
|
||||||
|
|
||||||
$this->notify($vacationRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function notify(VacationRequest $vacationRequest): void
|
|
||||||
{
|
|
||||||
$users = User::query()
|
|
||||||
->where("id", "!=", $vacationRequest->user->id)
|
|
||||||
->whereIn("role", [Role::TechnicalApprover, Role::AdministrativeApprover, Role::Administrator])
|
|
||||||
->get();
|
|
||||||
|
|
||||||
foreach ($users as $user) {
|
|
||||||
$user->notify(new VacationRequestStatusChangedNotification($vacationRequest, $user));
|
|
||||||
}
|
|
||||||
|
|
||||||
$vacationRequest->user->notify(new VacationRequestStatusChangedNotification($vacationRequest, $vacationRequest->user));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Actions\VacationRequest;
|
|
||||||
|
|
||||||
use Toby\Domain\Enums\Role;
|
|
||||||
use Toby\Domain\Notifications\VacationRequestStatusChangedNotification;
|
|
||||||
use Toby\Domain\VacationRequestStateManager;
|
|
||||||
use Toby\Domain\VacationTypeConfigRetriever;
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
|
||||||
use Toby\Infrastructure\Jobs\ClearVacationRequestDaysInGoogleCalendar;
|
|
||||||
|
|
||||||
class CancelAction
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected VacationRequestStateManager $stateManager,
|
|
||||||
protected VacationTypeConfigRetriever $configRetriever,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function execute(VacationRequest $vacationRequest, User $user): void
|
|
||||||
{
|
|
||||||
$this->stateManager->cancel($vacationRequest, $user);
|
|
||||||
|
|
||||||
ClearVacationRequestDaysInGoogleCalendar::dispatch($vacationRequest);
|
|
||||||
|
|
||||||
if ($this->configRetriever->isVacation($vacationRequest->type)) {
|
|
||||||
$this->notify($vacationRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function notify(VacationRequest $vacationRequest): void
|
|
||||||
{
|
|
||||||
$users = User::query()
|
|
||||||
->where("id", "!=", $vacationRequest->user->id)
|
|
||||||
->whereIn("role", [Role::TechnicalApprover, Role::AdministrativeApprover, Role::Administrator])
|
|
||||||
->get();
|
|
||||||
|
|
||||||
foreach ($users as $user) {
|
|
||||||
$user->notify(new VacationRequestStatusChangedNotification($vacationRequest, $user));
|
|
||||||
}
|
|
||||||
|
|
||||||
$vacationRequest->user->notify(new VacationRequestStatusChangedNotification($vacationRequest, $vacationRequest->user));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Actions\VacationRequest;
|
|
||||||
|
|
||||||
use Illuminate\Validation\ValidationException;
|
|
||||||
use Toby\Domain\Notifications\VacationRequestCreatedNotification;
|
|
||||||
use Toby\Domain\VacationRequestStateManager;
|
|
||||||
use Toby\Domain\VacationTypeConfigRetriever;
|
|
||||||
use Toby\Domain\Validation\VacationRequestValidator;
|
|
||||||
use Toby\Domain\WorkDaysCalculator;
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
|
||||||
|
|
||||||
class CreateAction
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected VacationRequestStateManager $stateManager,
|
|
||||||
protected VacationRequestValidator $vacationRequestValidator,
|
|
||||||
protected VacationTypeConfigRetriever $configRetriever,
|
|
||||||
protected WorkDaysCalculator $workDaysCalculator,
|
|
||||||
protected WaitForTechApprovalAction $waitForTechApprovalAction,
|
|
||||||
protected WaitForAdminApprovalAction $waitForAdminApprovalAction,
|
|
||||||
protected ApproveAction $approveAction,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws ValidationException
|
|
||||||
*/
|
|
||||||
public function execute(array $data, User $creator): VacationRequest
|
|
||||||
{
|
|
||||||
$vacationRequest = $this->createVacationRequest($data, $creator);
|
|
||||||
$this->handleCreatedVacationRequest($vacationRequest);
|
|
||||||
|
|
||||||
if ($this->configRetriever->isVacation($vacationRequest->type)) {
|
|
||||||
$this->notify($vacationRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $vacationRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws ValidationException
|
|
||||||
*/
|
|
||||||
protected function createVacationRequest(array $data, User $creator): VacationRequest
|
|
||||||
{
|
|
||||||
/** @var VacationRequest $vacationRequest */
|
|
||||||
$vacationRequest = $creator->createdVacationRequests()->make($data);
|
|
||||||
|
|
||||||
$this->vacationRequestValidator->validate($vacationRequest);
|
|
||||||
|
|
||||||
$vacationRequest->save();
|
|
||||||
|
|
||||||
$days = $this->workDaysCalculator->calculateDays($vacationRequest->from, $vacationRequest->to);
|
|
||||||
|
|
||||||
foreach ($days as $day) {
|
|
||||||
$vacationRequest->vacations()->create([
|
|
||||||
"date" => $day,
|
|
||||||
"user_id" => $vacationRequest->user->id,
|
|
||||||
"year_period_id" => $vacationRequest->yearPeriod->id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->stateManager->markAsCreated($vacationRequest);
|
|
||||||
|
|
||||||
return $vacationRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleCreatedVacationRequest(VacationRequest $vacationRequest): void
|
|
||||||
{
|
|
||||||
if ($vacationRequest->hasFlowSkipped()) {
|
|
||||||
$this->approveAction->execute($vacationRequest);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->configRetriever->needsTechnicalApproval($vacationRequest->type)) {
|
|
||||||
$this->waitForTechApprovalAction->execute($vacationRequest);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->configRetriever->needsAdministrativeApproval($vacationRequest->type)) {
|
|
||||||
$this->waitForAdminApprovalAction->execute($vacationRequest);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->stateManager->approve($vacationRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function notify(VacationRequest $vacationRequest): void
|
|
||||||
{
|
|
||||||
$vacationRequest->user->notify(new VacationRequestCreatedNotification($vacationRequest));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Actions\VacationRequest;
|
|
||||||
|
|
||||||
use Toby\Domain\Enums\Role;
|
|
||||||
use Toby\Domain\Notifications\VacationRequestStatusChangedNotification;
|
|
||||||
use Toby\Domain\VacationRequestStateManager;
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
|
||||||
|
|
||||||
class RejectAction
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected VacationRequestStateManager $stateManager,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function execute(VacationRequest $vacationRequest, User $user): void
|
|
||||||
{
|
|
||||||
$this->stateManager->reject($vacationRequest, $user);
|
|
||||||
|
|
||||||
$this->notify($vacationRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function notify(VacationRequest $vacationRequest): void
|
|
||||||
{
|
|
||||||
$users = User::query()
|
|
||||||
->where("id", "!=", $vacationRequest->user->id)
|
|
||||||
->whereIn("role", [Role::TechnicalApprover, Role::AdministrativeApprover, Role::Administrator])
|
|
||||||
->get();
|
|
||||||
|
|
||||||
foreach ($users as $user) {
|
|
||||||
$user->notify(new VacationRequestStatusChangedNotification($vacationRequest, $user));
|
|
||||||
}
|
|
||||||
|
|
||||||
$vacationRequest->user->notify(new VacationRequestStatusChangedNotification($vacationRequest, $vacationRequest->user));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Actions\VacationRequest;
|
|
||||||
|
|
||||||
use Toby\Domain\Enums\Role;
|
|
||||||
use Toby\Domain\Notifications\VacationRequestWaitsForApprovalNotification;
|
|
||||||
use Toby\Domain\VacationRequestStateManager;
|
|
||||||
use Toby\Domain\VacationTypeConfigRetriever;
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
|
||||||
|
|
||||||
class WaitForAdminApprovalAction
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected VacationRequestStateManager $stateManager,
|
|
||||||
protected VacationTypeConfigRetriever $configRetriever,
|
|
||||||
protected ApproveAction $approveAction,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function execute(VacationRequest $vacationRequest): void
|
|
||||||
{
|
|
||||||
$this->stateManager->waitForAdministrative($vacationRequest);
|
|
||||||
|
|
||||||
if ($this->configRetriever->isVacation($vacationRequest->type)) {
|
|
||||||
$this->notifyAdminApprovers($vacationRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function notifyAdminApprovers(VacationRequest $vacationRequest): void
|
|
||||||
{
|
|
||||||
$users = User::query()
|
|
||||||
->whereIn("role", [Role::AdministrativeApprover, Role::Administrator])
|
|
||||||
->get();
|
|
||||||
|
|
||||||
foreach ($users as $user) {
|
|
||||||
$user->notify(new VacationRequestWaitsForApprovalNotification($vacationRequest, $user));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Actions\VacationRequest;
|
|
||||||
|
|
||||||
use Toby\Domain\Enums\Role;
|
|
||||||
use Toby\Domain\Notifications\VacationRequestWaitsForApprovalNotification;
|
|
||||||
use Toby\Domain\VacationRequestStateManager;
|
|
||||||
use Toby\Domain\VacationTypeConfigRetriever;
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
|
||||||
|
|
||||||
class WaitForTechApprovalAction
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected VacationRequestStateManager $stateManager,
|
|
||||||
protected VacationTypeConfigRetriever $configRetriever,
|
|
||||||
protected ApproveAction $approveAction,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function execute(VacationRequest $vacationRequest): void
|
|
||||||
{
|
|
||||||
$this->stateManager->waitForTechnical($vacationRequest);
|
|
||||||
|
|
||||||
if ($this->configRetriever->isVacation($vacationRequest->type)) {
|
|
||||||
$this->notifyTechApprovers($vacationRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function notifyTechApprovers(VacationRequest $vacationRequest): void
|
|
||||||
{
|
|
||||||
$users = User::query()
|
|
||||||
->whereIn("role", [Role::TechnicalApprover, Role::Administrator])
|
|
||||||
->get();
|
|
||||||
|
|
||||||
foreach ($users as $user) {
|
|
||||||
$user->notify(new VacationRequestWaitsForApprovalNotification($vacationRequest, $user));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace Toby\Domain;
|
namespace Toby\Domain;
|
||||||
|
|
||||||
use Carbon\CarbonPeriod;
|
use Carbon\CarbonPeriod;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Toby\Eloquent\Helpers\YearPeriodRetriever;
|
use Toby\Eloquent\Helpers\YearPeriodRetriever;
|
||||||
@ -43,7 +44,6 @@ class CalendarGenerator
|
|||||||
"isWeekend" => $day->isWeekend(),
|
"isWeekend" => $day->isWeekend(),
|
||||||
"isHoliday" => $holidays->contains($day),
|
"isHoliday" => $holidays->contains($day),
|
||||||
"vacations" => $vacationsForDay->pluck("user_id"),
|
"vacations" => $vacationsForDay->pluck("user_id"),
|
||||||
"vacationTypes" => $vacationsForDay->pluck("vacationRequest.type", "user_id"),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,9 +54,8 @@ class CalendarGenerator
|
|||||||
{
|
{
|
||||||
return Vacation::query()
|
return Vacation::query()
|
||||||
->whereBetween("date", [$period->start, $period->end])
|
->whereBetween("date", [$period->start, $period->end])
|
||||||
->approved()
|
->whereRelation("vacationRequest", fn(Builder $query) => $query->states(VacationRequestStatesRetriever::successStates()))
|
||||||
->with("vacationRequest")
|
|
||||||
->get()
|
->get()
|
||||||
->groupBy(fn(Vacation $vacation): string => $vacation->date->toDateString());
|
->groupBy(fn(Vacation $vacation) => $vacation->date->toDateString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain;
|
|
||||||
|
|
||||||
use Illuminate\Support\Carbon;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Toby\Domain\Enums\VacationType;
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
use Toby\Eloquent\Models\Vacation;
|
|
||||||
|
|
||||||
class DailySummaryRetriever
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected VacationTypeConfigRetriever $configRetriever,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function getAbsences(Carbon $date): Collection
|
|
||||||
{
|
|
||||||
return Vacation::query()
|
|
||||||
->with(["user", "vacationRequest"])
|
|
||||||
->whereDate("date", $date)
|
|
||||||
->approved()
|
|
||||||
->whereTypes(
|
|
||||||
VacationType::all()->filter(fn(VacationType $type): bool => $this->configRetriever->isVacation($type)),
|
|
||||||
)
|
|
||||||
->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRemoteDays(Carbon $date): Collection
|
|
||||||
{
|
|
||||||
return Vacation::query()
|
|
||||||
->with(["user", "vacationRequest"])
|
|
||||||
->whereDate("date", $date)
|
|
||||||
->approved()
|
|
||||||
->whereTypes(
|
|
||||||
VacationType::all()->filter(fn(VacationType $type): bool => !$this->configRetriever->isVacation($type)),
|
|
||||||
)
|
|
||||||
->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getBirthdays(Carbon $date): Collection
|
|
||||||
{
|
|
||||||
return User::query()
|
|
||||||
->whereRelation("profile", "birthday", $date)
|
|
||||||
->get();
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,7 +21,7 @@ enum EmploymentForm: string
|
|||||||
$cases = collect(EmploymentForm::cases());
|
$cases = collect(EmploymentForm::cases());
|
||||||
|
|
||||||
return $cases->map(
|
return $cases->map(
|
||||||
fn(EmploymentForm $enum): array => [
|
fn(EmploymentForm $enum) => [
|
||||||
"label" => $enum->label(),
|
"label" => $enum->label(),
|
||||||
"value" => $enum->value,
|
"value" => $enum->value,
|
||||||
],
|
],
|
||||||
|
@ -21,7 +21,7 @@ enum Role: string
|
|||||||
$cases = collect(Role::cases());
|
$cases = collect(Role::cases());
|
||||||
|
|
||||||
return $cases->map(
|
return $cases->map(
|
||||||
fn(Role $enum): array => [
|
fn(Role $enum) => [
|
||||||
"label" => $enum->label(),
|
"label" => $enum->label(),
|
||||||
"value" => $enum->value,
|
"value" => $enum->value,
|
||||||
],
|
],
|
||||||
|
@ -4,8 +4,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Toby\Domain\Enums;
|
namespace Toby\Domain\Enums;
|
||||||
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
enum VacationType: string
|
enum VacationType: string
|
||||||
{
|
{
|
||||||
case Vacation = "vacation";
|
case Vacation = "vacation";
|
||||||
@ -17,8 +15,6 @@ enum VacationType: string
|
|||||||
case Volunteering = "volunteering_vacation";
|
case Volunteering = "volunteering_vacation";
|
||||||
case TimeInLieu = "time_in_lieu";
|
case TimeInLieu = "time_in_lieu";
|
||||||
case Sick = "sick_vacation";
|
case Sick = "sick_vacation";
|
||||||
case Absence = "absence";
|
|
||||||
case HomeOffice = "home_office";
|
|
||||||
|
|
||||||
public function label(): string
|
public function label(): string
|
||||||
{
|
{
|
||||||
@ -27,18 +23,13 @@ enum VacationType: string
|
|||||||
|
|
||||||
public static function casesToSelect(): array
|
public static function casesToSelect(): array
|
||||||
{
|
{
|
||||||
$cases = VacationType::all();
|
$cases = collect(VacationType::cases());
|
||||||
|
|
||||||
return $cases->map(
|
return $cases->map(
|
||||||
fn(VacationType $enum): array => [
|
fn(VacationType $enum) => [
|
||||||
"label" => $enum->label(),
|
"label" => $enum->label(),
|
||||||
"value" => $enum->value,
|
"value" => $enum->value,
|
||||||
],
|
],
|
||||||
)->toArray();
|
)->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function all(): Collection
|
|
||||||
{
|
|
||||||
return new Collection(VacationType::cases());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Events;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
|
class VacationRequestAcceptedByAdministrative
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public VacationRequest $vacationRequest,
|
||||||
|
) {}
|
||||||
|
}
|
19
app/Domain/Events/VacationRequestAcceptedByTechnical.php
Normal file
19
app/Domain/Events/VacationRequestAcceptedByTechnical.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Events;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
|
class VacationRequestAcceptedByTechnical
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public VacationRequest $vacationRequest,
|
||||||
|
) {}
|
||||||
|
}
|
19
app/Domain/Events/VacationRequestApproved.php
Normal file
19
app/Domain/Events/VacationRequestApproved.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Events;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
|
class VacationRequestApproved
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public VacationRequest $vacationRequest,
|
||||||
|
) {}
|
||||||
|
}
|
19
app/Domain/Events/VacationRequestCancelled.php
Normal file
19
app/Domain/Events/VacationRequestCancelled.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Events;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
|
class VacationRequestCancelled
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public VacationRequest $vacationRequest,
|
||||||
|
) {}
|
||||||
|
}
|
19
app/Domain/Events/VacationRequestCreated.php
Normal file
19
app/Domain/Events/VacationRequestCreated.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Events;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
|
class VacationRequestCreated
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public VacationRequest $vacationRequest,
|
||||||
|
) {}
|
||||||
|
}
|
19
app/Domain/Events/VacationRequestRejected.php
Normal file
19
app/Domain/Events/VacationRequestRejected.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Events;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
|
class VacationRequestRejected
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public VacationRequest $vacationRequest,
|
||||||
|
) {}
|
||||||
|
}
|
24
app/Domain/Events/VacationRequestStateChanged.php
Normal file
24
app/Domain/Events/VacationRequestStateChanged.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Events;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Toby\Domain\States\VacationRequest\VacationRequestState;
|
||||||
|
use Toby\Eloquent\Models\User;
|
||||||
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
|
class VacationRequestStateChanged
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public VacationRequest $vacationRequest,
|
||||||
|
public ?VacationRequestState $from,
|
||||||
|
public VacationRequestState $to,
|
||||||
|
public ?User $user = null,
|
||||||
|
) {}
|
||||||
|
}
|
19
app/Domain/Events/VacationRequestWaitsForAdminApproval.php
Normal file
19
app/Domain/Events/VacationRequestWaitsForAdminApproval.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Events;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
|
class VacationRequestWaitsForAdminApproval
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public VacationRequest $vacationRequest,
|
||||||
|
) {}
|
||||||
|
}
|
19
app/Domain/Events/VacationRequestWaitsForTechApproval.php
Normal file
19
app/Domain/Events/VacationRequestWaitsForTechApproval.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Events;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
|
class VacationRequestWaitsForTechApproval
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public VacationRequest $vacationRequest,
|
||||||
|
) {}
|
||||||
|
}
|
19
app/Domain/Listeners/CreateVacationRequestActivity.php
Normal file
19
app/Domain/Listeners/CreateVacationRequestActivity.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Listeners;
|
||||||
|
|
||||||
|
use Toby\Domain\Events\VacationRequestStateChanged;
|
||||||
|
|
||||||
|
class CreateVacationRequestActivity
|
||||||
|
{
|
||||||
|
public function handle(VacationRequestStateChanged $event): void
|
||||||
|
{
|
||||||
|
$event->vacationRequest->activities()->create([
|
||||||
|
"from" => $event->from,
|
||||||
|
"to" => $event->to,
|
||||||
|
"user_id" => $event->user?->id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Listeners;
|
||||||
|
|
||||||
|
use Toby\Domain\Events\VacationRequestAcceptedByAdministrative;
|
||||||
|
use Toby\Domain\VacationRequestStateManager;
|
||||||
|
|
||||||
|
class HandleAcceptedByAdministrativeVacationRequest
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected VacationRequestStateManager $stateManager,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function handle(VacationRequestAcceptedByAdministrative $event): void
|
||||||
|
{
|
||||||
|
$this->stateManager->approve($event->vacationRequest);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Listeners;
|
||||||
|
|
||||||
|
use Toby\Domain\Events\VacationRequestAcceptedByTechnical;
|
||||||
|
use Toby\Domain\VacationRequestStateManager;
|
||||||
|
use Toby\Domain\VacationTypeConfigRetriever;
|
||||||
|
|
||||||
|
class HandleAcceptedByTechnicalVacationRequest
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected VacationTypeConfigRetriever $configRetriever,
|
||||||
|
protected VacationRequestStateManager $stateManager,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function handle(VacationRequestAcceptedByTechnical $event): void
|
||||||
|
{
|
||||||
|
$vacationRequest = $event->vacationRequest;
|
||||||
|
|
||||||
|
if ($this->configRetriever->needsAdministrativeApproval($vacationRequest->type)) {
|
||||||
|
$this->stateManager->waitForAdministrative($vacationRequest);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->stateManager->approve($vacationRequest);
|
||||||
|
}
|
||||||
|
}
|
16
app/Domain/Listeners/HandleApprovedVacationRequest.php
Normal file
16
app/Domain/Listeners/HandleApprovedVacationRequest.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Listeners;
|
||||||
|
|
||||||
|
use Toby\Domain\Events\VacationRequestApproved;
|
||||||
|
use Toby\Infrastructure\Jobs\SendVacationRequestDaysToGoogleCalendar;
|
||||||
|
|
||||||
|
class HandleApprovedVacationRequest
|
||||||
|
{
|
||||||
|
public function handle(VacationRequestApproved $event): void
|
||||||
|
{
|
||||||
|
SendVacationRequestDaysToGoogleCalendar::dispatch($event->vacationRequest);
|
||||||
|
}
|
||||||
|
}
|
16
app/Domain/Listeners/HandleCancelledVacationRequest.php
Normal file
16
app/Domain/Listeners/HandleCancelledVacationRequest.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Listeners;
|
||||||
|
|
||||||
|
use Toby\Domain\Events\VacationRequestCancelled;
|
||||||
|
use Toby\Infrastructure\Jobs\ClearVacationRequestDaysInGoogleCalendar;
|
||||||
|
|
||||||
|
class HandleCancelledVacationRequest
|
||||||
|
{
|
||||||
|
public function handle(VacationRequestCancelled $event): void
|
||||||
|
{
|
||||||
|
ClearVacationRequestDaysInGoogleCalendar::dispatch($event->vacationRequest);
|
||||||
|
}
|
||||||
|
}
|
42
app/Domain/Listeners/HandleCreatedVacationRequest.php
Normal file
42
app/Domain/Listeners/HandleCreatedVacationRequest.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Listeners;
|
||||||
|
|
||||||
|
use Toby\Domain\Events\VacationRequestCreated;
|
||||||
|
use Toby\Domain\VacationRequestStateManager;
|
||||||
|
use Toby\Domain\VacationTypeConfigRetriever;
|
||||||
|
|
||||||
|
class HandleCreatedVacationRequest
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected VacationTypeConfigRetriever $configRetriever,
|
||||||
|
protected VacationRequestStateManager $stateManager,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function handle(VacationRequestCreated $event): void
|
||||||
|
{
|
||||||
|
$vacationRequest = $event->vacationRequest;
|
||||||
|
|
||||||
|
if ($vacationRequest->hasFlowSkipped()) {
|
||||||
|
$this->stateManager->approve($vacationRequest);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->configRetriever->needsTechnicalApproval($vacationRequest->type)) {
|
||||||
|
$this->stateManager->waitForTechnical($vacationRequest);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->configRetriever->needsAdministrativeApproval($vacationRequest->type)) {
|
||||||
|
$this->stateManager->waitForAdministrative($vacationRequest);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->stateManager->approve($vacationRequest);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Listeners;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Toby\Domain\Enums\Role;
|
||||||
|
use Toby\Domain\Events\VacationRequestApproved;
|
||||||
|
use Toby\Domain\Notifications\VacationRequestApprovedNotification;
|
||||||
|
use Toby\Eloquent\Models\User;
|
||||||
|
|
||||||
|
class SendApprovedVacationRequestNotification
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function handle(VacationRequestApproved $event): void
|
||||||
|
{
|
||||||
|
foreach ($this->getUsersForNotifications() as $user) {
|
||||||
|
$user->notify(new VacationRequestApprovedNotification($event->vacationRequest, $user));
|
||||||
|
}
|
||||||
|
|
||||||
|
$event->vacationRequest->user->notify(new VacationRequestApprovedNotification($event->vacationRequest, $event->vacationRequest->user));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getUsersForNotifications(): Collection
|
||||||
|
{
|
||||||
|
return User::query()
|
||||||
|
->whereIn("role", [Role::TechnicalApprover, Role::AdministrativeApprover])
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Listeners;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Toby\Domain\Enums\Role;
|
||||||
|
use Toby\Domain\Events\VacationRequestCancelled;
|
||||||
|
use Toby\Domain\Notifications\VacationRequestCancelledNotification;
|
||||||
|
use Toby\Eloquent\Models\User;
|
||||||
|
|
||||||
|
class SendCancelledVacationRequestNotification
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function handle(VacationRequestCancelled $event): void
|
||||||
|
{
|
||||||
|
foreach ($this->getUsersForNotifications() as $user) {
|
||||||
|
$user->notify(new VacationRequestCancelledNotification($event->vacationRequest, $user));
|
||||||
|
}
|
||||||
|
|
||||||
|
$event->vacationRequest->user->notify(new VacationRequestCancelledNotification($event->vacationRequest, $event->vacationRequest->user));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getUsersForNotifications(): Collection
|
||||||
|
{
|
||||||
|
return User::query()
|
||||||
|
->whereIn("role", [Role::TechnicalApprover, Role::AdministrativeApprover])
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Listeners;
|
||||||
|
|
||||||
|
use Toby\Domain\Events\VacationRequestCreated;
|
||||||
|
use Toby\Domain\Notifications\VacationRequestCreatedNotification;
|
||||||
|
use Toby\Domain\Notifications\VacationRequestCreatedOnEmployeeBehalf;
|
||||||
|
|
||||||
|
class SendCreatedVacationRequestNotification
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function handle(VacationRequestCreated $event): void
|
||||||
|
{
|
||||||
|
$vacationRequest = $event->vacationRequest;
|
||||||
|
|
||||||
|
if ($vacationRequest->creator->is($vacationRequest->user)) {
|
||||||
|
$vacationRequest->user->notify(new VacationRequestCreatedNotification($vacationRequest));
|
||||||
|
} else {
|
||||||
|
$vacationRequest->user->notify(new VacationRequestCreatedOnEmployeeBehalf($vacationRequest));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Listeners;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Toby\Domain\Enums\Role;
|
||||||
|
use Toby\Domain\Events\VacationRequestRejected;
|
||||||
|
use Toby\Domain\Notifications\VacationRequestRejectedNotification;
|
||||||
|
use Toby\Eloquent\Models\User;
|
||||||
|
|
||||||
|
class SendRejectedVacationRequestNotification
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function handle(VacationRequestRejected $event): void
|
||||||
|
{
|
||||||
|
foreach ($this->getUsersForNotifications() as $user) {
|
||||||
|
$user->notify(new VacationRequestRejectedNotification($event->vacationRequest, $user));
|
||||||
|
}
|
||||||
|
|
||||||
|
$event->vacationRequest->user->notify(new VacationRequestRejectedNotification($event->vacationRequest, $event->vacationRequest->user));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getUsersForNotifications(): Collection
|
||||||
|
{
|
||||||
|
return User::query()
|
||||||
|
->whereIn("role", [Role::TechnicalApprover, Role::AdministrativeApprover])
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Listeners;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Toby\Domain\Enums\Role;
|
||||||
|
use Toby\Domain\Events\VacationRequestWaitsForAdminApproval;
|
||||||
|
use Toby\Domain\Notifications\VacationRequestWaitsForAdminApprovalNotification;
|
||||||
|
use Toby\Eloquent\Models\User;
|
||||||
|
|
||||||
|
class SendWaitedForAdministrativeVacationRequestNotification
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function handle(VacationRequestWaitsForAdminApproval $event): void
|
||||||
|
{
|
||||||
|
foreach ($this->getUsersForNotifications() as $user) {
|
||||||
|
$user->notify(new VacationRequestWaitsForAdminApprovalNotification($event->vacationRequest, $user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getUsersForNotifications(): Collection
|
||||||
|
{
|
||||||
|
return User::query()
|
||||||
|
->where("role", [Role::AdministrativeApprover])
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Listeners;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Toby\Domain\Enums\Role;
|
||||||
|
use Toby\Domain\Events\VacationRequestWaitsForTechApproval;
|
||||||
|
use Toby\Domain\Notifications\VacationRequestWaitsForTechApprovalNotification;
|
||||||
|
use Toby\Eloquent\Models\User;
|
||||||
|
|
||||||
|
class SendWaitedForTechnicalVacationRequestNotification
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function handle(VacationRequestWaitsForTechApproval $event): void
|
||||||
|
{
|
||||||
|
foreach ($this->getUsersForNotifications() as $user) {
|
||||||
|
$user->notify(new VacationRequestWaitsForTechApprovalNotification($event->vacationRequest, $user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getUsersForNotifications(): Collection
|
||||||
|
{
|
||||||
|
return User::query()
|
||||||
|
->where("role", [Role::TechnicalApprover])
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Notifications;
|
|
||||||
|
|
||||||
class Channels
|
|
||||||
{
|
|
||||||
public const MAIL = "mail";
|
|
||||||
public const SLACK = "slack";
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Notifications;
|
|
||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
use Toby\Infrastructure\Slack\Elements\SlackMessage;
|
|
||||||
|
|
||||||
class KeyHasBeenGivenNotification extends Notification
|
|
||||||
{
|
|
||||||
use Queueable;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
protected User $sender,
|
|
||||||
protected User $recipient,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function via(): array
|
|
||||||
{
|
|
||||||
return [Channels::SLACK];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toSlack(Notifiable $notifiable): SlackMessage
|
|
||||||
{
|
|
||||||
return (new SlackMessage())
|
|
||||||
->text(__(":sender gives key no :key to :recipient", [
|
|
||||||
"sender" => $this->getName($this->sender),
|
|
||||||
"recipient" => $this->getName($this->recipient),
|
|
||||||
"key" => $notifiable->id,
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getName(User $user): string
|
|
||||||
{
|
|
||||||
if ($user->profile->slack_id !== null) {
|
|
||||||
return "<@{$user->profile->slack_id}>";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $user->profile->full_name;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Notifications;
|
|
||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
use Toby\Infrastructure\Slack\Elements\SlackMessage;
|
|
||||||
|
|
||||||
class KeyHasBeenTakenNotification extends Notification
|
|
||||||
{
|
|
||||||
use Queueable;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
protected User $recipient,
|
|
||||||
protected User $sender,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function via(): array
|
|
||||||
{
|
|
||||||
return [Channels::SLACK];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toSlack(Notifiable $notifiable): SlackMessage
|
|
||||||
{
|
|
||||||
return (new SlackMessage())
|
|
||||||
->text(__(":recipient takes key no :key from :sender", [
|
|
||||||
"recipient" => $this->getName($this->recipient),
|
|
||||||
"sender" => $this->getName($this->sender),
|
|
||||||
"key" => $notifiable->id,
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getName(User $user): string
|
|
||||||
{
|
|
||||||
if ($user->profile->slack_id !== null) {
|
|
||||||
return "<@{$user->profile->slack_id}>";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $user->profile->full_name;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Notifications;
|
|
||||||
|
|
||||||
interface Notifiable
|
|
||||||
{
|
|
||||||
public function notify($instance);
|
|
||||||
}
|
|
@ -10,9 +10,8 @@ use Illuminate\Notifications\Notification;
|
|||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Toby\Eloquent\Models\User;
|
use Toby\Eloquent\Models\User;
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
use Toby\Infrastructure\Slack\Elements\SlackMessage;
|
|
||||||
|
|
||||||
class VacationRequestStatusChangedNotification extends Notification
|
class VacationRequestApprovedNotification extends Notification
|
||||||
{
|
{
|
||||||
use Queueable;
|
use Queueable;
|
||||||
|
|
||||||
@ -23,16 +22,7 @@ class VacationRequestStatusChangedNotification extends Notification
|
|||||||
|
|
||||||
public function via(): array
|
public function via(): array
|
||||||
{
|
{
|
||||||
return [Channels::MAIL, Channels::SLACK];
|
return ["mail"];
|
||||||
}
|
|
||||||
|
|
||||||
public function toSlack(): SlackMessage
|
|
||||||
{
|
|
||||||
$url = route("vacation.requests.show", ["vacationRequest" => $this->vacationRequest->id]);
|
|
||||||
$seeDetails = __("See details");
|
|
||||||
|
|
||||||
return (new SlackMessage())
|
|
||||||
->text("{$this->buildDescription()}\n <${url}|${seeDetails}>");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,18 +42,25 @@ class VacationRequestStatusChangedNotification extends Notification
|
|||||||
|
|
||||||
protected function buildMailMessage(string $url): MailMessage
|
protected function buildMailMessage(string $url): MailMessage
|
||||||
{
|
{
|
||||||
$user = $this->user->profile->first_name;
|
$user = $this->user->first_name;
|
||||||
|
$title = $this->vacationRequest->name;
|
||||||
$type = $this->vacationRequest->type->label();
|
$type = $this->vacationRequest->type->label();
|
||||||
$from = $this->vacationRequest->from->toDisplayString();
|
$from = $this->vacationRequest->from->toDisplayString();
|
||||||
$to = $this->vacationRequest->to->toDisplayString();
|
$to = $this->vacationRequest->to->toDisplayString();
|
||||||
$days = $this->vacationRequest->vacations()->count();
|
$days = $this->vacationRequest->vacations()->count();
|
||||||
|
$requester = $this->vacationRequest->user->fullName;
|
||||||
|
|
||||||
return (new MailMessage())
|
return (new MailMessage())
|
||||||
->greeting(__("Hi :user!", [
|
->greeting(__("Hi :user!", [
|
||||||
"user" => $user,
|
"user" => $user,
|
||||||
]))
|
]))
|
||||||
->subject($this->buildSubject())
|
->subject(__("Vacation request :title has been approved", [
|
||||||
->line($this->buildDescription())
|
"title" => $title,
|
||||||
|
]))
|
||||||
|
->line(__("The vacation request :title for user :requester has been approved.", [
|
||||||
|
"title" => $title,
|
||||||
|
"requester" => $requester,
|
||||||
|
]))
|
||||||
->line(__("Vacation type: :type", [
|
->line(__("Vacation type: :type", [
|
||||||
"type" => $type,
|
"type" => $type,
|
||||||
]))
|
]))
|
||||||
@ -74,21 +71,4 @@ class VacationRequestStatusChangedNotification extends Notification
|
|||||||
]))
|
]))
|
||||||
->action(__("Click here for details"), $url);
|
->action(__("Click here for details"), $url);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildSubject(): string
|
|
||||||
{
|
|
||||||
return __("Vacation request :title has been :status", [
|
|
||||||
"title" => $this->vacationRequest->name,
|
|
||||||
"status" => $this->vacationRequest->state->label(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function buildDescription(): string
|
|
||||||
{
|
|
||||||
return __("The vacation request :title from user :requester has been :status.", [
|
|
||||||
"title" => $this->vacationRequest->name,
|
|
||||||
"requester" => $this->vacationRequest->user->profile->full_name,
|
|
||||||
"status" => $this->vacationRequest->state->label(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -8,12 +8,10 @@ use Illuminate\Bus\Queueable;
|
|||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
use Illuminate\Notifications\Notification;
|
use Illuminate\Notifications\Notification;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Toby\Domain\States\VacationRequest\WaitingForTechnical;
|
|
||||||
use Toby\Eloquent\Models\User;
|
use Toby\Eloquent\Models\User;
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
use Toby\Infrastructure\Slack\Elements\SlackMessage;
|
|
||||||
|
|
||||||
class VacationRequestWaitsForApprovalNotification extends Notification
|
class VacationRequestCancelledNotification extends Notification
|
||||||
{
|
{
|
||||||
use Queueable;
|
use Queueable;
|
||||||
|
|
||||||
@ -24,16 +22,7 @@ class VacationRequestWaitsForApprovalNotification extends Notification
|
|||||||
|
|
||||||
public function via(): array
|
public function via(): array
|
||||||
{
|
{
|
||||||
return [Channels::MAIL, Channels::SLACK];
|
return ["mail"];
|
||||||
}
|
|
||||||
|
|
||||||
public function toSlack(): SlackMessage
|
|
||||||
{
|
|
||||||
$url = route("vacation.requests.show", ["vacationRequest" => $this->vacationRequest->id]);
|
|
||||||
$seeDetails = __("See details");
|
|
||||||
|
|
||||||
return (new SlackMessage())
|
|
||||||
->text("{$this->buildDescription()}\n <${url}|${seeDetails}>");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,18 +42,25 @@ class VacationRequestWaitsForApprovalNotification extends Notification
|
|||||||
|
|
||||||
protected function buildMailMessage(string $url): MailMessage
|
protected function buildMailMessage(string $url): MailMessage
|
||||||
{
|
{
|
||||||
$user = $this->user->profile->first_name;
|
$user = $this->user->first_name;
|
||||||
|
$title = $this->vacationRequest->name;
|
||||||
$type = $this->vacationRequest->type->label();
|
$type = $this->vacationRequest->type->label();
|
||||||
$from = $this->vacationRequest->from->toDisplayString();
|
$from = $this->vacationRequest->from->toDisplayString();
|
||||||
$to = $this->vacationRequest->to->toDisplayString();
|
$to = $this->vacationRequest->to->toDisplayString();
|
||||||
$days = $this->vacationRequest->vacations()->count();
|
$days = $this->vacationRequest->vacations()->count();
|
||||||
|
$requester = $this->vacationRequest->user->fullName;
|
||||||
|
|
||||||
return (new MailMessage())
|
return (new MailMessage())
|
||||||
->greeting(__("Hi :user!", [
|
->greeting(__("Hi :user!", [
|
||||||
"user" => $user,
|
"user" => $user,
|
||||||
]))
|
]))
|
||||||
->subject($this->buildSubject())
|
->subject(__("Vacation request :title has been cancelled", [
|
||||||
->line($this->buildDescription())
|
"title" => $title,
|
||||||
|
]))
|
||||||
|
->line(__("The vacation request :title for user :requester has been cancelled.", [
|
||||||
|
"title" => $title,
|
||||||
|
"requester" => $requester,
|
||||||
|
]))
|
||||||
->line(__("Vacation type: :type", [
|
->line(__("Vacation type: :type", [
|
||||||
"type" => $type,
|
"type" => $type,
|
||||||
]))
|
]))
|
||||||
@ -75,37 +71,4 @@ class VacationRequestWaitsForApprovalNotification extends Notification
|
|||||||
]))
|
]))
|
||||||
->action(__("Click here for details"), $url);
|
->action(__("Click here for details"), $url);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildSubject(): string
|
|
||||||
{
|
|
||||||
$title = $this->vacationRequest->name;
|
|
||||||
|
|
||||||
if ($this->vacationRequest->state->equals(WaitingForTechnical::class)) {
|
|
||||||
return __("Vacation request :title is waiting for your technical approval", [
|
|
||||||
"title" => $title,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return __("Vacation request :title is waiting for your administrative approval", [
|
|
||||||
"title" => $title,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function buildDescription(): string
|
|
||||||
{
|
|
||||||
$title = $this->vacationRequest->name;
|
|
||||||
$requester = $this->vacationRequest->user->profile->full_name;
|
|
||||||
|
|
||||||
if ($this->vacationRequest->state->equals(WaitingForTechnical::class)) {
|
|
||||||
return __("The vacation request :title from user :requester is waiting for your technical approval.", [
|
|
||||||
"title" => $title,
|
|
||||||
"requester" => $requester,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return __("The vacation request :title from user :requester is waiting for your administrative approval.", [
|
|
||||||
"title" => $title,
|
|
||||||
"requester" => $requester,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -9,7 +9,6 @@ use Illuminate\Notifications\Messages\MailMessage;
|
|||||||
use Illuminate\Notifications\Notification;
|
use Illuminate\Notifications\Notification;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
use Toby\Infrastructure\Slack\Elements\SlackMessage;
|
|
||||||
|
|
||||||
class VacationRequestCreatedNotification extends Notification
|
class VacationRequestCreatedNotification extends Notification
|
||||||
{
|
{
|
||||||
@ -21,16 +20,7 @@ class VacationRequestCreatedNotification extends Notification
|
|||||||
|
|
||||||
public function via(): array
|
public function via(): array
|
||||||
{
|
{
|
||||||
return [Channels::MAIL, Channels::SLACK];
|
return ["mail"];
|
||||||
}
|
|
||||||
|
|
||||||
public function toSlack(): SlackMessage
|
|
||||||
{
|
|
||||||
$url = route("vacation.requests.show", ["vacationRequest" => $this->vacationRequest->id]);
|
|
||||||
$seeDetails = __("See details");
|
|
||||||
|
|
||||||
return (new SlackMessage())
|
|
||||||
->text("{$this->buildDescription()}\n <${url}|${seeDetails}>");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,64 +39,33 @@ class VacationRequestCreatedNotification extends Notification
|
|||||||
|
|
||||||
protected function buildMailMessage(string $url): MailMessage
|
protected function buildMailMessage(string $url): MailMessage
|
||||||
{
|
{
|
||||||
$user = $this->vacationRequest->user->profile->first_name;
|
$user = $this->vacationRequest->user->first_name;
|
||||||
|
$title = $this->vacationRequest->name;
|
||||||
$type = $this->vacationRequest->type->label();
|
$type = $this->vacationRequest->type->label();
|
||||||
$from = $this->vacationRequest->from->toDisplayString();
|
$from = $this->vacationRequest->from->toDisplayString();
|
||||||
$to = $this->vacationRequest->to->toDisplayString();
|
$to = $this->vacationRequest->to->toDisplayString();
|
||||||
$days = $this->vacationRequest->vacations()->count();
|
$days = $this->vacationRequest->vacations()->count();
|
||||||
|
$appName = config("app.name");
|
||||||
|
|
||||||
return (new MailMessage())
|
return (new MailMessage())
|
||||||
->greeting(
|
->greeting(__("Hi :user!", [
|
||||||
__("Hi :user!", [
|
"user" => $user,
|
||||||
"user" => $user,
|
]))
|
||||||
]),
|
->subject(__("Vacation request :title has been created", [
|
||||||
)
|
"title" => $title,
|
||||||
->subject($this->buildSubject())
|
]))
|
||||||
->line($this->buildDescription())
|
->line(__("The vacation request :title has been created correctly in the :appName.", [
|
||||||
->line(
|
"title" => $title,
|
||||||
__("Vacation type: :type", [
|
"appName" => $appName,
|
||||||
"type" => $type,
|
]))
|
||||||
]),
|
->line(__("Vacation type: :type", [
|
||||||
)
|
"type" => $type,
|
||||||
->line(
|
]))
|
||||||
__("From :from to :to (number of days: :days)", [
|
->line(__("From :from to :to (number of days: :days)", [
|
||||||
"from" => $from,
|
"from" => $from,
|
||||||
"to" => $to,
|
"to" => $to,
|
||||||
"days" => $days,
|
"days" => $days,
|
||||||
]),
|
]))
|
||||||
)
|
|
||||||
->action(__("Click here for details"), $url);
|
->action(__("Click here for details"), $url);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildSubject(): string
|
|
||||||
{
|
|
||||||
$name = $this->vacationRequest->name;
|
|
||||||
|
|
||||||
if ($this->vacationRequest->creator()->is($this->vacationRequest->user)) {
|
|
||||||
return __("Vacation request :title has been created", [
|
|
||||||
"title" => $name,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return __("Vacation request :title has been created on your behalf", [
|
|
||||||
"title" => $name,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function buildDescription(): string
|
|
||||||
{
|
|
||||||
$name = $this->vacationRequest->name;
|
|
||||||
|
|
||||||
if ($this->vacationRequest->creator()->is($this->vacationRequest->user)) {
|
|
||||||
return __("The vacation request :title has been created successfully.", [
|
|
||||||
"requester" => $this->vacationRequest->user->profile->full_name,
|
|
||||||
"title" => $name,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return __("The vacation request :title has been created successfully by user :creator on your behalf.", [
|
|
||||||
"title" => $this->vacationRequest->name,
|
|
||||||
"creator" => $this->vacationRequest->creator->profile->full_name,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Notifications;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
|
class VacationRequestCreatedOnEmployeeBehalf extends Notification
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected VacationRequest $vacationRequest,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function via(): array
|
||||||
|
{
|
||||||
|
return ["mail"];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function toMail(): MailMessage
|
||||||
|
{
|
||||||
|
$url = route(
|
||||||
|
"vacation.requests.show",
|
||||||
|
[
|
||||||
|
"vacationRequest" => $this->vacationRequest,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
return $this->buildMailMessage($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildMailMessage(string $url): MailMessage
|
||||||
|
{
|
||||||
|
$creator = $this->vacationRequest->creator->fullName;
|
||||||
|
$user = $this->vacationRequest->user->first_name;
|
||||||
|
$title = $this->vacationRequest->name;
|
||||||
|
$type = $this->vacationRequest->type->label();
|
||||||
|
$from = $this->vacationRequest->from->toDisplayString();
|
||||||
|
$to = $this->vacationRequest->to->toDisplayString();
|
||||||
|
$days = $this->vacationRequest->vacations()->count();
|
||||||
|
$appName = config("app.name");
|
||||||
|
|
||||||
|
return (new MailMessage())
|
||||||
|
->greeting(__("Hi :user!", [
|
||||||
|
"user" => $user,
|
||||||
|
]))
|
||||||
|
->subject(__("Vacation request :title has been created on your behalf", [
|
||||||
|
"title" => $title,
|
||||||
|
]))
|
||||||
|
->line(__("The vacation request :title has been created correctly by user :creator on your behalf in the :appName.", [
|
||||||
|
"title" => $title,
|
||||||
|
"appName" => $appName,
|
||||||
|
"creator" => $creator,
|
||||||
|
]))
|
||||||
|
->line(__("Vacation type: :type", [
|
||||||
|
"type" => $type,
|
||||||
|
]))
|
||||||
|
->line(__("From :from to :to (number of days: :days)", [
|
||||||
|
"from" => $from,
|
||||||
|
"to" => $to,
|
||||||
|
"days" => $days,
|
||||||
|
]))
|
||||||
|
->action(__("Click here for details"), $url);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Notifications;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Toby\Eloquent\Models\User;
|
||||||
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
|
class VacationRequestRejectedNotification extends Notification
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected VacationRequest $vacationRequest,
|
||||||
|
protected User $user,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function via(): array
|
||||||
|
{
|
||||||
|
return ["mail"];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function toMail(): MailMessage
|
||||||
|
{
|
||||||
|
$url = route(
|
||||||
|
"vacation.requests.show",
|
||||||
|
[
|
||||||
|
"vacationRequest" => $this->vacationRequest,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->buildMailMessage($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildMailMessage(string $url): MailMessage
|
||||||
|
{
|
||||||
|
$user = $this->user->first_name;
|
||||||
|
$title = $this->vacationRequest->name;
|
||||||
|
$type = $this->vacationRequest->type->label();
|
||||||
|
$from = $this->vacationRequest->from->toDisplayString();
|
||||||
|
$to = $this->vacationRequest->to->toDisplayString();
|
||||||
|
$days = $this->vacationRequest->vacations()->count();
|
||||||
|
$requester = $this->vacationRequest->user->fullName;
|
||||||
|
|
||||||
|
return (new MailMessage())
|
||||||
|
->greeting(__("Hi :user!", [
|
||||||
|
"user" => $user,
|
||||||
|
]))
|
||||||
|
->subject(__("Vacation request :title has been rejected", [
|
||||||
|
"title" => $title,
|
||||||
|
]))
|
||||||
|
->line(__("The vacation request :title for user :requester has been rejected.", [
|
||||||
|
"title" => $title,
|
||||||
|
"requester" => $requester,
|
||||||
|
]))
|
||||||
|
->line(__("Vacation type: :type", [
|
||||||
|
"type" => $type,
|
||||||
|
]))
|
||||||
|
->line(__("From :from to :to (number of days: :days)", [
|
||||||
|
"from" => $from,
|
||||||
|
"to" => $to,
|
||||||
|
"days" => $days,
|
||||||
|
]))
|
||||||
|
->action(__("Click here for details"), $url);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Notifications;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Toby\Eloquent\Models\User;
|
||||||
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
|
class VacationRequestWaitsForAdminApprovalNotification extends Notification
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected VacationRequest $vacationRequest,
|
||||||
|
protected User $user,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function via(): array
|
||||||
|
{
|
||||||
|
return ["mail"];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function toMail(): MailMessage
|
||||||
|
{
|
||||||
|
$url = route(
|
||||||
|
"vacation.requests.show",
|
||||||
|
[
|
||||||
|
"vacationRequest" => $this->vacationRequest,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->buildMailMessage($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildMailMessage(string $url): MailMessage
|
||||||
|
{
|
||||||
|
$user = $this->user->first_name;
|
||||||
|
$requester = $this->vacationRequest->user->fullName;
|
||||||
|
$title = $this->vacationRequest->name;
|
||||||
|
$type = $this->vacationRequest->type->label();
|
||||||
|
$from = $this->vacationRequest->from->toDisplayString();
|
||||||
|
$to = $this->vacationRequest->to->toDisplayString();
|
||||||
|
$days = $this->vacationRequest->vacations()->count();
|
||||||
|
|
||||||
|
return (new MailMessage())
|
||||||
|
->greeting(__("Hi :user!", [
|
||||||
|
"user" => $user,
|
||||||
|
]))
|
||||||
|
->subject(__("Vacation request :title is waiting for your approval", [
|
||||||
|
"title" => $title,
|
||||||
|
]))
|
||||||
|
->line(__("The vacation request :title from user: :requester is waiting for your approval.", [
|
||||||
|
"title" => $title,
|
||||||
|
"requester" => $requester,
|
||||||
|
]))
|
||||||
|
->line(__("Vacation type: :type", [
|
||||||
|
"type" => $type,
|
||||||
|
]))
|
||||||
|
->line(__("From :from to :to (number of days: :days)", [
|
||||||
|
"from" => $from,
|
||||||
|
"to" => $to,
|
||||||
|
"days" => $days,
|
||||||
|
]))
|
||||||
|
->action(__("Click here for details"), $url);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Domain\Notifications;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Toby\Eloquent\Models\User;
|
||||||
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
|
class VacationRequestWaitsForTechApprovalNotification extends Notification
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected VacationRequest $vacationRequest,
|
||||||
|
protected User $user,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function via(): array
|
||||||
|
{
|
||||||
|
return ["mail"];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function toMail(): MailMessage
|
||||||
|
{
|
||||||
|
$url = route(
|
||||||
|
"vacation.requests.show",
|
||||||
|
[
|
||||||
|
"vacationRequest" => $this->vacationRequest,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->buildMailMessage($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildMailMessage(string $url): MailMessage
|
||||||
|
{
|
||||||
|
$user = $this->user->first_name;
|
||||||
|
$requester = $this->vacationRequest->user->fullName;
|
||||||
|
$title = $this->vacationRequest->name;
|
||||||
|
$type = $this->vacationRequest->type->label();
|
||||||
|
$from = $this->vacationRequest->from->toDisplayString();
|
||||||
|
$to = $this->vacationRequest->to->toDisplayString();
|
||||||
|
$days = $this->vacationRequest->vacations()->count();
|
||||||
|
|
||||||
|
return (new MailMessage())
|
||||||
|
->greeting(__("Hi :user!", [
|
||||||
|
"user" => $user,
|
||||||
|
]))
|
||||||
|
->subject(__("Vacation request :title is waiting for your approval", [
|
||||||
|
"title" => $title,
|
||||||
|
]))
|
||||||
|
->line(__("The vacation request :title from user: :requester is waiting for your approval.", [
|
||||||
|
"title" => $title,
|
||||||
|
"requester" => $requester,
|
||||||
|
]))
|
||||||
|
->line(__("Vacation type: :type", [
|
||||||
|
"type" => $type,
|
||||||
|
]))
|
||||||
|
->line(__("From :from to :to (number of days: :days)", [
|
||||||
|
"from" => $from,
|
||||||
|
"to" => $to,
|
||||||
|
"days" => $days,
|
||||||
|
]))
|
||||||
|
->action(__("Click here for details"), $url);
|
||||||
|
}
|
||||||
|
}
|
@ -1,72 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Notifications;
|
|
||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
use Illuminate\Support\Carbon;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Toby\Infrastructure\Slack\Elements\SlackMessage;
|
|
||||||
use Toby\Infrastructure\Slack\Elements\VacationRequestsAttachment;
|
|
||||||
|
|
||||||
class VacationRequestsSummaryNotification extends Notification
|
|
||||||
{
|
|
||||||
use Queueable;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
protected Carbon $day,
|
|
||||||
protected Collection $vacationRequests,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function via(): array
|
|
||||||
{
|
|
||||||
return [Channels::MAIL, Channels::SLACK];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toSlack(): SlackMessage
|
|
||||||
{
|
|
||||||
return (new SlackMessage())
|
|
||||||
->text(__("Requests wait for your approval - status for day :date:", ["date" => $this->day->toDisplayString()]))
|
|
||||||
->withAttachment(new VacationRequestsAttachment($this->vacationRequests));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toMail(Notifiable $notifiable): MailMessage
|
|
||||||
{
|
|
||||||
$url = route(
|
|
||||||
"vacation.requests.indexForApprovers",
|
|
||||||
[
|
|
||||||
"status" => "waiting_for_action",
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
return $this->buildMailMessage($notifiable, $url);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function buildMailMessage(Notifiable $notifiable, string $url): MailMessage
|
|
||||||
{
|
|
||||||
$user = $notifiable->profile->first_name;
|
|
||||||
|
|
||||||
$message = (new MailMessage())
|
|
||||||
->greeting(
|
|
||||||
__("Hi :user!", [
|
|
||||||
"user" => $user,
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
->line (__("Requests list waits for your approval - status for day :date:", ["date" => $this->day->toDisplayString()]))
|
|
||||||
->subject(__("Requests wait for your approval - status for day :date:", ["date" => $this->day->toDisplayString()]));
|
|
||||||
|
|
||||||
foreach ($this->vacationRequests as $request) {
|
|
||||||
$url = route("vacation.requests.show", ["vacationRequest" => $request->id]);
|
|
||||||
|
|
||||||
$message->line(
|
|
||||||
__("- [request no. :request](:url) of user :user (:startDate - :endDate)", ["request" => $request->name, "url" => $url, "user" => $request->user->profile->full_name, "startDate" => $request->from->toDisplayString(), "endDate" => $request->to->toDisplayString()]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $message
|
|
||||||
->action(__("Go to requests"), $url);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Policies;
|
|
||||||
|
|
||||||
use Toby\Domain\Enums\Role;
|
|
||||||
use Toby\Eloquent\Models\Key;
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
|
|
||||||
class KeyPolicy
|
|
||||||
{
|
|
||||||
public function manage(User $user): bool
|
|
||||||
{
|
|
||||||
return $user->role === Role::AdministrativeApprover;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function give(User $user, Key $key): bool
|
|
||||||
{
|
|
||||||
if ($key->user()->is($user)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $user->role === Role::AdministrativeApprover;
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,9 +5,6 @@ declare(strict_types=1);
|
|||||||
namespace Toby\Domain\Policies;
|
namespace Toby\Domain\Policies;
|
||||||
|
|
||||||
use Toby\Domain\Enums\Role;
|
use Toby\Domain\Enums\Role;
|
||||||
use Toby\Domain\States\VacationRequest\Created;
|
|
||||||
use Toby\Domain\States\VacationRequest\WaitingForAdministrative;
|
|
||||||
use Toby\Domain\States\VacationRequest\WaitingForTechnical;
|
|
||||||
use Toby\Eloquent\Models\User;
|
use Toby\Eloquent\Models\User;
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
@ -43,16 +40,8 @@ class VacationRequestPolicy
|
|||||||
return in_array($user->role, [Role::AdministrativeApprover, Role::TechnicalApprover], true);
|
return in_array($user->role, [Role::AdministrativeApprover, Role::TechnicalApprover], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cancel(User $user, VacationRequest $vacationRequest): bool
|
public function cancel(User $user): bool
|
||||||
{
|
{
|
||||||
if ($vacationRequest->user->is($user) && $vacationRequest->state->equals(
|
|
||||||
Created::class,
|
|
||||||
WaitingForAdministrative::class,
|
|
||||||
WaitingForTechnical::class,
|
|
||||||
)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $user->role === Role::AdministrativeApprover;
|
return $user->role === Role::AdministrativeApprover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class PolishHolidaysRetriever
|
|||||||
|
|
||||||
protected function prepareHolidays(array $holidays): Collection
|
protected function prepareHolidays(array $holidays): Collection
|
||||||
{
|
{
|
||||||
return collect($holidays)->map(fn(Holiday $holiday): array => [
|
return collect($holidays)->map(fn(Holiday $holiday) => [
|
||||||
"name" => $holiday->getName([static::LANG_KEY]),
|
"name" => $holiday->getName([static::LANG_KEY]),
|
||||||
"date" => Carbon::createFromTimestamp($holiday->getTimestamp()),
|
"date" => Carbon::createFromTimestamp($holiday->getTimestamp()),
|
||||||
])->values();
|
])->values();
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain;
|
|
||||||
|
|
||||||
use Illuminate\Support\Carbon;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use PhpOffice\PhpWord\TemplateProcessor;
|
|
||||||
use Toby\Eloquent\Models\Resume;
|
|
||||||
|
|
||||||
class ResumeGenerator
|
|
||||||
{
|
|
||||||
public function generate(Resume $resume): string
|
|
||||||
{
|
|
||||||
$processor = new TemplateProcessor($this->getTemplate());
|
|
||||||
|
|
||||||
$processor->setValue("id", $resume->id);
|
|
||||||
$processor->setValue("name", $resume->user ? $resume->user->profile->full_name : $resume->name);
|
|
||||||
|
|
||||||
$this->fillTechnologies($processor, $resume);
|
|
||||||
$this->fillLanguages($processor, $resume);
|
|
||||||
$this->fillEducation($processor, $resume);
|
|
||||||
$this->fillProjects($processor, $resume);
|
|
||||||
|
|
||||||
return $processor->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTemplate(): string
|
|
||||||
{
|
|
||||||
return resource_path("views/docx/resume_eng.docx");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function fillTechnologies(TemplateProcessor $processor, Resume $resume): void
|
|
||||||
{
|
|
||||||
$processor->cloneBlock("technologies", 0, true, false, $this->getTechnologies($resume));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function fillLanguages(TemplateProcessor $processor, Resume $resume): void
|
|
||||||
{
|
|
||||||
$processor->cloneBlock("languages", 0, true, false, $this->getLanguages($resume));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function fillEducation(TemplateProcessor $processor, Resume $resume): void
|
|
||||||
{
|
|
||||||
$processor->cloneBlock("education", 0, true, false, $this->getEducation($resume));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function fillProjects(TemplateProcessor $processor, Resume $resume): void
|
|
||||||
{
|
|
||||||
$processor->cloneBlock("projects", $resume->projects->count(), true, true);
|
|
||||||
|
|
||||||
foreach ($resume->projects as $index => $project) {
|
|
||||||
++$index;
|
|
||||||
$processor->setValues($this->getProject($project, $index));
|
|
||||||
|
|
||||||
$processor->cloneBlock("project_technologies#{$index}", 0, true, false, $this->getProjectTechnologies($project, $index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getProject(array $project, int $index): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
"index#{$index}" => $index,
|
|
||||||
"start_date#{$index}" => Carbon::createFromFormat("m/Y", $project["startDate"])->format("n.Y"),
|
|
||||||
"end_date#{$index}" => $project["current"] ? "present" : Carbon::createFromFormat("m/Y", $project["endDate"])->format("n.Y"),
|
|
||||||
"description#{$index}" => $project["description"],
|
|
||||||
"tasks#{$index}" => $this->withNewLines($project["tasks"]),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function withNewLines(string $text): string
|
|
||||||
{
|
|
||||||
return Str::replace("\n", "</w:t><w:br/><w:t>", $text);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getProjectTechnologies(array $project, int $index): array
|
|
||||||
{
|
|
||||||
$technologies = new Collection($project["technologies"] ?? []);
|
|
||||||
|
|
||||||
return $technologies->map(fn(string $name) => [
|
|
||||||
"technology#{$index}" => $name,
|
|
||||||
])->all();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getTechnologies(Resume $resume): array
|
|
||||||
{
|
|
||||||
return $resume->technologies->map(fn(array $technology): array => [
|
|
||||||
"technology_name" => $technology["name"],
|
|
||||||
"technology_level" => __("resume.technology_levels.{$technology["level"]}"),
|
|
||||||
])->all();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getLanguages(Resume $resume): array
|
|
||||||
{
|
|
||||||
return $resume->languages->map(fn(array $language): array => [
|
|
||||||
"language_name" => $language["name"],
|
|
||||||
"language_level" => __("resume.language_levels.{$language["level"]}"),
|
|
||||||
])->all();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getEducation(Resume $resume): array
|
|
||||||
{
|
|
||||||
return $resume->education->map(fn(array $project, int $index): array => [
|
|
||||||
"start_date" => Carbon::createFromFormat("m/Y", $project["startDate"])->format("n.Y"),
|
|
||||||
"end_date" => $project["current"] ? "present" : Carbon::createFromFormat("m/Y", $project["endDate"])->format("n.Y"),
|
|
||||||
"school" => $project["school"],
|
|
||||||
"field_of_study" => $project["fieldOfStudy"],
|
|
||||||
"degree" => $project["degree"],
|
|
||||||
])->all();
|
|
||||||
}
|
|
||||||
}
|
|
@ -36,9 +36,4 @@ abstract class VacationRequestState extends State
|
|||||||
Approved::class,
|
Approved::class,
|
||||||
], Cancelled::class);
|
], Cancelled::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function label(): string
|
|
||||||
{
|
|
||||||
return __(static::$name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,21 +4,20 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Toby\Domain;
|
namespace Toby\Domain;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
||||||
use Toby\Eloquent\Models\User;
|
use Toby\Eloquent\Models\User;
|
||||||
|
|
||||||
class TimesheetExport implements WithMultipleSheets
|
class TimesheetExport implements WithMultipleSheets
|
||||||
{
|
{
|
||||||
protected Collection $users;
|
protected Collection $users;
|
||||||
protected Collection $types;
|
|
||||||
protected Carbon $month;
|
protected Carbon $month;
|
||||||
|
|
||||||
public function sheets(): array
|
public function sheets(): array
|
||||||
{
|
{
|
||||||
return $this->users
|
return $this->users
|
||||||
->map(fn(User $user): TimesheetPerUserSheet => new TimesheetPerUserSheet($user, $this->month, $this->types))
|
->map(fn(User $user) => new TimesheetPerUserSheet($user, $this->month))
|
||||||
->toArray();
|
->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,11 +34,4 @@ class TimesheetExport implements WithMultipleSheets
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function forVacationTypes(Collection $types): static
|
|
||||||
{
|
|
||||||
$this->types = $types;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ namespace Toby\Domain;
|
|||||||
use Carbon\CarbonInterface;
|
use Carbon\CarbonInterface;
|
||||||
use Carbon\CarbonPeriod;
|
use Carbon\CarbonPeriod;
|
||||||
use Generator;
|
use Generator;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Maatwebsite\Excel\Concerns\FromGenerator;
|
use Maatwebsite\Excel\Concerns\FromGenerator;
|
||||||
@ -26,6 +25,7 @@ use PhpOffice\PhpSpreadsheet\Style\Fill;
|
|||||||
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
||||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
use Toby\Domain\Enums\VacationType;
|
use Toby\Domain\Enums\VacationType;
|
||||||
|
use Toby\Domain\States\VacationRequest\Approved;
|
||||||
use Toby\Eloquent\Models\Holiday;
|
use Toby\Eloquent\Models\Holiday;
|
||||||
use Toby\Eloquent\Models\User;
|
use Toby\Eloquent\Models\User;
|
||||||
use Toby\Eloquent\Models\Vacation;
|
use Toby\Eloquent\Models\Vacation;
|
||||||
@ -41,16 +41,17 @@ class TimesheetPerUserSheet implements WithTitle, WithHeadings, WithEvents, With
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
protected User $user,
|
protected User $user,
|
||||||
protected Carbon $month,
|
protected Carbon $month,
|
||||||
protected Collection $types,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function title(): string
|
public function title(): string
|
||||||
{
|
{
|
||||||
return $this->user->profile->full_name;
|
return $this->user->fullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function headings(): array
|
public function headings(): array
|
||||||
{
|
{
|
||||||
|
$types = VacationType::cases();
|
||||||
|
|
||||||
$headings = [
|
$headings = [
|
||||||
__("Date"),
|
__("Date"),
|
||||||
__("Day of week"),
|
__("Day of week"),
|
||||||
@ -59,7 +60,7 @@ class TimesheetPerUserSheet implements WithTitle, WithHeadings, WithEvents, With
|
|||||||
__("Worked hours"),
|
__("Worked hours"),
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($this->types as $type) {
|
foreach ($types as $type) {
|
||||||
$headings[] = $type->label();
|
$headings[] = $type->label();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,14 +188,13 @@ class TimesheetPerUserSheet implements WithTitle, WithHeadings, WithEvents, With
|
|||||||
{
|
{
|
||||||
return $user->vacations()
|
return $user->vacations()
|
||||||
->with("vacationRequest")
|
->with("vacationRequest")
|
||||||
->whereRelation("vacationRequest", fn(Builder $query): Builder => $query->whereIn("type", $this->types))
|
|
||||||
->whereBetween("date", [$period->start, $period->end])
|
->whereBetween("date", [$period->start, $period->end])
|
||||||
->approved()
|
->whereRelation("vacationRequest", "state", Approved::$name)
|
||||||
->get()
|
->get()
|
||||||
->groupBy(
|
->groupBy(
|
||||||
[
|
[
|
||||||
fn(Vacation $vacation): string => $vacation->date->toDateString(),
|
fn(Vacation $vacation) => $vacation->date->toDateString(),
|
||||||
fn(Vacation $vacation): string => $vacation->vacationRequest->type->value,
|
fn(Vacation $vacation) => $vacation->vacationRequest->type->value,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,9 @@ declare(strict_types=1);
|
|||||||
namespace Toby\Domain;
|
namespace Toby\Domain;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Toby\Domain\Enums\VacationType;
|
use Toby\Domain\Enums\VacationType;
|
||||||
use Toby\Eloquent\Models\User;
|
use Toby\Eloquent\Models\User;
|
||||||
use Toby\Eloquent\Models\Vacation;
|
|
||||||
use Toby\Eloquent\Models\YearPeriod;
|
use Toby\Eloquent\Models\YearPeriod;
|
||||||
|
|
||||||
class UserVacationStatsRetriever
|
class UserVacationStatsRetriever
|
||||||
@ -21,39 +20,24 @@ class UserVacationStatsRetriever
|
|||||||
{
|
{
|
||||||
return $user
|
return $user
|
||||||
->vacations()
|
->vacations()
|
||||||
->whereBelongsTo($yearPeriod)
|
->where("year_period_id", $yearPeriod->id)
|
||||||
->whereRelation(
|
->whereRelation(
|
||||||
"vacationRequest",
|
"vacationRequest",
|
||||||
fn(Builder $query): Builder => $query
|
fn(Builder $query) => $query
|
||||||
->whereIn("type", $this->getLimitableVacationTypes())
|
->whereIn("type", $this->getLimitableVacationTypes())
|
||||||
->states(VacationRequestStatesRetriever::successStates()),
|
->states(VacationRequestStatesRetriever::successStates()),
|
||||||
)
|
)
|
||||||
->count();
|
->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUsedVacationDaysByMonth(User $user, YearPeriod $yearPeriod): Collection
|
|
||||||
{
|
|
||||||
return $user->vacations()
|
|
||||||
->whereBelongsTo($yearPeriod)
|
|
||||||
->whereRelation(
|
|
||||||
"vacationRequest",
|
|
||||||
fn(Builder $query): Builder => $query
|
|
||||||
->whereIn("type", $this->getLimitableVacationTypes())
|
|
||||||
->states(VacationRequestStatesRetriever::successStates()),
|
|
||||||
)
|
|
||||||
->get()
|
|
||||||
->groupBy(fn(Vacation $vacation): string => strtolower($vacation->date->englishMonth))
|
|
||||||
->map(fn(Collection $items): int => $items->count());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPendingVacationDays(User $user, YearPeriod $yearPeriod): int
|
public function getPendingVacationDays(User $user, YearPeriod $yearPeriod): int
|
||||||
{
|
{
|
||||||
return $user
|
return $user
|
||||||
->vacations()
|
->vacations()
|
||||||
->whereBelongsTo($yearPeriod)
|
->where("year_period_id", $yearPeriod->id)
|
||||||
->whereRelation(
|
->whereRelation(
|
||||||
"vacationRequest",
|
"vacationRequest",
|
||||||
fn(Builder $query): Builder => $query
|
fn(Builder $query) => $query
|
||||||
->whereIn("type", $this->getLimitableVacationTypes())
|
->whereIn("type", $this->getLimitableVacationTypes())
|
||||||
->states(VacationRequestStatesRetriever::pendingStates()),
|
->states(VacationRequestStatesRetriever::pendingStates()),
|
||||||
)
|
)
|
||||||
@ -64,26 +48,16 @@ class UserVacationStatsRetriever
|
|||||||
{
|
{
|
||||||
return $user
|
return $user
|
||||||
->vacations()
|
->vacations()
|
||||||
->whereBelongsTo($yearPeriod)
|
->where("year_period_id", $yearPeriod->id)
|
||||||
->whereRelation(
|
->whereRelation(
|
||||||
"vacationRequest",
|
"vacationRequest",
|
||||||
fn(Builder $query): Builder => $query
|
fn(Builder $query) => $query
|
||||||
->whereIn("type", $this->getNotLimitableVacationTypes())
|
->whereIn("type", $this->getNotLimitableVacationTypes())
|
||||||
->whereNot("type", VacationType::HomeOffice)
|
|
||||||
->states(VacationRequestStatesRetriever::successStates()),
|
->states(VacationRequestStatesRetriever::successStates()),
|
||||||
)
|
)
|
||||||
->count();
|
->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHomeOfficeDays(User $user, YearPeriod $yearPeriod): int
|
|
||||||
{
|
|
||||||
return $user
|
|
||||||
->vacations()
|
|
||||||
->whereBelongsTo($yearPeriod)
|
|
||||||
->whereRelation("vacationRequest", "type", VacationType::HomeOffice)
|
|
||||||
->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRemainingVacationDays(User $user, YearPeriod $yearPeriod): int
|
public function getRemainingVacationDays(User $user, YearPeriod $yearPeriod): int
|
||||||
{
|
{
|
||||||
$limit = $this->getVacationDaysLimit($user, $yearPeriod);
|
$limit = $this->getVacationDaysLimit($user, $yearPeriod);
|
||||||
@ -96,24 +70,24 @@ class UserVacationStatsRetriever
|
|||||||
public function getVacationDaysLimit(User $user, YearPeriod $yearPeriod): int
|
public function getVacationDaysLimit(User $user, YearPeriod $yearPeriod): int
|
||||||
{
|
{
|
||||||
$limit = $user->vacationLimits()
|
$limit = $user->vacationLimits()
|
||||||
->whereBelongsTo($yearPeriod)
|
->where("year_period_id", $yearPeriod->id)
|
||||||
->first()
|
->first()
|
||||||
?->days;
|
->days;
|
||||||
|
|
||||||
return $limit ?? 0;
|
return $limit ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getLimitableVacationTypes(): Collection
|
protected function getLimitableVacationTypes(): Collection
|
||||||
{
|
{
|
||||||
$types = VacationType::all();
|
$types = new Collection(VacationType::cases());
|
||||||
|
|
||||||
return $types->filter(fn(VacationType $type): bool => $this->configRetriever->hasLimit($type));
|
return $types->filter(fn(VacationType $type) => $this->configRetriever->hasLimit($type));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getNotLimitableVacationTypes(): Collection
|
protected function getNotLimitableVacationTypes(): Collection
|
||||||
{
|
{
|
||||||
$types = VacationType::all();
|
$types = new Collection(VacationType::cases());
|
||||||
|
|
||||||
return $types->filter(fn(VacationType $type): bool => !$this->configRetriever->hasLimit($type));
|
return $types->filter(fn(VacationType $type) => !$this->configRetriever->hasLimit($type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,11 @@ use Carbon\CarbonPeriod;
|
|||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Toby\Eloquent\Models\YearPeriod;
|
use Toby\Eloquent\Models\YearPeriod;
|
||||||
|
|
||||||
class WorkDaysCalculator
|
class VacationDaysCalculator
|
||||||
{
|
{
|
||||||
public function calculateDays(CarbonInterface $from, CarbonInterface $to): Collection
|
public function calculateDays(YearPeriod $yearPeriod, CarbonInterface $from, CarbonInterface $to): Collection
|
||||||
{
|
{
|
||||||
$period = CarbonPeriod::create($from, $to);
|
$period = CarbonPeriod::create($from, $to);
|
||||||
$yearPeriod = YearPeriod::findByYear($from->year);
|
|
||||||
$holidays = $yearPeriod->holidays()->pluck("date");
|
$holidays = $yearPeriod->holidays()->pluck("date");
|
||||||
|
|
||||||
$validDays = new Collection();
|
$validDays = new Collection();
|
@ -4,7 +4,17 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Toby\Domain;
|
namespace Toby\Domain;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Auth\Factory as Auth;
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
|
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\AcceptedByAdministrative;
|
||||||
use Toby\Domain\States\VacationRequest\AcceptedByTechnical;
|
use Toby\Domain\States\VacationRequest\AcceptedByTechnical;
|
||||||
use Toby\Domain\States\VacationRequest\Approved;
|
use Toby\Domain\States\VacationRequest\Approved;
|
||||||
@ -19,47 +29,63 @@ use Toby\Eloquent\Models\VacationRequest;
|
|||||||
class VacationRequestStateManager
|
class VacationRequestStateManager
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
protected Auth $auth,
|
||||||
protected Dispatcher $dispatcher,
|
protected Dispatcher $dispatcher,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function markAsCreated(VacationRequest $vacationRequest): void
|
public function markAsCreated(VacationRequest $vacationRequest, ?User $user = null): void
|
||||||
{
|
{
|
||||||
$this->createActivity($vacationRequest, null, $vacationRequest->state, $vacationRequest->creator);
|
$this->fireStateChangedEvent($vacationRequest, null, $vacationRequest->state, $user);
|
||||||
|
|
||||||
|
$this->dispatcher->dispatch(new VacationRequestCreated($vacationRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function approve(VacationRequest $vacationRequest, ?User $user = null): void
|
public function approve(VacationRequest $vacationRequest, ?User $user = null): void
|
||||||
{
|
{
|
||||||
$this->changeState($vacationRequest, Approved::class, $user);
|
$this->changeState($vacationRequest, Approved::class, $user);
|
||||||
|
|
||||||
|
$this->dispatcher->dispatch(new VacationRequestApproved($vacationRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reject(VacationRequest $vacationRequest, User $user): void
|
public function reject(VacationRequest $vacationRequest, ?User $user = null): void
|
||||||
{
|
{
|
||||||
$this->changeState($vacationRequest, Rejected::class, $user);
|
$this->changeState($vacationRequest, Rejected::class, $user);
|
||||||
|
$this->dispatcher->dispatch(new VacationRequestRejected($vacationRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cancel(VacationRequest $vacationRequest, User $user): void
|
public function cancel(VacationRequest $vacationRequest, ?User $user = null): void
|
||||||
{
|
{
|
||||||
$this->changeState($vacationRequest, Cancelled::class, $user);
|
$this->changeState($vacationRequest, Cancelled::class, $user);
|
||||||
|
|
||||||
|
$this->dispatcher->dispatch(new VacationRequestCancelled($vacationRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function acceptAsTechnical(VacationRequest $vacationRequest, User $user): void
|
public function acceptAsTechnical(VacationRequest $vacationRequest, ?User $user = null): void
|
||||||
{
|
{
|
||||||
$this->changeState($vacationRequest, AcceptedByTechnical::class, $user);
|
$this->changeState($vacationRequest, AcceptedByTechnical::class, $user);
|
||||||
|
|
||||||
|
$this->dispatcher->dispatch(new VacationRequestAcceptedByTechnical($vacationRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function acceptAsAdministrative(VacationRequest $vacationRequest, User $user): void
|
public function acceptAsAdministrative(VacationRequest $vacationRequest, ?User $user = null): void
|
||||||
{
|
{
|
||||||
$this->changeState($vacationRequest, AcceptedByAdministrative::class, $user);
|
$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, WaitingForTechnical::class);
|
$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, WaitingForAdministrative::class);
|
$this->changeState($vacationRequest, WaitingForAdministrative::class, $user);
|
||||||
|
|
||||||
|
$this->dispatcher->dispatch(new VacationRequestWaitsForAdminApproval($vacationRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function changeState(VacationRequest $vacationRequest, string $state, ?User $user = null): void
|
protected function changeState(VacationRequest $vacationRequest, string $state, ?User $user = null): void
|
||||||
@ -68,19 +94,16 @@ class VacationRequestStateManager
|
|||||||
$vacationRequest->state->transitionTo($state);
|
$vacationRequest->state->transitionTo($state);
|
||||||
$vacationRequest->save();
|
$vacationRequest->save();
|
||||||
|
|
||||||
$this->createActivity($vacationRequest, $previousState, $vacationRequest->state, $user);
|
$this->fireStateChangedEvent($vacationRequest, $previousState, $vacationRequest->state, $user);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createActivity(
|
protected function fireStateChangedEvent(
|
||||||
VacationRequest $vacationRequest,
|
VacationRequest $vacationRequest,
|
||||||
?VacationRequestState $from,
|
?VacationRequestState $from,
|
||||||
VacationRequestState $to,
|
VacationRequestState $to,
|
||||||
?User $user = null,
|
?User $user = null,
|
||||||
): void {
|
): void {
|
||||||
$vacationRequest->activities()->create([
|
$event = new VacationRequestStateChanged($vacationRequest, $from, $to, $user);
|
||||||
"from" => $from,
|
$this->dispatcher->dispatch($event);
|
||||||
"to" => $to,
|
|
||||||
"user_id" => $user?->id,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||||||
namespace Toby\Domain;
|
namespace Toby\Domain;
|
||||||
|
|
||||||
use Illuminate\Contracts\Config\Repository;
|
use Illuminate\Contracts\Config\Repository;
|
||||||
use Toby\Domain\Enums\EmploymentForm;
|
|
||||||
use Toby\Domain\Enums\VacationType;
|
use Toby\Domain\Enums\VacationType;
|
||||||
|
|
||||||
class VacationTypeConfigRetriever
|
class VacationTypeConfigRetriever
|
||||||
@ -14,8 +13,6 @@ class VacationTypeConfigRetriever
|
|||||||
public const KEY_ADMINISTRATIVE_APPROVAL = "administrative_approval";
|
public const KEY_ADMINISTRATIVE_APPROVAL = "administrative_approval";
|
||||||
public const KEY_BILLABLE = "billable";
|
public const KEY_BILLABLE = "billable";
|
||||||
public const KEY_HAS_LIMIT = "has_limit";
|
public const KEY_HAS_LIMIT = "has_limit";
|
||||||
public const KEY_AVAILABLE_FOR = "available_for";
|
|
||||||
public const KEY_IS_VACATION = "is_vacation";
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected Repository $config,
|
protected Repository $config,
|
||||||
@ -41,16 +38,6 @@ class VacationTypeConfigRetriever
|
|||||||
return $this->getConfigFor($type)[static::KEY_HAS_LIMIT];
|
return $this->getConfigFor($type)[static::KEY_HAS_LIMIT];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isVacation(VacationType $type): bool
|
|
||||||
{
|
|
||||||
return $this->getConfigFor($type)[static::KEY_IS_VACATION];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isAvailableFor(VacationType $type, EmploymentForm $employmentForm): bool
|
|
||||||
{
|
|
||||||
return in_array($employmentForm, $this->getConfigFor($type)[static::KEY_AVAILABLE_FOR], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getConfigFor(VacationType $type): array
|
protected function getConfigFor(VacationType $type): array
|
||||||
{
|
{
|
||||||
return $this->config->get("vacation_types.{$type->value}");
|
return $this->config->get("vacation_types.{$type->value}");
|
||||||
|
@ -5,11 +5,11 @@ declare(strict_types=1);
|
|||||||
namespace Toby\Domain\Validation\Rules;
|
namespace Toby\Domain\Validation\Rules;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Toby\Domain\Enums\VacationType;
|
use Toby\Domain\Enums\VacationType;
|
||||||
|
use Toby\Domain\VacationDaysCalculator;
|
||||||
use Toby\Domain\VacationRequestStatesRetriever;
|
use Toby\Domain\VacationRequestStatesRetriever;
|
||||||
use Toby\Domain\VacationTypeConfigRetriever;
|
use Toby\Domain\VacationTypeConfigRetriever;
|
||||||
use Toby\Domain\WorkDaysCalculator;
|
|
||||||
use Toby\Eloquent\Models\User;
|
use Toby\Eloquent\Models\User;
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
use Toby\Eloquent\Models\YearPeriod;
|
use Toby\Eloquent\Models\YearPeriod;
|
||||||
@ -18,7 +18,7 @@ class DoesNotExceedLimitRule implements VacationRequestRule
|
|||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected VacationTypeConfigRetriever $configRetriever,
|
protected VacationTypeConfigRetriever $configRetriever,
|
||||||
protected WorkDaysCalculator $workDaysCalculator,
|
protected VacationDaysCalculator $vacationDaysCalculator,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function check(VacationRequest $vacationRequest): bool
|
public function check(VacationRequest $vacationRequest): bool
|
||||||
@ -29,9 +29,7 @@ class DoesNotExceedLimitRule implements VacationRequestRule
|
|||||||
|
|
||||||
$limit = $this->getUserVacationLimit($vacationRequest->user, $vacationRequest->yearPeriod);
|
$limit = $this->getUserVacationLimit($vacationRequest->user, $vacationRequest->yearPeriod);
|
||||||
$vacationDays = $this->getVacationDaysWithLimit($vacationRequest->user, $vacationRequest->yearPeriod);
|
$vacationDays = $this->getVacationDaysWithLimit($vacationRequest->user, $vacationRequest->yearPeriod);
|
||||||
$estimatedDays = $this->workDaysCalculator
|
$estimatedDays = $this->vacationDaysCalculator->calculateDays($vacationRequest->yearPeriod, $vacationRequest->from, $vacationRequest->to)->count();
|
||||||
->calculateDays($vacationRequest->from, $vacationRequest->to)
|
|
||||||
->count();
|
|
||||||
|
|
||||||
return $limit >= ($vacationDays + $estimatedDays);
|
return $limit >= ($vacationDays + $estimatedDays);
|
||||||
}
|
}
|
||||||
@ -43,19 +41,16 @@ class DoesNotExceedLimitRule implements VacationRequestRule
|
|||||||
|
|
||||||
protected function getUserVacationLimit(User $user, YearPeriod $yearPeriod): int
|
protected function getUserVacationLimit(User $user, YearPeriod $yearPeriod): int
|
||||||
{
|
{
|
||||||
return $user->vacationLimits()
|
return $user->vacationLimits()->where("year_period_id", $yearPeriod->id)->first()->days ?? 0;
|
||||||
->whereBelongsTo($yearPeriod)
|
|
||||||
->first()
|
|
||||||
?->days ?? 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getVacationDaysWithLimit(User $user, YearPeriod $yearPeriod): int
|
protected function getVacationDaysWithLimit(User $user, YearPeriod $yearPeriod): int
|
||||||
{
|
{
|
||||||
return $user->vacations()
|
return $user->vacations()
|
||||||
->whereBelongsTo($yearPeriod)
|
->where("year_period_id", $yearPeriod->id)
|
||||||
->whereRelation(
|
->whereRelation(
|
||||||
"vacationRequest",
|
"vacationRequest",
|
||||||
fn(Builder $query): Builder => $query
|
fn(Builder $query) => $query
|
||||||
->whereIn("type", $this->getLimitableVacationTypes())
|
->whereIn("type", $this->getLimitableVacationTypes())
|
||||||
->noStates(VacationRequestStatesRetriever::failedStates()),
|
->noStates(VacationRequestStatesRetriever::failedStates()),
|
||||||
)
|
)
|
||||||
@ -64,8 +59,8 @@ class DoesNotExceedLimitRule implements VacationRequestRule
|
|||||||
|
|
||||||
protected function getLimitableVacationTypes(): Collection
|
protected function getLimitableVacationTypes(): Collection
|
||||||
{
|
{
|
||||||
$types = VacationType::all();
|
$types = new Collection(VacationType::cases());
|
||||||
|
|
||||||
return $types->filter(fn(VacationType $type): bool => $this->configRetriever->hasLimit($type));
|
return $types->filter(fn(VacationType $type) => $this->configRetriever->hasLimit($type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,19 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Toby\Domain\Validation\Rules;
|
namespace Toby\Domain\Validation\Rules;
|
||||||
|
|
||||||
use Toby\Domain\WorkDaysCalculator;
|
use Toby\Domain\VacationDaysCalculator;
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
class MinimumOneVacationDayRule implements VacationRequestRule
|
class MinimumOneVacationDayRule implements VacationRequestRule
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected WorkDaysCalculator $workDaysCalculator,
|
protected VacationDaysCalculator $vacationDaysCalculator,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function check(VacationRequest $vacationRequest): bool
|
public function check(VacationRequest $vacationRequest): bool
|
||||||
{
|
{
|
||||||
return $this->workDaysCalculator
|
return $this->vacationDaysCalculator
|
||||||
->calculateDays($vacationRequest->from, $vacationRequest->to)
|
->calculateDays($vacationRequest->yearPeriod, $vacationRequest->from, $vacationRequest->to)
|
||||||
->isNotEmpty();
|
->isNotEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,5 @@ use Toby\Eloquent\Models\VacationRequest;
|
|||||||
interface VacationRequestRule
|
interface VacationRequestRule
|
||||||
{
|
{
|
||||||
public function check(VacationRequest $vacationRequest): bool;
|
public function check(VacationRequest $vacationRequest): bool;
|
||||||
|
|
||||||
public function errorMessage(): string;
|
public function errorMessage(): string;
|
||||||
}
|
}
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Domain\Validation\Rules;
|
|
||||||
|
|
||||||
use Toby\Domain\Enums\VacationType;
|
|
||||||
use Toby\Domain\VacationTypeConfigRetriever;
|
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
|
||||||
|
|
||||||
class VacationTypeCanBeSelected implements VacationRequestRule
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected VacationTypeConfigRetriever $configRetriever,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function check(VacationRequest $vacationRequest): bool
|
|
||||||
{
|
|
||||||
$employmentForm = $vacationRequest->user->profile->employment_form;
|
|
||||||
|
|
||||||
$availableTypes = VacationType::all()
|
|
||||||
->filter(fn(VacationType $type): bool => $this->configRetriever->isAvailableFor($type, $employmentForm));
|
|
||||||
|
|
||||||
return $availableTypes->contains($vacationRequest->type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function errorMessage(): string
|
|
||||||
{
|
|
||||||
return __("You cannot create vacation request of this type.");
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,6 @@ use Toby\Domain\Validation\Rules\NoApprovedVacationRequestsInRange;
|
|||||||
use Toby\Domain\Validation\Rules\NoPendingVacationRequestInRange;
|
use Toby\Domain\Validation\Rules\NoPendingVacationRequestInRange;
|
||||||
use Toby\Domain\Validation\Rules\VacationRangeIsInTheSameYearRule;
|
use Toby\Domain\Validation\Rules\VacationRangeIsInTheSameYearRule;
|
||||||
use Toby\Domain\Validation\Rules\VacationRequestRule;
|
use Toby\Domain\Validation\Rules\VacationRequestRule;
|
||||||
use Toby\Domain\Validation\Rules\VacationTypeCanBeSelected;
|
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
class VacationRequestValidator
|
class VacationRequestValidator
|
||||||
@ -20,7 +19,6 @@ class VacationRequestValidator
|
|||||||
protected array $rules = [
|
protected array $rules = [
|
||||||
VacationRangeIsInTheSameYearRule::class,
|
VacationRangeIsInTheSameYearRule::class,
|
||||||
MinimumOneVacationDayRule::class,
|
MinimumOneVacationDayRule::class,
|
||||||
VacationTypeCanBeSelected::class,
|
|
||||||
DoesNotExceedLimitRule::class,
|
DoesNotExceedLimitRule::class,
|
||||||
NoPendingVacationRequestInRange::class,
|
NoPendingVacationRequestInRange::class,
|
||||||
NoApprovedVacationRequestsInRange::class,
|
NoApprovedVacationRequestsInRange::class,
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Eloquent\Helpers;
|
|
||||||
|
|
||||||
class ColorGenerator
|
|
||||||
{
|
|
||||||
public static function generate(string $text): string
|
|
||||||
{
|
|
||||||
$colors = config("colors");
|
|
||||||
$hash = static::calculateHash($text);
|
|
||||||
|
|
||||||
$index = $hash - count($colors) * floor($hash / count($colors));
|
|
||||||
|
|
||||||
return $colors[$index];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function calculateHash(string $text): int
|
|
||||||
{
|
|
||||||
$hash = 0;
|
|
||||||
|
|
||||||
if (empty($text)) {
|
|
||||||
return $hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($i = 0; $i < mb_strlen($text); $i++) {
|
|
||||||
$hash = abs((int)(($hash << 2) - $hash) + mb_ord($text[$i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $hash;
|
|
||||||
}
|
|
||||||
}
|
|
@ -30,20 +30,22 @@ class YearPeriodRetriever
|
|||||||
|
|
||||||
public function links(): array
|
public function links(): array
|
||||||
{
|
{
|
||||||
$selected = $this->selected();
|
$current = $this->selected();
|
||||||
$current = $this->current();
|
|
||||||
|
|
||||||
$years = YearPeriod::all();
|
$years = YearPeriod::query()->whereIn("year", $this->offset($current->year))->get();
|
||||||
|
$navigation = $years->map(fn(YearPeriod $yearPeriod) => $this->toNavigation($yearPeriod));
|
||||||
$navigation = $years->map(fn(YearPeriod $yearPeriod): array => $this->toNavigation($yearPeriod));
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
"current" => $this->toNavigation($current),
|
"current" => $current->year,
|
||||||
"selected" => $this->toNavigation($selected),
|
|
||||||
"navigation" => $navigation->toArray(),
|
"navigation" => $navigation->toArray(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function offset(int $year): array
|
||||||
|
{
|
||||||
|
return range($year - 2, $year + 2);
|
||||||
|
}
|
||||||
|
|
||||||
protected function toNavigation(YearPeriod $yearPeriod): array
|
protected function toNavigation(YearPeriod $yearPeriod): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
@ -21,6 +21,7 @@ class Holiday extends Model
|
|||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
"date" => "date",
|
"date" => "date",
|
||||||
];
|
];
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Eloquent\Models;
|
|
||||||
|
|
||||||
use Database\Factories\KeyFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
||||||
use Illuminate\Notifications\Notifiable;
|
|
||||||
use Toby\Domain\Notifications\Notifiable as NotifiableInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @property int $id
|
|
||||||
* @property User $user
|
|
||||||
*/
|
|
||||||
class Key extends Model implements NotifiableInterface
|
|
||||||
{
|
|
||||||
use HasFactory;
|
|
||||||
use Notifiable;
|
|
||||||
|
|
||||||
protected $guarded = [];
|
|
||||||
|
|
||||||
public function user(): BelongsTo
|
|
||||||
{
|
|
||||||
return $this->belongsTo(User::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function routeNotificationForSlack(): string
|
|
||||||
{
|
|
||||||
return config("services.slack.default_channel");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function newFactory(): KeyFactory
|
|
||||||
{
|
|
||||||
return KeyFactory::new();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Eloquent\Models;
|
|
||||||
|
|
||||||
use Database\Factories\ProfileFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
||||||
use Illuminate\Support\Carbon;
|
|
||||||
use Rackbeat\UIAvatars\HasAvatar;
|
|
||||||
use Toby\Domain\Enums\EmploymentForm;
|
|
||||||
use Toby\Eloquent\Helpers\ColorGenerator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @property string $first_name
|
|
||||||
* @property string $last_name
|
|
||||||
* @property string $position
|
|
||||||
* @property EmploymentForm $employment_form
|
|
||||||
* @property Carbon $employment_date
|
|
||||||
* @property Carbon $birthday
|
|
||||||
*/
|
|
||||||
class Profile extends Model
|
|
||||||
{
|
|
||||||
use HasFactory;
|
|
||||||
use HasAvatar;
|
|
||||||
|
|
||||||
protected $primaryKey = "user_id";
|
|
||||||
protected $guarded = [];
|
|
||||||
protected $casts = [
|
|
||||||
"employment_form" => EmploymentForm::class,
|
|
||||||
"employment_date" => "date",
|
|
||||||
"birthday" => "date",
|
|
||||||
];
|
|
||||||
|
|
||||||
public function user(): BelongsTo
|
|
||||||
{
|
|
||||||
return $this->belongsTo(User::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAvatar(): string
|
|
||||||
{
|
|
||||||
return $this->getAvatarGenerator()
|
|
||||||
->backgroundColor(ColorGenerator::generate($this->full_name))
|
|
||||||
->image();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getfullNameAttribute(): string
|
|
||||||
{
|
|
||||||
return "{$this->first_name} {$this->last_name}";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getAvatarName(): string
|
|
||||||
{
|
|
||||||
return mb_substr($this->first_name, 0, 1) . mb_substr($this->last_name, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function newFactory(): ProfileFactory
|
|
||||||
{
|
|
||||||
return ProfileFactory::new();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Eloquent\Models;
|
|
||||||
|
|
||||||
use Database\Factories\ResumeFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Casts\AsCollection;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @property int $id
|
|
||||||
* @property ?User $user
|
|
||||||
* @property string $name
|
|
||||||
* @property Collection $education
|
|
||||||
* @property Collection $languages
|
|
||||||
* @property Collection $technologies
|
|
||||||
* @property Collection $projects
|
|
||||||
*/
|
|
||||||
class Resume extends Model
|
|
||||||
{
|
|
||||||
use HasFactory;
|
|
||||||
|
|
||||||
protected $guarded = [];
|
|
||||||
protected $casts = [
|
|
||||||
"education" => AsCollection::class,
|
|
||||||
"languages" => AsCollection::class,
|
|
||||||
"technologies" => AsCollection::class,
|
|
||||||
"projects" => AsCollection::class,
|
|
||||||
];
|
|
||||||
protected $perPage = 50;
|
|
||||||
|
|
||||||
public function user(): BelongsTo
|
|
||||||
{
|
|
||||||
return $this->belongsTo(User::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function newFactory(): ResumeFactory
|
|
||||||
{
|
|
||||||
return ResumeFactory::new();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Eloquent\Models;
|
|
||||||
|
|
||||||
use Database\Factories\TechnologyFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @property int $id
|
|
||||||
* @property string $name
|
|
||||||
*/
|
|
||||||
class Technology extends Model
|
|
||||||
{
|
|
||||||
use HasFactory;
|
|
||||||
|
|
||||||
protected $guarded = [];
|
|
||||||
|
|
||||||
protected static function newFactory(): TechnologyFactory
|
|
||||||
{
|
|
||||||
return TechnologyFactory::new();
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,50 +8,47 @@ use Database\Factories\UserFactory;
|
|||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Rackbeat\UIAvatars\HasAvatar;
|
||||||
use Toby\Domain\Enums\EmploymentForm;
|
use Toby\Domain\Enums\EmploymentForm;
|
||||||
use Toby\Domain\Enums\Role;
|
use Toby\Domain\Enums\Role;
|
||||||
use Toby\Domain\Notifications\Notifiable as NotifiableInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
|
* @property string $first_name
|
||||||
|
* @property string $last_name
|
||||||
* @property string $email
|
* @property string $email
|
||||||
* @property string $password
|
* @property string $avatar
|
||||||
|
* @property string $position
|
||||||
* @property Role $role
|
* @property Role $role
|
||||||
* @property Profile $profile
|
* @property EmploymentForm $employment_form
|
||||||
|
* @property Carbon $employment_date
|
||||||
* @property Collection $vacationLimits
|
* @property Collection $vacationLimits
|
||||||
* @property Collection $vacationRequests
|
* @property Collection $vacationRequests
|
||||||
* @property Collection $vacations
|
* @property Collection $vacations
|
||||||
*/
|
*/
|
||||||
class User extends Authenticatable implements NotifiableInterface
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use Notifiable;
|
use Notifiable;
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
|
use HasAvatar;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
"role" => Role::class,
|
"role" => Role::class,
|
||||||
"last_active_at" => "datetime",
|
|
||||||
"employment_form" => EmploymentForm::class,
|
"employment_form" => EmploymentForm::class,
|
||||||
"employment_date" => "date",
|
"employment_date" => "date",
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
"remember_token",
|
"remember_token",
|
||||||
];
|
];
|
||||||
protected $with = [
|
|
||||||
"profile",
|
|
||||||
];
|
|
||||||
protected $perPage = 50;
|
|
||||||
|
|
||||||
public function profile(): HasOne
|
|
||||||
{
|
|
||||||
return $this->hasOne(Profile::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function vacationLimits(): HasMany
|
public function vacationLimits(): HasMany
|
||||||
{
|
{
|
||||||
@ -73,24 +70,6 @@ class User extends Authenticatable implements NotifiableInterface
|
|||||||
return $this->hasMany(Vacation::class);
|
return $this->hasMany(Vacation::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function keys(): HasMany
|
|
||||||
{
|
|
||||||
return $this->hasMany(Key::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasRole(Role $role): bool
|
|
||||||
{
|
|
||||||
return $this->role === $role;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasVacationLimit(YearPeriod $yearPeriod): bool
|
|
||||||
{
|
|
||||||
return $this->vacationLimits()
|
|
||||||
->whereBelongsTo($yearPeriod)
|
|
||||||
->whereNotNull("days")
|
|
||||||
->exists();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function scopeSearch(Builder $query, ?string $text): Builder
|
public function scopeSearch(Builder $query, ?string $text): Builder
|
||||||
{
|
{
|
||||||
if ($text === null) {
|
if ($text === null) {
|
||||||
@ -98,35 +77,33 @@ class User extends Authenticatable implements NotifiableInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $query
|
return $query
|
||||||
->where("email", "ILIKE", "%{$text}%")
|
->where("first_name", "ILIKE", $text)
|
||||||
->orWhereRelation(
|
->orWhere("last_name", "ILIKE", $text)
|
||||||
"profile",
|
->orWhere("email", "ILIKE", $text);
|
||||||
fn(Builder $query): Builder => $query
|
|
||||||
->where("first_name", "ILIKE", "%{$text}%")
|
|
||||||
->orWhere("last_name", "ILIKE", "%{$text}%"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function scopeOrderByProfileField(Builder $query, string $field): Builder
|
public function getAvatar(): string
|
||||||
{
|
{
|
||||||
$profileQuery = Profile::query()->select($field)->whereColumn("users.id", "profiles.user_id");
|
$colors = config("colors");
|
||||||
|
|
||||||
return $query->orderBy($profileQuery);
|
return $this->getAvatarGenerator()
|
||||||
|
->backgroundColor($colors[strlen($this->fullname) % count($colors)])
|
||||||
|
->image();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function scopeWithVacationLimitIn(Builder $query, YearPeriod $yearPeriod): Builder
|
public function getFullNameAttribute(): string
|
||||||
{
|
{
|
||||||
return $query->whereRelation(
|
return "{$this->first_name} {$this->last_name}";
|
||||||
"vacationlimits",
|
|
||||||
fn(Builder $query): Builder => $query
|
|
||||||
->whereBelongsTo($yearPeriod)
|
|
||||||
->whereNotNull("days"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function routeNotificationForSlack()
|
public function hasRole(Role $role): bool
|
||||||
{
|
{
|
||||||
return $this->profile->slack_id;
|
return $this->role === $role;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getAvatarNameKey(): string
|
||||||
|
{
|
||||||
|
return "fullName";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function newFactory(): UserFactory
|
protected static function newFactory(): UserFactory
|
||||||
|
@ -4,17 +4,15 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Toby\Eloquent\Models;
|
namespace Toby\Eloquent\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Toby\Domain\VacationRequestStatesRetriever;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
* @property Carbon $date
|
* @property Carbon $date
|
||||||
|
* @property string $event_id
|
||||||
* @property User $user
|
* @property User $user
|
||||||
* @property VacationRequest $vacationRequest
|
* @property VacationRequest $vacationRequest
|
||||||
* @property YearPeriod $yearPeriod
|
* @property YearPeriod $yearPeriod
|
||||||
@ -43,28 +41,4 @@ class Vacation extends Model
|
|||||||
{
|
{
|
||||||
return $this->belongsTo(YearPeriod::class);
|
return $this->belongsTo(YearPeriod::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function scopeApproved(Builder $query): Builder
|
|
||||||
{
|
|
||||||
return $query->whereRelation(
|
|
||||||
"vacationRequest",
|
|
||||||
fn(Builder $query): Builder => $query->states(VacationRequestStatesRetriever::successStates()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function scopePending(Builder $query): Builder
|
|
||||||
{
|
|
||||||
return $query->whereRelation(
|
|
||||||
"vacationRequest",
|
|
||||||
fn(Builder $query): Builder => $query->states(VacationRequestStatesRetriever::pendingStates()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function scopeWhereTypes(Builder $query, Collection $types): Builder
|
|
||||||
{
|
|
||||||
return $query->whereRelation(
|
|
||||||
"vacationRequest",
|
|
||||||
fn(Builder $query): Builder => $query->whereIn("type", $types),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace Toby\Eloquent\Models;
|
namespace Toby\Eloquent\Models;
|
||||||
|
|
||||||
use Database\Factories\VacationLimitFactory;
|
use Database\Factories\VacationLimitFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
@ -36,6 +37,13 @@ class VacationLimit extends Model
|
|||||||
return $this->belongsTo(YearPeriod::class);
|
return $this->belongsTo(YearPeriod::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scopeOrderByUserField(Builder $query, string $field): Builder
|
||||||
|
{
|
||||||
|
$userQuery = User::query()->select($field)->whereColumn("vacation_limits.user_id", "users.id");
|
||||||
|
|
||||||
|
return $query->orderBy($userQuery);
|
||||||
|
}
|
||||||
|
|
||||||
protected static function newFactory(): VacationLimitFactory
|
protected static function newFactory(): VacationLimitFactory
|
||||||
{
|
{
|
||||||
return VacationLimitFactory::new();
|
return VacationLimitFactory::new();
|
||||||
|
@ -6,12 +6,10 @@ namespace Toby\Eloquent\Models;
|
|||||||
|
|
||||||
use Database\Factories\VacationRequestFactory;
|
use Database\Factories\VacationRequestFactory;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Casts\AsCollection;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Spatie\ModelStates\HasStates;
|
use Spatie\ModelStates\HasStates;
|
||||||
@ -20,7 +18,6 @@ use Toby\Domain\States\VacationRequest\VacationRequestState;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
* @property string $name
|
|
||||||
* @property VacationType $type
|
* @property VacationType $type
|
||||||
* @property VacationRequestState $state
|
* @property VacationRequestState $state
|
||||||
* @property Carbon $from
|
* @property Carbon $from
|
||||||
@ -32,7 +29,6 @@ use Toby\Domain\States\VacationRequest\VacationRequestState;
|
|||||||
* @property YearPeriod $yearPeriod
|
* @property YearPeriod $yearPeriod
|
||||||
* @property Collection $activities
|
* @property Collection $activities
|
||||||
* @property Collection $vacations
|
* @property Collection $vacations
|
||||||
* @property Collection $event_ids
|
|
||||||
* @property Carbon $created_at
|
* @property Carbon $created_at
|
||||||
* @property Carbon $updated_at
|
* @property Carbon $updated_at
|
||||||
*/
|
*/
|
||||||
@ -42,14 +38,13 @@ class VacationRequest extends Model
|
|||||||
use HasStates;
|
use HasStates;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
"type" => VacationType::class,
|
"type" => VacationType::class,
|
||||||
"state" => VacationRequestState::class,
|
"state" => VacationRequestState::class,
|
||||||
"from" => "date",
|
"from" => "date",
|
||||||
"to" => "date",
|
"to" => "date",
|
||||||
"event_ids" => AsCollection::class,
|
|
||||||
];
|
];
|
||||||
protected $perPage = 50;
|
|
||||||
|
|
||||||
public function user(): BelongsTo
|
public function user(): BelongsTo
|
||||||
{
|
{
|
||||||
@ -86,13 +81,6 @@ class VacationRequest extends Model
|
|||||||
return $query->whereNotState("state", $states);
|
return $query->whereNotState("state", $states);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function scopeType(Builder $query, VacationType|array $types): Builder
|
|
||||||
{
|
|
||||||
$types = Arr::wrap($types);
|
|
||||||
|
|
||||||
return $query->whereIn("type", $types);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function scopeOverlapsWith(Builder $query, self $vacationRequest): Builder
|
public function scopeOverlapsWith(Builder $query, self $vacationRequest): Builder
|
||||||
{
|
{
|
||||||
return $query->where("from", "<=", $vacationRequest->to)
|
return $query->where("from", "<=", $vacationRequest->to)
|
||||||
|
@ -22,6 +22,7 @@ class VacationRequestActivity extends Model
|
|||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
"from" => VacationRequestState::class,
|
"from" => VacationRequestState::class,
|
||||||
"to" => VacationRequestState::class,
|
"to" => VacationRequestState::class,
|
||||||
|
@ -15,7 +15,6 @@ use Illuminate\Support\Collection;
|
|||||||
* @property int $id
|
* @property int $id
|
||||||
* @property int $year
|
* @property int $year
|
||||||
* @property Collection $vacationLimits
|
* @property Collection $vacationLimits
|
||||||
* @property Collection $vacationRequests
|
|
||||||
* @property Collection $holidays
|
* @property Collection $holidays
|
||||||
*/
|
*/
|
||||||
class YearPeriod extends Model
|
class YearPeriod extends Model
|
||||||
@ -42,11 +41,6 @@ class YearPeriod extends Model
|
|||||||
return $this->hasMany(VacationLimit::class);
|
return $this->hasMany(VacationLimit::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function vacationRequests(): HasMany
|
|
||||||
{
|
|
||||||
return $this->hasMany(VacationRequest::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function holidays(): HasMany
|
public function holidays(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(Holiday::class);
|
return $this->hasMany(Holiday::class);
|
||||||
|
@ -4,22 +4,19 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Toby\Eloquent\Observers;
|
namespace Toby\Eloquent\Observers;
|
||||||
|
|
||||||
use Illuminate\Contracts\Hashing\Hasher;
|
use Toby\Eloquent\Helpers\YearPeriodRetriever;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Toby\Eloquent\Models\User;
|
use Toby\Eloquent\Models\User;
|
||||||
|
|
||||||
class UserObserver
|
class UserObserver
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected Hasher $hash,
|
protected YearPeriodRetriever $yearPeriodRetriever,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function creating(User $user): void
|
public function created(User $user): void
|
||||||
{
|
{
|
||||||
/**
|
$user->vacationLimits()->create([
|
||||||
* A random password for user is generated because AuthenticateSession middleware needs a user's password
|
"year_period_id" => $this->yearPeriodRetriever->current()->id,
|
||||||
* for some checks. Users use Google to login, so they don't need to know the password (GitHub issue #84)
|
]);
|
||||||
*/
|
|
||||||
$user->password = $this->hash->make(Str::random(40));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,25 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Toby\Eloquent\Observers;
|
namespace Toby\Eloquent\Observers;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Auth\Factory as Auth;
|
||||||
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
use Toby\Eloquent\Models\VacationRequest;
|
use Toby\Eloquent\Models\VacationRequest;
|
||||||
|
|
||||||
class VacationRequestObserver
|
class VacationRequestObserver
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected Auth $auth,
|
||||||
|
protected Dispatcher $dispatcher,
|
||||||
|
) {}
|
||||||
|
|
||||||
public function creating(VacationRequest $vacationRequest): void
|
public function creating(VacationRequest $vacationRequest): void
|
||||||
{
|
{
|
||||||
$count = $vacationRequest->yearPeriod->vacationRequests()->count();
|
$year = $vacationRequest->from->year;
|
||||||
$number = $count + 1;
|
|
||||||
|
|
||||||
$vacationRequest->name = "{$number}/{$vacationRequest->yearPeriod->year}";
|
$vacationRequestNumber = $vacationRequest->user->vacationRequests()
|
||||||
|
->whereYear("from", $year)
|
||||||
|
->count() + 1;
|
||||||
|
|
||||||
|
$vacationRequest->name = "{$vacationRequestNumber}/${year}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,30 +2,22 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Toby\Domain\Actions;
|
namespace Toby\Eloquent\Observers;
|
||||||
|
|
||||||
use Toby\Domain\PolishHolidaysRetriever;
|
use Toby\Domain\PolishHolidaysRetriever;
|
||||||
use Toby\Eloquent\Models\User;
|
use Toby\Eloquent\Models\User;
|
||||||
use Toby\Eloquent\Models\YearPeriod;
|
use Toby\Eloquent\Models\YearPeriod;
|
||||||
|
|
||||||
class CreateYearPeriodAction
|
class YearPeriodObserver
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected PolishHolidaysRetriever $polishHolidaysRetriever,
|
protected PolishHolidaysRetriever $polishHolidaysRetriever,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function execute(int $year): YearPeriod
|
public function created(YearPeriod $yearPeriod): void
|
||||||
{
|
{
|
||||||
$yearPeriod = new YearPeriod([
|
|
||||||
"year" => $year,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$yearPeriod->save();
|
|
||||||
|
|
||||||
$this->createVacationLimitsFor($yearPeriod);
|
$this->createVacationLimitsFor($yearPeriod);
|
||||||
$this->createHolidaysFor($yearPeriod);
|
$this->createHolidaysFor($yearPeriod);
|
||||||
|
|
||||||
return $yearPeriod;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createVacationLimitsFor(YearPeriod $yearPeriod): void
|
protected function createVacationLimitsFor(YearPeriod $yearPeriod): void
|
22
app/Eloquent/Scopes/SelectedYearPeriodScope.php
Normal file
22
app/Eloquent/Scopes/SelectedYearPeriodScope.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Eloquent\Scopes;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Scope;
|
||||||
|
use Toby\Eloquent\Helpers\YearPeriodRetriever;
|
||||||
|
|
||||||
|
class SelectedYearPeriodScope implements Scope
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected YearPeriodRetriever $yearPeriodRetriever,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function apply(Builder $builder, Model $model): Builder
|
||||||
|
{
|
||||||
|
return $builder->where("year_period_id", $this->yearPeriodRetriever->selected()->id);
|
||||||
|
}
|
||||||
|
}
|
25
app/Infrastructure/Console/Commands/CreateUserCommand.php
Normal file
25
app/Infrastructure/Console/Commands/CreateUserCommand.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Infrastructure\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Toby\Eloquent\Models\User;
|
||||||
|
|
||||||
|
class CreateUserCommand extends Command
|
||||||
|
{
|
||||||
|
protected $signature = "user:create {email : an email for the user}";
|
||||||
|
protected $description = "Creates a user";
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$email = $this->argument("email");
|
||||||
|
|
||||||
|
User::factory([
|
||||||
|
"email" => $email,
|
||||||
|
])->create();
|
||||||
|
|
||||||
|
$this->info("The user has been created");
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Infrastructure\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Toby\Eloquent\Models\User;
|
|
||||||
|
|
||||||
class MoveUserDataToProfile extends Command
|
|
||||||
{
|
|
||||||
protected $signature = "toby:move-user-data-to-profile";
|
|
||||||
protected $description = "Move user data to their profiles";
|
|
||||||
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
$users = User::all();
|
|
||||||
|
|
||||||
foreach ($users as $user) {
|
|
||||||
$user->profile()->updateOrCreate(["user_id" => $user->id], [
|
|
||||||
"first_name" => $user->first_name,
|
|
||||||
"last_name" => $user->last_name,
|
|
||||||
"position" => $user->position,
|
|
||||||
"employment_form" => $user->employment_form,
|
|
||||||
"employment_date" => $user->employment_date,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Infrastructure\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Toby\Eloquent\Models\YearPeriod;
|
|
||||||
|
|
||||||
class RebuildDocumentNumberingSystem extends Command
|
|
||||||
{
|
|
||||||
protected $signature = "toby:rebuild-document-numbering-system";
|
|
||||||
protected $description = "Rebuilds the document numbering system to {number}/{year}";
|
|
||||||
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
$yearPeriods = YearPeriod::all();
|
|
||||||
|
|
||||||
foreach ($yearPeriods as $yearPeriod) {
|
|
||||||
$number = 1;
|
|
||||||
|
|
||||||
$vacationRequests = $yearPeriod
|
|
||||||
->vacationRequests()
|
|
||||||
->oldest()
|
|
||||||
->get();
|
|
||||||
|
|
||||||
foreach ($vacationRequests as $vacationRequest) {
|
|
||||||
$vacationRequest->update(["name" => "{$number}/{$yearPeriod->year}"]);
|
|
||||||
|
|
||||||
$number++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Infrastructure\Console\Commands;
|
|
||||||
|
|
||||||
use Carbon\CarbonInterface;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Carbon;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\Http;
|
|
||||||
use Toby\Domain\DailySummaryRetriever;
|
|
||||||
use Toby\Eloquent\Models\Holiday;
|
|
||||||
use Toby\Infrastructure\Slack\Elements\AbsencesAttachment;
|
|
||||||
use Toby\Infrastructure\Slack\Elements\BirthdaysAttachment;
|
|
||||||
use Toby\Infrastructure\Slack\Elements\RemotesAttachment;
|
|
||||||
|
|
||||||
class SendDailySummaryToSlack extends Command
|
|
||||||
{
|
|
||||||
protected $signature = "toby:slack:daily-summary {--f|force}";
|
|
||||||
protected $description = "Sent daily summary to Slack";
|
|
||||||
|
|
||||||
public function handle(DailySummaryRetriever $dailySummaryRetriever): void
|
|
||||||
{
|
|
||||||
$now = Carbon::today();
|
|
||||||
|
|
||||||
if (!$this->option("force") && !$this->shouldHandle($now)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$attachments = new Collection([
|
|
||||||
new AbsencesAttachment($dailySummaryRetriever->getAbsences($now)),
|
|
||||||
new RemotesAttachment($dailySummaryRetriever->getRemoteDays($now)),
|
|
||||||
new BirthdaysAttachment($dailySummaryRetriever->getBirthdays($now)),
|
|
||||||
]);
|
|
||||||
|
|
||||||
Http::withToken($this->getSlackClientToken())
|
|
||||||
->post($this->getUrl(), [
|
|
||||||
"channel" => $this->getSlackChannel(),
|
|
||||||
"text" => __("Daily summary for day :day", ["day" => $now->toDisplayString()]),
|
|
||||||
"attachments" => $attachments,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function shouldHandle(CarbonInterface $day): bool
|
|
||||||
{
|
|
||||||
$holidays = Holiday::query()->whereDate("date", $day)->pluck("date");
|
|
||||||
|
|
||||||
if ($day->isWeekend()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($holidays->contains($day)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getUrl(): string
|
|
||||||
{
|
|
||||||
return "{$this->getSlackBaseUrl()}/chat.postMessage";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getSlackBaseUrl(): ?string
|
|
||||||
{
|
|
||||||
return config("services.slack.url");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getSlackClientToken(): ?string
|
|
||||||
{
|
|
||||||
return config("services.slack.client_token");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getSlackChannel(): ?string
|
|
||||||
{
|
|
||||||
return config("services.slack.default_channel");
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user