src/Controller/HomeController.php line 322

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Configuration;
  4. use App\Entity\Space;
  5. use App\Entity\User;
  6. use App\Entity\WidgetNotes;
  7. use App\Entity\HtFile;
  8. use App\MDS\AvexpressBundle\Entity\AveFiles;
  9. use App\Entity\SettingsCompany;
  10. use App\MDS\VenuesBundle\Entity\ReservationInvoice;
  11. use App\Form\WidgetNotesType;
  12. use App\MDS\VenuesBundle\Entity\Reservation;
  13. use App\MDS\VenuesBundle\Entity\ReservationLoungeDetails;
  14. use App\MDS\VenuesBundle\Entity\ReservationLoungeSimple;
  15. use App\MDS\VenuesBundle\Entity\ReservationVisit;
  16. use App\Service\CalendarService;
  17. use App\Service\UserNotificationService;
  18. use Doctrine\ORM\EntityManagerInterface;
  19. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  20. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  21. use Symfony\Component\Routing\Annotation\Route;
  22. use Symfony\Component\HttpFoundation\Request;
  23. use Symfony\Component\HttpFoundation\Response;
  24. use Symfony\Component\HttpFoundation\Session\Session;
  25. use Symfony\Component\HttpFoundation\JsonResponse;
  26. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  27. use Symfony\Contracts\Translation\TranslatorInterface;
  28. use Google_Client;
  29. use Google_Service_Calendar;
  30. use Doctrine\ORM\Query;
  31. class HomeController extends AbstractController
  32. {
  33.     private $googleCalendar;
  34.     private $translator;
  35.     private $userNotificationService;
  36.     private SessionInterface $session;
  37.     public function __construct(TranslatorInterface $translatorUserNotificationService $userNotificationServiceSessionInterface $session)
  38.     {
  39.         $redirectUri 'https://' . ($_SERVER['HTTP_HOST'] ?? '') . '/calendar/token';
  40.         $client = new Google_Client();
  41.         $client->setApplicationName('Google Calendar API');
  42.         $client->setClientId('YOUR_GOOGLE_CLIENT_ID');
  43.         $client->setClientSecret('YOUR_GOOGLE_CLIENT_SECRET');
  44.         $client->setRedirectUri($redirectUri);
  45.         $client->addScope(Google_Service_Calendar::CALENDAR);
  46.         $guzzle = new \GuzzleHttp\Client(['curl' => [CURLOPT_SSL_VERIFYPEER => false]]);
  47.         $client->setHttpClient($guzzle);
  48.         $this->googleCalendar $client;
  49.         $this->translator $translator;
  50.         $this->userNotificationService $userNotificationService;
  51.         $this->session $session;
  52.     }
  53.     /**
  54.      * Portal principal con los 3 accesos (ManteVenues, ManteCatering, ManteAV)
  55.      * @Route("/inicio", name="ruta_inicio")
  56.      */
  57.     public function inicioAction(AuthenticationUtils $authenticationUtilsEntityManagerInterface $em): Response
  58.     {
  59.         $configuration $em->getRepository(Configuration::class)->findOneBy([]);
  60.         return $this->render('login/login.html.twig', [
  61.             'last_username' => $authenticationUtils->getLastUsername(),
  62.             'error' => $authenticationUtils->getLastAuthenticationError(),
  63.             'configuration' => $configuration,
  64.         ]);
  65.     }
  66.     /**
  67.      * @Route("/", name="homepage")
  68.      */
  69.     public function index(EntityManagerInterface $emRequest $request): Response
  70.     {
  71.         $user $this->getUser();
  72.         if (!$user) {
  73.             return $this->redirectToRoute('ruta_inicio');
  74.         }
  75.         // Recuperamos el tipo de acceso actual (Venues, Catering o AV) de la sesión
  76.         $accessType $this->session->get('current_access_type') ?? 'venues';
  77.         $tz = new \DateTimeZone('Europe/Madrid');
  78.         $from = (new \DateTimeImmutable('today'$tz))->setTime(000);
  79.         $to $from->setTime(235959);
  80.         $agendaMix = [];
  81.         $formattedReservations = [];
  82.         $formattedVisits = [];
  83.         if ($accessType === 'venues') {
  84.             // 1. FILTRADO POR EQUIPO "LEGENDS VIEW"
  85.             $isLegendTeam = (strtoupper(trim($user->getTeam() ?? '')) === 'LEGENDS VIEW');
  86.             $lSpace null;
  87.             if ($isLegendTeam) {
  88.                 $lSpace $em->getRepository(Space::class)->createQueryBuilder('s')
  89.                     ->where('s.name LIKE :n')->setParameter('n''%LEGEND%')
  90.                     ->setMaxResults(1)->getQuery()->getOneOrNullResult();
  91.             }
  92.             /** --- LÓGICA Módulo VENUES (Reservas y Visitas) --- */
  93.             $qbR $em->createQueryBuilder()
  94.                 ->select('r.id''r.title''r.dateStart''r.dateEnd''lounge.name as loungeName''rls.type''r.status')
  95.                 ->from(Reservation::class, 'r')
  96.                 ->leftJoin(ReservationLoungeSimple::class, 'rls''WITH''rls.idReservation = r.id')
  97.                 ->leftJoin(ReservationLoungeDetails::class, 'lounge''WITH''lounge.id = rls.idLounge')
  98.                 ->where('r.dateStart <= :to AND COALESCE(r.dateEnd, r.dateStart) >= :from')
  99.                 ->andWhere('r.status <> :deleted')
  100.                 ->setParameter('from'$from)
  101.                 ->setParameter('to'$to)
  102.                 ->setParameter('deleted''Deleted')
  103.                 ->orderBy('r.dateStart''ASC');
  104.             if ($isLegendTeam && $lSpace) {
  105.                 $qbR->andWhere('lounge.space = :sid')->setParameter('sid'$lSpace->getId());
  106.             }
  107.             $rawRows $qbR->getQuery()->getArrayResult();
  108.             $reservationsMap = [];
  109.             foreach ($rawRows as $row) {
  110.                 $id $row['id'];
  111.                 $type $row['type'];
  112.                 $key $id . ($type '_' $type '');
  113.                 if (!isset($reservationsMap[$key])) {
  114.                     $prefix $type '[' strtoupper($type) . '] ' '';
  115.                     $reservationsMap[$key] = [
  116.                         'id' => $id,
  117.                         'title' => $prefix . ($row['title'] ?? 'Reserva'),
  118.                         'dateStart' => $row['dateStart'],
  119.                         'rooms' => [],
  120.                         'tipo' => 'RESERVA',
  121.                         'status' => $row['status'],
  122.                         // URL específica de Venues
  123.                         'url' => $this->generateUrl('reservations_venues_edit_simple', ['id' => $id])
  124.                     ];
  125.                 }
  126.                 if ($row['loungeName']) {
  127.                     $reservationsMap[$key]['rooms'][] = $row['loungeName'];
  128.                 }
  129.             }
  130.             // Fetch invoices for these reservations to allow access for Legends
  131.             $rIds array_unique(array_column($reservationsMap'id'));
  132.             $invoiceMap = [];
  133.             if (!empty($rIds)) {
  134.                 $invoices $em->getRepository(ReservationInvoice::class)
  135.                     ->findBy(['reservationId' => $rIds], ['id' => 'DESC']);
  136.                 foreach ($invoices as $inv) {
  137.                     if (!isset($invoiceMap[$inv->getReservationId()])) {
  138.                         $invoiceMap[$inv->getReservationId()] = $inv->getId();
  139.                     }
  140.                 }
  141.             }
  142.             foreach ($reservationsMap as $res) {
  143.                 $res['room'] = implode(', 'array_unique($res['rooms']));
  144.                 $res['invoiceUrl'] = isset($invoiceMap[$res['id']]) 
  145.                     ? $this->generateUrl('reservations_invoiceorproforma_print', ['type' => 'I''id' => $invoiceMap[$res['id']]]) 
  146.                     : null;
  147.                 $res['showUrl'] = $this->generateUrl('reservations_venues_show_simple', ['id' => $res['id']]);
  148.                 $formattedReservations[] = $res;
  149.             }
  150.             // VISITAS
  151.             $qbV $em->getRepository(ReservationVisit::class)->createQueryBuilder('v')
  152.                 ->where('v.dateStart <= :to AND COALESCE(v.dateEnd, v.dateStart) >= :from')
  153.                 ->setParameter('from'$from)
  154.                 ->setParameter('to'$to)
  155.                 ->orderBy('v.dateStart''ASC');
  156.             if ($isLegendTeam && $lSpace) {
  157.                 $qbV->andWhere('v.space = :sid')->setParameter('sid'$lSpace->getId());
  158.             }
  159.             $visitEntities $qbV->getQuery()->getResult();
  160.             $loungeRepo $em->getRepository(ReservationLoungeDetails::class);
  161.             foreach ($visitEntities as $visit) {
  162.                 $lName '-';
  163.                 if ($visit->getIdLoungeDetails()) {
  164.                     $lounge $loungeRepo->find($visit->getIdLoungeDetails());
  165.                     $lName $lounge $lounge->getName() : '-';
  166.                 }
  167.                 $formattedVisits[] = [
  168.                     'id' => $visit->getId(),
  169.                     'title' => ($visit->getTitle() ?? 'Sin título'),
  170.                     'dateStart' => $visit->getDateStart(),
  171.                     'room' => $lName,
  172.                     'tipo' => 'VISITA',
  173.                     // También enviamos la URL correcta para las visitas
  174.                     'url' => $this->generateUrl('venues_edit_visit', ['id' => $visit->getId()])
  175.                 ];
  176.             }
  177.             $agendaMix array_merge($formattedReservations$formattedVisits);
  178.             usort($agendaMix, fn ($a$b) => $a['dateStart'] <=> $b['dateStart']);
  179.         } else if ($accessType === 'catering') {
  180.             /** --- LÓGICA Módulo CATERING (Higo & Trigo + Montajes Venues) --- */
  181.             // 1. Eventos de Catering
  182.             $htFiles $em->getRepository(HtFile::class)->createQueryBuilder('h')
  183.                 ->where('h.dateStart <= :to AND COALESCE(h.dateEnd, h.dateStart) >= :from')
  184.                 ->setParameter('from'$from)
  185.                 ->setParameter('to'$to)
  186.                 ->orderBy('h.dateStart''ASC')
  187.                 ->getQuery()->getResult();
  188.             $htEventsFormatted = [];
  189.             foreach ($htFiles as $ht) {
  190.                 $htEventsFormatted[] = [
  191.                     'id' => $ht->getId(),
  192.                     'title' => '[CATERING] ' . ($ht->getTitle() ?? 'Sin título'),
  193.                     'dateStart' => $ht->getDateStart(),
  194.                     'room' => 'Servicio Catering',
  195.                     'tipo' => 'RESERVA',
  196.                     // URL específica de Catering
  197.                     'url' => $this->generateUrl('app_ht_file_edit', ['id' => $ht->getId()])
  198.                 ];
  199.             }
  200.             // 2. Montajes/Desmontajes de Venues para Catering
  201.             $qbM $em->createQueryBuilder()
  202.                 ->select('r.id''r.title''r.dateStart''lounge.name as loungeName''rls.type')
  203.                 ->from(Reservation::class, 'r')
  204.                 ->leftJoin(ReservationLoungeSimple::class, 'rls''WITH''rls.idReservation = r.id')
  205.                 ->leftJoin(ReservationLoungeDetails::class, 'lounge''WITH''lounge.id = rls.idLounge')
  206.                 ->where('r.dateStart <= :to AND COALESCE(r.dateEnd, r.dateStart) >= :from')
  207.                 ->andWhere('rls.type IN (:types)')
  208.                 ->andWhere('r.status <> :deleted')
  209.                 ->setParameter('from'$from)
  210.                 ->setParameter('to'$to)
  211.                 ->setParameter('types', ['montaje''desmontaje'])
  212.                 ->setParameter('deleted''Deleted');
  213.             $venueRows $qbM->getQuery()->getArrayResult();
  214.             $venueEventsFormatted = [];
  215.             foreach ($venueRows as $row) {
  216.                 $venueEventsFormatted[] = [
  217.                     'id' => $row['id'],
  218.                     'title' => '[' strtoupper($row['type'] ?? 'TRABAJO') . '] ' . ($row['title'] ?? 'Reserva'),
  219.                     'dateStart' => $row['dateStart'],
  220.                     'room' => $row['loungeName'] ?? '-',
  221.                     'tipo' => 'RESERVA',
  222.                     // Mantenemos la ruta de Venues para montajes de salas
  223.                     'url' => $this->generateUrl('reservations_venues_edit_simple', ['id' => $row['id']])
  224.                 ];
  225.             }
  226.             $agendaMix array_merge($htEventsFormatted$venueEventsFormatted);
  227.             usort($agendaMix, fn ($a$b) => $a['dateStart'] <=> $b['dateStart']);
  228.             $formattedReservations $agendaMix;
  229.         } else if ($accessType === 'av') {
  230.             /** --- LÓGICA Módulo AV (Audiovisuales) --- */
  231.             $avFiles $em->getRepository(AveFiles::class)->createQueryBuilder('a')
  232.                 ->where('a.dateStart <= :to AND COALESCE(a.dateEnd, a.dateStart) >= :from')
  233.                 ->andWhere('a.status <> :deleted')
  234.                 ->setParameter('from'$from)
  235.                 ->setParameter('to'$to)
  236.                 ->setParameter('deleted''Deleted')
  237.                 ->orderBy('a.dateStart''ASC')
  238.                 ->getQuery()->getResult();
  239.             foreach ($avFiles as $av) {
  240.                 $formattedReservations[] = [
  241.                     'id' => $av->getId(),
  242.                     'title' => $av->getTitle() ?? 'Servicio AV',
  243.                     'dateStart' => $av->getDateStart(),
  244.                     'room' => 'Equipos AV',
  245.                     'tipo' => 'RESERVA',
  246.                     // URL específica de AV (Verifica que el nombre de ruta sea exacto el de tu controlador AV)
  247.                     'url' => $this->generateUrl('ave_edit_file', ['id' => $av->getId()])
  248.                 ];
  249.             }
  250.             $agendaMix $formattedReservations;
  251.         }
  252.         $this->userNotificationService->checkRecentNotifications($user);
  253.         return $this->render('home/index.html.twig', [
  254.             'accessType'           => $accessType,
  255.             'agenda'               => $agendaMix,
  256.             'upcomingReservations' => $formattedReservations,
  257.             'upcomingVisits'       => $formattedVisits,
  258.             'notifications'        => $this->userNotificationService->getForModal($user)
  259.         ]);
  260.     }
  261.     /**
  262.      * @Route("/calendar/catering/menu-testings", name="calendar_catering_menu_testings", methods={"GET"})
  263.      */
  264.     public function calendarCateringMenuTestings(Request $requestCalendarService $calendar): JsonResponse
  265.     {
  266.         $fromParam $request->query->get('start''first day of this month');
  267.         $toParam $request->query->get('end''last day of next month');
  268.         try {
  269.             $from = new \DateTimeImmutable($fromParam);
  270.             $to = new \DateTimeImmutable($toParam);
  271.         } catch (\Exception $e) {
  272.             return new JsonResponse(['error' => 'Formato de fecha inválido'], 400);
  273.         }
  274.         // Llamamos al método renombrado
  275.         return new JsonResponse($calendar->getMenuTestingsForCalendar($from$to));
  276.     }
  277.     /**
  278.      * @Route("/calendar/global/visits", name="calendar_global_visits", methods={"GET"})
  279.      */
  280.     public function globalVisits(Request $requestCalendarService $calendarEntityManagerInterface $em): JsonResponse
  281.     {
  282.         $user $this->getUser();
  283.         $spaceIds null;
  284.         if ($user && strtoupper(trim($user->getTeam() ?? '')) === 'LEGENDS VIEW') {
  285.             // Buscamos todos los espacios cuyo nombre sea o contenga 'LEGENDS VIEW'
  286.             $spaces $em->getRepository(Space::class)->createQueryBuilder('s')
  287.                 ->where('s.name LIKE :n')
  288.                 ->setParameter('n''%ESPACIO LEGENDS%')
  289.                 ->getQuery()
  290.                 ->getResult();
  291.             if (!empty($spaces)) {
  292.                 $spaceIds array_map(fn($s) => $s->getId(), $spaces);
  293.             } else {
  294.                 // Seguridad: Si no encuentra el espacio, ID inexistente para que no vea nada por error
  295.                 $spaceIds = [99999];
  296.             }
  297.         }
  298.         $from = new \DateTime($request->query->get('start'));
  299.         $to = new \DateTime($request->query->get('end'));
  300.         return new JsonResponse($calendar->getVisitsForCalendar($spaceIdsnull$from$to));
  301.     }
  302.     /**
  303.      * @Route("/calendar/global/reservations", name="calendar_global_reservations", methods={"GET"})
  304.      */
  305.     public function globalReservations(Request $requestCalendarService $calendarEntityManagerInterface $em): JsonResponse
  306.     {
  307.         $user $this->getUser();
  308.         $spaceId null;
  309.         if ($user && strtoupper(trim($user->getTeam() ?? '')) === 'LEGENDS VIEW') {
  310.             $lSpace $em->getRepository(Space::class)->createQueryBuilder('s')
  311.                 ->where('s.name LIKE :n')
  312.                 ->setParameter('n''%LEGEND%')
  313.                 ->setMaxResults(1)
  314.                 ->getQuery()
  315.                 ->getOneOrNullResult();
  316.             $spaceId $lSpace?->getId();
  317.         }
  318.         $from = new \DateTime($request->query->get('start'));
  319.         $to = new \DateTime($request->query->get('end'));
  320.         return new JsonResponse($calendar->getReservationsForCalendar($spaceIdnull$from$to));
  321.     }
  322.     /**
  323.      * Endpoint específico para que el perfil de Catering vea solo montajes y desmontajes.
  324.      * * @Route("/calendar/catering/reservations", name="calendar_catering_reservations", methods={"GET"})
  325.      */
  326.     public function cateringReservations(Request $requestCalendarService $calendar): JsonResponse
  327.     {
  328.         // Validamos las fechas de entrada que envía FullCalendar
  329.         $startParam $request->query->get('start');
  330.         $endParam $request->query->get('end');
  331.         if (!$startParam || !$endParam) {
  332.             return new JsonResponse(['error' => 'Missing date parameters'], 400);
  333.         }
  334.         try {
  335.             $from = new \DateTime($startParam);
  336.             $to = new \DateTime($endParam);
  337.         } catch (\Exception $e) {
  338.             return new JsonResponse(['error' => 'Invalid date format'], 400);
  339.         }
  340.         // Llamamos al nuevo método específico del servicio
  341.         $events $calendar->getCateringSetupsForCalendar($from$to);
  342.         return new JsonResponse($events);
  343.     }
  344.     /**
  345.      * @Route("/calendar/ht/events", name="calendar_ht_events", methods={"GET"})
  346.      */
  347.     public function calendarHtEvents(Request $requestCalendarService $calendar): JsonResponse
  348.     {
  349.         $from = new \DateTimeImmutable($request->query->get('start''first day of this month'));
  350.         $to = new \DateTimeImmutable($request->query->get('end''last day of next month'));
  351.         return new JsonResponse($calendar->getHtEvents($from$to));
  352.     }
  353.     /**
  354.      * @Route("/calendar/av/events", name="calendar_av_events", methods={"GET"})
  355.      */
  356.     public function calendarAvEvents(Request $requestCalendarService $calendar): JsonResponse
  357.     {
  358.         $from = new \DateTimeImmutable($request->query->get('start''first day of this month'));
  359.         $to = new \DateTimeImmutable($request->query->get('end''last day of next month'));
  360.         // 1. Sacamos los eventos propios de Audiovisuales
  361.         $avEvents $calendar->getAvEvents($from$to'av');
  362.         // 2. Sacamos los montajes y desmontajes asociados a Audiovisuales
  363.         $avSetups $calendar->getAvSetupsForCalendar($from$to'av');
  364.         // 3. Los unimos en un solo array
  365.         $allEvents array_merge($avEvents$avSetups);
  366.         return new JsonResponse($allEvents);
  367.     }
  368.     /**
  369.      * @Route("/ChangeLanguage/{lang}", name="change_language")
  370.      */
  371.     public function changeLanguage(Request $requeststring $lang): Response
  372.     {
  373.         $this->translator->setLocale($lang);
  374.         $request->getSession()->set('_locale'$lang);
  375.         return $this->redirect($request->headers->get('referer'));
  376.     }
  377.     /**
  378.      * @Route("/calendar-full", name="calendar_full")
  379.      */
  380.     public function calendarFull(Request $request): Response
  381.     {
  382.         $token $request->request->get('token');
  383.         $em $this->getDoctrine()->getManager();
  384.         $user $em->getRepository(User::class)->findOneByAccessKey($token);
  385.         if (!$user)
  386.             return $this->redirectToRoute('ruta_inicio');
  387.         $userId $user->getId();
  388.         $wnotes = new WidgetNotes();
  389.         $wnotes->setDateAt(new \DateTime());
  390.         $form $this->createForm(WidgetNotesType::class, $wnotes, [
  391.             'action' => $this->generateUrl('widget_notes_create'),
  392.             'method' => 'POST',
  393.         ]);
  394.         return $this->render('home/calendar-fullscreen.html.twig', [
  395.             'form' => $form->createView(),
  396.             'user' => $userId,
  397.             'token' => $token,
  398.         ]);
  399.     }
  400.     /**
  401.      * @Route("/external/calendar-reservation", name="calendar_external_reservation")
  402.      */
  403.     public function externalCalendar(): Response
  404.     {
  405.         return $this->render('home/calendar-reservation.html.twig');
  406.     }
  407. }