Merge branch 'master' into testing-202401

This commit is contained in:
Julien Fastré 2024-02-19 11:31:42 +01:00
commit 1004e98acd
Signed by: julienfastre
GPG Key ID: BDE2190974723FCB
55 changed files with 2095 additions and 108 deletions

View File

@ -0,0 +1,6 @@
kind: Feature
body: Create new filter for persons having a participation in an accompanying period
during a certain time span
time: 2023-12-18T15:31:51.489901829+01:00
custom:
Issue: "231"

View File

@ -0,0 +1,6 @@
kind: Feature
body: '[Export][List of accompanyign period] Add two columns: the list of persons
participating to the period, and their ids'
time: 2024-01-22T12:48:49.824833412+01:00
custom:
Issue: "241"

View File

@ -0,0 +1,5 @@
kind: Feature
body: 'Add capability to generate export about change of steps of accompanying period, and generate exports for this'
time: 2024-01-29T13:33:19.190365565+01:00
custom:
Issue: "244"

View File

@ -0,0 +1,5 @@
kind: Feature
body: 'Export: group accompanying period by person participating'
time: 2024-02-07T10:39:51.97331052+01:00
custom:
Issue: "253"

View File

@ -0,0 +1,5 @@
kind: Feature
body: 'Export: add filter for courses not linked to a reference address'
time: 2024-02-07T11:46:29.491027007+01:00
custom:
Issue: "243"

View File

@ -0,0 +1,5 @@
kind: Feature
body: Allow to group activities linked with accompanying period by reason
time: 2024-02-07T16:40:38.408575109+01:00
custom:
Issue: "229"

View File

@ -0,0 +1,6 @@
kind: Fixed
body: Fix error in logs about wrong typing of eventArgs in onEditNotificationComment
method
time: 2023-11-29T11:31:38.933538592+01:00
custom:
Issue: "220"

View File

@ -0,0 +1,6 @@
kind: Fixed
body: Fix the conditions upon which social actions should be optional or required
in relation to social issues within the activity creation form
time: 2024-01-30T14:03:01.942955636+01:00
custom:
Issue: "256"

View File

@ -34,6 +34,8 @@ variables:
DEFAULT_CARRIER_CODE: BE
# force a timezone
TZ: Europe/Brussels
# avoid direct deprecations (using symfony phpunit bridge: https://symfony.com/doc/4.x/components/phpunit_bridge.html#internal-deprecations
SYMFONY_DEPRECATIONS_HELPER: max[total]=99999999&max[self]=0&max[direct]=0&verbose=0
stages:
- Composer install

View File

@ -48,7 +48,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
git clone https://gitea.champs-libres.be/Chill-project/chill-skeleton-basic.git
cd chill-skeleton-basic

View File

