diff --git a/app/Architecture/Providers/EventServiceProvider.php b/app/Architecture/Providers/EventServiceProvider.php index 409c95a..19c34bf 100644 --- a/app/Architecture/Providers/EventServiceProvider.php +++ b/app/Architecture/Providers/EventServiceProvider.php @@ -9,20 +9,24 @@ use Toby\Domain\Events\VacationRequestAcceptedByAdministrative; use Toby\Domain\Events\VacationRequestAcceptedByTechnical; use Toby\Domain\Events\VacationRequestApproved; use Toby\Domain\Events\VacationRequestCreated; +use Toby\Domain\Events\VacationRequestRejected; use Toby\Domain\Events\VacationRequestStateChanged; use Toby\Domain\Listeners\CreateVacationRequestActivity; use Toby\Domain\Listeners\HandleAcceptedByAdministrativeVacationRequest; use Toby\Domain\Listeners\HandleAcceptedByTechnicalVacationRequest; use Toby\Domain\Listeners\HandleApprovedVacationRequest; use Toby\Domain\Listeners\HandleCreatedVacationRequest; +use Toby\Domain\Listeners\SendCreatedVacationRequestNotification; +use Toby\Domain\Listeners\SendRejectedVacationRequestNotification; class EventServiceProvider extends ServiceProvider { protected $listen = [ VacationRequestStateChanged::class => [CreateVacationRequestActivity::class], - VacationRequestCreated::class => [HandleCreatedVacationRequest::class], + VacationRequestCreated::class => [HandleCreatedVacationRequest::class, SendCreatedVacationRequestNotification::class], VacationRequestAcceptedByTechnical::class => [HandleAcceptedByTechnicalVacationRequest::class], VacationRequestAcceptedByAdministrative::class => [HandleAcceptedByAdministrativeVacationRequest::class], VacationRequestApproved::class => [HandleApprovedVacationRequest::class], + VacationRequestRejected::class => [SendRejectedVacationRequestNotification::class], ]; } diff --git a/app/Domain/Events/VacationRequestRejected.php b/app/Domain/Events/VacationRequestRejected.php new file mode 100644 index 0000000..5378736 --- /dev/null +++ b/app/Domain/Events/VacationRequestRejected.php @@ -0,0 +1,20 @@ +vacationRequest->user->notify(new VacationRequestCreatedNotification($event->vacationRequest)); + } +} diff --git a/app/Domain/Listeners/SendRejectedVacationRequestNotification.php b/app/Domain/Listeners/SendRejectedVacationRequestNotification.php new file mode 100644 index 0000000..af5665d --- /dev/null +++ b/app/Domain/Listeners/SendRejectedVacationRequestNotification.php @@ -0,0 +1,35 @@ +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(); + } +} diff --git a/app/Domain/Notifications/VacationRequestCreatedNotification.php b/app/Domain/Notifications/VacationRequestCreatedNotification.php new file mode 100644 index 0000000..fe64791 --- /dev/null +++ b/app/Domain/Notifications/VacationRequestCreatedNotification.php @@ -0,0 +1,59 @@ + $this->vacationRequest, + ], + ); + + return $this->buildMailMessage($url); + } + + protected function buildMailMessage(string $url): MailMessage + { + $title = $this->vacationRequest->name; + + $user = $this->vacationRequest->user; + + return (new MailMessage()) + ->greeting(__("Hi :user!", [ + "user" => $user->fullName, + ])) + ->subject(__("Vacation request :title", [ + "title" => $title, + ])) + ->line(__("Vacation request has been created.", [ + ])) + ->action(__("Show vacation request"), $url); + } +} diff --git a/app/Domain/Notifications/VacationRequestNotification.php b/app/Domain/Notifications/VacationRequestRejectedNotification.php similarity index 65% rename from app/Domain/Notifications/VacationRequestNotification.php rename to app/Domain/Notifications/VacationRequestRejectedNotification.php index a869edd..9ea88bf 100644 --- a/app/Domain/Notifications/VacationRequestNotification.php +++ b/app/Domain/Notifications/VacationRequestRejectedNotification.php @@ -11,17 +11,14 @@ use InvalidArgumentException; use Toby\Eloquent\Models\User; use Toby\Eloquent\Models\VacationRequest; -class VacationRequestNotification extends Notification +class VacationRequestRejectedNotification extends Notification { use Queueable; - protected User $user; - protected VacationRequest $vacationRequest; - - public function __construct(User $user, VacationRequest $vacationRequest) - { - $this->user = $user; - $this->vacationRequest = $vacationRequest; + public function __construct( + protected VacationRequest $vacationRequest, + protected User $user, + ) { } public function via(): array @@ -47,20 +44,15 @@ class VacationRequestNotification extends Notification protected function buildMailMessage(string $url): MailMessage { $title = $this->vacationRequest->name; - $state = $this->vacationRequest->state->label(); - - $user = $this->user->getFullNameAttribute(); return (new MailMessage()) ->greeting(__("Hi :user!", [ - "user" => $user, + "user" => $this->user->fullName, ])) ->subject(__("Vacation request :title", [ "title" => $title, ])) - ->line(__("The vacation request :title has changed state to :state.", [ - "title" => $title, - "state" => $state, + ->line(__("Vacation request has been rejected.", [ ])) ->action(__("Show vacation request"), $url); } diff --git a/app/Domain/VacationRequestNotificationSender.php b/app/Domain/VacationRequestNotificationSender.php deleted file mode 100644 index 1b04974..0000000 --- a/app/Domain/VacationRequestNotificationSender.php +++ /dev/null @@ -1,31 +0,0 @@ -getUsersForNotifications() as $user) { - $user->notify(new VacationRequestNotification($user, $vacationRequest)); - } - - $vacationRequest->user->notify(new VacationRequestNotification($vacationRequest->user, $vacationRequest)); - } - - protected function getUsersForNotifications(): Collection - { - return User::query() - ->where("role", Role::TECHNICAL_APPROVER) - ->orWhere("role", Role::ADMINISTRATIVE_APPROVER) - ->get(); - } -} diff --git a/app/Domain/VacationRequestStateManager.php b/app/Domain/VacationRequestStateManager.php index 7de8484..8384980 100644 --- a/app/Domain/VacationRequestStateManager.php +++ b/app/Domain/VacationRequestStateManager.php @@ -11,6 +11,7 @@ use Toby\Domain\Events\VacationRequestAcceptedByAdministrative; use Toby\Domain\Events\VacationRequestAcceptedByTechnical; use Toby\Domain\Events\VacationRequestApproved; use Toby\Domain\Events\VacationRequestCreated; +use Toby\Domain\Events\VacationRequestRejected; use Toby\Eloquent\Models\VacationRequest; class VacationRequestStateManager @@ -38,6 +39,7 @@ class VacationRequestStateManager public function reject(VacationRequest $vacationRequest): void { $this->changeState($vacationRequest, VacationRequestState::Rejected); + $this->dispatcher->dispatch(new VacationRequestRejected($vacationRequest)); } public function cancel(VacationRequest $vacationRequest): void diff --git a/app/Eloquent/Observers/VacationRequestObserver.php b/app/Eloquent/Observers/VacationRequestObserver.php index 3d56bf3..ecbc5b1 100644 --- a/app/Eloquent/Observers/VacationRequestObserver.php +++ b/app/Eloquent/Observers/VacationRequestObserver.php @@ -5,10 +5,9 @@ declare(strict_types=1); namespace Toby\Eloquent\Observers; use Illuminate\Contracts\Auth\Factory as Auth; -use Illuminate\Events\Dispatcher; +use Illuminate\Contracts\Events\Dispatcher; use Toby\Domain\Enums\VacationRequestState; use Toby\Domain\Events\VacationRequestStateChanged; -use Toby\Domain\VacationRequestNotificationSender; use Toby\Eloquent\Models\User; use Toby\Eloquent\Models\VacationRequest; @@ -17,7 +16,6 @@ class VacationRequestObserver public function __construct( protected Auth $auth, protected Dispatcher $dispatcher, - protected VacationRequestNotificationSender $vacationRequestNotificationSender, ) { } @@ -41,13 +39,6 @@ class VacationRequestObserver } } - public function updated(VacationRequest $vacationRequest): void - { - if ($vacationRequest->state !== VacationRequestState::CREATED) { - $this->vacationRequestNotificationSender->sendVacationRequestNotification($vacationRequest); - } - } - protected function fireStateChangedEvent( VacationRequest $vacationRequest, ?VacationRequestState $from, diff --git a/package-lock.json b/package-lock.json index 958999c..1d8d8f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "toby", + "name": "application", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/tests/Feature/VacationRequestTest.php b/tests/Feature/VacationRequestTest.php index 0841d1e..cea9a57 100644 --- a/tests/Feature/VacationRequestTest.php +++ b/tests/Feature/VacationRequestTest.php @@ -6,17 +6,19 @@ namespace Tests\Feature; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Support\Carbon; -use Illuminate\Support\Facades\Bus; +use Illuminate\Support\Facades\Event; use Inertia\Testing\AssertableInertia as Assert; use Tests\FeatureTestCase; use Toby\Domain\Enums\VacationRequestState; use Toby\Domain\Enums\VacationType; +use Toby\Domain\Events\VacationRequestAcceptedByAdministrative; +use Toby\Domain\Events\VacationRequestAcceptedByTechnical; +use Toby\Domain\Events\VacationRequestRejected; use Toby\Domain\PolishHolidaysRetriever; use Toby\Eloquent\Models\User; use Toby\Eloquent\Models\VacationLimit; use Toby\Eloquent\Models\VacationRequest; use Toby\Eloquent\Models\YearPeriod; -use Toby\Infrastructure\Jobs\SendVacationRequestDaysToGoogleCalendar; class VacationRequestTest extends FeatureTestCase { @@ -28,8 +30,6 @@ class VacationRequestTest extends FeatureTestCase { parent::setUp(); - Bus::fake(); - $this->polishHolidaysRetriever = $this->app->make(PolishHolidaysRetriever::class); } @@ -90,6 +90,8 @@ class VacationRequestTest extends FeatureTestCase public function testTechnicalApproverCanApproveVacationRequest(): void { + Event::fake(); + $user = User::factory()->createQuietly(); $technicalApprover = User::factory()->createQuietly(); $currentYearPeriod = YearPeriod::current(); @@ -106,13 +108,13 @@ class VacationRequestTest extends FeatureTestCase ->post("/vacation-requests/{$vacationRequest->id}/accept-as-technical") ->assertSessionHasNoErrors(); - $this->assertDatabaseHas("vacation_requests", [ - "state" => VacationRequestState::WaitingForAdministrative, - ]); + Event::assertDispatched(VacationRequestAcceptedByTechnical::class); } public function testAdministrativeApproverCanApproveVacationRequest(): void { + Event::fake(VacationRequestAcceptedByAdministrative::class); + $user = User::factory()->createQuietly(); $administrativeApprover = User::factory()->createQuietly(); @@ -129,20 +131,18 @@ class VacationRequestTest extends FeatureTestCase ->post("/vacation-requests/{$vacationRequest->id}/accept-as-administrative") ->assertSessionHasNoErrors(); - $this->assertDatabaseHas("vacation_requests", [ - "state" => VacationRequestState::Approved, - ]); - - Bus::assertDispatched(SendVacationRequestDaysToGoogleCalendar::class); + Event::assertDispatched(VacationRequestAcceptedByAdministrative::class); } public function testTechnicalApproverCanRejectVacationRequest(): void { + Event::fake(); + $user = User::factory()->createQuietly(); $technicalApprover = User::factory()->createQuietly(); $currentYearPeriod = YearPeriod::current(); - $vacationLimit = VacationLimit::factory([ + VacationLimit::factory([ "days" => 20, ]) ->for($user) @@ -161,9 +161,7 @@ class VacationRequestTest extends FeatureTestCase ->post("/vacation-requests/{$vacationRequest->id}/reject") ->assertSessionHasNoErrors(); - $this->assertDatabaseHas("vacation_requests", [ - "state" => VacationRequestState::Rejected, - ]); + Event::assertDispatched(VacationRequestRejected::class); } public function testUserCannotCreateVacationRequestIfHeExceedsHisVacationLimit(): void diff --git a/tests/Unit/VacationRequestNotificationTest.php b/tests/Unit/VacationRequestNotificationTest.php index 09b8bae..f2b197c 100644 --- a/tests/Unit/VacationRequestNotificationTest.php +++ b/tests/Unit/VacationRequestNotificationTest.php @@ -5,8 +5,8 @@ declare(strict_types=1); namespace Tests\Unit; use Illuminate\Foundation\Testing\DatabaseMigrations; -use Illuminate\Support\Facades\Notification; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Notification; use Tests\TestCase; use Tests\Traits\InteractsWithYearPeriods; use Toby\Domain\Enums\Role; @@ -34,20 +34,26 @@ class VacationRequestNotificationTest extends TestCase $this->createCurrentYearPeriod(); } - public function testAfterChangingVacationRequestStateNotificationAreSentToUsers() :void + public function testAfterChangingVacationRequestStateNotificationAreSentToUsers(): void { Notification::fake(); - $user = User::factory(["role" => Role::EMPLOYEE])->createQuietly(); - $technicalApprover = User::factory(["role" => Role::TECHNICAL_APPROVER])->createQuietly(); - $administrativeApprover = User::factory(["role" => Role::ADMINISTRATIVE_APPROVER])->createQuietly(); + $user = User::factory([ + "role" => Role::Employee, + ])->createQuietly(); + $technicalApprover = User::factory([ + "role" => Role::TechnicalApprover, + ])->createQuietly(); + $administrativeApprover = User::factory([ + "role" => Role::AdministrativeApprover, + ])->createQuietly(); $currentYearPeriod = YearPeriod::current(); /** @var VacationRequest $vacationRequest */ $vacationRequest = VacationRequest::factory([ - "type" => VacationType::VACATION->value, - "state" => VacationRequestState::CREATED, + "type" => VacationType::Vacation->value, + "state" => VacationRequestState::Created, "from" => Carbon::create($currentYearPeriod->year, 2, 1)->toDateString(), "to" => Carbon::create($currentYearPeriod->year, 2, 4)->toDateString(), "comment" => "Comment for the vacation request.", @@ -58,24 +64,32 @@ class VacationRequestNotificationTest extends TestCase $this->stateManager->waitForTechnical($vacationRequest); - Notification::assertSentTo([$user, $technicalApprover, $administrativeApprover],VacationRequestNotification::class); + Notification::assertSentTo([$user, $technicalApprover, $administrativeApprover], VacationRequestNotification::class); } - public function testAfterChangingVacationRequestStateNotificationIsNotSentToAnotherEmployee(): void { + public function testAfterChangingVacationRequestStateNotificationIsNotSentToAnotherEmployee(): void + { Notification::fake(); - $user = User::factory(["role" => Role::EMPLOYEE])->createQuietly(); - $anotherUser = User::factory(["role" => Role::EMPLOYEE])->createQuietly(); - $technicalApprover = User::factory(["role" => Role::TECHNICAL_APPROVER])->createQuietly(); - $administrativeApprover = User::factory(["role" => Role::ADMINISTRATIVE_APPROVER])->createQuietly(); - + $user = User::factory([ + "role" => Role::Employee, + ])->createQuietly(); + $anotherUser = User::factory([ + "role" => Role::Employee, + ])->createQuietly(); + $technicalApprover = User::factory([ + "role" => Role::TechnicalApprover, + ])->createQuietly(); + $administrativeApprover = User::factory([ + "role" => Role::AdministrativeApprover, + ])->createQuietly(); $currentYearPeriod = YearPeriod::current(); /** @var VacationRequest $vacationRequest */ $vacationRequest = VacationRequest::factory([ - "type" => VacationType::VACATION->value, - "state" => VacationRequestState::CREATED, + "type" => VacationType::Vacation->value, + "state" => VacationRequestState::Created, "from" => Carbon::create($currentYearPeriod->year, 2, 1)->toDateString(), "to" => Carbon::create($currentYearPeriod->year, 2, 4)->toDateString(), "comment" => "Comment for the vacation request.", @@ -86,9 +100,7 @@ class VacationRequestNotificationTest extends TestCase $this->stateManager->waitForTechnical($vacationRequest); - Notification::assertSentTo([$user, $technicalApprover, $administrativeApprover],VacationRequestNotification::class); - Notification::assertNotSentTo([$anotherUser],VacationRequestNotification::class); + Notification::assertSentTo([$user, $technicalApprover, $administrativeApprover], VacationRequestNotification::class); + Notification::assertNotSentTo([$anotherUser], VacationRequestNotification::class); } } - -