#126 - vacation request reminders
This commit is contained in:
		@@ -16,7 +16,6 @@ class ExceptionHandler extends Handler
 | 
				
			|||||||
        "password",
 | 
					        "password",
 | 
				
			||||||
        "password_confirmation",
 | 
					        "password_confirmation",
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected array $handleByInertia = [
 | 
					    protected array $handleByInertia = [
 | 
				
			||||||
        Response::HTTP_INTERNAL_SERVER_ERROR,
 | 
					        Response::HTTP_INTERNAL_SERVER_ERROR,
 | 
				
			||||||
        Response::HTTP_SERVICE_UNAVAILABLE,
 | 
					        Response::HTTP_SERVICE_UNAVAILABLE,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -99,4 +99,3 @@ class VacationRequestWaitsForApprovalNotification extends Notification
 | 
				
			|||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,5 +9,6 @@ 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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,6 @@ class Holiday extends Model
 | 
				
			|||||||
    use HasFactory;
 | 
					    use HasFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $guarded = [];
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected $casts = [
 | 
					    protected $casts = [
 | 
				
			||||||
        "date" => "date",
 | 
					        "date" => "date",
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,9 +26,7 @@ class Profile extends Model
 | 
				
			|||||||
    use HasAvatar;
 | 
					    use HasAvatar;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $primaryKey = "user_id";
 | 
					    protected $primaryKey = "user_id";
 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected $guarded = [];
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected $casts = [
 | 
					    protected $casts = [
 | 
				
			||||||
        "employment_form" => EmploymentForm::class,
 | 
					        "employment_form" => EmploymentForm::class,
 | 
				
			||||||
        "employment_date" => "date",
 | 
					        "employment_date" => "date",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,18 +33,15 @@ class User extends Authenticatable
 | 
				
			|||||||
    use SoftDeletes;
 | 
					    use SoftDeletes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $guarded = [];
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected $casts = [
 | 
					    protected $casts = [
 | 
				
			||||||
        "role" => Role::class,
 | 
					        "role" => Role::class,
 | 
				
			||||||
        "last_active_at" => "datetime",
 | 
					        "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 = [
 | 
					    protected $with = [
 | 
				
			||||||
        "profile",
 | 
					        "profile",
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ 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;
 | 
				
			||||||
@@ -41,7 +42,6 @@ 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,
 | 
				
			||||||
@@ -85,6 +85,13 @@ 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,7 +22,6 @@ 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,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Toby\Infrastructure\Console\Commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Console\Command;
 | 
				
			||||||
 | 
					use Illuminate\Support\Carbon;
 | 
				
			||||||
 | 
					use Toby\Domain\Enums\Role;
 | 
				
			||||||
 | 
					use Toby\Domain\Enums\VacationType;
 | 
				
			||||||
 | 
					use Toby\Domain\Notifications\VacationRequestWaitsForApprovalNotification;
 | 
				
			||||||
 | 
					use Toby\Domain\States\VacationRequest\WaitingForAdministrative;
 | 
				
			||||||
 | 
					use Toby\Domain\States\VacationRequest\WaitingForTechnical;
 | 
				
			||||||
 | 
					use Toby\Domain\VacationTypeConfigRetriever;
 | 
				
			||||||
 | 
					use Toby\Eloquent\Models\User;
 | 
				
			||||||
 | 
					use Toby\Eloquent\Models\VacationRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SendVacationRequestRemindersToApprovers extends Command
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public const REMINDER_INTERVAL = 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $signature = "toby:send-vacation-request-reminders";
 | 
				
			||||||
 | 
					    protected $description = "Sends vacation request reminders to approvers if they didn't approve";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function handle(VacationTypeConfigRetriever $configRetriever): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $vacationRequests = VacationRequest::query()
 | 
				
			||||||
 | 
					            ->type(VacationType::all()->filter(fn(VacationType $type) => $configRetriever->isVacation($type))->all())
 | 
				
			||||||
 | 
					            ->get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /** @var VacationRequest $vacationRequest */
 | 
				
			||||||
 | 
					        foreach ($vacationRequests as $vacationRequest) {
 | 
				
			||||||
 | 
					            if (!$this->shouldNotify($vacationRequest)) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($vacationRequest->state->equals(WaitingForTechnical::class)) {
 | 
				
			||||||
 | 
					                $this->notifyTechnicalApprovers($vacationRequest);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($vacationRequest->state->equals(WaitingForAdministrative::class)) {
 | 
				
			||||||
 | 
					                $this->notifyAdminApprovers($vacationRequest);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected function shouldNotify(VacationRequest $vacationRequest): bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $today = Carbon::today();
 | 
				
			||||||
 | 
					        $diff = $vacationRequest->updated_at->diffInDays($today);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $diff >= static::REMINDER_INTERVAL && ($diff % static::REMINDER_INTERVAL === 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected function notifyTechnicalApprovers(VacationRequest $vacationRequest): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $users = User::query()
 | 
				
			||||||
 | 
					            ->whereIn("role", [Role::TechnicalApprover, Role::Administrator])
 | 
				
			||||||
 | 
					            ->get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($users as $user) {
 | 
				
			||||||
 | 
					            $user->notify(new VacationRequestWaitsForApprovalNotification($vacationRequest, $user));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -40,7 +40,6 @@ class Kernel extends HttpKernel
 | 
				
			|||||||
        TrimStrings::class,
 | 
					        TrimStrings::class,
 | 
				
			||||||
        ConvertEmptyStringsToNull::class,
 | 
					        ConvertEmptyStringsToNull::class,
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected $middlewareGroups = [
 | 
					    protected $middlewareGroups = [
 | 
				
			||||||
        "web" => [
 | 
					        "web" => [
 | 
				
			||||||
            EncryptCookies::class,
 | 
					            EncryptCookies::class,
 | 
				
			||||||
@@ -58,7 +57,6 @@ class Kernel extends HttpKernel
 | 
				
			|||||||
            SubstituteBindings::class,
 | 
					            SubstituteBindings::class,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected $routeMiddleware = [
 | 
					    protected $routeMiddleware = [
 | 
				
			||||||
        "auth" => Authenticate::class,
 | 
					        "auth" => Authenticate::class,
 | 
				
			||||||
        "auth.basic" => AuthenticateWithBasicAuth::class,
 | 
					        "auth.basic" => AuthenticateWithBasicAuth::class,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										142
									
								
								tests/Unit/SendVacationRequestRemindersTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								tests/Unit/SendVacationRequestRemindersTest.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Tests\Unit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Foundation\Testing\DatabaseMigrations;
 | 
				
			||||||
 | 
					use Illuminate\Support\Carbon;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Notification;
 | 
				
			||||||
 | 
					use Tests\TestCase;
 | 
				
			||||||
 | 
					use Tests\Traits\InteractsWithYearPeriods;
 | 
				
			||||||
 | 
					use Toby\Domain\Enums\VacationType;
 | 
				
			||||||
 | 
					use Toby\Domain\Notifications\VacationRequestWaitsForApprovalNotification;
 | 
				
			||||||
 | 
					use Toby\Domain\States\VacationRequest\WaitingForAdministrative;
 | 
				
			||||||
 | 
					use Toby\Domain\States\VacationRequest\WaitingForTechnical;
 | 
				
			||||||
 | 
					use Toby\Eloquent\Models\User;
 | 
				
			||||||
 | 
					use Toby\Eloquent\Models\VacationRequest;
 | 
				
			||||||
 | 
					use Toby\Eloquent\Models\YearPeriod;
 | 
				
			||||||
 | 
					use Toby\Infrastructure\Console\Commands\SendVacationRequestRemindersToApprovers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SendVacationRequestRemindersTest extends TestCase
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use DatabaseMigrations;
 | 
				
			||||||
 | 
					    use InteractsWithYearPeriods;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected function setUp(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        parent::setUp();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->createCurrentYearPeriod();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Notification::fake();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function testReminderIsSentIfItsBeenThreeDaysSinceTheUpdate(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $currentYearPeriod = YearPeriod::current();
 | 
				
			||||||
 | 
					        $now = Carbon::today();
 | 
				
			||||||
 | 
					        $this->travelTo($now);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $user = User::factory()->create();
 | 
				
			||||||
 | 
					        $technicalApprover = User::factory()
 | 
				
			||||||
 | 
					            ->technicalApprover()
 | 
				
			||||||
 | 
					            ->create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        VacationRequest::factory([
 | 
				
			||||||
 | 
					            "type" => VacationType::Vacation->value,
 | 
				
			||||||
 | 
					            "state" => WaitingForTechnical::class,
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					            ->for($user)
 | 
				
			||||||
 | 
					            ->for($currentYearPeriod)
 | 
				
			||||||
 | 
					            ->create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->travelTo($now->addDays(3));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->artisan(SendVacationRequestRemindersToApprovers::class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Notification::assertSentTo([$technicalApprover], VacationRequestWaitsForApprovalNotification::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function testReminderIsSentIfItsBeenAnotherThreeDaysSinceTheUpdate(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $currentYearPeriod = YearPeriod::current();
 | 
				
			||||||
 | 
					        $now = Carbon::today();
 | 
				
			||||||
 | 
					        $this->travelTo($now);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $user = User::factory()->create();
 | 
				
			||||||
 | 
					        $technicalApprover = User::factory()
 | 
				
			||||||
 | 
					            ->technicalApprover()
 | 
				
			||||||
 | 
					            ->create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        VacationRequest::factory([
 | 
				
			||||||
 | 
					            "type" => VacationType::Vacation->value,
 | 
				
			||||||
 | 
					            "state" => WaitingForTechnical::class,
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					            ->for($user)
 | 
				
			||||||
 | 
					            ->for($currentYearPeriod)
 | 
				
			||||||
 | 
					            ->create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->travelTo($now->addDays(6));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->artisan(SendVacationRequestRemindersToApprovers::class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Notification::assertSentTo([$technicalApprover], VacationRequestWaitsForApprovalNotification::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function testReminderIsNotSentIfItHasntBeenThreeDays(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $currentYearPeriod = YearPeriod::current();
 | 
				
			||||||
 | 
					        $now = Carbon::today();
 | 
				
			||||||
 | 
					        $this->travelTo($now);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $user = User::factory()->create();
 | 
				
			||||||
 | 
					        $technicalApprover = User::factory()
 | 
				
			||||||
 | 
					            ->technicalApprover()
 | 
				
			||||||
 | 
					            ->create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        VacationRequest::factory([
 | 
				
			||||||
 | 
					            "type" => VacationType::Vacation->value,
 | 
				
			||||||
 | 
					            "state" => WaitingForTechnical::class,
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					            ->for($user)
 | 
				
			||||||
 | 
					            ->for($currentYearPeriod)
 | 
				
			||||||
 | 
					            ->create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->travelTo($now->addDays(2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->artisan(SendVacationRequestRemindersToApprovers::class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Notification::assertNotSentTo([$technicalApprover], VacationRequestWaitsForApprovalNotification::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function testReminderIsSentToProperApprover(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $currentYearPeriod = YearPeriod::current();
 | 
				
			||||||
 | 
					        $now = Carbon::today();
 | 
				
			||||||
 | 
					        $this->travelTo($now);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $user = User::factory()->create();
 | 
				
			||||||
 | 
					        $adminApprover = User::factory()
 | 
				
			||||||
 | 
					            ->administrativeApprover()
 | 
				
			||||||
 | 
					            ->create();
 | 
				
			||||||
 | 
					        $technicalApprover = User::factory()
 | 
				
			||||||
 | 
					            ->technicalApprover()
 | 
				
			||||||
 | 
					            ->create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        VacationRequest::factory([
 | 
				
			||||||
 | 
					            "type" => VacationType::Vacation->value,
 | 
				
			||||||
 | 
					            "state" => WaitingForAdministrative::class,
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					            ->for($user)
 | 
				
			||||||
 | 
					            ->for($currentYearPeriod)
 | 
				
			||||||
 | 
					            ->create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->travelTo($now->addDays(3));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->artisan(SendVacationRequestRemindersToApprovers::class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Notification::assertSentTo([$adminApprover], VacationRequestWaitsForApprovalNotification::class);
 | 
				
			||||||
 | 
					        Notification::assertNotSentTo([$technicalApprover, $user], VacationRequestWaitsForApprovalNotification::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user