diff --git a/.changes/unreleased/Feature-20231018-164927.yaml b/.changes/unreleased/Feature-20231018-164927.yaml new file mode 100644 index 000000000..cf0be6fef --- /dev/null +++ b/.changes/unreleased/Feature-20231018-164927.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: '[export] add a grouping on accompanying period export: group by activity type + associated to at least one activity within the accompanying period' +time: 2023-10-18T16:49:27.567166953+02:00 +custom: + Issue: "172" diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityTypeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityTypeAggregator.php new file mode 100644 index 000000000..cdb66bb77 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityTypeAggregator.php @@ -0,0 +1,122 @@ +add('after_date', PickRollingDateType::class, [ + 'required' => false, + 'label' => 'export.aggregator.acp.by_activity_type.after_date', + ]) + ->add('before_date', PickRollingDateType::class, [ + 'required' => false, + 'label' => 'export.aggregator.acp.by_activity_type.before_date', + ]); + } + + public function getFormDefaultData(): array + { + return [ + 'before_date' => null, + 'after_date' => null, + ]; + } + + public function getLabels($key, array $values, mixed $data) + { + return function (null|int|string $value): string { + if ('_header' === $value) { + return 'export.aggregator.acp.by_activity_type.activity_type'; + } + + if ('' === $value || null === $value || null === $activityType = $this->activityTypeRepository->find($value)) { + return ''; + } + + return $this->translatableStringHelper->localize($activityType->getName()); + }; + } + + public function getQueryKeys($data) + { + return [self::PREFIX.'_actype_id']; + } + + public function getTitle() + { + return 'export.aggregator.acp.by_activity_type.title'; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $p = self::PREFIX; + + // we make a left join, with acp having at least one activity of the given type + $exists = 'EXISTS (SELECT 1 FROM '.Activity::class." {$p}_activity WHERE {$p}_activity.accompanyingPeriod = acp AND {$p}_activity.activityType = {$p}_activity_type"; + + if (null !== $data['after_date']) { + $exists .= " AND {$p}_activity.date > :{$p}_after_date"; + $qb->setParameter("{$p}_after_date", $this->rollingDateConverter->convert($data['after_date'])); + } + + if (null !== $data['before_date']) { + $exists .= " AND {$p}_activity.date < :{$p}_before_date"; + $qb->setParameter("{$p}_before_date", $this->rollingDateConverter->convert($data['before_date'])); + } + + $exists .= ')'; + + $qb->leftJoin( + ActivityType::class, + "{$p}_activity_type", + Join::WITH, + $exists + ); + + $qb + ->addSelect("{$p}_activity_type.id AS {$p}_actype_id") + ->addGroupBy("{$p}_actype_id"); + } + + public function applyOn() + { + return Declarations::ACP_TYPE; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByActivityTypeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByActivityTypeAggregatorTest.php new file mode 100644 index 000000000..2d78585f7 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByActivityTypeAggregatorTest.php @@ -0,0 +1,87 @@ +rollingDateConverter = self::$container->get(RollingDateConverterInterface::class); + $this->activityTypeRepository = self::$container->get(ActivityTypeRepositoryInterface::class); + $this->translatableStringHelper = self::$container->get(TranslatableStringHelperInterface::class); + } + + public function getAggregator() + { + return new ByActivityTypeAggregator( + $this->rollingDateConverter, + $this->activityTypeRepository, + $this->translatableStringHelper, + ); + } + + public function getFormData() + { + return [ + [ + 'after_date' => null, + 'before_date' => null, + ], + [ + 'after_date' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), + 'before_date' => null, + ], + [ + 'after_date' => null, + 'before_date' => new RollingDate(RollingDate::T_TODAY), + ], + [ + 'after_date' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), + 'before_date' => new RollingDate(RollingDate::T_TODAY), + ], + ]; + } + + public function getQueryBuilders() + { + self::bootKernel(); + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(distinct acp.id)') + ->from(AccompanyingPeriod::class, 'acp'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/config/services/export.yaml b/src/Bundle/ChillActivityBundle/config/services/export.yaml index 3bb3b3695..24f10d399 100644 --- a/src/Bundle/ChillActivityBundle/config/services/export.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/export.yaml @@ -208,3 +208,7 @@ services: Chill\ActivityBundle\Export\Aggregator\PersonsAggregator: tags: - { name: chill.export_aggregator, alias: activity_by_persons_aggregator } + + Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByActivityTypeAggregator: + tags: + - { name: chill.export_aggregator, alias: acp_by_activity_type_aggregator } diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index 9c84e1815..b952ce3a8 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -383,6 +383,12 @@ export: persons taking part on the activity: Usagers participants à l'échange aggregator: + acp: + by_activity_type: + title: Grouper les parcours par type d'échange + after_date: Uniquement échanges après cette date + before_date: Uniquement échanges avant cette date + activity_type: Types d'échange activity: by_sent_received: Sent or received: Envoyé ou reçu diff --git a/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php b/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php index c23e0fa81..8ceb08578 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php @@ -34,6 +34,7 @@ class PickRollingDateType extends AbstractType ), 'multiple' => false, 'expanded' => false, + 'required' => $options['required'], 'label' => 'rolling_date.roll_movement', ]) ->add('fixedDate', ChillDateType::class, [ @@ -57,7 +58,10 @@ class PickRollingDateType extends AbstractType 'constraints' => [ new Callback($this->validate(...)), ], + 'required' => true, ]); + + $resolver->setAllowedTypes('required', 'bool'); } public function validate($data, ExecutionContextInterface $context, $payload): void