* #36 - wip * #36 - wip * #36 - wip * #36 - added some translations * #36 - fix * #36 - fix Co-authored-by: EwelinaLasowy <ewelina.lasowy@blumilk.pl>
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,23 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Domain\Validation\Rules; | ||||
|  | ||||
| use Closure; | ||||
| use Toby\Domain\VacationTypeConfigRetriever; | ||||
| use Toby\Eloquent\Models\VacationRequest; | ||||
|  | ||||
| class DoesNotExceedLimitRule implements VacationRequestRule | ||||
| { | ||||
|     public function check(VacationRequest $vacationRequest, Closure $next) | ||||
|     public function __construct( | ||||
|         protected VacationTypeConfigRetriever $configRetriever, | ||||
|     ) { | ||||
|     } | ||||
|  | ||||
|     public function check(VacationRequest $vacationRequest): bool | ||||
|     { | ||||
|         return $next($vacationRequest); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function errorMessage(): string | ||||
|     { | ||||
|         return __("You have exceeded your vacation limit."); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,13 +4,25 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Domain\Validation\Rules; | ||||
|  | ||||
| use Closure; | ||||
| use Toby\Domain\VacationDaysCalculator; | ||||
| use Toby\Eloquent\Models\VacationRequest; | ||||
|  | ||||
| class MinimumOneVacationDayRule implements VacationRequestRule | ||||
| { | ||||
|     public function check(VacationRequest $vacationRequest, Closure $next) | ||||
|     public function __construct( | ||||
|         protected VacationDaysCalculator $vacationDaysCalculator, | ||||
|     ) { | ||||
|     } | ||||
|  | ||||
|     public function check(VacationRequest $vacationRequest): bool | ||||
|     { | ||||
|         return $next($vacationRequest); | ||||
|         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 implements VacationRequestRule | ||||
| { | ||||
|     public function check(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 implements VacationRequestRule | ||||
| { | ||||
|     public function check(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 implements VacationRequestRule | ||||
| { | ||||
|     public function check(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,10 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Domain\Validation\Rules; | ||||
|  | ||||
| use Closure; | ||||
| use Toby\Eloquent\Models\VacationRequest; | ||||
|  | ||||
| interface VacationRequestRule | ||||
| { | ||||
|     public function check(VacationRequest $vacationRequest, Closure $next); | ||||
|     public function check(VacationRequest $vacationRequest): bool; | ||||
|     public function errorMessage(): string; | ||||
| } | ||||
|   | ||||
| @@ -4,32 +4,49 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Domain\Validation; | ||||
|  | ||||
| use Illuminate\Pipeline\Pipeline; | ||||
| use Toby\Domain\Validation\Rules\ApprovedVacationDaysInSameRange; | ||||
| use Illuminate\Contracts\Container\Container; | ||||
| use Illuminate\Validation\ValidationException; | ||||
| use Toby\Domain\Validation\Rules\DoesNotExceedLimitRule; | ||||
| use Toby\Domain\Validation\Rules\MinimumOneVacationDayRule; | ||||
| use Toby\Domain\Validation\Rules\PendingVacationRequestInSameRange; | ||||
| use Toby\Domain\Validation\Rules\NoApprovedVacationRequestsInRange; | ||||
| use Toby\Domain\Validation\Rules\NoPendingVacationRequestInRange; | ||||
| use Toby\Domain\Validation\Rules\VacationRangeIsInTheSameYearRule; | ||||
| use Toby\Domain\Validation\Rules\VacationRequestRule; | ||||
| 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( | ||||
|         protected Pipeline $pipeline, | ||||
|         protected Container $container, | ||||
|     ) { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @throws ValidationException | ||||
|      */ | ||||
|     public function validate(VacationRequest $vacationRequest): void | ||||
|     { | ||||
|         $this->pipeline | ||||
|             ->send($vacationRequest) | ||||
|             ->through($this->rules) | ||||
|             ->via("check"); | ||||
|         foreach ($this->rules as $rule) { | ||||
|             $rule = $this->makeRule($rule); | ||||
|  | ||||
|             if (!$rule->check($vacationRequest)) { | ||||
|                 throw ValidationException::withMessages([ | ||||
|                     "vacationRequest" => $rule->errorMessage(), | ||||
|                 ]); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected function makeRule(string $class): VacationRequestRule | ||||
|     { | ||||
|         return $this->container->make($class); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -21,6 +21,7 @@ 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 | ||||
| @@ -68,6 +69,12 @@ class VacationRequest extends Model | ||||
|         return $query->whereIn("state", $states); | ||||
|     } | ||||
|  | ||||
|     public function scopeOverlapsWith(Builder $query, self $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()); | ||||
|     } | ||||
| } | ||||
| @@ -11,6 +11,7 @@ use Illuminate\Http\Response as LaravelResponse; | ||||
| use Inertia\Response; | ||||
| use Toby\Domain\Enums\VacationRequestState; | ||||
| use Toby\Domain\Enums\VacationType; | ||||
| use Toby\Domain\VacationDaysCalculator; | ||||
| use Toby\Domain\VacationRequestStateManager; | ||||
| use Toby\Domain\Validation\VacationRequestValidator; | ||||
| use Toby\Eloquent\Helpers\YearPeriodRetriever; | ||||
| @@ -64,9 +65,15 @@ class VacationRequestController extends Controller | ||||
|         VacationRequestRequest $request, | ||||
|         VacationRequestValidator $vacationRequestValidator, | ||||
|         VacationRequestStateManager $stateManager, | ||||
|         VacationDaysCalculator $vacationDaysCalculator, | ||||
|     ): RedirectResponse { | ||||
|         /** @var VacationRequest $vacationRequest */ | ||||
|         $vacationRequest = $request->user()->vacationRequests()->make($request->data()); | ||||
|         $vacationRequest->estimated_days = $vacationDaysCalculator->calculateDays( | ||||
|             $vacationRequest->yearPeriod, | ||||
|             $vacationRequest->from, | ||||
|             $vacationRequest->to, | ||||
|         )->count(); | ||||
|  | ||||
|         $vacationRequestValidator->validate($vacationRequest); | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
| @@ -20,6 +20,7 @@ class VacationRequestResource extends JsonResource | ||||
|             "state" => $this->state->label(), | ||||
|             "from" => $this->from->toDisplayString(), | ||||
|             "to" => $this->to->toDisplayString(), | ||||
|             "estimatedDays" => $this->estimated_days, | ||||
|             "comment" => $this->comment, | ||||
|         ]; | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user