@ -5,72 +5,74 @@ Add condition with distinct alias on each export join clauses (Indicators + Filt
These are alias conventions :
| Entity | Join | Attribute | Alias |
|:----------------------------------------|:----------------------------------------|:-------------------------------------------|:---------------------------------------|
| AccompanyingPeriod::class | | | acp |
| | AccompanyingPeriodWork::class | acp.works | acpw |
| | AccompanyingPeriodParticipation::class | acp.participations | acppart |
| | Location::class | acp.administrativeLocation | acploc |
| | ClosingMotive::class | acp.closingMotive | acpmotive |
| | UserJob::class | acp.job | acpjob |
| | Origin::class | acp.origin | acporigin |
| | Scope::class | acp.scopes | acpscope |
| | SocialIssue::class | acp.socialIssues | acpsocialissue |
| | User::class | acp.user | acpuser |
| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories |
| | AccompanyingPeriodInfo::class | not existing (using custom WITH clause) | acpinfo |
| AccompanyingPeriodWork::class | | | acpw |
| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval |
| | SocialAction::class | acpw.socialAction | acpwsocialaction |
| | Goal::class | acpw.goals | goal |
| | Result::class | acpw.results | result |
| AccompanyingPeriodParticipation::class | | | acppart |
| | Person::class | acppart.person | partperson |
| AccompanyingPeriodWorkEvaluation::class | | | workeval |
| | Evaluation::class | workeval.evaluation | eval |
| AccompanyingPeriodInfo::class | | | acpinfo |
| | User::class | acpinfo.user | acpinfo_user |
| Goal::class | | | goal |
| | Result::class | goal.results | goalresult |
| Person::class | | | person |
| | Center::class | person.center | center |
| | HouseholdMember::class | partperson.householdParticipations | householdmember |
| | MaritalStatus::class | person.maritalStatus | personmarital |
| | VendeePerson::class | | vp |
| | VendeePersonMineur::class | | vpm |
| | CurrentPersonAddress::class | person.currentPersonAddress | currentPersonAddress (on a given date) |
| ResidentialAddress::class | | | resaddr |
| | ThirdParty::class | resaddr.hostThirdParty | tparty |
| ThirdParty::class | | | tparty |
| | ThirdPartyCategory::class | tparty.categories | tpartycat |
| HouseholdMember::class | | | householdmember |
| | Household::class | householdmember.household | household |
| | Person::class | householdmember.person | memberperson |
| | | memberperson.center | membercenter |
| Household::class | | | household |
| | HouseholdComposition::class | household.compositions | composition |
| Activity::class | | | activity |
| | Person::class | activity.person | actperson |
| | AccompanyingPeriod::class | activity.accompanyingPeriod | acp |
| | Person::class | activity\_person\_having\_activity.person | person\_person\_having\_activity |
| | ActivityReason::class | activity\_person\_having\_activity.reasons | reasons\_person\_having\_activity |
| | ActivityType::class | activity.activityType | acttype |
| | Location::class | activity.location | actloc |
| | SocialAction::class | activity.socialActions | actsocialaction |
| | SocialIssue::class | activity.socialIssues | actsocialssue |
| | ThirdParty::class | activity.thirdParties | acttparty |
| | User::class | activity.user | actuser |
| | User::class | activity.users | actusers |
| | ActivityReason::class | activity.reasons | actreasons |
| | Center::class | actperson.center | actcenter |
| | Person::class | activity.createdBy | actcreator |
| ActivityReason::class | | | actreasons |
| | ActivityReasonCategory::class | actreason.category | actreasoncat |
| Calendar::class | | | cal |
| | CancelReason::class | cal.cancelReason | calcancel |
| | Location::class | cal.location | calloc |
| | User::class | cal.user | caluser |
| VendeePerson::class | | | vp |
| | SituationProfessionelle::class | vp.situationProfessionelle | vpprof |
| | StatutLogement::class | vp.statutLogement | vplog |
| | TempsDeTravail::class | vp.tempsDeTravail | vptt |
| Entity | Join | Attribute | Alias |
|:----------------------------------------|:----------------------------------------|:-------------------------------------------|:-------------------------------------------|
| AccompanyingPeriodStepHistory::class | | | acpstephistory (contexte ACP_STEP_HISTORY) |
| | AccompanyingPeriod::class | acpstephistory.period | acp |
| AccompanyingPeriod::class | | | acp |
| | AccompanyingPeriodWork::class | acp.works | acpw |
| | AccompanyingPeriodParticipation::class | acp.participations | acppart |
| | Location::class | acp.administrativeLocation | acploc |
| | ClosingMotive::class | acp.closingMotive | acpmotive |
| | UserJob::class | acp.job | acpjob |
| | Origin::class | acp.origin | acporigin |
| | Scope::class | acp.scopes | acpscope |
| | SocialIssue::class | acp.socialIssues | acpsocialissue |
| | User::class | acp.user | acpuser |
| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories |
| | AccompanyingPeriodInfo::class | not existing (using custom WITH clause) | acpinfo |
| AccompanyingPeriodWork::class | | | acpw |
| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval |
| | SocialAction::class | acpw.socialAction | acpwsocialaction |
| | Goal::class | acpw.goals | goal |
| | Result::class | acpw.results | result |
| AccompanyingPeriodParticipation::class | | | acppart |
| | Person::class | acppart.person | partperson |
| AccompanyingPeriodWorkEvaluation::class | | | workeval |
| | Evaluation::class | workeval.evaluation | eval |
| AccompanyingPeriodInfo::class | | | acpinfo |
| | User::class | acpinfo.user | acpinfo_user |
| Goal::class | | | goal |
| | Result::class | goal.results | goalresult |
| Person::class | | | person |
| | Center::class | person.center | center |
| | HouseholdMember::class | partperson.householdParticipations | householdmember |
| | MaritalStatus::class | person.maritalStatus | personmarital |
| | VendeePerson::class | | vp |
| | VendeePersonMineur::class | | vpm |
| | CurrentPersonAddress::class | person.currentPersonAddress | currentPersonAddress (on a given date) |
| ResidentialAddress::class | | | resaddr |
| | ThirdParty::class | resaddr.hostThirdParty | tparty |
| ThirdParty::class | | | tparty |
| | ThirdPartyCategory::class | tparty.categories | tpartycat |
| HouseholdMember::class | | | householdmember |
| | Household::class | householdmember.household | household |
| | Person::class | householdmember.person | memberperson |
| | | memberperson.center | membercenter |
| Household::class | | | household |
| | HouseholdComposition::class | household.compositions | composition |
| Activity::class | | | activity |
| | Person::class | activity.person | actperson |
| | AccompanyingPeriod::class | activity.accompanyingPeriod | acp |
| | Person::class | activity\_person\_having\_activity.person | person\_person\_having\_activity |
| | ActivityReason::class | activity\_person\_having\_activity.reasons | reasons\_person\_having\_activity |
| | ActivityType::class | activity.activityType | acttype |
| | Location::class | activity.location | actloc |
| | SocialAction::class | activity.socialActions | actsocialaction |
| | SocialIssue::class | activity.socialIssues | actsocialssue |
| | ThirdParty::class | activity.thirdParties | acttparty |
| | User::class | activity.user | actuser |
| | User::class | activity.users | actusers |
| | ActivityReason::class | activity.reasons | actreasons |
| | Center::class | actperson.center | actcenter |
| | Person::class | activity.createdBy | actcreator |
| ActivityReason::class | | | actreasons |
| | ActivityReasonCategory::class | actreason.category | actreasoncat |
| Calendar::class | | | cal |
| | CancelReason::class | cal.cancelReason | calcancel |
| | Location::class | cal.location | calloc |
| | User::class | cal.user | caluser |
| VendeePerson::class | | | vp |
| | SituationProfessionelle::class | vp.situationProfessionelle | vpprof |
| | StatutLogement::class | vp.statutLogement | vplog |
| | TempsDeTravail::class | vp.tempsDeTravail | vptt |

View File

@ -291,7 +291,11 @@ class ActivityType
public function checkSocialActionsVisibility(ExecutionContextInterface $context, mixed $payload)
{
if ($this->socialIssuesVisible !== $this->socialActionsVisible) {
if (!(2 === $this->socialIssuesVisible && 1 === $this->socialActionsVisible)) {
// if social issues are invisible then social actions cannot be optional or required + if social issues are optional then social actions shouldn't be required
if (
(0 === $this->socialIssuesVisible && (1 === $this->socialActionsVisible || 2 === $this->socialActionsVisible))
|| (1 === $this->socialIssuesVisible && 2 === $this->socialActionsVisible)
) {
$context
->buildViolation('The socialActionsVisible value is not compatible with the socialIssuesVisible value')
->atPath('socialActionsVisible')

View File

@ -9,7 +9,7 @@ declare(strict_types=1);
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator\PersonAggregators;
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository;
@ -25,8 +25,11 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
class ActivityReasonAggregator implements AggregatorInterface, ExportElementValidatedInterface
{
public function __construct(protected ActivityReasonCategoryRepository $activityReasonCategoryRepository, protected ActivityReasonRepository $activityReasonRepository, protected TranslatableStringHelper $translatableStringHelper)
{
public function __construct(
protected ActivityReasonCategoryRepository $activityReasonCategoryRepository,
protected ActivityReasonRepository $activityReasonRepository,
protected TranslatableStringHelper $translatableStringHelper
) {
}
public function addRole(): ?string
@ -51,7 +54,7 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
// make a jointure only if needed
if (!\in_array('actreasons', $qb->getAllAliases(), true)) {
$qb->innerJoin('activity.reasons', 'actreasons');
$qb->leftJoin('activity.reasons', 'actreasons');
}
// join category if necessary
@ -62,19 +65,12 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
}
}
// add the "group by" part
$groupBy = $qb->getDQLPart('groupBy');
if (\count($groupBy) > 0) {
$qb->addGroupBy($alias);
} else {
$qb->groupBy($alias);
}
$qb->addGroupBy($alias);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_PERSON;
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
@ -96,7 +92,9 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
public function getFormDefaultData(): array
{
return [];
return [
'level' => 'reasons',
];
}
public function getLabels($key, array $values, $data)

View File

@ -9,10 +9,10 @@ declare(strict_types=1);
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Tests\Export\Aggregator\PersonAggregators;
namespace Chill\ActivityBundle\Tests\Export\Aggregator;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator;
use Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Doctrine\ORM\EntityManagerInterface;
use Prophecy\PhpUnit\ProphecyTrait;
@ -33,14 +33,14 @@ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest
self::bootKernel();
$this->aggregator = self::$container->get(ActivityReasonAggregator::class);
/*
$request = $this->prophesize()
->willExtend(\Symfony\Component\HttpFoundation\Request::class);
$request = $this->prophesize()
->willExtend(\Symfony\Component\HttpFoundation\Request::class);
$request->getLocale()->willReturn('fr');
$request->getLocale()->willReturn('fr');
self::$container->get('request_stack')
->push($request->reveal());
self::$container->get('request_stack')
->push($request->reveal());*/
}
public function getAggregator()
@ -65,7 +65,12 @@ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest
return [
$em->createQueryBuilder()
->select('count(activity.id)')
->from(Activity::class, 'activity'),
->from(Activity::class, 'activity')
->join('activity.person', 'person'),
$em->createQueryBuilder()
->select('count(activity.id)')
->from(Activity::class, 'activity')
->join('activity.accompanyingPeriod', 'accompanyingPeriod'),
$em->createQueryBuilder()
->select('count(activity.id)')
->from(Activity::class, 'activity')

View File

@ -153,7 +153,7 @@ services:
## Aggregators
Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator:
Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator:
tags:
- { name: chill.export_aggregator, alias: activity_reason_aggregator }

View File

@ -17,6 +17,7 @@ use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\NotificationRepository;
use Chill\MainBundle\Templating\UI\NotificationCounterInterface;
use Doctrine\ORM\Event\PostPersistEventArgs;
use Doctrine\ORM\Event\PostUpdateEventArgs;
use Doctrine\ORM\Event\PreFlushEventArgs;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Security\Core\User\UserInterface;
@ -62,7 +63,12 @@ final readonly class NotificationByUserCounter implements NotificationCounterInt
return 'chill_main_notif_unread_by_'.$user->getId();
}
public function onEditNotificationComment(NotificationComment $notificationComment, PostPersistEventArgs $eventArgs): void
public function onPersistNotificationComment(NotificationComment $notificationComment, PostPersistEventArgs $eventArgs): void
{
$this->resetCacheForNotification($notificationComment->getNotification());
}
public function onEditNotificationComment(NotificationComment $notificationComment, PostUpdateEventArgs $eventArgs): void
{
$this->resetCacheForNotification($notificationComment->getNotification());
}

View File

@ -37,7 +37,7 @@ final readonly class ScopeResolverDispatcher
/**
* @return Scope|iterable<Scope>|Scope|null
*/
public function resolveScope(mixed $entity, ?array $options = []): Scope|iterable|null
public function resolveScope(mixed $entity, ?array $options = []): iterable|Scope|null
{
foreach ($this->resolvers as $resolver) {
if ($resolver->supports($entity, $options)) {

View File

@ -41,7 +41,7 @@ services:
entity: 'Chill\MainBundle\Entity\NotificationComment'
# set the 'lazy' option to TRUE to only instantiate listeners when they are used
lazy: true
method: 'onEditNotificationComment'
method: 'onPersistNotificationComment'
Chill\MainBundle\Notification\Email\NotificationMailer:
autowire: true

View File

@ -62,7 +62,7 @@ class AccompanyingCourseController extends \Symfony\Bundle\FrameworkBundle\Contr
$workflow = $this->registry->get($accompanyingCourse);
if ($workflow->can($accompanyingCourse, 'close')) {
$workflow->apply($accompanyingCourse, 'close');
$workflow->apply($accompanyingCourse, 'close', ['closing_motive' => $form['closingMotive']->getData()]);
$em->flush();

View File

@ -98,6 +98,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
$loader->load('services/exports_accompanying_course.yaml');
$loader->load('services/exports_social_actions.yaml');
$loader->load('services/exports_evaluation.yaml');
$loader->load('services/exports_accompanying_period_step_history.yaml');
}
$loader->load('services/exports_household.yaml');

View File

@ -1449,7 +1449,7 @@ class AccompanyingPeriod implements
return $this;
}
public function setStep(string $step): self
public function setStep(string $step, array $context = []): self
{
$previous = $this->step;
@ -1464,7 +1464,7 @@ class AccompanyingPeriod implements
$history = new AccompanyingPeriodStepHistory();
$history->setStep($this->step)->setStartDate(new \DateTimeImmutable('now'));
$this->addStepHistory($history);
$this->addStepHistory($history, $context);
}
return $this;
@ -1507,11 +1507,14 @@ class AccompanyingPeriod implements
return $this;
}
private function addStepHistory(AccompanyingPeriodStepHistory $stepHistory): self
private function addStepHistory(AccompanyingPeriodStepHistory $stepHistory, array $context = []): self
{
if (!$this->stepHistories->contains($stepHistory)) {
$this->stepHistories[] = $stepHistory;
$stepHistory->setPeriod($this);
if (($context['closing_motive'] ?? null) instanceof ClosingMotive) {
$stepHistory->setClosingMotive($context['closing_motive']);
}
$this->ensureStepContinuity();
}

View File

@ -59,6 +59,13 @@ class AccompanyingPeriodStepHistory implements TrackCreationInterface, TrackUpda
*/
private string $step;
/**
* @ORM\ManyToOne(targetEntity=ClosingMotive::class)
*
* @ORM\JoinColumn(nullable=true)
*/
private ?ClosingMotive $closingMotive = null;
public function getEndDate(): ?\DateTimeImmutable
{
return $this->endDate;
@ -114,4 +121,16 @@ class AccompanyingPeriodStepHistory implements TrackCreationInterface, TrackUpda
return $this;
}
public function getClosingMotive(): ?ClosingMotive
{
return $this->closingMotive;
}
public function setClosingMotive(?ClosingMotive $closingMotive): self
{
$this->closingMotive = $closingMotive;
return $this;
}
}

View File

@ -147,10 +147,16 @@ class Relationship implements TrackCreationInterface, TrackUpdateInterface
public function getOpposite(Person $counterpart): Person
{
if ($this->fromPerson !== $counterpart && $this->toPerson !== $counterpart) {
throw new \RuntimeException('the counterpart is neither the from nor to person for this relationship');
// during tests, comparing using equality does not work. We have to compare the ids
if (
($this->fromPerson->getId() === $counterpart->getId() && $this->toPerson->getId() === $counterpart->getId())
|| null === $counterpart->getId()
) {
throw new \RuntimeException(sprintf('the counterpart is neither the from nor to person for this relationship, expecting counterpart from %d and available %d and %d', $counterpart->getId(), $this->getFromPerson()->getId(), $this->getToPerson()->getId()));
}
}
if ($this->fromPerson === $counterpart) {
if ($this->fromPerson === $counterpart || $this->fromPerson->getId() === $counterpart->getId()) {
return $this->toPerson;
}

View File

@ -0,0 +1,78 @@
<?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\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class PersonParticipatingAggregator implements AggregatorInterface
{
private const KEY = 'acp_person_part_agg';
public function __construct(
private LabelPersonHelper $labelPersonHelper,
) {
}
public function buildForm(FormBuilderInterface $builder)
{
// nothing to do here
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, mixed $data)
{
return match ($key) {
self::KEY => $this->labelPersonHelper->getLabel($key, $values, 'export.aggregator.course.by-user.header'),
default => throw new \UnexpectedValueException('key not supported: '.$key),
};
}
public function getQueryKeys($data)
{
return [self::KEY];
}
public function getTitle()
{
return 'export.aggregator.course.by-user.title';
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$k = self::KEY;
if (!in_array('acppart', $qb->getAllAliases(), true)) {
$qb->join('acp.participations', 'acppart');
}
$qb->addSelect("IDENTITY(acppart.person) AS {$k}")
->addGroupBy($k);
}
public function applyOn()
{
return Declarations::ACP_TYPE;
}
}

View File

@ -0,0 +1,84 @@
<?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\Aggregator\AccompanyingPeriodStepHistoryAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepositoryInterface;
use Chill\PersonBundle\Templating\Entity\ClosingMotiveRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class ByClosingMotiveAggregator implements AggregatorInterface
{
private const KEY = 'acpstephistory_by_closing_motive_agg';
public function __construct(
private ClosingMotiveRepositoryInterface $closingMotiveRepository,
private ClosingMotiveRender $closingMotiveRender,
) {
}
public function buildForm(FormBuilderInterface $builder)
{
// nothing to add here
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, mixed $data)
{
return function (int|string|null $value): string {
if ('_header' === $value) {
return 'export.aggregator.step_history.by_closing_motive.header';
}
if (null === $value || '' === $value || null === $closingMotive = $this->closingMotiveRepository->find((int) $value)) {
return '';
}
return $this->closingMotiveRender->renderString($closingMotive, []);
};
}
public function getQueryKeys($data)
{
return [
self::KEY,
];
}
public function getTitle()
{
return 'export.aggregator.step_history.by_closing_motive.title';
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->addSelect('IDENTITY(acpstephistory.closingMotive) AS '.self::KEY)
->addGroupBy(self::KEY);
}
public function applyOn()
{
return Declarations::ACP_STEP_HISTORY;
}
}

View File

@ -0,0 +1,90 @@
<?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\Aggregator\AccompanyingPeriodStepHistoryAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Enum\DateGroupingChoiceEnum;
use Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingPeriodStepHistoryAggregators\ByDateAggregatorTest;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* @see ByDateAggregatorTest
*/
final readonly class ByDateAggregator implements AggregatorInterface
{
private const KEY = 'acpstephistory_by_date_agg';
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('frequency', ChoiceType::class, [
'choices' => array_combine(
array_map(fn (DateGroupingChoiceEnum $c) => 'export.enum.frequency.'.$c->value, DateGroupingChoiceEnum::cases()),
array_map(fn (DateGroupingChoiceEnum $c) => $c->value, DateGroupingChoiceEnum::cases()),
),
'label' => 'export.aggregator.course.by_opening_date.frequency',
'multiple' => false,
'expanded' => true,
]);
}
public function getFormDefaultData(): array
{
return ['frequency' => DateGroupingChoiceEnum::YEAR->value];
}
public function getLabels($key, array $values, mixed $data)
{
return function (?string $value): string {
if ('_header' === $value) {
return 'export.aggregator.step_history.by_date.header';
}
if (null === $value || '' === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data)
{
return [self::KEY];
}
public function getTitle()
{
return 'export.aggregator.step_history.by_date.title';
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$p = self::KEY;
$qb->addSelect(sprintf("TO_CHAR(acpstephistory.startDate, '%s') AS {$p}", $data['frequency']));
$qb->addGroupBy($p);
$qb->addOrderBy($p, 'DESC');
}
public function applyOn()
{
return Declarations::ACP_STEP_HISTORY;
}
}

View File

@ -0,0 +1,86 @@
<?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\Aggregator\AccompanyingPeriodStepHistoryAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingPeriodStepHistoryAggregators\ByStepAggregatorTest;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @see ByStepAggregatorTest
*/
final readonly class ByStepAggregator implements AggregatorInterface
{
private const KEY = 'acpstephistory_step_agg';
public function __construct(
private TranslatorInterface $translator
) {
}
public function buildForm(FormBuilderInterface $builder)
{
// nothing in this form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, mixed $data)
{
return function (?string $step): string {
if ('_header' === $step) {
return 'export.aggregator.step_history.by_step.header';
}
if (null === $step || '' === $step) {
return '';
}
return $this->translator->trans('accompanying_period.'.$step);
};
}
public function getQueryKeys($data)
{
return [
self::KEY,
];
}
public function getTitle()
{
return 'export.aggregator.step_history.by_step.title';
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->addSelect('acpstephistory.step AS '.self::KEY)
->addGroupBy(self::KEY);
}
public function applyOn()
{
return Declarations::ACP_STEP_HISTORY;
}
}

View File

@ -18,6 +18,8 @@ abstract class Declarations
{
final public const ACP_TYPE = 'accompanying_period';
final public const ACP_STEP_HISTORY = 'accompanying_period_step_history';
final public const EVAL_TYPE = 'evaluation';
final public const HOUSEHOLD_TYPE = 'household';

View File

@ -0,0 +1,139 @@
<?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\Export;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\FormBuilderInterface;
class CountAccompanyingCourseStepHistory implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
private readonly bool $filterStatsByCenters;
public function __construct(
private readonly EntityManagerInterface $em,
ParameterBagInterface $parameterBag,
) {
$this->repository = $em->getRepository(AccompanyingPeriod::class);
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
public function buildForm(FormBuilderInterface $builder): void
{
// Nothing to add here
}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'export.export.acp_closing.description';
}
public function getGroup(): string
{
return 'Exports of accompanying courses';
}
public function getLabels($key, array $values, $data)
{
if ('export_result' !== $key) {
throw new \LogicException("the key {$key} is not used by this export");
}
return fn ($value) => '_header' === $value ? $this->getTitle() : $value;
}
public function getQueryKeys($data): array
{
return ['export_result'];
}
public function getResult($query, $data)
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
{
return 'export.export.acp_closing.title';
}
public function getType(): string
{
return Declarations::ACP_TYPE;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder
{
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->em->createQueryBuilder()
->select('COUNT(DISTINCT acpstephistory.id) As export_result')
->from(AccompanyingPeriod\AccompanyingPeriodStepHistory::class, 'acpstephistory')
->join('acpstephistory.period', 'acp');
$qb
->leftJoin('acp.participations', 'acppart')
->leftJoin('acppart.person', '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);
}
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
return $qb;
}
public function requiredRole(): string
{
return AccompanyingPeriodVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::ACP_TYPE,
Declarations::PERSON_TYPE,
Declarations::ACP_STEP_HISTORY,
];
}
}

View File

@ -71,6 +71,7 @@ class HasTemporaryLocationFilter implements FilterInterface
])
->add('calc_date', PickRollingDateType::class, [
'label' => 'export.filter.course.having_temporarily.Calculation date',
'required' => true,
]);
}

View File

@ -0,0 +1,99 @@
<?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\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class NotAssociatedWithAReferenceAddressFilter implements FilterInterface
{
public function __construct(
private RollingDateConverterInterface $rollingDateConverter,
) {
}
public function getTitle()
{
return 'export.filter.course.not_having_address_reference.title';
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_calc', PickRollingDateType::class, [
'label' => 'export.filter.course.not_having_address_reference.adress_at',
]);
}
public function getFormDefaultData(): array
{
return [
'date_calc' => new RollingDate(RollingDate::T_TODAY),
];
}
public function describeAction($data, $format = 'string')
{
return [
'exports.filter.course.not_having_address_reference.describe',
[
'date_calc' => $this->rollingDateConverter->convert($data['date_calc']),
],
];
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$k = 'acp_not_associated_ref_filter';
$qb
->leftJoin(
'acp.locationHistories',
$k,
Join::WITH,
"{$k}.period = acp AND {$k}.startDate <= :{$k}_date_calc AND ({$k}.endDate IS NULL OR {$k}.endDate > :{$k}_date_calc)"
)
->leftJoin(
PersonHouseholdAddress::class,
"{$k}_p_address",
Join::WITH,
"{$k}.personLocation = {$k}_p_address.person AND {$k}_p_address.validFrom <= :{$k}_date_calc AND ({$k}_p_address.validTo IS NULL OR {$k}_p_address.validTo > :{$k}_date_calc)"
)
->join(
Address::class,
"{$k}_address",
Join::WITH,
"{$k}_address.id = COALESCE(IDENTITY({$k}_p_address.address), IDENTITY({$k}.addressLocation))"
)
;
$qb->andWhere("{$k}_address.addressReference IS NULL");
$qb->setParameter("{$k}_date_calc", $this->rollingDateConverter->convert($data['date_calc']));
}
public function applyOn()
{
return Declarations::ACP_TYPE;
}
}

View File

@ -0,0 +1,123 @@
<?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\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use src\Bundle\ChillPersonBundle\Tests\Export\Filter\AccompanyingCourseFilters\ReferrerFilterBetweenDatesTest;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Class ReferrerFilterBetweenDates.
*
* This class implements the FilterInterface and provides functionality to filter query results
* based on a range of dates and accepted referrers.
*
* @see ReferrerFilterBetweenDatesTest for tests
*/
final readonly class ReferrerFilterBetweenDates implements FilterInterface
{
private const A = 'acp_referrer_filter_uhistory';
private const P = 'acp_referrer_filter_date_start';
private const Q = 'acp_referrer_filter_date_end';
private const PU = 'acp_referrer_filter_users';
public function __construct(
private RollingDateConverterInterface $rollingDateConverter,
private UserRender $userRender
) {
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$history = self::A;
$start = self::P;
$end = self::Q;
$users = self::PU;
$qb
->join('acp.userHistories', self::A)
->andWhere(
"OVERLAPSI({$history}.startDate, {$history}.endDate),(:{$start}, :{$end}) = TRUE"
)
->andWhere(
"{$history}.user IN (:{$users})",
)
->setParameter($users, $data['accepted_referrers'])
->setParameter($start, $this->rollingDateConverter->convert($data['start_date']))
->setParameter($end, $this->rollingDateConverter->convert($data['end_date']));
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('accepted_referrers', PickUserDynamicType::class, [
'multiple' => true,
])
->add('start_date', PickRollingDateType::class, [
'label' => 'export.filter.course.by_referrer_between_dates.start date',
'required' => true,
])
->add('end_date', PickRollingDateType::class, [
'label' => 'export.filter.course.by_referrer_between_dates.end date',
'required' => true,
]);
}
public function getFormDefaultData(): array
{
return [
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
'accepted_referrers' => [],
];
}
public function describeAction($data, $format = 'string'): array
{
$users = [];
foreach ($data['accepted_referrers'] as $r) {
$users[] = $this->userRender->renderString($r, []);
}
return [
'exports.filter.course.by_referrer_between_dates.description', [
'agents' => implode(', ', $users),
'start_date' => $this->rollingDateConverter->convert($data['start_date']),
'end_date' => $this->rollingDateConverter->convert($data['end_date']),
], ];
}
public function getTitle(): string
{
return 'export.filter.course.by_referrer_between_dates.title';
}
}

View File

@ -0,0 +1,96 @@
<?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\Filter\AccompanyingPeriodStepHistoryFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use src\Bundle\ChillPersonBundle\Tests\Export\Filter\AccompanyingPeriodStepHistoryFilters\ByDateFilterTest;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Class ByDateFilter
* Implements the FilterInterface.
*
* This class represents a filter that filters data based on a date range on the start date.
* It allows the user to select a start date and an end date to filter the data.
*
* @see ByDateFilterTest
*/
final readonly class ByDateFilter implements FilterInterface
{
public function __construct(
private RollingDateConverterInterface $rollingDateConverter
) {
}
public function getTitle()
{
return 'export.filter.step_history.by_date.title';
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('start_date', PickRollingDateType::class, [
'label' => 'export.filter.step_history.by_date.start_date_label',
])
->add('end_date', PickRollingDateType::class, [
'label' => 'export.filter.step_history.by_date.end_date_label',
]);
}
public function getFormDefaultData(): array
{
return [
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
];
}
public function describeAction($data, $format = 'string')
{
return [
'exports.filter.step_history.by_date.description',
[
'start_date' => $this->rollingDateConverter->convert($data['start_date']),
'end_date' => $this->rollingDateConverter->convert($data['end_date']),
],
];
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$startDate = 'acp_step_history_by_date_start_filter';
$endDate = 'acp_step_history_by_date_end_filter';
$qb
->andWhere(
"acpstephistory.startDate >= :{$startDate} AND (acpstephistory.endDate < :{$endDate} OR acpstephistory.endDate IS NULL)"
)
->setParameter($startDate, $this->rollingDateConverter->convert($data['start_date']))
->setParameter($endDate, $this->rollingDateConverter->convert($data['end_date']));
}
public function applyOn()
{
return Declarations::ACP_STEP_HISTORY;
}
}

View File

@ -0,0 +1,87 @@
<?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\Filter\AccompanyingPeriodStepHistoryFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
final readonly class ByStepFilter implements FilterInterface
{
public function __construct(
private TranslatorInterface $translator,
) {
}
public function getTitle()
{
return 'export.filter.step_history.by_step.title';
}
public function buildForm(FormBuilderInterface $builder)
{
$steps = [
AccompanyingPeriod::STEP_CONFIRMED,
AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_SHORT,
AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_LONG,
AccompanyingPeriod::STEP_CLOSED,
];
$builder->add('steps', ChoiceType::class, [
'choices' => array_combine(
array_map(static fn (string $step): string => 'accompanying_period.'.$step, $steps),
$steps
),
'label' => 'export.filter.step_history.by_step.pick_steps',
'multiple' => true,
'expanded' => true,
]);
}
public function getFormDefaultData(): array
{
return [
'steps' => [],
];
}
public function describeAction($data, $format = 'string')
{
return [
'export.filter.step_history.by_step.description',
[
'%steps%' => implode(', ', array_map(fn (string $step) => $this->translator->trans('accompanying_period.'.$step), $data['steps'])),
],
];
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->andWhere('acpstephistory.step IN (:acpstephistory_by_step_filter_steps)')
->setParameter('acpstephistory_by_step_filter_steps', $data['steps']);
}
public function applyOn()
{
return Declarations::ACP_STEP_HISTORY;
}
}

View File

@ -0,0 +1,89 @@
<?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\Filter\PersonFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class WithParticipationBetweenDatesFilter implements FilterInterface
{
public function __construct(
private RollingDateConverterInterface $rollingDateConverter,
) {
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$p = 'with_participation_between_dates_filter';
$qb
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM '.AccompanyingPeriodParticipation::class." {$p}_acp JOIN {$p}_acp.accompanyingPeriod {$p}_acpp ".
"WHERE {$p}_acp.person = person ".
"AND OVERLAPSI({$p}_acp.startDate, {$p}_acp.endDate), (:{$p}_date_after, :{$p}_date_before) = TRUE ".
"AND OVERLAPSI({$p}_acpp.openingDate, {$p}_acpp.closingDate), (:{$p}_date_after, :{$p}_date_before) = TRUE"
)
)
->setParameter("{$p}_date_after", $this->rollingDateConverter->convert($data['date_after']), Types::DATE_IMMUTABLE)
->setParameter("{$p}_date_before", $this->rollingDateConverter->convert($data['date_before']), Types::DATE_IMMUTABLE);
}
public function applyOn(): string
{
return Declarations::PERSON_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_after', PickRollingDateType::class, [
'label' => 'export.filter.person.with_participation_between_dates.date_after',
]);
$builder->add('date_before', PickRollingDateType::class, [
'label' => 'export.filter.person.with_participation_between_dates.date_before',
]);
}
public function getFormDefaultData(): array
{
return [
'date_after' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'date_before' => new RollingDate(RollingDate::T_TODAY),
];
}
public function describeAction($data, $format = 'string')
{
return ['export.filter.person.with_participation_between_dates.Filtered by participations during period: between %dateafter% and %datebefore%', [
'%dateafter%' => $this->rollingDateConverter->convert($data['date_after'])->format('d-m-Y'),
'%datebefore%' => $this->rollingDateConverter->convert($data['date_before'])->format('d-m-Y'),
]];
}
public function getTitle()
{
return 'export.filter.person.with_participation_between_dates.title';
}
}

