Commit e6386e3e authored by Stephane Bouvry's avatar Stephane Bouvry
Browse files

Fix : La commande échoue pour les personnes sans créneau

parent 52955949
Loading
Loading
Loading
Loading
Loading
+32 −6
Changes for module/Oscar/src/Oscar/Command/OscarTimesheetDeclarerRecallCommand.php: 32 added lines, 6 removed lines.
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
namespace Oscar\Command;


use Doctrine\ORM\NoResultException;
use Moment\Moment;
use Oscar\Entity\Authentification;
use Oscar\Entity\LogActivity;
@@ -67,7 +68,7 @@ class OscarTimesheetDeclarerRecallCommand extends OscarCommandAbstract
        $all = $input->getArgument(self::ARG_ALL);

        /// OPTIONS and PARAMETERS
        $declarerId = $input->getOption(self::OPT_DECLARER);
        $declarerLogin = $input->getOption(self::OPT_DECLARER);
        $processDate = $input->getOption(self::OPT_PROCESSDATE);

        if( $processDate ){
@@ -76,14 +77,27 @@ class OscarTimesheetDeclarerRecallCommand extends OscarCommandAbstract
            $processDate = new \DateTime();
        }

        if( $all == false && $declarerId == null ){
        $io->writeln("Date effective de la relance : <bold>". $processDate->format('D d M Y') ."</bold>");
        //die();

        if( $all == false && $declarerLogin == null ){
            $io->writeln("Utiliser l'option --declarant=ID");
            $this->declarersList($input, $output);
            return;
        }

        if( $all != 'all' ){
            $personId = $this->getPersonService()->getPersonByLdapLogin($all)->getId();
            try {
                $person = $this->getPersonService()->getPersonByLdapLogin($declarerLogin);
                if( !$person ){
                    throw new \Exception("Nop !");
                }
                $personId = $person->getId();

            } catch (NoResultException $e){
                $io->error("Impossible de charger la personne à partir de son identifiant de connexion : $declarerLogin");
                return;
            }
            $declarerId = $personId;
            $all = false;
        }
@@ -96,6 +110,9 @@ class OscarTimesheetDeclarerRecallCommand extends OscarCommandAbstract
            $declarerPeriod = DateTimeUtils::getPeriodStrFromDateStr($final);
        }

        $io->writeln("Pour la période : <bold>". $declarerPeriod ."</bold>.");


        $do = false;

        if( $all == true ){
@@ -103,6 +120,7 @@ class OscarTimesheetDeclarerRecallCommand extends OscarCommandAbstract
            $out = [];
            /** @var Person $declarer */
            foreach ($declarants['persons'] as $personId=>$datas) {
                //$io->text("Traitement pour " . $declarants)
                try {
                    $result = $this->recallDeclarer($personId, $declarerPeriod, $io, $processDate);
                } catch (\Exception $e) {
@@ -110,8 +128,14 @@ class OscarTimesheetDeclarerRecallCommand extends OscarCommandAbstract
                }
            }
        } else {
            $result = $this->recallDeclarer($declarerId, $declarerPeriod, $io, $processDate);
            try {
                $result = $this->recallDeclarer($personId, $declarerPeriod, $io, $processDate);
            } catch (\Exception $e) {
                $io->warning($e->getMessage());
            }
        }
        $io->comment("Opération terminée");
        return;
    }

    protected function recallDeclarer($declarerId, $declarerPeriod, SymfonyStyle $io, $processDate = null)
@@ -129,12 +153,14 @@ class OscarTimesheetDeclarerRecallCommand extends OscarCommandAbstract
            $io->writeln(sprintf("Min : <options=bold>%s</>", $result['min']));
            $io->writeln(sprintf("Mail requis : <options=bold>%s</>", $result['mailRequired'] ? "Oui" : "Non"));
            $io->writeln(sprintf("Mail envoyé : <options=bold>%s</>", $result['mailSend'] ? "Oui" : "Non"));
            $io->writeln(sprintf("Dernier envoi : <options=bold>%s</>", $result['lastSend']));
            $io->writeln(sprintf("Prochain envoi : <options=bold>%s</>", $result['nextSend']));
            $io->writeln(sprintf("Status : <options=bold>%s</>", $result['status']));
        }
        else {

            $io->warning("Pas de données pour $declarer à la période $declarerPeriod");
        }

        return;
    }

    /**
+0 −257
Changes for module/Oscar/src/Oscar/Entity/PersonHistoryRecall.php: 0 added lines, 257 removed lines.
Original line number Diff line number Diff line
<?php
/**
 * @author Stéphane Bouvry<stephane.bouvry@unicaen.fr>
 * @date: 27/01/16 16:15
 * @copyright Certic (c) 2016
 */

