Commit 9a914658 authored by Stephane Bouvry's avatar Stephane Bouvry
Browse files

- Migration vers VUEJS de l'interface d'administration des Rôles des organisations

 - La liste des rôles affiche le nombre d'utilisation du rôle dans les activités/projets
 - Ajout d'une option "Migrer" qui permet fusionner le rôle vers un autre rôle
parent f873bbcd
Loading
Loading
Loading
Loading
Loading
+108 −44
Original line number Diff line number Diff line
@@ -772,7 +772,6 @@ class AdministrationController extends AbstractOscarController implements UsePro
            $this->getLoggerService()->critical($e->getMessage());
            throw new OscarException("Erreur lors de la construction de l'index de recherche");
        }

    }

    public function organizationTypeAction()
@@ -1294,7 +1293,6 @@ class AdministrationController extends AbstractOscarController implements UsePro
            else {
                // Création
                if ($this->getHttpXMethod() == "POST") {

                    $this->getOscarUserContextService()->hasPrivileges(Privileges::DROIT_ROLE_EDITION);
                    $role = $this->getEntityManager()->getRepository(Role::class)->findOneBy(
                        ['roleId' => $request->getPost('roleId')]
@@ -1328,6 +1326,9 @@ class AdministrationController extends AbstractOscarController implements UsePro
        }
    }

    /**
     * Liste des rôles
     */
    public function rolesAction()
    {
        $this->getOscarUserContextService()->check(Privileges::DROIT_ROLE_VISUALISATION);
@@ -1369,36 +1370,74 @@ class AdministrationController extends AbstractOscarController implements UsePro
    }


    /**
     * Gestion des rôles des organisations.
     * url : /administration/organizationrole
     */
    public function organizationRoleApiAction()
    {
        $this->getLoggerService()->debug("> ORGANISATIONROLE API");
        $this->getOscarUserContextService()->check(Privileges::DROIT_ROLEORGA_VISUALISATION);
        $roleId = $this->params('roleid', null);

        /** @var Request $request */
        $request = $this->getRequest();
        
        if ($roleId == null) {
            $this->getLoggerService()->debug("Pas de ROLEID");
            ////////////////////////////////////////////////////////////////////
            // GET : Liste des rôles
            if ($this->getHttpXMethod() == 'GET') {
                $roles = $this->getEntityManager()->getRepository(OrganizationRole::class)->findBy(
                    [],
                    ['label' => 'ASC']
                );
                $out = [];
                /** @var OrganizationRole $role */
                foreach ($roles as $role) {
                    $out[] = $role->toArray();
                }
                try {
                    $out = $this->getOrganizationService()->getOrganizationsRolesAndUsage();
                    return $this->ajaxResponse($out);
                } catch (\Exception $exception){
                    $msg = "Impossible de charger les roles des organisations";
                    $this->getLoggerService()->critical("$msg : " . $exception->getMessage());
                    return $this->jsonError($msg);
                }
            }
            ////////////////////////////////////////////////////////////////////
            // POST : Nouveau rôle
            // POST : Nouveau rôle / Migration
            elseif ($this->getHttpXMethod() == 'POST') {
                $this->getOscarUserContextService()->check(Privileges::DROIT_ROLEORGA_EDITION);

                try {
                    $datas = $this->getPutDataJson();
                } catch (\Exception $exception) {
                    $msg = "Les données transmises à l'API 'OrganizationRole' sont incohérentes";
                    $this->getLoggerService()->critical("$msg : " . $exception->getMessage());
                    return $this->jsonError($msg);
                }

                if ($datas->action) {
                    switch ($datas->action) {
                        ////////////////////////////////////////////////////////////////////
                        // Migration
                        case 'merge' :
                            $this->getLoggerService()->info("Migration d'un role d'organisation");
                            try {
                                $this->getLoggerService()->debug(json_encode($datas));

                                $fromId = $datas['from']['id'];
                                $destId = $datas['to']['id'];
                                $this->getOrganizationService()->mergeRoleOrganization($fromId, $destId);
                                return $this->getResponseOk();
                            } catch (\Exception $exception) {
                                $msg = "Impossible de migrer le role organisation";
                                $this->getLoggerService()->critical("$msg : " . $exception->getMessage());
                                return $this->getResponseInternalError($msg);
                            }

                            return $this->getResponseNotImplemented();

                        // Erreur
                        default:
                            $this->getLoggerService()->critical("Action $datas->action inconnue !");
                            return $this->getResponseNotImplemented();
                    }
                }
                else {
                    // Contrôle du Role
                $roleId = trim($request->getPost('label'));
                    $roleId = trim($datas->get('label'));
                    if ($roleId == "") {
                        return $this->getResponseBadRequest("Impossible d'enregistrer un rôle vide");
                    }
@@ -1412,14 +1451,16 @@ class AdministrationController extends AbstractOscarController implements UsePro

                    $role = new OrganizationRole();
                    $role->setLabel($roleId)
                    ->setDescription($request->getPost('description'))
                    ->setPrincipal($request->getPost('principal') == 'true');
                        ->setDescription($datas->get('description'))
                        ->setPrincipal($datas->get('principal') == 'true');
                    $this->getEntityManager()->persist($role);
                    $this->getEntityManager()->flush();
                    return $this->ajaxResponse($role->toArray());
                }
            }
        }
        else {
            $method = $this->getHttpXMethod();
            $this->getOscarUserContextService()->check(Privileges::DROIT_ROLEORGA_EDITION);
            $role = $this->getEntityManager()->getRepository(OrganizationRole::class)->find($roleId);
            if (!$role) {
@@ -1428,12 +1469,13 @@ class AdministrationController extends AbstractOscarController implements UsePro

            if ($this->getHttpXMethod() == 'PUT') {
                try {
                    $datas = $this->getPutDataJson();

                    // Données du rôle à modifier
                    $id = (int)$request->getPost('id');
                    $label = trim($request->getPost('label'));
                    $description = trim($request->getPost('description'));
                    $principal = $request->getPost('principal') == "true";
                    $this->getLoggerService()->info("Modification du rôle d'organization $label");
                    $id = (int)$datas->get('id');
                    $label = trim($datas->get('label'));
                    $description = trim($datas->get('description'));
                    $principal = $datas->get('principal') == "true";

                    if ($label == "") {
                        throw new OscarException("Vous devez renseigner un intitulé");
@@ -1451,7 +1493,6 @@ class AdministrationController extends AbstractOscarController implements UsePro
                        ->setPrincipal($principal);

                    $this->getEntityManager()->flush();

                    return $this->ajaxResponse($roleObjEdited->toArray());
                } catch (\Exception $e) {
                    return $this->getResponseInternalError("Impossible de modifier le rôle : " . $e->getMessage());
@@ -1459,11 +1500,20 @@ class AdministrationController extends AbstractOscarController implements UsePro
            }

            ////////////////////////////////////////////////////////////////////
            // POST : Nouveau rôle
            // Suppression d'un rôle
            elseif ($this->getHttpXMethod() == 'DELETE') {
                try {
                    $this->getEntityManager()->remove($role);
                    $this->getEntityManager()->flush();
                    return $this->getResponseOk('le rôle a été supprimé.');
                } catch (ForeignKeyConstraintViolationException $exception) {
                    $this->getLoggerService()->warning($exception->getMessage());
                    return $this->jsonError("Ce role est utilisé, impossible de le supprimer");
                } catch (\Exception $exception) {
                    $msg = "Suppression du role impossible";
                    $this->getLoggerService()->error("$msg : " . $exception->getMessage());
                    return $this->getResponseInternalError($msg);
                }
            }
        }
        return $this->getResponseBadRequest("Accès à l'API improbable...");
@@ -1512,7 +1562,9 @@ class AdministrationController extends AbstractOscarController implements UsePro
                        'untyped_documents'   => $documentRepository->countUntypedDocuments(),
                        'documents_location'  => $this->getOscarConfigurationService()->getDocumentDropLocation(),
                        'documents_pcru_type' => $this->getOscarConfigurationService()->getPcruContractType(),
                        'signatureflows'      => $this->getSignatureService()->getSignatureFlows(SignatureConstants::FORMAT_ARRAY),
                        'signatureflows'      => $this->getSignatureService()->getSignatureFlows(
                            SignatureConstants::FORMAT_ARRAY
                        ),
                        'types'               => []
                    ];

@@ -1546,7 +1598,13 @@ class AdministrationController extends AbstractOscarController implements UsePro
                    $default = $this->params()->fromPost('default', '');
                    $this->getLoggerService()->info("Enregistrement de type de document '$signatureflow_id'");

                    $documentRepository->createOrUpdateTypeDocument($id, $label, $description, $default == 'on', $signatureflow_id);
                    $documentRepository->createOrUpdateTypeDocument(
                        $id,
                        $label,
                        $description,
                        $default == 'on',
                        $signatureflow_id
                    );
                } catch (\Exception $e) {
                    return $this->jsonErrorLogged("Impossible d'ajouter le type de document", $e);
                }
@@ -1568,7 +1626,13 @@ class AdministrationController extends AbstractOscarController implements UsePro
                    $signatureflow_id = intval($_PUT->get('signatureflow_id'));
                    $this->getLoggerService()->info(" > $id, $label, $description, $signatureflow_id");

                    $documentRepository->createOrUpdateTypeDocument($id, $label, $description, $default == 'on', $signatureflow_id);
                    $documentRepository->createOrUpdateTypeDocument(
                        $id,
                        $label,
                        $description,
                        $default == 'on',
                        $signatureflow_id
                    );
                    return $this->getResponseOk();
                } catch (\Exception $e) {
                    return $this->jsonErrorLogged(_("Impossible de mettre à jour le type de document"), $e);
+17 −0
Original line number Diff line number Diff line
@@ -352,4 +352,21 @@ class OrganizationRepository extends EntityRepository implements IConnectedRepos
        $this->getEntityManager()->remove($this->find($organizationId));
        $this->getEntityManager()->flush();
    }

    /**
     * Retourne les IDs des organisations qui ont le role $from dans une activité ou un projet.
     * @param OrganizationRole $from
     * @return array
     */
    public function getOrganizationsIdWithRole(OrganizationRole $from) :array
    {
        $qb = $this->createQueryBuilder('o')
            ->select('o.id')
            ->leftJoin('o.projects', 'p')
            ->leftJoin('o.activities', 'a')
            ->where('p.roleObj = :role OR a.roleObj = :role');
        $qb->setParameter('role', $from);
        $result = $qb->getQuery()->getResult();
        return array_column($result, 'id');
    }
}
 No newline at end of file
+36 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
namespace Oscar\Entity;


use Doctrine\DBAL\Exception;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NoResultException;
use Oscar\Formatter\OscarFormatterConst;
@@ -16,6 +17,24 @@ class OrganizationRoleRepository extends EntityRepository
{
    const FORMAT_ID_ROLE_ID = 'FORMAT_ID_ROLE_ID';

    /**
     * Retourne la liste des roles des organisations dans les activités/projets avec leur usage.
     *
     * @return array
     * @throws Exception
     */
    public function getRolesAndUsage() :array
    {
        $sql = 'select r.id, count(a.id) as "in_activity", r.principal, count(p.id) as "in_project",  r.label 
                from organizationrole r
                left join activityorganization a on a.roleobj_id = r.id
                left join projectpartner p on p.roleobj_id = r.id
                group by r.id order by r.label';

        $stm = $this->getEntityManager()->getConnection()->prepare($sql);
        return $stm->executeQuery()->fetchAllAssociative();
    }

    public function getRoleByRoleIdOrCreate($roleId)
    {
        try {
@@ -56,4 +75,21 @@ class OrganizationRoleRepository extends EntityRepository
            ->getQuery()
            ->getResult();
    }

    /**
     * @param OrganizationRole $from
     * @param OrganizationRole $to
     * @return void
     * @throws Exception
     */
    public function merge(OrganizationRole $from, OrganizationRole $to) :void
    {
        $sql = 'UPDATE activityorganization SET roleobj_id = :to WHERE roleobj_id = :from';
        $stm = $this->getEntityManager()->getConnection()->prepare($sql);
        $stm->executeQuery(['to' => $to->getId(), 'from' => $from->getId()]);

        $sql = 'UPDATE projectpartner SET roleobj_id = :to WHERE roleobj_id = :from';
        $stm = $this->getEntityManager()->getConnection()->prepare($sql);
        $stm->executeQuery(['to' => $to->getId(), 'from' => $from->getId()]);
    }
}
 No newline at end of file
+2 −2
Original line number Diff line number Diff line
@@ -78,9 +78,9 @@ class GearmanJobLauncherService implements UseOscarConfigurationService, UseLogg
    {
        /** @var ActivityOrganization $activityOrganization */
        foreach ($organization->getActivities() as $activityOrganization) {
            if( $activityOrganization->isPrincipal() ){
            //if( $activityOrganization->isPrincipal() ){
                $this->triggerUpdateNotificationActivity($activityOrganization->getActivity());
            }
            //}
        }

        /** @var ProjectPartner $projectPartner */
+90 −15
Original line number Diff line number Diff line
@@ -312,7 +312,8 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana

        if ($rolePrincipaux == true) {
            $roles = $this->getOscarUserContext()->getRoleIdPrimary();
        } else {
        }
        else {
            $roles = $this->getOscarUserContext()->getRoleId();
        }

@@ -420,7 +421,8 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana
     * @param int $idOrganization
     * @return Organization[]
     */
    public function getOrganizationAndParents( int $idOrganization ) :array {
    public function getOrganizationAndParents(int $idOrganization): array
    {
        return $this->getOrganizationRepository()->getOrganizationAndParents($idOrganization);
    }

@@ -479,7 +481,8 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana
        if (!in_array($subStructure->getId(), $parentChildren) && $masterOrganizationId != $subOrganizationId) {
            $subStructure->setParent($parent);
            $this->getEntityManager()->flush($subStructure);
        } else {
        }
        else {
            throw new OscarException("L'affectation va provoquer une récurrence, opération annulée");
        }
    }
@@ -630,7 +633,8 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana
                $connectorValue = $matches[2] . '%';
                $where = 'o.connectors LIKE \'%"' . $connectorName . '";s:%:"' . $connectorValue . '"%\'';
                $qb->orWhere($where);
            } else {
            }
            else {
                $qb
                    ->orWhere('LOWER(o.shortName) LIKE :search')
                    ->orWhere('LOWER(o.fullName) LIKE :search')
@@ -680,7 +684,8 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana
        if (isset($filter['active']) && $filter['active']) {
            if ($filter['active'] == 'ON') {
                $qb->andWhere('o.dateEnd IS NULL OR o.dateEnd > :now')->setParameter('now', new \DateTime());
            } else {
            }
            else {
                if ($filter['active'] == 'OFF') {
                    $qb->andWhere('o.dateEnd < :now')->setParameter('now', new \DateTime());
                }
@@ -732,7 +737,8 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana
            }

            $qb->where('o.id IN(:ids)')->setParameter('ids', $ids);
        } else {
        }
        else {
            $qb->addOrderBy('o.dateEnd', 'DESC')->addOrderBy('o.dateUpdated', 'DESC');
        }

@@ -766,7 +772,8 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana
        if (isset($filter['active']) && $filter['active']) {
            if ($filter['active'] == 'ON') {
                $qb->andWhere('o.dateEnd IS NULL OR o.dateEnd > :now')->setParameter('now', new \DateTime());
            } else {
            }
            else {
                if ($filter['active'] == 'OFF') {
                    $qb->andWhere('o.dateEnd < :now')->setParameter('now', new \DateTime());
                }
@@ -835,7 +842,8 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana
            }

            return $this->getOrganizationsByIds($ids);
        } else {
        }
        else {
            return $this->getSearchNativeQuery($search, [])->getQuery()->getResult();
        }
    }
@@ -889,6 +897,71 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana
    const STRUCTURES_BASE_DN = 'ou=structures,dc=unicaen,dc=fr';
    const STAFF_ACTIVE_OR_DISABLED = 'ou=people,dc=unicaen,dc=fr';

    /**
     * @return array
     * @throws OscarException
     */
    public function getOrganizationsRolesAndUsage(): array
    {
        try {
            return $this->getOrganizationRoleRepository()->getRolesAndUsage();
        } catch (\Exception $e) {
            $msg = "Impossible de charger les roles des organisations";
            $this->getLoggerService()->error("$msg : " . $e->getMessage());
            throw new OscarException($msg);
        }
    }

    /**
     * Procédure de "fusion" des roles des organisations. Cela fait :
     * - Mise à jour des affectations des organisations dans les activités/projets
     * - Mise à jour des notifications si besoin.
     *
     * @param $fromId
     * @param $destId
     * @return void
     * @throws OscarException
     */
    public function mergeRoleOrganization($fromId, $destId): void
    {
        /** @var OrganizationRole $from */
        $from = $this->getOrganizationRoleRepository()->find($fromId);

        /** @var OrganizationRole $to */
        $to = $this->getOrganizationRoleRepository()->find($destId);

        $notificationsUpdate = false;

        // --------------------------------------------------------------
        // Note métier :
        // En migrant d'un role principal vers un role non-principale, et inversement,
        // il faut mettre à jour les notifications des structures
        // Donc on commence par vérifier si le role source et destination sont différents (pricipal ou pas)

        if ($from->isPrincipal() != $to->isPrincipal()) {
            $organizationsIds = $this->getOrganizationRepository()->getOrganizationsIdWithRole($from);
            $this->getLoggerService()->debug(sprintf("%s organisation(s) à recalculer", count($organizationsIds)));
            $notificationsUpdate = count($organizationsIds) > 0;
        }

        try {
            $this->getOrganizationRoleRepository()->merge($from, $to);
        } catch (\Exception $exception) {
            throw new OscarException($exception->getMessage());
        }

        if ($notificationsUpdate) {
            try {
                $organizations = $this->getOrganizationsByIds($organizationsIds);
                foreach ($organizations as $organization) {
                    $this->getPersonService()->getGearmanJobLauncherService()->triggerUpdateNotificationOrganization($organization);
                }
            } catch (\Exception $exception) {
                $this->getLoggerService()->critical($exception->getMessage());
            }
        }
    }

    private function areSameOrganization(Organization $organizationA, Organization $organizationB)
    {
        if ($organizationA->getCentaureId() == $organizationB->getCentaureId()) {
@@ -910,7 +983,8 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana
        if ($id) {
            /** @var OrganizationType $urganizationType */
            $type = $this->getEntityManager()->getRepository(OrganizationType::class)->findOneBy(['id' => $id]);
        } else {
        }
        else {
            $type = new OrganizationType();
            $this->getEntityManager()->persist($type);
        }
@@ -1025,7 +1099,8 @@ class OrganizationService implements UseOscarConfigurationService, UseEntityMana
                    ->setEn($data['en'])
                    ->setFr($data['fr'])
                    ->setNumeric(intval($data['numeric']));
            } else {
            }
            else {
                $country = $exists[$data['alpha2']];
                $country->setAlpha2($data['alpha2'])
                    ->setAlpha3($data['alpha3'])
Loading