diff --git a/.gitignore b/.gitignore index d0a16a46a..7e0372d3c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .composer/* +composer composer.phar composer.lock docs/build/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 994d4bd61..df4dab422 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,22 @@ and this project adheres to ## Unreleased +* [person][export] Fixed: rename the alias for `accompanying_period` to `acp` in filter associated with person +* [activity][export] Feature: improve label for aliases in "Filter by activity type" +* [activity][export] DX/Feature: use of an `ActivityTypeRepositoryInterface` instead of the old-style EntityRepository +* [person][export] Fixed: some inconsistency with date filter on accompanying courses +* [person][export] Fixed: use left join for related entities in accompanying course aggregators + +## Test releases + +### 2.0.0-beta2 + +* [workflow]: Fixed: the notification is sent when the user is added to the first step. +* [budget] Feature: allow to desactivate some charges and resources, adding an `active` key in the configuration +* [person] Feature: on Evaluation, allow to configure an URL from the admin + +### 2022-06 + * [workflow]: added pagination to workflow list page * [homepage_widget]: null error on tasks widget fixed * [person-thirdparty]: fix quick-add of names that consist of multiple parts (eg. De Vlieger) within onthefly modal person/thirdparty @@ -19,8 +35,6 @@ and this project adheres to * [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) -## Test releases - ### 2022-05-30 * fix creating a new AccompanyingPeriodWorkEvaluationDocument when replacing the document (the workflow was lost) diff --git a/composer b/composer deleted file mode 100755 index 0e7ab8212..000000000 Binary files a/composer and /dev/null differ diff --git a/docs/source/installation/index.rst b/docs/source/installation/index.rst index 8acfc6174..00f27ed69 100644 --- a/docs/source/installation/index.rst +++ b/docs/source/installation/index.rst @@ -28,34 +28,42 @@ Requirements - This project use `docker `_ to be run. As a developer, use `docker-compose `_ to bootstrap a dev environment in a glance. You do not need any other dependencies ; - Make is used to automate scripts. -Installation in development mode -******************************** +Installation +************ + +If you plan to run chill in production: + +1. install it locally first, and check if everything is ok on your local machine; +2. once ready, build the image from your local machine, and deploy them. + +If you want to develop some bundles, the first step is sufficient (until you deploy on production). + 1. Get the code =============== -Clone or download the chill-app project and `cd` into the main directory. +Clone or download the chill-skeleton project and `cd` into the main directory. .. code-block:: bash - git clone https://gitlab.com/Chill-Projet/chill-app.git + git clone https://gitlab.com/Chill-Projet/chill-skeleton-basic.git cd chill-app As a developer, the code will stay on your computer and will be executed in docker container. To avoid permission problem, the code should be run with the same uid/gid from your current user. This is why we get your current user id with the command ``id -u`` in each following scripts. -2. Prepare composer download of sources -======================================= +2. Prepare composer to download the sources +=========================================== -As you are running in dev, you must configure an auth token for getting the source code. +As you are running in dev, you must configure an auth token for getting the source code. .. warning If you skip this part, the code will be downloaded from dist instead of source (with git repository). You will probably replace the source manually, but the next time you will run ```composer update```, your repository will be replaced and you might loose something. 1. Create a personal access token from https://gitlab.com/-/profile/personal_access_tokens, with the `read_api` scope. -2. add a file called ```.composer/auth.json```: +2. add a file called ```.composer/auth.json``` with this content: .. code-block:: json @@ -68,13 +76,25 @@ As you are running in dev, you must configure an auth token for getting the sour 2. Prepare your variables and environment ========================================= -Copy ```docker-compose.override.dev.yml``` into ```docker-compose.override.yml``` +Copy ```docker-compose.override.dev.yml``` into ```docker-compose.override.yml``` .. code-block:: bash cp docker-compose.override.dev.template.yml docker-compose.override.yml -Configure your environment variables, by creating a .env.local file and override the desired variables. +2. Prepare your variables and docker-compose +============================================ + +Have a look at the variable in ``.env`` and check if you need to adapt them. If they do not adapt with your need, or if some are missing: + +1. copy the file as ``.env.local``: ``cp .env .env.local`` +2. you may replace some variables inside ``.env`` + +Prepare also you docker-compose installation, and adapt it to your needs: + +1. If you plan to deploy on dev, copy the file ``docker-compose.override.dev.template.yml`` to ``docker-compose.override.yml``. +2. adapt to your needs. + **Note**: If you intend to use the bundle ``Chill-Doc-Store``, you will need to configure and install an openstack object storage container with temporary url middleware. You will have to configure `secret keys `_. @@ -90,10 +110,21 @@ This script can be run using `make` This script will : 1. force docker-compose to, eventually, pull the base images and build the image used by this project ; -2. run an install script to download `composer `_ ; +2. run an install script to download `composer `_ ; 3. install the php dependencies 4. build assets +.. warning:: + + The script will work only if the binary ``docker-compose`` is located into your ``PATH``. If you use ``compose`` as a docker plugin, + you can simulate this binary by creating this file at (for instance), ``/usr/local/bin/docker-compose`` (and run ``chmod +x /usr/local/bin/docker-compose``): + + .. code-block:: bash + + #!/bin/bash + + /usr/bin/docker compose "$@" + .. note:: @@ -117,13 +148,20 @@ This script will : .. code-block:: bash - make migrate + # mount into to container + ./docker-php.sh + # and load fixtures + bin/console doctrine:migrations:migrate + Chill will be available at ``http://localhost:8001.`` Currently, there isn't any user or data. To add fixtures, run .. code-block:: bash - docker-compose exec --user $(id -u) php bin/console doctrine:fixtures:load --purge-with-truncate + # mount into to container + ./docker-php.sh + # and load fixtures + bin/console doctrine:fixtures:load --purge-with-truncate There are several users available: @@ -134,13 +172,14 @@ The password is always ``password``. Now, read `Operations` below. + Operations ********** Build assets ============ -run those commands: +run those commands: .. code-block:: bash @@ -152,8 +191,8 @@ How to execute the console ? .. code-block:: bash # if a container is running - docker-compose exec --user $(id -u) php bin/console - # if not + ./docker-php.sh + # if not docker-compose run --user $(id -u) php bin/console How to create the database schema (= run migrations) ? @@ -162,8 +201,9 @@ How to create the database schema (= run migrations) ? .. code-block:: bash # if a container is running - docker-compose exec --user $(id -u) php bin/console doctrine:migrations:migrate - # if not + ./docker-php.sh + bin/console doctrine:migrations:migrate + # if not docker-compose run --user $(id -u) php bin/console doctrine:migrations:migrate @@ -180,8 +220,9 @@ How to load fixtures ? (development mode only) .. code-block:: bash # if a container is running - docker-compose exec --user $(id -u) php bin/console doctrine:fixtures:load - # if not + ./docker-php.sh + bin/console doctrine:fixtures:load + # if not docker-compose run --user $(id -u) php bin/console doctrine:fixtures:load How to open a terminal in the project @@ -190,8 +231,8 @@ How to open a terminal in the project .. code-block:: bash # if a container is running - docker-compose exec --user $(id -u) php /bin/bash - # if not + ./docker-php.sh + # if not docker-compose run --user $(id -u) php /bin/bash How to run composer ? @@ -200,21 +241,22 @@ How to run composer ? .. code-block:: bash # if a container is running - docker-compose exec --user $(id -u) php ./composer.phar - # if not - docker-compose run --user $(id -u) php ./composer.phar + ./docker-php.sh + composer + # if not + docker-compose run --user $(id -u) php composer How to access to PGADMIN ? ========================== -Pgadmin is installed with docker-compose. +Pgadmin is installed with docker-compose, and is available **only if you uncomment the appropriate lines into ``docker-compose.override.yml``. You can access it at ``http://localhost:8002``. Credentials: -- login: admin@chill.social -- password: password +- login: from the variable you set into ``docker-composer.override.yml`` +- password: same :-) How to run tests ? ================== @@ -223,30 +265,48 @@ 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 test inside `main` bundle: .. code-block:: bash # mount into the php image - docker-compose run --user $(id -u) php /bin/bash + ./docker-php.sh # cd into main directory - cd vendor/chill-project/main + cd vendor/chill-project/chill-bundles # download deps - php ../../../composer.phar install + git submodule init + git submodule update + composer install # run tests - /vendor/bin/phpunit + bin/phpunit src/Bundle/path/to/your/test How to run webpack interactively ================================ Executing :code:`bash docker-node.sh` will open a terminal in a node container, with volumes mounted. -Build the documentation API -=========================== +How to switch the branch for chill-bundles, and get new dependencies +==================================================================== -A basic configuration of `sami `_ is embedded within the project. +During development, you will switch to new branches for chill-bundles. As long as the dependencies are equals, this does not cause any problem. But sometimes, a new branch introduces a new dependency, and you must download it. -A configuration file for `phpDocumentor `_ is present. +.. warning:: + + Ensure that you have gitlab-token ready before updating your branches. See above. + +In order to do that without pain, use those steps: + +0. Ensuire you have a token, set +1. at the app's root, update the ``composer.json`` to your current branch: + +.. code-block:: json + + { + "require": { + "chill-bundles": "dev-@dev" + } + +2. mount into the php container (``./docker-php.sh``), and run ``composer update`` Error `An exception has been thrown during the rendering of a template ("Asset manifest file "/var/www/app/web/build/manifest.json" does not exist.").` on first run ==================================================================================================================================================================== @@ -260,8 +320,9 @@ Currently, to run this software in production, the *state of the art* is the fol 1. Run the software locally and tweak the configuration to your needs ; 2. Build the image and store them into a private container registry. This can be done using :code:`make build-and-push-image`. - - To be sure to target the correct container registry, you have to adapt the values ``IMAGE_NGINX`` and ``IMAGE_PHP`` date in the ``.env`` file. + + To be sure to target the correct container registry, you have to adapt the image names into your ``docker-compose.override.yml`` file. +3. Push the image on your registry, or upload them to the destination machine using ``docker image save`` and ``docker image load``. 3. Run the image on your production server, using docker-compose or eventually docker stack. You have to customize the variable set in docker-compose. See also the :ref:`running-production-tips-and-tricks` below. @@ -293,7 +354,7 @@ It is worth having an eye on the configuration of logstash container. Design principles ***************** -Why the DB URL is set in environment, and not in parameters.yml ? +Why the DB URL is also set in environment, and not in .env file ? ================================================================= Because, at startup, a script does check the db is up and, if not, wait for a couple of seconds before running ``entrypoint.sh``. For avoiding double configuration, the configuration of the PHP app takes his configuration from environment also (and it will be standard in future releases, with symfony 4.0). diff --git a/exports_alias_conventions.csv b/exports_alias_conventions.csv index 1215479d8..ab32cda8e 100644 --- a/exports_alias_conventions.csv +++ b/exports_alias_conventions.csv @@ -11,10 +11,10 @@ AccompanyingPeriod::class,,,acp ,User::class,acp.user,acpuser AccompanyingPeriodWork::class,,,acpw ,AccompanyingPeriodWorkEvaluation::class,acpw.accompanyingPeriodWorkEvaluations,workeval -,Goal::class,acpw.goals,goal ,User::class,acpw.referrers,acpwuser -,Result::class,acpw.results,acpwresult ,SocialAction::class,acpw.socialAction,acpwsocialaction +,Goal::class,acpw.goals,goal +,Result::class,acpw.results,result AccompanyingPeriodParticipation::class,,,acppart ,Person::class,acppart.person,partperson AccompanyingPeriodWorkEvaluation::class,,,workeval @@ -23,23 +23,23 @@ Goal::class,,,goal ,Result::class,goal.results,goalresult Person::class,,,person ,Center::class,person.center,center -,HouseholdMember::class,partperson.householdParticipations,member +,HouseholdMember::class,partperson.householdParticipations,householdmember ,MaritalStatus::class,person.maritalStatus,personmarital +,VendeePerson::class,,vp +,VendeePersonMineur::class,,vpm ResidentialAddress::class,,,resaddr -,Person::class,resaddr.person,resaddrperson -,Center::class,resaddrperson.center,resaddrcenter ,ThirdParty::class,resaddr.hostThirdParty,tparty ThirdParty::class,,,tparty ,ThirdPartyCategory::class,tparty.categories,tpartycat -HouseholdMember::class,,,member -,Household::class,member.household,household -,Person::class,member.person,memberperson +HouseholdMember::class,,,householdmember +,Household::class,householdmember.household,household +,Person::class,householdmember.person,memberperson ,,memberperson.center,membercenter Household::class,,,household ,HouseholdComposition::class,household.compositions,composition Activity::class,,,activity ,Person::class,activity.person,actperson -,AccompanyingPeriod::class,activity.accompanyingPeriod,actacp +,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 @@ -58,11 +58,6 @@ Calendar::class,,,cal ,Location::class,cal.location,calloc ,User::class,cal.user,caluser VendeePerson::class,,,vp -,Person::class,vp.person,vpperson -,Center::class,vpperson.center,vpcenter ,SituationProfessionelle::class,vp.situationProfessionelle,vpprof ,StatutLogement::class,vp.statutLogement,vplog ,TempsDeTravail::class,vp.tempsDeTravail,vptt -VendeePersonMineur::class,,,vpm -,Person::class,vpm.person,vpmperson -,Center::class,vpmperson.center,vpmcenter diff --git a/exports_alias_conventions.md b/exports_alias_conventions.md index 85973eec1..579d004a5 100644 --- a/exports_alias_conventions.md +++ b/exports_alias_conventions.md @@ -5,72 +5,67 @@ Add condition with distinct alias on each export join clauses (Indicators + Filt These are alias conventions : -| Entity | Join | Attribute | Alias | -| :--- | :--- |:-------------------------------------------|:----------------------------------| -| AccompanyingPeriod::class | | | acp | -| | AccompanyingPeriodWork::class | acp.works | acpw | -| | AccompanyingPeriodParticipation::class | acp.participations | acppart | -| | Location::class | acp.administrativeLocation | acploc | -| | ClosingMotive::class | acp.closingMotive | acpmotive | -| | UserJob::class | acp.job | acpjob | -| | Origin::class | acp.origin | acporigin | -| | Scope::class | acp.scopes | acpscope | -| | SocialIssue::class | acp.socialIssues | acpsocialissue | -| | User::class | acp.user | acpuser | -| AccompanyingPeriodWork::class | | | acpw | -| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval | -| | Goal::class | acpw.goals | goal | -| | User::class | acpw.referrers | acpwuser | -| | Result::class | acpw.results | acpwresult | -| | SocialAction::class | acpw.socialAction | acpwsocialaction | -| AccompanyingPeriodParticipation::class | | | acppart | -| | Person::class | acppart.person | partperson | -| AccompanyingPeriodWorkEvaluation::class | | | workeval | -| | Evaluation::class | workeval.evaluation | eval | -| Goal::class | | | goal | -| | Result::class | goal.results | goalresult | -| Person::class | | | person | -| | Center::class | person.center | center | -| | HouseholdMember::class | partperson.householdParticipations | member | -| | MaritalStatus::class | person.maritalStatus | personmarital | -| ResidentialAddress::class | | | resaddr | -| | Person::class | resaddr.person | resaddrperson | -| | Center::class | resaddrperson.center | resaddrcenter | -| | ThirdParty::class | resaddr.hostThirdParty | tparty | -| ThirdParty::class | | | tparty | -| | ThirdPartyCategory::class | tparty.categories | tpartycat | -| HouseholdMember::class | | | member | -| | Household::class | member.household | household | -| | Person::class | member.person | memberperson | -| | | memberperson.center | membercenter | -| Household::class | | | household | -| | HouseholdComposition::class | household.compositions | composition | -| Activity::class | | | activity | -| | Person::class | activity.person | actperson | -| | AccompanyingPeriod::class | activity.accompanyingPeriod | actacp | -| | 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 | -| | Location::class | activity.location | actloc | -| | SocialAction::class | activity.socialActions | actsocialaction | -| | SocialIssue::class | activity.socialIssues | actsocialssue | -| | ThirdParty::class | activity.thirdParties | acttparty | -| | User::class | activity.user | actuser | -| | User::class | activity.users | actusers | -| | ActivityReason::class | activity.reasons | actreasons | -| | Center::class | actperson.center | actcenter | -| ActivityReason::class | | | actreasons | -| | ActivityReasonCategory::class | actreason.category | actreasoncat | -| Calendar::class | | | cal | -| | CancelReason::class | cal.cancelReason | calcancel | -| | Location::class | cal.location | calloc | -| | User::class | cal.user | caluser | -| VendeePerson::class | | | vp | -| | Person::class | vp.person | vpperson | -| | Center::class | vpperson.center | vpcenter | -| | SituationProfessionelle::class | vp.situationProfessionelle | vpprof | -| | StatutLogement::class | vp.statutLogement | vplog | -| | TempsDeTravail::class | vp.tempsDeTravail | vptt | -| VendeePersonMineur::class | | | vpm | -| | Person::class | vpm.person | vpmperson | -| | Center::class | vpmperson.center | vpmcenter | +| Entity | Join | Attribute | Alias | +|:----------------------------------------|:----------------------------------------|:-------------------------------------------|:----------------------------------| +| AccompanyingPeriod::class | | | acp | +| | AccompanyingPeriodWork::class | acp.works | acpw | +| | AccompanyingPeriodParticipation::class | acp.participations | acppart | +| | Location::class | acp.administrativeLocation | acploc | +| | ClosingMotive::class | acp.closingMotive | acpmotive | +| | UserJob::class | acp.job | acpjob | +| | Origin::class | acp.origin | acporigin | +| | Scope::class | acp.scopes | acpscope | +| | SocialIssue::class | acp.socialIssues | acpsocialissue | +| | User::class | acp.user | acpuser | +| 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 | +| AccompanyingPeriodParticipation::class | | | acppart | +| | Person::class | acppart.person | partperson | +| AccompanyingPeriodWorkEvaluation::class | | | workeval | +| | Evaluation::class | workeval.evaluation | eval | +| Goal::class | | | goal | +| | Result::class | goal.results | goalresult | +| Person::class | | | person | +| | Center::class | person.center | center | +| | HouseholdMember::class | partperson.householdParticipations | householdmember | +| | MaritalStatus::class | person.maritalStatus | personmarital | +| | VendeePerson::class | | vp | +| | VendeePersonMineur::class | | vpm | +| ResidentialAddress::class | | | resaddr | +| | ThirdParty::class | resaddr.hostThirdParty | tparty | +| ThirdParty::class | | | tparty | +| | ThirdPartyCategory::class | tparty.categories | tpartycat | +| HouseholdMember::class | | | householdmember | +| | Household::class | householdmember.household | household | +| | Person::class | householdmember.person | memberperson | +| | | memberperson.center | membercenter | +| Household::class | | | household | +| | HouseholdComposition::class | household.compositions | composition | +| Activity::class | | | activity | +| | Person::class | activity.person | actperson | +| | 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 | +| | Location::class | activity.location | actloc | +| | SocialAction::class | activity.socialActions | actsocialaction | +| | SocialIssue::class | activity.socialIssues | actsocialssue | +| | ThirdParty::class | activity.thirdParties | acttparty | +| | User::class | activity.user | actuser | +| | User::class | activity.users | actusers | +| | ActivityReason::class | activity.reasons | actreasons | +| | Center::class | actperson.center | actcenter | +| ActivityReason::class | | | actreasons | +| | ActivityReasonCategory::class | actreason.category | actreasoncat | +| Calendar::class | | | cal | +| | CancelReason::class | cal.cancelReason | calcancel | +| | Location::class | cal.location | calloc | +| | User::class | cal.user | caluser | +| VendeePerson::class | | | vp | +| | SituationProfessionelle::class | vp.situationProfessionelle | vpprof | +| | StatutLogement::class | vp.statutLogement | vplog | +| | TempsDeTravail::class | vp.tempsDeTravail | vptt | diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php index 05ce2e812..b81d3937c 100644 --- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php +++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php @@ -17,7 +17,7 @@ use Chill\ActivityBundle\Form\ActivityType; use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface; use Chill\ActivityBundle\Repository\ActivityRepository; use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository; -use Chill\ActivityBundle\Repository\ActivityTypeRepository; +use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\ActivityBundle\Security\Authorization\ActivityVoter; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; use Chill\MainBundle\Repository\LocationRepository; @@ -55,7 +55,7 @@ final class ActivityController extends AbstractController private ActivityTypeCategoryRepository $activityTypeCategoryRepository; - private ActivityTypeRepository $activityTypeRepository; + private ActivityTypeRepositoryInterface $activityTypeRepository; private CenterResolverManagerInterface $centerResolver; @@ -77,7 +77,7 @@ final class ActivityController extends AbstractController public function __construct( ActivityACLAwareRepositoryInterface $activityACLAwareRepository, - ActivityTypeRepository $activityTypeRepository, + ActivityTypeRepositoryInterface $activityTypeRepository, ActivityTypeCategoryRepository $activityTypeCategoryRepository, PersonRepository $personRepository, ThirdPartyRepository $thirdPartyRepository, diff --git a/src/Bundle/ChillActivityBundle/Entity/ActivityType.php b/src/Bundle/ChillActivityBundle/Entity/ActivityType.php index 845b31ca2..a2b15ee23 100644 --- a/src/Bundle/ChillActivityBundle/Entity/ActivityType.php +++ b/src/Bundle/ChillActivityBundle/Entity/ActivityType.php @@ -516,6 +516,11 @@ class ActivityType return $this->userVisible; } + public function hasCategory(): bool + { + return null !== $this->getCategory(); + } + /** * Is active * return true if the type is active. diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialActionAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialActionAggregator.php index f35fe6e6b..251f2e9d4 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialActionAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialActionAggregator.php @@ -41,18 +41,11 @@ class BySocialActionAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('actsocialaction', $qb->getAllAliases(), true)) { - $qb->join('activity.socialActions', 'actsocialaction'); + $qb->leftJoin('activity.socialActions', 'actsocialaction'); } $qb->addSelect('actsocialaction.id AS socialaction_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('socialaction_aggregator'); - } else { - $qb->groupBy('socialaction_aggregator'); - } + $qb->addGroupBy('socialaction_aggregator'); } public function applyOn(): string @@ -72,6 +65,10 @@ class BySocialActionAggregator implements AggregatorInterface return 'Social action'; } + if (null === $value) { + return ''; + } + $sa = $this->actionRepository->find($value); return $this->actionRender->renderString($sa, []); diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialIssueAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialIssueAggregator.php index 1126b9373..d7abcfd49 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialIssueAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialIssueAggregator.php @@ -41,18 +41,11 @@ class BySocialIssueAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('actsocialissue', $qb->getAllAliases(), true)) { - $qb->join('activity.socialIssues', 'actsocialissue'); + $qb->leftJoin('activity.socialIssues', 'actsocialissue'); } $qb->addSelect('actsocialissue.id AS socialissue_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('socialissue_aggregator'); - } else { - $qb->groupBy('socialissue_aggregator'); - } + $qb->addGroupBy('socialissue_aggregator'); } public function applyOn(): string @@ -72,6 +65,10 @@ class BySocialIssueAggregator implements AggregatorInterface return 'Social issues'; } + if (null === $value) { + return ''; + } + $i = $this->issueRepository->find($value); return $this->issueRender->renderString($i, []); diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByThirdpartyAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByThirdpartyAggregator.php index 4b56c6fc7..ebd813af4 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByThirdpartyAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByThirdpartyAggregator.php @@ -41,18 +41,11 @@ class ByThirdpartyAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acttparty', $qb->getAllAliases(), true)) { - $qb->join('activity.thirdParties', 'acttparty'); + $qb->leftJoin('activity.thirdParties', 'acttparty'); } $qb->addSelect('acttparty.id AS thirdparty_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('thirdparty_aggregator'); - } else { - $qb->groupBy('thirdparty_aggregator'); - } + $qb->addGroupBy('thirdparty_aggregator'); } public function applyOn(): string @@ -72,6 +65,10 @@ class ByThirdpartyAggregator implements AggregatorInterface return 'Accepted thirdparty'; } + if (null === $value) { + return ''; + } + $tp = $this->thirdPartyRepository->find($value); return $this->thirdPartyRender->renderString($tp, []); diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php index 3787e5286..9eb5c307e 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php @@ -41,18 +41,11 @@ class ByUserAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('actusers', $qb->getAllAliases(), true)) { - $qb->join('activity.users', 'actusers'); + $qb->leftJoin('activity.users', 'actusers'); } $qb->addSelect('actusers.id AS users_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('users_aggregator'); - } else { - $qb->groupBy('users_aggregator'); - } + $qb->addGroupBy('users_aggregator'); } public function applyOn(): string @@ -72,6 +65,10 @@ class ByUserAggregator implements AggregatorInterface return 'Accepted users'; } + if (null === $value) { + return ''; + } + $u = $this->userRepository->find($value); return $this->userRender->renderString($u, []); diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php index 5603b1b78..058b1fc15 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php @@ -13,7 +13,6 @@ namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators; use Chill\ActivityBundle\Export\Declarations; use Chill\MainBundle\Export\AggregatorInterface; -use DateTime; use Doctrine\ORM\QueryBuilder; use RuntimeException; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; @@ -49,41 +48,27 @@ class DateAggregator implements AggregatorInterface switch ($data['frequency']) { case 'month': - $fmt = 'MM'; + $fmt = 'YYYY-MM'; -break; + break; case 'week': - $fmt = 'IW'; + $fmt = 'YYYY-IW'; -break; + break; case 'year': $fmt = 'YYYY'; $order = 'DESC'; -break; // order DESC does not works ! + break; // order DESC does not works ! default: throw new RuntimeException(sprintf("The frequency data '%s' is invalid.", $data['frequency'])); } $qb->addSelect(sprintf("TO_CHAR(activity.date, '%s') AS date_aggregator", $fmt)); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('date_aggregator'); - } else { - $qb->groupBy('date_aggregator'); - } - - $orderBy = $qb->getDQLPart('orderBy'); - - if (!empty($orderBy)) { - $qb->addOrderBy('date_aggregator', $order); - } else { - $qb->orderBy('date_aggregator', $order); - } + $qb->addGroupBy('date_aggregator'); + $qb->addOrderBy('date_aggregator', $order); } public function applyOn(): string @@ -109,16 +94,12 @@ break; // order DESC does not works ! return 'by ' . $data['frequency']; } + if (null === $value) { + return ''; + } + switch ($data['frequency']) { case 'month': - $month = DateTime::createFromFormat('!m', $value); - - return sprintf( - '%02d (%s)', - $value, - $month->format('M') - ); - case 'week': //return $this->translator->trans('for week') .' '. $value ; diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/LocationTypeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/LocationTypeAggregator.php index d05c0eb41..a4c1087db 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/LocationTypeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/LocationTypeAggregator.php @@ -41,18 +41,11 @@ class LocationTypeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('actloc', $qb->getAllAliases(), true)) { - $qb->join('activity.location', 'actloc'); + $qb->leftJoin('activity.location', 'actloc'); } $qb->addSelect('IDENTITY(actloc.locationType) AS locationtype_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('locationtype_aggregator'); - } else { - $qb->groupBy('locationtype_aggregator'); - } + $qb->addGroupBy('locationtype_aggregator'); } public function applyOn(): string @@ -72,6 +65,10 @@ class LocationTypeAggregator implements AggregatorInterface return 'Accepted locationtype'; } + if (null === $value) { + return ''; + } + $lt = $this->locationTypeRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php index faaa08b8e..1b7041d9d 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php @@ -41,18 +41,11 @@ class UserScopeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('actuser', $qb->getAllAliases(), true)) { - $qb->join('activity.user', 'actuser'); + $qb->leftJoin('activity.user', 'actuser'); } $qb->addSelect('IDENTITY(actuser.mainScope) AS userscope_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('userscope_aggregator'); - } else { - $qb->groupBy('userscope_aggregator'); - } + $qb->addGroupBy('userscope_aggregator'); } public function applyOn(): string @@ -72,6 +65,10 @@ class UserScopeAggregator implements AggregatorInterface return 'Scope'; } + if (null === $value) { + return ''; + } + $s = $this->scopeRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php index 5c285ca3e..04ce2014c 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php @@ -12,11 +12,10 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Aggregator; use Chill\ActivityBundle\Export\Declarations; -use Chill\ActivityBundle\Repository\ActivityTypeRepository; +use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Closure; -use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use function in_array; @@ -25,12 +24,12 @@ class ActivityTypeAggregator implements AggregatorInterface { public const KEY = 'activity_type_aggregator'; - protected ActivityTypeRepository $activityTypeRepository; + protected ActivityTypeRepositoryInterface $activityTypeRepository; protected TranslatableStringHelperInterface $translatableStringHelper; public function __construct( - ActivityTypeRepository $activityTypeRepository, + ActivityTypeRepositoryInterface $activityTypeRepository, TranslatableStringHelperInterface $translatableStringHelper ) { $this->activityTypeRepository = $activityTypeRepository; @@ -49,14 +48,7 @@ class ActivityTypeAggregator implements AggregatorInterface } $qb->addSelect(sprintf('IDENTITY(activity.activityType) AS %s', self::KEY)); - - $groupby = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy(self::KEY); - } else { - $qb->groupBy(self::KEY); - } + $qb->addGroupBy(self::KEY); } public function applyOn(): string @@ -79,6 +71,10 @@ class ActivityTypeAggregator implements AggregatorInterface return 'Activity type'; } + if (null === $value) { + return ''; + } + $t = $this->activityTypeRepository->find($value); return $this->translatableStringHelper->localize($t->getName()); diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php index 39c7c52cb..86e9e77bb 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php @@ -61,14 +61,15 @@ class ActivityUserAggregator implements AggregatorInterface public function getLabels($key, $values, $data): Closure { - // preload users at once - $this->userRepository->findBy(['id' => $values]); - return function ($value) { if ('_header' === $value) { return 'Activity user'; } + if (null === $value) { + return ''; + } + $u = $this->userRepository->find($value); return $this->userRender->renderString($u, []); diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/PersonAggregators/ActivityReasonAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/PersonAggregators/ActivityReasonAggregator.php index 9d7cd3116..bf6b3ca02 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/PersonAggregators/ActivityReasonAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/PersonAggregators/ActivityReasonAggregator.php @@ -25,8 +25,8 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface; -use function array_key_exists; use function count; +use function in_array; class ActivityReasonAggregator implements AggregatorInterface, ExportElementValidatedInterface { @@ -55,10 +55,10 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali { // add select element if ('reasons' === $data['level']) { - $elem = 'reasons.id'; + $elem = 'actreasons.id'; $alias = 'activity_reasons_id'; } elseif ('categories' === $data['level']) { - $elem = 'category.id'; + $elem = 'actreasoncat.id'; $alias = 'activity_categories_id'; } else { throw new RuntimeException('The data provided are not recognized.'); @@ -67,7 +67,7 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali $qb->addSelect($elem . ' as ' . $alias); // make a jointure only if needed - if (!in_array( 'actreasons', $qb->getAllAliases(), true)) { + if (!in_array('actreasons', $qb->getAllAliases(), true)) { $qb->innerJoin('activity.reasons', 'actreasons'); } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php index be20d2b9c..326033013 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php @@ -17,9 +17,13 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; +use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class AvgActivityDuration implements ExportInterface, GroupedExportInterface @@ -34,7 +38,6 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface public function buildForm(FormBuilderInterface $builder) { - // TODO: Implement buildForm() method. } public function getAllowedFormattersTypes(): array @@ -83,10 +86,27 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('activity') - ->join('activity.accompanyingPeriod', 'actacp'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - $qb->select('AVG(activity.durationTime) as export_avg_activity_duration'); + $qb = $this->repository->createQueryBuilder('activity'); + + $qb + ->join('activity.accompanyingPeriod', 'acp') + ->select('AVG(activity.durationTime) as export_avg_activity_duration') + ->andWhere($qb->expr()->isNotNull('activity.durationTime')); + + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers); return $qb; } @@ -101,7 +121,7 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface return [ Declarations::ACTIVITY, Declarations::ACTIVITY_ACP, - //PersonDeclarations::ACP_TYPE, + PersonDeclarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php index 021c82d72..6514be3e0 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php @@ -17,9 +17,13 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; +use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterface @@ -83,10 +87,27 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('activity') - ->join('activity.accompanyingPeriod', 'actacp'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - $qb->select('AVG(activity.travelTime) as export_avg_activity_visit_duration'); + $qb = $this->repository->createQueryBuilder('activity'); + + $qb + ->join('activity.accompanyingPeriod', 'acp') + ->select('AVG(activity.travelTime) as export_avg_activity_visit_duration') + ->andWhere($qb->expr()->isNotNull('activity.travelTime')); + + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers); return $qb; } @@ -101,7 +122,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac return [ Declarations::ACTIVITY, Declarations::ACTIVITY_ACP, - //PersonDeclarations::ACP_TYPE, + PersonDeclarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php index 1f8b742dd..92e8cdc27 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php @@ -17,6 +17,8 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; @@ -84,13 +86,26 @@ class CountActivity implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('activity'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('actacp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'actacp'); - } + $qb = $this->repository + ->createQueryBuilder('activity') + ->join('activity.accompanyingPeriod', 'acp'); - $qb->select('COUNT(activity.id) as export_count_activity'); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers); + + $qb->select('COUNT(DISTINCT activity.id) as export_count_activity'); return $qb; } @@ -105,7 +120,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface return [ Declarations::ACTIVITY, Declarations::ACTIVITY_ACP, - //PersonDeclarations::ACP_TYPE, + PersonDeclarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php index 4d9b43034..692ff5530 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php @@ -17,9 +17,13 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; +use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class SumActivityDuration implements ExportInterface, GroupedExportInterface @@ -83,13 +87,27 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('activity'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('actacp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'actacp'); - } + $qb = $this->repository + ->createQueryBuilder('activity') + ->join('activity.accompanyingPeriod', 'acp'); - $qb->select('SUM(activity.durationTime) as export_sum_activity_duration'); + $qb->select('SUM(activity.durationTime) as export_sum_activity_duration') + ->andWhere($qb->expr()->isNotNull('activity.durationTime')); + + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers); return $qb; } @@ -104,7 +122,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface return [ Declarations::ACTIVITY, Declarations::ACTIVITY_ACP, - //PersonDeclarations::ACP_TYPE, + PersonDeclarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php index 212b1384f..e9be03003 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php @@ -17,9 +17,13 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; +use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class SumActivityVisitDuration implements ExportInterface, GroupedExportInterface @@ -83,13 +87,27 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('activity'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('actacp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'actacp'); - } + $qb = $this->repository + ->createQueryBuilder('activity') + ->join('activity.accompanyingPeriod', 'acp'); - $qb->select('SUM(activity.travelTime) as export_sum_activity_visit_duration'); + $qb->select('SUM(activity.travelTime) as export_sum_activity_visit_duration') + ->andWhere($qb->expr()->isNotNull('activity.travelTime')); + + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers); return $qb; } @@ -104,7 +122,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac return [ Declarations::ACTIVITY, Declarations::ACTIVITY_ACP, - //PersonDeclarations::ACP_TYPE, + PersonDeclarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php index 398fb14bb..fec2b8617 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php @@ -84,16 +84,24 @@ class CountActivity implements ExportInterface, GroupedExportInterface { $centers = array_map(static fn ($el) => $el['center'], $acl); - $qb = $this->activityRepository->createQueryBuilder('activity'); - - if (!in_array('actperson', $qb->getAllAliases(), true)) { - $qb->join('activity.person', 'actperson'); - } + $qb = $this->activityRepository + ->createQueryBuilder('activity') + ->join('activity.person', 'person') + ->join('person.centerHistory', 'centerHistory'); $qb->select('COUNT(activity.id) as export_count_activity'); $qb - ->where($qb->expr()->in('person.center', ':centers')) + ->where( + $qb->expr()->andX( + $qb->expr()->lte('centerHistory.startDate', 'activity.date'), + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory.endDate'), + $qb->expr()->gt('centerHistory.endDate', 'activity.date') + ) + ) + ) + ->andWhere($qb->expr()->in('centerHistory.center', ':centers')) ->setParameter('centers', $centers); return $qb; @@ -109,7 +117,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface return [ Declarations::ACTIVITY, Declarations::ACTIVITY_PERSON, - //PersonDeclarations::PERSON_TYPE, + PersonDeclarations::PERSON_TYPE, ]; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/ListActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/ListActivity.php index 17b9bd687..78368b0eb 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/ListActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/ListActivity.php @@ -210,7 +210,7 @@ class ListActivity implements ListInterface, GroupedExportInterface $qb ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.person', 'actperson') + ->join('activity.person', 'person') ->join('actperson.center', 'actcenter') ->andWhere('actcenter IN (:authorized_centers)') ->setParameter('authorized_centers', $centers); diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php index bcb4da05d..f1228842f 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php @@ -119,11 +119,24 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface $select = 'SUM(activity.durationTime) AS export_stat_activity'; } - return $qb->select($select) - ->join('activity.person', 'actperson') - ->join('actperson.center', 'actcenter') - ->where($qb->expr()->in('actcenter', ':centers')) - ->setParameter(':centers', $centers); + $qb->select($select) + ->join('activity.person', 'person') + ->join('person.centerHistory', 'centerHistory'); + + $qb + ->where( + $qb->expr()->andX( + $qb->expr()->lte('centerHistory.startDate', 'activity.date'), + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory.endDate'), + $qb->expr()->gt('centerHistory.endDate', 'activity.date') + ) + ) + ) + ->andWhere($qb->expr()->in('centerHistory.center', ':centers')) + ->setParameter('centers', $centers); + + return $qb; } public function requiredRole(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php similarity index 53% rename from src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php rename to src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php index f76a00c33..b6e26dfdb 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php @@ -9,26 +9,31 @@ declare(strict_types=1); -namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; +namespace Chill\ActivityBundle\Export\Filter\ACPFilters; +use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\ActivityType; +use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\MainBundle\Export\FilterInterface; -use Chill\MainBundle\Templating\TranslatableStringHelper; +use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\Query\Expr\Andx; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; -/** - * TODO merge with ActivityTypeFilter in ChillActivity (!?). - */ class ActivityTypeFilter implements FilterInterface { - private TranslatableStringHelper $translatableStringHelper; + private ActivityTypeRepositoryInterface $activityTypeRepository; - public function __construct(TranslatableStringHelper $translatableStringHelper) - { + private TranslatableStringHelperInterface $translatableStringHelper; + + public function __construct( + ActivityTypeRepositoryInterface $activityTypeRepository, + TranslatableStringHelperInterface $translatableStringHelper + ) { + $this->activityTypeRepository = $activityTypeRepository; $this->translatableStringHelper = $translatableStringHelper; } @@ -39,31 +44,14 @@ class ActivityTypeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - // One2many between activity and accompanyingperiod is not reversed ! - // we replace indicator 'from' clause by 'act', and put 'acp' in a join - - $qb->resetDQLPart('from'); - $qb->from('ChillActivityBundle:Activity', 'activity'); - - if (!in_array('actacp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'actacp'); + if (!in_array('activity', $qb->getAllAliases(), true)) { + $qb->join(Activity::class, 'activity', Expr\Join::WITH, 'activity.accompanyingPeriod = acp'); } - if (!in_array('acttype', $qb->getAllAliases(), true)) { - $qb->join('activity.activityType', 'acttype'); - } + $clause = $qb->expr()->in('activity.activityType', ':selected_activity_types'); - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('acttype.id', ':activitytypes'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('activitytypes', $data['accepted_activitytypes']); + $qb->andWhere($clause); + $qb->setParameter('selected_activity_types', $data['types']); } public function applyOn() @@ -75,8 +63,12 @@ class ActivityTypeFilter implements FilterInterface { $builder->add('accepted_activitytypes', EntityType::class, [ 'class' => ActivityType::class, + 'choices' => $this->activityTypeRepository->findAllActive(), 'choice_label' => function (ActivityType $aty) { - return $this->translatableStringHelper->localize($aty->getName()); + return + ($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '') + . + $this->translatableStringHelper->localize($aty->getName()); }, 'multiple' => true, 'expanded' => true, @@ -92,7 +84,7 @@ class ActivityTypeFilter implements FilterInterface } return ['Filtered by activity types: only %activitytypes%', [ - '%activitytypes%' => implode(', ou ', $types), + '%activitytypes%' => implode(', ', $types), ]]; } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php index 77d887fdf..9bcae2b46 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php @@ -13,12 +13,10 @@ namespace Chill\ActivityBundle\Export\Filter; use Chill\ActivityBundle\Entity\ActivityType; use Chill\ActivityBundle\Export\Declarations; -use Chill\ActivityBundle\Repository\ActivityTypeRepository; +use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; -use Doctrine\ORM\Query\Expr; -use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; @@ -28,13 +26,13 @@ use function count; class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInterface { - protected ActivityTypeRepository $activityTypeRepository; + protected ActivityTypeRepositoryInterface $activityTypeRepository; protected TranslatableStringHelperInterface $translatableStringHelper; public function __construct( TranslatableStringHelperInterface $translatableStringHelper, - ActivityTypeRepository $activityTypeRepository + ActivityTypeRepositoryInterface $activityTypeRepository ) { $this->translatableStringHelper = $translatableStringHelper; $this->activityTypeRepository = $activityTypeRepository; @@ -47,16 +45,9 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); $clause = $qb->expr()->in('activity.activityType', ':selected_activity_types'); - if ($where instanceof Expr\Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); + $qb->andWhere($clause); $qb->setParameter('selected_activity_types', $data['types']); } @@ -68,11 +59,26 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter public function buildForm(FormBuilderInterface $builder) { $builder->add('types', EntityType::class, [ + 'choices' => $this->activityTypeRepository->findAllActive(), 'class' => ActivityType::class, - 'choice_label' => fn (ActivityType $type) => $this->translatableStringHelper->localize($type->getName()), - 'group_by' => fn (ActivityType $type) => $this->translatableStringHelper->localize($type->getCategory()->getName()), + 'choice_label' => function (ActivityType $aty) { + return + ($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '') + . + $this->translatableStringHelper->localize($aty->getName()); + }, + 'group_by' => function (ActivityType $type) { + if (!$type->hasCategory()) { + return null; + } + + return $this->translatableStringHelper->localize($type->getCategory()->getName()); + }, 'multiple' => true, 'expanded' => false, + 'attr' => [ + 'class' => 'select2', + ], ]); } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/ActivityReasonFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/ActivityReasonFilter.php index 6d78b3b38..871c5a829 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/ActivityReasonFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/ActivityReasonFilter.php @@ -19,14 +19,13 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Doctrine\ORM\Query\Expr; -use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface; -use function array_key_exists; use function count; +use function in_array; class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInterface { diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilter.php index 8db312e35..c15411b4e 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilter.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Filter\PersonFilters; +use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\ActivityReason; use Chill\ActivityBundle\Repository\ActivityReasonRepository; use Chill\MainBundle\Export\ExportElementValidatedInterface; @@ -59,10 +60,10 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt public function alterQuery(QueryBuilder $qb, $data) { - // create a query for activity + // create a subquery for activity $sqb = $qb->getEntityManager()->createQueryBuilder(); $sqb->select('person_person_having_activity.id') - ->from('ChillActivityBundle:Activity', 'activity_person_having_activity') + ->from(Activity::class, 'activity_person_having_activity') ->join('activity_person_having_activity.person', 'person_person_having_activity'); // add clause between date @@ -197,7 +198,7 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt public function getTitle() { - return 'Filtered by person having an activity in a period'; + return 'Filter by person having an activity in a period'; } public function validateForm($data, ExecutionContextInterface $context) diff --git a/src/Bundle/ChillActivityBundle/Form/Type/TranslatableActivityType.php b/src/Bundle/ChillActivityBundle/Form/Type/TranslatableActivityType.php index dc6328709..fd2784338 100644 --- a/src/Bundle/ChillActivityBundle/Form/Type/TranslatableActivityType.php +++ b/src/Bundle/ChillActivityBundle/Form/Type/TranslatableActivityType.php @@ -12,48 +12,33 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Form\Type; use Chill\ActivityBundle\Entity\ActivityType; -use Chill\ActivityBundle\Repository\ActivityTypeRepository; +use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; -use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class TranslatableActivityType extends AbstractType { - protected ActivityTypeRepository $activityTypeRepository; + protected ActivityTypeRepositoryInterface $activityTypeRepository; protected TranslatableStringHelperInterface $translatableStringHelper; public function __construct( TranslatableStringHelperInterface $helper, - ActivityTypeRepository $activityTypeRepository + ActivityTypeRepositoryInterface $activityTypeRepository ) { $this->translatableStringHelper = $helper; $this->activityTypeRepository = $activityTypeRepository; } - public function buildForm(FormBuilderInterface $builder, array $options) - { - /** @var QueryBuilder $qb */ - $qb = $options['query_builder']; - - if (true === $options['active_only']) { - $qb->where($qb->expr()->eq('at.active', ':active')); - $qb->setParameter('active', true, Types::BOOLEAN); - } - } - public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults( [ 'class' => ActivityType::class, 'active_only' => true, - 'query_builder' => $this->activityTypeRepository - ->createQueryBuilder('at'), + 'choices' => $this->activityTypeRepository->findAllActive(), 'choice_label' => function (ActivityType $type) { return $this->translatableStringHelper->localize($type->getName()); }, diff --git a/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepository.php b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepository.php index 735b70054..7bd679e8b 100644 --- a/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepository.php +++ b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepository.php @@ -12,19 +12,54 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Repository; use Chill\ActivityBundle\Entity\ActivityType; -use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; -use Doctrine\Persistence\ManagerRegistry; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\EntityRepository; -/** - * @method ActivityType|null find($id, $lockMode = null, $lockVersion = null) - * @method ActivityType|null findOneBy(array $criteria, array $orderBy = null) - * @method ActivityType[] findAll() - * @method ActivityType[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) - */ -class ActivityTypeRepository extends ServiceEntityRepository +final class ActivityTypeRepository implements ActivityTypeRepositoryInterface { - public function __construct(ManagerRegistry $registry) + private EntityRepository $repository; + + public function __construct(EntityManagerInterface $em) { - parent::__construct($registry, ActivityType::class); + $this->repository = $em->getRepository(ActivityType::class); + } + + public function find($id): ?ActivityType + { + return $this->repository->find($id); + } + + /** + * @return array|ActivityType[] + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array|ActivityType[] + */ + public function findAllActive(): array + { + return $this->findBy(['active' => true]); + } + + /** + * @return array|ActivityType[] + */ + public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria): ?ActivityType + { + return $this->repository->findOneBy($criteria); + } + + public function getClassName(): string + { + return ActivityType::class; } } diff --git a/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepositoryInterface.php b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepositoryInterface.php new file mode 100644 index 000000000..6172fe257 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepositoryInterface.php @@ -0,0 +1,23 @@ +helper = $helper; + $this->helper = $voterHelperFactory + ->generate(self::class) + ->addCheckFor(Center::class, [self::STATS, self::LISTS]) + ->build(); } public function getRoles(): array @@ -49,30 +49,14 @@ class ActivityStatsVoter extends AbstractChillVoter implements ProvideRoleHierar return $this->getAttributes(); } - protected function getSupportedClasses() - { - return [Center::class]; - } - - protected function isGranted($attribute, $object, $user = null) - { - if (!$user instanceof \Symfony\Component\Security\Core\User\UserInterface) { - return false; - } - - return $this->helper->userHasAccess($user, $object, $attribute); - } - protected function supports($attribute, $subject) { - if ( - $subject instanceof Center - && in_array($attribute, $this->getAttributes(), true) - ) { - return true; - } + return $this->helper->supports($attribute, $subject); + } - return false; + protected function voteOnAttribute($attribute, $subject, TokenInterface $token) + { + return $this->helper->voteOnAttribute($attribute, $subject, $token); } private function getAttributes() diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php new file mode 100644 index 000000000..2d28d0967 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php @@ -0,0 +1,62 @@ +aggregator = self::$container->get('chill.activity.export.bysocialaction_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'acp') + ->join('activity.socialActions', 'actsocialaction'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php new file mode 100644 index 000000000..36a7c7ceb --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php @@ -0,0 +1,62 @@ +aggregator = self::$container->get('chill.activity.export.bysocialissue_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'acp') + ->join('activity.socialIssues', 'actsocialissue'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php new file mode 100644 index 000000000..89c853e55 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php @@ -0,0 +1,62 @@ +aggregator = self::$container->get('chill.activity.export.bythirdparty_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'acp') + ->join('activity.thirdParties', 'acttparty'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php new file mode 100644 index 000000000..2ee78b2bf --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php @@ -0,0 +1,62 @@ +aggregator = self::$container->get('chill.activity.export.byuser_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'acp') + ->join('activity.users', 'actusers'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php new file mode 100644 index 000000000..8b682487b --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php @@ -0,0 +1,69 @@ +aggregator = self::$container->get('chill.activity.export.date_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [ + 'frequency' => 'month', + ], + [ + 'frequency' => 'week', + ], + [ + 'frequency' => 'year', + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'acp'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php new file mode 100644 index 000000000..81f7be13b --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php @@ -0,0 +1,62 @@ +aggregator = self::$container->get('chill.activity.export.locationtype_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'acp') + ->join('activity.location', 'actloc'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php new file mode 100644 index 000000000..28f13028f --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php @@ -0,0 +1,62 @@ +aggregator = self::$container->get('chill.activity.export.userscope_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'acp') + ->join('activity.user', 'actuser'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php index f6efe17a5..435d1043e 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php @@ -11,7 +11,10 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Aggregator; +use Chill\ActivityBundle\Export\Aggregator\ActivityTypeAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Doctrine\ORM\EntityManagerInterface; +use Prophecy\PhpUnit\ProphecyTrait; /** * Add tests for ActivityTypeAggregator. @@ -21,26 +24,22 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest; */ final class ActivityTypeAggregatorTest extends AbstractAggregatorTest { - /** - * @var \Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator - */ - private $aggregator; + use ProphecyTrait; + + private ActivityTypeAggregator $aggregator; protected function setUp(): void { self::bootKernel(); - $container = self::$kernel->getContainer(); + $this->aggregator = self::$container->get('chill.activity.export.type_aggregator'); - $this->aggregator = $container->get('chill.activity.export.type_aggregator'); + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); $request->getLocale()->willReturn('fr'); - $container->get('request_stack') + self::$container->get('request_stack') ->push($request->reveal()); } @@ -62,8 +61,7 @@ final class ActivityTypeAggregatorTest extends AbstractAggregatorTest self::bootKernel(); } - $em = self::$kernel->getContainer() - ->get('doctrine.orm.entity_manager'); + $em = self::$container->get(EntityManagerInterface::class); return [ $em->createQueryBuilder() @@ -72,12 +70,7 @@ final class ActivityTypeAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(activity.id)') ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.reasons', 'reasons'), - $em->createQueryBuilder() - ->select('count(activity.id)') - ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.reasons', 'reasons') - ->join('reasons.category', 'category'), + ->join('activity.activityType', 'acttype'), ]; } } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php index 1447f473b..ea76a362c 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php @@ -11,7 +11,10 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Aggregator; +use Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Doctrine\ORM\EntityManagerInterface; +use Prophecy\PhpUnit\ProphecyTrait; /** * Add tests for ActivityUsernAggregator. @@ -21,26 +24,22 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest; */ final class ActivityUserAggregatorTest extends AbstractAggregatorTest { - /** - * @var \Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator - */ - private $aggregator; + use ProphecyTrait; + + private ActivityUserAggregator $aggregator; protected function setUp(): void { self::bootKernel(); - $container = self::$kernel->getContainer(); + $this->aggregator = self::$container->get('chill.activity.export.user_aggregator'); - $this->aggregator = $container->get('chill.activity.export.user_aggregator'); + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); $request->getLocale()->willReturn('fr'); - $container->get('request_stack') + self::$container->get('request_stack') ->push($request->reveal()); } @@ -49,35 +48,25 @@ final class ActivityUserAggregatorTest extends AbstractAggregatorTest return $this->aggregator; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getQueryBuilders() + public function getQueryBuilders(): array { if (null === self::$kernel) { self::bootKernel(); } - $em = self::$kernel->getContainer() - ->get('doctrine.orm.entity_manager'); + $em = self::$container->get(EntityManagerInterface::class); return [ $em->createQueryBuilder() ->select('count(activity.id)') ->from('ChillActivityBundle:Activity', 'activity'), - $em->createQueryBuilder() - ->select('count(activity.id)') - ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.reasons', 'reasons'), - $em->createQueryBuilder() - ->select('count(activity.id)') - ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.reasons', 'reasons') - ->join('reasons.category', 'category'), ]; } } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php similarity index 57% rename from src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php rename to src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php index 436bfc697..69a82cffc 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php @@ -9,38 +9,35 @@ declare(strict_types=1); -namespace Chill\ActivityBundle\Tests\Export\Aggregator; +namespace Chill\ActivityBundle\Tests\Export\Aggregator\PersonAggregators; +use Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Doctrine\ORM\EntityManagerInterface; +use Prophecy\PhpUnit\ProphecyTrait; /** - * Add tests for ActivityReasonAggregator. - * * @internal * @coversNothing */ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest { - /** - * @var \Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator - */ - private $aggregator; + use ProphecyTrait; + + private ActivityReasonAggregator $aggregator; protected function setUp(): void { self::bootKernel(); - $container = self::$kernel->getContainer(); + $this->aggregator = self::$container->get('chill.activity.export.reason_aggregator'); - $this->aggregator = $container->get('chill.activity.export.reason_aggregator'); + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); $request->getLocale()->willReturn('fr'); - $container->get('request_stack') + self::$container->get('request_stack') ->push($request->reveal()); } @@ -49,7 +46,7 @@ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest return $this->aggregator; } - public function getFormData() + public function getFormData(): array { return [ ['level' => 'reasons'], @@ -57,14 +54,13 @@ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest ]; } - public function getQueryBuilders() + public function getQueryBuilders(): array { if (null === self::$kernel) { self::bootKernel(); } - $em = self::$kernel->getContainer() - ->get('doctrine.orm.entity_manager'); + $em = self::$container->get(EntityManagerInterface::class); return [ $em->createQueryBuilder() @@ -73,12 +69,12 @@ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(activity.id)') ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.reasons', 'reasons'), + ->join('activity.reasons', 'actreasons'), $em->createQueryBuilder() ->select('count(activity.id)') ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.reasons', 'reasons') - ->join('reasons.category', 'category'), + ->join('activity.reasons', 'actreasons') + ->join('actreasons.category', 'actreasoncat'), ]; } } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityDurationTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityDurationTest.php new file mode 100644 index 000000000..b175c4f15 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityDurationTest.php @@ -0,0 +1,51 @@ +export = self::$container->get('chill.activity.export.avg_activity_duration_linked_to_acp'); + } + + public function getExport() + { + return $this->export; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getModifiersCombination(): array + { + return [ + ['activity'], + ['activity', 'accompanying_period'], + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityVisitDurationTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityVisitDurationTest.php new file mode 100644 index 000000000..5d0e11be3 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityVisitDurationTest.php @@ -0,0 +1,51 @@ +export = self::$container->get('chill.activity.export.avg_activity_visit_duration_linked_to_acp'); + } + + public function getExport() + { + return $this->export; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getModifiersCombination(): array + { + return [ + ['activity'], + ['activity', 'accompanying_period'], + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/CountActivityTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/CountActivityTest.php new file mode 100644 index 000000000..78f8556c1 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/CountActivityTest.php @@ -0,0 +1,51 @@ +export = self::$container->get('chill.activity.export.count_activity_linked_to_acp'); + } + + public function getExport() + { + return $this->export; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getModifiersCombination(): array + { + return [ + ['activity'], + ['activity', 'accompanying_period'], + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityDurationTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityDurationTest.php new file mode 100644 index 000000000..939ca2a88 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityDurationTest.php @@ -0,0 +1,51 @@ +export = self::$container->get('chill.activity.export.sum_activity_duration_linked_to_acp'); + } + + public function getExport() + { + return $this->export; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getModifiersCombination(): array + { + return [ + ['activity'], + ['activity', 'accompanying_period'], + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityVisitDurationTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityVisitDurationTest.php new file mode 100644 index 000000000..7c0f79bc5 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityVisitDurationTest.php @@ -0,0 +1,51 @@ +export = self::$container->get('chill.activity.export.sum_activity_visit_duration_linked_to_acp'); + } + + public function getExport() + { + return $this->export; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getModifiersCombination(): array + { + return [ + ['activity'], + ['activity', 'accompanying_period'], + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/CountActivityTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/CountActivityTest.php similarity index 62% rename from src/Bundle/ChillActivityBundle/Tests/Export/Export/CountActivityTest.php rename to src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/CountActivityTest.php index cbf562dfa..f94ef3414 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Export/CountActivityTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/CountActivityTest.php @@ -9,8 +9,9 @@ declare(strict_types=1); -namespace Chill\ActivityBundle\Tests\Export\Export; +namespace Chill\ActivityBundle\Tests\Export\Export\LinkedToPerson; +use Chill\ActivityBundle\Export\Export\LinkedToPerson\CountActivity; use Chill\MainBundle\Test\Export\AbstractExportTest; /** @@ -19,19 +20,13 @@ use Chill\MainBundle\Test\Export\AbstractExportTest; */ final class CountActivityTest extends AbstractExportTest { - /** - * @var - */ - private $export; + private CountActivity $export; protected function setUp(): void { self::bootKernel(); - /** @var \Symfony\Component\DependencyInjection\ContainerInterface $container */ - $container = self::$kernel->getContainer(); - - $this->export = $container->get('chill.activity.export.count_activity'); + $this->export = self::$container->get('chill.activity.export.count_activity_linked_to_person'); } public function getExport() @@ -39,14 +34,14 @@ final class CountActivityTest extends AbstractExportTest return $this->export; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getModifiersCombination() + public function getModifiersCombination(): array { return [ ['activity'], diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/ListActivityTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/ListActivityTest.php similarity index 65% rename from src/Bundle/ChillActivityBundle/Tests/Export/Export/ListActivityTest.php rename to src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/ListActivityTest.php index 46d23b023..d39592b5e 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Export/ListActivityTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/ListActivityTest.php @@ -9,9 +9,11 @@ declare(strict_types=1); -namespace Chill\ActivityBundle\Tests\Export\Export; +namespace Chill\ActivityBundle\Tests\Export\Export\LinkedToPerson; +use Chill\ActivityBundle\Export\Export\LinkedToPerson\ListActivity; use Chill\MainBundle\Test\Export\AbstractExportTest; +use Prophecy\PhpUnit\ProphecyTrait; /** * @internal @@ -19,27 +21,22 @@ use Chill\MainBundle\Test\Export\AbstractExportTest; */ final class ListActivityTest extends AbstractExportTest { - /** - * @var \Chill\ActivityBundle\Export\Export\ListActivity - */ - private $export; + use ProphecyTrait; + + private ListActivity $export; protected function setUp(): void { self::bootKernel(); - /** @var \Symfony\Component\DependencyInjection\ContainerInterface $container */ - $container = self::$kernel->getContainer(); + $this->export = self::$container->get('chill.activity.export.list_activity_linked_to_person'); - $this->export = $container->get('chill.activity.export.list_activity'); + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); $request->getLocale()->willReturn('fr'); - $container->get('request_stack') + self::$container->get('request_stack') ->push($request->reveal()); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/StatActivityDurationSumTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/StatActivityDurationTest.php similarity index 55% rename from src/Bundle/ChillActivityBundle/Tests/Export/Export/StatActivityDurationSumTest.php rename to src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/StatActivityDurationTest.php index c69a31b5a..ae9e4f583 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Export/StatActivityDurationSumTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/StatActivityDurationTest.php @@ -9,8 +9,9 @@ declare(strict_types=1); -namespace Chill\ActivityBundle\Tests\Export\Export; +namespace Chill\ActivityBundle\Tests\Export\Export\LinkedToPerson; +use Chill\ActivityBundle\Export\Export\LinkedToPerson\StatActivityDuration; use Chill\MainBundle\Test\Export\AbstractExportTest; /** @@ -19,21 +20,15 @@ use Chill\MainBundle\Test\Export\AbstractExportTest; * @internal * @coversNothing */ -final class StatActivityDurationSumTest extends AbstractExportTest +final class StatActivityDurationTest extends AbstractExportTest { - /** - * @var \Chill\ActivityBundle\Export\Export\StatActivityDuration - */ - private $export; + private StatActivityDuration $export; protected function setUp(): void { self::bootKernel(); - /** @var \Symfony\Component\DependencyInjection\ContainerInterface $container */ - $container = self::$kernel->getContainer(); - - $this->export = $container->get('chill.activity.export.sum_activity_duration'); + $this->export = self::$container->get('chill.activity.export.sum_activity_duration_linked_to_person'); } public function getExport() @@ -41,14 +36,14 @@ final class StatActivityDurationSumTest extends AbstractExportTest return $this->export; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getModifiersCombination() + public function getModifiersCombination(): array { return [ ['activity'], diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php similarity index 63% rename from src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilterTest.php rename to src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php index bce092b6e..70918fb54 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php @@ -9,13 +9,15 @@ declare(strict_types=1); -namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters; +namespace Chill\ActivityBundle\Tests\Export\Filter\ACPFilters; +use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\ActivityType; +use Chill\ActivityBundle\Export\Filter\ACPFilters\ActivityTypeFilter; use Chill\MainBundle\Test\Export\AbstractFilterTest; -use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ActivityTypeFilter; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; +use Doctrine\ORM\Query\Expr; /** * @internal @@ -27,16 +29,9 @@ final class ActivityTypeFilterTest extends AbstractFilterTest protected function setUp(): void { - //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - - $this->filter = self::$container->get('chill.person.export.filter_activitytype'); + $this->filter = self::$container->get('chill.activity.export.filter_activitytype'); } public function getFilter() @@ -56,8 +51,10 @@ final class ActivityTypeFilterTest extends AbstractFilterTest $data = []; - foreach ($array as $t) { - $data[] = ['accepted_activitytypes' => $t]; + foreach ($array as $a) { + $data[] = [ + 'accepted_activitytypes' => $a, + ]; } return $data; @@ -73,8 +70,10 @@ final class ActivityTypeFilterTest extends AbstractFilterTest return [ $em->createQueryBuilder() - ->from('ChillPersonBundle:AccompanyingPeriod', 'acp') - ->select('acp.id'), + ->select('count(activity.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join(Activity::class, 'activity', Expr\Join::WITH, 'activity.accompanyingPeriod = acp') + ->join('activity.activityType', 'acttype'), ]; } } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php new file mode 100644 index 000000000..9beb48082 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php @@ -0,0 +1,76 @@ +filter = self::$container->get('chill.activity.export.bysocialaction_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(SocialAction::class, 'sa') + ->select('sa') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'accepted_socialactions' => $a, + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.socialActions', 'actsocialaction'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php new file mode 100644 index 000000000..f92a143ee --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php @@ -0,0 +1,76 @@ +filter = self::$container->get('chill.activity.export.bysocialissue_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(SocialIssue::class, 'si') + ->select('si') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'accepted_socialissues' => $a, + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.socialIssues', 'actsocialissue'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php new file mode 100644 index 000000000..6765a918d --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php @@ -0,0 +1,76 @@ +filter = self::$container->get('chill.activity.export.byuser_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(User::class, 'u') + ->select('u') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'accepted_users' => $a, + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.users', 'actusers'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php new file mode 100644 index 000000000..845bb6490 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php @@ -0,0 +1,61 @@ +filter = self::$container->get('chill.activity.export.emergency_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + ['accepted_emergency' => true], + ['accepted_emergency' => false], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php new file mode 100644 index 000000000..18558b7ee --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php @@ -0,0 +1,76 @@ +filter = self::$container->get('chill.activity.export.locationtype_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(LocationType::class, 'lt') + ->select('lt') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'accepted_locationtype' => $a, + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.location', 'actloc'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php new file mode 100644 index 000000000..a3cc6a941 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php @@ -0,0 +1,61 @@ +filter = self::$container->get('chill.activity.export.sentreceived_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + ['accepted_sentreceived' => Activity::SENTRECEIVED_SENT], + ['accepted_sentreceived' => Activity::SENTRECEIVED_RECEIVED], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php new file mode 100644 index 000000000..6dca9e18a --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php @@ -0,0 +1,75 @@ +filter = self::$container->get('chill.activity.export.user_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(User::class, 'u') + ->select('u') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'accepted_users' => $a, + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php new file mode 100644 index 000000000..9a869206e --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php @@ -0,0 +1,76 @@ +filter = self::$container->get('chill.activity.export.userscope_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(Scope::class, 's') + ->select('s') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'accepted_userscope' => $a, + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.user', 'actuser'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php new file mode 100644 index 000000000..150ec30f0 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php @@ -0,0 +1,64 @@ +filter = self::$container->get('chill.activity.export.date_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + [ + 'date_from' => DateTime::createFromFormat('Y-m-d', '2020-01-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2021-01-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php index 5b8ae08c3..0a7d059a6 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php @@ -11,8 +11,10 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Filter; +use Chill\ActivityBundle\Export\Filter\PersonFilters\ActivityReasonFilter; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Doctrine\Common\Collections\ArrayCollection; +use Prophecy\PhpUnit\ProphecyTrait; /** * @internal @@ -20,26 +22,22 @@ use Doctrine\Common\Collections\ArrayCollection; */ final class ActivityReasonFilterTest extends AbstractFilterTest { - /** - * @var \Chill\PersonBundle\Export\Filter\GenderFilter - */ - private $filter; + use ProphecyTrait; + + private ActivityReasonFilter $filter; protected function setUp(): void { self::bootKernel(); - $container = self::$kernel->getContainer(); + $this->filter = self::$container->get('chill.activity.export.reason_filter'); - $this->filter = $container->get('chill.activity.export.reason_filter'); + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); $request->getLocale()->willReturn('fr'); - $container->get('request_stack') + self::$container->get('request_stack') ->push($request->reveal()); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php new file mode 100644 index 000000000..97d63ddc9 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php @@ -0,0 +1,75 @@ +filter = self::$container->get('chill.activity.export.type_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(ActivityType::class, 'at') + ->select('at') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'types' => $a, + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php new file mode 100644 index 000000000..c47b0a0a7 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php @@ -0,0 +1,76 @@ +filter = self::$container->get('chill.activity.export.reason_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(ActivityReason::class, 'ar') + ->select('ar') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'reasons' => $a, + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.reasons', 'actreasons'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php new file mode 100644 index 000000000..c705c5462 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php @@ -0,0 +1,78 @@ +filter = self::$container->get('chill.activity.export.person_having_an_activity_between_date_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(ActivityReason::class, 'ar') + ->select('ar') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'date_from' => DateTime::createFromFormat('Y-m-d', '2021-07-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2022-07-01'), + 'reasons' => $a, + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php index 94d99c2a7..0e8199352 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Filter; +use Chill\ActivityBundle\Export\Filter\PersonFilters\PersonHavingActivityBetweenDateFilter; use Chill\MainBundle\Test\Export\AbstractFilterTest; use DateTime; use function array_slice; @@ -21,27 +22,20 @@ use function array_slice; */ final class PersonHavingActivityBetweenDateFilterTest extends AbstractFilterTest { - /** - * @var \Chill\PersonBundle\Export\Filter\PersonHavingActivityBetweenDateFilter - */ - private $filter; + private PersonHavingActivityBetweenDateFilter $filter; protected function setUp(): void { self::bootKernel(); - $container = self::$kernel->getContainer(); + $this->filter = self::$container->get('chill.activity.export.person_having_an_activity_between_date_filter'); - $this->filter = $container->get('chill.activity.export.' - . 'person_having_an_activity_between_date_filter'); + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); $request->getLocale()->willReturn('fr'); - $container->get('request_stack') + self::$container->get('request_stack') ->push($request->reveal()); } diff --git a/src/Bundle/ChillActivityBundle/config/services/export.yaml b/src/Bundle/ChillActivityBundle/config/services/export.yaml index c88046c5f..bdaae8c8a 100644 --- a/src/Bundle/ChillActivityBundle/config/services/export.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/export.yaml @@ -67,6 +67,11 @@ services: name: chill.export_filter alias: 'activity_person_having_ac_bw_date_filter' + chill.activity.export.filter_activitytype: + class: Chill\ActivityBundle\Export\Filter\ACPFilters\ActivityTypeFilter + tags: + - { name: chill.export_filter, alias: 'accompanyingcourse_activitytype_filter' } + chill.activity.export.locationtype_filter: class: Chill\ActivityBundle\Export\Filter\ACPFilters\LocationTypeFilter tags: diff --git a/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php b/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php index 73f6fb355..6443eeaf7 100644 --- a/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php +++ b/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php @@ -29,44 +29,58 @@ class ConfigRepository $this->charges = $charges; } - public function getChargesKeys(): array + public function getChargesKeys(bool $onlyActive = false): array { - return array_map(static function ($element) { return $element['key']; }, $this->charges); + return array_map(static function ($element) { return $element['key']; }, $this->getCharges($onlyActive)); } /** * @return array where keys are the resource'key and label the ressource label */ - public function getChargesLabels() + public function getChargesLabels(bool $onlyActive = false) { $charges = []; - foreach ($this->charges as $definition) { + foreach ($this->getCharges($onlyActive) as $definition) { $charges[$definition['key']] = $this->normalizeLabel($definition['labels']); } return $charges; } - public function getResourcesKeys(): array + public function getResourcesKeys(bool $onlyActive = false): array { - return array_map(static function ($element) { return $element['key']; }, $this->resources); + return array_map(static function ($element) { return $element['key']; }, $this->getResources($onlyActive)); } /** * @return array where keys are the resource'key and label the ressource label */ - public function getResourcesLabels() + public function getResourcesLabels(bool $onlyActive = false) { $resources = []; - foreach ($this->resources as $definition) { + foreach ($this->getResources($onlyActive) as $definition) { $resources[$definition['key']] = $this->normalizeLabel($definition['labels']); } return $resources; } + private function getCharges(bool $onlyActive = false): array + { + return $onlyActive ? + array_filter($this->charges, static function ($el) { return $el['active']; }) + : $this->charges; + } + + private function getResources(bool $onlyActive = false): array + { + return $onlyActive ? + array_filter($this->resources, static function ($el) { return $el['active']; }) + : $this->resources; + } + private function normalizeLabel($labels) { $normalizedLabels = []; diff --git a/src/Bundle/ChillBudgetBundle/DependencyInjection/Configuration.php b/src/Bundle/ChillBudgetBundle/DependencyInjection/Configuration.php index a18e17ae6..710fe43f0 100644 --- a/src/Bundle/ChillBudgetBundle/DependencyInjection/Configuration.php +++ b/src/Bundle/ChillBudgetBundle/DependencyInjection/Configuration.php @@ -14,11 +14,6 @@ namespace Chill\BudgetBundle\DependencyInjection; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; -/** - * This is the class that validates and merges configuration from your app/config files. - * - * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/configuration.html} - */ class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder() @@ -37,6 +32,7 @@ class Configuration implements ConfigurationInterface ->info('the key stored in database') ->example('salary') ->end() + ->booleanNode('active')->defaultTrue()->end() ->arrayNode('labels')->isRequired()->requiresAtLeastOneElement() ->arrayPrototype() ->children() @@ -59,6 +55,7 @@ class Configuration implements ConfigurationInterface ->info('the key stored in database') ->example('salary') ->end() + ->booleanNode('active')->defaultTrue()->end() ->arrayNode('labels')->isRequired()->requiresAtLeastOneElement() ->arrayPrototype() ->children() diff --git a/src/Bundle/ChillBudgetBundle/Form/ChargeType.php b/src/Bundle/ChillBudgetBundle/Form/ChargeType.php index 1e054d86b..85e7e45aa 100644 --- a/src/Bundle/ChillBudgetBundle/Form/ChargeType.php +++ b/src/Bundle/ChillBudgetBundle/Form/ChargeType.php @@ -103,7 +103,7 @@ class ChargeType extends AbstractType private function getTypes() { $charges = $this->configRepository - ->getChargesLabels(); + ->getChargesLabels(true); // rewrite labels to filter in language foreach ($charges as $key => $labels) { diff --git a/src/Bundle/ChillBudgetBundle/Form/ResourceType.php b/src/Bundle/ChillBudgetBundle/Form/ResourceType.php index 51e3f6799..5ad388a39 100644 --- a/src/Bundle/ChillBudgetBundle/Form/ResourceType.php +++ b/src/Bundle/ChillBudgetBundle/Form/ResourceType.php @@ -87,7 +87,7 @@ class ResourceType extends AbstractType private function getTypes() { $resources = $this->configRepository - ->getResourcesLabels(); + ->getResourcesLabels(true); // rewrite labels to filter in language foreach ($resources as $key => $labels) { diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php index 3c2b54855..6a3c90064 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Templating\Entity\UserRender; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class AgentAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php index 854710a79..6462fc189 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class CancelReasonAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php index 51500f45f..cff768ad3 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class JobAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php index 287dccec7..4823a2424 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php @@ -17,7 +17,7 @@ use Chill\MainBundle\Repository\LocationRepository; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Security\Core\Role\Role; +use function in_array; final class LocationAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php index 5d7559b2d..be41f7feb 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class LocationTypeAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php index b4f01db12..1f494fe96 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php @@ -16,7 +16,6 @@ use Chill\MainBundle\Export\AggregatorInterface; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Security\Core\Role\Role; class MonthYearAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php index 8d3685f96..852d985c7 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ScopeAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php index d5a70b3da..938a1a3f6 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php @@ -20,6 +20,7 @@ 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; class JobFilter implements FilterInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php index 9f12cbf19..046c4a5b4 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php @@ -20,6 +20,7 @@ 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; class ScopeFilter implements FilterInterface { diff --git a/src/Bundle/ChillDocStoreBundle/Form/PersonDocumentType.php b/src/Bundle/ChillDocStoreBundle/Form/PersonDocumentType.php index 3df9f261f..f7abd93dd 100644 --- a/src/Bundle/ChillDocStoreBundle/Form/PersonDocumentType.php +++ b/src/Bundle/ChillDocStoreBundle/Form/PersonDocumentType.php @@ -41,11 +41,13 @@ class PersonDocumentType extends AbstractType public function __construct( TranslatableStringHelperInterface $translatableStringHelper, ScopeResolverDispatcher $scopeResolverDispatcher, - ParameterBagInterface $parameterBag + ParameterBagInterface $parameterBag, + CenterResolverDispatcher $centerResolverDispatcher ) { $this->translatableStringHelper = $translatableStringHelper; $this->scopeResolverDispatcher = $scopeResolverDispatcher; $this->parameterBag = $parameterBag; + $this->centerResolverDispatcher = $centerResolverDispatcher; } public function buildForm(FormBuilderInterface $builder, array $options) diff --git a/src/Bundle/ChillEventBundle/migrations/Version20160318111334.php b/src/Bundle/ChillEventBundle/migrations/Version20160318111334.php index e879f1d29..241003abf 100644 --- a/src/Bundle/ChillEventBundle/migrations/Version20160318111334.php +++ b/src/Bundle/ChillEventBundle/migrations/Version20160318111334.php @@ -126,7 +126,7 @@ class Version20160318111334 extends AbstractMigration $this->addSql('ALTER TABLE chill_event_participation ' . 'ADD CONSTRAINT FK_4E7768AC217BBB47 ' . 'FOREIGN KEY (person_id) ' - . 'REFERENCES chill_person_person(id) ' + . 'REFERENCES Person (id) ' . 'NOT DEFERRABLE INITIALLY IMMEDIATE'); $this->addSql('ALTER TABLE chill_event_participation ' . 'ADD CONSTRAINT FK_4E7768ACD60322AC ' diff --git a/src/Bundle/ChillMainBundle/Command/LoadAddressesBEFromBestAddressCommand.php b/src/Bundle/ChillMainBundle/Command/LoadAddressesBEFromBestAddressCommand.php new file mode 100644 index 000000000..abd4e294a --- /dev/null +++ b/src/Bundle/ChillMainBundle/Command/LoadAddressesBEFromBestAddressCommand.php @@ -0,0 +1,52 @@ +addressImporter = $addressImporter; + $this->postalCodeBEFromBestAddressImporter = $postalCodeBEFromBestAddressImporter; + } + + protected function configure() + { + $this + ->setName('chill:main:address-ref-from-best-addresses') + ->addArgument('lang', InputArgument::REQUIRED) + ->addArgument('list', InputArgument::IS_ARRAY, 'The list to add'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->postalCodeBEFromBestAddressImporter->import(); + + $this->addressImporter->import($input->getArgument('lang'), $input->getArgument('list')); + + return 0; + } +} diff --git a/src/Bundle/ChillMainBundle/Command/LoadAddressesFRFromBANOCommand.php b/src/Bundle/ChillMainBundle/Command/LoadAddressesFRFromBANOCommand.php new file mode 100644 index 000000000..96e7023fb --- /dev/null +++ b/src/Bundle/ChillMainBundle/Command/LoadAddressesFRFromBANOCommand.php @@ -0,0 +1,47 @@ +addressReferenceFromBano = $addressReferenceFromBano; + } + + protected function configure() + { + $this->setName('chill:main:address-ref-from-bano') + ->addArgument('departementNo', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'a list of departement numbers') + ->setDescription('Import addresses from bano (see https://bano.openstreetmap.fr'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + foreach ($input->getArgument('departementNo') as $departementNo) { + $output->writeln('Import addresses for ' . $departementNo); + + $this->addressReferenceFromBano->import($departementNo); + } + + return 0; + } +} diff --git a/src/Bundle/ChillMainBundle/Command/LoadPostalCodeFR.php b/src/Bundle/ChillMainBundle/Command/LoadPostalCodeFR.php new file mode 100644 index 000000000..1c7f9cbc5 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Command/LoadPostalCodeFR.php @@ -0,0 +1,42 @@ +loader = $loader; + + parent::__construct(); + } + + public function configure(): void + { + $this->setName('chill:main:postal-code:load:FR') + ->setDescription('Load France\'s postal code from online open data'); + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + $this->loader->import(); + + return 0; + } +} diff --git a/src/Bundle/ChillMainBundle/Controller/ExportController.php b/src/Bundle/ChillMainBundle/Controller/ExportController.php index 1893a64b3..3d861fdfa 100644 --- a/src/Bundle/ChillMainBundle/Controller/ExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/ExportController.php @@ -23,6 +23,7 @@ use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -142,10 +143,8 @@ class ExportController extends AbstractController /** * Render the list of available exports. - * - * @return \Symfony\Component\HttpFoundation\Response */ - public function indexAction(Request $request) + public function indexAction(): Response { $exportManager = $this->exportManager; @@ -443,6 +442,12 @@ class ExportController extends AbstractController } $rawData = unserialize($serialized); + + $this->logger->notice('[export] choices for an export unserialized', [ + 'key' => $key, + 'rawData' => json_encode($rawData), + ]); + $alias = $rawData['alias']; $formCenters = $this->createCreateFormExport($alias, 'generate_centers'); diff --git a/src/Bundle/ChillMainBundle/Controller/WorkflowController.php b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php index 2803ff254..a4e2ff8bf 100644 --- a/src/Bundle/ChillMainBundle/Controller/WorkflowController.php +++ b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php @@ -318,19 +318,12 @@ class WorkflowController extends AbstractController ); } + // TODO symfony 5: add those "future" on context ($workflow->apply($entityWorkflow, $transition, $context) $entityWorkflow->futureDestUsers = $transitionForm['future_dest_users']->getData(); $entityWorkflow->futureDestEmails = $transitionForm['future_dest_emails']->getData(); $workflow->apply($entityWorkflow, $transition); - foreach ($transitionForm['future_dest_users']->getData() as $user) { - $entityWorkflow->getCurrentStep()->addDestUser($user); - } - - foreach ($transitionForm['future_dest_emails']->getData() as $email) { - $entityWorkflow->getCurrentStep()->addDestEmail($email); - } - $this->entityManager->flush(); return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]); diff --git a/src/Bundle/ChillMainBundle/Entity/AddressReference.php b/src/Bundle/ChillMainBundle/Entity/AddressReference.php index 5d581efd4..497720685 100644 --- a/src/Bundle/ChillMainBundle/Entity/AddressReference.php +++ b/src/Bundle/ChillMainBundle/Entity/AddressReference.php @@ -20,6 +20,9 @@ use Symfony\Component\Serializer\Annotation\Groups; * @ORM\Entity * @ORM\Table(name="chill_main_address_reference", indexes={ * @ORM\Index(name="address_refid", columns={"refId"}) + * }, + * uniqueConstraints={ + * @ORM\UniqueConstraint(name="chill_main_address_reference_unicity", columns={"refId", "source"}) * }) * @ORM\HasLifecycleCallbacks */ diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php index 9e119e30d..bd49fe892 100644 --- a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php @@ -14,15 +14,17 @@ namespace Chill\MainBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** - * @ORM\Table(name="chill_main_geographical_unit") - * @ORM\Entity + * @ORM\Table(name="chill_main_geographical_unit", uniqueConstraints={ + * @ORM\UniqueConstraint(name="geographical_unit_refid", columns={"layer_id", "unitRefId"}) + * }) + * @ORM\Entity(readOnly=true) */ class GeographicalUnit { /** * @ORM\Column(type="text", nullable=true) */ - private $geom; + private string $geom; /** * @ORM\Id @@ -32,23 +34,28 @@ class GeographicalUnit private ?int $id = null; /** - * @ORM\Column(type="string", length=255, nullable=true) + * @ORM\ManyToOne(targetEntity=GeographicalUnitLayer::class, inversedBy="units") */ - private $layerName; + private ?GeographicalUnitLayer $layer; /** - * @ORM\Column(type="string", length=255, nullable=true) + * @ORM\Column(type="text", nullable=false, options={"default": ""}) */ - private $unitName; + private string $unitName; + + /** + * @ORM\Column(type="text", nullable=false, options={"default": ""}) + */ + private string $unitRefId; public function getId(): ?int { return $this->id; } - public function getLayerName(): ?string + public function getLayer(): ?GeographicalUnitLayer { - return $this->layerName; + return $this->layer; } public function getUnitName(): ?string @@ -56,9 +63,9 @@ class GeographicalUnit return $this->unitName; } - public function setLayerName(?string $layerName): self + public function setLayer(?GeographicalUnitLayer $layer): GeographicalUnit { - $this->layerName = $layerName; + $this->layer = $layer; return $this; } @@ -69,4 +76,18 @@ class GeographicalUnit return $this; } + + public function setUnitRefId(string $unitRefId): GeographicalUnit + { + $this->unitRefId = $unitRefId; + + return $this; + } + + protected function setId(int $id): self + { + $this->id = $id; + + return $this; + } } diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php new file mode 100644 index 000000000..961a0e79a --- /dev/null +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php @@ -0,0 +1,79 @@ +units = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getName(): array + { + return $this->name; + } + + public function getRefId(): string + { + return $this->refId; + } + + public function getUnits(): Collection + { + return $this->units; + } + + public function setName(array $name): GeographicalUnitLayer + { + $this->name = $name; + + return $this; + } +} diff --git a/src/Bundle/ChillMainBundle/Entity/PostalCode.php b/src/Bundle/ChillMainBundle/Entity/PostalCode.php index 4b79f58e8..769f6dfd7 100644 --- a/src/Bundle/ChillMainBundle/Entity/PostalCode.php +++ b/src/Bundle/ChillMainBundle/Entity/PostalCode.php @@ -12,6 +12,11 @@ declare(strict_types=1); namespace Chill\MainBundle\Entity; use Chill\MainBundle\Doctrine\Model\Point; +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 DateTimeImmutable; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Serializer\Annotation\Groups; @@ -21,6 +26,10 @@ use Symfony\Component\Serializer\Annotation\Groups; * @ORM\Entity * @ORM\Table( * name="chill_main_postal_code", + * uniqueConstraints={ + * @ORM\UniqueConstraint(name="postal_code_import_unicity", columns={"code", "refpostalcodeid", "postalcodesource"}, + * options={"where": "refpostalcodeid is not null"}) + * }, * indexes={ * @ORM\Index(name="search_name_code", columns={"code", "label"}), * @ORM\Index(name="search_by_reference_code", columns={"code", "refpostalcodeid"}) @@ -28,8 +37,12 @@ use Symfony\Component\Serializer\Annotation\Groups; * * @ORM\HasLifecycleCallbacks */ -class PostalCode +class PostalCode implements TrackUpdateInterface, TrackCreationInterface { + use TrackCreationTrait; + + use TrackUpdateTrait; + /** * This is an internal column which is populated by database. * @@ -63,6 +76,11 @@ class PostalCode */ private $country; + /** + * @ORM\Column(type="datetime_immutable", nullable=true, options={"default": null}) + */ + private ?DateTimeImmutable $deletedAt = null; + /** * @var int * diff --git a/src/Bundle/ChillMainBundle/Export/ExportManager.php b/src/Bundle/ChillMainBundle/Export/ExportManager.php index e2d099ba8..37aa39695 100644 --- a/src/Bundle/ChillMainBundle/Export/ExportManager.php +++ b/src/Bundle/ChillMainBundle/Export/ExportManager.php @@ -13,7 +13,7 @@ namespace Chill\MainBundle\Export; use Chill\MainBundle\Form\Type\Export\ExportType; use Chill\MainBundle\Form\Type\Export\PickCenterType; -use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; use Generator; @@ -42,52 +42,38 @@ class ExportManager /** * The collected aggregators, injected by DI. * - * @var AggregatorInterface[] + * @var array|AggregatorInterface[] */ - private $aggregators = []; + private array $aggregators = []; - /** - * @var AuthorizationChecker - */ - private $authorizationChecker; + private AuthorizationCheckerInterface $authorizationChecker; - /** - * @var AuthorizationHelper - */ - private $authorizationHelper; + private AuthorizationHelperInterface $authorizationHelper; - /** - * @var EntityManagerInterface - */ - private $em; + private EntityManagerInterface $em; /** * Collected Exports, injected by DI. * - * @var ExportInterface[] + * @var array|ExportInterface[] */ - private $exports = []; + private array $exports = []; /** * The collected filters, injected by DI. * - * @var FilterInterface[] + * @var array|FilterInterface[] */ - private $filters = []; + private array $filters = []; /** * Collected Formatters, injected by DI. * - * @var FormatterInterface[] + * @var array|FormatterInterface[] */ - private $formatters = []; + private array $formatters = []; - /** - * a logger. - * - * @var LoggerInterface - */ - private $logger; + private LoggerInterface $logger; /** * @var \Symfony\Component\Security\Core\User\UserInterface @@ -98,7 +84,7 @@ class ExportManager LoggerInterface $logger, EntityManagerInterface $em, AuthorizationCheckerInterface $authorizationChecker, - AuthorizationHelper $authorizationHelper, + AuthorizationHelperInterface $authorizationHelper, TokenStorageInterface $tokenStorage ) { $this->logger = $logger; @@ -277,8 +263,8 @@ class ExportManager //handle aggregators $this->handleAggregators($export, $query, $data[ExportType::AGGREGATOR_KEY], $centers); - $this->logger->debug('current query is ' . $query->getDQL(), [ - 'class' => self::class, 'function' => __FUNCTION__, + $this->logger->notice('[export] will execute this qb in export', [ + 'dql' => $query->getDQL(), ]); } else { throw new UnexpectedValueException('The method `intiateQuery` should return ' @@ -547,19 +533,16 @@ class ExportManager . 'an ExportInterface.'); } - if (null === $centers) { - $centers = $this->authorizationHelper->getReachableCenters( + if (null === $centers || [] !== $centers) { + // we want to try if at least one center is reachabler + return [] !== $this->authorizationHelper->getReachableCenters( $this->user, $role ); } - if (count($centers) === 0) { - return false; - } - foreach ($centers as $center) { - if ($this->authorizationChecker->isGranted($role, $center) === false) { + if (false === $this->authorizationChecker->isGranted($role, $center)) { //debugging $this->logger->debug('user has no access to element', [ 'method' => __METHOD__, @@ -568,10 +551,6 @@ class ExportManager 'role' => $role, ]); - ///// Bypasse les autorisations qui empêche d'afficher les nouveaux exports - return true; - ///// TODO supprimer le return true - return false; } } diff --git a/src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php b/src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php index 86c304508..ab9b2d660 100644 --- a/src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php +++ b/src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php @@ -230,7 +230,8 @@ class SpreadSheetFormatter implements FormatterInterface $worksheet->fromArray( $sortedResults, null, - 'A' . $line + 'A' . $line, + true ); return $line + count($sortedResults) + 1; @@ -495,8 +496,13 @@ class SpreadSheetFormatter implements FormatterInterface // 3. iterate on `keysExportElementAssociation` to store the callable // in cache foreach ($keysExportElementAssociation as $key => [$element, $data]) { - $this->cacheDisplayableResult[$key] = - $element->getLabels($key, array_unique($allValues[$key]), $data); + // handle the case when there is not results lines (query is empty) + if ([] === $allValues) { + $this->cacheDisplayableResult[$key] = $element->getLabels($key, ['_header'], $data); + } else { + $this->cacheDisplayableResult[$key] = + $element->getLabels($key, array_unique($allValues[$key]), $data); + } } // the cache is initialized ! diff --git a/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php b/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php index c73402dd3..e74d8e8f4 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php @@ -14,8 +14,7 @@ namespace Chill\MainBundle\Form\Type\Export; use Chill\MainBundle\Center\GroupingCenterInterface; use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Export\ExportManager; -use Chill\MainBundle\Security\Authorization\AuthorizationHelper; -use Doctrine\ORM\EntityRepository; +use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\CallbackTransformer; @@ -24,6 +23,7 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\User\UserInterface; use function array_intersect; use function array_key_exists; use function array_merge; @@ -38,30 +38,21 @@ class PickCenterType extends AbstractType { public const CENTERS_IDENTIFIERS = 'c'; - /** - * @var AuthorizationHelper - */ - protected $authorizationHelper; + protected AuthorizationHelperInterface $authorizationHelper; + + protected ExportManager $exportManager; /** - * @var ExportManager + * @var array|GroupingCenterInterface[] */ - protected $exportManager; + protected array $groupingCenters = []; - /** - * @var GroupingCenterInterface[] - */ - protected $groupingCenters = []; - - /** - * @var \Symfony\Component\Security\Core\User\UserInterface - */ - protected $user; + protected UserInterface $user; public function __construct( TokenStorageInterface $tokenStorage, ExportManager $exportManager, - AuthorizationHelper $authorizationHelper + AuthorizationHelperInterface $authorizationHelper ) { $this->exportManager = $exportManager; $this->user = $tokenStorage->getToken()->getUser(); @@ -78,22 +69,12 @@ class PickCenterType extends AbstractType $export = $this->exportManager->getExport($options['export_alias']); $centers = $this->authorizationHelper->getReachableCenters( $this->user, - (string) $export->requiredRole() + $export->requiredRole() ); $builder->add(self::CENTERS_IDENTIFIERS, EntityType::class, [ 'class' => Center::class, - 'query_builder' => static function (EntityRepository $er) use ($centers) { - $qb = $er->createQueryBuilder('c'); - $ids = array_map( - static function (Center $el) { - return $el->getId(); - }, - $centers - ); - - return $qb->where($qb->expr()->in('c.id', $ids)); - }, + 'choices' => $centers, 'multiple' => true, 'expanded' => true, 'choice_label' => static function (Center $c) { diff --git a/src/Bundle/ChillMainBundle/Repository/CenterRepository.php b/src/Bundle/ChillMainBundle/Repository/CenterRepository.php index 554f39880..6a52f3399 100644 --- a/src/Bundle/ChillMainBundle/Repository/CenterRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/CenterRepository.php @@ -14,9 +14,8 @@ namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\Center; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; -use Doctrine\Persistence\ObjectRepository; -final class CenterRepository implements ObjectRepository +final class CenterRepository implements CenterRepositoryInterface { private EntityRepository $repository; @@ -30,6 +29,11 @@ final class CenterRepository implements ObjectRepository return $this->repository->find($id, $lockMode, $lockVersion); } + public function findActive(): array + { + return $this->findAll(); + } + /** * @return Center[] */ diff --git a/src/Bundle/ChillMainBundle/Repository/CenterRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/CenterRepositoryInterface.php new file mode 100644 index 000000000..eb656f322 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/CenterRepositoryInterface.php @@ -0,0 +1,27 @@ +repository = $em->getRepository($this->getClassName()); + } + + public function find($id): ?GeographicalUnitLayer + { + return $this->repository->find($id); + } + + /** + * @return array|GeographicalUnitLayer[] + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + public function findAllHavingUnits(): array + { + $qb = $this->repository->createQueryBuilder('l'); + + return $qb->where($qb->expr()->gt('SIZE(l.units)', 0)) + ->getQuery() + ->getResult(); + } + + /** + * @return array|GeographicalUnitLayer[] + */ + public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria): ?GeographicalUnitLayer + { + return $this->repository->findOneBy($criteria); + } + + public function getClassName(): string + { + return GeographicalUnitLayer::class; + } +} diff --git a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerRepositoryInterface.php new file mode 100644 index 000000000..dbc100bf7 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerRepositoryInterface.php @@ -0,0 +1,23 @@ +repository = $em->getRepository($this->getClassName()); + $this->em = $em; + } + + public function find($id): ?GeographicalUnit + { + return $this->repository->find($id); + } + + /** + * Will return only partial object, where the @see{GeographicalUnit::geom} property is not loaded. + * + * @return array|GeographicalUnit[] + */ + public function findAll(): array + { + return $this->repository + ->createQueryBuilder('gu') + ->select('PARTIAL gu.{id,unitName,unitRefId,layer}') + ->addOrderBy('IDENTITY(gu.layer)') + ->addOrderBy(('gu.unitName')) + ->getQuery() + ->getResult(); + } + + public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): ?GeographicalUnit + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria): ?GeographicalUnit + { + return $this->repository->findOneBy($criteria); + } + + public function getClassName(): string + { + return GeographicalUnit::class; + } +} diff --git a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepositoryInterface.php new file mode 100644 index 000000000..76db33422 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepositoryInterface.php @@ -0,0 +1,18 @@ +repository = $entityManager->getRepository(Scope::class); } - public function createQueryBuilder($alias, $indexBy = null) + public function createQueryBuilder($alias, $indexBy = null): QueryBuilder { return $this->repository->createQueryBuilder($alias, $indexBy); } @@ -59,7 +59,7 @@ final class ScopeRepository implements ObjectRepository return $this->repository->findOneBy($criteria, $orderBy); } - public function getClassName() + public function getClassName(): string { return Scope::class; } diff --git a/src/Bundle/ChillMainBundle/Repository/ScopeRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/ScopeRepositoryInterface.php new file mode 100644 index 000000000..e9de77018 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/ScopeRepositoryInterface.php @@ -0,0 +1,40 @@ + + + + + {{ export_group|trans }} + diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig index fa568d857..84eb85100 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig @@ -36,10 +36,7 @@ window.addEventListener("DOMContentLoaded", function(e) { {% block content %}
-
- - {{ export_group|trans }} -
+ {{ include('@ChillMain/Export/_breadcrumb.html.twig') }}

{{ export.title|trans }}

{{ "Download export"|trans }}

diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig index 8aac6c253..ad49d9aa6 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig @@ -22,15 +22,15 @@ {% block js %} {{ encore_entry_script_tags('page_export') }} + {% if export_alias == 'count_social_work_actions' %} + {{ encore_entry_script_tags('vue_export_action_goal_result') }} + {% endif %} {% endblock js %} {% block content %}
-
- - {{ export_group|trans }} -
+ {{ include('@ChillMain/Export/_breadcrumb.html.twig') }}

{{ export.title|trans }}

diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/new_centers_step.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/new_centers_step.html.twig index 4a2d9ab9e..8874744c4 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/new_centers_step.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/new_centers_step.html.twig @@ -22,11 +22,8 @@ {% block content %}
- -
- - {{ export_group|trans }} -
+ + {{ include('@ChillMain/Export/_breadcrumb.html.twig') }}

{{ export.title|trans }}

diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/new_formatter_step.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/new_formatter_step.html.twig index 443816611..8ecaf6dd8 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/new_formatter_step.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/new_formatter_step.html.twig @@ -23,10 +23,7 @@ {% block content %}
-
- - {{ export_group|trans }} -
+ {{ include('@ChillMain/Export/_breadcrumb.html.twig') }}

{{ export.title|trans }}

@@ -36,19 +33,21 @@

{{ 'Formatter'| trans }}

-
{% if form.children.formatter.children|length == 0 %}

{{ "No options availables. Your report is fully configured."|trans }}

{{ form_widget(form.children.formatter) }} {% else %} - {# we always have to render children, to mark as rendered #} - {% for input in form.children.formatter.children %} - {{ form_row(input) }} - {% endfor %} +
+ {# we always have to render children, to mark as rendered #} + {% for input in form.children.formatter.children %} +
+ {{ form_row(input) }} +
+ {% endfor %} +
{% endif %} -
diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/ChillExportVoter.php b/src/Bundle/ChillMainBundle/Security/Authorization/ChillExportVoter.php index ec1a0479d..dae75f111 100644 --- a/src/Bundle/ChillMainBundle/Security/Authorization/ChillExportVoter.php +++ b/src/Bundle/ChillMainBundle/Security/Authorization/ChillExportVoter.php @@ -11,7 +11,6 @@ declare(strict_types=1); namespace Chill\MainBundle\Security\Authorization; -use Chill\MainBundle\Entity\User; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; @@ -19,24 +18,23 @@ class ChillExportVoter extends Voter { public const EXPORT = 'chill_export'; - protected AuthorizationHelperInterface $authorizationHelper; + private VoterHelperInterface $helper; - public function __construct(AuthorizationHelperInterface $authorizationHelper) + public function __construct(VoterHelperFactoryInterface $voterHelperFactory) { - $this->authorizationHelper = $authorizationHelper; + $this->helper = $voterHelperFactory + ->generate(self::class) + ->addCheckFor(null, [self::EXPORT]) + ->build(); } protected function supports($attribute, $subject): bool { - return self::EXPORT === $attribute; + return $this->helper->supports($attribute, $subject); } protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool { - if (!$token->getUser() instanceof User) { - return false; - } - - return [] !== $this->authorizationHelper->getReachableCenters($token->getUser(), $attribute); + return $this->helper->voteOnAttribute($attribute, $subject, $token); } } diff --git a/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBEFromBestAddress.php b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBEFromBestAddress.php new file mode 100644 index 000000000..b034dbca7 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBEFromBestAddress.php @@ -0,0 +1,103 @@ +client = $client; + $this->baseImporter = $baseImporter; + } + + public function import(string $lang, array $lists): void + { + foreach ($lists as $list) { + $this->importList($lang, $list); + } + } + + private function getDownloadUrl(string $lang, string $list): string + { + try { + $release = $this->client->request('GET', self::RELEASE) + ->toArray(); + } catch (TransportExceptionInterface $e) { + throw new RuntimeException('could not get the release definition', 0, $e); + } + + $asset = array_filter($release['assets'], static function (array $item) use ($lang, $list) { + return 'addresses-' . $list . '.' . $lang . '.csv.gz' === $item['name']; + }); + + return array_values($asset)[0]['browser_download_url']; + } + + private function importList(string $lang, string $list): void + { + $downloadUrl = $this->getDownloadUrl($lang, $list); + + $response = $this->client->request('GET', $downloadUrl); + + if (200 !== $response->getStatusCode()) { + throw new Exception('Could not download CSV: ' . $response->getStatusCode()); + } + + $tmpname = tempnam(sys_get_temp_dir(), 'php-add-' . $list . $lang); + $file = fopen($tmpname, 'r+b'); + + foreach ($this->client->stream($response) as $chunk) { + fwrite($file, $chunk->getContent()); + } + + fclose($file); + + $uncompressedStream = gzopen($tmpname, 'r'); + + $csv = Reader::createFromStream($uncompressedStream); + $csv->setDelimiter(','); + $csv->setHeaderOffset(0); + + $stmt = Statement::create() + ->process($csv); + + foreach ($stmt as $record) { + $this->baseImporter->importAddress( + $record['best_id'], + $record['municipality_objectid'], + $record['postal_info_objectid'], + $record['streetname'], + $record['housenumber'] . $record['boxnumber'], + 'bestaddress.' . $list, + (float) $record['X'], + (float) $record['Y'], + 3812 + ); + } + + $this->baseImporter->finalize(); + + gzclose($uncompressedStream); + } +} diff --git a/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php new file mode 100644 index 000000000..474cb53de --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php @@ -0,0 +1,221 @@ + + */ + private array $cachingStatements = []; + + private ?string $currentSource = null; + + private Connection $defaultConnection; + + private bool $isInitialized = false; + + private LoggerInterface $logger; + + private array $waitingForInsert = []; + + public function __construct(Connection $defaultConnection, LoggerInterface $logger) + { + $this->defaultConnection = $defaultConnection; + $this->logger = $logger; + } + + public function finalize(): void + { + $this->doInsertPending(); + + $this->updateAddressReferenceTable(); + + $this->deleteTemporaryTable(); + + $this->currentSource = null; + $this->isInitialized = false; + } + + public function importAddress( + string $refAddress, + ?string $refPostalCode, + string $postalCode, + string $street, + string $streetNumber, + string $source, + ?float $lat = null, + ?float $lon = null, + ?int $srid = null + ): void { + if (!$this->isInitialized) { + $this->initialize($source); + } + + if ($this->currentSource !== $source) { + throw new LogicException('Cannot store addresses from different sources during same import. Execute finalize to commit inserts before changing the source'); + } + + $this->waitingForInsert[] = [ + $refAddress, + $refPostalCode, + $postalCode, + $street, + $streetNumber, + $source, + $lat, + $lon, + $srid, + ]; + + if (100 <= count($this->waitingForInsert)) { + $this->doInsertPending(); + } + } + + private function createTemporaryTable(): void + { + $this->defaultConnection->executeStatement('CREATE TEMPORARY TABLE reference_address_temp ( + postcode_id INT, + refid VARCHAR(255), + street VARCHAR(255), + streetnumber VARCHAR(255), + municipalitycode VARCHAR(255), + source VARCHAR(255), + point GEOMETRY + ); + '); + $this->defaultConnection->executeStatement('SET work_mem TO \'50MB\''); + } + + private function deleteTemporaryTable(): void + { + $this->defaultConnection->executeStatement('DROP TABLE IF EXISTS reference_address_temp'); + } + + private function doInsertPending(): void + { + if (!array_key_exists($forNumber = count($this->waitingForInsert), $this->cachingStatements)) { + $sql = strtr(self::INSERT, [ + '{{ values }}' => implode( + ', ', + array_fill(0, $forNumber, self::VALUE) + ), + ]); + + $this->logger->debug(self::LOG_PREFIX . ' generated sql for insert', [ + 'sql' => $sql, + 'forNumber' => $forNumber, + ]); + + $this->cachingStatements[$forNumber] = $this->defaultConnection->prepare($sql); + } + + if (0 === $forNumber) { + return; + } + + $this->logger->debug(self::LOG_PREFIX . ' inserting pending addresses', [ + 'number' => $forNumber, + 'first' => $this->waitingForInsert[0] ?? null, + ]); + + $statement = $this->cachingStatements[$forNumber]; + + try { + $affected = $statement->executeStatement(array_merge(...$this->waitingForInsert)); + + if (0 === $affected) { + throw new RuntimeException('no row affected'); + } + } catch (Exception $e) { + // in some case, we can add debug code here + //dump($this->waitingForInsert); + throw $e; + } finally { + $this->waitingForInsert = []; + } + } + + private function initialize(string $source): void + { + $this->currentSource = $source; + $this->deleteTemporaryTable(); + $this->createTemporaryTable(); + $this->isInitialized = true; + } + + private function updateAddressReferenceTable(): void + { + $this->defaultConnection->executeStatement( + 'CREATE INDEX idx_ref_add_temp ON reference_address_temp (refid)' + ); + + //1) Add new addresses + $this->logger->info(self::LOG_PREFIX . 'upsert new addresses'); + $affected = $this->defaultConnection->executeStatement("INSERT INTO chill_main_address_reference + (id, postcode_id, refid, street, streetnumber, municipalitycode, source, point, createdat, deletedat, updatedat) + SELECT + nextval('chill_main_address_reference_id_seq'), + postcode_id, + refid, + street, + streetnumber, + municipalitycode, + source, + point, + NOW(), + null, + NOW() + FROM reference_address_temp + ON CONFLICT (refid, source) DO UPDATE + SET postcode_id = excluded.postcode_id, refid = excluded.refid, street = excluded.street, streetnumber = excluded.streetnumber, municipalitycode = excluded.municipalitycode, source = excluded.source, point = excluded.point, updatedat = NOW(), deletedAt = NULL + "); + $this->logger->info(self::LOG_PREFIX . 'addresses upserted', ['upserted' => $affected]); + + //3) Delete addresses + $this->logger->info(self::LOG_PREFIX . 'soft delete adresses'); + $affected = $this->defaultConnection->executeStatement('UPDATE chill_main_address_reference + SET deletedat = NOW() + WHERE + chill_main_address_reference.refid NOT IN (SELECT refid FROM reference_address_temp WHERE source LIKE ?) + AND chill_main_address_reference.source LIKE ? + ', [$this->currentSource, $this->currentSource]); + $this->logger->info(self::LOG_PREFIX . 'addresses deleted', ['deleted' => $affected]); + } +} diff --git a/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceFromBano.php b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceFromBano.php new file mode 100644 index 000000000..c30b7c4f2 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceFromBano.php @@ -0,0 +1,87 @@ +client = $client; + $this->baseImporter = $baseImporter; + } + + public function import(string $departementNo): void + { + if (!is_numeric($departementNo) || !is_int((int) $departementNo)) { + throw new UnexpectedValueException('Could not parse this department number'); + } + + $url = "https://bano.openstreetmap.fr/data/bano-{$departementNo}.csv"; + + $response = $this->client->request('GET', $url); + + if (200 !== $response->getStatusCode()) { + throw new Exception('Could not download CSV: ' . $response->getStatusCode()); + } + + $file = tmpfile(); + + foreach ($this->client->stream($response) as $chunk) { + fwrite($file, $chunk->getContent()); + } + + fseek($file, 0); + + $csv = Reader::createFromStream($file); + $csv->setDelimiter(','); + $stmt = Statement::create() + ->process($csv, [ + 'refId', + 'streetNumber', + 'street', + 'postcode', + 'city', + '_o', + 'lat', + 'lon', + ]); + + foreach ($stmt as $record) { + $this->baseImporter->importAddress( + $record['refId'], + substr($record['refId'], 0, 5), // extract insee from reference + $record['postcode'], + $record['street'], + $record['streetNumber'], + 'BANO.' . $departementNo, + (float) $record['lat'], + (float) $record['lon'], + 4326 + ); + } + + $this->baseImporter->finalize(); + + fclose($file); + } +} diff --git a/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php b/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php new file mode 100644 index 000000000..2668d2c2e --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php @@ -0,0 +1,236 @@ + + */ + private array $cachingStatements = []; + + private Connection $defaultConnection; + + private bool $isInitialized = false; + + private LoggerInterface $logger; + + private array $waitingForInsert = []; + + public function __construct(Connection $defaultConnection, LoggerInterface $logger) + { + $this->defaultConnection = $defaultConnection; + $this->logger = $logger; + } + + public function finalize(): void + { + $this->doInsertPending(); + + $this->prepareForFinalize(); + + $this->updateGeographicalUnitTable(); + + $this->deleteTemporaryTable(); + + $this->isInitialized = false; + } + + public function importUnit( + string $layerKey, + array $layerName, + string $unitName, + string $unitKey, + string $geomAsWKT, + ?int $srid = null + ): void { + $this->initialize(); + + $this->waitingForInsert[] = [ + 'layerKey' => $layerKey, + 'layerName' => $layerName, + 'unitName' => $unitName, + 'unitKey' => $unitKey, + 'geomAsWKT' => $geomAsWKT, + 'srid' => $srid, + ]; + + if (100 <= count($this->waitingForInsert)) { + $this->doInsertPending(); + } + } + + private function createTemporaryTable(): void + { + $this->defaultConnection->executeStatement("CREATE TEMPORARY TABLE geographical_unit_temp ( + layerKey TEXT DEFAULT '' NOT NULL, + layerName JSONB DEFAULT '[]'::jsonb NOT NULL, + unitName TEXT default '' NOT NULL, + unitKey TEXT default '' NOT NULL, + geom GEOMETRY(MULTIPOLYGON, 4326) + )"); + + $this->defaultConnection->executeStatement('SET work_mem TO \'50MB\''); + } + + private function deleteTemporaryTable(): void + { + $this->defaultConnection->executeStatement('DROP TABLE IF EXISTS geographical_unit_temp'); + } + + private function doInsertPending(): void + { + $forNumber = count($this->waitingForInsert); + + if (0 === $forNumber) { + return; + } + + if (!array_key_exists($forNumber, $this->cachingStatements)) { + $sql = strtr(self::INSERT, [ + '{{ values }}' => implode( + ', ', + array_fill(0, $forNumber, self::VALUE) + ), + ]); + + $this->logger->debug(self::LOG_PREFIX . ' generated sql for insert', [ + 'sql' => $sql, + 'forNumber' => $forNumber, + ]); + + $this->cachingStatements[$forNumber] = $this->defaultConnection->prepare($sql); + } + + $statement = $this->cachingStatements[$forNumber]; + + try { + $i = 0; + + foreach ($this->waitingForInsert as $insert) { + $statement->bindValue(++$i, $insert['layerKey'], Types::STRING); + $statement->bindValue(++$i, $insert['layerName'], Types::JSON); + $statement->bindValue(++$i, $insert['unitName'], Types::STRING); + $statement->bindValue(++$i, $insert['unitKey'], Types::STRING); + $statement->bindValue(++$i, $insert['geomAsWKT'], Types::STRING); + $statement->bindValue(++$i, $insert['srid'], Types::INTEGER); + } + + $affected = $statement->executeStatement(); + + if (0 === $affected) { + throw new RuntimeException('no row affected'); + } + } catch (Exception $e) { + throw $e; + } finally { + $this->waitingForInsert = []; + } + } + + private function initialize(): void + { + if ($this->isInitialized) { + return; + } + + $this->deleteTemporaryTable(); + $this->createTemporaryTable(); + $this->isInitialized = true; + } + + private function prepareForFinalize(): void + { + $this->defaultConnection->executeStatement( + 'CREATE INDEX idx_ref_add_temp ON geographical_unit_temp (unitKey)' + ); + } + + private function updateGeographicalUnitTable(): void + { + $this->defaultConnection->transactional( + function () { + // 0) create new layers + $this->defaultConnection->executeStatement( + " + WITH unique_layers AS ( + SELECT DISTINCT layerKey, layerName FROM geographical_unit_temp + ) + INSERT INTO chill_main_geographical_unit_layer (id, name, refid) + SELECT + nextval('chill_main_geographical_unit_layer_id_seq'), + layerName, + layerKey + FROM unique_layers + ON CONFLICT (refid) + DO UPDATE SET name=EXCLUDED.name + " + ); + + //1) Add new units + $this->logger->info(self::LOG_PREFIX . 'upsert new units'); + $affected = $this->defaultConnection->executeStatement("INSERT INTO chill_main_geographical_unit + (id, geom, unitname, layer_id, unitrefid) + SELECT + nextval('chill_main_geographical_unit_id_seq'), + geom, + unitName, + layer.id, + unitKey + FROM geographical_unit_temp JOIN chill_main_geographical_unit_layer AS layer ON layer.refid = layerKey + ON CONFLICT (layer_id, unitrefid) + DO UPDATE + SET geom = EXCLUDED.geom, unitname = EXCLUDED.unitname + "); + $this->logger->info(self::LOG_PREFIX . 'units upserted', ['upserted' => $affected]); + + //3) Delete units + $this->logger->info(self::LOG_PREFIX . 'soft delete adresses'); + $affected = $this->defaultConnection->executeStatement('WITH to_delete AS ( + SELECT cmgu.id + FROM chill_main_geographical_unit AS cmgu + JOIN chill_main_geographical_unit_layer AS cmgul ON cmgul.id = cmgu.layer_id + JOIN geographical_unit_temp AS gut ON cmgul.refid = gut.layerKey AND cmgu.unitrefid = gut.unitKey + ) + DELETE FROM chill_main_geographical_unit + WHERE id NOT IN (SELECT id FROM to_delete) + '); + $this->logger->info(self::LOG_PREFIX . 'addresses deleted', ['deleted' => $affected]); + } + ); + } +} diff --git a/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBEFromBestAddress.php b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBEFromBestAddress.php new file mode 100644 index 000000000..a5b8b216b --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBEFromBestAddress.php @@ -0,0 +1,105 @@ +baseImporter = $baseImporter; + $this->client = $client; + $this->logger = $logger; + } + + public function import(string $lang = 'fr'): void + { + $fileDownloadUrl = $this->getFileDownloadUrl($lang); + + $response = $this->client->request('GET', $fileDownloadUrl); + + $tmpname = tempnam(sys_get_temp_dir(), 'postalcodes'); + $tmpfile = fopen($tmpname, 'r+b'); + + if (false === $tmpfile) { + throw new RuntimeException('could not create temporary file'); + } + + foreach ($this->client->stream($response) as $chunk) { + fwrite($tmpfile, $chunk->getContent()); + } + + fclose($tmpfile); + + $uncompressedStream = gzopen($tmpname, 'r'); + + $csv = Reader::createFromStream($uncompressedStream); + $csv->setDelimiter(','); + $csv->setHeaderOffset(0); + + foreach ($csv as $offset => $record) { + $this->handleRecord($record); + } + + gzclose($uncompressedStream); + unlink($tmpname); + + $this->logger->info(__CLASS__ . ' list of postal code downloaded'); + + $this->baseImporter->finalize(); + + $this->logger->info(__CLASS__ . ' postal code fetched', ['offset' => $offset ?? 0]); + } + + private function getFileDownloadUrl(string $lang): string + { + try { + $release = $this->client->request('GET', self::RELEASE) + ->toArray(); + } catch (TransportExceptionInterface $e) { + throw new RuntimeException('could not get the release definition', 0, $e); + } + + $postals = array_filter($release['assets'], static function (array $item) use ($lang) { + return 'postals.' . $lang . '.csv.gz' === $item['name']; + }); + + return array_values($postals)[0]['browser_download_url']; + } + + private function handleRecord(array $record): void + { + $this->baseImporter->importCode( + 'BE', + trim($record['municipality_name']), + trim($record['postal_info_objectid']), + $record['municipality_objectid'], + 'bestaddress', + $record['Y'], + $record['X'], + 3812 + ); + } +} diff --git a/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBaseImporter.php b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBaseImporter.php new file mode 100644 index 000000000..20b41420f --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBaseImporter.php @@ -0,0 +1,124 @@ + + */ + private array $cachingStatements = []; + + private Connection $defaultConnection; + + private array $waitingForInsert = []; + + public function __construct( + Connection $defaultConnection + ) { + $this->defaultConnection = $defaultConnection; + } + + public function finalize(): void + { + $this->doInsertPending(); + } + + public function importCode( + string $countryCode, + string $label, + string $code, + string $refPostalCodeId, + string $refPostalCodeSource, + float $centerLat, + float $centerLon, + int $centerSRID + ): void { + $this->waitingForInsert[] = [ + $countryCode, + $label, + $code, + $refPostalCodeId, + $refPostalCodeSource, + $centerLon, + $centerLat, + $centerSRID, + ]; + + if (100 <= count($this->waitingForInsert)) { + $this->doInsertPending(); + } + } + + private function doInsertPending(): void + { + if (!array_key_exists($forNumber = count($this->waitingForInsert), $this->cachingStatements)) { + $sql = strtr(self::QUERY, [ + '{{ values }}' => implode( + ', ', + array_fill(0, $forNumber, self::VALUE) + ), + ]); + + $this->cachingStatements[$forNumber] = $this->defaultConnection->prepare($sql); + } + + $statement = $this->cachingStatements[$forNumber]; + + try { + $statement->executeStatement(array_merge(...$this->waitingForInsert)); + } catch (Exception $e) { + // in some case, we can add debug code here + //dump($this->waitingForInsert); + throw $e; + } finally { + $this->waitingForInsert = []; + } + } +} diff --git a/src/Bundle/ChillMainBundle/Service/Import/PostalCodeFRFromOpenData.php b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeFRFromOpenData.php new file mode 100644 index 000000000..26a52c840 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeFRFromOpenData.php @@ -0,0 +1,105 @@ +baseImporter = $baseImporter; + $this->client = $client; + $this->logger = $logger; + } + + public function import(): void + { + $response = $this->client->request('GET', self::CSV); + + if (200 !== $response->getStatusCode()) { + throw new RuntimeException('could not download CSV'); + } + + $tmpfile = tmpfile(); + + if (false === $tmpfile) { + throw new RuntimeException('could not create temporary file'); + } + + foreach ($this->client->stream($response) as $chunk) { + fwrite($tmpfile, $chunk->getContent()); + } + + fseek($tmpfile, 0); + + $csv = Reader::createFromStream($tmpfile); + $csv->setDelimiter(';'); + $csv->setHeaderOffset(0); + + foreach ($csv as $offset => $record) { + $this->handleRecord($record); + } + + $this->baseImporter->finalize(); + fclose($tmpfile); + + $this->logger->info(__CLASS__ . ' postal code fetched', ['offset' => $offset ?? 0]); + } + + private function handleRecord(array $record): void + { + if ('' !== trim($record['coordonnees_gps'])) { + [$lat, $lon] = array_map(static fn ($el) => (float) trim($el), explode(',', $record['coordonnees_gps'])); + } else { + $lat = $lon = 0.0; + } + + $ref = trim($record['Code_commune_INSEE']); + + if ('987' === substr($ref, 0, 3)) { + // some differences in French Polynesia + $ref .= '.' . trim($record['Libellé_d_acheminement']); + } + + $this->baseImporter->importCode( + 'FR', + trim($record['Libellé_d_acheminement']), + trim($record['Code_postal']), + $ref, + 'INSEE', + $lat, + $lon, + 4326 + ); + } +} diff --git a/src/Bundle/ChillMainBundle/Test/ProphecyTrait.php b/src/Bundle/ChillMainBundle/Test/ProphecyTrait.php index a25624719..82a2c434f 100644 --- a/src/Bundle/ChillMainBundle/Test/ProphecyTrait.php +++ b/src/Bundle/ChillMainBundle/Test/ProphecyTrait.php @@ -18,6 +18,8 @@ namespace Chill\MainBundle\Test; * and use tearDownTrait after usage. * * @codeCoverageIgnore + * + * @deprecated use @class{Prophecy\PhpUnit\ProphecyTrait} instead */ trait ProphecyTrait { diff --git a/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php b/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php new file mode 100644 index 000000000..0ed1f2059 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php @@ -0,0 +1,106 @@ +importer = self::$container->get(AddressReferenceBaseImporter::class); + $this->addressReferenceRepository = self::$container->get(AddressReferenceRepository::class); + $this->entityManager = self::$container->get(EntityManagerInterface::class); + $this->postalCodeRepository = self::$container->get(PostalCodeRepository::class); + } + + public function testImportAddress(): void + { + $postalCode = (new PostalCode()) + ->setRefPostalCodeId($postalCodeId = '1234' . uniqid()) + ->setPostalCodeSource('testing') + ->setCode('TEST456') + ->setName('testing'); + + $this->entityManager->persist($postalCode); + $this->entityManager->flush(); + + $this->importer->importAddress( + '0000', + $postalCodeId, + 'TEST456', + 'Rue test abccc-guessed', + '-1', + 'unit-test', + 50.0, + 5.0, + 4326 + ); + + $this->importer->finalize(); + + $addresses = $this->addressReferenceRepository->findByPostalCodePattern( + $postalCode, + 'Rue test abcc guessed' + ); + + $this->assertCount(1, $addresses); + $this->assertEquals('Rue test abccc-guessed', $addresses[0]->getStreet()); + + $previousAddressId = $addresses[0]->getId(); + + $this->entityManager->clear(); + + $this->importer->importAddress( + '0000', + $postalCodeId, + 'TEST456', + 'Rue test abccc guessed fixed', + '-1', + 'unit-test', + 50.0, + 5.0, + 4326 + ); + + $this->importer->finalize(); + + $addresses = $this->addressReferenceRepository->findByPostalCodePattern( + $postalCode, + 'abcc guessed fixed' + ); + + $this->assertCount('1', $addresses); + $this->assertEquals('Rue test abccc guessed fixed', $addresses[0]->getStreet()); + $this->assertEquals($previousAddressId, $addresses[0]->getId()); + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Services/Import/GeographicalUnitBaseImporterTest.php b/src/Bundle/ChillMainBundle/Tests/Services/Import/GeographicalUnitBaseImporterTest.php new file mode 100644 index 000000000..60ad20fc5 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Services/Import/GeographicalUnitBaseImporterTest.php @@ -0,0 +1,100 @@ +connection = self::$container->get(Connection::class); + $this->entityManager = self::$container->get(EntityManagerInterface::class); + } + + public function testImportUnit(): void + { + $importer = new GeographicalUnitBaseImporter( + $this->connection, + new NullLogger() + ); + + $importer->importUnit( + 'test', + ['fr' => 'Test Layer'], + 'Layer one', + 'layer_one', + 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))', + 3812 + ); + + $importer->finalize(); + + $unit = $this->connection->executeQuery(' + SELECT unitname, unitrefid, cmgul.refid AS layerrefid, cmgul.name AS layername, ST_AsText(ST_snapToGrid(ST_Transform(u.geom, 3812), 1)) AS geom + FROM chill_main_geographical_unit u JOIN chill_main_geographical_unit_layer cmgul on u.layer_id = cmgul.id + WHERE u.unitrefid = ?', ['layer_one']); + + $results = $unit->fetchAssociative(); + + $this->assertEquals($results['unitrefid'], 'layer_one'); + $this->assertEquals($results['unitname'], 'Layer one'); + $this->assertEquals(json_decode($results['layername'], true), ['fr' => 'Test Layer']); + $this->assertEquals($results['layerrefid'], 'test'); + $this->assertEquals($results['geom'], 'MULTIPOLYGON(((30 20,45 40,10 40,30 20)),((15 5,40 10,10 20,5 10,15 5)))'); + + $importer = new GeographicalUnitBaseImporter( + $this->connection, + new NullLogger() + ); + + $importer->importUnit( + 'test', + ['fr' => 'Test Layer fixed'], + 'Layer one fixed', + 'layer_one', + 'MULTIPOLYGON (((130 120, 45 40, 10 40, 130 120)),((0 0, 15 5, 40 10, 10 20, 0 0)))', + 3812 + ); + + $importer->finalize(); + + $unit = $this->connection->executeQuery(' + SELECT unitname, unitrefid, cmgul.refid AS layerrefid, cmgul.name AS layername, ST_AsText(ST_snapToGrid(ST_Transform(u.geom, 3812), 1)) AS geom + FROM chill_main_geographical_unit u JOIN chill_main_geographical_unit_layer cmgul on u.layer_id = cmgul.id + WHERE u.unitrefid = ?', ['layer_one']); + + $results = $unit->fetchAssociative(); + + $this->assertEquals($results['unitrefid'], 'layer_one'); + $this->assertEquals($results['unitname'], 'Layer one fixed'); + $this->assertEquals(json_decode($results['layername'], true), ['fr' => 'Test Layer fixed']); + $this->assertEquals($results['layerrefid'], 'test'); + $this->assertEquals($results['geom'], 'MULTIPOLYGON(((130 120,45 40,10 40,130 120)),((0 0,15 5,40 10,10 20,0 0)))'); + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php b/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php new file mode 100644 index 000000000..ce40d40bf --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php @@ -0,0 +1,95 @@ +entityManager = self::$container->get(EntityManagerInterface::class); + $this->importer = self::$container->get(PostalCodeBaseImporter::class); + $this->postalCodeRepository = self::$container->get(PostalCodeRepository::class); + $this->countryRepository = self::$container->get(CountryRepository::class); + } + + public function testImportPostalCode(): void + { + $this->importer->importCode( + 'BE', + 'tested with pattern ' . ($uniqid = uniqid()), + '12345', + $refPostalCodeId = 'test' . uniqid(), + 'test', + 50.0, + 5.0, + 4326 + ); + + $this->importer->finalize(); + + $postalCodes = $this->postalCodeRepository->findByPattern( + 'with pattern ' . $uniqid, + $this->countryRepository->findOneBy(['countryCode' => 'BE']) + ); + + $this->assertCount(1, $postalCodes); + $this->assertStringStartsWith('tested with pattern', $postalCodes[0]->getName()); + + $previousId = $postalCodes[0]->getId(); + + $this->entityManager->clear(); + + $this->importer->importCode( + 'BE', + 'tested with adapted pattern ' . ($uniqid = uniqid()), + '12345', + $refPostalCodeId, + 'test', + 50.0, + 5.0, + 4326 + ); + + $this->importer->finalize(); + + $postalCodes = $this->postalCodeRepository->findByPattern( + 'with pattern ' . $uniqid, + $this->countryRepository->findOneBy(['countryCode' => 'BE']) + ); + + $this->assertCount(1, $postalCodes); + $this->assertStringStartsWith('tested with adapted pattern', $postalCodes[0]->getName()); + $this->assertEquals($previousId, $postalCodes[0]->getId()); + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php b/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php new file mode 100644 index 000000000..365b01a67 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php @@ -0,0 +1,116 @@ +prophesize(WorkflowInterface::class); + $workflow = $workflowProphecy->reveal(); + $entityWorkflow = new EntityWorkflow(); + $entityWorkflow + ->setWorkflowName('workflow_name') + ->setRelatedEntityClass(stdClass::class) + ->setRelatedEntityId(1); + // force an id to entityWorkflow: + $reflection = new ReflectionClass($entityWorkflow); + $id = $reflection->getProperty('id'); + $id->setAccessible(true); + $id->setValue($entityWorkflow, 1); + + $step = new EntityWorkflowStep(); + $entityWorkflow->addStep($step); + $step->addDestUser($dest) + ->setCurrentStep('to_state'); + + $em = $this->prophesize(EntityManagerInterface::class); + $em->persist(Argument::type(Notification::class))->should( + static function ($args) use ($dest) { + /** @var Call[] $args */ + if (1 !== count($args)) { + throw new FailedPredictionException('no notification sent'); + } + + $notification = $args[0]->getArguments()[0]; + + if (!$notification instanceof Notification) { + throw new FailedPredictionException('persist is not a notification'); + } + + if (!$notification->getAddressees()->contains($dest)) { + throw new FailedPredictionException('the dest is not notified'); + } + } + ); + + $engine = $this->prophesize(EngineInterface::class); + $engine->render(Argument::type('string'), Argument::type('array')) + ->willReturn('dummy text'); + + $extractor = $this->prophesize(MetadataExtractor::class); + $extractor->buildArrayPresentationForPlace(Argument::type(EntityWorkflow::class), Argument::any()) + ->willReturn([]); + $extractor->buildArrayPresentationForWorkflow(Argument::any()) + ->willReturn([]); + + $registry = $this->prophesize(Registry::class); + $registry->get(Argument::type(EntityWorkflow::class), Argument::type('string')) + ->willReturn($workflow); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($currentUser); + + $notificationOnTransition = new NotificationOnTransition( + $em->reveal(), + $engine->reveal(), + $extractor->reveal(), + $security->reveal(), + $registry->reveal() + ); + + $event = new Event($entityWorkflow, new Marking(), new Transition('dummy_transition', ['from_state'], ['to_state']), $workflow); + + $notificationOnTransition->onCompletedSendNotification($event); + } +} diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php index 7074be14d..0f6a4799a 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php +++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php @@ -41,11 +41,32 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac $this->userRender = $userRender; } + public function addDests(Event $event): void + { + if (!$event->getSubject() instanceof EntityWorkflow) { + return; + } + + /** @var EntityWorkflow $entityWorkflow */ + $entityWorkflow = $event->getSubject(); + + foreach ($entityWorkflow->futureDestUsers as $user) { + $entityWorkflow->getCurrentStep()->addDestUser($user); + } + + foreach ($entityWorkflow->futureDestEmails as $email) { + $entityWorkflow->getCurrentStep()->addDestEmail($email); + } + } + public static function getSubscribedEvents(): array { return [ 'workflow.transition' => 'onTransition', - 'workflow.completed' => 'onCompleted', + 'workflow.completed' => [ + ['markAsFinal', 2048], + ['addDests', 2048], + ], 'workflow.guard' => [ ['guardEntityWorkflow', 0], ], @@ -90,7 +111,7 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac } } - public function onCompleted(Event $event): void + public function markAsFinal(Event $event): void { if (!$event->getSubject() instanceof EntityWorkflow) { return; diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php index f7854c93f..c56dc34ce 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php +++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php @@ -52,10 +52,21 @@ class NotificationOnTransition implements EventSubscriberInterface public static function getSubscribedEvents(): array { return [ - 'workflow.completed' => 'onCompletedSendNotification', + 'workflow.completed' => ['onCompletedSendNotification', 2048], ]; } + /** + * Send a notification to:. + * + * * the dests of the new step; + * * the users which subscribed to workflow, on each step, or on final + * + * **Warning** take care that this method must be executed **after** the dest users are added to + * the step (@see{EntityWorkflowStep::addDestUser}). Currently, this is done during + * + * @see{EntityWorkflowTransitionEventSubscriber::addDests}. + */ public function onCompletedSendNotification(Event $event): void { if (!$event->getSubject() instanceof EntityWorkflow) { @@ -65,23 +76,28 @@ class NotificationOnTransition implements EventSubscriberInterface /** @var EntityWorkflow $entityWorkflow */ $entityWorkflow = $event->getSubject(); - $dests = array_merge( + /** @var array $dests array of unique values, where keys is the object's hash */ + $dests = []; + + foreach (array_merge( + // the subscriber to each step $entityWorkflow->getSubscriberToStep()->toArray(), + // the subscriber to final, only if final $entityWorkflow->isFinal() ? $entityWorkflow->getSubscriberToFinal()->toArray() : [], - $entityWorkflow->getCurrentStepChained()->getPrevious()->getDestUser()->toArray() - ); + // the dests for the current step + $entityWorkflow->getCurrentStep()->getDestUser()->toArray() + ) as $dest) { + $dests[spl_object_hash($dest)] = $dest; + } $place = $this->metadataExtractor->buildArrayPresentationForPlace($entityWorkflow); $workflow = $this->metadataExtractor->buildArrayPresentationForWorkflow( $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName()) ); - $visited = []; - foreach ($dests as $subscriber) { if ( $this->security->getUser() === $subscriber - || in_array($subscriber->getId(), $visited, true) ) { continue; } @@ -102,8 +118,6 @@ class NotificationOnTransition implements EventSubscriberInterface ->setMessage($this->engine->render('@ChillMain/Workflow/workflow_notification_on_transition_completed_content.fr.txt.twig', $context)) ->addAddressee($subscriber); $this->entityManager->persist($notification); - - $visited[] = $subscriber->getId(); } } } diff --git a/src/Bundle/ChillMainBundle/config/services.yaml b/src/Bundle/ChillMainBundle/config/services.yaml index a6dd8d827..10164932f 100644 --- a/src/Bundle/ChillMainBundle/config/services.yaml +++ b/src/Bundle/ChillMainBundle/config/services.yaml @@ -89,12 +89,12 @@ services: - { name: validator.constraint_validator, alias: 'role_scope_scope_presence' } Chill\MainBundle\Export\ExportManager: - arguments: - - "@logger" - - "@doctrine.orm.entity_manager" - - "@security.authorization_checker" - - "@chill.main.security.authorization.helper" - - "@security.token_storage" + autoconfigure: true + autowire: true Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface: '@Chill\MainBundle\Security\Resolver\CenterResolverDispatcher' + Chill\MainBundle\Service\Import\: + resource: '../Service/Import/' + autowire: true + autoconfigure: true diff --git a/src/Bundle/ChillMainBundle/config/services/command.yaml b/src/Bundle/ChillMainBundle/config/services/command.yaml index 87220bc1f..e5f285545 100644 --- a/src/Bundle/ChillMainBundle/config/services/command.yaml +++ b/src/Bundle/ChillMainBundle/config/services/command.yaml @@ -43,3 +43,21 @@ services: $entityManager: '@doctrine.orm.entity_manager' tags: - { name: console.command } + + Chill\MainBundle\Command\LoadAddressesFRFromBANOCommand: + autoconfigure: true + autowire: true + tags: + - { name: console.command } + + Chill\MainBundle\Command\LoadAddressesBEFromBestAddressCommand: + autoconfigure: true + autowire: true + tags: + - { name: console.command } + + Chill\MainBundle\Command\LoadPostalCodeFR: + autoconfigure: true + autowire: true + tags: + - { name: console.command } diff --git a/src/Bundle/ChillMainBundle/config/services/form.yaml b/src/Bundle/ChillMainBundle/config/services/form.yaml index 754d3b042..0d0739e23 100644 --- a/src/Bundle/ChillMainBundle/config/services/form.yaml +++ b/src/Bundle/ChillMainBundle/config/services/form.yaml @@ -81,12 +81,8 @@ services: chill.main.form.pick_centers_type: class: Chill\MainBundle\Form\Type\Export\PickCenterType - arguments: - - "@security.token_storage" - - '@Chill\MainBundle\Export\ExportManager' - - "@chill.main.security.authorization.helper" - tags: - - { name: form.type } + autowire: true + autoconfigure: true chill.main.form.formatter_type: class: Chill\MainBundle\Form\Type\Export\FormatterType diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220729205416.php b/src/Bundle/ChillMainBundle/migrations/Version20220729205416.php new file mode 100644 index 000000000..69ffd856f --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20220729205416.php @@ -0,0 +1,53 @@ +addSql('DROP INDEX postal_code_import_unicity'); + $this->addSql('ALTER TABLE chill_main_postal_code DROP deletedAt'); + $this->addSql('ALTER TABLE chill_main_postal_code DROP updatedAt'); + $this->addSql('ALTER TABLE chill_main_postal_code DROP createdAt'); + $this->addSql('ALTER TABLE chill_main_postal_code DROP updatedBy_id'); + $this->addSql('ALTER TABLE chill_main_postal_code DROP createdBy_id'); + $this->addSql('ALTER TABLE chill_main_postal_code DROP CONSTRAINT chill_internal_postal_code_import_unicity'); + } + + public function getDescription(): string + { + return 'postal code: add columns to track creation, update and deletion'; + } + + public function up(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_main_postal_code ADD deletedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_main_postal_code ADD updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_main_postal_code ADD createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_main_postal_code ADD updatedBy_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_main_postal_code ADD createdBy_id INT DEFAULT NULL'); + $this->addSql('COMMENT ON COLUMN chill_main_postal_code.deletedAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_main_postal_code.updatedAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_main_postal_code.createdAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE chill_main_postal_code ADD CONSTRAINT FK_6CA145FA65FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_main_postal_code ADD CONSTRAINT FK_6CA145FA3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_6CA145FA65FF1AEC ON chill_main_postal_code (updatedBy_id)'); + $this->addSql('CREATE INDEX IDX_6CA145FA3174800F ON chill_main_postal_code (createdBy_id)'); + $this->addSql('CREATE UNIQUE INDEX postal_code_import_unicity ON chill_main_postal_code (code, refpostalcodeid, postalcodesource) WHERE refpostalcodeid is not null'); + //$this->addSql('ALTER TABLE chill_main_postal_code ADD CONSTRAINT chill_internal_postal_code_import_unicity '. + // 'EXCLUDE (code WITH =, refpostalcodeid WITH =, postalcodesource WITH =) WHERE (refpostalcodeid IS NOT NULL)'); + } +} diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php b/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php new file mode 100644 index 000000000..e4ad98318 --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php @@ -0,0 +1,33 @@ +addSql('DROP INDEX chill_main_address_reference_unicity'); + } + + public function getDescription(): string + { + return 'Add an unique constraint on addresses references'; + } + + public function up(Schema $schema): void + { + $this->addSql('CREATE UNIQUE INDEX chill_main_address_reference_unicity ON chill_main_address_reference (refId, source)'); + } +} diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220913174922.php b/src/Bundle/ChillMainBundle/migrations/Version20220913174922.php new file mode 100644 index 000000000..f00cb5aa1 --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20220913174922.php @@ -0,0 +1,36 @@ +addSql('ALTER TABLE chill_main_geographical_unit ALTER COLUMN geom SET DATA TYPE TEXT'); + } + + public function getDescription(): string + { + return 'Geographical Unit correction'; + } + + public function up(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER COLUMN geom SET DATA TYPE GEOMETRY(MULTIPOLYGON, 4326)'); + } +} diff --git a/src/Bundle/ChillMainBundle/migrations/Version20221003112151.php b/src/Bundle/ChillMainBundle/migrations/Version20221003112151.php new file mode 100644 index 000000000..b9753ce8c --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20221003112151.php @@ -0,0 +1,64 @@ +throwIrreversibleMigrationException(); + + /* for memory + $this->addSql('ALTER TABLE chill_main_geographical_unit DROP CONSTRAINT FK_360A2B2FEA6EFDCD'); + $this->addSql('DROP SEQUENCE chill_main_geographical_unit_layer_id_seq CASCADE'); + $this->addSql('DROP TABLE chill_main_geographical_unit_layer'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ADD layername VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_main_geographical_unit DROP layer_id'); + $this->addSql('ALTER TABLE chill_main_geographical_unit DROP unitRefId'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER geom TYPE VARCHAR(255)'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitName TYPE VARCHAR(255)'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitName DROP DEFAULT'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitName DROP NOT NULL'); + */ + } + + public function getDescription(): string + { + return 'Add a proper entity for GeographicalUnitLayer'; + } + + public function up(Schema $schema): void + { + $this->addSql('CREATE SEQUENCE chill_main_geographical_unit_layer_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE chill_main_geographical_unit_layer (id INT NOT NULL, name JSONB DEFAULT \'[]\'::jsonb NOT NULL, refid TEXT DEFAULT \'\' NOT NULL, PRIMARY KEY(id))'); + $this->addSql("COMMENT ON COLUMN chill_main_geographical_unit_layer.name IS '(DC2Type:json)';"); + + $this->addSql('INSERT INTO chill_main_geographical_unit_layer (id, name, refid) + SELECT DISTINCT nextval(\'chill_main_geographical_unit_layer_id_seq\'), jsonb_build_object(\'fr\', layername), layername FROM chill_main_geographical_unit'); + + $this->addSql('ALTER TABLE chill_main_geographical_unit ADD layer_id INT DEFAULT NULL'); + + $this->addSql('UPDATE chill_main_geographical_unit SET layer_id = layer.id FROM chill_main_geographical_unit_layer AS layer WHERE layer.refid = chill_main_geographical_unit.layername'); + + $this->addSql('ALTER TABLE chill_main_geographical_unit ADD unitRefId TEXT DEFAULT \'\' NOT NULL'); + $this->addSql('ALTER TABLE chill_main_geographical_unit DROP layername'); + $this->addSql("COMMENT ON COLUMN chill_main_geographical_unit.geom IS '(DC2Type:text)';"); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitname TYPE TEXT'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitname SET DEFAULT \'\''); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitname SET NOT NULL'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ADD CONSTRAINT FK_360A2B2FEA6EFDCD FOREIGN KEY (layer_id) REFERENCES chill_main_geographical_unit_layer (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_360A2B2FEA6EFDCD ON chill_main_geographical_unit (layer_id)'); + } +} diff --git a/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php b/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php new file mode 100644 index 000000000..8186ce7ed --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php @@ -0,0 +1,37 @@ +addSql('DROP INDEX geographical_unit_layer_refid'); + $this->addSql('DROP INDEX geographical_unit_refid'); + $this->addSql('DROP INDEX chill_internal_geographical_unit_layer_geom_idx'); + } + + public function getDescription(): string + { + return 'Create indexes and unique constraints on geographical unit entities'; + } + + public function up(Schema $schema): void + { + $this->addSql('CREATE UNIQUE INDEX geographical_unit_layer_refid ON chill_main_geographical_unit_layer (refId)'); + $this->addSql('CREATE UNIQUE INDEX geographical_unit_refid ON chill_main_geographical_unit (layer_id, unitRefId)'); + $this->addSql('CREATE INDEX chill_internal_geographical_unit_layer_geom_idx ON chill_main_geographical_unit USING GIST (layer_id, geom)'); + } +} diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index 8e4dc5d25..e21dbfbac 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -27,6 +27,8 @@ use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint; use Chill\PersonBundle\Entity\Household\Household; use Chill\PersonBundle\Entity\Household\HouseholdMember; use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress; +use Chill\PersonBundle\Entity\Person\PersonCenterCurrent; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Entity\Person\PersonCurrentAddress; use Chill\PersonBundle\Entity\Person\PersonResource; use Chill\PersonBundle\Validator\Constraints\Household\HouseholdMembershipSequential; @@ -44,6 +46,7 @@ use libphonenumber\PhoneNumber; use Symfony\Component\Serializer\Annotation\DiscriminatorMap; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Context\ExecutionContextInterface; +use UnexpectedValueException; use function count; use function in_array; @@ -188,9 +191,23 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI * The person's center. * * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Center") + * + * @deprecated */ private ?Center $center = null; + /** + * @ORM\OneToOne(targetEntity=PersonCenterCurrent::class, mappedBy="person") + */ + private ?PersonCenterCurrent $centerCurrent = null; + + /** + * @ORM\OneToMany(targetEntity=PersonCenterHistory::class, mappedBy="person", cascade={"persist"}) + * + * @var Collection|PersonCenterHistory[] + */ + private Collection $centerHistory; + /** * Array where customfield's data are stored. * @@ -531,6 +548,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI $this->budgetResources = new ArrayCollection(); $this->budgetCharges = new ArrayCollection(); $this->resources = new ArrayCollection(); + $this->centerHistory = new ArrayCollection(); } /** @@ -905,7 +923,33 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI public function getCenter(): ?Center { - return $this->center; + if (null !== $this->centerCurrent) { + return $this->centerCurrent->getCenter(); + } + + if (null === $currentCenterHistory = $this->getCurrentCenterHistory()) { + return null; + } + + return $currentCenterHistory->getCenter(); + } + + public function getCenterCurrent(): ?PersonCenterCurrent + { + if (null !== $this->centerCurrent) { + return $this->centerCurrent; + } + + if (null === $currentCenterHistory = $this->getCurrentCenterHistory()) { + return null; + } + + return new PersonCenterCurrent($currentCenterHistory); + } + + public function getCenterHistory(): Collection + { + return $this->centerHistory; } public function getCFData(): ?array @@ -1518,17 +1562,39 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI return $this; } + /** + * Associate the center with the person. The association start on 'now'. + * + * @return $this + */ public function setCenter(Center $center): self { $this->center = $center; + $modification = new DateTimeImmutable('now'); + + foreach ($this->centerHistory as $centerHistory) { + if (null === $centerHistory->getEndDate()) { + $centerHistory->setEndDate($modification); + } + } + + $this->centerHistory[] = $new = new PersonCenterHistory($this, $center, $modification); + + return $this; + } + + public function setCenterHistory(Collection $centerHistory): Person + { + $this->centerHistory = $centerHistory; + return $this; } /** - * @return Report + * @return Person */ - public function setCFData(?array $cFData) + public function setCFData(?array $cFData): self { $this->cFData = $cFData; @@ -1727,6 +1793,34 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI return $this; } + private function getCurrentCenterHistory(): ?PersonCenterHistory + { + if (0 === $this->centerHistory->count()) { + return null; + } + + $criteria = Criteria::create(); + $now = new DateTimeImmutable('now'); + $criteria->where(Criteria::expr()->lte('startDate', $now)) + ->andWhere(Criteria::expr()->orX( + Criteria::expr()->isNull('endDate'), + Criteria::expr()->gt('endDate', $now) + )); + + $histories = $this->centerHistory->matching($criteria); + + switch ($histories->count()) { + case 0: + return null; + + case 1: + return $histories->first(); + + default: + throw new UnexpectedValueException('It should not contains more than one center at a time'); + } + } + /** * This private function scan accompanyingPeriodParticipations Collection, * searching for a given AccompanyingPeriod. diff --git a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php new file mode 100644 index 000000000..30de1f55a --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php @@ -0,0 +1,100 @@ +person = $history->getPerson(); + $this->center = $history->getCenter(); + $this->startDate = $history->getStartDate(); + $this->endDate = $history->getEndDate(); + $this->id = $history->getId(); + } + + public function getCenter(): Center + { + return $this->center; + } + + public function getEndDate(): ?DateTimeImmutable + { + return $this->endDate; + } + + /** + * The id will be the same as the current @see{PersonCenterHistory::class}. + */ + public function getId(): int + { + return $this->id; + } + + public function getPerson(): Person + { + return $this->person; + } + + public function getStartDate(): DateTimeImmutable + { + return $this->startDate; + } +} diff --git a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php new file mode 100644 index 000000000..a9ec61251 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php @@ -0,0 +1,121 @@ +person = $person; + $this->center = $center; + $this->startDate = $startDate; + } + + public function getCenter(): ?Center + { + return $this->center; + } + + public function getEndDate(): ?DateTimeImmutable + { + return $this->endDate; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getPerson(): ?Person + { + return $this->person; + } + + public function getStartDate(): ?DateTimeImmutable + { + return $this->startDate; + } + + public function setCenter(?Center $center): self + { + $this->center = $center; + + return $this; + } + + public function setEndDate(?DateTimeImmutable $endDate): self + { + $this->endDate = $endDate; + + return $this; + } + + public function setPerson(?Person $person): self + { + $this->person = $person; + + return $this; + } + + public function setStartDate(?DateTimeImmutable $startDate): self + { + $this->startDate = $startDate; + + return $this; + } +} diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php index e9c7496ff..131cc4bac 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php @@ -49,9 +49,8 @@ class Evaluation /** * @ORM\ManyToMany( * targetEntity=SocialAction::class, - * inversedBy="evaluations" + * mappedBy="evaluations" * ) - * @ORM\JoinTable(name="chill_person_social_work_evaluation_action") */ private Collection $socialActions; diff --git a/src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php b/src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php index 264f47955..4fa0b9ad8 100644 --- a/src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php +++ b/src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php @@ -25,23 +25,17 @@ class AbstractAccompanyingPeriodExportElement */ protected function addJoinAccompanyingPeriod(QueryBuilder $query): void { - if (false === $this->havingAccompanyingPeriodInJoin($query)) { - if (false === in_array('person', $query->getAllAliases(), true)) { - throw new LogicException("the alias 'person' does not exists in " - . 'query builder'); - } + if (false === in_array('person', $query->getAllAliases(), true)) { + throw new LogicException("the alias 'person' does not exists in " + . 'query builder'); + } - $query->join('person.accompanyingPeriods', 'accompanying_period'); + if (!in_array('acppart', $query->getAllAliases(), true)) { + $query->join('person.accompanyingPeriodParticipations', 'acppart'); + } + + if (!in_array('acp', $query->getAllAliases(), true)) { + $query->join('acppart.accompanyingPeriod', 'acp'); } } - - /** - * Return true if "accompanying_period" alias is present in the query alises. - */ - protected function havingAccompanyingPeriodInJoin(QueryBuilder $query): bool - { - $joins = $query->getDQLPart('join') ?? []; - - return in_array('accompanying_period', $query->getAllAliases(), true); - } } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php index 13185c2a2..b51b31c60 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class AdministrativeLocationAggregator implements AggregatorInterface { @@ -40,18 +41,11 @@ class AdministrativeLocationAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acploc', $qb->getAllAliases(), true)) { - $qb->join('acp.administrativeLocation', 'acploc'); + $qb->leftJoin('acp.administrativeLocation', 'acploc'); } $qb->addSelect('IDENTITY(acp.administrativeLocation) 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 @@ -71,6 +65,10 @@ class AdministrativeLocationAggregator implements AggregatorInterface return 'Administrative location'; } + if (null === $value) { + return ''; + } + $l = $this->locationRepository->find($value); return $l->getName() . ' (' . $this->translatableStringHelper->localize($l->getLocationType()->getTitle()) . ')'; diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php index 033ba4365..de3075747 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php @@ -13,23 +13,22 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; -use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\EntityManagerInterface; +use Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepositoryInterface; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; class ClosingMotiveAggregator implements AggregatorInterface { - private EntityManagerInterface $em; + private ClosingMotiveRepositoryInterface $motiveRepository; private TranslatableStringHelper $translatableStringHelper; public function __construct( - EntityManagerInterface $em, + ClosingMotiveRepositoryInterface $motiveRepository, TranslatableStringHelper $translatableStringHelper ) { - $this->motiveRepository = $em->getRepository(ClosingMotive::class); + $this->motiveRepository = $motiveRepository; $this->translatableStringHelper = $translatableStringHelper; } @@ -40,19 +39,8 @@ class ClosingMotiveAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('acpmotive', $qb->getAllAliases(), true)) { - $qb->join('acp.closingMotive', 'acpmotive'); - } - $qb->addSelect('IDENTITY(acp.closingMotive) AS closingmotive_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('closingmotive_aggregator'); - } else { - $qb->groupBy('closingmotive_aggregator'); - } + $qb->addGroupBy('closingmotive_aggregator'); } public function applyOn(): string @@ -72,6 +60,10 @@ class ClosingMotiveAggregator implements AggregatorInterface return 'Closing motive'; } + if (null === $value) { + return ''; + } + $cm = $this->motiveRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php index ec9bdd348..a2a4919aa 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php @@ -35,14 +35,7 @@ class ConfidentialAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { $qb->addSelect('acp.confidential AS confidential_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('confidential_aggregator'); - } else { - $qb->groupBy('confidential_aggregator'); - } + $qb->addGroupBy('confidential_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php index b43b0ba11..ee54da2af 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php @@ -44,15 +44,8 @@ final class DurationAggregator implements AggregatorInterface // 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) - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('duration_aggregator'); - } else { - $qb->groupBy('duration_aggregator'); - } - - $qb->orderBy('duration_aggregator'); + $qb->addGroupBy('duration_aggregator'); + $qb->addOrderBy('duration_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php index f59491ec5..297faf049 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php @@ -35,14 +35,7 @@ class EmergencyAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { $qb->addSelect('acp.emergency AS emergency_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('emergency_aggregator'); - } else { - $qb->groupBy('emergency_aggregator'); - } + $qb->addGroupBy('emergency_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregator.php index 120506aae..292303cea 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregator.php @@ -41,22 +41,15 @@ final class EvaluationAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpw', $qb->getAllAliases(), true)) { - $qb->join('acp.works', 'acpw'); + $qb->leftJoin('acp.works', 'acpw'); } if (!in_array('workeval', $qb->getAllAliases(), true)) { - $qb->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'); + $qb->leftJoin('acpw.accompanyingPeriodWorkEvaluations', 'workeval'); } $qb->addSelect('IDENTITY(workeval.evaluation) AS evaluation_aggregator'); - - $groupby = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('evaluation_aggregator'); - } else { - $qb->groupBy('evaluation_aggregator'); - } + $qb->addGroupBy('evaluation_aggregator'); } public function applyOn(): string @@ -76,6 +69,10 @@ final class EvaluationAggregator implements AggregatorInterface return 'Evaluation'; } + if (null === $value) { + return ''; + } + $e = $this->evaluationRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php index 18a1b3dea..884b9c8e9 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php @@ -1,96 +1,185 @@ repository = $em->getRepository(...::class); - } - */ - - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ('_header' === $value) { - return 'Geographical unit'; - } - - $g = $this->repository->find($value); - - return $g; //... - }; + $this->geographicalUnitLayerRepository = $geographicalUnitLayerRepository; + $this->translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['geographicalunitstat_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // TODO: Implement buildForm() method. - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by geographical unit'; - } - - /** - * @inheritDoc - */ public function addRole(): ?string { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { + if (!in_array('acp_geog_agg_location_history', $qb->getAllAliases(), true)) { + $qb->leftJoin('acp.locationHistories', 'acp_geog_agg_location_history'); - //$qb->addSelect('... AS geographicalunitstat_aggregator'); + $qb->andWhere( + $qb->expr()->andX( + 'acp_geog_agg_location_history.startDate <= :acp_geog_aggregator_date', + $qb->expr()->orX( + 'acp_geog_agg_location_history.endDate IS NULL', + 'acp_geog_agg_location_history.endDate > :acp_geog_aggregator_date' + ) + ) + ); - $groupby = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('geographicalunitstat_aggregator'); - } else { - $qb->groupBy('geographicalunitstat_aggregator'); + $qb->setParameter('acp_geog_aggregator_date', $data['date_calc']); } + + // link between location history and person + if (!in_array('acp_geog_agg_address_person_location', $qb->getAllAliases(), true)) { + $qb->leftJoin( + PersonHouseholdAddress::class, + 'acp_geog_agg_address_person_location', + Join::WITH, + $qb->expr()->andX( + 'IDENTITY(acp_geog_agg_address_person_location.person) = IDENTITY(acp_geog_agg_location_history.personLocation)', + 'acp_geog_agg_address_person_location.validFrom < :acp_geog_aggregator_date', + $qb->expr()->orX( + 'acp_geog_agg_address_person_location.validTo > :acp_geog_aggregator_date', + $qb->expr()->isNull('acp_geog_agg_address_person_location.validTo') + ) + ) + ); + + $qb->setParameter('acp_geog_aggregator_date', $data['date_calc']); + } + + // we finally find an address + if (!in_array('acp_geog_agg_address', $qb->getAllAliases(), true)) { + $qb->leftJoin( + Address::class, + 'acp_geog_agg_address', + Join::WITH, + 'COALESCE(IDENTITY(acp_geog_agg_address_person_location.address), IDENTITY(acp_geog_agg_location_history.addressLocation)) = acp_geog_agg_address.id' + ); + } + + // and we do a join with units + $qb->leftJoin( + GeographicalUnit::class, + 'acp_geog_units', + Join::WITH, + 'ST_CONTAINS(acp_geog_units.geom, acp_geog_agg_address.point) = TRUE' + ); + + $qb->andWhere($qb->expr()->eq('acp_geog_units.layer', ':acp_geog_unit_layer')); + + $qb->setParameter('acp_geog_unit_layer', $data['level']); + + // we add group by + $qb + ->addSelect('acp_geog_units.unitName AS acp_geog_agg_unitname') + ->addSelect('acp_geog_units.unitRefId AS acp_geog_agg_unitrefid') + ->addGroupBy('acp_geog_agg_unitname') + ->addGroupBy('acp_geog_agg_unitrefid'); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('date_calc', ChillDateType::class, [ + 'label' => 'Compute geographical location at date', + 'required' => true, + 'data' => new DateTimeImmutable('today'), + 'input' => 'datetime_immutable', + ]) + ->add('level', EntityType::class, [ + 'label' => 'Geographical layer', + 'placeholder' => 'Select a geographical layer', + 'class' => GeographicalUnitLayer::class, + 'choices' => $this->geographicalUnitLayerRepository->findAllHavingUnits(), + 'choice_label' => function (GeographicalUnitLayer $item) { + return $this->translatableStringHelper->localize($item->getName()); + }, + ]); + } + + public function getLabels($key, array $values, $data) + { + switch ($key) { + case 'acp_geog_agg_unitname': + return static function ($value): string { + if ('_header' === $value) { + return 'acp_geog_agg_unitname'; + } + + if (null === $value) { + return ''; + } + + return $value; + }; + + case 'acp_geog_agg_unitrefid': + return static function ($value): string { + if ('_header' === $value) { + return 'acp_geog_agg_unitrefid'; + } + + if (null === $value) { + return ''; + } + + return $value; + }; + + default: + throw new UnexpectedValueException('this value should not happens'); + } + } + + public function getQueryKeys($data): array + { + return ['acp_geog_agg_unitname', 'acp_geog_agg_unitrefid']; + } + + public function getTitle(): string + { + return 'Group by geographical unit'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php index 711f7ec0d..c418ea39f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php @@ -35,14 +35,7 @@ class IntensityAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { $qb->addSelect('acp.intensity AS intensity_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('intensity_aggregator'); - } else { - $qb->groupBy('intensity_aggregator'); - } + $qb->addGroupBy('intensity_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php index 7634ea37f..df48d61af 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class JobAggregator implements AggregatorInterface { @@ -40,18 +41,11 @@ final class JobAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpjob', $qb->getAllAliases(), true)) { - $qb->join('acp.job', 'acpjob'); + $qb->leftJoin('acp.job', 'acpjob'); } $qb->addSelect('IDENTITY(acp.job) 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 @@ -71,6 +65,10 @@ final class JobAggregator implements AggregatorInterface return 'Job'; } + if (null === $value) { + return ''; + } + $j = $this->jobRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php index 4c784e08d..e9c2ef878 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php @@ -19,6 +19,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class OriginAggregator implements AggregatorInterface { @@ -42,18 +43,11 @@ final class OriginAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acporigin', $qb->getAllAliases(), true)) { - $qb->join('acp.origin', 'acporigin'); + $qb->leftJoin('acp.origin', 'acporigin'); } - $qb->addSelect('o.id AS origin_aggregator'); - - $groupby = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('origin_aggregator'); - } else { - $qb->groupBy('origin_aggregator'); - } + $qb->addSelect('acporigin.id AS origin_aggregator'); + $qb->addGroupBy('origin_aggregator'); } public function applyOn(): string @@ -73,6 +67,10 @@ final class OriginAggregator implements AggregatorInterface return 'Origin'; } + if (null === $value) { + return ''; + } + $o = $this->repository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php index 22eca6f76..26e724526 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ReferrerAggregator implements AggregatorInterface { @@ -40,18 +41,11 @@ final class ReferrerAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpuser', $qb->getAllAliases(), true)) { - $qb->join('acp.user', 'acpuser'); + $qb->leftJoin('acp.user', 'acpuser'); } $qb->addSelect('acpuser.id AS referrer_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('referrer_aggregator'); - } else { - $qb->groupBy('referrer_aggregator'); - } + $qb->addGroupBy('referrer_aggregator'); } public function applyOn(): string @@ -71,6 +65,10 @@ final class ReferrerAggregator implements AggregatorInterface return 'Referrer'; } + if (null === $value) { + return ''; + } + $r = $this->userRepository->find($value); return $this->userRender->renderString($r, []); diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregator.php new file mode 100644 index 000000000..b56f70614 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregator.php @@ -0,0 +1,120 @@ +scopeRepository = $scopeRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $userHistory = 'acp_agg_refscope_user_history'; + $ref = 'acp_agg_refscope_user_history_ref'; + $scopeName = self::SCOPE_KEY; + $dateCalc = 'acp_agg_refscope_user_history_date_calc'; + + $qb + ->leftJoin('acp.userHistories', $userHistory) + ->leftJoin($userHistory . '.user', $ref) + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull($userHistory), + $qb->expr()->andX( + $qb->expr()->lte($userHistory . '.startDate', ':' . $dateCalc), + $qb->expr()->orX( + $qb->expr()->isNull($userHistory . '.endDate'), + $qb->expr()->lt($userHistory . '.endDate', ':' . $dateCalc) + ) + ) + ) + ) + ->setParameter($dateCalc, $data['date_calc']); + + // add groups + $qb + ->addSelect('IDENTITY(' . $ref . '.mainScope) AS ' . $scopeName) + ->addGroupBy($scopeName); + } + + public function applyOn() + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('date_calc', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'data' => new DateTimeImmutable('now'), + 'label' => 'export.aggregator.course.by_user_scope.Computation date for referrer', + 'required' => true, + ]); + } + + public function getLabels($key, array $values, $data) + { + return function ($value) { + if ('_header' === $value) { + return 'export.aggregator.course.by_user_scope.Referrer\'s scope'; + } + + if (null === $value) { + return ''; + } + + $scope = $this->scopeRepository->find($value); + + if (null === $scope) { + throw new LogicException('no scope found with this id: ' . $value); + } + + return $this->translatableStringHelper->localize($scope->getName()); + }; + } + + public function getQueryKeys($data) + { + return [self::SCOPE_KEY]; + } + + public function getTitle() + { + return 'export.aggregator.course.by_user_scope.Group course by referrer\'s scope'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregator.php index 80195bdcc..2f716c92a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregator.php @@ -57,15 +57,7 @@ final class RequestorAggregator implements AggregatorInterface END ) AS requestor_aggregator "); - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('requestor_aggregator'); - } else { - $qb->groupBy('requestor_aggregator'); - } - - // TODO 'order by' does not works ! + $qb->addGroupBy('requestor_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php index 5b2901921..f711cf9ef 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ScopeAggregator implements AggregatorInterface { @@ -40,18 +41,11 @@ final class ScopeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpscope', $qb->getAllAliases(), true)) { - $qb->join('acp.scopes', 'acpscope'); + $qb->leftJoin('acp.scopes', 'acpscope'); } $qb->addSelect('acpscope.id 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 @@ -71,6 +65,10 @@ final class ScopeAggregator implements AggregatorInterface return 'Scope'; } + if (null === $value) { + return ''; + } + $s = $this->scopeRepository->find($value); return $this->translatableStringHelper->localize( @@ -86,6 +84,6 @@ final class ScopeAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group by user scope'; + return 'Group course by scope'; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregator.php index 39ad085e3..dc8d2cfb3 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregator.php @@ -41,18 +41,12 @@ final class SocialActionAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpw', $qb->getAllAliases(), true)) { + // here, we will only see accompanying period linked with a socialAction $qb->join('acp.works', 'acpw'); } - $qb->addSelect('IDENTITY(acpw.socialAction) AS socialaction_aggregator'); // DISTINCT ?? - - $groupby = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('socialaction_aggregator'); - } else { - $qb->groupBy('socialaction_aggregator'); - } + $qb->addSelect('IDENTITY(acpw.socialAction) AS socialaction_aggregator'); + $qb->addGroupBy('socialaction_aggregator'); } public function applyOn(): string @@ -72,6 +66,10 @@ final class SocialActionAggregator implements AggregatorInterface return 'Social action'; } + if (null === $value) { + return ''; + } + $sa = $this->actionRepository->find($value); return $this->actionRender->renderString($sa, []); diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php index ac5ac94c0..e013f94d3 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php @@ -17,6 +17,7 @@ use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository; use Chill\PersonBundle\Templating\Entity\SocialIssueRender; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class SocialIssueAggregator implements AggregatorInterface { @@ -40,18 +41,12 @@ final class SocialIssueAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpsocialissue', $qb->getAllAliases(), true)) { + // we will see accompanying period linked with social issues $qb->join('acp.socialIssues', 'acpsocialissue'); } - + $qb->addSelect('acpsocialissue.id as socialissue_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('socialissue_aggregator'); - } else { - $qb->groupBy('socialissue_aggregator'); - } + $qb->addGroupBy('socialissue_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php index 5c923fdc2..809bb9645 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php @@ -41,15 +41,9 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface public function alterQuery(QueryBuilder $qb, $data) { $qb->addSelect('acp.step AS step_aggregator'); + $qb->addGroupBy('step_aggregator'); - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('step_aggregator'); - } else { - $qb->groupBy('step_aggregator'); - } - + /* // add date in where clause $where = $qb->getDQLPart('where'); @@ -69,6 +63,7 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface $qb->add('where', $where); $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); + */ } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php index b4f5e0071..29420d5c9 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php @@ -39,15 +39,8 @@ class EvaluationTypeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('IDENTITY(eval.evaluation) AS evaluationtype_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('evaluationtype_aggregator'); - } else { - $qb->groupBy('evaluationtype_aggregator'); - } + $qb->addSelect('IDENTITY(workeval.evaluation) AS eval_evaluationtype_aggregator'); + $qb->addGroupBy('eval_evaluationtype_aggregator'); } public function applyOn(): string @@ -67,6 +60,10 @@ class EvaluationTypeAggregator implements AggregatorInterface return 'Evaluation type'; } + if (null === $value) { + return ''; + } + $ev = $this->evaluationRepository->find($value); return $this->translatableStringHelper->localize($ev->getTitle()); @@ -75,7 +72,7 @@ class EvaluationTypeAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['evaluationtype_aggregator']; + return ['eval_evaluationtype_aggregator']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php index 240da475e..d51575d6e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php @@ -16,7 +16,7 @@ use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; use DateTime; use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\Query\Expr\Andx; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -39,39 +39,23 @@ class ChildrenNumberAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('composition', $qb->getAllAliases(), true)) { - $qb->join('household.compositions', 'composition'); + if (!in_array('composition_children', $qb->getAllAliases(), true)) { + $clause = $qb->expr()->andX( + $qb->expr()->lte('composition_children.startDate', ':ondate_composition_children'), + $qb->expr()->orX( + $qb->expr()->gt('composition_children.endDate', ':ondate_composition_children'), + $qb->expr()->isNull('composition_children.endDate') + ) + ); + + $qb->leftJoin('household.compositions', 'composition_children', Expr\Join::WITH, $clause); } - $qb->addSelect('composition.numberOfChildren AS childrennumber_aggregator'); + $qb + ->addSelect('composition_children.numberOfChildren AS childrennumber_aggregator') + ->addGroupBy('childrennumber_aggregator'); - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('childrennumber_aggregator'); - } else { - $qb->groupBy('childrennumber_aggregator'); - } - - // add date in where clause - $where = $qb->getDQLPart('where'); - - $clause = $qb->expr()->andX( - $qb->expr()->lte('composition.startDate', ':ondate'), - $qb->expr()->orX( - $qb->expr()->gt('composition.endDate', ':ondate'), - $qb->expr()->isNull('composition.endDate') - ) - ); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); + $qb->setParameter('ondate_composition_children', $data['on_date'], Types::DATE_MUTABLE); } public function applyOn(): string @@ -88,17 +72,16 @@ class ChildrenNumberAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return 'Number of children'; } - return $this->translator->trans( - 'household_composition.numberOfChildren children in household', - [ - 'numberOfChildren' => $value, - ] - ); + if (null === $value) { + return ''; + } + + return $value; }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php index fdb18e951..6e180934b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php @@ -18,7 +18,7 @@ use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\Household\HouseholdCompositionTypeRepository; use DateTime; use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\Query\Expr\Andx; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use function in_array; @@ -44,39 +44,23 @@ class CompositionAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('composition', $qb->getAllAliases(), true)) { - $qb->join('household.compositions', 'composition'); + if (!in_array('composition_type', $qb->getAllAliases(), true)) { + $clause = $qb->expr()->andX( + $qb->expr()->lte('composition_type.startDate', ':ondate_composition_type'), + $qb->expr()->orX( + $qb->expr()->gt('composition_type.endDate', ':ondate_composition_type'), + $qb->expr()->isNull('composition_type.endDate') + ) + ); + + $qb->leftJoin('household.compositions', 'composition_type', Expr\Join::WITH, $clause); } - $qb->addSelect('IDENTITY(composition.householdCompositionType) AS composition_aggregator'); + $qb + ->addSelect('IDENTITY(composition_type.householdCompositionType) AS composition_aggregator') + ->addGroupBy('composition_aggregator'); - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('composition_aggregator'); - } else { - $qb->groupBy('composition_aggregator'); - } - - // add date in where clause - $where = $qb->getDQLPart('where'); - - $clause = $qb->expr()->andX( - $qb->expr()->lte('composition.startDate', ':ondate'), - $qb->expr()->orX( - $qb->expr()->gt('composition.endDate', ':ondate'), - $qb->expr()->isNull('composition.endDate') - ) - ); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); + $qb->setParameter('ondate_composition_type', $data['on_date'], Types::DATE_MUTABLE); } public function applyOn(): string @@ -98,6 +82,10 @@ class CompositionAggregator implements AggregatorInterface return 'Composition'; } + if (null === $value) { + return ''; + } + $c = $this->typeRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GenderAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GenderAggregator.php index 0b94cbd4f..3cfadc355 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GenderAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GenderAggregator.php @@ -62,6 +62,9 @@ final class GenderAggregator implements AggregatorInterface case Person::BOTH_GENDER: return $this->translator->trans('both'); + case Person::NO_INFORMATION: + return $this->translator->trans('unknown'); + case null: return $this->translator->trans('Not given'); diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php new file mode 100644 index 000000000..feccafe50 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php @@ -0,0 +1,152 @@ +geographicalUnitLayerRepository = $geographicalUnitLayerRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data): void + { + $qb + ->leftJoin('person.householdAddresses', 'person_geog_agg_current_household_address') + ->leftJoin('person_geog_agg_current_household_address.address', 'person_geog_agg_address') + ->leftJoin(GeographicalUnit::class, 'person_geog_agg_geog_unit', Join::WITH, 'ST_CONTAINS(person_geog_agg_geog_unit.geom, person_geog_agg_address.point) = TRUE') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('person_geog_agg_current_household_address'), + $qb->expr()->andX( + $qb->expr()->lte('person_geog_agg_current_household_address.validFrom', ':person_geog_agg_date'), + $qb->expr()->orX( + $qb->expr()->isNull('person_geog_agg_current_household_address.validTo'), + $qb->expr()->gt('person_geog_agg_current_household_address.validTo', ':person_geog_agg_date') + ) + ) + ) + ) + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('person_geog_agg_geog_unit'), + $qb->expr()->in('person_geog_agg_geog_unit.layer', ':person_geog_agg_layers') + ) + ) + ->setParameter('person_geog_agg_date', $data['date_calc']) + ->setParameter('person_geog_agg_layers', $data['level']) + ->addSelect('person_geog_agg_geog_unit.unitName AS geog_unit_name') + ->addSelect('person_geog_agg_geog_unit.unitRefId AS geog_unit_key') + ->addGroupBy('geog_unit_name') + ->addGroupBy('geog_unit_key'); + } + + public function applyOn() + { + return Declarations::PERSON_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('date_calc', ChillDateType::class, [ + 'label' => 'Address valid at this date', + 'required' => true, + 'data' => new DateTimeImmutable('today'), + 'input' => 'datetime_immutable', + ]) + ->add('level', EntityType::class, [ + 'label' => 'Geographical layer', + 'placeholder' => 'Select a geographical layer', + 'class' => GeographicalUnitLayer::class, + 'choices' => $this->geographicalUnitLayerRepository->findAllHavingUnits(), + 'choice_label' => function (GeographicalUnitLayer $item) { + return $this->translatableStringHelper->localize($item->getName()); + }, + ]); + } + + public static function getDefaultAlias(): string + { + return 'person_geog_agg'; + } + + public function getLabels($key, array $values, $data) + { + switch ($key) { + case 'geog_unit_name': + return static function ($value): string { + if ('_header' === $value) { + return 'acp_geog_agg_unitname'; + } + + if (null === $value) { + return ''; + } + + return $value; + }; + + case 'geog_unit_key': + return static function ($value): string { + if ('_header' === $value) { + return 'acp_geog_agg_unitrefid'; + } + + if (null === $value) { + return ''; + } + + return $value; + }; + + default: + throw new LogicException('key not supported'); + } + } + + public function getQueryKeys($data) + { + return ['geog_unit_name', 'geog_unit_key']; + } + + public function getTitle() + { + return 'Group people by geographical unit based on his address'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php index c0b52053d..1f902357e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php @@ -16,12 +16,15 @@ use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\Household\HouseholdMember; +use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\Household\PositionRepository; use DateTime; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; final class HouseholdPositionAggregator implements AggregatorInterface, ExportElementValidatedInterface { @@ -31,8 +34,11 @@ final class HouseholdPositionAggregator implements AggregatorInterface, ExportEl private TranslatorInterface $translator; - public function __construct(TranslatorInterface $translator, TranslatableStringHelper $translatableStringHelper, PositionRepository $positionRepository) - { + public function __construct( + TranslatorInterface $translator, + TranslatableStringHelper $translatableStringHelper, + PositionRepository $positionRepository + ) { $this->translator = $translator; $this->positionRepository = $positionRepository; $this->translatableStringHelper = $translatableStringHelper; @@ -45,28 +51,25 @@ final class HouseholdPositionAggregator implements AggregatorInterface, ExportEl public function alterQuery(QueryBuilder $qb, $data) { - $qb->resetDQLPart('from'); - $qb->from(HouseholdMember::class, 'member'); - - if (!in_array('memberperson', $qb->getAllAliases(), true)) { - $qb->join('member.person', 'memberperson'); + if (!in_array('householdmember', $qb->getAllAliases(), true)) { + $qb->join(HouseholdMember::class, 'householdmember', Expr\Join::WITH, 'householdmember.person = person'); } - if (!in_array('membercenter', $qb->getAllAliases(), true)) { - $qb->join('memberperson.center', 'membercenter'); + if (!in_array('center', $qb->getAllAliases(), true)) { + $qb->join('person.center', 'center'); } $qb->andWhere($qb->expr()->andX( - $qb->expr()->lte('member.startDate', ':date'), + $qb->expr()->lte('householdmember.startDate', ':date'), $qb->expr()->orX( - $qb->expr()->isNull('member.endDate'), - $qb->expr()->gte('member.endDate', ':date') + $qb->expr()->isNull('householdmember.endDate'), + $qb->expr()->gte('householdmember.endDate', ':date') ) )); $qb->setParameter('date', $data['date_position']); - $qb->addSelect('IDENTITY(member.position) AS household_position_aggregator'); + $qb->addSelect('IDENTITY(householdmember.position) AS household_position_aggregator'); $groupBy = $qb->getDQLPart('groupBy'); @@ -79,7 +82,7 @@ final class HouseholdPositionAggregator implements AggregatorInterface, ExportEl public function applyOn() { - return 'person'; + return Declarations::PERSON_TYPE; } public function buildForm(FormBuilderInterface $builder) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php index e799d8dfa..333523487 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php @@ -17,6 +17,7 @@ use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\MaritalStatusRepository; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class MaritalStatusAggregator implements AggregatorInterface { @@ -59,6 +60,7 @@ final class MaritalStatusAggregator implements AggregatorInterface public function buildForm(FormBuilderInterface $builder) { + // no form } public function getLabels($key, array $values, $data) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php index fb64b3e01..38fd40071 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php @@ -17,6 +17,7 @@ use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository; use Chill\PersonBundle\Templating\Entity\SocialActionRender; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ActionTypeAggregator implements AggregatorInterface { @@ -40,18 +41,11 @@ final class ActionTypeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpwsocialaction', $qb->getAllAliases(), true)) { - $qb->join('acpw.socialAction', 'acpwsocialaction'); + $qb->leftJoin('acpw.socialAction', 'acpwsocialaction'); } $qb->addSelect('acpwsocialaction.id as action_type_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('action_type_aggregator'); - } else { - $qb->groupBy('action_type_aggregator'); - } + $qb->addGroupBy('action_type_aggregator'); } public function applyOn() @@ -71,6 +65,10 @@ final class ActionTypeAggregator implements AggregatorInterface return 'Social Action Type'; } + if (null === $value) { + return ''; + } + $sa = $this->socialActionRepository->find($value); return $this->actionRender->renderString($sa, []); diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php index 4310dcac2..353602db9 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php @@ -17,6 +17,7 @@ use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\SocialWork\GoalRepository; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class GoalAggregator implements AggregatorInterface { @@ -38,21 +39,14 @@ final class GoalAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('goal', $qb->getAllAliases(), true)) { - $qb->join('acpw.goals', 'goal'); + $qb->leftJoin('acpw.goals', 'goal'); } - $qb->addSelect('goal.id as goal_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('goal_aggregator'); - } else { - $qb->groupBy('goal_aggregator'); - } + $qb->addSelect('IDENTITY(goal.goal) as acpw_goal_aggregator'); + $qb->addGroupBy('acpw_goal_aggregator'); } - public function applyOn() + public function applyOn(): string { return Declarations::SOCIAL_WORK_ACTION_TYPE; } @@ -69,18 +63,24 @@ final class GoalAggregator implements AggregatorInterface return 'Goal Type'; } + if (null === $value) { + return ''; + } + $g = $this->goalRepository->find($value); - return $this->translatableStringHelper->localize($g->getTitle()); + return $this->translatableStringHelper->localize( + $g->getTitle() + ); }; } - public function getQueryKeys($data) + public function getQueryKeys($data): array { - return ['goal_aggregator']; + return ['acpw_goal_aggregator']; } - public function getTitle() + public function getTitle(): string { return 'Group social work actions by goal'; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php new file mode 100644 index 000000000..97745f576 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php @@ -0,0 +1,120 @@ +resultRepository = $resultRepository; + $this->goalRepository = $goalRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + if (!in_array('goal', $qb->getAllAliases(), true)) { + $qb->leftJoin('acpw.goals', 'goal'); + } + + if (!in_array('goalresult', $qb->getAllAliases(), true)) { + $qb->leftJoin('goal.results', 'goalresult'); + } + + $qb->addSelect('IDENTITY(goal.goal) as goal_aggregator'); + $qb->addSelect('goalresult.id as result_aggregator'); + $qb->addGroupBy('goal_aggregator')->addGroupBy('result_aggregator'); + } + + public function applyOn(): string + { + return Declarations::SOCIAL_WORK_ACTION_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value) use ($key): string { + if (null === $value) { + return ''; + } + + switch ($key) { + case 'goal_aggregator': + if ('_header' === $value) { + return 'Goal Type'; + } + + $g = $this->goalRepository->find($value); + + return $this->translatableStringHelper->localize( + $g->getTitle() + ); + + case 'result_aggregator': + if ('_header' === $value) { + return 'Result Type'; + } + + $r = $this->resultRepository->find($value); + + return $this->translatableStringHelper->localize( + $r->getTitle() + ); + + default: + throw new LogicException(); + } + }; + } + + public function getQueryKeys($data): array + { + return [ + 'goal_aggregator', + 'result_aggregator', + ]; + } + + public function getTitle(): string + { + return 'Group social work actions by goal and result'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php index 4d27aa873..c95f4bf50 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class JobAggregator implements AggregatorInterface { @@ -40,18 +41,11 @@ final class JobAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpwuser', $qb->getAllAliases(), true)) { - $qb->join('acpw.referrers', 'acpwuser'); + $qb->leftJoin('acpw.referrers', 'acpwuser'); } - $qb->addSelect('IDENTITY(acpwuser.userJob) as job_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('job_aggregator'); - } else { - $qb->groupBy('job_aggregator'); - } + $qb->addSelect('IDENTITY(acpwuser.userJob) as job_aggregator') + ->addGroupBy('job_aggregator'); } public function applyOn(): string @@ -71,6 +65,10 @@ final class JobAggregator implements AggregatorInterface return 'Job'; } + if (null === $value) { + return ''; + } + $j = $this->jobRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php index 35e1e218d..33469f719 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ReferrerAggregator implements AggregatorInterface { @@ -40,18 +41,11 @@ final class ReferrerAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpwuser', $qb->getAllAliases(), true)) { - $qb->join('acpw.referrers', 'acpwuser'); + $qb->leftJoin('acpw.referrers', 'acpwuser'); } $qb->addSelect('acpwuser.id AS referrer_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('referrer_aggregator'); - } else { - $qb->groupBy('referrer_aggregator'); - } + $qb->addGroupBy('referrer_aggregator'); } public function applyOn(): string @@ -71,6 +65,10 @@ final class ReferrerAggregator implements AggregatorInterface return 'Referrer'; } + if (null === $value) { + return ''; + } + $r = $this->userRepository->find($value); return $this->userRender->renderString($r, []); diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php index 47391ed69..215c51c3c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php @@ -17,6 +17,7 @@ use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\SocialWork\ResultRepository; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ResultAggregator implements AggregatorInterface { @@ -37,30 +38,15 @@ final class ResultAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('acpwresult', $qb->getAllAliases(), true)) { - $qb->join('acpw.results', 'acpwresult'); + if (!in_array('result', $qb->getAllAliases(), true)) { + $qb->leftJoin('acpw.results', 'result'); } - if (!in_array('goal', $qb->getAllAliases(), true)) { - $qb->join('acpw.goals', 'goal'); - } - - if (!in_array('goalresult', $qb->getAllAliases(), true)) { - $qb->join('goal.results', 'goalresult'); - } - - $qb->addSelect('acpwresult.id, IDENTITY(goal.results) as result_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('result_aggregator'); - } else { - $qb->groupBy('result_aggregator'); - } + $qb->addSelect('result.id AS acpw_result_aggregator'); + $qb->addGroupBy('acpw_result_aggregator'); } - public function applyOn() + public function applyOn(): string { return Declarations::SOCIAL_WORK_ACTION_TYPE; } @@ -77,18 +63,24 @@ final class ResultAggregator implements AggregatorInterface return 'Result Type'; } - $g = $this->resultRepository->find($value); + if (null === $value) { + return ''; + } - return $this->translatableStringHelper->localize($g->getTitle()); + $r = $this->resultRepository->find($value); + + return $this->translatableStringHelper->localize( + $r->getTitle() + ); }; } - public function getQueryKeys($data) + public function getQueryKeys($data): array { - return ['result_aggregator']; + return ['acpw_result_aggregator']; } - public function getTitle() + public function getTitle(): string { return 'Group social work actions by result'; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php index ee479d503..9db70af91 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ScopeAggregator implements AggregatorInterface { @@ -40,18 +41,11 @@ final class ScopeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpwuser', $qb->getAllAliases(), true)) { - $qb->join('acpw.referrers', 'acpwuser'); + $qb->leftJoin('acpw.referrers', 'acpwuser'); } $qb->addSelect('IDENTITY(acpwuser.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 @@ -71,6 +65,10 @@ final class ScopeAggregator implements AggregatorInterface return 'Scope'; } + if (null === $value) { + return ''; + } + $s = $this->scopeRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php index 11a10436e..f3995a335 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php @@ -15,6 +15,8 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; @@ -90,9 +92,24 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('acp'); - $qb->select('COUNT(acp.id) AS export_result'); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers); + + $qb->select('COUNT(DISTINCT acp.id) AS export_result'); return $qb; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php index 5b2ab65eb..544734055 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php @@ -15,12 +15,15 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class CountEvaluation implements ExportInterface, GroupedExportInterface { @@ -88,6 +91,10 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('acp'); if (!in_array('acpw', $qb->getAllAliases(), true)) { @@ -98,7 +105,18 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface $qb->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'); } - $qb->select('COUNT(workeval.id) AS export_result'); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers); + + $qb->select('COUNT(DISTINCT workeval.id) AS export_result'); return $qb; } @@ -112,8 +130,8 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface { return [ Declarations::EVAL_TYPE, - //Declarations::ACP_TYPE, - //Declarations::SOCIAL_WORK_ACTION_TYPE, + Declarations::SOCIAL_WORK_ACTION_TYPE, + Declarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php index f60f66310..2b358634f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php @@ -15,6 +15,9 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Household\HouseholdMember; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\HouseholdVoter; use Doctrine\ORM\EntityManagerInterface; @@ -88,26 +91,29 @@ class CountHousehold implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('acp'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('acppart', $qb->getAllAliases(), true)) { - $qb->join('acp.participations', 'acppart'); - } + $qb = $this->repository + ->createQueryBuilder('acp') + ->join('acp.participations', 'acppart') + // little optimization: we remove joins and make a direct join between participations and household members + ->join(HouseholdMember::class, 'member', Query\Expr\Join::WITH, 'IDENTITY(acppart.person) = IDENTITY(member.person)') + ->join('member.household', 'household'); - if (!in_array('partperson', $qb->getAllAliases(), true)) { - $qb->join('acppart.person', 'partperson'); - } + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers); - if (!in_array('member', $qb->getAllAliases(), true)) { - $qb->join('partperson.householdParticipations', 'member'); - } - - if (!in_array('household', $qb->getAllAliases(), true)) { - $qb->join('member.household', 'household'); - } - - - $qb->select('COUNT(DISTINCT member.household) AS export_result'); + $qb->select('COUNT(DISTINCT household.id) AS export_result'); return $qb; } @@ -121,7 +127,7 @@ class CountHousehold implements ExportInterface, GroupedExportInterface { return [ Declarations::HOUSEHOLD_TYPE, - //Declarations::ACP_TYPE + Declarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php index 18cefc6bb..c411fad2e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Export; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\PersonRepository; use Chill\PersonBundle\Security\Authorization\PersonVoter; @@ -100,8 +101,11 @@ class CountPerson implements ExportInterface, GroupedExportInterface $qb = $this->personRepository->createQueryBuilder('person'); $qb->select('COUNT(person.id) AS export_result') - ->join('person.center', 'center') - ->andWhere('center IN (:authorized_centers)') + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' + ) + ) ->setParameter('authorized_centers', $centers); return $qb; diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php index 800023fc5..df48fe7c6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php @@ -15,12 +15,14 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExportInterface { @@ -88,6 +90,10 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('acp'); if (!in_array('acppart', $qb->getAllAliases(), true)) { @@ -95,10 +101,16 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor } if (!in_array('partperson', $qb->getAllAliases(), true)) { - $qb->join('acppart.person', 'partperson'); + $qb->join('acppart.person', 'person'); } - $qb->select('COUNT(DISTINCT partperson.id) AS export_result'); + $qb->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' + ) + )->setParameter('authorized_centers', $centers); + + $qb->select('COUNT(DISTINCT person.id) AS export_result'); return $qb; } @@ -111,8 +123,8 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor public function supportsModifiers(): array { return [ - Declarations::ACP_TYPE, Declarations::PERSON_TYPE, + Declarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php index 17de7e5ae..caf6cdbc2 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php @@ -15,6 +15,8 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; @@ -90,13 +92,25 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder { - $qb = $this->repository->createQueryBuilder('acp'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('acpw', $qb->getAllAliases(), true)) { - $qb->join('acp.works', 'acpw'); - } + $qb = $this->repository->createQueryBuilder('acp') + ->join('acp.works', 'acpw'); - $qb->select('COUNT(acpw.id) as export_result'); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers); + + $qb->select('COUNT(DISTINCT acpw.id) as export_result'); return $qb; } @@ -110,6 +124,7 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface { return [ Declarations::SOCIAL_WORK_ACTION_TYPE, + Declarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php index 75d757f9e..543be933c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php @@ -24,7 +24,6 @@ use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Contracts\Translation\TranslatorInterface; diff --git a/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php b/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php index b805005be..f59be8399 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php @@ -16,6 +16,8 @@ use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use DateTime; @@ -94,15 +96,26 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('acp'); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers); + $qb ->select('AVG( - ( CASE - WHEN acp.closingDate IS NOT NULL - THEN acp.closingDate - ELSE :force_closingDate - END ) - acp.openingDate + COALESCE(acp.closingDate, :force_closingDate) - acp.openingDate ) AS export_result') ->setParameter('force_closingDate', $data['closingdate']); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php index 6e2fa3288..3ec26183c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php @@ -16,7 +16,6 @@ use Chill\MainBundle\Form\Type\ChillDateType; 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; @@ -29,24 +28,9 @@ class ActiveOneDayBetweenDatesFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); + $clause = "OVERLAPSI (acp.openingDate, acp.closingDate), (:datefrom, :dateto) = 'TRUE'"; - $clause = $qb->expr()->orX( - $qb->expr()->lt('(acp.openingDate + 1)', ':dateto'), - $qb->expr()->andX( - $qb->expr()->lt('acp.openingDate', ':datefrom'), - $qb->expr()->isNull('acp.closingDate') - ), - $qb->expr()->gt('(acp.closingDate - 1)', ':datefrom') - ); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); + $qb->andWhere($clause); $qb->setParameter('datefrom', $data['date_from'], Types::DATE_MUTABLE); $qb->setParameter('dateto', $data['date_to'], Types::DATE_MUTABLE); } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php index 569146614..8e164d668 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php @@ -20,6 +20,7 @@ use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Security\Core\Security; +use function in_array; class CurrentUserScopeFilter implements FilterInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php index c04d5bcd0..3b95261db 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php @@ -19,6 +19,7 @@ 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 EvaluationFilter implements FilterInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php index 8f6748b70..f720896c4 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php @@ -11,29 +11,42 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; +use Chill\MainBundle\Entity\Address; use Chill\MainBundle\Entity\GeographicalUnit; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\MainBundle\Repository\GeographicalUnitRepositoryInterface; +use Chill\MainBundle\Templating\TranslatableStringHelperInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress; use Chill\PersonBundle\Export\Declarations; -use DateTime; -use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\Query\Expr\Andx; +use DateTimeImmutable; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; /** - * e) par zone géographique. - * - * Paramètre: - * * Date - * * Choix unique entre: territoire, epci, canton, commune, secteur d'intervention - * * une fois le premier choix effectué, l'utilisateur choisi parmi les zones (choix multiple) - * - * Le filtre retiendra les parcours localisé dans un des territoires cochés, à la date indiquée en paramètre. + * Filter accompanying period by geographical zone. */ class GeographicalUnitStatFilter implements FilterInterface { + private EntityManagerInterface $em; + + private GeographicalUnitRepositoryInterface $geographicalUnitRepository; + + private TranslatableStringHelperInterface $translatableStringHelper; + + public function __construct( + EntityManagerInterface $em, + GeographicalUnitRepositoryInterface $geographicalUnitRepository, + TranslatableStringHelperInterface $translatableStringHelper + ) { + $this->em = $em; + $this->geographicalUnitRepository = $geographicalUnitRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + public function addRole(): ?string { return null; @@ -41,18 +54,31 @@ class GeographicalUnitStatFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->eq(1, 1); + $subQueryDql = + 'SELECT + 1 + FROM ' . AccompanyingPeriod\AccompanyingPeriodLocationHistory::class . ' acp_geog_filter_location_history + LEFT JOIN ' . PersonHouseholdAddress::class . ' acp_geog_filter_address_person_location + WITH IDENTITY(acp_geog_filter_location_history.personLocation) = IDENTITY(acp_geog_filter_address_person_location.person) + LEFT JOIN ' . Address::class . ' acp_geog_filter_address + WITH COALESCE(IDENTITY(acp_geog_filter_address_person_location.address), IDENTITY(acp_geog_filter_location_history.addressLocation)) = acp_geog_filter_address.id + LEFT JOIN ' . GeographicalUnit::class . ' acp_geog_filter_units WITH ST_CONTAINS(acp_geog_units.geom, acp_geog_filter_address.point) = TRUE + WHERE + (acp_geog_filter_location_history.startDate <= :acp_geog_filter_date AND ( + acp_geog_filter_location_history.endDate IS NULL OR acp_geog_filter_location_history.endDate < :acp_geog_filter_date + )) + AND + (acp_geog_filter_address_person_location.validFrom < :acp_geog_filter_date AND ( + acp_geog_filter_address_person_location.validTo IS NULL OR acp_geog_filter_address_person_location.validTo < :acp_geog_filter_date + )) + AND acp_geog_filter_units IN (:acp_geog_filter_units) + AND acp_geog_filter_location_history.period = acp.id + '; - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('date', $data['date'], Types::DATE_MUTABLE); - $qb->setParameter('loctype', $data['accepted_loctype']); + $qb + ->andWhere($qb->expr()->exists($subQueryDql)) + ->setParameter('acp_geog_filter_date', $data['date_calc']) + ->setParameter('acp_geog_filter_units', $data['units']); } public function applyOn(): string @@ -63,23 +89,40 @@ class GeographicalUnitStatFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { $builder - ->add('date', ChillDateType::class, [ - 'data' => new DateTime(), + ->add('date_calc', ChillDateType::class, [ + 'label' => 'Compute geographical location at date', + 'required' => true, + 'data' => new DateTimeImmutable('today'), + 'input' => 'datetime_immutable', ]) - ->add('accepted_loctype', EntityType::class, [ + ->add('units', EntityType::class, [ + 'label' => 'Geographical unit', + 'placeholder' => 'Select a geographical unit', 'class' => GeographicalUnit::class, - 'choice_label' => static function (GeographicalUnit $u) { - return $u->getUnitName(); + 'choices' => $this->geographicalUnitRepository->findAll(), + 'choice_label' => function (GeographicalUnit $item) { + return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); }, + 'attr' => [ + 'class' => 'select2', + ], 'multiple' => true, - 'expanded' => true, ]); } public function describeAction($data, $format = 'string'): array { - return ['Filtered by geographic unit: only %date%', [ - '%date%' => $data['date']->format('d-m-Y'), + return ['Filtered by geographic unit: computed at %date%, only in %units%', [ + '%date%' => $data['date_calc']->format('d-m-Y'), + '%units' => implode( + ', ', + array_map( + function (GeographicalUnit $item) { + return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); + }, + $data['units'] + ) + ), ]]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php index a782fdb88..814819a57 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php @@ -16,7 +16,6 @@ use Chill\MainBundle\Form\Type\ChillDateType; 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; @@ -29,20 +28,12 @@ class OpenBetweenDatesFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->andX( - $qb->expr()->lt('acp.openingDate', ':datefrom'), - $qb->expr()->gt('acp.closingDate', ':dateto') + $qb->expr()->gte('acp.openingDate', ':datefrom'), + $qb->expr()->lte('acp.openingDate', ':dateto') ); - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); + $qb->andWhere($clause); $qb->setParameter('datefrom', $data['date_from'], Types::DATE_MUTABLE); $qb->setParameter('dateto', $data['date_to'], Types::DATE_MUTABLE); } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php index c0338b42e..299dbafb6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php @@ -20,6 +20,7 @@ use Exception; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; final class RequestorFilter implements FilterInterface { @@ -55,7 +56,6 @@ final class RequestorFilter implements FilterInterface switch ($data['accepted_choices']) { case 'participation': - if (!in_array('acppart', $qb->getAllAliases(), true)) { $qb->join('acp.participations', 'acppart'); } @@ -70,10 +70,6 @@ final class RequestorFilter implements FilterInterface case 'other_person': $expr = $this->em->getExpressionBuilder(); - if (!in_array('acppart', $qb->getAllAliases(), true)) { - $qb->join('acp.participations', 'acppart'); - } - $clause = $expr->andX( $expr->isNotNull('acp.requestorPerson'), $expr->notIn( @@ -85,6 +81,7 @@ final class RequestorFilter implements FilterInterface ->join('acp2.participations', 'acppart2') ->select('identity(acp2.requestorPerson)') ->where($expr->eq('acp2.requestorPerson', 'acppart2.person')) + ->andWhere('acp2.id = acp.id') ->getDQL() ) ); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php index ad232d7c2..598652ff8 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php @@ -21,6 +21,7 @@ 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; class SocialIssueFilter implements FilterInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php index 3d00ed36b..94b55b67c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php @@ -18,7 +18,7 @@ use Chill\PersonBundle\Entity\Household\HouseholdCompositionType; use Chill\PersonBundle\Export\Declarations; use DateTime; use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\Query\Expr\Andx; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; @@ -41,32 +41,24 @@ class CompositionFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('composition', $qb->getAllAliases(), true)) { - $qb->join('household.compositions', 'composition'); + if (!in_array('composition_type_filter', $qb->getAllAliases(), true)) { + $clause = + $qb->expr()->andX( + $qb->expr()->lte('composition_type_filter.startDate', ':ondate_composition_type_filter'), + $qb->expr()->orX( + $qb->expr()->gt('composition_type_filter.endDate', ':ondate_composition_type_filter'), + $qb->expr()->isNull('composition_type_filter.endDate') + ) + ); + + $qb->join('household.compositions', 'composition', Expr\Join::WITH, $clause); } - $where = $qb->getDQLPart('where'); + $whereClause = $qb->expr()->in('composition_type_filter.householdCompositionType', ':compositions'); - $clause = $qb->expr()->andX( - $qb->expr()->in('composition.householdCompositionType', ':compositions'), - $qb->expr()->andX( - $qb->expr()->lte('composition.startDate', ':ondate'), - $qb->expr()->orX( - $qb->expr()->gt('composition.endDate', ':ondate'), - $qb->expr()->isNull('composition.endDate') - ) - ) - ); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); + $qb->andWhere($whereClause); $qb->setParameter('compositions', $data['accepted_composition']); - $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); + $qb->setParameter('ondate_composition_type_filter', $data['on_date'], Types::DATE_MUTABLE); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilter.php index e56782847..9258c9b0a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilter.php @@ -32,8 +32,8 @@ class AccompanyingPeriodClosingFilter extends AbstractAccompanyingPeriodExportEl $this->addJoinAccompanyingPeriod($qb); $clause = $qb->expr()->andX( - $qb->expr()->lte('accompanying_period.closingDate', ':date_to'), - $qb->expr()->gte('accompanying_period.closingDate', ':date_from') + $qb->expr()->lte('acp.closingDate', ':date_to'), + $qb->expr()->gte('acp.closingDate', ':date_from') ); $qb->andWhere($clause); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodFilter.php index 5a7ccb4a3..0c97ac6bf 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodFilter.php @@ -34,12 +34,12 @@ class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement i $clause = $qb->expr()->andX(); $clause->add( - $qb->expr()->lte('accompanying_period.openingDate', ':date_to') + $qb->expr()->lte('acp.openingDate', ':date_to') ); $clause->add( $qb->expr()->orX( - $qb->expr()->gte('accompanying_period.closingDate', ':date_from'), - $qb->expr()->isNull('accompanying_period.closingDate') + $qb->expr()->gte('acp.closingDate', ':date_from'), + $qb->expr()->isNull('acp.closingDate') ) ); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilter.php index 33805d392..00ff885ba 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilter.php @@ -32,8 +32,8 @@ class AccompanyingPeriodOpeningFilter extends AbstractAccompanyingPeriodExportEl $this->addJoinAccompanyingPeriod($qb); $clause = $qb->expr()->andX( - $qb->expr()->lte('accompanying_period.openingDate', ':date_to'), - $qb->expr()->gte('accompanying_period.openingDate', ':date_from') + $qb->expr()->lte('acp.openingDate', ':date_to'), + $qb->expr()->gte('acp.openingDate', ':date_from') ); $qb->andWhere($clause); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php index bada59a8d..394ccddcb 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php @@ -15,7 +15,8 @@ use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; -use DateTime; +use DateInterval; +use DateTimeImmutable; use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\Extension\Core\Type\IntegerType; @@ -34,17 +35,20 @@ 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'] : 200; + $max = null !== $data['max_age'] ? $data['max_age'] : 3000; $calc = $data['date_calc']; + $minDate = $calc->sub(new DateInterval('P' . $max . 'Y')); + $maxDate = $calc->sub(new DateInterval('P' . $min . 'Y')); + $clause = $qb->expr()->andX( $qb->expr()->gte( - 'DATE_DIFF(:calc_date, person.birthdate)/365', - ':min_age' + 'person.birthdate', + ':min_date' ), $qb->expr()->lte( - 'DATE_DIFF(:calc_date, person.birthdate)/365', - ':max_age' + 'person.birthdate', + ':max_date' ) ); @@ -55,9 +59,8 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface } $qb->add('where', $where); - $qb->setParameter('min_age', $min); - $qb->setParameter('max_age', $max); - $qb->setParameter('calc_date', $calc); + $qb->setParameter('min_date', $minDate); + $qb->setParameter('max_date', $maxDate); } public function applyOn() @@ -77,7 +80,8 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface $builder->add('date_calc', ChillDateType::class, [ 'label' => 'Calculate age in relation to this date', - 'data' => new DateTime('now'), + 'data' => new DateTimeImmutable('now'), + 'input' => 'datetime_immutable', ]); } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php index 00d05117b..c764bdbb7 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php @@ -39,10 +39,10 @@ class DeadOrAliveFilter implements FilterInterface $qb->expr()->andX( $qb->expr()->isNull('person.deathdate'), $qb->expr()->lte( - 'person.birthdate', - ':date_calc' - ) - ), + 'person.birthdate', + ':date_calc' + ) + ), $qb->expr()->andX( $qb->expr()->isNotNull('person.deathdate'), $qb->expr()->gt( @@ -108,6 +108,6 @@ class DeadOrAliveFilter implements FilterInterface public function getTitle() { - return 'Filtered by person\'s that are alive or have deceased at a certain date'; + return "Filter by person's that are alive or have deceased at a certain date"; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php new file mode 100644 index 000000000..7463c36a9 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php @@ -0,0 +1,123 @@ +geographicalUnitRepository = $geographicalUnitRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $subQuery = + 'SELECT 1 + FROM ' . PersonHouseholdAddress::class . ' person_filter_geog_person_household_address + JOIN person_filter_geog_person_household_address.address person_filter_geog_address + JOIN ' . GeographicalUnit::class . ' person_filter_geog_unit + WITH ST_CONTAINS(person_filter_geog_unit.geom, person_filter_geog_address.point) = TRUE + WHERE + person_filter_geog_person_household_address.validFrom <= :person_filter_geog_date + AND + (person_filter_geog_person_household_address.validTo IS NULL + OR person_filter_geog_person_household_address.validTo > :person_filter_geog_date) + AND + person_filter_geog_unit IN (:person_filter_geog_units) + AND + person_filter_geog_person_household_address.person = person + '; + + $qb + ->andWhere( + $qb->expr()->exists($subQuery) + ) + ->setParameter('person_filter_geog_date', $data['date_calc']) + ->setParameter('person_filter_geog_units', $data['units']); + } + + public function applyOn() + { + return Declarations::PERSON_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('date_calc', ChillDateType::class, [ + 'label' => 'Compute geographical location at date', + 'required' => true, + 'data' => new DateTimeImmutable('today'), + 'input' => 'datetime_immutable', + ]) + ->add('units', EntityType::class, [ + 'label' => 'Geographical unit', + 'placeholder' => 'Select a geographical unit', + 'class' => GeographicalUnit::class, + 'choices' => $this->geographicalUnitRepository->findAll(), + 'choice_label' => function (GeographicalUnit $item) { + return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); + }, + 'attr' => [ + 'class' => 'select2', + ], + 'multiple' => true, + ]); + } + + public function describeAction($data, $format = 'string') + { + return [ + 'exports.by_person.Filtered by person\'s geographical unit (based on address) computed at datecalc, only units', + [ + 'datecalc' => $data['date_calc']->format('Y-m-d'), + 'units' => implode( + ', ', + array_map( + function (GeographicalUnit $item) { + return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); + }, + $data['units']->toArray() + ) + ), + ], + ]; + } + + public function getTitle(): string + { + return 'Filter by person\'s geographical unit (based on address)'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php index a7c637bda..5d136c0e6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php @@ -11,18 +11,14 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\PersonFilters; -use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Templating\TranslatableStringHelper; -use Chill\PersonBundle\Entity\Household\HouseholdCompositionType; 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; -use Symfony\Component\Form\Extension\Core\Type\DateType; -use Symfony\Contracts\Translation\TranslatorInterface; class MaritalStatusFilter implements FilterInterface { @@ -77,7 +73,7 @@ class MaritalStatusFilter implements FilterInterface ); }, 'multiple' => true, - 'expanded' => true + 'expanded' => true, ]); $builder->add('calc_date', ChillDateType::class, [ diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php index 73c551286..ce632ddd0 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php @@ -14,6 +14,7 @@ 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\Person\ResidentialAddress; use Chill\PersonBundle\Export\Declarations; use Chill\ThirdPartyBundle\Entity\ThirdPartyCategory; use DateTime; @@ -21,6 +22,7 @@ use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class ResidentialAddressAtThirdpartyFilter implements FilterInterface { @@ -38,15 +40,12 @@ class ResidentialAddressAtThirdpartyFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->resetDQLPart('from'); - $qb->from('ChillPersonBundle:Person\ResidentialAddress', 'resaddr'); - - if (!in_array('resaddrperson', $qb->getAllAliases(), true)) { - $qb->join('resaddr.person', 'resaddrperson'); + if (!in_array('resaddr', $qb->getAllAliases(), true)) { + $qb->join(ResidentialAddress::class, 'resaddr', Expr\Join::WITH, 'resaddr.person = person'); } - if (!in_array('resaddrcenter', $qb->getAllAliases(), true)) { - $qb->join('resaddrperson.center', 'resaddrcenter'); + if (!in_array('center', $qb->getAllAliases(), true)) { + $qb->join('person.center', 'center'); } if (!in_array('tparty', $qb->getAllAliases(), true)) { @@ -58,10 +57,17 @@ class ResidentialAddressAtThirdpartyFilter implements FilterInterface } $where = $qb->getDQLPart('where'); + $clause = $qb->expr()->andX( $qb->expr()->isNotNull('resaddr.hostThirdParty'), - $qb->expr()->between(':date', 'resaddr.startDate', 'resaddr.endDate'), - $qb->expr()->in('tpartycat.id', ':type') + $qb->expr()->in('tpartycat.id', ':type'), + $qb->expr()->orX( + $qb->expr()->between(':date', 'resaddr.startDate', 'resaddr.endDate'), + $qb->expr()->andX( + $qb->expr()->lte('resaddr.startDate', ':date'), + $qb->expr()->isNull('resaddr.endDate') + ) + ) ); if ($where instanceof Expr\Andx) { @@ -108,6 +114,6 @@ class ResidentialAddressAtThirdpartyFilter implements FilterInterface public function getTitle() { - return 'Filtered by person\'s who have a residential address located at a thirdparty of type'; + return "Filter by person's who have a residential address located at a thirdparty of type"; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php index 5e78e574e..53e9de406 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php @@ -12,9 +12,14 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\PersonFilters; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\PersonBundle\Entity\Person\ResidentialAddress; use Chill\PersonBundle\Export\Declarations; +use DateTime; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; +use function in_array; class ResidentialAddressAtUserFilter implements FilterInterface { @@ -25,19 +30,26 @@ class ResidentialAddressAtUserFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->resetDQLPart('from'); - $qb->from('ChillPersonBundle:Person\ResidentialAddress', 'resaddr'); - - if (!in_array('resaddrperson', $qb->getAllAliases(), true)) { - $qb->join('resaddr.person', 'resaddrperson'); + if (!in_array('resaddr', $qb->getAllAliases(), true)) { + $qb->join(ResidentialAddress::class, 'resaddr', Expr\Join::WITH, 'resaddr.person = person'); } - if (!in_array('resaddrcenter', $qb->getAllAliases(), true)) { - $qb->join('resaddrperson.center', 'resaddrcenter'); + if (!in_array('center', $qb->getAllAliases(), true)) { + $qb->join('person.center', 'center'); } $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->isNotNull('resaddr.hostPerson'); + + $clause = $qb->expr()->andX( + $qb->expr()->isNotNull('resaddr.hostPerson'), + $qb->expr()->orX( + $qb->expr()->between(':date', 'resaddr.startDate', 'resaddr.endDate'), + $qb->expr()->andX( + $qb->expr()->lte('resaddr.startDate', ':date'), + $qb->expr()->isNull('resaddr.endDate') + ) + ) + ); if ($where instanceof Andx) { $where->add($clause); @@ -45,6 +57,7 @@ class ResidentialAddressAtUserFilter implements FilterInterface $where = $qb->expr()->andX($clause); } + $qb->setParameter('date', $data['date_calc']); $qb->add('where', $where); } @@ -55,16 +68,19 @@ class ResidentialAddressAtUserFilter implements FilterInterface public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder) { - // No form needed unless validity date should be added ( not specified in doc as a parameter ). + $builder->add('date_calc', ChillDateType::class, [ + 'label' => 'Date during which residential address was valid', + 'data' => new DateTime('now'), + ]); } public function describeAction($data, $format = 'string') { - return ['Filtered by person\'s who have a residential address located at another user']; + return ["Filtered by person's who have a residential address located at another user"]; } public function getTitle() { - return 'Filtered by person\'s who have a residential address located at another user'; + return "Filter by person's who have a residential address located at another user"; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php index a93dae54d..f43cc458a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php @@ -20,6 +20,7 @@ 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; class JobFilter implements FilterInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php index e1ddd70fd..a52b9e177 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php @@ -19,6 +19,7 @@ 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 ReferrerFilter implements FilterInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php index ae525a4e5..6efaf9989 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php @@ -20,6 +20,7 @@ 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; class ScopeFilter implements FilterInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php index fc41459ca..41125cfe1 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php @@ -14,34 +14,35 @@ namespace Chill\PersonBundle\Export\Filter\SocialWorkFilters; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\SocialWork\Goal; +use Chill\PersonBundle\Entity\SocialWork\Result; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Export\Declarations; -use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository; use Chill\PersonBundle\Templating\Entity\SocialActionRender; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; -use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\CallbackTransformer; +use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormEvent; -use Symfony\Component\Form\FormEvents; -use Symfony\Component\Form\FormInterface; +use function count; +use function in_array; class SocialWorkTypeFilter implements FilterInterface { - private SocialActionRender $socialActionRender; + private EntityManagerInterface $em; - private SocialActionRepository $socialActionRepository; + private SocialActionRender $socialActionRender; private TranslatableStringHelper $translatableStringHelper; public function __construct( SocialActionRender $socialActionRender, TranslatableStringHelper $translatableStringHelper, - SocialActionRepository $socialActionRepository + EntityManagerInterface $em ) { $this->socialActionRender = $socialActionRender; $this->translatableStringHelper = $translatableStringHelper; - $this->socialActionRepository = $socialActionRepository; + $this->em = $em; } public function addRole(): ?string @@ -52,16 +53,44 @@ class SocialWorkTypeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('r', ':referrers'); - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); + if (count($data['actionType']) > 0) { + $clause = $qb->expr()->in('acpw.socialAction', ':actionType'); + + if ($where instanceof Andx) { + $where->add($clause); + } else { + $where = $qb->expr()->andX($clause); + } + + $qb->setParameter('actionType', $data['actionType']); + } + + if (count($data['goal']) > 0) { + if (!in_array('goal', $qb->getAllAliases(), true)) { + $qb->join('acpw.goals', 'goal'); + } + + $where->add( + $qb->expr()->in('goal.id', ':goals') + ); + + $qb->setParameter('goals', $data['goal']); + } + + if (count($data['result']) > 0) { + if (!in_array('result', $qb->getAllAliases(), true)) { + $qb->join('acpw.results', 'result'); + } + + $where->add( + $qb->expr()->in('result.id', ':results') + ); + + $qb->setParameter('results', $data['result']); } $qb->add('where', $where); - $qb->setParameter('referrers', $data['referrers']); } public function applyOn(): string @@ -71,74 +100,87 @@ class SocialWorkTypeFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $socialActions = $this->socialActionRepository->findAll(); - - $builder->add('actionType', ChoiceType::class, [ - 'choices' => $socialActions, - 'choice_label' => function (SocialAction $sa) { - return $this->socialActionRender->renderString($sa, []); - }, - 'multiple' => true, - 'expanded' => true, - ]); - - /* - $refreshGoals = function (FormInterface $form, SocialAction $actionType = null) { - - $goals = null === $actionType ? [] : $actionType->getGoals(); - - $form->add('goal', ChoiceType::class, [ - 'placeholder' => '', - 'choices' => $goals, - 'choice_label' => function (Goal $g) { - return $this->translatableStringHelper->localize($g->getTitle()); - }, - ]); - }; - - $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($refreshGoals) { - $data = $event->getData(); - dump($data); - - $refreshGoals($event->getForm(), $data); - }); - - $builder->get('actionType')->addEventListener( - FormEvents::POST_SUBMIT, - function (FormEvent $event) use ($refreshGoals) { - $actionType = $event->getForm()->getData(); - dump($actionType); - $refreshGoals($event->getForm()->getParent(), $actionType); - } - ); - */ + $builder + ->add('actionType', HiddenType::class) + ->get('actionType') + ->addModelTransformer( + $this->iterableToIdTransformer(SocialAction::class) + ); + $builder + ->add('goal', HiddenType::class) + ->get('goal') + ->addModelTransformer( + $this->iterableToIdTransformer(Goal::class) + ); + $builder + ->add('result', HiddenType::class) + ->get('result') + ->addModelTransformer( + $this->iterableToIdTransformer(Result::class) + ); } public function describeAction($data, $format = 'string'): array { $actionTypes = []; - $objectives = []; + $goals = []; $results = []; foreach ($data['actionType'] as $at) { - $actionTypes[] = $at->getTitle(); + $actionTypes[] = $this->translatableStringHelper->localize( + $at->getTitle() + ); } - foreach ($data['objectives'] as $o) { - $objectives[] = $o->getTitle(); + foreach ($data['goal'] as $g) { + $goals[] = $this->translatableStringHelper->localize( + $g->getTitle() + ); } - foreach ($data['results'] as $r) { - $results[] = $r->getTitle(); + foreach ($data['result'] as $r) { + $results[] = $this->translatableStringHelper->localize( + $r->getTitle() + ); } - return ['Filtered by referrers: only %actionTypes%', [ - '%actionTypes%' => implode(', ou ', $actionTypes), + return ['Filtered actions by type, goals and results: %selected%', [ + '%selected%' => implode(', ', array_merge($actionTypes, $goals, $results)), ]]; } public function getTitle(): string { - return 'Filter by type of action, objectives and results'; + return 'Filter by type of action, goals and results'; + } + + private function iterableToIdTransformer(string $entity): CallbackTransformer + { + return new CallbackTransformer( + static function (?iterable $asIterable): string { + if (null === $asIterable) { + return ''; + } + $ids = []; + + foreach ($asIterable as $value) { + $ids[] = $value->getId(); + } + + return implode(',', $ids); + }, + function (?string $asString) use ($entity): array { + if (null === $asString) { + return []; + } + + return array_map( + fn (string $id) => $this->em + ->getRepository($entity) + ->findOneBy(['id' => (int) $id]), + explode(',', $asString) + ); + } + ); } } diff --git a/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php b/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php index bbe82b59f..c1261754c 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\UrlType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -40,6 +41,10 @@ class EvaluationType extends AbstractType ->add('title', TranslatableStringFormType::class, [ 'label' => 'Nom', ]) + ->add('url', UrlType::class, [ + 'label' => 'evaluation.url', + 'required' => false, + ]) ->add('delay', DateIntervalType::class, [ 'label' => 'evaluation.delay', 'required' => false, diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php index ea5b1feff..5cf394df6 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php @@ -16,7 +16,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query\ResultSetMappingBuilder; -final class ClosingMotiveRepository +final class ClosingMotiveRepository implements ClosingMotiveRepositoryInterface { private EntityManagerInterface $entityManager; @@ -28,6 +28,32 @@ final class ClosingMotiveRepository $this->repository = $entityManager->getRepository(ClosingMotive::class); } + public function find($id): ?ClosingMotive + { + return $this->repository->find($id); + } + + /** + * @return array|ClosingMotive[] + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array|ClosingMotive[] + */ + public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria): ?ClosingMotive + { + return $this->findOneBy($criteria); + } + /** * @return mixed */ @@ -54,4 +80,9 @@ final class ClosingMotiveRepository ->createNativeQuery($sql, $rsm) ->getResult(); } + + public function getClassName(): string + { + return ClosingMotive::class; + } } diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepositoryInterface.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepositoryInterface.php new file mode 100644 index 000000000..73bc3ff5d --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepositoryInterface.php @@ -0,0 +1,22 @@ +repository = $em->getRepository($this->getClassName()); + } + + public function find($id): ?PersonCenterHistory + { + return $this->repository->find($id); + } + + /** + * @return array|PersonCenterHistory[] + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array|PersonCenterHistory[] + */ + public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria): ?PersonCenterHistory + { + return $this->repository->findOneBy($criteria); + } + + public function getClassName(): string + { + return PersonCenterHistory::class; + } +} diff --git a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php index d5dc3cd7d..f7b361436 100644 --- a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php @@ -15,7 +15,7 @@ use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Repository\CountryRepository; use Chill\MainBundle\Search\ParsingException; use Chill\MainBundle\Search\SearchApiQuery; -use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Security\Authorization\PersonVoter; use DateTimeInterface; @@ -34,7 +34,7 @@ use function implode; final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterface { - private AuthorizationHelper $authorizationHelper; + private AuthorizationHelperInterface $authorizationHelper; private CountryRepository $countryRepository; @@ -46,7 +46,7 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac Security $security, EntityManagerInterface $em, CountryRepository $countryRepository, - AuthorizationHelper $authorizationHelper + AuthorizationHelperInterface $authorizationHelper ) { $this->security = $security; $this->em = $em; @@ -310,9 +310,10 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac } return $query + ->setFromClause($query->getFromClause() . ' JOIN view_chill_person_person_center_history_current vcppchc ON vcppchc.person_id = person.id', $query->getFromParams()) ->andWhereClause( strtr( - 'person.center_id IN ({{ center_ids }})', + 'vcppchc.center_id IN ({{ center_ids }})', [ '{{ center_ids }}' => implode( ', ', diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/App.vue new file mode 100644 index 000000000..d4d1be1b6 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/App.vue @@ -0,0 +1,381 @@ + + + + + \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/api.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/api.js new file mode 100644 index 000000000..afcece4f4 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/api.js @@ -0,0 +1,41 @@ +import { fetchResults } from 'ChillMainAssets/lib/api/apiMethods'; + +const getSocialActions = () => fetchResults( + '/api/1.0/person/social/social-action.json', { + item_per_page: 200 + } +); + +const getGoalByAction = (id) => { + let url = `/api/1.0/person/social-work/goal/by-social-action/${id}.json`; + return fetch(url) + .then(response => { + if (response.ok) { return response.json(); } + throw Error('Error with request resource response'); + }); +}; + +const getResultByAction = (id) => { + let url = `/api/1.0/person/social-work/result/by-social-action/${id}.json`; + return fetch(url) + .then(response => { + if (response.ok) { return response.json(); } + throw Error('Error with request resource response'); + }); +}; + +const getResultByGoal = (id) => { + let url = `/api/1.0/person/social-work/result/by-goal/${id}.json`; + return fetch(url) + .then(response => { + if (response.ok) { return response.json(); } + throw Error('Error with request resource response'); + }); +}; + +export { + getSocialActions, + getGoalByAction, + getResultByAction, + getResultByGoal, +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/index.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/index.js new file mode 100644 index 000000000..d838fb0be --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/index.js @@ -0,0 +1,13 @@ +import { createApp } from "vue"; +import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'; +import App from './App.vue'; + +const i18n = _createI18n({}); + +const app = createApp({ + template: ``, +}) +.use(i18n) +.component('app', App) +.mount('#export_export') +; \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/api.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/api.js index dc7f2632c..8d04ec79b 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/api.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/api.js @@ -5,8 +5,10 @@ import {makeFetch} from 'ChillMainAssets/lib/api/apiMethods.ts'; * @function getFetch * @param url * @returns {Promise} + * @deprecated use makeFetch instead */ const getFetch = (url) => { + console.error('deprecated method'); return makeFetch('GET', url, null) } @@ -51,8 +53,10 @@ const getHouseholdByPerson = (person) => { if (person.current_household_id === null) { throw 'Currently the person has not household!' } - return getFetch( - `/api/1.0/person/household/${person.current_household_id}.json`) + return makeFetch( + 'GET', + `/api/1.0/person/household/${person.current_household_id}.json` + ); } /** @@ -62,8 +66,10 @@ const getHouseholdByPerson = (person) => { */ const getCoursesByPerson = (person) => { //console.log('getCoursesByPerson', person._id) - return getFetch( - `/api/1.0/person/accompanying-course/by-person/${person._id}.json`) + return makeFetch( + 'GET', + `/api/1.0/person/accompanying-course/by-person/${person._id}.json` + ); } /** @@ -73,8 +79,10 @@ const getCoursesByPerson = (person) => { */ const getRelationshipsByPerson = (person) => { //console.log('getRelationshipsByPerson', person.id) - return getFetch( - `/api/1.0/relations/relationship/by-person/${person._id}.json`) + return makeFetch( + 'GET', + `/api/1.0/relations/relationship/by-person/${person._id}.json` + ); } /** @@ -82,7 +90,7 @@ const getRelationshipsByPerson = (person) => { * @returns {Promise} */ const getRelationsList = () => { - return getFetch(`/api/1.0/relations/relation.json`) + return makeFetch('GET', `/api/1.0/relations/relation.json`); } /** diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Household/summary.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Household/summary.html.twig index a8ff245d7..bdf06c0bd 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Household/summary.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Household/summary.html.twig @@ -180,7 +180,7 @@ {% if members|length > 0 %}
{% for m in members %} - {% if m.position.shareHousehold %} + {% if m.position is null or m.position.shareHousehold %} {% include '@ChillPerson/Household/_render_member.html.twig' with { 'member': m, 'customButtons': { 'before': _self.customButtons(m, household) } diff --git a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php index 8da2036d7..b9c9fb219 100644 --- a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php +++ b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Security\Authorization; +use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Security\Authorization\AbstractChillVoter; use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface; @@ -119,6 +120,7 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH ->addCheckFor(null, [self::CREATE, self::REASSIGN_BULK]) ->addCheckFor(AccompanyingPeriod::class, [self::TOGGLE_CONFIDENTIAL, ...self::ALL]) ->addCheckFor(Person::class, [self::SEE, self::CREATE]) + ->addCheckFor(Center::class, [self::STATS]) ->build(); } diff --git a/src/Bundle/ChillPersonBundle/Security/Authorization/HouseholdVoter.php b/src/Bundle/ChillPersonBundle/Security/Authorization/HouseholdVoter.php index ca956db63..900c948c1 100644 --- a/src/Bundle/ChillPersonBundle/Security/Authorization/HouseholdVoter.php +++ b/src/Bundle/ChillPersonBundle/Security/Authorization/HouseholdVoter.php @@ -11,6 +11,10 @@ declare(strict_types=1); namespace Chill\PersonBundle\Security\Authorization; +use Chill\MainBundle\Entity\Center; +use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface; +use Chill\MainBundle\Security\Authorization\VoterHelperInterface; +use Chill\MainBundle\Security\ProvideRoleHierarchyInterface; use Chill\PersonBundle\Entity\Household\Household; use Chill\PersonBundle\Entity\Household\HouseholdMember; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -19,7 +23,7 @@ use Symfony\Component\Security\Core\Security; use UnexpectedValueException; use function in_array; -class HouseholdVoter extends Voter +class HouseholdVoter extends Voter implements ProvideRoleHierarchyInterface { public const EDIT = 'CHILL_PERSON_HOUSEHOLD_EDIT'; @@ -36,17 +40,39 @@ class HouseholdVoter extends Voter self::EDIT, self::SEE, ]; + private VoterHelperInterface $helper; + private Security $security; - public function __construct(Security $security) + public function __construct(Security $security, VoterHelperFactoryInterface $voterHelperFactory) { $this->security = $security; + $this->helper = $voterHelperFactory + ->generate(self::class) + ->addCheckFor(Center::class, [self::STATS]) + ->build(); + } + + public function getRoles(): array + { + return [self::STATS]; + } + + public function getRolesWithHierarchy(): array + { + return ['Person' => $this->getRoles()]; + } + + public function getRolesWithoutScope(): array + { + return $this->getRoles(); } protected function supports($attribute, $subject) { - return $subject instanceof Household - && in_array($attribute, self::ALL, true); + return ($subject instanceof Household + && in_array($attribute, self::ALL, true)) + || $this->helper->supports($attribute, $subject); } protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool @@ -58,6 +84,9 @@ class HouseholdVoter extends Voter case self::EDIT: return $this->checkAssociatedMembersRole($subject, PersonVoter::UPDATE); + case self::STATS: + return $this->voteOnAttribute($attribute, $subject, $token); + default: throw new UnexpectedValueException('attribute not supported'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php index 18149c824..4633c3fcb 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Entity; +use Chill\MainBundle\Entity\Center; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Household\Household; use Chill\PersonBundle\Entity\Household\HouseholdMember; @@ -191,4 +192,29 @@ final class PersonTest extends \PHPUnit\Framework\TestCase $this->assertEquals($r['result'], Person::ERROR_ADDIND_PERIOD_AFTER_AN_OPEN_PERIOD); } + + public function testSetCenter() + { + $person = new Person(); + $centerA = new Center(); + $centerB = new Center(); + + $this->assertCount(0, $person->getCenterHistory()); + $this->assertNull($person->getCenter()); + $this->assertNull($person->getCenterCurrent()); + + $person->setCenter($centerA); + + $this->assertCount(1, $person->getCenterHistory()); + $this->assertSame($centerA, $person->getCenter()); + $this->assertInstanceOf(Person\PersonCenterCurrent::class, $person->getCenterCurrent()); + $this->assertSame($centerA, $person->getCenterCurrent()->getCenter()); + + $person->setCenter($centerB); + + $this->assertCount(2, $person->getCenterHistory()); + $this->assertSame($centerB, $person->getCenter()); + $this->assertInstanceOf(Person\PersonCenterCurrent::class, $person->getCenterCurrent()); + $this->assertSame($centerB, $person->getCenterCurrent()->getCenter()); + } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregatorTest.php new file mode 100644 index 000000000..84487b0b6 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregatorTest.php @@ -0,0 +1,61 @@ +aggregator = self::$container->get('chill.person.export.aggregator_administrative_location'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.administrativeLocation', 'acploc'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregatorTest.php new file mode 100644 index 000000000..2aa4e3e3e --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregatorTest.php @@ -0,0 +1,61 @@ +aggregator = self::$container->get('chill.person.export.aggregator_closingmotive'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.closingMotive', 'acpmotive'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregatorTest.php new file mode 100644 index 000000000..a96b14d28 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregatorTest.php @@ -0,0 +1,60 @@ +aggregator = self::$container->get('chill.person.export.aggregator_confidential'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php new file mode 100644 index 000000000..616ee83c5 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php @@ -0,0 +1,60 @@ +aggregator = self::$container->get('chill.person.export.aggregator_duration'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregatorTest.php new file mode 100644 index 000000000..c4a2fc74e --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregatorTest.php @@ -0,0 +1,60 @@ +aggregator = self::$container->get('chill.person.export.aggregator_emergency'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregatorTest.php new file mode 100644 index 000000000..cd5b1f4c7 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregatorTest.php @@ -0,0 +1,62 @@ +aggregator = self::$container->get('chill.person.export.aggregator_evaluation'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php new file mode 100644 index 000000000..8808961bf --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php @@ -0,0 +1,60 @@ +aggregator = self::$container->get('chill.person.export.aggregator_geographicalunitstat'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregatorTest.php new file mode 100644 index 000000000..e4c976cc9 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregatorTest.php @@ -0,0 +1,60 @@ +aggregator = self::$container->get('chill.person.export.aggregator_intensity'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php new file mode 100644 index 000000000..f1b980640 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php @@ -0,0 +1,61 @@ +aggregator = self::$container->get('chill.person.export.aggregator_referrer_job'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.job', 'acpjob'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregatorTest.php new file mode 100644 index 000000000..a2126c47c --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregatorTest.php @@ -0,0 +1,61 @@ +aggregator = self::$container->get('chill.person.export.aggregator_origin'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.origin', 'acporigin'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php new file mode 100644 index 000000000..c5c04374f --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php @@ -0,0 +1,61 @@ +aggregator = self::$container->get('chill.person.export.aggregator_referrer'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.user', 'acpuser'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregatorTest.php new file mode 100644 index 000000000..f91fedad9 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregatorTest.php @@ -0,0 +1,73 @@ +prophesize(TranslatableStringHelperInterface::class); + $translatableStringHelper->localize(Argument::type('array'))->willReturn('localized'); + + $scopeRepository = $this->prophesize(ScopeRepositoryInterface::class); + $scopeRepository->find(Argument::type('int'))->willReturn( + (new Scope())->setName(['fr' => 'scope']) + ); + + return new ReferrerScopeAggregator( + $scopeRepository->reveal(), + $translatableStringHelper->reveal() + ); + } + + public function getFormData() + { + return [ + [ + 'date_calc' => new DateTimeImmutable('now'), + ], + ]; + } + + public function getQueryBuilders() + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.scopes', 'acpscope'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregatorTest.php new file mode 100644 index 000000000..84924e262 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregatorTest.php @@ -0,0 +1,61 @@ +aggregator = self::$container->get('chill.person.export.aggregator_requestor'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.participations', 'acppart'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregatorTest.php new file mode 100644 index 000000000..066ff2bc5 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregatorTest.php @@ -0,0 +1,61 @@ +aggregator = self::$container->get('chill.person.export.aggregator_referrer_scope'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.scopes', 'acpscope'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregatorTest.php new file mode 100644 index 000000000..66b59e5f9 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregatorTest.php @@ -0,0 +1,61 @@ +aggregator = self::$container->get('chill.person.export.aggregator_socialaction'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregatorTest.php new file mode 100644 index 000000000..360937cac --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregatorTest.php @@ -0,0 +1,61 @@ +aggregator = self::$container->get('chill.person.export.aggregator_socialissue'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.socialIssues', 'acpsocialissue'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/StepAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/StepAggregatorTest.php new file mode 100644 index 000000000..f038c8a1c --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/StepAggregatorTest.php @@ -0,0 +1,63 @@ +aggregator = self::$container->get('chill.person.export.aggregator_step'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [ + 'on_date' => DateTime::createFromFormat('Y-m-d', '2022-01-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregatorTest.php new file mode 100644 index 000000000..624f03ef5 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregatorTest.php @@ -0,0 +1,62 @@ +aggregator = self::$container->get('chill.person.export.aggregator_evaluationtype'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregatorTest.php new file mode 100644 index 000000000..429cf825b --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregatorTest.php @@ -0,0 +1,68 @@ +aggregator = self::$container->get('chill.person.export.aggregator_household_childrennumber'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [ + 'on_date' => DateTime::createFromFormat('Y-m-d', '2022-07-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.participations', 'acppart') + ->join('acppart.person', 'partperson') + ->join('partperson.householdParticipations', 'member') + ->join('member.household', 'household') + ->join('household.compositions', 'composition'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/CompositionAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/CompositionAggregatorTest.php new file mode 100644 index 000000000..5e017144e --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/CompositionAggregatorTest.php @@ -0,0 +1,68 @@ +aggregator = self::$container->get('chill.person.export.aggregator_household_composition'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [ + 'on_date' => DateTime::createFromFormat('Y-m-d', '2022-07-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.participations', 'acppart') + ->join('acppart.person', 'partperson') + ->join('partperson.householdParticipations', 'member') + ->join('member.household', 'household') + ->join('household.compositions', 'composition'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/AgeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/AgeAggregatorTest.php index a0d869462..031ded74c 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/AgeAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/AgeAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\PersonAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Export\Aggregator\PersonAggregators\AgeAggregator; use DateTime; /** @@ -20,10 +21,7 @@ use DateTime; */ final class AgeAggregatorTest extends AbstractAggregatorTest { - /** - * @var \Chill\PersonBundle\Export\Aggregator\PersonAggregators\AgeAggregator - */ - private $aggregator; + private AgeAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/CountryOfBirthAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/CountryOfBirthAggregatorTest.php new file mode 100644 index 000000000..a22135ec5 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/CountryOfBirthAggregatorTest.php @@ -0,0 +1,62 @@ +aggregator = self::$container->get('chill.person.export.aggregator_country_of_birth'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + ['group_by_level' => 'continent'], + ['group_by_level' => 'country'], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(person.id)') + ->from(Person::class, 'person') + ->leftJoin('person.countryOfBirth', 'countryOfBirth'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/GenderAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/GenderAggregatorTest.php index 6389ac33d..16bca62d9 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/GenderAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/GenderAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\PersonAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Export\Aggregator\PersonAggregators\GenderAggregator; /** * @internal @@ -19,10 +20,7 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest; */ final class GenderAggregatorTest extends AbstractAggregatorTest { - /** - * @var \Chill\PersonBundle\Export\Aggregator\PersonAggregators\GenderAggregator - */ - private $aggregator; + private GenderAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/HouseholdPositionAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/HouseholdPositionAggregatorTest.php new file mode 100644 index 000000000..036b00302 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/HouseholdPositionAggregatorTest.php @@ -0,0 +1,67 @@ +aggregator = self::$container->get('chill.person.export.aggregator_household_position'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [ + 'date_position' => DateTime::createFromFormat('Y-m-d', '2022-07-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(person.id)') + ->from(Person::class, 'person') + ->join(HouseholdMember::class, 'householdmember', Expr\Join::WITH, 'householdmember.person = person') + ->join('person.center', 'center'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/MaritalStatusAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/MaritalStatusAggregatorTest.php new file mode 100644 index 000000000..a24210fb4 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/MaritalStatusAggregatorTest.php @@ -0,0 +1,61 @@ +aggregator = self::$container->get('chill.person.export.aggregator_marital_status'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(person.id)') + ->from(Person::class, 'person') + ->join('person.maritalStatus', 'personmarital'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/NationalityAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/NationalityAggregatorTest.php index c093d96cc..7de9587eb 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/NationalityAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/NationalityAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\PersonAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Export\Aggregator\PersonAggregators\NationalityAggregator; /** * @internal @@ -19,10 +20,7 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest; */ final class NationalityAggregatorTest extends AbstractAggregatorTest { - /** - * @var \Chill\PersonBundle\Export\Aggregator\PersonAggregators\NationalityAggregator - */ - private $aggregator; + private NationalityAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregatorTest.php index ab0492d11..3ba391d60 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\ActionTypeAggregator; /** @@ -34,14 +35,14 @@ final class ActionTypeAggregatorTest extends AbstractAggregatorTest return $this->aggregator; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getQueryBuilders() + public function getQueryBuilders(): array { if (null === self::$kernel) { self::bootKernel(); @@ -53,7 +54,7 @@ final class ActionTypeAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acpw.id)') - ->from('ChillPersonBundle:AccompanyingPeriodWork', 'acpw'), + ->from(AccompanyingPeriodWork::class, 'acpw'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalAggregatorTest.php index ef5bd5097..deef482ee 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\GoalAggregator; /** @@ -34,14 +35,14 @@ final class GoalAggregatorTest extends AbstractAggregatorTest return $this->aggregator; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getQueryBuilders() + public function getQueryBuilders(): array { if (null === self::$kernel) { self::bootKernel(); @@ -53,7 +54,7 @@ final class GoalAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acpw.id)') - ->from('ChillPersonBundle:AccompanyingPeriodWork', 'acpw'), + ->from(AccompanyingPeriodWork::class, 'acpw'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalResultAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalResultAggregatorTest.php new file mode 100644 index 000000000..429833569 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalResultAggregatorTest.php @@ -0,0 +1,63 @@ +aggregator = self::$container->get('chill.person.export.aggregator_goalresult'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.goals', 'goal') + ->join('goal.results', 'goalresult'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php new file mode 100644 index 000000000..e8ed02aa1 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php @@ -0,0 +1,62 @@ +aggregator = self::$container->get('chill.person.export.aggregator_treatingagent_job'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.referrers', 'acpwuser'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php index 446cf1e37..060655eb3 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\ReferrerAggregator; /** @@ -34,14 +35,14 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest return $this->aggregator; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getQueryBuilders() + public function getQueryBuilders(): array { if (null === self::$kernel) { self::bootKernel(); @@ -53,7 +54,7 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acpw.id)') - ->from('ChillPersonBundle:AccompanyingPeriodWork', 'acpw'), + ->from(AccompanyingPeriodWork::class, 'acpw'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ResultAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ResultAggregatorTest.php index a34a16a8a..cca033b5b 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ResultAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ResultAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\ResultAggregator; /** @@ -34,14 +35,14 @@ final class ResultAggregatorTest extends AbstractAggregatorTest return $this->aggregator; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getQueryBuilders() + public function getQueryBuilders(): array { if (null === self::$kernel) { self::bootKernel(); @@ -53,7 +54,7 @@ final class ResultAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acpw.id)') - ->from('ChillPersonBundle:AccompanyingPeriodWork', 'acpw'), + ->from(AccompanyingPeriodWork::class, 'acpw'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php new file mode 100644 index 000000000..95b3a784c --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php @@ -0,0 +1,62 @@ +aggregator = self::$container->get('chill.person.export.aggregator_treatingagent_scope'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.referrers', 'acpwuser'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListPersonTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListPersonTest.php index f3408a064..d3564c0c6 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListPersonTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListPersonTest.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Export\Export; use Chill\MainBundle\Test\Export\AbstractExportTest; use Chill\PersonBundle\Export\Export\ListPerson; use DateTime; +use Prophecy\PhpUnit\ProphecyTrait; /** * Test the export "ListPerson". @@ -23,10 +24,9 @@ use DateTime; */ final class ListPersonTest extends AbstractExportTest { - /** - * @var ListPerson - */ - private $export; + use ProphecyTrait; + + private ListPerson $export; protected function setUp(): void { @@ -34,10 +34,9 @@ final class ListPersonTest extends AbstractExportTest $this->export = self::$container->get('chill.person.export.list_person'); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); self::$container->get('request_stack') @@ -49,7 +48,7 @@ final class ListPersonTest extends AbstractExportTest return $this->export; } - public function getFormData() + public function getFormData(): array { return [ ['fields' => ['id', 'firstName', 'lastName']], @@ -65,7 +64,7 @@ final class ListPersonTest extends AbstractExportTest ]; } - public function getModifiersCombination() + public function getModifiersCombination(): array { return [ ['person'], diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilterTest.php index 3a8160cd2..ac94a9c33 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ActiveOnDateFilter; use DateTime; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal @@ -30,12 +29,6 @@ final class ActiveOnDateFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_activeondate'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilterTest.php index 30957879e..5c25abefc 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ActiveOneDayBetweenDatesFilter; use DateTime; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal @@ -30,12 +29,6 @@ final class ActiveOneDayBetweenDatesFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_activeonedaybetweendates'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilterTest.php index c09c50fbc..df301537c 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilterTest.php @@ -9,13 +9,12 @@ declare(strict_types=1); -namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters;; +namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\AdministrativeLocationFilter; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal @@ -30,12 +29,6 @@ final class AdministrativeLocationFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_administrative_location'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilterTest.php index 8ff47ac6c..02e0de4d2 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilterTest.php @@ -28,12 +28,6 @@ final class ClosingMotiveFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_closingmotive'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ConfidentialFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ConfidentialFilterTest.php index db469bd49..c3e185e1f 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ConfidentialFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ConfidentialFilterTest.php @@ -27,12 +27,6 @@ final class ConfidentialFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_confidential'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php index 77881a218..0507333ab 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\UserJobFilter; +use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserJobFilter; use Doctrine\ORM\EntityManagerInterface; /** @@ -21,18 +21,12 @@ use Doctrine\ORM\EntityManagerInterface; */ final class CurrentUserJobFilterTest extends AbstractFilterTest { - private UserJobFilter $filter; + private CurrentUserJobFilter $filter; protected function setUp(): void { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_userjob'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php index 1ea4d40b0..12c3ce96f 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\UserScopeFilter; +use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserScopeFilter; use Doctrine\ORM\EntityManagerInterface; /** @@ -21,18 +21,12 @@ use Doctrine\ORM\EntityManagerInterface; */ final class CurrentUserScopeFilterTest extends AbstractFilterTest { - private UserScopeFilter $filter; + private CurrentUserScopeFilter $filter; protected function setUp(): void { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_userscope'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EmergencyFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EmergencyFilterTest.php index 5a7c1228f..7f5fe9f32 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EmergencyFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EmergencyFilterTest.php @@ -27,12 +27,6 @@ final class EmergencyFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_emergency'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EvaluationFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EvaluationFilterTest.php index c3c25870d..b0fdb24b9 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EvaluationFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EvaluationFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\SocialWork\Evaluation; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\EvaluationFilter; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal @@ -30,12 +29,6 @@ final class EvaluationFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_evaluation'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilterTest.php index 8baaf5df7..72a838f1e 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\GeographicalUnitStatFilter; use DateTime; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal @@ -30,12 +29,6 @@ final class GeographicalUnitStatFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_geographicalunitstat'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/IntensityFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/IntensityFilterTest.php index 6319b2acf..2f4c0caf8 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/IntensityFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/IntensityFilterTest.php @@ -27,12 +27,6 @@ final class IntensityFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_intensity'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilterTest.php index 5334c17f8..e11e8228f 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\OpenBetweenDatesFilter; use DateTime; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal @@ -30,12 +29,6 @@ final class OpenBetweenDatesFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_openbetweendates'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OriginFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OriginFilterTest.php index 745e62ca6..3182d0063 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OriginFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OriginFilterTest.php @@ -28,12 +28,6 @@ final class OriginFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_origin'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ReferrerFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ReferrerFilterTest.php index 8d2c8ef5b..ade7de2f9 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ReferrerFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ReferrerFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Entity\User; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ReferrerFilter; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal @@ -29,12 +28,6 @@ final class ReferrerFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_referrer'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php index d936f09ae..d56c57741 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php @@ -14,7 +14,6 @@ namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\RequestorFilter; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal @@ -26,15 +25,8 @@ final class RequestorFilterTest extends AbstractFilterTest protected function setUp(): void { - //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_requestor'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php index 39321dad1..15454c899 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\SocialActionFilter; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal @@ -30,12 +29,6 @@ final class SocialActionFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_socialaction'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialIssueFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialIssueFilterTest.php index a7815d3ed..9b7eec446 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialIssueFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialIssueFilterTest.php @@ -28,12 +28,6 @@ final class SocialIssueFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_socialissue'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/StepFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/StepFilterTest.php index e9353b461..0dce61dc3 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/StepFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/StepFilterTest.php @@ -27,12 +27,6 @@ final class StepFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_step'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php new file mode 100644 index 000000000..c6c407348 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php @@ -0,0 +1,77 @@ +filter = self::$container->get('chill.person.export.filter_evaluationtype'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(Evaluation::class, 'e') + ->select('e') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $r) { + $data[] = [ + 'accepted_evaluationtype' => $r, + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('workeval.id') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php new file mode 100644 index 000000000..71658d711 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php @@ -0,0 +1,63 @@ +filter = self::$container->get('chill.person.export.filter_maxdate'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + ['maxdate' => false], + ['maxdate' => true], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('workeval.id') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php new file mode 100644 index 000000000..c0eccef81 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php @@ -0,0 +1,77 @@ +filter = self::$container->get('chill.person.export.filter_household_composition'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(HouseholdCompositionType::class, 'r') + ->select('r') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $r) { + $data[] = [ + 'accepted_composition' => $r, + 'on_date' => DateTime::createFromFormat('Y-m-d', '2022-05-01'), + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('h.id') + ->from(Household::class, 'h'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilterTest.php new file mode 100644 index 000000000..1440eca51 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilterTest.php @@ -0,0 +1,90 @@ +filter = self::$container->get('chill.person.export.filter_accompanying_period_closing'); + } catch (ServiceNotFoundException $e) { + $this->markTestSkipped('The current configuration does not use accompanying_periods'); + } + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + [ + 'date_from' => DateTime::createFromFormat('Y-m-d', '2000-01-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2010-01-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container + ->get('doctrine.orm.entity_manager'); + + return [ + $em->createQueryBuilder() + ->select('person.firstName') + ->from('ChillPersonBundle:Person', 'person'), + $em->createQueryBuilder() + ->select('person.firstName') + ->from('ChillPersonBundle:Person', 'person') + // add a dummy where clause + ->where('person.firstname IS NOT NULL'), + $em->createQueryBuilder() + ->select('count(IDENTITY(p))') + ->from('ChillPersonBundle:Person', 'person') + // add a dummy where clause + ->where('person.firstname IS NOT NULL'), + $em->createQueryBuilder() + ->select('count(IDENTITY(p))') + ->from('ChillPersonBundle:Person', 'person') + ->join('person.accompanyingPeriods', 'accompanying_period') + // add a dummy where clause + ->where('person.firstname IS NOT NULL'), + $em->createQueryBuilder() + ->select('activity.date AS date') + ->select('activity.attendee as attendee') + ->from('ChillActivityBundle:Activity', 'activity') + ->join('activity.person', 'person') + ->join('person.center', 'center'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodFilterTest.php index fb975000a..943f20d73 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodFilterTest.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Filter\PersonFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; -use Chill\PersonBundle\Export\Filter\PersonFilters\BirthdateFilter; +use Chill\PersonBundle\Export\Filter\PersonFilters\AccompanyingPeriodFilter; use DateTime; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; @@ -22,7 +22,7 @@ use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; */ final class AccompanyingPeriodFilterTest extends AbstractFilterTest { - private BirthdateFilter $filter; + private AccompanyingPeriodFilter $filter; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilterTest.php new file mode 100644 index 000000000..1cb46257d --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilterTest.php @@ -0,0 +1,90 @@ +filter = self::$container->get('chill.person.export.filter_accompanying_period_opening'); + } catch (ServiceNotFoundException $e) { + $this->markTestSkipped('The current configuration does not use accompanying_periods'); + } + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData() + { + return [ + [ + 'date_from' => DateTime::createFromFormat('Y-m-d', '2000-01-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2010-01-01'), + ], + ]; + } + + public function getQueryBuilders() + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container + ->get('doctrine.orm.entity_manager'); + + return [ + $em->createQueryBuilder() + ->select('person.firstName') + ->from('ChillPersonBundle:Person', 'person'), + $em->createQueryBuilder() + ->select('person.firstName') + ->from('ChillPersonBundle:Person', 'person') + // add a dummy where clause + ->where('person.firstname IS NOT NULL'), + $em->createQueryBuilder() + ->select('count(IDENTITY(p))') + ->from('ChillPersonBundle:Person', 'person') + // add a dummy where clause + ->where('person.firstname IS NOT NULL'), + $em->createQueryBuilder() + ->select('count(IDENTITY(p))') + ->from('ChillPersonBundle:Person', 'person') + ->join('person.accompanyingPeriods', 'accompanying_period') + // add a dummy where clause + ->where('person.firstname IS NOT NULL'), + $em->createQueryBuilder() + ->select('activity.date AS date') + ->select('activity.attendee as attendee') + ->from('ChillActivityBundle:Activity', 'activity') + ->join('activity.person', 'person') + ->join('person.center', 'center'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php new file mode 100644 index 000000000..0131016ab --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php @@ -0,0 +1,70 @@ +filter = self::$container->get('chill.person.export.filter_age'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + [ + 'min_age' => '18', + 'max_age' => '60', + 'date_calc' => DateTime::createFromFormat('Y-m-d', '2020-05-01'), + ], + [ // ça devrait faire boum ! + 'min_age' => '35', + 'max_age' => '30', + 'date_calc' => DateTime::createFromFormat('Y-m-d', '2020-05-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/BirthdayFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/BirthdateFilterTest.php similarity index 96% rename from src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/BirthdayFilterTest.php rename to src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/BirthdateFilterTest.php index 4ff4f5ce3..68664b8b7 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/BirthdayFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/BirthdateFilterTest.php @@ -19,7 +19,7 @@ use DateTime; * @internal * @coversNothing */ -final class BirthdayFilterTest extends AbstractFilterTest +final class BirthdateFilterTest extends AbstractFilterTest { private BirthdateFilter $filter; diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php new file mode 100644 index 000000000..ce48394c6 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php @@ -0,0 +1,68 @@ +filter = self::$container->get('chill.person.export.filter_dead_or_alive'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + [ + 'person_state' => 'alive', + 'date_calc' => DateTime::createFromFormat('Y-m-d', '2021-05-01'), + ], + [ + 'person_state' => 'deceased', + 'date_calc' => DateTime::createFromFormat('Y-m-d', '2021-05-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php new file mode 100644 index 000000000..6562233e4 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php @@ -0,0 +1,64 @@ +filter = self::$container->get('chill.person.export.filter_deathdate'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + [ + 'date_from' => DateTime::createFromFormat('Y-m-d', '2020-05-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2022-05-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/GenderFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/GenderFilterTest.php index 8ce02a653..9ebf99b6d 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/GenderFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/GenderFilterTest.php @@ -27,12 +27,6 @@ final class GenderFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_gender'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php new file mode 100644 index 000000000..a01566ba8 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php @@ -0,0 +1,77 @@ +filter = self::$container->get('chill.person.export.filter_marital_status'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(MaritalStatus::class, 'm') + ->select('m') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $m) { + $data[] = [ + 'maritalStatus' => $m, + 'calc_date' => DateTime::createFromFormat('Y-m-d', '2022-05-01'), + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php new file mode 100644 index 000000000..e142fa9a0 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php @@ -0,0 +1,71 @@ +filter = self::$container->get('chill.person.export.filter_nationality'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $countries = $em->getRepository(Country::class)->findAll(); + + $data = []; + + foreach ($countries as $c) { + $data[] = [ + 'nationalities' => $c, + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php new file mode 100644 index 000000000..1c9225f80 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php @@ -0,0 +1,83 @@ +filter = self::$container->get('chill.person.export.filter_residential_address_at_thirdparty'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(ThirdPartyCategory::class, 'tpc') + ->select('tpc') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $r) { + $data[] = [ + 'thirdparty_cat' => $r, + 'date_calc' => DateTime::createFromFormat('Y-m-d', '2022-05-01'), + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p') + ->join(ResidentialAddress::class, 'resaddr', Expr\Join::WITH, 'resaddr.person = p') + ->join('p.center', 'center') + ->join('resaddr.hostThirdParty', 'tparty') + ->join('tparty.categories', 'tpartycat'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php new file mode 100644 index 000000000..4ce578ca2 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php @@ -0,0 +1,61 @@ +filter = self::$container->get('chill.person.export.filter_residential_address_at_user'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return []; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p') + ->join(Person\ResidentialAddress::class, 'resaddr', Expr\Join::WITH, 'resaddr.person = p') + ->join('p.center', 'center'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/JobFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/JobFilterTest.php index 5116af979..76462ebe5 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/JobFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/JobFilterTest.php @@ -27,12 +27,6 @@ final class JobFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_job'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php new file mode 100644 index 000000000..8b4a69c98 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php @@ -0,0 +1,72 @@ +filter = self::$container->get('chill.person.export.filter_treatingagent'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $users = $em->getRepository(User::class)->findAll(); + + $data = []; + + foreach ($users as $u) { + $data[] = [ + 'accepted_agents' => $u, + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('acpw.id') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ScopeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ScopeFilterTest.php index b12eb4e5b..2eb1301ca 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ScopeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ScopeFilterTest.php @@ -27,12 +27,6 @@ final class ScopeFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_scope'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php new file mode 100644 index 000000000..99fb3aae9 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php @@ -0,0 +1,109 @@ +filter = self::$container->get('chill.person.export.filter_social_work_type'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $action_repository = self::$container->get(SocialActionRepository::class); + $goal_repository = self::$container->get(GoalRepository::class); + $result_repository = self::$container->get(ResultRepository::class); + + $actions = []; + $goals = []; + $results = []; + + $social_actions = $action_repository->findAll(); + + foreach ($social_actions as $action) { + $actions[] = $action->getId(); + $goals_by_action = $goal_repository->findBySocialActionWithDescendants($action); + + foreach ($goals_by_action as $goal) { + $goals[] = $goal->getId(); + $results_by_goal = $result_repository->findByGoal($goal); + + foreach ($results_by_goal as $result) { + $results[] = $result->getId(); + } + } + $results_by_action = $result_repository->findBySocialActionWithDescendants($action); + + foreach ($results_by_action as $result) { + $results[] = $result->getId(); + } + } + + sort($actions); + sort($goals); + sort($results); + + $actions = array_unique($actions); + $goals = array_unique($goals); + $results = array_unique($results); + + $data = [ + [ + 'actionType' => implode(',', $actions), + 'goal' => implode(',', $goals), + 'result' => implode(',', $results), + ], + ]; + /// TODO ne fonctionne pas + var_dump($data); + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('acpw.id') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php b/src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php new file mode 100644 index 000000000..3f7ac3027 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php @@ -0,0 +1,101 @@ +entityManager = self::$container->get(EntityManagerInterface::class); + $this->countryRepository = self::$container->get(CountryRepository::class); + $this->centerRepository = self::$container->get(CenterRepositoryInterface::class); + } + + public function testCountByCriteria() + { + $user = new User(); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(PersonVoter::SEE)) + ->willReturn($this->centerRepository->findAll()); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $repository = new PersonACLAwareRepository( + $security->reveal(), + $this->entityManager, + $this->countryRepository, + $authorizationHelper->reveal() + ); + + $number = $repository->countBySearchCriteria('diallo'); + + $this->assertGreaterThan(0, $number); + } + + public function testFindByCriteria() + { + $user = new User(); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(PersonVoter::SEE)) + ->willReturn($this->centerRepository->findAll()); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $repository = new PersonACLAwareRepository( + $security->reveal(), + $this->entityManager, + $this->countryRepository, + $authorizationHelper->reveal() + ); + + $results = $repository->findBySearchCriteria(0, 5, false, 'diallo'); + + $this->assertGreaterThan(0, count($results)); + $this->assertContainsOnlyInstancesOf(Person::class, $results); + + foreach ($results as $person) { + $this->assertStringContainsString('diallo', strtolower($person->getFirstName() . ' ' . $person->getLastName())); + } + } +} diff --git a/src/Bundle/ChillPersonBundle/chill.webpack.config.js b/src/Bundle/ChillPersonBundle/chill.webpack.config.js index 85ee83c93..a445959e5 100644 --- a/src/Bundle/ChillPersonBundle/chill.webpack.config.js +++ b/src/Bundle/ChillPersonBundle/chill.webpack.config.js @@ -13,6 +13,7 @@ module.exports = function(encore, entries) encore.addEntry('vue_accourse_work_create', __dirname + '/Resources/public/vuejs/AccompanyingCourseWorkCreate/index.js'); encore.addEntry('vue_accourse_work_edit', __dirname + '/Resources/public/vuejs/AccompanyingCourseWorkEdit/index.js'); encore.addEntry('vue_visgraph', __dirname + '/Resources/public/vuejs/VisGraph/index.js'); + encore.addEntry('vue_export_action_goal_result', __dirname + '/Resources/public/vuejs/ExportFormActionGoalResult/index.js'); encore.addEntry('mod_set_referrer', __dirname + '/Resources/public/mod/AccompanyingPeriod/setReferrer.js'); diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml index 73bd1cce3..874f28b72 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml @@ -44,12 +44,12 @@ services: 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_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 @@ -65,13 +65,6 @@ services: tags: - { name: chill.export_filter, alias: accompanyingcourse_evaluation_filter } - chill.person.export.filter_activitytype: - class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ActivityTypeFilter - autowire: true - autoconfigure: true - tags: - - { name: chill.export_filter, alias: accompanyingcourse_activitytype_filter } - chill.person.export.filter_origin: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\OriginFilter autowire: true @@ -155,7 +148,7 @@ services: autowire: true autoconfigure: true tags: - - { name: chill.export_aggregator, alias: accompanyingcourse_referrer_scope_aggregator } + - { name: chill.export_aggregator, alias: accompanyingcourse_scope_aggregator } chill.person.export.aggregator_referrer_job: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\JobAggregator @@ -178,12 +171,12 @@ services: 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_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 @@ -262,3 +255,10 @@ services: 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 } + + diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml index fa1c8df8d..ffffaf175 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml @@ -90,6 +90,12 @@ services: tags: - { name: chill.export_filter, alias: person_marital_status_filter } + Chill\PersonBundle\Export\Filter\PersonFilters\GeographicalUnitFilter: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: person_geog_filter } + ## Aggregators chill.person.export.aggregator_nationality: class: Chill\PersonBundle\Export\Aggregator\PersonAggregators\NationalityAggregator @@ -132,3 +138,10 @@ services: autoconfigure: true tags: - { name: chill.export_aggregator, alias: person_household_position_aggregator } + + Chill\PersonBundle\Export\Aggregator\PersonAggregators\GeographicalUnitAggregator: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_aggregator, alias: person_geog_aggregator } + diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml index d5f7376db..450899659 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml @@ -79,3 +79,10 @@ services: autoconfigure: true tags: - { name: chill.export_aggregator, alias: social_work_actions_result_aggregator } + + chill.person.export.aggregator_goalresult: + class: Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\GoalResultAggregator + autowire: true + autoconfigure: true + tags: + - { name: chill.export_aggregator, alias: social_work_actions_goal_result_aggregator } diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php b/src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php new file mode 100644 index 000000000..e04d6b90a --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php @@ -0,0 +1,76 @@ +addSql('DROP VIEW view_chill_person_person_center_history_current'); + $this->addSql('DROP SEQUENCE chill_person_person_center_history_id_seq CASCADE'); + $this->addSql('DROP TABLE chill_person_person_center_history'); + } + + public function getDescription(): string + { + return 'Add a center history on person'; + } + + public function up(Schema $schema): void + { + $this->addSql('CREATE SEQUENCE chill_person_person_center_history_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE chill_person_person_center_history ( + id INT NOT NULL, person_id INT DEFAULT NULL, center_id INT DEFAULT NULL, + startDate DATE NOT NULL, endDate DATE DEFAULT 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('CREATE INDEX IDX_CACA67FA217BBB47 ON chill_person_person_center_history (person_id)'); + $this->addSql('CREATE INDEX IDX_CACA67FA5932F377 ON chill_person_person_center_history (center_id)'); + $this->addSql('CREATE INDEX IDX_CACA67FA3174800F ON chill_person_person_center_history (createdBy_id)'); + $this->addSql('CREATE INDEX IDX_CACA67FA65FF1AEC ON chill_person_person_center_history (updatedBy_id)'); + $this->addSql('COMMENT ON COLUMN chill_person_person_center_history.startDate IS \'(DC2Type:date_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_person_center_history.endDate IS \'(DC2Type:date_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_person_center_history.createdAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_person_center_history.updatedAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT FK_CACA67FA217BBB47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT FK_CACA67FA5932F377 FOREIGN KEY (center_id) REFERENCES centers (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT FK_CACA67FA3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT FK_CACA67FA65FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + + // check consistency of history on database side + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CHECK (startdate <= enddate)'); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT ' . + 'chill_internal_person_person_center_history_not_overlaps EXCLUDE USING GIST( + -- extension btree_gist required to include comparaison with integer + person_id WITH =, + daterange(startdate, enddate, \'[)\') WITH && + ) + INITIALLY DEFERRED'); + + // create index on search by person and date + $this->addSql('CREATE INDEX chill_internal_person_person_center_history_by_date ON chill_person_person_center_history (person_id DESC, startdate DESC, enddate DESC NULLS FIRST)'); + + // create a view to get the current center on a person + $this->addSql( + 'CREATE VIEW view_chill_person_person_center_history_current AS + SELECT id, person_id, center_id, startDate, endDate, createdAt, updatedAt, createdBy_id, updatedBy_id + FROM chill_person_person_center_history WHERE startDate <= NOW() AND (enddate IS NULL OR enddate > NOW())' + ); + + $this->addSql('INSERT INTO chill_person_person_center_history (id, person_id, center_id, startdate) + SELECT nextval(\'chill_person_person_center_history_id_seq\'), id, center_id, COALESCE(createdat, NOW()) + FROM chill_person_person WHERE center_id IS NOT NULL'); + } +} diff --git a/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml b/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml index 50cb9f0e6..091b9232f 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml +++ b/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml @@ -130,3 +130,8 @@ periods: many {Masquer # parcours clôturés ou anciens parcours} other {Masquer # parcours clôturés ou anciens parcours} } + +exports: + by_person: + Filtered by person\'s geographical unit (based on address) computed at date, only units: + "Filtré par zone géographique sur base de l'adresse, calculé à {datecalc, date, short}, seulement les zones suivantes: {units}" diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index e909f0ab7..664498a95 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -86,8 +86,8 @@ Civility: Civilité choose civility: -- All genders: tous les genres Any person selected: Aucune personne sélectionnée -Create a household and add an address: Ajouter une adresse pour un usager non suivi et seul dans un ménage -A new household will be created. The person will be member of this household.: Un nouveau ménage va être créé. L'usager sera membre de ce ménage. +Create a household and add an address: Ajouter une adresse pour une personne non suivie et seule dans un ménage +A new household will be created. The person will be member of this household.: Un nouveau ménage va être créé. La personne sera membre de ce ménage. Comment on the gender: Commentaire sur le genre # dédoublonnage @@ -127,8 +127,8 @@ address_country_code: Code pays 'Alreay existing person': 'Dossiers déjà encodés' 'Add the person': 'Ajouter la personne' -'Add the person and create an accompanying period': "Créer l'usager ET créer une période d'accompagnement" -'Add the person and create a household': "Créer l'usager ET créer un ménage" +'Add the person and create an accompanying period': "Créer la personne ET créer une période d'accompagnement" +'Add the person and create a household': "Créer la personne ET créer un ménage" Show person: Voir le dossier de la personne 'Confirm the creation': 'Confirmer la création' 'You will create this person': 'Vous allez créer le dossier suivant' @@ -200,7 +200,7 @@ Participants: Personnes impliquées Create an accompanying course: Créer un parcours Accompanying courses of users: Parcours des utilisateurs This accompanying course is still a draft: Ce parcours est encore à l'état brouillon. -Associated peoples: Usagers concernés +Associated peoples: Personnes concernées Resources: Interlocuteurs privilégiés Any requestor to this accompanying course: Aucun demandeur pour ce parcours Social action: Action d'accompagnement @@ -217,7 +217,7 @@ See this period: Voir cette période Requestor: Demandeur No requestor: Pas de demandeur No resources: "Pas d'interlocuteurs privilégiés" -Persons associated: Usagers concernés +Persons associated: Personnes concernés Referrer: Référent Referrers: Agents traitants Some peoples does not belong to any household currently. Add them to an household soon: Certaines personnes n'appartiennent à aucun ménage actuellement. Renseignez leur ménage dès que possible. @@ -244,7 +244,7 @@ List of resources: "Liste des ressources" There are no available resources: "Aucun ressource" no comment found: "Aucun commentaire" Select a type: "Choisissez un type" -Select a person: "Choisissez un usager" +Select a person: "Choisissez une personne" Select a thirdparty: "Choisissez un tiers" Contact person: "Personne de contact" Kind: "Type" @@ -276,11 +276,11 @@ Which kind of residential address would you create ?: Quel type d'adresse de ré The address of another person: L'adresse d'une autre personne The address of a third party: L'adresse d'un tiers A new address: Une nouvelle adresse -residential_address_person_explanation: L'adresse sera positionnée auprès d'un usager. Lorsque l'usager déménage, l'adresse de résidence suivra également cet usager +residential_address_person_explanation: L'adresse sera positionnée auprès d'une personne. Lorsque la personne déménage, l'adresse de résidence suivra également cette personne residential_address_third_party_explanation: L'adresse sera associée à celle d'un tiers. residential_address_new_address_explanation: Créer une nouvelle adresse. L'adresse sera fixe. New residential address: Nouvelle adresse de résidence -Host person: Choisir l'adresse d'un usager +Host person: Choisir l'adresse d'une personne Host third party: Choisir l'adresse d'un tiers The new residential address was created successfully: La nouvelle adresse de résidence a été créée Edit a residential address: Modifier l'addresse de résidence @@ -318,6 +318,7 @@ CHILL_PERSON_ACCOMPANYING_PERIOD_FULL: Voir les détails, créer, supprimer et m CHILL_PERSON_ACCOMPANYING_COURSE_REASSIGN_BULK: Réassigner les parcours en lot CHILL_PERSON_ACCOMPANYING_PERIOD_SEE_DETAILS: Voir les détails d'une période d'accompagnement CHILL_PERSON_ACCOMPANYING_PERIOD_STATS: Statistiques sur les parcours d'accompagnement +CHILL_PERSON_HOUSEHOLD_STATS: Statistiques sur les ménages #period Period closed!: Période clôturée! @@ -330,16 +331,16 @@ Accompanyied people: Personnes accompagnées ## exports Exports of persons: Exports des personnes -Count people by various parameters.: Compte le nombre d'usagers en fonction de différents filtres. -Count people: Nombre d'usagers +Count people by various parameters.: Compte le nombre de personnes en fonction de différents filtres. +Count people: Nombre de personnes List peoples: Liste des personnes Create a list of people according to various filters.: Crée une liste des personnes selon différents filtres. Fields to include in export: Champs à inclure dans l'export Address valid at this date: Addresse valide à cette date List duplicates: Liste des doublons Create a list of duplicate people: Créer la liste des personnes détectées comme doublons. -Count people participating in an accompanying course by various parameters.: Nombre d'usagers concernées par un parcours -Count people participating in an accompanying course: Nombre d'usagers concernés par un parcours +Count people participating in an accompanying course: Nombre de personnes concernées par un parcours +Count people participating in an accompanying course by various parameters.: Compte le nombre de personnes concernées par un parcours Exports of accompanying courses: Exports des parcours d'accompagnement Count accompanying courses: Nombre de parcours @@ -362,76 +363,82 @@ Count households: Nombre de ménages Count household by various parameters.: Compte le nombre de ménages impliqués dans un parcours selon différents filtres. ## persons filters -Filter by person gender: Filtrer par genre de la personne +Filter by person gender: Filtrer les personnes par genre Accepted genders: Genres acceptés 'Filtering by genders: only %genders%': 'Filtré par genre: seulement %genders%' -Filter by person's nationality: Filtrer par nationalité +Filter by person's nationality: Filtrer les personnes par nationalité Nationalities: Nationalités Choose countries: Choisir les nationalités 'Filtered by nationality : %nationalities%': 'Filtré par nationalité : seulement %nationalities%' -Filter by person's birthdate: Filtrer par date de naissance de la personne +Filter by person's birthdate: Filtrer les personnes par date de naissance Born after this date: Nés après cette date Born before this date: Nés avant cette date This field should not be empty: Ce champ ne peut pas être vide This date should be after the date given in "born after" field: Cette date doit être après la date donnée du le champ "nés après le" "Filtered by person's birthdate: between %date_from% and %date_to%": "Filtré par date de naissance de la personne: uniquement nés entre le %date_from% et %date_to%" -Filter by person's deathdate: Filtrer par date de décès de la personne +Filter by person's deathdate: Filtrer les personnes par date de décès "Filtered by person's deathdate: between %date_from% and %date_to%": "Filtré par date de naissance de la personne: uniquement nés entre le %date_from% et %date_to%" Death after this date: Décédé après cette date Deathdate before: Décédé avant cette date - Alive: Vivant Deceased: Décédé Filter in relation to this date: Filtrer par rapport à cette date -"Filtered by a state of %deadOrAlive% at this date %date_calc%": Filtré par personnes qui sont %deadOrAlive% à cette date %date_calc% +"Filtered by a state of %deadOrAlive%: at this date %date_calc%": Filtré par personnes qui sont %deadOrAlive% à cette date %date_calc% -Filter by person's age: Filtrer par âge de la personne +Filter by person's age: Filtrer les personnes par age "Filtered by person's age: between %min_age% and %max_age%": "Filtré par âge de la personne entre %min_age% et %max_age%" Minimum age: Âge minimum Maximum age: Âge maximum The minimum age should be less than the maximum age.: L'âge minimum doit être plus bas que l'âge maximum. Date during which residential address was valid: Date de validité -Filtered by person\'s who have a residential address located at a thirdparty of type %thirparty_type%: Uniquement les usagers qui ont une addresse de résidence chez un tiers de catégorie %thirdparty_type% +Filtered by person\'s who have a residential address located at a thirdparty of type %thirparty_type%: Uniquement les personnes qui ont une addresse de résidence chez un tiers de catégorie %thirdparty_type% Family composition: Composition familiale Family composition at this time: Composition familiale à cette date. +Filter by person's marital status: Filtrer les personnes par état matrimonial Filtered by person's marital status: Filtré par état matrimonial -Filter by person's marital status: Filtrer par état matrimonial Marital status at this time: État matrimonial par rapport à cette date -Filter by entrusted child status: Filtrer les usagers qui sont "enfant confié" -Filtered by entrusted child status: Uniquement les usagers qui sont "enfant confié" +Filter by entrusted child status: Filtrer les personnes "enfant confié" +Filtered by entrusted child status: Uniquement les personnes qui sont "enfant confié" -Filter by nomadic status: Filtrer les usagers qui sont "gens de voyage" -Filtered by nomadic status: Uniquement les usagers qui sont "gens de voyage" +Filter by nomadic status: Filtrer les personnes "gens du voyage" +Filtered by nomadic status: Uniquement les personnes qui sont "gens du voyage" -Filtered by person's who have a residential address located at another user: Uniquement les usagers qui ont une addresse de résidence chez un autre usager +"Filter by person's who have a residential address located at another user": Filtrer les personnes qui ont une addresse de résidence chez une autre personne +"Filtered by person's who have a residential address located at another user": Uniquement les personnes qui ont une addresse de résidence chez une autre personne -Filtered by person's that are alive or have deceased at a certain date: Filtrer par usagers qui sont décédé ou vivant à une certaine date +Filter by person's that are alive or have deceased at a certain date: Filtrer les personnes qui sont décédées ou vivantes à une certaine date +Filtered by person's that are alive or have deceased at a certain date: Uniquement les personnes qui sont décédées ou vivantes à une certaine date -"Filter by accompanying period: active period": "Filtrer par période d'accompagnement: en file active" +"Filter by accompanying period: active period": "Filtrer les personnes par période d'accompagnement: en file active" Having an accompanying period opened after this date: Ayant une période d'accompagnement ouverte après cette date Having an accompanying period ending before this date, or still opened at this date: Ayant une période d'accompagnement fermée après cette date, ou toujours ouverte à cette date "Filtered by accompanying period: persons having an accompanying period opened after the %date_from% and closed before the %date_to% (or still opened at the %date_to%)": "Filtré par période d'accompagnement: personnes ayant une période d'accompagnement ouverte après le %date_from%, et cloturée le %date_to% (ou toujours ouverte le %date_to%)" -"Filter by accompanying period: starting between two dates": "Filtrer par période d'accompagnement: début de la période entre deux dates" +"Filter by accompanying period: starting between two dates": "Filtrer les personnes par période d'accompagnement: début de la période entre deux dates" "Having an accompanying period opened before this date": "Ayant une période d'accompagnement ouverte avant cette date" "Filtered by accompanying period: persons having an accompanying period opened between the %date_from% and %date_to%": "Filtrer par période d'accompagnement: ayant une période ouverte entre le %date_from% et le %date_to%" -"Filter by accompanying period: closed between two dates": "Filtrer par période d'accompagnement: période fermée entre deux dates" +"Filter by accompanying period: closed between two dates": "Filtrer les personnes par période d'accompagnement: période fermée entre deux dates" Having an accompanying period closed after this date: Ayant une période d'accompagnement fermée après cette date "Having an accompanying period closed before this date": "Ayant une période d'accompagnement fermée avant cette date" "Filtered by accompanying period: persons having an accompanying period closed between the %date_from% and %date_to%": "Filtrer par période d'accompagnement: ayant une période fermée entre le %date_from% et le %date_to%" +Filter by person having an activity in a period: Filtrer les personnes ayant eu une échange pendant la période donnée +Filtered by person having an activity between %date_from% and %date_to% with reasons %reasons_name%: Uniquement les personnes associées à une échange entre %date_from% et %date_to% avec les sujets %reasons_name% + + ## accompanying course filters/aggr Filter by user scope: Filtrer les parcours par service du référent "Filtered by user main scope: only %scope%": "Filtré par service du référent: uniquement %scope%" -Group by user scope: Grouper les parcours par service du référent + +Group course by scope: Grouper les parcours par service Filter by user job: Filtrer les parcours par métier du référent "Filtered by user job: only %job%": "Filtré par métier du référent: uniquement %job%" @@ -450,13 +457,22 @@ Group by step: Grouper les parcours par statut du parcours Filter by geographical unit: Filtrer les parcours par zone géographique Group by geographical unit: Grouper les parcours par zone géographique +Compute geographical location at date: Date de calcul de la localisation géographique Geographical unit: Zone géographique +acp_geog_agg_unitname: Zone géographique +acp_geog_agg_unitrefid: Clé de la zone géographique +Geographical layer: Couche géographique +Select a geographical layer: Choisir une couche géographique +Group people by geographical unit based on his address: Grouper les personnes par zone géographique (sur base de l'adresse) +Filter by person's geographical unit (based on address): Filter les personnes par zone géographique (sur base de l'adresse) Filter by socialaction: Filtrer les parcours par action d'accompagnement Accepted socialactions: Actions d'accompagnement "Filtered by socialactions: only %socialactions%": "Filtré par action d'accompagnement: uniquement %socialactions%" Group by social action: Grouper les parcours par action d'accompagnement -Filter by type of action, objectives and results: "Filtrer par type d'action, objectif et résultat" + +Filter by type of action, goals and results: "Filtrer les actions par type, objectif et résultat" +'Filtered actions by type, goals and results: %selected%': "Actions filtrées par: %selected%" Filter by evaluation: Filtrer les parcours par évaluation Accepted evaluations: Évaluations @@ -484,13 +500,13 @@ Administrative location: Localisation administrative "Filtered by administratives locations: only %locations%": "Filtré par localisation administrative: uniquement %locations%" Group by administrative location: Grouper les parcours par localisation administrative -Filter by requestor: Filtrer les parcours selon la présence du demandeur au sein des usagers concernés +Filter by requestor: Filtrer les parcours selon la présence du demandeur au sein des personnes concernées Accepted choices: '' -is person concerned: Le demandeur est un usager concerné -is other person: Le demandeur est un usager, mais n'est pas concerné +is person concerned: Le demandeur est une personne concernée +is other person: Le demandeur est une personne, mais n'est pas concernée is thirdparty: Le demandeur est un tiers no requestor: Le parcours ne comporte pas de demandeur -"Filtered by requestor: only %choice%": "Filtré par présence du demandeur au sein des usagers concernés: uniquement si %choice%" +"Filtered by requestor: only %choice%": "Filtré par présence du demandeur au sein des personnes concernées: uniquement si %choice%" Group by requestor: Grouper les parcours selon la nature du demandeur Filter by confidential: Filtrer les parcours par confidentialité @@ -553,6 +569,10 @@ Group by treating agent: Grouper les actions par agent traitant Group social work actions by action type: Grouper les actions par type Group social work actions by goal: Grouper les actions par objectif Group social work actions by result: Grouper les actions par résultat +Group social work actions by goal and result: Grouper les actions par objectif et résultat +Goal Type: Objectif +Result Type: Résultat +Goal and result Type: Objectif et résultat ## evaluations filters/aggr Filter by evaluation type: Filtrer les évaluations par type @@ -576,7 +596,7 @@ Group by composition: Grouper les ménages par composition familiale Group by number of children: Grouper les ménages par nombre d'enfants ## persons aggregators -Group by duration: Grouper par durée du parcours +Group by duration: Grouper les parcours par durée Rounded month duration: Durée en mois (arrondie) current duration: en cours duration 0 month: 0 mois (<15 jours) @@ -589,13 +609,15 @@ Group by country: Grouper par pays Group people by gender: Grouper les personnes par genre Group people by their professional situation: Grouper les personnes par situation professionelle Group people by marital status: Grouper les personnes par état matrimonial -Aggregate by household position: Grouper par position dans le ménage + +Aggregate by household position: Grouper les personnes par position dans le ménage Household position in relation to this date: Position dans le ménage par rapport à cette date Household position: Position dans le ménage -Filtered by person's who have a residential address located at a thirdparty of type: Uniquement les usagers qui ont une addresse de résidence chez un tiers de catégorie "xxx" -"Filtered by person's who have a residential address located at a thirdparty of type %thirdparty_type% and valid on %date_calc%": "Uniquement les usagers qui ont une addresse de résidence chez un tiers de catégorie %thirdparty_type% et valide sur la date %date_calc%" -Aggregate by age: Grouper par âge +Filter by person's who have a residential address located at a thirdparty of type: Filtrer les personnes qui ont une addresse de résidence chez un tiers de catégorie "xxx" +"Filtered by person's who have a residential address located at a thirdparty of type %thirdparty_type% and valid on %date_calc%": "Uniquement les personnes qui ont une addresse de résidence chez un tiers de catégorie %thirdparty_type% et valide sur la date %date_calc%" + +Aggregate by age: Grouper les personnes par âge Calculate age in relation to this date: Calculer l'âge par rapport à cette date Group people by country of birth: Grouper les personnes par pays de naissance @@ -682,6 +704,7 @@ origin: evaluation: delay: Délai notificationDelay: Délai de notification + url: Lien internet goal: desactivationDate: Date de désactivation @@ -762,7 +785,7 @@ This course is located at a temporarily address. You should locate this course t Accompanying course location: Localisation du parcours This course is located by: Localisé auprès de This course has a temporarily location: Localisation temporaire -Choose a person to locate by: Localiser auprès d'un usager concerné +Choose a person to locate by: Localiser auprès d'une personne concernée Associate at least one member with an household, and set an address to this household: Associez au moins un membre du parcours à un ménage, et indiquez une adresse à ce ménage. Locate by: Localiser auprès de fix it: Compléter @@ -847,7 +870,7 @@ Person addresses: Adresses de résidence Household addresses: Adresses de domicile Insert an address: Insérer une adresse see social issues: Voir les problématiques sociales -see persons associated: Voir les usagers concernés +see persons associated: Voir les personnes concernées docgen: Accompanying Period basic: "Parcours d'accompagnement (basique)" @@ -868,10 +891,10 @@ docgen: period_notification: period_designated_subject: Vous êtes référent d'un parcours d'accompagnement You are designated to a new period: Vous avez été désigné référent d'un parcours d'accompagnement. - Persons are: Les usagers concernés sont les suivants + Persons are: Les personnes concernées sont les suivantes Social issues are: Les problématiques sociales renseignées sont les suivantes See it online: Visualisez le parcours en ligne - Person locating period has moved: L'usager qui localise un parcours a déménagé + Person locating period has moved: La personne qui localise un parcours a déménagé You are getting a notification for a period which does not exists any more: Cette notification ne correspond pas à une période d'accompagnement valide. You are getting a notification for a period you are not allowed to see: La notification fait référence à une période d'accompagnement à laquelle vous n'avez pas accès. @@ -918,3 +941,11 @@ reassign: notification: Notify referrer: Notifier le référent Notify any: Notifier d'autres utilisateurs + +export: + aggregator: + course: + 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 diff --git a/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php b/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php new file mode 100644 index 000000000..b8d4510de --- /dev/null +++ b/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php @@ -0,0 +1,64 @@ +filter = self::$container->get('chill.report.export.filter_date'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + [ + 'date_from' => DateTime::createFromFormat('Y-m-d', '2021-07-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2022-07-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('r.id') + ->from(Report::class, 'r'), + ]; + } +} diff --git a/src/Bundle/ChillReportBundle/config/services/export.yaml b/src/Bundle/ChillReportBundle/config/services/export.yaml index 1ae624118..9650b9e33 100644 --- a/src/Bundle/ChillReportBundle/config/services/export.yaml +++ b/src/Bundle/ChillReportBundle/config/services/export.yaml @@ -8,6 +8,7 @@ services: tags: - { name: chill.export_elements_provider, prefix: 'report' } - Chill\ReportBundle\Export\Filter\ReportDateFilter: + chill.report.export.filter_date: + class: Chill\ReportBundle\Export\Filter\ReportDateFilter tags: - - { name: chill.export_filter, alias: 'report_date' } + - { name: chill.export_filter, alias: 'report_date_filter' } diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php index d2dd7fc18..11fd1aa4c 100644 --- a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\TaskBundle\Repository; use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; -use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface; +use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; use Chill\TaskBundle\Entity\SingleTask; @@ -31,14 +31,14 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository { private AuthorizationHelperInterface $authorizationHelper; - private CenterResolverDispatcherInterface $centerResolverDispatcher; + private CenterResolverManagerInterface $centerResolverDispatcher; private EntityManagerInterface $em; private Security $security; public function __construct( - CenterResolverDispatcherInterface $centerResolverDispatcher, + CenterResolverManagerInterface $centerResolverDispatcher, EntityManagerInterface $em, Security $security, AuthorizationHelperInterface $authorizationHelper @@ -304,14 +304,18 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository QueryBuilder $qb, $entity ): QueryBuilder { - $scopes = $this->authorizationHelper->getReachableScopes( - $this->security->getUser(), - TaskVoter::SHOW, - $this->centerResolverDispatcher->resolveCenter($entity) - ); + foreach ($this->centerResolverDispatcher->resolveCenters($entity) as $center) { + $scopes = $this->authorizationHelper->getReachableScopes( + $this->security->getUser(), + TaskVoter::SHOW, + $center + ); - return $qb->andWhere($qb->expr()->in('t.circle', ':scopes')) - ->setParameter('scopes', $scopes); + $qb->andWhere($qb->expr()->in('t.circle', ':scopes')) + ->setParameter('scopes', $scopes); + } + + return $qb; } private function addACLGlobal( @@ -329,7 +333,9 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository $qb->leftJoin('t.person', 'person') ->leftJoin('t.course', 'course') ->leftJoin('course.participations', 'participation') - ->leftJoin('participation.person', 'person_p'); + ->leftJoin('participation.person', 'person_p') + ->leftJoin('person.centerCurrent', 'center_current_person') + ->leftJoin('person_p.centerCurrent', 'center_current_participation'); $qb->distinct(true); $k = 0; @@ -344,8 +350,8 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository $and = $qb->expr()->andX( $qb->expr()->orX( - $qb->expr()->eq('person.center', ':center_' . $k), - $qb->expr()->eq('person_p.center', ':center_' . $k) + $qb->expr()->eq('center_current_person.center', ':center_' . $k), + $qb->expr()->eq('center_current_participation.center', ':center_' . $k) ), $qb->expr()->in('t.circle', ':scopes_' . $k) ); diff --git a/src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php b/src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php new file mode 100644 index 000000000..2cd0788ac --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php @@ -0,0 +1,226 @@ +em = self::$container->get(EntityManagerInterface::class); + $this->userRepository = self::$container->get(UserRepository::class); + $this->centerRepository = self::$container->get(CenterRepositoryInterface::class); + $this->scopeRepository = self::$container->get(ScopeRepository::class); + $this->personRepository = self::$container->get(PersonRepository::class); + } + + public function testCountByAllViewable(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(TaskVoter::SHOW)) + ->willReturn([$centerA]); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $nb = $repository->countByAllViewable(null, []); + + $this->assertGreaterThanOrEqual(0, $nb); + } + + public function testCountByPerson(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + $person = $this->getRandomPerson($this->em); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $nb = $repository->countByPerson($person, null, []); + + $this->assertGreaterThanOrEqual(0, $nb); + } + + public function testFindByAllViewable(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(TaskVoter::SHOW)) + ->willReturn([$centerA]); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $tasks = $repository->findByAllViewable(null, []); + + $this->assertGreaterThanOrEqual(0, count($tasks)); + } + + public function testFindByCourse(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + /** @var Person $person */ + $person = $this->em->createQuery( + 'SELECT p FROM ' . Person::class . ' p JOIN p.centerCurrent cc + WHERE SIZE(p.accompanyingPeriodParticipations) > 0 + AND cc.center = :center' + ) + ->setParameter('center', $centerA) + ->setMaxResults(1) + ->getSingleResult(); + $period = $person->getAccompanyingPeriodParticipations()->first()->getAccompanyingPeriod(); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(AccompanyingPeriod::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::any()) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $tasks = $repository->findByCourse($period); + + $this->assertGreaterThanOrEqual(0, count($tasks)); + } + + public function testFindByPerson(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + $person = $this->getRandomPerson($this->em); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $tasks = $repository->findByPerson($person, null, []); + + $this->assertGreaterThanOrEqual(0, count($tasks)); + } +}