View File

@ -18,6 +18,7 @@ use Chill\MainBundle\Export\Helper\ExportAddressHelper;
use Chill\MainBundle\Export\Helper\UserHelper;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Repository\PersonRepository;
@ -40,6 +41,8 @@ final readonly class ListAccompanyingPeriodHelper
'closingDate',
'referrer',
'referrerSince',
'acpParticipantPersons',
'acpParticipantPersonsIds',
'administrativeLocation',
'locationIsPerson',
'locationIsTemp',
@ -79,6 +82,7 @@ final readonly class ListAccompanyingPeriodHelper
private TranslatableStringHelperInterface $translatableStringHelper,
private TranslatorInterface $translator,
private UserHelper $userHelper,
private LabelPersonHelper $labelPersonHelper,
) {
}
@ -110,6 +114,21 @@ final readonly class ListAccompanyingPeriodHelper
return $this->translatableStringHelper->localize(json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR));
},
'acpCreatedBy', 'acpUpdatedBy', 'referrer' => $this->userHelper->getLabel($key, $values, 'export.list.acp.'.$key),
'acpParticipantPersons' => $this->labelPersonHelper->getLabelMulti($key, $values, 'export.list.acp.'.$key),
'acpParticipantPersonsIds' => function ($value) use ($key): string {
if ('_header' === $value) {
return 'export.list.acp.'.$key;
}
if (null === $value || '' === $value) {
return '';
}
$keys = json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR);
return implode('|', $keys);
},
'locationPersonName', 'requestorPerson' => function ($value) use ($key) {
if ('_header' === $value) {
return 'export.list.acp.'.$key;
@ -222,6 +241,11 @@ final readonly class ListAccompanyingPeriodHelper
->addSelect(sprintf('%s_t.%s AS %s', $entity, $field, $entity));
}
// persons
$qb
->addSelect(sprintf('(SELECT AGGREGATE(IDENTITY(participant_persons_n.person)) FROM %s participant_persons_n WHERE participant_persons_n.accompanyingPeriod = acp) AS acpParticipantPersons', AccompanyingPeriodParticipation::class))
->addSelect(sprintf('(SELECT AGGREGATE(IDENTITY(participant_persons_ids.person)) FROM %s participant_persons_ids WHERE participant_persons_ids.accompanyingPeriod = acp) AS acpParticipantPersonsIds', AccompanyingPeriodParticipation::class));
// step at date
$qb
->addSelect('stepHistory.step AS step')

View File

@ -312,4 +312,34 @@ final class AccompanyingPeriodTest extends \PHPUnit\Framework\TestCase
$this->assertNull($period->getRequestorPerson());
$this->assertNull($period->getRequestor());
}
public function testSetStep(): void
{
$period = new AccompanyingPeriod();
$period->setStep(AccompanyingPeriod::STEP_CONFIRMED);
self::assertEquals(AccompanyingPeriod::STEP_CONFIRMED, $period->getStep());
self::assertCount(1, $period->getStepHistories());
$period->setStep(AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_SHORT);
self::assertEquals(AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_SHORT, $period->getStep());
self::assertCount(2, $period->getStepHistories());
$periodInactiveSteps = $period->getStepHistories()->filter(fn (AccompanyingPeriod\AccompanyingPeriodStepHistory $h) => AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_SHORT === $h->getStep());
self::assertCount(1, $periodInactiveSteps);
$period->setStep(AccompanyingPeriod::STEP_CLOSED, ['closing_motive' => $closingMotive = new AccompanyingPeriod\ClosingMotive()]);
self::assertEquals(AccompanyingPeriod::STEP_CLOSED, $period->getStep());
self::assertCount(3, $period->getStepHistories());
$periodClosedSteps = $period->getStepHistories()->filter(fn (AccompanyingPeriod\AccompanyingPeriodStepHistory $h) => AccompanyingPeriod::STEP_CLOSED === $h->getStep());
self::assertCount(1, $periodClosedSteps);
$periodClosedStep = $periodClosedSteps->first();
self::assertSame($closingMotive, $periodClosedStep->getClosingMotive());
}
}

