mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-10-31 01:08:26 +00:00 
			
		
		
		
	[export] add a filter and aggregator on accompanying periods: group by activity type (accompanying course having at least one activity from this type)
This commit is contained in:
		
							
								
								
									
										6
									
								
								.changes/unreleased/Feature-20231018-164927.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Feature-20231018-164927.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -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" | ||||
| @@ -0,0 +1,122 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators; | ||||
|  | ||||
| use Chill\ActivityBundle\Entity\Activity; | ||||
| use Chill\ActivityBundle\Entity\ActivityType; | ||||
| use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; | ||||
| use Chill\MainBundle\Export\AggregatorInterface; | ||||
| use Chill\MainBundle\Form\Type\PickRollingDateType; | ||||
| use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface; | ||||
| use Chill\MainBundle\Templating\TranslatableStringHelperInterface; | ||||
| use Chill\PersonBundle\Export\Declarations; | ||||
| use Doctrine\ORM\Query\Expr\Join; | ||||
| use Doctrine\ORM\QueryBuilder; | ||||
| use Symfony\Component\Form\FormBuilderInterface; | ||||
|  | ||||
| final readonly class ByActivityTypeAggregator implements AggregatorInterface | ||||
| { | ||||
|     private const PREFIX = 'acp_by_activity_type_agg'; | ||||
|  | ||||
|     public function __construct( | ||||
|         private RollingDateConverterInterface $rollingDateConverter, | ||||
|         private ActivityTypeRepositoryInterface $activityTypeRepository, | ||||
|         private TranslatableStringHelperInterface $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function buildForm(FormBuilderInterface $builder) | ||||
|     { | ||||
|         $builder | ||||
|             ->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; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,87 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\ActivityBundle\Tests\Export\Aggregator\ACPAggregators; | ||||
|  | ||||
| use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByActivityTypeAggregator; | ||||
| use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; | ||||
| use Chill\MainBundle\Service\RollingDate\RollingDate; | ||||
| use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface; | ||||
| use Chill\MainBundle\Templating\TranslatableStringHelperInterface; | ||||
| use Chill\MainBundle\Test\Export\AbstractAggregatorTest; | ||||
| use Chill\PersonBundle\Entity\AccompanyingPeriod; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
|  | ||||
| /** | ||||
|  * @internal | ||||
|  * | ||||
|  * @coversNothing | ||||
|  */ | ||||
| class ByActivityTypeAggregatorTest extends AbstractAggregatorTest | ||||
| { | ||||
|     private RollingDateConverterInterface $rollingDateConverter; | ||||
|     private ActivityTypeRepositoryInterface $activityTypeRepository; | ||||
|     private TranslatableStringHelperInterface $translatableStringHelper; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         parent::setUp(); | ||||
|  | ||||
|         self::bootKernel(); | ||||
|         $this->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'), | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @@ -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 } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user