src/Controller/GuardianController.php line 471

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Child;
  4. use App\Entity\ChildVacation;
  5. use App\Entity\EarlyPayment;
  6. use App\Entity\Guardian;
  7. use App\Entity\GuardianRegister\GuardianRegister;
  8. use App\Entity\Lesson;
  9. use App\Entity\LessonDeleted;
  10. use App\Entity\Payment;
  11. use App\Entity\PriceChange;
  12. use App\Entity\Teacher;
  13. use App\Entity\PaymentNotificationLog;
  14. use App\Form\ChildType;
  15. use App\Form\GuardianType;
  16. use App\Message\RecalculateLessonMessage;
  17. use App\Message\SendMailMessage;
  18. use App\Message\SendSMSMessage;
  19. use App\Repository\AdminRepository;
  20. use App\Repository\PaymentNotificationLogRepository;
  21. use App\Repository\AuthLogRepository;
  22. use App\Repository\ChildRepository;
  23. use App\Repository\ConfigurationRepository;
  24. use App\Repository\EarlyPaymentRepository;
  25. use App\Repository\EmailLogRepository;
  26. use App\Repository\GuardianActionLogRepository;
  27. use App\Repository\GuardianContractRepository;
  28. use App\Repository\GuardianRepository;
  29. use App\Repository\LessonDeletedRepository;
  30. use App\Repository\LessonRepository;
  31. use App\Repository\MonthlyGoalRepository;
  32. use App\Repository\PaymentRepository;
  33. use App\Repository\PriceChangeRepository;
  34. use App\Repository\ReminderConfigurationRepository;
  35. use App\Repository\TeacherPaymentLogRepository;
  36. use App\Repository\TeacherRepository;
  37. use App\Service\ChildPreferenceService;
  38. use App\Service\CredentialsService;
  39. use App\Service\EarlyPaymentService;
  40. use App\Service\GuardianEarlyPaymentConversionService;
  41. use App\Service\GuardianLogService;
  42. use App\Service\LessonDeletionService;
  43. use App\Service\NotificationService;
  44. use App\Service\PaymentService;
  45. use App\Service\PriceIncreaseService;
  46. use App\Utils\HelperUtils;
  47. use App\Utils\RestUtils;
  48. use Doctrine\DBAL\Exception;
  49. use Doctrine\ORM\EntityManagerInterface;
  50. use Doctrine\ORM\QueryBuilder;
  51. use Psr\Log\LoggerInterface;
  52. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
  53. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  54. use Symfony\Component\HttpFoundation\JsonResponse;
  55. use Symfony\Component\HttpFoundation\Request;
  56. use Symfony\Component\HttpFoundation\Response;
  57. use Symfony\Component\Mailer\MailerInterface;
  58. use Symfony\Component\Messenger\MessageBusInterface;
  59. use Symfony\Component\Mime\Address;
  60. use Symfony\Component\Mime\Email;
  61. use Symfony\Component\Mime\Message;
  62. use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  63. use Symfony\Component\Routing\Annotation\Route;
  64. use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
  65. use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
  66. use Symfony\Contracts\Cache\CacheInterface;
  67. use Symfony\Contracts\Cache\ItemInterface;
  68. use Symfony\Contracts\Translation\TranslatorInterface;
  69. /**
  70. * @Route("/guardians")
  71. */
  72. class GuardianController extends AbstractController
  73. {
  74. public const FILE_NAME_DISCOUNT_CODE_MARKETING = "guardian-discount-code-marketing.csv";
  75. private const EARLY_PAYMENT_INVOICE_VISIBILITY_DAYS = 5;
  76. private const PAYMENT_SOURCE_EARLY = 'EARLY';
  77. private const PAYMENT_SOURCE_REGULAR = 'REGULAR';
  78. private MessageBusInterface $messageBus;
  79. private UserPasswordHasherInterface $userPasswordHasher;
  80. public function __construct(UserPasswordHasherInterface $userPasswordHasher, MessageBusInterface $messageBus)
  81. {
  82. $this->messageBus = $messageBus;
  83. $this->userPasswordHasher = $userPasswordHasher;
  84. }
  85. /**
  86. * @Route("/", name="guardian_index", methods={"GET"})
  87. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  88. */
  89. public function index(GuardianRepository $guardianRepository, EntityManagerInterface $entityManager, MonthlyGoalRepository $monthlyGoalRepository, AdminRepository $adminRepository, CacheInterface $cache): Response
  90. {
  91. $dashboardData = $cache->get('guardian_index_dashboard_v2', function (ItemInterface $item) use (
  92. $guardianRepository,
  93. $entityManager,
  94. $monthlyGoalRepository,
  95. $adminRepository
  96. ) {
  97. $item->expiresAfter(20 * 60);
  98. $trackedStatuses = [
  99. Guardian::STATUS_PREPARING['value'],
  100. Guardian::STATUS_SENT['value'],
  101. Guardian::STATUS_CONFIRMED['value'],
  102. Guardian::STATUS_CANCELLED['value'],
  103. Guardian::STATUS_LEFT['value'],
  104. Guardian::STATUS_OVERPAID['value'],
  105. ];
  106. $countsByStatus = $guardianRepository->getStatusCounts($trackedStatuses);
  107. $preparingCount = $countsByStatus[Guardian::STATUS_PREPARING['value']] ?? 0;
  108. $sentCount = $countsByStatus[Guardian::STATUS_SENT['value']] ?? 0;
  109. $confirmedCount = $countsByStatus[Guardian::STATUS_CONFIRMED['value']] ?? 0;
  110. $cancelledCount = $countsByStatus[Guardian::STATUS_CANCELLED['value']] ?? 0;
  111. $leftCount = $countsByStatus[Guardian::STATUS_LEFT['value']] ?? 0;
  112. $overpaidCount = $countsByStatus[Guardian::STATUS_OVERPAID['value']] ?? 0;
  113. $RAW_QUERY = "SELECT SUM(TIMESTAMPDIFF(MINUTE,child_preference_time.start_time, child_preference_time.end_time)) as suma
  114. FROM guardian
  115. LEFT JOIN child on child.guardian_id = guardian.id
  116. LEFT JOIN child_preference on child_preference.child_id = child.id
  117. LEFT JOIN child_preference_time on child_preference_time.child_preference_id = child_preference.id
  118. where child.lessons_end is null AND
  119. guardian.status IN ('CONFIRMED') AND
  120. child_preference.teacher_id is not null AND
  121. child_preference.end_time is null";
  122. $statement = $entityManager->getConnection()->prepare($RAW_QUERY);
  123. $resultSet = $statement->executeQuery();
  124. $activeClientsMins = $resultSet->fetchAllAssociative();
  125. $activeClientsMins = reset($activeClientsMins);
  126. $activeClientsMins= $activeClientsMins['suma'];
  127. $RAW_QUERY = "SELECT SUM(TIMESTAMPDIFF(MINUTE,child_preference_time.start_time, child_preference_time.end_time)) as suma
  128. FROM guardian
  129. LEFT JOIN child on child.guardian_id = guardian.id
  130. LEFT JOIN child_preference on child_preference.child_id = child.id
  131. LEFT JOIN child_preference_time on child_preference_time.child_preference_id = child_preference.id
  132. where child.lessons_end is null AND
  133. guardian.status IN ('CONFIRMED') AND
  134. COALESCE(guardian.autumn2024back, 0) <> 1 AND
  135. child_preference.teacher_id is not null AND
  136. child_preference.end_time is null";
  137. $statement = $entityManager->getConnection()->prepare($RAW_QUERY);
  138. $resultSet = $statement->executeQuery();
  139. $activeClientsMinsExcludingReturning = $resultSet->fetchAllAssociative();
  140. $activeClientsMinsExcludingReturning = reset($activeClientsMinsExcludingReturning);
  141. $activeClientsMinsExcludingReturning = $activeClientsMinsExcludingReturning['suma'];
  142. $RAW_QUERY = "SELECT SUM(TIMESTAMPDIFF(MINUTE,child_preference_time.start_time, child_preference_time.end_time)) as suma
  143. FROM guardian
  144. LEFT JOIN child on child.guardian_id = guardian.id
  145. LEFT JOIN child_preference on child_preference.child_id = child.id
  146. LEFT JOIN child_preference_time on child_preference_time.child_preference_id = child_preference.id
  147. where child.lessons_end is null AND
  148. guardian.status IN ('CONFIRMED') AND
  149. guardian.summer IN ('YES_35', 'YES_20') AND
  150. child_preference.teacher_id is not null AND
  151. child_preference.end_time is null";
  152. $statement = $entityManager->getConnection()->prepare($RAW_QUERY);
  153. $resultSet = $statement->executeQuery();
  154. $summerClientsMins = $resultSet->fetchAllAssociative();
  155. $summerClientsMins = reset($summerClientsMins);
  156. $summerClientsMins= $summerClientsMins['suma'];
  157. $RAW_QUERY = "SELECT COUNT(DISTINCT(guardian.id)) as suma
  158. FROM guardian
  159. LEFT JOIN child on child.guardian_id = guardian.id
  160. LEFT JOIN child_preference on child_preference.child_id = child.id
  161. LEFT JOIN child_preference_time on child_preference_time.child_preference_id = child_preference.id
  162. where child.lessons_end is null AND
  163. guardian.status IN ('CONFIRMED','PREPARING') AND
  164. child_preference.teacher_id is not null AND
  165. TIMESTAMPDIFF(MINUTE,child_preference_time.start_time, child_preference_time.end_time) > 0";
  166. $statement = $entityManager->getConnection()->prepare($RAW_QUERY);
  167. $resultSet = $statement->executeQuery();
  168. $clientsCount = $resultSet->fetchAllAssociative();
  169. $clientsCount = reset($clientsCount);
  170. $clientsCount= $clientsCount['suma'];
  171. $dafeFor = date('Y-m');
  172. $query = $monthlyGoalRepository->createQueryBuilder('mg');
  173. $monthlyGoal = $query
  174. ->andWhere('mg.dateFor like :date')
  175. ->setParameter('date', $dafeFor.'%')
  176. ->setMaxResults(1)
  177. ->getQuery()
  178. ->getOneOrNullResult();
  179. $currentAmount = 0;
  180. $goalAmount = 0;
  181. $goalPercentAchieved = 0;
  182. $RAW_QUERY = "SELECT SUM(amount) as suma
  183. FROM montonio_log
  184. where created_at like '{$dafeFor}%' and status = 'PAID'";
  185. $statement = $entityManager->getConnection()->prepare($RAW_QUERY);
  186. $resultSet = $statement->executeQuery();
  187. $currentAmount = $resultSet->fetchAllAssociative();
  188. $currentAmount = reset($currentAmount);
  189. $currentAmount = $currentAmount['suma'];
  190. if($monthlyGoal)
  191. {
  192. $goalAmount = $monthlyGoal->getGoalAmount();
  193. }
  194. if($monthlyGoal && $currentAmount)
  195. {
  196. $goalPercentAchieved = round(($currentAmount / $goalAmount * 100),2);
  197. }
  198. if($goalAmount)
  199. {
  200. $goalAmount = number_format($goalAmount, 2, '.', ' ');
  201. }
  202. if($currentAmount)
  203. {
  204. $currentAmount = number_format($currentAmount, 2, '.', ' ');
  205. }
  206. $notPaidStatus = Payment::STATUS_NOTPAID;
  207. $RAW_QUERY_TOTAL_AMOUNT_MONTH = "SELECT SUM(payment.amount) as totalAmount from guardian left join payment on payment.guardian_id = guardian.id where payment.status = '{$notPaidStatus}' and payment.amount > 0 and payment.date_for like '{$dafeFor}%'";
  208. $statement = $entityManager->getConnection()->prepare($RAW_QUERY_TOTAL_AMOUNT_MONTH);
  209. $resultSet = $statement->executeQuery();
  210. $currentDebt = $resultSet->fetchAllAssociative();
  211. $currentDebt = reset($currentDebt);
  212. $currentDebt = $currentDebt['totalAmount'];
  213. if($currentDebt)
  214. {
  215. $currentDebt = number_format($currentDebt, 2, '.', ' ');
  216. }
  217. $admins = $adminRepository->findAll();
  218. $adminsToFront = [];
  219. foreach ($admins as $admin)
  220. {
  221. if(in_array($admin->getId(),[2,3,5]))
  222. {
  223. continue;
  224. }
  225. $adminsToFront[$admin->getFullname()] = $admin->getFullname();
  226. }
  227. $dateStart = new \DateTime();
  228. $dateStart->modify('monday this week');
  229. $dateStart->setTime(0, 0, 0);
  230. $dateEnd = new \DateTime();
  231. $dateEnd->modify('sunday this week');
  232. $dateEnd->setTime(23, 59, 59);
  233. $currentWeekSalesMinutes = ["weekNumber" => $dateStart->format('W'), "minutes" => $this->getSalesMinutes($dateStart, $dateEnd, $entityManager)];
  234. $dateStart = new \DateTime();
  235. $dateStart->modify('monday this week');
  236. $dateStart->modify('-1 week');
  237. $dateStart->setTime(0, 0, 0);
  238. $dateEnd = new \DateTime();
  239. $dateEnd->modify('sunday this week');
  240. $dateEnd->modify('-1 week');
  241. $dateEnd->setTime(23, 59, 59);
  242. $lastWeekSalesMinutes = ["weekNumber" => $dateStart->format('W'), "minutes" => $this->getSalesMinutes($dateStart, $dateEnd, $entityManager)];
  243. $dateStart = new \DateTime();
  244. $dateStart->modify('monday this week');
  245. $dateStart->modify('-2 week');
  246. $dateStart->setTime(0, 0, 0);
  247. $dateEnd = new \DateTime();
  248. $dateEnd->modify('sunday this week');
  249. $dateEnd->modify('-2 week');
  250. $dateEnd->setTime(23, 59, 59);
  251. $twoWeekOldSalesMinutes = ["weekNumber" => $dateStart->format('W'), "minutes" => $this->getSalesMinutes($dateStart, $dateEnd, $entityManager)];
  252. $salesMinutes = [$currentWeekSalesMinutes,$lastWeekSalesMinutes,$twoWeekOldSalesMinutes];
  253. $statuses = [
  254. Lesson::STATUS_FREE['value'],
  255. Lesson::STATUS_REGULAR['value'],
  256. Lesson::STATUS_REGULAR_MOVED['value'],
  257. Lesson::STATUS_WAITING_PAYMENT['value'],
  258. Lesson::STATUS_GIFT['value'],
  259. Lesson::STATUS_CHILD_UNINFORMED_MISSED['value'],
  260. Lesson::STATUS_ADDITIONAL['value'],
  261. ];
  262. $previousMonthLessonsMinutes = $this->getPreviousMonthLessonsMinutes($entityManager, $statuses);
  263. $currentMonthLessonsMinutes = $this->getCurrentMonthLessonsMinutes($entityManager, $statuses);
  264. return [
  265. 'preparingCount' => $preparingCount,
  266. 'sentCount' => $sentCount,
  267. 'confirmedCount' => $confirmedCount,
  268. 'cancelledCount' => $cancelledCount,
  269. 'leftCount' => $leftCount,
  270. 'overpaidCount' => $overpaidCount,
  271. 'clientsCount' => $clientsCount,
  272. 'activeClientsMins' => $activeClientsMins,
  273. 'activeClientsMinsExcludingReturning' => $activeClientsMinsExcludingReturning,
  274. 'summerClientsMins' => $summerClientsMins,
  275. 'currentAmount' => $currentAmount,
  276. 'goalAmount' => $goalAmount,
  277. 'goalPercentAchieved' => $goalPercentAchieved,
  278. 'currentDebt' => $currentDebt,
  279. 'adminsToFront' => $adminsToFront,
  280. 'salesMinutes' => $salesMinutes,
  281. 'previousMonthLessonsMinutes' => $previousMonthLessonsMinutes,
  282. 'currrentMonthLessonsMinutes' => $currentMonthLessonsMinutes,
  283. ];
  284. }
  285. );
  286. return $this->render('guardian/index.html.twig', $dashboardData);
  287. }
  288. /**
  289. * @Route("/search", name="guardian_search", methods={"GET", "POST"})
  290. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  291. */
  292. public function guardianSearch(Request $request, EntityManagerInterface $entityManager): Response
  293. {
  294. $term = $request->query->get('term');
  295. if ($term) {
  296. $RAW_QUERY = "SELECT id, concat(fullname, ' ', email) as text FROM guardian
  297. where guardian.fullname like '%{$term}%' OR
  298. guardian.email like '%{$term}%';";
  299. $statement = $entityManager->getConnection()->prepare($RAW_QUERY);
  300. $resultSet = $statement->executeQuery();
  301. $guardians = $resultSet->fetchAllAssociative();
  302. } else {
  303. $guardians = [];
  304. }
  305. return new JsonResponse([
  306. 'result' => $guardians,
  307. 'pagination' => ['more' => false]
  308. ]);
  309. }
  310. /**
  311. * @Route("/not_paid", name="guardian_not_paid_index", methods={"GET"})
  312. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  313. */
  314. public function notPaidIndex(GuardianRepository $guardianRepository): Response
  315. {
  316. // $preparingCount = count($guardianRepository->findBy(['status' => Guardian::STATUS_PREPARING['value']]));
  317. // $sentCount = count($guardianRepository->findBy(['status' => Guardian::STATUS_SENT['value']]));
  318. // $confirmedCount = count($guardianRepository->findBy(['status' => Guardian::STATUS_CONFIRMED['value']]));
  319. // $cancelledCount = count($guardianRepository->findBy(['status' => Guardian::STATUS_CANCELLED['value']]));
  320. // $leftCount = count($guardianRepository->findBy(['status' => Guardian::STATUS_LEFT['value']]));
  321. // $overpaidCount = count($guardianRepository->findBy(['status' => Guardian::STATUS_OVERPAID['value']]));
  322. return $this->render('not_paid_customers/not_paid_customers_list.html.twig', [
  323. // 'preparingCount' => $preparingCount,
  324. // 'sentCount' => $sentCount,
  325. // 'confirmedCount' => $confirmedCount,
  326. // 'cancelledCount' => $cancelledCount,
  327. // 'leftCount' => $leftCount,
  328. // 'overpaidCount' => $overpaidCount,
  329. ]);
  330. }
  331. /**
  332. * @Route("/early_payment_not_paid", name="early_payment_not_paid", methods={"GET"})
  333. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  334. */
  335. public function early_payment_not_paid(): Response
  336. {
  337. return $this->render('not_paid_customers/early_payment_not_paid.html.twig', [
  338. ]);
  339. }
  340. /**
  341. * @Route("/missed", name="guardian_missed_index", methods={"GET"})
  342. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  343. */
  344. public function missedIndex(GuardianRepository $guardianRepository): Response
  345. {
  346. return $this->render('uninformed_missed_customers/uninformed_missed_customers_list.html.twig');
  347. }
  348. /**
  349. * @Route("/emails", name="guardian_emails", methods={"GET", "POST"})
  350. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  351. */
  352. public function sendEmail(Request $request, MessageBusInterface $messageBus, GuardianRepository $guardianRepository): Response
  353. {
  354. $guardianIds = $request->query->get('parentIds');
  355. $guardianIds = explode(',', $guardianIds);
  356. $text = $this->renderView("email/invoice_reminder.html.twig", []);
  357. foreach ($guardianIds as $guardianId) {
  358. $guardian = $guardianRepository->find($guardianId);
  359. $this->sendReminderEmailToGuardian($guardian, $text, $messageBus);
  360. }
  361. if (!$guardianIds) {
  362. $guardians = $guardianRepository->findAll();
  363. foreach ($guardians as $guardian) {
  364. $this->sendReminderEmailToGuardian($guardian, $text, $messageBus);
  365. }
  366. }
  367. return new JsonResponse();
  368. }
  369. public function sendReminderEmailToGuardian($guardian, $text, $messageBus)
  370. {
  371. if ($guardian) {
  372. $send = false;
  373. foreach ($guardian->getPayments() as $payment) {
  374. if ($payment->getStatus() == Payment::STATUS_NOTPAID) {
  375. $send = true;
  376. }
  377. }
  378. if ($send) {
  379. $messageBus->dispatch(new SendMailMessage($guardian->getEmail(), 'Prašome apmokėti sąskaitą!', $text));
  380. }
  381. }
  382. }
  383. function getSalesMinutes($dateStart, $dateEnd, $entityManager)
  384. {
  385. $RAW_QUERY_SUMMER_MINS_BY_ADMIN = "SELECT SUM(guardian_register_child_preference.amount * guardian_register_child_preference.duration) as diff, admin.fullname
  386. FROM `guardian_register_child_preference`
  387. LEFT JOIN guardian_register_child on guardian_register_child.id = guardian_register_child_preference.guardian_register_child_id
  388. LEFT JOIN guardian_register on guardian_register_child.guardian_register_id = guardian_register.id
  389. LEFT JOIN guardian on guardian.id = guardian_register.guardian_id
  390. LEFT JOIN admin on admin.id = guardian_register.sales_person_id
  391. WHERE guardian_register.contract_signed_at > '{$dateStart->format('Y-m-d H:i:s')}' AND guardian_register.contract_signed_at < '{$dateEnd->format('Y-m-d H:i:s')}'
  392. group by guardian_register.sales_person_id
  393. ";
  394. $statement = $entityManager->getConnection()->prepare($RAW_QUERY_SUMMER_MINS_BY_ADMIN);
  395. $resultSet = $statement->executeQuery();
  396. return $resultSet->fetchAllAssociative();
  397. }
  398. /**
  399. * @Route("/ajax", name="ajax_guardian", methods={"POST"})
  400. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  401. * @param Request $request
  402. * @param EntityManagerInterface $entityManager
  403. * @return Response
  404. * @throws \Doctrine\ORM\NoResultException
  405. * @throws \Doctrine\ORM\NonUniqueResultException
  406. * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
  407. */
  408. public function ajax(Request $request, GuardianRepository $guardianRepository, PaymentRepository $paymentRepository): Response
  409. {
  410. $objectRepository = $guardianRepository;
  411. $page = $request->request->get('page');
  412. $perPage = $request->request->get('perPage');
  413. $page = ($page && $page > 1) ? $page : 1;
  414. $perPage = ($perPage && $perPage > 0) ? $perPage : 10; // should change in the future
  415. $searchFilters = $request->request->get('filters', []);
  416. $sorters = $request->request->get('sorters', []);
  417. $query = $objectRepository->createQueryBuilder('a');
  418. if ($searchFilters) {
  419. foreach ($searchFilters as $filter) {
  420. if (isset($filter['value'])) {
  421. $searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
  422. if($filter['field'] == 'email') {
  423. $query->andWhere("REPLACE(a.email, '.', '') LIKE :email")
  424. ->setParameter('email', '%' . str_replace('.', '', $searchValue) . '%');
  425. if (!in_array('c', $query->getAllAliases())) {
  426. $query->leftJoin('a.children', 'c');
  427. }
  428. $query->orWhere("REPLACE(c.email, '.', '') LIKE :email");
  429. }
  430. elseif($filter['field'] == 'phoneNumber')
  431. {
  432. if(!in_array('c',$query->getAllAliases()))
  433. {
  434. $query->leftJoin('a.children', 'c');
  435. }
  436. $searchValue = preg_replace('/\D+/', '', (string) $searchValue);
  437. $searchValue = substr($searchValue, -8);
  438. $query->orWhere("c.{$filter['field']} {$filter['type']} :{$filter['field']}");
  439. if ($filter['type'] == "like") {
  440. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  441. ->setParameter("{$filter['field']}", "%{$searchValue}%");
  442. } else {
  443. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  444. ->setParameter("{$filter['field']}", "{$searchValue}");
  445. }
  446. }
  447. elseif($filter['field'] == 'flagEarlyPayment')
  448. {
  449. if($searchValue == 1) {
  450. $query->andWhere("a.flagEarlyPayment = 1");
  451. }
  452. else
  453. {
  454. $query->andWhere("a.flagEarlyPayment = 0 OR a.flagEarlyPayment IS NULL");
  455. }
  456. }
  457. else
  458. {
  459. // if ($filter['type'] == "like") {
  460. // $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  461. // ->setParameter("{$filter['field']}", "%{$searchValue}%");
  462. // } else {
  463. // $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  464. // ->setParameter("{$filter['field']}", "{$searchValue}");
  465. // }
  466. $relation = explode('.', $filter['field']);
  467. if (count($relation) == 1) {
  468. $searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
  469. if ($filter['type'] == "like") {
  470. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  471. ->setParameter("{$filter['field']}", "%{$searchValue}%");
  472. } else {
  473. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  474. ->setParameter("{$filter['field']}", "{$searchValue}");
  475. }
  476. } elseif (count($relation) == 2) {
  477. $searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
  478. $field = $relation[1];
  479. $relation = $relation[0];
  480. if ($filter['type'] == "like") {
  481. if (!in_array($relation, $query->getAllAliases())) {
  482. $query
  483. ->leftJoin("a." . $relation, $relation);
  484. }
  485. $query
  486. ->andWhere("{$relation}.{$field} {$filter['type']} :{$field}")
  487. ->setParameter("{$field}", "%{$searchValue}%");
  488. } else {
  489. if (!in_array($relation, $query->getAllAliases())) {
  490. $query
  491. ->leftJoin("a." . $relation, $relation);
  492. }
  493. $query
  494. ->andWhere("{$relation}.{$field} {$filter['type']} :{$field}")
  495. ->setParameter("{$field}", "{$searchValue}");
  496. }
  497. } elseif (count($relation) == 3) {
  498. $searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
  499. $relationOne = $relation[0];
  500. $relationTwo = $relation[1];
  501. $field = $relation[2];
  502. if (!in_array($relationOne, $query->getAllAliases())) {
  503. $query
  504. ->leftJoin("a." . $relationOne, $relationOne);
  505. }
  506. if (!in_array($relationTwo, $query->getAllAliases())) {
  507. $query
  508. ->leftJoin("{$relationOne}." . $relationTwo, $relationTwo);
  509. }
  510. if ($filter['type'] == "like") {
  511. $query
  512. ->andWhere("{$relationTwo}.{$field} {$filter['type']} :{$field}")
  513. ->setParameter("{$field}", "%{$searchValue}%");
  514. } else {
  515. $query
  516. ->andWhere("{$relationTwo}.{$field} {$filter['type']} :{$field}")
  517. ->setParameter("{$field}", "{$searchValue}");
  518. }
  519. }
  520. }
  521. }
  522. }
  523. }
  524. if ($sorters) {
  525. foreach ($sorters as $sorter) {
  526. $query->addOrderBy('a.' . $sorter['field'], $sorter['dir']);
  527. }
  528. }
  529. $count = RestUtils::getQueryCount(clone $query);
  530. $pagesCount = ceil($count / $perPage);
  531. $guardiansObj = RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage);
  532. $guardians = [];
  533. /**
  534. * @var $guardianObject Guardian
  535. */
  536. foreach ($guardiansObj as $guardianObject) {
  537. $payment = $paymentRepository->findOneBy(['guardian' => $guardianObject->getId()], ['dateFor' => 'desc']);
  538. if ($payment) {
  539. if ($payment->getStatus() == Payment::STATUS_PAID) {
  540. $lastMonthValue = $payment->getAmount();
  541. } else {
  542. $lastMonthValue = $payment->getAmount() * -1;
  543. }
  544. } else {
  545. $lastMonthValue = 0;
  546. }
  547. $guardian = [];
  548. $guardian['id'] = $guardianObject->getId();
  549. $guardian['dateAdd'] = $guardianObject->getDateAdd()->format("Y-m-d");
  550. $guardian['fullname'] = "{$guardianObject->getFullname()}";
  551. $guardian['email'] = $guardianObject->getEmail();
  552. $guardian['phoneNumber'] = $guardianObject->getPhoneNumber();
  553. $guardian['lastMonthValue'] = $lastMonthValue;
  554. $guardian['balanceStatus'] = $guardianObject->getBalanceStatus();
  555. $guardian['status'] = $guardianObject->getStatus();
  556. $guardian['summer'] = $guardianObject->getSummer();
  557. $guardian['summerAdditional'] = $guardianObject->getSummerAdditional();
  558. $guardian['progress'] = $guardianObject->isProgress();
  559. $guardian['autumn2024back'] = $guardianObject->isAutumn2024back();
  560. $guardian['flagEarlyPayment'] = $guardianObject->isFlagEarlyPayment();
  561. $guardian['newPriceEmailSent'] = $guardianObject->isNewPriceEmailSent();
  562. $guardian['newEmailOpened'] = $guardianObject->getNewEmailOpened()?$guardianObject->getNewEmailOpened()->format('Y-m-d H:i'):'';
  563. $guardian['newEmailSubmitted'] = $guardianObject->isNewEmailSubmitted();
  564. $guardian['guardianRegister']['salesPerson']['fullname'] = null;
  565. if($guardianObject->getGuardianRegister() && $guardianObject->getGuardianRegister()->getSalesPerson()) {
  566. $guardian['guardianRegister']['salesPerson']['fullname'] = $guardianObject->getGuardianRegister()->getSalesPerson()->getFullname();
  567. }
  568. foreach ($guardianObject->getChildren() as $child) {
  569. $amount = 0;
  570. $disciplines = "";
  571. $assignedTeachers = "";
  572. foreach ($child->getChildPreferences() as $preference) {
  573. $endTime = $preference->getEndTime();
  574. if ($endTime && $endTime <= new \DateTime())
  575. {
  576. continue;
  577. }
  578. $amount += $preference->getAmount();
  579. $discipline = $preference->getDiscipline();
  580. if ($preference->getDiscipline()) {
  581. if (!$disciplines) {
  582. $disciplines = $preference->getDiscipline()->getName();
  583. } else {
  584. $disciplines .= ", " . $preference->getDiscipline()->getName();
  585. }
  586. }
  587. if ($preference->getTeacher()) {
  588. if (!$assignedTeachers) {
  589. $names = explode(' ', $preference->getTeacher()->getFullname(true));
  590. $name = $names[0];
  591. if (isset($names[1]) && $names[1]) {
  592. $lastname = $names[1];
  593. } else {
  594. $lastname = "";
  595. }
  596. $name = mb_substr($name, 0, 1, 'UTF-8');
  597. $assignedTeachers = "{$name}. {$lastname}";
  598. } else {
  599. $names = explode(' ', $preference->getTeacher()->getFullname(true));
  600. $name = $names[0];
  601. $lastname = isset($names[1]) ? $names[1] : '';
  602. $assignedTeachers .= ", " . mb_substr($name, 0, 1, 'UTF-8') . '. ' . $lastname;
  603. }
  604. }
  605. }
  606. $guardian['children'][] = [
  607. 'id' => $child->getId(),
  608. 'fullname' => "{$child->getFullname()}",
  609. 'class' => $child->getClass(),
  610. 'lessonAmount' => $amount,
  611. 'disciplines' => $disciplines,
  612. 'teachers' => $assignedTeachers,
  613. ];
  614. }
  615. $guardians[] = $guardian;
  616. }
  617. // $guardians = $this->get('serializer')->normalize(RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage), null, ['groups' => "GuardianList"]);
  618. // foreach ($guardians as $guardian){
  619. // dd($guardian);
  620. // }
  621. return new JsonResponse([
  622. "last_page" => $pagesCount,
  623. "data" => $guardians
  624. ]);
  625. }
  626. /**
  627. * @Route("/not_paid/ajax", name="not_paid_ajax_guardian", methods={"POST"})
  628. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  629. * @param Request $request
  630. * @param EntityManagerInterface $entityManager
  631. * @return Response
  632. * @throws \Doctrine\ORM\NoResultException
  633. * @throws \Doctrine\ORM\NonUniqueResultException
  634. * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
  635. */
  636. public function notPaidAjax(Request $request, GuardianRepository $guardianRepository, PaymentRepository $paymentRepository): Response
  637. {
  638. $objectRepository = $guardianRepository;
  639. $page = $request->request->get('page');
  640. $perPage = $request->request->get('perPage');
  641. $page = ($page && $page > 1) ? $page : 1;
  642. $perPage = ($perPage && $perPage > 0) ? $perPage : 10; // should change in the future
  643. $searchFilters = $request->request->get('filters', []);
  644. $sorters = $request->request->get('sorters', []);
  645. $query = $objectRepository->createQueryBuilder('a');
  646. $dateFor = $request->request->get('dateFor', 'now');
  647. $dateFor = new \DateTime($dateFor);
  648. $dateFor->modify("first day of {$dateFor->format('Y-M')}");
  649. $dateFor->setTime(0, 0);
  650. if ($searchFilters) {
  651. foreach ($searchFilters as $filter) {
  652. if (isset($filter['value'])) {
  653. $searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
  654. if ($filter['type'] == "like") {
  655. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  656. ->setParameter("{$filter['field']}", "%{$searchValue}%");
  657. } else {
  658. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  659. ->setParameter("{$filter['field']}", "{$searchValue}");
  660. }
  661. }
  662. }
  663. }
  664. $query->leftJoin('a.payments', 'p')
  665. ->andWhere("p.dateFor = :dateFor")
  666. ->setParameter('dateFor', $dateFor)
  667. ->andWhere("p.status = 'NOT_PAID'")
  668. ->andWhere("p.amount > 0")
  669. ->leftJoin('p.guardian', 'g')
  670. ->andWhere('g.flagEarlyPayment = 0 OR g.flagEarlyPayment IS NULL');
  671. if ($sorters) {
  672. foreach ($sorters as $sorter) {
  673. $query->addOrderBy('a.' . $sorter['field'], $sorter['dir']);
  674. }
  675. }
  676. $guardiansObj = RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage);
  677. $guardians = [];
  678. /**
  679. * @var $guardianObject Guardian
  680. */
  681. foreach ($guardiansObj as $guardianObject) {
  682. $payment = $paymentRepository->findOneBy(['guardian' => $guardianObject->getId()], ['dateFor' => 'desc']);
  683. if ($payment) {
  684. if ($payment->getStatus() == Payment::STATUS_PAID) {
  685. $lastMonthValue = $payment->getAmount();
  686. } else {
  687. $lastMonthValue = $payment->getAmount() * -1;
  688. }
  689. } else {
  690. $lastMonthValue = 0;
  691. }
  692. $currentMonthValue = 0;
  693. $wholeValue = 0;
  694. $notifications = [];
  695. foreach ($guardianObject->getPayments() as $payment)
  696. {
  697. if($payment->getDateFor() == $dateFor && $payment->getStatus() == Payment::STATUS_NOTPAID)
  698. {
  699. $currentMonthValue = $payment->getAmount() * -1;
  700. }
  701. if($payment->getStatus() == Payment::STATUS_NOTPAID)
  702. {
  703. $wholeValue += $payment->getAmount() * -1;
  704. $notifications[] = $payment->getNotificationStatus();
  705. }
  706. }
  707. $guardian = [];
  708. $guardian['id'] = $guardianObject->getId();
  709. $guardian['dateAdd'] = $guardianObject->getDateAdd()->format("Y-m-d");
  710. $guardian['fullname'] = "{$guardianObject->getFullname()}";
  711. $guardian['phoneNumber'] = $guardianObject->getPhoneNumber();
  712. $guardian['currentMonthValue'] = $currentMonthValue;
  713. $guardian['wholeValue'] = $wholeValue;
  714. $guardian['email'] = $guardianObject->getEmail();
  715. $guardian['marksignName'] = $guardianObject->getMarksignName();
  716. $guardian['marksignSurname'] = $guardianObject->getMarksignSurname();
  717. // $guardian['comment'] = $guardianObject->getComment();
  718. // $guardian['balanceStatus'] = $guardianObject->getBalanceStatus();
  719. $guardian['status'] = $guardianObject->getStatus();
  720. $guardian['esign'] = ($guardianObject->getGuardianRegister() && $guardianObject->getGuardianRegister()->getSign()) ? 'Yes' : 'No';
  721. $guardian['notifications'] = implode(',',$notifications);
  722. // $guardian['children'] = "";
  723. // $guardian['disciplines'] = "";
  724. // foreach ($guardianObject->getChildren() as $child) {
  725. // $disciplines = "";
  726. // foreach ($child->getChildPreferences() as $preference) {
  727. // if ($preference->getDiscipline()) {
  728. // if (!$disciplines) {
  729. // $disciplines = $preference->getDiscipline()->getName();
  730. // } else {
  731. // $disciplines .= ", " . $preference->getDiscipline()->getName();
  732. // }
  733. //
  734. // }
  735. // }
  736. //
  737. // $guardian['children'] .= "{$child->getFullname()} ";
  738. // $guardian['disciplines'] .= "{$disciplines} ";
  739. // }
  740. $guardians[] = $guardian;
  741. }
  742. $count = count($guardians);
  743. $pagesCount = ceil($count / $perPage);
  744. return new JsonResponse([
  745. "last_page" => $pagesCount,
  746. "data" => $guardians
  747. ]);
  748. }
  749. /**
  750. * @Route("/early_not_paid/ajax", name="early_not_paid_ajax_guardian", methods={"POST"})
  751. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  752. * @param Request $request
  753. * @param EntityManagerInterface $entityManager
  754. * @return Response
  755. * @throws \Doctrine\ORM\NoResultException
  756. * @throws \Doctrine\ORM\NonUniqueResultException
  757. * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
  758. */
  759. public function early_not_paid_ajax_guardian(Request $request, GuardianRepository $guardianRepository, QuickPaymentController $quickPaymentController): Response
  760. {
  761. $objectRepository = $guardianRepository;
  762. $page = $request->request->get('page');
  763. $perPage = $request->request->get('perPage');
  764. $page = ($page && $page > 1) ? $page : 1;
  765. $perPage = ($perPage && $perPage > 0) ? $perPage : 10; // should change in the future
  766. $searchFilters = $request->request->get('filters', []);
  767. $sorters = $request->request->get('sorters', []);
  768. $query = $objectRepository->createQueryBuilder('a');
  769. if ($searchFilters) {
  770. foreach ($searchFilters as $filter) {
  771. if (isset($filter['value'])) {
  772. $searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
  773. if ($filter['type'] == "like") {
  774. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  775. ->setParameter("{$filter['field']}", "%{$searchValue}%");
  776. } else {
  777. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  778. ->setParameter("{$filter['field']}", "{$searchValue}");
  779. }
  780. }
  781. }
  782. }
  783. $query->andWhere("a.flagEarlyPayment = 1")
  784. ->join('a.guardianRegister', 'gr')
  785. ->andWhere('gr.status = :notPaid48')
  786. ->setParameter('notPaid48', GuardianRegister::STATUS_NOT_PAID_48['value']);
  787. if ($sorters) {
  788. foreach ($sorters as $sorter) {
  789. $query->addOrderBy('a.' . $sorter['field'], $sorter['dir']);
  790. }
  791. }
  792. $guardiansObj = RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage);
  793. $guardians = [];
  794. /**
  795. * @var $guardianObject Guardian
  796. */
  797. foreach ($guardiansObj as $guardianObject) {
  798. $earlyPaymentData = $quickPaymentController->getEarlyPaymentData($guardianObject);
  799. $amount = $earlyPaymentData['amount'];
  800. $lessonsToPay = $earlyPaymentData['lessonsToPay'];
  801. $periodEndTime = $earlyPaymentData['previousPeriodEndTime'];
  802. if($periodEndTime > new \DateTime())
  803. {
  804. continue;
  805. }
  806. if($amount > 0) {
  807. $notPaidDays = date_diff($periodEndTime, new \DateTime())->format("%a");
  808. // dump($notPaidDays);
  809. // dump($guardianObject->getId());
  810. // dump($periodEndTime);
  811. // die();
  812. $guardian = [];
  813. $guardian['id'] = $guardianObject->getId();
  814. $guardian['dateAdd'] = $guardianObject->getDateAdd()->format("Y-m-d");
  815. $guardian['fullname'] = "{$guardianObject->getFullname()}";
  816. $guardian['phoneNumber'] = $guardianObject->getPhoneNumber();
  817. $guardian['email'] = $guardianObject->getEmail();
  818. $guardian['marksignName'] = $guardianObject->getMarksignName();
  819. $guardian['marksignSurname'] = $guardianObject->getMarksignSurname();
  820. $guardian['notPaidDays'] = $notPaidDays;
  821. $guardian['amount'] = $amount;
  822. $guardian['sales'] = ($guardianObject->getGuardianRegister() && $guardianObject->getGuardianRegister()->getSalesPerson()) ? $guardianObject->getGuardianRegister()->getSalesPerson()->getFullname() : '';
  823. $guardians[] = $guardian;
  824. }
  825. }
  826. //die();
  827. $count = count($guardians);
  828. $pagesCount = ceil($count / $perPage);
  829. return new JsonResponse([
  830. "last_page" => $pagesCount,
  831. "data" => $guardians
  832. ]);
  833. }
  834. /**
  835. * @Route("/missed/ajax", name="missed_ajax_guardian", methods={"POST"})
  836. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  837. * @param Request $request
  838. * @param EntityManagerInterface $entityManager
  839. * @return Response
  840. * @throws \Doctrine\ORM\NoResultException
  841. * @throws \Doctrine\ORM\NonUniqueResultException
  842. * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
  843. */
  844. public function missedAjax(Request $request, GuardianRepository $guardianRepository, LessonRepository $lessonRepository): Response
  845. {
  846. $objectRepository = $guardianRepository;
  847. $page = $request->request->get('page');
  848. $perPage = $request->request->get('perPage');
  849. $page = ($page && $page > 1) ? $page : 1;
  850. $perPage = ($perPage && $perPage > 0) ? $perPage : 10; // should change in the future
  851. $searchFilters = $request->request->get('filters', []);
  852. $sorters = $request->request->get('sorters', []);
  853. $query = $objectRepository->createQueryBuilder('a');
  854. // $dateFor = $request->request->get('dateFor', 'now');
  855. $dateStart = new \DateTime($request->request->get('dateStart', 'now'));
  856. $dateEnd = new \DateTime($request->request->get('dateEnd', 'now'));
  857. // $dateFor = new \DateTime($dateFor);
  858. // $dateFor->modify("first day of {$dateFor->format('Y-M')}");
  859. $dateStart->setTime(0, 0);
  860. $dateEnd->setTime(0, 0);
  861. if ($searchFilters) {
  862. foreach ($searchFilters as $filter) {
  863. if (isset($filter['value'])) {
  864. $searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
  865. if ($filter['type'] == "like") {
  866. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  867. ->setParameter("{$filter['field']}", "%{$searchValue}%");
  868. } else {
  869. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  870. ->setParameter("{$filter['field']}", "{$searchValue}");
  871. }
  872. }
  873. }
  874. }
  875. $query->leftJoin('a.children', 'c')
  876. ->leftJoin('c.lessons', 'l')
  877. ->andWhere("l.status = :status")
  878. ->setParameter('status', Lesson::STATUS_CHILD_UNINFORMED_MISSED['value'])
  879. ->andWhere("l.startTime >= :dateStart")
  880. ->andWhere("l.endTime <= :dateEnd")
  881. ->setParameter('dateStart', $dateStart)
  882. ->setParameter('dateEnd', $dateEnd);
  883. if ($sorters) {
  884. foreach ($sorters as $sorter) {
  885. $query->addOrderBy('a.' . $sorter['field'], $sorter['dir']);
  886. }
  887. }
  888. $count = RestUtils::getQueryCount(clone $query);
  889. $pagesCount = ceil($count / $perPage);
  890. $guardiansObj = RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage);
  891. $guardians = [];
  892. /**
  893. * @var $guardianObject Guardian
  894. */
  895. foreach ($guardiansObj as $guardianObject) {
  896. $missedLessonsInRow = 0;
  897. $add = false;
  898. foreach ($guardianObject->getChildren() as $child)
  899. {
  900. $lessonsQuery = $lessonRepository->createQueryBuilder('l');
  901. $lessons = $lessonsQuery
  902. ->andWhere(':child member of l.children')
  903. ->setParameter('child', $child)
  904. ->andWhere("l.startTime >= :dateStart")
  905. ->andWhere("l.endTime <= :dateEnd")
  906. ->setParameter('dateStart', $dateStart)
  907. ->setParameter('dateEnd', $dateEnd)
  908. ->orderBy('l.startTime', 'ASC')
  909. ->getQuery()
  910. ->getResult();
  911. foreach ($lessons as $lesson)
  912. {
  913. // dump($lesson->getStatus());
  914. if($lesson->getStatus() == Lesson::STATUS_CHILD_UNINFORMED_MISSED['text'])
  915. {
  916. $missedLessonsInRow++;
  917. if($missedLessonsInRow >= 2)
  918. {
  919. $add = true;
  920. break;
  921. }
  922. // dump($missedLessonsInRow);
  923. }
  924. else {
  925. $missedLessonsInRow = 0;
  926. }
  927. }
  928. }
  929. // die();
  930. if($add) {
  931. $guardian = [];
  932. $guardian['id'] = $guardianObject->getId();
  933. $guardian['dateAdd'] = $guardianObject->getDateAdd()->format("Y-m-d");
  934. $guardian['fullname'] = "{$guardianObject->getFullname()}";
  935. $guardian['phoneNumber'] = $guardianObject->getPhoneNumber();
  936. $guardian['email'] = $guardianObject->getEmail();
  937. $guardian['status'] = $guardianObject->getStatus();
  938. $guardians[] = $guardian;
  939. }
  940. }
  941. return new JsonResponse([
  942. "last_page" => $pagesCount,
  943. "data" => $guardians
  944. ]);
  945. }
  946. /**
  947. * @Route("/not_paid/counts/ajax", name="not_paid_counts_ajax_guardian", methods={"GET"})
  948. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  949. * @param Request $request
  950. * @param EntityManagerInterface $entityManager
  951. * @return Response
  952. * @throws \Doctrine\ORM\NoResultException
  953. * @throws \Doctrine\ORM\NonUniqueResultException
  954. * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
  955. */
  956. public function notPaidCountsAjax(Request $request, GuardianRepository $guardianRepository, PaymentRepository $paymentRepository, EntityManagerInterface $entityManager): Response
  957. {
  958. $dateOffset = $request->query->get('date');
  959. if($dateOffset != 'null') {
  960. $dateFor = new \DateTime($dateOffset);
  961. }
  962. else
  963. {
  964. $dateFor = new \DateTime();
  965. }
  966. $dateFor->modify("first day of {$dateFor->format('Y-M')}");
  967. $dateFor->setTime(0, 0);
  968. $notPaidStatus = Payment::STATUS_NOTPAID;
  969. $RAW_QUERY_TOTAL = "SELECT COUNT(guardian.id) as totalCount from guardian left join payment on payment.guardian_id = guardian.id and payment.status = '{$notPaidStatus}' where guardian_survey.date_for like '{$dateFor->format('Y-m')}%'";
  970. $RAW_QUERY_TOTAL = "SELECT COUNT(DISTINCT(guardian.id)) as totalCount from guardian left join payment on payment.guardian_id = guardian.id where payment.status = '{$notPaidStatus}' and payment.amount > 0";
  971. $RAW_QUERY_TOTAL_AMOUNT = "SELECT SUM(payment.amount) as totalAmount from guardian left join payment on payment.guardian_id = guardian.id where payment.status = '{$notPaidStatus}' and payment.amount > 0";
  972. $RAW_QUERY_TOTAL_MONTH = "SELECT COUNT(DISTINCT(guardian.id)) as totalCount from guardian left join payment on payment.guardian_id = guardian.id where payment.status = '{$notPaidStatus}' and payment.amount > 0 and payment.date_for like '{$dateFor->format('Y-m')}%'";
  973. $RAW_QUERY_TOTAL_AMOUNT_MONTH = "SELECT SUM(payment.amount) as totalAmount from guardian left join payment on payment.guardian_id = guardian.id where payment.status = '{$notPaidStatus}' and payment.amount > 0 and payment.date_for like '{$dateFor->format('Y-m')}%'";
  974. $statement = $entityManager->getConnection()->prepare($RAW_QUERY_TOTAL);
  975. $resultSet = $statement->executeQuery();
  976. $total = $resultSet->fetchAllAssociative();
  977. $statement = $entityManager->getConnection()->prepare($RAW_QUERY_TOTAL_AMOUNT);
  978. $resultSet = $statement->executeQuery();
  979. $totolAmount = $resultSet->fetchAllAssociative();
  980. $statement = $entityManager->getConnection()->prepare($RAW_QUERY_TOTAL_MONTH);
  981. $resultSet = $statement->executeQuery();
  982. $totalMonth = $resultSet->fetchAllAssociative();
  983. $statement = $entityManager->getConnection()->prepare($RAW_QUERY_TOTAL_AMOUNT_MONTH);
  984. $resultSet = $statement->executeQuery();
  985. $totolAmountMonth = $resultSet->fetchAllAssociative();
  986. // $statement = $entityManager->getConnection()->prepare($RAW_QUERY_SUBMITTED);
  987. // $statement->execute();
  988. // $submitted = $statement->fetchAll();
  989. return new JsonResponse([
  990. "data" => [
  991. 'monthDept' => round($totolAmountMonth[0]['totalAmount'],2),
  992. 'monthDeptCount' => $totalMonth[0]['totalCount'],
  993. 'dept' => round($totolAmount[0]['totalAmount'],2),
  994. 'deptCount' => $total[0]['totalCount'],
  995. ]
  996. ]);
  997. }
  998. /**
  999. * @Route("/ajax/{id}/edit", name="guardian_ajax_edit", methods={"POST"})
  1000. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  1001. * @param Guardian $guardian
  1002. * @param Request $request
  1003. * @return Response
  1004. */
  1005. public function ajaxEdit(Guardian $guardian, Request $request, MailerInterface $mailer, ReminderConfigurationRepository $reminderConfigurationRepository, ChildRepository $childRepository, EntityManagerInterface $entityManager, CredentialsService $credentialsService, MessageBusInterface $messageBus, ChildPreferenceService $childPreferenceService, GuardianLogService $guardianLogService): Response
  1006. {
  1007. if (!$guardian) {
  1008. $error = new JsonResponse();
  1009. $error
  1010. ->setStatusCode(Response::HTTP_NOT_FOUND)
  1011. ->setData(['status' => "error"]);
  1012. return $error;
  1013. }
  1014. $data = json_decode($request->getContent(), true);
  1015. if (isset($data['status'])) {
  1016. if ($data['status'] == Guardian::STATUS_SENT['value'] && (!$guardian->getEmail() || $guardian->getEmail() == "")) {
  1017. return new JsonResponse(['status' => "Vartotojas neturi elektroninio pasto"], Response::HTTP_BAD_REQUEST);
  1018. }
  1019. if($data['status'] == Guardian::STATUS_CONFIRMED['value'] && $guardian->getStatus() != Guardian::STATUS_CONFIRMED['text'])
  1020. {
  1021. $this->generateAndSendChildrenCredentials($request,$credentialsService, $guardian, $mailer, $reminderConfigurationRepository, $childRepository,$entityManager);
  1022. $guardianLogService->addGuardianActionLog('status_confirmed', $guardian, null, 'status_confirmed', false);
  1023. $entityManager->flush();
  1024. }
  1025. }
  1026. if (isset($data['progress'])) {
  1027. if ($data['progress'] != $guardian->isProgress()) {
  1028. $guardian->setProgressByAdmin($this->getUser());
  1029. $guardian->setProgressDate(new \DateTime());
  1030. $guardian->setProgress($data['progress']);
  1031. }
  1032. }
  1033. if (isset($data['autumn2024back'])) {
  1034. if ($data['autumn2024back'] != $guardian->isAutumn2024back()) {
  1035. $guardian->setAutumn2024backByAdmin($this->getUser());
  1036. $guardian->setAutumn2024backDate(new \DateTime());
  1037. $guardian->setAutumn2024back($data['autumn2024back']);
  1038. }
  1039. }
  1040. if (isset($data['summer'])) {
  1041. if ($data['summer'] != $guardian->getSummer()) {
  1042. $guardian->setSummer($data['summer']);
  1043. $guardian->setSummerAt(new \DateTime());
  1044. $guardian->setSummerByAdmin($this->getUser());
  1045. $guardian->setSummerDate(new \DateTime());
  1046. if($guardian->getSummer() == "YES_35")
  1047. {
  1048. $guardianDiscount = new Guardian\GuardianPriceDiscount();
  1049. $guardianDiscount->setGuardian($guardian);
  1050. $guardianDiscount->setDiscountPercent(35);
  1051. $guardianDiscount->setDateFrom(new \DateTime("2025-07-01 00:00:00"));
  1052. $guardianDiscount->setDateTo(new \DateTime("2025-08-31 23:59:59"));
  1053. $entityManager->persist($guardianDiscount);
  1054. $entityManager->flush();
  1055. $guardian->addGuardianPriceDiscount($guardianDiscount);
  1056. $entityManager->persist($guardian);
  1057. $entityManager->flush();
  1058. $childPreferenceService->recalculatePricesForFurtherLessons($guardian,new \DateTime("2025-07-01 00:00:00"));
  1059. $messageBus->dispatch(new SendMailMessage($guardian->getEmail(), '', '', [], 'NOTIF_SUMMER_GUARDIAN_ACCEPTED'));
  1060. }
  1061. if($guardian->getSummer() == "YES_20")
  1062. {
  1063. $guardianDiscount = new Guardian\GuardianPriceDiscount();
  1064. $guardianDiscount->setGuardian($guardian);
  1065. $guardianDiscount->setDiscountPercent(20);
  1066. $guardianDiscount->setDateFrom(new \DateTime("2025-07-01 00:00:00"));
  1067. $guardianDiscount->setDateTo(new \DateTime("2025-08-31 23:59:59"));
  1068. $entityManager->persist($guardianDiscount);
  1069. $entityManager->flush();
  1070. $guardian->addGuardianPriceDiscount($guardianDiscount);
  1071. $entityManager->persist($guardian);
  1072. $entityManager->flush();
  1073. $childPreferenceService->recalculatePricesForFurtherLessons($guardian,new \DateTime("2025-07-01 00:00:00"));
  1074. $messageBus->dispatch(new SendMailMessage($guardian->getEmail(), '', '', [], 'NOTIF_SUMMER_GUARDIAN_ACCEPTED'));
  1075. }
  1076. if($guardian->getSummer() == "NO")
  1077. {
  1078. $messageBus->dispatch(new SendMailMessage($guardian->getEmail(), '', '', [], 'NOTIF_SUMMER_GUARDIAN_NOT_ACCEPTED'));
  1079. }
  1080. }
  1081. }
  1082. $guardian = $this->get('serializer')->deserialize($request->getContent(), Guardian::class, 'json', [
  1083. AbstractNormalizer::OBJECT_TO_POPULATE => $guardian
  1084. ]);
  1085. RestUtils::saveObject($this->getDoctrine()->getManager(), $guardian);
  1086. return new JsonResponse(['status' => "OK"], Response::HTTP_OK);
  1087. }
  1088. /**
  1089. * @Route("/{id}/payments", name="guardians_ajax_controller", methods={"POST"})
  1090. * @Route("/payments", name="guardians_ajax_controller_no_id", methods={"POST"})
  1091. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY') or is_granted('ROLE_GUARDIAN') and is_granted('IS_AUTHENTICATED_FULLY')")
  1092. * @param Request $request
  1093. * @param EntityManagerInterface $entityManager
  1094. * @return Response
  1095. * @throws \Doctrine\ORM\NoResultException
  1096. * @throws \Doctrine\ORM\NonUniqueResultException
  1097. * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
  1098. */
  1099. public function fetchData(?Guardian $guardian, Request $request, PaymentRepository $paymentRepository, EarlyPaymentRepository $earlyPaymentRepository): Response
  1100. {
  1101. $user = $this->getUser();
  1102. if (in_array("ROLE_GUARDIAN", $user->getRoles())) {
  1103. $guardian = $user;
  1104. }
  1105. $page = $request->request->get('page');
  1106. $perPage = $request->request->get('perPage');
  1107. $page = ($page && $page > 1) ? $page : 1;
  1108. $perPage = ($perPage && $perPage > 0) ? $perPage : 10; // should change in the future
  1109. $searchFilters = $request->request->get('filters', []);
  1110. if ($guardian->isFlagEarlyPayment()) {
  1111. return $this->fetchEarlyPaymentGuardianPayments(
  1112. $guardian,
  1113. $earlyPaymentRepository,
  1114. $paymentRepository,
  1115. $searchFilters,
  1116. $page,
  1117. $perPage
  1118. );
  1119. }
  1120. $query = $paymentRepository->createQueryBuilder('a');
  1121. $query->andWhere("a.guardian = :guardian")
  1122. ->setParameter("guardian", $guardian->getId());
  1123. $this->applyPaymentSearchFilters($query, $searchFilters);
  1124. $query->orderBy('a.dateFor', 'DESC');
  1125. $count = RestUtils::getQueryCount(clone $query);
  1126. $pagesCount = ceil($count / $perPage);
  1127. $payments = $this->normalizePaymentsWithSource(
  1128. RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage),
  1129. self::PAYMENT_SOURCE_REGULAR
  1130. );
  1131. return new JsonResponse([
  1132. "last_page" => $pagesCount,
  1133. "data" => $payments
  1134. ]);
  1135. }
  1136. private function fetchEarlyPaymentGuardianPayments(
  1137. Guardian $guardian,
  1138. EarlyPaymentRepository $earlyPaymentRepository,
  1139. PaymentRepository $paymentRepository,
  1140. array $searchFilters,
  1141. int $page,
  1142. int $perPage
  1143. ): JsonResponse {
  1144. $earlyPaymentQuery = $earlyPaymentRepository->createQueryBuilder('a');
  1145. $earlyPaymentQuery->andWhere('a.guardian = :guardian')
  1146. ->setParameter('guardian', $guardian->getId());
  1147. $latestPerMonthIds = array_column(
  1148. $earlyPaymentRepository->createQueryBuilder('latest')
  1149. ->select('MAX(latest.id) as id')
  1150. ->andWhere('latest.guardian = :guardian')
  1151. ->andWhere('latest.status != :abandoned')
  1152. ->setParameter('guardian', $guardian->getId())
  1153. ->setParameter('abandoned', EarlyPayment::STATUS_ABANDONED)
  1154. ->groupBy('latest.dateFor')
  1155. ->getQuery()
  1156. ->getScalarResult(),
  1157. 'id'
  1158. );
  1159. $visibleUntil = (new \DateTime())
  1160. ->modify("+" . self::EARLY_PAYMENT_INVOICE_VISIBILITY_DAYS . " days");
  1161. $earlyPaymentQuery->andWhere('a.id IN (:latestPerMonthIds)')
  1162. ->andWhere('a.status = :paidStatus OR a.dateFor <= :visibleUntil')
  1163. ->setParameter('latestPerMonthIds', $latestPerMonthIds ?: [0])
  1164. ->setParameter('paidStatus', EarlyPayment::STATUS_PAID)
  1165. ->setParameter('visibleUntil', $visibleUntil);
  1166. $this->applyPaymentSearchFilters($earlyPaymentQuery, $searchFilters);
  1167. $earlyPaymentQuery->orderBy('a.dateFor', 'DESC');
  1168. $earlyPayments = $this->normalizePaymentsWithSource(
  1169. $earlyPaymentQuery->getQuery()->getResult(),
  1170. self::PAYMENT_SOURCE_EARLY
  1171. );
  1172. $regularPaymentQuery = $paymentRepository->createQueryBuilder('a');
  1173. $regularPaymentQuery->andWhere('a.guardian = :guardian')
  1174. ->andWhere('a.status = :paidStatus')
  1175. ->setParameter('guardian', $guardian->getId())
  1176. ->setParameter('paidStatus', Payment::STATUS_PAID);
  1177. $this->applyPaymentSearchFilters($regularPaymentQuery, $searchFilters);
  1178. $regularPaymentQuery->orderBy('a.dateFor', 'DESC');
  1179. $regularPayments = $this->normalizePaymentsWithSource(
  1180. $regularPaymentQuery->getQuery()->getResult(),
  1181. self::PAYMENT_SOURCE_REGULAR
  1182. );
  1183. $combinedPayments = array_merge($earlyPayments, $regularPayments);
  1184. usort($combinedPayments, static fn(array $a, array $b) => strcmp($b['dateFor'], $a['dateFor']));
  1185. $pagesCount = ceil(count($combinedPayments) / $perPage);
  1186. $payments = array_slice($combinedPayments, ($page - 1) * $perPage, $perPage);
  1187. return new JsonResponse([
  1188. "last_page" => $pagesCount,
  1189. "data" => $payments
  1190. ]);
  1191. }
  1192. private function normalizePaymentsWithSource(array $payments, string $source): array
  1193. {
  1194. $normalizedPayments = $this->get('serializer')->normalize($payments, null, ['groups' => "PaymentList"]);
  1195. foreach ($normalizedPayments as &$normalizedPayment) {
  1196. $normalizedPayment['source'] = $source;
  1197. $normalizedPayment['rowKey'] = $source . '_' . $normalizedPayment['id'];
  1198. }
  1199. unset($normalizedPayment);
  1200. return $normalizedPayments;
  1201. }
  1202. private function applyPaymentSearchFilters(QueryBuilder $query, array $searchFilters): void
  1203. {
  1204. foreach ($searchFilters as $filter) {
  1205. if (!isset($filter['value'])) {
  1206. continue;
  1207. }
  1208. $relation = explode('.', $filter['field']);
  1209. $searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
  1210. if (count($relation) == 1) {
  1211. if ($filter['type'] == "like") {
  1212. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  1213. ->setParameter("{$filter['field']}", "%{$searchValue}%");
  1214. } else {
  1215. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  1216. ->setParameter("{$filter['field']}", "{$searchValue}");
  1217. }
  1218. } elseif (count($relation) == 2) {
  1219. $field = $relation[1];
  1220. $relationName = $relation[0];
  1221. if ($filter['type'] == "like") {
  1222. $query
  1223. ->leftJoin("a." . $relationName, $relationName)
  1224. ->andWhere("{$relationName}.{$field} {$filter['type']} :{$field}")
  1225. ->setParameter("{$field}", "%{$searchValue}%");
  1226. } else {
  1227. $query
  1228. ->leftJoin("a." . $relationName, $relationName)
  1229. ->andWhere("{$relationName}.{$field} {$filter['type']} :{$field}")
  1230. ->setParameter("{$field}", "{$searchValue}");
  1231. }
  1232. }
  1233. }
  1234. }
  1235. /**
  1236. * @Route("/{id}/actions", name="guardian_actions_log", methods={"POST"})
  1237. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  1238. * @param Request $request
  1239. * @param EntityManagerInterface $entityManager
  1240. * @return Response
  1241. * @throws \Doctrine\ORM\NoResultException
  1242. * @throws \Doctrine\ORM\NonUniqueResultException
  1243. * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
  1244. */
  1245. public function actionLogs(?Guardian $guardian, Request $request, GuardianActionLogRepository $guardianActionLogRepository, TeacherRepository $teacherRepository, AdminRepository $adminRepository, TranslatorInterface $translator): Response
  1246. {
  1247. $objectRepository = $guardianActionLogRepository;
  1248. $page = $request->request->get('page');
  1249. $perPage = $request->request->get('perPage');
  1250. $page = ($page && $page > 1) ? $page : 1;
  1251. $perPage = ($perPage && $perPage > 0) ? $perPage : 10; // should change in the future
  1252. $searchFilters = $request->request->get('filters', []);
  1253. $sorters = $request->request->get('sorters', []);
  1254. $query = $objectRepository->createQueryBuilder('a');
  1255. $query->andWhere("a.guardian = :guardian")
  1256. ->setParameter("guardian", $guardian->getId());
  1257. if ($searchFilters) {
  1258. foreach ($searchFilters as $filter) {
  1259. if (isset($filter['value'])) {
  1260. $relation = explode('.', $filter['field']);
  1261. if (count($relation) == 1) {
  1262. $searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
  1263. if($searchValue == 'true')
  1264. {
  1265. $searchValue = 1;
  1266. }
  1267. if($searchValue == 'false')
  1268. {
  1269. $searchValue = 0;
  1270. }
  1271. if ($filter['type'] == "like") {
  1272. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  1273. ->setParameter("{$filter['field']}", "%{$searchValue}%");
  1274. } else {
  1275. $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
  1276. ->setParameter("{$filter['field']}", "{$searchValue}");
  1277. }
  1278. } elseif (count($relation) == 2) {
  1279. $searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
  1280. $field = $relation[1];
  1281. $relation = $relation[0];
  1282. if ($filter['type'] == "like") {
  1283. $query
  1284. ->leftJoin("a." . $relation, $relation)
  1285. ->andWhere("{$relation}.{$field} {$filter['type']} :{$field}")
  1286. ->setParameter("{$field}", "%{$searchValue}%");
  1287. } else {
  1288. $query
  1289. ->leftJoin("a." . $relation, $relation)
  1290. ->andWhere("{$relation}.{$field} {$filter['type']} :{$field}")
  1291. ->setParameter("{$field}", "{$searchValue}");
  1292. }
  1293. }
  1294. }
  1295. }
  1296. }
  1297. // if($sorters){
  1298. // foreach($sorters as $sorter){
  1299. // $query->addOrderBy('a.'.$sorter['field'],$sorter['dir']);
  1300. // }
  1301. // }
  1302. $query->orderBy('a.createdAt', 'DESC');
  1303. $count = RestUtils::getQueryCount(clone $query);
  1304. $pagesCount = ceil($count / $perPage);
  1305. $actionLogs = $this->get('serializer')->normalize(RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage), null, ['groups' => "GuardianActionList"]);
  1306. foreach ($actionLogs as $key => $actionLog)
  1307. {
  1308. if($actionLog['actionType'] == 'update')
  1309. {
  1310. $actionLogs[$key]['actionType'] = 'Atnaujinimas';
  1311. }
  1312. if($actionLog['actionType'] == 'delete')
  1313. {
  1314. $actionLogs[$key]['actionType'] = 'Ištrynimas';
  1315. }
  1316. if($actionLog['actionType'] == 'insert')
  1317. {
  1318. $actionLogs[$key]['actionType'] = 'Pridėjimas';
  1319. }
  1320. if($actionLog['userType'] == 'Admin')
  1321. {
  1322. $actionLogs[$key]['fullname'] = $adminRepository->find($actionLog['userId'])->getFullname();
  1323. }
  1324. // if($actionLog['userType'] == 'Admin')
  1325. // {
  1326. $data = json_decode($actionLogs[$key]['data'], true);
  1327. if (json_last_error() !== JSON_ERROR_NONE) {
  1328. $data = $actionLogs[$key]['data'];
  1329. }
  1330. $dataString = '';
  1331. if(is_array($data)) {
  1332. foreach ($data as $field => $dataLine) {
  1333. if ($dataString) {
  1334. $dataString .= '<br>';
  1335. }
  1336. $fieldTranslated = $translator->trans('forms.labels.' . $field);
  1337. if ($fieldTranslated == 'forms.labels.' . $field) {
  1338. $fieldTranslated = $translator->trans($field);
  1339. }
  1340. if(is_array($dataLine)) {
  1341. $dataString .= "<b>{$fieldTranslated}</b> iš <b>{$dataLine[0]}</b> į <b>{$dataLine[1]}</b>";
  1342. }
  1343. else {
  1344. $dataString .= "<b>{$fieldTranslated}</b> į <b>{$dataLine}</b>";
  1345. }
  1346. }
  1347. }
  1348. else
  1349. {
  1350. $dataString = $data;
  1351. }
  1352. $actionLogs[$key]['data'] = $dataString;
  1353. }
  1354. // }
  1355. return new JsonResponse([
  1356. "last_page" => $pagesCount,
  1357. "data" => $actionLogs
  1358. ]);
  1359. }
  1360. /**
  1361. * @Route("/lessons/{id}", name="guardian_lessons_ajax_controller", methods={"GET", "POST"})
  1362. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY') or is_granted('ROLE_GUARDIAN') and is_granted('IS_AUTHENTICATED_FULLY') or is_granted('ROLE_CHILD') and is_granted('IS_AUTHENTICATED_FULLY')")
  1363. * @param Request $request
  1364. * @param EntityManagerInterface $entityManager
  1365. * @return Response
  1366. * @throws \Doctrine\ORM\NoResultException
  1367. * @throws \Doctrine\ORM\NonUniqueResultException
  1368. * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
  1369. */
  1370. public function fetchLessonsData(Child $childObj, Request $request, LessonRepository $lessonRepository, ChildRepository $childRepository, LessonDeletedRepository $lessonDeletedRepository): Response
  1371. {
  1372. $user = $this->getUser();
  1373. if (in_array("ROLE_GUARDIAN", $user->getRoles())) {
  1374. if ($childObj->getGuardian()->getId() != $user->getId()) {
  1375. return new JsonResponse(null, 401);
  1376. }
  1377. }
  1378. if (in_array("ROLE_CHILD", $user->getRoles())) {
  1379. if ($childObj->getId() != $user->getId()) {
  1380. return new JsonResponse(null, 401);
  1381. }
  1382. }
  1383. $date = new \DateTime();
  1384. $offset = $request->request->get('offset', 0);
  1385. $date->modify("first day of {$date->format('Y-m')}");
  1386. $date = $date->modify("{$offset} months");
  1387. $date->modify("first day of {$date->format('Y-m')}");
  1388. $date->setTime(0, 0, 0);
  1389. $end = new \DateTime($date->format('Y-m-t'));
  1390. $end = $end->setTime(23, 59, 59);
  1391. $days = [];
  1392. while ($date <= $end) {
  1393. $day_num = $date->format('d');
  1394. $date = $date->modify('+1 day');
  1395. $days[(int)$day_num] = [];
  1396. }
  1397. $objectRepository = $lessonRepository;
  1398. $page = $request->request->get('page');
  1399. $perPage = $request->request->get('perPage');
  1400. $page = ($page && $page > 1) ? $page : 1;
  1401. $perPage = ($perPage && $perPage > 0) ? $perPage : 100; // should change in the future
  1402. $searchFilters = $request->request->get('filters', []);
  1403. $sorters = $request->request->get('sorters', []);
  1404. $query = $objectRepository->createQueryBuilder('a');
  1405. $query
  1406. ->leftJoin('a.children', 'c')
  1407. ->andWhere('c.id = :child')
  1408. ->setParameter("child", $childObj->getId());
  1409. if ($searchFilters) {
  1410. foreach ($searchFilters as $filter) {
  1411. if (isset($filter['value'])) {
  1412. $searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
  1413. if ($filter['field'] == 'teacherName') {
  1414. $query
  1415. ->leftJoin('a.teacher', 't')
  1416. ->andWhere('t.fullname LIKE :teacher')
  1417. ->setParameter("teacher", "%{$searchValue}%");
  1418. }
  1419. }
  1420. }
  1421. }
  1422. $date = new \DateTime();
  1423. $offset = $request->request->get('offset', 0);
  1424. $date->modify("first day of {$date->format('Y-m')}");
  1425. $date = $date->modify("{$offset} months");
  1426. $date->modify("first day of {$date->format('Y-m')}");
  1427. $date->setTime(0, 0, 0);
  1428. $end = new \DateTime($date->format('Y-m-t'));
  1429. $end = $end->setTime(23, 59, 59);
  1430. $end->modify('+2 minutes');
  1431. //Optimizacija
  1432. // $query
  1433. // ->andWhere('a.startTime >= :startTime')
  1434. // ->setParameter('startTime', $date->format('Y-m-d H:i'))
  1435. // ->andWhere('a.endTime <= :endTime')
  1436. // ->setParameter('endTime', $end->format('Y-m-d H:i'));
  1437. if ($sorters) {
  1438. foreach ($sorters as $sorter) {
  1439. $query->addOrderBy('a.' . $sorter['field'], $sorter['dir']);
  1440. }
  1441. }
  1442. $lineArray = [];
  1443. $childrenQuery = $childRepository->createQueryBuilder('a');
  1444. $childrenQuery
  1445. ->andWhere('a.id = :child')
  1446. ->setParameter("child", $childObj->getId());
  1447. if ($searchFilters) {
  1448. foreach ($searchFilters as $filter) {
  1449. if (isset($filter['value'])) {
  1450. $searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
  1451. if ($filter['field'] == 'teacherName') {
  1452. $childrenQuery
  1453. ->leftJoin('a.lessons', 'l')
  1454. ->leftJoin('l.teacher', 't')
  1455. ->andWhere('t.fullname LIKE :teacher')
  1456. ->setParameter("teacher", "%{$searchValue}%");
  1457. }
  1458. }
  1459. }
  1460. }
  1461. $children = $childrenQuery->getQuery()->getResult();
  1462. /** @var Child $child */
  1463. foreach ($children as $child) {
  1464. $query = $lessonRepository->createQueryBuilder('a');
  1465. $query
  1466. ->andWhere('a.startTime >= :startTime')
  1467. ->setParameter('startTime', $date->format('Y-m-d H:i'))
  1468. ->andWhere('a.endTime <= :endTime')
  1469. ->setParameter('endTime', $end->format('Y-m-d H:i'))
  1470. ->andWhere(':child MEMBER OF a.children')
  1471. ->setParameter('child', $child->getId());
  1472. if ($searchFilters) {
  1473. foreach ($searchFilters as $filter) {
  1474. if (isset($filter['value'])) {
  1475. $searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
  1476. if ($filter['field'] == 'teacherName') {
  1477. $query
  1478. ->leftJoin('a.teacher', 't')
  1479. ->andWhere('t.fullname LIKE :teacher')
  1480. ->setParameter("teacher", "%{$searchValue}%");
  1481. }
  1482. if ($filter['field'] == 'discipline') {
  1483. $query
  1484. ->leftJoin('a.discipline', 'd')
  1485. ->andWhere('d.name LIKE :discipline')
  1486. ->setParameter("discipline", "%{$searchValue}%");
  1487. }
  1488. }
  1489. }
  1490. }
  1491. $lessons = $query->getQuery()->getResult();
  1492. /**
  1493. * @var $lesson Lesson
  1494. */
  1495. foreach ($lessons as $lesson) {
  1496. foreach ($lineArray as $lineCheck) {
  1497. if ($lineCheck['key'] == $child->getId() . '-' . $lesson->getTeacher()->getId() . '-' . $lesson->getDiscipline()->getId()) {
  1498. continue 2;
  1499. }
  1500. }
  1501. $line = [
  1502. 'key' => $child->getId() . '-' . $lesson->getTeacher()->getId() . '-' . $lesson->getDiscipline()->getId(),
  1503. 'teacher' => ['name' => "{$lesson->getTeacher()->getFullname()}", 'id' => $lesson->getTeacher()->getId(),'sum'=>$lesson->getTeacher()->getSummer(),'lessons_inside'=>$lesson->getTeacher()->getUnickoFunction(),'work_end'=>$lesson->getTeacher()->getWorkEnd() ? $lesson->getTeacher()->getWorkEnd()->format('Y-m-d H:i'):""],
  1504. 'child' => ['name' => "{$child->getEmail()}", 'id' => $child->getId()],
  1505. 'discipline' => ['name' => $lesson->getDiscipline()->getName(), 'id' => $lesson->getDiscipline()->getId()],
  1506. 'days' => $days
  1507. ];
  1508. $lineArray[] = $line;
  1509. }
  1510. $lessonDeletedQuery = $lessonDeletedRepository->createQueryBuilder('ld');
  1511. $deletedLessons = $lessonDeletedQuery
  1512. ->andWhere('ld.child = :child')
  1513. ->setParameter('child', $child->getId())
  1514. ->andWhere('ld.startTime >= :startTime')
  1515. ->setParameter('startTime', $date->format('Y-m-d H:i'))
  1516. ->andWhere('ld.endTime <= :endTime')
  1517. ->setParameter('endTime', $end->format('Y-m-d H:i'))
  1518. ->getQuery()
  1519. ->getResult();
  1520. /**
  1521. * @var $lesson LessonDeleted
  1522. */
  1523. foreach ($deletedLessons as $lesson) {
  1524. foreach ($lineArray as $lineCheck) {
  1525. if ($lineCheck['key'] == $child->getId() . '-' . $lesson->getTeacher()->getId() . '-' . $lesson->getDiscipline()->getId()) {
  1526. continue 2;
  1527. }
  1528. }
  1529. $line = [
  1530. 'key' => $child->getId() . '-' . $lesson->getTeacher()->getId() . '-' . $lesson->getDiscipline()->getId(),
  1531. 'teacher' => ['name' => "{$lesson->getTeacher()->getFullname()}", 'id' => $lesson->getTeacher()->getId(),'sum'=>$lesson->getTeacher()->getSummer(),'lessons_inside'=>$lesson->getTeacher()->getUnickoFunction(),'work_end'=>$lesson->getTeacher()->getWorkEnd() ? $lesson->getTeacher()->getWorkEnd()->format('Y-m-d H:i'):""],
  1532. 'child' => ['name' => "{$child->getEmail()}", 'id' => $child->getId()],
  1533. 'discipline' => ['name' => $lesson->getDiscipline()->getName(), 'id' => $lesson->getDiscipline()->getId()],
  1534. 'days' => $days
  1535. ];
  1536. $lineArray[] = $line;
  1537. }
  1538. // if(isset($lineArray[0])) {
  1539. // $lineArray[0]['vacations'] = [];
  1540. // foreach ($child->getChildVacations() as $vacation) {
  1541. // if ($vacation->isIsDeleted()) {
  1542. // continue;
  1543. // }
  1544. // $lineArray[0]['vacations'][] = ['from' => $vacation->getDateFrom()->format('Y-m-d H:i'), 'to' => $vacation->getDateTo()->format('Y-m-d H:i')];
  1545. // }
  1546. // }
  1547. }
  1548. foreach ($lineArray as $key => $line)
  1549. {
  1550. $lineArray[$key]['vacations'] = [];
  1551. foreach ($child->getChildVacations() as $vacation) {
  1552. if ($vacation->isIsDeleted()) {
  1553. continue;
  1554. }
  1555. $lineArray[$key]['vacations'][] = [
  1556. 'from' => $vacation->getDateFrom()->format('Y-m-d H:i'),
  1557. 'to' => $vacation->getDateTo()->format('Y-m-d H:i'),
  1558. 'disciplineIds' => $this->normalizeVacationDisciplineIds($vacation->getDisciplineIds()),
  1559. ];
  1560. }
  1561. }
  1562. $date = new \DateTime();
  1563. $offset = $request->request->get('offset', 0);
  1564. $date->modify("first day of {$date->format('Y-m')}");
  1565. $date = $date->modify("{$offset} months");
  1566. $date->modify("first day of {$date->format('Y-m')}");
  1567. $date->setTime(0, 0, 0);
  1568. foreach ($lineArray as $key => $line) {
  1569. //0 child id
  1570. //1 teacher id
  1571. //2 discipline id
  1572. $keys = explode('-', $line['key']);
  1573. foreach ($line['days'] as $day => $element) {
  1574. $startTime = new \DateTime();
  1575. $startTime = $startTime->setDate((int)$date->format('Y'), (int)$date->format('m'), $day);
  1576. $startTime->setTime(0, 0, 0);
  1577. $endTime = new \DateTime();
  1578. $endTime = $endTime->setDate((int)$date->format('Y'), (int)$date->format('m'), $day);
  1579. $endTime->setTime(23, 59, 59);
  1580. $endTime->modify('+2 minutes');
  1581. $lessonsQuery = $lessonRepository->createQueryBuilder('l');
  1582. $lessons = $lessonsQuery
  1583. ->andWhere('l.teacher = :teacher')
  1584. ->setParameter('teacher', $keys[1])
  1585. ->andWhere('l.discipline = :discipline')
  1586. ->setParameter('discipline', $keys[2])
  1587. ->andWhere(':child MEMBER OF l.children')
  1588. ->setParameter('child', $keys[0])
  1589. ->andWhere('l.startTime >= :startTime')
  1590. ->setParameter('startTime', $startTime->format('Y-m-d H:i'))
  1591. ->andWhere('l.endTime <= :endTime')
  1592. ->setParameter('endTime', $endTime->format('Y-m-d H:i'))
  1593. ->getQuery()
  1594. ->getResult();
  1595. $lessonDeletedQuery = $lessonDeletedRepository->createQueryBuilder('ld');
  1596. $deletedLessons = $lessonDeletedQuery
  1597. ->andWhere('ld.teacher = :teacher')
  1598. ->setParameter('teacher', $keys[1])
  1599. ->andWhere('ld.child = :child')
  1600. ->setParameter('child', $keys[0])
  1601. ->andWhere('ld.discipline = :discipline')
  1602. ->setParameter('discipline', $keys[2])
  1603. ->andWhere('ld.startTime >= :startTime')
  1604. ->setParameter('startTime', $startTime->format('Y-m-d H:i'))
  1605. ->andWhere('ld.endTime <= :endTime')
  1606. ->setParameter('endTime', $endTime->format('Y-m-d H:i'))
  1607. ->getQuery()
  1608. ->getResult();
  1609. foreach ($lessons as $lesson) {
  1610. $lineArray[$key]['days'][$day][] = [
  1611. 'id' => $lesson->getId(),
  1612. 'duration' => $lesson->getDuration(),
  1613. 'startTime' => $lesson->getStartTime()->format('Y-m-d H:i'),
  1614. 'endTime' => $lesson->getEndTime()->format('Y-m-d H:i'),
  1615. 'status' => $lesson->getStatus(),
  1616. 'statusColor' => $lesson->getStatusColor(),
  1617. 'type' => $lesson->getType(),
  1618. ];
  1619. }
  1620. /** @var LessonDeleted $deletedLesson */
  1621. foreach ($deletedLessons as $deletedLesson) {
  1622. $lineArray[$key]['days'][$day][] = [
  1623. 'id' => $deletedLesson->getId().'_deleted',
  1624. 'duration' => $deletedLesson->getDuration(),
  1625. 'startTime' => $deletedLesson->getStartTime()->format('Y-m-d H:i'),
  1626. 'endTime' => $deletedLesson->getEndTime()->format('Y-m-d H:i'),
  1627. 'status' => 'Anuliuotas užsiėmimas',
  1628. 'statusColor' => '#fff',
  1629. 'type' => $deletedLesson->getType(),
  1630. ];
  1631. }
  1632. }
  1633. }
  1634. foreach ($lineArray as $key => $line) {
  1635. unset($lineArray[$key]['key']);
  1636. }
  1637. $page -= 1;
  1638. $pagesCount = ceil(count($lineArray) / $perPage);
  1639. $lineArray = array_slice($lineArray, $page * $perPage, $perPage);
  1640. return new JsonResponse([
  1641. "last_page" => $pagesCount,
  1642. "data" => $lineArray
  1643. ]);
  1644. }
  1645. /**
  1646. * @Route("/vacation_lessons/{id}", name="guardian_vacation_lessons_ajax", methods={"GET", "POST"})
  1647. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY') or is_granted('ROLE_GUARDIAN') and is_granted('IS_AUTHENTICATED_FULLY') or is_granted('ROLE_CHILD') and is_granted('IS_AUTHENTICATED_FULLY')")
  1648. * @param Request $request
  1649. * @param EntityManagerInterface $entityManager
  1650. * @return Response
  1651. * @throws \Doctrine\ORM\NoResultException
  1652. * @throws \Doctrine\ORM\NonUniqueResultException
  1653. * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
  1654. */
  1655. public function fetchVacationLessonsData(
  1656. Child $childObj,
  1657. Request $request,
  1658. LessonRepository $lessonRepository,
  1659. ChildRepository $childRepository,
  1660. EntityManagerInterface $entityManager,
  1661. MessageBusInterface $messageBus,
  1662. NotificationService $notificationService,
  1663. TeacherRepository $teacherRepository,
  1664. LessonController $lessonController,
  1665. LessonDeletedRepository $lessonDeletedRepository,
  1666. GuardianLogService $guardianLogService,
  1667. TeacherPaymentLogRepository $teacherPaymentLogRepository
  1668. ): Response
  1669. {
  1670. if($request->request->get('ignoreNotifications')) {
  1671. $newDates = [];
  1672. $parseHolidayDisciplineIds = function($raw): ?array {
  1673. if (!isset($raw) || $raw === null || $raw === '') return null;
  1674. $parsed = array_values(array_filter(array_map('intval', (array) $raw), fn($v) => $v > 0));
  1675. return !empty($parsed) ? $parsed : null;
  1676. };
  1677. $submittedHolidays = array_map(function($h) use ($parseHolidayDisciplineIds) {
  1678. return [
  1679. 'from' => $h['from'],
  1680. 'to' => $h['to'],
  1681. 'disciplineIds' => $parseHolidayDisciplineIds($h['disciplineIds'] ?? null),
  1682. ];
  1683. }, $request->request->get('holidays', []));
  1684. $filterActive = (bool) $request->request->get('filterActive', false);
  1685. $filteredDisciplinesParam = $request->request->get('filteredDisciplines', []);
  1686. $filteredDisciplineIds = $filterActive
  1687. ? array_map('intval', is_array($filteredDisciplinesParam) ? $filteredDisciplinesParam : [])
  1688. : null;
  1689. $submittedDisciplineIdsByRange = [];
  1690. foreach ($submittedHolidays as $h) {
  1691. $rangeKey = $this->vacationRangeKey($h['from'], $h['to']);
  1692. $submittedDisciplineIdsByRange[$rangeKey] = array_values(array_unique(array_merge(
  1693. $submittedDisciplineIdsByRange[$rangeKey] ?? [],
  1694. $h['disciplineIds'] ?? []
  1695. )));
  1696. }
  1697. $submittedNullGroupRanges = array_map(
  1698. fn($h) => $this->vacationRangeKey($h['from'], $h['to']),
  1699. array_filter($submittedHolidays, fn($h) => $h['disciplineIds'] === null)
  1700. );
  1701. foreach ($childObj->getChildVacations() as $vacation) {
  1702. if ($vacation->isIsDeleted()) {
  1703. continue;
  1704. }
  1705. $vacDiscIds = $this->normalizeVacationDisciplineIds($vacation->getDisciplineIds());
  1706. $rangeKey = $this->vacationRangeKey($vacation->getDateFrom(), $vacation->getDateTo());
  1707. if ($vacDiscIds === null) {
  1708. if ($filteredDisciplineIds !== null || in_array($rangeKey, $submittedNullGroupRanges, true)) {
  1709. continue;
  1710. }
  1711. $removedDisciplineIds = null;
  1712. $newDisciplineIds = [];
  1713. } else {
  1714. $relevantManagedIds = $filteredDisciplineIds === null
  1715. ? $vacDiscIds
  1716. : array_intersect($vacDiscIds, $filteredDisciplineIds);
  1717. if (empty($relevantManagedIds)) {
  1718. continue;
  1719. }
  1720. $keptOutOfScopeIds = $filteredDisciplineIds === null
  1721. ? []
  1722. : array_diff($vacDiscIds, $filteredDisciplineIds);
  1723. $submittedIdsForRange = $submittedDisciplineIdsByRange[$rangeKey] ?? [];
  1724. $survivingManagedIds = array_intersect($relevantManagedIds, $submittedIdsForRange);
  1725. $removedDisciplineIds = array_values(array_diff($relevantManagedIds, $survivingManagedIds));
  1726. if (empty($removedDisciplineIds)) {
  1727. continue;
  1728. }
  1729. $newDisciplineIds = array_values(array_unique(array_merge($survivingManagedIds, $keptOutOfScopeIds)));
  1730. }
  1731. if (empty($newDisciplineIds)) {
  1732. $vacation->setIsDeleted(true);
  1733. } else {
  1734. $vacation->setDisciplineIds($newDisciplineIds);
  1735. }
  1736. $entityManager->persist($vacation);
  1737. $entityManager->flush();
  1738. $this->restoreDeletedLessonsForVacation(
  1739. $childObj,
  1740. $vacation->getDateFrom(),
  1741. $vacation->getDateTo(),
  1742. $removedDisciplineIds,
  1743. $lessonDeletedRepository,
  1744. $lessonRepository,
  1745. $teacherPaymentLogRepository,
  1746. $entityManager
  1747. );
  1748. }
  1749. foreach ($submittedHolidays as $h) {
  1750. $hGroupKey = $h['disciplineIds'] !== null ? implode(',', $h['disciplineIds']) : 'null';
  1751. foreach ($childObj->getChildVacations() as $vacation) {
  1752. if ($vacation->isIsDeleted()) {
  1753. continue;
  1754. }
  1755. $vacDiscIds = $this->normalizeVacationDisciplineIds($vacation->getDisciplineIds());
  1756. $vacGroupKey = $vacDiscIds !== null ? implode(',', $vacDiscIds) : 'null';
  1757. if ($vacGroupKey === $hGroupKey &&
  1758. (new \DateTime($h['from']))->format('Y-m-d') === $vacation->getDateFrom()->format('Y-m-d') &&
  1759. (new \DateTime($h['to']))->format('Y-m-d') === $vacation->getDateTo()->format('Y-m-d')) {
  1760. continue 2;
  1761. }
  1762. }
  1763. $disciplineIds = $h['disciplineIds'];
  1764. if ($disciplineIds === null) {
  1765. $currentIds = [];
  1766. foreach ($childObj->getChildPreferences() as $cp) {
  1767. if ($cp->getEndTime() === null && $cp->getDiscipline()) {
  1768. $currentIds[] = $cp->getDiscipline()->getId();
  1769. }
  1770. }
  1771. $disciplineIds = array_values(array_unique($currentIds));
  1772. }
  1773. $childVacation = new ChildVacation();
  1774. $childVacation->setChild($childObj);
  1775. $childVacation->setDateAdd(new \DateTime());
  1776. $childVacation->setAddedByAdmin($this->getUser());
  1777. $childVacation->setDateFrom(new \DateTime($h['from']));
  1778. $childVacation->setDateTo(new \DateTime($h['to']));
  1779. $childVacation->setDisciplineIds($disciplineIds);
  1780. $entityManager->persist($childVacation);
  1781. $entityManager->flush();
  1782. $newDates[] = ['from' => new \DateTime($h['from']), 'to' => new \DateTime($h['to']), 'disciplineIds' => $disciplineIds];
  1783. }
  1784. $disciplines = [];
  1785. $lessonDates = [];
  1786. $teachers = [];
  1787. //Guardian Email
  1788. foreach ($newDates as $newDate) {
  1789. $newDateDiscIds = $newDate['disciplineIds'];
  1790. if ($newDateDiscIds !== null && empty($newDateDiscIds)) {
  1791. continue;
  1792. }
  1793. $query = $lessonRepository->createQueryBuilder('l');
  1794. $query
  1795. ->andWhere(':child MEMBER OF l.children')
  1796. ->setParameter('child', $childObj)
  1797. ->andWhere('l.startTime >= :from')
  1798. ->setParameter('from', $newDate['from'])
  1799. ->andWhere('l.startTime <= :to')
  1800. ->setParameter('to', $newDate['to']);
  1801. if (!empty($newDateDiscIds)) {
  1802. $query->andWhere('l.discipline IN (:disciplines)')
  1803. ->setParameter('disciplines', $newDateDiscIds);
  1804. }
  1805. $lessons = $query->getQuery()->getResult();
  1806. /** @var Lesson $lesson */
  1807. foreach ($lessons as $lesson) {
  1808. $disciplines[] = $lesson->getDiscipline()->getName();
  1809. $lessonDates[] = $lesson->getStartTime()->format('Y-m-d H:i');
  1810. if (!isset($teachers["{$lesson->getTeacher()->getId()}"])) {
  1811. $teachers["{$lesson->getTeacher()->getId()}"] = ['disciplines' => [], 'lessonDates' => []];
  1812. }
  1813. $teachers["{$lesson->getTeacher()->getId()}"]['disciplines'][] = $lesson->getDiscipline()->getName();
  1814. $teachers["{$lesson->getTeacher()->getId()}"]['lessonDates'][] = $lesson->getStartTime()->format('Y-m-d H:i');
  1815. $lessonController->makeDeletedLessonLog($lesson, 'Mokinio atostogos (Automatinis funkcionaluams)');
  1816. $mailSent = false;
  1817. if($request->request->get('ignoreNotifications') == 'false') {
  1818. $mailSent = true;
  1819. }
  1820. $guardianLogService->addGuardianActionLog("Ištrintas užsiėmimas {$lesson->getStartTime()->format('Y-m-d H:i')}", $lesson->getChild()->getGuardian(), null, 'delete', $mailSent);
  1821. $entityManager->flush();
  1822. }
  1823. }
  1824. $disciplines = array_unique($disciplines);
  1825. $replacements = [
  1826. '[CHILD_NAME]' => $childObj->getFullname(),
  1827. '[DISCIPLINE_SHORT_NAME]' => implode(', ', $disciplines),
  1828. '[VACATION_LESSON_DATES]' => implode('<br>', $lessonDates),
  1829. ];
  1830. if($request->request->get('ignoreNotifications') == 'false') {
  1831. $messageBus->dispatch(new SendMailMessage($childObj->getGuardian()->getEmail(), '', '', $replacements, 'CHILD_VACATION_CREATED_GUARDIAN'));
  1832. foreach ($teachers as $teacherId => $data) {
  1833. $disciplines = $data['disciplines'];
  1834. $disciplines = array_unique($disciplines);
  1835. $replacements = [
  1836. '[CHILD_NAME]' => $childObj->getFullname(),
  1837. '[DISCIPLINE_SHORT_NAME]' => implode(', ', $disciplines),
  1838. '[VACATION_LESSON_DATES]' => implode('<br>', $data['lessonDates']),
  1839. ];
  1840. $teacher = $teacherRepository->find($teacherId);
  1841. $notificationService->sendNotificationTemplate($teacher, 'CHILD_VACATION_CREATED_TUTOR', $replacements);
  1842. }
  1843. }
  1844. return new JsonResponse(['success' => true]);
  1845. }
  1846. $user = $this->getUser();
  1847. if (in_array("ROLE_GUARDIAN", $user->getRoles())) {
  1848. if ($childObj->getGuardian()->getId() != $user->getId()) {
  1849. return new JsonResponse(null, 401);
  1850. }
  1851. }
  1852. if (in_array("ROLE_CHILD", $user->getRoles())) {
  1853. if ($childObj->getId() != $user->getId()) {
  1854. return new JsonResponse(null, 401);
  1855. }
  1856. }
  1857. $date = new \DateTime();
  1858. $offset = $request->request->get('offset', 0);
  1859. $date->modify("first day of {$date->format('Y-m')}");
  1860. $date = $date->modify("{$offset} months");
  1861. $date->modify("first day of {$date->format('Y-m')}");
  1862. $date->setTime(0, 0, 0);
  1863. $end = new \DateTime($date->format('Y-m-t'));
  1864. $end = $end->setTime(23, 59, 59);
  1865. $objectRepository = $lessonRepository;
  1866. $query = $objectRepository->createQueryBuilder('a');
  1867. $query
  1868. ->leftJoin('a.children', 'c')
  1869. ->andWhere('c.id = :child')
  1870. ->setParameter("child", $childObj->getId());
  1871. $date = new \DateTime();
  1872. $offset = $request->request->get('offset', 0);
  1873. $date->modify("first day of {$date->format('Y-m')}");
  1874. $date = $date->modify("{$offset} months");
  1875. $date->modify("first day of {$date->format('Y-m')}");
  1876. $date->setTime(0, 0, 0);
  1877. $end = new \DateTime($date->format('Y-m-t'));
  1878. $end = $end->setTime(23, 59, 59);
  1879. $end->modify('+2 minutes');
  1880. $lessonsArray = [];
  1881. $query = $lessonRepository->createQueryBuilder('a');
  1882. $query
  1883. ->andWhere('a.startTime >= :startTime')
  1884. ->setParameter('startTime', $date->format('Y-m-d H:i'))
  1885. ->andWhere('a.endTime <= :endTime')
  1886. ->setParameter('endTime', $end->format('Y-m-d H:i'))
  1887. ->andWhere(':child MEMBER OF a.children')
  1888. ->setParameter('child', $childObj->getId());
  1889. $lessons = $query->getQuery()->getResult();
  1890. /**
  1891. * @var $lesson Lesson
  1892. */
  1893. foreach ($lessons as $lesson) {
  1894. $lessonsArray [] = [
  1895. 'start' => $lesson->getStartTime()->format('Y-m-d H:i'),
  1896. 'end' => $lesson->getEndTime()->format('Y-m-d H:i'),
  1897. 'status' => $lesson->getStatus(),
  1898. 'statusColor' => $lesson->getStatusColor(),
  1899. 'disciplineId' => $lesson->getDiscipline() ? $lesson->getDiscipline()->getId() : null,
  1900. 'disciplineName' => $lesson->getDiscipline() ? $lesson->getDiscipline()->getName() : null,
  1901. ];
  1902. }
  1903. $childDisciplines = [];
  1904. $seenDisciplineIds = [];
  1905. foreach ($childObj->getChildPreferences() as $pref) {
  1906. if ($pref->getDiscipline() === null) {
  1907. continue;
  1908. }
  1909. if (in_array($pref->getStatus(), [\App\Entity\ChildPreference::STATUS_CANCELLED['value'], \App\Entity\ChildPreference::STATUS_NOT_FOUND['value']])) {
  1910. continue;
  1911. }
  1912. $disc = $pref->getDiscipline();
  1913. if (!in_array($disc->getId(), $seenDisciplineIds)) {
  1914. $childDisciplines[] = ['id' => $disc->getId(), 'name' => $disc->getName(), 'color' => \App\Constants\DisciplineColors::getColor($disc->getName())];
  1915. $seenDisciplineIds[] = $disc->getId();
  1916. }
  1917. }
  1918. $currentVacations = [];
  1919. foreach ($childObj->getChildVacations() as $vacation) {
  1920. if($vacation->isIsDeleted())
  1921. {
  1922. continue;
  1923. }
  1924. $currentVacations[] = [
  1925. 'from' => $vacation->getDateFrom()->format('Y-m-d H:i'),
  1926. 'to' => $vacation->getDateTo()->format('Y-m-d H:i'),
  1927. 'disciplineIds' => $this->normalizeVacationDisciplineIds($vacation->getDisciplineIds()),
  1928. ];
  1929. }
  1930. $alreadyDeleted = [];
  1931. foreach ($childObj->getChildVacations() as $vacation) {
  1932. if($vacation->isIsDeleted())
  1933. {
  1934. continue;
  1935. }
  1936. $query = $lessonDeletedRepository->createQueryBuilder('l');
  1937. $deletedListByAdmin = $query
  1938. ->andWhere('l.startTime >= :dateFrom')
  1939. ->setParameter('dateFrom', $vacation->getDateFrom())
  1940. ->andWhere('l.startTime <= :dateTo')
  1941. ->setParameter('dateTo', $vacation->getDateTo())
  1942. ->andWhere('l.child = :child')
  1943. ->setParameter('child', $childObj)
  1944. ->getQuery()
  1945. ->getResult();
  1946. /**
  1947. * @var LessonDeleted $deletedByAdmin
  1948. */
  1949. foreach ($deletedListByAdmin as $deletedByAdmin) {
  1950. $alreadyInList = false;
  1951. foreach ($alreadyDeleted as $alreadyDeletedLesson)
  1952. {
  1953. if($alreadyDeletedLesson['start'] == $deletedByAdmin->getStartTime()->format('Y-m-d H:i'))
  1954. {
  1955. $alreadyInList = true;
  1956. break;
  1957. }
  1958. }
  1959. if(!$alreadyInList) {
  1960. $alreadyDeleted[] = [
  1961. 'start' => $deletedByAdmin->getStartTime()->format('Y-m-d H:i'),
  1962. 'end' => $deletedByAdmin->getEndTime()->format('Y-m-d H:i'),
  1963. 'status' => $deletedByAdmin->getStatus(),
  1964. "date" => $deletedByAdmin->getStartTime()->format('Y-m-d H:i:s'),
  1965. 'reason' => $deletedByAdmin->getDeleteReason()
  1966. ];
  1967. }
  1968. }
  1969. }
  1970. return new JsonResponse([
  1971. "data" => $lessonsArray,
  1972. 'currentVacations' => $currentVacations,
  1973. 'alreadyDeletedByAdmin' => $alreadyDeleted,
  1974. 'disciplines' => $childDisciplines,
  1975. ]);
  1976. }
  1977. /**
  1978. * @Route("/new", name="guardian_new", methods={"GET","POST"})
  1979. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  1980. */
  1981. public function new(
  1982. Request $request,
  1983. GuardianContractRepository $guardianContractRepository,
  1984. PriceIncreaseService $priceIncreaseService,
  1985. MailerInterface $mailer,
  1986. ConfigurationRepository $configurationRepository,
  1987. EntityManagerInterface $entityManager
  1988. ): Response
  1989. {
  1990. $guardian = new Guardian();
  1991. $contract = $guardianContractRepository->findLatestForNewRegistration(
  1992. new \DateTime(),
  1993. $priceIncreaseService->getDuplicateMarkerLikePattern()
  1994. );
  1995. $guardian->setGuardianContract($contract);
  1996. $form = $this->createForm(GuardianType::class, $guardian);
  1997. $form->handleRequest($request);
  1998. if ($form->isSubmitted() && $form->isValid()) {
  1999. if ($guardian->isFlagEarlyPayment()) {
  2000. $guardian->setEarlyPaymentFrom(new \DateTime());
  2001. }
  2002. foreach ($guardian->getPriceChanges() as $priceChange) {
  2003. $priceChange->setGuardian($guardian);
  2004. $entityManager->persist($priceChange);
  2005. }
  2006. foreach ($guardian->getGuardianPriceDiscounts() as $guardianPriceDiscount) {
  2007. $guardianPriceDiscount->setGuardian($guardian);
  2008. $entityManager->persist($guardianPriceDiscount);
  2009. }
  2010. $requestData = $request->request->all();
  2011. $entityManager = $this->getDoctrine()->getManager();
  2012. $plainpwd = $guardian->getPassword();
  2013. if ($plainpwd) {
  2014. $encoded = $this->userPasswordHasher->hashPassword($guardian, $plainpwd);
  2015. $guardian->setPassword($encoded);
  2016. }
  2017. if ($plainpwd == null) {
  2018. $guardian->setPassword('');
  2019. }
  2020. $entityManager->persist($guardian);
  2021. $entityManager->flush();
  2022. return $this->redirectToRoute('guardian_show', ['id' => $guardian->getId()], Response::HTTP_SEE_OTHER);
  2023. }
  2024. $defaultGuardianPriceChange = $configurationRepository->findOneBy(['name' => 'DEFAULT_GUARDIAN_PRICE_CHANGE']);
  2025. return $this->renderForm('guardian/new.html.twig', [
  2026. 'guardian' => $guardian,
  2027. 'form' => $form,
  2028. 'defaultGuardianPriceChange' => $defaultGuardianPriceChange->getValue(),
  2029. ]);
  2030. }
  2031. /**
  2032. * @Route("/{id}/convert-to-early-payment", name="guardian_convert_to_early_payment", methods={"POST"})
  2033. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2034. */
  2035. public function convertToEarlyPayment(
  2036. Guardian $guardian,
  2037. Request $request,
  2038. GuardianEarlyPaymentConversionService $conversionService,
  2039. LoggerInterface $logger
  2040. ): JsonResponse {
  2041. $data = $request->toArray();
  2042. $earlyPaymentDay = isset($data['earlyPaymentDay']) ? (int) $data['earlyPaymentDay'] : 0;
  2043. if ($earlyPaymentDay < 1 || $earlyPaymentDay > 28) {
  2044. return new JsonResponse([
  2045. 'status' => 'error',
  2046. 'message' => 'Mokėjimo diena turi būti nuo 1 iki 28.',
  2047. ], Response::HTTP_BAD_REQUEST);
  2048. }
  2049. try {
  2050. $result = $conversionService->convert($guardian, $earlyPaymentDay);
  2051. return new JsonResponse([
  2052. 'status' => 'OK',
  2053. 'balance' => $result['balance'],
  2054. 'debt' => $result['debt'],
  2055. 'pastLessonCount' => $result['pastLessonCount'],
  2056. 'futureLessonCount' => $result['futureLessonCount'],
  2057. ]);
  2058. } catch (\InvalidArgumentException $e) {
  2059. $logger->warning('Guardian early payment conversion rejected', [
  2060. 'guardianId' => $guardian->getId(),
  2061. 'earlyPaymentDay' => $earlyPaymentDay,
  2062. 'message' => $e->getMessage(),
  2063. ]);
  2064. return new JsonResponse([
  2065. 'status' => 'error',
  2066. 'message' => $e->getMessage(),
  2067. ], Response::HTTP_BAD_REQUEST);
  2068. } catch (\Throwable $e) {
  2069. $logger->error('Guardian early payment conversion failed', [
  2070. 'guardianId' => $guardian->getId(),
  2071. 'earlyPaymentDay' => $earlyPaymentDay,
  2072. 'exception' => $e,
  2073. ]);
  2074. return new JsonResponse([
  2075. 'status' => 'error',
  2076. 'message' => 'Konvertavimas nepavyko.',
  2077. ], Response::HTTP_INTERNAL_SERVER_ERROR);
  2078. }
  2079. }
  2080. /**
  2081. * @Route("/{id}/convert-to-regular-payment", name="guardian_convert_to_regular_payment", methods={"POST"})
  2082. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2083. */
  2084. public function convertToRegularPayment(
  2085. Guardian $guardian,
  2086. GuardianEarlyPaymentConversionService $conversionService,
  2087. EarlyPaymentService $earlyPaymentService,
  2088. LoggerInterface $logger
  2089. ): JsonResponse {
  2090. try {
  2091. $result = $conversionService->convertToRegular($guardian, $earlyPaymentService);
  2092. return new JsonResponse([
  2093. 'status' => 'OK',
  2094. 'revertedLessonCount' => $result['revertedLessonCount'],
  2095. 'creditedAmount' => $result['creditedAmount'],
  2096. 'balanceCarriedAsCorrection' => $result['balanceCarriedAsCorrection'],
  2097. 'abandonedEarlyPaymentCount' => $result['abandonedEarlyPaymentCount'],
  2098. ]);
  2099. } catch (\InvalidArgumentException $e) {
  2100. $logger->warning('Guardian regular payment conversion rejected', [
  2101. 'guardianId' => $guardian->getId(),
  2102. 'message' => $e->getMessage(),
  2103. ]);
  2104. return new JsonResponse([
  2105. 'status' => 'error',
  2106. 'message' => $e->getMessage(),
  2107. ], Response::HTTP_BAD_REQUEST);
  2108. } catch (\Throwable $e) {
  2109. $logger->error('Guardian regular payment conversion failed', [
  2110. 'guardianId' => $guardian->getId(),
  2111. 'exception' => $e,
  2112. ]);
  2113. return new JsonResponse([
  2114. 'status' => 'error',
  2115. 'message' => 'Konvertavimas nepavyko.',
  2116. ], Response::HTTP_INTERNAL_SERVER_ERROR);
  2117. }
  2118. }
  2119. /**
  2120. * @Route("/{id}", name="guardian_show", methods={"GET"})
  2121. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2122. */
  2123. public function show(Guardian $guardian, AuthLogRepository $authLogRepository, LessonRepository $lessonRepository): Response
  2124. {
  2125. $lastSuccessAuth = $authLogRepository->findBy(['email'=>$guardian->getEmail()]);
  2126. $vacations = [];
  2127. foreach ($guardian->getChildren() as $child){
  2128. foreach ($child->getChildVacations() as $vacation){
  2129. $alreadyExists = false;
  2130. foreach ($vacations as $key => $vacationArrayItem){
  2131. if($vacationArrayItem['from'] == $vacation->getDateFrom() && $vacationArrayItem['to'] == $vacation->getDateTo())
  2132. {
  2133. $alreadyExists = true;
  2134. break;
  2135. }
  2136. }
  2137. if(!$alreadyExists) {
  2138. $vacations[] = [
  2139. 'from' => $vacation->getDateFrom(),
  2140. 'to' => $vacation->getDateTo(),
  2141. 'child' => $vacation->getChild()->getFullname(),
  2142. 'isDeleted' => $vacation->isIsDeleted()
  2143. ];
  2144. }
  2145. else
  2146. {
  2147. $vacations[$key]['child'] .= ",{$vacation->getChild()->getFullname()}";
  2148. }
  2149. }
  2150. }
  2151. usort($vacations, fn($a, $b) => $b['from'] <=> $a['from']);
  2152. $totalLessons = $lessonRepository
  2153. ->createQueryBuilder('l')
  2154. ->select('COUNT(l)')
  2155. ->andWhere('l.status in (:status)')
  2156. ->setParameter('status', [Lesson::STATUS_REGULAR['value'],Lesson::STATUS_REGULAR_MOVED['value'],Lesson::STATUS_ADDITIONAL['value'], Lesson::STATUS_CHILD_UNINFORMED_MISSED['value'], Lesson::STATUS_FREE['value'], Lesson::STATUS_GIFT['value']])
  2157. ->andWhere('l.startTime >= :startTime')
  2158. ->setParameter('startTime', new \DateTime('2024-12-01 00:00:00'))
  2159. ->andWhere('l.startTime <= :now')
  2160. ->setParameter('now', new \DateTime())
  2161. ->andWhere(':children member of l.children')
  2162. ->setParameter('children', $guardian->getChildren())
  2163. ->getQuery()
  2164. ->getSingleScalarResult();
  2165. $eightLesson = $lessonRepository
  2166. ->createQueryBuilder('l')
  2167. ->andWhere('l.status in (:status)')
  2168. ->setParameter('status', [Lesson::STATUS_REGULAR['value'],Lesson::STATUS_REGULAR_MOVED['value'],Lesson::STATUS_ADDITIONAL['value'], Lesson::STATUS_CHILD_UNINFORMED_MISSED['value'], Lesson::STATUS_FREE['value'], Lesson::STATUS_GIFT['value']])
  2169. ->andWhere('l.startTime >= :startTime')
  2170. ->setParameter('startTime', new \DateTime('2024-12-01 00:00:00'))
  2171. ->andWhere('l.startTime <= :now')
  2172. ->setParameter('now', new \DateTime())
  2173. ->andWhere(':children member of l.children')
  2174. ->setParameter('children', $guardian->getChildren())
  2175. ->setFirstResult(7)
  2176. ->setMaxResults(1)
  2177. ->getQuery()
  2178. ->getOneOrNullResult();
  2179. return $this->render('guardian/show.html.twig', [
  2180. 'vacations'=> $vacations,
  2181. 'guardian' => $guardian,
  2182. 'lastAuth'=> $lastSuccessAuth ? end($lastSuccessAuth) : "",
  2183. 'totalLessons' => $totalLessons,
  2184. 'eightLesson' => $eightLesson,
  2185. ]);
  2186. }
  2187. /**
  2188. * @Route("/{id}/recalculate", name="guardian_recalculate", methods={"GET"})
  2189. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2190. */
  2191. public function guardian_recalculate(Guardian $guardian, AuthLogRepository $authLogRepository, LessonRepository $lessonRepository): Response
  2192. {
  2193. $lessons = $lessonRepository
  2194. ->createQueryBuilder('l')
  2195. ->andWhere('l.startTime > :startTime')
  2196. ->setParameter('startTime', new \DateTime('2025-10-01 00:00:00'))
  2197. ->andWhere(':children MEMBER OF l.children')
  2198. ->setParameter('children', $guardian->getChildren())
  2199. ->getQuery()
  2200. ->getResult();
  2201. foreach ($lessons as $lesson) {
  2202. $this->messageBus->dispatch(new RecalculateLessonMessage($lesson->getId()));;
  2203. }
  2204. die('Pamoku kainos perskaiciavimas ijungtas nuo 2025-10-01 00:00:00');
  2205. }
  2206. /**
  2207. * @Route("/{id}/edit", name="guardian_edit", methods={"GET","POST"})
  2208. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2209. */
  2210. public function edit(Request $request, Guardian $guardian, MailerInterface $mailer, ReminderConfigurationRepository $reminderConfigurationRepository, ChildRepository $childRepository, EntityManagerInterface $entityManager, PaymentService $paymentService, LessonRepository $lessonRepository): Response
  2211. {
  2212. $autumn2024back = $guardian->isAutumn2024back();
  2213. $progress = $guardian->isProgress();
  2214. $notCommingBack2024 = $guardian->isNotCommingBack2024();
  2215. $summer = $guardian->getSummer();
  2216. $form = $this->createForm(GuardianType::class, $guardian);
  2217. $encoded = $guardian->getPassword();
  2218. $originalPriceChanges = $guardian->getPriceChanges()->getValues();
  2219. $originalGuardianPriceDiscounts = $guardian->getGuardianPriceDiscounts()->getValues();
  2220. $originalStatus = $guardian->getStatus();
  2221. $recalculatePeriods = [];
  2222. $form->handleRequest($request);
  2223. if ($form->isSubmitted() && $form->isValid()) {
  2224. if($progress != $guardian->isProgress() && $guardian->isProgress())
  2225. {
  2226. $guardian->setProgressByAdmin($this->getUser());
  2227. $guardian->setProgressDate(new \DateTime());
  2228. }
  2229. if($notCommingBack2024 != $guardian->isNotCommingBack2024() && $guardian->isNotCommingBack2024())
  2230. {
  2231. $guardian->setNotCommingBack2024ByAdmin($this->getUser());
  2232. $guardian->setNotCommingBack2024Date(new \DateTime());
  2233. }
  2234. if($autumn2024back != $guardian->isAutumn2024back() && $guardian->isAutumn2024back())
  2235. {
  2236. $guardian->setAutumn2024backByAdmin($this->getUser());
  2237. $guardian->setAutumn2024backDate(new \DateTime());
  2238. }
  2239. if($summer != $guardian->getSummer() && $guardian->getSummer())
  2240. {
  2241. $guardian->setSummerByAdmin($this->getUser());
  2242. $guardian->setSummerDate(new \DateTime());
  2243. }
  2244. if ($guardian->isFlagEarlyPayment() && !$guardian->getEarlyPaymentFrom()) {
  2245. $guardian->setEarlyPaymentFrom(new \DateTime());
  2246. }
  2247. foreach ($originalPriceChanges as $originalPriceChange) {
  2248. if (!$guardian->getPriceChanges()->contains($originalPriceChange)) {
  2249. $entityManager->remove($originalPriceChange);
  2250. }
  2251. }
  2252. foreach ($guardian->getPriceChanges() as $priceChange) {
  2253. $priceChange->setGuardian($guardian);
  2254. $entityManager->persist($priceChange);
  2255. }
  2256. foreach ($originalGuardianPriceDiscounts as $originalGuardianPriceDiscount) {
  2257. if (!$guardian->getGuardianPriceDiscounts()->contains($originalGuardianPriceDiscount)) {
  2258. $recalculatePeriods[] = ['from' => $originalGuardianPriceDiscount->getDateFrom(), 'to' => $originalGuardianPriceDiscount->getDateTo()];
  2259. $entityManager->remove($originalGuardianPriceDiscount);
  2260. }
  2261. }
  2262. foreach ($guardian->getGuardianPriceDiscounts() as $guardianPriceDiscount) {
  2263. $guardianPriceDiscount->setGuardian($guardian);
  2264. $entityManager->persist($guardianPriceDiscount);
  2265. $recalculatePeriods[] = ['from' => $guardianPriceDiscount->getDateFrom(), 'to' => $guardianPriceDiscount->getDateTo()];
  2266. }
  2267. $plainpwd = $guardian->getPassword();
  2268. if ($guardian->getPassword()) {
  2269. $encoded = $this->userPasswordHasher->hashPassword($guardian, $plainpwd);
  2270. }
  2271. $guardian->setPassword($encoded);
  2272. $entityManager->persist($guardian);
  2273. $entityManager->flush();
  2274. foreach ($recalculatePeriods as $recalculatePeriod)
  2275. {
  2276. $paymentService->recalculateGuardianLessons($guardian, $recalculatePeriod['from'], $recalculatePeriod['to']);
  2277. }
  2278. if($guardian == Guardian::STATUS_CONFIRMED['text'] && $originalStatus != Guardian::STATUS_CONFIRMED['text'])
  2279. {
  2280. $this->generateAndSendChildrenCredentials($request, $guardian, $mailer, $reminderConfigurationRepository, $childRepository, $entityManager);
  2281. }
  2282. return $this->redirectToRoute('guardian_index', [], Response::HTTP_SEE_OTHER);
  2283. }
  2284. $guardianPriceChanges = $this->get('serializer')->normalize($guardian->getPriceChanges(), null, ['groups' => "GuardianPriceChangesEdit"]);
  2285. $childPreferences = [];
  2286. foreach ($guardian->getChildren() as $child){
  2287. foreach ($child->getChildPreferences() as $childPreference) {
  2288. $childPreferences[]=['name'=>$childPreference->getAdminUser()?$childPreference->getAdminUser()->getFullname():"Nėra",'discipline'=>$childPreference->getDiscipline()->getName()];
  2289. }
  2290. }
  2291. $totalLessons = $lessonRepository
  2292. ->createQueryBuilder('l')
  2293. ->select('COUNT(l)')
  2294. ->andWhere('l.status in (:status)')
  2295. ->setParameter('status', [Lesson::STATUS_REGULAR['value'],Lesson::STATUS_REGULAR_MOVED['value'],Lesson::STATUS_ADDITIONAL['value'], Lesson::STATUS_CHILD_UNINFORMED_MISSED['value'], Lesson::STATUS_FREE['value'], Lesson::STATUS_GIFT['value']])
  2296. ->andWhere('l.startTime >= :startTime')
  2297. ->setParameter('startTime', new \DateTime('2024-12-01 00:00:00'))
  2298. ->andWhere('l.startTime <= :now')
  2299. ->setParameter('now', new \DateTime())
  2300. ->andWhere(':children member of l.children')
  2301. ->setParameter('children', $guardian->getChildren())
  2302. ->getQuery()
  2303. ->getSingleScalarResult();
  2304. $eightLesson = $lessonRepository
  2305. ->createQueryBuilder('l')
  2306. ->select('COUNT(l)')
  2307. ->andWhere('l.status in (:status)')
  2308. ->setParameter('status', [Lesson::STATUS_REGULAR['value'],Lesson::STATUS_REGULAR_MOVED['value'],Lesson::STATUS_ADDITIONAL['value'], Lesson::STATUS_CHILD_UNINFORMED_MISSED['value'], Lesson::STATUS_FREE['value'], Lesson::STATUS_GIFT['value']])
  2309. ->andWhere('l.startTime >= :startTime')
  2310. ->setParameter('startTime', new \DateTime('2024-12-01 00:00:00'))
  2311. ->andWhere('l.startTime <= :now')
  2312. ->setParameter('now', new \DateTime())
  2313. ->andWhere(':children member of l.children')
  2314. ->setParameter('children', $guardian->getChildren())
  2315. ->setFirstResult(7)
  2316. ->setMaxResults(1)
  2317. ->getQuery()
  2318. ->getOneOrNullResult();
  2319. return $this->renderForm('guardian/edit.html.twig', [
  2320. 'guardian' => $guardian,
  2321. 'form' => $form,
  2322. 'guardian_price_changes' => $guardianPriceChanges,
  2323. 'admin_log_disciplines' =>$childPreferences,
  2324. 'totalLessons' => $totalLessons,
  2325. 'eightLesson' => $eightLesson,
  2326. ]);
  2327. }
  2328. /**
  2329. * @Route("/{id}/new_price_email", name="guardian_new_price_email", methods={"GET"})
  2330. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2331. */
  2332. public function guardian_new_price_email(Request $request, Guardian $guardian, EmailLogRepository $emailLogRepository): Response
  2333. {
  2334. $email = $emailLogRepository->createQueryBuilder('e')
  2335. ->andWhere('e.email = :email')
  2336. ->setParameter('email', $guardian->getEmail())
  2337. ->andWhere('e.subject like :subject')
  2338. ->setParameter('subject', "Svarbi informacija: paslaugų įkainių pasikeitimas | Corepetitus korepetitoriai%")
  2339. ->setMaxResults(1)
  2340. ->getQuery()
  2341. ->getOneOrNullResult();
  2342. if($email)
  2343. {
  2344. echo $email->getContent();
  2345. die();
  2346. }
  2347. die();
  2348. }
  2349. /**
  2350. * @Route("/delete/{id}", name="guardian_delete", methods={"POST","DELETE"})
  2351. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2352. */
  2353. public function delete(Request $request, Guardian $guardian): Response
  2354. {
  2355. $entityManager = $this->getDoctrine()->getManager();
  2356. if ($guardian->getStatus() == Guardian::STATUS_PREPARING['text'] || $guardian->getStatus() == Guardian::STATUS_SENT['text']) {
  2357. $entityManager->remove($guardian);
  2358. $entityManager->flush();
  2359. } elseif ($guardian->getStatus() == Guardian::STATUS_CANCELLED['text']) {
  2360. $guardian->setIsDeleted(1);
  2361. $entityManager->persist($guardian);
  2362. $entityManager->flush();
  2363. }
  2364. //hard delete
  2365. if(false)
  2366. {
  2367. foreach ($guardian->getChildren() as $child) {
  2368. foreach ($child->getChildPreferenceHistories() as $childPreferenceHistory)
  2369. {
  2370. $entityManager->remove($childPreferenceHistory);
  2371. }
  2372. $entityManager->remove($child);
  2373. }
  2374. foreach ($guardian->getPriceChanges() as $priceChange)
  2375. {
  2376. $entityManager->remove($priceChange);
  2377. }
  2378. $entityManager->remove($guardian);
  2379. $entityManager->flush();
  2380. }
  2381. return $this->redirectToRoute('guardian_index', [], Response::HTTP_SEE_OTHER);
  2382. }
  2383. /**
  2384. * @Route("/{id}/new/child", name="guardian_child_new", methods={"GET","POST"})
  2385. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2386. */
  2387. public function newChild(Request $request, Guardian $guardian, ChildRepository $childRepository, MailerInterface $mailer, ReminderConfigurationRepository $reminderConfigurationRepository): Response
  2388. {
  2389. $child = new Child();
  2390. $child->setGuardian($guardian);
  2391. $form = $this->createForm(ChildType::class, $child);
  2392. $form->handleRequest($request);
  2393. $childrenCredentials = '';
  2394. if ($form->isSubmitted() && $form->isValid()) {
  2395. $entityManager = $this->getDoctrine()->getManager();
  2396. if($guardian->getStatus() == Guardian::STATUS_CONFIRMED['text'])
  2397. {
  2398. $plainpwd = HelperUtils::generateLithuanianPassword(1,4);
  2399. $encoded = $this->userPasswordHasher->hashPassword($child, $plainpwd);
  2400. $child->setPassword($encoded);
  2401. $username = $child->getUsername();
  2402. if(strpos($username,'cor') == false) {
  2403. $username = HelperUtils::removeLtLetters(strtolower(explode(' ', $child->getFullname())[0]));
  2404. if (!$username) {
  2405. $username = HelperUtils::generateLithuanianPassword(1, 0);
  2406. }
  2407. $username = $username . 'cor';
  2408. $counter = 1;
  2409. while ($childRepository->findOneBy(['username' => $username . $counter])) {
  2410. $counter++;
  2411. if ($counter == 69) {
  2412. $counter++;
  2413. }
  2414. }
  2415. $username = $username . $counter;
  2416. $child->setUsername($username);
  2417. $childrenCredentials .= "
  2418. <b>Mokinys:</b> {$child->getFullname()}<br>
  2419. <b>Mokinio prisijungimo vardas:</b> {$username}<br>
  2420. <b>Slaptažodis:</b> {$plainpwd}<br><br>
  2421. ";
  2422. }
  2423. $replacements = [];
  2424. $replacements['[CHILD_NAME]'] = $child->getFullname();
  2425. $replacements['[CHILD_EMAIL]'] = $child->getEmail();
  2426. $replacements['[CHILD_USERNAME]'] = $child->getEmail() ?: $child->getUsername();
  2427. $replacements['[CHILD_PASSWORD]'] = $plainpwd;
  2428. $replacements['[CHILDREN_CREDENTIALS]'] = $childrenCredentials;
  2429. $this->messageBus->dispatch(new SendMailMessage($guardian->getEmail(), '', '', $replacements, 'GUARDIAN_CHILDREN_CREDENTIALS'));
  2430. if($child->getEmail())
  2431. {
  2432. $this->messageBus->dispatch(new SendMailMessage($child->getEmail(), '', '', $replacements, 'CHILD_NEW_CREDENTIALS'));
  2433. }
  2434. }
  2435. $token = HelperUtils::createRandomString();
  2436. while($childRepository->findOneBy(['loginToken' => $token]))
  2437. {
  2438. $token = HelperUtils::createRandomString();
  2439. }
  2440. $child->setLoginToken($token);
  2441. $entityManager->persist($child);
  2442. $entityManager->flush();
  2443. return $this->redirectToRoute('guardian_show', ['id' => $guardian->getId()], Response::HTTP_SEE_OTHER);
  2444. }
  2445. return $this->renderForm('child/new.html.twig', [
  2446. 'child' => $child,
  2447. 'form' => $form,
  2448. ]);
  2449. }
  2450. /**
  2451. * @Route("/{id}/children_credentials", name="guardian_children_credentials", methods={"GET","POST"})
  2452. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2453. */
  2454. public function generateAndSendChildrenCredentials(Request $request, CredentialsService $credentialsService, Guardian $guardian, MailerInterface $mailer, ReminderConfigurationRepository $reminderConfigurationRepository, ChildRepository $childRepository, EntityManagerInterface $entityManager): Response
  2455. {
  2456. $credentialsService->sendChildrenCredentialsEmail($guardian);
  2457. return $this->redirectToRoute('guardian_show', ['id' => $guardian->getId()], Response::HTTP_SEE_OTHER);
  2458. }
  2459. /**
  2460. * @Route("/{id}/child_credentials", name="guardian_child_credentials", methods={"GET","POST"})
  2461. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2462. */
  2463. public function generateAndSendChildCredentials(Request $request, CredentialsService $credentialsService, Child $child, MailerInterface $mailer, ReminderConfigurationRepository $reminderConfigurationRepository, ChildRepository $childRepository, EntityManagerInterface $entityManager): Response
  2464. {
  2465. $credentialsService->sendChildEmailLoginToChildAndGuardian($child);
  2466. return $this->redirectToRoute('guardian_show', ['id' => $child->getGuardian()->getId()], Response::HTTP_SEE_OTHER);
  2467. }
  2468. /**
  2469. * @Route("/summer_guardians", name="summer_guardians_csv", priority="1", methods={"GET"})
  2470. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2471. */
  2472. public function summerGuardiansCsv()
  2473. {
  2474. $date = new \DateTime();
  2475. $file = $this->getParameter('kernel.project_dir') . "/var/summer-{$date->format('Y-m-d')}.csv";
  2476. if (file_exists($file)) {
  2477. $f = fopen($file, 'r');
  2478. header('Content-Encoding: UTF-8');
  2479. header('Content-Type: text/csv; charset=UTF-8');
  2480. header('Content-Disposition: attachment; filename="summer.csv";');
  2481. echo "\xEF\xBB\xBF"; // UTF-8 BOM
  2482. fpassthru($f);
  2483. fclose($f);
  2484. }
  2485. exit();
  2486. }
  2487. /**
  2488. * @Route("/guardians_csv", name="guardians_csv", priority="2", methods={"GET"})
  2489. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2490. */
  2491. public function guardiansCSV()
  2492. {
  2493. $date = new \DateTime();
  2494. $file = $this->getParameter('kernel.project_dir') . "/var/guardian-{$date->format('Y-m-d')}.csv";
  2495. if (file_exists($file)) {
  2496. $f = fopen($file, 'r');
  2497. header('Content-Encoding: UTF-8');
  2498. header('Content-Type: text/csv; charset=UTF-8');
  2499. header('Content-Disposition: attachment; filename="guardian.csv";');
  2500. echo "\xEF\xBB\xBF"; // UTF-8 BOM
  2501. fpassthru($f);
  2502. fclose($f);
  2503. }
  2504. exit();
  2505. }
  2506. /**
  2507. * @Route("/guardians_all_csv", name="guardians_all_csv", priority="2", methods={"GET"})
  2508. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2509. */
  2510. public function guardiansAllCSV()
  2511. {
  2512. $date = new \DateTime();
  2513. $file = $this->getParameter('kernel.project_dir') . "/var/guardian-all-{$date->format('Y-m-d')}.csv";
  2514. if (file_exists($file)) {
  2515. $f = fopen($file, 'r');
  2516. header('Content-Encoding: UTF-8');
  2517. header('Content-Type: text/csv; charset=UTF-8');
  2518. header('Content-Disposition: attachment; filename="guardian-all.csv";');
  2519. echo "\xEF\xBB\xBF"; // UTF-8 BOM
  2520. fpassthru($f);
  2521. fclose($f);
  2522. }
  2523. exit();
  2524. }
  2525. /**
  2526. * @Route("/ajax/payment/{id}/edit", name="guardian_payment_edit", methods={"POST"})
  2527. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2528. * @param Payment $payment
  2529. * @param Request $request
  2530. * @return Response
  2531. */
  2532. public function ajaxPaymentEdit(Payment $payment, Request $request, PaymentService $paymentService, GuardianLogService $guardianLogService): Response
  2533. {
  2534. if (!$payment) {
  2535. $error = new JsonResponse();
  2536. $error
  2537. ->setStatusCode(Response::HTTP_NOT_FOUND)
  2538. ->setData(['status' => "Sąskaita nerasta sistemoje."]);
  2539. return $error;
  2540. }
  2541. if ($payment->getStatus() == Payment::STATUS_PAID) {
  2542. $error = new JsonResponse();
  2543. $error
  2544. ->setStatusCode(Response::HTTP_NOT_FOUND)
  2545. ->setData(['status' => "Sąskaita jau apmokėta!"]);
  2546. return $error;
  2547. }
  2548. $paymentService->recalculateNewestGuardianPayment($payment->getGuardian(), $payment->getDateFor());
  2549. $payment = $this->get('serializer')->deserialize($request->getContent(), Payment::class, 'json', [
  2550. AbstractNormalizer::OBJECT_TO_POPULATE => $payment
  2551. ]);
  2552. /**
  2553. * @var $payment Payment
  2554. */
  2555. $payment->setAmount($payment->getExpectedAmount() + $payment->getCorrectionAmount());
  2556. $guardianLogService->addGuardianActionLog($request->getContent(), $payment->getGuardian(), $payment, 'update', false);
  2557. RestUtils::saveObject($this->getDoctrine()->getManager(), $payment);
  2558. return new JsonResponse(['status' => "OK"], Response::HTTP_OK);
  2559. }
  2560. /**
  2561. * @Route("/payment-notification/{id}/resend", name="guardian_payment_notification_resend", priority="2", methods={"POST"})
  2562. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2563. */
  2564. public function resendPaymentNotification(
  2565. PaymentNotificationLog $paymentNotificationLog,
  2566. EntityManagerInterface $entityManager
  2567. ): JsonResponse {
  2568. $smsContent = $paymentNotificationLog->getSmsContent();
  2569. $phoneNumber = $paymentNotificationLog->getPhonenumber();
  2570. if ($smsContent && $phoneNumber) {
  2571. $this->messageBus->dispatch(new SendSMSMessage($phoneNumber, $smsContent));
  2572. }
  2573. $manualType = 'MANUAL_' . $paymentNotificationLog->getNotificationType();
  2574. $resendLog = (new PaymentNotificationLog())
  2575. ->setDateAdd(new \DateTime())
  2576. ->setGuardian($paymentNotificationLog->getGuardian())
  2577. ->setEmail($paymentNotificationLog->getEmail())
  2578. ->setNotificationType($manualType)
  2579. ->setPhonenumber($phoneNumber)
  2580. ->setSmsContent($smsContent);
  2581. $entityManager->persist($resendLog);
  2582. $entityManager->flush();
  2583. return new JsonResponse(['success' => true]);
  2584. }
  2585. private function normalizeVacationDisciplineIds(?array $rawIds): ?array
  2586. {
  2587. if ($rawIds === null) return null;
  2588. $parsed = array_map(function($id) {
  2589. $id = (string) $id;
  2590. return str_contains($id, ':') ? (int) explode(':', $id)[0] : (int) $id;
  2591. }, $rawIds);
  2592. $result = array_values(array_unique(array_filter($parsed, fn($v) => $v > 0)));
  2593. return !empty($result) ? $result : null;
  2594. }
  2595. /**
  2596. * @param \DateTimeInterface|string $from
  2597. * @param \DateTimeInterface|string $to
  2598. */
  2599. private function vacationRangeKey($from, $to): string
  2600. {
  2601. $fromDate = $from instanceof \DateTimeInterface ? $from : new \DateTime($from);
  2602. $toDate = $to instanceof \DateTimeInterface ? $to : new \DateTime($to);
  2603. return $fromDate->format('Y-m-d') . '|' . $toDate->format('Y-m-d');
  2604. }
  2605. private function restoreDeletedLessonsForVacation(
  2606. Child $childObj,
  2607. \DateTimeInterface $dateFrom,
  2608. \DateTimeInterface $dateTo,
  2609. ?array $disciplineIds,
  2610. LessonDeletedRepository $lessonDeletedRepository,
  2611. LessonRepository $lessonRepository,
  2612. TeacherPaymentLogRepository $teacherPaymentLogRepository,
  2613. EntityManagerInterface $entityManager
  2614. ): void {
  2615. $query = $lessonDeletedRepository->createQueryBuilder('l')
  2616. ->andWhere('l.startTime >= :dateFrom')
  2617. ->setParameter('dateFrom', $dateFrom)
  2618. ->andWhere('l.startTime <= :dateTo')
  2619. ->setParameter('dateTo', $dateTo)
  2620. ->andWhere('l.child = :child')
  2621. ->setParameter('child', $childObj);
  2622. if ($disciplineIds !== null) {
  2623. $query->andWhere('l.discipline IN (:disciplines)')
  2624. ->setParameter('disciplines', $disciplineIds);
  2625. }
  2626. /** @var LessonDeleted[] $deletedListByAdmin */
  2627. $deletedListByAdmin = $query->getQuery()->getResult();
  2628. foreach ($deletedListByAdmin as $deletedByAdmin) {
  2629. if ($teacherPaymentLogRepository->count(['lessonDeleted' => $deletedByAdmin]) > 0) {
  2630. // The teacher was already paid for this cancelled lesson; keep the historical
  2631. // record instead of deleting it, since removing it would violate the
  2632. // teacher_payment_log foreign key and abort the whole save.
  2633. continue;
  2634. }
  2635. $lessons = $lessonRepository->createQueryBuilder('l')
  2636. ->andWhere('l.teacher = :teacher')
  2637. ->setParameter('teacher', $deletedByAdmin->getTeacher()->getId())
  2638. ->andWhere('l.discipline = :discipline')
  2639. ->setParameter('discipline', $deletedByAdmin->getDiscipline()->getId())
  2640. ->andWhere(':child MEMBER OF l.children')
  2641. ->setParameter('child', $deletedByAdmin->getChild()->getId())
  2642. ->andWhere('l.startTime = :startTime')
  2643. ->setParameter('startTime', $deletedByAdmin->getStartTime()->format('Y-m-d H:i'))
  2644. ->andWhere('l.endTime = :endTime')
  2645. ->setParameter('endTime', $deletedByAdmin->getEndTime()->format('Y-m-d H:i'))
  2646. ->andWhere('l.type = :type')
  2647. ->setParameter('type', $deletedByAdmin->getType())
  2648. ->andWhere('l.duration = :duration')
  2649. ->setParameter('duration', $deletedByAdmin->getDuration())
  2650. ->getQuery()
  2651. ->getResult();
  2652. if (!$lessons && $deletedByAdmin->getTeacher()->getStatus() == Teacher::STATUS_ACTIVE) {
  2653. $lesson = (new Lesson())
  2654. ->setType($deletedByAdmin->getType())
  2655. ->setStatus($deletedByAdmin->getStatus())
  2656. ->setClass($deletedByAdmin->getChild()->getClass())
  2657. ->setDiscipline($deletedByAdmin->getDiscipline())
  2658. ->setTeacher($deletedByAdmin->getTeacher())
  2659. ->setDuration($deletedByAdmin->getDuration())
  2660. ->setStartTime($deletedByAdmin->getStartTime())
  2661. ->setEndTime($deletedByAdmin->getEndTime());
  2662. $lesson->addChild($deletedByAdmin->getChild());
  2663. $entityManager->persist($lesson);
  2664. $entityManager->flush();
  2665. }
  2666. $entityManager->remove($deletedByAdmin);
  2667. $entityManager->flush();
  2668. }
  2669. }
  2670. /**
  2671. * @Route("/guardians_all_csv_marketing", name="guardians_all_csv_marketing", priority="2", methods={"GET"})
  2672. * @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
  2673. */
  2674. public function guardiansAllCSVMarketing()
  2675. {
  2676. $file = $this->getParameter('kernel.project_dir') . "/var/csv/" . self::FILE_NAME_DISCOUNT_CODE_MARKETING;
  2677. if (file_exists($file)) {
  2678. $f = fopen($file, 'r');
  2679. header('Content-Encoding: UTF-8');
  2680. header('Content-Type: text/csv; charset=UTF-8');
  2681. header('Content-Disposition: attachment; filename="Marketingo.csv";');
  2682. echo "\xEF\xBB\xBF"; // UTF-8 BOM
  2683. fpassthru($f);
  2684. fclose($f);
  2685. }
  2686. exit();
  2687. }
  2688. /**
  2689. * @param string[] $statuses
  2690. * @throws Exception
  2691. */
  2692. private function getPreviousMonthLessonsMinutes(EntityManagerInterface $entityManager, array $statuses): int
  2693. {
  2694. $conn = $entityManager->getConnection();
  2695. $dateStart = new \DateTime('first day of last month');
  2696. $dateStart->setTime(0, 0, 0);
  2697. $dateEnd = new \DateTime('last day of last month');
  2698. $dateEnd->setTime(23, 59, 59);
  2699. $quotedStatuses = array_map([$conn, 'quote'], $statuses);
  2700. $in = implode(',', $quotedStatuses);
  2701. $RAW_QUERY = "SELECT SUM(lesson.duration) as diff
  2702. FROM `lesson`
  2703. WHERE start_time >= '" . $dateStart->format('Y-m-d H:i:s') . "'
  2704. AND start_time <= '" . $dateEnd->format('Y-m-d H:i:s') . "'
  2705. AND status IN (" . $in . ")";
  2706. $statement = $conn->prepare($RAW_QUERY);
  2707. $resultSet = $statement->executeQuery();
  2708. $rows = $resultSet->fetchAllAssociative();
  2709. $row = reset($rows);
  2710. return (int) ($row['diff'] ?? 0);
  2711. }
  2712. /**
  2713. * @param string[] $statuses
  2714. * @throws Exception
  2715. */
  2716. private function getCurrentMonthLessonsMinutes(EntityManagerInterface $entityManager, array $statuses): int
  2717. {
  2718. $conn = $entityManager->getConnection();
  2719. $dateStart = new \DateTime('first day of this month');
  2720. $dateStart->setTime(0, 0, 0);
  2721. $dateEnd = new \DateTime('last day of this month');
  2722. $dateEnd->setTime(23, 59, 59);
  2723. $quotedStatuses = array_map([$conn, 'quote'], $statuses);
  2724. $in = implode(',', $quotedStatuses);
  2725. $RAW_QUERY = "SELECT SUM(lesson.duration) as diff
  2726. FROM `lesson`
  2727. WHERE start_time >= '" . $dateStart->format('Y-m-d H:i:s') . "'
  2728. AND start_time <= '" . $dateEnd->format('Y-m-d H:i:s') . "'
  2729. AND status IN (" . $in . ")";
  2730. $statement = $conn->prepare($RAW_QUERY);
  2731. $resultSet = $statement->executeQuery();
  2732. $rows = $resultSet->fetchAllAssociative();
  2733. $row = reset($rows);
  2734. return (int) ($row['diff'] ?? 0);
  2735. }
  2736. }