View File

@ -0,0 +1,61 @@
<?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\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
use Doctrine\ORM\EntityManagerInterface;
use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\PersonParticipatingAggregator;
/**
* @internal
*
* @coversNothing
*/
final class PersonParticipatingAggregatorTest extends AbstractAggregatorTest
{
private LabelPersonHelper $labelPersonHelper;
protected function setUp(): void
{
self::bootKernel();
$this->labelPersonHelper = self::$container->get(LabelPersonHelper::class);
}
public function getAggregator()
{
return new PersonParticipatingAggregator($this->labelPersonHelper);
}
public function getFormData()
{
return [[]];
}
public function getQueryBuilders()
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
return [
$em->createQueryBuilder()
->select('count(acp.id)')
->from(AccompanyingPeriod::class, 'acp'),
$em->createQueryBuilder()
->select('count(acp.id)')
->from(AccompanyingPeriod::class, 'acp')
->join('acp.participations', 'acppart'),
];
}
}

View File

@ -0,0 +1,68 @@
<?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\Aggregator\AccompanyingPeriodStepHistoryAggregators;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodStepHistory;
use Chill\PersonBundle\Export\Aggregator\AccompanyingPeriodStepHistoryAggregators\ByClosingMotiveAggregator;
use Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepositoryInterface;
use Chill\PersonBundle\Templating\Entity\ClosingMotiveRender;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
*
* @coversNothing
*/
class ByClosingMotiveAggregatorTest extends AbstractAggregatorTest
{
private ClosingMotiveRender $closingMotiveRender;
private ClosingMotiveRepositoryInterface $closingMotiveRepository;
protected function setUp(): void
{
parent::setUp();
self::bootKernel();
$this->closingMotiveRender = self::$container->get(ClosingMotiveRender::class);
$this->closingMotiveRepository = self::$container->get(ClosingMotiveRepositoryInterface::class);
}
public function getAggregator()
{
return new ByClosingMotiveAggregator(
$this->closingMotiveRepository,
$this->closingMotiveRender
);
}
public function getFormData()
{
return [
[],
];
}
public function getQueryBuilders()
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
$qb = $em->createQueryBuilder()
->select('COUNT(DISTINCT acpstephistory.id) As export_result')
->from(AccompanyingPeriodStepHistory::class, 'acpstephistory')
->join('acpstephistory.period', 'acp');
return [
$qb,
];
}
}

