From 145c1df313e0328830fe406c12b1afcf03b070dd Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 5 Jul 2023 09:43:13 +0200 Subject: [PATCH 01/25] cleaning --- .../Resources/public/chill/scss/forms.scss | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/forms.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/forms.scss index cd81f36dc..a517a5516 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/forms.scss +++ b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/forms.scss @@ -44,13 +44,5 @@ form { } .chill_filter_order { - background: $gray-100; /* - border: 3px dashed $white; - background: repeating-linear-gradient( - -45deg, - $gray-100, - $gray-100 2px, - $white 2px, - $white 6px - ); */ + background: $gray-100; } \ No newline at end of file From c04fd66163f2014ac54362f56bafebca9f38e1ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 5 Jul 2023 22:20:27 +0200 Subject: [PATCH 02/25] do not show filter on job or activity type if less than 2 possibilities --- .../Controller/ActivityController.php | 39 +++++++++++-------- .../Templating/Listing/FilterOrderHelper.php | 20 ++++++++++ 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php index 63639c149..9b6e69bf0 100644 --- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php +++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php @@ -259,8 +259,8 @@ final class ActivityController extends AbstractController $filterArgs = [ 'my_activities' => $filter->getSingleCheckboxData('my_activities'), - 'types' => $filter->getEntityChoiceData('activity_types'), - 'jobs' => $filter->getEntityChoiceData('jobs'), + 'types' => $filter->hasEntityChoice('activity_type') ? $filter->getEntityChoiceData('activity_types') : [], + 'jobs' => $filter->hasEntityChoice('jobs') ? $filter->getEntityChoiceData('jobs') : [], 'before' => $filter->getDateRangeData('activity_date')['to'], 'after' => $filter->getDateRangeData('activity_date')['from'], ]; @@ -327,21 +327,28 @@ final class ActivityController extends AbstractController $filterBuilder ->addDateRange('activity_date', 'activity.date') - ->addSingleCheckbox('my_activities', 'activity_filter.My activities') - ->addEntityChoice('activity_types', 'activity_filter.Types', \Chill\ActivityBundle\Entity\ActivityType::class, $types, [ - 'choice_label' => function (\Chill\ActivityBundle\Entity\ActivityType $activityType) { - $text = match ($activityType->hasCategory()) { - true => $this->translatableStringHelper->localize($activityType->getCategory()->getName()) . ' > ', - false => '', - }; + ->addSingleCheckbox('my_activities', 'activity_filter.My activities'); - return $text . $this->translatableStringHelper->localize($activityType->getName()); - } - ]) - ->addEntityChoice('jobs', 'activity_filter.Jobs', UserJob::class, $jobs, [ - 'choice_label' => fn (UserJob $u) => $this->translatableStringHelper->localize($u->getLabel()) - ]) - ; + if (1 < count($types)) { + $filterBuilder + ->addEntityChoice('activity_types', 'activity_filter.Types', \Chill\ActivityBundle\Entity\ActivityType::class, $types, [ + 'choice_label' => function (\Chill\ActivityBundle\Entity\ActivityType $activityType) { + $text = match ($activityType->hasCategory()) { + true => $this->translatableStringHelper->localize($activityType->getCategory()->getName()) . ' > ', + false => '', + }; + + return $text . $this->translatableStringHelper->localize($activityType->getName()); + } + ]); + } + + if (1 < count($jobs)) { + $filterBuilder + ->addEntityChoice('jobs', 'activity_filter.Jobs', UserJob::class, $jobs, [ + 'choice_label' => fn (UserJob $u) => $this->translatableStringHelper->localize($u->getLabel()) + ]); + } return $filterBuilder->build(); } diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php index 28cc8e331..5c9971890 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php @@ -116,16 +116,31 @@ class FilterOrderHelper ->handleRequest($this->requestStack->getCurrentRequest()); } + public function hasCheckboxData(string $name): bool + { + return array_key_exists($name, $this->checkboxes); + } + public function getCheckboxData(string $name): array { return $this->getFormData()['checkboxes'][$name]; } + public function hasSingleCheckboxData(string $name): bool + { + return array_key_exists($name, $this->singleCheckbox); + } + public function getSingleCheckboxData(string $name): ?bool { return $this->getFormData()['single_checkboxes'][$name]; } + public function hasEntityChoice(string $name): bool + { + return array_key_exists($name, $this->entityChoices); + } + public function getEntityChoiceData($name): mixed { return $this->getFormData()['entity_choices'][$name]; @@ -144,6 +159,11 @@ class FilterOrderHelper return $this->singleCheckbox; } + public function hasDateRangeData(string $name): bool + { + return array_key_exists($name, $this->dateRanges); + } + /** * @return array{to: ?DateTimeImmutable, from: ?DateTimeImmutable} */ From 7ccff61c254cea360b68bf33d74ea0988ee1a5e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 7 Jul 2023 09:17:36 +0200 Subject: [PATCH 03/25] Refactor ListAccompanyingPeriod to use a helper for most of the work --- .../Export/Export/ListAccompanyingPeriod.php | 313 +----------------- .../Helper/ListAccompanyingPeriodHelper.php | 306 +++++++++++++++++ .../translations/messages.fr.yml | 2 +- 3 files changed, 315 insertions(+), 306 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php index af66ab312..3f7821bbc 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php @@ -29,6 +29,7 @@ use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress; use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use Chill\PersonBundle\Export\Declarations; +use Chill\PersonBundle\Export\Helper\ListAccompanyingPeriodHelper; use Chill\PersonBundle\Repository\PersonRepository; use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository; use Chill\PersonBundle\Security\Authorization\PersonVoter; @@ -45,95 +46,13 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; use function strlen; -class ListAccompanyingPeriod implements ListInterface, GroupedExportInterface +final readonly class ListAccompanyingPeriod implements ListInterface, GroupedExportInterface { - private const FIELDS = [ - 'id', - 'step', - 'stepSince', - 'openingDate', - 'closingDate', - 'referrer', - 'referrerSince', - 'administrativeLocation', - 'locationIsPerson', - 'locationIsTemp', - 'locationPersonName', - 'locationPersonId', - 'origin', - 'closingMotive', - 'confidential', - 'emergency', - 'intensity', - 'job', - 'isRequestorPerson', - 'isRequestorThirdParty', - 'requestorPerson', - 'requestorPersonId', - 'requestorThirdParty', - 'requestorThirdPartyId', - 'scopes', - 'socialIssues', - 'createdAt', - 'createdBy', - 'updatedAt', - 'updatedBy', - ]; - - private ExportAddressHelper $addressHelper; - - private DateTimeHelper $dateTimeHelper; - - private EntityManagerInterface $entityManager; - - private PersonRenderInterface $personRender; - - private PersonRepository $personRepository; - - private RollingDateConverterInterface $rollingDateConverter; - - private SocialIssueRender $socialIssueRender; - - private SocialIssueRepository $socialIssueRepository; - - private ThirdPartyRender $thirdPartyRender; - - private ThirdPartyRepository $thirdPartyRepository; - - private TranslatableStringHelperInterface $translatableStringHelper; - - private TranslatorInterface $translator; - - private UserHelper $userHelper; - public function __construct( - ExportAddressHelper $addressHelper, - DateTimeHelper $dateTimeHelper, - EntityManagerInterface $entityManager, - PersonRenderInterface $personRender, - PersonRepository $personRepository, - ThirdPartyRepository $thirdPartyRepository, - ThirdPartyRender $thirdPartyRender, - SocialIssueRepository $socialIssueRepository, - SocialIssueRender $socialIssueRender, - TranslatableStringHelperInterface $translatableStringHelper, - TranslatorInterface $translator, - RollingDateConverterInterface $rollingDateConverter, - UserHelper $userHelper + private EntityManagerInterface $entityManager, + private RollingDateConverterInterface $rollingDateConverter, + private ListAccompanyingPeriodHelper $listAccompanyingPeriodHelper, ) { - $this->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->translator = $translator; - $this->rollingDateConverter = $rollingDateConverter; - $this->userHelper = $userHelper; } public function buildForm(FormBuilderInterface $builder) @@ -169,141 +88,12 @@ class ListAccompanyingPeriod implements ListInterface, GroupedExportInterface 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, 512, JSON_THROW_ON_ERROR)); - }; - - 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, 512, JSON_THROW_ON_ERROR) - ) - ); - }; - - 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, 512, JSON_THROW_ON_ERROR) - ) - ); - }; - - case 'step': - return fn ($value) => match ($value) { - '_header' => 'export.list.acp.step', - null => '', - AccompanyingPeriod::STEP_DRAFT => $this->translator->trans('course.draft'), - AccompanyingPeriod::STEP_CONFIRMED => $this->translator->trans('course.confirmed'), - AccompanyingPeriod::STEP_CLOSED => $this->translator->trans('course.closed'), - AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_SHORT => $this->translator->trans('course.inactive_short'), - AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_LONG => $this->translator->trans('course.inactive_long'), - default => $value, - }; - - case 'intensity': - return fn ($value) => match ($value) { - '_header' => 'export.list.acp.intensity', - null => '', - AccompanyingPeriod::INTENSITY_OCCASIONAL => $this->translator->trans('occasional'), - AccompanyingPeriod::INTENSITY_REGULAR => $this->translator->trans('regular'), - default => $value, - }; - - default: - return static function ($value) use ($key) { - if ('_header' === $value) { - return 'export.list.acp.' . $key; - } - - if (null === $value) { - return ''; - } - - return $value; - }; - } + return $this->listAccompanyingPeriodHelper->getLabels($key, $values, $data); } public function getQueryKeys($data) { - return array_merge( - self::FIELDS, - $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields') - ); + return $this->listAccompanyingPeriodHelper->getQueryKeys($data); } public function getResult($query, $data) @@ -341,7 +131,7 @@ class ListAccompanyingPeriod implements ListInterface, GroupedExportInterface ->setParameter('list_acp_step', AccompanyingPeriod::STEP_DRAFT) ->setParameter('authorized_centers', $centers); - $this->addSelectClauses($qb, $this->rollingDateConverter->convert($data['calc_date'])); + $this->listAccompanyingPeriodHelper->addSelectClauses($qb, $this->rollingDateConverter->convert($data['calc_date'])); return $qb; } @@ -357,91 +147,4 @@ class ListAccompanyingPeriod implements ListInterface, GroupedExportInterface 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 AND (personAddress.validFrom <= :calcDate AND (personAddress.validTo IS NULL OR 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/Helper/ListAccompanyingPeriodHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php new file mode 100644 index 000000000..8671b4977 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php @@ -0,0 +1,306 @@ +addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields') + ); + } + + 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, 512, JSON_THROW_ON_ERROR)); + }; + + 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, 512, JSON_THROW_ON_ERROR) + ) + ); + }; + + 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, 512, JSON_THROW_ON_ERROR) + ) + ); + }; + + case 'step': + return fn ($value) => match ($value) { + '_header' => 'export.list.acp.step', + null => '', + AccompanyingPeriod::STEP_DRAFT => $this->translator->trans('course.draft'), + AccompanyingPeriod::STEP_CONFIRMED => $this->translator->trans('course.confirmed'), + AccompanyingPeriod::STEP_CLOSED => $this->translator->trans('course.closed'), + AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_SHORT => $this->translator->trans('course.inactive_short'), + AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_LONG => $this->translator->trans('course.inactive_long'), + default => $value, + }; + + case 'intensity': + return fn ($value) => match ($value) { + '_header' => 'export.list.acp.intensity', + null => '', + AccompanyingPeriod::INTENSITY_OCCASIONAL => $this->translator->trans('occasional'), + AccompanyingPeriod::INTENSITY_REGULAR => $this->translator->trans('regular'), + default => $value, + }; + + default: + return static function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value) { + return ''; + } + + return $value; + }; + } + } + + public 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 AND (personAddress.validFrom <= :calcDate AND (personAddress.validTo IS NULL OR 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/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 2f7800e2b..08e51aeab 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -1175,7 +1175,7 @@ export: 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é + locationPersonName: 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 ? From 56d9072abe971cf69f07a55a931e8becabe8fb68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 7 Jul 2023 09:33:03 +0200 Subject: [PATCH 04/25] change id, to avoid collision between ListPersonHelper and ListAccompanyingPeriodHelper --- .../Export/Helper/ListAccompanyingPeriodHelper.php | 6 ++++-- .../ChillPersonBundle/Export/Helper/ListPersonHelper.php | 7 ++++++- src/Bundle/ChillPersonBundle/translations/messages.fr.yml | 4 +++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php index 8671b4977..a32d78904 100644 --- a/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php @@ -32,7 +32,7 @@ use Symfony\Contracts\Translation\TranslatorInterface; final readonly class ListAccompanyingPeriodHelper { public const FIELDS = [ - 'id', + 'acpId', 'step', 'stepSince', 'openingDate', @@ -219,8 +219,10 @@ final readonly class ListAccompanyingPeriodHelper public function addSelectClauses(QueryBuilder $qb, \DateTimeImmutable $calcDate): void { + $qb->addSelect('acp.id AS acpId'); + // add the regular fields - foreach (['id', 'openingDate', 'closingDate', 'confidential', 'emergency', 'intensity', 'createdAt', 'updatedAt'] as $field) { + foreach (['openingDate', 'closingDate', 'confidential', 'emergency', 'intensity', 'createdAt', 'updatedAt'] as $field) { $qb->addSelect(sprintf('acp.%s AS %s', $field, $field)); } diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php index 77a1d9c86..697019ca3 100644 --- a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php @@ -42,7 +42,7 @@ use function strlen; class ListPersonHelper { public const FIELDS = [ - 'id', + 'personId', 'civility', 'firstName', 'lastName', @@ -124,6 +124,11 @@ class ListPersonHelper } switch ($f) { + case 'personId': + $qb->addSelect('person.id AS personId'); + + break; + case 'countryOfBirth': case 'nationality': $qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f)); diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 08e51aeab..0a82fc330 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -997,6 +997,8 @@ notification: Notify referrer: Notifier le référent Notify any: Notifier d'autres utilisateurs +personId: Identifiant de l'usager + export: export: acp_stats: @@ -1152,7 +1154,7 @@ export: Generate a list of accompanying periods, filtered on different parameters.: Génère une liste des parcours 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 + acpId: Identifiant du parcours openingDate: Date d'ouverture du parcours closingDate: Date de fermeture du parcours closingMotive: Motif de cloture From 7f30742fc3615311500931f9d22563f228e3c752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 7 Jul 2023 09:36:39 +0200 Subject: [PATCH 05/25] Rename ListPersonWithAccompanyingPeriod to ListPersonHavingAccompanyingPeriod --- ...ngPeriod.php => ListPersonHavingAccompanyingPeriod.php} | 7 ++++++- .../ChillPersonBundle/config/services/exports_person.yaml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) rename src/Bundle/ChillPersonBundle/Export/Export/{ListPersonWithAccompanyingPeriod.php => ListPersonHavingAccompanyingPeriod.php} (96%) diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonHavingAccompanyingPeriod.php similarity index 96% rename from src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php rename to src/Bundle/ChillPersonBundle/Export/Export/ListPersonHavingAccompanyingPeriod.php index 370046232..f2e4de4e3 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonHavingAccompanyingPeriod.php @@ -35,7 +35,12 @@ use function count; use function in_array; use function strlen; -class ListPersonWithAccompanyingPeriod implements ExportElementValidatedInterface, ListInterface, GroupedExportInterface +/** + * List the persons, having an accompanying period. + * + * Details of the accompanying period are not included + */ +class ListPersonHavingAccompanyingPeriod implements ExportElementValidatedInterface, ListInterface, GroupedExportInterface { private ExportAddressHelper $addressHelper; diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml index 64360aee9..81875fafb 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml @@ -24,7 +24,7 @@ services: tags: - { name: chill.export, alias: list_person } - Chill\PersonBundle\Export\Export\ListPersonWithAccompanyingPeriod: + Chill\PersonBundle\Export\Export\ListPersonHavingAccompanyingPeriod: autowire: true autoconfigure: true tags: From 17d2b795b41d9aee8457ad9ade199e07d1c5be60 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Fri, 7 Jul 2023 11:38:00 +0200 Subject: [PATCH 06/25] update changelog --- .changes/unreleased/DX-20230623-122408.yaml | 5 --- .../unreleased/Feature-20230623-122530.yaml | 5 --- .../unreleased/Feature-20230623-122702.yaml | 6 --- .../unreleased/Feature-20230623-124438.yaml | 5 --- .../unreleased/Fixed-20230628-170055.yaml | 6 --- .../unreleased/Fixed-20230629-124412.yaml | 6 --- .../unreleased/Fixed-20230629-231503.yaml | 5 --- .../unreleased/Fixed-20230630-171119.yaml | 5 --- .../unreleased/Fixed-20230630-171153.yaml | 5 --- .changie.yaml | 2 + CHANGELOG.md | 37 +++++++++++++++++++ .../Resources/public/chill/scss/forms.scss | 2 +- 12 files changed, 40 insertions(+), 49 deletions(-) delete mode 100644 .changes/unreleased/DX-20230623-122408.yaml delete mode 100644 .changes/unreleased/Feature-20230623-122530.yaml delete mode 100644 .changes/unreleased/Feature-20230623-122702.yaml delete mode 100644 .changes/unreleased/Feature-20230623-124438.yaml delete mode 100644 .changes/unreleased/Fixed-20230628-170055.yaml delete mode 100644 .changes/unreleased/Fixed-20230629-124412.yaml delete mode 100644 .changes/unreleased/Fixed-20230629-231503.yaml delete mode 100644 .changes/unreleased/Fixed-20230630-171119.yaml delete mode 100644 .changes/unreleased/Fixed-20230630-171153.yaml diff --git a/.changes/unreleased/DX-20230623-122408.yaml b/.changes/unreleased/DX-20230623-122408.yaml deleted file mode 100644 index 58dd96180..000000000 --- a/.changes/unreleased/DX-20230623-122408.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: DX -body: '[FilterOrderHelper] add entity choice and singleCheckbox' -time: 2023-06-23T12:24:08.133491895+02:00 -custom: - Issue: "" diff --git a/.changes/unreleased/Feature-20230623-122530.yaml b/.changes/unreleased/Feature-20230623-122530.yaml deleted file mode 100644 index 922750ea8..000000000 --- a/.changes/unreleased/Feature-20230623-122530.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: Feature -body: '[activity list] add filtering for activities list' -time: 2023-06-23T12:25:30.49643551+02:00 -custom: - Issue: "" diff --git a/.changes/unreleased/Feature-20230623-122702.yaml b/.changes/unreleased/Feature-20230623-122702.yaml deleted file mode 100644 index e1d1b0e1f..000000000 --- a/.changes/unreleased/Feature-20230623-122702.yaml +++ /dev/null @@ -1,6 +0,0 @@ -kind: Feature -body: '[activity list] in person context, show also the activities from the accompanying - periods where the person participates' -time: 2023-06-23T12:27:02.159041095+02:00 -custom: - Issue: "" diff --git a/.changes/unreleased/Feature-20230623-124438.yaml b/.changes/unreleased/Feature-20230623-124438.yaml deleted file mode 100644 index bc199d3bb..000000000 --- a/.changes/unreleased/Feature-20230623-124438.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: Feature -body: '[activity list] add pagination to the list of activities' -time: 2023-06-23T12:44:38.879098862+02:00 -custom: - Issue: "" diff --git a/.changes/unreleased/Fixed-20230628-170055.yaml b/.changes/unreleased/Fixed-20230628-170055.yaml deleted file mode 100644 index 7f9ec3028..000000000 --- a/.changes/unreleased/Fixed-20230628-170055.yaml +++ /dev/null @@ -1,6 +0,0 @@ -kind: Fixed -body: '[export] Rename label for CurrentActionFilter (on accompanying period work) - to make precision between "ouvert" and "sans date de fin"' -time: 2023-06-28T17:00:55.206937751+02:00 -custom: - Issue: "" diff --git a/.changes/unreleased/Fixed-20230629-124412.yaml b/.changes/unreleased/Fixed-20230629-124412.yaml deleted file mode 100644 index 7fc3d3eb0..000000000 --- a/.changes/unreleased/Fixed-20230629-124412.yaml +++ /dev/null @@ -1,6 +0,0 @@ -kind: Fixed -body: Force the db to have either a person_location or a address_location, and avoid - to have both also internally in the entity -time: 2023-06-29T12:44:12.019663991+02:00 -custom: - Issue: "" diff --git a/.changes/unreleased/Fixed-20230629-231503.yaml b/.changes/unreleased/Fixed-20230629-231503.yaml deleted file mode 100644 index e021d1fda..000000000 --- a/.changes/unreleased/Fixed-20230629-231503.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: Fixed -body: '[export] set rolling date on person age aggregator' -time: 2023-06-29T23:15:03.20841309+02:00 -custom: - Issue: "" diff --git a/.changes/unreleased/Fixed-20230630-171119.yaml b/.changes/unreleased/Fixed-20230630-171119.yaml deleted file mode 100644 index f3185ace2..000000000 --- a/.changes/unreleased/Fixed-20230630-171119.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: Fixed -body: '[export] fix list when a person locating a course is without address' -time: 2023-06-30T17:11:19.454081914+02:00 -custom: - Issue: "" diff --git a/.changes/unreleased/Fixed-20230630-171153.yaml b/.changes/unreleased/Fixed-20230630-171153.yaml deleted file mode 100644 index c09bd93d0..000000000 --- a/.changes/unreleased/Fixed-20230630-171153.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: Fixed -body: '[export] remove unused condition on course about duration participation' -time: 2023-06-30T17:11:53.076615549+02:00 -custom: - Issue: "" diff --git a/.changie.yaml b/.changie.yaml index 8a25ed695..cda69de65 100644 --- a/.changie.yaml +++ b/.changie.yaml @@ -30,6 +30,8 @@ kinds: auto: patch - label: DX auto: patch + - label: UX + auto: patch newlines: afterChangelogHeader: 1 beforeChangelogVersion: 1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 93ff93556..1f51bf06e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,43 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), and is generated by [Changie](https://github.com/miniscruff/changie). +## v2.4.0 - 2023-07-07 +### Feature +* [activity list] add filtering for activities list + + +* [activity list] in person context, show also the activities from the accompanying periods where the person participates + + +* [activity list] add pagination to the list of activities + + +* ([#118](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/118)) improve UX design for filterOrder box + + + + +### Fixed +* [export] Rename label for CurrentActionFilter (on accompanying period work) to make precision between "ouvert" and "sans date de fin" + + +* Force the db to have either a person_location or a address_location, and avoid to have both also internally in the entity + + +* [export] set rolling date on person age aggregator + + +* [export] fix list when a person locating a course is without address + + +* [export] remove unused condition on course about duration participation + + +### DX +* [FilterOrderHelper] add entity choice and singleCheckbox + + + ## v2.3.0 - 2023-06-27 ### Feature * ([#110](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/110)) Edit saved exports options: the saved exports options (forms, filters, aggregators) are now editable. diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/forms.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/forms.scss index a517a5516..28c597bc0 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/forms.scss +++ b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/forms.scss @@ -44,5 +44,5 @@ form { } .chill_filter_order { - background: $gray-100; + background: $gray-100; } \ No newline at end of file From c8146ded17b332a3566b58b7c824840259b7f7ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 7 Jul 2023 12:36:24 +0200 Subject: [PATCH 07/25] Feature: add a list for people with their associated accompanying course --- .../unreleased/Feature-20230707-123609.yaml | 5 + .../Export/ExportInterface.php | 2 +- ...istPersonWithAccompanyingPeriodDetails.php | 149 ++++++++++++++++++ .../Helper/ListAccompanyingPeriodHelper.php | 39 +++-- .../Export/Helper/ListPersonHelper.php | 42 ++--- .../config/services/exports_person.yaml | 20 +-- .../translations/messages.fr.yml | 10 +- 7 files changed, 213 insertions(+), 54 deletions(-) create mode 100644 .changes/unreleased/Feature-20230707-123609.yaml create mode 100644 src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriodDetails.php diff --git a/.changes/unreleased/Feature-20230707-123609.yaml b/.changes/unreleased/Feature-20230707-123609.yaml new file mode 100644 index 000000000..51ff94d4c --- /dev/null +++ b/.changes/unreleased/Feature-20230707-123609.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: '[export] Add a list for people with their associated course' +time: 2023-07-07T12:36:09.596469063+02:00 +custom: + Issue: "125" diff --git a/src/Bundle/ChillMainBundle/Export/ExportInterface.php b/src/Bundle/ChillMainBundle/Export/ExportInterface.php index f357a9fdb..a11a51746 100644 --- a/src/Bundle/ChillMainBundle/Export/ExportInterface.php +++ b/src/Bundle/ChillMainBundle/Export/ExportInterface.php @@ -97,7 +97,7 @@ interface ExportInterface extends ExportElementInterface * @param mixed[] $values The values from the result. if there are duplicates, those might be given twice. Example: array('FR', 'BE', 'CZ', 'FR', 'BE', 'FR') * @param mixed $data The data from the export's form (as defined in `buildForm`) * - * @return callable(null|string|int|float|'_header' $value): string|int|\DateTimeInterface where the first argument is the value, and the function should return the label to show in the formatted file. Example : `function($countryCode) use ($countries) { return $countries[$countryCode]->getName(); }` + * @return (callable(null|string|int|float|'_header' $value): string|int|\DateTimeInterface) where the first argument is the value, and the function should return the label to show in the formatted file. Example : `function($countryCode) use ($countries) { return $countries[$countryCode]->getName(); }` */ public function getLabels($key, array $values, $data); diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriodDetails.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriodDetails.php new file mode 100644 index 000000000..295d87842 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriodDetails.php @@ -0,0 +1,149 @@ +add('address_date', PickRollingDateType::class, [ + 'label' => 'Data valid at this date', + 'help' => 'Data regarding center, addresses, and so on will be computed at this date', + ]); + } + public function getFormDefaultData(): array + { + return ['address_date' => new RollingDate(RollingDate::T_TODAY)]; + } + + public function getAllowedFormattersTypes() + { + return [FormatterInterface::TYPE_LIST]; + } + + public function getDescription() + { + return 'export.list.person_with_acp.Create a list of people having an accompaying periods with details of period, according to various filters.'; + } + + public function getGroup(): string + { + return 'Exports of persons'; + } + + public function getLabels($key, array $values, $data) + { + if (in_array($key, $this->listPersonHelper->getAllKeys(), true)) { + return $this->listPersonHelper->getLabels($key, $values, $data); + } + + return $this->listAccompanyingPeriodHelper->getLabels($key, $values, $data); + } + + public function getQueryKeys($data) + { + return array_merge( + $this->listPersonHelper->getAllKeys(), + $this->listAccompanyingPeriodHelper->getQueryKeys($data), + ); + } + + public function getResult($query, $data) + { + return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR); + } + + public function getTitle() + { + return 'export.list.person_with_acp.List peoples having an accompanying period with period details'; + } + + public function getType() + { + return Declarations::PERSON_TYPE; + } + + /** + * param array{fields: string[], address_date: DateTimeImmutable} $data. + */ + public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) + { + $centers = array_map(static fn ($el) => $el['center'], $acl); + + $qb = $this->entityManager->createQueryBuilder(); + + $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)' + ) + )->setParameter('authorized_centers', $centers); + + $this->listPersonHelper->addSelect($qb, ListPersonHelper::FIELDS, $this->rollingDateConverter->convert($data['address_date'])); + $this->listAccompanyingPeriodHelper->addSelectClauses($qb, $this->rollingDateConverter->convert($data['address_date'])); + + return $qb; + } + + public function requiredRole(): string + { + return PersonVoter::LISTS; + } + + public function supportsModifiers() + { + return [Declarations::PERSON_TYPE, Declarations::PERSON_IMPLIED_IN, Declarations::ACP_TYPE]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php index a32d78904..5fa2252cd 100644 --- a/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php @@ -58,10 +58,10 @@ final readonly class ListAccompanyingPeriodHelper 'requestorThirdPartyId', 'scopes', 'socialIssues', - 'createdAt', - 'createdBy', - 'updatedAt', - 'updatedBy', + 'acpCreatedAt', + 'acpCreatedBy', + 'acpUpdatedAt', + 'acpUpdatedBy', ]; public function __construct( @@ -82,14 +82,14 @@ final readonly class ListAccompanyingPeriodHelper { return array_merge( ListAccompanyingPeriodHelper::FIELDS, - $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields') + $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'acp_address_fields') ); } 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'); + if (str_starts_with($key, 'acp_address_fields')) { + return $this->addressHelper->getLabel($key, $values, $data, 'acp_address_fields'); } switch ($key) { @@ -97,8 +97,8 @@ final readonly class ListAccompanyingPeriodHelper case 'openingDate': case 'closingDate': case 'referrerSince': - case 'createdAt': - case 'updatedAt': + case 'acpCreatedAt': + case 'acpUpdatedAt': return $this->dateTimeHelper->getLabel('export.list.acp.' . $key); case 'origin': @@ -220,14 +220,23 @@ final readonly class ListAccompanyingPeriodHelper public function addSelectClauses(QueryBuilder $qb, \DateTimeImmutable $calcDate): void { $qb->addSelect('acp.id AS acpId'); + $qb->addSelect('acp.createdAt AS acpCreatedAt'); + $qb->addSelect('acp.updatedAt AS acpUpdatedAt'); // add the regular fields - foreach (['openingDate', 'closingDate', 'confidential', 'emergency', 'intensity', 'createdAt', 'updatedAt'] as $field) { + foreach (['openingDate', 'closingDate', 'confidential', 'emergency', 'intensity'] 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('acp.createdBy', "acp_created_by_t") + ->addSelect('acp_created_by_t.label AS acpCreatedBy'); + $qb + ->leftJoin('acp.updatedBy', "acp_updated_by_t") + ->addSelect('acp_updated_by_t.label AS acpUpdatedBy'); + + foreach (['origin' => 'label', 'closingMotive' => 'name', 'job' => 'label', 'administrativeLocation' => 'name'] as $entity => $field) { $qb ->leftJoin(sprintf('acp.%s', $entity), "{$entity}_t") ->addSelect(sprintf('%s_t.%s AS %s', $entity, $field, $entity)); @@ -279,13 +288,13 @@ final readonly class ListAccompanyingPeriodHelper ) ->leftJoin( PersonHouseholdAddress::class, - 'personAddress', + 'acpPersonAddress', Join::WITH, - 'locationHistory.personLocation = personAddress.person AND (personAddress.validFrom <= :calcDate AND (personAddress.validTo IS NULL OR personAddress.validTo > :calcDate))' + 'locationHistory.personLocation = acpPersonAddress.person AND (acpPersonAddress.validFrom <= :calcDate AND (acpPersonAddress.validTo IS NULL OR acpPersonAddress.validTo > :calcDate))' ) - ->leftJoin(Address::class, 'acp_address', Join::WITH, 'COALESCE(IDENTITY(locationHistory.addressLocation), IDENTITY(personAddress.address)) = acp_address.id'); + ->leftJoin(Address::class, 'acp_address', Join::WITH, 'COALESCE(IDENTITY(locationHistory.addressLocation), IDENTITY(acpPersonAddress.address)) = acp_address.id'); - $this->addressHelper->addSelectClauses(ExportAddressHelper::F_ALL, $qb, 'acp_address', 'address_fields'); + $this->addressHelper->addSelectClauses(ExportAddressHelper::F_ALL, $qb, 'acp_address', 'acp_address_fields'); // requestor $qb diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php index 697019ca3..198794326 100644 --- a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Helper; +use Chill\MainBundle\Entity\Language; use Chill\MainBundle\Export\Helper\ExportAddressHelper; use Chill\MainBundle\Repository\CenterRepositoryInterface; use Chill\MainBundle\Repository\CivilityRepositoryInterface; @@ -114,7 +115,26 @@ class ListPersonHelper } /** - * @param array|value-of[] $fields + * Those keys are the "direct" keys, which are created when we decide to use to list all the keys. + * + * This method must be used in `getKeys` instead of the `self::FIELDS` + * + * @return array + */ + public function getAllKeys(): array + { + return [ + ...array_filter( + ListPersonHelper::FIELDS, + fn (string $key) => !in_array($key, ['address_fields', 'lifecycleUpdate'], true) + ), + ...$this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields'), + ...['createdAt', 'createdBy', 'updatedAt', 'updatedBy'], + ]; + } + + /** + * @param array> $fields */ public function addSelect(QueryBuilder $qb, array $fields, DateTimeImmutable $computedDate): void { @@ -143,25 +163,7 @@ class ListPersonHelper break; case 'spokenLanguages': - $qb - ->leftJoin('person.spokenLanguages', 'spokenLanguage') - ->addSelect('AGGREGATE(spokenLanguage.id) AS spokenLanguages') - ->addGroupBy('person'); - - if (in_array('center', $fields, true)) { - $qb->addGroupBy('center'); - } - - if (in_array('address_fields', $fields, true)) { - $qb - ->addGroupBy('address_fieldsid') - ->addGroupBy('address_fieldscountry_t.id') - ->addGroupBy('address_fieldspostcode_t.id'); - } - - if (in_array('household_id', $fields, true)) { - $qb->addGroupBy('household_id'); - } + $qb->addSelect('(SELECT AGGREGATE(language.id) FROM ' . Language::class . ' language WHERE language MEMBER OF person.spokenLanguages) AS spokenLanguages'); break; diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml index 81875fafb..43f5556cf 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml @@ -4,35 +4,27 @@ services: autowire: true ## Indicators - chill.person.export.count_person: - class: Chill\PersonBundle\Export\Export\CountPerson - autowire: true - autoconfigure: true + Chill\PersonBundle\Export\Export\CountPerson: tags: - { name: chill.export, alias: count_person } - chill.person.export.count_person_with_accompanying_course: - class: Chill\PersonBundle\Export\Export\CountPersonWithAccompanyingCourse - autowire: true - autoconfigure: true + Chill\PersonBundle\Export\Export\CountPersonWithAccompanyingCourse: tags: - { name: chill.export, alias: count_person_with_accompanying_course } Chill\PersonBundle\Export\Export\ListPerson: - autowire: true - autoconfigure: true tags: - { name: chill.export, alias: list_person } Chill\PersonBundle\Export\Export\ListPersonHavingAccompanyingPeriod: - autowire: true - autoconfigure: true tags: - { name: chill.export, alias: list_person_with_acp } + Chill\PersonBundle\Export\Export\ListPersonWithAccompanyingPeriodDetails: + tags: + - { name: chill.export, alias: list_person_with_acp_details } + Chill\PersonBundle\Export\Export\ListAccompanyingPeriod: - autowire: true - autoconfigure: true tags: - { name: chill.export, alias: list_acp } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 0a82fc330..1159b2c49 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -1148,7 +1148,9 @@ export: list: person_with_acp: List peoples having an accompanying period: Liste des usagers ayant un parcours d'accompagnement + List peoples having an accompanying period with period details: Liste des usagers concernés avec détail de chaque parcours Create a list of people having an accompaying periods, according to various filters.: Génère une liste des usagers ayant un parcours d'accompagnement, selon différents critères liés au parcours ou à l'usager + Create a list of people having an accompaying periods with details of period, according to various filters.: Génère une liste des usagers ayant un parcours d'accompagnement, selon différents critères liés au parcours ou à l'usager. Ajoute les détails du parcours à la liste. acp: List of accompanying periods: Liste des parcours d'accompagnements Generate a list of accompanying periods, filtered on different parameters.: Génère une liste des parcours d'accompagnement, filtrée sur différents paramètres. @@ -1162,14 +1164,14 @@ export: confidential: Confidentiel emergency: Urgent intensity: Intensité - createdAt: Créé le - updatedAt: Dernière mise à jour le + acpCreatedAt: Créé le + acpUpdatedAt: Dernière mise à jour le acpOrigin: Origine du parcours origin: Origine du parcours acpClosingMotive: Motif de fermeture acpJob: Métier du parcours - createdBy: Créé par - updatedBy: Dernière modification par + acpCreatedBy: Créé par + acpUpdatedBy: Dernière modification par administrativeLocation: Location administrative step: Etape stepSince: Dernière modification de l'étape From 63f9bd554897e1363d0b321c9e80c28e2b7745bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 7 Jul 2023 12:42:32 +0200 Subject: [PATCH 08/25] [export] Add ordering by person''s lastname or course opening date in list which concerns accompanying course or people --- .changes/unreleased/Feature-20230707-124132.yaml | 6 ++++++ .../Export/Export/ListAccompanyingPeriod.php | 5 +++++ .../Export/Export/ListPersonHavingAccompanyingPeriod.php | 5 +++++ .../Export/ListPersonWithAccompanyingPeriodDetails.php | 6 ++++++ 4 files changed, 22 insertions(+) create mode 100644 .changes/unreleased/Feature-20230707-124132.yaml diff --git a/.changes/unreleased/Feature-20230707-124132.yaml b/.changes/unreleased/Feature-20230707-124132.yaml new file mode 100644 index 000000000..4ad93ad22 --- /dev/null +++ b/.changes/unreleased/Feature-20230707-124132.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: '[export] Add ordering by person''s lastname or course opening date in list + which concerns accompanying course or peoples' +time: 2023-07-07T12:41:32.112725962+02:00 +custom: + Issue: "" diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php index 3f7821bbc..ab9c0db2f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php @@ -133,6 +133,11 @@ final readonly class ListAccompanyingPeriod implements ListInterface, GroupedExp $this->listAccompanyingPeriodHelper->addSelectClauses($qb, $this->rollingDateConverter->convert($data['calc_date'])); + $qb + ->addOrderBy('acp.openingDate') + ->addOrderBy('acp.closingDate') + ->addOrderBy('acp.id'); + return $qb; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonHavingAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonHavingAccompanyingPeriod.php index f2e4de4e3..408d0b3af 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonHavingAccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonHavingAccompanyingPeriod.php @@ -190,6 +190,11 @@ class ListPersonHavingAccompanyingPeriod implements ExportElementValidatedInterf $this->listPersonHelper->addSelect($qb, $fields, $data['address_date']); + $qb + ->addOrderBy('person.lastName') + ->addOrderBy('person.firstName') + ->addOrderBy('person.id'); + return $qb; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriodDetails.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriodDetails.php index 295d87842..ddb16bb2d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriodDetails.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriodDetails.php @@ -134,6 +134,12 @@ final readonly class ListPersonWithAccompanyingPeriodDetails implements ListInte $this->listPersonHelper->addSelect($qb, ListPersonHelper::FIELDS, $this->rollingDateConverter->convert($data['address_date'])); $this->listAccompanyingPeriodHelper->addSelectClauses($qb, $this->rollingDateConverter->convert($data['address_date'])); + $qb + ->addOrderBy('person.lastName') + ->addOrderBy('person.firstName') + ->addOrderBy('person.id') + ->addOrderBy('acp.id'); + return $qb; } From 20e64e87680d102c686000f1c35d0139d00eecdf Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Fri, 7 Jul 2023 15:41:29 +0200 Subject: [PATCH 09/25] test filterOrder in an accordion --- .../views/FilterOrder/base.html.twig | 162 ++++++++++-------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig index b2673b60c..f642c82aa 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig @@ -1,92 +1,102 @@ {{ form_start(form) }} - {% set btnSubmit = 0 %} -
-
- {% if form.vars.has_search_box %} -
-
- {{ form_widget(form.q) }} - -
-
- {% endif %} -
- {% if form.dateRanges is defined %} - {% set btnSubmit = 1 %} - {% if form.dateRanges|length > 0 %} - {% for dateRangeName, _o in form.dateRanges %} -
- {% if form.dateRanges[dateRangeName].vars.label is not same as(false) %} - {{ form_label(form.dateRanges[dateRangeName])}} - {% else %} -
{{ 'activity_filter.By date'|trans }}
- {% endif %} -
-
- {{ 'chill_calendar.From'|trans }} - {{ form_widget(form.dateRanges[dateRangeName]['from']) }} - {{ 'chill_calendar.To'|trans }} - {{ form_widget(form.dateRanges[dateRangeName]['to']) }} -
+
+

+ +

+
+ {% set btnSubmit = 0 %} +
+
+ {% if form.vars.has_search_box %} +
+
+ {{ form_widget(form.q) }} +
- {% endfor %} + {% endif %} +
+ {% if form.dateRanges is defined %} + {% set btnSubmit = 1 %} + {% if form.dateRanges|length > 0 %} + {% for dateRangeName, _o in form.dateRanges %} +
+ {% if form.dateRanges[dateRangeName].vars.label is not same as(false) %} + {{ form_label(form.dateRanges[dateRangeName])}} + {% else %} +
{{ 'activity_filter.By date'|trans }}
+ {% endif %} +
+
+ {{ 'chill_calendar.From'|trans }} + {{ form_widget(form.dateRanges[dateRangeName]['from']) }} + {{ 'chill_calendar.To'|trans }} + {{ form_widget(form.dateRanges[dateRangeName]['to']) }} +
+
+
+ {% endfor %} + {% endif %} {% endif %} - {% endif %} - {% if form.checkboxes is defined %} - {% set btnSubmit = 1 %} - {% if form.checkboxes|length > 0 %} - {% for checkbox_name, options in form.checkboxes %} + {% if form.checkboxes is defined %} + {% set btnSubmit = 1 %} + {% if form.checkboxes|length > 0 %} + {% for checkbox_name, options in form.checkboxes %} +
+
{{ 'activity_filter.By'|trans }}
+
+ {% for c in form['checkboxes'][checkbox_name].children %} + {{ form_widget(c) }} + {{ form_label(c) }} + {% endfor %} +
+
+ {% endfor %} + {% endif %} + {% endif %} + {% if form.entity_choices is defined %} + {% set btnSubmit = 1 %} + {% if form.entity_choices |length > 0 %} + {% for checkbox_name, options in form.entity_choices %} +
+ {% if form.entity_choices[checkbox_name].vars.label is not same as(false) %} + {{ form_label(form.entity_choices[checkbox_name])}} + {% endif %} +
+ {% for c in form['entity_choices'][checkbox_name].children %} + {{ form_widget(c) }} + {{ form_label(c) }} + {% endfor %} +
+
+ {% endfor %} + {% endif %} + {% endif %} + {% if form.single_checkboxes is defined %} + {% set btnSubmit = 1 %} + {% for name, _o in form.single_checkboxes %}
{{ 'activity_filter.By'|trans }}
- {% for c in form['checkboxes'][checkbox_name].children %} - {{ form_widget(c) }} - {{ form_label(c) }} - {% endfor %} + {{ form_widget(form.single_checkboxes[name]) }}
{% endfor %} {% endif %} - {% endif %} - {% if form.entity_choices is defined %} - {% set btnSubmit = 1 %} - {% if form.entity_choices |length > 0 %} - {% for checkbox_name, options in form.entity_choices %} -
- {% if form.entity_choices[checkbox_name].vars.label is not same as(false) %} - {{ form_label(form.entity_choices[checkbox_name])}} - {% endif %} -
- {% for c in form['entity_choices'][checkbox_name].children %} - {{ form_widget(c) }} - {{ form_label(c) }} - {% endfor %} -
-
- {% endfor %} - {% endif %} - {% endif %} - {% if form.single_checkboxes is defined %} - {% set btnSubmit = 1 %} - {% for name, _o in form.single_checkboxes %} + + {% if btnSubmit == 1 %}
-
{{ 'activity_filter.By'|trans }}
-
- {{ form_widget(form.single_checkboxes[name]) }} -
+
- {% endfor %} - {% endif %} - - {% if btnSubmit == 1 %} -
- -
- {% endif %} + {% endif %} +
+
- {% for k,v in otherParameters %} - - {% endfor %} +{% for k,v in otherParameters %} + +{% endfor %} {{ form_end(form) }} + From 6bdb3e969538bc3cf0ad3458d5015d3ab180626e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 7 Jul 2023 21:49:36 +0200 Subject: [PATCH 10/25] fix typo which prevent to apply a filter on activity types --- .../ChillActivityBundle/Controller/ActivityController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php index 9b6e69bf0..1e911ff08 100644 --- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php +++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php @@ -259,7 +259,7 @@ final class ActivityController extends AbstractController $filterArgs = [ 'my_activities' => $filter->getSingleCheckboxData('my_activities'), - 'types' => $filter->hasEntityChoice('activity_type') ? $filter->getEntityChoiceData('activity_types') : [], + 'types' => $filter->hasEntityChoice('activity_types') ? $filter->getEntityChoiceData('activity_types') : [], 'jobs' => $filter->hasEntityChoice('jobs') ? $filter->getEntityChoiceData('jobs') : [], 'before' => $filter->getDateRangeData('activity_date')['to'], 'after' => $filter->getDateRangeData('activity_date')['from'], From 39896ea6e26e3cb2392830b2e18c9bb8f396cefe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 10 Jul 2023 15:26:54 +0200 Subject: [PATCH 11/25] [FilterOrder] add a method to get all the active filters --- .../Form/Type/Listing/FilterOrderType.php | 25 +++--- .../views/FilterOrder/base.html.twig | 13 ++- .../Templating/Listing/FilterOrderHelper.php | 79 ++++++++++++++++--- .../Listing/FilterOrderHelperBuilder.php | 10 ++- .../Listing/FilterOrderHelperFactory.php | 8 +- .../Listing/FilterOrderPositionEnum.php | 12 +++ .../translations/messages+intl-icu.fr.yaml | 5 ++ 7 files changed, 126 insertions(+), 26 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderPositionEnum.php diff --git a/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php b/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php index 1f373400c..d16ce3813 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php @@ -47,16 +47,7 @@ final class FilterOrderType extends \Symfony\Component\Form\AbstractType $checkboxesBuilder = $builder->create('checkboxes', null, ['compound' => true]); foreach ($helper->getCheckboxes() as $name => $c) { - $choices = array_combine( - array_map(static function ($c, $t) { - if (null !== $t) { - return $t; - } - - return $c; - }, $c['choices'], $c['trans']), - $c['choices'] - ); + $choices = self::buildCheckboxChoices($c['choices'], $c['trans']); $checkboxesBuilder->add($name, ChoiceType::class, [ 'choices' => $choices, @@ -125,6 +116,20 @@ final class FilterOrderType extends \Symfony\Component\Form\AbstractType } } + public static function buildCheckboxChoices(array $choices, array $trans = []): array + { + return array_combine( + array_map(static function ($c, $t) { + if (null !== $t) { + return $t; + } + + return $c; + }, $choices, $trans), + $choices + ); + } + public function buildView(FormView $view, FormInterface $form, array $options) { /** @var FilterOrderHelper $helper */ diff --git a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig index f642c82aa..4de7604cf 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig @@ -85,7 +85,7 @@
{% endfor %} {% endif %} - + {% if btnSubmit == 1 %}
@@ -93,6 +93,17 @@ {% endif %}
+ {% set active = helper.getActiveFilters() %} + {% if active|length > 0 %} +
+ {% for f in active %} + {% if f.label != '' %}{{ f.label|trans }} : {% endif %}{{ f.value }} + {% endfor %} +
+ {% endif %} +
+ +
{% for k,v in otherParameters %} diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php index c0ef1cd89..a038dfd71 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php @@ -13,16 +13,19 @@ namespace Chill\MainBundle\Templating\Listing; use Chill\MainBundle\Form\Type\Listing\FilterOrderType; use DateTimeImmutable; -use Symfony\Component\Form\Extension\Core\Type\FormType; -use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\PropertyAccess\PropertyAccessor; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\PropertyAccess\PropertyPathInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use function array_merge; use function count; -class FilterOrderHelper +final class FilterOrderHelper { private array $checkboxes = []; @@ -33,16 +36,12 @@ class FilterOrderHelper private array $dateRanges = []; - private FormFactoryInterface $formFactory; - public const FORM_NAME = 'f'; private array $formOptions = []; private string $formType = FilterOrderType::class; - private RequestStack $requestStack; - private ?array $searchBoxFields = null; private ?array $submitted = null; @@ -52,12 +51,13 @@ class FilterOrderHelper */ private array $entityChoices = []; + public function __construct( - FormFactoryInterface $formFactory, - RequestStack $requestStack + private readonly FormFactoryInterface $formFactory, + private readonly RequestStack $requestStack, + private readonly TranslatorInterface $translator, + private readonly PropertyAccessorInterface $propertyAccessor, ) { - $this->formFactory = $formFactory; - $this->requestStack = $requestStack; } public function addSingleCheckbox(string $name, string $label): self @@ -199,6 +199,63 @@ class FilterOrderHelper return $this; } + /** + * Return all the data required to display the active filters + * + * @return array + */ + public function getActiveFilters(): array + { + $result = []; + + if ($this->hasSearchBox() && '' !== $this->getQueryString()) { + $result[] = ['label' => '', 'value' => $this->getQueryString(), 'position' => FilterOrderPositionEnum::SearchBox->value, 'name' => 'q']; + } + + foreach ($this->dateRanges as $name => ['label' => $label]) { + $base = ['position' => FilterOrderPositionEnum::DateRange->value, 'name' => $name, 'label' => (string) $label]; + + if (null !== ($from = $this->getDateRangeData($name)['from'] ?? null)) { + $result[] = ['value' => $this->translator->trans('filter_order.by_date.From', ['from_date' => $from]), ...$base]; + } + if (null !== ($to = $this->getDateRangeData($name)['to'] ?? null)) { + $result[] = ['value' => $this->translator->trans('filter_order.by_date.To', ['to_date' => $to]), ...$base]; + } + } + + foreach ($this->checkboxes as $name => ['choices' => $choices, 'trans' => $trans, 'options' => $options]) { + foreach ($this->getCheckboxData($name) as $keyChoice) { + $result[] = ['value' => $choices['keyChoice'], 'label' => $options['label'], 'position' => FilterOrderPositionEnum::Checkboxes->value, 'name' => $name]; + } + } + + foreach ($this->entityChoices as $name => ['label' => $label, 'class' => $class, 'choices' => $choices, 'options' => $options]) { + foreach ($this->getEntityChoiceData($name) as $selected) { + if (is_callable($options['choice_label'])) { + $value = call_user_func($options['choice_label'], $selected); + } elseif ($options['choice_label'] instanceof PropertyPathInterface || is_string($options['choice_label'])) { + $value = $this->propertyAccessor->getValue($selected, $options['choice_label']); + } else { + if (!$selected instanceof \Stringable) { + throw new \UnexpectedValueException(sprintf("we are not able to transform the value of %s to a string. Implements \Stringable or add a 'choice_label' option to the filterFormBuilder", get_class($selected))); + } + + $value = (string) $selected; + } + + $result[] = ['value' => $value, 'label' => $label, 'position' => FilterOrderPositionEnum::EntityChoice->value, 'name' => $name]; + } + } + + foreach ($this->singleCheckbox as $name => ['label' => $label]) { + if (true === $this->getSingleCheckboxData($name)) { + $result[] = ['label' => '', 'value' => $this->translator->trans($label), 'position' => FilterOrderPositionEnum::SingleCheckbox->value, 'name' => $name]; + } + } + + return $result; + } + private function getDefaultData(): array { $r = [ diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperBuilder.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperBuilder.php index e176e27c6..dfa361f6e 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperBuilder.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperBuilder.php @@ -14,6 +14,8 @@ namespace Chill\MainBundle\Templating\Listing; use DateTimeImmutable; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class FilterOrderHelperBuilder { @@ -39,7 +41,9 @@ class FilterOrderHelperBuilder public function __construct( FormFactoryInterface $formFactory, - RequestStack $requestStack + RequestStack $requestStack, + private readonly TranslatorInterface $translator, + private readonly PropertyAccessorInterface $propertyAccessor, ) { $this->formFactory = $formFactory; $this->requestStack = $requestStack; @@ -87,7 +91,9 @@ class FilterOrderHelperBuilder { $helper = new FilterOrderHelper( $this->formFactory, - $this->requestStack + $this->requestStack, + $this->translator, + $this->propertyAccessor ); $helper->setSearchBox($this->searchBoxFields); diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperFactory.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperFactory.php index c88c71af5..3fbb2864d 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperFactory.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperFactory.php @@ -13,6 +13,8 @@ namespace Chill\MainBundle\Templating\Listing; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class FilterOrderHelperFactory implements FilterOrderHelperFactoryInterface { @@ -22,7 +24,9 @@ class FilterOrderHelperFactory implements FilterOrderHelperFactoryInterface public function __construct( FormFactoryInterface $formFactory, - RequestStack $requestStack + RequestStack $requestStack, + private readonly TranslatorInterface $translator, + private readonly PropertyAccessorInterface $propertyAccessor, ) { $this->formFactory = $formFactory; $this->requestStack = $requestStack; @@ -30,6 +34,6 @@ class FilterOrderHelperFactory implements FilterOrderHelperFactoryInterface public function create(string $context, ?array $options = []): FilterOrderHelperBuilder { - return new FilterOrderHelperBuilder($this->formFactory, $this->requestStack); + return new FilterOrderHelperBuilder($this->formFactory, $this->requestStack, $this->translator, $this->propertyAccessor); } } diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderPositionEnum.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderPositionEnum.php new file mode 100644 index 000000000..cda8119f5 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderPositionEnum.php @@ -0,0 +1,12 @@ + Date: Mon, 10 Jul 2023 15:39:00 +0200 Subject: [PATCH 12/25] [filterOrder] fix error in method getActiveFilters when dealing with entityChoice with incorrect number of translation --- .../Templating/Listing/FilterOrderHelper.php | 13 ++++++++----- .../Templating/Listing/FilterOrderPositionEnum.php | 9 +++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php index a038dfd71..701238208 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php @@ -84,9 +84,11 @@ final class FilterOrderHelper public function addCheckbox(string $name, array $choices, ?array $default = [], ?array $trans = [], array $options = []): self { - $missing = count($choices) - count($trans) - 1; + $missing = count($choices) - count($trans); + $this->checkboxes[$name] = [ - 'choices' => $choices, 'default' => $default, + 'choices' => $choices, + 'default' => $default, 'trans' => array_merge( $trans, 0 < $missing ? @@ -223,9 +225,10 @@ final class FilterOrderHelper } } - foreach ($this->checkboxes as $name => ['choices' => $choices, 'trans' => $trans, 'options' => $options]) { + foreach ($this->checkboxes as $name => ['choices' => $choices, 'trans' => $trans]) { + $translatedChoice = array_combine($choices, [...$trans]); foreach ($this->getCheckboxData($name) as $keyChoice) { - $result[] = ['value' => $choices['keyChoice'], 'label' => $options['label'], 'position' => FilterOrderPositionEnum::Checkboxes->value, 'name' => $name]; + $result[] = ['value' => $translatedChoice[$keyChoice], 'label' => '', 'position' => FilterOrderPositionEnum::Checkboxes->value, 'name' => $name]; } } @@ -237,7 +240,7 @@ final class FilterOrderHelper $value = $this->propertyAccessor->getValue($selected, $options['choice_label']); } else { if (!$selected instanceof \Stringable) { - throw new \UnexpectedValueException(sprintf("we are not able to transform the value of %s to a string. Implements \Stringable or add a 'choice_label' option to the filterFormBuilder", get_class($selected))); + throw new \UnexpectedValueException(sprintf("we are not able to transform the value of %s to a string. Implements \\Stringable or add a 'choice_label' option to the filterFormBuilder", get_class($selected))); } $value = (string) $selected; diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderPositionEnum.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderPositionEnum.php index cda8119f5..09e8d39aa 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderPositionEnum.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderPositionEnum.php @@ -1,5 +1,14 @@ Date: Mon, 10 Jul 2023 15:55:05 +0200 Subject: [PATCH 13/25] render active filters like pills --- .../Resources/views/FilterOrder/base.html.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig index 4de7604cf..7a05a2f80 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig @@ -95,9 +95,9 @@ {% set active = helper.getActiveFilters() %} {% if active|length > 0 %} -
+
{% for f in active %} - {% if f.label != '' %}{{ f.label|trans }} : {% endif %}{{ f.value }} + {% if f.label != '' %}{{ f.label|trans }} : {% endif %}{{ f.value }} {% endfor %}
{% endif %} From 0d365e16e58df1614b132fc4b1fa2dc655458e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 10 Jul 2023 15:59:17 +0200 Subject: [PATCH 14/25] add missing translations --- .../ChillMainBundle/Templating/Listing/FilterOrderHelper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php index 701238208..2b24ffa0d 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php @@ -228,7 +228,7 @@ final class FilterOrderHelper foreach ($this->checkboxes as $name => ['choices' => $choices, 'trans' => $trans]) { $translatedChoice = array_combine($choices, [...$trans]); foreach ($this->getCheckboxData($name) as $keyChoice) { - $result[] = ['value' => $translatedChoice[$keyChoice], 'label' => '', 'position' => FilterOrderPositionEnum::Checkboxes->value, 'name' => $name]; + $result[] = ['value' => $this->translator->trans($translatedChoice[$keyChoice]), 'label' => '', 'position' => FilterOrderPositionEnum::Checkboxes->value, 'name' => $name]; } } @@ -246,7 +246,7 @@ final class FilterOrderHelper $value = (string) $selected; } - $result[] = ['value' => $value, 'label' => $label, 'position' => FilterOrderPositionEnum::EntityChoice->value, 'name' => $name]; + $result[] = ['value' => $this->translator->trans($value), 'label' => $label, 'position' => FilterOrderPositionEnum::EntityChoice->value, 'name' => $name]; } } From bf93c1ddb21f2bcbb28d42094b4f0d7c4cc78ef5 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Tue, 11 Jul 2023 14:06:10 +0200 Subject: [PATCH 15/25] fix label color in active filters pills --- .../translations/messages.fr.yml | 3 --- .../Form/Type/Listing/FilterOrderType.php | 2 +- .../Resources/views/FilterOrder/base.html.twig | 16 ++++++++++++---- .../translations/messages+intl-icu.fr.yaml | 4 ++++ 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index 3099e99b0..c53a04f31 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -96,9 +96,6 @@ activity_filter: My activities: Mes échanges (où j'interviens) Types: Par type d'échange Jobs: Par métier impliqué - By: Filtrer par - Search: Chercher dans la liste - By date: Filtrer par date #timeline '%user% has done an %activity_type%': '%user% a effectué un échange de type "%activity_type%"' diff --git a/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php b/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php index d16ce3813..f22b6bfba 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php @@ -39,7 +39,7 @@ final class FilterOrderType extends \Symfony\Component\Form\AbstractType 'label' => false, 'required' => false, 'attr' => [ - 'placeholder' => 'activity_filter.Search', + 'placeholder' => 'filter_order.Search', ] ]); } diff --git a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig index 7a05a2f80..0dcc4ce3f 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig @@ -26,7 +26,7 @@ {% if form.dateRanges[dateRangeName].vars.label is not same as(false) %} {{ form_label(form.dateRanges[dateRangeName])}} {% else %} -
{{ 'activity_filter.By date'|trans }}
+
{{ 'filter_order.By date'|trans }}
{% endif %}
@@ -45,7 +45,7 @@ {% if form.checkboxes|length > 0 %} {% for checkbox_name, options in form.checkboxes %}
-
{{ 'activity_filter.By'|trans }}
+
{{ 'filter_order.By'|trans }}
{% for c in form['checkboxes'][checkbox_name].children %} {{ form_widget(c) }} @@ -78,7 +78,7 @@ {% set btnSubmit = 1 %} {% for name, _o in form.single_checkboxes %}
-
{{ 'activity_filter.By'|trans }}
+
{{ 'filter_order.By'|trans }}
{{ form_widget(form.single_checkboxes[name]) }}
@@ -97,7 +97,15 @@ {% if active|length > 0 %}
{% for f in active %} - {% if f.label != '' %}{{ f.label|trans }} : {% endif %}{{ f.value }} + + {%- if f.label != '' %} + {{ f.label|trans }} : + {% endif -%} + {%- if f.position == 'search_box' and f.value is not null %} + {{ 'filter_order.search_box'|trans ~ ' :' }} + {% endif -%} + {{ f.value}}{# + #} {% endfor %}
{% endif %} diff --git a/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml b/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml index e1dc89dc2..96b2edd98 100644 --- a/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml +++ b/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml @@ -59,3 +59,7 @@ filter_order: by_date: From: Depuis le {from_date, date, long} To: Jusqu'au {to_date, date, long} + By: Filtrer par + Search: Chercher dans la liste + By date: Filtrer par date + search_box: Filtrer par contenu From 88114e3ba69fbd97a27eb850600acc78e27f526a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 11 Jul 2023 14:17:02 +0200 Subject: [PATCH 16/25] Fixed: [filterOrder] refactor active filter helper to a dedicated class and fix loading of multiple entity choices --- .../views/FilterOrder/base.html.twig | 1 - .../FilterOrderGetActiveFilterHelper.php | 84 +++++++++++++++++++ .../Templating/Listing/FilterOrderHelper.php | 70 +--------------- .../Listing/FilterOrderHelperBuilder.php | 4 - .../Listing/FilterOrderHelperFactory.php | 4 +- .../Templating/Listing/Templating.php | 2 + 6 files changed, 91 insertions(+), 74 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderGetActiveFilterHelper.php diff --git a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig index 0dcc4ce3f..b517eb154 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig @@ -93,7 +93,6 @@ {% endif %}
- {% set active = helper.getActiveFilters() %} {% if active|length > 0 %}
{% for f in active %} diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderGetActiveFilterHelper.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderGetActiveFilterHelper.php new file mode 100644 index 000000000..6b204e552 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderGetActiveFilterHelper.php @@ -0,0 +1,84 @@ + + */ + public function getActiveFilters(FilterOrderHelper $filterOrderHelper): array + { + $result = []; + + if ($filterOrderHelper->hasSearchBox() && '' !== $filterOrderHelper->getQueryString()) { + $result[] = ['label' => '', 'value' => $filterOrderHelper->getQueryString(), 'position' => FilterOrderPositionEnum::SearchBox->value, 'name' => 'q']; + } + + foreach ($filterOrderHelper->getDateRanges() as $name => ['label' => $label]) { + $base = ['position' => FilterOrderPositionEnum::DateRange->value, 'name' => $name, 'label' => (string)$label]; + + if (null !== ($from = $filterOrderHelper->getDateRangeData($name)['from'] ?? null)) { + $result[] = ['value' => $this->translator->trans('filter_order.by_date.From', ['from_date' => $from]), ...$base]; + } + if (null !== ($to = $filterOrderHelper->getDateRangeData($name)['to'] ?? null)) { + $result[] = ['value' => $this->translator->trans('filter_order.by_date.To', ['to_date' => $to]), ...$base]; + } + } + + foreach ($filterOrderHelper->getCheckboxes() as $name => ['choices' => $choices, 'trans' => $trans]) { + $translatedChoice = array_combine($choices, [...$trans]); + foreach ($filterOrderHelper->getCheckboxData($name) as $keyChoice) { + $result[] = ['value' => $this->translator->trans($translatedChoice[$keyChoice]), 'label' => '', 'position' => FilterOrderPositionEnum::Checkboxes->value, 'name' => $name]; + } + } + + foreach ($filterOrderHelper->getEntityChoices() as $name => ['label' => $label, 'class' => $class, 'choices' => $choices, 'options' => $options]) { + foreach ($filterOrderHelper->getEntityChoiceData($name) as $selected) { + if (is_callable($options['choice_label'])) { + $value = call_user_func($options['choice_label'], $selected); + } elseif ($options['choice_label'] instanceof PropertyPathInterface || is_string($options['choice_label'])) { + $value = $this->propertyAccessor->getValue($selected, $options['choice_label']); + } else { + if (!$selected instanceof \Stringable) { + throw new \UnexpectedValueException(sprintf("we are not able to transform the value of %s to a string. Implements \\Stringable or add a 'choice_label' option to the filterFormBuilder", get_class($selected))); + } + + $value = (string)$selected; + } + + $result[] = ['value' => $this->translator->trans($value), 'label' => $label, 'position' => FilterOrderPositionEnum::EntityChoice->value, 'name' => $name]; + } + } + + foreach ($filterOrderHelper->getSingleCheckbox() as $name => ['label' => $label]) { + if (true === $filterOrderHelper->getSingleCheckboxData($name)) { + $result[] = ['label' => '', 'value' => $this->translator->trans($label), 'position' => FilterOrderPositionEnum::SingleCheckbox->value, 'name' => $name]; + } + } + + return $result; + } +} diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php index 2b24ffa0d..84939a052 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php @@ -55,8 +55,6 @@ final class FilterOrderHelper public function __construct( private readonly FormFactoryInterface $formFactory, private readonly RequestStack $requestStack, - private readonly TranslatorInterface $translator, - private readonly PropertyAccessorInterface $propertyAccessor, ) { } @@ -84,16 +82,14 @@ final class FilterOrderHelper public function addCheckbox(string $name, array $choices, ?array $default = [], ?array $trans = [], array $options = []): self { - $missing = count($choices) - count($trans); + if ([] === $trans) { + $trans = $choices; + } $this->checkboxes[$name] = [ 'choices' => $choices, 'default' => $default, - 'trans' => array_merge( - $trans, - 0 < $missing ? - array_fill(0, $missing, null) : [] - ), + 'trans' => $trans, ...$options, ]; @@ -201,64 +197,6 @@ final class FilterOrderHelper return $this; } - /** - * Return all the data required to display the active filters - * - * @return array - */ - public function getActiveFilters(): array - { - $result = []; - - if ($this->hasSearchBox() && '' !== $this->getQueryString()) { - $result[] = ['label' => '', 'value' => $this->getQueryString(), 'position' => FilterOrderPositionEnum::SearchBox->value, 'name' => 'q']; - } - - foreach ($this->dateRanges as $name => ['label' => $label]) { - $base = ['position' => FilterOrderPositionEnum::DateRange->value, 'name' => $name, 'label' => (string) $label]; - - if (null !== ($from = $this->getDateRangeData($name)['from'] ?? null)) { - $result[] = ['value' => $this->translator->trans('filter_order.by_date.From', ['from_date' => $from]), ...$base]; - } - if (null !== ($to = $this->getDateRangeData($name)['to'] ?? null)) { - $result[] = ['value' => $this->translator->trans('filter_order.by_date.To', ['to_date' => $to]), ...$base]; - } - } - - foreach ($this->checkboxes as $name => ['choices' => $choices, 'trans' => $trans]) { - $translatedChoice = array_combine($choices, [...$trans]); - foreach ($this->getCheckboxData($name) as $keyChoice) { - $result[] = ['value' => $this->translator->trans($translatedChoice[$keyChoice]), 'label' => '', 'position' => FilterOrderPositionEnum::Checkboxes->value, 'name' => $name]; - } - } - - foreach ($this->entityChoices as $name => ['label' => $label, 'class' => $class, 'choices' => $choices, 'options' => $options]) { - foreach ($this->getEntityChoiceData($name) as $selected) { - if (is_callable($options['choice_label'])) { - $value = call_user_func($options['choice_label'], $selected); - } elseif ($options['choice_label'] instanceof PropertyPathInterface || is_string($options['choice_label'])) { - $value = $this->propertyAccessor->getValue($selected, $options['choice_label']); - } else { - if (!$selected instanceof \Stringable) { - throw new \UnexpectedValueException(sprintf("we are not able to transform the value of %s to a string. Implements \\Stringable or add a 'choice_label' option to the filterFormBuilder", get_class($selected))); - } - - $value = (string) $selected; - } - - $result[] = ['value' => $this->translator->trans($value), 'label' => $label, 'position' => FilterOrderPositionEnum::EntityChoice->value, 'name' => $name]; - } - } - - foreach ($this->singleCheckbox as $name => ['label' => $label]) { - if (true === $this->getSingleCheckboxData($name)) { - $result[] = ['label' => '', 'value' => $this->translator->trans($label), 'position' => FilterOrderPositionEnum::SingleCheckbox->value, 'name' => $name]; - } - } - - return $result; - } - private function getDefaultData(): array { $r = [ diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperBuilder.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperBuilder.php index dfa361f6e..f2bded220 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperBuilder.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperBuilder.php @@ -42,8 +42,6 @@ class FilterOrderHelperBuilder public function __construct( FormFactoryInterface $formFactory, RequestStack $requestStack, - private readonly TranslatorInterface $translator, - private readonly PropertyAccessorInterface $propertyAccessor, ) { $this->formFactory = $formFactory; $this->requestStack = $requestStack; @@ -92,8 +90,6 @@ class FilterOrderHelperBuilder $helper = new FilterOrderHelper( $this->formFactory, $this->requestStack, - $this->translator, - $this->propertyAccessor ); $helper->setSearchBox($this->searchBoxFields); diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperFactory.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperFactory.php index 3fbb2864d..6665750dd 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperFactory.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperFactory.php @@ -25,8 +25,6 @@ class FilterOrderHelperFactory implements FilterOrderHelperFactoryInterface public function __construct( FormFactoryInterface $formFactory, RequestStack $requestStack, - private readonly TranslatorInterface $translator, - private readonly PropertyAccessorInterface $propertyAccessor, ) { $this->formFactory = $formFactory; $this->requestStack = $requestStack; @@ -34,6 +32,6 @@ class FilterOrderHelperFactory implements FilterOrderHelperFactoryInterface public function create(string $context, ?array $options = []): FilterOrderHelperBuilder { - return new FilterOrderHelperBuilder($this->formFactory, $this->requestStack, $this->translator, $this->propertyAccessor); + return new FilterOrderHelperBuilder($this->formFactory, $this->requestStack); } } diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/Templating.php b/src/Bundle/ChillMainBundle/Templating/Listing/Templating.php index b91cd86e8..2d32813cb 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/Templating.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/Templating.php @@ -24,6 +24,7 @@ class Templating extends AbstractExtension { public function __construct( private readonly RequestStack $requestStack, + private readonly FilterOrderGetActiveFilterHelper $filterOrderGetActiveFilterHelper, ) { } @@ -68,6 +69,7 @@ class Templating extends AbstractExtension return $environment->render($template, [ 'helper' => $helper, + 'active' => $this->filterOrderGetActiveFilterHelper->getActiveFilters($helper), 'form' => $helper->buildForm()->createView(), 'options' => $options, 'otherParameters' => $otherParameters, From 6065680e1e0bc71c19dd05ef158d301324f61805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 11 Jul 2023 15:01:32 +0200 Subject: [PATCH 17/25] Feature: [export] allow to group activities by location --- .../unreleased/Feature-20230711-150055.yaml | 5 ++ .../Aggregator/ActivityLocationAggregator.php | 80 +++++++++++++++++++ .../config/services/export.yaml | 4 + .../translations/messages.fr.yml | 3 + 4 files changed, 92 insertions(+) create mode 100644 .changes/unreleased/Feature-20230711-150055.yaml create mode 100644 src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityLocationAggregator.php diff --git a/.changes/unreleased/Feature-20230711-150055.yaml b/.changes/unreleased/Feature-20230711-150055.yaml new file mode 100644 index 000000000..ecee61b49 --- /dev/null +++ b/.changes/unreleased/Feature-20230711-150055.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: '[Export] allow to group activities by localisation' +time: 2023-07-11T15:00:55.770070399+02:00 +custom: + Issue: "128" diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityLocationAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityLocationAggregator.php new file mode 100644 index 000000000..9103943e4 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityLocationAggregator.php @@ -0,0 +1,80 @@ +getAllAliases(), true)) { + $qb->leftJoin('activity.location', 'actloc'); + } + $qb->addSelect(sprintf('actloc.name AS %s', self::KEY)); + $qb->addGroupBy(self::KEY); + } + + public function applyOn(): string + { + return Declarations::ACTIVITY; + } + + public function buildForm(FormBuilderInterface $builder) + { + // no form required for this aggregator + } + public function getFormDefaultData(): array + { + return []; + } + + public function getLabels($key, array $values, $data): Closure + { + return function ($value): string { + if ('_header' === $value) { + return 'export.aggregator.activity.by_location.Activity Location'; + } + + if (null === $value || '' === $value) { + return ''; + } + + return $value; + }; + } + + public function getQueryKeys($data): array + { + return [self::KEY]; + } + + public function getTitle() + { + return 'export.aggregator.activity.by_location.Title'; + } +} diff --git a/src/Bundle/ChillActivityBundle/config/services/export.yaml b/src/Bundle/ChillActivityBundle/config/services/export.yaml index 09817d80e..03285c416 100644 --- a/src/Bundle/ChillActivityBundle/config/services/export.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/export.yaml @@ -144,6 +144,10 @@ services: tags: - { name: chill.export_aggregator, alias: activity_common_type_aggregator } + Chill\ActivityBundle\Export\Aggregator\ActivityLocationAggregator: + tags: + - { name: chill.export_aggregator, alias: activity_common_location_aggregator } + chill.activity.export.user_aggregator: class: Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator tags: diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index abef160d3..037c24f3f 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -372,6 +372,9 @@ export: is sent: envoyé is received: reçu Group activity by sentreceived: Grouper les échanges par envoyé / reçu + by_location: + Activity Location: Localisation de l'échange + Title: Grouper les échanges par localisation de l'échange generic_doc: filter: From 2882038efcd4be2b65db67df7cb4f2498fcb0f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 11 Jul 2023 16:00:40 +0200 Subject: [PATCH 18/25] [export] Add a filter "filter course having an activity between two dates" --- .../unreleased/Feature-20230711-155929.yaml | 5 ++ ...PeriodHavingActivityBetweenDatesFilter.php | 90 +++++++++++++++++++ .../config/services/export.yaml | 4 + .../translations/messages+intl-icu.fr.yml | 5 ++ .../translations/messages.fr.yml | 6 ++ 5 files changed, 110 insertions(+) create mode 100644 .changes/unreleased/Feature-20230711-155929.yaml create mode 100644 src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/PeriodHavingActivityBetweenDatesFilter.php create mode 100644 src/Bundle/ChillActivityBundle/translations/messages+intl-icu.fr.yml diff --git a/.changes/unreleased/Feature-20230711-155929.yaml b/.changes/unreleased/Feature-20230711-155929.yaml new file mode 100644 index 000000000..329bbb677 --- /dev/null +++ b/.changes/unreleased/Feature-20230711-155929.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: '[export] Add a filter "filter course having an activity between two dates"' +time: 2023-07-11T15:59:29.065329834+02:00 +custom: + Issue: "129" diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/PeriodHavingActivityBetweenDatesFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/PeriodHavingActivityBetweenDatesFilter.php new file mode 100644 index 000000000..27e012d0b --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/PeriodHavingActivityBetweenDatesFilter.php @@ -0,0 +1,90 @@ +add('start_date', PickRollingDateType::class, [ + 'label' => 'export.filter.activity.course_having_activity_between_date.Receiving an activity after' + ]) + ->add('end_date', PickRollingDateType::class, [ + 'label' => 'export.filter.activity.course_having_activity_between_date.Receiving an activity before' + ]); + } + + public function getFormDefaultData(): array + { + return [ + 'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START), + 'end_date' => new RollingDate(RollingDate::T_TODAY) + ]; + } + + public function describeAction($data, $format = 'string') + { + return [ + 'export.filter.activity.course_having_activity_between_date.Only course having an activity between from and to', + [ + 'from' => $this->rollingDateConverter->convert($data['start_date']), + 'to' => $this->rollingDateConverter->convert($data['end_date']), + ] + ]; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $alias = 'act_period_having_act_betw_date_alias'; + $from = 'act_period_having_act_betw_date_start'; + $to = 'act_period_having_act_betw_date_end'; + + $qb->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . Activity::class . " {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = acp" + ) + ); + + $qb + ->setParameter($from, $this->rollingDateConverter->convert($data['start_date'])) + ->setParameter($to, $this->rollingDateConverter->convert($data['end_date'])); + } + + public function applyOn() + { + return \Chill\PersonBundle\Export\Declarations::ACP_TYPE; + } +} diff --git a/src/Bundle/ChillActivityBundle/config/services/export.yaml b/src/Bundle/ChillActivityBundle/config/services/export.yaml index 09817d80e..932985083 100644 --- a/src/Bundle/ChillActivityBundle/config/services/export.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/export.yaml @@ -135,6 +135,10 @@ services: tags: - { name: chill.export_filter, alias: 'accompanyingcourse_has_no_activity_filter' } + Chill\ActivityBundle\Export\Filter\ACPFilters\PeriodHavingActivityBetweenDatesFilter: + tags: + - { name: chill.export_filter, alias: 'period_having_activity_betw_dates_filter' } + ## Aggregators Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator: tags: diff --git a/src/Bundle/ChillActivityBundle/translations/messages+intl-icu.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages+intl-icu.fr.yml new file mode 100644 index 000000000..ab3b963ab --- /dev/null +++ b/src/Bundle/ChillActivityBundle/translations/messages+intl-icu.fr.yml @@ -0,0 +1,5 @@ +export: + filter: + activity: + course_having_activity_between_date: + Only course having an activity between from and to: Seulement les parcours ayant reçu au moins un échange entre le {from, date, short} et le {to, date, short} diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index abef160d3..551a63d27 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -365,6 +365,12 @@ export: by_usersscope: Filter by users scope: Filtrer les échanges par services d'au moins un utilisateur participant 'Filtered activity by users scope: only %scopes%': 'Filtré par service d''au moins un utilisateur participant: seulement %scopes%' + course_having_activity_between_date: + Title: Filtre les parcours ayant reçu un échange entre deux dates + Receiving an activity after: Ayant reçu un échange après le + Receiving an activity before: Ayant reçu un échange avant le + + aggregator: activity: by_sent_received: From edd66f6a6cf3622dd82a8535a9f4a6272cc9bb1d Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 12 Jul 2023 09:04:15 +0200 Subject: [PATCH 19/25] FIX [budget][templates] reimplement display of all calculator results --- .../Resources/views/Budget/_macros.html.twig | 33 ++++++++++++++----- .../Resources/views/Person/index.html.twig | 2 +- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/Bundle/ChillBudgetBundle/Resources/views/Budget/_macros.html.twig b/src/Bundle/ChillBudgetBundle/Resources/views/Budget/_macros.html.twig index dfa286af4..a1fee19ce 100644 --- a/src/Bundle/ChillBudgetBundle/Resources/views/Budget/_macros.html.twig +++ b/src/Bundle/ChillBudgetBundle/Resources/views/Budget/_macros.html.twig @@ -1,11 +1,12 @@ -{% macro table_elements(elements, family) %} +{% macro table_elements(elements, type) %} + - - - - + + + + @@ -38,17 +39,17 @@
    {% if is_granted('CHILL_BUDGET_ELEMENT_SEE', f) %}
  • - +
  • {% endif %} {% if is_granted('CHILL_BUDGET_ELEMENT_UPDATE', f) %}
  • - +
  • {% endif %} {% if is_granted('CHILL_BUDGET_ELEMENT_DELETE', f) %}
  • - +
  • {% endif %}
@@ -69,7 +70,7 @@
{{ 'Budget element type'|trans }}{{ 'Amount'|trans }}{{ 'Validity period'|trans }} {{ 'Budget element type'|trans }}{{ 'Amount'|trans }}{{ 'Validity period'|trans }} 
{% endmacro %} -{% macro table_results(actualCharges, actualResources) %} +{% macro table_results(actualCharges, actualResources, results) %} {% set totalCharges = 0 %} {% for c in actualCharges %} @@ -97,6 +98,20 @@ {{ result|format_currency('EUR') }} + {% for result in results %} + + {{ result.label }} + + {% if result.type == 'currency' %} + {{ result.result|format_currency('EUR') }} + {% elseif result.type == 'percentage' %} + {{ result.result|round(2, 'ceil') ~ '%' }} + {% else %} + {{ result.result|round(2, 'common') }} + {% endif %} + + + {% endfor %} {% endmacro %} diff --git a/src/Bundle/ChillBudgetBundle/Resources/views/Person/index.html.twig b/src/Bundle/ChillBudgetBundle/Resources/views/Person/index.html.twig index 18d04b889..aba564206 100644 --- a/src/Bundle/ChillBudgetBundle/Resources/views/Person/index.html.twig +++ b/src/Bundle/ChillBudgetBundle/Resources/views/Person/index.html.twig @@ -25,7 +25,7 @@

{{ 'Budget calculator'|trans }}

- {{ table_results(charges, resources) }} + {{ table_results(charges, resources, results) }}
{% if is_granted('CHILL_BUDGET_ELEMENT_CREATE', person) %} From f7d385eba1877756c34ed64857d0e847aedccf9d Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 12 Jul 2023 09:06:08 +0200 Subject: [PATCH 20/25] DX add changie --- .changes/unreleased/Fixed-20230712-090514.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changes/unreleased/Fixed-20230712-090514.yaml diff --git a/.changes/unreleased/Fixed-20230712-090514.yaml b/.changes/unreleased/Fixed-20230712-090514.yaml new file mode 100644 index 000000000..51a8b9317 --- /dev/null +++ b/.changes/unreleased/Fixed-20230712-090514.yaml @@ -0,0 +1,5 @@ +kind: Fixed +body: reimplement the visualization of all calculator results (specific to AMLI) +time: 2023-07-12T09:05:14.416268226+02:00 +custom: + Issue: "" From a2e705bd92e085007fb0313b163f194497bcb5bf Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 12 Jul 2023 10:38:11 +0200 Subject: [PATCH 21/25] fixed: error with parent joins in thirdparty api search query --- .../Controller/resquery.bad.sql | 21 +++++++++++++++++++ .../Controller/resquery.fixed.sql | 21 +++++++++++++++++++ .../Search/ThirdPartyApiSearch.php | 2 +- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillMainBundle/Controller/resquery.bad.sql create mode 100644 src/Bundle/ChillMainBundle/Controller/resquery.fixed.sql diff --git a/src/Bundle/ChillMainBundle/Controller/resquery.bad.sql b/src/Bundle/ChillMainBundle/Controller/resquery.bad.sql new file mode 100644 index 000000000..1033ec28c --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/resquery.bad.sql @@ -0,0 +1,21 @@ +SELECT +'tparty' AS key, jsonb_build_object('id', tparty.id) AS metadata, GREATEST( +STRICT_WORD_SIMILARITY(LOWER(UNACCENT('areams')), tparty.canonicalized), +STRICT_WORD_SIMILARITY(LOWER(UNACCENT('areams')), parent.canonicalized) +) + GREATEST( +(tparty.canonicalized LIKE '%s' || LOWER(UNACCENT('areams')) || '%')::int, +(parent.canonicalized LIKE '%s' || LOWER(UNACCENT('areams')) || '%')::int +) + COALESCE((LOWER(UNACCENT(cmpc.label)) LIKE '%' || LOWER(UNACCENT('areams')) || '%')::int * 0.3, 0) + COALESCE((LOWER(UNACCENT(cmpc_p.label)) LIKE '%' || LOWER(UNACCENT('areams')) || '%')::int * 0.3, 0) + 1 AS pertinence +FROM chill_3party.third_party AS tparty +LEFT JOIN chill_main_address cma ON cma.id = tparty.address_id +LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id +LEFT JOIN chill_3party.third_party AS parent ON tparty.parent_id = parent.id +LEFT JOIN chill_main_address cma_p ON parent.address_id = cma_p.id +LEFT JOIN chill_main_postal_code cmpc_p ON cma_p.postcode_id = cmpc.id +WHERE (tparty.active IS TRUE) AND (((LOWER(UNACCENT('areams')) <<% tparty.canonicalized OR +tparty.canonicalized LIKE '%' || LOWER(UNACCENT('areams')) || '%') +OR +(LOWER(UNACCENT('areams')) <<% parent.canonicalized OR +parent.canonicalized LIKE '%' || LOWER(UNACCENT('areams')) || '%')) +AND tparty.active IS TRUE and (parent.active IS TRUE OR parent IS NULL)) +ORDER BY pertinence DESC LIMIT 50 OFFSET 0; diff --git a/src/Bundle/ChillMainBundle/Controller/resquery.fixed.sql b/src/Bundle/ChillMainBundle/Controller/resquery.fixed.sql new file mode 100644 index 000000000..dbb55f187 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/resquery.fixed.sql @@ -0,0 +1,21 @@ +SELECT +'tparty' AS key, jsonb_build_object('id', tparty.id) AS metadata, GREATEST( +STRICT_WORD_SIMILARITY(LOWER(UNACCENT('areams')), tparty.canonicalized), +STRICT_WORD_SIMILARITY(LOWER(UNACCENT('areams')), parent.canonicalized) +) + GREATEST( +(tparty.canonicalized LIKE '%s' || LOWER(UNACCENT('areams')) || '%')::int, +(parent.canonicalized LIKE '%s' || LOWER(UNACCENT('areams')) || '%')::int +) + COALESCE((LOWER(UNACCENT(cmpc.label)) LIKE '%' || LOWER(UNACCENT('areams')) || '%')::int * 0.3, 0) + COALESCE((LOWER(UNACCENT(cmpc_p.label)) LIKE '%' || LOWER(UNACCENT('areams')) || '%')::int * 0.3, 0) + 1 AS pertinence +FROM chill_3party.third_party AS tparty +LEFT JOIN chill_main_address cma ON cma.id = tparty.address_id +LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id +LEFT JOIN chill_3party.third_party AS parent ON tparty.parent_id = parent.id +LEFT JOIN chill_main_address cma_p ON parent.address_id = cma_p.id +LEFT JOIN chill_main_postal_code cmpc_p ON cma_p.postcode_id = cmpc_p.id +WHERE (tparty.active IS TRUE) AND (((LOWER(UNACCENT('areams')) <<% tparty.canonicalized OR +tparty.canonicalized LIKE '%' || LOWER(UNACCENT('areams')) || '%') +OR +(LOWER(UNACCENT('areams')) <<% parent.canonicalized OR +parent.canonicalized LIKE '%' || LOWER(UNACCENT('areams')) || '%')) +AND tparty.active IS TRUE and (parent.active IS TRUE OR parent IS NULL)) +ORDER BY pertinence DESC LIMIT 50 OFFSET 0; diff --git a/src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php b/src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php index 86c0fa9db..42b98622f 100644 --- a/src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php +++ b/src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php @@ -75,7 +75,7 @@ class ThirdPartyApiSearch implements SearchApiInterface LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id LEFT JOIN chill_3party.third_party AS parent ON tparty.parent_id = parent.id LEFT JOIN chill_main_address cma_p ON parent.address_id = cma_p.id - LEFT JOIN chill_main_postal_code cmpc_p ON cma_p.postcode_id = cmpc.id') + LEFT JOIN chill_main_postal_code cmpc_p ON cma_p.postcode_id = cmpc_p.id') ->andWhereClause('tparty.active IS TRUE'); $strs = explode(' ', $pattern); From f3829d3390ae7e22939f02a6bb71db305ebe060e Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 12 Jul 2023 10:50:17 +0200 Subject: [PATCH 22/25] adapt query to simplify join clauses (lightly improve perfs) --- .../Controller/resquery.good.sql | 19 +++++++++++++++++++ .../Search/ThirdPartyApiSearch.php | 10 +++------- 2 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Controller/resquery.good.sql diff --git a/src/Bundle/ChillMainBundle/Controller/resquery.good.sql b/src/Bundle/ChillMainBundle/Controller/resquery.good.sql new file mode 100644 index 000000000..5877b04f8 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/resquery.good.sql @@ -0,0 +1,19 @@ +SELECT +'tparty' AS key, jsonb_build_object('id', tparty.id) AS metadata, GREATEST( +STRICT_WORD_SIMILARITY(LOWER(UNACCENT('areams')), tparty.canonicalized), +STRICT_WORD_SIMILARITY(LOWER(UNACCENT('areams')), parent.canonicalized) +) + GREATEST( +(tparty.canonicalized LIKE '%s' || LOWER(UNACCENT('areams')) || '%')::int, +(parent.canonicalized LIKE '%s' || LOWER(UNACCENT('areams')) || '%')::int +) + COALESCE((LOWER(UNACCENT(cmpc.label)) LIKE '%' || LOWER(UNACCENT('areams')) || '%')::int * 0.3, 0) + 1 AS pertinence +FROM chill_3party.third_party AS tparty +LEFT JOIN chill_3party.third_party AS parent ON tparty.parent_id = parent.id +LEFT JOIN chill_main_address cma ON cma.id = COALESCE(parent.address_id, tparty.address_id) +LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id +WHERE (((LOWER(UNACCENT('areams')) <<% tparty.canonicalized OR +tparty.canonicalized LIKE '%' || LOWER(UNACCENT('areams')) || '%') +OR +(LOWER(UNACCENT('areams')) <<% parent.canonicalized OR +parent.canonicalized LIKE '%' || LOWER(UNACCENT('areams')) || '%')) +AND tparty.active IS TRUE and (parent.active IS TRUE OR parent IS NULL)) +ORDER BY pertinence DESC, tparty.id ASC LIMIT 500 OFFSET 0; diff --git a/src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php b/src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php index 42b98622f..bb5303143 100644 --- a/src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php +++ b/src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php @@ -71,12 +71,9 @@ class ThirdPartyApiSearch implements SearchApiInterface ->setSelectKey('tparty') ->setSelectJsonbMetadata("jsonb_build_object('id', tparty.id)") ->setFromClause('chill_3party.third_party AS tparty - LEFT JOIN chill_main_address cma ON cma.id = tparty.address_id - LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id LEFT JOIN chill_3party.third_party AS parent ON tparty.parent_id = parent.id - LEFT JOIN chill_main_address cma_p ON parent.address_id = cma_p.id - LEFT JOIN chill_main_postal_code cmpc_p ON cma_p.postcode_id = cmpc_p.id') - ->andWhereClause('tparty.active IS TRUE'); + LEFT JOIN chill_main_address cma ON cma.id = COALESCE(parent.address_id, tparty.address_id) + LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id'); $strs = explode(' ', $pattern); $wheres = []; @@ -102,8 +99,7 @@ class ThirdPartyApiSearch implements SearchApiInterface (parent.canonicalized LIKE '%s' || LOWER(UNACCENT(?)) || '%')::int ) + " . // take postcode label into account, but lower than the canonicalized field - "COALESCE((LOWER(UNACCENT(cmpc.label)) LIKE '%' || LOWER(UNACCENT(?)) || '%')::int * 0.3, 0) + " . - "COALESCE((LOWER(UNACCENT(cmpc_p.label)) LIKE '%' || LOWER(UNACCENT(?)) || '%')::int * 0.3, 0)"; + "COALESCE((LOWER(UNACCENT(cmpc.label)) LIKE '%' || LOWER(UNACCENT(?)) || '%')::int * 0.3, 0)"; $pertinenceArgs[] = [$str, $str, $str, $str, $str, $str]; } } From efee2d8b44369e6546d13afa19d58b8a43d086db Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 12 Jul 2023 10:53:12 +0200 Subject: [PATCH 23/25] cleaning --- .../Controller/resquery.bad.sql | 21 ------------------- .../Controller/resquery.fixed.sql | 21 ------------------- .../Controller/resquery.good.sql | 19 ----------------- 3 files changed, 61 deletions(-) delete mode 100644 src/Bundle/ChillMainBundle/Controller/resquery.bad.sql delete mode 100644 src/Bundle/ChillMainBundle/Controller/resquery.fixed.sql delete mode 100644 src/Bundle/ChillMainBundle/Controller/resquery.good.sql diff --git a/src/Bundle/ChillMainBundle/Controller/resquery.bad.sql b/src/Bundle/ChillMainBundle/Controller/resquery.bad.sql deleted file mode 100644 index 1033ec28c..000000000 --- a/src/Bundle/ChillMainBundle/Controller/resquery.bad.sql +++ /dev/null @@ -1,21 +0,0 @@ -SELECT -'tparty' AS key, jsonb_build_object('id', tparty.id) AS metadata, GREATEST( -STRICT_WORD_SIMILARITY(LOWER(UNACCENT('areams')), tparty.canonicalized), -STRICT_WORD_SIMILARITY(LOWER(UNACCENT('areams')), parent.canonicalized) -) + GREATEST( -(tparty.canonicalized LIKE '%s' || LOWER(UNACCENT('areams')) || '%')::int, -(parent.canonicalized LIKE '%s' || LOWER(UNACCENT('areams')) || '%')::int -) + COALESCE((LOWER(UNACCENT(cmpc.label)) LIKE '%' || LOWER(UNACCENT('areams')) || '%')::int * 0.3, 0) + COALESCE((LOWER(UNACCENT(cmpc_p.label)) LIKE '%' || LOWER(UNACCENT('areams')) || '%')::int * 0.3, 0) + 1 AS pertinence -FROM chill_3party.third_party AS tparty -LEFT JOIN chill_main_address cma ON cma.id = tparty.address_id -LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id -LEFT JOIN chill_3party.third_party AS parent ON tparty.parent_id = parent.id -LEFT JOIN chill_main_address cma_p ON parent.address_id = cma_p.id -LEFT JOIN chill_main_postal_code cmpc_p ON cma_p.postcode_id = cmpc.id -WHERE (tparty.active IS TRUE) AND (((LOWER(UNACCENT('areams')) <<% tparty.canonicalized OR -tparty.canonicalized LIKE '%' || LOWER(UNACCENT('areams')) || '%') -OR -(LOWER(UNACCENT('areams')) <<% parent.canonicalized OR -parent.canonicalized LIKE '%' || LOWER(UNACCENT('areams')) || '%')) -AND tparty.active IS TRUE and (parent.active IS TRUE OR parent IS NULL)) -ORDER BY pertinence DESC LIMIT 50 OFFSET 0; diff --git a/src/Bundle/ChillMainBundle/Controller/resquery.fixed.sql b/src/Bundle/ChillMainBundle/Controller/resquery.fixed.sql deleted file mode 100644 index dbb55f187..000000000 --- a/src/Bundle/ChillMainBundle/Controller/resquery.fixed.sql +++ /dev/null @@ -1,21 +0,0 @@ -SELECT -'tparty' AS key, jsonb_build_object('id', tparty.id) AS metadata, GREATEST( -STRICT_WORD_SIMILARITY(LOWER(UNACCENT('areams')), tparty.canonicalized), -STRICT_WORD_SIMILARITY(LOWER(UNACCENT('areams')), parent.canonicalized) -) + GREATEST( -(tparty.canonicalized LIKE '%s' || LOWER(UNACCENT('areams')) || '%')::int, -(parent.canonicalized LIKE '%s' || LOWER(UNACCENT('areams')) || '%')::int -) + COALESCE((LOWER(UNACCENT(cmpc.label)) LIKE '%' || LOWER(UNACCENT('areams')) || '%')::int * 0.3, 0) + COALESCE((LOWER(UNACCENT(cmpc_p.label)) LIKE '%' || LOWER(UNACCENT('areams')) || '%')::int * 0.3, 0) + 1 AS pertinence -FROM chill_3party.third_party AS tparty -LEFT JOIN chill_main_address cma ON cma.id = tparty.address_id -LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id -LEFT JOIN chill_3party.third_party AS parent ON tparty.parent_id = parent.id -LEFT JOIN chill_main_address cma_p ON parent.address_id = cma_p.id -LEFT JOIN chill_main_postal_code cmpc_p ON cma_p.postcode_id = cmpc_p.id -WHERE (tparty.active IS TRUE) AND (((LOWER(UNACCENT('areams')) <<% tparty.canonicalized OR -tparty.canonicalized LIKE '%' || LOWER(UNACCENT('areams')) || '%') -OR -(LOWER(UNACCENT('areams')) <<% parent.canonicalized OR -parent.canonicalized LIKE '%' || LOWER(UNACCENT('areams')) || '%')) -AND tparty.active IS TRUE and (parent.active IS TRUE OR parent IS NULL)) -ORDER BY pertinence DESC LIMIT 50 OFFSET 0; diff --git a/src/Bundle/ChillMainBundle/Controller/resquery.good.sql b/src/Bundle/ChillMainBundle/Controller/resquery.good.sql deleted file mode 100644 index 5877b04f8..000000000 --- a/src/Bundle/ChillMainBundle/Controller/resquery.good.sql +++ /dev/null @@ -1,19 +0,0 @@ -SELECT -'tparty' AS key, jsonb_build_object('id', tparty.id) AS metadata, GREATEST( -STRICT_WORD_SIMILARITY(LOWER(UNACCENT('areams')), tparty.canonicalized), -STRICT_WORD_SIMILARITY(LOWER(UNACCENT('areams')), parent.canonicalized) -) + GREATEST( -(tparty.canonicalized LIKE '%s' || LOWER(UNACCENT('areams')) || '%')::int, -(parent.canonicalized LIKE '%s' || LOWER(UNACCENT('areams')) || '%')::int -) + COALESCE((LOWER(UNACCENT(cmpc.label)) LIKE '%' || LOWER(UNACCENT('areams')) || '%')::int * 0.3, 0) + 1 AS pertinence -FROM chill_3party.third_party AS tparty -LEFT JOIN chill_3party.third_party AS parent ON tparty.parent_id = parent.id -LEFT JOIN chill_main_address cma ON cma.id = COALESCE(parent.address_id, tparty.address_id) -LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id -WHERE (((LOWER(UNACCENT('areams')) <<% tparty.canonicalized OR -tparty.canonicalized LIKE '%' || LOWER(UNACCENT('areams')) || '%') -OR -(LOWER(UNACCENT('areams')) <<% parent.canonicalized OR -parent.canonicalized LIKE '%' || LOWER(UNACCENT('areams')) || '%')) -AND tparty.active IS TRUE and (parent.active IS TRUE OR parent IS NULL)) -ORDER BY pertinence DESC, tparty.id ASC LIMIT 500 OFFSET 0; From 1409a3b23af53b9744cca5fd1dadc60874b82951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 13 Jul 2023 10:27:29 +0200 Subject: [PATCH 24/25] update changelog --- .changes/unreleased/Fixed-20230713-102640.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changes/unreleased/Fixed-20230713-102640.yaml diff --git a/.changes/unreleased/Fixed-20230713-102640.yaml b/.changes/unreleased/Fixed-20230713-102640.yaml new file mode 100644 index 000000000..e731e5252 --- /dev/null +++ b/.changes/unreleased/Fixed-20230713-102640.yaml @@ -0,0 +1,6 @@ +kind: Fixed +body: | + Correct bug in thirdparty API search query: simplify address joins clause for child and parent kind +time: 2023-07-13T10:26:40.503796155+02:00 +custom: + Issue: "126" From 28c41aaf8543dac4a3f3da0eb195c87199e19c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 13 Jul 2023 10:32:56 +0200 Subject: [PATCH 25/25] fix the number of parameters in the query --- src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php b/src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php index bb5303143..1d4e12074 100644 --- a/src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php +++ b/src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php @@ -100,7 +100,7 @@ class ThirdPartyApiSearch implements SearchApiInterface ) + " . // take postcode label into account, but lower than the canonicalized field "COALESCE((LOWER(UNACCENT(cmpc.label)) LIKE '%' || LOWER(UNACCENT(?)) || '%')::int * 0.3, 0)"; - $pertinenceArgs[] = [$str, $str, $str, $str, $str, $str]; + $pertinenceArgs[] = [$str, $str, $str, $str, $str]; } }