Loading module/Oscar/src/Oscar/Entity/ActivityPayment.php +18 −2 Original line number Diff line number Diff line Loading @@ -48,11 +48,22 @@ class ActivityPayment implements ITrackable self::getStatusPayments()[$this->getStatus()] : ''; } public function getDateRef() { if( $this->getStatus() == self::STATUS_PREVISIONNEL ){ return $this->getDatePredicted() ? $this->getDatePredicted()->format('Y-m-d') : 'No Date'; } if( $this->getStatus() == self::STATUS_REALISE ){ return $this->getDatePayment() ? $this->getDatePayment()->format('Y-m-d') : 'No Date'; } return '???'; } public function __toString() { return sprintf("Versement %s de %s%s", return sprintf("Versement %s de %s %s (%s)", self::getStatusPayments()[$this->getStatus()], $this->getAmount(), $this->getCurrency() $this->getAmount(), $this->getCurrency(), $this->getDateRef() ); } Loading Loading @@ -283,6 +294,11 @@ class ActivityPayment implements ITrackable return $this->getStatus() == self::STATUS_PREVISIONNEL && $this->getDatePredicted() < new \DateTime(); } public function isDone() :bool { return $this->getStatus() == self::STATUS_REALISE; } function __construct() { $this->datePayment = new \DateTime(); Loading module/Oscar/src/Oscar/Entity/Notification.php +45 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ class Notification const OBJECT_ACTIVITY = 'activity'; const OBJECT_APPLICATION = 'application'; const OBJECT_TIMESHEET = 'timesheet'; const OBJECT_PAYMENT = 'payment'; const OBJECT_MILESTONE = 'milestone'; // Valeurs par défaut const DEFAULT_LEVEL = self::LEVEL_INFO; Loading Loading @@ -285,12 +287,55 @@ class Notification return $this->context; } private ?string $_contextKey = null; private ?int $_contextId = null; private function getContextDatas() :array { if( $this->_contextKey === null ){ $contextData = explode(":", $this->getContext()); $this->_contextKey = $contextData[0]; if( count($contextData) > 1 ){ $this->_contextId = $contextData[1]; } else { $this->_contextId = null; } } return [ 'key' => $this->_contextKey, 'id' =>$this->_contextId ]; } public function getContextKey() :string { return $this->getContextDatas()['key']; } public function getContextId() :?int { return $this->getContextDatas()['id']; } public function isPayement() :bool { return $this->getContextKey() == self::OBJECT_PAYMENT; } public function getSubscribersIds() :array { return array_map(function($p) { return $p->getPerson()->getId(); }, $this->getPersons()->toArray()); } /** * @param mixed $context */ public function setContext($context) { $this->context = $context; $this->_contextKey = null; return $this; } Loading module/Oscar/src/Oscar/Entity/NotificationRepository.php +13 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,19 @@ class NotificationRepository extends EntityRepository return $qb->getQuery()->getResult(); } public function removeNotificationPersons( int $notificationId, array $personsIds ) :void { /** @var Notification $notification */ $notification = $this->getEntityManager()->find(Notification::class, $notificationId); /** @var NotificationPerson $notificationPerson */ foreach ($notification->getPersons() as $notificationPerson) { if( in_array($notificationPerson->getPerson()->getId(), $personsIds) ){ $this->getEntityManager()->remove($notificationPerson); } } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// NotificationPerson Loading module/Oscar/src/Oscar/Service/NotificationService.php +134 −34 Original line number Diff line number Diff line Loading @@ -243,16 +243,62 @@ class NotificationService implements UseServiceContainer * @return mixed * @throws OscarException */ public function getRolesIdsWithPersonsIds(Activity $activity) public function getRolesIdsWithPersonsIds(Activity $activity): array { $activityId = $activity->getId(); $personsService = $this->getPersonService(); /** @var Person[] $persons Liste des personnes impliquées dans une activité avec leurs rôles cherche en profondeur */ $persons = $personsService->getAllPersonsWithRolesInActivity($activity); $this->_object_roles_persons[$activityId] = $persons; if (!array_key_exists($activityId, $this->_object_roles_persons)) { $this->_object_roles_persons[$activityId] = $this->getPersonService()->getAllPersonsWithRolesInActivity( $activity ); } return $this->_object_roles_persons[$activityId]; } /** * Retourne la liste des IDS des rôles qui peuvent voir les payements. * * @return array * @throws OscarException */ public function getRolesIdConcernedByPayments(): array { $rolesAllowPayments = $this->getOscarUserContextService()->getRolesWithPrivileges( Privileges::ACTIVITY_PAYMENT_SHOW ); return array_map( function ($r) { return $r->getId(); }, $rolesAllowPayments ); } private array $_cacheActivityPayementPersons = []; /** * Retourne les identifiants des personnes concernées par les payments d'une activité. * * @param Activity $activity * @return array * @throws OscarException */ protected function getPersonsIdsConcernedByPaymentInActivity(Activity $activity): array { $activityId = $activity->getId(); if (!array_key_exists($activityId, $this->_cacheActivityPayementPersons)) { $output = []; $idsPersonsRoles = $this->getRolesIdsWithPersonsIds($activity); $idsRolesConcerned = $this->getRolesIdConcernedByPayments(); foreach ($idsPersonsRoles as $roleId => $personsIds) { if (in_array($roleId, $idsRolesConcerned)) { $output = array_merge($output, $idsPersonsRoles[$roleId]); } } $this->_cacheActivityPayementPersons[$activityId] = array_unique($output); } return $this->_cacheActivityPayementPersons[$activityId]; } /** * Déclenche l'envoi d'un document lors de l'upload. Loading Loading @@ -592,6 +638,7 @@ class NotificationService implements UseServiceContainer public function updateNotificationsActivity(Activity $activity) { $this->updateNotificationCore($activity); $this->updateNotificationCorePayment($activity); $this->updateNotificationsMilestonePersonActivity($activity); } Loading @@ -602,6 +649,65 @@ class NotificationService implements UseServiceContainer } } public function updateNotificationsPayment(Notification $notification): void { /** @var ActivityPayment $payment */ $payment = $this->getEntityManager()->getRepository(ActivityPayment::class)->find( $notification->getContextId() ); if (!$payment) { $this->getLoggerService()->alert("Impossible de mettre à jour les notifications pour le payment ($notification)"); return; } if( $payment->isDone() ){ return; } // TODO calculer si le payment est eligible aux notifications (date passée, fait, en retard, etc...) $haveToSub = $this->getPersonsIdsConcernedByPaymentInActivity($payment->getActivity()); $this->updateSubscribersToNotification($notification, $haveToSub); } /** * Mise à jour des inscriptions des personnes à la notification. * * @param Notification $notification * @param array $expectedSubscribersIds ID des personnes à inscrire */ protected function updateSubscribersToNotification(Notification $notification, array $expectedSubscribersIds): void { // identifiants des personnes déjà inscrites $alreadySub = $notification->getSubscribersIds(); // Personnes à retirer $subscribeToRemove = array_diff($alreadySub, $expectedSubscribersIds); // Personnes à ajouter $subscribeToDo = array_diff($expectedSubscribersIds, $alreadySub); try { foreach ($subscribeToDo as $idPerson) { $person = $this->getEntityManager()->getRepository(Person::class)->find($idPerson); if (!$person) { continue; } $subscribe = new NotificationPerson(); $this->getEntityManager()->persist($subscribe); $subscribe->setNotification($notification)->setPerson($person); } $this->getNotificationRepository()->removeNotificationPersons($notification->getId(), $subscribeToRemove); $this->getEntityManager()->flush(); } catch (\Exception $e) { $this->getLoggerService()->alert("Impossible de mettre à jour les notifications dans '$notification'"); } } /** * @param Activity $activity * @param $ignorePast Loading @@ -618,15 +724,10 @@ class NotificationService implements UseServiceContainer // Personnes devant être inscrites $idsPersonsRoles = $this->getRolesIdsWithPersonsIds($activity); $this->getLoggerService()->debug("Rôles concernées : "); foreach ($idsPersonsRoles as $roleId => $personsIds) { $this->getLoggerService()->debug(" - rôle '$roleId' : : " . count($personsIds) . " personne(s)"); } $now = (new \DateTime('now'))->modify('-1 month'); //$na = notification /** @var Notification $na */ foreach ($notificationsActivity as $na) { $contextNotification = explode(":", $na->getContext()); Loading @@ -634,13 +735,31 @@ class NotificationService implements UseServiceContainer continue; } //ActivityDate = Jalon donc Milestone (ancienne nomenclature, terminologie métier) $idActivityDate = $contextNotification[1]; $context = $na->getContextKey(); $contextId = $na->getContextId(); if ($context == 'payment') { try { $this->updateNotificationsPayment($na); } catch (\Exception $e) { $this->getLoggerService()->alert( "Un problème est survenu lors de la génération des notifications pour le payment $contextId" ); } continue; } if ($context != Notification::OBJECT_MILESTONE) { continue; } try { $activityDate = $this->getEntityManager()->getRepository(ActivityDate::class)->findOneBy( ["id" => $idActivityDate] ["id" => $contextId] ); } catch (\Exception $e) { die("idActivityDate : $idActivityDate --- " . $na->getContext()); $this->getLoggerService()->alert("Problème de génération de notification pour $na : " . $e->getMessage()); continue; } if (!$activityDate) { Loading @@ -652,9 +771,9 @@ class NotificationService implements UseServiceContainer // Si pas de rôles on passe directement, pas de calcul de notifications if (count($rolesActivityDate) == 0) { $this->getLoggerService()->debug(" > Skip (pas de rôles)"); continue; } // Si finishable et pas fait // TODO // if (!$activityDate->isLate()){ Loading Loading @@ -721,25 +840,6 @@ class NotificationService implements UseServiceContainer $this->getEntityManager()->flush(); } public function deleteNotificationActivityById(int $idActivity): void { try { $activity = $this->getPersonService()->getProjectGrantService()->getActivityById($idActivity); } catch (\Exception $exception) { throw new OscarException($exception->getMessage()); } $this->deleteNotificationActivity($activity); } public function deleteNotificationActivity(Activity $activity): void { $notifications = $this->getNotificationRepository()->getNotificationsActivity($activity->getId()); foreach ($notifications as $notification) { $this->getEntityManager()->remove($notification); } $this->getEntityManager()->flush(); } /** * Marque les notifications comme lues Loading Loading
module/Oscar/src/Oscar/Entity/ActivityPayment.php +18 −2 Original line number Diff line number Diff line Loading @@ -48,11 +48,22 @@ class ActivityPayment implements ITrackable self::getStatusPayments()[$this->getStatus()] : ''; } public function getDateRef() { if( $this->getStatus() == self::STATUS_PREVISIONNEL ){ return $this->getDatePredicted() ? $this->getDatePredicted()->format('Y-m-d') : 'No Date'; } if( $this->getStatus() == self::STATUS_REALISE ){ return $this->getDatePayment() ? $this->getDatePayment()->format('Y-m-d') : 'No Date'; } return '???'; } public function __toString() { return sprintf("Versement %s de %s%s", return sprintf("Versement %s de %s %s (%s)", self::getStatusPayments()[$this->getStatus()], $this->getAmount(), $this->getCurrency() $this->getAmount(), $this->getCurrency(), $this->getDateRef() ); } Loading Loading @@ -283,6 +294,11 @@ class ActivityPayment implements ITrackable return $this->getStatus() == self::STATUS_PREVISIONNEL && $this->getDatePredicted() < new \DateTime(); } public function isDone() :bool { return $this->getStatus() == self::STATUS_REALISE; } function __construct() { $this->datePayment = new \DateTime(); Loading
module/Oscar/src/Oscar/Entity/Notification.php +45 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ class Notification const OBJECT_ACTIVITY = 'activity'; const OBJECT_APPLICATION = 'application'; const OBJECT_TIMESHEET = 'timesheet'; const OBJECT_PAYMENT = 'payment'; const OBJECT_MILESTONE = 'milestone'; // Valeurs par défaut const DEFAULT_LEVEL = self::LEVEL_INFO; Loading Loading @@ -285,12 +287,55 @@ class Notification return $this->context; } private ?string $_contextKey = null; private ?int $_contextId = null; private function getContextDatas() :array { if( $this->_contextKey === null ){ $contextData = explode(":", $this->getContext()); $this->_contextKey = $contextData[0]; if( count($contextData) > 1 ){ $this->_contextId = $contextData[1]; } else { $this->_contextId = null; } } return [ 'key' => $this->_contextKey, 'id' =>$this->_contextId ]; } public function getContextKey() :string { return $this->getContextDatas()['key']; } public function getContextId() :?int { return $this->getContextDatas()['id']; } public function isPayement() :bool { return $this->getContextKey() == self::OBJECT_PAYMENT; } public function getSubscribersIds() :array { return array_map(function($p) { return $p->getPerson()->getId(); }, $this->getPersons()->toArray()); } /** * @param mixed $context */ public function setContext($context) { $this->context = $context; $this->_contextKey = null; return $this; } Loading
module/Oscar/src/Oscar/Entity/NotificationRepository.php +13 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,19 @@ class NotificationRepository extends EntityRepository return $qb->getQuery()->getResult(); } public function removeNotificationPersons( int $notificationId, array $personsIds ) :void { /** @var Notification $notification */ $notification = $this->getEntityManager()->find(Notification::class, $notificationId); /** @var NotificationPerson $notificationPerson */ foreach ($notification->getPersons() as $notificationPerson) { if( in_array($notificationPerson->getPerson()->getId(), $personsIds) ){ $this->getEntityManager()->remove($notificationPerson); } } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// NotificationPerson Loading
module/Oscar/src/Oscar/Service/NotificationService.php +134 −34 Original line number Diff line number Diff line Loading @@ -243,16 +243,62 @@ class NotificationService implements UseServiceContainer * @return mixed * @throws OscarException */ public function getRolesIdsWithPersonsIds(Activity $activity) public function getRolesIdsWithPersonsIds(Activity $activity): array { $activityId = $activity->getId(); $personsService = $this->getPersonService(); /** @var Person[] $persons Liste des personnes impliquées dans une activité avec leurs rôles cherche en profondeur */ $persons = $personsService->getAllPersonsWithRolesInActivity($activity); $this->_object_roles_persons[$activityId] = $persons; if (!array_key_exists($activityId, $this->_object_roles_persons)) { $this->_object_roles_persons[$activityId] = $this->getPersonService()->getAllPersonsWithRolesInActivity( $activity ); } return $this->_object_roles_persons[$activityId]; } /** * Retourne la liste des IDS des rôles qui peuvent voir les payements. * * @return array * @throws OscarException */ public function getRolesIdConcernedByPayments(): array { $rolesAllowPayments = $this->getOscarUserContextService()->getRolesWithPrivileges( Privileges::ACTIVITY_PAYMENT_SHOW ); return array_map( function ($r) { return $r->getId(); }, $rolesAllowPayments ); } private array $_cacheActivityPayementPersons = []; /** * Retourne les identifiants des personnes concernées par les payments d'une activité. * * @param Activity $activity * @return array * @throws OscarException */ protected function getPersonsIdsConcernedByPaymentInActivity(Activity $activity): array { $activityId = $activity->getId(); if (!array_key_exists($activityId, $this->_cacheActivityPayementPersons)) { $output = []; $idsPersonsRoles = $this->getRolesIdsWithPersonsIds($activity); $idsRolesConcerned = $this->getRolesIdConcernedByPayments(); foreach ($idsPersonsRoles as $roleId => $personsIds) { if (in_array($roleId, $idsRolesConcerned)) { $output = array_merge($output, $idsPersonsRoles[$roleId]); } } $this->_cacheActivityPayementPersons[$activityId] = array_unique($output); } return $this->_cacheActivityPayementPersons[$activityId]; } /** * Déclenche l'envoi d'un document lors de l'upload. Loading Loading @@ -592,6 +638,7 @@ class NotificationService implements UseServiceContainer public function updateNotificationsActivity(Activity $activity) { $this->updateNotificationCore($activity); $this->updateNotificationCorePayment($activity); $this->updateNotificationsMilestonePersonActivity($activity); } Loading @@ -602,6 +649,65 @@ class NotificationService implements UseServiceContainer } } public function updateNotificationsPayment(Notification $notification): void { /** @var ActivityPayment $payment */ $payment = $this->getEntityManager()->getRepository(ActivityPayment::class)->find( $notification->getContextId() ); if (!$payment) { $this->getLoggerService()->alert("Impossible de mettre à jour les notifications pour le payment ($notification)"); return; } if( $payment->isDone() ){ return; } // TODO calculer si le payment est eligible aux notifications (date passée, fait, en retard, etc...) $haveToSub = $this->getPersonsIdsConcernedByPaymentInActivity($payment->getActivity()); $this->updateSubscribersToNotification($notification, $haveToSub); } /** * Mise à jour des inscriptions des personnes à la notification. * * @param Notification $notification * @param array $expectedSubscribersIds ID des personnes à inscrire */ protected function updateSubscribersToNotification(Notification $notification, array $expectedSubscribersIds): void { // identifiants des personnes déjà inscrites $alreadySub = $notification->getSubscribersIds(); // Personnes à retirer $subscribeToRemove = array_diff($alreadySub, $expectedSubscribersIds); // Personnes à ajouter $subscribeToDo = array_diff($expectedSubscribersIds, $alreadySub); try { foreach ($subscribeToDo as $idPerson) { $person = $this->getEntityManager()->getRepository(Person::class)->find($idPerson); if (!$person) { continue; } $subscribe = new NotificationPerson(); $this->getEntityManager()->persist($subscribe); $subscribe->setNotification($notification)->setPerson($person); } $this->getNotificationRepository()->removeNotificationPersons($notification->getId(), $subscribeToRemove); $this->getEntityManager()->flush(); } catch (\Exception $e) { $this->getLoggerService()->alert("Impossible de mettre à jour les notifications dans '$notification'"); } } /** * @param Activity $activity * @param $ignorePast Loading @@ -618,15 +724,10 @@ class NotificationService implements UseServiceContainer // Personnes devant être inscrites $idsPersonsRoles = $this->getRolesIdsWithPersonsIds($activity); $this->getLoggerService()->debug("Rôles concernées : "); foreach ($idsPersonsRoles as $roleId => $personsIds) { $this->getLoggerService()->debug(" - rôle '$roleId' : : " . count($personsIds) . " personne(s)"); } $now = (new \DateTime('now'))->modify('-1 month'); //$na = notification /** @var Notification $na */ foreach ($notificationsActivity as $na) { $contextNotification = explode(":", $na->getContext()); Loading @@ -634,13 +735,31 @@ class NotificationService implements UseServiceContainer continue; } //ActivityDate = Jalon donc Milestone (ancienne nomenclature, terminologie métier) $idActivityDate = $contextNotification[1]; $context = $na->getContextKey(); $contextId = $na->getContextId(); if ($context == 'payment') { try { $this->updateNotificationsPayment($na); } catch (\Exception $e) { $this->getLoggerService()->alert( "Un problème est survenu lors de la génération des notifications pour le payment $contextId" ); } continue; } if ($context != Notification::OBJECT_MILESTONE) { continue; } try { $activityDate = $this->getEntityManager()->getRepository(ActivityDate::class)->findOneBy( ["id" => $idActivityDate] ["id" => $contextId] ); } catch (\Exception $e) { die("idActivityDate : $idActivityDate --- " . $na->getContext()); $this->getLoggerService()->alert("Problème de génération de notification pour $na : " . $e->getMessage()); continue; } if (!$activityDate) { Loading @@ -652,9 +771,9 @@ class NotificationService implements UseServiceContainer // Si pas de rôles on passe directement, pas de calcul de notifications if (count($rolesActivityDate) == 0) { $this->getLoggerService()->debug(" > Skip (pas de rôles)"); continue; } // Si finishable et pas fait // TODO // if (!$activityDate->isLate()){ Loading Loading @@ -721,25 +840,6 @@ class NotificationService implements UseServiceContainer $this->getEntityManager()->flush(); } public function deleteNotificationActivityById(int $idActivity): void { try { $activity = $this->getPersonService()->getProjectGrantService()->getActivityById($idActivity); } catch (\Exception $exception) { throw new OscarException($exception->getMessage()); } $this->deleteNotificationActivity($activity); } public function deleteNotificationActivity(Activity $activity): void { $notifications = $this->getNotificationRepository()->getNotificationsActivity($activity->getId()); foreach ($notifications as $notification) { $this->getEntityManager()->remove($notification); } $this->getEntityManager()->flush(); } /** * Marque les notifications comme lues Loading