#36 - wip
This commit is contained in:
42
app/Domain/VacationDaysCalculator.php
Normal file
42
app/Domain/VacationDaysCalculator.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Toby\Domain;
|
||||
|
||||
use Carbon\CarbonInterface;
|
||||
use Carbon\CarbonPeriod;
|
||||
use Illuminate\Support\Collection;
|
||||
use Toby\Eloquent\Models\YearPeriod;
|
||||
|
||||
class VacationDaysCalculator
|
||||
{
|
||||
public function calculateDays(YearPeriod $yearPeriod, CarbonInterface $from, CarbonInterface $to): Collection
|
||||
{
|
||||
$period = CarbonPeriod::create($from, $to);
|
||||
$holidays = $yearPeriod->holidays()->pluck("date");
|
||||
|
||||
$validDays = collect();
|
||||
|
||||
foreach ($period as $day) {
|
||||
if ($this->passes($day, $holidays)) {
|
||||
$validDays->add($day);
|
||||
}
|
||||
}
|
||||
|
||||
return $validDays;
|
||||
}
|
||||
|
||||
protected function passes(CarbonInterface $day, Collection $holidays): bool
|
||||
{
|
||||
if ($day->isWeekend()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($holidays->contains($day)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Toby\Domain\Validation\Rules;
|
||||
|
||||
use Closure;
|
||||
use Toby\Eloquent\Models\VacationRequest;
|
||||
|
||||
class ApprovedVacationDaysInSameRange implements VacationRequestRule
|
||||
{
|
||||
public function check(VacationRequest $vacationRequest, Closure $next)
|
||||
{
|
||||
return $next($vacationRequest);
|
||||
}
|
||||
}
|
@@ -4,13 +4,22 @@ declare(strict_types=1);
|
||||
|
||||
namespace Toby\Domain\Validation\Rules;
|
||||
|
||||
use Closure;
|
||||
use Toby\Domain\VacationTypeConfigRetriever;
|
||||
use Toby\Eloquent\Models\VacationRequest;
|
||||
|
||||
class DoesNotExceedLimitRule implements VacationRequestRule
|
||||
class DoesNotExceedLimitRule extends VacationRequestRule
|
||||
{
|
||||
public function check(VacationRequest $vacationRequest, Closure $next)
|
||||
public function __construct(protected VacationTypeConfigRetriever $configRetriever)
|
||||
{
|
||||
return $next($vacationRequest);
|
||||
}
|
||||
|
||||
public function passes(VacationRequest $vacationRequest): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function errorMessage(): string
|
||||
{
|
||||
return __("You have exceeded your vacation limit.");
|
||||
}
|
||||
}
|
||||
|
@@ -4,13 +4,24 @@ declare(strict_types=1);
|
||||
|
||||
namespace Toby\Domain\Validation\Rules;
|
||||
|
||||
use Closure;
|
||||
use Toby\Domain\VacationDaysCalculator;
|
||||
use Toby\Eloquent\Models\VacationRequest;
|
||||
|
||||
class MinimumOneVacationDayRule implements VacationRequestRule
|
||||
class MinimumOneVacationDayRule extends VacationRequestRule
|
||||
{
|
||||
public function check(VacationRequest $vacationRequest, Closure $next)
|
||||
public function __construct(protected VacationDaysCalculator $vacationDaysCalculator)
|
||||
{
|
||||
return $next($vacationRequest);
|
||||
}
|
||||
|
||||
public function passes(VacationRequest $vacationRequest): bool
|
||||
{
|
||||
return $this->vacationDaysCalculator
|
||||
->calculateDays($vacationRequest->yearPeriod, $vacationRequest->from, $vacationRequest->to)
|
||||
->isNotEmpty();
|
||||
}
|
||||
|
||||
public function errorMessage(): string
|
||||
{
|
||||
return __("Vacation needs minimum one day.");
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Toby\Domain\Validation\Rules;
|
||||
|
||||
use Toby\Domain\Enums\VacationRequestState;
|
||||
use Toby\Eloquent\Models\VacationRequest;
|
||||
|
||||
class NoApprovedVacationRequestsInRange extends VacationRequestRule
|
||||
{
|
||||
public function passes(VacationRequest $vacationRequest): bool
|
||||
{
|
||||
return $vacationRequest
|
||||
->user
|
||||
->vacationRequests()
|
||||
->overlapsWith($vacationRequest)
|
||||
->states(VacationRequestState::successStates())
|
||||
->exists();
|
||||
}
|
||||
|
||||
public function errorMessage(): string
|
||||
{
|
||||
return __("You have approved vacation request in this range.");
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Toby\Domain\Validation\Rules;
|
||||
|
||||
use Toby\Domain\Enums\VacationRequestState;
|
||||
use Toby\Eloquent\Models\VacationRequest;
|
||||
|
||||
class NoPendingVacationRequestInRange extends VacationRequestRule
|
||||
{
|
||||
public function passes(VacationRequest $vacationRequest): bool
|
||||
{
|
||||
return $vacationRequest
|
||||
->user
|
||||
->vacationRequests()
|
||||
->overlapsWith($vacationRequest)
|
||||
->states(VacationRequestState::pendingStates())
|
||||
->exists();
|
||||
}
|
||||
|
||||
public function errorMessage(): string
|
||||
{
|
||||
return __("You have pending vacation request in this range.");
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Toby\Domain\Validation\Rules;
|
||||
|
||||
use Closure;
|
||||
use Toby\Eloquent\Models\VacationRequest;
|
||||
|
||||
class PendingVacationRequestInSameRange implements VacationRequestRule
|
||||
{
|
||||
public function check(VacationRequest $vacationRequest, Closure $next)
|
||||
{
|
||||
return $next($vacationRequest);
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Toby\Domain\Validation\Rules;
|
||||
|
||||
use Closure;
|
||||
use Toby\Eloquent\Models\VacationRequest;
|
||||
|
||||
class UsedVacationDaysInSameRange
|
||||
{
|
||||
public function check(VacationRequest $vacationRequest, Closure $next)
|
||||
{
|
||||
return $next($vacationRequest);
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Toby\Domain\Validation\Rules;
|
||||
|
||||
use Toby\Eloquent\Models\VacationRequest;
|
||||
|
||||
class VacationRangeIsInTheSameYearRule extends VacationRequestRule
|
||||
{
|
||||
public function passes(VacationRequest $vacationRequest): bool
|
||||
{
|
||||
return $vacationRequest->from->isSameYear($vacationRequest->to);
|
||||
}
|
||||
|
||||
public function errorMessage(): string
|
||||
{
|
||||
return __("The vacation request cannot be created at the turn of the year.");
|
||||
}
|
||||
}
|
@@ -4,10 +4,18 @@ declare(strict_types=1);
|
||||
|
||||
namespace Toby\Domain\Validation\Rules;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Toby\Eloquent\Models\VacationRequest;
|
||||
|
||||
interface VacationRequestRule
|
||||
abstract class VacationRequestRule
|
||||
{
|
||||
public function check(VacationRequest $vacationRequest, Closure $next);
|
||||
public function check(VacationRequest $vacationRequest): void
|
||||
{
|
||||
if (! $this->passes($vacationRequest)) {
|
||||
throw ValidationException::withMessages(["vacationRequest" => $this->errorMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract function passes(VacationRequest $vacationRequest): bool;
|
||||
public abstract function errorMessage(): string;
|
||||
}
|
||||
|
@@ -5,19 +5,21 @@ declare(strict_types=1);
|
||||
namespace Toby\Domain\Validation;
|
||||
|
||||
use Illuminate\Pipeline\Pipeline;
|
||||
use Toby\Domain\Validation\Rules\ApprovedVacationDaysInSameRange;
|
||||
use Toby\Domain\Validation\Rules\NoApprovedVacationRequestsInRange;
|
||||
use Toby\Domain\Validation\Rules\DoesNotExceedLimitRule;
|
||||
use Toby\Domain\Validation\Rules\MinimumOneVacationDayRule;
|
||||
use Toby\Domain\Validation\Rules\PendingVacationRequestInSameRange;
|
||||
use Toby\Domain\Validation\Rules\NoPendingVacationRequestInRange;
|
||||
use Toby\Domain\Validation\Rules\VacationRangeIsInTheSameYearRule;
|
||||
use Toby\Eloquent\Models\VacationRequest;
|
||||
|
||||
class VacationRequestValidator
|
||||
{
|
||||
protected array $rules = [
|
||||
VacationRangeIsInTheSameYearRule::class,
|
||||
MinimumOneVacationDayRule::class,
|
||||
DoesNotExceedLimitRule::class,
|
||||
PendingVacationRequestInSameRange::class,
|
||||
ApprovedVacationDaysInSameRange::class,
|
||||
NoPendingVacationRequestInRange::class,
|
||||
NoApprovedVacationRequestsInRange::class,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
@@ -27,9 +29,8 @@ class VacationRequestValidator
|
||||
|
||||
public function validate(VacationRequest $vacationRequest): void
|
||||
{
|
||||
$this->pipeline
|
||||
->send($vacationRequest)
|
||||
->through($this->rules)
|
||||
->via("check");
|
||||
foreach ($this->rules as $rule) {
|
||||
app($rule)->check($vacationRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Toby\Eloquent\Models;
|
||||
|
||||
use Carbon\CarbonInterface;
|
||||
use Database\Factories\VacationRequestFactory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -68,6 +69,12 @@ class VacationRequest extends Model
|
||||
return $query->whereIn("state", $states);
|
||||
}
|
||||
|
||||
public function scopeOverlapsWith(Builder $query, VacationRequest $vacationRequest): Builder
|
||||
{
|
||||
return $query->where("from", '<=', $vacationRequest->to)
|
||||
->where("to", '>=', $vacationRequest->from);
|
||||
}
|
||||
|
||||
protected static function newFactory(): VacationRequestFactory
|
||||
{
|
||||
return VacationRequestFactory::new();
|
||||
|
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Toby\Infrastructure\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Toby\Domain\VacationDaysCalculator;
|
||||
use Toby\Infrastructure\Http\Controllers\Controller;
|
||||
use Toby\Infrastructure\Http\Requests\Api\CalculateVacationDaysRequest;
|
||||
|
||||
class CalculateVacationDaysController extends Controller
|
||||
{
|
||||
public function __invoke(CalculateVacationDaysRequest $request, VacationDaysCalculator $calculator): JsonResponse
|
||||
{
|
||||
$days = $calculator->calculateDays($request->yearPeriod(), $request->from(), $request->to());
|
||||
|
||||
return new JsonResponse($days->all());
|
||||
}
|
||||
}
|
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Toby\Infrastructure\Http\Controllers;
|
||||
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response as LaravelResponse;
|
||||
|
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Toby\Infrastructure\Http\Requests\Api;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Toby\Eloquent\Models\YearPeriod;
|
||||
use Toby\Infrastructure\Http\Rules\YearPeriodExists;
|
||||
|
||||
class CalculateVacationDaysRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
"from" => ["required", "date_format:Y-m-d", new YearPeriodExists()],
|
||||
"to" => ["required", "date_format:Y-m-d", new YearPeriodExists()],
|
||||
];
|
||||
}
|
||||
|
||||
public function from(): Carbon
|
||||
{
|
||||
return Carbon::create($this->request->get("from"));
|
||||
}
|
||||
|
||||
public function to(): Carbon
|
||||
{
|
||||
return Carbon::create($this->request->get("to"));
|
||||
}
|
||||
|
||||
public function yearPeriod(): YearPeriod
|
||||
{
|
||||
return YearPeriod::findByYear(Carbon::create($this->request->get("from"))->year);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user