View File

@ -0,0 +1,61 @@
<?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\Aggregator\AccompanyingPeriodStepHistoryAggregators;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodStepHistory;
use Chill\PersonBundle\Export\Enum\DateGroupingChoiceEnum;
use Doctrine\ORM\EntityManagerInterface;
use Chill\PersonBundle\Export\Aggregator\AccompanyingPeriodStepHistoryAggregators\ByDateAggregator;
/**
* @internal
*
* @coversNothing
*/
class ByDateAggregatorTest extends AbstractAggregatorTest
{
public function getAggregator()
{
return new ByDateAggregator();
}
public function getFormData()
{
return [
[
'frequency' => DateGroupingChoiceEnum::YEAR->value,
],
[
'frequency' => DateGroupingChoiceEnum::WEEK->value,
],
[
'frequency' => DateGroupingChoiceEnum::MONTH->value,
],
];
}
public function getQueryBuilders()
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
$qb = $em->createQueryBuilder()
->select('COUNT(DISTINCT acpstephistory.id) As export_result')
->from(AccompanyingPeriodStepHistory::class, 'acpstephistory')
->join('acpstephistory.period', 'acp');
return [
$qb,
];
}
}

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\PersonBundle\Tests\Export\Aggregator\AccompanyingPeriodStepHistoryAggregators;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodStepHistory;
use Chill\PersonBundle\Export\Aggregator\AccompanyingPeriodStepHistoryAggregators\ByStepAggregator;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @internal
*
* @coversNothing
*/
class ByStepAggregatorTest extends AbstractAggregatorTest
{
public function getAggregator()
{
$translator = new class () implements TranslatorInterface {
public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null)
{
return $id;
}
};
return new ByStepAggregator($translator);
}
public function getFormData()
{
return [[]];
}
public function getQueryBuilders()
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
$qb = $em->createQueryBuilder()
->select('COUNT(DISTINCT acpstephistory.id) As export_result')
->from(AccompanyingPeriodStepHistory::class, 'acpstephistory')
->join('acpstephistory.period', 'acp');
return [
$qb,
];
}
}

