diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/ListActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/ListActivity.php new file mode 100644 index 000000000..77444e414 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/ListActivity.php @@ -0,0 +1,165 @@ +helper = $helper; + $this->entityManager = $entityManager; + $this->translatableStringExportLabelHelper = $translatableStringExportLabelHelper; + } + + public function buildForm(FormBuilderInterface $builder) + { + $this->helper->buildForm($builder); + } + + public function getAllowedFormattersTypes() + { + return $this->helper->getAllowedFormattersTypes(); + } + + public function getDescription() + { + return ListActivityHelper::MSG_KEY . 'List activities linked to an accompanying course'; + } + + public function getGroup(): string + { + return 'Exports of activities linked to an accompanying period'; + } + + public function getLabels($key, array $values, $data) + { + switch ($key) { + case 'acpId': + return static function ($value) { + if ('_header' === $value) { + return ListActivityHelper::MSG_KEY . 'accompanying course id'; + } + + return $value ?? ''; + }; + + case 'scopesNames': + return $this->translatableStringExportLabelHelper->getLabelMulti($key, $values, ListActivityHelper::MSG_KEY . 'course circles'); + + default: + return $this->helper->getLabels($key, $values, $data); + } + } + + public function getQueryKeys($data) + { + return + array_merge( + $this->helper->getQueryKeys($data), + [ + 'acpId', + 'scopesNames', + ] + ); + } + + public function getResult($query, $data) + { + return $this->helper->getResult($query, $data); + } + + public function getTitle() + { + return ListActivityHelper::MSG_KEY . 'List activity linked to a course'; + } + + public function getType() + { + return $this->helper->getType(); + } + + public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) + { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + + $qb = $this->entityManager->createQueryBuilder(); + + $qb + ->distinct() + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'acp') + ->leftJoin('acp.participations', 'acppart') + ->leftJoin('acppart.person', 'person') + ->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL') + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 + FROM ' . PersonCenterHistory::class . ' acl_count_person_history + WHERE acl_count_person_history.person = person + AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + // some grouping are necessary + ->addGroupBy('acp.id') + ->addOrderBy('activity.date') + ->addOrderBy('activity.id') + ->setParameter('authorized_centers', $centers); + + $this->helper->addSelect($qb); + + // add select for this step + $qb + ->addSelect('acp.id AS acpId') + ->addSelect('(SELECT AGGREGATE(acpScope.name) FROM ' . Scope::class . ' acpScope WHERE acpScope MEMBER OF acp.scopes) AS scopesNames') + ->addGroupBy('scopesNames'); + + return $qb; + } + + public function requiredRole(): string + { + return ActivityStatsVoter::LISTS; + } + + public function supportsModifiers() + { + return array_merge( + $this->helper->supportsModifiers(), + [ + \Chill\PersonBundle\Export\Declarations::ACP_TYPE, + ] + ); + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Export/ListActivityHelper.php b/src/Bundle/ChillActivityBundle/Export/Export/ListActivityHelper.php new file mode 100644 index 000000000..0e8b28ab4 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Export/ListActivityHelper.php @@ -0,0 +1,269 @@ +activityPresenceRepository = $activityPresenceRepository; + $this->activityTypeRepository = $activityTypeRepository; + $this->dateTimeHelper = $dateTimeHelper; + $this->labelPersonHelper = $labelPersonHelper; + $this->labelThirdPartyHelper = $labelThirdPartyHelper; + $this->translator = $translator; + $this->translatableStringHelper = $translatableStringHelper; + $this->translatableStringLabelHelper = $translatableStringLabelHelper; + $this->userHelper = $userHelper; + } + + public function addSelect(QueryBuilder $qb): void + { + $qb + ->addSelect('activity.id AS id') + ->addSelect('activity.date') + ->addSelect('IDENTITY(activity.activityType) AS typeName') + ->leftJoin('activity.reasons', 'reasons') + ->addSelect('AGGREGATE(reasons.name) AS listReasons') + ->leftJoin('activity.persons', 'actPerson') + ->addSelect('AGGREGATE(actPerson.id) AS personsIds') + ->addSelect('AGGREGATE(actPerson.id) AS personsNames') + ->leftJoin('activity.users', 'users_u') + ->addSelect('AGGREGATE(users_u.id) AS usersIds') + ->addSelect('AGGREGATE(users_u.id) AS usersNames') + ->leftJoin('activity.thirdParties', 'thirdparty') + ->addSelect('AGGREGATE(thirdparty.id) AS thirdPartiesIds') + ->addSelect('AGGREGATE(thirdparty.id) AS thirdPartiesNames') + ->addSelect('IDENTITY(activity.attendee) AS attendeeName') + ->addSelect('activity.durationTime') + ->addSelect('activity.travelTime') + ->addSelect('activity.emergency') + ->leftJoin('activity.location', 'location') + ->addSelect('location.name AS locationName') + ->addSelect('activity.sentReceived') + ->addSelect('IDENTITY(activity.createdBy) AS createdBy') + ->addSelect('activity.createdAt') + ->addSelect('IDENTITY(activity.updatedBy) AS updatedBy') + ->addSelect('activity.updatedAt') + ->addGroupBy('activity.id') + ->addGroupBy('location.id'); + } + + public function buildForm(FormBuilderInterface $builder) + { + } + + public function getAllowedFormattersTypes() + { + return [FormatterInterface::TYPE_LIST]; + } + + public function getLabels($key, array $values, $data) + { + switch ($key) { + case 'createdAt': + case 'updatedAt': + return $this->dateTimeHelper->getLabel($key); + + case 'createdBy': + case 'updatedBy': + return $this->userHelper->getLabel($key, $values, $key); + + case 'date': + return $this->dateTimeHelper->getLabel(self::MSG_KEY . $key); + + case 'attendeeName': + return function ($value) { + if ('_header' === $value) { + return 'Attendee'; + } + + if (null === $value || null === $presence = $this->activityPresenceRepository->find($value)) { + return ''; + } + + return $this->translatableStringHelper->localize($presence->getName()); + }; + + case 'listReasons': + return $this->translatableStringLabelHelper->getLabelMulti($key, $values, 'Activity Reasons'); + + case 'typeName': + return function ($value) { + if ('_header' === $value) { + return 'Activity type'; + } + + if (null === $value || null === $type = $this->activityTypeRepository->find($value)) { + return ''; + } + + return $this->translatableStringHelper->localize($type->getName()); + }; + + case 'usersNames': + return $this->userHelper->getLabelMulti($key, $values, self::MSG_KEY . 'users name'); + + case 'usersIds': + case 'thirdPartiesIds': + case 'personsIds': + return static function ($value) use ($key) { + if ('_header' === $value) { + switch ($key) { + case 'usersIds': + return self::MSG_KEY . 'users ids'; + + case 'thirdPartiesIds': + return self::MSG_KEY . 'third parties ids'; + + case 'personsIds': + return self::MSG_KEY . 'persons ids'; + + default: + throw new LogicException('key not supported'); + } + } + + $decoded = json_decode($value); + + return implode( + '|', + array_unique( + array_filter($decoded, static fn (?int $id) => null !== $id), + SORT_NUMERIC + ) + ); + }; + + case 'personsNames': + return $this->labelPersonHelper->getLabelMulti($key, $values, self::MSG_KEY . 'persons name'); + + case 'thirdPartiesNames': + return $this->labelThirdPartyHelper->getLabelMulti($key, $values, self::MSG_KEY . 'thirds parties'); + + case 'sentReceived': + return function ($value) { + if ('_header' === $value) { + return self::MSG_KEY . 'sent received'; + } + + if (null === $value) { + return ''; + } + + return $this->translator->trans($value); + }; + + default: + return function ($value) use ($key) { + if ('_header' === $value) { + return self::MSG_KEY . $key; + } + + if (null === $value) { + return ''; + } + + return $this->translator->trans($value); + }; + } + } + + public function getQueryKeys($data) + { + return [ + 'id', + 'date', + 'typeName', + 'listReasons', + 'attendeeName', + 'durationTime', + 'travelTime', + 'emergency', + 'locationName', + 'sentReceived', + 'personsIds', + 'personsNames', + 'usersIds', + 'usersNames', + 'thirdPartiesIds', + 'thirdPartiesNames', + 'createdBy', + 'createdAt', + 'updatedBy', + 'updatedAt', + ]; + } + + public function getResult($query, $data) + { + return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR); + } + + public function getType(): string + { + return Declarations::ACTIVITY; + } + + public function supportsModifiers() + { + return [ + Declarations::ACTIVITY, + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Repository/ActivityPresenceRepository.php b/src/Bundle/ChillActivityBundle/Repository/ActivityPresenceRepository.php new file mode 100644 index 000000000..2cf9f9470 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Repository/ActivityPresenceRepository.php @@ -0,0 +1,51 @@ +repository = $entityManager->getRepository($this->getClassName()); + } + + public function find($id): ?ActivityPresence + { + return $this->repository->find($id); + } + + public function findAll(): array + { + return $this->repository->findAll(); + } + + public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array + { + return $this->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria): ?ActivityPresence + { + return $this->findOneBy($criteria); + } + + public function getClassName(): string + { + return ActivityPresence::class; + } +} diff --git a/src/Bundle/ChillActivityBundle/Repository/ActivityPresenceRepositoryInterface.php b/src/Bundle/ChillActivityBundle/Repository/ActivityPresenceRepositoryInterface.php new file mode 100644 index 000000000..228d70856 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Repository/ActivityPresenceRepositoryInterface.php @@ -0,0 +1,33 @@ +translatableStringHelper = $translatableStringHelper; + } + + public function getLabel(string $key, array $values, string $header) + { + return function ($value) use ($header) { + if ('_header' === $value) { + return $header; + } + + if (null === $value) { + return ''; + } + + return $this->translatableStringHelper->localize(json_decode($value, true)); + }; + } + + public function getLabelMulti(string $key, array $values, string $header) + { + return function ($value) use ($header) { + if ('_header' === $value) { + return $header; + } + + if (null === $value) { + return ''; + } + + $decoded = json_decode($value, true); + + return implode( + '|', + array_unique( + array_map( + fn (array $translatableString) => $this->translatableStringHelper->localize($translatableString), + array_filter($decoded, static fn ($elem) => null !== $elem) + ) + ) + ); + }; + } +} diff --git a/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php index 98c4c5579..d8eb7e9cc 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php @@ -13,6 +13,8 @@ namespace Chill\MainBundle\Export\Helper; use Chill\MainBundle\Repository\UserRepositoryInterface; use Chill\MainBundle\Templating\Entity\UserRender; +use function count; +use const SORT_NUMERIC; class UserHelper { @@ -40,4 +42,43 @@ class UserHelper return $this->userRender->renderString($user, []); }; } + + public function getLabelMulti($key, array $values, string $header): callable + { + return function ($value) { + if ('_header' === $value) { + return 'users name'; + } + + if (null === $value) { + return ''; + } + + $decoded = json_decode($value); + + if (0 === count($decoded)) { + return ''; + } + + return + implode( + '|', + array_map( + function (int $userId) { + $user = $this->userRepository->find($userId); + + if (null === $user) { + return ''; + } + + return $this->userRender->renderString($user, []); + }, + array_unique( + array_filter($decoded, static fn (?int $userId) => null !== $userId), + SORT_NUMERIC + ) + ) + ); + }; + } } diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index fbd8da3a7..f41b8d36d 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -56,6 +56,12 @@ Until %date%: Jusqu'au %date% until %date%: jusqu'au %date% Since: Depuis le Until: Jusqu'au + +updatedAt: Mise à jour le +updatedBy: Mise à jour par +createdAt: Créé le +createdBy: Créé par + #elements used in software centers: centres Centers: Centres diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/LabelPersonHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/LabelPersonHelper.php new file mode 100644 index 000000000..2cfc8d3cf --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Helper/LabelPersonHelper.php @@ -0,0 +1,69 @@ +personRepository = $personRepository; + $this->personRender = $personRender; + } + + public function getLabelMulti(string $key, array $values, string $header): callable + { + return function ($value) use ($header) { + if ('_header' === $value) { + return $header; + } + + if (null === $value) { + return ''; + } + + $decoded = json_decode($value); + + if (0 === count($decoded)) { + return ''; + } + + return + implode( + '|', + array_map( + function (int $personId) { + $person = $this->personRepository->find($personId); + + if (null === $person) { + return ''; + } + + return $this->personRender->renderString($person, []); + }, + array_unique( + array_filter($decoded, static fn (?int $id) => null !== $id), + SORT_NUMERIC + ) + ) + ); + }; + } +} diff --git a/src/Bundle/ChillThirdPartyBundle/Export/Helper/LabelThirdPartyHelper.php b/src/Bundle/ChillThirdPartyBundle/Export/Helper/LabelThirdPartyHelper.php new file mode 100644 index 000000000..dc5f8ddc6 --- /dev/null +++ b/src/Bundle/ChillThirdPartyBundle/Export/Helper/LabelThirdPartyHelper.php @@ -0,0 +1,69 @@ +thirdPartyRender = $thirdPartyRender; + $this->thirdPartyRepository = $thirdPartyRepository; + } + + public function getLabelMulti(string $key, array $values, string $header): callable + { + return function ($value) use ($header) { + if ('_header' === $value) { + return $header; + } + + if (null === $value) { + return ''; + } + + $decoded = json_decode($value); + + if (0 === count($decoded)) { + return ''; + } + + return + implode( + '|', + array_map( + function (int $tpId) { + $tp = $this->thirdPartyRepository->find($tpId); + + if (null === $tp) { + return ''; + } + + return $this->thirdPartyRender->renderString($tp, []); + }, + array_unique( + array_filter($decoded, static fn (?int $id) => null !== $id), + SORT_NUMERIC + ) + ) + ); + }; + } +} diff --git a/src/Bundle/ChillThirdPartyBundle/config/services.yaml b/src/Bundle/ChillThirdPartyBundle/config/services.yaml index 8f9420a67..0024556f6 100644 --- a/src/Bundle/ChillThirdPartyBundle/config/services.yaml +++ b/src/Bundle/ChillThirdPartyBundle/config/services.yaml @@ -6,3 +6,8 @@ services: tags: - { name: 'serializer.normalizer', priority: 64 } + Chill\ThirdPartyBundle\Export\: + autowire: true + autoconfigure: true + resource: '../Export/' +