147 lines
7.0 KiB
PHP
147 lines
7.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Http\Controllers\Api\V1;
|
|
|
|
use App\Rules\CountryCodeExists;
|
|
use App\Rules\DateHI;
|
|
use App\Rules\DateYMDHI;
|
|
use App\Services\Utils\DurationConvertor;
|
|
use App\Services\WorkingDays\DurationSolverFactory;
|
|
use Brick\DateTime\Duration;
|
|
use Brick\DateTime\LocalTime;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Support\Facades\Validator;
|
|
|
|
final class TaskEstimation
|
|
{
|
|
public function __construct(
|
|
private readonly DurationConvertor $durationConvertor,
|
|
private readonly DurationSolverFactory $durationSolverFactory,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @OA\Get(
|
|
* path="/api/v1/task-estimation/{countryCode}/{startDate}/{minutesDuration}/{calculateWithWorkingDays}/{startWorkday}/{endWorkday}",
|
|
* summary="Get estimation date for a given task",
|
|
* description="Returns estimation date for a given task based on the given start date, duration, working day calculation",
|
|
* @OA\Parameter(name="countryCode", in="path", description="The ISO 3166-1 alpha-2 country code"),
|
|
* @OA\Parameter(name="startDate", in="path", description="Date in YYYY-MM-DD HH:MM format"),
|
|
* @OA\Parameter(name="minutesDuration", in="path", description="Duration of task in minutes"),
|
|
* @OA\Parameter(name="calculateWithWorkingDays", in="path", description="Include working days calculation (0 for no, 1 for yes)"),
|
|
* @OA\Parameter(name="startWorkday", in="path", description="Start of workday in HH:MM format"),
|
|
* @OA\Parameter(name="endWorkday", in="path", description="End of workday in HH:MM format"),
|
|
* @OA\Response(
|
|
* response="200",
|
|
* description="Returns the working day status for the given date and country code",
|
|
* @OA\JsonContent(
|
|
* properties={
|
|
* @OA\Property(property="estimation_date", type="string", format="date"),
|
|
* }
|
|
* )
|
|
* ),
|
|
* @OA\Response(
|
|
* response="400",
|
|
* description="Invalid request parameters",
|
|
* @OA\JsonContent(
|
|
* type="object",
|
|
* properties={
|
|
* @OA\Property (
|
|
* type="object",
|
|
* property="errors",
|
|
* anyOf={
|
|
* @OA\Property(type="object", description="Error", properties={@OA\Property (property="countryCode", type="string", example="Invalid date reason")}),
|
|
* @OA\Property(type="object", description="Error", properties={@OA\Property (property="startDate", type="string", example="Invalid startDate reason")}),
|
|
* @OA\Property(type="object", description="Error", properties={@OA\Property (property="minutesDuration", type="string", example="Invalid minutesDuration reason")}),
|
|
* @OA\Property(type="object", description="Error", properties={@OA\Property (property="calculateWithWorkingDays", type="string", example="Invalid workingDays reason")}),
|
|
* @OA\Property(type="object", description="Error", properties={@OA\Property (property="startWorkday", type="string", example="Invalid startWorkday reason")}),
|
|
* @OA\Property(type="object", description="Error", properties={@OA\Property (property="endWorkday", type="string", example="Invalid endWorkday reason")}),
|
|
* }
|
|
* )
|
|
* })
|
|
* )
|
|
* )
|
|
*/
|
|
public function estimate(string $countryCode, string $startDate, int $minutesDuration, bool $calculateWithWorkingDays, string $startWorkday, string $endWorkday): JsonResponse
|
|
{
|
|
|
|
$validator = Validator::make(
|
|
[
|
|
'countryCode' => $countryCode,
|
|
'startDate' => $startDate,
|
|
'minutesDuration' => $minutesDuration,
|
|
'calculateWithWorkingDays' => $calculateWithWorkingDays,
|
|
'startWorkday' => $startWorkday,
|
|
'endWorkday' => $endWorkday,
|
|
],
|
|
[
|
|
'countryCode' => [new CountryCodeExists()],
|
|
'startDate' => ['required', new DateYMDHI()],
|
|
'minutesDuration' => 'integer|min:1',
|
|
'calculateWithWorkingDays' => 'boolean',
|
|
'startWorkday' => ['required', new DateHI()],
|
|
'endWorkday' => ['required', new DateHI()],
|
|
]
|
|
);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json(['errors' => $validator->errors()], 400);
|
|
}
|
|
|
|
$duration = Duration::parse(sprintf('P0DT%dM', $minutesDuration));
|
|
$startWorkday = LocalTime::parse($startWorkday);
|
|
$startDate = \DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $startDate . ":00");
|
|
$endWorkday = LocalTime::parse($endWorkday);
|
|
|
|
if ($startDate === false) {
|
|
return response()->json(['errors' => ['startDate' => ['validation.date_ymdhi']]], 400);
|
|
}
|
|
|
|
if (($result = $this->validateParsedData($startDate, $startWorkday, $endWorkday)) !== null) {
|
|
return $result;
|
|
}
|
|
|
|
$workMinutes = $this->durationConvertor->convertIntoWorkDuration($duration, $startWorkday, $endWorkday, $startDate);
|
|
if ($workMinutes->overlaps === true) {
|
|
$dueDate = $startDate->setTime($startWorkday->getHour(), $startWorkday->getMinute(), $startWorkday->getSecond())
|
|
->add(new \DateInterval(sprintf('PT%dM', $workMinutes->duration->toMinutes())));
|
|
} else {
|
|
$dueDate = $startDate->add(new \DateInterval(sprintf('PT%dM', $workMinutes->duration->toMinutes())));
|
|
}
|
|
|
|
// test if false startDate + startDate must be after startWorkday and before endWorkday
|
|
if ($calculateWithWorkingDays) {
|
|
$durationSolver = $this->durationSolverFactory->createDurationSolverForCountry($countryCode);
|
|
$dueDate = $durationSolver->calculateDuration($startDate, $dueDate);
|
|
}
|
|
|
|
return response()->json([
|
|
'estimation_date' => $dueDate->format('Y-m-d H:i:s'),
|
|
]);
|
|
}
|
|
|
|
|
|
private function validateParsedData(\DateTimeInterface $startDate, LocalTime $startWorkday, LocalTime $endWorkday): JsonResponse|null
|
|
{
|
|
if ($startWorkday->isAfter($endWorkday)) {
|
|
return response()->json(['errors' => ['startWorkday' => ['validation.start_workday_must_be_before_end_workday']]], 400);
|
|
}
|
|
|
|
if ($startWorkday->isEqualTo($endWorkday)) {
|
|
return response()->json(['errors' => ['startWorkday' => ['validation.start_workday_must_not_be_equal_to_end_workday']]], 400);
|
|
}
|
|
|
|
if ($startWorkday->isAfter(LocalTime::fromNativeDateTime($startDate))) {
|
|
return response()->json(['errors' => ['startDate' => ['validation.start_workday_must_be_before_start_date']]], 400);
|
|
}
|
|
|
|
if ($endWorkday->isBefore(LocalTime::fromNativeDateTime($startDate))) {
|
|
return response()->json(['errors' => ['endWorkday' => ['validation.end_workday_must_be_after_start_date']]], 400);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|