View File

@ -0,0 +1,48 @@
<?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 src\Bundle\ChillPersonBundle\Tests\Export\Export;
use Chill\MainBundle\Test\Export\AbstractExportTest;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Export\CountAccompanyingCourseStepHistory;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
*
* @coversNothing
*/
final class CountAccompanyingCourseStepHistoryTest extends AbstractExportTest
{
protected function setUp(): void
{
self::bootKernel();
}
public function getExport()
{
$em = self::$container->get(EntityManagerInterface::class);
yield new CountAccompanyingCourseStepHistory($em, $this->getParameters(true));
yield new CountAccompanyingCourseStepHistory($em, $this->getParameters(false));
}
public function getFormData(): array
{
return [[]];
}
public function getModifiersCombination()
{
return [[Declarations::ACP_TYPE], [Declarations::ACP_TYPE, Declarations::ACP_STEP_HISTORY]];
}
}

View File

@ -0,0 +1,67 @@
<?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\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\HasTemporaryLocationFilter;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
*
* @coversNothing
*/
class HasTemporaryLocationFilterTest extends AbstractFilterTest
{
private RollingDateConverterInterface $rollingDateConverter;
protected function setUp(): void
{
self::bootKernel();
$this->rollingDateConverter = self::$container->get(RollingDateConverterInterface::class);
}
public function getFilter()
{
return new HasTemporaryLocationFilter($this->rollingDateConverter);
}
public function getFormData()
{
return [
[
'having_temporarily' => true,
'calc_date' => new RollingDate(RollingDate::T_TODAY),
],
[
'having_temporarily' => false,
'calc_date' => new RollingDate(RollingDate::T_TODAY),
],
];
}
public function getQueryBuilders()
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
return [
$em->createQueryBuilder()
->from('ChillPersonBundle:AccompanyingPeriod', 'acp')
->select('acp.id'),
];
}
}

