Compare commits

...

34 Commits

Author SHA1 Message Date
60ede58af0 Update chill-bundles version 2023-12-20 11:49:38 +01:00
afb79141f0 Merge branch 'fix_household_export' into 'master'
Fix household export

See merge request Chill-Projet/chill-bundles!640
2023-12-20 10:46:30 +00:00
4d72933edb Add condition to household export queries to exclude accompanying periods that are in draft. 2023-12-20 10:46:29 +00:00
0da12cdc2e Merge branch '245-rewrite-test-relationship-api' into 'master'
Refactor RelationshipApiControllerTest to avoid failures

Closes #245

See merge request Chill-Projet/chill-bundles!635
2023-12-13 15:02:42 +00:00
d1ce99e6d3 Merge branch '167build_the_docs' into 'master'
DX: re-instate the build of documentation on read-the-docs

See merge request Chill-Projet/chill-bundles!638
2023-12-13 08:39:56 +00:00
juminet
0ad27328c9 DX: re-instate the build of documentation on read-the-docs 2023-12-13 08:39:56 +00:00
91e6b035bd release 2.15.0 2023-12-11 17:23:47 +01:00
2adb6105eb Merge branch '246-do-not-show-confidential-in-list' into 'master'
Filter confidential courses from list of accompanying periods and related

Closes #246

See merge request Chill-Projet/chill-bundles!633
2023-12-11 16:22:38 +00:00
33d187f329 Merge branch '228-fix-export-activity-from-v1' into 'master'
Fix export for people created before 'createdAt' column introduction

Closes #228

See merge request Chill-Projet/chill-bundles!634
2023-12-11 16:22:12 +00:00
f02873c6e0 Refactor RelationshipApiControllerTest
The codebase has been improved by refining and optimizing the test methods in the RelationshipApiControllerTest class. The check for relations was also added to exclude persons without any relationship, this may exclude duplicate relations. This change should avoid errors on this test.
2023-12-08 11:07:14 +01:00
8cc93a8b07 add changie [ci-skip] 2023-12-07 23:25:38 +01:00
f7184ca7bb Fix export for people created before 'createdAt' column introduction
This commit introduces a migration to fix the export for individuals who were created before the introduction of the 'createdAt' column. The 'startdate' column is now updated to match the individual's first activity date. This migration is irreversible. It's been added as a response to Issue #228
2023-12-07 23:22:48 +01:00
dab80a84d8 Merge branch '235-add-date-filter-course-by-activity' into 'master'
Add dates in the filter "filter course by activity type"

Closes #235

See merge request Chill-Projet/chill-bundles!632
2023-12-07 15:25:09 +00:00
68e00dc42f Merge branch '234-fix-date-validation-aside-activity-filter-by-date' into 'master'
Fix "filter aside activity by date"

Closes #234

See merge request Chill-Projet/chill-bundles!631
2023-12-07 15:24:16 +00:00
5fae49821f Merge branch '191-export-number-of-household-by-activities' into 'master'
Export: add export "number of household associated with an activity"

Closes #191

See merge request Chill-Projet/chill-bundles!629
2023-12-07 15:23:43 +00:00
0e599a99a7 Merge branch '233-fix-filter-by-evaluation-type' into 'master'
Fix filter evaluation by evaluation type

Closes #233

See merge request Chill-Projet/chill-bundles!628
2023-12-07 15:23:24 +00:00
f0605c6b08 Use dedicated FilterListAccompanyingPeriodHelper to filter confidential course in different lists 2023-12-07 16:18:37 +01:00
54606403b4 Refactor confidential filtering on list to let it usable by other lists 2023-12-07 15:13:54 +01:00
47d829d72d Filter confidential courses from list of accompanying periods 2023-12-06 22:46:50 +01:00
fa3b305ab9 Merge branch 'issue214_fix_error_empty_comment' into 'master'
Notification: avoid the comment's content to be null

Closes #214

See merge request Chill-Projet/chill-bundles!623
2023-12-05 16:26:06 +00:00
b26b7a2706 Notification: avoid the comment's content to be null 2023-12-05 16:26:06 +00:00
fe54e51362 Add dates in the filter "filter course by activity type" 2023-12-04 18:17:00 +01:00
179e3e92ed Fix "filter aside activity by date" 2023-12-04 17:30:40 +01:00
c00c6066a9 Merge branch 'fix-rector-issues-20231204' into 'master'
Fix issues with new version of rector

See merge request Chill-Projet/chill-bundles!630
2023-12-04 16:11:38 +00:00
f424c5464f Fix issues with new version of rector 2023-12-04 16:46:48 +01:00
b17b2a8cfb Export: add export "number of household associated with an activity" 2023-12-04 16:39:01 +01:00
00de657cae Fix filter evaluation by evaluation type 2023-12-01 09:46:02 +01:00
673518e0eb Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2023-11-30 12:15:47 +01:00
4700a0fef7 update URL for postal code FR import 2023-11-30 12:15:22 +01:00
f4d258882f Release 2.14.1 2023-11-29 22:23:42 +01:00
fffc4a9c33 Fix error on listEvaluation
- force default calcDate
- Export / UserHelper: handle case when a single user is given, when we expect more than one user
2023-11-29 22:22:06 +01:00
a344f36592 Merge branch 'issue100_add_paginator_budget_admin' into 'master'
add paginator to budget admin pages

See merge request Chill-Projet/chill-bundles!624
2023-11-29 20:21:42 +00:00
eef93b8a0d add changie 2023-11-29 11:33:26 +01:00
75b78d3c99 add paginator to budget admin pages 2023-11-28 17:27:26 +01:00
47 changed files with 1090 additions and 322 deletions

View File

@@ -1,5 +0,0 @@
kind: Fixed
body: 'Export: fix list person with custom fields'
time: 2023-11-27T21:01:38.260730706+01:00
custom:
Issue: ""

5
.changes/v2.14.1.md Normal file
View File

