diff --git a/app/Domain/CalendarGenerator.php b/app/Domain/CalendarGenerator.php index 4ecb2ba..96c0667 100644 --- a/app/Domain/CalendarGenerator.php +++ b/app/Domain/CalendarGenerator.php @@ -50,8 +50,11 @@ class CalendarGenerator protected function generateCalendar(CarbonPeriod $period, Collection $holidays): array { $calendar = []; + $vacations = $this->getVacationsForPeriod($period); foreach ($period as $day) { + $vacationsForDay = $vacations[$day->toDateString()] ?? new Collection(); + $calendar[] = [ "date" => $day->toDateString(), "dayOfMonth" => $day->translatedFormat("j"), @@ -59,18 +62,19 @@ class CalendarGenerator "isToday" => $day->isToday(), "isWeekend" => $day->isWeekend(), "isHoliday" => $holidays->contains($day), - "vacations" => $this->getVacationsForDay($day), + "vacations" => $vacationsForDay->pluck("user_id"), ]; } return $calendar; } - protected function getVacationsForDay(CarbonInterface $day): Collection + protected function getVacationsForPeriod(CarbonPeriod $period): Collection { return Vacation::query() - ->whereDate("date", $day) + ->whereBetween("date", [$period->start, $period->end]) ->whereRelation("vacationRequest", "state", VacationRequestState::APPROVED->value) - ->pluck("user_id"); + ->get() + ->groupBy(fn(Vacation $vacation) => $vacation->date->toDateString()); } } diff --git a/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php b/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php index c9c4148..0fb6d1d 100644 --- a/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php +++ b/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php @@ -4,23 +4,64 @@ declare(strict_types=1); namespace Toby\Domain\Validation\Rules; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Collection; +use Toby\Domain\Enums\VacationRequestState; +use Toby\Domain\Enums\VacationType; +use Toby\Domain\VacationDaysCalculator; use Toby\Domain\VacationTypeConfigRetriever; +use Toby\Eloquent\Models\User; use Toby\Eloquent\Models\VacationRequest; +use Toby\Eloquent\Models\YearPeriod; class DoesNotExceedLimitRule implements VacationRequestRule { public function __construct( protected VacationTypeConfigRetriever $configRetriever, + protected VacationDaysCalculator $vacationDaysCalculator, ) { } public function check(VacationRequest $vacationRequest): bool { - return true; + if (!$this->configRetriever->hasLimit($vacationRequest->type)) { + return true; + } + + $limit = $this->getUserVacationLimit($vacationRequest->user, $vacationRequest->yearPeriod); + $vacationDays = $this->getVacationDaysWithLimit($vacationRequest->user, $vacationRequest->yearPeriod); + $estimatedDays = $this->vacationDaysCalculator->calculateDays($vacationRequest->yearPeriod, $vacationRequest->from, $vacationRequest->to)->count(); + + return $limit >= ($vacationDays + $estimatedDays); } public function errorMessage(): string { return __("You have exceeded your vacation limit."); } + + protected function getUserVacationLimit(User $user, YearPeriod $yearPeriod): int + { + return $user->vacationLimits()->where("year_period_id", $yearPeriod->id)->first()->days ?? 0; + } + + protected function getVacationDaysWithLimit(User $user, YearPeriod $yearPeriod): int + { + return $user->vacations() + ->where("year_period_id", $yearPeriod->id) + ->whereRelation( + "vacationRequest", + fn(Builder $query) => $query + ->whereIn("type", $this->getLimitableVacationTypes()) + ->noStates(VacationRequestState::failedStates()), + ) + ->count(); + } + + protected function getLimitableVacationTypes(): Collection + { + $types = new Collection(VacationType::cases()); + + return $types->filter(fn(VacationType $type) => $this->configRetriever->hasLimit($type)); + } } diff --git a/app/Eloquent/Models/Vacation.php b/app/Eloquent/Models/Vacation.php index e359926..c22cf76 100644 --- a/app/Eloquent/Models/Vacation.php +++ b/app/Eloquent/Models/Vacation.php @@ -14,6 +14,7 @@ use Illuminate\Support\Carbon; * @property Carbon $date * @property User $user * @property VacationRequest $vacationRequest + * @property YearPeriod $yearPeriod */ class Vacation extends Model { @@ -34,4 +35,9 @@ class Vacation extends Model { return $this->belongsTo(VacationRequest::class); } + + public function yearPeriod(): BelongsTo + { + return $this->belongsTo(YearPeriod::class); + } } diff --git a/app/Eloquent/Models/VacationRequest.php b/app/Eloquent/Models/VacationRequest.php index 7b84a07..f8d29b9 100644 --- a/app/Eloquent/Models/VacationRequest.php +++ b/app/Eloquent/Models/VacationRequest.php @@ -21,7 +21,6 @@ use Toby\Domain\Enums\VacationType; * @property VacationRequestState $state * @property Carbon $from * @property Carbon $to - * @property int $estimated_days * @property string $comment * @property User $user * @property YearPeriod $yearPeriod @@ -75,6 +74,11 @@ class VacationRequest extends Model return $query->whereIn("state", $states); } + public function scopeNoStates(Builder $query, array $states): Builder + { + return $query->whereNotIn("state", $states); + } + public function scopeOverlapsWith(Builder $query, self $vacationRequest): Builder { return $query->where("from", "<=", $vacationRequest->to) diff --git a/app/Infrastructure/Http/Controllers/VacationCalendarController.php b/app/Infrastructure/Http/Controllers/VacationCalendarController.php index c19c00d..d37cce6 100644 --- a/app/Infrastructure/Http/Controllers/VacationCalendarController.php +++ b/app/Infrastructure/Http/Controllers/VacationCalendarController.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Toby\Infrastructure\Http\Controllers; -use Carbon\CarbonInterface; use Illuminate\Http\Request; use Illuminate\Support\Carbon; use Illuminate\Support\Str; @@ -36,22 +35,4 @@ class VacationCalendarController extends Controller "users" => UserResource::collection($users), ]); } - - protected function monthNameToNumber(?string $name): int - { - return match ($name) { - default => CarbonInterface::JANUARY, - "february" => CarbonInterface::FEBRUARY, - "march" => CarbonInterface::MARCH, - "april" => CarbonInterface::APRIL, - "may" => CarbonInterface::MAY, - "june" => CarbonInterface::JUNE, - "july" => CarbonInterface::JULY, - "august" => CarbonInterface::AUGUST, - "september" => CarbonInterface::SEPTEMBER, - "october" => CarbonInterface::OCTOBER, - "november" => CarbonInterface::NOVEMBER, - "december" => CarbonInterface::DECEMBER, - }; - } } diff --git a/app/Infrastructure/Http/Controllers/VacationRequestController.php b/app/Infrastructure/Http/Controllers/VacationRequestController.php index 739a1da..481bdd9 100644 --- a/app/Infrastructure/Http/Controllers/VacationRequestController.php +++ b/app/Infrastructure/Http/Controllers/VacationRequestController.php @@ -75,6 +75,9 @@ class VacationRequestController extends Controller /** @var VacationRequest $vacationRequest */ $vacationRequest = $request->user()->vacationRequests()->make($request->data()); $vacationRequestValidator->validate($vacationRequest); + + dd("ok"); + $vacationRequest->save(); $days = $vacationDaysCalculator->calculateDays( diff --git a/database/factories/VacationFactory.php b/database/factories/VacationFactory.php index baa180d..d70c43d 100644 --- a/database/factories/VacationFactory.php +++ b/database/factories/VacationFactory.php @@ -8,15 +8,8 @@ use Illuminate\Database\Eloquent\Factories\Factory; class VacationFactory extends Factory { - /** - * Define the model's default state. - * - * @return array - */ - public function definition() + public function definition(): array { - return [ - // - ]; + return []; } } diff --git a/database/migrations/2022_02_07_133018_create_vacations_table.php b/database/migrations/2022_02_07_133018_create_vacations_table.php index 713806d..648be3c 100644 --- a/database/migrations/2022_02_07_133018_create_vacations_table.php +++ b/database/migrations/2022_02_07_133018_create_vacations_table.php @@ -7,6 +7,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; use Toby\Eloquent\Models\User; use Toby\Eloquent\Models\VacationRequest; +use Toby\Eloquent\Models\YearPeriod; return new class() extends Migration { public function up(): void @@ -15,6 +16,7 @@ return new class() extends Migration { $table->id(); $table->foreignIdFor(User::class)->constrained()->cascadeOnDelete(); $table->foreignIdFor(VacationRequest::class)->constrained()->cascadeOnDelete(); + $table->foreignIdFor(YearPeriod::class)->constrained()->cascadeOnDelete(); $table->date("date"); }); } diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 0788740..614d712 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -91,6 +91,7 @@ class DatabaseSeeder extends Seeder $vacationRequest->vacations()->create([ "date" => $day, "user_id" => $vacationRequest->user->id, + "year_period_id" => $vacationRequest->yearPeriod->id, ]); } })