<?php namespace App\Admin; use App\Entity\AccountAdherent; use App\Entity\Adherent; use App\Entity\CotisationAdherent; use App\Entity\DependentChild; use App\Entity\Geoloc; use App\Entity\GlobalParameter; use App\Entity\Groupe; use App\Entity\User; use App\Entity\Usergroup; use App\Entity\ProfilDeCotisation; use App\Enum\CurrencyEnum; use App\Events\MLCEvents; use App\Exporter\CustomDoctrineORMQuerySourceIterator; use App\Form\Type\DependentChildFormType; use App\Form\Type\GeolocFormType; use App\Form\Type\UserFormType; use App\Utils\TAVCotisationUtils; use Doctrine\ORM\Query; use FOS\UserBundle\Event\UserEvent; use Knp\Menu\ItemInterface as MenuItemInterface; use Sonata\AdminBundle\Admin\AbstractAdmin; use Sonata\AdminBundle\Admin\AdminInterface; use Sonata\AdminBundle\Datagrid\DatagridInterface; use Sonata\AdminBundle\Datagrid\DatagridMapper; use Sonata\AdminBundle\Datagrid\ListMapper; use Sonata\AdminBundle\Datagrid\ProxyQueryInterface; use Sonata\AdminBundle\Form\FormMapper; use Sonata\AdminBundle\Route\RouteCollection; use Sonata\AdminBundle\Show\ShowMapper; use Sonata\DoctrineORMAdminBundle\Datagrid\OrderByToSelectWalker; use Sonata\DoctrineORMAdminBundle\Filter\CallbackFilter; use Sonata\Form\Type\DateTimeRangePickerType; use Sonata\UserBundle\Model\UserManagerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\Extension\Core\Type\PasswordType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\NumberType; use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Security; use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; /** * Administration des adhérents. * * KOHINOS : Outil de gestion de Monnaie Locale Complémentaire * * @author Julien Jorry <julien.jorry@gmail.com> */ class AdherentAdmin extends AbstractAdmin { protected $baseRouteName = 'adherent'; protected $baseRoutePattern = 'adherent'; protected $security; protected $tavCotisationUtils; protected $datagridValues = [ // reverse order (default = 'ASC') '_sort_order' => 'DESC', // name of the ordered field (default = the model's id field, if any) '_sort_by' => 'createdAt', // '_page' => 1, // '_per_page' => 32 ]; public function setSecurity(Security $security) { $this->security = $security; } public function setTavCotisationUtils(TAVCotisationUtils $tavCotisationUtils) { $this->tavCotisationUtils = $tavCotisationUtils; } public function configure() { parent::configure(); } protected function configureQuery(ProxyQueryInterface $query): ProxyQueryInterface { $user = $this->security->getUser(); $query = parent::configureQuery($query); $query ->innerJoin($query->getRootAliases()[0] . '.user', 'u') ->addSelect('u') ; // if ($this->hasRequest()) { // if (empty($this->getRequest()->getSession()->get('_groupegere'))) { // if ($this->security->isGranted('ROLE_GESTION_GROUPE') || $this->security->isGranted('ROLE_CONTACT')) { // $query->andWhere('false = true'); // } // } else { // $query // ->andWhere($query->getRootAliases()[0] . '.groupe = :groupe') // ->setParameter('groupe', $this->getRequest()->getSession()->get('_groupegere')) // ; // } // } return $query; } protected function configureSideMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null) { if (!$childAdmin && !in_array($action, ['edit', 'show'])) { return; } $admin = $this->isChild() ? $this->getParent() : $this; $id = $admin->getRequest()->get('id'); $user = $this->getConfigurationPool()->getContainer()->get('doctrine')->getRepository(User::class)->findOneBy(['adherent' => $id]); if ($this->isGranted('EDIT') && null != $user) { $menu->addChild("Modifier l'utilisateur", [ 'uri' => $this->getConfigurationPool()->getContainer()->get('router')->generate('admin_app_user_edit', ['id' => $user->getId()], UrlGeneratorInterface::ABSOLUTE_URL), ]); } if (!$this->getConfigurationPool()->getContainer()->getParameter('tav_env')) { $menu->addChild('Ajouter une cotisation', [ 'uri' => $this->getConfigurationPool()->getContainer()->get('router')->generate('cotisation_adherent_create', ['expediteur' => $id], UrlGeneratorInterface::ABSOLUTE_URL), ]); $menu->addChild('Voir les cotisations', [ 'uri' => $this->getConfigurationPool()->getContainer()->get('router')->generate('cotisation_adherent_list', ['filter' => ['expediteur' => ['value' => $id]]], UrlGeneratorInterface::ABSOLUTE_URL), ]); } } /** * {@inheritdoc} */ protected function configureFormFields(FormMapper $formMapper): void { // Initialize adherent $adherent = $this->getSubject(); $now = new \DateTime(); if ($this->isCurrentRoute('create')) { $user = $this->userManager->createUser(); $groupe = $this->getConfigurationPool()->getContainer()->get('doctrine')->getRepository(Usergroup::class)->findOneByName('Adherent'); $user->setEnabled(true); $user->addPossiblegroup($groupe); $user->setGroups([$groupe]); $adherent->setEcompte(0); $user->setAdherent($adherent); $adherent->setUser($user); } if (null == $adherent->getGeoloc()) { $adherent->setGeoloc(new Geoloc()); } // params $tav_env = $this->getConfigurationPool()->getContainer()->getParameter('tav_env'); $household_based_allowance = $this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance'); $formMapper ->tab('General') ->with('Identité', ['class' => 'col-md-7']) ->add('user', UserFormType::class, [ 'label' => false, ]) ->end() ->with('Adresse', ['class' => 'col-md-5']) ->add('geoloc', GeolocFormType::class, [ 'label' => false, 'required' => true, 'with_geoloc' => false, 'with_latlon' => false, 'with_subterritory' => $tav_env && $household_based_allowance, 'with_quartier' => $tav_env && $household_based_allowance ]) ->end() ->with('Groupe', ['class' => 'col-md-5']) ->add('groupe', ChoiceType::class, [ 'required' => true, 'label' => 'Groupe local :', 'choices' => $this->getConfigurationPool()->getContainer()->get('doctrine')->getRepository(Groupe::class)->findAll(), 'choice_label' => 'name', 'placeholder' => 'Choisir un groupe', ]) ->end() ->end() ; /** * In TAV env, 2 allowance processes possible: * - household based (if param set) * - cotisation profile and rate based (default) */ if ($tav_env) { if ($household_based_allowance) { $formMapper ->tab('General') ->with('Foyer', ['class' => 'col-md-7']) ->add('householdComposition',ChoiceType::class, [ 'choices' => [ "Personne seule" => "Personne seule", "Couple sans enfant à charge" => "Couple sans enfant à charge", "Famille mono-parentale" => "Famille mono-parentale", "Couple avec enfant(s) à charge" => "Couple avec enfant(s) à charge", "Autre" => "Autre" ], 'label' => "Composition du foyer (pour information)", 'required' => true, 'placeholder' => "Choix de la composition du foyer", ]) ->add('householdAdultCount',IntegerType::class, [ 'label' => "Nombre total d'adultes dans le foyer (pour calculer l'allocation)", 'constraints' => [ new GreaterThanOrEqual(['value' => 0]), ], 'required' => true, 'attr' => [ 'autocomplete' => false ] ]) ->add('dependentChildren', CollectionType::class, [ 'entry_type' => DependentChildFormType::class, 'entry_options' => [ 'label' => true, 'data_class' => DependentChild::class, 'attr' => ['class' => 'border pl-3 pr-3 pt-2'] ], 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false, 'label' => "Enfant(s) à charge (pour calculer l'allocation)" ]) ->end() ->end(); // Add cotisation info $formMapper ->tab('General') ->with('Informations de cotisation', ['class' => 'col-md-5']) ->add('cotisationAmount', NumberType::class, [ 'label' => 'Montant de la cotisation (en €)', 'help' => 'Montant minimum : 10€ par foyer + 5€/personne supplémentaire du foyer' ]) ->add('allocationAmount', NumberType::class, [ 'label' => 'Montant d\'allocation prévu en fonction du foyer (en MonA)', 'disabled' => true, 'required' => false, 'help' => 'Le montant de l\'allocation sera calculé automatiquement en fonction des informations du foyer une fois les informations sauvegardées.' ]) ->end() ->end(); } else { // For Comptoir role in edit mode, hide profile choice $displayProfilChoice = true; $isComptoirOnly = $this->security->isGranted('ROLE_COMPTOIR') && !$this->isGranted('ROLE_ADMIN') && !$this->isGranted('ROLE_SUPER_ADMIN') && !$this->isGranted('ROLE_ADMIN_SIEGE'); if ($isComptoirOnly && $this->isCurrentRoute('edit')) { $displayProfilChoice = false; } if ($displayProfilChoice) { $formMapper ->tab('General') ->with('Informations de cotisation', ['class' => 'col-md-5']) ->add('profilDeCotisation', ChoiceType::class, [ 'required' => true, 'label' => 'Choix du profil de cotisation :', 'choices' => $this->getConfigurationPool()->getContainer()->get('doctrine')->getRepository(ProfilDeCotisation::class)->findBy([], ['montant' => 'ASC']), 'empty_data' => null, 'placeholder' => 'Choisir un profil', 'choice_label' => function ($choice, $key, $value) { if (null === $choice) { return ''; } return $choice->__toString(); }, 'attr' => [ 'autocomplete' => 'off' ] ]) ->end() ->end(); } $formMapper ->tab('General') ->with('Informations de cotisation', ['class' => 'col-md-5']) ->add('moyenDePaiement', ChoiceType::class, [ 'required' => true, 'label' => 'Moyen de paiement :', 'choices' => [ "CB" => "CB", "espèces" => "espèces", "chèque" => "chèque", "prélèvement" => "prélèvement" ], 'empty_data' => null, 'placeholder' => 'Choisir un moyen de paiement', 'attr' => [ 'autocomplete' => 'off' ] ]) ->add('jourPrelevement', ChoiceType::class, [ 'required' => false, 'label' => 'Jour de prélèvement :', 'choices' => $this->daysOfMonth(), 'empty_data' => null, 'placeholder' => 'Choisir un jour de prélèvement', 'attr' => [ 'autocomplete' => 'off' ] ]) ->add('mailRappelCotisation', CheckboxType::class, [ 'required' => false, 'label' => 'Recevoir un rappel du paiement de ma cotisation par mail', 'attr' => [ 'autocomplete' => 'off' ] ]) ->add('jourMailRappelCotisation', ChoiceType::class, [ 'required' => false, 'label' => 'Jour de l\'envoi du mail de rappel :', 'choices' => $this->daysOfMonth(), 'empty_data' => null, 'placeholder' => 'Choisir un jour pour l\'envoi du mail de rappel', 'attr' => [ 'autocomplete' => 'off' ] ]) ->end() ->end(); } if (!empty($adherent) && !empty($adherent->getEmlcAccount()) ) { $em = $this->getConfigurationPool()->getContainer()->get('doctrine')->getManager(); $balance = $adherent->getEmlcAccount()->getBalance(); $mlc = $em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_SYMBOL); $formMapper ->tab('General') ->with('Informations de cotisation') ->add('idmlc', TextType::class, [ 'disabled' => true, 'required' => false, 'label' => 'Solde e-' . $mlc . ' :', 'data' => $balance . ' ' . $mlc, 'attr' => [ 'autocomplete' => 'off' ] ]) ->end() ->end(); //Add form part allowing super admin to fix balance if($this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance') && $this->security->isGranted('ROLE_SUPER_ADMIN')) { $formMapper ->tab('General') ->with('Informations de cotisation') ->add('fixedBalance', TextType::class, [ 'label' => "Corriger le solde suite à une erreur de cotisation", 'mapped' => false, 'required' => false, 'attr' => [ 'autocomplete' => 'off', 'class' => 'fixBalanceAdherentFormPart' ] ]) ->add('justification', TextType::class, [ 'mapped' => false, 'required' => false, 'attr' => [ 'autocomplete' => 'off', 'class' => 'fixBalanceAdherentFormPart' ] ]) ->add('password', PasswordType::class, [ 'label' => 'Mot de passe pour corriger le solde', 'mapped' => false, 'required' => false, 'data' => "", 'attr' => [ 'autocomplete' => 'off', 'class' => 'fixBalanceAdherentFormPart' ] ]) ->end() ->end(); } } } $em = $this->getConfigurationPool()->getContainer()->get('doctrine')->getManager(); $formMapper->getFormBuilder()->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use ($em) { $adherent = $event->getData(); // Check user email $user = $adherent->getUser(); if (!$user || null === $user->getId()) { $repo = $em->getRepository(User::class); $emailExist = $repo->findBy(['email' => $user->getEmail()]); if (count($emailExist) > 0) { $event->getForm()->get('user')->get('email')->addError(new FormError('Courriel déjà utilisé !')); } else { $user->setUsername($user->getEmail()); } } if ($this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance')) { // check cotisation amount $adultsCount = $adherent->getHouseholdAdultCount(); $dependentChildrenCount = count($adherent->getDependentChildren()); $minCotisationAmount = 10 + 5 * ( $adultsCount - 1 ) + 5 * $dependentChildrenCount; if ($adherent->getCotisationAmount() < $minCotisationAmount) { $event->getForm()->get('cotisationAmount')->addError(new FormError('Le montant minimum est de ' . $minCotisationAmount . '€ (selon les données du foyer indiquées)')); } // try to fix balance if required if($this->security->isGranted('ROLE_SUPER_ADMIN') && $event->getForm()->has('fixedBalance') && $event->getForm()->get('fixedBalance')->getData() && $event->getForm()->get('fixedBalance')->getData() >= 0) { $password = $this->getConfigurationPool()->getContainer()->getParameter('fix_balance_adherent_password'); //this password purpose is to be an additional warning for super admin and //is not intended to be securely stored as only super admin can use this feature if ($event->getForm()->get('password')->getData() !== $password) { $event->getForm()->get('password')->addError(new FormError('Mot de passe incorrect.')); } elseif(!$event->getForm()->get('justification')->getData()) { $event->getForm()->get('justification')->addError(new FormError('Merci de justifier cette opération sensible.')); } else { $this->tavCotisationUtils->fixBalance( $adherent, $event->getForm()->get('fixedBalance')->getData(), $event->getForm()->get('justification')->getData() ); $em->flush(); } } } }); parent::configureFormFields($formMapper); } public function preUpdate($adherent) { $this->updateAdherent($adherent); } public function prePersist($adherent) { $this->updateAdherent($adherent); $this->eventDispatcher->dispatch(MLCEvents::REGISTRATION_ADHERENT, new UserEvent($adherent->getUser(), $this->getRequest())); } private function updateAdherent($adherent) { $em = $this->getConfigurationPool()->getContainer()->get('doctrine')->getManager(); if (empty($adherent->getUser()->getUsername())) { $adherent->getUser()->setUsername($adherent->getUser()->getEmail()); } if (empty($adherent->getUser()->getPassword())) { // @TODO : generate password with tokengenerator // $tokenGenerator = $this->getConfigurationPool()->getContainer()->get('fos_user.util.token_generator'); // $password = substr($tokenGenerator->generateToken(), 0, 12); $bytes = random_bytes(64); $password = rtrim(strtr(base64_encode($bytes), '+/', '-_'), '='); $adherent->getUser()->setPassword($password); } $this->userManager->updateCanonicalFields($adherent->getUser()); $adherent->getUser()->createEmailToken(); $account = $em->getRepository(AccountAdherent::class)->findOneBy(['adherent' => $adherent, 'currency' => CurrencyEnum::CURRENCY_EMLC]); if (null == $account) { $account = new AccountAdherent(); $account ->setCurrency(CurrencyEnum::CURRENCY_EMLC) ; $adherent->addAccount($account); $em->persist($account); } if ($this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance')) { $this->tavCotisationUtils->calculateAllowanceAccordingToHousehold($adherent); } $em->persist($adherent->getUser()); $em->persist($adherent); $em->flush(); } /** * {@inheritdoc} */ protected function configureDatagridFilters(DatagridMapper $datagridMapper): void { $datagridMapper ->add('full_text', CallbackFilter::class, [ 'callback' => [$this, 'getFullTextFilter'], 'field_type' => TextType::class, 'label' => 'Recherche par Nom / Prenom / Courriel', 'show_filter' => true, 'advanced_filter' => false, ]) ->add('cotisationajour', CallbackFilter::class, [ 'callback' => [$this, 'getCotisationFilter'], 'field_type' => ChoiceType::class, 'field_options' => [ 'choices' => [ 'Oui' => true, 'Non' => false, ], ], 'multiple' => false, 'label' => 'Cotisation à jour ?', 'show_filter' => true, 'advanced_filter' => false, ]) ->add('groupe', null, [ 'label' => 'Groupe', 'show_filter' => true, 'advanced_filter' => false, ]) ->add('createdAt', 'doctrine_orm_datetime_range', [ 'field_type' => DateTimeRangePickerType::class, 'label' => 'Date de création', ]) ->add('updatedAt', 'doctrine_orm_datetime_range', [ 'field_type' => DateTimeRangePickerType::class, 'label' => 'Date de mise à jour', ]) ; if ($this->getConfigurationPool()->getContainer()->getParameter('tav_env')) { $datagridMapper ->remove('cotisationajour'); if($this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance')) { $datagridMapper->add('geoloc.subterritory', null, [ 'label' => 'Territoire', 'advanced_filter' => false, 'show_filter' => true, ]); } } } public function getCotisationFilter($queryBuilder, $alias, $field, $value) { if (null === $value['value']) { return false; } $container = $this->getConfigurationPool()->getContainer(); $em = $container->get('doctrine.orm.entity_manager'); $expr = $em->getExpressionBuilder(); $a = $em->getRepository(CotisationAdherent::class) ->createQueryBuilder('c') ->select('a.id') ->leftJoin('c.expediteur', 'a') ->leftJoin('c.cotisationInfos', 'i') ->where('i.fin > :now') ->getQuery() ->getDQL(); if (true === $value['value']) { $queryBuilder->andWhere( $expr->in( $alias . '.id', $a ) ); } else { $queryBuilder->andWhere( $expr->notIn( $alias . '.id', $a ) ); } $queryBuilder->setParameter('now', new \DateTime('now')); return true; } public function getFullTextFilter($queryBuilder, $alias, $field, $value) { if (!$value['value']) { return false; } // Use `andWhere` instead of `where` to prevent overriding existing `where` conditions $queryBuilder->andWhere($queryBuilder->expr()->orX( $queryBuilder->expr()->like('u.username', $queryBuilder->expr()->literal('%' . $value['value'] . '%')), $queryBuilder->expr()->like('u.email', $queryBuilder->expr()->literal('%' . $value['value'] . '%')), $queryBuilder->expr()->like('u.firstname', $queryBuilder->expr()->literal('%' . $value['value'] . '%')), $queryBuilder->expr()->like('u.lastname', $queryBuilder->expr()->literal('%' . $value['value'] . '%')) )); return true; } /** * @param EventDispatcherInterface $userManager */ public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): void { $this->eventDispatcher = $eventDispatcher; } /** * @return EventDispatcherInterface */ public function getEventDispatcher() { return $this->eventDispatcher; } /** * @param UserManagerInterface $userManager */ public function setUserManager(UserManagerInterface $userManager): void { $this->userManager = $userManager; } /** * @return UserManagerInterface */ public function getUserManager() { return $this->userManager; } protected function configureListFields(ListMapper $listMapper): void { unset($this->listModes['mosaic']); $listMapper ->addIdentifier('user.lastname', null, ['label' => 'Nom']) ->addIdentifier('user.firstname', null, ['label' => 'Prénom']) ->addIdentifier('user.email', null, ['label' => 'Email']) ; if ($this->security->isGranted('ROLE_TRESORIER')) { $actions = []; } else { $actions = ['edit' => []]; } if (!$this->getConfigurationPool()->getContainer()->getParameter('tav_env')) { $listMapper ->add( 'cotisation', null, [ 'label' => 'Cotisation à jour', 'template' => '@kohinos/bundles/SonataAdminBundle/CRUD/list_user_cotisation.html.twig', ] ); } else { $listMapper ->add( 'cotisation', null, [ 'label' => 'Dernière cotisation', 'template' => '@kohinos/tav/list_user_tav_cotisation.html.twig', ] ); if( $this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance') && ( $this->security->isGranted('ROLE_SUPER_ADMIN') || $this->security->isGranted('ROLE_ADMIN_SIEGE') || $this->security->isGranted('ROLE_TRESORIER') ) ) { $listMapper ->add( 'ceiling', null, [ 'label' => 'Solde & plafond', 'template' => '@kohinos/tav/list_user_ssa_ceiling.html.twig', ] ); $actions['withdrawDownToTheCeiling'] = [ 'template' => '@kohinos/tav/adherent_action_withdraw_down_to_the_ceiling.html.twig' ]; } } $listMapper ->addIdentifier('groupe', null, [ 'label' => 'Groupe', 'sortable' => true, 'sort_field_mapping' => ['fieldName' => 'name'], 'sort_parent_association_mappings' => [['fieldName' => 'groupe']], ]) ->add('user.enabled', null, [ 'label' => 'Activé', 'editable' => true, ]) ->add('user.createdAt', 'date', [ 'pattern' => 'dd/MM/YYYY HH:mm', 'label' => 'Crée le', ]) ->add('user.updatedAt', 'date', [ 'pattern' => 'dd/MM/YYYY HH:mm', 'label' => 'Mis à jour le', ]) ->add('_action', null, [ 'actions' => $actions, ]) ; } protected function configureRoutes(RouteCollection $collection) { parent::configureRoutes($collection); $collection ->remove('delete') ->add('withdrawDownToTheCeiling', $this->getRouterIdParameter() . '/withdrawDownToTheCeiling'); } public function getBatchActions() { $actions = parent::getBatchActions(); unset($actions['delete']); return $actions; } public function getExportFields() { return [ 'Id' => 'id', 'Nom' => 'user.lastname', 'Prénom' => 'user.firstname', 'username' => 'user.username', 'Email' => 'user.email', 'Groupe' => 'groupe.name', 'Téléphone' => 'user.phone', 'Mobile' => 'user.mobile', 'Crée le' => 'createdAt', 'Mise à jour le' => 'updatedAt', ]; } public function getDataSourceIterator() { $datagrid = $this->getDatagrid(); $datagrid->buildPager(); $fields = []; foreach ($this->getExportFields() as $key => $field) { // NEXT_MAJOR: Remove the following code in favor of the commented one. $label = $this->getTranslationLabel($field, 'export', 'label'); $transLabel = $this->getTranslator()->trans($label, [], $this->getTranslationDomain()); if ($transLabel === $label) { $fields[$key] = $field; } else { $fields[$transLabel] = $field; } } return $this->getModelDataSourceIterator($datagrid, $fields); } /** * @return DoctrineORMQuerySourceIterator */ public function getModelDataSourceIterator(DatagridInterface $datagrid, array $fields, $firstResult = null, $maxResult = null) { $datagrid->buildPager(); $query = $datagrid->getQuery(); $query->select('DISTINCT ' . current($query->getRootAliases())); $query->setFirstResult($firstResult); $query->setMaxResults($maxResult); if ($query instanceof ProxyQueryInterface) { $sortBy = $query->getSortBy(); if (!empty($sortBy)) { $query->addOrderBy($sortBy, $query->getSortOrder()); $query = $query->getQuery(); $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [OrderByToSelectWalker::class]); } else { $query = $query->getQuery(); } } $container = $this->getConfigurationPool()->getContainer(); $em = $container->get('doctrine')->getManager(); $cotisationUtils = $container->get('app.utils.cotisations'); $iterator = new CustomDoctrineORMQuerySourceIterator($cotisationUtils, $em, $container, $query, $fields); $iterator->setDateTimeFormat('d/m/Y H:i:s'); //change this to suit your needs return $iterator; } private function daysOfMonth() { $res = []; for($i = 1 ; $i < 29 ; $i++) { $res[$i] = $i; } return $res; } }