@@ -0,0 +1,5 @@
## v2.14.1 - 2023-11-29
### Fixed
* Export: fix list person with custom fields
* ([#100](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/100)) Add a paginator to budget elements (resource and charge types) in the admin
* Fix error in ListEvaluation when "handling agents" are alone

11
.changes/v2.15.0.md Normal file
View File

@@ -0,0 +1,11 @@
## v2.15.0 - 2023-12-11
### Feature
* ([#191](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/191)) Add export "number of household associate with an exchange"
* ([#235](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/235)) Export: add dates on the filter "filter course by activity type"
### Fixed
* ([#214](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/214)) Fix error when posting an empty comment on an accompanying period.
* ([#233](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/233)) Fix "filter evaluation by evaluation type" (and add select2 to the list of evaluation types to pick)
* ([#234](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/234)) Fix "filter aside activity by date"
* ([#228](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/228)) Fix export of activity for people created before the introduction of the createdAt column on person (during v1)
* ([#246](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/246)) Do not show activities, evaluations and social work when associated to a confidential accompanying period, except for the users which are allowed to see them

5
.changes/v2.15.1.md Normal file
View File

@@ -0,0 +1,5 @@
## v2.15.1 - 2023-12-20
### Fixed
* Fix the household export query to exclude accompanying periods that are in draft state.
### DX
* ([#167](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/167)) Fixed readthedocs compilation by updating readthedocs config file and requirements for Sphinx

View File

@@ -1,10 +1,14 @@
---
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.7"
sphinx:
configuration: docs/source/conf.py
python:
version: 3.7
install:
- requirements: docs/requirements.txt
- requirements: docs/requirements.txt

View File

@@ -6,6 +6,30 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).
## v2.15.1 - 2023-12-20
### Fixed
* Fix the household export query to exclude accompanying periods that are in draft state.
### DX
* ([#167](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/167)) Fixed readthedocs compilation by updating readthedocs config file and requirements for Sphinx
## v2.15.0 - 2023-12-11
### Feature
* ([#191](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/191)) Add export "number of household associate with an exchange"
* ([#235](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/235)) Export: add dates on the filter "filter course by activity type"
### Fixed
* ([#214](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/214)) Fix error when posting an empty comment on an accompanying period.
* ([#233](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/233)) Fix "filter evaluation by evaluation type" (and add select2 to the list of evaluation types to pick)
* ([#234](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/234)) Fix "filter aside activity by date"
* ([#228](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/228)) Fix export of activity for people created before the introduction of the createdAt column on person (during v1)
* ([#246](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/246)) Do not show activities, evaluations and social work when associated to a confidential accompanying period, except for the users which are allowed to see them
## v2.14.1 - 2023-11-29
### Fixed
* Export: fix list person with custom fields
* ([#100](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/100)) Add a paginator to budget elements (resource and charge types) in the admin
* Fix error in ListEvaluation when "handling agents" are alone
## v2.14.0 - 2023-11-24
### Feature
* ([#161](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/161)) Export: in filter "Filter accompanying period work (social action) by type, goal and result", order the items alphabetically or with the defined order

View File

@@ -27,7 +27,7 @@ To compile this documentation :
Contribute
===========
Issue tracker : https://git.framasoft.org/groups/Chill-project/issues
Issue tracker : https://gitlab.com/Chill-Projet/chill-bundles/-/issues
Licence
=======

View File

@@ -1,6 +1,7 @@
docutils==0.13.1
Pygments==2.2.0
sphinx==1.8.5
Jinja2<3.1
git+https://github.com/fabpot/sphinx-php.git@v2.0.2#egg_name=sphinx-php
jsx-lexer===0.0.8
sphinx_rtd_theme==0.5.0

View File

@@ -49,7 +49,7 @@ Clone or download the chill-skeleton project and `cd` into the main directory.
.. code-block:: bash
git clone https://gitlab.com/Chill-Projet/chill-skeleton-basic.git
cd chill-app
cd chill-skeleton-basic
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.

View File

@@ -0,0 +1,147 @@
<?php
declare(strict_types=1);
/*
* 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.
*/
namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class CountHouseholdOnActivity implements ExportInterface, GroupedExportInterface
{
private EntityRepository $repository;
private bool $filterStatsByCenters;
public function __construct(
EntityManagerInterface $em,
ParameterBagInterface $parameterBag,
) {
$this->repository = $em->getRepository(Activity::class);
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
public function buildForm(FormBuilderInterface $builder) {}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'export.export.count_household_on_activity.description';
}
public function getGroup(): string
{
return 'Exports of activities linked to an accompanying period';
}
public function getLabels($key, array $values, $data)
{
if ('export_count_activity' !== $key) {
throw new \LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'export.export.count_household_on_activity.header' : $value;
}
public function getQueryKeys($data): array
{
return ['export_count_activity'];
}
public function getResult($query, $data)
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
{
return 'export.export.count_household_on_activity.title';
}
public function getType(): string
{
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->repository
->createQueryBuilder('activity')
->join('activity.persons', 'person')
->join('activity.accompanyingPeriod', 'acp')
->join(
HouseholdMember::class,
'householdmember',
Query\Expr\Join::WITH,
'person.id = IDENTITY(householdmember.person) AND householdmember.startDate <= activity.date AND (householdmember.endDate IS NULL OR householdmember.endDate > activity.date)'
)
->join('householdmember.household', 'household');
if ($this->filterStatsByCenters) {
$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);
}
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
$qb->select('COUNT(DISTINCT household.id) as export_count_activity');
return $qb;
}
public function requiredRole(): string
{
return ActivityStatsVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_ACP,
PersonDeclarations::ACP_TYPE,
PersonDeclarations::PERSON_TYPE,
PersonDeclarations::HOUSEHOLD_TYPE,
];
}
}

View File

@@ -20,23 +20,18 @@ use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\Helper\TranslatableStringExportLabelHelper;
use Chill\MainBundle\Export\ListInterface;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelperInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\FormBuilderInterface;
class ListActivity implements ListInterface, GroupedExportInterface
final readonly class ListActivity implements ListInterface, GroupedExportInterface
{
private readonly bool $filterStatsByCenters;
public function __construct(
private readonly ListActivityHelper $helper,
private readonly EntityManagerInterface $entityManager,
private readonly TranslatableStringExportLabelHelper $translatableStringExportLabelHelper,
ParameterBagInterface $parameterBag,
) {
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
private ListActivityHelper $helper,
private EntityManagerInterface $entityManager,
private TranslatableStringExportLabelHelper $translatableStringExportLabelHelper,
private FilterListAccompanyingPeriodHelperInterface $filterListAccompanyingPeriodHelper,
) {}
public function buildForm(FormBuilderInterface $builder)
{
@@ -119,19 +114,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
->leftJoin('acppart.person', 'person')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL');
if ($this->filterStatsByCenters) {
$qb
->andWhere(
$qb->expr()->exists(
'SELECT 1
FROM '.PersonCenterHistory::class.' acl_count_person_history
WHERE acl_count_person_history.person = person
AND acl_count_person_history.center IN (:authorized_centers)
'
)
)
->setParameter('authorized_centers', $centers);
}
$this->filterListAccompanyingPeriodHelper->addFilterAccompanyingPeriods($qb, $requiredModifiers, $acl, $data);
$qb
// some grouping are necessary

View File

@@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
/*
* 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.
*/
namespace Chill\ActivityBundle\Export\Export\LinkedToPerson;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityRepository;
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\Household\HouseholdMember;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\Query;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class CountHouseholdOnActivity implements ExportInterface, GroupedExportInterface
{
private bool $filterStatsByCenters;
public function __construct(
private ActivityRepository $activityRepository,
ParameterBagInterface $parameterBag,
) {
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
public function buildForm(FormBuilderInterface $builder) {}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes()
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription()
{
return 'export.export.count_household_on_activity_person.description';
}
public function getGroup(): string
{
return 'Exports of activities linked to a person';
}
public function getLabels($key, array $values, $data)
{
if ('export_count_activity' !== $key) {
throw new \LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'export.export.count_household_on_activity_person.header' : $value;
}
public function getQueryKeys($data)
{
return ['export_count_activity'];
}
public function getResult($query, $data)
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle()
{
return 'export.export.count_household_on_activity_person.title';
}
public function getType(): string
{
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->activityRepository
->createQueryBuilder('activity')
->join('activity.person', 'person')
->join(
HouseholdMember::class,
'householdmember',
Query\Expr\Join::WITH,
'person = householdmember.person AND householdmember.startDate <= activity.date AND (householdmember.endDate IS NULL OR householdmember.endDate > activity.date)'
)
->join('householdmember.household', 'household');
$qb->select('COUNT(DISTINCT household.id) as export_count_activity');
if ($this->filterStatsByCenters) {
$qb
->join('person.centerHistory', 'centerHistory')
->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
{
return ActivityStatsVoter::STATS;
}
public function supportsModifiers()
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_PERSON,
PersonDeclarations::PERSON_TYPE,
PersonDeclarations::HOUSEHOLD_TYPE,
];
}
}

View File

@@ -15,17 +15,22 @@ use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class ActivityTypeFilter implements FilterInterface
final readonly class ActivityTypeFilter implements FilterInterface
{
private const BASE_EXISTS = 'SELECT 1 FROM '.Activity::class.' act_type_filter_activity WHERE act_type_filter_activity.accompanyingPeriod = acp';
public function __construct(
private readonly ActivityTypeRepositoryInterface $activityTypeRepository,
private readonly TranslatableStringHelperInterface $translatableStringHelper
private ActivityTypeRepositoryInterface $activityTypeRepository,
private TranslatableStringHelperInterface $translatableStringHelper,
private RollingDateConverterInterface $rollingDateConverter,
) {}
public function addRole(): ?string
@@ -35,13 +40,26 @@ class ActivityTypeFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM '.Activity::class.' act_type_filter_activity
WHERE act_type_filter_activity.activityType IN (:act_type_filter_activity_types) AND act_type_filter_activity.accompanyingPeriod = acp'
)
);
$qb->setParameter('act_type_filter_activity_types', $data['accepted_activitytypes']);
$exists = self::BASE_EXISTS;
if (count($data['accepted_activitytypes']) > 0) {
$exists .= ' AND act_type_filter_activity.activityType IN (:act_type_filter_activity_types)';
$qb->setParameter('act_type_filter_activity_types', $data['accepted_activitytypes']);
}
if (null !== $data['date_after']) {
$exists .= ' AND act_type_filter_activity.date >= :act_type_filter_activity_date_after';
$qb->setParameter('act_type_filter_activity_date_after', $this->rollingDateConverter->convert($data['date_after']));
}
if (null !== $data['date_before']) {
$exists .= ' AND act_type_filter_activity.date >= :act_type_filter_activity_date_before';
$qb->setParameter('act_type_filter_activity_date_before', $this->rollingDateConverter->convert($data['date_before']));
}
if (self::BASE_EXISTS !== $exists) {
$qb->andWhere($qb->expr()->exists($exists));
}
}
public function applyOn()
@@ -60,11 +78,27 @@ class ActivityTypeFilter implements FilterInterface
'multiple' => true,
'expanded' => true,
]);
$builder->add('date_after', PickRollingDateType::class, [
'label' => 'export.filter.activity.acp_by_activity_type.activity after',
'help' => 'export.filter.activity.acp_by_activity_type.activity after help',
'required' => false,
]);
$builder->add('date_before', PickRollingDateType::class, [
'label' => 'export.filter.activity.acp_by_activity_type.activity before',
'help' => 'export.filter.activity.acp_by_activity_type.activity before help',
'required' => false,
]);
}
public function getFormDefaultData(): array
{
return [];
return [
'accepted_activitytypes' => [],
'date_after' => null,
'date_before' => null,
];
}
public function describeAction($data, $format = 'string'): array
@@ -75,8 +109,12 @@ class ActivityTypeFilter implements FilterInterface
$types[] = $this->translatableStringHelper->localize($aty->getName());
}
return ['export.filter.activity.acp_by_activity_type.acp_containing_at_least_one_%activitytypes%', [
'%activitytypes%' => implode(', ', $types),
return ['export.filter.activity.acp_by_activity_type.acp_containing_at_least_one_activitytypes', [
'activitytypes' => implode(', ', $types),
'has_date_after' => null !== $data['date_after'] ? 1 : 0,
'date_after' => $this->rollingDateConverter->convert($data['date_after']),
'has_date_before' => null !== $data['date_before'] ? 1 : 0,
'date_before' => $this->rollingDateConverter->convert($data['date_before']),
]];
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/*
* 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.
*/
namespace Chill\ActivityBundle\Tests\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Export\Export\LinkedToACP\CountHouseholdOnActivity;
use Chill\MainBundle\Test\Export\AbstractExportTest;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
*
* @coversNothing
*/
class CountHouseholdOnActivityTest extends AbstractExportTest
{
private EntityManagerInterface $entityManager;
protected function setUp(): void
{
self::bootKernel();
$this->entityManager = self::$container->get(EntityManagerInterface::class);
}
public function getExport()
{
yield new CountHouseholdOnActivity($this->entityManager, $this->getParameters(true));
yield new CountHouseholdOnActivity($this->entityManager, $this->getParameters(false));
}
public function getFormData()
{
return [
[],
];
}
public function getModifiersCombination()
{
return [
[
Declarations::ACTIVITY,
Declarations::ACTIVITY_ACP,
PersonDeclarations::ACP_TYPE,
PersonDeclarations::PERSON_TYPE,
PersonDeclarations::HOUSEHOLD_TYPE,
],
];
}
}

View File

@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
/*
* 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.
*/
namespace Chill\ActivityBundle\Tests\Export\Export\LinkedToPerson;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Export\Export\LinkedToPerson\CountHouseholdOnActivity;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\MainBundle\Test\Export\AbstractExportTest;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
/**
* @internal
*
* @coversNothing
*/
class CountHouseholdOnActivityTest extends AbstractExportTest
{
private ActivityRepository $activityRepository;
protected function setUp(): void
{
self::bootKernel();
$this->activityRepository = self::$container->get(ActivityRepository::class);
}
public function getExport()
{
yield new CountHouseholdOnActivity($this->activityRepository, $this->getParameters(true));
yield new CountHouseholdOnActivity($this->activityRepository, $this->getParameters(false));
}
public function getFormData()
{
return [
[],
];
}
public function getModifiersCombination()
{
return [
[
Declarations::ACTIVITY,
Declarations::ACTIVITY_PERSON,
PersonDeclarations::PERSON_TYPE,
PersonDeclarations::HOUSEHOLD_TYPE,
],
];
}
}

View File

@@ -13,6 +13,7 @@ namespace Chill\ActivityBundle\Tests\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\Common\Collections\ArrayCollection;
@@ -55,8 +56,30 @@ final class ActivityTypeFilterTest extends AbstractFilterTest
$data = [];
foreach ($array as $a) {
$data[] = [
'accepted_activitytypes' => [],
'date_after' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START),
'date_before' => new RollingDate(RollingDate::T_TODAY),
];
$data[] = [
'accepted_activitytypes' => new ArrayCollection([$a]),
'date_after' => null,
'date_before' => null,
];
$data[] = [
'accepted_activitytypes' => [$a],
'date_after' => null,
'date_before' => null,
];
$data[] = [
'accepted_activitytypes' => [$a],
'date_after' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START),
'date_before' => new RollingDate(RollingDate::T_TODAY),
];
$data[] = [
'accepted_activitytypes' => [],
'date_after' => null,
'date_before' => null,
];
}

View File

@@ -20,6 +20,14 @@ services:
tags:
- { name: chill.export, alias: 'count_person_on_activity' }
Chill\ActivityBundle\Export\Export\LinkedToACP\CountHouseholdOnActivity:
tags:
- { name: chill.export, alias: 'count_household_on_activity_acp' }
Chill\ActivityBundle\Export\Export\LinkedToPerson\CountHouseholdOnActivity:
tags:
- { name: chill.export, alias: 'count_household_on_activity_person' }
chill.activity.export.count_activity_linked_to_acp:
class: Chill\ActivityBundle\Export\Export\LinkedToACP\CountActivity
tags:

View File

@@ -3,7 +3,12 @@ export:
activity:
course_having_activity_between_date:
Only course having an activity between from and to: Seulement les parcours ayant reçu au moins un échange entre le {from, date, short} et le {to, date, short}
person_between_dates:
acp_by_activity_type:
'acp_containing_at_least_one_activitytypes': >-
Parcours filtrés: uniquement ceux qui contiennent au moins un échange d'un des types suivants: {activitytypes}
{has_date_after, select, 1 {, après le {date_after, date}} other {}}
{has_date_before, select, 1 {, avant le {date_before, date}} other {}}
describe_action_with_no_subject: >-
Filtré par personne ayant eu un échange entre le {date_from, date} et le {date_to, date}
describe_action_with_subject: >-

View File

@@ -337,6 +337,14 @@ export:
title: Nombre d'usagers concernés par les échanges
description: Compte le nombre d'usagers concernés par les échanges. Si un usager est présent dans plusieurs échanges, il n'est comptabilisé qu'une seule fois.
header: Nombre d'usagers concernés par des échanges
count_household_on_activity:
title: Nombre de ménages concernés par les échanges
description: Compte le nombre de ménages concernés par les échanges. Si un ménage est présent dans plusieurs échanges, il n'est comptabilisé qu'une seule fois. Les usagers sans ménages ne sont pas comptabilisés.
header: Nombre de ménage concernés par des échanges
count_household_on_activity_person:
title: Nombre de ménages concernés par les échanges
description: Compte le nombre de ménages concernés par les échanges. Si un ménage est présent dans plusieurs échanges, il n'est comptabilisé qu'une seule fois. Les usagers sans ménages ne sont pas comptabilisés. Lorsqu'un usager change de ménage, chaque ménage est comptabilisé une fois.
header: Nombre de ménage concernés par des échanges
list:
activity:
users name: Nom des utilisateurs
@@ -371,7 +379,10 @@ export:
Receiving an activity after: Ayant reçu un échange après le
Receiving an activity before: Ayant reçu un échange avant le
acp_by_activity_type:
'acp_containing_at_least_one_%activitytypes%': 'Parcours filtrés: uniquement ceux qui contiennent au moins un échange d''un des types suivants: %activitytypes%'
'activity after': Échanges après le
activity after help: Si laissé vide, ne sera pas pris en compte
activity before: Echanges avant le
activity before help: Si laissé vide, ne sera pas pris en compte
person_between_dates:
Implied in an activity after this date: Impliqué dans un échange après cette date
Implied in an activity before this date: Impliqué dans un échange avant cette date

View File

@@ -13,15 +13,11 @@ namespace Chill\AsideActivityBundle\Export\Filter;
use Chill\AsideActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\Export\FilterType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Contracts\Translation\TranslatorInterface;
class ByDateFilter implements FilterInterface
@@ -66,51 +62,14 @@ class ByDateFilter implements FilterInterface
->add('date_to', PickRollingDateType::class, [
'label' => 'export.filter.Aside activities before this date',
]);
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
/** @var \Symfony\Component\Form\FormInterface $filterForm */
$filterForm = $event->getForm()->getParent();
$enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData();
if (true === $enabled) {
// if the filter is enabled, add some validation
$form = $event->getForm();
$date_from = $form->get('date_from')->getData();
$date_to = $form->get('date_to')->getData();
// check that fields are not empty
if (null === $date_from) {
$form->get('date_from')->addError(new FormError(
$this->translator->trans('This field '
.'should not be empty')
));
}
if (null === $date_to) {
$form->get('date_to')->addError(new FormError(
$this->translator->trans('This field '
.'should not be empty')
));
}
// check that date_from is before date_to
if (
(null !== $date_from && null !== $date_to)
&& $date_from >= $date_to
) {
$form->get('date_to')->addError(new FormError(
$this->translator->trans('export.filter.This date should be after '
.'the date given in "Implied in an aside activity after '
.'this date" field')
));
}
}
});
}
public function getFormDefaultData(): array
{
return ['date_from' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), 'date_to' => new RollingDate(RollingDate::T_TODAY)];
return [
'date_from' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START),
'date_to' => new RollingDate(RollingDate::T_TODAY),
];
}
public function describeAction($data, $format = 'string'): array

View File

@@ -23,6 +23,6 @@ class ChargeKindController extends CRUDController
/* @var QueryBuilder $query */
$query->addOrderBy('e.ordering', 'ASC');
return $query;
return parent::orderQuery($action, $query, $request, $paginator);
}
}

View File

@@ -23,6 +23,6 @@ class ResourceKindController extends CRUDController
/* @var QueryBuilder $query */
$query->addOrderBy('e.ordering', 'ASC');
return $query;
return parent::orderQuery($action, $query, $request, $paginator);
}
}

View File

@@ -92,6 +92,11 @@ class UserHelper
}
$asStrings = [];
if (array_key_exists('uid', $decoded) || is_numeric($decoded)) {
// this is a single value. We have to wrap it into an array
$decoded = [$decoded];
}
foreach ($decoded as $userId) {
if (is_array($userId)) {
$uid = $userId['uid'];

View File

@@ -58,7 +58,7 @@ class AddressReferenceFromBano
foreach ($stmt as $record) {
$this->baseImporter->importAddress(
$record['refId'],
substr($record['refId'], 0, 5), // extract insee from reference
substr((string) $record['refId'], 0, 5), // extract insee from reference
$record['postcode'],
$record['street'],
$record['streetNumber'],

View File

@@ -23,7 +23,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface;
*/
class PostalCodeFRFromOpenData
{
private const CSV = 'https://datanova.legroupe.laposte.fr/explore/dataset/laposte_hexasmal/download/?format=csv&timezone=Europe/Berlin&lang=fr&use_labels_for_header=true&csv_separator=%3B';
private const CSV = 'https://datanova.laposte.fr/data-fair/api/v1/datasets/laposte-hexasmal/data-files/019HexaSmal.csv';
public function __construct(private readonly PostalCodeBaseImporter $baseImporter, private readonly HttpClientInterface $client, private readonly LoggerInterface $logger) {}

View File

@@ -41,7 +41,7 @@ class Comment implements TrackCreationInterface, TrackUpdateInterface
private ?AccompanyingPeriod $accompanyingPeriod = null;
/**
* @ORM\Column(type="text")
* @ORM\Column(type="text", nullable=false, options={"default":""})
*
* @Groups({"read", "write", "docgen:read"})
*

View File

@@ -17,6 +17,7 @@ use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
@@ -121,7 +122,9 @@ class CountHouseholdInPeriod implements ExportInterface, GroupedExportInterface
->join('acppart.accompanyingPeriod', 'acp')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
->andWhere('hmember.startDate <= :count_household_at_date AND (hmember.endDate IS NULL OR hmember.endDate > :count_household_at_date)')
->setParameter('count_household_at_date', $this->rollingDateConverter->convert($data['calc_date']));
->andWhere('acp.step != :count_acp_step')
->setParameter('count_household_at_date', $this->rollingDateConverter->convert($data['calc_date']))
->setParameter('count_acp_step', AccompanyingPeriod::STEP_DRAFT);
$qb
->select('COUNT(DISTINCT household.id) AS household_export_result')

View File

@@ -19,28 +19,22 @@ use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelperInterface;
use Chill\PersonBundle\Export\Helper\ListAccompanyingPeriodHelper;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class ListAccompanyingPeriod implements ListInterface, GroupedExportInterface
{
private bool $filterStatsByCenters;
public function __construct(
private EntityManagerInterface $entityManager,
private RollingDateConverterInterface $rollingDateConverter,
private ListAccompanyingPeriodHelper $listAccompanyingPeriodHelper,
ParameterBagInterface $parameterBag,
) {
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
private FilterListAccompanyingPeriodHelperInterface $filterListAccompanyingPeriodHelper,
) {}
public function buildForm(FormBuilderInterface $builder)
{
@@ -101,8 +95,6 @@ final readonly class ListAccompanyingPeriod implements ListInterface, GroupedExp
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->entityManager->createQueryBuilder();
$qb
@@ -110,18 +102,7 @@ final readonly class ListAccompanyingPeriod implements ListInterface, GroupedExp
->andWhere('acp.step != :list_acp_step')
->setParameter('list_acp_step', AccompanyingPeriod::STEP_DRAFT);
if ($this->filterStatsByCenters) {
$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);
}
$this->filterListAccompanyingPeriodHelper->addFilterAccompanyingPeriods($qb, $requiredModifiers, $acl, $data);
$this->listAccompanyingPeriodHelper->addSelectClauses($qb, $this->rollingDateConverter->convert($data['calc_date']));

View File

@@ -28,11 +28,11 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory;
use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Chill\PersonBundle\Entity\SocialWork\Goal;
use Chill\PersonBundle\Entity\SocialWork\Result;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelperInterface;
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
@@ -44,10 +44,9 @@ use Chill\ThirdPartyBundle\Export\Helper\LabelThirdPartyHelper;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\FormBuilderInterface;
class ListAccompanyingPeriodWorkAssociatePersonOnAccompanyingPeriod implements ListInterface, GroupedExportInterface
final readonly class ListAccompanyingPeriodWorkAssociatePersonOnAccompanyingPeriod implements ListInterface, GroupedExportInterface
{
private const FIELDS = [
'id',
@@ -79,8 +78,6 @@ class ListAccompanyingPeriodWorkAssociatePersonOnAccompanyingPeriod implements L
'updatedBy',
];
private readonly bool $filterStatsByCenters;
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly DateTimeHelper $dateTimeHelper,
@@ -94,10 +91,8 @@ class ListAccompanyingPeriodWorkAssociatePersonOnAccompanyingPeriod implements L
private readonly RollingDateConverterInterface $rollingDateConverter,
private readonly AggregateStringHelper $aggregateStringHelper,
private readonly SocialActionRepository $socialActionRepository,
ParameterBagInterface $parameterBag,
) {
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
private readonly FilterListAccompanyingPeriodHelperInterface $filterListAccompanyingPeriodHelper,
) {}
public function buildForm(FormBuilderInterface $builder)
{
@@ -223,17 +218,7 @@ class ListAccompanyingPeriodWorkAssociatePersonOnAccompanyingPeriod implements L
->andWhere('acppart.startDate <= :calc_date AND (acppart.endDate > :calc_date OR acppart.endDate IS NULL)')
->setParameter('calc_date', $this->rollingDateConverter->convert($calcDate));
if ($this->filterStatsByCenters) {
$qb
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
AND acl_count_person_history.center IN (:authorized_centers)
'
)
)
->setParameter('authorized_centers', $centers);
}
$this->filterListAccompanyingPeriodHelper->addFilterAccompanyingPeriods($qb, $requiredModifiers, $acl, $data);
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);

View File

@@ -28,11 +28,11 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory;
use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Chill\PersonBundle\Entity\SocialWork\Goal;
use Chill\PersonBundle\Entity\SocialWork\Result;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelperInterface;
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
@@ -44,10 +44,9 @@ use Chill\ThirdPartyBundle\Export\Helper\LabelThirdPartyHelper;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\FormBuilderInterface;
class ListAccompanyingPeriodWorkAssociatePersonOnWork implements ListInterface, GroupedExportInterface
final readonly class ListAccompanyingPeriodWorkAssociatePersonOnWork implements ListInterface, GroupedExportInterface
{
private const FIELDS = [
'id',
@@ -79,8 +78,6 @@ class ListAccompanyingPeriodWorkAssociatePersonOnWork implements ListInterface,
'updatedBy',
];
private readonly bool $filterStatsByCenters;
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly DateTimeHelper $dateTimeHelper,
@@ -94,10 +91,8 @@ class ListAccompanyingPeriodWorkAssociatePersonOnWork implements ListInterface,
private readonly RollingDateConverterInterface $rollingDateConverter,
private readonly AggregateStringHelper $aggregateStringHelper,
private readonly SocialActionRepository $socialActionRepository,
ParameterBagInterface $parameterBag,
) {
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
private FilterListAccompanyingPeriodHelperInterface $filterListAccompanyingPeriodHelper,
) {}
public function buildForm(FormBuilderInterface $builder)
{
@@ -218,17 +213,7 @@ class ListAccompanyingPeriodWorkAssociatePersonOnWork implements ListInterface,
->join('acpw.persons', 'person')
;
if ($this->filterStatsByCenters) {
$qb
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
AND acl_count_person_history.center IN (:authorized_centers)
'
)
)
->setParameter('authorized_centers', $centers);
}
$this->filterListAccompanyingPeriodHelper->addFilterAccompanyingPeriods($qb, $requiredModifiers, $acl, $data);
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);

View File

@@ -26,8 +26,8 @@ use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelperInterface;
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
@@ -37,10 +37,9 @@ use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\FormBuilderInterface;
class ListEvaluation implements ListInterface, GroupedExportInterface
final readonly class ListEvaluation implements ListInterface, GroupedExportInterface
{
private const FIELDS = [
'id',
@@ -69,8 +68,6 @@ class ListEvaluation implements ListInterface, GroupedExportInterface
'updatedBy',
];
private readonly bool $filterStatsByCenters;
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly SocialIssueRender $socialIssueRender,
@@ -83,10 +80,8 @@ class ListEvaluation implements ListInterface, GroupedExportInterface
private readonly TranslatableStringExportLabelHelper $translatableStringExportLabelHelper,
private readonly AggregateStringHelper $aggregateStringHelper,
private readonly RollingDateConverterInterface $rollingDateConverter,
ParameterBagInterface $parameterBag,
) {
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
private FilterListAccompanyingPeriodHelperInterface $filterListAccompanyingPeriodHelper,
) {}
public function buildForm(FormBuilderInterface $builder)
{
@@ -193,6 +188,7 @@ class ListEvaluation implements ListInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static fn ($el) => $el['center'], $acl);
$calcDate = $data['calc_date'] ?? new RollingDate(RollingDate::T_TODAY);
$qb = $this->entityManager->createQueryBuilder();
@@ -208,23 +204,13 @@ class ListEvaluation implements ListInterface, GroupedExportInterface
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
// get participants at the given date
->andWhere('acppart.startDate <= :calc_date AND (acppart.endDate > :calc_date OR acppart.endDate IS NULL)')
->setParameter('calc_date', $this->rollingDateConverter->convert($data['calc_date']));
->setParameter('calc_date', $this->rollingDateConverter->convert($calcDate));
if ($this->filterStatsByCenters) {
$qb
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
AND acl_count_person_history.center IN (:authorized_centers)
'
)
)
->setParameter('authorized_centers', $centers);
}
$this->filterListAccompanyingPeriodHelper->addFilterAccompanyingPeriods($qb, $requiredModifiers, $acl, $data);
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
$this->addSelectClauses($qb, $this->rollingDateConverter->convert($data['calc_date']));
$this->addSelectClauses($qb, $this->rollingDateConverter->convert($calcDate));
return $qb;
}
@@ -278,7 +264,7 @@ class ListEvaluation implements ListInterface, GroupedExportInterface
// referrers => at date XXXX
$qb
->addSelect('(SELECT JSON_BUILD_OBJECT(\'uid\', IDENTITY(history.user), \'d\', history.startDate) FROM '.UserHistory::class.' history '.
'WHERE history.accompanyingPeriod = acp AND history.startDate <= :calc_date AND (history.endDate IS NULL OR history.endDate > :calc_date)) AS referrers');
'WHERE history.accompanyingPeriod = acp AND history.startDate <= :calc_date AND (history.endDate IS NULL OR history.endDate > :calc_date)) AS acpw_referrers');
// persons
$qb

View File

@@ -20,6 +20,7 @@ use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdComposition;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
@@ -144,7 +145,9 @@ class ListHouseholdInPeriod implements ListInterface, GroupedExportInterface
->join('acppart.accompanyingPeriod', 'acp')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
->andWhere('hmember.startDate <= :count_household_at_date AND (hmember.endDate IS NULL OR hmember.endDate > :count_household_at_date)')
->setParameter('count_household_at_date', $this->rollingDateConverter->convert($data['calc_date']));
->andWhere('acp.step != :list_acp_step')
->setParameter('count_household_at_date', $this->rollingDateConverter->convert($data['calc_date']))
->setParameter('list_acp_step', AccompanyingPeriod::STEP_DRAFT);
if ($this->filterStatsByCenters) {
$qb

View File

@@ -20,15 +20,14 @@ use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelperInterface;
use Chill\PersonBundle\Export\Helper\ListAccompanyingPeriodHelper;
use Chill\PersonBundle\Export\Helper\ListPersonHelper;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use DateTimeImmutable;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\FormBuilderInterface;
/**
@@ -36,17 +35,13 @@ use Symfony\Component\Form\FormBuilderInterface;
*/
final readonly class ListPersonWithAccompanyingPeriodDetails implements ListInterface, GroupedExportInterface
{
private bool $filterStatsByCenters;
public function __construct(
private ListPersonHelper $listPersonHelper,
private ListAccompanyingPeriodHelper $listAccompanyingPeriodHelper,
private EntityManagerInterface $entityManager,
private RollingDateConverterInterface $rollingDateConverter,
ParameterBagInterface $parameterBag,
) {
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
private FilterListAccompanyingPeriodHelperInterface $filterListAccompanyingPeriodHelper,
) {}
public function buildForm(FormBuilderInterface $builder)
{
@@ -122,14 +117,7 @@ final readonly class ListPersonWithAccompanyingPeriodDetails implements ListInte
->join('acppart.accompanyingPeriod', 'acp')
->andWhere($qb->expr()->neq('acp.step', "'".AccompanyingPeriod::STEP_DRAFT."'"));
if ($this->filterStatsByCenters) {
$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);
}
$this->filterListAccompanyingPeriodHelper->addFilterAccompanyingPeriods($qb, $requiredModifiers, $acl, $data);
$this->listPersonHelper->addSelect($qb, ListPersonHelper::FIELDS, $this->rollingDateConverter->convert($data['address_date']));
$this->listAccompanyingPeriodHelper->addSelectClauses($qb, $this->rollingDateConverter->convert($data['address_date']));

View File

@@ -15,14 +15,14 @@ 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;
final readonly class EvaluationTypeFilter implements FilterInterface
{
public function __construct(private TranslatableStringHelper $translatableStringHelper) {}
public function __construct(private TranslatableStringHelper $translatableStringHelper, private EvaluationRepositoryInterface $evaluationRepository) {}
public function addRole(): ?string
{
@@ -31,16 +31,9 @@ final readonly class EvaluationTypeFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('eval.evaluation', ':evaluationtype');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->andWhere(
$qb->expr()->in('workeval.evaluation', ':evaluationtype')
);
$qb->setParameter('evaluationtype', $data['accepted_evaluationtype']);
}
@@ -51,11 +44,17 @@ final readonly class EvaluationTypeFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$evaluations = $this->evaluationRepository->findAllActive();
usort($evaluations, fn (Evaluation $a, Evaluation $b) => $this->translatableStringHelper->localize($a->getTitle()) <=> $this->translatableStringHelper->localize($b->getTitle()));
$builder->add('accepted_evaluationtype', EntityType::class, [
'class' => Evaluation::class,
'choices' => $evaluations,
'choice_label' => fn (Evaluation $ev): string => $this->translatableStringHelper->localize($ev->getTitle()),
'multiple' => true,
'expanded' => true,
'expanded' => false,
'attr' => ['class' => 'select2'],
]);
}

View File

@@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
/*
* 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.
*/
namespace Chill\PersonBundle\Export\Helper;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\CenterRepositoryInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Security\Core\Security;
/**
* Filter accompanying period list and related, removing confidential ones
* based on ACL rules.
*/
final readonly class FilterListAccompanyingPeriodHelper implements FilterListAccompanyingPeriodHelperInterface
{
private bool $filterStatsByCenters;
public function __construct(
private Security $security,
private CenterRepositoryInterface $centerRepository,
private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser,
ParameterBagInterface $parameterBag,
) {
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
public function addFilterAccompanyingPeriods(QueryBuilder &$qb, array $requiredModifiers, array $acl, array $data = []): void
{
$centers = match ($this->filterStatsByCenters) {
true => array_map(static fn ($el) => $el['center'], $acl),
false => $this->centerRepository->findAll(),
};
$user = $this->security->getUser();
if (!$user instanceof User) {
throw new \RuntimeException('only a regular user can run this export');
}
// add filtering on confidential accompanying period. The confidential is applyed on the current status of
// the accompanying period (we do not use the 'calc_date' here
$aclConditionsOrX = $qb->expr()->orX(
// either the current user is the refferer for the course
'acp.user = :list_acp_current_user',
);
$qb->setParameter('list_acp_current_user', $user);
$i = 0;
foreach ($centers as $center) {
$scopes = $this->authorizationHelperForCurrentUser->getReachableScopes(AccompanyingPeriodVoter::SEE_DETAILS, $center);
$scopesConfidential =
$this->authorizationHelperForCurrentUser->getReachableScopes(AccompanyingPeriodVoter::SEE_CONFIDENTIAL_ALL, $center);
$orScopes = $qb->expr()->orX();
foreach ($scopes as $scope) {
$scopeCondition = match (in_array($scope, $scopesConfidential, true)) {
true => ":scope_{$i} MEMBER OF acp.scopes",
false => $qb->expr()->andX(
'acp.confidential = FALSE',
":scope_{$i} MEMBER OF acp.scopes",
),
};
$orScopes->add($scopeCondition);
$qb->setParameter("scope_{$i}", $scope);
++$i;
}
if ($this->filterStatsByCenters) {
$andX = $qb->expr()->andX(
$qb->expr()->exists(
'SELECT 1 FROM '.AccompanyingPeriodParticipation::class." acl_count_part_{$i}
JOIN ".PersonCenterHistory::class." acl_count_person_history_{$i} WITH IDENTITY(acl_count_person_history_{$i}.person) = IDENTITY(acl_count_part_{$i}.person)
WHERE acl_count_part_{$i}.accompanyingPeriod = acp.id AND acl_count_person_history_{$i}.center IN (:authorized_center_{$i})
AND acl_count_person_history_{$i}.startDate <= CURRENT_DATE() AND (acl_count_person_history_{$i}.endDate IS NULL or acl_count_person_history_{$i}.endDate > CURRENT_DATE())
"
),
$orScopes,
);
$qb->setParameter('authorized_center_'.$i, $center);
$aclConditionsOrX->add($andX);
} else {
$aclConditionsOrX->add($orScopes);
}
++$i;
}
$qb->andWhere($aclConditionsOrX);
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
/*
* 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.
*/
namespace Chill\PersonBundle\Export\Helper;
use Doctrine\ORM\QueryBuilder;
/**
* Filter accompanying period list and related, removing confidential ones
* based on ACL rules.
*/
interface FilterListAccompanyingPeriodHelperInterface
{
public function addFilterAccompanyingPeriods(QueryBuilder &$qb, array $requiredModifiers, array $acl, array $data = []): void;
}

View File

@@ -26,6 +26,7 @@ class AccompanyingCourseCommentType extends AbstractType
{
$builder->add('content', ChillTextareaType::class, [
'required' => false,
'empty_data' => '',
]);
}

View File

@@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Controller;
use Chill\MainBundle\Test\PrepareClientTrait;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Relationships\Relation;
use Chill\PersonBundle\Entity\Relationships\Relationship;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Request;
@@ -43,31 +44,34 @@ final class RelationshipApiControllerTest extends WebTestCase
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
$countPersons = $em->createQueryBuilder()
->select('count(p)')
->from(Person::class, 'p')
->join('p.centerCurrent', 'center_current')
->join('center_current.center', 'c')
->where('c.name LIKE :name')
->setParameter('name', 'Center A')
->getQuery()
->getSingleScalarResult();
$person = $em->createQueryBuilder()
->select('p')
$personIdHavingRelation = $em->createQueryBuilder()
->select('p.id')
->from(Person::class, 'p')
->join('p.centerCurrent', 'center_current')
->join('center_current.center', 'c')
->where('c.name LIKE :name')
->andWhere('EXISTS (SELECT 1 FROM '.Relationship::class.' r WHERE r.fromPerson = p OR r.toPerson = p)')
->setParameter('name', 'Center A')
->getQuery()
->setMaxResults(1)
->setFirstResult(\random_int(0, $countPersons - 1))
->getSingleResult();
->getSingleScalarResult();
$personIdWithoutRelation = $em->createQueryBuilder()
->select('p.id')
->from(Person::class, 'p')
->join('p.centerCurrent', 'center_current')
->join('center_current.center', 'c')
->where('c.name LIKE :name')
->andWhere('NOT EXISTS (SELECT 1 FROM '.Relationship::class.' r WHERE r.fromPerson = p OR r.toPerson = p)')
->setParameter('name', 'Center A')
->getQuery()
->setMaxResults(1)
->getSingleScalarResult();
self::ensureKernelShutdown();
return [
[$person->getId()],
[$personIdHavingRelation, $personIdWithoutRelation],
];
}
@@ -75,39 +79,29 @@ final class RelationshipApiControllerTest extends WebTestCase
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
$countPersons = $em->createQueryBuilder()
->select('count(DISTINCT p)')
->from(Person::class, 'p')
->join('p.centerCurrent', 'center_current')
->join('center_current.center', 'c')
->where('c.name LIKE :name')
->setParameter('name', 'Center A')
->getQuery()
->getSingleScalarResult();
$persons = $em->createQueryBuilder()
->select('p')
$personIdWithoutRelations = $em->createQueryBuilder()
->select('p.id')
->from(Person::class, 'p')
->join('p.centerCurrent', 'center_current')
->join('center_current.center', 'c')
->where('c.name LIKE :name')
->andWhere('NOT EXISTS (SELECT 1 FROM '.Relationship::class.' r WHERE r.fromPerson = p OR r.toPerson = p)')
->setParameter('name', 'Center A')
->getQuery()
->setMaxResults(2)
->setFirstResult(\random_int(0, $countPersons - 1))
->getResult();
self::ensureKernelShutdown();
return [
[$persons[0]->getId(), $persons[1]->getId(), $this->getRandomRelation($em)->getId(), true],
[$personIdWithoutRelations[0]['id'], $personIdWithoutRelations[1]['id'], $this->getRandomRelation($em)->getId(), true],
];
}
/**
* @dataProvider personProvider
*/
public function testGetRelationshipByPerson(mixed $personId)
public function testGetRelationshipByPerson(int $personId)
{
self::ensureKernelShutdown();
$client = $this->getClientAuthenticated();

View File

@@ -11,14 +11,23 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Tests\Export\Export;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\CenterRepositoryInterface;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Test\Export\AbstractExportTest;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Export\ListAccompanyingPeriod;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelper;
use Chill\PersonBundle\Export\Helper\ListAccompanyingPeriodHelper;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\EntityManagerInterface;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\Security\Core\Security;
/**
* @internal
@@ -27,6 +36,8 @@ use Doctrine\ORM\EntityManagerInterface;
*/
class ListAccompanyingPeriodTest extends AbstractExportTest
{
use ProphecyTrait;
private readonly ListAccompanyingPeriod $listAccompanyingPeriod;
private readonly CenterRepositoryInterface $centerRepository;
@@ -39,12 +50,54 @@ class ListAccompanyingPeriodTest extends AbstractExportTest
public function getExport()
{
/** @var EntityManagerInterface::class $em */
$em = self::$container->get(EntityManagerInterface::class);
$rollingDateConverter = self::$container->get(RollingDateConverterInterface::class);
$listAccompanyingPeriodHelper = self::$container->get(ListAccompanyingPeriodHelper::class);
$centerRepository = self::$container->get(CenterRepositoryInterface::class);
$scopeRepository = self::$container->get(ScopeRepositoryInterface::class);
yield new ListAccompanyingPeriod($em, $rollingDateConverter, $listAccompanyingPeriodHelper, $this->getParameters(true));
yield new ListAccompanyingPeriod($em, $rollingDateConverter, $listAccompanyingPeriodHelper, $this->getParameters(false));
// mock security
$user = $em->createQuery('SELECT u FROM '.User::class.' u')
->setMaxResults(1)->getSingleResult();
if (null === $user) {
throw new \RuntimeException('no user found');
}
$security = $this->prophesize(Security::class);
$security->getUser()->willReturn($user);
// mock authorization helper
$scopes = $scopeRepository->findAll();
$scopesConfidentials = [] !== $scopes ? [$scopes[0]] : [];
$authorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class);
$authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE_DETAILS, Argument::type(Center::class))
->willReturn($scopes);
$authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE_CONFIDENTIAL_ALL, Argument::type(Center::class))
->willReturn($scopesConfidentials);
yield new ListAccompanyingPeriod(
$em,
$rollingDateConverter,
$listAccompanyingPeriodHelper,
new FilterListAccompanyingPeriodHelper(
$security->reveal(),
$centerRepository,
$authorizationHelper->reveal(),
$this->getParameters(true)
)
);
yield new ListAccompanyingPeriod(
$em,
$rollingDateConverter,
$listAccompanyingPeriodHelper,
new FilterListAccompanyingPeriodHelper(
$security->reveal(),
$centerRepository,
$authorizationHelper->reveal(),
$this->getParameters(false)
)
);
}
public function getFormData()

View File

@@ -20,6 +20,7 @@ use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Test\Export\AbstractExportTest;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Export\ListAccompanyingPeriodWorkAssociatePersonOnAccompanyingPeriod;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelperInterface;
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
@@ -27,6 +28,7 @@ use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Chill\ThirdPartyBundle\Export\Helper\LabelThirdPartyHelper;
use Doctrine\ORM\EntityManagerInterface;
use Prophecy\PhpUnit\ProphecyTrait;
/**
* @internal
@@ -35,6 +37,8 @@ use Doctrine\ORM\EntityManagerInterface;
*/
class ListAccompanyingPeriodWorkAssociatePersonOnAccompanyingPeriodTest extends AbstractExportTest
{
use ProphecyTrait;
protected function setUp(): void
{
parent::setUp();
@@ -55,6 +59,7 @@ class ListAccompanyingPeriodWorkAssociatePersonOnAccompanyingPeriodTest extends
$rollingDateConverter = self::$container->get(RollingDateConverterInterface::class);
$aggregateStringHelper = self::$container->get(AggregateStringHelper::class);
$socialActionRepository = self::$container->get(SocialActionRepository::class);
$filterListAccompanyingPeriodHelper = $this->prophesize(FilterListAccompanyingPeriodHelperInterface::class);
yield new ListAccompanyingPeriodWorkAssociatePersonOnAccompanyingPeriod(
$entityManager,
@@ -69,23 +74,7 @@ class ListAccompanyingPeriodWorkAssociatePersonOnAccompanyingPeriodTest extends
$rollingDateConverter,
$aggregateStringHelper,
$socialActionRepository,
$this->getParameters(true),
);
yield new ListAccompanyingPeriodWorkAssociatePersonOnAccompanyingPeriod(
$entityManager,
$dateTimeHelper,
$userHelper,
$personHelper,
$thirdPartyHelper,
$translatableStringExportLabelHelper,
$socialIssueRender,
$socialIssueRepository,
$socialActionRender,
$rollingDateConverter,
$aggregateStringHelper,
$socialActionRepository,
$this->getParameters(false),
$filterListAccompanyingPeriodHelper->reveal(),
);
}

View File

@@ -20,6 +20,7 @@ use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Test\Export\AbstractExportTest;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Export\ListAccompanyingPeriodWorkAssociatePersonOnWork;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelperInterface;
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
@@ -27,6 +28,7 @@ use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Chill\ThirdPartyBundle\Export\Helper\LabelThirdPartyHelper;
use Doctrine\ORM\EntityManagerInterface;
use Prophecy\PhpUnit\ProphecyTrait;
/**
* @internal
@@ -35,6 +37,8 @@ use Doctrine\ORM\EntityManagerInterface;
*/
class ListAccompanyingPeriodWorkAssociatePersonOnWorkTest extends AbstractExportTest
{
use ProphecyTrait;
protected function setUp(): void
{
parent::setUp();
@@ -55,6 +59,7 @@ class ListAccompanyingPeriodWorkAssociatePersonOnWorkTest extends AbstractExport
$rollingDateConverter = self::$container->get(RollingDateConverterInterface::class);
$aggregateStringHelper = self::$container->get(AggregateStringHelper::class);
$socialActionRepository = self::$container->get(SocialActionRepository::class);
$filterHelper = $this->prophesize(FilterListAccompanyingPeriodHelperInterface::class);
yield new ListAccompanyingPeriodWorkAssociatePersonOnWork(
$entityManager,
@@ -69,23 +74,7 @@ class ListAccompanyingPeriodWorkAssociatePersonOnWorkTest extends AbstractExport
$rollingDateConverter,
$aggregateStringHelper,
$socialActionRepository,
$this->getParameters(true),
);
yield new ListAccompanyingPeriodWorkAssociatePersonOnWork(
$entityManager,
$dateTimeHelper,
$userHelper,
$personHelper,
$thirdPartyHelper,
$translatableStringExportLabelHelper,
$socialIssueRender,
$socialIssueRepository,
$socialActionRender,
$rollingDateConverter,
$aggregateStringHelper,
$socialActionRepository,
$this->getParameters(false),
$filterHelper->reveal(),
);
}

View File

@@ -11,7 +11,6 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Tests\Export\Export;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Export\Helper\AggregateStringHelper;
use Chill\MainBundle\Export\Helper\DateTimeHelper;
use Chill\MainBundle\Export\Helper\TranslatableStringExportLabelHelper;
@@ -22,13 +21,14 @@ use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Test\Export\AbstractExportTest;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Export\ListEvaluation;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelperInterface;
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Prophecy\PhpUnit\ProphecyTrait;
/**
* @internal
@@ -37,7 +37,7 @@ use Doctrine\ORM\EntityManagerInterface;
*/
class ListEvaluationTest extends AbstractExportTest
{
private ListEvaluation $listEvaluation;
use ProphecyTrait;
private CenterRepositoryInterface $centerRepository;
@@ -46,7 +46,6 @@ class ListEvaluationTest extends AbstractExportTest
parent::setUp();
self::bootKernel();
$this->listEvaluation = self::$container->get(ListEvaluation::class);
$this->centerRepository = self::$container->get(CenterRepositoryInterface::class);
}
@@ -63,6 +62,7 @@ class ListEvaluationTest extends AbstractExportTest
$rollingDateConverter = self::$container->get(RollingDateConverterInterface::class);
$aggregateStringHelper = self::$container->get(AggregateStringHelper::class);
$socialActionRepository = self::$container->get(SocialActionRepository::class);
$filterListHelper = $this->prophesize(FilterListAccompanyingPeriodHelperInterface::class);
yield new ListEvaluation(
$entityManager,
@@ -76,22 +76,7 @@ class ListEvaluationTest extends AbstractExportTest
$translatableStringExportLabelHelper,
$aggregateStringHelper,
$rollingDateConverter,
$this->getParameters(true),
);
yield new ListEvaluation(
$entityManager,
$socialIssueRender,
$socialIssueRepository,
$socialActionRender,
$socialActionRepository,
$userHelper,
$personHelper,
$dateTimeHelper,
$translatableStringExportLabelHelper,
$aggregateStringHelper,
$rollingDateConverter,
$this->getParameters(false),
$filterListHelper->reveal(),
);
}
@@ -104,14 +89,4 @@ class ListEvaluationTest extends AbstractExportTest
{
return [[Declarations::ACP_TYPE]];
}
public function testQuery(): void
{
$centers = $this->centerRepository->findAll();
$query = $this->listEvaluation->initiateQuery([], array_map(fn (Center $c) => ['center' => $c], $centers), ['calc_date' => new RollingDate(RollingDate::T_TODAY)]);
$query->setMaxResults(1);
self::assertIsArray($query->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY));
}
}

View File

@@ -16,9 +16,11 @@ use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Test\Export\AbstractExportTest;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Export\ListPersonWithAccompanyingPeriodDetails;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelperInterface;
use Chill\PersonBundle\Export\Helper\ListAccompanyingPeriodHelper;
use Chill\PersonBundle\Export\Helper\ListPersonHelper;
use Doctrine\ORM\EntityManagerInterface;
use Prophecy\PhpUnit\ProphecyTrait;
/**
* @internal
@@ -27,6 +29,8 @@ use Doctrine\ORM\EntityManagerInterface;
*/
class ListPersonWithAccompanyingPeriodDetailsTest extends AbstractExportTest
{
use ProphecyTrait;
protected function setUp(): void
{
parent::setUp();
@@ -39,21 +43,14 @@ class ListPersonWithAccompanyingPeriodDetailsTest extends AbstractExportTest
$listAccompanyingPeriodHelper = self::$container->get(ListAccompanyingPeriodHelper::class);
$entityManager = self::$container->get(EntityManagerInterface::class);
$rollingDateConverter = self::$container->get(RollingDateConverterInterface::class);
$filterHelper = $this->prophesize(FilterListAccompanyingPeriodHelperInterface::class);
yield new ListPersonWithAccompanyingPeriodDetails(
$listPersonHelper,
$listAccompanyingPeriodHelper,
$entityManager,
$rollingDateConverter,
$this->getParameters(true),
);
yield new ListPersonWithAccompanyingPeriodDetails(
$listPersonHelper,
$listAccompanyingPeriodHelper,
$entityManager,
$rollingDateConverter,
$this->getParameters(false),
$filterHelper->reveal()
);
}

View File

@@ -69,10 +69,10 @@ final class EvaluationTypeFilterTest extends AbstractFilterTest
return [
$em->createQueryBuilder()
->select('eval.id')
->select('workeval.id')
->from(AccompanyingPeriod::class, 'acp')
->join('acp.works', 'acpw')
->join('acpw.accompanyingPeriodWorkEvaluations', 'eval'),
->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'),
];
}
}

View File

@@ -0,0 +1,137 @@
<?php
declare(strict_types=1);
/*
* 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.
*/
namespace Chill\PersonBundle\Tests\Export\Helper;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\CenterRepositoryInterface;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelper;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Security\Core\Security;
/**
* @internal
*
* @coversNothing
*/
final class FilterListAccompanyingPeriodHelperTest extends KernelTestCase
{
use ProphecyTrait;
private CenterRepositoryInterface $centerRepository;
private ScopeRepositoryInterface $scopeRepository;
private EntityManagerInterface $entityManager;
protected function setUp(): void
{
parent::setUp();
self::bootKernel();
$this->centerRepository = self::$container->get(CenterRepositoryInterface::class);
$this->scopeRepository = self::$container->get(ScopeRepositoryInterface::class);
$this->entityManager = self::$container->get(EntityManagerInterface::class);
}
/**
* @dataProvider dataProviderTestAddFilterAccompanyingPeriod
*/
public function testAddFilterAccompanyingPeriod(QueryBuilder $qb, ParameterBagInterface $parameterBag): void
{
// mock security
$user = $this->entityManager->createQuery('SELECT u FROM '.User::class.' u')
->setMaxResults(1)->getSingleResult();
if (null === $user) {
throw new \RuntimeException('no user found');
}
$security = $this->prophesize(Security::class);
$security->getUser()->willReturn($user);
// mock authorization helper
$scopes = $this->scopeRepository->findAll();
$scopesConfidentials = [] !== $scopes ? [$scopes[0]] : [];
$authorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class);
$authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE_DETAILS, Argument::type(Center::class))
->willReturn($scopes);
$authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE_CONFIDENTIAL_ALL, Argument::type(Center::class))
->willReturn($scopesConfidentials);
$filter = new FilterListAccompanyingPeriodHelper(
$security->reveal(),
$this->centerRepository,
$authorizationHelper->reveal(),
$parameterBag
);
$filter->addFilterAccompanyingPeriods($qb, [], $this->getACL(), []);
$qb->setMaxResults(1);
$result = $qb->getQuery()->getResult();
self::assertIsArray($result);
}
public function dataProviderTestAddFilterAccompanyingPeriod(): iterable
{
self::setUp();
$qb = $this->entityManager->createQueryBuilder();
$qb
->select('acp.id')
->from(AccompanyingPeriod::class, 'acp');
yield [
$qb,
new ParameterBag(['chill_main' => ['acl' => ['filter_stats_by_center' => true]]]),
];
yield [
$qb,
new ParameterBag(['chill_main' => ['acl' => ['filter_stats_by_center' => false]]]),
];
}
/**
* @return list<array{center: Center, circles: list<Scope>}> the ACL, structured as an array
*
* @throws \RuntimeException when no center or circle is found
*/
private function getACL(): array
{
$centers = $this->centerRepository->findAll();
$circles = $this->scopeRepository->findAll();
if (0 === \count($centers)) {
throw new \RuntimeException('No center found. Did you forget to run `doctrine:fixtures:load` command before ?');
}
if (0 === \count($circles)) {
throw new \RuntimeException('No circle found. Did you forget to run `doctrine:fixtures:load` command before ?');
}
return [[
'center' => $centers[0],
'circles' => [
$circles,
], ]];
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/*
* 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.
*/
namespace Chill\Migrations\Person;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20231128143534 extends AbstractMigration
{
public function getDescription(): string
{
return 'Set a default for the content column of accompanying_period_comment';
}
public function up(Schema $schema): void
{
$this->addSql("UPDATE chill_person_accompanying_period_comment SET content='' WHERE content IS NULL");
$this->addSql('ALTER TABLE chill_person_accompanying_period_comment ALTER COLUMN content SET NOT NULL');
$this->addSql('ALTER TABLE chill_person_accompanying_period_comment ALTER COLUMN content SET DEFAULT \'\'');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_person_accompanying_period_comment ALTER COLUMN content DROP DEFAULT');
$this->addSql('ALTER TABLE chill_person_accompanying_period_comment ALTER COLUMN content DROP NOT NULL');
}
}

View File

@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
/*
* 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.
*/
namespace Chill\Migrations\Person;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Fix lines in chill_person_person_center_history for people created before the introduction of the createdAt column.
*
* This class represents a migration for fixing lines in the 'chill_person_person_center_history' table.
*
* It updates the 'startdate' column for people created before the introduction
* of the 'createdAt' column, and set it at the first activity date. This migration is irreversible.
*/
final class Version20231207221700 extends AbstractMigration
{
public function getDescription(): string
{
return 'Fix lines in chill_person_person_center_history for people created before the introduction of the createdAt column';
}
public function up(Schema $schema): void
{
$this->addSql('WITH first_history_line AS (SELECT *
FROM (SELECT id,
person_id,
startdate,
rank() OVER (PARTITION BY person_id ORDER BY startdate ASC, id ASC) AS r
FROM chill_person_person_center_history) AS sk
WHERE sk.r = 1),
first_activity AS (SELECT *
FROM (SELECT id, date, person_id, rank() OVER (PARTITION BY person_id ORDER BY date ASC, id ASC) AS r
FROM activity
WHERE person_id IS NOT NULL) sq
WHERE sq.r = 1)
UPDATE chill_person_person_center_history cppch SET startdate=first_activity.date
FROM first_history_line, first_activity
WHERE
first_history_line.id = cppch.id
AND first_activity.person_id = cppch.person_id
AND first_activity.date < first_history_line.startDate');
}
public function down(Schema $schema): void
{
$this->throwIrreversibleMigrationException();
}
}