namespace Oscar\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * Dates des Activités (Jalons)
 * @ORM\Entity(repositoryClass="SpentTypeGroupRepository")
 */
class SpentTypeGroup implements ITrackable
{
    use TraitTrackable;

    /**
     * @var string
     * @ORM\Column(type="string")
     */
    private $label;

    /**
     * @ORM\Column(type="string")
     */
    private $description;

    /**
     * @ORM\Column(type="string")
     */
    private $code;

    /**
     * @ORM\Column(type="string", options={"default" : ""}, nullable=true)
     */
    private $annexe;

    /**
     * @ORM\Column(type="integer")
     */
    private $rgt;

    /**
     * @ORM\Column(type="integer")
     */
    private $lft;

    /**
     * @ORM\Column(type="boolean", options={"default" : false})
     */
    private $blind = false;

    /**
     * @var
     * @ORM\ManyToOne(targetEntity="SpentTypeGroup")
     */
    private $parent;


    private $spentTypes;

    /**
     * @return bool
     */
    public function isLeaf(){
        return $this->getLft() + 1 == $this->getRgt();
    }

    /**
     * @return mixed
     */
    public function getAnnexe()
    {
        if( $this->getBlind() ){
            return "0";
        }
        return $this->annexe;
    }

    /**
     * @param mixed $annexe
     */
    public function setAnnexe($annexe)
    {
        $this->annexe = $annexe;
        return $this;
    }

    /**
     * @return mixed
     */
    public function getBlind()
    {
        return $this->blind;
    }

    /**
     * @param mixed $blind
     */
    public function setBlind($blind): void
    {
        $this->blind = $blind;
    }

    /**
     * SpentTypeGroup constructor.
     * @param string $label
     */
    public function __construct()
    {
        $this->spentTypes = new ArrayCollection();
    }

    /**
     * @return string
     */
    public function getLabel()
    {
        return $this->label;
    }

    /**
     * @param mixed $label
     */
    public function setLabel($label)
    {
        $this->label = $label;
        return $this;
    }

    /**
     * @return mixed
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * @param mixed $description
     */
    public function setDescription($description)
    {
        $this->description = $description;
        return $this;
    }

    /**
     * @return mixed
     */
    public function getCode()
    {
        return $this->code;
    }

    /**
     * @param mixed $code
     */
    public function setCode($code)
    {
        $this->code = $code;
        return $this;
    }

    /**
     * @return mixed
     */
    public function getParent()
    {
        return $this->parent;
    }

    /**
     * @param mixed $parent
     */
    public function setParent($parent)
    {
        $this->parent = $parent;
    }

    /**
     * @return mixed
     */
    public function getSpentTypes()
    {
        return $this->spentTypes;
    }

    /**
     * @param mixed $spentTypes
     */
    public function setSpentTypes($spentTypes)
    {
        $this->spentTypes = $spentTypes;
    }

    /**
     * @return mixed
     */
    public function getRgt()
    {
        return $this->rgt;
    }

    /**
     * @param mixed $rgt
     */
    public function setRgt($rgt)
    {
        $this->rgt = $rgt;
        return $this;
    }

    /**
     * @return mixed
     */
    public function getLft()
    {
        return $this->lft;
    }

    /**
     * @param integer $lft
     * @return $this
     */
    public function setLft($lft)
    {
        $this->lft = $lft;
        return $this;
    }


    public function toJson(){
        return [
            'id'    => $this->getId(),
            'label' => $this->getLabel(),
            'parent' => $this->getParent() ? $this->getParent()->getId() : null,
            'description' => $this->getDescription(),
            'annexe' => $this->getAnnexe(),
            'code' => $this->getCode(),
            'blind' => $this->getBlind(),
            'rgt' => $this->getRgt(),
            'lft' => $this->getLft()
        ];
    }

