Merge branch '111_exports_suite' into testing

This commit is contained in:
Julien Fastré 2022-10-05 10:21:42 +02:00
commit 4388331b2f
88 changed files with 1501 additions and 610 deletions

View File

@ -28,34 +28,42 @@ Requirements
- This project use `docker <https://docker.com>`_ to be run. As a developer, use `docker-compose <https://docs.docker.com/compose/overview/>`_ 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 <https://docs.openstack.org/swift/latest/api/temporary_url_middleware.html#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 <https://getcomposer.org>`_ ;
2. run an install script to download `composer <https://getcomposer.org>`_ ;
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,28 +172,6 @@ The password is always ``password``.
Now, read `Operations` below.
Prepare for development
***********************
Add a Gitlab token to ensure that you get always the source code:
1. generate a gitlab token there: https://gitlab.com/oauth/token
2. run this command (in php container, at the app's root): :code:`composer config gitlab-token.gitlab.com <your token>`
The auth token should appears now in the directory :code:`.composer`:
.. code-block: bash
$ cat .composer/auth.json
{
"gitlab-token": {
"gitlab.com": "<your token>"
}
}
See also "how to switch branch and get new dependencies".
Operations
**********
@ -163,7 +179,7 @@ Operations
Build assets
============
run those commands:
run those commands:
.. code-block:: bash
@ -175,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) ?
@ -185,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
@ -203,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
@ -213,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 ?
@ -223,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 ?
==================
@ -246,18 +265,20 @@ 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
================================
@ -269,10 +290,14 @@ How to switch the branch for chill-bundles, and get new dependencies
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.
.. 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:
1. at the app's root, update the ``composer.json`` to your current branch:
.. code-block:: json
@ -281,14 +306,7 @@ In order to do that without pain, use those steps:
"chill-bundles": "dev-<my-branch>@dev"
}
2. mount into the php container, and run `composer update`
Build the documentation API
===========================
A basic configuration of `sami <https://github.com/FriendsOfPhp/Sami>`_ is embedded within the project.
A configuration file for `phpDocumentor <https://www.phpdoc.org>`_ is present.
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
====================================================================================================================================================================
@ -302,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.
@ -335,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).

View File

@ -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;
@ -83,6 +85,10 @@ class AvgActivityDuration 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('activity');
$qb
@ -90,6 +96,18 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface
->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;
}

View File

@ -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,6 +86,10 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$qb = $this->repository->createQueryBuilder('activity');
$qb
@ -92,6 +98,18 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
->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;
}

View File

@ -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,11 +86,25 @@ 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('acp', $qb->getAllAliases(), true)) {
$qb->join('activity.accompanyingPeriod', 'acp');
}
$qb = $this->repository
->createQueryBuilder('activity')
->join('activity.accompanyingPeriod', '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('COUNT(DISTINCT activity.id) as export_count_activity');

View File

@ -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,15 +86,29 @@ 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('acp', $qb->getAllAliases(), true)) {
$qb->join('activity.accompanyingPeriod', 'acp');
}
$qb = $this->repository
->createQueryBuilder('activity')
->join('activity.accompanyingPeriod', 'acp');
$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;
}

View File

@ -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,15 +86,29 @@ 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('acp', $qb->getAllAliases(), true)) {
$qb->join('activity.accompanyingPeriod', 'acp');
}
$qb = $this->repository
->createQueryBuilder('activity')
->join('activity.accompanyingPeriod', 'acp');
$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;
}

View File

@ -84,16 +84,25 @@ class CountActivity implements ExportInterface, GroupedExportInterface
{
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->activityRepository->createQueryBuilder('activity');
if (!in_array('person', $qb->getAllAliases(), true)) {
$qb->join('activity.person', 'person');
}
$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;

View File

@ -119,11 +119,24 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
$select = 'SUM(activity.durationTime) AS export_stat_activity';
}
return $qb->select($select)
$qb->select($select)
->join('activity.person', 'person')
->join('actperson.center', 'actcenter')
->where($qb->expr()->in('actcenter', ':centers'))
->setParameter(':centers', $centers);
->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

