From b5a9dac62e766d42d5c918b0a5d077f70b6c4270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 28 Oct 2022 22:25:53 +0200 Subject: [PATCH] Feature: [export] Add a list of accompanying periods --- .../ChillMainExtension.php | 4 + .../ChillMainBundle/Doctrine/DQL/STX.php | 37 ++ .../ChillMainBundle/Doctrine/DQL/STY.php | 37 ++ .../Export/Helper/DateTimeHelper.php | 60 +++ .../Export/Helper/ExportAddressHelper.php | 174 +++++--- .../Export/Helper/UserHelper.php | 43 ++ .../Export/Export/CountAccompanyingCourse.php | 2 +- .../Export/Export/ListAccompanyingPeriod.php | 416 ++++++++++++++++++ .../Export/Export/ListPerson.php | 2 +- .../ListPersonWithAccompanyingPeriod.php | 4 +- .../Export/Helper/ListPersonHelper.php | 42 +- .../config/services/exports_person.yaml | 6 + .../translations/messages.fr.yml | 44 +- 13 files changed, 778 insertions(+), 93 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Doctrine/DQL/STX.php create mode 100644 src/Bundle/ChillMainBundle/Doctrine/DQL/STY.php create mode 100644 src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php create mode 100644 src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 9164607a7..dc50846fb 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -34,6 +34,8 @@ use Chill\MainBundle\Doctrine\DQL\Replace; use Chill\MainBundle\Doctrine\DQL\Similarity; use Chill\MainBundle\Doctrine\DQL\STContains; use Chill\MainBundle\Doctrine\DQL\StrictWordSimilarityOPS; +use Chill\MainBundle\Doctrine\DQL\STX; +use Chill\MainBundle\Doctrine\DQL\STY; use Chill\MainBundle\Doctrine\DQL\ToChar; use Chill\MainBundle\Doctrine\DQL\Unaccent; use Chill\MainBundle\Doctrine\ORM\Hydration\FlatHierarchyEntityHydrator; @@ -245,6 +247,8 @@ class ChillMainExtension extends Extension implements 'STRICT_WORD_SIMILARITY_OPS' => StrictWordSimilarityOPS::class, 'ST_CONTAINS' => STContains::class, 'JSONB_ARRAY_LENGTH' => JsonbArrayLength::class, + 'ST_X' => STX::class, + 'ST_Y' => STY::class, ], 'datetime_functions' => [ 'EXTRACT' => Extract::class, diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/STX.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/STX.php new file mode 100644 index 000000000..d5d8d0a3f --- /dev/null +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/STX.php @@ -0,0 +1,37 @@ +field->dispatch($sqlWalker)); + } + + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->field = $parser->ArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/STY.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/STY.php new file mode 100644 index 000000000..e827da543 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/STY.php @@ -0,0 +1,37 @@ +field->dispatch($sqlWalker)); + } + + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->field = $parser->ArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php new file mode 100644 index 000000000..86a2458b2 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php @@ -0,0 +1,60 @@ +translator = $translator; + } + + public function getLabel($header): callable + { + return function ($value) use ($header) { + if ('_header' === $value) { + return $this->translator->trans($header); + } + + if (null === $value) { + return ''; + } + + // warning: won't work with DateTimeImmutable as we reset time a few lines later + $date = DateTime::createFromFormat('Y-m-d', $value); + $hasTime = false; + + if (false === $date) { + $date = DateTime::createFromFormat('Y-m-d H:i:s', $value); + $hasTime = true; + } + + // check that the creation could occurs. + if (false === $date) { + throw new Exception(sprintf('The value %s could ' + . 'not be converted to %s', $value, DateTime::class)); + } + + if (!$hasTime) { + $date->setTime(0, 0, 0); + } + + return $date; + }; + } +} diff --git a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php index 6c6fcb76e..41443e3d8 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -14,9 +14,11 @@ namespace Chill\MainBundle\Export\Helper; use Chill\MainBundle\Repository\AddressRepository; use Chill\MainBundle\Templating\Entity\AddressRender; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; +use Doctrine\ORM\QueryBuilder; use LogicException; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessor; +use function in_array; use function strlen; /** @@ -24,6 +26,10 @@ use function strlen; */ class ExportAddressHelper { + public const F_ALL = + self::F_ATTRIBUTES | self::F_BUILDING | self::F_COUNTRY | + self::F_GEOM | self::F_POSTAL_CODE | self::F_STREET; + public const F_AS_STRING = 0b00010000; public const F_ATTRIBUTES = 0b01000000; @@ -77,6 +83,87 @@ class ExportAddressHelper $this->addressRender = $addressRender; } + public function addSelectClauses(int $params, QueryBuilder $queryBuilder, $entityName = 'address', $prefix = 'add') + { + foreach (self::ALL as $key => $bitmask) { + if (($params & $bitmask) === $bitmask) { + foreach (self::COLUMN_MAPPING[$key] as $field) { + switch ($field) { + case 'id': + case '_as_string': + $queryBuilder->addSelect(sprintf('%s.id AS %s%s', $entityName, $prefix, $field)); + + break; + + case 'street': + case 'streetNumber': + case 'building': + case 'floor': + case 'corridor': + case 'steps': + case 'buildingName': + case 'flat': + case 'distribution': + case 'extra': + $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $entityName, $field, $prefix, $field)); + + break; + + case 'country': + case 'postcode_name': + case 'postcode_code': + $postCodeAlias = sprintf('%spostcode_t', $prefix); + + if (!in_array($postCodeAlias, $queryBuilder->getAllAliases(), true)) { + $queryBuilder->leftJoin($entityName . '.postcode', $postCodeAlias); + } + + if ('postcode_name' === $field) { + $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $postCodeAlias, 'name', $prefix, $field)); + + break; + } + + if ('postcode_code' === $field) { + $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $postCodeAlias, 'code', $prefix, $field)); + + break; + } + + $countryAlias = sprintf('%scountry_t', $prefix); + + if (!in_array($countryAlias, $queryBuilder->getAllAliases(), true)) { + $queryBuilder->leftJoin(sprintf('%s.country', $postCodeAlias), $countryAlias); + } + + $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $countryAlias, 'name', $prefix, $field)); + + break; + + case 'isNoAddress': + case 'confidential': + $queryBuilder->addSelect(sprintf('CASE WHEN %s.%s = \'TRUE\' THEN 1 ELSE 0 END AS %s%s', $entityName, $field, $prefix, $field)); + + break; + + case '_lat': + $queryBuilder->addSelect(sprintf('ST_Y(%s.point) AS %s%s', $entityName, $prefix, $field)); + + break; + + case '_lon': + $queryBuilder->addSelect(sprintf('ST_X(%s.point) AS %s%s', $entityName, $prefix, $field)); + + break; + + default: + throw new LogicException('This key is not supported: ' . $key); + } + } + } + } + } + /** * @param self::F_* $params * @@ -115,23 +202,11 @@ class ExportAddressHelper case 'extra': case 'flat': case 'floor': - return function ($value) use ($sanitizedKey, $translationPrefix) { - if ('_header' === $value) { - return $translationPrefix . $sanitizedKey; - } - - if (null === $value) { - return ''; - } - - $address = $this->addressRepository->find($value); - - return $this->propertyAccess->getValue($address, $sanitizedKey); - }; - case '_lat': case '_lon': - return function ($value) use ($sanitizedKey, $translationPrefix) { + case 'postcode_code': + case 'postcode_name': + return static function ($value) use ($sanitizedKey, $translationPrefix) { if ('_header' === $value) { return $translationPrefix . $sanitizedKey; } @@ -140,28 +215,25 @@ class ExportAddressHelper return ''; } - $address = $this->addressRepository->find($value); - $geom = $address->getPoint(); + return $value; + }; - if (null === $geom) { + case 'country': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp' . $key; + } + + if (null === $value) { return ''; } - switch ($sanitizedKey) { - case '_lat': - return $geom->getLat(); - - case '_lon': - return $geom->getLon(); - - default: - throw new LogicException('only _lat or _lon accepted, given: ' . $sanitizedKey); - } + return $this->translatableStringHelper->localize(json_decode($value, true)); }; case 'isNoAddress': case 'confidential': - return function ($value) use ($sanitizedKey, $translationPrefix) { + return static function ($value) use ($sanitizedKey, $translationPrefix) { if ('_header' === $value) { return $translationPrefix . $sanitizedKey; } @@ -170,9 +242,7 @@ class ExportAddressHelper return ''; } - $address = $this->addressRepository->find($value); - - switch ($val = $this->propertyAccess->getValue($address, $sanitizedKey)) { + switch ($value) { case null: return ''; @@ -187,21 +257,6 @@ class ExportAddressHelper } }; - case 'country': - return function ($value) use ($sanitizedKey, $translationPrefix) { - if ('_header' === $value) { - return $translationPrefix . $sanitizedKey; - } - - if (null === $value) { - return ''; - } - - $address = $this->addressRepository->find($value); - - return $this->translatableStringHelper->localize($address->getPostcode()->getCountry()->getName()); - }; - case '_as_string': return function ($value) use ($sanitizedKey, $translationPrefix) { if ('_header' === $value) { @@ -217,31 +272,6 @@ class ExportAddressHelper return $this->addressRender->renderString($address, []); }; - case 'postcode_code': - case 'postcode_name': - return function ($value) use ($sanitizedKey, $translationPrefix) { - if ('_header' === $value) { - return $translationPrefix . $sanitizedKey; - } - - if (null === $value) { - return ''; - } - - $address = $this->addressRepository->find($value); - - switch ($sanitizedKey) { - case 'postcode_code': - return $address->getPostcode()->getCode(); - - case 'postcode_name': - return $address->getPostcode()->getName(); - - default: - throw new LogicException('this key is not supported: ' . $sanitizedKey); - } - }; - default: throw new LogicException('this key is not supported: ' . $sanitizedKey); } diff --git a/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php new file mode 100644 index 000000000..98c4c5579 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php @@ -0,0 +1,43 @@ +userRender = $userRender; + $this->userRepository = $userRepository; + } + + public function getLabel($key, array $values, string $header): callable + { + return function ($value) use ($header) { + if ('_header' === $value) { + return $header; + } + + if (null === $value || null === $user = $this->userRepository->find($value)) { + return ''; + } + + return $this->userRender->renderString($user, []); + }; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php index 8c8744dfd..d07ee3639 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php @@ -38,7 +38,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface public function buildForm(FormBuilderInterface $builder): void { - // TODO: Implement buildForm() method. + // Nothing to add here } public function getAllowedFormattersTypes(): array diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php new file mode 100644 index 000000000..28ad94bd3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php @@ -0,0 +1,416 @@ +addressHelper = $addressHelper; + $this->dateTimeHelper = $dateTimeHelper; + $this->entityManager = $entityManager; + $this->personRender = $personRender; + $this->personRepository = $personRepository; + $this->socialIssueRender = $socialIssueRender; + $this->socialIssueRepository = $socialIssueRepository; + $this->thirdPartyRender = $thirdPartyRender; + $this->thirdPartyRepository = $thirdPartyRepository; + $this->translatableStringHelper = $translatableStringHelper; + $this->userHelper = $userHelper; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('calc_date', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'label' => 'export.list.acp.Date of calculation for associated elements', + 'help' => 'export.list.acp.The associated referree, localisation, and other elements will be valid at this date', + 'required' => true, + ]); + } + + public function getAllowedFormattersTypes() + { + return [FormatterInterface::TYPE_LIST]; + } + + public function getDescription() + { + return 'export.list.acp.Generate a list of accompanying periods, filtered on different parameters.'; + } + + public function getGroup(): string + { + return 'Exports of accompanying courses'; + } + + public function getLabels($key, array $values, $data) + { + if (substr($key, 0, strlen('address_fields')) === 'address_fields') { + return $this->addressHelper->getLabel($key, $values, $data, 'address_fields'); + } + + switch ($key) { + case 'stepSince': + case 'openingDate': + case 'closingDate': + case 'referrerSince': + case 'createdAt': + case 'updatedAt': + return $this->dateTimeHelper->getLabel('export.list.acp.' . $key); + + case 'origin': + case 'closingMotive': + case 'job': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value) { + return ''; + } + + return $this->translatableStringHelper->localize(json_decode($value, true)); + }; + + case 'locationPersonName': + case 'requestorPerson': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value || null === $person = $this->personRepository->find($value)) { + return ''; + } + + return $this->personRender->renderString($person, []); + }; + + case 'requestorThirdParty': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value || null === $thirdparty = $this->thirdPartyRepository->find($value)) { + return ''; + } + + return $this->thirdPartyRender->renderString($thirdparty, []); + }; + + case 'scopes': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value) { + return ''; + } + + return implode( + '|', + array_map( + fn ($s) => $this->translatableStringHelper->localize($s), + json_decode($value, true) + ) + ); + }; + + case 'socialIssues': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value) { + return ''; + } + + return implode( + '|', + array_map( + fn ($s) => $this->socialIssueRender->renderString($this->socialIssueRepository->find($s), []), + json_decode($value, true) + ) + ); + }; + + default: + return static function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value) { + return ''; + } + + return $value; + }; + } + } + + public function getQueryKeys($data) + { + return array_merge( + self::FIELDS, + $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields') + ); + } + + public function getResult($query, $data) + { + return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR); + } + + public function getTitle() + { + return 'export.list.acp.List of accompanying periods'; + } + + public function getType() + { + return Declarations::PERSON_TYPE; + } + + public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) + { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + + $qb = $this->entityManager->createQueryBuilder(); + + $qb + ->from(AccompanyingPeriod::class, 'acp') + ->andWhere('acp.step != :list_acp_step') + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('list_acp_step', AccompanyingPeriod::STEP_DRAFT) + ->setParameter('authorized_centers', $centers); + + $this->addSelectClauses($qb, $data['calc_date']); + + return $qb; + } + + public function requiredRole(): string + { + return PersonVoter::LISTS; + } + + public function supportsModifiers() + { + return [ + Declarations::ACP_TYPE, + ]; + } + + private function addSelectClauses(QueryBuilder $qb, DateTimeImmutable $calcDate): void + { + // add the regular fields + foreach (['id', 'openingDate', 'closingDate', 'confidential', 'emergency', 'intensity', 'createdAt', 'updatedAt'] as $field) { + $qb->addSelect(sprintf('acp.%s AS %s', $field, $field)); + } + + // add the field which are simple association + foreach (['origin' => 'label', 'closingMotive' => 'name', 'job' => 'label', 'createdBy' => 'label', 'updatedBy' => 'label', 'administrativeLocation' => 'name'] as $entity => $field) { + $qb + ->leftJoin(sprintf('acp.%s', $entity), "{$entity}_t") + ->addSelect(sprintf('%s_t.%s AS %s', $entity, $field, $entity)); + } + + // step at date + $qb + ->addSelect('stepHistory.step AS step') + ->addSelect('stepHistory.startDate AS stepSince') + ->leftJoin('acp.stepHistories', 'stepHistory') + ->andWhere( + $qb->expr()->andX( + $qb->expr()->lte('stepHistory.startDate', ':calcDate'), + $qb->expr()->orX($qb->expr()->isNull('stepHistory.endDate'), $qb->expr()->gt('stepHistory.endDate', ':calcDate')) + ) + ); + + // referree at date + $qb + ->addSelect('referrer_t.label AS referrer') + ->addSelect('userHistory.startDate AS referrerSince') + ->leftJoin('acp.userHistories', 'userHistory') + ->leftJoin('userHistory.user', 'referrer_t') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('userHistory'), + $qb->expr()->andX( + $qb->expr()->lte('userHistory.startDate', ':calcDate'), + $qb->expr()->orX($qb->expr()->isNull('userHistory.endDate'), $qb->expr()->gt('userHistory.endDate', ':calcDate')) + ) + ) + ); + + // location of the acp + $qb + ->addSelect('CASE WHEN locationHistory.personLocation IS NOT NULL THEN 1 ELSE 0 END AS locationIsPerson') + ->addSelect('CASE WHEN locationHistory.personLocation IS NOT NULL THEN 0 ELSE 1 END AS locationIsTemp') + ->addSelect('IDENTITY(locationHistory.personLocation) AS locationPersonName') + ->addSelect('IDENTITY(locationHistory.personLocation) AS locationPersonId') + ->leftJoin('acp.locationHistories', 'locationHistory') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('locationHistory'), + $qb->expr()->andX( + $qb->expr()->lte('locationHistory.startDate', ':calcDate'), + $qb->expr()->orX($qb->expr()->isNull('locationHistory.endDate'), $qb->expr()->gt('locationHistory.endDate', ':calcDate')) + ) + ) + ) + ->leftJoin(PersonHouseholdAddress::class, 'personAddress', Join::WITH, 'locationHistory.personLocation = personAddress.person') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('personAddress'), + $qb->expr()->andX( + $qb->expr()->lte('personAddress.validFrom', ':calcDate'), + $qb->expr()->orX($qb->expr()->isNull('personAddress.validTo'), $qb->expr()->gt('personAddress.validTo', ':calcDate')) + ) + ) + ) + ->leftJoin(Address::class, 'acp_address', Join::WITH, 'COALESCE(IDENTITY(locationHistory.addressLocation), IDENTITY(personAddress.address)) = acp_address.id'); + + $this->addressHelper->addSelectClauses(ExportAddressHelper::F_ALL, $qb, 'acp_address', 'address_fields'); + + // requestor + $qb + ->addSelect('CASE WHEN acp.requestorPerson IS NULL THEN 1 ELSE 0 END AS isRequestorPerson') + ->addSelect('CASE WHEN acp.requestorPerson IS NULL THEN 0 ELSE 1 END AS isRequestorThirdParty') + ->addSelect('IDENTITY(acp.requestorPerson) AS requestorPersonId') + ->addSelect('IDENTITY(acp.requestorThirdParty) AS requestorThirdPartyId') + ->addSelect('IDENTITY(acp.requestorPerson) AS requestorPerson') + ->addSelect('IDENTITY(acp.requestorThirdParty) AS requestorThirdParty'); + + $qb + // scopes + ->addSelect('(SELECT AGGREGATE(scope.name) FROM ' . Scope::class . ' scope WHERE scope MEMBER OF acp.scopes) AS scopes') + // social issues + ->addSelect('(SELECT AGGREGATE(socialIssue.id) FROM ' . SocialIssue::class . ' socialIssue WHERE socialIssue MEMBER OF acp.socialIssues) AS socialIssues'); + + // add parameter + $qb->setParameter('calcDate', $calcDate); + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index eb746e122..d1a23a570 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -153,7 +153,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } if (substr($key, 0, strlen('address_fields')) === 'address_fields') { - $fields = array_merge($fields, $this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields')); + $fields = array_merge($fields, $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields')); continue; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php index ce97ce662..534eaecc6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Export\Helper\ExportAddressHelper; use Chill\MainBundle\Export\ListInterface; use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; @@ -121,7 +122,7 @@ class ListPersonWithAccompanyingPeriod implements ExportElementValidatedInterfac } if (substr($key, 0, strlen('address_fields')) === 'address_fields') { - $fields = array_merge($fields, $this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields')); + $fields = array_merge($fields, $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields')); continue; } @@ -173,6 +174,7 @@ class ListPersonWithAccompanyingPeriod implements ExportElementValidatedInterfac $qb->from(Person::class, 'person') ->join('person.accompanyingPeriodParticipations', 'acppart') ->join('acppart.accompanyingPeriod', 'acp') + ->andWhere($qb->expr()->neq('acp.step', "'" . AccompanyingPeriod::STEP_DRAFT . "'")) ->andWhere( $qb->expr()->exists( 'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php index 69f797c3d..de8870674 100644 --- a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Helper; use Chill\MainBundle\Export\Helper\ExportAddressHelper; +use Chill\MainBundle\Repository\CenterRepositoryInterface; use Chill\MainBundle\Repository\CivilityRepositoryInterface; use Chill\MainBundle\Repository\CountryRepository; use Chill\MainBundle\Repository\LanguageRepositoryInterface; @@ -72,16 +73,10 @@ class ListPersonHelper 'lifecycleUpdate', ]; - public const HELPER_ATTRIBUTES = ExportAddressHelper::F_ATTRIBUTES | - ExportAddressHelper::F_BUILDING | - ExportAddressHelper::F_COUNTRY | - ExportAddressHelper::F_GEOM | - ExportAddressHelper::F_POSTAL_CODE | - ExportAddressHelper::F_STREET | - ExportAddressHelper::F_AS_STRING; - private ExportAddressHelper $addressHelper; + private CenterRepositoryInterface $centerRepository; + private CivilityRepositoryInterface $civilityRepository; private CountryRepository $countryRepository; @@ -98,6 +93,7 @@ class ListPersonHelper public function __construct( ExportAddressHelper $addressHelper, + CenterRepositoryInterface $centerRepository, CivilityRepositoryInterface $civilityRepository, CountryRepository $countryRepository, LanguageRepositoryInterface $languageRepository, @@ -107,6 +103,7 @@ class ListPersonHelper UserRepositoryInterface $userRepository ) { $this->addressHelper = $addressHelper; + $this->centerRepository = $centerRepository; $this->civilityRepository = $civilityRepository; $this->countryRepository = $countryRepository; $this->languageRepository = $languageRepository; @@ -134,12 +131,9 @@ class ListPersonHelper break; case 'address_fields': - foreach ($this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields') as $key) { - $qb - ->addSelect(sprintf('IDENTITY(personHouseholdAddress.address) AS %s', $key)); - } - $this->addCurrentAddressAt($qb, $computedDate); + $qb->leftJoin('personHouseholdAddress.address', 'personAddress'); + $this->addressHelper->addSelectClauses(ExportAddressHelper::F_ALL, $qb, 'personAddress', 'address_fields'); break; @@ -154,7 +148,10 @@ class ListPersonHelper } if (in_array('address_fields', $fields, true)) { - $qb->addGroupBy('address_fieldsid'); + $qb + ->addGroupBy('address_fieldsid') + ->addGroupBy('address_fieldscountry_t.id') + ->addGroupBy('address_fieldspostcode_t.id'); } if (in_array('household_id', $fields, true)) { @@ -234,7 +231,7 @@ class ListPersonHelper return array_merge( self::FIELDS, ['createdAt', 'createdBy', 'updatedAt', 'updatedBy'], - $this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields') + $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields') ); } @@ -245,6 +242,19 @@ class ListPersonHelper } switch ($key) { + case 'center': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value || null === $center = $this->centerRepository->find($value)) { + return ''; + } + + return $center->getName(); + }; + case 'birthdate': case 'deathdate': case 'maritalStatusDate': @@ -413,7 +423,7 @@ class ListPersonHelper ->leftJoin('person.householdAddresses', 'personHouseholdAddress') ->andWhere( $qb->expr()->orX( - // no address at this time + // no address at this time $qb->expr()->isNull('personHouseholdAddress'), // there is one address... $qb->expr()->andX( diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml index b9030ec62..b2b182c5b 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml @@ -27,6 +27,12 @@ services: tags: - { name: chill.export, alias: list_person_with_acp } + Chill\PersonBundle\Export\Export\ListAccompanyingPeriod: + autowire: true + autoconfigure: true + tags: + - { name: chill.export, alias: list_acp } + chill.person.export.list_person.duplicate: class: Chill\PersonBundle\Export\Export\ListPersonDuplicate arguments: diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 5f43608ce..319231489 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -1020,8 +1020,48 @@ export: by_referrer: Computation date for referrer: Date à laquelle le référent était actif list: - person_with_acp.List peoples having an accompanying period: Liste des personnes ayant un parcours d'accompagnement - Create a list of people having an accompaying periods, according to various filters.: Génère une liste des personnes ayant un parcours d'accompagnement, selon différents critères liés au parcours ou à l'usager + person_with_acp: + List peoples having an accompanying period: Liste des personnes ayant un parcours d'accompagnement + Create a list of people having an accompaying periods, according to various filters.: Génère une liste des personnes ayant un parcours d'accompagnement, selon différents critères liés au parcours ou à l'usager + acp: + List of accompanying periods: Liste de périodes d'accompagnements + Generate a list of accompanying periods, filtered on different parameters.: Génère une liste des périodes d'accompagnement, filtrée sur différents paramètres. + Date of calculation for associated elements: Date de calcul des éléments associés + The associated referree, localisation, and other elements will be valid at this date: Les éléments associés, comme la localisation, le référent et d'autres éléments seront valides à cette date + id: Identifiant du parcours + openingDate: Date d'ouverture du parcours + closingDate: Date de fermeture du parcours + confidential: Confidentiel + emergency: Urgent + intensity: Intensité + createdAt: Créé le + updatedAt: Dernière mise à jour le + acpOrigin: Origine du parcours + acpClosingMotive: Motif de fermeture + acpJob: Métier du parcours + createdBy: Créé par + updatedBy: Dernière modification par + administrativeLocation: Location administrative + step: Etape + stepSince: Dernière modification de l'étape + referrer: Référent + referrerSince: Référent depuis le + locationIsPerson: Parcours localisé auprès d'un usager concerné + locationIsTemp: Parcours avec une localisation temporaire + acpLocationPersonName: Usager auprès duquel le parcours est localisé + locationPersonId: Identifiant de l'usager auprès duquel le parcours est localisé + acpaddress_fieldscountry: Pays de l'adresse + isRequestorPerson: Le demandeur est-il un usager ? + isRequestorThirdParty: Le demandeur est-il un tiers ? + requestorPersonId: Identifiant du demandeur personne + requestorThirdPartyId: Identifiant du tiers + acprequestorPerson: Nom du demandeur personne + acprequestorThirdPaty: Nom du demandeur tiers + scopes: Services + socialIssues: Problématiques sociales + + + social_action: and children: et dérivés