diff --git a/config/packages/chill_aside_activity.yaml b/config/packages/chill_aside_activity.yaml new file mode 100644 index 000000000..eb5c2d70e --- /dev/null +++ b/config/packages/chill_aside_activity.yaml @@ -0,0 +1,2 @@ +chill_aside_activity: + show_concerned_persons_count: hidden diff --git a/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/ChillAsideActivityExtension.php b/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/ChillAsideActivityExtension.php index 056f29ba1..6fa123146 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/ChillAsideActivityExtension.php +++ b/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/ChillAsideActivityExtension.php @@ -25,6 +25,7 @@ final class ChillAsideActivityExtension extends Extension implements PrependExte $config = $this->processConfiguration($configuration, $configs); $container->setParameter('chill_aside_activity.form.time_duration', $config['form']['time_duration']); + $container->setParameter('chill_aside_activity.show_concerned_persons_count', 'visible' === $config['show_concerned_persons_count']); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config')); $loader->load('services.yaml'); @@ -38,6 +39,24 @@ final class ChillAsideActivityExtension extends Extension implements PrependExte { $this->prependRoute($container); $this->prependCruds($container); + $this->prependTwigConfig($container); + } + + protected function prependTwigConfig(ContainerBuilder $container) + { + // Get the configuration for this bundle + $chillAsideActivityConfig = $container->getExtensionConfig($this->getAlias()); + $config = $this->processConfiguration($this->getConfiguration($chillAsideActivityConfig, $container), $chillAsideActivityConfig); + + // Add configuration to twig globals + $twigConfig = [ + 'globals' => [ + 'chill_aside_activity_config' => [ + 'show_concerned_persons_count' => 'visible' === $config['show_concerned_persons_count'], + ], + ], + ]; + $container->prependExtensionConfig('twig', $twigConfig); } protected function prependCruds(ContainerBuilder $container) diff --git a/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/Configuration.php b/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/Configuration.php index 241a545a8..66f3d3c86 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/Configuration.php +++ b/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/Configuration.php @@ -141,6 +141,12 @@ class Configuration implements ConfigurationInterface ->end() ->end() ->end() + ->end() + ->enumNode('show_concerned_persons_count') + ->values(['hidden', 'visible']) + ->defaultValue('hidden') + ->info('Show the concerned persons count field in aside activity forms and views') + ->end() ->end(); return $treeBuilder; diff --git a/src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivity.php index b7671c61a..0082deaf9 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivity.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivity.php @@ -62,6 +62,10 @@ class AsideActivity implements TrackCreationInterface, TrackUpdateInterface #[ORM\ManyToOne(targetEntity: User::class)] private User $updatedBy; + #[Assert\GreaterThanOrEqual(0)] + #[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: true)] + private ?int $concernedPersonsCount = 0; + public function getAgent(): ?User { return $this->agent; @@ -186,4 +190,16 @@ class AsideActivity implements TrackCreationInterface, TrackUpdateInterface return $this; } + + public function getConcernedPersonsCount(): ?int + { + return $this->concernedPersonsCount; + } + + public function setConcernedPersonsCount(?int $concernedPersonsCount): self + { + $this->concernedPersonsCount = $concernedPersonsCount; + + return $this; + } } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByConcernedPersonsCountAggregator.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByConcernedPersonsCountAggregator.php new file mode 100644 index 000000000..444c49269 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByConcernedPersonsCountAggregator.php @@ -0,0 +1,86 @@ +addSelect('aside.concernedPersonsCount AS by_concerned_persons_count_aggregator') + ->addGroupBy('by_concerned_persons_count_aggregator'); + } + + public function applyOn(): string + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + public function buildForm(FormBuilderInterface $builder): void + { + // No form needed + } + + public function getNormalizationVersion(): int + { + return 1; + } + + public function normalizeFormData(array $formData): array + { + return []; + } + + public function denormalizeFormData(array $formData, int $fromVersion): array + { + return []; + } + + public function getFormDefaultData(): array + { + return []; + } + + public function getLabels($key, array $values, $data): callable + { + return function ($value): string { + if ('_header' === $value) { + return 'export.aggregator.Concerned persons count'; + } + + if (null === $value) { + return 'export.aggregator.No concerned persons count specified'; + } + + return (string) $value; + }; + } + + public function getQueryKeys($data): array + { + return ['by_concerned_persons_count_aggregator']; + } + + public function getTitle(): string + { + return 'export.aggregator.Group by concerned persons count'; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/SumConcernedPersonsCountAsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/SumConcernedPersonsCountAsideActivity.php new file mode 100644 index 000000000..ab22302c2 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/SumConcernedPersonsCountAsideActivity.php @@ -0,0 +1,116 @@ +getTitle(); + + return static fn ($value) => $labels[$value]; + } + + public function getQueryKeys($data): array + { + return ['export_sum_concerned_persons_count']; + } + + public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array + { + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); + } + + public function getTitle(): string + { + return 'export.Sum concerned persons count for aside activities'; + } + + public function getType(): string + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder + { + $qb = $this->repository->createQueryBuilder('aside'); + + $qb->select('SUM(COALESCE(aside.concernedPersonsCount, 0)) as export_sum_concerned_persons_count'); + + return $qb; + } + + public function requiredRole(): string + { + return AsideActivityVoter::STATS; + } + + public function supportsModifiers(): array + { + return [ + Declarations::ASIDE_ACTIVITY_TYPE, + ]; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php b/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php index d6fcb821c..ea04fdc42 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php @@ -21,6 +21,7 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; @@ -29,11 +30,13 @@ use Symfony\Component\OptionsResolver\OptionsResolver; final class AsideActivityFormType extends AbstractType { private readonly array $timeChoices; + private readonly bool $showConcernedPersonsCount; public function __construct( ParameterBagInterface $parameterBag, ) { $this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration'); + $this->showConcernedPersonsCount = $parameterBag->get('chill_aside_activity.show_concerned_persons_count'); } public function buildForm(FormBuilderInterface $builder, array $options) @@ -76,6 +79,16 @@ final class AsideActivityFormType extends AbstractType ->add('location', PickUserLocationType::class) ; + if ($this->showConcernedPersonsCount) { + $builder->add('concernedPersonsCount', IntegerType::class, [ + 'label' => 'Concerned persons count', + 'required' => false, + 'attr' => [ + 'min' => 0, + ], + ]); + } + foreach (['duration'] as $fieldName) { $builder->get($fieldName) ->addModelTransformer($durationTimeTransformer); diff --git a/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivity/index.html.twig b/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivity/index.html.twig index 0a8648749..0d06e5ba9 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivity/index.html.twig +++ b/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivity/index.html.twig @@ -42,6 +42,11 @@ {%- if entity.location.name is defined -%}
{{ entity.location.name }}
{%- endif -%} + + {%- if entity.concernedPersonsCount > 0 -%} +
{{ entity.concernedPersonsCount }}
+ {%- endif -%} +
diff --git a/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivity/view.html.twig b/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivity/view.html.twig index ef6faa9c6..330a2ab13 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivity/view.html.twig +++ b/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivity/view.html.twig @@ -38,6 +38,11 @@
{{ 'Duration'|trans }}
{{ entity.duration|date('H:i') }}
+ {% if chill_aside_activity_config.show_concerned_persons_count == 'visible' %} +
{{ 'Concerned persons count'|trans }}
+
{{ entity.concernedPersonsCount }}
+ {% endif %} +
{{ 'Remark'|trans }}
{%- if entity.note is empty -%}
diff --git a/src/Bundle/ChillAsideActivityBundle/src/Tests/Export/Aggregator/ByConcernedPersonsCountAggregatorTest.php b/src/Bundle/ChillAsideActivityBundle/src/Tests/Export/Aggregator/ByConcernedPersonsCountAggregatorTest.php new file mode 100644 index 000000000..a1d133d8c --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Tests/Export/Aggregator/ByConcernedPersonsCountAggregatorTest.php @@ -0,0 +1,49 @@ +get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(aside.id)') + ->from(AsideActivity::class, 'aside'), + ]; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Tests/Export/Export/SumConcernedPersonsCountAsideActivityTest.php b/src/Bundle/ChillAsideActivityBundle/src/Tests/Export/Export/SumConcernedPersonsCountAsideActivityTest.php new file mode 100644 index 000000000..499986ac1 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Tests/Export/Export/SumConcernedPersonsCountAsideActivityTest.php @@ -0,0 +1,50 @@ +get(AsideActivityRepository::class); + + yield new SumConcernedPersonsCountAsideActivity($repository); + } + + public static function getFormData(): array + { + return [ + [], + ]; + } + + public static function getModifiersCombination(): array + { + return [ + ['aside_activity'], + ]; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml b/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml index 40cb120da..efba7a8af 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml +++ b/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml @@ -20,6 +20,10 @@ services: tags: - { name: chill.export, alias: 'avg_aside_activity_duration' } + Chill\AsideActivityBundle\Export\Export\SumConcernedPersonsCountAsideActivity: + tags: + - { name: chill.export, alias: 'sum_aside_activity_concerned_persons_count' } + ## Filters chill.aside_activity.export.date_filter: class: Chill\AsideActivityBundle\Export\Filter\ByDateFilter @@ -70,3 +74,7 @@ services: Chill\AsideActivityBundle\Export\Aggregator\ByLocationAggregator: tags: - { name: chill.export_aggregator, alias: 'aside_activity_location_aggregator' } + + Chill\AsideActivityBundle\Export\Aggregator\ByConcernedPersonsCountAggregator: + tags: + - { name: chill.export_aggregator, alias: 'aside_activity_concerned_persons_count_aggregator' } diff --git a/src/Bundle/ChillAsideActivityBundle/src/migrations/Version20251006113048.php b/src/Bundle/ChillAsideActivityBundle/src/migrations/Version20251006113048.php new file mode 100644 index 000000000..8ea1dbf4c --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/migrations/Version20251006113048.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE chill_asideactivity.asideactivity ADD concernedPersonsCount INT DEFAULT 0'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_asideactivity.AsideActivity DROP concernedPersonsCount'); + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml index 7d3c7a20e..664cd41b7 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml +++ b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml @@ -27,6 +27,7 @@ Emergency: Urgent by: "Par " location: Lieu Asideactivity location: Localisation de l'activité +Concerned persons count: Nombre d'usager concernés # Crud crud: @@ -190,6 +191,7 @@ export: Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères Average aside activities duration: Durée moyenne des activités annexes Sum aside activities duration: Durée des activités annexes + Sum concerned persons count for aside activities: Nombre d'usager concernés par les activités annexes filter: Filter by aside activity date: Filtrer les activités annexes par date Filter by aside activity type: Filtrer les activités annexes par type d'activité @@ -210,6 +212,8 @@ export: 'Filtered by aside activity location: only %location%': "Filtré par localisation: uniquement %location%" aggregator: Group by aside activity type: Grouper les activités annexes par type d'activité + Group by concerned persons count: Grouper les activités annexes par nombre d'usagers conernés + Concerned persons count: Nombre d'usagers concernés Aside activity type: Type d'activité annexe by_user_job: Aggregate by user job: Grouper les activités annexes par métier des utilisateurs