View File

@ -1,82 +0,0 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Export\Aggregator;
use Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
/**
* Add tests for ActivityReasonAggregator.
*
* @internal
* @coversNothing
*/
final class ActivityReasonAggregatorTest extends AbstractAggregatorTest
{
private ActivityReasonAggregator $aggregator;
protected function setUp(): void
{
self::bootKernel();
$container = self::$kernel->getContainer();
$this->aggregator = $container->get('chill.activity.export.reason_aggregator');
// 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')
->push($request->reveal());
}
public function getAggregator()
{
return $this->aggregator;
}
public function getFormData()
{
return [
['level' => 'reasons'],
['level' => 'categories'],
];
}
public function getQueryBuilders()
{
if (null === self::$kernel) {
self::bootKernel();
}
$em = self::$kernel->getContainer()
->get('doctrine.orm.entity_manager');
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'),
];
}
}

View File

@ -11,8 +11,10 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Export\Aggregator;
use Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityTypeAggregator;
use Chill\ActivityBundle\Export\Aggregator\ActivityTypeAggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Doctrine\ORM\EntityManagerInterface;
use Prophecy\PhpUnit\ProphecyTrait;
/**
* Add tests for ActivityTypeAggregator.
@ -22,23 +24,22 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
*/
final class ActivityTypeAggregatorTest extends AbstractAggregatorTest
{
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());
}
@ -60,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()
@ -70,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'),
];
}
}

View File

@ -13,6 +13,8 @@ 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.
@ -22,23 +24,22 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
*/
final class ActivityUserAggregatorTest extends AbstractAggregatorTest
{
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());
}
@ -47,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'),
];
}
}

View File

@ -15,9 +15,12 @@ use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Doctrine\ORM\EntityManagerInterface;
use Prophecy\PhpUnit\ProphecyTrait;
final class ActivityReasonAggregatorTest extends AbstractAggregatorTest
{
use ProphecyTrait;
private ActivityReasonAggregator $aggregator;
protected function setUp(): void
@ -25,6 +28,14 @@ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest
self::bootKernel();
$this->aggregator = self::$container->get('chill.activity.export.reason_aggregator');
$request = $this->prophesize()
->willExtend(\Symfony\Component\HttpFoundation\Request::class);
$request->getLocale()->willReturn('fr');
self::$container->get('request_stack')
->push($request->reveal());
}
public function getAggregator()
@ -35,12 +46,8 @@ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest
public function getFormData(): array
{
return [
[
'level' => 'reasons',
],
[
'level' => 'categories',
]
['level' => 'reasons'],
['level' => 'categories'],
];
}
@ -55,11 +62,16 @@ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest
return [
$em->createQueryBuilder()
->select('count(activity.id)')
->from(Activity::class, 'activity')
->join('activity.person', 'actperson')
->innerJoin('activity.reasons', 'actreasons')
->join('actreasons.category', 'actreasoncat')
,
->from('ChillActivityBundle:Activity', 'activity'),
$em->createQueryBuilder()
->select('count(activity.id)')
->from('ChillActivityBundle:Activity', 'activity')
->join('activity.reasons', 'actreasons'),
$em->createQueryBuilder()
->select('count(activity.id)')
->from('ChillActivityBundle:Activity', 'activity')
->join('activity.reasons', 'actreasons')
->join('actreasons.category', 'actreasoncat'),
];
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Export\Export\LinkedToACP\AvgActivityDuration;
use Chill\MainBundle\Test\Export\AbstractExportTest;
/**
* @internal
* @coversNothing
*/
final class AvgActivityDurationTest extends AbstractExportTest
{
private AvgActivityDuration $export;
protected function setUp(): void
{
self::bootKernel();
$this->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'],
];
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Export\Export\LinkedToACP\AvgActivityVisitDuration;
use Chill\MainBundle\Test\Export\AbstractExportTest;
/**
* @internal
* @coversNothing
*/
final class AvgActivityVisitDurationTest extends AbstractExportTest
{
private AvgActivityVisitDuration $export;
protected function setUp(): void
{
self::bootKernel();
$this->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'],
];
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Export\Export\LinkedToACP\CountActivity;
use Chill\MainBundle\Test\Export\AbstractExportTest;
/**
* @internal
* @coversNothing
*/
final class CountActivityTest extends AbstractExportTest
{
private CountActivity $export;
protected function setUp(): void
{
self::bootKernel();
$this->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'],
];
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Export\Export\LinkedToACP\SumActivityDuration;
use Chill\MainBundle\Test\Export\AbstractExportTest;
/**
* @internal
* @coversNothing
*/
final class SumActivityDurationTest extends AbstractExportTest
{
private SumActivityDuration $export;
protected function setUp(): void
{
self::bootKernel();
$this->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'],
];
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Export\Export\LinkedToACP\SumActivityVisitDuration;
use Chill\MainBundle\Test\Export\AbstractExportTest;
/**
* @internal
* @coversNothing
*/
final class SumActivityVisitDurationTest extends AbstractExportTest
{
private SumActivityVisitDuration $export;
protected function setUp(): void
{
self::bootKernel();
$this->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'],
];
}
}

View File

@ -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'],

View File

@ -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());
}

View File

@ -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'],

View File

@ -31,12 +31,6 @@ final class ActivityTypeFilterTest 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.activity.export.filter_activitytype');
}

