<?php
namespace App\EventSubscriber;
use ApiPlatform\Core\EventListener\EventPriorities;
use App\Entity\Employee;
use App\Entity\Holiday;
use App\Entity\HolidayIncompatibility;
use App\Entity\PublicHoliday;
use App\Entity\Workplace;
use App\Exception\Api\HolidaysAreIncompatibleException;
use App\Service\UserManipulatorService;
use Carbon\Carbon;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Config\Definition\Exception\Exception;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\KernelEvents;
final class CheckHolidaysSubscriber implements EventSubscriberInterface
{
/**
* @var EntityManagerInterface
*/
private $entityManager;
/**
* @var UserManipulatorService
*/
private $generateUserFromEmployeeService;
public function __construct(EntityManagerInterface $entityManager, UserManipulatorService $generateUserFromEmployeeService)
{
$this->entityManager = $entityManager;
$this->generateUserFromEmployeeService = $generateUserFromEmployeeService;
}
public static function getSubscribedEvents()
{
return [
KernelEvents::VIEW => ['checkHolidays', EventPriorities::PRE_VALIDATE],
];
}
public function checkHolidays(ViewEvent $viewEvent)
{
/** @var Holiday $holiday */
$holiday = $viewEvent->getControllerResult();
$method = $viewEvent->getRequest()->getMethod();
if (!$holiday instanceof Holiday || Request::METHOD_POST !== $method) {
return;
}
if (Carbon::parse($holiday->getFromDate())->greaterThan(Carbon::parse($holiday->getToDate()))) {
throw new HolidaysAreIncompatibleException(sprintf('4#Error the fromDate %s greater than toDate: %s', $holiday->getFromDate(), $holiday->getToDate()));
}
/* Comprueba que no coja vacaciones dentro del periodo ya cogido */
$this->checkHolidaysInThisPeriod($holiday, $method);
/* Comprueba si el empleado tiene alguna incompatibilidad con otros usuarios con los que forme un equipo */
$this->checkHolidayIncompatible($holiday);
/* Calculo de los dias consumidos, descontando festivos (obtenidos del correspondiente workplace) y fines de semana */
$workplace = $holiday->getEmployee()->getWorkplace();
/* Compruebo si existen dias festivos en ese rango de fechas */
if ($workplace) {
$publicHolidays = $this->entityManager->getRepository(PublicHoliday::class)
->findPublicHolidaysByWorkplaceAndBetweenDates($workplace, $holiday->getFromDate(), $holiday->getToDate());
} else {
// Si no tiene asignado un workcenter, le damos por defecto el primero
$workplace = $this->entityManager->getRepository(Workplace::class)->find(1);
$publicHolidays = $this->entityManager->getRepository(PublicHoliday::class)
->findPublicHolidaysByWorkplaceAndBetweenDates($workplace, $holiday->getFromDate(), $holiday->getToDate());
}
$daysConsumed = $this->calculateDaysConsumed($holiday->getFromDate(), $holiday->getToDate(), $publicHolidays);
/* Comprueba cuantos dias de vacaciones quedan, si los dias consumidos es mayor al numero de dias permitidos
para cojer vacaciones 23 dara una excepción */
$totalDaysConsumed = $this->entityManager->getRepository(Holiday::class)->getTotalDaysConsumedInYearExercise($holiday);
$daysRemain = $holiday->getEmployee()->getCurrentDaysOff() - $totalDaysConsumed;
if ($daysConsumed > $daysRemain) {
throw new HolidaysAreIncompatibleException(sprintf('3#Exceeds the maximum number of vacation days, remains %s and has requested %s', $daysRemain, $daysConsumed));
}
$holiday->setDaysConsumed($daysConsumed);
/* TODO que pasa si no tiene teamManager */
}
/**
* Devuelve el número de dias hábiles de vacaciones descontando fines de semana y festivos
* @param $fromDate
* @param $toDate
* @param $publicHolidays
* @return int
*/
public function calculateDaysConsumed($fromDate, $toDate, $publicHolidays)
{
$start = Carbon::createFromFormat('Y-m-d', $fromDate)->setTime(0, 0, 0);
$end = Carbon::createFromFormat('Y-m-d', $toDate)->setTime(23, 59, 59);
$listPublicHolidays = [];
/** @var PublicHoliday $publicHoliday */
foreach ($publicHolidays as $publicHoliday) {
$aPublicHoliday = Carbon::createFromFormat('Y-m-d', $publicHoliday->getDate())->setTime(0, 0, 0);
if ($aPublicHoliday->isSunday()) {
$aPublicHoliday->addDay();
}
$listPublicHolidays[] = $aPublicHoliday;
}
$days = $start->diffInDaysFiltered(function (Carbon $date) use ($listPublicHolidays) {
return $date->isWeekday() && !in_array($date, $listPublicHolidays);
}, $end);
return $days;
}
/**
* Comprueba si el usuario tiene alguna incompatiblidad para cojer vacaciones con algun miembro de su equipo
* @param Holiday $holiday
* @throws HolidaysAreIncompatibleException
*/
public function checkHolidayIncompatible(Holiday $holiday): void
{
/* Si la fecha es Nochebuena o Nochevieja no se comprueba */
$nochebuena = Carbon::createFromDate(null, 12, 24);
if ($nochebuena->isSunday()) {
$nochebuena->addDay();
}
$nochevieja = Carbon::createFromDate(null, 12, 31);
if ($nochevieja->isSunday()) {
$nochevieja->addDay();
}
if ($nochebuena->isSameDay(Carbon::createFromFormat('Y-m-d', $holiday->getFromDate())) ||
$nochevieja->isSameDay(Carbon::createFromFormat('Y-m-d', $holiday->getToDate()))) {
return;
}
/** @var HolidayIncompatibility $holidayIncompatibilities */
$holidayIncompatibilities = $holiday->getEmployee()->getHolidayIncompatibilities();
if (count($holidayIncompatibilities->getSnapshot()) > 0) {
$numEmployees = 0;
$totalNumEmployees = 0;
/** @var HolidayIncompatibility $holidayIncompatibility */
foreach ($holidayIncompatibilities as $holidayIncompatibility) {
$employeesIncompatibles = $holidayIncompatibility->getEmployees();
/** @var Employee $employeeIncompatible */
foreach ($employeesIncompatibles as $employeeIncompatible) {
// if ($employeeIncompatible->isActive()) {
$totalNumEmployees += 1;
// comprobar que empleados han cogido vacaciones en ese rango de fechas
// $employeeIncompatible->getHolidays();
if ($employeeIncompatible->getId() !== $holiday->getEmployee()->getId()) {
$holidays = $this->entityManager->getRepository(Holiday::class)
->findHolidaysByEmployeeBetweenDates($employeeIncompatible, $holiday->getFromDate(), $holiday->getToDate());
if (count($holidays) != 0) {
$numEmployees += 1;
}
}
// }
}
}
if ($numEmployees >= ($totalNumEmployees - $holidayIncompatibility->getMinNumberEmployees())) {
throw new HolidaysAreIncompatibleException(sprintf('1#For the employee %s the holidays are incompatibles, there are other employees with this holidays', $holiday->getEmployee()->getId()), '1');
}
}
}
public function checkHolidaysInThisPeriod(Holiday $holiday, $method)
{
$from = $holiday->getFromDate();
$toDate = $holiday->getToDate();
$holidays = $this->entityManager->getRepository(Holiday::class)
->findHolidaysByEmployeeBetweenDatesAndApproved($holiday->getEmployee(), $holiday->getFromDate(), $holiday->getToDate());
if ($holidays) {
$message = sprintf('2#For the employee %s has holidays between %s and %s', $holiday->getEmployee()->getId(), $holiday->getFromDate(), $holiday->getToDate());
throw new HolidaysAreIncompatibleException($message);
}
}
}