    public function __toString()
    {
        return sprintf("[%s] %s (%s, %s)", $this->getCode(), $this->getLabel(), $this->getLft(), $this->getRgt());
    }


}
 No newline at end of file
+0 −383
Changes for module/Oscar/src/Oscar/Entity/PersonHistoryRecallRepository.php: 0 added lines, 383 removed lines.
Original line number Diff line number Diff line
<?php
/**
 * @author Stéphane Bouvry<stephane.bouvry@unicaen.fr>
 * @copyright Certic (c) 2015
 */

namespace Oscar\Entity;


use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\Query\Expr\Join;
use mysql_xdevapi\Exception;
use Oscar\Connector\IConnectedRepository;
use Oscar\Exception\OscarException;
use Oscar\Import\Data\DataExtractorFullname;

/**
 * Class ProjectGrantRepository
 * @package Oscar\Entity
 */
class PersonRepository extends EntityRepository implements IConnectedRepository
{
    private $_cacheSelectebleRolesOrganisation;


    public function removePersonById(int $personId){
        $sql = "DELETE FROM person WHERE id = :id";
        $params = array('id'=>$personId);
        $stmt = $this->getEntityManager()->getConnection()->prepare($sql);
        return $stmt->execute($params);
    }


    public function removePerson(Person $person){
        $this->getEntityManager()->remove($person);
    }

    public function removePersons( array $ids ){
        return $this->createQueryBuilder('p')->delete()->where('p.id IN(:ids)')
            ->setParameter('ids', $ids)
            ->getQuery()
            ->execute();
    }

    public function getPersonsByIds( array $ids ){
        $qb = $this->getBaseQuery()
            ->where('p.id IN (:ids)')
            ->setParameter('ids', $ids);
        return $qb->getQuery()->execute();
    }


    /**
     * Retourne la liste des UIDS des personnes issues d'une synchronisation dans
     * Oscar pour le connector $connector.
     *
     * @param $connector
     * @return array
     */
    public function getUidsConnector($connector){
        $qb = $this->getEntityManager()->createQueryBuilder();
        $search = sprintf('s:%s:"%s";', strlen($connector), $connector);

        $qb->select('p.connectors')
            ->from(Person::class, 'p')
            ->where('p.connectors LIKE :search')
            ->setParameter('search', "%$search%");

        $uids = [];
        foreach ($qb->getQuery()->getArrayResult() as $a){
            $uids[] = $a['connectors'][$connector];
        }
        return $uids;
    }

    public function getPersonsIdsForActivitiesids( $activitiesIds ){
        $queryActivity = $this->createQueryBuilder('p')
            ->select('DISTINCT p.id')
            ->innerJoin('p.activities', 'ap')
            ->innerJoin('ap.activity', 'a')
            ->where('a.id IN (:ids)')
            ->setParameter('ids', $activitiesIds);

        $queryProject = $this->createQueryBuilder('p')
            ->select('DISTINCT p.id')
            ->innerJoin('p.projectAffectations', 'pra')
            ->innerJoin('pra.project', 'pr')
            ->innerJoin('pr.grants', 'a')
            ->where('a.id IN (:ids)')
            ->setParameter('ids', $activitiesIds);

        $fromActivities = array_map('current', $queryActivity->getQuery()->getResult());
        $fromProject = array_map('current', $queryProject->getQuery()->getResult());

        return array_unique(array_merge($fromActivities, $fromProject));
    }

    /**
     * Retourne la liste des IDS des personnes identifiées directement comme membre dans la/les organisations.
     *
     * @param $organisationIds
     * @return array
     */
    public function getPersonIdsInOrganizations( $organisationIds ){
        $result = $this->createQueryBuilder('p')
            ->select('p.id')
            ->innerJoin('p.organizations', 'op')
            ->where('op.organization IN(:organisationsIds)')
            ->setParameter('organisationsIds', $organisationIds)
            ->getQuery()
            ->getResult();
        return array_map('current', $result);
    }

