137 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			137 lines
		
	
	
		
			6.7 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 ($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);
 | 
						|
        }
 | 
						|
 | 
						|
        $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'),
 | 
						|
        ]);
 | 
						|
    }
 | 
						|
}
 |