View File

@ -0,0 +1,66 @@
<?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\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\NotAssociatedWithAReferenceAddressFilter;
use Doctrine\ORM\EntityManagerInterface;
use Prophecy\PhpUnit\ProphecyTrait;
/**
* @internal
*
* @coversNothing
*/
class NotAssociatedWithAReferenceAddressFilterTest extends AbstractFilterTest
{
use ProphecyTrait;
public function getFilter()
{
$dateConverter = new class () implements RollingDateConverterInterface {
public function convert(?RollingDate $rollingDate): ?\DateTimeImmutable
{
if (null === $rollingDate) {
return null;
}
return new \DateTimeImmutable('now');
}
};
return new NotAssociatedWithAReferenceAddressFilter($dateConverter);
}
public function getFormData()
{
return [
['date_calc' => new RollingDate(RollingDate::T_TODAY)],
];
}
public function getQueryBuilders()
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
return [
$em->createQueryBuilder()
->select('acp.id')
->from(AccompanyingPeriod::class, 'acp'),
];
}
}

View File

@ -0,0 +1,89 @@
<?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 src\Bundle\ChillPersonBundle\Tests\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ReferrerFilterBetweenDates;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
*
* @coversNothing
*/
class ReferrerFilterBetweenDatesTest extends AbstractFilterTest
{
private RollingDateConverterInterface $rollingDateConverter;
private UserRender $userRender;
protected function setUp(): void
{
parent::setUp();
self::bootKernel();
$this->rollingDateConverter = self::$container->get(RollingDateConverterInterface::class);
$this->userRender = self::$container->get(UserRender::class);
}
public function getFilter()
{
return new ReferrerFilterBetweenDates($this->rollingDateConverter, $this->userRender);
}
public function getFormData()
{
self:self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
$users = $em->createQueryBuilder()
->from(User::class, 'u')
->select('u')
->getQuery()
->setMaxResults(1)
->getResult();
return [
[
'accepted_referrers' => $users[0],
'start_date' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
],
];
}
public function getQueryBuilders()
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
yield $em->createQueryBuilder()
->from(AccompanyingPeriod::class, 'acp')
->select('acp.id');
$qb = $em->createQueryBuilder();
$qb
->from(AccompanyingPeriod\AccompanyingPeriodWork::class, 'acpw')
->join('acpw.accompanyingPeriod', 'acp')
->join('acp.participations', 'acppart')
->join('acppart.person', 'person')
;
$qb->select('COUNT(DISTINCT acpw.id) as export_result');
yield $qb;
}
}

View File

@ -31,7 +31,7 @@ final class ReferrerFilterTest extends AbstractFilterTest
{
self::bootKernel();
$this->filter = self::$container->get('chill.person.export.filter_referrer');
$this->filter = self::$container->get(ReferrerFilter::class);
}
public function getFilter()

View File

@ -0,0 +1,67 @@
<?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 src\Bundle\ChillPersonBundle\Tests\Export\Filter\AccompanyingPeriodStepHistoryFilters;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodStepHistory;
use Chill\PersonBundle\Export\Filter\AccompanyingPeriodStepHistoryFilters\ByDateFilter;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
*
* @coversNothing
*/
class ByDateFilterTest extends AbstractFilterTest
{
private RollingDateConverterInterface $rollingDateConverter;
protected function setUp(): void
{
parent::setUp();
self::bootKernel();
$this->rollingDateConverter = self::$container->get(RollingDateConverterInterface::class);
}
public function getFilter()
{
return new ByDateFilter($this->rollingDateConverter);
}
public function getFormData()
{
return [
[
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
],
];
}
public function getQueryBuilders()
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
$qb = $em->createQueryBuilder()
->select('COUNT(DISTINCT acpstephistory.id) As export_result')
->from(AccompanyingPeriodStepHistory::class, 'acpstephistory')
->join('acpstephistory.period', 'acp');
return [
$qb,
];
}
}

View File

@ -0,0 +1,66 @@
<?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 src\Bundle\ChillPersonBundle\Tests\Export\Filter\AccompanyingPeriodStepHistoryFilters;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodStepHistory;
use Chill\PersonBundle\Export\Filter\AccompanyingPeriodStepHistoryFilters\ByStepFilter;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @internal
*
* @coversNothing
*/
class ByStepFilterTest extends AbstractFilterTest
{
public function getFilter()
{
$translator = new class () implements TranslatorInterface {
public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null)
{
return $id;
}
};
return new ByStepFilter($translator);
}
public function getFormData()
{
return [
[
'steps' => [AccompanyingPeriod::STEP_CONFIRMED, AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_LONG],
],
[
'steps' => [AccompanyingPeriod::STEP_CLOSED],
],
];
}
public function getQueryBuilders()
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
$qb = $em->createQueryBuilder()
->select('COUNT(DISTINCT acpstephistory.id) As export_result')
->from(AccompanyingPeriodStepHistory::class, 'acpstephistory')
->join('acpstephistory.period', 'acp');
return [
$qb,
];
}
}

View File

@ -0,0 +1,63 @@
<?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 Export\Filter\PersonFilters;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Export\Filter\PersonFilters\WithParticipationBetweenDatesFilter;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
*
* @coversNothing
*/
final class WithParticipationBetweenDatesFilterTest extends AbstractFilterTest
{
private WithParticipationBetweenDatesFilter $filter;
protected function setUp(): void
{
self::bootKernel();
$this->filter = self::$container->get(WithParticipationBetweenDatesFilter::class);
}
public function getFilter()
{
return $this->filter;
}
public function getFormData()
{
return [
[
'date_after' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'date_before' => new RollingDate(RollingDate::T_TODAY),
],
];
}
public function getQueryBuilders()
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
return [
$em->createQueryBuilder()
->select('person.id')
->from(Person::class, 'person'),
];
}
}

View File

@ -96,11 +96,14 @@ services:
tags:
- { name: chill.export_filter, alias: accompanyingcourse_activeonedaybetweendates_filter }
chill.person.export.filter_referrer:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ReferrerFilter
Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ReferrerFilter:
tags:
- { name: chill.export_filter, alias: accompanyingcourse_referrer_filter }
Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ReferrerFilterBetweenDates:
tags:
- { name: chill.export_filter, alias: accompanyingcourse_referrer_filter_between_dates }
chill.person.export.filter_openbetweendates:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\OpenBetweenDatesFilter
tags:
@ -147,6 +150,10 @@ services:
tags:
- { name: chill.export_filter, alias: accompanyingcourse_info_within_filter }
Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\NotAssociatedWithAReferenceAddressFilter:
tags:
- { name: chill.export_filter, alias: accompanyingcourse_not_having_addr_reference_filter }
## Aggregators
chill.person.export.aggregator_referrer_scope:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ScopeAggregator
@ -259,3 +266,7 @@ services:
Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ClosingDateAggregator:
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_closing_date_aggregator }
Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\PersonParticipatingAggregator:
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_person_part_aggregator }

View File

