diff --git a/CHANGELOG.md b/CHANGELOG.md index 542c99a37..b2f951479 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,9 @@ and this project adheres to * [social_action]: only show active objectives (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/625) * [household]: Reposition and cut button for enfant hors menage have been deleted (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/620) * [admin]: Add crud for composition type in admin (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/611) +* [social_action]: only show active objectives (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/625) + +## Test releases ### 2022-05-30 diff --git a/docs/source/_static/code/exports/CountPerson.php b/docs/source/_static/code/exports/CountPerson.php index 108a368c9..48eab8a55 100644 --- a/docs/source/_static/code/exports/CountPerson.php +++ b/docs/source/_static/code/exports/CountPerson.php @@ -18,7 +18,6 @@ use Chill\PersonBundle\Security\Authorization\PersonVoter; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Security\Core\Role\Role; class CountPerson implements ExportInterface { @@ -73,9 +72,9 @@ class CountPerson implements ExportInterface return ['export_result']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle() @@ -106,9 +105,9 @@ class CountPerson implements ExportInterface return $qb; } - public function requiredRole() + public function requiredRole(): string { - return new Role(PersonVoter::STATS); + return PersonVoter::STATS; } public function supportsModifiers() diff --git a/docs/source/installation/index.rst b/docs/source/installation/index.rst index 00f27ed69..01a8c4aee 100644 --- a/docs/source/installation/index.rst +++ b/docs/source/installation/index.rst @@ -265,7 +265,7 @@ Tests reside inside the installed bundles. You must `cd` into that directory, do **Note**: some bundle require the fixture to be executed. See the dedicated _how-tos_. -Exemple, for running test inside `main` bundle: +Exemple, for running unit test inside `main` bundle: .. code-block:: bash @@ -280,6 +280,30 @@ Exemple, for running test inside `main` bundle: # run tests bin/phpunit src/Bundle/path/to/your/test +Or for running tests to check code style and php conventions with csfixer and phpstan: + +.. code-block:: bash + + # run code style fixer + bin/grumphp run --tasks=phpcsfixer + # run phpstan + bin/grumphp run --tasks=phpstan + + +.. note:: + + To avoid phpstan block your commits: + + .. code-block:: bash + + git commit -n ... + + To avoid phpstan block your commits permanently: + + .. code-block:: bash + + ./bin/grumphp git:deinit + How to run webpack interactively ================================ diff --git a/exports_alias_conventions.md b/exports_alias_conventions.md index 579d004a5..62fc745d8 100644 --- a/exports_alias_conventions.md +++ b/exports_alias_conventions.md @@ -17,12 +17,13 @@ These are alias conventions : | | Scope::class | acp.scopes | acpscope | | | SocialIssue::class | acp.socialIssues | acpsocialissue | | | User::class | acp.user | acpuser | +| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories | | AccompanyingPeriodWork::class | | | acpw | | | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval | | | User::class | acpw.referrers | acpwuser | | | SocialAction::class | acpw.socialAction | acpwsocialaction | | | Goal::class | acpw.goals | goal | -| | Result::class | acpw.results | result | +| | Result::class | acpw.results | result | | AccompanyingPeriodParticipation::class | | | acppart | | | Person::class | acppart.person | partperson | | AccompanyingPeriodWorkEvaluation::class | | | workeval | @@ -47,7 +48,7 @@ These are alias conventions : | | HouseholdComposition::class | household.compositions | composition | | Activity::class | | | activity | | | Person::class | activity.person | actperson | -| | AccompanyingPeriod::class | activity.accompanyingPeriod | acp | +| | AccompanyingPeriod::class | activity.accompanyingPeriod | acp | | | Person::class | activity\_person\_having\_activity.person | person\_person\_having\_activity | | | ActivityReason::class | activity\_person\_having\_activity.reasons | reasons\_person\_having\_activity | | | ActivityType::class | activity.activityType | acttype | @@ -59,6 +60,7 @@ These are alias conventions : | | User::class | activity.users | actusers | | | ActivityReason::class | activity.reasons | actreasons | | | Center::class | actperson.center | actcenter | +| | Person::class | activity.createdBy | actcreator | | ActivityReason::class | | | actreasons | | | ActivityReasonCategory::class | actreason.category | actreasoncat | | Calendar::class | | | cal | diff --git a/phpstan-deprecations.neon b/phpstan-deprecations.neon index 9a6d862b5..aa8506411 100644 --- a/phpstan-deprecations.neon +++ b/phpstan-deprecations.neon @@ -23,150 +23,6 @@ parameters: count: 1 path: src/Bundle/ChillActivityBundle/Entity/ActivityReasonCategory.php - - - message: """ - #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityReasonAggregator.php - - - - message: """ - #^Return type of method Chill\\\\ActivityBundle\\\\Export\\\\Aggregator\\\\ActivityReasonAggregator\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityReasonAggregator.php - - - - message: """ - #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php - - - - message: """ - #^Return type of method Chill\\\\ActivityBundle\\\\Export\\\\Aggregator\\\\ActivityTypeAggregator\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php - - - - message: """ - #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php - - - - message: """ - #^Return type of method Chill\\\\ActivityBundle\\\\Export\\\\Aggregator\\\\ActivityUserAggregator\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php - - - - message: """ - #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Export/CountActivity.php - - - - message: """ - #^Return type of method Chill\\\\ActivityBundle\\\\Export\\\\Export\\\\CountActivity\\:\\:requiredRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Export/CountActivity.php - - - - message: """ - #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Export/ListActivity.php - - - - message: """ - #^Return type of method Chill\\\\ActivityBundle\\\\Export\\\\Export\\\\ListActivity\\:\\:requiredRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Export/ListActivity.php - - - - message: """ - #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Export/StatActivityDuration.php - - - - message: """ - #^Return type of method Chill\\\\ActivityBundle\\\\Export\\\\Export\\\\StatActivityDuration\\:\\:requiredRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Export/StatActivityDuration.php - - - - message: """ - #^Return type of method Chill\\\\ActivityBundle\\\\Export\\\\Filter\\\\ActivityDateFilter\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Filter/ActivityDateFilter.php - - - - message: """ - #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Filter/ActivityReasonFilter.php - - - - message: """ - #^Return type of method Chill\\\\ActivityBundle\\\\Export\\\\Filter\\\\ActivityReasonFilter\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Filter/ActivityReasonFilter.php - - - - message: """ - #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php - - - - message: """ - #^Return type of method Chill\\\\ActivityBundle\\\\Export\\\\Filter\\\\ActivityTypeFilter\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php - - - - message: """ - #^Return type of method Chill\\\\ActivityBundle\\\\Export\\\\Filter\\\\PersonHavingActivityBetweenDateFilter\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Filter/PersonHavingActivityBetweenDateFilter.php - - message: """ #^Fetching class constant class of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: @@ -359,31 +215,6 @@ parameters: count: 1 path: src/Bundle/ChillMainBundle/Entity/User.php - - - message: """ - #^Return type of method Chill\\\\MainBundle\\\\Export\\\\DirectExportInterface\\:\\:requiredRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillMainBundle/Export/DirectExportInterface.php - - - - message: """ - #^Return type of method Chill\\\\MainBundle\\\\Export\\\\ExportInterface\\:\\:requiredRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillMainBundle/Export/ExportInterface.php - - - - - message: """ - #^Return type of method Chill\\\\MainBundle\\\\Export\\\\ModifierInterface\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillMainBundle/Export/ModifierInterface.php - - message: """ #^Class Chill\\\\MainBundle\\\\Form\\\\Event\\\\CustomizeFormEvent extends deprecated class Symfony\\\\Component\\\\EventDispatcher\\\\Event\\: @@ -543,142 +374,6 @@ parameters: count: 2 path: src/Bundle/ChillPersonBundle/Entity/Person.php - - - message: """ - #^Return type of method Chill\\\\PersonBundle\\\\Export\\\\Aggregator\\\\AgeAggregator\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Aggregator/AgeAggregator.php - - - - message: """ - #^Return type of method Chill\\\\PersonBundle\\\\Export\\\\Aggregator\\\\CountryOfBirthAggregator\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Aggregator/CountryOfBirthAggregator.php - - - - message: """ - #^Return type of method Chill\\\\PersonBundle\\\\Export\\\\Aggregator\\\\GenderAggregator\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Aggregator/GenderAggregator.php - - - - message: """ - #^Return type of method Chill\\\\PersonBundle\\\\Export\\\\Aggregator\\\\NationalityAggregator\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Aggregator/NationalityAggregator.php - - - - message: """ - #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php - - - - message: """ - #^Return type of method Chill\\\\PersonBundle\\\\Export\\\\Export\\\\CountPerson\\:\\:requiredRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php - - - - message: """ - #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php - - - - message: """ - #^Return type of method Chill\\\\PersonBundle\\\\Export\\\\Export\\\\ListPerson\\:\\:requiredRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php - - - - message: """ - #^Call to deprecated method execute\\(\\) of class Doctrine\\\\DBAL\\\\Statement\\: - Statement\\:\\:execute\\(\\) is deprecated, use Statement\\:\\:executeQuery\\(\\) or executeStatement\\(\\) instead$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php - - - - message: """ - #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php - - - - message: """ - #^Return type of method Chill\\\\PersonBundle\\\\Export\\\\Export\\\\ListPersonDuplicate\\:\\:requiredRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php - - - - message: """ - #^Return type of method Chill\\\\PersonBundle\\\\Export\\\\Filter\\\\AccompanyingPeriodClosingFilter\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingPeriodClosingFilter.php - - - - message: """ - #^Return type of method Chill\\\\PersonBundle\\\\Export\\\\Filter\\\\AccompanyingPeriodFilter\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingPeriodFilter.php - - - - message: """ - #^Return type of method Chill\\\\PersonBundle\\\\Export\\\\Filter\\\\AccompanyingPeriodOpeningFilter\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingPeriodOpeningFilter.php - - - - message: """ - #^Return type of method Chill\\\\PersonBundle\\\\Export\\\\Filter\\\\BirthdateFilter\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Filter/BirthdateFilter.php - - - - message: """ - #^Return type of method Chill\\\\PersonBundle\\\\Export\\\\Filter\\\\GenderFilter\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Filter/GenderFilter.php - - - - message: """ - #^Return type of method Chill\\\\PersonBundle\\\\Export\\\\Filter\\\\NationalityFilter\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Filter/NationalityFilter.php - - message: """ #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: @@ -740,29 +435,6 @@ parameters: count: 3 path: src/Bundle/ChillReportBundle/Controller/ReportController.php - - - message: """ - #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillReportBundle/Export/Export/ReportList.php - - - - message: """ - #^Return type of method Chill\\\\ReportBundle\\\\Export\\\\Export\\\\ReportList\\:\\:requiredRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillReportBundle/Export/Export/ReportList.php - - - - message: """ - #^Return type of method Chill\\\\ReportBundle\\\\Export\\\\Filter\\\\ReportDateFilter\\:\\:addRole\\(\\) has typehint with deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: - since Symfony 4\\.3, to be removed in 5\\.0\\. Use strings as roles instead\\.$# - """ - count: 1 - path: src/Bundle/ChillReportBundle/Export/Filter/ReportDateFilter.php - message: """ @@ -845,14 +517,6 @@ parameters: count: 1 path: src/Bundle/ChillTaskBundle/Form/SingleTaskType.php - - - message: """ - #^Parameter \\$centerResolverDispatcher of method Chill\\\\TaskBundle\\\\Repository\\\\SingleTaskAclAwareRepository\\:\\:__construct\\(\\) has typehint with deprecated interface Chill\\\\MainBundle\\\\Security\\\\Resolver\\\\CenterResolverDispatcherInterface\\: - Use CenterResolverManager and its interface CenterResolverManagerInterface$# - """ - count: 1 - path: src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php - - message: """ #^Instantiation of deprecated class Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role\\: diff --git a/phpstan-types.neon b/phpstan-types.neon index c8a0791e0..a0493ce0b 100644 --- a/phpstan-types.neon +++ b/phpstan-types.neon @@ -245,11 +245,6 @@ parameters: count: 1 path: src/Bundle/ChillMainBundle/Entity/User.php - - - message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#" - count: 1 - path: src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 1 diff --git a/src/Bundle/ChillActivityBundle/Entity/Activity.php b/src/Bundle/ChillActivityBundle/Entity/Activity.php index 98142149f..8fcae3e0b 100644 --- a/src/Bundle/ChillActivityBundle/Entity/Activity.php +++ b/src/Bundle/ChillActivityBundle/Entity/Activity.php @@ -13,6 +13,10 @@ namespace Chill\ActivityBundle\Entity; use Chill\ActivityBundle\Validator\Constraints as ActivityValidator; use Chill\DocStoreBundle\Entity\StoredObject; +use Chill\MainBundle\Doctrine\Model\TrackCreationInterface; +use Chill\MainBundle\Doctrine\Model\TrackCreationTrait; +use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface; +use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait; use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; use Chill\MainBundle\Entity\Embeddable\PrivateCommentEmbeddable; @@ -55,8 +59,12 @@ use Symfony\Component\Validator\Constraints as Assert; * getUserFunction="getUser", * path="scope") */ -class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface, HasCentersInterface, HasScopesInterface +class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface, HasCentersInterface, HasScopesInterface, TrackCreationInterface, TrackUpdateInterface { + use TrackCreationTrait; + + use TrackUpdateTrait; + public const SENTRECEIVED_RECEIVED = 'received'; public const SENTRECEIVED_SENT = 'sent'; diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByCreatorAggregator.php similarity index 72% rename from src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php rename to src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByCreatorAggregator.php index 337e38705..c09685e4e 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByCreatorAggregator.php @@ -13,20 +13,19 @@ namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators; use Chill\ActivityBundle\Export\Declarations; use Chill\MainBundle\Export\AggregatorInterface; -use Chill\MainBundle\Repository\UserRepository; +use Chill\MainBundle\Repository\UserRepositoryInterface; use Chill\MainBundle\Templating\Entity\UserRender; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use function in_array; -class ByUserAggregator implements AggregatorInterface +class ByCreatorAggregator implements AggregatorInterface { private UserRender $userRender; - private UserRepository $userRepository; + private UserRepositoryInterface $userRepository; public function __construct( - UserRepository $userRepository, + UserRepositoryInterface $userRepository, UserRender $userRender ) { $this->userRepository = $userRepository; @@ -40,12 +39,8 @@ class ByUserAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('actusers', $qb->getAllAliases(), true)) { - $qb->leftJoin('activity.users', 'actusers'); - } - - $qb->addSelect('actusers.id AS users_aggregator'); - $qb->addGroupBy('users_aggregator'); + $qb->addSelect('IDENTITY(activity.createdBy) AS creator_aggregator'); + $qb->addGroupBy('creator_aggregator'); } public function applyOn(): string @@ -62,7 +57,7 @@ class ByUserAggregator implements AggregatorInterface { return function ($value): string { if ('_header' === $value) { - return 'Accepted users'; + return 'Created by'; } if (null === $value) { @@ -77,11 +72,11 @@ class ByUserAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['users_aggregator']; + return ['creator_aggregator']; } public function getTitle(): string { - return 'Group activity by linked users'; + return 'Group activity by creator'; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/CreatorScopeAggregator.php similarity index 81% rename from src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php rename to src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/CreatorScopeAggregator.php index 2c2cb50d2..2041fcbb4 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/CreatorScopeAggregator.php @@ -19,7 +19,7 @@ use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use function in_array; -class UserScopeAggregator implements AggregatorInterface +class CreatorScopeAggregator implements AggregatorInterface { private ScopeRepository $scopeRepository; @@ -40,12 +40,12 @@ class UserScopeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('actuser', $qb->getAllAliases(), true)) { - $qb->leftJoin('activity.user', 'actuser'); + if (!in_array('actcreator', $qb->getAllAliases(), true)) { + $qb->leftJoin('activity.createdBy', 'actcreator'); } - $qb->addSelect('IDENTITY(actuser.mainScope) AS userscope_aggregator'); - $qb->addGroupBy('userscope_aggregator'); + $qb->addSelect('IDENTITY(actcreator.mainScope) AS creatorscope_aggregator'); + $qb->addGroupBy('creatorscope_aggregator'); } public function applyOn(): string @@ -79,11 +79,11 @@ class UserScopeAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['userscope_aggregator']; + return ['creatorscope_aggregator']; } public function getTitle(): string { - return 'Group activity by userscope'; + return 'Group activity by creator scope'; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersAggregator.php new file mode 100644 index 000000000..ccccc48a0 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersAggregator.php @@ -0,0 +1,86 @@ +userRepository = $userRepository; + $this->userRender = $userRender; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + if (!in_array('actusers', $qb->getAllAliases(), true)) { + $qb->leftJoin('activity.users', 'actusers'); + } + + $qb + ->addSelect('actusers.id AS activity_users_aggregator') + ->addGroupBy('activity_users_aggregator'); + } + + public function applyOn(): string + { + return Declarations::ACTIVITY; + } + + public function buildForm(FormBuilderInterface $builder) + { + // nothing to add on the form + } + + public function getLabels($key, array $values, $data) + { + return function ($value) { + if ('_header' === $value) { + return 'Activity users'; + } + + if (null === $value) { + return ''; + } + + $u = $this->userRepository->find($value); + + return $this->userRender->renderString($u, []); + }; + } + + public function getQueryKeys($data) + { + return ['activity_users_aggregator']; + } + + public function getTitle() + { + return 'Aggregate by activity users'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersJobAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersJobAggregator.php new file mode 100644 index 000000000..a0a6a439b --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersJobAggregator.php @@ -0,0 +1,87 @@ +userJobRepository = $userJobRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + if (!in_array('actusers', $qb->getAllAliases(), true)) { + $qb->leftJoin('activity.users', 'actusers'); + } + + $qb + ->addSelect('IDENTITY(actusers.userJob) AS activity_users_job_aggregator') + ->addGroupBy('activity_users_job_aggregator'); + } + + public function applyOn() + { + return Declarations::ACTIVITY; + } + + public function buildForm(FormBuilderInterface $builder) + { + // nothing to add in the form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Users \'s job'; + } + + if (null === $value) { + return ''; + } + + $j = $this->userJobRepository->find($value); + + return $this->translatableStringHelper->localize( + $j->getLabel() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['activity_users_job_aggregator']; + } + + public function getTitle() + { + return 'Aggregate by users job'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersScopeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersScopeAggregator.php new file mode 100644 index 000000000..975c5df27 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersScopeAggregator.php @@ -0,0 +1,87 @@ +scopeRepository = $scopeRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + if (!in_array('actusers', $qb->getAllAliases(), true)) { + $qb->leftJoin('activity.users', 'actusers'); + } + + $qb + ->addSelect('IDENTITY(actusers.mainScope) AS activity_users_main_scope_aggregator') + ->addGroupBy('activity_users_main_scope_aggregator'); + } + + public function applyOn() + { + return Declarations::ACTIVITY; + } + + public function buildForm(FormBuilderInterface $builder) + { + // nothing to add in the form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Users \'s scope'; + } + + if (null === $value) { + return ''; + } + + $s = $this->scopeRepository->find($value); + + return $this->translatableStringHelper->localize( + $s->getName() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['activity_users_main_scope_aggregator']; + } + + public function getTitle() + { + return 'Aggregate by users scope'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php index d4007aaea..2b6919340 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php @@ -69,9 +69,9 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface return ['export_avg_activity_duration']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle(): string diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php index 673e4408a..359593059 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php @@ -70,9 +70,9 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac return ['export_avg_activity_visit_duration']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle(): string diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php index 0902ced67..2dc844aa2 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php @@ -69,9 +69,9 @@ class CountActivity implements ExportInterface, GroupedExportInterface return ['export_count_activity']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle(): string diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php index e55bd416e..1cf20dc5f 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php @@ -70,9 +70,9 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface return ['export_sum_activity_duration']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle(): string diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php index 1370edea7..2c160f3cf 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php @@ -70,9 +70,9 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac return ['export_sum_activity_visit_duration']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle(): string diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php index cf1acc40e..4246df173 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php @@ -65,9 +65,9 @@ class CountActivity implements ExportInterface, GroupedExportInterface return ['export_count_activity']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle() diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php index 387ab3f27..050034954 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php @@ -64,6 +64,8 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface if (self::SUM === $this->action) { return 'Sum activities linked to a person duration by various parameters.'; } + + throw new LogicException('this action is not supported: ' . $this->action); } public function getGroup(): string @@ -87,9 +89,9 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface return ['export_stat_activity']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle() @@ -97,6 +99,8 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface if (self::SUM === $this->action) { return 'Sum activity linked to a person duration'; } + + throw new LogicException('This action is not supported: ' . $this->action); } public function getType(): string diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByUserFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByCreatorFilter.php similarity index 54% rename from src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByUserFilter.php rename to src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByCreatorFilter.php index 7079be302..322393f32 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByUserFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByCreatorFilter.php @@ -12,16 +12,13 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Filter\ACPFilters; use Chill\ActivityBundle\Export\Declarations; -use Chill\MainBundle\Entity\User; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\PickUserDynamicType; use Chill\MainBundle\Templating\Entity\UserRender; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; -use function in_array; -class ByUserFilter implements FilterInterface +class ByCreatorFilter implements FilterInterface { private UserRender $userRender; @@ -37,22 +34,11 @@ class ByUserFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); - - if (!in_array('actusers', $qb->getAllAliases(), true)) { - $qb->join('activity.users', 'actusers'); - } - - $clause = $qb->expr()->in('actusers.id', ':users'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('users', $data['accepted_users']); + $qb + ->andWhere( + $qb->expr()->in('activity.createdBy', ':users') + ) + ->setParameter('users', $data['accepted_users']); } public function applyOn(): string @@ -62,13 +48,8 @@ class ByUserFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('accepted_users', EntityType::class, [ - 'class' => User::class, - 'choice_label' => function (User $u) { - return $this->userRender->renderString($u, []); - }, + $builder->add('accepted_users', PickUserDynamicType::class, [ 'multiple' => true, - 'expanded' => true, ]); } @@ -80,13 +61,13 @@ class ByUserFilter implements FilterInterface $users[] = $this->userRender->renderString($u, []); } - return ['Filtered activity by linked users: only %users%', [ - '%users%' => implode(', ou ', $users), + return ['Filtered activity by creator: only %users%', [ + '%users%' => implode(', ', $users), ]]; } public function getTitle(): string { - return 'Filter activity by linked users'; + return 'Filter activity by creator'; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialActionFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialActionFilter.php index e3ce8b287..d0c1b0fc7 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialActionFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialActionFilter.php @@ -14,10 +14,9 @@ namespace Chill\ActivityBundle\Export\Filter\ACPFilters; use Chill\ActivityBundle\Export\Declarations; use Chill\MainBundle\Export\FilterInterface; use Chill\PersonBundle\Entity\SocialWork\SocialAction; +use Chill\PersonBundle\Form\Type\PickSocialActionType; use Chill\PersonBundle\Templating\Entity\SocialActionRender; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use function in_array; @@ -37,22 +36,17 @@ class BySocialActionFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); - if (!in_array('actsocialaction', $qb->getAllAliases(), true)) { $qb->join('activity.socialActions', 'actsocialaction'); } $clause = $qb->expr()->in('actsocialaction.id', ':socialactions'); - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('socialactions', $data['accepted_socialactions']); + $qb->andWhere($clause) + ->setParameter( + 'socialactions', + SocialAction::getDescendantsWithThisForActions($data['accepted_socialactions']) + ); } public function applyOn(): string @@ -62,13 +56,8 @@ class BySocialActionFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('accepted_socialactions', EntityType::class, [ - 'class' => SocialAction::class, - 'choice_label' => function (SocialAction $sa) { - return $this->actionRender->renderString($sa, []); - }, + $builder->add('accepted_socialactions', PickSocialActionType::class, [ 'multiple' => true, - 'expanded' => true, ]); } @@ -76,12 +65,14 @@ class BySocialActionFilter implements FilterInterface { $actions = []; - foreach ($data['accepted_socialactions'] as $sa) { - $actions[] = $this->actionRender->renderString($sa, []); + foreach ($data['accepted_socialactions'] as $action) { + $actions[] = $this->actionRender->renderString($action, [ + 'show_and_children' => true, + ]); } return ['Filtered activity by linked socialaction: only %actions%', [ - '%actions%' => implode(', ou ', $actions), + '%actions%' => implode(', ', $actions), ]]; } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialIssueFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialIssueFilter.php index f5d552011..bbb882a65 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialIssueFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialIssueFilter.php @@ -14,10 +14,9 @@ namespace Chill\ActivityBundle\Export\Filter\ACPFilters; use Chill\ActivityBundle\Export\Declarations; use Chill\MainBundle\Export\FilterInterface; use Chill\PersonBundle\Entity\SocialWork\SocialIssue; +use Chill\PersonBundle\Form\Type\PickSocialIssueType; use Chill\PersonBundle\Templating\Entity\SocialIssueRender; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use function in_array; @@ -37,22 +36,17 @@ class BySocialIssueFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); - if (!in_array('actsocialissue', $qb->getAllAliases(), true)) { $qb->join('activity.socialIssues', 'actsocialissue'); } $clause = $qb->expr()->in('actsocialissue.id', ':socialissues'); - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('socialissues', $data['accepted_socialissues']); + $qb->andWhere($clause) + ->setParameter( + 'socialissues', + SocialIssue::getDescendantsWithThisForIssues($data['accepted_socialissues']) + ); } public function applyOn(): string @@ -62,13 +56,8 @@ class BySocialIssueFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('accepted_socialissues', EntityType::class, [ - 'class' => SocialIssue::class, - 'choice_label' => function (SocialIssue $si) { - return $this->issueRender->renderString($si, []); - }, + $builder->add('accepted_socialissues', PickSocialIssueType::class, [ 'multiple' => true, - 'expanded' => true, ]); } @@ -76,12 +65,14 @@ class BySocialIssueFilter implements FilterInterface { $issues = []; - foreach ($data['accepted_socialissues'] as $si) { - $issues[] = $this->issueRender->renderString($si, []); + foreach ($data['accepted_socialissues'] as $issue) { + $issues[] = $this->issueRender->renderString($issue, [ + 'show_and_children' => true, + ]); } return ['Filtered activity by linked socialissue: only %issues%', [ - '%issues%' => implode(', ou ', $issues), + '%issues%' => implode(', ', $issues), ]]; } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/EmergencyFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/EmergencyFilter.php index b74db1cc1..b79c2ca10 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/EmergencyFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/EmergencyFilter.php @@ -74,15 +74,13 @@ class EmergencyFilter implements FilterInterface public function describeAction($data, $format = 'string'): array { - foreach (self::CHOICES as $k => $v) { - if ($v === $data['accepted_emergency']) { - $choice = $k; - } - } - - return ['Filtered activity by emergency: only %emergency%', [ - '%emergency%' => $this->translator->trans($choice), - ]]; + return [ + 'Filtered by emergency: only %emergency%', [ + '%emergency%' => $this->translator->trans( + $data['accepted_emergency'] ? 'is emergency' : 'is not emergency' + ), + ], + ]; } public function getTitle(): string diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationTypeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationTypeFilter.php index 92dc1b0eb..5fe928b6c 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationTypeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationTypeFilter.php @@ -12,12 +12,11 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Filter\ACPFilters; use Chill\ActivityBundle\Export\Declarations; -use Chill\MainBundle\Entity\LocationType; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\PickLocationTypeType; use Chill\MainBundle\Templating\TranslatableStringHelper; use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use function in_array; @@ -61,13 +60,9 @@ class LocationTypeFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('accepted_locationtype', EntityType::class, [ - 'class' => LocationType::class, - 'choice_label' => function (LocationType $type) { - return $this->translatableStringHelper->localize($type->getTitle()); - }, + $builder->add('accepted_locationtype', PickLocationTypeType::class, [ 'multiple' => true, - 'expanded' => true, + //'label' => false, ]); } @@ -82,7 +77,7 @@ class LocationTypeFilter implements FilterInterface } return ['Filtered activity by locationtype: only %types%', [ - '%types%' => implode(', ou ', $types), + '%types%' => implode(', ', $types), ]]; } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserFilter.php index b7b5aa83e..6350f3ace 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserFilter.php @@ -12,12 +12,11 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Filter\ACPFilters; use Chill\ActivityBundle\Export\Declarations; -use Chill\MainBundle\Entity\User; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\PickUserDynamicType; use Chill\MainBundle\Templating\Entity\UserRender; use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; class UserFilter implements FilterInterface @@ -57,13 +56,8 @@ class UserFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('accepted_users', EntityType::class, [ - 'class' => User::class, - 'choice_label' => function (User $u) { - return $this->userRender->renderString($u, []); - }, + $builder->add('accepted_users', PickUserDynamicType::class, [ 'multiple' => true, - 'expanded' => true, 'label' => 'Creators', ]); } @@ -77,7 +71,7 @@ class UserFilter implements FilterInterface } return ['Filtered activity by user: only %users%', [ - '%users%' => implode(', ou ', $users), + '%users%' => implode(', ', $users), ]]; } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserScopeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserScopeFilter.php index 4568cd006..1906db75e 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserScopeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserScopeFilter.php @@ -85,7 +85,7 @@ class UserScopeFilter implements FilterInterface } return ['Filtered activity by userscope: only %scopes%', [ - '%scopes%' => implode(', ou ', $scopes), + '%scopes%' => implode(', ', $scopes), ]]; } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php index 5338da791..d1758039a 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php @@ -91,7 +91,7 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter ); return ['Filtered by activity type: only %list%', [ - '%list%' => implode(', ou ', $reasonsNames), + '%list%' => implode(', ', $reasonsNames), ]]; } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ActivityUsersFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityUsersFilter.php new file mode 100644 index 000000000..2f6cd8462 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityUsersFilter.php @@ -0,0 +1,77 @@ +userRender = $userRender; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $orX = $qb->expr()->orX(); + + foreach ($data['accepted_users'] as $key => $user) { + $orX->add($qb->expr()->isMemberOf(':activity_users_filter_u' . $key, 'activity.users')); + $qb->setParameter('activity_users_filter_u' . $key, $user); + } + + $qb->andWhere($orX); + } + + public function applyOn() + { + return Declarations::ACTIVITY; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_users', PickUserDynamicType::class, [ + 'multiple' => true, + 'label' => 'Users', + ]); + } + + public function describeAction($data, $format = 'string') + { + $users = []; + + foreach ($data['accepted_users'] as $u) { + $users[] = $this->userRender->renderString($u, []); + } + + return ['Filtered activity by users: only %users%', [ + '%users%' => implode(', ', $users), + ]]; + } + + public function getTitle(): string + { + return 'Filter activity by users'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php new file mode 100644 index 000000000..dcdacd84a --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php @@ -0,0 +1,81 @@ +translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . Activity::class . ' activity_users_job_filter_act + JOIN activity_users_job_filter_act.users users WHERE users.userJob IN (:activity_users_job_filter_jobs) AND activity_users_job_filter_act = activity ' + ) + ) + ->setParameter('activity_users_job_filter_jobs', $data['jobs']); + } + + public function applyOn() + { + return Declarations::ACTIVITY; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('jobs', EntityType::class, [ + 'class' => UserJob::class, + 'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize($j->getLabel()), + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string') + { + return ['export.filter.activity.by_usersjob.Filtered activity by users job: only %jobs%', [ + '%jobs%' => implode( + ', ', + array_map( + fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()), + $data['jobs']->toArray() + ) + ), + ]]; + } + + public function getTitle() + { + return 'export.filter.activity.by_usersjob.Filter by users job'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/UsersScopeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/UsersScopeFilter.php new file mode 100644 index 000000000..61b12264e --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Filter/UsersScopeFilter.php @@ -0,0 +1,88 @@ +scopeRepository = $scopeRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . Activity::class . ' activity_users_scope_filter_act + JOIN activity_users_scope_filter_act.users users WHERE users.mainScope IN (:activity_users_scope_filter_scopes) AND activity_users_scope_filter_act = activity ' + ) + ) + ->setParameter('activity_users_scope_filter_scopes', $data['scopes']); + } + + public function applyOn() + { + return Declarations::ACTIVITY; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('scopes', EntityType::class, [ + 'class' => Scope::class, + 'choices' => $this->scopeRepository->findAllActive(), + 'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()), + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string') + { + return ['export.filter.activity.by_usersscope.Filtered activity by users scope: only %scopes%', [ + '%scopes%' => implode( + ', ', + array_map( + fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()), + $data['scopes']->toArray() + ) + ), + ]]; + } + + public function getTitle() + { + return 'export.filter.activity.by_usersscope.Filter by users scope'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php index 78f3ee79d..ff4f42ec4 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Aggregator\ACPAggregators; use Chill\ActivityBundle\Entity\Activity; -use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByUserAggregator; +use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByCreatorAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; @@ -22,7 +22,7 @@ use Doctrine\ORM\EntityManagerInterface; */ final class ByUserAggregatorTest extends AbstractAggregatorTest { - private ByUserAggregator $aggregator; + private ByCreatorAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php index 1d33fedbe..1265804f9 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Aggregator\ACPAggregators; use Chill\ActivityBundle\Entity\Activity; -use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\UserScopeAggregator; +use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\CreatorScopeAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; @@ -22,7 +22,7 @@ use Doctrine\ORM\EntityManagerInterface; */ final class UserScopeAggregatorTest extends AbstractAggregatorTest { - private UserScopeAggregator $aggregator; + private CreatorScopeAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php index d6e3a465e..47e76e25c 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Filter\ACPFilters; use Chill\ActivityBundle\Entity\Activity; -use Chill\ActivityBundle\Export\Filter\ACPFilters\ByUserFilter; +use Chill\ActivityBundle\Export\Filter\ACPFilters\ByCreatorFilter; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Doctrine\ORM\EntityManagerInterface; @@ -23,7 +23,7 @@ use Doctrine\ORM\EntityManagerInterface; */ final class ByUserFilterTest extends AbstractFilterTest { - private ByUserFilter $filter; + private ByCreatorFilter $filter; protected function setUp(): void { diff --git a/src/Bundle/ChillActivityBundle/config/services/export.yaml b/src/Bundle/ChillActivityBundle/config/services/export.yaml index bdaae8c8a..224075e6f 100644 --- a/src/Bundle/ChillActivityBundle/config/services/export.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/export.yaml @@ -4,18 +4,15 @@ services: autoconfigure: true ## Indicators - chill.activity.export.count_activity_linked_to_person: - class: Chill\ActivityBundle\Export\Export\LinkedToPerson\CountActivity + Chill\ActivityBundle\Export\Export\LinkedToPerson\CountActivity: tags: - { name: chill.export, alias: 'count_activity_linked_to_person' } - chill.activity.export.sum_activity_duration_linked_to_person: - class: Chill\ActivityBundle\Export\Export\LinkedToPerson\StatActivityDuration + Chill\ActivityBundle\Export\Export\LinkedToPerson\StatActivityDuration: tags: - { name: chill.export, alias: 'sum_activity_duration_linked_to_person' } - chill.activity.export.list_activity_linked_to_person: - class: Chill\ActivityBundle\Export\Export\LinkedToPerson\ListActivity + Chill\ActivityBundle\Export\Export\LinkedToPerson\ListActivity: tags: - { name: chill.export, alias: 'list_activity_linked_to_person' } @@ -55,6 +52,10 @@ services: tags: - { name: chill.export_filter, alias: 'activity_date_filter' } + Chill\ActivityBundle\Export\Filter\ActivityUsersFilter: + tags: + - { name: chill.export_filter, alias: 'activity_users_filter' } + chill.activity.export.reason_filter: class: Chill\ActivityBundle\Export\Filter\PersonFilters\ActivityReasonFilter tags: @@ -77,10 +78,9 @@ services: tags: - { name: chill.export_filter, alias: 'activity_locationtype_filter' } - chill.activity.export.byuser_filter: # TMS (M2M) - class: Chill\ActivityBundle\Export\Filter\ACPFilters\ByUserFilter + Chill\ActivityBundle\Export\Filter\ACPFilters\ByCreatorFilter: tags: - - { name: chill.export_filter, alias: 'activity_byuser_filter' } + - { name: chill.export_filter, alias: 'activity_bycreator_filter' } chill.activity.export.emergency_filter: class: Chill\ActivityBundle\Export\Filter\ACPFilters\EmergencyFilter @@ -112,9 +112,16 @@ services: tags: - { name: chill.export_filter, alias: 'activity_userscope_filter' } + Chill\ActivityBundle\Export\Filter\UsersJobFilter: + tags: + - { name: chill.export_filter, alias: 'activity_usersjob_filter' } + + Chill\ActivityBundle\Export\Filter\UsersScopeFilter: + tags: + - { name: chill.export_filter, alias: 'activity_usersscope_filter' } + ## Aggregators - chill.activity.export.reason_aggregator: - class: Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator + Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator: tags: - { name: chill.export_aggregator, alias: activity_reason_aggregator } @@ -138,10 +145,9 @@ services: tags: - { name: chill.export_aggregator, alias: activity_date_aggregator } - chill.activity.export.byuser_aggregator: - class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByUserAggregator + Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByCreatorAggregator: tags: - - { name: chill.export_aggregator, alias: activity_byuser_aggregator } + - { name: chill.export_aggregator, alias: activity_by_creator_aggregator } chill.activity.export.bythirdparty_aggregator: class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByThirdpartyAggregator @@ -158,7 +164,18 @@ services: tags: - { name: chill.export_aggregator, alias: activity_bysocialissue_aggregator } - chill.activity.export.userscope_aggregator: - class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\UserScopeAggregator + Chill\ActivityBundle\Export\Aggregator\ACPAggregators\CreatorScopeAggregator: tags: - - { name: chill.export_aggregator, alias: activity_userscope_aggregator } + - { name: chill.export_aggregator, alias: activity_creator_scope_aggregator } + + Chill\ActivityBundle\Export\Aggregator\ActivityUsersAggregator: + tags: + - { name: chill.export_aggregator, alias: activity_users_aggregator } + + Chill\ActivityBundle\Export\Aggregator\ActivityUsersScopeAggregator: + tags: + - { name: chill.export_aggregator, alias: activity_users_scope_aggregator } + + Chill\ActivityBundle\Export\Aggregator\ActivityUsersJobAggregator: + tags: + - { name: chill.export_aggregator, alias: activity_users_job_aggregator } diff --git a/src/Bundle/ChillActivityBundle/migrations/Version20221014130554.php b/src/Bundle/ChillActivityBundle/migrations/Version20221014130554.php new file mode 100644 index 000000000..4cbe47d26 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/migrations/Version20221014130554.php @@ -0,0 +1,59 @@ +addSql('ALTER TABLE activity DROP updatedAt'); + $this->addSql('ALTER TABLE activity DROP createdAt'); + $this->addSql('ALTER TABLE activity DROP updatedBy_id'); + $this->addSql('ALTER TABLE activity DROP createdBy_id'); + + // rename some indexes on activity + $this->addSql('ALTER INDEX idx_ac74095a217bbb47 RENAME TO idx_55026b0c217bbb47'); + $this->addSql('ALTER INDEX idx_ac74095a682b5931 RENAME TO idx_55026b0c682b5931'); + $this->addSql('ALTER INDEX idx_ac74095aa76ed395 RENAME TO idx_55026b0ca76ed395'); + $this->addSql('ALTER INDEX idx_ac74095ac54c8c93 RENAME TO idx_55026b0cc54c8c93'); + } + + public function getDescription(): string + { + return 'Track update and create on activity'; + } + + public function up(Schema $schema): void + { + $this->addSql('ALTER TABLE activity ADD updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + $this->addSql('ALTER TABLE activity ADD createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + $this->addSql('ALTER TABLE activity ADD updatedBy_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE activity ADD createdBy_id INT DEFAULT NULL'); + $this->addSql('COMMENT ON COLUMN activity.updatedAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN activity.createdAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE activity ADD CONSTRAINT FK_AC74095A65FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE activity ADD CONSTRAINT FK_AC74095A3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_AC74095A65FF1AEC ON activity (updatedBy_id)'); + $this->addSql('CREATE INDEX IDX_AC74095A3174800F ON activity (createdBy_id)'); + + // rename some indexes on activity + $this->addSql('ALTER INDEX idx_55026b0cc54c8c93 RENAME TO IDX_AC74095AC54C8C93'); + $this->addSql('ALTER INDEX idx_55026b0c217bbb47 RENAME TO IDX_AC74095A217BBB47'); + $this->addSql('ALTER INDEX idx_55026b0c682b5931 RENAME TO IDX_AC74095A682B5931'); + $this->addSql('ALTER INDEX idx_55026b0ca76ed395 RENAME TO IDX_AC74095AA76ED395'); + + $this->addSql('UPDATE activity SET updatedBy_id=user_id, createdBy_id=user_id, createdAt="date", updatedAt="date"'); + } +} diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index 9f8ed3d7c..6873b3736 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -119,15 +119,15 @@ Activity Presences: Presences aux activités # Crud crud: - activity_type: - title_new: Nouveau type d'activité - title_edit: Edition d'un type d'activité - activity_type_category: - title_new: Nouvelle catégorie de type d'activité - title_edit: Edition d'une catégorie de type d'activité - activity_presence: - title_new: Nouvelle Présence aux activités - title_edit: Edition d'une Présence aux activités + activity_type: + title_new: Nouveau type d'activité + title_edit: Edition d'un type d'activité + activity_type_category: + title_new: Nouvelle catégorie de type d'activité + title_edit: Edition d'une catégorie de type d'activité + activity_presence: + title_new: Nouvelle Présence aux activités + title_edit: Edition d'une Présence aux activités # activity reason admin ActivityReason list: Liste des sujets @@ -252,8 +252,6 @@ Filter by activity type: Filtrer les activités par type Filter activity by locationtype: Filtrer les activités par type de localisation 'Filtered activity by locationtype: only %types%': "Filtré par type de localisation: uniquement %types%" Accepted locationtype: Types de localisation -Filter activity by linked users: Filtrer les activités par TMS -'Filtered activity by linked users: only %users%': "Filtré par TMS: uniquement %users%" Accepted users: TMS(s) Filter activity by emergency: Filtrer les activités par urgence 'Filtered activity by emergency: only %emergency%': "Filtré par urgence: uniquement si %emergency%" @@ -269,7 +267,11 @@ Filter activity by linked socialaction: Filtrer les activités par action liée Filter activity by linked socialissue: Filtrer les activités par problématique liée 'Filtered activity by linked socialissue: only %issues%': "Filtré par problématique liée: uniquement %issues%" Filter activity by user: Filtrer les activités par créateur -'Filtered activity by user: only %users%': "Filtré par créateur: uniquement %users%" +Filter activity by users: Filtrer les activités par utilisateur participant +Filter activity by creator: Filtrer les activités par créateur de l'échange +'Filtered activity by user: only %users%': "Filtré par référent: uniquement %users%" +'Filtered activity by users: only %users%': "Filtré par utilisateurs participants: uniquement %users%" +'Filtered activity by creator: only %users%': "Filtré par créateur: uniquement %users%" Creators: Créateurs Filter activity by userscope: Filtrer les activités par service du créateur 'Filtered activity by userscope: only %scopes%': "Filtré par service du créateur: uniquement %scopes%" @@ -282,9 +284,14 @@ By reason: Par sujet By category of reason: Par catégorie de sujet Reason's level: Niveau du sujet Group by reasons: Sujet d'activité -Aggregate by activity user: Grouper les activités par utilisateur +Aggregate by activity user: Grouper les activités par référent +Aggregate by activity users: Grouper les activités par utilisateurs participants Aggregate by activity type: Grouper les activités par type Aggregate by activity reason: Grouper les activités par sujet +Aggregate by users scope: Grouper les activités par service principal de l'utilisateur +Users 's scope: Service principal des utilisateurs participants à l'activité +Aggregate by users job: Grouper les activités par métier des utilisateurs participants +Users 's job: Métier des utilisateurs participants à l'activité Group activity by locationtype: Grouper les activités par type de localisation Group activity by date: Grouper les activités par date @@ -294,7 +301,8 @@ by week: Par semaine for week: Semaine by year: Par année in year: En -Group activity by linked users: Grouper les activités par TMS impliqué +Group activity by creator: Grouper les activités par créateur de l'échange +Group activity by creator scope: Grouper les activités par service du créateur de l'échange Group activity by linked thirdparties: Grouper les activités par tiers impliqué Accepted thirdparty: Tiers impliqué Group activity by linked socialaction: Grouper les activités par action liée @@ -314,3 +322,13 @@ docgen: A basic context for activity: Contexte pour les échanges Accompanying period with a list of activities: Parcours d'accompagnement avec liste des échanges Accompanying period with a list of activities description: Ce contexte reprend les informations du parcours, et tous les échanges pour un parcours. Les échanges ne sont pas filtrés. + +export: + filter: + activity: + by_usersjob: + Filter by users job: Filtrer les activités par métier d'au moins un utilisateur participant + 'Filtered activity by users job: only %jobs%': 'Filtré par métier d''au moins un utilisateur participant: seulement %jobs%' + by_usersscope: + Filter by users scope: Filtrer les activités 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%' diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php index e2903d2b2..9b0ad9057 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php @@ -46,14 +46,7 @@ final class AgentAggregator implements AggregatorInterface } $qb->addSelect('caluser.id AS agent_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('agent_aggregator'); - } else { - $qb->groupBy('agent_aggregator'); - } + $qb->addGroupBy('agent_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php index d8ef43b9f..329e2e50e 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php @@ -47,14 +47,7 @@ class CancelReasonAggregator implements AggregatorInterface } $qb->addSelect('IDENTITY(cal.cancelReason) as cancel_reason_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('cancel_reason_aggregator'); - } else { - $qb->groupBy('cancel_reason_aggregator'); - } + $qb->addGroupBy('cancel_reason_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php index 6ae641a82..2a6f3b63e 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php @@ -46,14 +46,7 @@ final class JobAggregator implements AggregatorInterface } $qb->addSelect('IDENTITY(caluser.userJob) as job_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('job_aggregator'); - } else { - $qb->groupBy('job_aggregator'); - } + $qb->addGroupBy('job_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php index ffa77c6c2..687dc9096 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php @@ -40,14 +40,7 @@ final class LocationAggregator implements AggregatorInterface $qb->join('cal.location', 'calloc'); } $qb->addSelect('IDENTITY(cal.location) as location_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('location_aggregator'); - } else { - $qb->groupBy('location_aggregator'); - } + $qb->addGroupBy('location_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php index bb020d24a..b23b304f6 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php @@ -46,14 +46,7 @@ final class LocationTypeAggregator implements AggregatorInterface } $qb->addSelect('IDENTITY(calloc.locationType) as location_type_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('location_type_aggregator'); - } else { - $qb->groupBy('location_type_aggregator'); - } + $qb->addGroupBy('location_type_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php index 40bc43629..7b2a5e898 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php @@ -28,13 +28,7 @@ class MonthYearAggregator implements AggregatorInterface { $qb->addSelect("to_char(cal.startDate, 'MM-YYYY') AS month_year_aggregator"); // $qb->addSelect("extract(month from age(cal.startDate, cal.endDate)) AS month_aggregator"); - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('month_year_aggregator'); - } else { - $qb->groupBy('month_year_aggregator'); - } + $qb->addGroupBy('month_year_aggregator'); } public function applyOn(): string @@ -54,10 +48,7 @@ class MonthYearAggregator implements AggregatorInterface return 'by month and year'; } - $month = (int) substr($value, 0, 2); - $year = (int) substr($value, 3, 4); - - return strftime('%B %G', mktime(0, 0, 0, $month, 1, $year)); + return $value; }; } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php index 37b66e95d..b16b06d84 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php @@ -46,14 +46,7 @@ final class ScopeAggregator implements AggregatorInterface } $qb->addSelect('IDENTITY(caluser.mainScope) as scope_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('scope_aggregator'); - } else { - $qb->groupBy('scope_aggregator'); - } + $qb->addGroupBy('scope_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php b/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php index 486980d9c..06d6defca 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php @@ -71,9 +71,9 @@ class CountCalendars implements ExportInterface, GroupedExportInterface return ['export_result']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR); + return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR); } public function getTitle(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php index 3ee328505..ddecba415 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php @@ -71,9 +71,9 @@ class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface return ['export_result']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php index 39ae7d555..d99e73a2e 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php @@ -71,9 +71,9 @@ class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface return ['export_result']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php index 140e8410d..18a4b0f4b 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php @@ -76,7 +76,7 @@ class AgentFilter implements FilterInterface return [ 'Filtered by agent: only %agents%', [ - '%agents' => implode(', ou ', $users), + '%agents' => implode(', ', $users), ], ]; } diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php index 19a142bce..0f0f42adc 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php @@ -90,7 +90,7 @@ class JobFilter implements FilterInterface } return ['Filtered by agent job: only %jobs%', [ - '%jobs%' => implode(', ou ', $userJobs), + '%jobs%' => implode(', ', $userJobs), ]]; } diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php index d07adb247..4d84543a3 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php @@ -90,7 +90,7 @@ class ScopeFilter implements FilterInterface } return ['Filtered by agent scope: only %scopes%', [ - '%scopes%' => implode(', ou ', $scopes), + '%scopes%' => implode(', ', $scopes), ]]; } diff --git a/src/Bundle/ChillDocGeneratorBundle/Service/Context/BaseContextData.php b/src/Bundle/ChillDocGeneratorBundle/Service/Context/BaseContextData.php index cbeeaeb16..e7b56ed88 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Service/Context/BaseContextData.php +++ b/src/Bundle/ChillDocGeneratorBundle/Service/Context/BaseContextData.php @@ -42,6 +42,9 @@ class BaseContextData $data['createdAt'] = $this->normalizer->normalize(new DateTimeImmutable(), 'docgen', [ 'docgen:expects' => DateTimeImmutable::class, 'groups' => ['docgen:read'], ]); + $data['createdAtDate'] = $this->normalizer->normalize(new DateTimeImmutable('today'), 'docgen', [ + 'docgen:expects' => DateTimeImmutable::class, 'groups' => ['docgen:read'], + ]); $data['location'] = $this->normalizer->normalize( $user instanceof User ? $user->getCurrentLocation() : null, 'docgen', diff --git a/src/Bundle/ChillDocStoreBundle/Entity/AccompanyingCourseDocument.php b/src/Bundle/ChillDocStoreBundle/Entity/AccompanyingCourseDocument.php index 1fc09a907..d7bd1f274 100644 --- a/src/Bundle/ChillDocStoreBundle/Entity/AccompanyingCourseDocument.php +++ b/src/Bundle/ChillDocStoreBundle/Entity/AccompanyingCourseDocument.php @@ -34,7 +34,7 @@ class AccompanyingCourseDocument extends Document implements HasScopesInterface public function getScopes(): iterable { - if (null !== $this->course) { + if (null === $this->course) { return []; } diff --git a/src/Bundle/ChillMainBundle/Controller/ExportController.php b/src/Bundle/ChillMainBundle/Controller/ExportController.php index d00d9d040..96c9ed08d 100644 --- a/src/Bundle/ChillMainBundle/Controller/ExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/ExportController.php @@ -22,6 +22,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\SessionInterface; @@ -191,18 +192,12 @@ class ExportController extends AbstractController case 'export': return $this->exportFormStep($request, $export, $alias); - break; - case 'formatter': return $this->formatterFormStep($request, $export, $alias); - break; - case 'generate': return $this->forwardToGenerate($request, $export, $alias); - break; - default: throw $this->createNotFoundException("The given step '{$step}' is invalid"); } @@ -214,10 +209,8 @@ class ExportController extends AbstractController * @param string $alias * @param array $data the data from previous step. Required for steps 'formatter' and 'generate_formatter' * @param mixed $step - * - * @return \Symfony\Component\Form\Form */ - protected function createCreateFormExport($alias, $step, $data = []) + protected function createCreateFormExport($alias, $step, $data = []): FormInterface { /** @var \Chill\MainBundle\Export\ExportManager $exportManager */ $exportManager = $this->exportManager; @@ -475,8 +468,6 @@ class ExportController extends AbstractController * @param \Chill\MainBundle\Export\DirectExportInterface|\Chill\MainBundle\Export\ExportInterface $export * @param string $alias * - * @throws type - * * @return Response */ protected function selectCentersStep(Request $request, $export, $alias) @@ -543,6 +534,8 @@ class ExportController extends AbstractController } } } + + return ''; } /** diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index d7f3f0bc5..9164607a7 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -22,6 +22,7 @@ use Chill\MainBundle\Controller\UserController; use Chill\MainBundle\Controller\UserJobApiController; use Chill\MainBundle\Controller\UserJobController; use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface; +use Chill\MainBundle\Doctrine\DQL\Age; use Chill\MainBundle\Doctrine\DQL\Extract; use Chill\MainBundle\Doctrine\DQL\GetJsonFieldByKey; use Chill\MainBundle\Doctrine\DQL\JsonAggregate; @@ -248,6 +249,7 @@ class ChillMainExtension extends Extension implements 'datetime_functions' => [ 'EXTRACT' => Extract::class, 'TO_CHAR' => ToChar::class, + 'AGE' => Age::class, ], ], 'hydrators' => [ diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/ExportsCompilerPass.php b/src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/ExportsCompilerPass.php index ae85ba22c..5c351728a 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/ExportsCompilerPass.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/ExportsCompilerPass.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\MainBundle\DependencyInjection\CompilerPass; +use Chill\MainBundle\Export\ExportManager; use LogicException; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -30,53 +31,19 @@ class ExportsCompilerPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { - if (!$container->has('Chill\MainBundle\Export\ExportManager')) { - throw new LogicException('service Chill\MainBundle\Export\ExportManager ' + if (!$container->has(ExportManager::class)) { + throw new LogicException('service ' . ExportManager::class . ' ' . 'is not defined. It is required by ExportsCompilerPass'); } $chillManagerDefinition = $container->findDefinition( - 'Chill\MainBundle\Export\ExportManager' + ExportManager::class ); - $this->compileExports($chillManagerDefinition, $container); - $this->compileFilters($chillManagerDefinition, $container); - $this->compileAggregators($chillManagerDefinition, $container); $this->compileFormatters($chillManagerDefinition, $container); $this->compileExportElementsProvider($chillManagerDefinition, $container); } - private function compileAggregators( - Definition $chillManagerDefinition, - ContainerBuilder $container - ) { - $taggedServices = $container->findTaggedServiceIds( - 'chill.export_aggregator' - ); - - $knownAliases = []; - - foreach ($taggedServices as $id => $tagAttributes) { - foreach ($tagAttributes as $attributes) { - if (!isset($attributes['alias'])) { - throw new LogicException("the 'alias' attribute is missing in your " . - "service '{$id}' definition"); - } - - if (array_search($attributes['alias'], $knownAliases, true)) { - throw new LogicException('There is already a chill.export_aggregator service with alias ' - . $attributes['alias'] . '. Choose another alias.'); - } - $knownAliases[] = $attributes['alias']; - - $chillManagerDefinition->addMethodCall( - 'addAggregator', - [new Reference($id), $attributes['alias']] - ); - } - } - } - private function compileExportElementsProvider( Definition $chillManagerDefinition, ContainerBuilder $container @@ -108,68 +75,6 @@ class ExportsCompilerPass implements CompilerPassInterface } } - private function compileExports( - Definition $chillManagerDefinition, - ContainerBuilder $container - ) { - $taggedServices = $container->findTaggedServiceIds( - 'chill.export' - ); - - $knownAliases = []; - - foreach ($taggedServices as $id => $tagAttributes) { - foreach ($tagAttributes as $attributes) { - if (!isset($attributes['alias'])) { - throw new LogicException("the 'alias' attribute is missing in your " . - "service '{$id}' definition"); - } - - if (array_search($attributes['alias'], $knownAliases, true)) { - throw new LogicException('There is already a chill.export service with alias ' - . $attributes['alias'] . '. Choose another alias.'); - } - $knownAliases[] = $attributes['alias']; - - $chillManagerDefinition->addMethodCall( - 'addExport', - [new Reference($id), $attributes['alias']] - ); - } - } - } - - private function compileFilters( - Definition $chillManagerDefinition, - ContainerBuilder $container - ) { - $taggedServices = $container->findTaggedServiceIds( - 'chill.export_filter' - ); - - $knownAliases = []; - - foreach ($taggedServices as $id => $tagAttributes) { - foreach ($tagAttributes as $attributes) { - if (!isset($attributes['alias'])) { - throw new LogicException("the 'alias' attribute is missing in your " . - "service '{$id}' definition"); - } - - if (array_search($attributes['alias'], $knownAliases, true)) { - throw new LogicException('There is already a chill.export_filter service with alias ' - . $attributes['alias'] . '. Choose another alias.'); - } - $knownAliases[] = $attributes['alias']; - - $chillManagerDefinition->addMethodCall( - 'addFilter', - [new Reference($id), $attributes['alias']] - ); - } - } - } - private function compileFormatters( Definition $chillManagerDefinition, ContainerBuilder $container diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/Age.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/Age.php new file mode 100644 index 000000000..ad148fe99 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/Age.php @@ -0,0 +1,54 @@ +value2) { + return sprintf( + 'AGE(%s, %s)', + $this->value1->dispatch($sqlWalker), + $this->value2->dispatch($sqlWalker) + ); + } + + return sprintf( + 'AGE(%s)', + $this->value1->dispatch($sqlWalker), + ); + } + + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->value1 = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_COMMA); + + $this->value2 = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbExistsInArray.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbExistsInArray.php index 3dbe9145c..1556753b6 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbExistsInArray.php +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbExistsInArray.php @@ -27,7 +27,7 @@ class JsonbExistsInArray extends FunctionNode return sprintf( '%s ?? %s', $this->expr1->dispatch($sqlWalker), - $sqlWalker->walkInputParameter($this->expr2) + $this->expr2->dispatch($sqlWalker) ); } diff --git a/src/Bundle/ChillMainBundle/Doctrine/Type/NativeDateIntervalType.php b/src/Bundle/ChillMainBundle/Doctrine/Type/NativeDateIntervalType.php index 432c53aad..896ddb600 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/Type/NativeDateIntervalType.php +++ b/src/Bundle/ChillMainBundle/Doctrine/Type/NativeDateIntervalType.php @@ -17,6 +17,7 @@ use Doctrine\DBAL\Types\ConversionException; use Doctrine\DBAL\Types\DateIntervalType; use Exception; +use LogicException; use function count; use function current; use function preg_match; @@ -40,7 +41,7 @@ class NativeDateIntervalType extends DateIntervalType return $value->format(self::FORMAT); } - throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'DateInterval']); + throw ConversionException::conversionFailedInvalidType($value, 'string', ['null', 'DateInterval']); } public function convertToPHPValue($value, AbstractPlatform $platform) @@ -80,7 +81,7 @@ class NativeDateIntervalType extends DateIntervalType protected function createConversionException($value, $exception = null) { - return ConversionException::conversionFailedFormat($value, $this->getName(), 'xx year xx mons xx days 01:02:03', $exception); + return ConversionException::conversionFailedFormat($value, 'string', 'xx year xx mons xx days 01:02:03', $exception); } private function convertEntry(&$strings) @@ -125,5 +126,7 @@ class NativeDateIntervalType extends DateIntervalType return $intervalSpec; } + + throw new LogicException(); } } diff --git a/src/Bundle/ChillMainBundle/Entity/Scope.php b/src/Bundle/ChillMainBundle/Entity/Scope.php index b0cb9046b..df4f75e4c 100644 --- a/src/Bundle/ChillMainBundle/Entity/Scope.php +++ b/src/Bundle/ChillMainBundle/Entity/Scope.php @@ -28,6 +28,11 @@ use Symfony\Component\Serializer\Annotation\Groups; */ class Scope { + /** + * @ORM\Column(type="boolean", nullable=false, options={"default": true}) + */ + private bool $active = true; + /** * @ORM\Id * @ORM\Column(name="id", type="integer") @@ -88,6 +93,18 @@ class Scope return $this->roleScopes; } + public function isActive(): bool + { + return $this->active; + } + + public function setActive(bool $active): Scope + { + $this->active = $active; + + return $this; + } + /** * @param $name * diff --git a/src/Bundle/ChillMainBundle/Export/DirectExportInterface.php b/src/Bundle/ChillMainBundle/Export/DirectExportInterface.php index 9e89ebaad..9703a42de 100644 --- a/src/Bundle/ChillMainBundle/Export/DirectExportInterface.php +++ b/src/Bundle/ChillMainBundle/Export/DirectExportInterface.php @@ -11,28 +11,22 @@ declare(strict_types=1); namespace Chill\MainBundle\Export; -use Symfony\Component\Security\Core\Role\Role; +use Symfony\Component\HttpFoundation\Response; interface DirectExportInterface extends ExportElementInterface { /** * Generate the export. - * - * @return \Symfony\Component\HttpFoundation\Response */ - public function generate(array $acl, array $data = []); + public function generate(array $acl, array $data = []): Response; /** * get a description, which will be used in UI (and translated). - * - * @return string */ - public function getDescription(); + public function getDescription(): string; /** * authorized role. - * - * @return \Symfony\Component\Security\Core\Role\Role */ - public function requiredRole(); + public function requiredRole(): string; } diff --git a/src/Bundle/ChillMainBundle/Export/ExportManager.php b/src/Bundle/ChillMainBundle/Export/ExportManager.php index aec5998ff..f39926083 100644 --- a/src/Bundle/ChillMainBundle/Export/ExportManager.php +++ b/src/Bundle/ChillMainBundle/Export/ExportManager.php @@ -14,10 +14,8 @@ namespace Chill\MainBundle\Export; use Chill\MainBundle\Form\Type\Export\ExportType; use Chill\MainBundle\Form\Type\Export\PickCenterType; use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; -use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; use Generator; -use InvalidArgumentException; use LogicException; use Psr\Log\LoggerInterface; use RuntimeException; @@ -50,8 +48,6 @@ class ExportManager private AuthorizationHelperInterface $authorizationHelper; - private EntityManagerInterface $em; - /** * Collected Exports, injected by DI. * @@ -82,16 +78,28 @@ class ExportManager public function __construct( LoggerInterface $logger, - EntityManagerInterface $em, AuthorizationCheckerInterface $authorizationChecker, AuthorizationHelperInterface $authorizationHelper, - TokenStorageInterface $tokenStorage + TokenStorageInterface $tokenStorage, + iterable $exports, + iterable $aggregators, + iterable $filters + //iterable $formatters, + //iterable $exportElementProvider ) { $this->logger = $logger; - $this->em = $em; $this->authorizationChecker = $authorizationChecker; $this->authorizationHelper = $authorizationHelper; $this->user = $tokenStorage->getToken()->getUser(); + $this->exports = iterator_to_array($exports); + $this->aggregators = iterator_to_array($aggregators); + $this->filters = iterator_to_array($filters); + // NOTE: PHP crashes on the next line (exit error code 11). This is desactivated until further investigation + //$this->formatters = iterator_to_array($formatters); + + //foreach ($exportElementProvider as $prefix => $provider) { + // $this->addExportElementsProvider($provider, $prefix); + //} } /** @@ -141,52 +149,17 @@ class ExportManager } } - /** - * add an aggregator. - * - * @internal used by DI - * - * @param string $alias - */ - public function addAggregator(AggregatorInterface $aggregator, $alias) - { - $this->aggregators[$alias] = $aggregator; - } - - /** - * add an export. - * - * @internal used by DI - * - * @param DirectExportInterface|ExportInterface $export - * @param type $alias - */ - public function addExport($export, $alias) - { - if ($export instanceof ExportInterface || $export instanceof DirectExportInterface) { - $this->exports[$alias] = $export; - } else { - throw new InvalidArgumentException(sprintf( - 'The export with alias %s ' - . 'does not implements %s or %s.', - $alias, - ExportInterface::class, - DirectExportInterface::class - )); - } - } - public function addExportElementsProvider(ExportElementsProviderInterface $provider, $prefix) { foreach ($provider->getExportElements() as $suffix => $element) { $alias = $prefix . '_' . $suffix; if ($element instanceof ExportInterface) { - $this->addExport($element, $alias); + $this->exports[$alias] = $element; } elseif ($element instanceof FilterInterface) { - $this->addFilter($element, $alias); + $this->filters[$alias] = $element; } elseif ($element instanceof AggregatorInterface) { - $this->addAggregator($element, $alias); + $this->aggregators[$alias] = $element; } elseif ($element instanceof FormatterInterface) { $this->addFormatter($element, $alias); } else { @@ -196,24 +169,12 @@ class ExportManager } } - /** - * add a Filter. - * - * @internal Normally used by the dependency injection - * - * @param string $alias - */ - public function addFilter(FilterInterface $filter, $alias) - { - $this->filters[$alias] = $filter; - } - /** * add a formatter. * * @internal used by DI * - * @param type $alias + * @param string $alias */ public function addFormatter(FormatterInterface $formatter, $alias) { @@ -231,7 +192,6 @@ class ExportManager public function generate($exportAlias, array $pickedCentersData, array $data, array $formatterData) { $export = $this->getExport($exportAlias); - //$qb = $this->em->createQueryBuilder(); $centers = $this->getPickedCenters($pickedCentersData); if ($export instanceof DirectExportInterface) { diff --git a/src/Bundle/ChillMainBundle/Form/Type/PickLocationTypeType.php b/src/Bundle/ChillMainBundle/Form/Type/PickLocationTypeType.php new file mode 100644 index 000000000..6774e0941 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Form/Type/PickLocationTypeType.php @@ -0,0 +1,50 @@ +translatableStringHelper = $translatableStringHelper; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver + ->setDefaults([ + 'class' => LocationType::class, + 'choice_label' => function (LocationType $type) { + return $this->translatableStringHelper->localize($type->getTitle()); + }, + 'placeholder' => 'Pick a location type', + 'required' => false, + 'attr' => ['class' => 'select2'], + 'label' => 'Location type', + 'multiple' => false, + ]) + ->setAllowedTypes('multiple', ['bool']); + } + + public function getParent(): string + { + return EntityType::class; + } +} diff --git a/src/Bundle/ChillMainBundle/Form/Type/PickUserLocationType.php b/src/Bundle/ChillMainBundle/Form/Type/PickUserLocationType.php new file mode 100644 index 000000000..792daa39e --- /dev/null +++ b/src/Bundle/ChillMainBundle/Form/Type/PickUserLocationType.php @@ -0,0 +1,57 @@ +translatableStringHelper = $translatableStringHelper; + $this->locationRepository = $locationRepository; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver + ->setDefaults([ + 'class' => Location::class, + 'choices' => $this->locationRepository->findByPublicLocations(), + 'choice_label' => function (Location $entity) { + return $entity->getName() ? + $entity->getName() . ' (' . $this->translatableStringHelper->localize($entity->getLocationType()->getTitle()) . ')' : + $this->translatableStringHelper->localize($entity->getLocationType()->getTitle()); + }, + 'placeholder' => 'Pick a location', + 'required' => false, + 'attr' => ['class' => 'select2'], + 'label' => 'Current location', + 'multiple' => false, + ]) + ->setAllowedTypes('multiple', ['bool']); + } + + public function getParent(): string + { + return EntityType::class; + } +} diff --git a/src/Bundle/ChillMainBundle/Form/Type/ScopePickerType.php b/src/Bundle/ChillMainBundle/Form/Type/ScopePickerType.php index 06f6019d7..1cba1ba58 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/ScopePickerType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/ScopePickerType.php @@ -15,9 +15,9 @@ use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Form\DataMapper\ScopePickerDataMapper; -use Chill\MainBundle\Repository\ScopeRepository; use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; -use Chill\MainBundle\Templating\TranslatableStringHelper; +use Chill\MainBundle\Templating\TranslatableStringHelperInterface; +use RuntimeException; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\HiddenType; @@ -26,11 +26,9 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Role\Role; -use Symfony\Component\Security\Core\Security; -use function array_map; +use Symfony\Component\Security\Core\Security; use function count; /** @@ -44,47 +42,37 @@ use function count; */ class ScopePickerType extends AbstractType { - protected AuthorizationHelperInterface $authorizationHelper; + private AuthorizationHelperInterface $authorizationHelper; - /** - * @var ScopeRepository - */ - protected $scopeRepository; + private Security $security; - protected Security $security; - - /** - * @var TokenStorageInterface - */ - protected $tokenStorage; - - /** - * @var TranslatableStringHelper - */ - protected $translatableStringHelper; + private TranslatableStringHelperInterface $translatableStringHelper; public function __construct( AuthorizationHelperInterface $authorizationHelper, - TokenStorageInterface $tokenStorage, - ScopeRepository $scopeRepository, Security $security, - TranslatableStringHelper $translatableStringHelper + TranslatableStringHelperInterface $translatableStringHelper ) { $this->authorizationHelper = $authorizationHelper; - $this->tokenStorage = $tokenStorage; - $this->scopeRepository = $scopeRepository; $this->security = $security; $this->translatableStringHelper = $translatableStringHelper; } public function buildForm(FormBuilderInterface $builder, array $options) { - $items = $this->authorizationHelper->getReachableScopes( - $this->security->getUser(), - $options['role'] instanceof Role ? $options['role']->getRole() : $options['role'], - $options['center'] + $items = array_filter( + $this->authorizationHelper->getReachableScopes( + $this->security->getUser(), + $options['role'] instanceof Role ? $options['role']->getRole() : $options['role'], + $options['center'] + ), + static function (Scope $s) { return $s->isActive(); } ); + if (0 === count($items)) { + throw new RuntimeException('no scopes are reachable. This form should not be shown to user'); + } + if (1 !== count($items)) { $builder->add('scope', EntityType::class, [ 'class' => Scope::class, @@ -123,35 +111,4 @@ class ScopePickerType extends AbstractType ->setRequired('role') ->setAllowedTypes('role', ['string', Role::class]); } - - /** - * @param array|Center|Center[] $center - * @param string $role - * - * @return \Doctrine\ORM\QueryBuilder - */ - protected function buildAccessibleScopeQuery($center, $role) - { - $roles = $this->authorizationHelper->getParentRoles($role); - $roles[] = $role; - $centers = $center instanceof Center ? [$center] : $center; - - $qb = $this->scopeRepository->createQueryBuilder('s'); - $qb - // jointure to center - ->join('s.roleScopes', 'rs') - ->join('rs.permissionsGroups', 'pg') - ->join('pg.groupCenters', 'gc') - // add center constraint - ->where($qb->expr()->in('IDENTITY(gc.center)', ':centers')) - ->setParameter('centers', array_map(static fn (Center $c) => $c->getId(), $centers)) - // role constraints - ->andWhere($qb->expr()->in('rs.role', ':roles')) - ->setParameter('roles', $roles) - // user contraint - ->andWhere(':user MEMBER OF gc.users') - ->setParameter('user', $this->tokenStorage->getToken()->getUser()); - - return $qb; - } } diff --git a/src/Bundle/ChillMainBundle/Form/UserCurrentLocationType.php b/src/Bundle/ChillMainBundle/Form/UserCurrentLocationType.php index 3f839d3ed..bf6a5d172 100644 --- a/src/Bundle/ChillMainBundle/Form/UserCurrentLocationType.php +++ b/src/Bundle/ChillMainBundle/Form/UserCurrentLocationType.php @@ -11,39 +11,14 @@ declare(strict_types=1); namespace Chill\MainBundle\Form; -use Chill\MainBundle\Entity\Location; -use Chill\MainBundle\Repository\LocationRepository; -use Chill\MainBundle\Templating\TranslatableStringHelper; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; +use Chill\MainBundle\Form\Type\PickUserLocationType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; class UserCurrentLocationType extends AbstractType { - private LocationRepository $locationRepository; - - private TranslatableStringHelper $translatableStringHelper; - - public function __construct(TranslatableStringHelper $translatableStringHelper, LocationRepository $locationRepository) - { - $this->translatableStringHelper = $translatableStringHelper; - $this->locationRepository = $locationRepository; - } - public function buildForm(FormBuilderInterface $builder, array $options) { - $builder - ->add('currentLocation', EntityType::class, [ - 'class' => Location::class, - 'choices' => $this->locationRepository->findByPublicLocations(), - 'choice_label' => function (Location $entity) { - return $entity->getName() ? - $entity->getName() . ' (' . $this->translatableStringHelper->localize($entity->getLocationType()->getTitle()) . ')' : - $this->translatableStringHelper->localize($entity->getLocationType()->getTitle()); - }, - 'placeholder' => 'Pick a location', - 'required' => false, - 'attr' => ['class' => 'select2'], - ]); + $builder->add('currentLocation', PickUserLocationType::class); } } diff --git a/src/Bundle/ChillMainBundle/Repository/ScopeRepository.php b/src/Bundle/ChillMainBundle/Repository/ScopeRepository.php index 21d05e538..158678fb6 100644 --- a/src/Bundle/ChillMainBundle/Repository/ScopeRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/ScopeRepository.php @@ -43,6 +43,15 @@ final class ScopeRepository implements ScopeRepositoryInterface return $this->repository->findAll(); } + public function findAllActive(): array + { + $qb = $this->repository->createQueryBuilder('s'); + + $qb->where('s.active = \'TRUE\''); + + return $qb->getQuery()->getResult(); + } + /** * @param mixed|null $limit * @param mixed|null $offset diff --git a/src/Bundle/ChillMainBundle/Repository/ScopeRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/ScopeRepositoryInterface.php index 1093513f1..3cfb6042d 100644 --- a/src/Bundle/ChillMainBundle/Repository/ScopeRepositoryInterface.php +++ b/src/Bundle/ChillMainBundle/Repository/ScopeRepositoryInterface.php @@ -22,10 +22,15 @@ interface ScopeRepositoryInterface extends ObjectRepository public function find($id, $lockMode = null, $lockVersion = null): ?Scope; /** - * @return Scope[] + * @return array|Scope[] */ public function findAll(): array; + /** + * @return array|Scope[] + */ + public function findAllActive(): array; + /** * @param null|mixed $limit * @param null|mixed $offset diff --git a/src/Bundle/ChillMainBundle/Repository/UserJobRepository.php b/src/Bundle/ChillMainBundle/Repository/UserJobRepository.php index a137727cb..0be3e1b9c 100644 --- a/src/Bundle/ChillMainBundle/Repository/UserJobRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/UserJobRepository.php @@ -14,9 +14,8 @@ namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\UserJob; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; -use Doctrine\Persistence\ObjectRepository; -class UserJobRepository implements ObjectRepository +class UserJobRepository implements UserJobRepositoryInterface { private EntityRepository $repository; @@ -38,6 +37,11 @@ class UserJobRepository implements ObjectRepository return $this->repository->findAll(); } + public function findAllActive(): array + { + return $this->repository->findBy(['active' => true]); + } + /** * @param mixed|null $limit * @param mixed|null $offset @@ -49,12 +53,12 @@ class UserJobRepository implements ObjectRepository return $this->repository->findBy($criteria, $orderBy, $limit, $offset); } - public function findOneBy(array $criteria) + public function findOneBy(array $criteria): ?UserJob { return $this->repository->findOneBy($criteria); } - public function getClassName() + public function getClassName(): string { return UserJob::class; } diff --git a/src/Bundle/ChillMainBundle/Repository/UserJobRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/UserJobRepositoryInterface.php new file mode 100644 index 000000000..a752f452b --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/UserJobRepositoryInterface.php @@ -0,0 +1,42 @@ +getQueryBuilders() as $qb) { + foreach ($this->getFormData() as $data) { + yield [clone $qb, $data]; + } + } + } + /** * provide data for `testAlterQuery`. */ @@ -95,6 +107,28 @@ abstract class AbstractAggregatorTest extends KernelTestCase */ abstract public function getQueryBuilders(); + /** + * Compare aliases array before and after that aggregator alter query. + * + * @dataProvider dataProviderAliasDidNotDisappears + * + * @return void + */ + public function testAliasDidNotDisappears(QueryBuilder $qb, array $data) + { + $aliases = $qb->getAllAliases(); + + $this->getAggregator()->alterQuery($qb, $data); + + $alteredQuery = $qb->getAllAliases(); + + $this->assertGreaterThanOrEqual(count($aliases), count($alteredQuery)); + + foreach ($aliases as $alias) { + $this->assertContains($alias, $alteredQuery); + } + } + /** * test the alteration of query by the filter. * diff --git a/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php b/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php index 88607e7d9..e1c8665ec 100644 --- a/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php +++ b/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php @@ -40,6 +40,18 @@ abstract class AbstractFilterTest extends KernelTestCase $this->prophet = $this->getProphet(); } + /** + * provide data for `testAliasDidNotDisappears`. + */ + public function dataProviderAliasDidNotDisappears() + { + foreach ($this->getQueryBuilders() as $qb) { + foreach ($this->getFormData() as $data) { + yield [clone $qb, $data]; + } + } + } + public function dataProviderAlterQuery() { foreach ($this->getQueryBuilders() as $qb) { @@ -87,6 +99,28 @@ abstract class AbstractFilterTest extends KernelTestCase */ abstract public function getQueryBuilders(); + /** + * Compare aliases array before and after that filter alter query. + * + * @dataProvider dataProviderAliasDidNotDisappears + * + * @return void + */ + public function testAliasDidNotDisappears(QueryBuilder $qb, array $data) + { + $aliases = $qb->getAllAliases(); + + $this->getFilter()->alterQuery($qb, $data); + + $alteredQuery = $qb->getAllAliases(); + + $this->assertGreaterThanOrEqual(count($aliases), count($alteredQuery)); + + foreach ($aliases as $alias) { + $this->assertContains($alias, $alteredQuery); + } + } + /** * test the alteration of query by the filter. * diff --git a/src/Bundle/ChillMainBundle/Tests/Doctrine/DQL/AgeTest.php b/src/Bundle/ChillMainBundle/Tests/Doctrine/DQL/AgeTest.php new file mode 100644 index 000000000..90f6a24bf --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Doctrine/DQL/AgeTest.php @@ -0,0 +1,79 @@ +entityManager = self::$container->get(EntityManagerInterface::class); + } + + public function generateQueries(): iterable + { + yield [ + 'SELECT AGE(a.validFrom, a.validTo) FROM ' . Address::class . ' a', + [], + ]; + + yield [ + 'SELECT AGE(:date0, :date1) FROM ' . Address::class . ' a', + [ + 'date0' => new DateTimeImmutable('now'), + 'date1' => new DateTimeImmutable('2020-01-01'), + ], + ]; + + yield [ + 'SELECT AGE(a.validFrom, :date1) FROM ' . Address::class . ' a', + [ + 'date1' => new DateTimeImmutable('now'), + ], + ]; + + yield [ + 'SELECT AGE(:date0, a.validFrom) FROM ' . Address::class . ' a', + [ + 'date0' => new DateTimeImmutable('now'), + ], + ]; + } + + /** + * @dataProvider generateQueries + */ + public function testWorking(string $dql, array $args) + { + $dql = $this->entityManager->createQuery($dql)->setMaxResults(3); + + foreach ($args as $key => $value) { + $dql->setParameter($key, $value); + } + + $results = $dql->getResult(); + + $this->assertIsArray($results); + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Doctrine/DQL/JsonbExistsInArrayTest.php b/src/Bundle/ChillMainBundle/Tests/Doctrine/DQL/JsonbExistsInArrayTest.php new file mode 100644 index 000000000..67c41d3d9 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Doctrine/DQL/JsonbExistsInArrayTest.php @@ -0,0 +1,41 @@ +em = self::$container->get(EntityManagerInterface::class); + } + + public function testDQLFunctionWorks() + { + $result = $this->em + ->createQuery('SELECT JSONB_EXISTS_IN_ARRAY(u.attributes, :param) FROM ' . User::class . ' u') + ->setParameter('param', 'fr') + ->getResult(); + + $this->assertIsArray($result); + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Form/Type/ScopePickerTypeTest.php b/src/Bundle/ChillMainBundle/Tests/Form/Type/ScopePickerTypeTest.php new file mode 100644 index 000000000..d64f68951 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Form/Type/ScopePickerTypeTest.php @@ -0,0 +1,127 @@ +factory->create(ScopePickerType::class, null, [ + 'center' => new Center(), + 'role' => 'ONE_SCOPE', + ]); + + $view = $form->createView(); + + $this->assertContains('hidden', $view['scope']->vars['block_prefixes']); + } + + public function testBuildThreeScopesIsSuccessful() + { + $form = $this->factory->create(ScopePickerType::class, null, [ + 'center' => new Center(), + 'role' => 'THREE_SCOPE', + ]); + + $view = $form->createView(); + + $this->assertContains('entity', $view['scope']->vars['block_prefixes']); + } + + public function testBuildTwoScopesIsSuccessful() + { + $form = $this->factory->create(ScopePickerType::class, null, [ + 'center' => new Center(), + 'role' => 'TWO_SCOPE', + ]); + + $view = $form->createView(); + + $this->assertContains('entity', $view['scope']->vars['block_prefixes']); + } + + protected function getExtensions() + { + $user = new User(); + $role1Scope = 'ONE_SCOPE'; + $role2Scope = 'TWO_SCOPE'; + $role3Scope = 'THREE_SCOPE'; + $scopeA = (new Scope())->setName(['fr' => 'scope a']); + $scopeB = (new Scope())->setName(['fr' => 'scope b']); + $scopeC = (new Scope())->setName(['fr' => 'scope b'])->setActive(false); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableScopes($user, $role1Scope, Argument::any()) + ->willReturn([$scopeA]); + $authorizationHelper->getReachableScopes($user, $role2Scope, Argument::any()) + ->willReturn([$scopeA, $scopeB]); + $authorizationHelper->getReachableScopes($user, $role3Scope, Argument::any()) + ->willReturn([$scopeA, $scopeB, $scopeC]); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class); + $translatableStringHelper->localize(Argument::type('array'))->will( + static function ($args) { return $args[0]['fr']; } + ); + + $type = new ScopePickerType( + $authorizationHelper->reveal(), + $security->reveal(), + $translatableStringHelper->reveal() + ); + + // add the mocks for creating EntityType + $entityManager = DoctrineTestHelper::createTestEntityManager(); + $em = $this->prophesize(EntityManagerInterface::class); + $em->getClassMetadata(Scope::class)->willReturn($entityManager->getClassMetadata(Scope::class)); + $em->contains(Argument::type(Scope::class))->willReturn(true); + $em->initializeObject(Argument::type(Scope::class))->will(static fn ($o) => $o); + $emRevealed = $em->reveal(); + $managerRegistry = $this->prophesize(ManagerRegistry::class); + $managerRegistry->getManager(Argument::any())->willReturn($emRevealed); + $managerRegistry->getManagerForClass(Scope::class)->willReturn($emRevealed); + + $entityType = $this->prophesize(EntityType::class); + $entityType->getParent()->willReturn(ChoiceType::class); + + return [ + new PreloadedExtension([$type], []), + new DoctrineOrmExtension($managerRegistry->reveal()), + ]; + } +} diff --git a/src/Bundle/ChillMainBundle/config/services.yaml b/src/Bundle/ChillMainBundle/config/services.yaml index 10164932f..b44f4b51c 100644 --- a/src/Bundle/ChillMainBundle/config/services.yaml +++ b/src/Bundle/ChillMainBundle/config/services.yaml @@ -91,6 +91,14 @@ services: Chill\MainBundle\Export\ExportManager: autoconfigure: true autowire: true + arguments: + $exports: !tagged_iterator { tag: chill.export, index_by: alias } + $aggregators: !tagged_iterator { tag: chill.export_aggregator, index_by: alias } + $filters: !tagged_iterator { tag: chill.export_filter, index_by: alias } + # for an unknown reason, iterator_to_array($formatter) cause a segmentation fault error (php-fpm code 11). removed temporarily + # $formatters: !tagged_iterator { tag: chill.export_formatter, index_by: alias } + # remove until we can properly test it + # $exportElementProvider: !tagged_iterator { tag: chill.export_elements_provider, index_by: prefix } Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface: '@Chill\MainBundle\Security\Resolver\CenterResolverDispatcher' diff --git a/src/Bundle/ChillMainBundle/config/services/form.yaml b/src/Bundle/ChillMainBundle/config/services/form.yaml index 0d0739e23..58f01883f 100644 --- a/src/Bundle/ChillMainBundle/config/services/form.yaml +++ b/src/Bundle/ChillMainBundle/config/services/form.yaml @@ -122,8 +122,6 @@ services: Chill\MainBundle\Form\Type\PickAddressType: ~ - Chill\MainBundle\Form\DataTransform\AddressToIdDataTransformer: ~ - Chill\MainBundle\Form\DataTransform\AddressToIdDataTransformer: autoconfigure: true autowire: true @@ -132,10 +130,6 @@ services: autowire: true autoconfigure: true - Chill\MainBundle\Form\UserCurrentLocationType: - autowire: true - autoconfigure: true - Chill\MainBundle\Form\Type\LocationFormType: ~ Chill\MainBundle\Form\WorkflowStepType: ~ diff --git a/src/Bundle/ChillMainBundle/migrations/Version20221010142417.php b/src/Bundle/ChillMainBundle/migrations/Version20221010142417.php new file mode 100644 index 000000000..117dd4d22 --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20221010142417.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE scopes DROP active'); + } + + public function getDescription(): string + { + return 'Allow a scope to be desactivated'; + } + + public function up(Schema $schema): void + { + $this->addSql('ALTER TABLE scopes ADD active BOOLEAN DEFAULT true NOT NULL'); + } +} diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index 36f793817..b4ce910c4 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -189,6 +189,7 @@ Main scope: Cercle Main center: Centre user job: Métier de l'utilisateur Job: Métier +Jobs: Métiers Choose a main center: Choisir un centre Choose a main scope: Choisir un cercle choose a job: Choisir un métier @@ -227,6 +228,7 @@ never: jamais Create a new location: Créer une nouvelle localisation Location list: Liste des localisations Location type: Type de localisation +Pick a location type: Choisir un type de localisation Phonenumber1: Numéro de téléphone Phonenumber2: Autre numéro de téléphone Location configuration: Configuration des localisations diff --git a/src/Bundle/ChillPersonBundle/Controller/SocialWorkEvaluationApiController.php b/src/Bundle/ChillPersonBundle/Controller/SocialWorkEvaluationApiController.php index 41ca55d0e..bd12ec8c2 100644 --- a/src/Bundle/ChillPersonBundle/Controller/SocialWorkEvaluationApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/SocialWorkEvaluationApiController.php @@ -13,6 +13,7 @@ namespace Chill\PersonBundle\Controller; use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Serializer\Model\Collection; +use Chill\PersonBundle\Entity\SocialWork\Evaluation; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -39,9 +40,11 @@ class SocialWorkEvaluationApiController extends AbstractController */ public function listEvaluationBySocialAction(SocialAction $action): Response { - $pagination = $this->paginatorFactory->create($action->getEvaluations()->count()); + $evaluations = $action->getEvaluations()->filter(static fn (Evaluation $eval) => $eval->isActive()); - $evaluations = $action->getEvaluations()->slice( + $pagination = $this->paginatorFactory->create($evaluations->count()); + + $evaluations = $evaluations->slice( $pagination->getCurrentPageFirstItemNumber(), $pagination->getItemsPerPage() ); diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 61c0291db..0b6efe328 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -22,6 +22,7 @@ use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\UserJob; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodLocationHistory; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodStepHistory; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive; use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment; @@ -341,6 +342,12 @@ class AccompanyingPeriod implements */ private string $step = self::STEP_DRAFT; + /** + * @ORM\OneToMany(targetEntity=AccompanyingPeriodStepHistory::class, + * mappedBy="period", cascade={"persist", "remove"}, orphanRemoval=true) + */ + private Collection $stepHistories; + /** * @ORM\Column(type="datetime", nullable=true, options={"default": NULL}) */ @@ -395,8 +402,7 @@ class AccompanyingPeriod implements */ public function __construct(?DateTime $dateOpening = null) { - $this->setOpeningDate($dateOpening ?? new DateTime('now')); - $this->calendars = new ArrayCollection(); + $this->calendars = new ArrayCollection(); // TODO we cannot add a dependency between AccompanyingPeriod and calendars $this->participations = new ArrayCollection(); $this->scopes = new ArrayCollection(); $this->socialIssues = new ArrayCollection(); @@ -405,6 +411,8 @@ class AccompanyingPeriod implements $this->resources = new ArrayCollection(); $this->userHistories = new ArrayCollection(); $this->locationHistories = new ArrayCollection(); + $this->stepHistories = new ArrayCollection(); + $this->setOpeningDate($dateOpening ?? new DateTime('now')); } /** @@ -995,6 +1003,11 @@ class AccompanyingPeriod implements return $this->step; } + public function getStepHistories(): Collection + { + return $this->stepHistories; + } + public function getUser(): ?User { return $this->user; @@ -1263,7 +1276,11 @@ class AccompanyingPeriod implements */ public function setOpeningDate($openingDate) { - $this->openingDate = $openingDate; + if ($this->openingDate !== $openingDate) { + $this->openingDate = $openingDate; + + $this->ensureStepContinuity(); + } return $this; } @@ -1362,6 +1379,14 @@ class AccompanyingPeriod implements $this->bootPeriod(); } + if (self::STEP_DRAFT !== $this->step && $previous !== $step) { + // we create a new history + $history = new AccompanyingPeriodStepHistory(); + $history->setStep($this->step)->setStartDate(new DateTimeImmutable('now')); + + $this->addStepHistory($history); + } + return $this; } @@ -1402,6 +1427,17 @@ class AccompanyingPeriod implements return $this; } + private function addStepHistory(AccompanyingPeriodStepHistory $stepHistory): self + { + if (!$this->stepHistories->contains($stepHistory)) { + $this->stepHistories[] = $stepHistory; + $stepHistory->setPeriod($this); + $this->ensureStepContinuity(); + } + + return $this; + } + private function bootPeriod(): void { // first location history @@ -1413,6 +1449,43 @@ class AccompanyingPeriod implements $this->addLocationHistory($locationHistory); } + private function ensureStepContinuity(): void + { + // ensure continuity of histories + $criteria = new Criteria(); + $criteria->orderBy(['startDate' => Criteria::ASC, 'id' => Criteria::ASC]); + + /** @var Iterator $steps */ + $steps = $this->getStepHistories()->matching($criteria)->getIterator(); + $steps->rewind(); + + // we set the start date of the first step as the opening date, only if it is + // not greater than the end date + /** @var AccompanyingPeriodStepHistory $current */ + $current = $steps->current(); + + if (null === $current) { + return; + } + + if ($this->getOpeningDate()->format('Y-m-d') !== $current->getStartDate()->format('Y-m-d') + && ($this->getOpeningDate() <= $current->getEndDate() || null === $current->getEndDate())) { + $current->setStartDate(DateTimeImmutable::createFromMutable($this->getOpeningDate())); + } + + // then we set all the end date to the start date of the next one + do { + /** @var AccompanyingPeriodStepHistory $current */ + $current = $steps->current(); + $steps->next(); + + if ($steps->valid()) { + $next = $steps->current(); + $current->setEndDate($next->getStartDate()); + } + } while ($steps->valid()); + } + private function setRequestorPerson(?Person $requestorPerson = null): self { $this->requestorPerson = $requestorPerson; diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodStepHistory.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodStepHistory.php new file mode 100644 index 000000000..f9baffa35 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodStepHistory.php @@ -0,0 +1,115 @@ +endDate; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getPeriod(): AccompanyingPeriod + { + return $this->period; + } + + public function getStartDate(): ?DateTimeImmutable + { + return $this->startDate; + } + + public function getStep(): string + { + return $this->step; + } + + public function setEndDate(?DateTimeImmutable $endDate): self + { + $this->endDate = $endDate; + + return $this; + } + + /** + * @internal use AccompanyingPeriod::addLocationHistory + */ + public function setPeriod(AccompanyingPeriod $period): self + { + $this->period = $period; + + return $this; + } + + public function setStartDate(?DateTimeImmutable $startDate): self + { + $this->startDate = $startDate; + + return $this; + } + + public function setStep(string $step): AccompanyingPeriodStepHistory + { + $this->step = $step; + + return $this; + } +} diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Comment.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Comment.php index 9054aba5f..592c833d3 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Comment.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Comment.php @@ -36,7 +36,7 @@ class Comment implements TrackCreationInterface, TrackUpdateInterface * inversedBy="comments") * @ORM\JoinColumn(nullable=false, onDelete="CASCADE") */ - private ?AccompanyingPeriod $accompanyingPeriod; + private ?AccompanyingPeriod $accompanyingPeriod = null; /** * @ORM\Column(type="text") diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index ebf55009f..1134674df 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -1488,7 +1488,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI $participation = $this->participationsContainAccompanyingPeriod($accompanyingPeriod); if (!null === $participation) { - $participation->setEndDate(DateTimeImmutable::class); + $participation->setEndDate(new DateTime()); $this->accompanyingPeriodParticipations->removeElement($participation); } } @@ -1569,8 +1569,6 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI */ public function setCenter(Center $center): self { - $this->center = $center; - $modification = new DateTimeImmutable('now'); foreach ($this->centerHistory as $centerHistory) { diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php index 3324a236f..08dc0f425 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php @@ -26,6 +26,11 @@ use Symfony\Component\Serializer\Annotation as Serializer; */ class Evaluation { + /** + * @ORM\Column(type="boolean", nullable=false, options={"default": true}) + */ + private bool $active = true; + /** * @ORM\Column(type="dateinterval", nullable=true, options={"default": null}) * @Serializer\Groups({"read"}) @@ -114,6 +119,11 @@ class Evaluation return $this->url; } + public function isActive(): bool + { + return $this->active; + } + /** * @return $this * @@ -128,6 +138,13 @@ class Evaluation return $this; } + public function setActive(bool $active): Evaluation + { + $this->active = $active; + + return $this; + } + public function setDelay(?DateInterval $delay): self { $this->delay = $delay; diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php index 6dfed9f34..bc439c083 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php @@ -228,6 +228,22 @@ class SocialAction return $descendants; } + /** + * @param Collection|SocialAction[] $socialActions + */ + public static function getDescendantsWithThisForActions($socialActions): Collection + { + $unique = []; + + foreach ($socialActions as $action) { + foreach ($action->getDescendantsWithThis() as $child) { + $unique[spl_object_hash($child)] = $child; + } + } + + return new ArrayCollection(array_values($unique)); + } + public function getEvaluations(): Collection { return $this->evaluations; @@ -274,6 +290,11 @@ class SocialAction return $this->title; } + public function hasChildren(): bool + { + return 0 < $this->getChildren()->count(); + } + public function hasParent(): bool { return $this->getParent() instanceof self; @@ -369,6 +390,8 @@ class SocialAction { $this->parent = $parent; + $parent->addChild($this); + return $this; } diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php index 06f05c91f..42c8442c1 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php @@ -71,11 +71,17 @@ class SocialIssue $this->socialActions = new ArrayCollection(); } + /** + * @internal use @see{SocialIssue::setParent} instead + * + * @param SocialIssue $child + * + * @return $this + */ public function addChild(self $child): self { if (!$this->children->contains($child)) { $this->children[] = $child; - $child->setParent($this); } return $this; @@ -215,6 +221,22 @@ class SocialIssue return $descendants; } + /** + * @param array|SocialIssue[] $socialIssues + */ + public static function getDescendantsWithThisForIssues(array $socialIssues): Collection + { + $unique = []; + + foreach ($socialIssues as $issue) { + foreach ($issue->getDescendantsWithThis() as $child) { + $unique[spl_object_hash($child)] = $child; + } + } + + return new ArrayCollection(array_values($unique)); + } + public function getId(): ?int { return $this->id; @@ -262,6 +284,11 @@ class SocialIssue return $this->title; } + public function hasChildren(): bool + { + return 0 < $this->getChildren()->count(); + } + public function hasParent(): bool { return null !== $this->parent; @@ -329,6 +356,8 @@ class SocialIssue { $this->parent = $parent; + $parent->addChild($this); + return $this; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php new file mode 100644 index 000000000..e96c8a228 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php @@ -0,0 +1,131 @@ +householdCompositionTypeRepository = $householdCompositionTypeRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $p = self::PREFIX; + + if (!in_array('acppart', $qb->getAllAliases(), true)) { + $qb->leftJoin('acp.participations', 'acppart'); + } + + $qb + ->leftJoin( + HouseholdMember::class, + "{$p}_hm", + Join::WITH, + $qb->expr()->orX( + $qb->expr()->isNull("{$p}_hm"), + $qb->expr()->andX( + $qb->expr()->lte("{$p}_hm.startDate", ":{$p}_date"), + $qb->expr()->orX( + $qb->expr()->isNull("{$p}_hm.endDate"), + $qb->expr()->gt("{$p}_hm.endDate", ":{$p}_date") + ) + ) + ) + ) + ->leftJoin( + HouseholdComposition::class, + "{$p}_compo", + Join::WITH, + $qb->expr()->orX( + $qb->expr()->isNull("{$p}_compo"), + $qb->expr()->andX( + $qb->expr()->lte("{$p}_compo.startDate", ":{$p}_date"), + $qb->expr()->orX( + $qb->expr()->isNull("{$p}_compo.endDate"), + $qb->expr()->gt("{$p}_compo.endDate", ":{$p}_date") + ) + ) + ) + ) + ->addSelect("IDENTITY({$p}_compo.householdCompositionType) AS {$p}_select") + ->setParameter("{$p}_date", $data['date_calc']) + ->addGroupBy("{$p}_select"); + } + + public function applyOn() + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('date_calc', ChillDateType::class, [ + 'label' => 'export.aggregator.course.by_household_composition.Calc date', + 'input_format' => 'datetime_immutable', + 'data' => new \DateTimeImmutable('now'), + ]); + } + + public function getLabels($key, array $values, $data) + { + return function ($value) { + if ('_header' === $value) { + return 'export.aggregator.course.by_household_composition.Household composition'; + } + + if (null === $value) { + return ''; + } + + if (null === $o = $this->householdCompositionTypeRepository->find($value)) { + return ''; + } + + return $this->translatableStringHelper->localize($o->getLabel()); + }; + } + + public function getQueryKeys($data) + { + return [self::PREFIX . '_select']; + } + + public function getTitle() + { + return 'export.aggregator.course.by_household_composition.Group course by household composition'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php index 6adb43d05..e9b9e28fa 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -65,8 +66,6 @@ class ConfidentialAggregator implements AggregatorInterface default: throw new LogicException(sprintf('The value %s is not valid', $value)); } - - return $value; }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php index 2f8686da0..0dc66e81e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php @@ -13,12 +13,21 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; +use DateTimeImmutable; use Doctrine\ORM\QueryBuilder; +use LogicException; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; final class DurationAggregator implements AggregatorInterface { + private const CHOICES = [ + 'month', + 'week', + 'day', + ]; + private TranslatorInterface $translator; public function __construct(TranslatorInterface $translator) @@ -33,19 +42,31 @@ final class DurationAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect( - ' - (acp.closingDate - acp.openingDate +15) *12/365 - AS duration_aggregator' - ); + switch ($data['precision']) { + case 'day': + $qb->addSelect('(COALESCE(acp.closingDate, :now) - acp.openingDate) AS duration_aggregator'); - // TODO Pour avoir un interval plus précis (nécessaire ?): - // adapter la fonction extract pour pouvoir l'utiliser avec des intervals: extract(month from interval) - // et ajouter une fonction custom qui calcule plus précisément les intervals, comme doctrineum/date-interval - // https://packagist.org/packages/doctrineum/date-interval#3.1.0 (mais composer fait un conflit de dépendance) + break; - $qb->addGroupBy('duration_aggregator'); - $qb->addOrderBy('duration_aggregator'); + case 'week': + $qb->addSelect('(COALESCE(acp.closingDate, :now) - acp.openingDate) / 7 AS duration_aggregator'); + + break; + + case 'month': + $qb->addSelect('(EXTRACT (MONTH FROM AGE(COALESCE(acp.closingDate, :now), acp.openingDate)) * 12 + + EXTRACT (MONTH FROM AGE(COALESCE(acp.closingDate, :now), acp.openingDate))) AS duration_aggregator'); + + break; + + default: + throw new LogicException('precision not supported: ' . $data['precision']); + } + + $qb + ->setParameter('now', new DateTimeImmutable('now')) + ->addGroupBy('duration_aggregator') + ->addOrderBy('duration_aggregator'); } public function applyOn(): string @@ -55,25 +76,27 @@ final class DurationAggregator implements AggregatorInterface public function buildForm(FormBuilderInterface $builder) { - // no form + $builder->add('precision', ChoiceType::class, [ + 'choices' => array_combine(self::CHOICES, self::CHOICES), + 'label' => 'export.aggregator.course.duration.Precision', + 'choice_label' => static fn (string $c) => 'export.aggregator.course.duration.' . $c, + 'multiple' => false, + 'expanded' => true, + ]); } public function getLabels($key, array $values, $data) { - return function ($value): ?string { + return static function ($value) use ($data) { if ('_header' === $value) { - return 'Rounded month duration'; + return 'export.aggregator.course.duration.' . $data['precision']; } if (null === $value) { - return $this->translator->trans('current duration'); // when closingDate is null + return 0; } - if (0 === $value) { - return $this->translator->trans('duration 0 month'); - } - - return $value . $this->translator->trans(' months'); + return $value; }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php index 8a8c97c52..02f9f5cd9 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -65,8 +66,6 @@ class EmergencyAggregator implements AggregatorInterface default: throw new LogicException(sprintf('The value %s is not valid', $value)); } - - return $value; }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php index 6c04086e5..a3618f40f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php index 1498fc3a1..327b76a65 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php @@ -12,15 +12,20 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; use Chill\MainBundle\Export\AggregatorInterface; +use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Repository\UserRepository; use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Export\Declarations; +use DateTimeImmutable; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use function in_array; final class ReferrerAggregator implements AggregatorInterface { + private const A = 'acp_ref_agg_uhistory'; + + private const P = 'acp_ref_agg_date'; + private UserRender $userRender; private UserRepository $userRepository; @@ -40,12 +45,23 @@ final class ReferrerAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('acpuser', $qb->getAllAliases(), true)) { - $qb->leftJoin('acp.user', 'acpuser'); - } - - $qb->addSelect('acpuser.id AS referrer_aggregator'); - $qb->addGroupBy('referrer_aggregator'); + $qb + ->addSelect('IDENTITY(' . self::A . '.user) AS referrer_aggregator') + ->addGroupBy('referrer_aggregator') + ->leftJoin('acp.userHistories', self::A) + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull(self::A), + $qb->expr()->andX( + $qb->expr()->lte(self::A . '.startDate', ':' . self::P), + $qb->expr()->orX( + $qb->expr()->isNull(self::A . '.endDate'), + $qb->expr()->gt(self::A . '.endDate', ':' . self::P) + ) + ) + ) + ) + ->setParameter(self::P, $data['date_calc']); } public function applyOn(): string @@ -55,7 +71,13 @@ final class ReferrerAggregator implements AggregatorInterface public function buildForm(FormBuilderInterface $builder) { - // no form + $builder + ->add('date_calc', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'data' => new DateTimeImmutable('now'), + 'label' => 'export.aggregator.course.by_referrer.Computation date for referrer', + 'required' => true, + ]); } public function getLabels($key, array $values, $data) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php index 1c01b76bd..fc2fcc7b2 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php @@ -12,19 +12,21 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; use Chill\MainBundle\Export\AggregatorInterface; -//use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Declarations; use DateTime; -use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; -final class StepAggregator implements AggregatorInterface //, FilterInterface +final class StepAggregator implements AggregatorInterface { + private const A = 'acpstephistories'; + + private const P = 'acp_step_agg_date'; + private TranslatorInterface $translator; public function __construct( @@ -40,30 +42,26 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('acp.step AS step_aggregator'); - $qb->addGroupBy('step_aggregator'); - - /* - // add date in where clause - $where = $qb->getDQLPart('where'); - - $clause = $qb->expr()->andX( - $qb->expr()->lte('acp.openingDate', ':ondate'), - $qb->expr()->orX( - $qb->expr()->gt('acp.closingDate', ':ondate'), - $qb->expr()->isNull('acp.closingDate') - ) - ); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); + if (!in_array(self::A, $qb->getAllAliases(), true)) { + $qb->leftJoin('acp.stepHistories', self::A); } - $qb->add('where', $where); - $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); - */ + $qb + ->addSelect(self::A . '.step AS step_aggregator') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull(self::A . '.step'), + $qb->expr()->andX( + $qb->expr()->lte(self::A . '.startDate', ':' . self::P), + $qb->expr()->orX( + $qb->expr()->isNull(self::A . '.endDate'), + $qb->expr()->lt(self::A . '.endDate', ':' . self::P) + ) + ) + ) + ) + ->setParameter(self::P, $data['on_date']) + ->addGroupBy('step_aggregator'); } public function applyOn(): string @@ -94,8 +92,11 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface case '_header': return 'Step'; + case null: + return ''; + default: - throw new LogicException(sprintf('The value %s is not valid', $value)); + return $value; } }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CountryOfBirthAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CountryOfBirthAggregator.php index 4686e64d9..395070a8a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CountryOfBirthAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CountryOfBirthAggregator.php @@ -23,6 +23,7 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; final class CountryOfBirthAggregator implements AggregatorInterface, ExportElementValidatedInterface { @@ -83,16 +84,12 @@ final class CountryOfBirthAggregator implements AggregatorInterface, ExportEleme . ' is not known.'); } - $qb->leftJoin('person.countryOfBirth', 'countryOfBirth'); + if (!in_array('countryOfBirth', $qb->getAllAliases(), true)) { + $qb->leftJoin('person.countryOfBirth', 'countryOfBirth'); + } // add group by - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('country_of_birth_aggregator'); - } else { - $qb->groupBy('country_of_birth_aggregator'); - } + $qb->addGroupBy('country_of_birth_aggregator'); } public function applyOn() diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php index 73e0eedd6..2001466ab 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php @@ -70,14 +70,7 @@ final class HouseholdPositionAggregator implements AggregatorInterface, ExportEl $qb->setParameter('date', $data['date_position']); $qb->addSelect('IDENTITY(householdmember.position) AS household_position_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('household_position_aggregator'); - } else { - $qb->groupBy('household_position_aggregator'); - } + $qb->addGroupBy('household_position_aggregator'); } public function applyOn() diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php index 686f907ef..e20ecfb62 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php @@ -43,14 +43,7 @@ final class MaritalStatusAggregator implements AggregatorInterface } $qb->addSelect('personmarital.id as marital_status_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('marital_status_aggregator'); - } else { - $qb->groupBy('marital_status_aggregator'); - } + $qb->addGroupBy('marital_status_aggregator'); } public function applyOn() diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/NationalityAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/NationalityAggregator.php index cbaa7a7ed..1eced58d3 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/NationalityAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/NationalityAggregator.php @@ -83,15 +83,7 @@ final class NationalityAggregator implements AggregatorInterface, ExportElementV } $qb->leftJoin('person.nationality', 'nationality'); - - // add group by - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('nationality_aggregator'); - } else { - $qb->groupBy('nationality_aggregator'); - } + $qb->addGroupBy('nationality_aggregator'); } public function applyOn() diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php index d063af2c0..8c8744dfd 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php @@ -75,9 +75,9 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface return ['export_result']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle(): string @@ -99,6 +99,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface $qb = $this->repository->createQueryBuilder('acp'); $qb + ->andWhere('acp.step != :count_acp_step') ->andWhere( $qb->expr()->exists( 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part @@ -107,6 +108,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface ' ) ) + ->setParameter('count_acp_step', AccompanyingPeriod::STEP_DRAFT) ->setParameter('authorized_centers', $centers); $qb->select('COUNT(DISTINCT acp.id) AS export_result'); diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php index f0f4d4880..e62752d13 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php @@ -22,6 +22,7 @@ use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; use function in_array; @@ -74,9 +75,9 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface return ['export_result']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php index a96d1a2c7..0c838beff 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php @@ -23,6 +23,7 @@ use Chill\PersonBundle\Security\Authorization\HouseholdVoter; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class CountHousehold implements ExportInterface, GroupedExportInterface @@ -74,9 +75,9 @@ class CountHousehold implements ExportInterface, GroupedExportInterface return ['export_result']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php index 848abea5f..98708149a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php @@ -72,9 +72,9 @@ class CountPerson implements ExportInterface, GroupedExportInterface return ['export_result']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle() diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php index ebda38566..7acbd09cf 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php @@ -21,6 +21,7 @@ use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; use function in_array; @@ -73,9 +74,9 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor return ['export_result']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php index e20947b1c..9333312fd 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php @@ -75,9 +75,9 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface return ['export_result']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php index bde0ec910..e40ffccce 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php @@ -120,10 +120,7 @@ class ListPersonDuplicate implements DirectExportInterface, ExportElementValidat return $response; } - /** - * @return string - */ - public function getDescription() + public function getDescription(): string { return 'Create a list of duplicate people'; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php b/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php index 4706593ce..a0e68beea 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php @@ -25,6 +25,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportInterface @@ -79,9 +80,9 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn return ['export_result']; } - public function getResult($qb, $data) + public function getResult($query, $data) { - return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilter.php index 4ded757f3..c74e309b2 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilter.php @@ -11,13 +11,11 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; -use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\PickUserLocationType; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; class AdministrativeLocationFilter implements FilterInterface @@ -37,17 +35,10 @@ class AdministrativeLocationFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); $clause = $qb->expr()->in('acp.administrativeLocation', ':locations'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('locations', $data['accepted_locations']); + $qb + ->andWhere($clause) + ->setParameter('locations', $data['accepted_locations']); } public function applyOn(): string @@ -57,13 +48,9 @@ class AdministrativeLocationFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('accepted_locations', EntityType::class, [ - 'class' => Location::class, - 'choice_label' => function (Location $l) { - return $l->getName() . ' (' . $this->translatableStringHelper->localize($l->getLocationType()->getTitle()) . ')'; - }, + $builder->add('accepted_locations', PickUserLocationType::class, [ + 'label' => 'Accepted locations', 'multiple' => true, - 'expanded' => true, ]); } @@ -76,7 +63,7 @@ class AdministrativeLocationFilter implements FilterInterface } return ['Filtered by administratives locations: only %locations%', [ - '%locations%' => implode(', ou ', $locations), + '%locations%' => implode(', ', $locations), ]]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilter.php index 00ea8210d..07e61b42e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilter.php @@ -77,7 +77,7 @@ class ClosingMotiveFilter implements FilterInterface return [ 'Filtered by closingmotive: only %closingmotives%', [ - '%closingmotives%' => implode(', ou ', $motives), + '%closingmotives%' => implode(', ', $motives), ], ]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ConfidentialFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ConfidentialFilter.php index 3eb4a2554..8fcd874fe 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ConfidentialFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ConfidentialFilter.php @@ -73,15 +73,11 @@ class ConfidentialFilter implements FilterInterface public function describeAction($data, $format = 'string'): array { - foreach (self::CHOICES as $k => $v) { - if ($v === $data['accepted_confidentials']) { - $choice = $k; - } - } - return [ 'Filtered by confidential: only %confidential%', [ - '%confidential%' => $this->translator->trans($choice), + '%confidential%' => $this->translator->trans( + $data['accepted_confidentials'] ? 'is confidential' : 'is not confidential' + ), ], ]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilter.php deleted file mode 100644 index 9a522449c..000000000 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilter.php +++ /dev/null @@ -1,90 +0,0 @@ -translatableStringHelper = $translatableStringHelper; - $this->security = $security; - } - - public function addRole(): ?string - { - return null; - } - - public function alterQuery(QueryBuilder $qb, $data) - { - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->eq('acp.job', ':userjob'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('userjob', $this->getUserJob()); - } - - public function applyOn() - { - return Declarations::ACP_TYPE; - } - - public function buildForm(FormBuilderInterface $builder) - { - } - - public function describeAction($data, $format = 'string') - { - return [ - 'Filtered by user job: only %job%', [ - '%job%' => $this->translatableStringHelper->localize( - $this->getUserJob()->getLabel() - ), - ], - ]; - } - - public function getTitle() - { - return 'Filter by user job'; - } - - private function getUserJob(): UserJob - { - /** @var User $user */ - $user = $this->security->getUser(); - - return $user->getUserJob(); - } -} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php deleted file mode 100644 index 3accacf2c..000000000 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php +++ /dev/null @@ -1,95 +0,0 @@ -translatableStringHelper = $translatableStringHelper; - $this->security = $security; - } - - public function addRole(): ?string - { - return null; - } - - public function alterQuery(QueryBuilder $qb, $data) - { - if (!in_array('acpscope', $qb->getAllAliases(), true)) { - $qb->join('acp.scopes', 'acpscope'); - } - - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->eq('acpscope.id', ':userscope'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('userscope', $this->getUserMainScope()); - } - - public function applyOn() - { - return Declarations::ACP_TYPE; - } - - public function buildForm(FormBuilderInterface $builder) - { - } - - public function describeAction($data, $format = 'string') - { - return [ - 'Filtered by user main scope: only %scope%', [ - '%scope%' => $this->translatableStringHelper->localize( - $this->getUserMainScope()->getName() - ), - ], - ]; - } - - public function getTitle() - { - return 'Filter by user scope'; - } - - private function getUserMainScope(): Scope - { - /** @var User $user */ - $user = $this->security->getUser(); - - return $user->getMainScope(); - } -} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EmergencyFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EmergencyFilter.php index 7639ef54b..afdb717f0 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EmergencyFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EmergencyFilter.php @@ -73,15 +73,11 @@ class EmergencyFilter implements FilterInterface public function describeAction($data, $format = 'string'): array { - foreach (self::CHOICES as $k => $v) { - if ($v === $data['accepted_emergency']) { - $choice = $k; - } - } - return [ 'Filtered by emergency: only %emergency%', [ - '%emergency%' => $this->translator->trans($choice), + '%emergency%' => $this->translator->trans( + $data['accepted_emergency'] ? 'is emergency' : 'is not emergency' + ), ], ]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php index ec4a5abe7..1839268e1 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php @@ -15,7 +15,7 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\SocialWork\Evaluation; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\Query\Expr\Andx; +use Chill\PersonBundle\Repository\SocialWork\EvaluationRepositoryInterface; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; @@ -23,11 +23,15 @@ use function in_array; class EvaluationFilter implements FilterInterface { + private EvaluationRepositoryInterface $evaluationRepository; + private TranslatableStringHelper $translatableStringHelper; public function __construct( + EvaluationRepositoryInterface $evaluationRepository, TranslatableStringHelper $translatableStringHelper ) { + $this->evaluationRepository = $evaluationRepository; $this->translatableStringHelper = $translatableStringHelper; } @@ -50,16 +54,8 @@ class EvaluationFilter implements FilterInterface $qb->join('workeval.evaluation', 'eval'); } - $where = $qb->getDQLPart('where'); $clause = $qb->expr()->in('eval.id', ':evaluations'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); + $qb->andWhere($clause); $qb->setParameter('evaluations', $data['accepted_evaluations']); } @@ -72,11 +68,13 @@ class EvaluationFilter implements FilterInterface { $builder->add('accepted_evaluations', EntityType::class, [ 'class' => Evaluation::class, + 'choices' => $this->evaluationRepository->findAllActive(), 'choice_label' => function (Evaluation $ev) { return $this->translatableStringHelper->localize($ev->getTitle()); }, 'multiple' => true, - 'expanded' => true, + 'expanded' => false, + 'attr' => ['class' => 'select2'], ]); } @@ -89,7 +87,7 @@ class EvaluationFilter implements FilterInterface } return ['Filtered by evaluations: only %evals%', [ - '%evals%' => implode(', ou ', $evaluations), + '%evals%' => implode(', ', $evaluations), ]]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/IntensityFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/IntensityFilter.php index 82468c4ff..b0c2205a0 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/IntensityFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/IntensityFilter.php @@ -73,15 +73,9 @@ class IntensityFilter implements FilterInterface public function describeAction($data, $format = 'string'): array { - foreach (self::CHOICES as $k => $v) { - if ($v === $data['accepted_intensities']) { - $choice = $k; - } - } - return [ 'Filtered by intensity: only %intensity%', [ - '%intensity%' => $this->translator->trans($choice), + '%intensity%' => $this->translator->trans($data['accepted_intensities']), ], ]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OriginFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OriginFilter.php index 9f87f0ca3..445535734 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OriginFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OriginFilter.php @@ -76,7 +76,7 @@ class OriginFilter implements FilterInterface } return ['Filtered by origins: only %origins%', [ - '%origins%' => implode(', ou ', $origins), + '%origins%' => implode(', ', $origins), ]]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ReferrerFilter.php index 53ab10564..7d1aabc22 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ReferrerFilter.php @@ -11,17 +11,23 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; -use Chill\MainBundle\Entity\User; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\MainBundle\Form\Type\PickUserDynamicType; use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\Query\Expr\Andx; +use DateTimeImmutable; use Doctrine\ORM\QueryBuilder; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; class ReferrerFilter implements FilterInterface { + private const A = 'acp_referrer_filter_uhistory'; + + private const P = 'acp_referrer_filter_date'; + + private const PU = 'acp_referrer_filter_users'; + private UserRender $userRender; public function __construct(UserRender $userRender) @@ -36,17 +42,22 @@ class ReferrerFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('acp.user', ':referrers'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('referrers', $data['accepted_referrers']); + $qb + ->join('acp.userHistories', self::A) + ->andWhere( + $qb->expr()->andX( + $qb->expr()->lte(self::A . '.startDate', ':' . self::P), + $qb->expr()->orX( + $qb->expr()->isNull(self::A . '.endDate'), + $qb->expr()->gt(self::A . '.endDate', ':' . self::P) + ) + ) + ) + ->andWhere( + $qb->expr()->in(self::A . '.user', ':' . self::PU) + ) + ->setParameter(self::PU, $data['accepted_referrers']) + ->setParameter(self::P, $data['date_calc']); } public function applyOn(): string @@ -56,14 +67,16 @@ class ReferrerFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('accepted_referrers', EntityType::class, [ - 'class' => User::class, - 'choice_label' => function (User $u) { - return $this->userRender->renderString($u, []); - }, - 'multiple' => true, - 'expanded' => true, - ]); + $builder + ->add('accepted_referrers', PickUserDynamicType::class, [ + 'multiple' => true, + ]) + ->add('date_calc', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'data' => new DateTimeImmutable('now'), + 'label' => 'export.filter.course.by_referrer.Computation date for referrer', + 'required' => true, + ]); } public function describeAction($data, $format = 'string'): array @@ -76,7 +89,7 @@ class ReferrerFilter implements FilterInterface return [ 'Filtered by referrer: only %referrers%', [ - '%referrers' => implode(', ou ', $users), + '%referrers' => implode(', ', $users), ], ]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialActionFilter.php index c5c37ce21..bc1f368da 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialActionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialActionFilter.php @@ -15,10 +15,9 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Export\Declarations; +use Chill\PersonBundle\Form\Type\PickSocialActionType; use Chill\PersonBundle\Templating\Entity\SocialActionRender; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use function in_array; @@ -51,17 +50,13 @@ class SocialActionFilter implements FilterInterface $qb->join('acpw.socialAction', 'acpwsocialaction'); } - $where = $qb->getDQLPart('where'); $clause = $qb->expr()->in('acpwsocialaction.id', ':socialactions'); - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('socialactions', $data['accepted_socialactions']); + $qb->andWhere($clause) + ->setParameter( + 'socialactions', + SocialAction::getDescendantsWithThisForActions($data['accepted_socialactions'])->toArray() + ); } public function applyOn(): string @@ -71,26 +66,25 @@ class SocialActionFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('accepted_socialactions', EntityType::class, [ - 'class' => SocialAction::class, - 'choice_label' => function (SocialAction $sa) { - return $this->actionRender->renderString($sa, []); - }, + $builder->add('accepted_socialactions', PickSocialActionType::class, [ 'multiple' => true, - 'expanded' => true, ]); } public function describeAction($data, $format = 'string'): array { - $socialactions = []; + $actions = []; - foreach ($data['accepted_socialactions'] as $sa) { - $socialactions[] = $this->actionRender->renderString($sa, []); + $socialactions = $data['accepted_socialactions']; + + foreach ($socialactions as $action) { + $actions[] = $this->actionRender->renderString($action, [ + 'show_and_children' => true, + ]); } return ['Filtered by socialactions: only %socialactions%', [ - '%socialactions%' => implode(', ou ', $socialactions), + '%socialactions%' => implode(', ', $actions), ]]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php index d896d6395..141a1a2db 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php @@ -15,10 +15,9 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use Chill\PersonBundle\Export\Declarations; +use Chill\PersonBundle\Form\Type\PickSocialIssueType; use Chill\PersonBundle\Templating\Entity\SocialIssueRender; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; use function in_array; @@ -55,20 +54,13 @@ class SocialIssueFilter implements FilterInterface $qb->join('acp.socialIssues', 'acpsocialissue'); } - $where = $qb->getDQLPart('where'); $clause = $qb->expr()->in('acpsocialissue.id', ':socialissues'); - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter( - 'socialissues', - $this->addParentIssues($data['accepted_socialissues']) - ); + $qb->andWhere($clause) + ->setParameter( + 'socialissues', + SocialIssue::getDescendantsWithThisForIssues($data['accepted_socialissues']) + ); } public function applyOn() @@ -78,74 +70,31 @@ class SocialIssueFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('accepted_socialissues', EntityType::class, [ - 'class' => SocialIssue::class, - 'choice_label' => function ($socialIssue) { - return $this->socialIssueRender->renderString($socialIssue, []); - }, + $builder->add('accepted_socialissues', PickSocialIssueType::class, [ 'multiple' => true, - 'expanded' => true, ]); } - public function describeAction($data, $format = 'string') + public function describeAction($data, $format = 'string'): array { $issues = []; - $socialissues = $this->addParentIssues($data['accepted_socialissues']); + $socialissues = $data['accepted_socialissues']; - foreach ($socialissues as $i) { - if ('null' === $i) { - $issues[] = $this->translator->trans('Not given'); - } else { - $issues[] = $this->socialIssueRender->renderString($i, []); - } + foreach ($socialissues as $issue) { + $issues[] = $this->socialIssueRender->renderString($issue, [ + 'show_and_children' => true, + ]); } return [ 'Filtered by socialissues: only %socialissues%', [ - '%socialissues%' => implode(', ou ', $issues), + '%socialissues%' => implode(', ', $issues), ], ]; } - public function getTitle() + public function getTitle(): string { return 'Filter by social issue'; } - - /** - * "Le filtre retiendra les parcours qui comportent cette problématique, - * ou une problématique parente à celles choisies.". - * - * Add parent of each socialissue selected, and remove duplicates - * - * @param $accepted_issues - */ - private function addParentIssues($accepted_issues): array - { - $array = []; - - foreach ($accepted_issues as $i) { - /** @var SocialIssue $i */ - if ($i->hasParent()) { - $array[] = $i->getParent(); - } - $array[] = $i; - } - - return $this->removeDuplicate($array); - } - - private function removeDuplicate(array $array): array - { - $ids = array_map(static function ($item) { - return $item->getId(); - }, $array); - - $unique_ids = array_unique($ids); - - return array_values( - array_intersect_key($array, $unique_ids) - ); - } } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php new file mode 100644 index 000000000..a18442c2a --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php @@ -0,0 +1,130 @@ +security = $security; + $this->translatableStringHelper = $translatableStringHelper; + $this->userJobRepository = $userJobRepository; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $qb + ->join('acp.userHistories', self::A) + ->andWhere( + $qb->expr()->andX( + $qb->expr()->lte(self::A . '.startDate', ':' . self::P), + $qb->expr()->orX( + $qb->expr()->isNull(self::A . '.endDate'), + $qb->expr()->gt(self::A . '.endDate', ':' . self::P) + ) + ) + ) + ->setParameter(self::P, $data['date_calc']) + ->join(self::A . '.user', self::AU) + ->andWhere( + $qb->expr()->in(self::AU . '.userJob', ':' . self::PJ) + ) + ->setParameter(self::PJ, $data['jobs']); + } + + public function applyOn() + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('jobs', EntityType::class, [ + 'class' => UserJob::class, + 'choices' => $this->userJobRepository->findAllActive(), + 'multiple' => true, + 'expanded' => true, + 'choice_label' => fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()), + 'label' => 'Job', + ]) + ->add('date_calc', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'data' => new DateTimeImmutable('now'), + 'label' => 'export.filter.course.by_user_scope.Computation date for referrer', + 'required' => true, + ]); + } + + public function describeAction($data, $format = 'string') + { + return [ + 'Filtered by user job: only %job%', [ + '%job%' => implode( + ', ', + array_map( + fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()), + $data['jobs']->toArray() + ) + ), + ], + ]; + } + + public function getTitle() + { + return 'Filter by user job'; + } + + private function getUserJob(): UserJob + { + /** @var User $user */ + $user = $this->security->getUser(); + + return $user->getUserJob(); + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserScopeFilter.php new file mode 100644 index 000000000..48a97f9f7 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserScopeFilter.php @@ -0,0 +1,129 @@ +scopeRepository = $scopeRepository; + $this->security = $security; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $qb + ->join('acp.userHistories', self::A) + ->andWhere( + $qb->expr()->andX( + $qb->expr()->lte(self::A . '.startDate', ':' . self::P), + $qb->expr()->orX( + $qb->expr()->isNull(self::A . '.endDate'), + $qb->expr()->gt(self::A . '.endDate', ':' . self::P) + ) + ) + ) + ->setParameter(self::P, $data['date_calc']) + ->join(self::A . '.user', self::AU) + ->andWhere( + $qb->expr()->in(self::AU . '.mainScope', ':' . self::PS) + ) + ->setParameter(self::PS, $data['scopes']); + } + + public function applyOn() + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('scopes', EntityType::class, [ + 'class' => Scope::class, + 'choices' => $this->scopeRepository->findAllActive(), + 'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()), + 'multiple' => true, + 'expanded' => true, + ]) + ->add('date_calc', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'data' => new DateTimeImmutable('now'), + 'label' => 'export.filter.course.by_user_scope.Computation date for referrer', + 'required' => true, + ]); + } + + public function describeAction($data, $format = 'string') + { + return [ + 'Filtered by user main scope: only %scope%', [ + '%scope%' => implode( + ', ', + array_map( + fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()), + $data['scopes']->toArray() + ) + ), + ], + ]; + } + + public function getTitle() + { + return 'Filter by user scope'; + } + + private function getUserMainScope(): Scope + { + /** @var User $user */ + $user = $this->security->getUser(); + + return $user->getMainScope(); + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/EvaluationTypeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/EvaluationTypeFilter.php index 2a5c143c7..65e38f41e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/EvaluationTypeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/EvaluationTypeFilter.php @@ -76,7 +76,7 @@ final class EvaluationTypeFilter implements FilterInterface } return ['Filtered by evaluation type: only %evals%', [ - '%evals%' => implode(', ou ', $evals), + '%evals%' => implode(', ', $evals), ]]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/MaxDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/MaxDateFilter.php index 5235207ff..4a65452a1 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/MaxDateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/MaxDateFilter.php @@ -13,7 +13,6 @@ namespace Chill\PersonBundle\Export\Filter\EvaluationFilters; use Chill\MainBundle\Export\FilterInterface; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; @@ -22,8 +21,8 @@ use Symfony\Contracts\Translation\TranslatorInterface; class MaxDateFilter implements FilterInterface { private const MAXDATE_CHOICES = [ - 'is specified' => true, - 'is not specified' => false, + 'maxdate is specified' => true, + 'maxdate is not specified' => false, ]; private TranslatorInterface $translator; @@ -40,21 +39,13 @@ class MaxDateFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); - if (true === $data['maxdate']) { - $clause = $qb->expr()->isNotNull('eval.maxDate'); + $clause = $qb->expr()->isNotNull('workeval.maxDate'); } else { - $clause = $qb->expr()->isNull('eval.maxDate'); + $clause = $qb->expr()->isNull('workeval.maxDate'); } - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); + $qb->andWhere($clause); } public function applyOn(): string @@ -73,19 +64,15 @@ class MaxDateFilter implements FilterInterface public function describeAction($data, $format = 'string'): array { - foreach (self::MAXDATE_CHOICES as $k => $v) { - if ($v === $data['maxdate']) { - $choice = $k; - } - } - return ['Filtered by maxdate: only %choice%', [ - '%choice%' => $this->translator->trans($choice), + '%choice%' => $this->translator->trans( + $data['maxdate'] ? 'maxdate is specified' : 'maxdate is not specified' + ), ]]; } public function getTitle(): string { - return 'Filter by maxdate'; + return 'Filter evaluations by maxdate mention'; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php index 9296717ce..3ff8cb63e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php @@ -95,7 +95,7 @@ class CompositionFilter implements FilterInterface } return ['Filtered by composition: only %compositions% on %ondate%', [ - '%compositions%' => implode(', ou ', $compositions), + '%compositions%' => implode(', ', $compositions), '%ondate%' => $data['on_date']->format('d-m-Y'), ]]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php index 1c6b362d4..218ae483e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php @@ -35,7 +35,7 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface $where = $qb->getDQLPart('where'); $min = null !== $data['min_age'] ? $data['min_age'] : 0; - $max = null !== $data['max_age'] ? $data['max_age'] : 3000; + $max = null !== $data['max_age'] ? $data['max_age'] : 150; $calc = $data['date_calc']; $minDate = $calc->sub(new DateInterval('P' . $max . 'Y')); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php index a3d2260e3..aad98a394 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php @@ -12,12 +12,9 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\PersonFilters; use Chill\MainBundle\Export\FilterInterface; -use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\MaritalStatus; use Chill\PersonBundle\Export\Declarations; -use DateTime; -use Doctrine\ORM\Query\Expr\Andx; use Symfony\Bridge\Doctrine\Form\Type\EntityType; class MaritalStatusFilter implements FilterInterface @@ -37,25 +34,10 @@ class MaritalStatusFilter implements FilterInterface public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); - - $clause = $qb->expr()->andX( - $qb->expr()->in('person.maritalStatus', ':maritalStatus'), - $qb->expr()->orX( - $qb->expr()->eq('person.maritalStatusDate', ':calc_date'), - $qb->expr()->isNull('person.maritalStatusDate') - ) + $qb->andWhere( + $qb->expr()->in('person.maritalStatus', ':maritalStatus') ); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); $qb->setParameter('maritalStatus', $data['maritalStatus']); - $qb->setParameter('calc_date', $data['calc_date']); } public function applyOn() @@ -75,11 +57,6 @@ class MaritalStatusFilter implements FilterInterface 'multiple' => true, 'expanded' => true, ]); - - $builder->add('calc_date', ChillDateType::class, [ - 'label' => 'Marital status at this time', - 'data' => new DateTime(), - ]); } public function describeAction($data, $format = 'string') diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php index fd8e14c83..a69e1ac76 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php @@ -90,7 +90,7 @@ class JobFilter implements FilterInterface } return ['Filtered by treating agent job: only %jobs%', [ - '%jobs%' => implode(', ou ', $userjobs), + '%jobs%' => implode(', ', $userjobs), ]]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php index 65d97cbf1..febc57a17 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php @@ -81,7 +81,7 @@ class ReferrerFilter implements FilterInterface return [ 'Filtered by treating agent: only %agents%', [ - '%agents' => implode(', ou ', $users), + '%agents' => implode(', ', $users), ], ]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php index 6d130ce75..8cb2f6421 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php @@ -90,7 +90,7 @@ class ScopeFilter implements FilterInterface } return ['Filtered by treating agent scope: only %scopes%', [ - '%scopes%' => implode(', ou ', $scopes), + '%scopes%' => implode(', ', $scopes), ]]; } diff --git a/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php b/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php index 685915203..668a00276 100644 --- a/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php +++ b/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php @@ -16,6 +16,7 @@ use Chill\MainBundle\Form\Type\TranslatableStringFormType; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\SocialWork\Evaluation; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\UrlType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -52,6 +53,14 @@ class EvaluationType extends AbstractType ->add('notificationDelay', DateIntervalType::class, [ 'label' => 'evaluation.notificationDelay', 'required' => false, + ]) + ->add('active', ChoiceType::class, [ + 'label' => 'active', + 'choices' => [ + 'active' => true, + 'inactive' => false, + ], + 'required' => true, ]); } diff --git a/src/Bundle/ChillPersonBundle/Form/Type/PickSocialActionType.php b/src/Bundle/ChillPersonBundle/Form/Type/PickSocialActionType.php new file mode 100644 index 000000000..7c0da43b1 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Form/Type/PickSocialActionType.php @@ -0,0 +1,57 @@ +actionRender = $actionRender; + $this->actionRepository = $actionRepository; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver + ->setDefaults([ + 'class' => SocialAction::class, + 'choices' => $this->actionRepository->findAllActive(), + 'choice_label' => function (SocialAction $sa) { + return $this->actionRender->renderString($sa, []); + }, + 'placeholder' => 'Pick a social action', + 'required' => false, + 'attr' => ['class' => 'select2'], + 'label' => 'Social actions', + 'multiple' => false, + ]) + ->setAllowedTypes('multiple', ['bool']); + } + + public function getParent(): string + { + return EntityType::class; + } +} diff --git a/src/Bundle/ChillPersonBundle/Form/Type/PickSocialIssueType.php b/src/Bundle/ChillPersonBundle/Form/Type/PickSocialIssueType.php new file mode 100644 index 000000000..e79c17191 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Form/Type/PickSocialIssueType.php @@ -0,0 +1,57 @@ +issueRender = $issueRender; + $this->issueRepository = $issueRepository; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver + ->setDefaults([ + 'class' => SocialIssue::class, + 'choices' => $this->issueRepository->findAllActive(), + 'choice_label' => function (SocialIssue $si) { + return $this->issueRender->renderString($si, []); + }, + 'placeholder' => 'Pick a social issue', + 'required' => false, + 'attr' => ['class' => 'select2'], + 'label' => 'Social issues', + 'multiple' => false, + ]) + ->setAllowedTypes('multiple', ['bool']); + } + + public function getParent(): string + { + return EntityType::class; + } +} diff --git a/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdCompositionRepository.php b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdCompositionRepository.php index 453d32a4d..e25d0c867 100644 --- a/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdCompositionRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdCompositionRepository.php @@ -15,9 +15,8 @@ use Chill\PersonBundle\Entity\Household\Household; use Chill\PersonBundle\Entity\Household\HouseholdComposition; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; -use Doctrine\Persistence\ObjectRepository; -class HouseholdCompositionRepository implements ObjectRepository +final class HouseholdCompositionRepository implements HouseholdCompositionRepositoryInterface { private EntityRepository $repository; diff --git a/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdCompositionRepositoryInterface.php b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdCompositionRepositoryInterface.php new file mode 100644 index 000000000..84c62392b --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdCompositionRepositoryInterface.php @@ -0,0 +1,45 @@ +repository->findAll(); } + public function findAllActive(): array + { + return $this->findBy(['active' => true]); + } + /** * @param mixed|null $limit * @param mixed|null $offset diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepositoryInterface.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepositoryInterface.php new file mode 100644 index 000000000..9ca390ff9 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepositoryInterface.php @@ -0,0 +1,45 @@ + + */ + public function findAll(): array; + + /** + * @return array + */ + public function findAllActive(): array; + + /** + * @param mixed|null $limit + * @param mixed|null $offset + * + * @return array + */ + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array; + + public function findOneBy(array $criteria, ?array $orderBy = null): ?Evaluation; + + /** + * @return class-string + */ + public function getClassName(): string; +} diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php index f6212e608..fd24b7b9d 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php @@ -63,13 +63,9 @@ final class GoalRepository implements ObjectRepository } /** - * @param mixed|null $orderBy - * @param mixed|null $limit - * @param mixed|null $offset - * * @return Goal[] */ - public function findBySocialActionWithDescendants(SocialAction $action, $orderBy = null, $limit = null, $offset = null): array + public function findBySocialActionWithDescendants(SocialAction $action, array $orderBy = [], ?int $limit = null, ?int $offset = null): array { $qb = $this->buildQueryBySocialActionWithDescendants($action); $qb->select('g'); diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php index df73bde21..e751f88cb 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php @@ -96,13 +96,9 @@ final class ResultRepository implements ObjectRepository } /** - * @param mixed|null $orderBy - * @param mixed|null $limit - * @param mixed|null $offset - * * @return Result[] */ - public function findBySocialActionWithDescendants(SocialAction $action, $orderBy = null, $limit = null, $offset = null): array + public function findBySocialActionWithDescendants(SocialAction $action, array $orderBy = [], ?int $limit = null, ?int $offset = null): array { $qb = $this->buildQueryBySocialActionWithDescendants($action); $qb->select('r'); diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php index 052432741..370704aed 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Repository\SocialWork; use Chill\PersonBundle\Entity\SocialWork\SocialAction; +use DateTime; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\QueryBuilder; @@ -44,6 +45,14 @@ final class SocialActionRepository implements ObjectRepository return $this->repository->findAll(); } + /** + * @return array|SocialAction[] + */ + public function findAllActive(): array + { + return $this->buildQueryWithDesactivatedDateCriteria()->getQuery()->getResult(); + } + /** * @param mixed|null $limit * @param mixed|null $offset @@ -67,4 +76,16 @@ final class SocialActionRepository implements ObjectRepository { return SocialAction::class; } + + private function buildQueryWithDesactivatedDateCriteria(): QueryBuilder + { + $qb = $this->repository->createQueryBuilder('sa'); + + $qb->where('sa.desactivationDate is null') + ->orWhere('sa.desactivationDate > :now') + ->orderBy('sa.ordering', 'ASC') + ->setParameter('now', new DateTime('now')); + + return $qb; + } } diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php index 27b4ed4e9..8dd8f6a62 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php @@ -12,8 +12,10 @@ declare(strict_types=1); namespace Chill\PersonBundle\Repository\SocialWork; use Chill\PersonBundle\Entity\SocialWork\SocialIssue; +use DateTime; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ObjectRepository; final class SocialIssueRepository implements ObjectRepository @@ -38,6 +40,14 @@ final class SocialIssueRepository implements ObjectRepository return $this->repository->findAll(); } + /** + * @return array|SocialIssue[] + */ + public function findAllActive(): array + { + return $this->buildQueryWithDesactivatedDateCriteria()->getQuery()->getResult(); + } + /** * @param mixed|null $limit * @param mixed|null $offset @@ -61,4 +71,16 @@ final class SocialIssueRepository implements ObjectRepository { return SocialIssue::class; } + + private function buildQueryWithDesactivatedDateCriteria(): QueryBuilder + { + $qb = $this->repository->createQueryBuilder('si'); + + $qb->where('si.desactivationDate is null') + ->orWhere('si.desactivationDate > :now') + ->orderBy('si.ordering', 'ASC') + ->setParameter('now', new DateTime('now')); + + return $qb; + } } diff --git a/src/Bundle/ChillPersonBundle/Templating/Entity/SocialActionRender.php b/src/Bundle/ChillPersonBundle/Templating/Entity/SocialActionRender.php index f2ee13fc1..bfe49c0a4 100644 --- a/src/Bundle/ChillPersonBundle/Templating/Entity/SocialActionRender.php +++ b/src/Bundle/ChillPersonBundle/Templating/Entity/SocialActionRender.php @@ -15,16 +15,20 @@ use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Symfony\Component\Templating\EngineInterface; - +use Symfony\Contracts\Translation\TranslatorInterface; use function array_merge; use function array_reverse; use function implode; class SocialActionRender implements ChillEntityRenderInterface { + public const AND_CHILDREN_MENTION = 'show_and_children_mention'; + public const DEFAULT_ARGS = [ self::SEPARATOR_KEY => ' > ', self::NO_BADGE => false, + self::SHOW_AND_CHILDREN => false, + self::AND_CHILDREN_MENTION => 'social_action.and children', ]; /** @@ -34,14 +38,26 @@ class SocialActionRender implements ChillEntityRenderInterface public const SEPARATOR_KEY = 'default.separator'; + /** + * Show a mention "and children" on each SocialAction, if the social action + * has at least one child. + */ + public const SHOW_AND_CHILDREN = 'show_and_children'; + private EngineInterface $engine; private TranslatableStringHelper $translatableStringHelper; - public function __construct(TranslatableStringHelper $translatableStringHelper, EngineInterface $engine) - { + private TranslatorInterface $translator; + + public function __construct( + TranslatableStringHelper $translatableStringHelper, + EngineInterface $engine, + TranslatorInterface $translator + ) { $this->translatableStringHelper = $translatableStringHelper; $this->engine = $engine; + $this->translator = $translator; } public function renderBox($socialAction, array $options): string @@ -72,7 +88,13 @@ class SocialActionRender implements ChillEntityRenderInterface $titles = array_reverse($titles); - return implode($options[self::SEPARATOR_KEY], $titles); + $title = implode($options[self::SEPARATOR_KEY], $titles); + + if ($options[self::SHOW_AND_CHILDREN] && $socialAction->hasChildren()) { + $title .= ' (' . $this->translator->trans($options[self::AND_CHILDREN_MENTION]) . ')'; + } + + return $title; } public function supports($entity, array $options): bool diff --git a/src/Bundle/ChillPersonBundle/Templating/Entity/SocialIssueRender.php b/src/Bundle/ChillPersonBundle/Templating/Entity/SocialIssueRender.php index 014b23c8e..62d17f47e 100644 --- a/src/Bundle/ChillPersonBundle/Templating/Entity/SocialIssueRender.php +++ b/src/Bundle/ChillPersonBundle/Templating/Entity/SocialIssueRender.php @@ -15,26 +15,42 @@ use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use Symfony\Component\Templating\EngineInterface; - +use Symfony\Contracts\Translation\TranslatorInterface; use function array_reverse; use function implode; final class SocialIssueRender implements ChillEntityRenderInterface { + public const AND_CHILDREN_MENTION = 'show_and_children_mention'; + public const DEFAULT_ARGS = [ self::SEPARATOR_KEY => ' > ', + self::SHOW_AND_CHILDREN => false, + self::AND_CHILDREN_MENTION => 'social_issue.and children', ]; public const SEPARATOR_KEY = 'default.separator'; + /** + * Show a mention "and children" on each SocialIssue, if the social issue + * has at least one child. + */ + public const SHOW_AND_CHILDREN = 'show_and_children'; + private EngineInterface $engine; private TranslatableStringHelper $translatableStringHelper; - public function __construct(TranslatableStringHelper $translatableStringHelper, EngineInterface $engine) - { + private TranslatorInterface $translator; + + public function __construct( + TranslatableStringHelper $translatableStringHelper, + EngineInterface $engine, + TranslatorInterface $translator + ) { $this->translatableStringHelper = $translatableStringHelper; $this->engine = $engine; + $this->translator = $translator; } /** @@ -78,7 +94,13 @@ final class SocialIssueRender implements ChillEntityRenderInterface $titles = array_reverse($titles); - return implode($options[self::SEPARATOR_KEY], $titles); + $title = implode($options[self::SEPARATOR_KEY], $titles); + + if ($options[self::SHOW_AND_CHILDREN] && $socialIssue->hasChildren()) { + $title .= ' (' . $this->translator->trans($options[self::AND_CHILDREN_MENTION]) . ')'; + } + + return $title; } public function supports($entity, array $options): bool diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/SocialWorkEvaluationApiControllerTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/SocialWorkEvaluationApiControllerTest.php new file mode 100644 index 000000000..aa98fdcd3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/SocialWorkEvaluationApiControllerTest.php @@ -0,0 +1,86 @@ +evaluationToReset) { + return; + } + + self::bootKernel(); + + $em = self::$container->get(EntityManagerInterface::class); + $evaluation = $em->find(Evaluation::class, $this->evaluationToReset->getId()); + + $evaluation->setActive(true); + $em->flush(); + } + + public function dataGenerateSocialActionWithEvaluations(): iterable + { + self::bootKernel(); + + $this->em = self::$container->get(EntityManagerInterface::class); + + /** @var SocialAction $socialAction */ + $socialAction = $this->em->createQuery( + 'SELECT s FROM ' . SocialAction::class . ' s WHERE SIZE(s.evaluations) >= 2' + ) + ->setMaxResults(1) + ->getSingleResult(); + + // set the first evaluation as inactive and save + $this->evaluationToReset = $socialAction->getEvaluations()->first(); + $this->evaluationToReset->setActive(false); + + $this->em->flush(); + + yield [$socialAction, $this->evaluationToReset]; + } + + /** + * @dataProvider dataGenerateSocialActionWithEvaluations + */ + public function testListEvaluationBySocialAction(SocialAction $action, Evaluation $inactiveEvaluation): void + { + $client = $this->getClientAuthenticated(); + + $client->request('GET', sprintf('/api/1.0/person/social-work/evaluation/by-social-action/%d.json', $action->getId())); + + $this->assertResponseIsSuccessful(); + + $content = json_decode($client->getResponse()->getContent(), true); + + $ids = array_map(static fn (array $item) => $item['id'], $content['results']); + + $this->assertNotContains($inactiveEvaluation->getId(), $ids); + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php index 4c6033ca9..2f38da244 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php @@ -29,6 +29,38 @@ use function count; */ final class AccompanyingPeriodTest extends \PHPUnit\Framework\TestCase { + public function testChangeStepKeepHistory() + { + $period = new AccompanyingPeriod(); + + $this->assertCount(0, $period->getStepHistories(), 'at initialization, period should not have any step history'); + + $period->setStep(AccompanyingPeriod::STEP_DRAFT); + + $this->assertCount(0, $period->getStepHistories(), 're applying a draft should not create a history'); + + $period->setStep(AccompanyingPeriod::STEP_CONFIRMED); + + $this->assertCount(1, $period->getStepHistories()); + $this->assertEquals(AccompanyingPeriod::STEP_CONFIRMED, $period->getStepHistories()->first()->getStep()); + + $period->setOpeningDate($aMonthAgo = new DateTime('1 month ago')); + + $this->assertCount(1, $period->getStepHistories()); + $this->assertEquals($aMonthAgo, $period->getStepHistories()->first()->getStartDate(), 'when changing the opening date, the start date of the first history should change'); + + $period->setOpeningDate($tenDaysAgo = new DateTime('10 days ago')); + + $this->assertCount(1, $period->getStepHistories()); + $this->assertEquals($tenDaysAgo, $period->getStepHistories()->first()->getStartDate(), 'when changing the opening date, the start date of the first history should change'); + + $period->setStep(AccompanyingPeriod::STEP_CLOSED); + $this->assertCount(2, $period->getStepHistories()); + + $period->setOpeningDate($tomorrow = new DateTime('tomorrow')); + $this->assertEquals($tenDaysAgo, $period->getStepHistories()->first()->getStartDate(), 'when changing the opening date to a later one and no history after, start date should change'); + } + public function testClosingEqualOpening() { $datetime = new DateTime('now'); diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/SocialWork/SocialActionTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/SocialWork/SocialActionTest.php new file mode 100644 index 000000000..2dcfa6b11 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/SocialWork/SocialActionTest.php @@ -0,0 +1,51 @@ +setParent($parentA); + $grandChildA = (new SocialAction())->setParent($childA); + $grandGrandChildA = (new SocialAction())->setParent($grandChildA); + $unrelatedA = new SocialAction(); + + $parentB = new SocialAction(); + $childB = (new SocialAction())->setParent($parentB); + $grandChildB = (new SocialAction())->setParent($childB); + $grandGrandChildB = (new SocialAction())->setParent($grandChildB); + $unrelatedB = new SocialAction(); + + $actual = SocialAction::getDescendantsWithThisForActions([$parentA, $parentB]); + + $this->assertContains($parentA, $actual); + $this->assertContains($parentB, $actual); + $this->assertContains($childA, $actual); + $this->assertContains($childB, $actual); + $this->assertContains($grandChildA, $actual); + $this->assertContains($grandChildB, $actual); + $this->assertContains($grandGrandChildA, $actual); + $this->assertContains($grandGrandChildB, $actual); + $this->assertCount(8, $actual); + $this->assertNotContains($unrelatedA, $actual); + $this->assertNotContains($unrelatedB, $actual); + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/SocialWork/SocialIssueTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/SocialWork/SocialIssueTest.php index edc1e5474..b846ae5f4 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Entity/SocialWork/SocialIssueTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/SocialWork/SocialIssueTest.php @@ -55,6 +55,35 @@ final class SocialIssueTest extends TestCase $this->assertCount(0, $unrelated->getAncestors(false)); } + public function testGetDescendantsWithThisForIssues() + { + $parentA = new SocialIssue(); + $childA = (new SocialIssue())->setParent($parentA); + $grandChildA = (new SocialIssue())->setParent($childA); + $grandGrandChildA = (new SocialIssue())->setParent($grandChildA); + $unrelatedA = new SocialIssue(); + + $parentB = new SocialIssue(); + $childB = (new SocialIssue())->setParent($parentB); + $grandChildB = (new SocialIssue())->setParent($childB); + $grandGrandChildB = (new SocialIssue())->setParent($grandChildB); + $unrelatedB = new SocialIssue(); + + $actual = SocialIssue::getDescendantsWithThisForIssues([$parentA, $parentB]); + + $this->assertContains($parentA, $actual); + $this->assertContains($parentB, $actual); + $this->assertContains($childA, $actual); + $this->assertContains($childB, $actual); + $this->assertContains($grandChildA, $actual); + $this->assertContains($grandChildB, $actual); + $this->assertContains($grandGrandChildA, $actual); + $this->assertContains($grandGrandChildB, $actual); + $this->assertCount(8, $actual); + $this->assertNotContains($unrelatedA, $actual); + $this->assertNotContains($unrelatedB, $actual); + } + public function testIsDescendantOf() { $parent = new SocialIssue(); diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php index 013dddd4f..15f4796d2 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php @@ -39,7 +39,9 @@ final class DurationAggregatorTest extends AbstractAggregatorTest public function getFormData(): array { return [ - [], + ['precision' => 'day'], + ['precision' => 'week'], + ['precision' => 'month'], ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php index 49ca0a6c1..dc7ccaad7 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregato use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\GeographicalUnitStatAggregator; +use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; /** @@ -38,8 +39,9 @@ final class GeographicalUnitStatAggregatorTest extends AbstractAggregatorTest public function getFormData(): array { + // TODO: add geographical unit stat into fixtures and provide a level return [ - [], + ['date_calc' => new DateTimeImmutable('today'), 'level' => null], ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php index baee88871..e60130a6f 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregato use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ReferrerAggregator; +use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; /** @@ -39,7 +40,7 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest public function getFormData(): array { return [ - [], + ['date_calc' => new DateTimeImmutable('now')], ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Export/CountSocialWorkActionsTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Export/CountSocialWorkActionsTest.php index 512c2ef59..3ad0f7c1b 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Export/CountSocialWorkActionsTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Export/CountSocialWorkActionsTest.php @@ -13,6 +13,7 @@ namespace Export\Export; use Chill\MainBundle\Test\Export\AbstractExportTest; use Chill\PersonBundle\Export\Declarations; +use Chill\PersonBundle\Export\Export\CountSocialWorkActions; /** * @internal diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php index 01b315e9a..dbf7a8fe3 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; -use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserJobFilter; +use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserJobFilter; use Doctrine\ORM\EntityManagerInterface; /** @@ -21,7 +21,7 @@ use Doctrine\ORM\EntityManagerInterface; */ final class CurrentUserJobFilterTest extends AbstractFilterTest { - private CurrentUserJobFilter $filter; + private UserJobFilter $filter; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php index c9353a317..9140d5c41 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; -use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserScopeFilter; +use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserScopeFilter; use Doctrine\ORM\EntityManagerInterface; /** @@ -21,7 +21,7 @@ use Doctrine\ORM\EntityManagerInterface; */ final class CurrentUserScopeFilterTest extends AbstractFilterTest { - private CurrentUserScopeFilter $filter; + private UserScopeFilter $filter; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml index 88f6124ea..04d5d8371 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml @@ -1,264 +1,193 @@ services: + _defaults: + autowire: true + autoconfigure: true ## Indicators chill.person.export.count_accompanyingcourse: class: Chill\PersonBundle\Export\Export\CountAccompanyingCourse - autowire: true - autoconfigure: true tags: - { name: chill.export, alias: count_accompanyingcourse } chill.person.export.avg_accompanyingcourse_duration: class: Chill\PersonBundle\Export\Export\StatAccompanyingCourseDuration - autowire: true - autoconfigure: true tags: - { name: chill.export, alias: avg_accompanyingcourse_duration } ## Filters - chill.person.export.filter_current_userscope: - class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserScopeFilter - autowire: true - autoconfigure: true + Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserScopeFilter: tags: - - { name: chill.export_filter, alias: accompanyingcourse_current_userscope_filter } + - { name: chill.export_filter, alias: accompanyingcourse_userscope_filter } - chill.person.export.filter_current_userjob: - class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserJobFilter - autowire: true - autoconfigure: true + Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserJobFilter: tags: - - { name: chill.export_filter, alias: accompanyingcourse_current_userjob_filter } + - { name: chill.export_filter, alias: accompanyingcourse_userjob_filter } chill.person.export.filter_socialissue: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\SocialIssueFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_socialissue_filter } chill.person.export.filter_step: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\StepFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_step_filter } chill.person.export.filter_geographicalunitstat: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\GeographicalUnitStatFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_geographicalunitstat_filter } chill.person.export.filter_socialaction: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\SocialActionFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_socialaction_filter } chill.person.export.filter_evaluation: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\EvaluationFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_evaluation_filter } chill.person.export.filter_origin: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\OriginFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_origin_filter } chill.person.export.filter_closingmotive: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ClosingMotiveFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_closingmotive_filter } chill.person.export.filter_administrative_location: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\AdministrativeLocationFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_administrative_location_filter } chill.person.export.filter_requestor: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\RequestorFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_requestor_filter } chill.person.export.filter_confidential: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ConfidentialFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_confidential_filter } chill.person.export.filter_emergency: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\EmergencyFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_emergency_filter } chill.person.export.filter_intensity: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\IntensityFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_intensity_filter } chill.person.export.filter_activeondate: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ActiveOnDateFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_activeondate_filter } chill.person.export.filter_activeonedaybetweendates: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ActiveOneDayBetweenDatesFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_activeonedaybetweendates_filter } chill.person.export.filter_referrer: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ReferrerFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_referrer_filter } chill.person.export.filter_openbetweendates: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\OpenBetweenDatesFilter - autowire: true - autoconfigure: true tags: - { name: chill.export_filter, alias: accompanyingcourse_openbetweendates_filter } ## Aggregators chill.person.export.aggregator_referrer_scope: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ScopeAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_scope_aggregator } chill.person.export.aggregator_referrer_job: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\JobAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_referrer_job_aggregator } chill.person.export.aggregator_socialissue: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\SocialIssueAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_socialissue_aggregator } chill.person.export.aggregator_step: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\StepAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_step_aggregator } chill.person.export.aggregator_geographicalunitstat: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\GeographicalUnitStatAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_geographicalunitstat_aggregator } chill.person.export.aggregator_socialaction: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\SocialActionAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_socialaction_aggregator } chill.person.export.aggregator_evaluation: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\EvaluationAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_evaluation_aggregator } chill.person.export.aggregator_origin: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\OriginAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_origin_aggregator } chill.person.export.aggregator_closingmotive: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ClosingMotiveAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_closingmotive_aggregator } chill.person.export.aggregator_administrative_location: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\AdministrativeLocationAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_administrative_location_aggregator } chill.person.export.aggregator_requestor: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\RequestorAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_requestor_aggregator } chill.person.export.aggregator_confidential: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ConfidentialAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_confidential_aggregator } chill.person.export.aggregator_emergency: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\EmergencyAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_emergency_aggregator } chill.person.export.aggregator_intensity: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\IntensityAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_intensity_aggregator } chill.person.export.aggregator_referrer: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ReferrerAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_referrer_aggregator } chill.person.export.aggregator_duration: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\DurationAggregator - autowire: true - autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_duration_aggregator } Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ReferrerScopeAggregator: - autoconfigure: true - autowire: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_ref_scope_aggregator } - + Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ByHouseholdCompositionAggregator: + tags: + - { name: chill.export_aggregator, alias: accompanyingcourse_by_household_compo_aggregator } diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20221013131221.php b/src/Bundle/ChillPersonBundle/migrations/Version20221013131221.php new file mode 100644 index 000000000..e8cb0d392 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20221013131221.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE chill_person_social_work_evaluation DROP active'); + } + + public function getDescription(): string + { + return 'Add an active column on evaluation'; + } + + public function up(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_person_social_work_evaluation ADD active BOOLEAN DEFAULT true NOT NULL'); + } +} diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20221014115500.php b/src/Bundle/ChillPersonBundle/migrations/Version20221014115500.php new file mode 100644 index 000000000..fe7b920d9 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20221014115500.php @@ -0,0 +1,69 @@ +addSql('DROP SEQUENCE chill_person_accompanying_period_step_history_id_seq CASCADE'); + $this->addSql('DROP TABLE chill_person_accompanying_period_step_history'); + } + + public function getDescription(): string + { + return 'Add step history on accompanying periods'; + } + + public function up(Schema $schema): void + { + $this->addSql('CREATE SEQUENCE chill_person_accompanying_period_step_history_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE chill_person_accompanying_period_step_history (id INT NOT NULL, period_id INT DEFAULT NULL, + endDate DATE DEFAULT NULL, startDate DATE NOT NULL, step TEXT NOT NULL, createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, + updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, createdBy_id INT DEFAULT NULL, + updatedBy_id INT DEFAULT NULL, PRIMARY KEY(id)) + '); + $this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CHECK (startDate <= endDate)'); + + $this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT ' . + 'chill_internal_acp_steps_not_overlaps EXCLUDE USING GIST( + -- extension btree_gist required to include comparaison with integer + period_id WITH =, + daterange(startDate, endDate, \'[)\') WITH && + ) + INITIALLY DEFERRED'); + + $this->addSql('CREATE INDEX IDX_84D514ACEC8B7ADE ON chill_person_accompanying_period_step_history (period_id)'); + $this->addSql('CREATE INDEX IDX_84D514AC3174800F ON chill_person_accompanying_period_step_history (createdBy_id)'); + $this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.endDate IS \'(DC2Type:date_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.startDate IS \'(DC2Type:date_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.createdAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.updatedAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT FK_84D514ACEC8B7ADE FOREIGN KEY (period_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT FK_84D514AC3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_84D514AC65FF1AEC ON chill_person_accompanying_period_step_history (updatedBy_id)'); + + // fill the tables with current state + $this->addSql( + 'INSERT INTO chill_person_accompanying_period_step_history (id, period_id, startDate, endDate, step, createdAt, updatedAt) + SELECT nextval(\'chill_person_accompanying_period_step_history_id_seq\'), id, openingDate, null, step, NOW(), NOW() FROM chill_person_accompanying_period WHERE step = \'CONFIRMED\' + UNION + SELECT nextval(\'chill_person_accompanying_period_step_history_id_seq\'), id, openingDate, closingDate, \'CONFIRMED\', NOW(), NOW() FROM chill_person_accompanying_period WHERE step = \'CLOSED\' + UNION + SELECT nextval(\'chill_person_accompanying_period_step_history_id_seq\'), id, closingDate, null, \'CLOSED\', NOW(), NOW() FROM chill_person_accompanying_period WHERE step = \'CLOSED\' + ' + ); + } +} diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index cbb269480..f3fd6c430 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -205,9 +205,11 @@ Resources: Interlocuteurs privilégiés Any requestor to this accompanying course: Aucun demandeur pour ce parcours Social action: Action d'accompagnement Social actions: Actions d'accompagnement +Pick a social action: Choisir une action d'accompagnement Last social actions: Les dernières actions d'accompagnement Social issue: Problématique sociale Social issues: Problématiques sociales +Pick a social issue: Choisir une problématique sociale Last events on accompanying course: Dernières actions de suivi Edit & activate accompanying course: Modifier et valider See accompanying periods: Voir toutes les périodes d'accompagnement @@ -581,10 +583,10 @@ Accepted evaluationtype: Évaluations Group by evaluation type: Grouper les évaluations par type Evaluation type: Type d'évaluation -Filter by maxdate: Filtrer les évaluations par date d'échéance +Filter evaluations by maxdate mention: Filtrer les évaluations qui possèdent une date d'échéance Maxdate: '' -is specified: La date d'échéance est spécifiée -is not specified: La date d'échéance n'est pas spécifiée +maxdate is specified: la date d'échéance est spécifiée +maxdate is not specified: la date d'échéance n'est pas spécifiée "Filtered by maxdate: only %choice%": "Filtré par date d'échéance: uniquement si %choice%" ## household filters/aggr @@ -951,7 +953,30 @@ notification: export: aggregator: course: + by_referrer: + Computation date for referrer: Date à laquelle le référent était actif by_user_scope: Group course by referrer's scope: Grouper les parcours par service du référent Computation date for referrer: Date à laquelle le référent était actif Referrer's scope: Service du référent de parcours + duration: + day: Durée du parcours en jours + week: Durée du parcours en semaines + month: Durée du parcours en mois + Precision: Unité de la durée + by_household_composition: + Household composition: Composition du ménage + Group course by household composition: Grouper les parcours par composition familiale des ménages des usagers concernés + Calc date: Date de calcul de la composition du ménage + filter: + course: + by_user_scope: + Computation date for referrer: Date à laquelle le référent était actif + by_referrer: + Computation date for referrer: Date à laquelle le référent était actif + +social_action: + and children: et dérivés + +social_issue: + and children: et dérivés