View File

@ -29,12 +29,6 @@ final class BySocialActionFilterTest 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.activity.export.bysocialaction_filter');
}

View File

@ -29,12 +29,6 @@ final class BySocialIssueFilterTest 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.activity.export.bysocialissue_filter');
}

View File

@ -29,12 +29,6 @@ final class ByUserFilterTest 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.activity.export.byuser_filter');
}

View File

@ -28,12 +28,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.activity.export.emergency_filter');
}

View File

@ -29,12 +29,6 @@ final class LocationTypeFilterTest 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.activity.export.locationtype_filter');
}

View File

@ -28,12 +28,6 @@ final class SentReceivedFilterTest 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.activity.export.sentreceived_filter');
}

View File

@ -29,12 +29,6 @@ final class UserFilterTest 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.activity.export.user_filter');
}

View File

@ -29,12 +29,6 @@ final class UserScopeFilterTest 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.activity.export.userscope_filter');
}

View File

@ -28,12 +28,6 @@ final class ActivityDateFilterTest 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.activity.export.date_filter');
}

View File

@ -14,6 +14,7 @@ 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
@ -21,23 +22,22 @@ use Doctrine\Common\Collections\ArrayCollection;
*/
final class ActivityReasonFilterTest extends AbstractFilterTest
{
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');
// 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');
$container->get('request_stack')
self::$container->get('request_stack')
->push($request->reveal());
}

View File

@ -29,12 +29,6 @@ final class ActivityTypeFilterTest 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.activity.export.type_filter');
}

View File

@ -29,12 +29,6 @@ final class ActivityReasonFilterTest 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.activity.export.reason_filter');
}

View File

@ -29,12 +29,6 @@ final class PersonHavingActivityBetweenDateFilterTest 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.activity.export.person_having_an_activity_between_date_filter');
}

View File

@ -28,18 +28,14 @@ final class PersonHavingActivityBetweenDateFilterTest extends AbstractFilterTest
{
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());
}

View File

