<?php
namespace App\Controller;
use App\Entity\Child;
use App\Entity\ChildVacation;
use App\Entity\EarlyPayment;
use App\Entity\Guardian;
use App\Entity\GuardianRegister\GuardianRegister;
use App\Entity\Lesson;
use App\Entity\LessonDeleted;
use App\Entity\Payment;
use App\Entity\PriceChange;
use App\Entity\Teacher;
use App\Entity\PaymentNotificationLog;
use App\Form\ChildType;
use App\Form\GuardianType;
use App\Message\RecalculateLessonMessage;
use App\Message\SendMailMessage;
use App\Message\SendSMSMessage;
use App\Repository\AdminRepository;
use App\Repository\PaymentNotificationLogRepository;
use App\Repository\AuthLogRepository;
use App\Repository\ChildRepository;
use App\Repository\ConfigurationRepository;
use App\Repository\EarlyPaymentRepository;
use App\Repository\EmailLogRepository;
use App\Repository\GuardianActionLogRepository;
use App\Repository\GuardianContractRepository;
use App\Repository\GuardianRepository;
use App\Repository\LessonDeletedRepository;
use App\Repository\LessonRepository;
use App\Repository\MonthlyGoalRepository;
use App\Repository\PaymentRepository;
use App\Repository\PriceChangeRepository;
use App\Repository\ReminderConfigurationRepository;
use App\Repository\TeacherPaymentLogRepository;
use App\Repository\TeacherRepository;
use App\Service\ChildPreferenceService;
use App\Service\CredentialsService;
use App\Service\EarlyPaymentService;
use App\Service\GuardianEarlyPaymentConversionService;
use App\Service\GuardianLogService;
use App\Service\LessonDeletionService;
use App\Service\NotificationService;
use App\Service\PaymentService;
use App\Service\PriceIncreaseService;
use App\Utils\HelperUtils;
use App\Utils\RestUtils;
use Doctrine\DBAL\Exception;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use Psr\Log\LoggerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Message;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @Route("/guardians")
*/
class GuardianController extends AbstractController
{
public const FILE_NAME_DISCOUNT_CODE_MARKETING = "guardian-discount-code-marketing.csv";
private const EARLY_PAYMENT_INVOICE_VISIBILITY_DAYS = 5;
private const PAYMENT_SOURCE_EARLY = 'EARLY';
private const PAYMENT_SOURCE_REGULAR = 'REGULAR';
private MessageBusInterface $messageBus;
private UserPasswordHasherInterface $userPasswordHasher;
public function __construct(UserPasswordHasherInterface $userPasswordHasher, MessageBusInterface $messageBus)
{
$this->messageBus = $messageBus;
$this->userPasswordHasher = $userPasswordHasher;
}
/**
* @Route("/", name="guardian_index", methods={"GET"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function index(GuardianRepository $guardianRepository, EntityManagerInterface $entityManager, MonthlyGoalRepository $monthlyGoalRepository, AdminRepository $adminRepository, CacheInterface $cache): Response
{
$dashboardData = $cache->get('guardian_index_dashboard_v2', function (ItemInterface $item) use (
$guardianRepository,
$entityManager,
$monthlyGoalRepository,
$adminRepository
) {
$item->expiresAfter(20 * 60);
$trackedStatuses = [
Guardian::STATUS_PREPARING['value'],
Guardian::STATUS_SENT['value'],
Guardian::STATUS_CONFIRMED['value'],
Guardian::STATUS_CANCELLED['value'],
Guardian::STATUS_LEFT['value'],
Guardian::STATUS_OVERPAID['value'],
];
$countsByStatus = $guardianRepository->getStatusCounts($trackedStatuses);
$preparingCount = $countsByStatus[Guardian::STATUS_PREPARING['value']] ?? 0;
$sentCount = $countsByStatus[Guardian::STATUS_SENT['value']] ?? 0;
$confirmedCount = $countsByStatus[Guardian::STATUS_CONFIRMED['value']] ?? 0;
$cancelledCount = $countsByStatus[Guardian::STATUS_CANCELLED['value']] ?? 0;
$leftCount = $countsByStatus[Guardian::STATUS_LEFT['value']] ?? 0;
$overpaidCount = $countsByStatus[Guardian::STATUS_OVERPAID['value']] ?? 0;
$RAW_QUERY = "SELECT SUM(TIMESTAMPDIFF(MINUTE,child_preference_time.start_time, child_preference_time.end_time)) as suma
FROM guardian
LEFT JOIN child on child.guardian_id = guardian.id
LEFT JOIN child_preference on child_preference.child_id = child.id
LEFT JOIN child_preference_time on child_preference_time.child_preference_id = child_preference.id
where child.lessons_end is null AND
guardian.status IN ('CONFIRMED') AND
child_preference.teacher_id is not null AND
child_preference.end_time is null";
$statement = $entityManager->getConnection()->prepare($RAW_QUERY);
$resultSet = $statement->executeQuery();
$activeClientsMins = $resultSet->fetchAllAssociative();
$activeClientsMins = reset($activeClientsMins);
$activeClientsMins= $activeClientsMins['suma'];
$RAW_QUERY = "SELECT SUM(TIMESTAMPDIFF(MINUTE,child_preference_time.start_time, child_preference_time.end_time)) as suma
FROM guardian
LEFT JOIN child on child.guardian_id = guardian.id
LEFT JOIN child_preference on child_preference.child_id = child.id
LEFT JOIN child_preference_time on child_preference_time.child_preference_id = child_preference.id
where child.lessons_end is null AND
guardian.status IN ('CONFIRMED') AND
COALESCE(guardian.autumn2024back, 0) <> 1 AND
child_preference.teacher_id is not null AND
child_preference.end_time is null";
$statement = $entityManager->getConnection()->prepare($RAW_QUERY);
$resultSet = $statement->executeQuery();
$activeClientsMinsExcludingReturning = $resultSet->fetchAllAssociative();
$activeClientsMinsExcludingReturning = reset($activeClientsMinsExcludingReturning);
$activeClientsMinsExcludingReturning = $activeClientsMinsExcludingReturning['suma'];
$RAW_QUERY = "SELECT SUM(TIMESTAMPDIFF(MINUTE,child_preference_time.start_time, child_preference_time.end_time)) as suma
FROM guardian
LEFT JOIN child on child.guardian_id = guardian.id
LEFT JOIN child_preference on child_preference.child_id = child.id
LEFT JOIN child_preference_time on child_preference_time.child_preference_id = child_preference.id
where child.lessons_end is null AND
guardian.status IN ('CONFIRMED') AND
guardian.summer IN ('YES_35', 'YES_20') AND
child_preference.teacher_id is not null AND
child_preference.end_time is null";
$statement = $entityManager->getConnection()->prepare($RAW_QUERY);
$resultSet = $statement->executeQuery();
$summerClientsMins = $resultSet->fetchAllAssociative();
$summerClientsMins = reset($summerClientsMins);
$summerClientsMins= $summerClientsMins['suma'];
$RAW_QUERY = "SELECT COUNT(DISTINCT(guardian.id)) as suma
FROM guardian
LEFT JOIN child on child.guardian_id = guardian.id
LEFT JOIN child_preference on child_preference.child_id = child.id
LEFT JOIN child_preference_time on child_preference_time.child_preference_id = child_preference.id
where child.lessons_end is null AND
guardian.status IN ('CONFIRMED','PREPARING') AND
child_preference.teacher_id is not null AND
TIMESTAMPDIFF(MINUTE,child_preference_time.start_time, child_preference_time.end_time) > 0";
$statement = $entityManager->getConnection()->prepare($RAW_QUERY);
$resultSet = $statement->executeQuery();
$clientsCount = $resultSet->fetchAllAssociative();
$clientsCount = reset($clientsCount);
$clientsCount= $clientsCount['suma'];
$dafeFor = date('Y-m');
$query = $monthlyGoalRepository->createQueryBuilder('mg');
$monthlyGoal = $query
->andWhere('mg.dateFor like :date')
->setParameter('date', $dafeFor.'%')
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
$currentAmount = 0;
$goalAmount = 0;
$goalPercentAchieved = 0;
$RAW_QUERY = "SELECT SUM(amount) as suma
FROM montonio_log
where created_at like '{$dafeFor}%' and status = 'PAID'";
$statement = $entityManager->getConnection()->prepare($RAW_QUERY);
$resultSet = $statement->executeQuery();
$currentAmount = $resultSet->fetchAllAssociative();
$currentAmount = reset($currentAmount);
$currentAmount = $currentAmount['suma'];
if($monthlyGoal)
{
$goalAmount = $monthlyGoal->getGoalAmount();
}
if($monthlyGoal && $currentAmount)
{
$goalPercentAchieved = round(($currentAmount / $goalAmount * 100),2);
}
if($goalAmount)
{
$goalAmount = number_format($goalAmount, 2, '.', ' ');
}
if($currentAmount)
{
$currentAmount = number_format($currentAmount, 2, '.', ' ');
}
$notPaidStatus = Payment::STATUS_NOTPAID;
$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}%'";
$statement = $entityManager->getConnection()->prepare($RAW_QUERY_TOTAL_AMOUNT_MONTH);
$resultSet = $statement->executeQuery();
$currentDebt = $resultSet->fetchAllAssociative();
$currentDebt = reset($currentDebt);
$currentDebt = $currentDebt['totalAmount'];
if($currentDebt)
{
$currentDebt = number_format($currentDebt, 2, '.', ' ');
}
$admins = $adminRepository->findAll();
$adminsToFront = [];
foreach ($admins as $admin)
{
if(in_array($admin->getId(),[2,3,5]))
{
continue;
}
$adminsToFront[$admin->getFullname()] = $admin->getFullname();
}
$dateStart = new \DateTime();
$dateStart->modify('monday this week');
$dateStart->setTime(0, 0, 0);
$dateEnd = new \DateTime();
$dateEnd->modify('sunday this week');
$dateEnd->setTime(23, 59, 59);
$currentWeekSalesMinutes = ["weekNumber" => $dateStart->format('W'), "minutes" => $this->getSalesMinutes($dateStart, $dateEnd, $entityManager)];
$dateStart = new \DateTime();
$dateStart->modify('monday this week');
$dateStart->modify('-1 week');
$dateStart->setTime(0, 0, 0);
$dateEnd = new \DateTime();
$dateEnd->modify('sunday this week');
$dateEnd->modify('-1 week');
$dateEnd->setTime(23, 59, 59);
$lastWeekSalesMinutes = ["weekNumber" => $dateStart->format('W'), "minutes" => $this->getSalesMinutes($dateStart, $dateEnd, $entityManager)];
$dateStart = new \DateTime();
$dateStart->modify('monday this week');
$dateStart->modify('-2 week');
$dateStart->setTime(0, 0, 0);
$dateEnd = new \DateTime();
$dateEnd->modify('sunday this week');
$dateEnd->modify('-2 week');
$dateEnd->setTime(23, 59, 59);
$twoWeekOldSalesMinutes = ["weekNumber" => $dateStart->format('W'), "minutes" => $this->getSalesMinutes($dateStart, $dateEnd, $entityManager)];
$salesMinutes = [$currentWeekSalesMinutes,$lastWeekSalesMinutes,$twoWeekOldSalesMinutes];
$statuses = [
Lesson::STATUS_FREE['value'],
Lesson::STATUS_REGULAR['value'],
Lesson::STATUS_REGULAR_MOVED['value'],
Lesson::STATUS_WAITING_PAYMENT['value'],
Lesson::STATUS_GIFT['value'],
Lesson::STATUS_CHILD_UNINFORMED_MISSED['value'],
Lesson::STATUS_ADDITIONAL['value'],
];
$previousMonthLessonsMinutes = $this->getPreviousMonthLessonsMinutes($entityManager, $statuses);
$currentMonthLessonsMinutes = $this->getCurrentMonthLessonsMinutes($entityManager, $statuses);
return [
'preparingCount' => $preparingCount,
'sentCount' => $sentCount,
'confirmedCount' => $confirmedCount,
'cancelledCount' => $cancelledCount,
'leftCount' => $leftCount,
'overpaidCount' => $overpaidCount,
'clientsCount' => $clientsCount,
'activeClientsMins' => $activeClientsMins,
'activeClientsMinsExcludingReturning' => $activeClientsMinsExcludingReturning,
'summerClientsMins' => $summerClientsMins,
'currentAmount' => $currentAmount,
'goalAmount' => $goalAmount,
'goalPercentAchieved' => $goalPercentAchieved,
'currentDebt' => $currentDebt,
'adminsToFront' => $adminsToFront,
'salesMinutes' => $salesMinutes,
'previousMonthLessonsMinutes' => $previousMonthLessonsMinutes,
'currrentMonthLessonsMinutes' => $currentMonthLessonsMinutes,
];
}
);
return $this->render('guardian/index.html.twig', $dashboardData);
}
/**
* @Route("/search", name="guardian_search", methods={"GET", "POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function guardianSearch(Request $request, EntityManagerInterface $entityManager): Response
{
$term = $request->query->get('term');
if ($term) {
$RAW_QUERY = "SELECT id, concat(fullname, ' ', email) as text FROM guardian
where guardian.fullname like '%{$term}%' OR
guardian.email like '%{$term}%';";
$statement = $entityManager->getConnection()->prepare($RAW_QUERY);
$resultSet = $statement->executeQuery();
$guardians = $resultSet->fetchAllAssociative();
} else {
$guardians = [];
}
return new JsonResponse([
'result' => $guardians,
'pagination' => ['more' => false]
]);
}
/**
* @Route("/not_paid", name="guardian_not_paid_index", methods={"GET"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function notPaidIndex(GuardianRepository $guardianRepository): Response
{
// $preparingCount = count($guardianRepository->findBy(['status' => Guardian::STATUS_PREPARING['value']]));
// $sentCount = count($guardianRepository->findBy(['status' => Guardian::STATUS_SENT['value']]));
// $confirmedCount = count($guardianRepository->findBy(['status' => Guardian::STATUS_CONFIRMED['value']]));
// $cancelledCount = count($guardianRepository->findBy(['status' => Guardian::STATUS_CANCELLED['value']]));
// $leftCount = count($guardianRepository->findBy(['status' => Guardian::STATUS_LEFT['value']]));
// $overpaidCount = count($guardianRepository->findBy(['status' => Guardian::STATUS_OVERPAID['value']]));
return $this->render('not_paid_customers/not_paid_customers_list.html.twig', [
// 'preparingCount' => $preparingCount,
// 'sentCount' => $sentCount,
// 'confirmedCount' => $confirmedCount,
// 'cancelledCount' => $cancelledCount,
// 'leftCount' => $leftCount,
// 'overpaidCount' => $overpaidCount,
]);
}
/**
* @Route("/early_payment_not_paid", name="early_payment_not_paid", methods={"GET"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function early_payment_not_paid(): Response
{
return $this->render('not_paid_customers/early_payment_not_paid.html.twig', [
]);
}
/**
* @Route("/missed", name="guardian_missed_index", methods={"GET"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function missedIndex(GuardianRepository $guardianRepository): Response
{
return $this->render('uninformed_missed_customers/uninformed_missed_customers_list.html.twig');
}
/**
* @Route("/emails", name="guardian_emails", methods={"GET", "POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function sendEmail(Request $request, MessageBusInterface $messageBus, GuardianRepository $guardianRepository): Response
{
$guardianIds = $request->query->get('parentIds');
$guardianIds = explode(',', $guardianIds);
$text = $this->renderView("email/invoice_reminder.html.twig", []);
foreach ($guardianIds as $guardianId) {
$guardian = $guardianRepository->find($guardianId);
$this->sendReminderEmailToGuardian($guardian, $text, $messageBus);
}
if (!$guardianIds) {
$guardians = $guardianRepository->findAll();
foreach ($guardians as $guardian) {
$this->sendReminderEmailToGuardian($guardian, $text, $messageBus);
}
}
return new JsonResponse();
}
public function sendReminderEmailToGuardian($guardian, $text, $messageBus)
{
if ($guardian) {
$send = false;
foreach ($guardian->getPayments() as $payment) {
if ($payment->getStatus() == Payment::STATUS_NOTPAID) {
$send = true;
}
}
if ($send) {
$messageBus->dispatch(new SendMailMessage($guardian->getEmail(), 'Prašome apmokėti sąskaitą!', $text));
}
}
}
function getSalesMinutes($dateStart, $dateEnd, $entityManager)
{
$RAW_QUERY_SUMMER_MINS_BY_ADMIN = "SELECT SUM(guardian_register_child_preference.amount * guardian_register_child_preference.duration) as diff, admin.fullname
FROM `guardian_register_child_preference`
LEFT JOIN guardian_register_child on guardian_register_child.id = guardian_register_child_preference.guardian_register_child_id
LEFT JOIN guardian_register on guardian_register_child.guardian_register_id = guardian_register.id
LEFT JOIN guardian on guardian.id = guardian_register.guardian_id
LEFT JOIN admin on admin.id = guardian_register.sales_person_id
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')}'
group by guardian_register.sales_person_id
";
$statement = $entityManager->getConnection()->prepare($RAW_QUERY_SUMMER_MINS_BY_ADMIN);
$resultSet = $statement->executeQuery();
return $resultSet->fetchAllAssociative();
}
/**
* @Route("/ajax", name="ajax_guardian", methods={"POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
* @param Request $request
* @param EntityManagerInterface $entityManager
* @return Response
* @throws \Doctrine\ORM\NoResultException
* @throws \Doctrine\ORM\NonUniqueResultException
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
*/
public function ajax(Request $request, GuardianRepository $guardianRepository, PaymentRepository $paymentRepository): Response
{
$objectRepository = $guardianRepository;
$page = $request->request->get('page');
$perPage = $request->request->get('perPage');
$page = ($page && $page > 1) ? $page : 1;
$perPage = ($perPage && $perPage > 0) ? $perPage : 10; // should change in the future
$searchFilters = $request->request->get('filters', []);
$sorters = $request->request->get('sorters', []);
$query = $objectRepository->createQueryBuilder('a');
if ($searchFilters) {
foreach ($searchFilters as $filter) {
if (isset($filter['value'])) {
$searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
if($filter['field'] == 'email') {
$query->andWhere("REPLACE(a.email, '.', '') LIKE :email")
->setParameter('email', '%' . str_replace('.', '', $searchValue) . '%');
if (!in_array('c', $query->getAllAliases())) {
$query->leftJoin('a.children', 'c');
}
$query->orWhere("REPLACE(c.email, '.', '') LIKE :email");
}
elseif($filter['field'] == 'phoneNumber')
{
if(!in_array('c',$query->getAllAliases()))
{
$query->leftJoin('a.children', 'c');
}
$searchValue = preg_replace('/\D+/', '', (string) $searchValue);
$searchValue = substr($searchValue, -8);
$query->orWhere("c.{$filter['field']} {$filter['type']} :{$filter['field']}");
if ($filter['type'] == "like") {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "%{$searchValue}%");
} else {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "{$searchValue}");
}
}
elseif($filter['field'] == 'flagEarlyPayment')
{
if($searchValue == 1) {
$query->andWhere("a.flagEarlyPayment = 1");
}
else
{
$query->andWhere("a.flagEarlyPayment = 0 OR a.flagEarlyPayment IS NULL");
}
}
else
{
// if ($filter['type'] == "like") {
// $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
// ->setParameter("{$filter['field']}", "%{$searchValue}%");
// } else {
// $query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
// ->setParameter("{$filter['field']}", "{$searchValue}");
// }
$relation = explode('.', $filter['field']);
if (count($relation) == 1) {
$searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
if ($filter['type'] == "like") {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "%{$searchValue}%");
} else {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "{$searchValue}");
}
} elseif (count($relation) == 2) {
$searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
$field = $relation[1];
$relation = $relation[0];
if ($filter['type'] == "like") {
if (!in_array($relation, $query->getAllAliases())) {
$query
->leftJoin("a." . $relation, $relation);
}
$query
->andWhere("{$relation}.{$field} {$filter['type']} :{$field}")
->setParameter("{$field}", "%{$searchValue}%");
} else {
if (!in_array($relation, $query->getAllAliases())) {
$query
->leftJoin("a." . $relation, $relation);
}
$query
->andWhere("{$relation}.{$field} {$filter['type']} :{$field}")
->setParameter("{$field}", "{$searchValue}");
}
} elseif (count($relation) == 3) {
$searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
$relationOne = $relation[0];
$relationTwo = $relation[1];
$field = $relation[2];
if (!in_array($relationOne, $query->getAllAliases())) {
$query
->leftJoin("a." . $relationOne, $relationOne);
}
if (!in_array($relationTwo, $query->getAllAliases())) {
$query
->leftJoin("{$relationOne}." . $relationTwo, $relationTwo);
}
if ($filter['type'] == "like") {
$query
->andWhere("{$relationTwo}.{$field} {$filter['type']} :{$field}")
->setParameter("{$field}", "%{$searchValue}%");
} else {
$query
->andWhere("{$relationTwo}.{$field} {$filter['type']} :{$field}")
->setParameter("{$field}", "{$searchValue}");
}
}
}
}
}
}
if ($sorters) {
foreach ($sorters as $sorter) {
$query->addOrderBy('a.' . $sorter['field'], $sorter['dir']);
}
}
$count = RestUtils::getQueryCount(clone $query);
$pagesCount = ceil($count / $perPage);
$guardiansObj = RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage);
$guardians = [];
/**
* @var $guardianObject Guardian
*/
foreach ($guardiansObj as $guardianObject) {
$payment = $paymentRepository->findOneBy(['guardian' => $guardianObject->getId()], ['dateFor' => 'desc']);
if ($payment) {
if ($payment->getStatus() == Payment::STATUS_PAID) {
$lastMonthValue = $payment->getAmount();
} else {
$lastMonthValue = $payment->getAmount() * -1;
}
} else {
$lastMonthValue = 0;
}
$guardian = [];
$guardian['id'] = $guardianObject->getId();
$guardian['dateAdd'] = $guardianObject->getDateAdd()->format("Y-m-d");
$guardian['fullname'] = "{$guardianObject->getFullname()}";
$guardian['email'] = $guardianObject->getEmail();
$guardian['phoneNumber'] = $guardianObject->getPhoneNumber();
$guardian['lastMonthValue'] = $lastMonthValue;
$guardian['balanceStatus'] = $guardianObject->getBalanceStatus();
$guardian['status'] = $guardianObject->getStatus();
$guardian['summer'] = $guardianObject->getSummer();
$guardian['summerAdditional'] = $guardianObject->getSummerAdditional();
$guardian['progress'] = $guardianObject->isProgress();
$guardian['autumn2024back'] = $guardianObject->isAutumn2024back();
$guardian['flagEarlyPayment'] = $guardianObject->isFlagEarlyPayment();
$guardian['newPriceEmailSent'] = $guardianObject->isNewPriceEmailSent();
$guardian['newEmailOpened'] = $guardianObject->getNewEmailOpened()?$guardianObject->getNewEmailOpened()->format('Y-m-d H:i'):'';
$guardian['newEmailSubmitted'] = $guardianObject->isNewEmailSubmitted();
$guardian['guardianRegister']['salesPerson']['fullname'] = null;
if($guardianObject->getGuardianRegister() && $guardianObject->getGuardianRegister()->getSalesPerson()) {
$guardian['guardianRegister']['salesPerson']['fullname'] = $guardianObject->getGuardianRegister()->getSalesPerson()->getFullname();
}
foreach ($guardianObject->getChildren() as $child) {
$amount = 0;
$disciplines = "";
$assignedTeachers = "";
foreach ($child->getChildPreferences() as $preference) {
$endTime = $preference->getEndTime();
if ($endTime && $endTime <= new \DateTime())
{
continue;
}
$amount += $preference->getAmount();
$discipline = $preference->getDiscipline();
if ($preference->getDiscipline()) {
if (!$disciplines) {
$disciplines = $preference->getDiscipline()->getName();
} else {
$disciplines .= ", " . $preference->getDiscipline()->getName();
}
}
if ($preference->getTeacher()) {
if (!$assignedTeachers) {
$names = explode(' ', $preference->getTeacher()->getFullname(true));
$name = $names[0];
if (isset($names[1]) && $names[1]) {
$lastname = $names[1];
} else {
$lastname = "";
}
$name = mb_substr($name, 0, 1, 'UTF-8');
$assignedTeachers = "{$name}. {$lastname}";
} else {
$names = explode(' ', $preference->getTeacher()->getFullname(true));
$name = $names[0];
$lastname = isset($names[1]) ? $names[1] : '';
$assignedTeachers .= ", " . mb_substr($name, 0, 1, 'UTF-8') . '. ' . $lastname;
}
}
}
$guardian['children'][] = [
'id' => $child->getId(),
'fullname' => "{$child->getFullname()}",
'class' => $child->getClass(),
'lessonAmount' => $amount,
'disciplines' => $disciplines,
'teachers' => $assignedTeachers,
];
}
$guardians[] = $guardian;
}
// $guardians = $this->get('serializer')->normalize(RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage), null, ['groups' => "GuardianList"]);
// foreach ($guardians as $guardian){
// dd($guardian);
// }
return new JsonResponse([
"last_page" => $pagesCount,
"data" => $guardians
]);
}
/**
* @Route("/not_paid/ajax", name="not_paid_ajax_guardian", methods={"POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
* @param Request $request
* @param EntityManagerInterface $entityManager
* @return Response
* @throws \Doctrine\ORM\NoResultException
* @throws \Doctrine\ORM\NonUniqueResultException
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
*/
public function notPaidAjax(Request $request, GuardianRepository $guardianRepository, PaymentRepository $paymentRepository): Response
{
$objectRepository = $guardianRepository;
$page = $request->request->get('page');
$perPage = $request->request->get('perPage');
$page = ($page && $page > 1) ? $page : 1;
$perPage = ($perPage && $perPage > 0) ? $perPage : 10; // should change in the future
$searchFilters = $request->request->get('filters', []);
$sorters = $request->request->get('sorters', []);
$query = $objectRepository->createQueryBuilder('a');
$dateFor = $request->request->get('dateFor', 'now');
$dateFor = new \DateTime($dateFor);
$dateFor->modify("first day of {$dateFor->format('Y-M')}");
$dateFor->setTime(0, 0);
if ($searchFilters) {
foreach ($searchFilters as $filter) {
if (isset($filter['value'])) {
$searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
if ($filter['type'] == "like") {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "%{$searchValue}%");
} else {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "{$searchValue}");
}
}
}
}
$query->leftJoin('a.payments', 'p')
->andWhere("p.dateFor = :dateFor")
->setParameter('dateFor', $dateFor)
->andWhere("p.status = 'NOT_PAID'")
->andWhere("p.amount > 0")
->leftJoin('p.guardian', 'g')
->andWhere('g.flagEarlyPayment = 0 OR g.flagEarlyPayment IS NULL');
if ($sorters) {
foreach ($sorters as $sorter) {
$query->addOrderBy('a.' . $sorter['field'], $sorter['dir']);
}
}
$guardiansObj = RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage);
$guardians = [];
/**
* @var $guardianObject Guardian
*/
foreach ($guardiansObj as $guardianObject) {
$payment = $paymentRepository->findOneBy(['guardian' => $guardianObject->getId()], ['dateFor' => 'desc']);
if ($payment) {
if ($payment->getStatus() == Payment::STATUS_PAID) {
$lastMonthValue = $payment->getAmount();
} else {
$lastMonthValue = $payment->getAmount() * -1;
}
} else {
$lastMonthValue = 0;
}
$currentMonthValue = 0;
$wholeValue = 0;
$notifications = [];
foreach ($guardianObject->getPayments() as $payment)
{
if($payment->getDateFor() == $dateFor && $payment->getStatus() == Payment::STATUS_NOTPAID)
{
$currentMonthValue = $payment->getAmount() * -1;
}
if($payment->getStatus() == Payment::STATUS_NOTPAID)
{
$wholeValue += $payment->getAmount() * -1;
$notifications[] = $payment->getNotificationStatus();
}
}
$guardian = [];
$guardian['id'] = $guardianObject->getId();
$guardian['dateAdd'] = $guardianObject->getDateAdd()->format("Y-m-d");
$guardian['fullname'] = "{$guardianObject->getFullname()}";
$guardian['phoneNumber'] = $guardianObject->getPhoneNumber();
$guardian['currentMonthValue'] = $currentMonthValue;
$guardian['wholeValue'] = $wholeValue;
$guardian['email'] = $guardianObject->getEmail();
$guardian['marksignName'] = $guardianObject->getMarksignName();
$guardian['marksignSurname'] = $guardianObject->getMarksignSurname();
// $guardian['comment'] = $guardianObject->getComment();
// $guardian['balanceStatus'] = $guardianObject->getBalanceStatus();
$guardian['status'] = $guardianObject->getStatus();
$guardian['esign'] = ($guardianObject->getGuardianRegister() && $guardianObject->getGuardianRegister()->getSign()) ? 'Yes' : 'No';
$guardian['notifications'] = implode(',',$notifications);
// $guardian['children'] = "";
// $guardian['disciplines'] = "";
// foreach ($guardianObject->getChildren() as $child) {
// $disciplines = "";
// foreach ($child->getChildPreferences() as $preference) {
// if ($preference->getDiscipline()) {
// if (!$disciplines) {
// $disciplines = $preference->getDiscipline()->getName();
// } else {
// $disciplines .= ", " . $preference->getDiscipline()->getName();
// }
//
// }
// }
//
// $guardian['children'] .= "{$child->getFullname()} ";
// $guardian['disciplines'] .= "{$disciplines} ";
// }
$guardians[] = $guardian;
}
$count = count($guardians);
$pagesCount = ceil($count / $perPage);
return new JsonResponse([
"last_page" => $pagesCount,
"data" => $guardians
]);
}
/**
* @Route("/early_not_paid/ajax", name="early_not_paid_ajax_guardian", methods={"POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
* @param Request $request
* @param EntityManagerInterface $entityManager
* @return Response
* @throws \Doctrine\ORM\NoResultException
* @throws \Doctrine\ORM\NonUniqueResultException
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
*/
public function early_not_paid_ajax_guardian(Request $request, GuardianRepository $guardianRepository, QuickPaymentController $quickPaymentController): Response
{
$objectRepository = $guardianRepository;
$page = $request->request->get('page');
$perPage = $request->request->get('perPage');
$page = ($page && $page > 1) ? $page : 1;
$perPage = ($perPage && $perPage > 0) ? $perPage : 10; // should change in the future
$searchFilters = $request->request->get('filters', []);
$sorters = $request->request->get('sorters', []);
$query = $objectRepository->createQueryBuilder('a');
if ($searchFilters) {
foreach ($searchFilters as $filter) {
if (isset($filter['value'])) {
$searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
if ($filter['type'] == "like") {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "%{$searchValue}%");
} else {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "{$searchValue}");
}
}
}
}
$query->andWhere("a.flagEarlyPayment = 1")
->join('a.guardianRegister', 'gr')
->andWhere('gr.status = :notPaid48')
->setParameter('notPaid48', GuardianRegister::STATUS_NOT_PAID_48['value']);
if ($sorters) {
foreach ($sorters as $sorter) {
$query->addOrderBy('a.' . $sorter['field'], $sorter['dir']);
}
}
$guardiansObj = RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage);
$guardians = [];
/**
* @var $guardianObject Guardian
*/
foreach ($guardiansObj as $guardianObject) {
$earlyPaymentData = $quickPaymentController->getEarlyPaymentData($guardianObject);
$amount = $earlyPaymentData['amount'];
$lessonsToPay = $earlyPaymentData['lessonsToPay'];
$periodEndTime = $earlyPaymentData['previousPeriodEndTime'];
if($periodEndTime > new \DateTime())
{
continue;
}
if($amount > 0) {
$notPaidDays = date_diff($periodEndTime, new \DateTime())->format("%a");
// dump($notPaidDays);
// dump($guardianObject->getId());
// dump($periodEndTime);
// die();
$guardian = [];
$guardian['id'] = $guardianObject->getId();
$guardian['dateAdd'] = $guardianObject->getDateAdd()->format("Y-m-d");
$guardian['fullname'] = "{$guardianObject->getFullname()}";
$guardian['phoneNumber'] = $guardianObject->getPhoneNumber();
$guardian['email'] = $guardianObject->getEmail();
$guardian['marksignName'] = $guardianObject->getMarksignName();
$guardian['marksignSurname'] = $guardianObject->getMarksignSurname();
$guardian['notPaidDays'] = $notPaidDays;
$guardian['amount'] = $amount;
$guardian['sales'] = ($guardianObject->getGuardianRegister() && $guardianObject->getGuardianRegister()->getSalesPerson()) ? $guardianObject->getGuardianRegister()->getSalesPerson()->getFullname() : '';
$guardians[] = $guardian;
}
}
//die();
$count = count($guardians);
$pagesCount = ceil($count / $perPage);
return new JsonResponse([
"last_page" => $pagesCount,
"data" => $guardians
]);
}
/**
* @Route("/missed/ajax", name="missed_ajax_guardian", methods={"POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
* @param Request $request
* @param EntityManagerInterface $entityManager
* @return Response
* @throws \Doctrine\ORM\NoResultException
* @throws \Doctrine\ORM\NonUniqueResultException
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
*/
public function missedAjax(Request $request, GuardianRepository $guardianRepository, LessonRepository $lessonRepository): Response
{
$objectRepository = $guardianRepository;
$page = $request->request->get('page');
$perPage = $request->request->get('perPage');
$page = ($page && $page > 1) ? $page : 1;
$perPage = ($perPage && $perPage > 0) ? $perPage : 10; // should change in the future
$searchFilters = $request->request->get('filters', []);
$sorters = $request->request->get('sorters', []);
$query = $objectRepository->createQueryBuilder('a');
// $dateFor = $request->request->get('dateFor', 'now');
$dateStart = new \DateTime($request->request->get('dateStart', 'now'));
$dateEnd = new \DateTime($request->request->get('dateEnd', 'now'));
// $dateFor = new \DateTime($dateFor);
// $dateFor->modify("first day of {$dateFor->format('Y-M')}");
$dateStart->setTime(0, 0);
$dateEnd->setTime(0, 0);
if ($searchFilters) {
foreach ($searchFilters as $filter) {
if (isset($filter['value'])) {
$searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
if ($filter['type'] == "like") {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "%{$searchValue}%");
} else {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "{$searchValue}");
}
}
}
}
$query->leftJoin('a.children', 'c')
->leftJoin('c.lessons', 'l')
->andWhere("l.status = :status")
->setParameter('status', Lesson::STATUS_CHILD_UNINFORMED_MISSED['value'])
->andWhere("l.startTime >= :dateStart")
->andWhere("l.endTime <= :dateEnd")
->setParameter('dateStart', $dateStart)
->setParameter('dateEnd', $dateEnd);
if ($sorters) {
foreach ($sorters as $sorter) {
$query->addOrderBy('a.' . $sorter['field'], $sorter['dir']);
}
}
$count = RestUtils::getQueryCount(clone $query);
$pagesCount = ceil($count / $perPage);
$guardiansObj = RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage);
$guardians = [];
/**
* @var $guardianObject Guardian
*/
foreach ($guardiansObj as $guardianObject) {
$missedLessonsInRow = 0;
$add = false;
foreach ($guardianObject->getChildren() as $child)
{
$lessonsQuery = $lessonRepository->createQueryBuilder('l');
$lessons = $lessonsQuery
->andWhere(':child member of l.children')
->setParameter('child', $child)
->andWhere("l.startTime >= :dateStart")
->andWhere("l.endTime <= :dateEnd")
->setParameter('dateStart', $dateStart)
->setParameter('dateEnd', $dateEnd)
->orderBy('l.startTime', 'ASC')
->getQuery()
->getResult();
foreach ($lessons as $lesson)
{
// dump($lesson->getStatus());
if($lesson->getStatus() == Lesson::STATUS_CHILD_UNINFORMED_MISSED['text'])
{
$missedLessonsInRow++;
if($missedLessonsInRow >= 2)
{
$add = true;
break;
}
// dump($missedLessonsInRow);
}
else {
$missedLessonsInRow = 0;
}
}
}
// die();
if($add) {
$guardian = [];
$guardian['id'] = $guardianObject->getId();
$guardian['dateAdd'] = $guardianObject->getDateAdd()->format("Y-m-d");
$guardian['fullname'] = "{$guardianObject->getFullname()}";
$guardian['phoneNumber'] = $guardianObject->getPhoneNumber();
$guardian['email'] = $guardianObject->getEmail();
$guardian['status'] = $guardianObject->getStatus();
$guardians[] = $guardian;
}
}
return new JsonResponse([
"last_page" => $pagesCount,
"data" => $guardians
]);
}
/**
* @Route("/not_paid/counts/ajax", name="not_paid_counts_ajax_guardian", methods={"GET"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
* @param Request $request
* @param EntityManagerInterface $entityManager
* @return Response
* @throws \Doctrine\ORM\NoResultException
* @throws \Doctrine\ORM\NonUniqueResultException
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
*/
public function notPaidCountsAjax(Request $request, GuardianRepository $guardianRepository, PaymentRepository $paymentRepository, EntityManagerInterface $entityManager): Response
{
$dateOffset = $request->query->get('date');
if($dateOffset != 'null') {
$dateFor = new \DateTime($dateOffset);
}
else
{
$dateFor = new \DateTime();
}
$dateFor->modify("first day of {$dateFor->format('Y-M')}");
$dateFor->setTime(0, 0);
$notPaidStatus = Payment::STATUS_NOTPAID;
$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')}%'";
$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";
$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";
$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')}%'";
$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')}%'";
$statement = $entityManager->getConnection()->prepare($RAW_QUERY_TOTAL);
$resultSet = $statement->executeQuery();
$total = $resultSet->fetchAllAssociative();
$statement = $entityManager->getConnection()->prepare($RAW_QUERY_TOTAL_AMOUNT);
$resultSet = $statement->executeQuery();
$totolAmount = $resultSet->fetchAllAssociative();
$statement = $entityManager->getConnection()->prepare($RAW_QUERY_TOTAL_MONTH);
$resultSet = $statement->executeQuery();
$totalMonth = $resultSet->fetchAllAssociative();
$statement = $entityManager->getConnection()->prepare($RAW_QUERY_TOTAL_AMOUNT_MONTH);
$resultSet = $statement->executeQuery();
$totolAmountMonth = $resultSet->fetchAllAssociative();
// $statement = $entityManager->getConnection()->prepare($RAW_QUERY_SUBMITTED);
// $statement->execute();
// $submitted = $statement->fetchAll();
return new JsonResponse([
"data" => [
'monthDept' => round($totolAmountMonth[0]['totalAmount'],2),
'monthDeptCount' => $totalMonth[0]['totalCount'],
'dept' => round($totolAmount[0]['totalAmount'],2),
'deptCount' => $total[0]['totalCount'],
]
]);
}
/**
* @Route("/ajax/{id}/edit", name="guardian_ajax_edit", methods={"POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
* @param Guardian $guardian
* @param Request $request
* @return Response
*/
public function ajaxEdit(Guardian $guardian, Request $request, MailerInterface $mailer, ReminderConfigurationRepository $reminderConfigurationRepository, ChildRepository $childRepository, EntityManagerInterface $entityManager, CredentialsService $credentialsService, MessageBusInterface $messageBus, ChildPreferenceService $childPreferenceService, GuardianLogService $guardianLogService): Response
{
if (!$guardian) {
$error = new JsonResponse();
$error
->setStatusCode(Response::HTTP_NOT_FOUND)
->setData(['status' => "error"]);
return $error;
}
$data = json_decode($request->getContent(), true);
if (isset($data['status'])) {
if ($data['status'] == Guardian::STATUS_SENT['value'] && (!$guardian->getEmail() || $guardian->getEmail() == "")) {
return new JsonResponse(['status' => "Vartotojas neturi elektroninio pasto"], Response::HTTP_BAD_REQUEST);
}
if($data['status'] == Guardian::STATUS_CONFIRMED['value'] && $guardian->getStatus() != Guardian::STATUS_CONFIRMED['text'])
{
$this->generateAndSendChildrenCredentials($request,$credentialsService, $guardian, $mailer, $reminderConfigurationRepository, $childRepository,$entityManager);
$guardianLogService->addGuardianActionLog('status_confirmed', $guardian, null, 'status_confirmed', false);
$entityManager->flush();
}
}
if (isset($data['progress'])) {
if ($data['progress'] != $guardian->isProgress()) {
$guardian->setProgressByAdmin($this->getUser());
$guardian->setProgressDate(new \DateTime());
$guardian->setProgress($data['progress']);
}
}
if (isset($data['autumn2024back'])) {
if ($data['autumn2024back'] != $guardian->isAutumn2024back()) {
$guardian->setAutumn2024backByAdmin($this->getUser());
$guardian->setAutumn2024backDate(new \DateTime());
$guardian->setAutumn2024back($data['autumn2024back']);
}
}
if (isset($data['summer'])) {
if ($data['summer'] != $guardian->getSummer()) {
$guardian->setSummer($data['summer']);
$guardian->setSummerAt(new \DateTime());
$guardian->setSummerByAdmin($this->getUser());
$guardian->setSummerDate(new \DateTime());
if($guardian->getSummer() == "YES_35")
{
$guardianDiscount = new Guardian\GuardianPriceDiscount();
$guardianDiscount->setGuardian($guardian);
$guardianDiscount->setDiscountPercent(35);
$guardianDiscount->setDateFrom(new \DateTime("2025-07-01 00:00:00"));
$guardianDiscount->setDateTo(new \DateTime("2025-08-31 23:59:59"));
$entityManager->persist($guardianDiscount);
$entityManager->flush();
$guardian->addGuardianPriceDiscount($guardianDiscount);
$entityManager->persist($guardian);
$entityManager->flush();
$childPreferenceService->recalculatePricesForFurtherLessons($guardian,new \DateTime("2025-07-01 00:00:00"));
$messageBus->dispatch(new SendMailMessage($guardian->getEmail(), '', '', [], 'NOTIF_SUMMER_GUARDIAN_ACCEPTED'));
}
if($guardian->getSummer() == "YES_20")
{
$guardianDiscount = new Guardian\GuardianPriceDiscount();
$guardianDiscount->setGuardian($guardian);
$guardianDiscount->setDiscountPercent(20);
$guardianDiscount->setDateFrom(new \DateTime("2025-07-01 00:00:00"));
$guardianDiscount->setDateTo(new \DateTime("2025-08-31 23:59:59"));
$entityManager->persist($guardianDiscount);
$entityManager->flush();
$guardian->addGuardianPriceDiscount($guardianDiscount);
$entityManager->persist($guardian);
$entityManager->flush();
$childPreferenceService->recalculatePricesForFurtherLessons($guardian,new \DateTime("2025-07-01 00:00:00"));
$messageBus->dispatch(new SendMailMessage($guardian->getEmail(), '', '', [], 'NOTIF_SUMMER_GUARDIAN_ACCEPTED'));
}
if($guardian->getSummer() == "NO")
{
$messageBus->dispatch(new SendMailMessage($guardian->getEmail(), '', '', [], 'NOTIF_SUMMER_GUARDIAN_NOT_ACCEPTED'));
}
}
}
$guardian = $this->get('serializer')->deserialize($request->getContent(), Guardian::class, 'json', [
AbstractNormalizer::OBJECT_TO_POPULATE => $guardian
]);
RestUtils::saveObject($this->getDoctrine()->getManager(), $guardian);
return new JsonResponse(['status' => "OK"], Response::HTTP_OK);
}
/**
* @Route("/{id}/payments", name="guardians_ajax_controller", methods={"POST"})
* @Route("/payments", name="guardians_ajax_controller_no_id", methods={"POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY') or is_granted('ROLE_GUARDIAN') and is_granted('IS_AUTHENTICATED_FULLY')")
* @param Request $request
* @param EntityManagerInterface $entityManager
* @return Response
* @throws \Doctrine\ORM\NoResultException
* @throws \Doctrine\ORM\NonUniqueResultException
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
*/
public function fetchData(?Guardian $guardian, Request $request, PaymentRepository $paymentRepository, EarlyPaymentRepository $earlyPaymentRepository): Response
{
$user = $this->getUser();
if (in_array("ROLE_GUARDIAN", $user->getRoles())) {
$guardian = $user;
}
$page = $request->request->get('page');
$perPage = $request->request->get('perPage');
$page = ($page && $page > 1) ? $page : 1;
$perPage = ($perPage && $perPage > 0) ? $perPage : 10; // should change in the future
$searchFilters = $request->request->get('filters', []);
if ($guardian->isFlagEarlyPayment()) {
return $this->fetchEarlyPaymentGuardianPayments(
$guardian,
$earlyPaymentRepository,
$paymentRepository,
$searchFilters,
$page,
$perPage
);
}
$query = $paymentRepository->createQueryBuilder('a');
$query->andWhere("a.guardian = :guardian")
->setParameter("guardian", $guardian->getId());
$this->applyPaymentSearchFilters($query, $searchFilters);
$query->orderBy('a.dateFor', 'DESC');
$count = RestUtils::getQueryCount(clone $query);
$pagesCount = ceil($count / $perPage);
$payments = $this->normalizePaymentsWithSource(
RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage),
self::PAYMENT_SOURCE_REGULAR
);
return new JsonResponse([
"last_page" => $pagesCount,
"data" => $payments
]);
}
private function fetchEarlyPaymentGuardianPayments(
Guardian $guardian,
EarlyPaymentRepository $earlyPaymentRepository,
PaymentRepository $paymentRepository,
array $searchFilters,
int $page,
int $perPage
): JsonResponse {
$earlyPaymentQuery = $earlyPaymentRepository->createQueryBuilder('a');
$earlyPaymentQuery->andWhere('a.guardian = :guardian')
->setParameter('guardian', $guardian->getId());
$latestPerMonthIds = array_column(
$earlyPaymentRepository->createQueryBuilder('latest')
->select('MAX(latest.id) as id')
->andWhere('latest.guardian = :guardian')
->andWhere('latest.status != :abandoned')
->setParameter('guardian', $guardian->getId())
->setParameter('abandoned', EarlyPayment::STATUS_ABANDONED)
->groupBy('latest.dateFor')
->getQuery()
->getScalarResult(),
'id'
);
$visibleUntil = (new \DateTime())
->modify("+" . self::EARLY_PAYMENT_INVOICE_VISIBILITY_DAYS . " days");
$earlyPaymentQuery->andWhere('a.id IN (:latestPerMonthIds)')
->andWhere('a.status = :paidStatus OR a.dateFor <= :visibleUntil')
->setParameter('latestPerMonthIds', $latestPerMonthIds ?: [0])
->setParameter('paidStatus', EarlyPayment::STATUS_PAID)
->setParameter('visibleUntil', $visibleUntil);
$this->applyPaymentSearchFilters($earlyPaymentQuery, $searchFilters);
$earlyPaymentQuery->orderBy('a.dateFor', 'DESC');
$earlyPayments = $this->normalizePaymentsWithSource(
$earlyPaymentQuery->getQuery()->getResult(),
self::PAYMENT_SOURCE_EARLY
);
$regularPaymentQuery = $paymentRepository->createQueryBuilder('a');
$regularPaymentQuery->andWhere('a.guardian = :guardian')
->andWhere('a.status = :paidStatus')
->setParameter('guardian', $guardian->getId())
->setParameter('paidStatus', Payment::STATUS_PAID);
$this->applyPaymentSearchFilters($regularPaymentQuery, $searchFilters);
$regularPaymentQuery->orderBy('a.dateFor', 'DESC');
$regularPayments = $this->normalizePaymentsWithSource(
$regularPaymentQuery->getQuery()->getResult(),
self::PAYMENT_SOURCE_REGULAR
);
$combinedPayments = array_merge($earlyPayments, $regularPayments);
usort($combinedPayments, static fn(array $a, array $b) => strcmp($b['dateFor'], $a['dateFor']));
$pagesCount = ceil(count($combinedPayments) / $perPage);
$payments = array_slice($combinedPayments, ($page - 1) * $perPage, $perPage);
return new JsonResponse([
"last_page" => $pagesCount,
"data" => $payments
]);
}
private function normalizePaymentsWithSource(array $payments, string $source): array
{
$normalizedPayments = $this->get('serializer')->normalize($payments, null, ['groups' => "PaymentList"]);
foreach ($normalizedPayments as &$normalizedPayment) {
$normalizedPayment['source'] = $source;
$normalizedPayment['rowKey'] = $source . '_' . $normalizedPayment['id'];
}
unset($normalizedPayment);
return $normalizedPayments;
}
private function applyPaymentSearchFilters(QueryBuilder $query, array $searchFilters): void
{
foreach ($searchFilters as $filter) {
if (!isset($filter['value'])) {
continue;
}
$relation = explode('.', $filter['field']);
$searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
if (count($relation) == 1) {
if ($filter['type'] == "like") {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "%{$searchValue}%");
} else {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "{$searchValue}");
}
} elseif (count($relation) == 2) {
$field = $relation[1];
$relationName = $relation[0];
if ($filter['type'] == "like") {
$query
->leftJoin("a." . $relationName, $relationName)
->andWhere("{$relationName}.{$field} {$filter['type']} :{$field}")
->setParameter("{$field}", "%{$searchValue}%");
} else {
$query
->leftJoin("a." . $relationName, $relationName)
->andWhere("{$relationName}.{$field} {$filter['type']} :{$field}")
->setParameter("{$field}", "{$searchValue}");
}
}
}
}
/**
* @Route("/{id}/actions", name="guardian_actions_log", methods={"POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
* @param Request $request
* @param EntityManagerInterface $entityManager
* @return Response
* @throws \Doctrine\ORM\NoResultException
* @throws \Doctrine\ORM\NonUniqueResultException
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
*/
public function actionLogs(?Guardian $guardian, Request $request, GuardianActionLogRepository $guardianActionLogRepository, TeacherRepository $teacherRepository, AdminRepository $adminRepository, TranslatorInterface $translator): Response
{
$objectRepository = $guardianActionLogRepository;
$page = $request->request->get('page');
$perPage = $request->request->get('perPage');
$page = ($page && $page > 1) ? $page : 1;
$perPage = ($perPage && $perPage > 0) ? $perPage : 10; // should change in the future
$searchFilters = $request->request->get('filters', []);
$sorters = $request->request->get('sorters', []);
$query = $objectRepository->createQueryBuilder('a');
$query->andWhere("a.guardian = :guardian")
->setParameter("guardian", $guardian->getId());
if ($searchFilters) {
foreach ($searchFilters as $filter) {
if (isset($filter['value'])) {
$relation = explode('.', $filter['field']);
if (count($relation) == 1) {
$searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
if($searchValue == 'true')
{
$searchValue = 1;
}
if($searchValue == 'false')
{
$searchValue = 0;
}
if ($filter['type'] == "like") {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "%{$searchValue}%");
} else {
$query->andWhere("a.{$filter['field']} {$filter['type']} :{$filter['field']}")
->setParameter("{$filter['field']}", "{$searchValue}");
}
} elseif (count($relation) == 2) {
$searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
$field = $relation[1];
$relation = $relation[0];
if ($filter['type'] == "like") {
$query
->leftJoin("a." . $relation, $relation)
->andWhere("{$relation}.{$field} {$filter['type']} :{$field}")
->setParameter("{$field}", "%{$searchValue}%");
} else {
$query
->leftJoin("a." . $relation, $relation)
->andWhere("{$relation}.{$field} {$filter['type']} :{$field}")
->setParameter("{$field}", "{$searchValue}");
}
}
}
}
}
// if($sorters){
// foreach($sorters as $sorter){
// $query->addOrderBy('a.'.$sorter['field'],$sorter['dir']);
// }
// }
$query->orderBy('a.createdAt', 'DESC');
$count = RestUtils::getQueryCount(clone $query);
$pagesCount = ceil($count / $perPage);
$actionLogs = $this->get('serializer')->normalize(RestUtils::getPaginatedResults($query->getQuery(), $page, $perPage), null, ['groups' => "GuardianActionList"]);
foreach ($actionLogs as $key => $actionLog)
{
if($actionLog['actionType'] == 'update')
{
$actionLogs[$key]['actionType'] = 'Atnaujinimas';
}
if($actionLog['actionType'] == 'delete')
{
$actionLogs[$key]['actionType'] = 'Ištrynimas';
}
if($actionLog['actionType'] == 'insert')
{
$actionLogs[$key]['actionType'] = 'Pridėjimas';
}
if($actionLog['userType'] == 'Admin')
{
$actionLogs[$key]['fullname'] = $adminRepository->find($actionLog['userId'])->getFullname();
}
// if($actionLog['userType'] == 'Admin')
// {
$data = json_decode($actionLogs[$key]['data'], true);
if (json_last_error() !== JSON_ERROR_NONE) {
$data = $actionLogs[$key]['data'];
}
$dataString = '';
if(is_array($data)) {
foreach ($data as $field => $dataLine) {
if ($dataString) {
$dataString .= '<br>';
}
$fieldTranslated = $translator->trans('forms.labels.' . $field);
if ($fieldTranslated == 'forms.labels.' . $field) {
$fieldTranslated = $translator->trans($field);
}
if(is_array($dataLine)) {
$dataString .= "<b>{$fieldTranslated}</b> iš <b>{$dataLine[0]}</b> į <b>{$dataLine[1]}</b>";
}
else {
$dataString .= "<b>{$fieldTranslated}</b> į <b>{$dataLine}</b>";
}
}
}
else
{
$dataString = $data;
}
$actionLogs[$key]['data'] = $dataString;
}
// }
return new JsonResponse([
"last_page" => $pagesCount,
"data" => $actionLogs
]);
}
/**
* @Route("/lessons/{id}", name="guardian_lessons_ajax_controller", methods={"GET", "POST"})
* @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')")
* @param Request $request
* @param EntityManagerInterface $entityManager
* @return Response
* @throws \Doctrine\ORM\NoResultException
* @throws \Doctrine\ORM\NonUniqueResultException
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
*/
public function fetchLessonsData(Child $childObj, Request $request, LessonRepository $lessonRepository, ChildRepository $childRepository, LessonDeletedRepository $lessonDeletedRepository): Response
{
$user = $this->getUser();
if (in_array("ROLE_GUARDIAN", $user->getRoles())) {
if ($childObj->getGuardian()->getId() != $user->getId()) {
return new JsonResponse(null, 401);
}
}
if (in_array("ROLE_CHILD", $user->getRoles())) {
if ($childObj->getId() != $user->getId()) {
return new JsonResponse(null, 401);
}
}
$date = new \DateTime();
$offset = $request->request->get('offset', 0);
$date->modify("first day of {$date->format('Y-m')}");
$date = $date->modify("{$offset} months");
$date->modify("first day of {$date->format('Y-m')}");
$date->setTime(0, 0, 0);
$end = new \DateTime($date->format('Y-m-t'));
$end = $end->setTime(23, 59, 59);
$days = [];
while ($date <= $end) {
$day_num = $date->format('d');
$date = $date->modify('+1 day');
$days[(int)$day_num] = [];
}
$objectRepository = $lessonRepository;
$page = $request->request->get('page');
$perPage = $request->request->get('perPage');
$page = ($page && $page > 1) ? $page : 1;
$perPage = ($perPage && $perPage > 0) ? $perPage : 100; // should change in the future
$searchFilters = $request->request->get('filters', []);
$sorters = $request->request->get('sorters', []);
$query = $objectRepository->createQueryBuilder('a');
$query
->leftJoin('a.children', 'c')
->andWhere('c.id = :child')
->setParameter("child", $childObj->getId());
if ($searchFilters) {
foreach ($searchFilters as $filter) {
if (isset($filter['value'])) {
$searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
if ($filter['field'] == 'teacherName') {
$query
->leftJoin('a.teacher', 't')
->andWhere('t.fullname LIKE :teacher')
->setParameter("teacher", "%{$searchValue}%");
}
}
}
}
$date = new \DateTime();
$offset = $request->request->get('offset', 0);
$date->modify("first day of {$date->format('Y-m')}");
$date = $date->modify("{$offset} months");
$date->modify("first day of {$date->format('Y-m')}");
$date->setTime(0, 0, 0);
$end = new \DateTime($date->format('Y-m-t'));
$end = $end->setTime(23, 59, 59);
$end->modify('+2 minutes');
//Optimizacija
// $query
// ->andWhere('a.startTime >= :startTime')
// ->setParameter('startTime', $date->format('Y-m-d H:i'))
// ->andWhere('a.endTime <= :endTime')
// ->setParameter('endTime', $end->format('Y-m-d H:i'));
if ($sorters) {
foreach ($sorters as $sorter) {
$query->addOrderBy('a.' . $sorter['field'], $sorter['dir']);
}
}
$lineArray = [];
$childrenQuery = $childRepository->createQueryBuilder('a');
$childrenQuery
->andWhere('a.id = :child')
->setParameter("child", $childObj->getId());
if ($searchFilters) {
foreach ($searchFilters as $filter) {
if (isset($filter['value'])) {
$searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
if ($filter['field'] == 'teacherName') {
$childrenQuery
->leftJoin('a.lessons', 'l')
->leftJoin('l.teacher', 't')
->andWhere('t.fullname LIKE :teacher')
->setParameter("teacher", "%{$searchValue}%");
}
}
}
}
$children = $childrenQuery->getQuery()->getResult();
/** @var Child $child */
foreach ($children as $child) {
$query = $lessonRepository->createQueryBuilder('a');
$query
->andWhere('a.startTime >= :startTime')
->setParameter('startTime', $date->format('Y-m-d H:i'))
->andWhere('a.endTime <= :endTime')
->setParameter('endTime', $end->format('Y-m-d H:i'))
->andWhere(':child MEMBER OF a.children')
->setParameter('child', $child->getId());
if ($searchFilters) {
foreach ($searchFilters as $filter) {
if (isset($filter['value'])) {
$searchValue = (is_string($filter['value']) ? $filter['value'] : reset($filter['value']));
if ($filter['field'] == 'teacherName') {
$query
->leftJoin('a.teacher', 't')
->andWhere('t.fullname LIKE :teacher')
->setParameter("teacher", "%{$searchValue}%");
}
if ($filter['field'] == 'discipline') {
$query
->leftJoin('a.discipline', 'd')
->andWhere('d.name LIKE :discipline')
->setParameter("discipline", "%{$searchValue}%");
}
}
}
}
$lessons = $query->getQuery()->getResult();
/**
* @var $lesson Lesson
*/
foreach ($lessons as $lesson) {
foreach ($lineArray as $lineCheck) {
if ($lineCheck['key'] == $child->getId() . '-' . $lesson->getTeacher()->getId() . '-' . $lesson->getDiscipline()->getId()) {
continue 2;
}
}
$line = [
'key' => $child->getId() . '-' . $lesson->getTeacher()->getId() . '-' . $lesson->getDiscipline()->getId(),
'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'):""],
'child' => ['name' => "{$child->getEmail()}", 'id' => $child->getId()],
'discipline' => ['name' => $lesson->getDiscipline()->getName(), 'id' => $lesson->getDiscipline()->getId()],
'days' => $days
];
$lineArray[] = $line;
}
$lessonDeletedQuery = $lessonDeletedRepository->createQueryBuilder('ld');
$deletedLessons = $lessonDeletedQuery
->andWhere('ld.child = :child')
->setParameter('child', $child->getId())
->andWhere('ld.startTime >= :startTime')
->setParameter('startTime', $date->format('Y-m-d H:i'))
->andWhere('ld.endTime <= :endTime')
->setParameter('endTime', $end->format('Y-m-d H:i'))
->getQuery()
->getResult();
/**
* @var $lesson LessonDeleted
*/
foreach ($deletedLessons as $lesson) {
foreach ($lineArray as $lineCheck) {
if ($lineCheck['key'] == $child->getId() . '-' . $lesson->getTeacher()->getId() . '-' . $lesson->getDiscipline()->getId()) {
continue 2;
}
}
$line = [
'key' => $child->getId() . '-' . $lesson->getTeacher()->getId() . '-' . $lesson->getDiscipline()->getId(),
'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'):""],
'child' => ['name' => "{$child->getEmail()}", 'id' => $child->getId()],
'discipline' => ['name' => $lesson->getDiscipline()->getName(), 'id' => $lesson->getDiscipline()->getId()],
'days' => $days
];
$lineArray[] = $line;
}
// if(isset($lineArray[0])) {
// $lineArray[0]['vacations'] = [];
// foreach ($child->getChildVacations() as $vacation) {
// if ($vacation->isIsDeleted()) {
// continue;
// }
// $lineArray[0]['vacations'][] = ['from' => $vacation->getDateFrom()->format('Y-m-d H:i'), 'to' => $vacation->getDateTo()->format('Y-m-d H:i')];
// }
// }
}
foreach ($lineArray as $key => $line)
{
$lineArray[$key]['vacations'] = [];
foreach ($child->getChildVacations() as $vacation) {
if ($vacation->isIsDeleted()) {
continue;
}
$lineArray[$key]['vacations'][] = [
'from' => $vacation->getDateFrom()->format('Y-m-d H:i'),
'to' => $vacation->getDateTo()->format('Y-m-d H:i'),
'disciplineIds' => $this->normalizeVacationDisciplineIds($vacation->getDisciplineIds()),
];
}
}
$date = new \DateTime();
$offset = $request->request->get('offset', 0);
$date->modify("first day of {$date->format('Y-m')}");
$date = $date->modify("{$offset} months");
$date->modify("first day of {$date->format('Y-m')}");
$date->setTime(0, 0, 0);
foreach ($lineArray as $key => $line) {
//0 child id
//1 teacher id
//2 discipline id
$keys = explode('-', $line['key']);
foreach ($line['days'] as $day => $element) {
$startTime = new \DateTime();
$startTime = $startTime->setDate((int)$date->format('Y'), (int)$date->format('m'), $day);
$startTime->setTime(0, 0, 0);
$endTime = new \DateTime();
$endTime = $endTime->setDate((int)$date->format('Y'), (int)$date->format('m'), $day);
$endTime->setTime(23, 59, 59);
$endTime->modify('+2 minutes');
$lessonsQuery = $lessonRepository->createQueryBuilder('l');
$lessons = $lessonsQuery
->andWhere('l.teacher = :teacher')
->setParameter('teacher', $keys[1])
->andWhere('l.discipline = :discipline')
->setParameter('discipline', $keys[2])
->andWhere(':child MEMBER OF l.children')
->setParameter('child', $keys[0])
->andWhere('l.startTime >= :startTime')
->setParameter('startTime', $startTime->format('Y-m-d H:i'))
->andWhere('l.endTime <= :endTime')
->setParameter('endTime', $endTime->format('Y-m-d H:i'))
->getQuery()
->getResult();
$lessonDeletedQuery = $lessonDeletedRepository->createQueryBuilder('ld');
$deletedLessons = $lessonDeletedQuery
->andWhere('ld.teacher = :teacher')
->setParameter('teacher', $keys[1])
->andWhere('ld.child = :child')
->setParameter('child', $keys[0])
->andWhere('ld.discipline = :discipline')
->setParameter('discipline', $keys[2])
->andWhere('ld.startTime >= :startTime')
->setParameter('startTime', $startTime->format('Y-m-d H:i'))
->andWhere('ld.endTime <= :endTime')
->setParameter('endTime', $endTime->format('Y-m-d H:i'))
->getQuery()
->getResult();
foreach ($lessons as $lesson) {
$lineArray[$key]['days'][$day][] = [
'id' => $lesson->getId(),
'duration' => $lesson->getDuration(),
'startTime' => $lesson->getStartTime()->format('Y-m-d H:i'),
'endTime' => $lesson->getEndTime()->format('Y-m-d H:i'),
'status' => $lesson->getStatus(),
'statusColor' => $lesson->getStatusColor(),
'type' => $lesson->getType(),
];
}
/** @var LessonDeleted $deletedLesson */
foreach ($deletedLessons as $deletedLesson) {
$lineArray[$key]['days'][$day][] = [
'id' => $deletedLesson->getId().'_deleted',
'duration' => $deletedLesson->getDuration(),
'startTime' => $deletedLesson->getStartTime()->format('Y-m-d H:i'),
'endTime' => $deletedLesson->getEndTime()->format('Y-m-d H:i'),
'status' => 'Anuliuotas užsiėmimas',
'statusColor' => '#fff',
'type' => $deletedLesson->getType(),
];
}
}
}
foreach ($lineArray as $key => $line) {
unset($lineArray[$key]['key']);
}
$page -= 1;
$pagesCount = ceil(count($lineArray) / $perPage);
$lineArray = array_slice($lineArray, $page * $perPage, $perPage);
return new JsonResponse([
"last_page" => $pagesCount,
"data" => $lineArray
]);
}
/**
* @Route("/vacation_lessons/{id}", name="guardian_vacation_lessons_ajax", methods={"GET", "POST"})
* @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')")
* @param Request $request
* @param EntityManagerInterface $entityManager
* @return Response
* @throws \Doctrine\ORM\NoResultException
* @throws \Doctrine\ORM\NonUniqueResultException
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
*/
public function fetchVacationLessonsData(
Child $childObj,
Request $request,
LessonRepository $lessonRepository,
ChildRepository $childRepository,
EntityManagerInterface $entityManager,
MessageBusInterface $messageBus,
NotificationService $notificationService,
TeacherRepository $teacherRepository,
LessonController $lessonController,
LessonDeletedRepository $lessonDeletedRepository,
GuardianLogService $guardianLogService,
TeacherPaymentLogRepository $teacherPaymentLogRepository
): Response
{
if($request->request->get('ignoreNotifications')) {
$newDates = [];
$parseHolidayDisciplineIds = function($raw): ?array {
if (!isset($raw) || $raw === null || $raw === '') return null;
$parsed = array_values(array_filter(array_map('intval', (array) $raw), fn($v) => $v > 0));
return !empty($parsed) ? $parsed : null;
};
$submittedHolidays = array_map(function($h) use ($parseHolidayDisciplineIds) {
return [
'from' => $h['from'],
'to' => $h['to'],
'disciplineIds' => $parseHolidayDisciplineIds($h['disciplineIds'] ?? null),
];
}, $request->request->get('holidays', []));
$filterActive = (bool) $request->request->get('filterActive', false);
$filteredDisciplinesParam = $request->request->get('filteredDisciplines', []);
$filteredDisciplineIds = $filterActive
? array_map('intval', is_array($filteredDisciplinesParam) ? $filteredDisciplinesParam : [])
: null;
$submittedDisciplineIdsByRange = [];
foreach ($submittedHolidays as $h) {
$rangeKey = $this->vacationRangeKey($h['from'], $h['to']);
$submittedDisciplineIdsByRange[$rangeKey] = array_values(array_unique(array_merge(
$submittedDisciplineIdsByRange[$rangeKey] ?? [],
$h['disciplineIds'] ?? []
)));
}
$submittedNullGroupRanges = array_map(
fn($h) => $this->vacationRangeKey($h['from'], $h['to']),
array_filter($submittedHolidays, fn($h) => $h['disciplineIds'] === null)
);
foreach ($childObj->getChildVacations() as $vacation) {
if ($vacation->isIsDeleted()) {
continue;
}
$vacDiscIds = $this->normalizeVacationDisciplineIds($vacation->getDisciplineIds());
$rangeKey = $this->vacationRangeKey($vacation->getDateFrom(), $vacation->getDateTo());
if ($vacDiscIds === null) {
if ($filteredDisciplineIds !== null || in_array($rangeKey, $submittedNullGroupRanges, true)) {
continue;
}
$removedDisciplineIds = null;
$newDisciplineIds = [];
} else {
$relevantManagedIds = $filteredDisciplineIds === null
? $vacDiscIds
: array_intersect($vacDiscIds, $filteredDisciplineIds);
if (empty($relevantManagedIds)) {
continue;
}
$keptOutOfScopeIds = $filteredDisciplineIds === null
? []
: array_diff($vacDiscIds, $filteredDisciplineIds);
$submittedIdsForRange = $submittedDisciplineIdsByRange[$rangeKey] ?? [];
$survivingManagedIds = array_intersect($relevantManagedIds, $submittedIdsForRange);
$removedDisciplineIds = array_values(array_diff($relevantManagedIds, $survivingManagedIds));
if (empty($removedDisciplineIds)) {
continue;
}
$newDisciplineIds = array_values(array_unique(array_merge($survivingManagedIds, $keptOutOfScopeIds)));
}
if (empty($newDisciplineIds)) {
$vacation->setIsDeleted(true);
} else {
$vacation->setDisciplineIds($newDisciplineIds);
}
$entityManager->persist($vacation);
$entityManager->flush();
$this->restoreDeletedLessonsForVacation(
$childObj,
$vacation->getDateFrom(),
$vacation->getDateTo(),
$removedDisciplineIds,
$lessonDeletedRepository,
$lessonRepository,
$teacherPaymentLogRepository,
$entityManager
);
}
foreach ($submittedHolidays as $h) {
$hGroupKey = $h['disciplineIds'] !== null ? implode(',', $h['disciplineIds']) : 'null';
foreach ($childObj->getChildVacations() as $vacation) {
if ($vacation->isIsDeleted()) {
continue;
}
$vacDiscIds = $this->normalizeVacationDisciplineIds($vacation->getDisciplineIds());
$vacGroupKey = $vacDiscIds !== null ? implode(',', $vacDiscIds) : 'null';
if ($vacGroupKey === $hGroupKey &&
(new \DateTime($h['from']))->format('Y-m-d') === $vacation->getDateFrom()->format('Y-m-d') &&
(new \DateTime($h['to']))->format('Y-m-d') === $vacation->getDateTo()->format('Y-m-d')) {
continue 2;
}
}
$disciplineIds = $h['disciplineIds'];
if ($disciplineIds === null) {
$currentIds = [];
foreach ($childObj->getChildPreferences() as $cp) {
if ($cp->getEndTime() === null && $cp->getDiscipline()) {
$currentIds[] = $cp->getDiscipline()->getId();
}
}
$disciplineIds = array_values(array_unique($currentIds));
}
$childVacation = new ChildVacation();
$childVacation->setChild($childObj);
$childVacation->setDateAdd(new \DateTime());
$childVacation->setAddedByAdmin($this->getUser());
$childVacation->setDateFrom(new \DateTime($h['from']));
$childVacation->setDateTo(new \DateTime($h['to']));
$childVacation->setDisciplineIds($disciplineIds);
$entityManager->persist($childVacation);
$entityManager->flush();
$newDates[] = ['from' => new \DateTime($h['from']), 'to' => new \DateTime($h['to']), 'disciplineIds' => $disciplineIds];
}
$disciplines = [];
$lessonDates = [];
$teachers = [];
//Guardian Email
foreach ($newDates as $newDate) {
$newDateDiscIds = $newDate['disciplineIds'];
if ($newDateDiscIds !== null && empty($newDateDiscIds)) {
continue;
}
$query = $lessonRepository->createQueryBuilder('l');
$query
->andWhere(':child MEMBER OF l.children')
->setParameter('child', $childObj)
->andWhere('l.startTime >= :from')
->setParameter('from', $newDate['from'])
->andWhere('l.startTime <= :to')
->setParameter('to', $newDate['to']);
if (!empty($newDateDiscIds)) {
$query->andWhere('l.discipline IN (:disciplines)')
->setParameter('disciplines', $newDateDiscIds);
}
$lessons = $query->getQuery()->getResult();
/** @var Lesson $lesson */
foreach ($lessons as $lesson) {
$disciplines[] = $lesson->getDiscipline()->getName();
$lessonDates[] = $lesson->getStartTime()->format('Y-m-d H:i');
if (!isset($teachers["{$lesson->getTeacher()->getId()}"])) {
$teachers["{$lesson->getTeacher()->getId()}"] = ['disciplines' => [], 'lessonDates' => []];
}
$teachers["{$lesson->getTeacher()->getId()}"]['disciplines'][] = $lesson->getDiscipline()->getName();
$teachers["{$lesson->getTeacher()->getId()}"]['lessonDates'][] = $lesson->getStartTime()->format('Y-m-d H:i');
$lessonController->makeDeletedLessonLog($lesson, 'Mokinio atostogos (Automatinis funkcionaluams)');
$mailSent = false;
if($request->request->get('ignoreNotifications') == 'false') {
$mailSent = true;
}
$guardianLogService->addGuardianActionLog("Ištrintas užsiėmimas {$lesson->getStartTime()->format('Y-m-d H:i')}", $lesson->getChild()->getGuardian(), null, 'delete', $mailSent);
$entityManager->flush();
}
}
$disciplines = array_unique($disciplines);
$replacements = [
'[CHILD_NAME]' => $childObj->getFullname(),
'[DISCIPLINE_SHORT_NAME]' => implode(', ', $disciplines),
'[VACATION_LESSON_DATES]' => implode('<br>', $lessonDates),
];
if($request->request->get('ignoreNotifications') == 'false') {
$messageBus->dispatch(new SendMailMessage($childObj->getGuardian()->getEmail(), '', '', $replacements, 'CHILD_VACATION_CREATED_GUARDIAN'));
foreach ($teachers as $teacherId => $data) {
$disciplines = $data['disciplines'];
$disciplines = array_unique($disciplines);
$replacements = [
'[CHILD_NAME]' => $childObj->getFullname(),
'[DISCIPLINE_SHORT_NAME]' => implode(', ', $disciplines),
'[VACATION_LESSON_DATES]' => implode('<br>', $data['lessonDates']),
];
$teacher = $teacherRepository->find($teacherId);
$notificationService->sendNotificationTemplate($teacher, 'CHILD_VACATION_CREATED_TUTOR', $replacements);
}
}
return new JsonResponse(['success' => true]);
}
$user = $this->getUser();
if (in_array("ROLE_GUARDIAN", $user->getRoles())) {
if ($childObj->getGuardian()->getId() != $user->getId()) {
return new JsonResponse(null, 401);
}
}
if (in_array("ROLE_CHILD", $user->getRoles())) {
if ($childObj->getId() != $user->getId()) {
return new JsonResponse(null, 401);
}
}
$date = new \DateTime();
$offset = $request->request->get('offset', 0);
$date->modify("first day of {$date->format('Y-m')}");
$date = $date->modify("{$offset} months");
$date->modify("first day of {$date->format('Y-m')}");
$date->setTime(0, 0, 0);
$end = new \DateTime($date->format('Y-m-t'));
$end = $end->setTime(23, 59, 59);
$objectRepository = $lessonRepository;
$query = $objectRepository->createQueryBuilder('a');
$query
->leftJoin('a.children', 'c')
->andWhere('c.id = :child')
->setParameter("child", $childObj->getId());
$date = new \DateTime();
$offset = $request->request->get('offset', 0);
$date->modify("first day of {$date->format('Y-m')}");
$date = $date->modify("{$offset} months");
$date->modify("first day of {$date->format('Y-m')}");
$date->setTime(0, 0, 0);
$end = new \DateTime($date->format('Y-m-t'));
$end = $end->setTime(23, 59, 59);
$end->modify('+2 minutes');
$lessonsArray = [];
$query = $lessonRepository->createQueryBuilder('a');
$query
->andWhere('a.startTime >= :startTime')
->setParameter('startTime', $date->format('Y-m-d H:i'))
->andWhere('a.endTime <= :endTime')
->setParameter('endTime', $end->format('Y-m-d H:i'))
->andWhere(':child MEMBER OF a.children')
->setParameter('child', $childObj->getId());
$lessons = $query->getQuery()->getResult();
/**
* @var $lesson Lesson
*/
foreach ($lessons as $lesson) {
$lessonsArray [] = [
'start' => $lesson->getStartTime()->format('Y-m-d H:i'),
'end' => $lesson->getEndTime()->format('Y-m-d H:i'),
'status' => $lesson->getStatus(),
'statusColor' => $lesson->getStatusColor(),
'disciplineId' => $lesson->getDiscipline() ? $lesson->getDiscipline()->getId() : null,
'disciplineName' => $lesson->getDiscipline() ? $lesson->getDiscipline()->getName() : null,
];
}
$childDisciplines = [];
$seenDisciplineIds = [];
foreach ($childObj->getChildPreferences() as $pref) {
if ($pref->getDiscipline() === null) {
continue;
}
if (in_array($pref->getStatus(), [\App\Entity\ChildPreference::STATUS_CANCELLED['value'], \App\Entity\ChildPreference::STATUS_NOT_FOUND['value']])) {
continue;
}
$disc = $pref->getDiscipline();
if (!in_array($disc->getId(), $seenDisciplineIds)) {
$childDisciplines[] = ['id' => $disc->getId(), 'name' => $disc->getName(), 'color' => \App\Constants\DisciplineColors::getColor($disc->getName())];
$seenDisciplineIds[] = $disc->getId();
}
}
$currentVacations = [];
foreach ($childObj->getChildVacations() as $vacation) {
if($vacation->isIsDeleted())
{
continue;
}
$currentVacations[] = [
'from' => $vacation->getDateFrom()->format('Y-m-d H:i'),
'to' => $vacation->getDateTo()->format('Y-m-d H:i'),
'disciplineIds' => $this->normalizeVacationDisciplineIds($vacation->getDisciplineIds()),
];
}
$alreadyDeleted = [];
foreach ($childObj->getChildVacations() as $vacation) {
if($vacation->isIsDeleted())
{
continue;
}
$query = $lessonDeletedRepository->createQueryBuilder('l');
$deletedListByAdmin = $query
->andWhere('l.startTime >= :dateFrom')
->setParameter('dateFrom', $vacation->getDateFrom())
->andWhere('l.startTime <= :dateTo')
->setParameter('dateTo', $vacation->getDateTo())
->andWhere('l.child = :child')
->setParameter('child', $childObj)
->getQuery()
->getResult();
/**
* @var LessonDeleted $deletedByAdmin
*/
foreach ($deletedListByAdmin as $deletedByAdmin) {
$alreadyInList = false;
foreach ($alreadyDeleted as $alreadyDeletedLesson)
{
if($alreadyDeletedLesson['start'] == $deletedByAdmin->getStartTime()->format('Y-m-d H:i'))
{
$alreadyInList = true;
break;
}
}
if(!$alreadyInList) {
$alreadyDeleted[] = [
'start' => $deletedByAdmin->getStartTime()->format('Y-m-d H:i'),
'end' => $deletedByAdmin->getEndTime()->format('Y-m-d H:i'),
'status' => $deletedByAdmin->getStatus(),
"date" => $deletedByAdmin->getStartTime()->format('Y-m-d H:i:s'),
'reason' => $deletedByAdmin->getDeleteReason()
];
}
}
}
return new JsonResponse([
"data" => $lessonsArray,
'currentVacations' => $currentVacations,
'alreadyDeletedByAdmin' => $alreadyDeleted,
'disciplines' => $childDisciplines,
]);
}
/**
* @Route("/new", name="guardian_new", methods={"GET","POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function new(
Request $request,
GuardianContractRepository $guardianContractRepository,
PriceIncreaseService $priceIncreaseService,
MailerInterface $mailer,
ConfigurationRepository $configurationRepository,
EntityManagerInterface $entityManager
): Response
{
$guardian = new Guardian();
$contract = $guardianContractRepository->findLatestForNewRegistration(
new \DateTime(),
$priceIncreaseService->getDuplicateMarkerLikePattern()
);
$guardian->setGuardianContract($contract);
$form = $this->createForm(GuardianType::class, $guardian);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if ($guardian->isFlagEarlyPayment()) {
$guardian->setEarlyPaymentFrom(new \DateTime());
}
foreach ($guardian->getPriceChanges() as $priceChange) {
$priceChange->setGuardian($guardian);
$entityManager->persist($priceChange);
}
foreach ($guardian->getGuardianPriceDiscounts() as $guardianPriceDiscount) {
$guardianPriceDiscount->setGuardian($guardian);
$entityManager->persist($guardianPriceDiscount);
}
$requestData = $request->request->all();
$entityManager = $this->getDoctrine()->getManager();
$plainpwd = $guardian->getPassword();
if ($plainpwd) {
$encoded = $this->userPasswordHasher->hashPassword($guardian, $plainpwd);
$guardian->setPassword($encoded);
}
if ($plainpwd == null) {
$guardian->setPassword('');
}
$entityManager->persist($guardian);
$entityManager->flush();
return $this->redirectToRoute('guardian_show', ['id' => $guardian->getId()], Response::HTTP_SEE_OTHER);
}
$defaultGuardianPriceChange = $configurationRepository->findOneBy(['name' => 'DEFAULT_GUARDIAN_PRICE_CHANGE']);
return $this->renderForm('guardian/new.html.twig', [
'guardian' => $guardian,
'form' => $form,
'defaultGuardianPriceChange' => $defaultGuardianPriceChange->getValue(),
]);
}
/**
* @Route("/{id}/convert-to-early-payment", name="guardian_convert_to_early_payment", methods={"POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function convertToEarlyPayment(
Guardian $guardian,
Request $request,
GuardianEarlyPaymentConversionService $conversionService,
LoggerInterface $logger
): JsonResponse {
$data = $request->toArray();
$earlyPaymentDay = isset($data['earlyPaymentDay']) ? (int) $data['earlyPaymentDay'] : 0;
if ($earlyPaymentDay < 1 || $earlyPaymentDay > 28) {
return new JsonResponse([
'status' => 'error',
'message' => 'Mokėjimo diena turi būti nuo 1 iki 28.',
], Response::HTTP_BAD_REQUEST);
}
try {
$result = $conversionService->convert($guardian, $earlyPaymentDay);
return new JsonResponse([
'status' => 'OK',
'balance' => $result['balance'],
'debt' => $result['debt'],
'pastLessonCount' => $result['pastLessonCount'],
'futureLessonCount' => $result['futureLessonCount'],
]);
} catch (\InvalidArgumentException $e) {
$logger->warning('Guardian early payment conversion rejected', [
'guardianId' => $guardian->getId(),
'earlyPaymentDay' => $earlyPaymentDay,
'message' => $e->getMessage(),
]);
return new JsonResponse([
'status' => 'error',
'message' => $e->getMessage(),
], Response::HTTP_BAD_REQUEST);
} catch (\Throwable $e) {
$logger->error('Guardian early payment conversion failed', [
'guardianId' => $guardian->getId(),
'earlyPaymentDay' => $earlyPaymentDay,
'exception' => $e,
]);
return new JsonResponse([
'status' => 'error',
'message' => 'Konvertavimas nepavyko.',
], Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
/**
* @Route("/{id}/convert-to-regular-payment", name="guardian_convert_to_regular_payment", methods={"POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function convertToRegularPayment(
Guardian $guardian,
GuardianEarlyPaymentConversionService $conversionService,
EarlyPaymentService $earlyPaymentService,
LoggerInterface $logger
): JsonResponse {
try {
$result = $conversionService->convertToRegular($guardian, $earlyPaymentService);
return new JsonResponse([
'status' => 'OK',
'revertedLessonCount' => $result['revertedLessonCount'],
'creditedAmount' => $result['creditedAmount'],
'balanceCarriedAsCorrection' => $result['balanceCarriedAsCorrection'],
'abandonedEarlyPaymentCount' => $result['abandonedEarlyPaymentCount'],
]);
} catch (\InvalidArgumentException $e) {
$logger->warning('Guardian regular payment conversion rejected', [
'guardianId' => $guardian->getId(),
'message' => $e->getMessage(),
]);
return new JsonResponse([
'status' => 'error',
'message' => $e->getMessage(),
], Response::HTTP_BAD_REQUEST);
} catch (\Throwable $e) {
$logger->error('Guardian regular payment conversion failed', [
'guardianId' => $guardian->getId(),
'exception' => $e,
]);
return new JsonResponse([
'status' => 'error',
'message' => 'Konvertavimas nepavyko.',
], Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
/**
* @Route("/{id}", name="guardian_show", methods={"GET"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function show(Guardian $guardian, AuthLogRepository $authLogRepository, LessonRepository $lessonRepository): Response
{
$lastSuccessAuth = $authLogRepository->findBy(['email'=>$guardian->getEmail()]);
$vacations = [];
foreach ($guardian->getChildren() as $child){
foreach ($child->getChildVacations() as $vacation){
$alreadyExists = false;
foreach ($vacations as $key => $vacationArrayItem){
if($vacationArrayItem['from'] == $vacation->getDateFrom() && $vacationArrayItem['to'] == $vacation->getDateTo())
{
$alreadyExists = true;
break;
}
}
if(!$alreadyExists) {
$vacations[] = [
'from' => $vacation->getDateFrom(),
'to' => $vacation->getDateTo(),
'child' => $vacation->getChild()->getFullname(),
'isDeleted' => $vacation->isIsDeleted()
];
}
else
{
$vacations[$key]['child'] .= ",{$vacation->getChild()->getFullname()}";
}
}
}
usort($vacations, fn($a, $b) => $b['from'] <=> $a['from']);
$totalLessons = $lessonRepository
->createQueryBuilder('l')
->select('COUNT(l)')
->andWhere('l.status in (:status)')
->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']])
->andWhere('l.startTime >= :startTime')
->setParameter('startTime', new \DateTime('2024-12-01 00:00:00'))
->andWhere('l.startTime <= :now')
->setParameter('now', new \DateTime())
->andWhere(':children member of l.children')
->setParameter('children', $guardian->getChildren())
->getQuery()
->getSingleScalarResult();
$eightLesson = $lessonRepository
->createQueryBuilder('l')
->andWhere('l.status in (:status)')
->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']])
->andWhere('l.startTime >= :startTime')
->setParameter('startTime', new \DateTime('2024-12-01 00:00:00'))
->andWhere('l.startTime <= :now')
->setParameter('now', new \DateTime())
->andWhere(':children member of l.children')
->setParameter('children', $guardian->getChildren())
->setFirstResult(7)
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
return $this->render('guardian/show.html.twig', [
'vacations'=> $vacations,
'guardian' => $guardian,
'lastAuth'=> $lastSuccessAuth ? end($lastSuccessAuth) : "",
'totalLessons' => $totalLessons,
'eightLesson' => $eightLesson,
]);
}
/**
* @Route("/{id}/recalculate", name="guardian_recalculate", methods={"GET"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function guardian_recalculate(Guardian $guardian, AuthLogRepository $authLogRepository, LessonRepository $lessonRepository): Response
{
$lessons = $lessonRepository
->createQueryBuilder('l')
->andWhere('l.startTime > :startTime')
->setParameter('startTime', new \DateTime('2025-10-01 00:00:00'))
->andWhere(':children MEMBER OF l.children')
->setParameter('children', $guardian->getChildren())
->getQuery()
->getResult();
foreach ($lessons as $lesson) {
$this->messageBus->dispatch(new RecalculateLessonMessage($lesson->getId()));;
}
die('Pamoku kainos perskaiciavimas ijungtas nuo 2025-10-01 00:00:00');
}
/**
* @Route("/{id}/edit", name="guardian_edit", methods={"GET","POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function edit(Request $request, Guardian $guardian, MailerInterface $mailer, ReminderConfigurationRepository $reminderConfigurationRepository, ChildRepository $childRepository, EntityManagerInterface $entityManager, PaymentService $paymentService, LessonRepository $lessonRepository): Response
{
$autumn2024back = $guardian->isAutumn2024back();
$progress = $guardian->isProgress();
$notCommingBack2024 = $guardian->isNotCommingBack2024();
$summer = $guardian->getSummer();
$form = $this->createForm(GuardianType::class, $guardian);
$encoded = $guardian->getPassword();
$originalPriceChanges = $guardian->getPriceChanges()->getValues();
$originalGuardianPriceDiscounts = $guardian->getGuardianPriceDiscounts()->getValues();
$originalStatus = $guardian->getStatus();
$recalculatePeriods = [];
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if($progress != $guardian->isProgress() && $guardian->isProgress())
{
$guardian->setProgressByAdmin($this->getUser());
$guardian->setProgressDate(new \DateTime());
}
if($notCommingBack2024 != $guardian->isNotCommingBack2024() && $guardian->isNotCommingBack2024())
{
$guardian->setNotCommingBack2024ByAdmin($this->getUser());
$guardian->setNotCommingBack2024Date(new \DateTime());
}
if($autumn2024back != $guardian->isAutumn2024back() && $guardian->isAutumn2024back())
{
$guardian->setAutumn2024backByAdmin($this->getUser());
$guardian->setAutumn2024backDate(new \DateTime());
}
if($summer != $guardian->getSummer() && $guardian->getSummer())
{
$guardian->setSummerByAdmin($this->getUser());
$guardian->setSummerDate(new \DateTime());
}
if ($guardian->isFlagEarlyPayment() && !$guardian->getEarlyPaymentFrom()) {
$guardian->setEarlyPaymentFrom(new \DateTime());
}
foreach ($originalPriceChanges as $originalPriceChange) {
if (!$guardian->getPriceChanges()->contains($originalPriceChange)) {
$entityManager->remove($originalPriceChange);
}
}
foreach ($guardian->getPriceChanges() as $priceChange) {
$priceChange->setGuardian($guardian);
$entityManager->persist($priceChange);
}
foreach ($originalGuardianPriceDiscounts as $originalGuardianPriceDiscount) {
if (!$guardian->getGuardianPriceDiscounts()->contains($originalGuardianPriceDiscount)) {
$recalculatePeriods[] = ['from' => $originalGuardianPriceDiscount->getDateFrom(), 'to' => $originalGuardianPriceDiscount->getDateTo()];
$entityManager->remove($originalGuardianPriceDiscount);
}
}
foreach ($guardian->getGuardianPriceDiscounts() as $guardianPriceDiscount) {
$guardianPriceDiscount->setGuardian($guardian);
$entityManager->persist($guardianPriceDiscount);
$recalculatePeriods[] = ['from' => $guardianPriceDiscount->getDateFrom(), 'to' => $guardianPriceDiscount->getDateTo()];
}
$plainpwd = $guardian->getPassword();
if ($guardian->getPassword()) {
$encoded = $this->userPasswordHasher->hashPassword($guardian, $plainpwd);
}
$guardian->setPassword($encoded);
$entityManager->persist($guardian);
$entityManager->flush();
foreach ($recalculatePeriods as $recalculatePeriod)
{
$paymentService->recalculateGuardianLessons($guardian, $recalculatePeriod['from'], $recalculatePeriod['to']);
}
if($guardian == Guardian::STATUS_CONFIRMED['text'] && $originalStatus != Guardian::STATUS_CONFIRMED['text'])
{
$this->generateAndSendChildrenCredentials($request, $guardian, $mailer, $reminderConfigurationRepository, $childRepository, $entityManager);
}
return $this->redirectToRoute('guardian_index', [], Response::HTTP_SEE_OTHER);
}
$guardianPriceChanges = $this->get('serializer')->normalize($guardian->getPriceChanges(), null, ['groups' => "GuardianPriceChangesEdit"]);
$childPreferences = [];
foreach ($guardian->getChildren() as $child){
foreach ($child->getChildPreferences() as $childPreference) {
$childPreferences[]=['name'=>$childPreference->getAdminUser()?$childPreference->getAdminUser()->getFullname():"Nėra",'discipline'=>$childPreference->getDiscipline()->getName()];
}
}
$totalLessons = $lessonRepository
->createQueryBuilder('l')
->select('COUNT(l)')
->andWhere('l.status in (:status)')
->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']])
->andWhere('l.startTime >= :startTime')
->setParameter('startTime', new \DateTime('2024-12-01 00:00:00'))
->andWhere('l.startTime <= :now')
->setParameter('now', new \DateTime())
->andWhere(':children member of l.children')
->setParameter('children', $guardian->getChildren())
->getQuery()
->getSingleScalarResult();
$eightLesson = $lessonRepository
->createQueryBuilder('l')
->select('COUNT(l)')
->andWhere('l.status in (:status)')
->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']])
->andWhere('l.startTime >= :startTime')
->setParameter('startTime', new \DateTime('2024-12-01 00:00:00'))
->andWhere('l.startTime <= :now')
->setParameter('now', new \DateTime())
->andWhere(':children member of l.children')
->setParameter('children', $guardian->getChildren())
->setFirstResult(7)
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
return $this->renderForm('guardian/edit.html.twig', [
'guardian' => $guardian,
'form' => $form,
'guardian_price_changes' => $guardianPriceChanges,
'admin_log_disciplines' =>$childPreferences,
'totalLessons' => $totalLessons,
'eightLesson' => $eightLesson,
]);
}
/**
* @Route("/{id}/new_price_email", name="guardian_new_price_email", methods={"GET"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function guardian_new_price_email(Request $request, Guardian $guardian, EmailLogRepository $emailLogRepository): Response
{
$email = $emailLogRepository->createQueryBuilder('e')
->andWhere('e.email = :email')
->setParameter('email', $guardian->getEmail())
->andWhere('e.subject like :subject')
->setParameter('subject', "Svarbi informacija: paslaugų įkainių pasikeitimas | Corepetitus korepetitoriai%")
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
if($email)
{
echo $email->getContent();
die();
}
die();
}
/**
* @Route("/delete/{id}", name="guardian_delete", methods={"POST","DELETE"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function delete(Request $request, Guardian $guardian): Response
{
$entityManager = $this->getDoctrine()->getManager();
if ($guardian->getStatus() == Guardian::STATUS_PREPARING['text'] || $guardian->getStatus() == Guardian::STATUS_SENT['text']) {
$entityManager->remove($guardian);
$entityManager->flush();
} elseif ($guardian->getStatus() == Guardian::STATUS_CANCELLED['text']) {
$guardian->setIsDeleted(1);
$entityManager->persist($guardian);
$entityManager->flush();
}
//hard delete
if(false)
{
foreach ($guardian->getChildren() as $child) {
foreach ($child->getChildPreferenceHistories() as $childPreferenceHistory)
{
$entityManager->remove($childPreferenceHistory);
}
$entityManager->remove($child);
}
foreach ($guardian->getPriceChanges() as $priceChange)
{
$entityManager->remove($priceChange);
}
$entityManager->remove($guardian);
$entityManager->flush();
}
return $this->redirectToRoute('guardian_index', [], Response::HTTP_SEE_OTHER);
}
/**
* @Route("/{id}/new/child", name="guardian_child_new", methods={"GET","POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function newChild(Request $request, Guardian $guardian, ChildRepository $childRepository, MailerInterface $mailer, ReminderConfigurationRepository $reminderConfigurationRepository): Response
{
$child = new Child();
$child->setGuardian($guardian);
$form = $this->createForm(ChildType::class, $child);
$form->handleRequest($request);
$childrenCredentials = '';
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
if($guardian->getStatus() == Guardian::STATUS_CONFIRMED['text'])
{
$plainpwd = HelperUtils::generateLithuanianPassword(1,4);
$encoded = $this->userPasswordHasher->hashPassword($child, $plainpwd);
$child->setPassword($encoded);
$username = $child->getUsername();
if(strpos($username,'cor') == false) {
$username = HelperUtils::removeLtLetters(strtolower(explode(' ', $child->getFullname())[0]));
if (!$username) {
$username = HelperUtils::generateLithuanianPassword(1, 0);
}
$username = $username . 'cor';
$counter = 1;
while ($childRepository->findOneBy(['username' => $username . $counter])) {
$counter++;
if ($counter == 69) {
$counter++;
}
}
$username = $username . $counter;
$child->setUsername($username);
$childrenCredentials .= "
<b>Mokinys:</b> {$child->getFullname()}<br>
<b>Mokinio prisijungimo vardas:</b> {$username}<br>
<b>Slaptažodis:</b> {$plainpwd}<br><br>
";
}
$replacements = [];
$replacements['[CHILD_NAME]'] = $child->getFullname();
$replacements['[CHILD_EMAIL]'] = $child->getEmail();
$replacements['[CHILD_USERNAME]'] = $child->getEmail() ?: $child->getUsername();
$replacements['[CHILD_PASSWORD]'] = $plainpwd;
$replacements['[CHILDREN_CREDENTIALS]'] = $childrenCredentials;
$this->messageBus->dispatch(new SendMailMessage($guardian->getEmail(), '', '', $replacements, 'GUARDIAN_CHILDREN_CREDENTIALS'));
if($child->getEmail())
{
$this->messageBus->dispatch(new SendMailMessage($child->getEmail(), '', '', $replacements, 'CHILD_NEW_CREDENTIALS'));
}
}
$token = HelperUtils::createRandomString();
while($childRepository->findOneBy(['loginToken' => $token]))
{
$token = HelperUtils::createRandomString();
}
$child->setLoginToken($token);
$entityManager->persist($child);
$entityManager->flush();
return $this->redirectToRoute('guardian_show', ['id' => $guardian->getId()], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('child/new.html.twig', [
'child' => $child,
'form' => $form,
]);
}
/**
* @Route("/{id}/children_credentials", name="guardian_children_credentials", methods={"GET","POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function generateAndSendChildrenCredentials(Request $request, CredentialsService $credentialsService, Guardian $guardian, MailerInterface $mailer, ReminderConfigurationRepository $reminderConfigurationRepository, ChildRepository $childRepository, EntityManagerInterface $entityManager): Response
{
$credentialsService->sendChildrenCredentialsEmail($guardian);
return $this->redirectToRoute('guardian_show', ['id' => $guardian->getId()], Response::HTTP_SEE_OTHER);
}
/**
* @Route("/{id}/child_credentials", name="guardian_child_credentials", methods={"GET","POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function generateAndSendChildCredentials(Request $request, CredentialsService $credentialsService, Child $child, MailerInterface $mailer, ReminderConfigurationRepository $reminderConfigurationRepository, ChildRepository $childRepository, EntityManagerInterface $entityManager): Response
{
$credentialsService->sendChildEmailLoginToChildAndGuardian($child);
return $this->redirectToRoute('guardian_show', ['id' => $child->getGuardian()->getId()], Response::HTTP_SEE_OTHER);
}
/**
* @Route("/summer_guardians", name="summer_guardians_csv", priority="1", methods={"GET"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function summerGuardiansCsv()
{
$date = new \DateTime();
$file = $this->getParameter('kernel.project_dir') . "/var/summer-{$date->format('Y-m-d')}.csv";
if (file_exists($file)) {
$f = fopen($file, 'r');
header('Content-Encoding: UTF-8');
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="summer.csv";');
echo "\xEF\xBB\xBF"; // UTF-8 BOM
fpassthru($f);
fclose($f);
}
exit();
}
/**
* @Route("/guardians_csv", name="guardians_csv", priority="2", methods={"GET"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function guardiansCSV()
{
$date = new \DateTime();
$file = $this->getParameter('kernel.project_dir') . "/var/guardian-{$date->format('Y-m-d')}.csv";
if (file_exists($file)) {
$f = fopen($file, 'r');
header('Content-Encoding: UTF-8');
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="guardian.csv";');
echo "\xEF\xBB\xBF"; // UTF-8 BOM
fpassthru($f);
fclose($f);
}
exit();
}
/**
* @Route("/guardians_all_csv", name="guardians_all_csv", priority="2", methods={"GET"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function guardiansAllCSV()
{
$date = new \DateTime();
$file = $this->getParameter('kernel.project_dir') . "/var/guardian-all-{$date->format('Y-m-d')}.csv";
if (file_exists($file)) {
$f = fopen($file, 'r');
header('Content-Encoding: UTF-8');
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="guardian-all.csv";');
echo "\xEF\xBB\xBF"; // UTF-8 BOM
fpassthru($f);
fclose($f);
}
exit();
}
/**
* @Route("/ajax/payment/{id}/edit", name="guardian_payment_edit", methods={"POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
* @param Payment $payment
* @param Request $request
* @return Response
*/
public function ajaxPaymentEdit(Payment $payment, Request $request, PaymentService $paymentService, GuardianLogService $guardianLogService): Response
{
if (!$payment) {
$error = new JsonResponse();
$error
->setStatusCode(Response::HTTP_NOT_FOUND)
->setData(['status' => "Sąskaita nerasta sistemoje."]);
return $error;
}
if ($payment->getStatus() == Payment::STATUS_PAID) {
$error = new JsonResponse();
$error
->setStatusCode(Response::HTTP_NOT_FOUND)
->setData(['status' => "Sąskaita jau apmokėta!"]);
return $error;
}
$paymentService->recalculateNewestGuardianPayment($payment->getGuardian(), $payment->getDateFor());
$payment = $this->get('serializer')->deserialize($request->getContent(), Payment::class, 'json', [
AbstractNormalizer::OBJECT_TO_POPULATE => $payment
]);
/**
* @var $payment Payment
*/
$payment->setAmount($payment->getExpectedAmount() + $payment->getCorrectionAmount());
$guardianLogService->addGuardianActionLog($request->getContent(), $payment->getGuardian(), $payment, 'update', false);
RestUtils::saveObject($this->getDoctrine()->getManager(), $payment);
return new JsonResponse(['status' => "OK"], Response::HTTP_OK);
}
/**
* @Route("/payment-notification/{id}/resend", name="guardian_payment_notification_resend", priority="2", methods={"POST"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function resendPaymentNotification(
PaymentNotificationLog $paymentNotificationLog,
EntityManagerInterface $entityManager
): JsonResponse {
$smsContent = $paymentNotificationLog->getSmsContent();
$phoneNumber = $paymentNotificationLog->getPhonenumber();
if ($smsContent && $phoneNumber) {
$this->messageBus->dispatch(new SendSMSMessage($phoneNumber, $smsContent));
}
$manualType = 'MANUAL_' . $paymentNotificationLog->getNotificationType();
$resendLog = (new PaymentNotificationLog())
->setDateAdd(new \DateTime())
->setGuardian($paymentNotificationLog->getGuardian())
->setEmail($paymentNotificationLog->getEmail())
->setNotificationType($manualType)
->setPhonenumber($phoneNumber)
->setSmsContent($smsContent);
$entityManager->persist($resendLog);
$entityManager->flush();
return new JsonResponse(['success' => true]);
}
private function normalizeVacationDisciplineIds(?array $rawIds): ?array
{
if ($rawIds === null) return null;
$parsed = array_map(function($id) {
$id = (string) $id;
return str_contains($id, ':') ? (int) explode(':', $id)[0] : (int) $id;
}, $rawIds);
$result = array_values(array_unique(array_filter($parsed, fn($v) => $v > 0)));
return !empty($result) ? $result : null;
}
/**
* @param \DateTimeInterface|string $from
* @param \DateTimeInterface|string $to
*/
private function vacationRangeKey($from, $to): string
{
$fromDate = $from instanceof \DateTimeInterface ? $from : new \DateTime($from);
$toDate = $to instanceof \DateTimeInterface ? $to : new \DateTime($to);
return $fromDate->format('Y-m-d') . '|' . $toDate->format('Y-m-d');
}
private function restoreDeletedLessonsForVacation(
Child $childObj,
\DateTimeInterface $dateFrom,
\DateTimeInterface $dateTo,
?array $disciplineIds,
LessonDeletedRepository $lessonDeletedRepository,
LessonRepository $lessonRepository,
TeacherPaymentLogRepository $teacherPaymentLogRepository,
EntityManagerInterface $entityManager
): void {
$query = $lessonDeletedRepository->createQueryBuilder('l')
->andWhere('l.startTime >= :dateFrom')
->setParameter('dateFrom', $dateFrom)
->andWhere('l.startTime <= :dateTo')
->setParameter('dateTo', $dateTo)
->andWhere('l.child = :child')
->setParameter('child', $childObj);
if ($disciplineIds !== null) {
$query->andWhere('l.discipline IN (:disciplines)')
->setParameter('disciplines', $disciplineIds);
}
/** @var LessonDeleted[] $deletedListByAdmin */
$deletedListByAdmin = $query->getQuery()->getResult();
foreach ($deletedListByAdmin as $deletedByAdmin) {
if ($teacherPaymentLogRepository->count(['lessonDeleted' => $deletedByAdmin]) > 0) {
// The teacher was already paid for this cancelled lesson; keep the historical
// record instead of deleting it, since removing it would violate the
// teacher_payment_log foreign key and abort the whole save.
continue;
}
$lessons = $lessonRepository->createQueryBuilder('l')
->andWhere('l.teacher = :teacher')
->setParameter('teacher', $deletedByAdmin->getTeacher()->getId())
->andWhere('l.discipline = :discipline')
->setParameter('discipline', $deletedByAdmin->getDiscipline()->getId())
->andWhere(':child MEMBER OF l.children')
->setParameter('child', $deletedByAdmin->getChild()->getId())
->andWhere('l.startTime = :startTime')
->setParameter('startTime', $deletedByAdmin->getStartTime()->format('Y-m-d H:i'))
->andWhere('l.endTime = :endTime')
->setParameter('endTime', $deletedByAdmin->getEndTime()->format('Y-m-d H:i'))
->andWhere('l.type = :type')
->setParameter('type', $deletedByAdmin->getType())
->andWhere('l.duration = :duration')
->setParameter('duration', $deletedByAdmin->getDuration())
->getQuery()
->getResult();
if (!$lessons && $deletedByAdmin->getTeacher()->getStatus() == Teacher::STATUS_ACTIVE) {
$lesson = (new Lesson())
->setType($deletedByAdmin->getType())
->setStatus($deletedByAdmin->getStatus())
->setClass($deletedByAdmin->getChild()->getClass())
->setDiscipline($deletedByAdmin->getDiscipline())
->setTeacher($deletedByAdmin->getTeacher())
->setDuration($deletedByAdmin->getDuration())
->setStartTime($deletedByAdmin->getStartTime())
->setEndTime($deletedByAdmin->getEndTime());
$lesson->addChild($deletedByAdmin->getChild());
$entityManager->persist($lesson);
$entityManager->flush();
}
$entityManager->remove($deletedByAdmin);
$entityManager->flush();
}
}
/**
* @Route("/guardians_all_csv_marketing", name="guardians_all_csv_marketing", priority="2", methods={"GET"})
* @Security("is_granted('ROLE_ADMIN') and is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function guardiansAllCSVMarketing()
{
$file = $this->getParameter('kernel.project_dir') . "/var/csv/" . self::FILE_NAME_DISCOUNT_CODE_MARKETING;
if (file_exists($file)) {
$f = fopen($file, 'r');
header('Content-Encoding: UTF-8');
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="Marketingo.csv";');
echo "\xEF\xBB\xBF"; // UTF-8 BOM
fpassthru($f);
fclose($f);
}
exit();
}
/**
* @param string[] $statuses
* @throws Exception
*/
private function getPreviousMonthLessonsMinutes(EntityManagerInterface $entityManager, array $statuses): int
{
$conn = $entityManager->getConnection();
$dateStart = new \DateTime('first day of last month');
$dateStart->setTime(0, 0, 0);
$dateEnd = new \DateTime('last day of last month');
$dateEnd->setTime(23, 59, 59);
$quotedStatuses = array_map([$conn, 'quote'], $statuses);
$in = implode(',', $quotedStatuses);
$RAW_QUERY = "SELECT SUM(lesson.duration) as diff
FROM `lesson`
WHERE start_time >= '" . $dateStart->format('Y-m-d H:i:s') . "'
AND start_time <= '" . $dateEnd->format('Y-m-d H:i:s') . "'
AND status IN (" . $in . ")";
$statement = $conn->prepare($RAW_QUERY);
$resultSet = $statement->executeQuery();
$rows = $resultSet->fetchAllAssociative();
$row = reset($rows);
return (int) ($row['diff'] ?? 0);
}
/**
* @param string[] $statuses
* @throws Exception
*/
private function getCurrentMonthLessonsMinutes(EntityManagerInterface $entityManager, array $statuses): int
{
$conn = $entityManager->getConnection();
$dateStart = new \DateTime('first day of this month');
$dateStart->setTime(0, 0, 0);
$dateEnd = new \DateTime('last day of this month');
$dateEnd->setTime(23, 59, 59);
$quotedStatuses = array_map([$conn, 'quote'], $statuses);
$in = implode(',', $quotedStatuses);
$RAW_QUERY = "SELECT SUM(lesson.duration) as diff
FROM `lesson`
WHERE start_time >= '" . $dateStart->format('Y-m-d H:i:s') . "'
AND start_time <= '" . $dateEnd->format('Y-m-d H:i:s') . "'
AND status IN (" . $in . ")";
$statement = $conn->prepare($RAW_QUERY);
$resultSet = $statement->executeQuery();
$rows = $resultSet->fetchAllAssociative();
$row = reset($rows);
return (int) ($row['diff'] ?? 0);
}
}