diff --git a/exports_alias_conventions.md b/exports_alias_conventions.md index 579d004a5..62fc745d8 100644 --- a/exports_alias_conventions.md +++ b/exports_alias_conventions.md @@ -17,12 +17,13 @@ These are alias conventions : | | Scope::class | acp.scopes | acpscope | | | SocialIssue::class | acp.socialIssues | acpsocialissue | | | User::class | acp.user | acpuser | +| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories | | AccompanyingPeriodWork::class | | | acpw | | | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval | | | User::class | acpw.referrers | acpwuser | | | SocialAction::class | acpw.socialAction | acpwsocialaction | | | Goal::class | acpw.goals | goal | -| | Result::class | acpw.results | result | +| | Result::class | acpw.results | result | | AccompanyingPeriodParticipation::class | | | acppart | | | Person::class | acppart.person | partperson | | AccompanyingPeriodWorkEvaluation::class | | | workeval | @@ -47,7 +48,7 @@ These are alias conventions : | | HouseholdComposition::class | household.compositions | composition | | Activity::class | | | activity | | | Person::class | activity.person | actperson | -| | AccompanyingPeriod::class | activity.accompanyingPeriod | acp | +| | AccompanyingPeriod::class | activity.accompanyingPeriod | acp | | | Person::class | activity\_person\_having\_activity.person | person\_person\_having\_activity | | | ActivityReason::class | activity\_person\_having\_activity.reasons | reasons\_person\_having\_activity | | | ActivityType::class | activity.activityType | acttype | @@ -59,6 +60,7 @@ These are alias conventions : | | User::class | activity.users | actusers | | | ActivityReason::class | activity.reasons | actreasons | | | Center::class | actperson.center | actcenter | +| | Person::class | activity.createdBy | actcreator | | ActivityReason::class | | | actreasons | | | ActivityReasonCategory::class | actreason.category | actreasoncat | | Calendar::class | | | cal | diff --git a/src/Bundle/ChillActivityBundle/Entity/Activity.php b/src/Bundle/ChillActivityBundle/Entity/Activity.php index 98142149f..8fcae3e0b 100644 --- a/src/Bundle/ChillActivityBundle/Entity/Activity.php +++ b/src/Bundle/ChillActivityBundle/Entity/Activity.php @@ -13,6 +13,10 @@ namespace Chill\ActivityBundle\Entity; use Chill\ActivityBundle\Validator\Constraints as ActivityValidator; use Chill\DocStoreBundle\Entity\StoredObject; +use Chill\MainBundle\Doctrine\Model\TrackCreationInterface; +use Chill\MainBundle\Doctrine\Model\TrackCreationTrait; +use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface; +use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait; use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; use Chill\MainBundle\Entity\Embeddable\PrivateCommentEmbeddable; @@ -55,8 +59,12 @@ use Symfony\Component\Validator\Constraints as Assert; * getUserFunction="getUser", * path="scope") */ -class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface, HasCentersInterface, HasScopesInterface +class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface, HasCentersInterface, HasScopesInterface, TrackCreationInterface, TrackUpdateInterface { + use TrackCreationTrait; + + use TrackUpdateTrait; + public const SENTRECEIVED_RECEIVED = 'received'; public const SENTRECEIVED_SENT = 'sent'; diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByCreatorAggregator.php similarity index 72% rename from src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php rename to src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByCreatorAggregator.php index 337e38705..c09685e4e 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByCreatorAggregator.php @@ -13,20 +13,19 @@ namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators; use Chill\ActivityBundle\Export\Declarations; use Chill\MainBundle\Export\AggregatorInterface; -use Chill\MainBundle\Repository\UserRepository; +use Chill\MainBundle\Repository\UserRepositoryInterface; use Chill\MainBundle\Templating\Entity\UserRender; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use function in_array; -class ByUserAggregator implements AggregatorInterface +class ByCreatorAggregator implements AggregatorInterface { private UserRender $userRender; - private UserRepository $userRepository; + private UserRepositoryInterface $userRepository; public function __construct( - UserRepository $userRepository, + UserRepositoryInterface $userRepository, UserRender $userRender ) { $this->userRepository = $userRepository; @@ -40,12 +39,8 @@ class ByUserAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('actusers', $qb->getAllAliases(), true)) { - $qb->leftJoin('activity.users', 'actusers'); - } - - $qb->addSelect('actusers.id AS users_aggregator'); - $qb->addGroupBy('users_aggregator'); + $qb->addSelect('IDENTITY(activity.createdBy) AS creator_aggregator'); + $qb->addGroupBy('creator_aggregator'); } public function applyOn(): string @@ -62,7 +57,7 @@ class ByUserAggregator implements AggregatorInterface { return function ($value): string { if ('_header' === $value) { - return 'Accepted users'; + return 'Created by'; } if (null === $value) { @@ -77,11 +72,11 @@ class ByUserAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['users_aggregator']; + return ['creator_aggregator']; } public function getTitle(): string { - return 'Group activity by linked users'; + return 'Group activity by creator'; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/CreatorScopeAggregator.php similarity index 81% rename from src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php rename to src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/CreatorScopeAggregator.php index 2c2cb50d2..2041fcbb4 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/CreatorScopeAggregator.php @@ -19,7 +19,7 @@ use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use function in_array; -class UserScopeAggregator implements AggregatorInterface +class CreatorScopeAggregator implements AggregatorInterface { private ScopeRepository $scopeRepository; @@ -40,12 +40,12 @@ class UserScopeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('actuser', $qb->getAllAliases(), true)) { - $qb->leftJoin('activity.user', 'actuser'); + if (!in_array('actcreator', $qb->getAllAliases(), true)) { + $qb->leftJoin('activity.createdBy', 'actcreator'); } - $qb->addSelect('IDENTITY(actuser.mainScope) AS userscope_aggregator'); - $qb->addGroupBy('userscope_aggregator'); + $qb->addSelect('IDENTITY(actcreator.mainScope) AS creatorscope_aggregator'); + $qb->addGroupBy('creatorscope_aggregator'); } public function applyOn(): string @@ -79,11 +79,11 @@ class UserScopeAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['userscope_aggregator']; + return ['creatorscope_aggregator']; } public function getTitle(): string { - return 'Group activity by userscope'; + return 'Group activity by creator scope'; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersAggregator.php new file mode 100644 index 000000000..ccccc48a0 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersAggregator.php @@ -0,0 +1,86 @@ +userRepository = $userRepository; + $this->userRender = $userRender; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + if (!in_array('actusers', $qb->getAllAliases(), true)) { + $qb->leftJoin('activity.users', 'actusers'); + } + + $qb + ->addSelect('actusers.id AS activity_users_aggregator') + ->addGroupBy('activity_users_aggregator'); + } + + public function applyOn(): string + { + return Declarations::ACTIVITY; + } + + public function buildForm(FormBuilderInterface $builder) + { + // nothing to add on the form + } + + public function getLabels($key, array $values, $data) + { + return function ($value) { + if ('_header' === $value) { + return 'Activity users'; + } + + if (null === $value) { + return ''; + } + + $u = $this->userRepository->find($value); + + return $this->userRender->renderString($u, []); + }; + } + + public function getQueryKeys($data) + { + return ['activity_users_aggregator']; + } + + public function getTitle() + { + return 'Aggregate by activity users'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersJobAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersJobAggregator.php new file mode 100644 index 000000000..a0a6a439b --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersJobAggregator.php @@ -0,0 +1,87 @@ +userJobRepository = $userJobRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + if (!in_array('actusers', $qb->getAllAliases(), true)) { + $qb->leftJoin('activity.users', 'actusers'); + } + + $qb + ->addSelect('IDENTITY(actusers.userJob) AS activity_users_job_aggregator') + ->addGroupBy('activity_users_job_aggregator'); + } + + public function applyOn() + { + return Declarations::ACTIVITY; + } + + public function buildForm(FormBuilderInterface $builder) + { + // nothing to add in the form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Users \'s job'; + } + + if (null === $value) { + return ''; + } + + $j = $this->userJobRepository->find($value); + + return $this->translatableStringHelper->localize( + $j->getLabel() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['activity_users_job_aggregator']; + } + + public function getTitle() + { + return 'Aggregate by users job'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersScopeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersScopeAggregator.php new file mode 100644 index 000000000..975c5df27 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUsersScopeAggregator.php @@ -0,0 +1,87 @@ +scopeRepository = $scopeRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + if (!in_array('actusers', $qb->getAllAliases(), true)) { + $qb->leftJoin('activity.users', 'actusers'); + } + + $qb + ->addSelect('IDENTITY(actusers.mainScope) AS activity_users_main_scope_aggregator') + ->addGroupBy('activity_users_main_scope_aggregator'); + } + + public function applyOn() + { + return Declarations::ACTIVITY; + } + + public function buildForm(FormBuilderInterface $builder) + { + // nothing to add in the form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Users \'s scope'; + } + + if (null === $value) { + return ''; + } + + $s = $this->scopeRepository->find($value); + + return $this->translatableStringHelper->localize( + $s->getName() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['activity_users_main_scope_aggregator']; + } + + public function getTitle() + { + return 'Aggregate by users scope'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByUserFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByCreatorFilter.php similarity index 67% rename from src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByUserFilter.php rename to src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByCreatorFilter.php index d276cdce8..ffabc5934 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByUserFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByCreatorFilter.php @@ -15,12 +15,10 @@ use Chill\ActivityBundle\Export\Declarations; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\PickUserDynamicType; use Chill\MainBundle\Templating\Entity\UserRender; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use function in_array; -class ByUserFilter implements FilterInterface +class ByCreatorFilter implements FilterInterface { private UserRender $userRender; @@ -36,22 +34,11 @@ class ByUserFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); - - if (!in_array('actusers', $qb->getAllAliases(), true)) { - $qb->join('activity.users', 'actusers'); - } - - $clause = $qb->expr()->in('actusers.id', ':users'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('users', $data['accepted_users']); + $qb + ->andWhere( + $qb->expr()->in('activity.createdBy', ':users') + ) + ->setParameter('users', $data['accepted_users']); } public function applyOn(): string @@ -74,13 +61,13 @@ class ByUserFilter implements FilterInterface $users[] = $this->userRender->renderString($u, []); } - return ['Filtered activity by linked users: only %users%', [ + return ['Filtered activity by creator: only %users%', [ '%users%' => implode(', ou ', $users), ]]; } public function getTitle(): string { - return 'Filter activity by linked users'; + return 'Filter activity by creator'; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ActivityUsersFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityUsersFilter.php new file mode 100644 index 000000000..2f6cd8462 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityUsersFilter.php @@ -0,0 +1,77 @@ +userRender = $userRender; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $orX = $qb->expr()->orX(); + + foreach ($data['accepted_users'] as $key => $user) { + $orX->add($qb->expr()->isMemberOf(':activity_users_filter_u' . $key, 'activity.users')); + $qb->setParameter('activity_users_filter_u' . $key, $user); + } + + $qb->andWhere($orX); + } + + public function applyOn() + { + return Declarations::ACTIVITY; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_users', PickUserDynamicType::class, [ + 'multiple' => true, + 'label' => 'Users', + ]); + } + + public function describeAction($data, $format = 'string') + { + $users = []; + + foreach ($data['accepted_users'] as $u) { + $users[] = $this->userRender->renderString($u, []); + } + + return ['Filtered activity by users: only %users%', [ + '%users%' => implode(', ', $users), + ]]; + } + + public function getTitle(): string + { + return 'Filter activity by users'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php index 78f3ee79d..ff4f42ec4 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Aggregator\ACPAggregators; use Chill\ActivityBundle\Entity\Activity; -use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByUserAggregator; +use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByCreatorAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; @@ -22,7 +22,7 @@ use Doctrine\ORM\EntityManagerInterface; */ final class ByUserAggregatorTest extends AbstractAggregatorTest { - private ByUserAggregator $aggregator; + private ByCreatorAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php index 1d33fedbe..1265804f9 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Aggregator\ACPAggregators; use Chill\ActivityBundle\Entity\Activity; -use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\UserScopeAggregator; +use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\CreatorScopeAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; @@ -22,7 +22,7 @@ use Doctrine\ORM\EntityManagerInterface; */ final class UserScopeAggregatorTest extends AbstractAggregatorTest { - private UserScopeAggregator $aggregator; + private CreatorScopeAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php index d6e3a465e..47e76e25c 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Filter\ACPFilters; use Chill\ActivityBundle\Entity\Activity; -use Chill\ActivityBundle\Export\Filter\ACPFilters\ByUserFilter; +use Chill\ActivityBundle\Export\Filter\ACPFilters\ByCreatorFilter; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Doctrine\ORM\EntityManagerInterface; @@ -23,7 +23,7 @@ use Doctrine\ORM\EntityManagerInterface; */ final class ByUserFilterTest extends AbstractFilterTest { - private ByUserFilter $filter; + private ByCreatorFilter $filter; protected function setUp(): void { diff --git a/src/Bundle/ChillActivityBundle/config/services/export.yaml b/src/Bundle/ChillActivityBundle/config/services/export.yaml index bdaae8c8a..69565e29a 100644 --- a/src/Bundle/ChillActivityBundle/config/services/export.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/export.yaml @@ -55,6 +55,10 @@ services: tags: - { name: chill.export_filter, alias: 'activity_date_filter' } + Chill\ActivityBundle\Export\Filter\ActivityUsersFilter: + tags: + - { name: chill.export_filter, alias: 'activity_users_filter' } + chill.activity.export.reason_filter: class: Chill\ActivityBundle\Export\Filter\PersonFilters\ActivityReasonFilter tags: @@ -77,10 +81,9 @@ services: tags: - { name: chill.export_filter, alias: 'activity_locationtype_filter' } - chill.activity.export.byuser_filter: # TMS (M2M) - class: Chill\ActivityBundle\Export\Filter\ACPFilters\ByUserFilter + Chill\ActivityBundle\Export\Filter\ACPFilters\ByCreatorFilter: tags: - - { name: chill.export_filter, alias: 'activity_byuser_filter' } + - { name: chill.export_filter, alias: 'activity_bycreator_filter' } chill.activity.export.emergency_filter: class: Chill\ActivityBundle\Export\Filter\ACPFilters\EmergencyFilter @@ -138,10 +141,9 @@ services: tags: - { name: chill.export_aggregator, alias: activity_date_aggregator } - chill.activity.export.byuser_aggregator: - class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByUserAggregator + Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByCreatorAggregator: tags: - - { name: chill.export_aggregator, alias: activity_byuser_aggregator } + - { name: chill.export_aggregator, alias: activity_by_creator_aggregator } chill.activity.export.bythirdparty_aggregator: class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByThirdpartyAggregator @@ -158,7 +160,18 @@ services: tags: - { name: chill.export_aggregator, alias: activity_bysocialissue_aggregator } - chill.activity.export.userscope_aggregator: - class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\UserScopeAggregator + Chill\ActivityBundle\Export\Aggregator\ACPAggregators\CreatorScopeAggregator: tags: - - { name: chill.export_aggregator, alias: activity_userscope_aggregator } + - { name: chill.export_aggregator, alias: activity_creator_scope_aggregator } + + Chill\ActivityBundle\Export\Aggregator\ActivityUsersAggregator: + tags: + - { name: chill.export_aggregator, alias: activity_users_aggregator } + + Chill\ActivityBundle\Export\Aggregator\ActivityUsersScopeAggregator: + tags: + - { name: chill.export_aggregator, alias: activity_users_scope_aggregator } + + Chill\ActivityBundle\Export\Aggregator\ActivityUsersJobAggregator: + tags: + - { name: chill.export_aggregator, alias: activity_users_job_aggregator } diff --git a/src/Bundle/ChillActivityBundle/migrations/Version20221014130554.php b/src/Bundle/ChillActivityBundle/migrations/Version20221014130554.php new file mode 100644 index 000000000..4cbe47d26 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/migrations/Version20221014130554.php @@ -0,0 +1,59 @@ +addSql('ALTER TABLE activity DROP updatedAt'); + $this->addSql('ALTER TABLE activity DROP createdAt'); + $this->addSql('ALTER TABLE activity DROP updatedBy_id'); + $this->addSql('ALTER TABLE activity DROP createdBy_id'); + + // rename some indexes on activity + $this->addSql('ALTER INDEX idx_ac74095a217bbb47 RENAME TO idx_55026b0c217bbb47'); + $this->addSql('ALTER INDEX idx_ac74095a682b5931 RENAME TO idx_55026b0c682b5931'); + $this->addSql('ALTER INDEX idx_ac74095aa76ed395 RENAME TO idx_55026b0ca76ed395'); + $this->addSql('ALTER INDEX idx_ac74095ac54c8c93 RENAME TO idx_55026b0cc54c8c93'); + } + + public function getDescription(): string + { + return 'Track update and create on activity'; + } + + public function up(Schema $schema): void + { + $this->addSql('ALTER TABLE activity ADD updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + $this->addSql('ALTER TABLE activity ADD createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + $this->addSql('ALTER TABLE activity ADD updatedBy_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE activity ADD createdBy_id INT DEFAULT NULL'); + $this->addSql('COMMENT ON COLUMN activity.updatedAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN activity.createdAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE activity ADD CONSTRAINT FK_AC74095A65FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE activity ADD CONSTRAINT FK_AC74095A3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_AC74095A65FF1AEC ON activity (updatedBy_id)'); + $this->addSql('CREATE INDEX IDX_AC74095A3174800F ON activity (createdBy_id)'); + + // rename some indexes on activity + $this->addSql('ALTER INDEX idx_55026b0cc54c8c93 RENAME TO IDX_AC74095AC54C8C93'); + $this->addSql('ALTER INDEX idx_55026b0c217bbb47 RENAME TO IDX_AC74095A217BBB47'); + $this->addSql('ALTER INDEX idx_55026b0c682b5931 RENAME TO IDX_AC74095A682B5931'); + $this->addSql('ALTER INDEX idx_55026b0ca76ed395 RENAME TO IDX_AC74095AA76ED395'); + + $this->addSql('UPDATE activity SET updatedBy_id=user_id, createdBy_id=user_id, createdAt="date", updatedAt="date"'); + } +} diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index 9f8ed3d7c..3a6ff1f05 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -252,8 +252,6 @@ Filter by activity type: Filtrer les activités par type Filter activity by locationtype: Filtrer les activités par type de localisation 'Filtered activity by locationtype: only %types%': "Filtré par type de localisation: uniquement %types%" Accepted locationtype: Types de localisation -Filter activity by linked users: Filtrer les activités par TMS -'Filtered activity by linked users: only %users%': "Filtré par TMS: uniquement %users%" Accepted users: TMS(s) Filter activity by emergency: Filtrer les activités par urgence 'Filtered activity by emergency: only %emergency%': "Filtré par urgence: uniquement si %emergency%" @@ -269,7 +267,11 @@ Filter activity by linked socialaction: Filtrer les activités par action liée Filter activity by linked socialissue: Filtrer les activités par problématique liée 'Filtered activity by linked socialissue: only %issues%': "Filtré par problématique liée: uniquement %issues%" Filter activity by user: Filtrer les activités par créateur -'Filtered activity by user: only %users%': "Filtré par créateur: uniquement %users%" +Filter activity by users: Filtrer les activités par utilisateur participant +Filter activity by creator: Filtrer les activités par créateur de l'échange +'Filtered activity by user: only %users%': "Filtré par référent: uniquement %users%" +'Filtered activity by users: only %users%': "Filtré par utilisateurs participants: uniquement %users%" +'Filtered activity by creator: only %users%': "Filtré par créateur: uniquement %users%" Creators: Créateurs Filter activity by userscope: Filtrer les activités par service du créateur 'Filtered activity by userscope: only %scopes%': "Filtré par service du créateur: uniquement %scopes%" @@ -282,9 +284,14 @@ By reason: Par sujet By category of reason: Par catégorie de sujet Reason's level: Niveau du sujet Group by reasons: Sujet d'activité -Aggregate by activity user: Grouper les activités par utilisateur +Aggregate by activity user: Grouper les activités par référent +Aggregate by activity users: Grouper les activités par utilisateurs participants Aggregate by activity type: Grouper les activités par type Aggregate by activity reason: Grouper les activités par sujet +Aggregate by users scope: Grouper les activités par service principal de l'utilisateur +Users 's scope: Service principal des utilisateurs participants à l'activité +Aggregate by users job: Grouper les activités par métier des utilisateurs participants +Users 's job: Métier des utilisateurs participants à l'activité Group activity by locationtype: Grouper les activités par type de localisation Group activity by date: Grouper les activités par date @@ -294,7 +301,8 @@ by week: Par semaine for week: Semaine by year: Par année in year: En -Group activity by linked users: Grouper les activités par TMS impliqué +Group activity by creator: Grouper les activités par créateur de l'échange +Group activity by creator scope: Grouper les activités par service du créateur de l'échange Group activity by linked thirdparties: Grouper les activités par tiers impliqué Accepted thirdparty: Tiers impliqué Group activity by linked socialaction: Grouper les activités par action liée diff --git a/src/Bundle/ChillMainBundle/Repository/UserRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/UserRepositoryInterface.php index 75e8b90b4..c869e1b82 100644 --- a/src/Bundle/ChillMainBundle/Repository/UserRepositoryInterface.php +++ b/src/Bundle/ChillMainBundle/Repository/UserRepositoryInterface.php @@ -1,14 +1,14 @@ paginatorFactory->create($action->getEvaluations()->count()); + $evaluations = $action->getEvaluations()->filter(static fn (Evaluation $eval) => $eval->isActive()); - $evaluations = $action->getEvaluations()->slice( + $pagination = $this->paginatorFactory->create($evaluations->count()); + + $evaluations = $evaluations->slice( $pagination->getCurrentPageFirstItemNumber(), $pagination->getItemsPerPage() ); diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 61c0291db..d00e09dcd 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -22,6 +22,7 @@ use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\UserJob; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodLocationHistory; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodStepHistory; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive; use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment; @@ -341,6 +342,12 @@ class AccompanyingPeriod implements */ private string $step = self::STEP_DRAFT; + /** + * @ORM\OneToMany(targetEntity=AccompanyingPeriodStepHistory::class, + * mappedBy="period", cascade={"persist", "remove"}, orphanRemoval=true) + */ + private Collection $stepHistories; + /** * @ORM\Column(type="datetime", nullable=true, options={"default": NULL}) */ @@ -395,7 +402,6 @@ class AccompanyingPeriod implements */ public function __construct(?DateTime $dateOpening = null) { - $this->setOpeningDate($dateOpening ?? new DateTime('now')); $this->calendars = new ArrayCollection(); $this->participations = new ArrayCollection(); $this->scopes = new ArrayCollection(); @@ -405,6 +411,8 @@ class AccompanyingPeriod implements $this->resources = new ArrayCollection(); $this->userHistories = new ArrayCollection(); $this->locationHistories = new ArrayCollection(); + $this->stepHistories = new ArrayCollection(); + $this->setOpeningDate($dateOpening ?? new DateTime('now')); } /** @@ -995,6 +1003,11 @@ class AccompanyingPeriod implements return $this->step; } + public function getStepHistories(): Collection + { + return $this->stepHistories; + } + public function getUser(): ?User { return $this->user; @@ -1263,7 +1276,11 @@ class AccompanyingPeriod implements */ public function setOpeningDate($openingDate) { - $this->openingDate = $openingDate; + if ($this->openingDate !== $openingDate) { + $this->openingDate = $openingDate; + + $this->ensureStepContinuity(); + } return $this; } @@ -1362,6 +1379,14 @@ class AccompanyingPeriod implements $this->bootPeriod(); } + if (self::STEP_DRAFT !== $this->step && $previous !== $step) { + // we create a new history + $history = new AccompanyingPeriodStepHistory(); + $history->setStep($this->step)->setStartDate(new DateTimeImmutable('now')); + + $this->addStepHistory($history); + } + return $this; } @@ -1402,6 +1427,17 @@ class AccompanyingPeriod implements return $this; } + private function addStepHistory(AccompanyingPeriodStepHistory $stepHistory): self + { + if (!$this->stepHistories->contains($stepHistory)) { + $this->stepHistories[] = $stepHistory; + $stepHistory->setPeriod($this); + $this->ensureStepContinuity(); + } + + return $this; + } + private function bootPeriod(): void { // first location history @@ -1413,6 +1449,43 @@ class AccompanyingPeriod implements $this->addLocationHistory($locationHistory); } + private function ensureStepContinuity(): void + { + // ensure continuity of histories + $criteria = new Criteria(); + $criteria->orderBy(['startDate' => Criteria::ASC, 'id' => Criteria::ASC]); + + /** @var Iterator $steps */ + $steps = $this->getStepHistories()->matching($criteria)->getIterator(); + $steps->rewind(); + + // we set the start date of the first step as the opening date, only if it is + // not greater than the end date + /** @var AccompanyingPeriodStepHistory $current */ + $current = $steps->current(); + + if (null === $current) { + return; + } + + if ($this->getOpeningDate()->format('Y-m-d') !== $current->getStartDate()->format('Y-m-d') + && ($this->getOpeningDate() <= $current->getEndDate() || null === $current->getEndDate())) { + $current->setStartDate(DateTimeImmutable::createFromMutable($this->getOpeningDate())); + } + + // then we set all the end date to the start date of the next one + do { + /** @var AccompanyingPeriodStepHistory $current */ + $current = $steps->current(); + $steps->next(); + + if ($steps->valid()) { + $next = $steps->current(); + $current->setEndDate($next->getStartDate()); + } + } while ($steps->valid()); + } + private function setRequestorPerson(?Person $requestorPerson = null): self { $this->requestorPerson = $requestorPerson; diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodStepHistory.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodStepHistory.php new file mode 100644 index 000000000..f9baffa35 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodStepHistory.php @@ -0,0 +1,115 @@ +endDate; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getPeriod(): AccompanyingPeriod + { + return $this->period; + } + + public function getStartDate(): ?DateTimeImmutable + { + return $this->startDate; + } + + public function getStep(): string + { + return $this->step; + } + + public function setEndDate(?DateTimeImmutable $endDate): self + { + $this->endDate = $endDate; + + return $this; + } + + /** + * @internal use AccompanyingPeriod::addLocationHistory + */ + public function setPeriod(AccompanyingPeriod $period): self + { + $this->period = $period; + + return $this; + } + + public function setStartDate(?DateTimeImmutable $startDate): self + { + $this->startDate = $startDate; + + return $this; + } + + public function setStep(string $step): AccompanyingPeriodStepHistory + { + $this->step = $step; + + return $this; + } +} diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Comment.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Comment.php index 9054aba5f..592c833d3 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Comment.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Comment.php @@ -36,7 +36,7 @@ class Comment implements TrackCreationInterface, TrackUpdateInterface * inversedBy="comments") * @ORM\JoinColumn(nullable=false, onDelete="CASCADE") */ - private ?AccompanyingPeriod $accompanyingPeriod; + private ?AccompanyingPeriod $accompanyingPeriod = null; /** * @ORM\Column(type="text") diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php index 3324a236f..08dc0f425 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php @@ -26,6 +26,11 @@ use Symfony\Component\Serializer\Annotation as Serializer; */ class Evaluation { + /** + * @ORM\Column(type="boolean", nullable=false, options={"default": true}) + */ + private bool $active = true; + /** * @ORM\Column(type="dateinterval", nullable=true, options={"default": null}) * @Serializer\Groups({"read"}) @@ -114,6 +119,11 @@ class Evaluation return $this->url; } + public function isActive(): bool + { + return $this->active; + } + /** * @return $this * @@ -128,6 +138,13 @@ class Evaluation return $this; } + public function setActive(bool $active): Evaluation + { + $this->active = $active; + + return $this; + } + public function setDelay(?DateInterval $delay): self { $this->delay = $delay; diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php index 1498fc3a1..327b76a65 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php @@ -12,15 +12,20 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; use Chill\MainBundle\Export\AggregatorInterface; +use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Repository\UserRepository; use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Export\Declarations; +use DateTimeImmutable; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use function in_array; final class ReferrerAggregator implements AggregatorInterface { + private const A = 'acp_ref_agg_uhistory'; + + private const P = 'acp_ref_agg_date'; + private UserRender $userRender; private UserRepository $userRepository; @@ -40,12 +45,23 @@ final class ReferrerAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('acpuser', $qb->getAllAliases(), true)) { - $qb->leftJoin('acp.user', 'acpuser'); - } - - $qb->addSelect('acpuser.id AS referrer_aggregator'); - $qb->addGroupBy('referrer_aggregator'); + $qb + ->addSelect('IDENTITY(' . self::A . '.user) AS referrer_aggregator') + ->addGroupBy('referrer_aggregator') + ->leftJoin('acp.userHistories', self::A) + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull(self::A), + $qb->expr()->andX( + $qb->expr()->lte(self::A . '.startDate', ':' . self::P), + $qb->expr()->orX( + $qb->expr()->isNull(self::A . '.endDate'), + $qb->expr()->gt(self::A . '.endDate', ':' . self::P) + ) + ) + ) + ) + ->setParameter(self::P, $data['date_calc']); } public function applyOn(): string @@ -55,7 +71,13 @@ final class ReferrerAggregator implements AggregatorInterface public function buildForm(FormBuilderInterface $builder) { - // no form + $builder + ->add('date_calc', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'data' => new DateTimeImmutable('now'), + 'label' => 'export.aggregator.course.by_referrer.Computation date for referrer', + 'required' => true, + ]); } public function getLabels($key, array $values, $data) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php index 79fed3160..fc2fcc7b2 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php @@ -12,20 +12,21 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; use Chill\MainBundle\Export\AggregatorInterface; -//use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Declarations; use DateTime; -use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; -use LogicException; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; -final class StepAggregator implements AggregatorInterface //, FilterInterface +final class StepAggregator implements AggregatorInterface { + private const A = 'acpstephistories'; + + private const P = 'acp_step_agg_date'; + private TranslatorInterface $translator; public function __construct( @@ -41,30 +42,26 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('acp.step AS step_aggregator'); - $qb->addGroupBy('step_aggregator'); - - /* - // add date in where clause - $where = $qb->getDQLPart('where'); - - $clause = $qb->expr()->andX( - $qb->expr()->lte('acp.openingDate', ':ondate'), - $qb->expr()->orX( - $qb->expr()->gt('acp.closingDate', ':ondate'), - $qb->expr()->isNull('acp.closingDate') - ) - ); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); + if (!in_array(self::A, $qb->getAllAliases(), true)) { + $qb->leftJoin('acp.stepHistories', self::A); } - $qb->add('where', $where); - $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); - */ + $qb + ->addSelect(self::A . '.step AS step_aggregator') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull(self::A . '.step'), + $qb->expr()->andX( + $qb->expr()->lte(self::A . '.startDate', ':' . self::P), + $qb->expr()->orX( + $qb->expr()->isNull(self::A . '.endDate'), + $qb->expr()->lt(self::A . '.endDate', ':' . self::P) + ) + ) + ) + ) + ->setParameter(self::P, $data['on_date']) + ->addGroupBy('step_aggregator'); } public function applyOn(): string @@ -95,8 +92,11 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface case '_header': return 'Step'; + case null: + return ''; + default: - throw new LogicException(sprintf('The value %s is not valid', $value)); + return $value; } }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CountryOfBirthAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CountryOfBirthAggregator.php index 1ba9d9f06..395070a8a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CountryOfBirthAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CountryOfBirthAggregator.php @@ -23,6 +23,7 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; final class CountryOfBirthAggregator implements AggregatorInterface, ExportElementValidatedInterface { @@ -83,7 +84,9 @@ final class CountryOfBirthAggregator implements AggregatorInterface, ExportEleme . ' is not known.'); } - $qb->leftJoin('person.countryOfBirth', 'countryOfBirth'); + if (!in_array('countryOfBirth', $qb->getAllAliases(), true)) { + $qb->leftJoin('person.countryOfBirth', 'countryOfBirth'); + } // add group by $qb->addGroupBy('country_of_birth_aggregator'); diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php index 7e5a01f3d..8c8744dfd 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php @@ -99,6 +99,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface $qb = $this->repository->createQueryBuilder('acp'); $qb + ->andWhere('acp.step != :count_acp_step') ->andWhere( $qb->expr()->exists( 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part @@ -107,6 +108,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface ' ) ) + ->setParameter('count_acp_step', AccompanyingPeriod::STEP_DRAFT) ->setParameter('authorized_centers', $centers); $qb->select('COUNT(DISTINCT acp.id) AS export_result'); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php index ec4a5abe7..996ff36e3 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php @@ -15,7 +15,7 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\SocialWork\Evaluation; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\Query\Expr\Andx; +use Chill\PersonBundle\Repository\SocialWork\EvaluationRepositoryInterface; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; @@ -23,11 +23,15 @@ use function in_array; class EvaluationFilter implements FilterInterface { + private EvaluationRepositoryInterface $evaluationRepository; + private TranslatableStringHelper $translatableStringHelper; public function __construct( + EvaluationRepositoryInterface $evaluationRepository, TranslatableStringHelper $translatableStringHelper ) { + $this->evaluationRepository = $evaluationRepository; $this->translatableStringHelper = $translatableStringHelper; } @@ -50,16 +54,8 @@ class EvaluationFilter implements FilterInterface $qb->join('workeval.evaluation', 'eval'); } - $where = $qb->getDQLPart('where'); $clause = $qb->expr()->in('eval.id', ':evaluations'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); + $qb->andWhere($clause); $qb->setParameter('evaluations', $data['accepted_evaluations']); } @@ -72,11 +68,13 @@ class EvaluationFilter implements FilterInterface { $builder->add('accepted_evaluations', EntityType::class, [ 'class' => Evaluation::class, + 'choices' => $this->evaluationRepository->findAllActive(), 'choice_label' => function (Evaluation $ev) { return $this->translatableStringHelper->localize($ev->getTitle()); }, 'multiple' => true, - 'expanded' => true, + 'expanded' => false, + 'attr' => ['class' => 'select2'], ]); } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ReferrerFilter.php index 53ab10564..8781f6cad 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ReferrerFilter.php @@ -11,17 +11,23 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; -use Chill\MainBundle\Entity\User; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\MainBundle\Form\Type\PickUserDynamicType; use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\Query\Expr\Andx; +use DateTimeImmutable; use Doctrine\ORM\QueryBuilder; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; class ReferrerFilter implements FilterInterface { + private const A = 'acp_referrer_filter_uhistory'; + + private const P = 'acp_referrer_filter_date'; + + private const PU = 'acp_referrer_filter_users'; + private UserRender $userRender; public function __construct(UserRender $userRender) @@ -36,17 +42,22 @@ class ReferrerFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('acp.user', ':referrers'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('referrers', $data['accepted_referrers']); + $qb + ->join('acp.userHistories', self::A) + ->andWhere( + $qb->expr()->andX( + $qb->expr()->lte(self::A . '.startDate', ':' . self::P), + $qb->expr()->orX( + $qb->expr()->isNull(self::A . '.endDate'), + $qb->expr()->gt(self::A . '.endDate', ':' . self::P) + ) + ) + ) + ->andWhere( + $qb->expr()->in(self::A . '.user', ':' . self::PU) + ) + ->setParameter(self::PU, $data['accepted_referrers']) + ->setParameter(self::P, $data['date_calc']); } public function applyOn(): string @@ -56,14 +67,16 @@ class ReferrerFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('accepted_referrers', EntityType::class, [ - 'class' => User::class, - 'choice_label' => function (User $u) { - return $this->userRender->renderString($u, []); - }, - 'multiple' => true, - 'expanded' => true, - ]); + $builder + ->add('accepted_referrers', PickUserDynamicType::class, [ + 'multiple' => true, + ]) + ->add('date_calc', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'data' => new DateTimeImmutable('now'), + 'label' => 'export.filter.course.by_referrer.Computation date for referrer', + 'required' => true, + ]); } public function describeAction($data, $format = 'string'): array diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php index e55c391ef..a18442c2a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php @@ -14,9 +14,11 @@ namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Repository\UserJobRepositoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; +use DateTimeImmutable; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; @@ -24,6 +26,14 @@ use Symfony\Component\Security\Core\Security; class UserJobFilter implements FilterInterface { + private const A = 'acp_ujob_filter_uhistory'; + + private const AU = 'acp_ujob_filter_uhistory_user'; + + private const P = 'acp_ujob_filter_date'; + + private const PJ = 'acp_ujob_filter_job'; + private Security $security; private TranslatableStringHelper $translatableStringHelper; @@ -48,10 +58,22 @@ class UserJobFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { $qb + ->join('acp.userHistories', self::A) ->andWhere( - $qb->expr()->in('acp.job', ':acp_user_job_filter_j') + $qb->expr()->andX( + $qb->expr()->lte(self::A . '.startDate', ':' . self::P), + $qb->expr()->orX( + $qb->expr()->isNull(self::A . '.endDate'), + $qb->expr()->gt(self::A . '.endDate', ':' . self::P) + ) + ) ) - ->setParameter('acp_user_job_filter_j', $data['jobs']); + ->setParameter(self::P, $data['date_calc']) + ->join(self::A . '.user', self::AU) + ->andWhere( + $qb->expr()->in(self::AU . '.userJob', ':' . self::PJ) + ) + ->setParameter(self::PJ, $data['jobs']); } public function applyOn() @@ -61,14 +83,21 @@ class UserJobFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('jobs', EntityType::class, [ - 'class' => UserJob::class, - 'choices' => $this->userJobRepository->findAllActive(), - 'multiple' => true, - 'expanded' => true, - 'choice_label' => fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()), - 'label' => 'Job', - ]); + $builder + ->add('jobs', EntityType::class, [ + 'class' => UserJob::class, + 'choices' => $this->userJobRepository->findAllActive(), + 'multiple' => true, + 'expanded' => true, + 'choice_label' => fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()), + 'label' => 'Job', + ]) + ->add('date_calc', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'data' => new DateTimeImmutable('now'), + 'label' => 'export.filter.course.by_user_scope.Computation date for referrer', + 'required' => true, + ]); } public function describeAction($data, $format = 'string') diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserScopeFilter.php index c079b2bec..48a97f9f7 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserScopeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserScopeFilter.php @@ -14,9 +14,11 @@ namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Repository\ScopeRepositoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; +use DateTimeImmutable; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; @@ -24,6 +26,14 @@ use Symfony\Component\Security\Core\Security; class UserScopeFilter implements FilterInterface { + private const A = 'acp_uscope_filter_uhistory'; + + private const AU = 'acp_uscope_filter_uhistory_user'; + + private const P = 'acp_uscope_filter_date'; + + private const PS = 'acp_uscope_filter_scopes'; + private ScopeRepositoryInterface $scopeRepository; private Security $security; @@ -47,14 +57,23 @@ class UserScopeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - foreach ($data['scopes'] as $key => $scope) { - /** @var Scope $scope */ - $qb - ->andWhere( - $qb->expr()->isMemberOf(':acp_scope_filter_s_' . $key, 'acp.scopes') + $qb + ->join('acp.userHistories', self::A) + ->andWhere( + $qb->expr()->andX( + $qb->expr()->lte(self::A . '.startDate', ':' . self::P), + $qb->expr()->orX( + $qb->expr()->isNull(self::A . '.endDate'), + $qb->expr()->gt(self::A . '.endDate', ':' . self::P) + ) ) - ->setParameter('acp_scope_filter_s_' . $key, $scope); - } + ) + ->setParameter(self::P, $data['date_calc']) + ->join(self::A . '.user', self::AU) + ->andWhere( + $qb->expr()->in(self::AU . '.mainScope', ':' . self::PS) + ) + ->setParameter(self::PS, $data['scopes']); } public function applyOn() @@ -64,13 +83,20 @@ class UserScopeFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('scopes', EntityType::class, [ - 'class' => Scope::class, - 'choices' => $this->scopeRepository->findAllActive(), - 'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()), - 'multiple' => true, - 'expanded' => true, - ]); + $builder + ->add('scopes', EntityType::class, [ + 'class' => Scope::class, + 'choices' => $this->scopeRepository->findAllActive(), + 'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()), + 'multiple' => true, + 'expanded' => true, + ]) + ->add('date_calc', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'data' => new DateTimeImmutable('now'), + 'label' => 'export.filter.course.by_user_scope.Computation date for referrer', + 'required' => true, + ]); } public function describeAction($data, $format = 'string') diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php index 1c6b362d4..218ae483e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php @@ -35,7 +35,7 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface $where = $qb->getDQLPart('where'); $min = null !== $data['min_age'] ? $data['min_age'] : 0; - $max = null !== $data['max_age'] ? $data['max_age'] : 3000; + $max = null !== $data['max_age'] ? $data['max_age'] : 150; $calc = $data['date_calc']; $minDate = $calc->sub(new DateInterval('P' . $max . 'Y')); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php index a3d2260e3..aad98a394 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php @@ -12,12 +12,9 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\PersonFilters; use Chill\MainBundle\Export\FilterInterface; -use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\MaritalStatus; use Chill\PersonBundle\Export\Declarations; -use DateTime; -use Doctrine\ORM\Query\Expr\Andx; use Symfony\Bridge\Doctrine\Form\Type\EntityType; class MaritalStatusFilter implements FilterInterface @@ -37,25 +34,10 @@ class MaritalStatusFilter implements FilterInterface public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); - - $clause = $qb->expr()->andX( - $qb->expr()->in('person.maritalStatus', ':maritalStatus'), - $qb->expr()->orX( - $qb->expr()->eq('person.maritalStatusDate', ':calc_date'), - $qb->expr()->isNull('person.maritalStatusDate') - ) + $qb->andWhere( + $qb->expr()->in('person.maritalStatus', ':maritalStatus') ); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); $qb->setParameter('maritalStatus', $data['maritalStatus']); - $qb->setParameter('calc_date', $data['calc_date']); } public function applyOn() @@ -75,11 +57,6 @@ class MaritalStatusFilter implements FilterInterface 'multiple' => true, 'expanded' => true, ]); - - $builder->add('calc_date', ChillDateType::class, [ - 'label' => 'Marital status at this time', - 'data' => new DateTime(), - ]); } public function describeAction($data, $format = 'string') diff --git a/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php b/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php index 685915203..668a00276 100644 --- a/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php +++ b/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php @@ -16,6 +16,7 @@ use Chill\MainBundle\Form\Type\TranslatableStringFormType; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\SocialWork\Evaluation; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\UrlType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -52,6 +53,14 @@ class EvaluationType extends AbstractType ->add('notificationDelay', DateIntervalType::class, [ 'label' => 'evaluation.notificationDelay', 'required' => false, + ]) + ->add('active', ChoiceType::class, [ + 'label' => 'active', + 'choices' => [ + 'active' => true, + 'inactive' => false, + ], + 'required' => true, ]); } diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php index b02fef8ba..925d4598b 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php @@ -14,9 +14,8 @@ namespace Chill\PersonBundle\Repository\SocialWork; use Chill\PersonBundle\Entity\SocialWork\Evaluation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; -use Doctrine\Persistence\ObjectRepository; -final class EvaluationRepository implements ObjectRepository +final class EvaluationRepository implements EvaluationRepositoryInterface { private EntityRepository $repository; @@ -38,6 +37,11 @@ final class EvaluationRepository implements ObjectRepository return $this->repository->findAll(); } + public function findAllActive(): array + { + return $this->findBy(['active' => true]); + } + /** * @param mixed|null $limit * @param mixed|null $offset diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepositoryInterface.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepositoryInterface.php new file mode 100644 index 000000000..9ca390ff9 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepositoryInterface.php @@ -0,0 +1,45 @@ + + */ + public function findAll(): array; + + /** + * @return array + */ + public function findAllActive(): array; + + /** + * @param mixed|null $limit + * @param mixed|null $offset + * + * @return array + */ + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array; + + public function findOneBy(array $criteria, ?array $orderBy = null): ?Evaluation; + + /** + * @return class-string + */ + public function getClassName(): string; +} diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php index f6212e608..fd24b7b9d 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php @@ -63,13 +63,9 @@ final class GoalRepository implements ObjectRepository } /** - * @param mixed|null $orderBy - * @param mixed|null $limit - * @param mixed|null $offset - * * @return Goal[] */ - public function findBySocialActionWithDescendants(SocialAction $action, $orderBy = null, $limit = null, $offset = null): array + public function findBySocialActionWithDescendants(SocialAction $action, array $orderBy = [], ?int $limit = null, ?int $offset = null): array { $qb = $this->buildQueryBySocialActionWithDescendants($action); $qb->select('g'); diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php index df73bde21..e751f88cb 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php @@ -96,13 +96,9 @@ final class ResultRepository implements ObjectRepository } /** - * @param mixed|null $orderBy - * @param mixed|null $limit - * @param mixed|null $offset - * * @return Result[] */ - public function findBySocialActionWithDescendants(SocialAction $action, $orderBy = null, $limit = null, $offset = null): array + public function findBySocialActionWithDescendants(SocialAction $action, array $orderBy = [], ?int $limit = null, ?int $offset = null): array { $qb = $this->buildQueryBySocialActionWithDescendants($action); $qb->select('r'); diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/SocialWorkEvaluationApiControllerTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/SocialWorkEvaluationApiControllerTest.php new file mode 100644 index 000000000..aa98fdcd3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/SocialWorkEvaluationApiControllerTest.php @@ -0,0 +1,86 @@ +evaluationToReset) { + return; + } + + self::bootKernel(); + + $em = self::$container->get(EntityManagerInterface::class); + $evaluation = $em->find(Evaluation::class, $this->evaluationToReset->getId()); + + $evaluation->setActive(true); + $em->flush(); + } + + public function dataGenerateSocialActionWithEvaluations(): iterable + { + self::bootKernel(); + + $this->em = self::$container->get(EntityManagerInterface::class); + + /** @var SocialAction $socialAction */ + $socialAction = $this->em->createQuery( + 'SELECT s FROM ' . SocialAction::class . ' s WHERE SIZE(s.evaluations) >= 2' + ) + ->setMaxResults(1) + ->getSingleResult(); + + // set the first evaluation as inactive and save + $this->evaluationToReset = $socialAction->getEvaluations()->first(); + $this->evaluationToReset->setActive(false); + + $this->em->flush(); + + yield [$socialAction, $this->evaluationToReset]; + } + + /** + * @dataProvider dataGenerateSocialActionWithEvaluations + */ + public function testListEvaluationBySocialAction(SocialAction $action, Evaluation $inactiveEvaluation): void + { + $client = $this->getClientAuthenticated(); + + $client->request('GET', sprintf('/api/1.0/person/social-work/evaluation/by-social-action/%d.json', $action->getId())); + + $this->assertResponseIsSuccessful(); + + $content = json_decode($client->getResponse()->getContent(), true); + + $ids = array_map(static fn (array $item) => $item['id'], $content['results']); + + $this->assertNotContains($inactiveEvaluation->getId(), $ids); + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php index 4c6033ca9..2f38da244 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php @@ -29,6 +29,38 @@ use function count; */ final class AccompanyingPeriodTest extends \PHPUnit\Framework\TestCase { + public function testChangeStepKeepHistory() + { + $period = new AccompanyingPeriod(); + + $this->assertCount(0, $period->getStepHistories(), 'at initialization, period should not have any step history'); + + $period->setStep(AccompanyingPeriod::STEP_DRAFT); + + $this->assertCount(0, $period->getStepHistories(), 're applying a draft should not create a history'); + + $period->setStep(AccompanyingPeriod::STEP_CONFIRMED); + + $this->assertCount(1, $period->getStepHistories()); + $this->assertEquals(AccompanyingPeriod::STEP_CONFIRMED, $period->getStepHistories()->first()->getStep()); + + $period->setOpeningDate($aMonthAgo = new DateTime('1 month ago')); + + $this->assertCount(1, $period->getStepHistories()); + $this->assertEquals($aMonthAgo, $period->getStepHistories()->first()->getStartDate(), 'when changing the opening date, the start date of the first history should change'); + + $period->setOpeningDate($tenDaysAgo = new DateTime('10 days ago')); + + $this->assertCount(1, $period->getStepHistories()); + $this->assertEquals($tenDaysAgo, $period->getStepHistories()->first()->getStartDate(), 'when changing the opening date, the start date of the first history should change'); + + $period->setStep(AccompanyingPeriod::STEP_CLOSED); + $this->assertCount(2, $period->getStepHistories()); + + $period->setOpeningDate($tomorrow = new DateTime('tomorrow')); + $this->assertEquals($tenDaysAgo, $period->getStepHistories()->first()->getStartDate(), 'when changing the opening date to a later one and no history after, start date should change'); + } + public function testClosingEqualOpening() { $datetime = new DateTime('now'); diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php index 013dddd4f..15f4796d2 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php @@ -39,7 +39,9 @@ final class DurationAggregatorTest extends AbstractAggregatorTest public function getFormData(): array { return [ - [], + ['precision' => 'day'], + ['precision' => 'week'], + ['precision' => 'month'], ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php index 49ca0a6c1..dc7ccaad7 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregato use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\GeographicalUnitStatAggregator; +use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; /** @@ -38,8 +39,9 @@ final class GeographicalUnitStatAggregatorTest extends AbstractAggregatorTest public function getFormData(): array { + // TODO: add geographical unit stat into fixtures and provide a level return [ - [], + ['date_calc' => new DateTimeImmutable('today'), 'level' => null], ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php index baee88871..e60130a6f 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregato use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ReferrerAggregator; +use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; /** @@ -39,7 +40,7 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest public function getFormData(): array { return [ - [], + ['date_calc' => new DateTimeImmutable('now')], ]; } diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20221013131221.php b/src/Bundle/ChillPersonBundle/migrations/Version20221013131221.php new file mode 100644 index 000000000..e8cb0d392 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20221013131221.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE chill_person_social_work_evaluation DROP active'); + } + + public function getDescription(): string + { + return 'Add an active column on evaluation'; + } + + public function up(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_person_social_work_evaluation ADD active BOOLEAN DEFAULT true NOT NULL'); + } +} diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20221014115500.php b/src/Bundle/ChillPersonBundle/migrations/Version20221014115500.php new file mode 100644 index 000000000..fe7b920d9 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20221014115500.php @@ -0,0 +1,69 @@ +addSql('DROP SEQUENCE chill_person_accompanying_period_step_history_id_seq CASCADE'); + $this->addSql('DROP TABLE chill_person_accompanying_period_step_history'); + } + + public function getDescription(): string + { + return 'Add step history on accompanying periods'; + } + + public function up(Schema $schema): void + { + $this->addSql('CREATE SEQUENCE chill_person_accompanying_period_step_history_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE chill_person_accompanying_period_step_history (id INT NOT NULL, period_id INT DEFAULT NULL, + endDate DATE DEFAULT NULL, startDate DATE NOT NULL, step TEXT NOT NULL, createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, + updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, createdBy_id INT DEFAULT NULL, + updatedBy_id INT DEFAULT NULL, PRIMARY KEY(id)) + '); + $this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CHECK (startDate <= endDate)'); + + $this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT ' . + 'chill_internal_acp_steps_not_overlaps EXCLUDE USING GIST( + -- extension btree_gist required to include comparaison with integer + period_id WITH =, + daterange(startDate, endDate, \'[)\') WITH && + ) + INITIALLY DEFERRED'); + + $this->addSql('CREATE INDEX IDX_84D514ACEC8B7ADE ON chill_person_accompanying_period_step_history (period_id)'); + $this->addSql('CREATE INDEX IDX_84D514AC3174800F ON chill_person_accompanying_period_step_history (createdBy_id)'); + $this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.endDate IS \'(DC2Type:date_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.startDate IS \'(DC2Type:date_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.createdAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.updatedAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT FK_84D514ACEC8B7ADE FOREIGN KEY (period_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT FK_84D514AC3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_84D514AC65FF1AEC ON chill_person_accompanying_period_step_history (updatedBy_id)'); + + // fill the tables with current state + $this->addSql( + 'INSERT INTO chill_person_accompanying_period_step_history (id, period_id, startDate, endDate, step, createdAt, updatedAt) + SELECT nextval(\'chill_person_accompanying_period_step_history_id_seq\'), id, openingDate, null, step, NOW(), NOW() FROM chill_person_accompanying_period WHERE step = \'CONFIRMED\' + UNION + SELECT nextval(\'chill_person_accompanying_period_step_history_id_seq\'), id, openingDate, closingDate, \'CONFIRMED\', NOW(), NOW() FROM chill_person_accompanying_period WHERE step = \'CLOSED\' + UNION + SELECT nextval(\'chill_person_accompanying_period_step_history_id_seq\'), id, closingDate, null, \'CLOSED\', NOW(), NOW() FROM chill_person_accompanying_period WHERE step = \'CLOSED\' + ' + ); + } +} diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 7e9fbfeaa..76fb12d18 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -947,6 +947,8 @@ notification: export: aggregator: course: + by_referrer: + Computation date for referrer: Date à laquelle le référent était actif by_user_scope: Group course by referrer's scope: Grouper les parcours par service du référent Computation date for referrer: Date à laquelle le référent était actif @@ -956,3 +958,10 @@ export: week: Durée du parcours en semaines month: Durée du parcours en mois Precision: Unité de la durée + filter: + course: + by_user_scope: + Computation date for referrer: Date à laquelle le référent était actif + by_referrer: + Computation date for referrer: Date à laquelle le référent était actif +