@ -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;
@ -188,9 +190,21 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* The person's center.
*
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Center")
* @deprecated
*/
private ?Center $center = null;
/**
* @ORM\OneToMany(targetEntity=PersonCenterHistory::class, mappedBy="person", cascade={"persist"})
* @var Collection|PersonCenterHistory[]
*/
private Collection $centerHistory;
/**
* @ORM\OneToOne(targetEntity=PersonCenterCurrent::class, mappedBy="person")
*/
private ?PersonCenterCurrent $centerCurrent = null;
/**
* Array where customfield's data are stored.
*
@ -531,6 +545,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
$this->budgetResources = new ArrayCollection();
$this->budgetCharges = new ArrayCollection();
$this->resources = new ArrayCollection();
$this->centerHistory = new ArrayCollection();
}
/**
@ -903,9 +918,43 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
return $this->budgetResources;
}
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');
}
}
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 getCFData(): ?array
@ -1518,17 +1567,67 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
return $this;
}
/**
* Associate the center with the person. The association start on 'now'.
*
* @param Center $center
* @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;
}
/**
* @return Report
* @return Collection
*/
public function setCFData(?array $cFData)
public function getCenterHistory(): Collection
{
return $this->centerHistory;
}
/**
* @param Collection $centerHistory
* @return Person
*/
public function setCenterHistory(Collection $centerHistory): Person
{
$this->centerHistory = $centerHistory;
return $this;
}
/**
* @return PersonCenterCurrent|null
*/
public function getCenterCurrent(): ?PersonCenterCurrent
{
if (null !== $this->centerCurrent) {
return $this->centerCurrent;
}
if (null === $currentCenterHistory = $this->getCurrentCenterHistory()) {
return null;
}
return new PersonCenterCurrent($currentCenterHistory);
}
/**
* @return Person
*/
public function setCFData(?array $cFData): self
{
$this->cFData = $cFData;

View File

@ -0,0 +1,112 @@
<?php
namespace Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\Person;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* Associate a Person with the current center.
*
* The process of selecting the current center is done on database side,
* using a SQL view.
*
* @ORM\Entity(readOnly=true)
* @ORM\Table(name="view_chill_person_person_center_history_current")
* @psalm-internal Chill\PersonBundle\Entity
*/
class PersonCenterCurrent
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity=Person::class, inversedBy="centerCurrent")
*/
private Person $person;
/**
* @ORM\ManyToOne(targetEntity=Center::class)
*/
private Center $center;
/**
* @ORM\Column(type="date_immutable", nullable=false)
*/
private \DateTimeImmutable $startDate;
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
*/
private ?\DateTimeImmutable $endDate = null;
/**
* Populate the properties person, center, start and end date from history.
*
* The creator and updatedby are not filled.
*
* @internal Should not be instantied, unless inside Person entity
* @param PersonCenterHistory $history
*/
public function __construct(PersonCenterHistory $history)
{
$this->person = $history->getPerson();
$this->center = $history->getCenter();
$this->startDate = $history->getStartDate();
$this->endDate = $history->getEndDate();
$this->id = $history->getId();
}
/**
* The id will be the same as the current @link{PersonCenterHistory::class}
*
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return Person
*/
public function getPerson(): Person
{
return $this->person;
}
/**
* @return Center
*/
public function getCenter(): Center
{
return $this->center;
}
/**
* @return \DateTimeImmutable
*/
public function getStartDate(): \DateTimeImmutable
{
return $this->startDate;
}
/**
* @return \DateTimeImmutable|null
*/
public function getEndDate(): ?\DateTimeImmutable
{
return $this->endDate;
}
}

View File

