src/Controller/DefaultController.php line 47

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\AssignedBadge;
  4. use App\Entity\Badge;
  5. use App\Entity\CompanyOrder;
  6. use App\Entity\ErrorStamped;
  7. use App\Entity\Operator;
  8. use App\Entity\Stamped;
  9. use App\Entity\User;
  10. use App\Form\StampedInOutType;
  11. use App\Form\StampedMensaSacchettoType;
  12. use App\Form\StampedMensaType;
  13. use App\Repository\StampedRepository;
  14. use App\Service\TimbratoreLogger;
  15. use App\Utils\DateTimeUtils;
  16. use Doctrine\Persistence\ManagerRegistry;
  17. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  18. use Symfony\Component\Config\Definition\Exception\Exception;
  19. use Symfony\Component\HttpFoundation\JsonResponse;
  20. use Symfony\Component\HttpFoundation\Request;
  21. use Symfony\Component\HttpFoundation\Response;
  22. use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
  23. use Symfony\Component\Routing\Annotation\Route;
  24. class DefaultController extends AbstractController
  25. {
  26. private $doctrine;
  27. private $timbratoreLogger;
  28. public function __construct(
  29. ManagerRegistry $doctrine,
  30. TimbratoreLogger $timbratoreLogger
  31. )
  32. {
  33. $this->doctrine = $doctrine;
  34. $this->timbratoreLogger = $timbratoreLogger;
  35. }
  36. /**
  37. * @Route("/", name="home", methods={"GET","POST"})
  38. * @param StampedRepository $stampedRepository
  39. * @return Response
  40. * @throws \Exception
  41. */
  42. public function index(StampedRepository $stampedRepository): Response
  43. {
  44. if ($this->isGranted("ROLE_ADMIN_EMZA")) {
  45. return $this->redirectToRoute('stamped_index', ['page' => '1', 'companyOrderType' => CompanyOrder::TYPE_EMZA]);
  46. } elseif ($this->isGranted("ROLE_ADMIN")) {
  47. return $this->redirectToRoute('stamped_index', ['page' => '1', 'companyOrderType' => CompanyOrder::TYPE_CANTIERE]);
  48. }elseif ($this->isGranted("ROLE_ADMIN_MENSA")) {
  49. return $this->redirectToRoute('stamped_index', ['page' => '1', 'companyOrderType' => CompanyOrder::TYPE_MENSA]);
  50. }elseif ($this->isGranted("ROLE_ADMIN_CANTIERE")) {
  51. return $this->redirectToRoute('stamped_index', ['page' => '1', 'companyOrderType' => CompanyOrder::TYPE_CANTIERE]);
  52. } elseif ($this->isGranted("ROLE_MENSA_ASPORTO")) {
  53. $form = $this->createForm(StampedMensaSacchettoType::class);
  54. $userCompanyOrder = $this->get('security.token_storage')->getToken()->getUser()->getCompanyOrder();
  55. $this->storeTimbratoreCompanyOrder($userCompanyOrder);
  56. return $this->render('default/home-mensa-sacchetto.html.twig', [
  57. 'form' => $form->createView(),
  58. 'userCompany' => $userCompanyOrder,
  59. ]);
  60. } elseif ($this->isGranted("ROLE_MENSA")) {
  61. $form = $this->createForm(StampedMensaType::class);
  62. $userCompanyOrder = $this->get('security.token_storage')->getToken()->getUser()->getCompanyOrder();
  63. $this->storeTimbratoreCompanyOrder($userCompanyOrder);
  64. return $this->render('default/home-mensa.html.twig', [
  65. 'form' => $form->createView(),
  66. 'userCompany' => $userCompanyOrder,
  67. ]);
  68. } else {
  69. $form = $this->createForm(StampedInOutType::class);
  70. $userCompanyOrder = $this->get('security.token_storage')->getToken()->getUser()->getCompanyOrder();
  71. $this->storeTimbratoreCompanyOrder($userCompanyOrder);
  72. return $this->render('default/home.html.twig', [
  73. 'form' => $form->createView(),
  74. 'userCompany' => $userCompanyOrder,
  75. ]);
  76. }
  77. }
  78. private function storeTimbratoreCompanyOrder(?CompanyOrder $companyOrder): void
  79. {
  80. $request = $this->container->get('request_stack')->getCurrentRequest();
  81. if (!$request || !$request->hasSession()) {
  82. return;
  83. }
  84. if ($companyOrder) {
  85. $request->getSession()->set('timbratore_company_order_id', $companyOrder->getId());
  86. $request->getSession()->set('timbratore_name', (string) $companyOrder);
  87. } else {
  88. $request->getSession()->remove('timbratore_company_order_id');
  89. $request->getSession()->remove('timbratore_name');
  90. }
  91. }
  92. /**
  93. * Controllo online del timbratore.
  94. *
  95. * Il controllo usa lo stesso server applicativo del timbratore: se questa
  96. * chiamata risponde significa che il timbratore riesce a raggiungere il
  97. * server GAP. Non viene scritto alcun log per evitare un record ogni minuto.
  98. *
  99. * @Route("/ajax/timbratore-online", name="timbratore_online", methods={"GET"})
  100. * @IsGranted("ROLE_USER")
  101. */
  102. public function timbratoreOnline(): JsonResponse
  103. {
  104. return new JsonResponse([
  105. 'online' => true,
  106. 'timestamp' => time(),
  107. ], Response::HTTP_OK, [
  108. 'Cache-Control' => 'no-store, no-cache, must-revalidate, max-age=0',
  109. 'Pragma' => 'no-cache',
  110. ]);
  111. }
  112. /**
  113. * @Route("/ajax/timbra", name="timbra_ajax", methods={"POST"})
  114. * @param Request $request
  115. * @return Response
  116. * @throws \Exception
  117. */
  118. public function timbraAjax(Request $request): Response
  119. {
  120. $datas = $request->request->get('data');
  121. $result = new JsonResponse(['Inizializzo il result'], Response::HTTP_BAD_REQUEST);
  122. $user = $this->getUser();
  123. $userCompanyOrder = $user instanceof User ? $user->getCompanyOrder() : null;
  124. $this->timbratoreLogger->log(
  125. 'stamp_request_received',
  126. 'INFO',
  127. 'Richiesta di timbrata ricevuta dal timbratore.',
  128. [
  129. 'data_type' => is_array($datas) && isset($datas['type']) ? $datas['type'] : 'Cantiere',
  130. 'items' => is_array($datas) ? (isset($datas['type']) ? 1 : count($datas)) : 0,
  131. ],
  132. $request,
  133. $user instanceof User ? $user : null,
  134. $userCompanyOrder
  135. );
  136. try {
  137. if (array_key_exists('type', $datas) && $datas['type'] === 'MensaSacchetto') {
  138. $typeForm = StampedMensaSacchettoType::class;
  139. $data = [
  140. 'stamped_in_out[Badge' => $datas['stamped_mensa_sacchetto[Badge'],
  141. 'stamped_in_out[InOut' => 'in',
  142. 'stamped_in_out[date' => DateTimeUtils::getNow(),
  143. ];
  144. $bagType = $datas['stamped_mensa_sacchetto[BagType'] ?? null;
  145. $result = $this->saveTimbrate($data, $typeForm, 'MensaSacchetto', $bagType, $request);
  146. } elseif (array_key_exists('type', $datas)) {
  147. $typeForm = StampedMensaType::class;
  148. $data = [
  149. 'stamped_in_out[Badge' => $datas['stamped_mensa[Badge'],
  150. 'stamped_in_out[InOut' => $datas['stamped_mensa[InOut'],
  151. 'stamped_in_out[date' => DateTimeUtils::getNow(),
  152. ];
  153. $result = $this->saveTimbrate($data, $typeForm, 'Mensa', null, $request);
  154. } else {
  155. $typeForm = StampedInOutType::class;
  156. foreach ($datas as $data) {
  157. $result = $this->saveTimbrate($data, $typeForm, 'Cantiere', null, $request);
  158. }
  159. }
  160. } catch (\Throwable $ex) {
  161. $this->timbratoreLogger->log(
  162. 'stamp_ajax_exception',
  163. 'ERROR',
  164. $ex->getMessage(),
  165. [
  166. 'exception' => get_class($ex),
  167. 'file' => $ex->getFile(),
  168. 'line' => $ex->getLine(),
  169. ],
  170. $request,
  171. $user instanceof User ? $user : null,
  172. $userCompanyOrder
  173. );
  174. $result = new JsonResponse(['Non ci sono dati da processare'], Response::HTTP_BAD_REQUEST);
  175. }
  176. $this->timbratoreLogger->log(
  177. $result->getStatusCode() >= 400 ? 'stamp_request_failed' : 'stamp_request_completed',
  178. $result->getStatusCode() >= 400 ? 'ERROR' : 'INFO',
  179. 'Elaborazione richiesta timbrata completata.',
  180. ['status' => $result->getStatusCode()],
  181. $request,
  182. $user instanceof User ? $user : null,
  183. $userCompanyOrder
  184. );
  185. return $result;
  186. }
  187. /**
  188. * @Route("/ajax/timbratore-log", name="timbratore_log_ajax", methods={"POST"})
  189. * @IsGranted("ROLE_USER")
  190. */
  191. public function timbratoreLogAjax(Request $request): JsonResponse
  192. {
  193. $user = $this->getUser();
  194. $companyOrder = $user instanceof User ? $user->getCompanyOrder() : null;
  195. $payload = $request->request->all();
  196. if (str_starts_with((string) $request->headers->get('Content-Type'), 'application/json')) {
  197. $raw = $request->getContent();
  198. $decoded = json_decode($raw, true);
  199. if (is_array($decoded)) {
  200. $payload = $decoded;
  201. }
  202. }
  203. $event = isset($payload['event']) ? trim((string) $payload['event']) : '';
  204. $level = isset($payload['level']) ? strtoupper(trim((string) $payload['level'])) : 'INFO';
  205. $message = isset($payload['message']) ? (string) $payload['message'] : null;
  206. $clientId = isset($payload['clientId']) ? (string) $payload['clientId'] : null;
  207. $page = isset($payload['page']) ? (string) $payload['page'] : null;
  208. $context = [];
  209. if (isset($payload['context'])) {
  210. if (is_string($payload['context'])) {
  211. $decodedContext = json_decode($payload['context'], true);
  212. if (is_array($decodedContext)) {
  213. $context = $decodedContext;
  214. }
  215. } elseif (is_array($payload['context'])) {
  216. $context = $payload['context'];
  217. }
  218. }
  219. if ($page !== null) {
  220. $context['page'] = substr($page, 0, 1000);
  221. }
  222. if ($event === '') {
  223. return new JsonResponse(['error' => 'Evento mancante'], Response::HTTP_BAD_REQUEST);
  224. }
  225. $this->timbratoreLogger->log(
  226. $event,
  227. $level,
  228. $message,
  229. $context,
  230. $request,
  231. $user instanceof User ? $user : null,
  232. $companyOrder,
  233. $clientId
  234. );
  235. return new JsonResponse(['ok' => true], Response::HTTP_OK);
  236. }
  237. /**
  238. * Salva una nuova timbrata (entrata o uscita) per un operatore.
  239. *
  240. * FUNZIONAMENTO MULTI-CANTIERE - CHIAVE DEL SISTEMA:
  241. * La timbrata viene associata al cantiere del TIMBRATORE ($userCompanyOrder), NON al cantiere di appartenenza dell'operatore.
  242. * Questo permette ad un operatore registrato su un cantiere di timbrare liberamente su qualsiasi altro timbratore.
  243. *
  244. * FLUSSO OPERATIVO:
  245. * 1. Identifica l'operatore tramite il badge scansionato
  246. * 2. Recupera il cantiere del timbratore corrente ($userCompanyOrder = cantiere dove si trova fisicamente il timbratore)
  247. * 3. Verifica duplicati SOLO per la coppia (operatore + cantiere timbratore) nelle ultime N ore
  248. * - N = 12h per le mense (tipi 'Mensa' e 'MensaSacchetto')
  249. * - N = 4h per il timbratore cantiere (tipo 'Cantiere')
  250. * 4. Salva la timbrata associandola al cantiere del timbratore
  251. *
  252. * ESEMPIO PRATICO MULTI-CANTIERE (finestra default = 4h):
  253. * Operatore "Mario Rossi" registrato su Sovere:
  254. * - Scenario A: Timbra su timbratore di Sovere
  255. * → $userCompanyOrder = Sovere
  256. * → Controllo duplicati su (Mario + Sovere)
  257. * → Timbrata salvata con companyOrder = Sovere
  258. *
  259. * - Scenario B: Timbra su timbratore di Brescia
  260. * → $userCompanyOrder = Brescia
  261. * → Controllo duplicati su (Mario + Brescia) - cantiere diverso, nessun problema!
  262. * → Timbrata salvata con companyOrder = Brescia
  263. *
  264. * - Scenario C: Ritorna su timbratore di Sovere dopo 30 minuti
  265. * → $userCompanyOrder = Sovere
  266. * → Controllo duplicati su (Mario + Sovere) - trova timbrata precedente nella finestra!
  267. * → Timbrata BLOCCATA (duplicato)
  268. *
  269. * LOGICA ANTI-DUPLICATO:
  270. * - Per ENTRATE: chiama getStampedWithinHoursEntry($userCompanyOrder, $operatorId, $duplicateCheckHours)
  271. * - Per USCITE: chiama getStampedWithinHoursRelease($userCompanyOrder, $operatorId, $duplicateCheckHours)
  272. * - Se trova duplicato E operatore non è isMulti → blocca la timbrata
  273. * - Se NON trova duplicato OPPURE operatore è isMulti (mensa) → salva la timbrata
  274. *
  275. * GESTIONE ERRORI:
  276. * - Badge non trovato → salva in ErrorStamped
  277. * - Badge non assegnato → salva in ErrorStamped
  278. * - Form non valido → ritorna errore 400
  279. *
  280. * @param array $data Dati form con badge, tipo timbrata (in/out), data
  281. * @param string $typeForm Tipo form (StampedInOutType, StampedMensaType, StampedMensaSacchettoType)
  282. * @param string $t Tipo timbrata ('Cantiere', 'Mensa', 'MensaSacchetto')
  283. * @return JsonResponse Risposta JSON con esito operazione
  284. * @throws \Exception
  285. */
  286. public function saveTimbrate($data, $typeForm, $t, ?string $bagType = null, ?Request $request = null): JsonResponse
  287. {
  288. $form = $this->createForm($typeForm);
  289. $form->submit($data);
  290. if ($form->isSubmitted() && $form->isValid()) {
  291. $actionForm = $data['stamped_in_out[InOut'];
  292. $badgeForm = $data['stamped_in_out[Badge'];
  293. $entityManager = $this->doctrine->getManager();
  294. // PUNTO CHIAVE MULTI-CANTIERE: il companyOrder è del TIMBRATORE, non dell'operatore
  295. // Questo permette di salvare la timbrata sul cantiere dove si trova fisicamente il timbratore
  296. $user = $this->getUser();
  297. $userCompanyOrder = $user instanceof User ? $user->getCompanyOrder() : null;
  298. $this->timbratoreLogger->log(
  299. 'stamp_processing_started',
  300. 'INFO',
  301. 'Inizio elaborazione timbrata.',
  302. [
  303. 'type' => $t,
  304. 'action' => $data['stamped_in_out[InOut'] ?? null,
  305. 'badge' => $data['stamped_in_out[Badge'] ?? null,
  306. 'bagType' => $bagType,
  307. ],
  308. $request,
  309. $user instanceof User ? $user : null,
  310. $userCompanyOrder
  311. );
  312. // Determina se il timbratore corrente è una Mensa (Type = 2)
  313. $isCurrentOrderMensa = ($userCompanyOrder && $userCompanyOrder->getType() == 2);
  314. // Finestra anti-duplicato: 12h per le mense (Mensa e MensaSacchetto), 4h per il timbratore cantiere
  315. $duplicateCheckHours = ($t === 'Mensa' || $t === 'MensaSacchetto') ? 12 : 4;
  316. $assignedBadgeRepository = $entityManager->getRepository(AssignedBadge::class);
  317. $badgeRepository = $entityManager->getRepository(Badge::class);
  318. $stampedRepository = $entityManager->getRepository(Stamped::class);
  319. $errorStamped = new ErrorStamped();
  320. $errorStamped->setCompanyOrder($userCompanyOrder);
  321. $errorStamped->setCodBadge($badgeForm);
  322. $stamped = new Stamped();
  323. /** @var Badge $badge */
  324. $badge = $badgeRepository->findOneBy(['codBadge' => $badgeForm]);
  325. if (!$badge) {
  326. $this->timbratoreLogger->log(
  327. 'badge_not_found',
  328. 'WARNING',
  329. 'Badge non trovato in archivio.',
  330. ['badge' => $badgeForm, 'action' => $actionForm, 'type' => $t],
  331. $request,
  332. $user instanceof User ? $user : null,
  333. $userCompanyOrder
  334. );
  335. } else {
  336. $this->timbratoreLogger->log(
  337. 'badge_found',
  338. 'DEBUG',
  339. 'Badge trovato in archivio.',
  340. ['badgeId' => $badge->getId(), 'badge' => $badgeForm],
  341. $request,
  342. $user instanceof User ? $user : null,
  343. $userCompanyOrder
  344. );
  345. }
  346. $dataIn = DateTimeUtils::getNow();
  347. $dataOut = DateTimeUtils::getNow();
  348. try {
  349. if (isset($data['stamped_in_out[date']) && $data['stamped_in_out[date']) {
  350. if ($actionForm === 'in') {
  351. $dataIn = $t === 'Cantiere' ? new \DateTime($data['stamped_in_out[date']) : DateTimeUtils::getNow();
  352. } elseif ($actionForm === 'out') {
  353. $dataOut = $t === 'Cantiere' ? new \DateTime($data['stamped_in_out[date']) : DateTimeUtils::getNow();
  354. }
  355. }
  356. } catch (\Throwable $e) {
  357. $this->timbratoreLogger->log(
  358. 'stamp_date_error',
  359. 'ERROR',
  360. 'Errore nella data della timbrata.',
  361. [
  362. 'exception' => get_class($e),
  363. 'error' => $e->getMessage(),
  364. 'action' => $actionForm,
  365. ],
  366. $request,
  367. $user instanceof User ? $user : null,
  368. $userCompanyOrder
  369. );
  370. return new JsonResponse(['Non ci sono settate le date di timbrata'], Response::HTTP_BAD_REQUEST);
  371. }
  372. if ($badge) {
  373. $idBadge = $badge->getId();
  374. /** @var AssignedBadge $assignedBadge */
  375. $assignedBadge = $assignedBadgeRepository->findOneBy([
  376. 'badge' => $idBadge,
  377. 'validTo' => null,
  378. ]);
  379. if ($assignedBadge) {
  380. $operatorBadge = $assignedBadge->getOperator()->getId();
  381. $this->timbratoreLogger->log(
  382. 'badge_assigned',
  383. 'INFO',
  384. 'Badge associato a un operatore.',
  385. [
  386. 'badgeId' => $badge->getId(),
  387. 'operatorId' => $operatorBadge,
  388. 'action' => $actionForm,
  389. ],
  390. $request,
  391. $user instanceof User ? $user : null,
  392. $userCompanyOrder
  393. );
  394. $operatorRepository = $entityManager->getRepository(Operator::class);
  395. /** @var Operator $op */
  396. $op = $operatorRepository->findOneBy(['id' => $operatorBadge]);
  397. if ($actionForm === 'out') {
  398. /** @var Stamped $stamped */
  399. $stamped = $stampedRepository->getStamped($userCompanyOrder, $operatorBadge);
  400. if ($stamped) {
  401. // CONTROLLO DUPLICATI CON NUOVA LOGICA MENSA
  402. $stampedRecent = $stampedRepository->getStampedWithinHoursRelease($userCompanyOrder, $operatorBadge, $duplicateCheckHours, $isCurrentOrderMensa);
  403. if (!$stampedRecent) {
  404. $stamped->setReleaseDate($dataOut);
  405. $this->timbratoreLogger->log(
  406. 'stamp_release_updated',
  407. 'INFO',
  408. 'Uscita associata a una timbrata di entrata esistente.',
  409. [
  410. 'stampedId' => $stamped->getId(),
  411. 'operatorId' => $operatorBadge,
  412. 'action' => $actionForm,
  413. ],
  414. $request,
  415. $user instanceof User ? $user : null,
  416. $userCompanyOrder
  417. );
  418. } else {
  419. $this->timbratoreLogger->log(
  420. 'stamp_release_duplicate_ignored',
  421. 'WARNING',
  422. 'Uscita duplicata ignorata perché esiste già una timbrata recente.',
  423. [
  424. 'stampedId' => $stamped->getId(),
  425. 'operatorId' => $operatorBadge,
  426. 'duplicateCheckHours' => $duplicateCheckHours,
  427. ],
  428. $request,
  429. $user instanceof User ? $user : null,
  430. $userCompanyOrder
  431. );
  432. }
  433. } else {
  434. /** @var Stamped $stampedWithinFourHours */
  435. $stampedWithinFourHours = $stampedRepository->getStampedWithinHoursRelease($userCompanyOrder, $operatorBadge, $duplicateCheckHours, $isCurrentOrderMensa);
  436. if ($stampedWithinFourHours and $op->getIsMulti() === false and $this->isMensaRole()) {
  437. $this->timbratoreLogger->log(
  438. 'duplicate_blocked',
  439. 'WARNING',
  440. 'Timbrata bloccata perché già presente nella finestra anti-duplicato.',
  441. [
  442. 'operatorId' => $operatorBadge,
  443. 'action' => $actionForm,
  444. 'duplicateCheckHours' => $duplicateCheckHours,
  445. ],
  446. $request,
  447. $user instanceof User ? $user : null,
  448. $userCompanyOrder
  449. );
  450. return new JsonResponse(['Non puoi mangiare in mensa piu volte nel giro di poco'], Response::HTTP_BAD_REQUEST);
  451. }
  452. // Crea una nuova timbrata se non esiste duplicato nella finestra $duplicateCheckHours OPPURE se operatore è multi per mensa
  453. if (!$stampedWithinFourHours || ($op->getIsMulti() === true && $this->isMensaRole())) {
  454. $stamped = new Stamped();
  455. $stamped->setCompanyOrder($userCompanyOrder);
  456. $stamped->setOperator($op);
  457. $stamped->setReleaseDate($dataOut);
  458. $stamped->setStatus(Stamped::STATUS_ERROR);
  459. } else {
  460. // Se esiste un duplicato e non è multi, non creiamo un nuovo record
  461. $this->timbratoreLogger->log(
  462. 'stamp_duplicate_ignored',
  463. 'WARNING',
  464. 'Nuova timbrata ignorata perché duplicata.',
  465. [
  466. 'operatorId' => $operatorBadge,
  467. 'action' => $actionForm,
  468. 'duplicateCheckHours' => $duplicateCheckHours,
  469. ],
  470. $request,
  471. $user instanceof User ? $user : null,
  472. $userCompanyOrder
  473. );
  474. $stamped = null;
  475. }
  476. }
  477. } elseif ($actionForm === 'in') {
  478. // CONTROLLO DUPLICATI CON NUOVA LOGICA MENSA
  479. /** @var Stamped $stampedWithinFourHours */
  480. $stampedWithinFourHours = $stampedRepository->getStampedWithinHoursEntry($userCompanyOrder, $operatorBadge, $duplicateCheckHours, $isCurrentOrderMensa);
  481. if ($stampedWithinFourHours and $op->getIsMulti() === false and $this->isMensaRole()) {
  482. $this->timbratoreLogger->log(
  483. 'duplicate_blocked',
  484. 'WARNING',
  485. 'Timbrata bloccata perché già presente nella finestra anti-duplicato.',
  486. [
  487. 'operatorId' => $operatorBadge,
  488. 'action' => $actionForm,
  489. 'duplicateCheckHours' => $duplicateCheckHours,
  490. ],
  491. $request,
  492. $user instanceof User ? $user : null,
  493. $userCompanyOrder
  494. );
  495. return new JsonResponse(['Non puoi mangiare in mensa piu volte nel giro di poco'], Response::HTTP_BAD_REQUEST);
  496. }
  497. // Crea una timbrata se non esiste duplicato nella finestra $duplicateCheckHours OPPURE se operatore è multi per mensa
  498. if (!$stampedWithinFourHours || ($op->getIsMulti() === true && $this->isMensaRole())) {
  499. $stamped->setEntryDate($dataIn);
  500. $stamped->setCompanyOrder($userCompanyOrder);
  501. $stamped->setOperator($op);
  502. if ($this->isMensaRole()) {
  503. $stamped->setReleaseDate($dataIn);
  504. }
  505. } else {
  506. // Se esiste un duplicato e non è multi, non creiamo un nuovo record
  507. $this->timbratoreLogger->log(
  508. 'stamp_duplicate_ignored',
  509. 'WARNING',
  510. 'Nuova timbrata ignorata perché duplicata.',
  511. [
  512. 'operatorId' => $operatorBadge,
  513. 'action' => $actionForm,
  514. 'duplicateCheckHours' => $duplicateCheckHours,
  515. ],
  516. $request,
  517. $user instanceof User ? $user : null,
  518. $userCompanyOrder
  519. );
  520. $stamped = null;
  521. }
  522. }
  523. // Salvo solo se stamped non è null (non è un duplicato)
  524. if ($stamped !== null) {
  525. if ($bagType !== null) {
  526. $stamped->setBagType($bagType);
  527. }
  528. $entityManager->persist($stamped);
  529. $entityManager->flush();
  530. $this->timbratoreLogger->log(
  531. 'stamp_saved',
  532. 'INFO',
  533. 'Timbrata salvata correttamente.',
  534. [
  535. 'stampedId' => $stamped->getId(),
  536. 'operatorId' => $stamped->getOperator() ? $stamped->getOperator()->getId() : null,
  537. 'companyOrderId' => $stamped->getCompanyOrder() ? $stamped->getCompanyOrder()->getId() : null,
  538. 'action' => $actionForm,
  539. 'bagType' => $bagType,
  540. ],
  541. $request,
  542. $user instanceof User ? $user : null,
  543. $userCompanyOrder
  544. );
  545. }
  546. } else {
  547. $this->timbratoreLogger->log(
  548. 'badge_not_assigned',
  549. 'WARNING',
  550. 'Badge trovato ma non assegnato a un operatore.',
  551. ['badgeId' => $badge->getId(), 'badge' => $badgeForm, 'action' => $actionForm],
  552. $request,
  553. $user instanceof User ? $user : null,
  554. $userCompanyOrder
  555. );
  556. $errorStamped->setNBadge($badge->getNBadge());
  557. if ($actionForm === 'out') {
  558. $errorStamped->setReleaseDate($dataOut);
  559. } elseif ($actionForm === 'in') {
  560. $errorStamped->setEntryDate($dataIn);
  561. if ($this->isMensaRole()) {
  562. $this->timbratoreLogger->log(
  563. 'operator_not_registered',
  564. 'WARNING',
  565. 'Operatore non registrato/associato al badge.',
  566. ['badge' => $badgeForm, 'action' => $actionForm],
  567. $request,
  568. $user instanceof User ? $user : null,
  569. $userCompanyOrder
  570. );
  571. return new JsonResponse(['Non sei registrato, richiedere al responsabile l inserimento a sistema'], Response::HTTP_BAD_REQUEST);
  572. }
  573. }
  574. $errorStamped->setType(ErrorStamped::TYPE_ERROR_ASSIGNED);
  575. $entityManager->persist($errorStamped);
  576. $entityManager->flush();
  577. $this->timbratoreLogger->log(
  578. 'error_stamped_saved',
  579. 'WARNING',
  580. 'Errore di timbratura registrato in ErrorStamped.',
  581. [
  582. 'errorType' => $errorStamped->getType(),
  583. 'badge' => $badgeForm,
  584. 'action' => $actionForm,
  585. ],
  586. $request,
  587. $user instanceof User ? $user : null,
  588. $userCompanyOrder
  589. );
  590. /*
  591. * Sul timbratore CANTIERE un badge senza assegnazione
  592. * attiva è una timbrata registrata come errore.
  593. * Restituiamo 409 per permettere al frontend di mostrare
  594. * il popup arancione senza trattare l'evento come un
  595. * normale errore tecnico.
  596. */
  597. if ($t === 'Cantiere') {
  598. return new JsonResponse(
  599. [$actionForm === 'out'
  600. ? 'USCITA REGISTRATA COME ERRORE PERCHE’ NON ABILITATO'
  601. : 'ENTRATA REGISTRATA COME ERRORE PERCHE’ NON ABILITATO'],
  602. Response::HTTP_CONFLICT
  603. );
  604. }
  605. }
  606. } else {
  607. if ($actionForm === 'out') {
  608. $errorStamped->setReleaseDate($dataOut);
  609. } elseif ($actionForm === 'in') {
  610. $errorStamped->setEntryDate($dataIn);
  611. if ($this->isMensaRole()) {
  612. $this->timbratoreLogger->log(
  613. 'operator_not_registered',
  614. 'WARNING',
  615. 'Operatore non registrato/associato al badge.',
  616. ['badge' => $badgeForm, 'action' => $actionForm],
  617. $request,
  618. $user instanceof User ? $user : null,
  619. $userCompanyOrder
  620. );
  621. return new JsonResponse(['Non sei registrato, richiedere al responsabile l inserimento a sistema'], Response::HTTP_BAD_REQUEST);
  622. }
  623. }
  624. $errorStamped->setType(ErrorStamped::TYPE_ERROR_ASSIGNED);
  625. $entityManager->persist($errorStamped);
  626. $entityManager->flush();
  627. $this->timbratoreLogger->log(
  628. 'error_stamped_saved',
  629. 'WARNING',
  630. 'Badge non riconosciuto: errore di timbratura registrato.',
  631. [
  632. 'errorType' => $errorStamped->getType(),
  633. 'badge' => $badgeForm,
  634. 'action' => $actionForm,
  635. ],
  636. $request,
  637. $user instanceof User ? $user : null,
  638. $userCompanyOrder
  639. );
  640. if ($t === 'Cantiere') {
  641. return new JsonResponse(
  642. [$actionForm === 'out'
  643. ? 'USCITA REGISTRATA COME ERRORE PERCHE’ NON ABILITATO'
  644. : 'ENTRATA REGISTRATA COME ERRORE PERCHE’ NON ABILITATO'],
  645. Response::HTTP_CONFLICT
  646. );
  647. }
  648. }
  649. } else {
  650. $this->timbratoreLogger->log(
  651. 'stamp_form_invalid',
  652. 'ERROR',
  653. 'Il form della timbrata non è valido.',
  654. [
  655. 'type' => $t,
  656. 'formErrors' => (string) $form->getErrors(true),
  657. ],
  658. $request,
  659. $this->getUser() instanceof User ? $this->getUser() : null,
  660. $this->getUser() instanceof User ? $this->getUser()->getCompanyOrder() : null
  661. );
  662. return new JsonResponse(['Il form non è valido contattare l amministratore'], Response::HTTP_BAD_REQUEST);
  663. }
  664. if ($stamped && $badge && $this->isMensaRole() && $stamped->getId()) {
  665. $responseData = [
  666. 'ok',
  667. 'stampedId' => $stamped->getId(),
  668. 'badgeStamped' => $badge->getNBadge(),
  669. 'codOperator' => $stamped->getOperator()->getId(),
  670. 'dataStamped' => $stamped->getCreatedAt() ? $stamped->getCreatedAt()->format('d-m-Y H:i:s') : DateTimeUtils::getNow()->format('d-m-Y H:i:s')
  671. ];
  672. if ($stamped->getBagType() !== null) {
  673. $responseData['bagType'] = $stamped->getBagType();
  674. $responseData['bagTypeLabel'] = Stamped::BAG_TYPE_LABELS[$stamped->getBagType()] ?? $stamped->getBagType();
  675. }
  676. return new JsonResponse($responseData, Response::HTTP_OK);
  677. } else {
  678. return new JsonResponse(['ok'], Response::HTTP_OK);
  679. }
  680. }
  681. /**
  682. * @Route("/delete-multiple-ids", name="delete_multiple_ids", methods={"DELETE"})
  683. * @IsGranted ("ROLE_ADMIN")
  684. *
  685. * @param Request $request
  686. * @return Response
  687. */
  688. public function deleteAjax(Request $request): Response
  689. {
  690. if (!$request->request->get('type')) {
  691. return new JsonResponse(['Error, missing type parameter'], Response::HTTP_BAD_REQUEST);
  692. }
  693. $ids = $request->request->get('ids');
  694. if (!$ids || !is_array($request->request->get('ids'))) {
  695. return new JsonResponse(['Error ids not valid'], Response::HTTP_BAD_REQUEST);
  696. }
  697. switch ($request->request->get('type')) {
  698. case 'stamped':
  699. $result = $this->deleteMultipleIds($ids, Stamped::class);
  700. break;
  701. default:
  702. return new JsonResponse(['Error, this type is not registered'], Response::HTTP_BAD_REQUEST);
  703. }
  704. return $result ? new JsonResponse(['ok'], Response::HTTP_OK) : new JsonResponse(['Error while deleting records'], Response::HTTP_BAD_REQUEST);
  705. }
  706. private function isMensaRole(): bool
  707. {
  708. return $this->isGranted("ROLE_MENSA") || $this->isGranted("ROLE_MENSA_ASPORTO");
  709. }
  710. private function deleteMultipleIds(array $ids, $className)
  711. {
  712. try {
  713. $qb = $this->doctrine->getManager()->createQueryBuilder();
  714. $qb->delete($className, 'e')
  715. ->andWhere('e.id IN (:id)')
  716. ->setParameter('id', $ids);
  717. return $qb->getQuery()->execute();
  718. } catch (Exception $e) {
  719. return false;
  720. }
  721. }
  722. }