    public function getPersonIdsForOrganizationsActivities( $organizationIds ){
        $activityMembers = $this->getEntityManager()->getRepository(Person::class)->createQueryBuilder('p')
            ->select('DISTINCT p.id')
            ->innerJoin('p.activities', 'ap')
            ->innerJoin('ap.activity', 'a')
            ->innerJoin('a.organizations', 'o')
            ->innerJoin('o.roleObj', 'r')
            ->where('r.principal = \'true\' AND o.organization IN(:organizationIds)')
            ->setParameter('organizationIds', $organizationIds)
            ->getQuery();

        $projectMembers = $this->getEntityManager()->getRepository(Person::class)->createQueryBuilder('p')
            ->select('DISTINCT p.id')
            ->innerJoin('p.projectAffectations', 'p_p')
            ->innerJoin('p_p.project', 'prj')
            ->innerJoin('prj.partners', 'partner')
            ->innerJoin('partner.organization', 'org')
            ->innerJoin('partner.roleObj', 'r')
            ->where('r.principal = \'true\' AND org.id IN(:organizationIds)')
            ->setParameter('organizationIds', $organizationIds)
            ->getQuery();

        $idsFromActivities = array_map('current', $activityMembers->getResult());
        $idsFromProjects = array_map('current', $projectMembers->getResult());

        return array_unique(array_merge($idsFromActivities, $idsFromProjects));
    }

    public function getSubordinates( $idReferent ){
        $ids = $this->getSubordinatesIds($idReferent);
        return $this->createQueryBuilder('p')
            ->where('p.id IN(:ids)')
            ->setParameter('ids', $ids)
            ->getQuery()
            ->getResult()
        ;

    }

    public function getSubordinatesIds( $referentId ){
        $result = $this->getEntityManager()->getRepository(Referent::class)->createQueryBuilder('r')
            ->select('p.id')
            ->innerJoin('r.person', 'p')
            ->where('r.referent = :idReferent')
            ->setParameter('idReferent', $referentId)
            ->getQuery()
            ->getResult();
        return array_map('current', $result);

    }

    /**
     * Retourne la liste des personnes qui ont des notifications non-lues et ayant un compte d'authentification.
     *
     * @return mixed
     */
    public function getPersonsWithUnreadNotificationsAndAuthentification(){
        $qb = $this->createQueryBuilder('p');
        $persons = $qb
            ->select('p')
            ->innerJoin(Authentification::class, 'a', Join::WITH, $qb->expr()->eq('p.ladapLogin', 'a.username'))
            ->innerJoin(NotificationPerson::class, 'n', Join::WITH, $qb->expr()->eq('p.id', 'n.person'))
            ->where('n.read IS NULL')
            ->getQuery()
            ->getResult();

        return $persons;
    }

    function getPersonByDisplayName($displayName)
    {
        /** @var Query $queryPerson */
        static $queryPerson;
        if ($queryPerson === null) {
            $queryPerson = $this->createQueryBuilder('p')
                ->where('CONCAT(p.firstname, \' \', p.lastname) = :displayName')
                ->getQuery();
        }
        return $queryPerson->setParameter('displayName', $displayName)->getResult();
    }

    /**
     * Recherche dans Oscar une personne à partir du nom complet.
     *
     * @param $displayName
     * @return null
     */
    public function getPersonByDisplayNameOrCreate($displayName)
    {
        $person = $this->getPersonByDisplayName($displayName);
        if (!$person) {
            $person = new Person();
            $this->getEntityManager()->persist($person);
            $fullname = (new DataExtractorFullname())->extract($displayName);
            if ($fullname) {
                $person->setFirstname($fullname['firstname'])
                    ->setLastname($fullname['lastname'])
                    ->setEmail($fullname['email']);

                $this->getEntityManager()->flush($person);
            } else {
                return null;
            }
        } else {
            return $person[0];
        }
    }

    /**
     * Retourne la liste des rôles dans les organisations pour la création de select.
     */
    public function getSelectableRolesOrganization()
    {
        if ($this->_cacheSelectebleRolesOrganisation === null) {
            $this->_cacheSelectebleRolesOrganisation = [];
            /** @var RoleOrganization $roleOrganization */
            foreach ($this->getRolesOrganization() as $roleOrganization) {
                $this->_cacheSelectebleRolesOrganisation[$roleOrganization->getId()] = $roleOrganization->getRoleId();
            }
        }
        return $this->_cacheSelectebleRolesOrganisation;
    }