@ -0,0 +1,142 @@
<?php
namespace Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
use Chill\MainBundle\Entity\Center;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\Mapping as ORM;
/**
* Associate a Person with a Center. The association may change on date intervals
*
* @ORM\Entity
* @ORM\Table(name="chill_person_person_center_history")
*/
class PersonCenterHistory implements TrackCreationInterface, TrackUpdateInterface
{
use TrackCreationTrait;
use TrackUpdateTrait;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity=Person::class, inversedBy="centerHistory")
*/
private ?Person $person = null;
/**
* @ORM\ManyToOne(targetEntity=Center::class)
*/
private ?Center $center = null;
/**
* @ORM\Column(type="date_immutable", nullable=false)
*/
private ?\DateTimeImmutable $startDate = null;
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
*/
private ?\DateTimeImmutable $endDate = null;
/**
* @param Person|null $person
* @param Center|null $center
* @param \DateTimeImmutable|null $startDate
*/
public function __construct(?Person $person = null, ?Center $center = null, ?\DateTimeImmutable $startDate = null)
{
$this->person = $person;
$this->center = $center;
$this->startDate = $startDate;
}
/**
* @return int|null
*/
public function getId(): ?int
{
return $this->id;
}
/**
* @return Person|null
*/
public function getPerson(): ?Person
{
return $this->person;
}
/**
* @param Person|null $person
*/
public function setPerson(?Person $person): self
{
$this->person = $person;
return $this;
}
/**
* @return Center|null
*/
public function getCenter(): ?Center
{
return $this->center;
}
/**
* @param Center|null $center
*/
public function setCenter(?Center $center): self
{
$this->center = $center;
return $this;
}
/**
* @return \DateTimeImmutable|null
*/
public function getStartDate(): ?\DateTimeImmutable
{
return $this->startDate;
}
/**
* @param \DateTimeImmutable|null $startDate
*/
public function setStartDate(?\DateTimeImmutable $startDate): self
{
$this->startDate = $startDate;
return $this;
}
/**
* @return \DateTimeImmutable|null
*/
public function getEndDate(): ?\DateTimeImmutable
{
return $this->endDate;
}
/**
* @param \DateTimeImmutable|null $endDate
*/
public function setEndDate(?\DateTimeImmutable $endDate): self
{
$this->endDate = $endDate;
return $this;
}
}

View File

@ -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,25 @@ 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;
}

View File

@ -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;
@ -88,6 +90,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,6 +104,18 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface
$qb->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval');
}
$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;

View File

@ -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,23 +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');
}
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
->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 household.id) AS export_result');

View File

@ -14,9 +14,11 @@ 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;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use LogicException;
@ -100,9 +102,12 @@ 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)')
->setParameter('authorized_centers', $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;
}

View File

@ -15,6 +15,7 @@ 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;
@ -88,6 +89,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)) {
@ -98,6 +103,12 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor
$qb->join('acppart.person', 'person');
}
$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;

View File

@ -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,26 @@ 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;
}

View File

@ -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,27 @@ 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']);

View File

@ -0,0 +1,10 @@
<?php
namespace Chill\PersonBundle\Repository\Person;
use Doctrine\Persistence\ObjectRepository;
interface PersonCenterHistoryInterface extends ObjectRepository
{
}

View File

@ -0,0 +1,48 @@
<?php
namespace Chill\PersonBundle\Repository\Person;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
class PersonCenterHistoryRepository implements PersonCenterHistoryInterface
{
private EntityRepository $repository;
public function __construct(EntityManagerInterface $em)
{
$this->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;
}
}

View File

@ -16,6 +16,7 @@ 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 +35,7 @@ use function implode;
final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterface
{
private AuthorizationHelper $authorizationHelper;
private AuthorizationHelperInterface $authorizationHelper;
private CountryRepository $countryRepository;
@ -46,7 +47,7 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac
Security $security,
EntityManagerInterface $em,
CountryRepository $countryRepository,
AuthorizationHelper $authorizationHelper
AuthorizationHelperInterface $authorizationHelper
) {
$this->security = $security;
$this->em = $em;
@ -310,9 +311,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(
', ',

View File

@ -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());
}
}

View File

@ -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'],

View File

@ -30,12 +30,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');
}

View File

@ -30,12 +30,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');
}

View File

@ -30,12 +30,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');
}

View File

@ -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');
}

View File

@ -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');
}

View File

@ -27,12 +27,6 @@ final class CurrentUserJobFilterTest 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_userjob');
}

View File

@ -27,12 +27,6 @@ final class CurrentUserScopeFilterTest 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_userscope');
}

View File

@ -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');
}

View File

@ -30,12 +30,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');
}

View File