@ -0,0 +1,31 @@
services:
_defaults:
autowire: true
autoconfigure: true
# exports
Chill\PersonBundle\Export\Export\CountAccompanyingCourseStepHistory:
tags:
- { name: chill.export, alias: count_acpstephistory }
# filters
Chill\PersonBundle\Export\Filter\AccompanyingPeriodStepHistoryFilters\ByDateFilter:
tags:
- { name: chill.export_filter, alias: acpstephistory_filter_by_date }
Chill\PersonBundle\Export\Filter\AccompanyingPeriodStepHistoryFilters\ByStepFilter:
tags:
- { name: chill.export_filter, alias: acpstephistory_filter_by_step }
# aggregators
Chill\PersonBundle\Export\Aggregator\AccompanyingPeriodStepHistoryAggregators\ByClosingMotiveAggregator:
tags:
- { name: chill.export_aggregator, alias: acpstephistory_agg_by_closing_motive }
Chill\PersonBundle\Export\Aggregator\AccompanyingPeriodStepHistoryAggregators\ByDateAggregator:
tags:
- { name: chill.export_aggregator, alias: acpstephistory_agg_by_date }
Chill\PersonBundle\Export\Aggregator\AccompanyingPeriodStepHistoryAggregators\ByStepAggregator:
tags:
- { name: chill.export_aggregator, alias: acpstephistory_agg_by_step }

View File

@ -116,6 +116,10 @@ services:
tags:
- { name: chill.export_filter, alias: person_without_household_composition_filter }
Chill\PersonBundle\Export\Filter\PersonFilters\WithParticipationBetweenDatesFilter:
tags:
- { name: chill.export_filter, alias: person_with_participation_between_dates_filter }
## Aggregators
chill.person.export.aggregator_nationality:
class: Chill\PersonBundle\Export\Aggregator\PersonAggregators\NationalityAggregator

View File

@ -0,0 +1,52 @@
<?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 Version20240123161457 extends AbstractMigration
{
public function getDescription(): string
{
return 'Store closing motive when closing a course';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD closingMotive_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT FK_84D514AC504CB38D FOREIGN KEY (closingMotive_id) REFERENCES chill_person_accompanying_period_closingmotive (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT FK_84D514AC65FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql(<<<'EOF'
WITH last_step AS (
SELECT * FROM (
SELECT *, rank() OVER (partition by period_id ORDER BY startdate DESC, id DESC) AS r FROM chill_person_accompanying_period_step_history cpapsh
) as sq
WHERE r = 1
)
UPDATE chill_person_accompanying_period_step_history
SET closingMotive_id = chill_person_accompanying_period.closingmotive_id
FROM last_step, chill_person_accompanying_period
WHERE last_step.period_id = chill_person_accompanying_period_step_history.period_id AND chill_person_accompanying_period.id = chill_person_accompanying_period_step_history.period_id
AND last_step.step = 'CLOSED';
EOF);
$this->addSql('CREATE INDEX IDX_84D514AC504CB38D ON chill_person_accompanying_period_step_history (closingMotive_id)');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history DROP CONSTRAINT FK_84D514AC504CB38D');
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history DROP CONSTRAINT FK_84D514AC65FF1AEC');
$this->addSql('DROP INDEX IDX_84D514AC504CB38D');
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history DROP closingMotive_id');
}
}

View File

@ -136,10 +136,22 @@ exports:
Filtered by person\'s geographical unit (based on address) computed at date, only units:
"Filtré par zone géographique sur base de l'adresse, calculé à {datecalc, date, short}, seulement les zones suivantes: {units}"
filter:
course:
not_having_address_reference:
describe: >-
Uniquement les parcours qui ne sont pas localisés à une adresse de référence, à la date du {date_calc, date, medium}
by_referrer_between_dates:
description: >-
Filtré par référent du parcours, entre deux dates: depuis le {start_date, date, medium}, jusqu'au {end_date, date, medium}, seulement {agents}
work:
by_treating_agent:
Filtered by treating agent at date: >-
Les agents traitant au { agent_at, date, medium }, seulement {agents}
step_history:
by_date:
description: >-
Changements de statuts filtrés par date: après le { start_date, date, medium } (inclus), avant le { end_date, date, medium }
'total persons matching the search pattern': >-
{ total, plural,
=0 {Aucun usager ne correspond aux termes de recherche}

View File

@ -785,7 +785,9 @@ accompanying_period:
dates_from_%opening_date%_to_%closing_date%: Ouvert du %opening_date% au %closing_date%
DRAFT: Brouillon
CONFIRMED: Confirmé
CLOSED: Clotûré
CLOSED: Clôturé
CONFIRMED_INACTIVE_SHORT: Hors file active
CONFIRMED_INACTIVE_LONG: Pré-archivé
emergency: Urgent
occasional: ponctuel
regular: régulier
@ -985,6 +987,9 @@ export:
YYYY-MM: par mois
YYYY: par année
export:
acp_closing:
title: Nombre de changements de statuts de parcours
description: Compte le nombre de changements de statuts de parcours. Cet export est indiqué pour obtenir le nombre de parcours ouverts ou fermés pendant une période de temps (un parcours pouvant être clôturé, puis ré-ouvert pendant la période de temps indiquée)
acp_stats:
avg_duration: Moyenne de la durée de participation de chaque usager concerné
count_participations: Nombre de participations distinctes
@ -1037,7 +1042,23 @@ export:
at_date: Date de calcul de l'adresse
header: Code postal
step_history:
by_step:
title: Grouper les changements de statut du parcours par étape
header: Nouveau statut du parcours
by_date:
title: Grouper les changements de statut du parcours par date
header: Date du changement de statut du parcours
date_grouping_label: Grouper par
by_closing_motive:
title: Grouper les changements de statut du parcours par motif de clôture
header: Motif de clôture
course:
by-user:
title: Grouper les parcours par usager participant
header: Usager participant
by_referrer:
Computation date for referrer: Date à laquelle le référent était actif
by_user_scope:
@ -1124,6 +1145,16 @@ export:
by_geog_unit:
Filtered by person's geographical unit (based on address) computed at %datecalc%, only %units%: Filtré par unité géographique (sur base de l'adresse), calculé le %datecalc%, seulement %units%
step_history:
by_step:
title: Filtrer les changements de statut du parcours par étape
pick_steps: Nouvelles étapes
description: "Filtré par étape: seulement %steps%"
by_date:
title: Filtrer les changements de statut du parcours par date
start_date_label: Changements après le
end_date_label: Changements avant le
person:
by_composition:
Filter by household composition: Filtrer les usagers par composition du ménage
@ -1142,8 +1173,16 @@ export:
Filtered by person\'s address status computed at %datecalc%, only %statuses%: Filtré par comparaison à l'adresse de référence, calculé à %datecalc%, seulement %statuses%
Status: Statut
Address at date: Adresse à la date
with_participation_between_dates:
date_after: Concerné par un parcours après le
date_before: Concerné par un parcours avant le
title: Filtrer les usagers ayant été associés à un parcours ouverts un jour dans la période de temps indiquée
'Filtered by participations during period: between %dateafter% and %datebefore%': 'Filtré par personne concerné par un parcours dans la periode entre: %dateafter% et %datebefore%'
course:
not_having_address_reference:
title: Filtrer les parcours non localisés à une adresse de réference
adress_at: Adresse à la date du
having_info_within_interval:
title: Filtrer les parcours ayant reçu une intervention entre deux dates
start_date: Début de la période
@ -1179,6 +1218,10 @@ export:
"Filtered by user main scope: only %scope%": "Filtré par service du référent: uniquement %scope%"
by_referrer:
Computation date for referrer: Date à laquelle le référent était actif
by_referrer_between_dates:
title: Filtrer les parcours par référent (entre deux dates)
start date: Le référent était actif après le
end date: Le référent était actif avant le
having_temporarily:
label: Qualité de la localisation
Having a temporarily location: Ayant une localisation temporaire
@ -1296,6 +1339,8 @@ export:
socialIssues: Problématiques sociales
requestorPerson: Demandeur (personne)
requestorThirdParty: Demandeur (tiers)
acpParticipantPersons: Usagers concernés
acpParticipantPersonsIds: Usagers concernés (identifiants)
eval:
List of evaluations: Liste des évaluations