    public function getRolesOrganizationArray()
    {
        return $this->getEntityManager()->getRepository(Role::class)->getRolesAtOrganizationArray();
    }


    /**
     * Retourne la liste des rôles dans les organisations.
     */
    public function getRolesOrganization()
    {
        return $this->getEntityManager()->getRepository(RoleOrganization::class)->findAll();
    }


    public function flush($mixed)
    {
        $this->getEntityManager()->flush($mixed);
    }

    /**
     * @return Person
     * @throws \Doctrine\ORM\ORMException
     */
    public function newPersistantPerson()
    {
        $person = new Person();
        $this->getEntityManager()->persist($person);
        return $person;
    }

    /**
     * @param $email
     * @return Person[]
     */
    public function getPersonByEmail($email)
    {
        static $personByEmail;
        if ($personByEmail == null) {
            $personByEmail = $this->getBaseQuery()->where('p.email = :email');
        }
        return $personByEmail->setParameter('email', $email)->getQuery()->getResult();
    }


    /**
     * @return \Doctrine\ORM\QueryBuilder
     */
    public function getBaseQuery()
    {
        $qb = $this->getEntityManager()->createQueryBuilder();

        $qb->select('pm, p, pj')
            ->from('Oscar\Entity\Person', 'p')
            ->leftJoin('p.projectAffectations', 'pm')
            ->leftJoin('pm.project', 'pj');

        return $qb;
    }

    public function search($search)
    {
        $query = $this->getBaseQuery();
        $query->orWhere('p.firstname LIKE :search')
            ->orWhere('p.lastname LIKE :search');
        return $query->getQuery()->execute(['search' => '%' . $search . '%']);
    }

    /**
     * Retourne la personne en fonction du connecteur.
     *
     * @param $connector
     * @param $value
     * @return mixed
     * @throws \Doctrine\ORM\NoResultException
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function getPersonByConnectorID($connector, $value)
    {
        return $this->getPersonByConnectorQuery($connector, $value)->getQuery()->getSingleResult();
    }

    /**
     * @param $connector
     * @param $value
     * @return mixed
     * @throws \Doctrine\ORM\NoResultException
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function getPersonsByConnectorID($connector, $value)
    {
        return $this->getPersonByConnectorQuery($connector, $value)->getQuery()->getResult();
    }

    /**
     * @param $connector
     * @param $value
     * @return \Doctrine\ORM\QueryBuilder
     */
    public function getPersonByConnectorQuery($connector, $value)
    {
        $qb = $this->getEntityManager()->createQueryBuilder();
        $search = sprintf('s:%s:"%s";s:%s:"%s";', strlen($connector), $connector, strlen($value), $value);
        $qb->select('p')
            ->from(Person::class, 'p')
            ->where('p.connectors LIKE :search')
            ->setParameter('search', "%$search%");
        
        return $qb;
    }


    public function getRolesLdapUsed()
    {
        $query = $this->getEntityManager()->createQuery(
            'SELECT DISTINCT r.ldapFilter, r.roleId, r.principal FROM ' . Role::class . ' r WHERE r.ldapFilter IS NOT NULL');
        $filtersUsed = [];
        foreach ($query->getResult() as $row) {
            $ldapFilter = preg_replace('/\(memberOf=(.*)\)/i', '$1', $row['ldapFilter']);
            $roleId = $row['roleId'];

            if (!array_key_exists($ldapFilter, $filtersUsed)) {
                $filtersUsed[$ldapFilter] = [];
            }
            $filtersUsed[$ldapFilter][] = [
                'label' => $roleId,
                'principal' => $row['principal']
            ];
        }
        return $filtersUsed;

    }

    public function getObjectByConnectorID($connectorName, $connectorID)
    {
        return $this->getPersonByConnectorID($connectorName, $connectorID);
    }

    public function newPersistantObject()
    {
        return $this->newPersistantPerson();
    }


}
 No newline at end of file
