Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#177 - Delegations #349

Merged
merged 3 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion app/Domain/Actions/VacationRequest/CreateAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ protected function createVacationRequest(array $data, User $creator): VacationRe

$vacationRequest->save();

$days = $this->workDaysCalculator->calculateDays($vacationRequest->from, $vacationRequest->to);
$days = $this->workDaysCalculator->calculateDays(
$vacationRequest->from,
$vacationRequest->to,
$vacationRequest->type,
);

foreach ($days as $day) {
$vacationRequest->vacations()->create([
Expand Down
1 change: 1 addition & 0 deletions app/Domain/Enums/VacationType.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ enum VacationType: string
case Sick = "sick_vacation";
case Absence = "absence";
case RemoteWork = "remote_work";
case Delegation = "delegation";

public function label(): string
{
Expand Down
40 changes: 40 additions & 0 deletions app/Domain/UnavailableDaysRetriever.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace Toby\Domain;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Toby\Domain\Enums\VacationType;
use Toby\Eloquent\Models\User;
use Toby\Eloquent\Models\YearPeriod;

class UnavailableDaysRetriever
{
public function __construct(
protected UserVacationStatsRetriever $vacationStatsRetriever,
protected VacationTypeConfigRetriever $configRetriever,
) {}

public function getUnavailableDays(User $user, YearPeriod $yearPeriod, ?VacationType $vacationType = null): Collection
{
$unavailableDays = $user->vacations()
->whereBelongsTo($yearPeriod)
->whereRelation(
"vacationRequest",
fn(Builder $query): Builder => $query->noStates(VacationRequestStatesRetriever::failedStates()),
)
->pluck("date");

if (!$vacationType || !$this->configRetriever->isDuringNonWorkDays($vacationType)) {
$unavailableDays->push(...$yearPeriod->weekends());
$unavailableDays->push(...$yearPeriod->holidays()->pluck("date"));
}

return $unavailableDays
->unique()
->sort()
->values();
}
}
1 change: 1 addition & 0 deletions app/Domain/UserVacationStatsRetriever.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public function getOtherApprovedVacationDays(User $user, YearPeriod $yearPeriod)
fn(Builder $query): Builder => $query
->whereIn("type", $this->getNotLimitableVacationTypes())
->whereNot("type", VacationType::RemoteWork)
->whereNot("type", VacationType::Delegation)
->states(VacationRequestStatesRetriever::successStates()),
)
->count();
Expand Down
13 changes: 13 additions & 0 deletions app/Domain/VacationTypeConfigRetriever.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Illuminate\Contracts\Config\Repository;
use Toby\Domain\Enums\EmploymentForm;
use Toby\Domain\Enums\Role;
use Toby\Domain\Enums\VacationType;

class VacationTypeConfigRetriever
Expand All @@ -16,6 +17,8 @@ class VacationTypeConfigRetriever
public const KEY_HAS_LIMIT = "has_limit";
public const KEY_AVAILABLE_FOR = "available_for";
public const KEY_IS_VACATION = "is_vacation";
public const KEY_DURING_NON_WORKDAYS = "during_non_workdays";
public const KEY_REQUEST_ALLOWED_FOR = "request_allowed_for";

public function __construct(
protected Repository $config,
Expand Down Expand Up @@ -46,11 +49,21 @@ public function isVacation(VacationType $type): bool
return $this->getConfigFor($type)[static::KEY_IS_VACATION];
}

public function isDuringNonWorkDays(VacationType $type): bool
{
return $this->getConfigFor($type)[static::KEY_DURING_NON_WORKDAYS];
}

public function isAvailableFor(VacationType $type, EmploymentForm $employmentForm): bool
{
return in_array($employmentForm, $this->getConfigFor($type)[static::KEY_AVAILABLE_FOR], true);
}

public function isRequestAllowedFor(VacationType $type, Role $role): bool
{
return in_array($role, $this->getConfigFor($type)[static::KEY_REQUEST_ALLOWED_FOR], true);
}

protected function getConfigFor(VacationType $type): array
{
return $this->config->get("vacation_types.{$type->value}");
Expand Down
2 changes: 1 addition & 1 deletion app/Domain/Validation/Rules/DoesNotExceedLimitRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function check(VacationRequest $vacationRequest): bool
$limit = $this->getUserVacationLimit($vacationRequest->user, $vacationRequest->yearPeriod);
$vacationDays = $this->getVacationDaysWithLimit($vacationRequest->user, $vacationRequest->yearPeriod);
$estimatedDays = $this->workDaysCalculator
->calculateDays($vacationRequest->from, $vacationRequest->to)
->calculateDays($vacationRequest->from, $vacationRequest->to, $vacationRequest->type)
->count();

return $limit >= ($vacationDays + $estimatedDays);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public function __construct(
public function check(VacationRequest $vacationRequest): bool
{
return $this->workDaysCalculator
->calculateDays($vacationRequest->from, $vacationRequest->to)
->calculateDays($vacationRequest->from, $vacationRequest->to, $vacationRequest->type)
->isNotEmpty();
}

Expand Down
15 changes: 12 additions & 3 deletions app/Domain/WorkDaysCalculator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@
use Carbon\CarbonInterface;
use Carbon\CarbonPeriod;
use Illuminate\Support\Collection;
use Toby\Domain\Enums\VacationType;
use Toby\Eloquent\Models\YearPeriod;

class WorkDaysCalculator
{
public function calculateDays(CarbonInterface $from, CarbonInterface $to): Collection
public function __construct(
protected VacationTypeConfigRetriever $configRetriever,
) {}

public function calculateDays(CarbonInterface $from, CarbonInterface $to, ?VacationType $vacationType = null): Collection
{
$period = CarbonPeriod::create($from, $to);
$yearPeriod = YearPeriod::findByYear($from->year);
Expand All @@ -20,16 +25,20 @@ public function calculateDays(CarbonInterface $from, CarbonInterface $to): Colle
$validDays = new Collection();

foreach ($period as $day) {
if ($this->passes($day, $holidays)) {
if ($this->passes($day, $holidays, $vacationType)) {
$validDays->add($day);
}
}

return $validDays;
}

protected function passes(CarbonInterface $day, Collection $holidays): bool
protected function passes(CarbonInterface $day, Collection $holidays, ?VacationType $vacationType = null): bool
{
if ($vacationType && $this->configRetriever->isDuringNonWorkDays($vacationType)) {
return true;
}

if ($day->isWeekend()) {
return false;
}
Expand Down
6 changes: 6 additions & 0 deletions app/Eloquent/Models/Vacation.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Toby\Eloquent\Models;

use Database\Factories\VacationFactory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
Expand Down Expand Up @@ -68,4 +69,9 @@ public function scopeWhereTypes(Builder $query, Collection $types): Builder
fn(Builder $query): Builder => $query->whereIn("type", $types),
);
}

protected static function newFactory(): VacationFactory
{
return VacationFactory::new();
}
}
18 changes: 18 additions & 0 deletions app/Eloquent/Models/YearPeriod.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,24 @@ public function holidays(): HasMany
return $this->hasMany(Holiday::class);
}

public function weekends(): Collection
{
$start = Carbon::create($this->year);
$end = Carbon::create($this->year)->endOfYear();

$weekends = new Collection();

while ($start->lessThanOrEqualTo($end)) {
if ($start->isWeekend()) {
$weekends->push($start->copy());
}

$start->addDay();
}

return $weekends;
}

protected static function newFactory(): YearPeriodFactory
{
return YearPeriodFactory::new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@

namespace Toby\Infrastructure\Http\Controllers\Api;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Carbon;
use Toby\Domain\UnavailableDaysRetriever;
use Toby\Domain\UserVacationStatsRetriever;
use Toby\Domain\VacationRequestStatesRetriever;
use Toby\Eloquent\Helpers\YearPeriodRetriever;
use Toby\Eloquent\Models\User;
use Toby\Infrastructure\Http\Controllers\Controller;
Expand All @@ -20,23 +19,16 @@ public function __invoke(
CalculateUserUnavailableDaysRequest $request,
UserVacationStatsRetriever $vacationStatsRetriever,
YearPeriodRetriever $yearPeriodRetriever,
UnavailableDaysRetriever $unavailableDaysRetriever,
): JsonResponse {
/** @var User $user */
$user = User::query()->find($request->get("user"));
$yearPeriod = $yearPeriodRetriever->selected();

$holidays = $yearPeriod->holidays()->pluck("date");
$vacationDays = $user->vacations()
->whereBelongsTo($yearPeriod)
->whereRelation(
"vacationRequest",
fn(Builder $query): Builder => $query->noStates(VacationRequestStatesRetriever::failedStates()),
)
->pluck("date");
$unavailableDays = $unavailableDaysRetriever->getUnavailableDays($user, $yearPeriod, $request->vacationType())
->map(fn(Carbon $date): string => $date->toDateString())
->toArray();

return new JsonResponse([
...$holidays->map(fn(Carbon $date): string => $date->toDateString()),
...$vacationDays->map(fn(Carbon $date): string => $date->toDateString()),
]);
return new JsonResponse($unavailableDays);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class CalculateVacationDaysController extends Controller
{
public function __invoke(CalculateVacationDaysRequest $request, WorkDaysCalculator $calculator): JsonResponse
{
$days = $calculator->calculateDays($request->from(), $request->to());
$days = $calculator->calculateDays($request->from(), $request->to(), $request->vacationType());

return new JsonResponse($days->map(fn(Carbon $day): string => $day->toDateString())->all());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public function __invoke(

$types = VacationType::all()
->filter(fn(VacationType $type): bool => $configRetriever->isAvailableFor($type, $user->profile->employment_form))
->filter(fn(VacationType $type): bool => $configRetriever->isRequestAllowedFor($type, $request->user()->role))
->map(fn(VacationType $type): array => [
"label" => $type->label(),
"value" => $type->value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,23 @@
namespace Toby\Infrastructure\Http\Requests\Api;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
use Toby\Domain\Enums\VacationType;

class CalculateUserUnavailableDaysRequest extends FormRequest
{
public function rules(): array
{
return [
"vacationType" => [new Enum(VacationType::class)],
"user" => ["required", "exists:users,id"],
];
}

public function vacationType(): ?VacationType
{
return $this->request->has("vacationType")
? VacationType::from($this->request->get("vacationType"))
: null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Carbon;
use Illuminate\Validation\Rules\Enum;
use Toby\Domain\Enums\VacationType;
use Toby\Eloquent\Models\YearPeriod;
use Toby\Infrastructure\Http\Rules\YearPeriodExists;

Expand All @@ -14,11 +16,17 @@ class CalculateVacationDaysRequest extends FormRequest
public function rules(): array
{
return [
"vacationType" => ["required", new Enum(VacationType::class)],
"from" => ["required", "date_format:Y-m-d", new YearPeriodExists()],
"to" => ["required", "date_format:Y-m-d", new YearPeriodExists()],
];
}

public function vacationType(): VacationType
{
return VacationType::from($this->request->get("vacationType"));
}

public function from(): Carbon
{
return Carbon::create($this->request->get("from"));
Expand Down
Loading