@ -30,12 +30,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');
}

View File

@ -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');
}

View File

@ -30,12 +30,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');
}

View File

@ -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');
}

View File

@ -29,12 +29,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');
}

View File

@ -26,15 +26,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');
}

View File

@ -30,12 +30,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');
}

View File

@ -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');
}

View File

@ -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');
}

View File

@ -29,12 +29,6 @@ final class EvaluationTypeFilterTest 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_evaluationtype');
}

View File

@ -29,12 +29,6 @@ final class MaxDateFilterTest 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_maxdate');
}

View File

@ -29,12 +29,6 @@ final class CompositionFilterTest 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_household_composition');
}

View File

@ -28,12 +28,6 @@ final class AgeFilterTest 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_age');
}

View File

@ -28,12 +28,6 @@ final class DeadOrAliveFilterTest 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_dead_or_alive');
}

View File

@ -28,12 +28,6 @@ final class DeathdateFilterTest 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_deathdate');
}

View File

@ -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');
}

View File

@ -29,12 +29,6 @@ final class MaritalStatusFilterTest 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_marital_status');
}

View File

@ -29,12 +29,6 @@ final class NationalityFilterTest 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_nationality');
}

View File

@ -31,12 +31,6 @@ final class ResidentialAddressAtThirdpartyFilterTest 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_residential_address_at_thirdparty');
}

View File

@ -31,12 +31,6 @@ final class ResidentialAddressAtUserFilterTest 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_residential_address_at_user');
}

View File

@ -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');
}

View File

@ -29,12 +29,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(\Symfony\Component\HttpFoundation\Request::class);
$request->getLocale()->willReturn('fr');
$this->filter = self::$container->get('chill.person.export.filter_treatingagent');
}

View File

@ -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');
}

View File

@ -31,12 +31,6 @@ final class SocialWorkTypeFilterTest 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_social_work_type');
}

View File

@ -0,0 +1,79 @@
<?php
namespace Chill\PersonBundle\Tests\Repository;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\CenterRepositoryInterface;
use Chill\MainBundle\Repository\CountryRepository;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\PersonACLAwareRepository;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Doctrine\ORM\EntityManagerInterface;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Security\Core\Security;
class PersonACLAwareRepositoryTest extends KernelTestCase
{
use ProphecyTrait;
private EntityManagerInterface $entityManager;
private CountryRepository $countryRepository;
private CenterRepositoryInterface $centerRepository;
protected function setUp(): void
{
self::bootKernel();
$this->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()));
}
}
}

View File

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Person;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20220926154347 extends AbstractMigration
{
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');
}
public function down(Schema $schema): void
{
$this->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');
}
}

View File

@ -28,12 +28,6 @@ final class ReportDateFilterTest 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.report.export.filter_date');
}

View File

@ -13,6 +13,7 @@ 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 +32,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 +305,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 +334,10 @@ 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 +352,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)
);

View File

@ -0,0 +1,211 @@
<?php
namespace Chill\TaskBundle\Tests\Repository;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\CenterRepositoryInterface;
use Chill\MainBundle\Repository\ScopeRepository;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
use Chill\PersonBundle\DataFixtures\Helper\PersonRandomHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\TaskBundle\Entity\SingleTask;
use Chill\TaskBundle\Repository\SingleTaskAclAwareRepository;
use Chill\TaskBundle\Security\Authorization\TaskVoter;
use Doctrine\ORM\EntityManagerInterface;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Security\Core\Security;
class SingleTaskACLAwareRepositoryTest extends KernelTestCase
{
private EntityManagerInterface $em;
private UserRepository $userRepository;
private AuthorizationHelperInterface $authorizationHelper;
private CenterRepositoryInterface $centerRepository;
private ScopeRepository $scopeRepository;
private PersonRepository $personRepository;
use PersonRandomHelper;
use ProphecyTrait;
protected function setUp(): void
{
self::bootKernel();
$this->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 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 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));
}
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 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 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));
}
}