+18 −9
Changes for module/Oscar/src/Oscar/Service/TimesheetService.php: 18 added lines, 9 removed lines.
Original line number Diff line number Diff line
@@ -2596,7 +2596,6 @@ class TimesheetService implements UseOscarUserContextService, UseOscarConfigurat
        if (!in_array($period, $periods)) {
            $this->getLoggerService()->info("Pas de déclaration pour la période $period");
            throw new ConnectorException("$declarer n'est pas déclarant sur un projet pour la période $period");
            return;
        }

        $validations = $this->getValidationPeriodRepository()->getValidationPeriodForPersonAtPeriod($declarerId, $period);
@@ -2666,8 +2665,12 @@ class TimesheetService implements UseOscarUserContextService, UseOscarConfigurat
        }

        $declarer = $this->getPersonService()->getPersonById($declarerId);

        $result = $this->getPersonRecallDeclarationPeriod($declarerId, $period);

        $result['mailSend'] = false;
        $result['lastSend'] = "-";
        $result['nextSend'] = "-";

        /** @var RecallDeclarationRepository $recallDeclarationRepository */
        $recallDeclarationRepository = $this->getRecallDeclarationRepository();
@@ -2681,11 +2684,14 @@ class TimesheetService implements UseOscarUserContextService, UseOscarConfigurat
        $declarerSecondDay = $this->getOscarConfigurationService()->getDeclarersRelanceJour2();
        $declarerFirstMsg = $this->getOscarConfigurationService()->getDeclarersRelance1();
        $declarerSecondMsg = $this->getOscarConfigurationService()->getDeclarersRelance2();
        $message = $declarerFirstMsg;

        $recalls = $recallDeclarationRepository->getRecallDeclarationsPersonPeriod($declarerId, $periodYear, $periodMonth);
        $result['nbrRelance'] = count($recalls);

        // Premier rappel
        if( count($recalls) == 0 ){

            // On test si le jour d'envois est valide (Valeur de J1)
            $processDay = (int)$processDate->format('d');

@@ -2695,9 +2701,12 @@ class TimesheetService implements UseOscarUserContextService, UseOscarConfigurat
                $this->getEntityManager()->persist($recallSend);
                $recallSend->setStartProcess($processDate);
            } else {
                return null;
                $result['needSend'] = false;
                $result['message'] = "Le jour de relance n'est pas encore atteind";
            }
        }

        // Au-delà du premier rappel
        elseif (count($recalls) == 1 ){
            /** @var RecallDeclaration $recallSend */
            $recallSend = $recalls[0];
@@ -2705,22 +2714,22 @@ class TimesheetService implements UseOscarUserContextService, UseOscarConfigurat
            // Date d'envois
            $lastSend = $recallSend->getLastSend();
            $daySend = (int) $lastSend->format('d');
            echo "Dernier envoi jour : " . $daySend . "\n";

            $nextSendDay = $declarerSecondDay + $daySend;
            echo "Prochain envoi : $nextSendDay\n";
            $message = $declarerSecondMsg;

            $result['lastSend'] = "Jour $daySend";
            $result['nextSend'] = "Jour $nextSendDay";

            /** @var \DateInterval $interval */
            $interval = $lastSend->diff($processDate);
            $effectifDaysSinceLastSend = $interval->days;
            echo "JOURS écoulés depuis le dernier envois : " . $effectifDaysSinceLastSend . "\n";
            $result['daysSinceLastSend'] = "Jour $effectifDaysSinceLastSend";

            if( $effectifDaysSinceLastSend >= $declarerSecondDay ){
                echo "Envoi de la relance";
                $result['needSend'] = true;
            } else {
                return null;
                $result['needSend'] = false;
                $result['message'] = "En attente avant la prochaine relance";
            }
        } else {
            throw new OscarException("Doublon présent pour le système de contrôle des rappels pour $declarer");
@@ -2728,7 +2737,7 @@ class TimesheetService implements UseOscarUserContextService, UseOscarConfigurat


        if ($result['needSend']) {
            $body = $declarerSecondMsg;
            $body = $message;

            // Replace
            $find       = [ "{PERSON}",     "{PERIOD}"];