mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge remote-tracking branch 'origin/rector/rules-symfony' into rector/rules-symfony
This commit is contained in:
commit
a6fcdb5256
6
.changes/unreleased/Feature-20230920-175207.yaml
Normal file
6
.changes/unreleased/Feature-20230920-175207.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
kind: Feature
|
||||
body: |
|
||||
Add history to scopes and to jobs in administrator section. When user job or main scope of user is changed, automaticaly add a new row in history.
|
||||
time: 2023-09-20T17:52:07.160601133+02:00
|
||||
custom:
|
||||
Issue: "147"
|
5
.changes/unreleased/Feature-20231011-155115.yaml
Normal file
5
.changes/unreleased/Feature-20231011-155115.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
kind: Feature
|
||||
body: '[export] add an aggregator for activities: group by job scope aggregator'
|
||||
time: 2023-10-11T15:51:15.022779832+02:00
|
||||
custom:
|
||||
Issue: ""
|
@ -1,63 +0,0 @@
|
||||
Entity,Join,Attribute,Alias
|
||||
AccompanyingPeriod::class,,,acp
|
||||
,AccompanyingPeriodWork::class,acp.works,acpw
|
||||
,AccompanyingPeriodParticipation::class,acp.participations,acppart
|
||||
,Location::class,acp.administrativeLocation,acploc
|
||||
,ClosingMotive::class,acp.closingMotive,acpmotive
|
||||
,UserJob::class,acp.job,acpjob
|
||||
,Origin::class,acp.origin,acporigin
|
||||
,Scope::class,acp.scopes,acpscope
|
||||
,SocialIssue::class,acp.socialIssues,acpsocialissue
|
||||
,User::class,acp.user,acpuser
|
||||
AccompanyingPeriodWork::class,,,acpw
|
||||
,AccompanyingPeriodWorkEvaluation::class,acpw.accompanyingPeriodWorkEvaluations,workeval
|
||||
,User::class,acpw.referrers,acpwuser
|
||||
,SocialAction::class,acpw.socialAction,acpwsocialaction
|
||||
,Goal::class,acpw.goals,goal
|
||||
,Result::class,acpw.results,result
|
||||
AccompanyingPeriodParticipation::class,,,acppart
|
||||
,Person::class,acppart.person,partperson
|
||||
AccompanyingPeriodWorkEvaluation::class,,,workeval
|
||||
,Evaluation::class,workeval.evaluation,eval
|
||||
Goal::class,,,goal
|
||||
,Result::class,goal.results,goalresult
|
||||
Person::class,,,person
|
||||
,Center::class,person.center,center
|
||||
,HouseholdMember::class,partperson.householdParticipations,householdmember
|
||||
,MaritalStatus::class,person.maritalStatus,personmarital
|
||||
,VendeePerson::class,,vp
|
||||
,VendeePersonMineur::class,,vpm
|
||||
ResidentialAddress::class,,,resaddr
|
||||
,ThirdParty::class,resaddr.hostThirdParty,tparty
|
||||
ThirdParty::class,,,tparty
|
||||
,ThirdPartyCategory::class,tparty.categories,tpartycat
|
||||
HouseholdMember::class,,,householdmember
|
||||
,Household::class,householdmember.household,household
|
||||
,Person::class,householdmember.person,memberperson
|
||||
,,memberperson.center,membercenter
|
||||
Household::class,,,household
|
||||
,HouseholdComposition::class,household.compositions,composition
|
||||
Activity::class,,,activity
|
||||
,Person::class,activity.person,actperson
|
||||
,AccompanyingPeriod::class,activity.accompanyingPeriod,acp
|
||||
,Person::class,activity_person_having_activity.person,person_person_having_activity
|
||||
,ActivityReason::class,activity_person_having_activity.reasons,reasons_person_having_activity
|
||||
,ActivityType::class,activity.activityType,acttype
|
||||
,Location::class,activity.location,actloc
|
||||
,SocialAction::class,activity.socialActions,actsocialaction
|
||||
,SocialIssue::class,activity.socialIssues,actsocialssue
|
||||
,ThirdParty::class,activity.thirdParties,acttparty
|
||||
,User::class,activity.user,actuser
|
||||
,User::class,activity.users,actusers
|
||||
,ActivityReason::class,activity.reasons,actreasons
|
||||
,Center::class,actperson.center,actcenter
|
||||
ActivityReason::class,,,actreasons
|
||||
,ActivityReasonCategory::class,actreason.category,actreasoncat
|
||||
Calendar::class,,,cal
|
||||
,CancelReason::class,cal.cancelReason,calcancel
|
||||
,Location::class,cal.location,calloc
|
||||
,User::class,cal.user,caluser
|
||||
VendeePerson::class,,,vp
|
||||
,SituationProfessionelle::class,vp.situationProfessionelle,vpprof
|
||||
,StatutLogement::class,vp.statutLogement,vplog
|
||||
,TempsDeTravail::class,vp.tempsDeTravail,vptt
|
|
@ -21,7 +21,6 @@ These are alias conventions :
|
||||
| | AccompanyingPeriodInfo::class | not existing (using custom WITH clause) | acpinfo |
|
||||
| AccompanyingPeriodWork::class | | | acpw |
|
||||
| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval |
|
||||
| | User::class | acpw.referrers | acpwuser |
|
||||
| | SocialAction::class | acpw.socialAction | acpwsocialaction |
|
||||
| | Goal::class | acpw.goals | goal |
|
||||
| | Result::class | acpw.results | result |
|
||||
|
@ -12,15 +12,22 @@ declare(strict_types=1);
|
||||
namespace Chill\ActivityBundle\Export\Aggregator;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
class ActivityUsersJobAggregator implements \Chill\MainBundle\Export\AggregatorInterface
|
||||
class ActivityUsersJobAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private readonly UserJobRepositoryInterface $userJobRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||
private const PREFIX = 'act_agg_user_job';
|
||||
|
||||
public function __construct(
|
||||
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -29,24 +36,37 @@ class ActivityUsersJobAggregator implements \Chill\MainBundle\Export\AggregatorI
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('actusers', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('activity.users', 'actusers');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->addSelect('IDENTITY(actusers.userJob) AS activity_users_job_aggregator')
|
||||
->addGroupBy('activity_users_job_aggregator');
|
||||
->leftJoin("activity.users", "{$p}_user")
|
||||
->leftJoin(
|
||||
UserJobHistory::class,
|
||||
"{$p}_history",
|
||||
Expr\Join::WITH,
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
|
||||
)
|
||||
// job_at based on activity.date
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "activity.date"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "activity.date")
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_history.job) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACTIVITY;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// nothing to add in the form
|
||||
}
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
@ -73,11 +93,11 @@ class ActivityUsersJobAggregator implements \Chill\MainBundle\Export\AggregatorI
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['activity_users_job_aggregator'];
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Aggregate by users job';
|
||||
return 'export.aggregator.activity.by_user_job.Aggregate by users job';
|
||||
}
|
||||
}
|
||||
|
@ -12,15 +12,22 @@ declare(strict_types=1);
|
||||
namespace Chill\ActivityBundle\Export\Aggregator;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
class ActivityUsersScopeAggregator implements \Chill\MainBundle\Export\AggregatorInterface
|
||||
class ActivityUsersScopeAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private readonly ScopeRepositoryInterface $scopeRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||
private const PREFIX = 'act_agg_user_scope';
|
||||
|
||||
public function __construct(
|
||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -29,24 +36,37 @@ class ActivityUsersScopeAggregator implements \Chill\MainBundle\Export\Aggregato
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('actusers', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('activity.users', 'actusers');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->addSelect('IDENTITY(actusers.mainScope) AS activity_users_main_scope_aggregator')
|
||||
->addGroupBy('activity_users_main_scope_aggregator');
|
||||
->leftJoin("activity.users", "{$p}_user")
|
||||
->leftJoin(
|
||||
UserScopeHistory::class,
|
||||
"{$p}_history",
|
||||
Expr\Join::WITH,
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
|
||||
)
|
||||
// scope_at based on activity.date
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "activity.date"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "activity.date")
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_history.scope) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACTIVITY;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// nothing to add in the form
|
||||
}
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
@ -73,11 +93,11 @@ class ActivityUsersScopeAggregator implements \Chill\MainBundle\Export\Aggregato
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['activity_users_main_scope_aggregator'];
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Aggregate by users scope';
|
||||
return 'export.aggregator.activity.by_user_scope.Aggregate by users scope';
|
||||
}
|
||||
}
|
||||
|
@ -12,16 +12,22 @@ declare(strict_types=1);
|
||||
namespace Chill\ActivityBundle\Export\Aggregator;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepository;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
class CreatorScopeAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private readonly ScopeRepository $scopeRepository, private readonly TranslatableStringHelper $translatableStringHelper) {}
|
||||
private const PREFIX = 'acp_agg_creator_scope';
|
||||
|
||||
public function __construct(
|
||||
private readonly ScopeRepository $scopeRepository,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -30,12 +36,28 @@ class CreatorScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('actcreator', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('activity.createdBy', 'actcreator');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb->addSelect('IDENTITY(actcreator.mainScope) AS creatorscope_aggregator');
|
||||
$qb->addGroupBy('creatorscope_aggregator');
|
||||
$qb
|
||||
->leftJoin("activity.createdBy", "{$p}_user")
|
||||
->leftJoin(
|
||||
UserScopeHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
|
||||
)
|
||||
// scope_at based on activity.date
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "activity.date"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "activity.date")
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_history.scope) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -43,10 +65,8 @@ class CreatorScopeAggregator implements AggregatorInterface
|
||||
return Declarations::ACTIVITY;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// no form
|
||||
}
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
@ -73,11 +93,11 @@ class CreatorScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['creatorscope_aggregator'];
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Group activity by creator scope';
|
||||
return 'export.aggregator.activity.by_creator_scope.Group activity by creator scope';
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,104 @@
|
||||
<?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\Aggregator;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepository;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class JobScopeAggregator implements AggregatorInterface
|
||||
{
|
||||
private const PREFIX = 'acp_agg_creator_job';
|
||||
|
||||
public function __construct(
|
||||
private readonly ScopeRepository $scopeRepository,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->leftJoin("activity.createdBy", "{$p}_user")
|
||||
->leftJoin(
|
||||
UserJobHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
|
||||
)
|
||||
// job_at based on activity.date
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "activity.date"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "activity.date")
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_history.job) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACTIVITY;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
return function ($value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'Scope';
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$s = $this->scopeRepository->find($value);
|
||||
|
||||
return $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.aggregator.activity.by_creator_job.Group activity by creator job';
|
||||
}
|
||||
}
|
@ -57,7 +57,7 @@ class ListActivityHelper
|
||||
->addSelect('AGGREGATE(actPerson.id) AS personsNames')
|
||||
->leftJoin('activity.users', 'users_u')
|
||||
->addSelect('AGGREGATE(users_u.id) AS usersIds')
|
||||
->addSelect('AGGREGATE(users_u.id) AS usersNames')
|
||||
->addSelect('AGGREGATE(JSON_BUILD_OBJECT(\'uid\', users_u.id, \'d\', activity.date)) AS usersNames')
|
||||
->leftJoin('activity.thirdParties', 'thirdparty')
|
||||
->addSelect('AGGREGATE(thirdparty.id) AS thirdPartiesIds')
|
||||
->addSelect('AGGREGATE(thirdparty.id) AS thirdPartiesNames')
|
||||
@ -68,9 +68,9 @@ class ListActivityHelper
|
||||
->leftJoin('activity.location', 'location')
|
||||
->addSelect('location.name AS locationName')
|
||||
->addSelect('activity.sentReceived')
|
||||
->addSelect('IDENTITY(activity.createdBy) AS createdBy')
|
||||
->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.createdBy), \'d\', activity.createdAt) AS createdBy')
|
||||
->addSelect('activity.createdAt')
|
||||
->addSelect('IDENTITY(activity.updatedBy) AS updatedBy')
|
||||
->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.updatedBy), \'d\', activity.updatedAt) AS updatedBy')
|
||||
->addSelect('activity.updatedAt')
|
||||
->addGroupBy('activity.id')
|
||||
->addGroupBy('location.id');
|
||||
|
@ -13,17 +13,21 @@ namespace Chill\ActivityBundle\Export\Filter;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
class UserScopeFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(private readonly TranslatableStringHelper $translatableStringHelper) {}
|
||||
private const PREFIX = 'acp_act_filter_user_scope'; // creator ? cfr translation
|
||||
|
||||
public function __construct(
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -32,22 +36,33 @@ class UserScopeFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('actuser', $qb->getAllAliases(), true)) {
|
||||
$qb->join('activity.user', 'actuser');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$where = $qb->getDQLPart('where');
|
||||
|
||||
$clause = $qb->expr()->in('actuser.mainScope', ':userscope');
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter('userscope', $data['accepted_userscope']);
|
||||
$qb
|
||||
->leftJoin("activity.user", "{$p}_user") // createdBy ? cfr translation
|
||||
->leftJoin(
|
||||
UserScopeHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
|
||||
)
|
||||
// scope_at based on activity.date
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "activity.date"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "activity.date")
|
||||
)
|
||||
)
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->in("{$p}_history.scope", ":{$p}_scopes")
|
||||
)
|
||||
->setParameter(
|
||||
"{$p}_scopes",
|
||||
$data["scopes"],
|
||||
);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -57,37 +72,41 @@ class UserScopeFilter implements FilterInterface
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('accepted_userscope', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
$builder
|
||||
->add('scopes', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
{
|
||||
$scopes = [];
|
||||
|
||||
foreach ($data['accepted_userscope'] as $s) {
|
||||
foreach ($data['scopes'] as $s) {
|
||||
$scopes[] = $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
);
|
||||
}
|
||||
|
||||
return ['Filtered activity by userscope: only %scopes%', [
|
||||
return ['export.filter.activity.by_creator_scope.Filtered activity by user scope: only %scopes%', [
|
||||
'%scopes%' => implode(', ', $scopes),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [
|
||||
'scopes' => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Filter activity by userscope';
|
||||
return 'export.filter.activity.by_creator_scope.Filter activity by user scope';
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ namespace Chill\ActivityBundle\Export\Filter;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
@ -22,7 +23,11 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class UsersJobFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(private readonly TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||
private const PREFIX = 'act_filter_user_job';
|
||||
|
||||
public function __construct(
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -31,14 +36,25 @@ class UsersJobFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM ' . Activity::class . ' activity_users_job_filter_act
|
||||
JOIN activity_users_job_filter_act.users users WHERE users.userJob IN (:activity_users_job_filter_jobs) AND activity_users_job_filter_act = activity '
|
||||
"SELECT 1 FROM " . Activity::class . " {$p}_act "
|
||||
. "JOIN {$p}_act.users {$p}_user "
|
||||
. "JOIN " . UserJobHistory::class . " {$p}_history WITH {$p}_history.user = {$p}_user "
|
||||
. "WHERE {$p}_act = activity "
|
||||
// job_at based on activity.date
|
||||
. "AND {$p}_history.startDate <= activity.date "
|
||||
. "AND ({$p}_history.endDate IS NULL OR {$p}_history.endDate > activity.date) "
|
||||
. "AND {$p}_history.job IN ( :{$p}_jobs )"
|
||||
)
|
||||
)
|
||||
->setParameter('activity_users_job_filter_jobs', $data['jobs']);
|
||||
->setParameter(
|
||||
"{$p}_jobs",
|
||||
$data["jobs"]
|
||||
);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
@ -48,21 +64,18 @@ class UsersJobFilter implements FilterInterface
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('jobs', EntityType::class, [
|
||||
'class' => UserJob::class,
|
||||
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize($j->getLabel()),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
$builder
|
||||
->add('jobs', EntityType::class, [
|
||||
'class' => UserJob::class,
|
||||
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize($j->getLabel()),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
return ['export.filter.activity.by_usersjob.Filtered activity by users job: only %jobs%', [
|
||||
return ['export.filter.activity.by_users_job.Filtered activity by users job: only %jobs%', [
|
||||
'%jobs%' => implode(
|
||||
', ',
|
||||
array_map(
|
||||
@ -73,8 +86,15 @@ class UsersJobFilter implements FilterInterface
|
||||
]];
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [
|
||||
'jobs' => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'export.filter.activity.by_usersjob.Filter by users job';
|
||||
return 'export.filter.activity.by_users_job.Filter by users job';
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace Chill\ActivityBundle\Export\Filter;
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
@ -23,7 +24,12 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class UsersScopeFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(private readonly ScopeRepositoryInterface $scopeRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||
private const PREFIX = 'act_filter_user_scope';
|
||||
|
||||
public function __construct(
|
||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -32,39 +38,47 @@ class UsersScopeFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM ' . Activity::class . ' activity_users_scope_filter_act
|
||||
JOIN activity_users_scope_filter_act.users users WHERE users.mainScope IN (:activity_users_scope_filter_scopes) AND activity_users_scope_filter_act = activity '
|
||||
"SELECT 1 FROM " . Activity::class . " {$p}_act "
|
||||
. "JOIN {$p}_act.users {$p}_user "
|
||||
. "JOIN " . UserScopeHistory::class . " {$p}_history WITH {$p}_history.user = {$p}_user "
|
||||
. "WHERE {$p}_act = activity "
|
||||
// scope_at based on activity.date
|
||||
. "AND {$p}_history.startDate <= activity.date "
|
||||
. "AND ({$p}_history.endDate IS NULL OR {$p}_history.endDate > activity.date) "
|
||||
. "AND {$p}_history.scope IN ( :{$p}_scopes )"
|
||||
)
|
||||
)
|
||||
->setParameter('activity_users_scope_filter_scopes', $data['scopes']);
|
||||
->setParameter(
|
||||
"{$p}_scopes",
|
||||
$data["scopes"]
|
||||
);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACTIVITY;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('scopes', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
'choices' => $this->scopeRepository->findAllActive(),
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
$builder
|
||||
->add('scopes', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
'choices' => $this->scopeRepository->findAllActive(),
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
{
|
||||
return ['export.filter.activity.by_usersscope.Filtered activity by users scope: only %scopes%', [
|
||||
return ['export.filter.activity.by_users_scope.Filtered activity by users scope: only %scopes%', [
|
||||
'%scopes%' => implode(
|
||||
', ',
|
||||
array_map(
|
||||
@ -75,8 +89,15 @@ class UsersScopeFilter implements FilterInterface
|
||||
]];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return 'export.filter.activity.by_usersscope.Filter by users scope';
|
||||
return [
|
||||
'scopes' => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.filter.activity.by_users_scope.Filter by users scope';
|
||||
}
|
||||
}
|
||||
|
@ -122,9 +122,30 @@ final readonly class ActivityACLAwareRepository implements ActivityACLAwareRepos
|
||||
->leftJoin('a.user', 'activity_u')
|
||||
->andWhere(
|
||||
$qb->expr()->orX(
|
||||
'creator.userJob IN (:jobs)',
|
||||
'activity_u.userJob IN (:jobs)',
|
||||
'EXISTS (SELECT 1 FROM ' . User::class . ' activity_user WHERE activity_user MEMBER OF a.users AND activity_user.userJob IN (:jobs))'
|
||||
$qb->expr()->exists(
|
||||
sprintf(
|
||||
"SELECT 1 FROM %s ujh_creator WHERE ujh_creator.user = a.createdBy "
|
||||
. "AND ujh_creator.job IN (:jobs) AND a.createdAt > ujh_creator.startDate "
|
||||
. "AND (ujh_creator.endDate IS NULL or ujh_creator.endDate > a.date)",
|
||||
User\UserJobHistory::class
|
||||
)
|
||||
),
|
||||
$qb->expr()->exists(
|
||||
sprintf(
|
||||
"SELECT 1 FROM %s ujh_u WHERE ujh_u.user = a.user "
|
||||
. "AND ujh_u.job IN (:jobs) AND a.createdAt > ujh_u.startDate "
|
||||
. "AND (ujh_u.endDate IS NULL or ujh_u.endDate > a.date)",
|
||||
User\UserJobHistory::class
|
||||
)
|
||||
),
|
||||
$qb->expr()->exists(
|
||||
sprintf(
|
||||
"SELECT 1 FROM %s ujh_users WHERE ujh_users.user MEMBER OF a.users "
|
||||
. "AND ujh_users.job IN (:jobs) AND a.createdAt > ujh_users.startDate "
|
||||
. "AND (ujh_users.endDate IS NULL or ujh_users.endDate > a.date)",
|
||||
User\UserJobHistory::class
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
->setParameter('jobs', $jobs);
|
||||
@ -175,13 +196,14 @@ final readonly class ActivityACLAwareRepository implements ActivityACLAwareRepos
|
||||
public function findUserJobByAssociated(AccompanyingPeriod|Person $associated): array
|
||||
{
|
||||
$in = $this->em->createQueryBuilder();
|
||||
$in->select('IDENTITY(u.userJob)')
|
||||
->from(User::class, 'u')
|
||||
$in->select('IDENTITY(u.job)')
|
||||
->distinct()
|
||||
->from(User\UserJobHistory::class, 'u')
|
||||
->join(
|
||||
Activity::class,
|
||||
'a',
|
||||
Join::WITH,
|
||||
'a.createdBy = u OR a.user = u OR u MEMBER OF a.users'
|
||||
'a.createdBy = u.user OR a.user = u.user OR u.user MEMBER OF a.users AND a.date >= u.startDate ANd (u.endDate IS NULL or u.endDate > a.date)'
|
||||
);
|
||||
|
||||
if ($associated instanceof Person) {
|
||||
|
@ -12,7 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\ActivityBundle\Tests\Export\Aggregator\ACPAggregators;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\ActivityBundle\Export\Aggregator\BySocialActionAggregator;
|
||||
use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\BySocialActionAggregator;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
|
@ -12,7 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\ActivityBundle\Tests\Export\Aggregator\ACPAggregators;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\ActivityBundle\Export\Aggregator\BySocialIssueAggregator;
|
||||
use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\BySocialIssueAggregator;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
|
@ -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\Aggregator;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\ActivityBundle\Export\Aggregator\CreatorScopeAggregator;
|
||||
use Chill\ActivityBundle\Export\Aggregator\JobScopeAggregator;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class JobScopeAggregatorTest extends AbstractAggregatorTest
|
||||
{
|
||||
private JobScopeAggregator $aggregator;
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$this->aggregator = self::$container->get(JobScopeAggregator::class);
|
||||
}
|
||||
|
||||
public function getAggregator()
|
||||
{
|
||||
return $this->aggregator;
|
||||
}
|
||||
|
||||
public function getFormData(): array
|
||||
{
|
||||
return [
|
||||
[],
|
||||
];
|
||||
}
|
||||
|
||||
public function getQueryBuilders(): array
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$em = self::$container->get(EntityManagerInterface::class);
|
||||
|
||||
return [
|
||||
$em->createQueryBuilder()
|
||||
->select('count(activity.id)')
|
||||
->from(Activity::class, 'activity')
|
||||
->leftJoin('activity.accompanyingPeriod', 'acp')
|
||||
->leftJoin('activity.user', 'actuser'),
|
||||
];
|
||||
}
|
||||
}
|
@ -53,7 +53,7 @@ final class UserScopeFilterTest extends AbstractFilterTest
|
||||
|
||||
foreach ($array as $a) {
|
||||
$data[] = [
|
||||
'accepted_userscope' => $a,
|
||||
'scopes' => $a,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -177,6 +177,10 @@ services:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_creator_scope_aggregator }
|
||||
|
||||
Chill\ActivityBundle\Export\Aggregator\JobScopeAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_creator_job_aggregator }
|
||||
|
||||
Chill\ActivityBundle\Export\Aggregator\ActivityUsersAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_users_aggregator }
|
||||
|
@ -287,8 +287,6 @@ Filter activity by creator: Filtrer les échanges par créateur de l'échange
|
||||
'Filtered activity by users: only %users%': "Filtré par utilisateurs participants: uniquement %users%"
|
||||
'Filtered activity by creator: only %users%': "Filtré par créateur: uniquement %users%"
|
||||
Creators: Créateurs
|
||||
Filter activity by userscope: Filtrer les échanges par service du créateur
|
||||
'Filtered activity by userscope: only %scopes%': "Filtré par service du créateur: uniquement %scopes%"
|
||||
Accepted userscope: Services
|
||||
|
||||
Filter acp which has no activity: Filtrer les parcours qui n’ont pas d’échange
|
||||
@ -306,10 +304,6 @@ Aggregate by activity user: Grouper les échanges par référent
|
||||
Aggregate by activity users: Grouper les échanges par utilisateurs participants
|
||||
Aggregate by activity type: Grouper les échanges par type
|
||||
Aggregate by activity reason: Grouper les échanges par sujet
|
||||
Aggregate by users scope: Grouper les échanges par service principal de l'utilisateur
|
||||
Users 's scope: Service principal des utilisateurs participants à l'échange
|
||||
Aggregate by users job: Grouper les échanges par métier des utilisateurs participants
|
||||
Users 's job: Métier des utilisateurs participants à l'échange
|
||||
|
||||
Group activity by locationtype: Grouper les échanges par type de localisation
|
||||
Group activity by date: Grouper les échanges par date
|
||||
@ -320,7 +314,6 @@ for week: Semaine
|
||||
by year: Par année
|
||||
in year: En
|
||||
Group activity by creator: Grouper les échanges par créateur de l'échange
|
||||
Group activity by creator scope: Grouper les échanges par service du créateur de l'échange
|
||||
Group activity by linked thirdparties: Grouper les échanges par tiers impliqué
|
||||
Accepted thirdparty: Tiers impliqué
|
||||
Group activity by linked socialaction: Grouper les échanges par action liée
|
||||
@ -367,10 +360,10 @@ export:
|
||||
|
||||
filter:
|
||||
activity:
|
||||
by_usersjob:
|
||||
by_users_job:
|
||||
Filter by users job: Filtrer les échanges par métier d'au moins un utilisateur participant
|
||||
'Filtered activity by users job: only %jobs%': 'Filtré par métier d''au moins un utilisateur participant: seulement %jobs%'
|
||||
by_usersscope:
|
||||
by_users_scope:
|
||||
Filter by users scope: Filtrer les échanges par services d'au moins un utilisateur participant
|
||||
'Filtered activity by users scope: only %scopes%': 'Filtré par service d''au moins un utilisateur participant: seulement %scopes%'
|
||||
course_having_activity_between_date:
|
||||
@ -379,7 +372,9 @@ export:
|
||||
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%'
|
||||
|
||||
by_creator_scope:
|
||||
Filter activity by user scope: Filtrer les échanges par service du créateur de l'échange
|
||||
'Filtered activity by user scope: only %scopes%': "Filtré par service du créateur: uniquement %scopes%"
|
||||
|
||||
aggregator:
|
||||
activity:
|
||||
@ -391,6 +386,18 @@ export:
|
||||
by_location:
|
||||
Activity Location: Localisation de l'échange
|
||||
Title: Grouper les échanges par localisation de l'échange
|
||||
by_user_job:
|
||||
Users 's job: Métier des utilisateurs participants à l'échange
|
||||
Aggregate by users job: Grouper les échanges par métier des utilisateurs participants
|
||||
by_user_scope:
|
||||
Users 's scope: Service principal des utilisateurs participants à l'échange
|
||||
Aggregate by users scope: Grouper les échanges par service principal de l'utilisateur
|
||||
by_creator_scope:
|
||||
Group activity by creator scope: Grouper les échanges par service du créateur de l'échange
|
||||
Calc date: Date de calcul du service du créateur de l'échange
|
||||
by_creator_job:
|
||||
Group activity by creator job: Grouper les échanges par service du créateur de l'échange
|
||||
Calc date: Date de calcul du service du créateur de l'échange
|
||||
|
||||
generic_doc:
|
||||
filter:
|
||||
|
@ -12,17 +12,22 @@ declare(strict_types=1);
|
||||
namespace Chill\AsideActivityBundle\Export\Aggregator;
|
||||
|
||||
use Chill\AsideActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
use function in_array;
|
||||
|
||||
class ByUserJobAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private readonly UserJobRepositoryInterface $userJobRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||
private const PREFIX = 'aside_act_agg_user_job';
|
||||
|
||||
public function __construct(
|
||||
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -31,24 +36,37 @@ class ByUserJobAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('aside_user', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('aside.agent', 'aside_user');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->addSelect('IDENTITY(aside_user.userJob) AS aside_activity_user_job_aggregator')
|
||||
->addGroupBy('aside_activity_user_job_aggregator');
|
||||
->leftJoin("aside.agent", "{$p}_user")
|
||||
->leftJoin(
|
||||
UserJobHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
|
||||
)
|
||||
// job_at based on aside.date
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "aside.date"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "aside.date")
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_history.job) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// nothing to add in the form
|
||||
}
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
@ -75,11 +93,11 @@ class ByUserJobAggregator implements AggregatorInterface
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['aside_activity_user_job_aggregator'];
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.aggregator.Aggregate by user job';
|
||||
return 'export.aggregator.by_user_job.Aggregate by user job';
|
||||
}
|
||||
}
|
||||
|
@ -12,17 +12,22 @@ declare(strict_types=1);
|
||||
namespace Chill\AsideActivityBundle\Export\Aggregator;
|
||||
|
||||
use Chill\AsideActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
use function in_array;
|
||||
|
||||
class ByUserScopeAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private readonly ScopeRepositoryInterface $scopeRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||
private const PREFIX = 'aside_act_agg_user_scope';
|
||||
|
||||
public function __construct(
|
||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -31,24 +36,36 @@ class ByUserScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('aside_user', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('aside.agent', 'aside_user');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->addSelect('IDENTITY(aside_user.mainScope) AS aside_activity_user_scope_aggregator')
|
||||
->addGroupBy('aside_activity_user_scope_aggregator');
|
||||
->leftJoin("aside.agent", "{$p}_user")
|
||||
->leftJoin(
|
||||
UserScopeHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "aside.date"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "aside.date")
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_history.scope) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// nothing to add in the form
|
||||
}
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
@ -75,11 +92,11 @@ class ByUserScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['aside_activity_user_scope_aggregator'];
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.aggregator.Aggregate by user scope';
|
||||
return 'export.aggregator.by_user_scope.Aggregate by user scope';
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +182,10 @@ final readonly class ListAsideActivity implements ListInterface, GroupedExportIn
|
||||
{
|
||||
$qb = $this->em->createQueryBuilder()
|
||||
->from(AsideActivity::class, 'aside')
|
||||
->leftJoin('aside.agent', 'agent');
|
||||
->leftJoin('aside.agent', 'agent')
|
||||
->leftJoin('agent.scopeHistories', 'scopeHistories')
|
||||
->andWhere('scopeHistories.startDate <= aside.date AND (scopeHistories.endDate IS NULL or scopeHistories.endDate > aside.date)')
|
||||
;
|
||||
|
||||
$qb
|
||||
->addSelect('aside.id AS id')
|
||||
@ -190,7 +193,7 @@ final readonly class ListAsideActivity implements ListInterface, GroupedExportIn
|
||||
->addSelect('aside.updatedAt AS updatedAt')
|
||||
->addSelect('IDENTITY(aside.agent) AS agent_id')
|
||||
->addSelect('IDENTITY(aside.createdBy) AS creator_id')
|
||||
->addSelect('IDENTITY(agent.mainScope) AS main_scope')
|
||||
->addSelect('IDENTITY(scopeHistories.scope) AS main_scope')
|
||||
->addSelect('IDENTITY(agent.mainCenter) AS main_center')
|
||||
->addSelect('IDENTITY(aside.type) AS aside_activity_type')
|
||||
->addSelect('aside.date')
|
||||
|
@ -13,6 +13,7 @@ namespace Chill\AsideActivityBundle\Export\Filter;
|
||||
|
||||
use Chill\AsideActivityBundle\Entity\AsideActivity;
|
||||
use Chill\AsideActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
@ -22,7 +23,11 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class ByUserJobFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(private readonly TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||
private const PREFIX = 'aside_act_filter_user_job';
|
||||
|
||||
public function __construct(
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -31,38 +36,46 @@ class ByUserJobFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM ' . AsideActivity::class . ' aside_activity_user_job_filter_act
|
||||
JOIN aside_activity_user_job_filter_act.agent aside_activity_user_job_filter_user WHERE aside_activity_user_job_filter_user.userJob IN (:aside_activity_user_job_filter_jobs) AND aside_activity_user_job_filter_act = aside'
|
||||
"SELECT 1 FROM " . AsideActivity::class . " {$p}_act "
|
||||
. "JOIN {$p}_act.agent {$p}_user "
|
||||
. "JOIN " . UserJobHistory::class . " {$p}_history WITH {$p}_history.user = {$p}_user "
|
||||
. "WHERE {$p}_act = aside "
|
||||
// job_at based on aside.date
|
||||
. "AND {$p}_history.startDate <= aside.date "
|
||||
. "AND ({$p}_history.endDate IS NULL OR {$p}_history.endDate > aside.date) "
|
||||
. "AND {$p}_history.job IN ( :{$p}_jobs )"
|
||||
)
|
||||
)
|
||||
->setParameter('aside_activity_user_job_filter_jobs', $data['jobs']);
|
||||
->setParameter(
|
||||
"{$p}_jobs",
|
||||
$data["jobs"],
|
||||
);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('jobs', EntityType::class, [
|
||||
'class' => UserJob::class,
|
||||
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize($j->getLabel()),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
$builder
|
||||
->add('jobs', EntityType::class, [
|
||||
'class' => UserJob::class,
|
||||
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize($j->getLabel()),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
{
|
||||
return ['export.filter.Filtered aside activities by user jobs: only %jobs%', [
|
||||
return ['export.filter.by_user_job.Filtered aside activities by user jobs: only %jobs%', [
|
||||
'%jobs%' => implode(
|
||||
', ',
|
||||
array_map(
|
||||
@ -73,8 +86,15 @@ class ByUserJobFilter implements FilterInterface
|
||||
]];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return 'export.filter.Filter by user jobs';
|
||||
return [
|
||||
'jobs' => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.filter.by_user_job.Filter by user jobs';
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace Chill\AsideActivityBundle\Export\Filter;
|
||||
use Chill\AsideActivityBundle\Entity\AsideActivity;
|
||||
use Chill\AsideActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
@ -23,7 +24,12 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class ByUserScopeFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(private readonly ScopeRepositoryInterface $scopeRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||
private const PREFIX = 'aside_act_filter_user_scope';
|
||||
|
||||
public function __construct(
|
||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -32,39 +38,47 @@ class ByUserScopeFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM ' . AsideActivity::class . ' aside_activity_user_scope_filter_act
|
||||
JOIN aside_activity_user_scope_filter_act.agent aside_activity_user_scope_filter_user WHERE aside_activity_user_scope_filter_user.mainScope IN (:aside_activity_user_scope_filter_scopes) AND aside_activity_user_scope_filter_act = aside '
|
||||
"SELECT 1 FROM " . AsideActivity::class . " {$p}_act "
|
||||
. "JOIN {$p}_act.agent {$p}_user "
|
||||
. "JOIN " . UserScopeHistory::class . " {$p}_history WITH {$p}_history.user = {$p}_user "
|
||||
. "WHERE {$p}_act = aside "
|
||||
// scope_at based on aside.date
|
||||
. "AND {$p}_history.startDate <= aside.date "
|
||||
. "AND ({$p}_history.endDate IS NULL OR {$p}_history.endDate > aside.date) "
|
||||
. "AND {$p}_history.scope IN ( :{$p}_scopes )"
|
||||
)
|
||||
)
|
||||
->setParameter('aside_activity_user_scope_filter_scopes', $data['scopes']);
|
||||
->setParameter(
|
||||
"{$p}_scopes",
|
||||
$data["scopes"],
|
||||
);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('scopes', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
'choices' => $this->scopeRepository->findAllActive(),
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
$builder
|
||||
->add('scopes', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
'choices' => $this->scopeRepository->findAllActive(),
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
return ['export.filter.Filtered aside activities by user scope: only %scopes%', [
|
||||
return ['export.filter.by_user_scope.Filtered aside activities by user scope: only %scopes%', [
|
||||
'%scopes%' => implode(
|
||||
', ',
|
||||
array_map(
|
||||
@ -75,8 +89,15 @@ class ByUserScopeFilter implements FilterInterface
|
||||
]];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return 'export.filter.Filter by user scope';
|
||||
return [
|
||||
'scopes' => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.filter.by_user_scope.Filter by user scope';
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
<?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\AsideActivityBundle\Tests\Export\Export;
|
||||
|
||||
use Chill\AsideActivityBundle\Export\Export\ListAsideActivity;
|
||||
use Chill\MainBundle\Test\Export\AbstractExportTest;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class ListAsideActivityTest extends KernelTestCase
|
||||
{
|
||||
private ListAsideActivity $listAsideActivity;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
self::bootKernel();
|
||||
$this->listAsideActivity = self::$container->get(ListAsideActivity::class);
|
||||
}
|
||||
|
||||
public function testExecuteQuery(): void
|
||||
{
|
||||
$qb = $this->listAsideActivity->initiateQuery([], [], [])
|
||||
->setMaxResults(1);
|
||||
|
||||
$results = $qb->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||
|
||||
self::assertIsArray($results, "smoke test: test that the result is an array");
|
||||
}
|
||||
}
|
@ -200,17 +200,21 @@ export:
|
||||
Aside activities before this date: Actvitités annexes avant cette date
|
||||
'Filtered aside activity by user: only %users%': "Filtré par utilisateur: uniquement %users%"
|
||||
Filter aside activity by user: Filtrer par utilisateur
|
||||
'Filtered aside activities by user jobs: only %jobs%': "Filtré par métier des utilisateurs: uniquement %jobs%"
|
||||
Filter by user jobs: Filtrer les activités annexes par métier des utilisateurs
|
||||
'Filtered aside activities by user scope: only %scopes%': "Filtré par service des utilisateur: uniquement %scopes%"
|
||||
Filter by user scope: Filtrer les activités annexes par service d'utilisateur
|
||||
by_user_job:
|
||||
'Filtered aside activities by user jobs: only %jobs%': "Filtré par métier des utilisateurs: uniquement %jobs%"
|
||||
Filter by user jobs: Filtrer les activités annexes par métier des utilisateurs
|
||||
by_user_scope:
|
||||
'Filtered aside activities by user scope: only %scopes%': "Filtré par service des utilisateurs: uniquement %scopes%"
|
||||
Filter by user scope: Filtrer les activités annexes par service d'utilisateur
|
||||
Filter by aside activity location: Filtrer les activités annexes par localisation
|
||||
'Filtered by aside activity location: only %location%': "Filtré par localisation: uniquement %location%"
|
||||
aggregator:
|
||||
Group by aside activity type: Grouper les activités annexes par type d'activité
|
||||
Aside activity type: Type d'activité annexe
|
||||
Aggregate by user job: Grouper les activités annexes par métier des utilisateurs
|
||||
Aggregate by user scope: Grouper les activités annexes par service des utilisateurs
|
||||
by_user_job:
|
||||
Aggregate by user job: Grouper les activités annexes par métier des utilisateurs
|
||||
by_user_scope:
|
||||
Aggregate by user scope: Grouper les activités annexes par service des utilisateurs
|
||||
Aside activity location: Localisation des activités annexe
|
||||
Group by aside activity location: Grouper les activités annexes par localisation
|
||||
Aside activity localisation: Localisation
|
||||
|
@ -12,17 +12,23 @@ declare(strict_types=1);
|
||||
namespace Chill\CalendarBundle\Export\Aggregator;
|
||||
|
||||
use Chill\CalendarBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\UserJobRepository;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Closure;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
final readonly class JobAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private UserJobRepository $jobRepository, private TranslatableStringHelper $translatableStringHelper) {}
|
||||
private const PREFIX = 'cal_agg_job';
|
||||
|
||||
public function __construct(
|
||||
private UserJobRepository $jobRepository,
|
||||
private TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -31,12 +37,28 @@ final readonly class JobAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('caluser', $qb->getAllAliases(), true)) {
|
||||
$qb->join('cal.mainUser', 'caluser');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb->addSelect('IDENTITY(caluser.userJob) as job_aggregator');
|
||||
$qb->addGroupBy('job_aggregator');
|
||||
$qb
|
||||
->leftJoin('cal.mainUser', "{$p}_user")
|
||||
->leftJoin(
|
||||
UserJobHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
|
||||
)
|
||||
// job_at based on cal.startDate
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "cal.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "cal.startDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_history.job) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -44,10 +66,8 @@ final readonly class JobAggregator implements AggregatorInterface
|
||||
return Declarations::CALENDAR_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// no form
|
||||
}
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
@ -64,7 +84,9 @@ final readonly class JobAggregator implements AggregatorInterface
|
||||
return '';
|
||||
}
|
||||
|
||||
$j = $this->jobRepository->find($value);
|
||||
if (null === $j = $this->jobRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
@ -74,11 +96,11 @@ final readonly class JobAggregator implements AggregatorInterface
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['job_aggregator'];
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Group calendars by agent job';
|
||||
return 'export.aggregator.calendar.agent_job.Group calendars by agent job';
|
||||
}
|
||||
}
|
||||
|
@ -12,16 +12,19 @@ declare(strict_types=1);
|
||||
namespace Chill\CalendarBundle\Export\Aggregator;
|
||||
|
||||
use Chill\CalendarBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepository;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Closure;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
final readonly class ScopeAggregator implements AggregatorInterface
|
||||
{
|
||||
private const PREFIX = "cal_agg_scope";
|
||||
|
||||
public function __construct(
|
||||
private ScopeRepository $scopeRepository,
|
||||
private TranslatableStringHelper $translatableStringHelper
|
||||
@ -34,12 +37,28 @@ final readonly class ScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('caluser', $qb->getAllAliases(), true)) {
|
||||
$qb->join('cal.mainUser', 'caluser');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb->addSelect('IDENTITY(caluser.mainScope) as scope_aggregator');
|
||||
$qb->addGroupBy('scope_aggregator');
|
||||
$qb
|
||||
->leftJoin("cal.mainUser", "{$p}_user")
|
||||
->leftJoin(
|
||||
UserScopeHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
|
||||
)
|
||||
// scope_at based on cal.startDate
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "cal.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "cal.startDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_history.scope) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -47,10 +66,8 @@ final readonly class ScopeAggregator implements AggregatorInterface
|
||||
return Declarations::CALENDAR_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// no form
|
||||
}
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
@ -67,7 +84,9 @@ final readonly class ScopeAggregator implements AggregatorInterface
|
||||
return '';
|
||||
}
|
||||
|
||||
$s = $this->scopeRepository->find($value);
|
||||
if (null === $s = $this->scopeRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
@ -77,11 +96,11 @@ final readonly class ScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['scope_aggregator'];
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Group calendars by agent scope';
|
||||
return 'export.aggregator.calendar.agent_scope.Group calendars by agent scope';
|
||||
}
|
||||
}
|
||||
|
@ -12,19 +12,23 @@ declare(strict_types=1);
|
||||
namespace Chill\CalendarBundle\Export\Filter;
|
||||
|
||||
use Chill\CalendarBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function in_array;
|
||||
|
||||
class JobFilter implements FilterInterface
|
||||
final readonly class JobFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(protected TranslatorInterface $translator, private readonly TranslatableStringHelper $translatableStringHelper) {}
|
||||
private const PREFIX = 'cal_filter_job';
|
||||
|
||||
public function __construct(
|
||||
private TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -33,21 +37,32 @@ class JobFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('caluser', $qb->getAllAliases(), true)) {
|
||||
$qb->join('cal.mainUser', 'caluser');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$where = $qb->getDQLPart('where');
|
||||
$clause = $qb->expr()->in('caluser.userJob', ':job');
|
||||
$qb
|
||||
->leftJoin("cal.mainUser", "{$p}_user")
|
||||
->leftJoin(
|
||||
UserJobHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
|
||||
)
|
||||
// job_at based on cal.startDate
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "cal.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "cal.startDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
->andWhere($qb->expr()->in("{$p}_history.job", ":{$p}_job"))
|
||||
->setParameter(
|
||||
"{$p}_job",
|
||||
$data["job"]
|
||||
);
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter('job', $data['job']);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -57,18 +72,15 @@ class JobFilter implements FilterInterface
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('job', EntityType::class, [
|
||||
'class' => UserJob::class,
|
||||
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
$builder
|
||||
->add('job', EntityType::class, [
|
||||
'class' => UserJob::class,
|
||||
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
@ -81,13 +93,20 @@ class JobFilter implements FilterInterface
|
||||
);
|
||||
}
|
||||
|
||||
return ['Filtered by agent job: only %jobs%', [
|
||||
return ['export.filter.calendar.agent_job.Filtered by agent job: only %jobs%', [
|
||||
'%jobs%' => implode(', ', $userJobs),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [
|
||||
'job' => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Filter calendars by agent job';
|
||||
return 'export.filter.calendar.agent_job.Filter calendars by agent job';
|
||||
}
|
||||
}
|
||||
|
@ -13,18 +13,23 @@ namespace Chill\CalendarBundle\Export\Filter;
|
||||
|
||||
use Chill\CalendarBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function in_array;
|
||||
|
||||
class ScopeFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(protected TranslatorInterface $translator, private readonly TranslatableStringHelper $translatableStringHelper) {}
|
||||
private const PREFIX = 'cal_filter_scope';
|
||||
|
||||
public function __construct(
|
||||
protected TranslatorInterface $translator,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -33,45 +38,52 @@ class ScopeFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('caluser', $qb->getAllAliases(), true)) {
|
||||
$qb->join('cal.mainUser', 'caluser');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$where = $qb->getDQLPart('where');
|
||||
$clause = $qb->expr()->in('caluser.mainScope', ':scope');
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter('scope', $data['scope']);
|
||||
$qb
|
||||
->leftJoin("cal.mainUser", "{$p}_user")
|
||||
->leftJoin(
|
||||
UserScopeHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
|
||||
)
|
||||
// scope_at based on cal.startDate
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "cal.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "cal.startDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
->andWhere($qb->expr()->in("{$p}_history.scope", ":{$p}_scope"))
|
||||
->setParameter(
|
||||
"{$p}_scope",
|
||||
$data["scope"]
|
||||
);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::CALENDAR_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('scope', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
$builder
|
||||
->add('scope', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
{
|
||||
$scopes = [];
|
||||
|
||||
@ -81,13 +93,20 @@ class ScopeFilter implements FilterInterface
|
||||
);
|
||||
}
|
||||
|
||||
return ['Filtered by agent scope: only %scopes%', [
|
||||
return ['export.filter.calendar.agent_scope.Filtered by agent scope: only %scopes%', [
|
||||
'%scopes%' => implode(', ', $scopes),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return 'Filter calendars by agent scope';
|
||||
return [
|
||||
'scope' => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.filter.calendar.agent_scope.Filter calendars by agent scope';
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ final class JobAggregatorTest extends AbstractAggregatorTest
|
||||
public function getFormData(): array
|
||||
{
|
||||
return [
|
||||
[],
|
||||
[]
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ use Chill\CalendarBundle\Entity\Calendar;
|
||||
use Chill\CalendarBundle\Export\Filter\JobFilter;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
@ -62,7 +63,11 @@ final class JobFilterTest extends AbstractFilterTest
|
||||
->setMaxResults(1)
|
||||
->getResult();
|
||||
|
||||
yield ['job' => $array];
|
||||
return [
|
||||
[
|
||||
'job' => new ArrayCollection($array)
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function getQueryBuilders(): array
|
||||
|
@ -22,6 +22,7 @@ use Chill\CalendarBundle\Entity\Calendar;
|
||||
use Chill\CalendarBundle\Export\Filter\ScopeFilter;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
@ -62,15 +63,11 @@ final class ScopeFilterTest extends AbstractFilterTest
|
||||
->setMaxResults(1)
|
||||
->getResult();
|
||||
|
||||
$data = [];
|
||||
|
||||
foreach ($array as $a) {
|
||||
$data[] = [
|
||||
'scope' => $a,
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
return [
|
||||
[
|
||||
'scope' => new ArrayCollection($array)
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function getQueryBuilders(): array
|
||||
|
@ -99,24 +99,32 @@ Get the sum of appointment durations according to various filters: Calcule la so
|
||||
|
||||
'Filtered by agent: only %agents%': "Filtré par agents: uniquement %agents%"
|
||||
Filter calendars by agent: Filtrer les rendez-vous par agents
|
||||
Filter calendars by agent job: Filtrer les rendez-vous par métiers des agents
|
||||
'Filtered by agent job: only %jobs%': 'Filtré par métiers des agents: uniquement les %jobs%'
|
||||
Filter calendars by agent scope: Filtrer les rendez-vous par services des agents
|
||||
'Filtered by agent scope: only %scopes%': 'Filtré par services des agents: uniquement les services %scopes%'
|
||||
Filter calendars between certain dates: Filtrer les rendez-vous par date du rendez-vous
|
||||
'Filtered by calendars between %dateFrom% and %dateTo%': 'Filtré par rendez-vous entre %dateFrom% et %dateTo%'
|
||||
'Filtered by calendar range: only %calendarRange%': 'Filtré par rendez-vous par plage de disponibilité: uniquement les %calendarRange%'
|
||||
Filter by calendar range: Filtrer par rendez-vous dans une plage de disponibilité ou non
|
||||
|
||||
Group calendars by agent: Grouper les rendez-vous par agent
|
||||
Group calendars by agent job: Grouper les rendez-vous par métier de l'agent
|
||||
Group calendars by agent scope: Grouper les rendez-vous par service de l'agent
|
||||
Group calendars by location type: Grouper les rendez-vous par type de localisation
|
||||
Group calendars by location: Grouper les rendez-vous par lieu de rendez-vous
|
||||
Group calendars by cancel reason: Grouper les rendez-vous par motif d'annulation
|
||||
Group calendars by month and year: Grouper les rendez-vous par mois et année
|
||||
Group calendars by urgency: Grouper les rendez-vous par urgent ou non
|
||||
|
||||
export:
|
||||
aggregator.calendar:
|
||||
agent_job:
|
||||
Group calendars by agent job: Grouper les rendez-vous par métier de l'agent
|
||||
agent_scope:
|
||||
Group calendars by agent scope: Grouper les rendez-vous par service de l'agent
|
||||
filter.calendar:
|
||||
agent_job:
|
||||
Filter calendars by agent job: Filtrer les rendez-vous par métiers des agents (utilisateurs principaux)
|
||||
'Filtered by agent job: only %jobs%': 'Filtré par métiers des agents (utilisateurs principaux): uniquement les %jobs%'
|
||||
agent_scope:
|
||||
Filter calendars by agent scope: Filtrer les rendez-vous par services des agents (utilisateurs principaux)
|
||||
'Filtered by agent scope: only %scopes%': 'Filtré par services des agents (utilisateurs principaux): uniquement les services %scopes%'
|
||||
|
||||
Scope: Service
|
||||
Job: Métier
|
||||
Location type: Type de localisation
|
||||
|
@ -50,7 +50,7 @@ final readonly class UserExportController
|
||||
fn (string $e) => $this->translator->trans('admin.users.export.' . $e),
|
||||
[
|
||||
'id',
|
||||
'username',
|
||||
// 'username',
|
||||
'email',
|
||||
'enabled',
|
||||
'civility_id',
|
||||
|
@ -0,0 +1,45 @@
|
||||
<?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\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Twig\Environment;
|
||||
|
||||
class UserJobScopeHistoriesController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Environment $engine,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @Route("/{_locale}/admin/main/user/{id}/job-scope-history", name="admin_user_job_scope_history")
|
||||
*/
|
||||
public function indexAction(User $user): Response
|
||||
{
|
||||
$jobHistories = $user->getUserJobHistoriesOrdered();
|
||||
$scopeHistories = $user->getMainScopeHistoriesOrdered();
|
||||
|
||||
return new Response(
|
||||
$this->engine->render(
|
||||
'@ChillMain/User/history.html.twig',
|
||||
[
|
||||
'user' => $user,
|
||||
'jobHistories' => $jobHistories,
|
||||
'scopeHistories' => $scopeHistories,
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -23,6 +23,8 @@ use Chill\MainBundle\Controller\RegroupmentController;
|
||||
use Chill\MainBundle\Controller\UserController;
|
||||
use Chill\MainBundle\Controller\UserJobApiController;
|
||||
use Chill\MainBundle\Controller\UserJobController;
|
||||
use Chill\MainBundle\Controller\UserJobHistoryController;
|
||||
use Chill\MainBundle\Controller\UserScopeHistoryController;
|
||||
use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
|
||||
use Chill\MainBundle\Doctrine\DQL\Age;
|
||||
use Chill\MainBundle\Doctrine\DQL\Extract;
|
||||
@ -31,6 +33,7 @@ use Chill\MainBundle\Doctrine\DQL\Greatest;
|
||||
use Chill\MainBundle\Doctrine\DQL\JsonAggregate;
|
||||
use Chill\MainBundle\Doctrine\DQL\JsonbArrayLength;
|
||||
use Chill\MainBundle\Doctrine\DQL\JsonbExistsInArray;
|
||||
use Chill\MainBundle\Doctrine\DQL\JsonBuildObject;
|
||||
use Chill\MainBundle\Doctrine\DQL\JsonExtract;
|
||||
use Chill\MainBundle\Doctrine\DQL\Least;
|
||||
use Chill\MainBundle\Doctrine\DQL\OverlapsI;
|
||||
@ -56,6 +59,8 @@ use Chill\MainBundle\Entity\Regroupment;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Form\CenterType;
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Form\CivilityType;
|
||||
use Chill\MainBundle\Form\CountryType;
|
||||
use Chill\MainBundle\Form\LanguageType;
|
||||
@ -251,6 +256,7 @@ class ChillMainExtension extends Extension implements
|
||||
'AGGREGATE' => JsonAggregate::class,
|
||||
'REPLACE' => Replace::class,
|
||||
'JSON_EXTRACT' => JsonExtract::class,
|
||||
'JSON_BUILD_OBJECT' => JsonBuildObject::class,
|
||||
],
|
||||
'numeric_functions' => [
|
||||
'JSONB_EXISTS_IN_ARRAY' => JsonbExistsInArray::class,
|
||||
|
53
src/Bundle/ChillMainBundle/Doctrine/DQL/JsonBuildObject.php
Normal file
53
src/Bundle/ChillMainBundle/Doctrine/DQL/JsonBuildObject.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?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\MainBundle\Doctrine\DQL;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
||||
use Doctrine\ORM\Query\AST\Node;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
|
||||
/**
|
||||
* Return an aggregation of values in a json representation, as a string.
|
||||
*
|
||||
* Internally, this function use the postgresql `jsonb_agg` function. Using
|
||||
* json allow to aggregate data from different types without have to cast them.
|
||||
*/
|
||||
class JsonBuildObject extends FunctionNode
|
||||
{
|
||||
/**
|
||||
* @var array|Node[]
|
||||
*/
|
||||
private array $exprs = [];
|
||||
|
||||
public function getSql(SqlWalker $sqlWalker)
|
||||
{
|
||||
return 'JSONB_BUILD_OBJECT(' . implode(', ', array_map(static fn (Node $expr) => $expr->dispatch($sqlWalker), $this->exprs)) . ')';
|
||||
}
|
||||
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$lexer = $parser->getLexer();
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->exprs[] = $parser->ArithmeticPrimary();
|
||||
|
||||
while (Lexer::T_COMMA === $lexer->lookahead['type']) {
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$this->exprs[] = $parser->ArithmeticPrimary();
|
||||
}
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
@ -11,10 +11,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Entity;
|
||||
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Iterator;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
@ -113,9 +118,11 @@ class User implements UserInterface, \Stringable
|
||||
private ?Location $mainLocation = null;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Scope::class)
|
||||
* @var Collection&Selectable<int, UserScopeHistory>
|
||||
* @ORM\OneToMany(targetEntity=UserScopeHistory::class,
|
||||
* mappedBy="user", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||
*/
|
||||
private ?Scope $mainScope = null;
|
||||
private Collection&Selectable $scopeHistories;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
@ -130,9 +137,11 @@ class User implements UserInterface, \Stringable
|
||||
private ?string $salt = null;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=UserJob::class)
|
||||
* @var Collection&Selectable<int, UserJobHistory>
|
||||
* @ORM\OneToMany(targetEntity=UserJobHistory::class,
|
||||
* mappedBy="user", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||
*/
|
||||
private ?UserJob $userJob = null;
|
||||
private Collection&Selectable $jobHistories;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=80)
|
||||
@ -154,6 +163,8 @@ class User implements UserInterface, \Stringable
|
||||
public function __construct()
|
||||
{
|
||||
$this->groupCenters = new ArrayCollection();
|
||||
$this->scopeHistories = new ArrayCollection();
|
||||
$this->jobHistories = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -252,9 +263,38 @@ class User implements UserInterface, \Stringable
|
||||
return $this->mainLocation;
|
||||
}
|
||||
|
||||
public function getMainScope(): ?Scope
|
||||
public function getMainScope(?DateTimeImmutable $at = null): ?Scope
|
||||
{
|
||||
return $this->mainScope;
|
||||
$at ??= new DateTimeImmutable('now');
|
||||
|
||||
foreach ($this->scopeHistories as $scopeHistory) {
|
||||
if ($at >= $scopeHistory->getStartDate() && (
|
||||
null === $scopeHistory->getEndDate() || $at < $scopeHistory->getEndDate()
|
||||
)) {
|
||||
return $scopeHistory->getScope();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getMainScopeHistories(): Collection
|
||||
{
|
||||
return $this->scopeHistories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayCollection|UserScopeHistory[]
|
||||
*/
|
||||
public function getMainScopeHistoriesOrdered(): ArrayCollection
|
||||
{
|
||||
$scopeHistories = $this->getMainScopeHistories();
|
||||
|
||||
$sortedScopeHistories = $scopeHistories->toArray();
|
||||
|
||||
usort($sortedScopeHistories, fn ($a, $b) => $a->getStartDate() < $b->getStartDate() ? 1 : -1);
|
||||
|
||||
return new ArrayCollection($sortedScopeHistories);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -275,9 +315,38 @@ class User implements UserInterface, \Stringable
|
||||
return $this->salt;
|
||||
}
|
||||
|
||||
public function getUserJob(): ?UserJob
|
||||
public function getUserJob(?DateTimeImmutable $at = null): ?UserJob
|
||||
{
|
||||
return $this->userJob;
|
||||
$at ??= new DateTimeImmutable('now');
|
||||
|
||||
foreach ($this->jobHistories as $jobHistory) {
|
||||
if ($at >= $jobHistory->getStartDate() && (
|
||||
null === $jobHistory->getEndDate() || $at < $jobHistory->getEndDate()
|
||||
)) {
|
||||
return $jobHistory->getJob();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getUserJobHistories(): Collection
|
||||
{
|
||||
return $this->jobHistories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayCollection|UserJobHistory[]
|
||||
*/
|
||||
public function getUserJobHistoriesOrdered(): ArrayCollection
|
||||
{
|
||||
$jobHistories = $this->getUserJobHistories();
|
||||
|
||||
$sortedJobHistories = $jobHistories->toArray();
|
||||
|
||||
usort($sortedJobHistories, fn ($a, $b) => $a->getStartDate() < $b->getStartDate() ? 1 : -1);
|
||||
|
||||
return new ArrayCollection($sortedJobHistories);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -455,7 +524,36 @@ class User implements UserInterface, \Stringable
|
||||
|
||||
public function setMainScope(?Scope $mainScope): User
|
||||
{
|
||||
$this->mainScope = $mainScope;
|
||||
if ($mainScope === $this->getMainScope()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$newScope = new UserScopeHistory();
|
||||
|
||||
$newScope
|
||||
->setStartDate(new DateTimeImmutable('now'))
|
||||
->setScope($mainScope)
|
||||
->setUser($this);
|
||||
|
||||
$this->scopeHistories[] = $newScope;
|
||||
|
||||
$criteria = new Criteria();
|
||||
$criteria->orderBy(['startDate' => Criteria::ASC, 'id' => Criteria::ASC]);
|
||||
|
||||
/** @var Iterator $scopes */
|
||||
$scopes = $this->scopeHistories->matching($criteria)->getIterator();
|
||||
$scopes->rewind();
|
||||
|
||||
do {
|
||||
/** @var UserScopeHistory $current */
|
||||
$current = $scopes->current();
|
||||
$scopes->next();
|
||||
|
||||
if ($scopes->valid()) {
|
||||
$next = $scopes->current();
|
||||
$current->setEndDate($next->getStartDate());
|
||||
}
|
||||
} while ($scopes->valid());
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -486,7 +584,36 @@ class User implements UserInterface, \Stringable
|
||||
|
||||
public function setUserJob(?UserJob $userJob): User
|
||||
{
|
||||
$this->userJob = $userJob;
|
||||
if ($userJob === $this->getUserJob()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$newJob = new UserJobHistory();
|
||||
|
||||
$newJob
|
||||
->setStartDate(new DateTimeImmutable('now'))
|
||||
->setJob($userJob)
|
||||
->setUser($this);
|
||||
|
||||
$this->jobHistories[] = $newJob;
|
||||
|
||||
$criteria = new Criteria();
|
||||
$criteria->orderBy(['startDate' => Criteria::ASC, 'id' => Criteria::ASC]);
|
||||
|
||||
/** @var Iterator $jobs */
|
||||
$jobs = $this->jobHistories->matching($criteria)->getIterator();
|
||||
$jobs->rewind();
|
||||
|
||||
do {
|
||||
/** @var UserJobHistory $current */
|
||||
$current = $jobs->current();
|
||||
$jobs->next();
|
||||
|
||||
if ($jobs->valid()) {
|
||||
$next = $jobs->current();
|
||||
$current->setEndDate($next->getStartDate());
|
||||
}
|
||||
} while ($jobs->valid());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
107
src/Bundle/ChillMainBundle/Entity/User/UserJobHistory.php
Normal file
107
src/Bundle/ChillMainBundle/Entity/User/UserJobHistory.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?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\MainBundle\Entity\User;
|
||||
|
||||
use App\Repository\UserJobHistoryRepository;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="chill_main_user_job_history")
|
||||
* @ORM\Entity(repositoryClass=UserJobHistoryRepository::class)
|
||||
*/
|
||||
class UserJobHistory
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="datetime_immutable", nullable=true)
|
||||
*/
|
||||
private ?DateTimeImmutable $endDate = null;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=UserJob::class)
|
||||
*/
|
||||
private ?UserJob $job = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime_immutable")
|
||||
*/
|
||||
private DateTimeImmutable $startDate;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=User::class)
|
||||
*/
|
||||
private User $user;
|
||||
|
||||
|
||||
public function getEndDate(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getJob(): ?UserJob
|
||||
{
|
||||
return $this->job;
|
||||
}
|
||||
|
||||
public function getStartDate(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
public function getUser(): User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setEndDate(?DateTimeImmutable $endDate): self
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setJob(?UserJob $job): UserJobHistory
|
||||
{
|
||||
$this->job = $job;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setStartDate(DateTimeImmutable $startDate): self
|
||||
{
|
||||
$this->startDate = $startDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setUser(User $user): UserJobHistory
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
107
src/Bundle/ChillMainBundle/Entity/User/UserScopeHistory.php
Normal file
107
src/Bundle/ChillMainBundle/Entity/User/UserScopeHistory.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?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\MainBundle\Entity\User;
|
||||
|
||||
use App\Repository\UserScopeHistoryRepository;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="chill_main_user_scope_history")
|
||||
* @ORM\Entity(repositoryClass=UserScopeHistoryRepository::class)
|
||||
*/
|
||||
class UserScopeHistory
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="datetime_immutable", nullable=true)
|
||||
*/
|
||||
private ?DateTimeImmutable $endDate = null;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Scope::class)
|
||||
*/
|
||||
private ?Scope $scope = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime_immutable")
|
||||
*/
|
||||
private DateTimeImmutable $startDate;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=User::class)
|
||||
*/
|
||||
private User $user;
|
||||
|
||||
|
||||
public function getEndDate(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getScope(): ?Scope
|
||||
{
|
||||
return $this->scope;
|
||||
}
|
||||
|
||||
public function getStartDate(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
public function getUser(): User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setEndDate(?DateTimeImmutable $endDate): self
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setScope(?Scope $scope): UserScopeHistory
|
||||
{
|
||||
$this->scope = $scope;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setStartDate(DateTimeImmutable $startDate): self
|
||||
{
|
||||
$this->startDate = $startDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setUser(User $user): UserScopeHistory
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
@ -281,9 +281,9 @@ class ExportAddressHelper
|
||||
};
|
||||
|
||||
case 'country':
|
||||
return function ($value) use ($key) {
|
||||
return function ($value) use ($sanitizedKey, $translationPrefix) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.list.acp' . $key;
|
||||
return $translationPrefix . $sanitizedKey;
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
|
@ -20,21 +20,64 @@ class UserHelper
|
||||
{
|
||||
public function __construct(private readonly UserRender $userRender, private readonly UserRepositoryInterface $userRepository) {}
|
||||
|
||||
/**
|
||||
* Return a callable that will transform a value into a string representing a user
|
||||
*
|
||||
* The callable may receive as argument:
|
||||
*
|
||||
* - an int or a string, the id of the user;
|
||||
* - a string containing a json which will be decoded, and will have this structure: array{uid: int, d: string}. The job and scopes will be shown at this date
|
||||
*
|
||||
* @param string $key the key of the content
|
||||
* @param array $values the list of values
|
||||
* @param string $header the header's content
|
||||
*/
|
||||
public function getLabel($key, array $values, string $header): callable
|
||||
{
|
||||
return function ($value) use ($header) {
|
||||
return function (null|int|string $value) use ($header) {
|
||||
if ('_header' === $value) {
|
||||
return $header;
|
||||
}
|
||||
|
||||
if (null === $value || null === $user = $this->userRepository->find($value)) {
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->userRender->renderString($user, []);
|
||||
if (is_numeric($value)) {
|
||||
$uid = $value;
|
||||
$date = null;
|
||||
} else {
|
||||
$decode = json_decode($value, true, 512, JSON_THROW_ON_ERROR);
|
||||
$uid = $decode['uid'];
|
||||
|
||||
if (null === $uid) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$date = new \DateTimeImmutable($decode['d']);
|
||||
}
|
||||
|
||||
if (null === $user = $this->userRepository->find($uid)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->userRender->renderString($user, ['at' => $date]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a callable that will transform a value into a string representing a user
|
||||
*
|
||||
* The callable may receive as argument:
|
||||
*
|
||||
* - an int or a string, the id of the user;
|
||||
* - a string containing a json which will be decoded, and will have this structure: array{uid: int, d: string}. The job and scopes will be shown at this date * @param $key
|
||||
*
|
||||
* @param string $key the key of the element
|
||||
* @param array $values a list of values
|
||||
* @param string $header the header's content
|
||||
* @return callable
|
||||
*/
|
||||
public function getLabelMulti($key, array $values, string $header): callable
|
||||
{
|
||||
return function ($value) use ($header) {
|
||||
@ -46,31 +89,36 @@ class UserHelper
|
||||
return '';
|
||||
}
|
||||
|
||||
$decoded = json_decode((string) $value, null, 512, JSON_THROW_ON_ERROR);
|
||||
$decoded = json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
if (0 === count($decoded)) {
|
||||
return '';
|
||||
}
|
||||
$asStrings = [];
|
||||
|
||||
return
|
||||
implode(
|
||||
'|',
|
||||
array_map(
|
||||
function (int $userId) {
|
||||
$user = $this->userRepository->find($userId);
|
||||
foreach ($decoded as $userId) {
|
||||
if (is_array($userId)) {
|
||||
$uid = $userId['uid'];
|
||||
$date = new \DateTimeImmutable($userId['d']);
|
||||
} else {
|
||||
$uid = $userId;
|
||||
$date = null;
|
||||
}
|
||||
|
||||
if (null === $user) {
|
||||
return '';
|
||||
}
|
||||
if (null === $uid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $this->userRender->renderString($user, []);
|
||||
},
|
||||
array_unique(
|
||||
array_filter($decoded, static fn (?int $userId) => null !== $userId),
|
||||
SORT_NUMERIC
|
||||
)
|
||||
)
|
||||
);
|
||||
$user = $this->userRepository->find($uid);
|
||||
|
||||
if (null === $user) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$asStrings[$uid] = $this->userRender->renderString($user, ['absence' => false, 'at' => $date]);
|
||||
}
|
||||
|
||||
return implode('|', $asStrings);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
<?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\MainBundle\Repository\User;
|
||||
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\ORM\OptimisticLockException;
|
||||
use Doctrine\ORM\ORMException;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<UserJobHistory>
|
||||
*/
|
||||
class UserJobHistoryRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, UserJobHistory::class);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<?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\MainBundle\Repository\User;
|
||||
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\ORM\OptimisticLockException;
|
||||
use Doctrine\ORM\ORMException;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<UserScopeHistory>
|
||||
*/
|
||||
final class UserScopeHistoryRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, UserScopeHistory::class);
|
||||
}
|
||||
}
|
@ -13,7 +13,8 @@ namespace Chill\MainBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\GroupCenter;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\NoResultException;
|
||||
@ -27,7 +28,11 @@ final readonly class UserRepository implements UserRepositoryInterface
|
||||
{
|
||||
private EntityRepository $repository;
|
||||
|
||||
public function __construct(private EntityManagerInterface $entityManager)
|
||||
private const FIELDS = ['id', 'email', 'enabled', 'civility_id', 'civility_abbreviation', 'civility_name', 'label', 'mainCenter_id',
|
||||
'mainCenter_name', 'mainScope_id', 'mainScope_name', 'userJob_id', 'userJob_name', 'currentLocation_id', 'currentLocation_name',
|
||||
'mainLocation_id', 'mainLocation_name'];
|
||||
|
||||
public function __construct(private EntityManagerInterface $entityManager, private Connection $connection)
|
||||
{
|
||||
$this->repository = $entityManager->getRepository(User::class);
|
||||
}
|
||||
@ -75,48 +80,55 @@ final readonly class UserRepository implements UserRepositoryInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $lang
|
||||
* @throws Exception
|
||||
*/
|
||||
public function findAllAsArray(string $lang): iterable
|
||||
{
|
||||
$dql = sprintf(<<<'DQL'
|
||||
$sql = sprintf(<<<'SQL'
|
||||
SELECT
|
||||
u.id AS id,
|
||||
u.id,
|
||||
u.username AS username,
|
||||
u.email,
|
||||
u.email AS email,
|
||||
u.enabled,
|
||||
IDENTITY(u.civility) AS civility_id,
|
||||
JSON_EXTRACT(civility.abbreviation, :lang) AS civility_abbreviation,
|
||||
JSON_EXTRACT(civility.name, :lang) AS civility_name,
|
||||
u.civility_id,
|
||||
civility.abbreviation->>:lang AS civility_abbreviation,
|
||||
civility.name->>:lang AS civility_name,
|
||||
u.label,
|
||||
mainCenter.id AS mainCenter_id,
|
||||
mainCenter.name AS mainCenter_name,
|
||||
IDENTITY(u.mainScope) AS mainScope_id,
|
||||
JSON_EXTRACT(mainScope.name, :lang) AS mainScope_name,
|
||||
IDENTITY(u.userJob) AS userJob_id,
|
||||
JSON_EXTRACT(userJob.label, :lang) AS userJob_name,
|
||||
mainScope.id AS mainScope_id,
|
||||
mainScope.name->>:lang AS mainScope_name,
|
||||
userJob.id AS userJob_id,
|
||||
userJob.label->>:lang AS userJob_name,
|
||||
currentLocation.id AS currentLocation_id,
|
||||
currentLocation.name AS currentLocation_name,
|
||||
mainLocation.id AS mainLocation_id,
|
||||
mainLocation.name AS mainLocation_name,
|
||||
u.absenceStart
|
||||
FROM Chill\MainBundle\Entity\User u
|
||||
LEFT JOIN u.civility civility
|
||||
LEFT JOIN u.currentLocation currentLocation
|
||||
LEFT JOIN u.mainLocation mainLocation
|
||||
LEFT JOIN u.mainCenter mainCenter
|
||||
LEFT JOIN u.mainScope mainScope
|
||||
LEFT JOIN u.userJob userJob
|
||||
ORDER BY u.label
|
||||
DQL);
|
||||
FROM users u
|
||||
LEFT JOIN chill_main_civility civility ON u.civility_id = civility.id
|
||||
LEFT JOIN centers mainCenter ON u.maincenter_id = mainCenter.id
|
||||
LEFT JOIN chill_main_user_job_history userJobHistory ON u.id = userJobHistory.user_id
|
||||
LEFT JOIN chill_main_user_job userJob ON userJobHistory.job_id = userJob.id AND tstzrange(userJobHistory.startdate, userJobHistory.enddate) @> NOW()
|
||||
LEFT JOIN chill_main_user_scope_history userScopeHistory ON u.id = userScopeHistory.user_id AND tstzrange(userScopeHistory.startdate, userScopeHistory.enddate) @> NOW()
|
||||
LEFT JOIN scopes mainScope ON userScopeHistory.scope_id = mainScope.id
|
||||
LEFT JOIN chill_main_location currentLocation ON u.currentlocation_id = currentLocation.id
|
||||
LEFT JOIN chill_main_location mainLocation ON u.mainlocation_id = mainLocation.id
|
||||
ORDER BY u.label, u.id
|
||||
SQL);
|
||||
|
||||
$query = $this->entityManager->createQuery($dql)
|
||||
->setHydrationMode(AbstractQuery::HYDRATE_ARRAY)
|
||||
->setParameter('lang', $lang)
|
||||
;
|
||||
$query = $this->connection->prepare($sql);
|
||||
|
||||
foreach ($query->toIterable() as $u) {
|
||||
yield $u;
|
||||
foreach ($query->executeQuery(['lang' => $lang])->iterateAssociative() as $u) {
|
||||
$converted = [];
|
||||
foreach (self::FIELDS as $f) {
|
||||
$converted[$f] = $u[strtolower($f)];
|
||||
}
|
||||
|
||||
$converted['absenceStart'] = null !== $u['absencestart'] ? new \DateTimeImmutable($u['absencestart']) : null;
|
||||
|
||||
/** @phpstan-ignore-next-line phpstan does not take into account that all required keys will be present */
|
||||
yield $converted;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
<span class="chill-entity entity-user">
|
||||
{{- user.label }}
|
||||
{%- if opts['user_job'] and user.userJob is not null %}
|
||||
<span class="user-job">({{ user.userJob.label|localize_translatable_string }})</span>
|
||||
{%- if opts['user_job'] and user.userJob(opts['at']) is not null %}
|
||||
<span class="user-job">({{ user.userJob(opts['at']).label|localize_translatable_string }})</span>
|
||||
{%- endif -%}
|
||||
{%- if opts['main_scope'] and user.mainScope is not null %}
|
||||
<span class="main-scope">({{ user.mainScope.name|localize_translatable_string }})</span>
|
||||
{%- if opts['main_scope'] and user.mainScope(opts['at']) is not null %}
|
||||
<span class="main-scope">({{ user.mainScope(opts['at']).name|localize_translatable_string }})</span>
|
||||
{%- endif -%}
|
||||
{%- if opts['absence'] and user.isAbsent %}
|
||||
<span class="badge bg-danger rounded-pill" title="{{ 'absence.Absent'|trans|escape('html_attr') }}">{{ 'absence.A'|trans }}</span>
|
||||
|
@ -4,20 +4,20 @@
|
||||
|
||||
{% block admin_content -%}
|
||||
{% embed '@ChillMain/CRUD/_index.html.twig' %}
|
||||
|
||||
|
||||
{% block index_header %}
|
||||
<h1>{{ 'List circles'|trans }}</h1>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block filter_order %}{% endblock %}
|
||||
|
||||
|
||||
{% block table_entities_thead_tr %}
|
||||
<th>id</th>
|
||||
<th>{{ 'Name'|trans }}</th>
|
||||
<th>{{ 'Active'|trans }}</th>
|
||||
<th>{{ 'Actions'|trans }}</th>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block table_entities_tbody %}
|
||||
{% for entity in entities %}
|
||||
<tr>
|
||||
@ -40,9 +40,9 @@
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block pagination %}{% endblock %}
|
||||
|
||||
|
||||
{% block list_actions %}
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class='cancel'>
|
||||
|
@ -1,7 +1,8 @@
|
||||
{% extends '@ChillMain/Admin/layoutWithVerticalMenu.html.twig' %}
|
||||
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
|
||||
|
||||
{% block admin_content -%}
|
||||
{% embed '@ChillMain/CRUD/_edit_content.html.twig' %}
|
||||
|
||||
{% block crud_content_after_form %}
|
||||
{% if access_permissions_group_list %}
|
||||
<h2 class="mt-5">{{ 'Permissions granted'|trans }}</h2>
|
||||
@ -54,5 +55,14 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content_form_actions_save_and_show %}{% endblock %}
|
||||
|
||||
{% block content_form_actions_view %}
|
||||
<li>
|
||||
<a href="{{ path('admin_user_job_scope_history', {id: entity.id}) }}" class="btn btn-show">
|
||||
{{ 'admin.users.job_scope_histories.Show history'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
|
||||
{% endembed %}
|
||||
{% endblock %}
|
||||
|
@ -0,0 +1,81 @@
|
||||
{% extends '@ChillMain/Admin/layoutWithVerticalMenu.html.twig' %}
|
||||
|
||||
{% block title %}{{ 'admin.users.job_scope_histories.index.histories'|trans }}{% endblock title %}
|
||||
|
||||
{% block admin_content %}
|
||||
<h2>{{ user.usernameCanonical }}</h2>
|
||||
<h1>{{ 'admin.users.job_scope_histories.index.histories'|trans }}</h1>
|
||||
|
||||
<h3 class="mt-5">{{ 'admin.users.job_scope_histories.index.job_history.title'|trans }}</h3>
|
||||
<table class="records_list table table-bordered border-dark">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-25">{{ 'admin.users.job_scope_histories.index.job_history.start'|trans }}</th>
|
||||
<th class="w-25">{{ 'admin.users.job_scope_histories.index.job_history.end'|trans }}</th>
|
||||
<th>{{ 'admin.users.job_scope_histories.index.job_history.job'|trans }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entity in jobHistories %}
|
||||
<tr>
|
||||
<td>{{ entity.startDate|format_datetime('medium') }}</td>
|
||||
<td>
|
||||
{% if entity.endDate is not null %}
|
||||
{{ entity.endDate|format_datetime('medium') }}
|
||||
{% else %}
|
||||
<i class="opacity-50">{{ "admin.users.job_scope_histories.index.job_history.today"|trans }}</i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if entity.job %}
|
||||
{{ entity.job.label|localize_translatable_string }}
|
||||
{% else %}
|
||||
<i class="opacity-50">{{ 'admin.users.job_scope_histories.index.job_history.undefined'|trans }}</i>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3 class="mt-5">{{ 'admin.users.job_scope_histories.index.scope_history.title'|trans }}</h3>
|
||||
<table class="records_list table table-bordered border-dark">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-25">{{ 'admin.users.job_scope_histories.index.scope_history.start'|trans }}</th>
|
||||
<th class="w-25">{{ 'admin.users.job_scope_histories.index.scope_history.end'|trans }}</th>
|
||||
<th>{{ 'admin.users.job_scope_histories.index.scope_history.scope'|trans }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entity in scopeHistories %}
|
||||
<tr>
|
||||
<td>{{ entity.startDate|format_datetime('medium') }}</td>
|
||||
<td>
|
||||
{% if entity.endDate is not null %}
|
||||
{{ entity.endDate|format_datetime('medium') }}
|
||||
{% else %}
|
||||
<i class="opacity-50">{{ "admin.users.job_scope_histories.index.scope_history.today"|trans }}</i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if entity.scope %}
|
||||
{{ entity.scope.name|localize_translatable_string }}
|
||||
{% else %}
|
||||
<i class="opacity-50">{{ 'admin.users.job_scope_histories.index.scope_history.undefined'|trans }}</i>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class='cancel'>
|
||||
<a href="{{ path('chill_crud_admin_user_edit', {id: user.id}) }}" class="btn btn-cancel">
|
||||
{{'admin.users.job_scope_histories.index.Back to user job'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
@ -56,7 +56,9 @@
|
||||
</li>
|
||||
<li>
|
||||
<span class="dt">métier:</span>
|
||||
{% if entity.userJob %}{{ entity.userJob.label|localize_translatable_string }}{% endif %}
|
||||
{% if entity.userJob %}
|
||||
{{ entity.userJob.label|localize_translatable_string }}
|
||||
{% endif %}
|
||||
</li>
|
||||
<li>
|
||||
<span class="dt">cercle/centre:</span>
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
{% block actions_before %}
|
||||
<li class='cancel'>
|
||||
<a href="{{ path('chill_main_admin_central') }}" class="btn btn-cancel">{{'Back to the admin'|trans}}</a>
|
||||
<a href="{{ path('chill_main_admin_central') }}" class="btn btn-cancel">{{'Back to the admin'|trans }}</a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -34,7 +34,7 @@ final readonly class ScopeResolverDispatcher
|
||||
|
||||
/**
|
||||
* @param array|null $options
|
||||
* @return iterable<Scope>|Scope|null
|
||||
* @return Scope|iterable<Scope>|Scope|null
|
||||
*/
|
||||
public function resolveScope(mixed $entity, ?array $options = []): null|\Chill\MainBundle\Entity\Scope|iterable
|
||||
{
|
||||
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\MainBundle\Service\EntityInfo;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class ViewEntityInfoManager
|
||||
{
|
||||
@ -21,6 +22,7 @@ class ViewEntityInfoManager
|
||||
*/
|
||||
private readonly iterable $vienEntityInfoProviders,
|
||||
private readonly Connection $connection,
|
||||
private readonly LoggerInterface $logger,
|
||||
) {}
|
||||
|
||||
public function synchronizeOnDB(): void
|
||||
@ -28,6 +30,8 @@ class ViewEntityInfoManager
|
||||
$this->connection->transactional(function (Connection $conn): void {
|
||||
foreach ($this->vienEntityInfoProviders as $viewProvider) {
|
||||
foreach ($this->createOrReplaceViewSQL($viewProvider, $viewProvider->getViewName()) as $sql) {
|
||||
$this->logger->debug("Will execute create view sql", ['sql' => $sql]);
|
||||
$this->logger->debug($sql);
|
||||
$conn->executeQuery($sql);
|
||||
}
|
||||
}
|
||||
@ -41,7 +45,7 @@ class ViewEntityInfoManager
|
||||
{
|
||||
return [
|
||||
"DROP VIEW IF EXISTS {$viewName}",
|
||||
sprintf("CREATE VIEW {$viewName} AS %s", $viewProvider->getViewQuery())
|
||||
sprintf("CREATE OR REPLACE VIEW {$viewName} AS %s", $viewProvider->getViewQuery())
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -28,10 +28,14 @@ class UserRender implements ChillEntityRenderInterface
|
||||
'main_scope' => true,
|
||||
'user_job' => true,
|
||||
'absence' => true,
|
||||
'at' => null,
|
||||
];
|
||||
|
||||
public function __construct(private readonly TranslatableStringHelper $translatableStringHelper, private readonly \Twig\Environment $engine, private readonly TranslatorInterface $translator) {}
|
||||
|
||||
/**
|
||||
* @param mixed $entity
|
||||
*/
|
||||
public function renderBox($entity, array $options): string
|
||||
{
|
||||
$opts = array_merge(self::DEFAULT_OPTIONS, $options);
|
||||
@ -42,20 +46,23 @@ class UserRender implements ChillEntityRenderInterface
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $entity
|
||||
*/
|
||||
public function renderString($entity, array $options): string
|
||||
{
|
||||
$opts = array_merge(self::DEFAULT_OPTIONS, $options);
|
||||
|
||||
$str = $entity->getLabel();
|
||||
|
||||
if (null !== $entity->getUserJob() && $opts['user_job']) {
|
||||
if (null !== $entity->getUserJob($opts['at']) && $opts['user_job']) {
|
||||
$str .= ' (' . $this->translatableStringHelper
|
||||
->localize($entity->getUserJob()->getLabel()) . ')';
|
||||
->localize($entity->getUserJob($opts['at'])->getLabel()) . ')';
|
||||
}
|
||||
|
||||
if (null !== $entity->getMainScope() && $opts['main_scope']) {
|
||||
if (null !== $entity->getMainScope($opts['at']) && $opts['main_scope']) {
|
||||
$str .= ' (' . $this->translatableStringHelper
|
||||
->localize($entity->getMainScope()->getName()) . ')';
|
||||
->localize($entity->getMainScope($opts['at'])->getName()) . ')';
|
||||
}
|
||||
|
||||
if ($entity->isAbsent() && $opts['absence']) {
|
||||
|
@ -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\MainBundle\Tests\Doctrine\DQL;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class JsonBuildObjectTest extends KernelTestCase
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
self::bootKernel();
|
||||
$this->entityManager = self::$container->get(EntityManagerInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideQueries
|
||||
*/
|
||||
public function testQuery(string $sql, array $params, array $paramType): void
|
||||
{
|
||||
$query = $this->entityManager->createQuery($sql);
|
||||
|
||||
foreach ($params as $k => $v) {
|
||||
$query->setParameter($k, $v, $paramType[$k]);
|
||||
}
|
||||
$query->setMaxResults(1);
|
||||
|
||||
$result = $query->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||
|
||||
self::assertIsArray($result);
|
||||
}
|
||||
|
||||
public function provideQueries(): iterable
|
||||
{
|
||||
yield ["SELECT JSON_BUILD_OBJECT(1, 2, 3, 4) FROM " . Address::class . " a", [], []];
|
||||
yield ["SELECT JSON_BUILD_OBJECT('st', a.street, 'sn', a.streetNumber) FROM " . Address::class . ' a', [], []];
|
||||
// next query make the test fails. But we do not need it for now.
|
||||
//yield ["SELECT JSON_BUILD_OBJECT(a.street, :param), LOWER(:param) FROM " . Address::class . " a", ['param' => 1], ['param' => Types::INTEGER]];
|
||||
}
|
||||
|
||||
}
|
71
src/Bundle/ChillMainBundle/Tests/Entity/UserTest.php
Normal file
71
src/Bundle/ChillMainBundle/Tests/Entity/UserTest.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?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 Entity;
|
||||
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class UserTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
public function testMainScopeHistory()
|
||||
{
|
||||
$user = new User();
|
||||
$scopeA = new Scope();
|
||||
$scopeB = new Scope();
|
||||
|
||||
$user->setMainScope($scopeA);
|
||||
$user->setMainScope($scopeB);
|
||||
|
||||
// 1. check getMainScope get now scopeB, not scopeA
|
||||
self::assertSame($scopeB, $user->getMainScope());
|
||||
|
||||
// 2. get scopeA history, check endDate is not null
|
||||
self::assertNotNull(
|
||||
$user
|
||||
->getMainScopeHistories()
|
||||
->filter(fn (User\UserScopeHistory $userScopeHistory) => $userScopeHistory->getScope() === $scopeA)
|
||||
->first()->getEndDate()
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public function testUserJobHistory()
|
||||
{
|
||||
$user = new User();
|
||||
$jobA = new UserJob();
|
||||
$jobB = new UserJob();
|
||||
|
||||
$user->setUserJob($jobA);
|
||||
$user->setUserJob($jobB);
|
||||
|
||||
// 1. check getUserJob get now jobB, not jobA
|
||||
self::assertSame($jobB, $user->getUserJob());
|
||||
|
||||
// 2. get jobA history, check endDate is not null
|
||||
self::assertNotNull(
|
||||
$user
|
||||
->getUserJobHistories()
|
||||
->filter(fn (User\UserJobHistory $userJobHistory) => $userJobHistory->getJob() === $jobA)
|
||||
->first()->getEndDate()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -18,7 +18,7 @@ final class Version20230711152947 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add data to ';
|
||||
return 'Add last execution data to cronjon execution table';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
|
136
src/Bundle/ChillMainBundle/migrations/Version20230913114115.php
Normal file
136
src/Bundle/ChillMainBundle/migrations/Version20230913114115.php
Normal file
@ -0,0 +1,136 @@
|
||||
<?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\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20230913114115 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// drop job_history
|
||||
$this->addSql('ALTER TABLE chill_main_user_job_history DROP CONSTRAINT user_job_history_endate_null_or_after_startdate');
|
||||
$this->addSql('ALTER TABLE chill_main_user_job_history DROP CONSTRAINT user_job_history_not_overlaps');
|
||||
$this->addSql('DROP SEQUENCE chill_main_user_job_history_id_seq CASCADE');
|
||||
$this->addSql('ALTER TABLE chill_main_user_job_history DROP CONSTRAINT FK_4E3BF4DDBE04EA9');
|
||||
$this->addSql('ALTER TABLE chill_main_user_job_history DROP CONSTRAINT FK_4E3BF4DDA76ED395');
|
||||
$this->addSql('DROP TABLE chill_main_user_job_history');
|
||||
|
||||
$this->addSql('ALTER TABLE users ADD userjob_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE users ADD CONSTRAINT fk_1483a5e964b65c5b FOREIGN KEY (userjob_id) REFERENCES chill_main_user_job (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX idx_1483a5e964b65c5b ON users (userjob_id)');
|
||||
|
||||
// drop scope_history
|
||||
$this->addSql('ALTER TABLE chill_main_user_scope_history DROP CONSTRAINT user_scope_history_endate_null_or_after_startdate');
|
||||
$this->addSql('ALTER TABLE chill_main_user_scope_history DROP CONSTRAINT user_scope_history_not_overlaps');
|
||||
$this->addSql('DROP SEQUENCE chill_main_user_scope_history_id_seq CASCADE');
|
||||
$this->addSql('ALTER TABLE chill_main_user_scope_history DROP CONSTRAINT FK_48B969D7682B5931');
|
||||
$this->addSql('ALTER TABLE chill_main_user_scope_history DROP CONSTRAINT FK_48B969D7A76ED395');
|
||||
$this->addSql('DROP TABLE chill_main_user_scope_history');
|
||||
|
||||
$this->addSql('ALTER TABLE users ADD mainscope_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE users ADD CONSTRAINT fk_1483a5e9115e73f3 FOREIGN KEY (mainscope_id) REFERENCES scopes (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX idx_1483a5e9115e73f3 ON users (mainscope_id)');
|
||||
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add new entities UserScopeHistory and UserJobHistory';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// create scope_history
|
||||
$this->addSql('CREATE SEQUENCE chill_main_user_scope_history_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE chill_main_user_scope_history ('
|
||||
. 'id INT NOT NULL,'
|
||||
. 'scope_id INT DEFAULT NULL,'
|
||||
. 'user_id INT DEFAULT NULL,'
|
||||
. 'endDate TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL,'
|
||||
. 'startDate TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,'
|
||||
. 'PRIMARY KEY(id))');
|
||||
|
||||
$this->addSql('CREATE INDEX IDX_48B969D7682B5931 ON chill_main_user_scope_history (scope_id)');
|
||||
$this->addSql('CREATE INDEX IDX_48B969D7A76ED395 ON chill_main_user_scope_history (user_id)');
|
||||
|
||||
$this->addSql('COMMENT ON COLUMN chill_main_user_scope_history.endDate IS \'(DC2Type:datetime_immutable)\'');
|
||||
$this->addSql('COMMENT ON COLUMN chill_main_user_scope_history.startDate IS \'(DC2Type:datetime_immutable)\'');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_main_user_scope_history ADD CONSTRAINT FK_48B969D7682B5931 '
|
||||
. 'FOREIGN KEY (scope_id) REFERENCES scopes (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_main_user_scope_history ADD CONSTRAINT FK_48B969D7A76ED395 '
|
||||
. 'FOREIGN KEY (user_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_main_user_scope_history '
|
||||
. 'ADD CONSTRAINT user_scope_history_not_overlaps '
|
||||
. 'EXCLUDE USING GIST (user_id with =, tsrange(startDate, endDate) with &&) '
|
||||
. 'DEFERRABLE INITIALLY DEFERRED');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_main_user_scope_history '
|
||||
. 'ADD CONSTRAINT user_scope_history_endate_null_or_after_startdate '
|
||||
. 'CHECK (startDate <= endDate OR endDate IS NULL)');
|
||||
|
||||
// insert user scope_history datas
|
||||
$this->addSql('INSERT INTO chill_main_user_scope_history (id, startDate, endDate, user_id, scope_id) '
|
||||
. 'SELECT nextval(\'chill_main_user_scope_history_id_seq\'), \'1970-01-01\'::date, NULL, users.id, mainscope_id '
|
||||
. 'FROM users');
|
||||
|
||||
// remove mainscope
|
||||
$this->addSql('ALTER TABLE users DROP CONSTRAINT fk_1483a5e9115e73f3');
|
||||
$this->addSql('ALTER TABLE users DROP mainscope_id');
|
||||
|
||||
|
||||
// create job_history
|
||||
$this->addSql('CREATE SEQUENCE chill_main_user_job_history_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE chill_main_user_job_history ('
|
||||
. 'id INT NOT NULL,'
|
||||
. 'job_id INT DEFAULT NULL,'
|
||||
. 'user_id INT DEFAULT NULL,'
|
||||
. 'endDate TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL,'
|
||||
. 'startDate TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,'
|
||||
. 'PRIMARY KEY(id))');
|
||||
|
||||
$this->addSql('CREATE INDEX IDX_4E3BF4DDBE04EA9 ON chill_main_user_job_history (job_id)');
|
||||
$this->addSql('CREATE INDEX IDX_4E3BF4DDA76ED395 ON chill_main_user_job_history (user_id)');
|
||||
|
||||
$this->addSql('COMMENT ON COLUMN chill_main_user_job_history.endDate IS \'(DC2Type:datetime_immutable)\'');
|
||||
$this->addSql('COMMENT ON COLUMN chill_main_user_job_history.startDate IS \'(DC2Type:datetime_immutable)\'');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_main_user_job_history ADD CONSTRAINT FK_4E3BF4DDBE04EA9 '
|
||||
. 'FOREIGN KEY (job_id) REFERENCES chill_main_user_job (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_main_user_job_history ADD CONSTRAINT FK_4E3BF4DDA76ED395 '
|
||||
. 'FOREIGN KEY (user_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_main_user_job_history '
|
||||
. 'ADD CONSTRAINT user_job_history_not_overlaps '
|
||||
. 'EXCLUDE USING GIST (user_id with =, tsrange(startDate, endDate) with &&) '
|
||||
. 'DEFERRABLE INITIALLY DEFERRED');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_main_user_job_history '
|
||||
. 'ADD CONSTRAINT user_job_history_endate_null_or_after_startdate '
|
||||
. 'CHECK (startDate <= endDate OR endDate IS NULL)');
|
||||
|
||||
// insert user job_history datas
|
||||
$this->addSql('INSERT INTO chill_main_user_job_history (id, startDate, endDate, user_id, job_id) '
|
||||
. 'SELECT nextval(\'chill_main_user_job_history_id_seq\'), \'1970-01-01\'::date, NULL, users.id, userjob_id '
|
||||
. 'FROM users');
|
||||
|
||||
// remove userjob
|
||||
$this->addSql('ALTER TABLE users DROP CONSTRAINT fk_1483a5e964b65c5b');
|
||||
$this->addSql('ALTER TABLE users DROP userjob_id');
|
||||
|
||||
}
|
||||
}
|
@ -651,3 +651,24 @@ admin:
|
||||
center_name: Centre
|
||||
permissionsGroup_id: Identifiant du groupe de permissions
|
||||
permissionsGroup_name: Groupe de permissions
|
||||
job_scope_histories:
|
||||
Show history: Voir l'historique
|
||||
index:
|
||||
histories: Historique des services et des métiers
|
||||
Back to user job: Revenir à l'utilisateur
|
||||
job_history:
|
||||
title: Historique des métiers
|
||||
start: Du
|
||||
end: Jusque
|
||||
today: en cours
|
||||
undefined: non défini
|
||||
user: Utilisateur
|
||||
job: Métier
|
||||
scope_history:
|
||||
title: Historique des services
|
||||
start: Du
|
||||
end: Jusque
|
||||
today: en cours
|
||||
undefined: non défini
|
||||
user: Utilisateur
|
||||
scope: Service
|
||||
|
@ -36,7 +36,17 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class HouseholdCompositionController extends AbstractController
|
||||
{
|
||||
public function __construct(private readonly Security $security, private readonly HouseholdCompositionRepository $householdCompositionRepository, private readonly HouseholdRepository $householdRepository, private readonly PaginatorFactory $paginatorFactory, private readonly FormFactoryInterface $formFactory, private readonly EntityManagerInterface $entityManager, private readonly TranslatorInterface $translator, private readonly \Twig\Environment $engine, private readonly UrlGeneratorInterface $urlGenerator) {}
|
||||
public function __construct(
|
||||
private readonly Security $security,
|
||||
private readonly HouseholdCompositionRepository $householdCompositionRepository,
|
||||
private readonly HouseholdRepository $householdRepository,
|
||||
private readonly PaginatorFactory $paginatorFactory,
|
||||
private readonly FormFactoryInterface $formFactory,
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly TranslatorInterface $translator,
|
||||
private readonly \Twig\Environment $engine,
|
||||
private readonly UrlGeneratorInterface $urlGenerator
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @Route("/{_locale}/person/household/{household_id}/composition/{composition_id}/delete", name="chill_person_household_composition_delete")
|
||||
|
@ -26,6 +26,7 @@ use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\ReadableCollection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use LogicException;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
@ -154,14 +155,10 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
|
||||
private PrivateCommentEmbeddable $privateComment;
|
||||
|
||||
/**
|
||||
* @var Collection<User>
|
||||
* @ORM\ManyToMany(targetEntity=User::class)
|
||||
* @ORM\JoinTable(name="chill_person_accompanying_period_work_referrer")
|
||||
* @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"})
|
||||
* @Serializer\Groups({"accompanying_period_work:edit"})
|
||||
* @Serializer\Groups({"accompanying_period_work:create"})
|
||||
* @var Collection<int, AccompanyingPeriodWorkReferrerHistory>
|
||||
* @ORM\OneToMany(targetEntity=AccompanyingPeriodWorkReferrerHistory::class, cascade={"persist", "remove"}, mappedBy="accompanyingPeriodWork", orphanRemoval=true)
|
||||
*/
|
||||
private Collection $referrers;
|
||||
private Collection $referrersHistory;
|
||||
|
||||
/**
|
||||
* @var Collection<Result>
|
||||
@ -220,7 +217,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
|
||||
$this->thirdParties = new ArrayCollection();
|
||||
$this->persons = new ArrayCollection();
|
||||
$this->accompanyingPeriodWorkEvaluations = new ArrayCollection();
|
||||
$this->referrers = new ArrayCollection();
|
||||
$this->referrersHistory = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function addAccompanyingPeriodWorkEvaluation(AccompanyingPeriodWorkEvaluation $evaluation): self
|
||||
@ -254,8 +251,9 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
|
||||
|
||||
public function addReferrer(User $referrer): self
|
||||
{
|
||||
if (!$this->referrers->contains($referrer)) {
|
||||
$this->referrers[] = $referrer;
|
||||
if (!$this->getReferrers()->contains($referrer)) {
|
||||
$this->referrersHistory[] =
|
||||
new AccompanyingPeriodWorkReferrerHistory($this, $referrer, new DateTimeImmutable('today'));
|
||||
}
|
||||
|
||||
return $this;
|
||||
@ -351,15 +349,26 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|User[]
|
||||
* @return ReadableCollection<int, User>
|
||||
* @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"})
|
||||
* @Serializer\Groups({"accompanying_period_work:edit"})
|
||||
* @Serializer\Groups({"accompanying_period_work:create"})
|
||||
*/
|
||||
public function getReferrers(): Collection
|
||||
public function getReferrers(): ReadableCollection
|
||||
{
|
||||
return $this->referrers;
|
||||
return $this->referrersHistory
|
||||
->filter(fn (AccompanyingPeriodWorkReferrerHistory $h) => null === $h->getEndDate())
|
||||
->map(fn (AccompanyingPeriodWorkReferrerHistory $h) => $h->getUser());
|
||||
}
|
||||
|
||||
public function getReferrersHistory(): Collection
|
||||
{
|
||||
return $this->referrersHistory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Collection|Result[]
|
||||
* @return Collection<int, Result>
|
||||
*/
|
||||
public function getResults(): Collection
|
||||
{
|
||||
@ -434,7 +443,16 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
|
||||
|
||||
public function removeReferrer(User $referrer): self
|
||||
{
|
||||
$this->referrers->removeElement($referrer);
|
||||
foreach ($this->referrersHistory as $history) {
|
||||
if ($history->isOpen() && $referrer === $history->getUser()) {
|
||||
$history->setEndDate(new DateTimeImmutable('today'));
|
||||
|
||||
if ($history->isDateRangeEmpty()) {
|
||||
$history->removeAccompanyingPeriodWork();
|
||||
$this->referrersHistory->removeElement($history);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -0,0 +1,120 @@
|
||||
<?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\Entity\AccompanyingPeriod;
|
||||
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="chill_person_accompanying_period_work_referrer")
|
||||
*/
|
||||
class AccompanyingPeriodWorkReferrerHistory implements TrackCreationInterface, TrackUpdateInterface
|
||||
{
|
||||
use TrackCreationTrait;
|
||||
use TrackUpdateTrait;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @var \DateTimeImmutable|null
|
||||
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
|
||||
*/
|
||||
private ?\DateTimeImmutable $endDate = null;
|
||||
|
||||
public function __construct(
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=AccompanyingPeriodWork::class, inversedBy="referrersHistory")
|
||||
*/
|
||||
private ?AccompanyingPeriodWork $accompanyingPeriodWork,
|
||||
/**
|
||||
* @var User
|
||||
* @ORM\ManyToOne(targetEntity=User::class)
|
||||
*/
|
||||
private User $user,
|
||||
/**
|
||||
* @var \DateTimeImmutable
|
||||
* @ORM\Column(type="date_immutable", nullable=false)
|
||||
*/
|
||||
private \DateTimeImmutable $startDate
|
||||
) {}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getEndDate(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function setEndDate(?\DateTimeImmutable $endDate): AccompanyingPeriodWorkReferrerHistory
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAccompanyingPeriodWork(): AccompanyingPeriodWork
|
||||
{
|
||||
return $this->accompanyingPeriodWork;
|
||||
}
|
||||
|
||||
public function getUser(): User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function getStartDate(): \DateTimeImmutable
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* to be used when the history is removed (when startDate = endDate)
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function removeAccompanyingPeriodWork(): self
|
||||
{
|
||||
$this->accompanyingPeriodWork = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool true if the endDate is null
|
||||
*/
|
||||
public function isOpen(): bool
|
||||
{
|
||||
return null === $this->getEndDate();
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if the date range is empty (start date and end date are equal).
|
||||
*
|
||||
* @return bool true if the start date and end date are equal.
|
||||
*/
|
||||
public function isDateRangeEmpty(): bool
|
||||
{
|
||||
return $this->getStartDate()->format('Y-m-d') === $this->getEndDate()?->format('Y-m-d');
|
||||
}
|
||||
}
|
@ -11,17 +11,24 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
|
||||
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\UserJobRepository;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
class CreatorJobAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private readonly UserJobRepository $jobRepository, private readonly TranslatableStringHelper $translatableStringHelper) {}
|
||||
private const PREFIX = 'acp_agg_creator_job';
|
||||
|
||||
public function __construct(
|
||||
private readonly UserJobRepository $jobRepository,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -30,12 +37,41 @@ class CreatorJobAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acp_creator', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('acp.createdBy', 'acp_creator');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb->addSelect('IDENTITY(acp_creator.userJob) AS acp_creator_job_aggregator')
|
||||
->addGroupBy('acp_creator_job_aggregator');
|
||||
$qb
|
||||
->leftJoin(
|
||||
"acp.userHistories",
|
||||
"{$p}_userHistory",
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->eq("{$p}_userHistory.accompanyingPeriod", "acp.id"),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->gte("COALESCE(acp.closingDate, CURRENT_TIMESTAMP())", "{$p}_userHistory.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_userHistory.endDate"),
|
||||
$qb->expr()->lt("COALESCE(acp.closingDate, CURRENT_TIMESTAMP())", "{$p}_userHistory.endDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->leftJoin(
|
||||
UserJobHistory::class,
|
||||
"{$p}_jobHistory",
|
||||
Expr\Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->eq("{$p}_jobHistory.user", "{$p}_userHistory.createdBy"), // et si il est null ?
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_jobHistory.startDate", "{$p}_userHistory.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_jobHistory.endDate"),
|
||||
$qb->expr()->gt("{$p}_jobHistory.endDate", "{$p}_userHistory.startDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_jobHistory.job) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -43,10 +79,8 @@ class CreatorJobAggregator implements AggregatorInterface
|
||||
return Declarations::ACP_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// No form needed
|
||||
}
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
@ -71,11 +105,11 @@ class CreatorJobAggregator implements AggregatorInterface
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['acp_creator_job_aggregator'];
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Group by creator job';
|
||||
return 'export.aggregator.course.by_creator_job.Group by creator job';
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
|
||||
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
@ -22,18 +23,57 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
final readonly class JobWorkingOnCourseAggregator implements AggregatorInterface
|
||||
{
|
||||
private const COLUMN_NAME = 'user_working_on_course_job_id';
|
||||
private const PREFIX = 'acp_agg_user_job_working_on_course';
|
||||
|
||||
public function __construct(
|
||||
private UserJobRepositoryInterface $userJobRepository,
|
||||
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
public function addRole(): ?string
|
||||
{
|
||||
// nothing to add here
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->leftJoin(
|
||||
AccompanyingPeriodInfo::class,
|
||||
"acpinfo",
|
||||
Join::WITH,
|
||||
$qb->expr()->eq("IDENTITY(acpinfo.accompanyingPeriod)", "acp.id")
|
||||
)
|
||||
->leftJoin("acpinfo.user", "{$p}_user")
|
||||
->leftJoin(
|
||||
UserJobHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
|
||||
)
|
||||
// job_at based on _info.infoDate
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "acpinfo.infoDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "acpinfo.infoDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_history.job) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACP_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
@ -58,42 +98,13 @@ final readonly class JobWorkingOnCourseAggregator implements AggregatorInterface
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return [self::COLUMN_NAME];
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.aggregator.course.by_job_working.title';
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acpinfo', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin(
|
||||
AccompanyingPeriodInfo::class,
|
||||
'acpinfo',
|
||||
Join::WITH,
|
||||
'acp.id = IDENTITY(acpinfo.accompanyingPeriod)'
|
||||
);
|
||||
}
|
||||
|
||||
if (!in_array('acpinfo_user', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('acpinfo.user', 'acpinfo_user');
|
||||
}
|
||||
|
||||
$qb->addSelect('IDENTITY(acpinfo_user.userJob) AS ' . self::COLUMN_NAME);
|
||||
$qb->addGroupBy(self::COLUMN_NAME);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ACP_TYPE;
|
||||
}
|
||||
}
|
||||
|
@ -11,22 +11,24 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
|
||||
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use LogicException;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class ReferrerScopeAggregator implements AggregatorInterface
|
||||
readonly class ReferrerScopeAggregator implements AggregatorInterface
|
||||
{
|
||||
private const SCOPE_KEY = 'acp_agg_refscope_user_history_ref_scope_name';
|
||||
private const PREFIX = 'acp_agg_referrer_scope';
|
||||
|
||||
public function __construct(private readonly ScopeRepositoryInterface $scopeRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly RollingDateConverterInterface $rollingDateConverter) {}
|
||||
public function __construct(
|
||||
private ScopeRepositoryInterface $scopeRepository,
|
||||
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -35,52 +37,53 @@ class ReferrerScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$userHistory = 'acp_agg_refscope_user_history';
|
||||
$ref = 'acp_agg_refscope_user_history_ref';
|
||||
$scopeName = self::SCOPE_KEY;
|
||||
$dateCalc = 'acp_agg_refscope_user_history_date_calc';
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->leftJoin('acp.userHistories', $userHistory)
|
||||
->leftJoin($userHistory . '.user', $ref)
|
||||
->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull($userHistory),
|
||||
->leftJoin(
|
||||
"acp.userHistories",
|
||||
"{$p}_userHistory",
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->eq("{$p}_userHistory.accompanyingPeriod", "acp.id"),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte($userHistory . '.startDate', ':' . $dateCalc),
|
||||
$qb->expr()->gte("COALESCE(acp.closingDate, CURRENT_TIMESTAMP())", "{$p}_userHistory.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull($userHistory . '.endDate'),
|
||||
$qb->expr()->gt($userHistory . '.endDate', ':' . $dateCalc)
|
||||
$qb->expr()->isNull("{$p}_userHistory.endDate"),
|
||||
$qb->expr()->lt("COALESCE(acp.closingDate, CURRENT_TIMESTAMP())", "{$p}_userHistory.endDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->setParameter(
|
||||
$dateCalc,
|
||||
$this->rollingDateConverter->convert($data['date_calc'])
|
||||
);
|
||||
|
||||
// add groups
|
||||
$qb
|
||||
->addSelect('IDENTITY(' . $ref . '.mainScope) AS ' . $scopeName)
|
||||
->addGroupBy($scopeName);
|
||||
->leftJoin(
|
||||
UserScopeHistory::class,
|
||||
"{$p}_scopeHistory",
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->eq("{$p}_scopeHistory.user", "{$p}_userHistory.user"),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_scopeHistory.startDate", "{$p}_userHistory.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_scopeHistory.endDate"),
|
||||
$qb->expr()->gt("{$p}_scopeHistory.endDate", "{$p}_userHistory.startDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_scopeHistory.scope) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACP_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('date_calc', PickRollingDateType::class, [
|
||||
'label' => 'export.aggregator.course.by_user_scope.Computation date for referrer',
|
||||
'required' => true,
|
||||
]);
|
||||
}
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return ['date_calc' => new RollingDate(RollingDate::T_TODAY)];
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
@ -104,12 +107,12 @@ class ReferrerScopeAggregator implements AggregatorInterface
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return [self::SCOPE_KEY];
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.aggregator.course.by_user_scope.Group course by referrer\'s scope';
|
||||
}
|
||||
|
@ -80,6 +80,6 @@ final readonly class ScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Group course by scope';
|
||||
return 'export.aggregator.course.by_scope.Group course by scope';
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
|
||||
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodInfo;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
@ -23,18 +23,57 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
final readonly class ScopeWorkingOnCourseAggregator implements AggregatorInterface
|
||||
{
|
||||
private const COLUMN_NAME = 'user_working_on_course_scope_id';
|
||||
private const PREFIX = 'acp_agg_user_scope_working_on_course';
|
||||
|
||||
public function __construct(
|
||||
private ScopeRepositoryInterface $scopeRepository,
|
||||
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
public function addRole(): ?string
|
||||
{
|
||||
// nothing to add here
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->leftJoin(
|
||||
AccompanyingPeriodInfo::class,
|
||||
"acpinfo",
|
||||
Join::WITH,
|
||||
$qb->expr()->eq("IDENTITY(acpinfo.accompanyingPeriod)", "acp.id")
|
||||
)
|
||||
->leftJoin("acpinfo.user", "{$p}_user")
|
||||
->leftJoin(
|
||||
UserScopeHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
|
||||
)
|
||||
// scope_at based on _info.infoDate
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "acpinfo.infoDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "acpinfo.infoDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_history.scope) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACP_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
@ -59,42 +98,13 @@ final readonly class ScopeWorkingOnCourseAggregator implements AggregatorInterfa
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return [self::COLUMN_NAME];
|
||||
return [self::PREFIX. '_select'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.aggregator.course.by_scope_working.title';
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acpinfo', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin(
|
||||
AccompanyingPeriodInfo::class,
|
||||
'acpinfo',
|
||||
Join::WITH,
|
||||
'acp.id = IDENTITY(acpinfo.accompanyingPeriod)'
|
||||
);
|
||||
}
|
||||
|
||||
if (!in_array('acpinfo_user', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('acpinfo.user', 'acpinfo_user');
|
||||
}
|
||||
|
||||
$qb->addSelect('IDENTITY(acpinfo_user.mainScope) AS ' . self::COLUMN_NAME);
|
||||
$qb->addGroupBy(self::COLUMN_NAME);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ACP_TYPE;
|
||||
}
|
||||
}
|
||||
|
@ -11,17 +11,23 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
|
||||
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\UserJobRepository;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
final readonly class UserJobAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private UserJobRepository $jobRepository, private TranslatableStringHelper $translatableStringHelper) {}
|
||||
private const PREFIX = 'acp_agg_user_job';
|
||||
|
||||
public function __construct(
|
||||
private UserJobRepository $jobRepository,
|
||||
private TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -30,12 +36,41 @@ final readonly class UserJobAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acpuser', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('acp.user', 'acpuser');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb->addSelect('IDENTITY(acpuser.userJob) AS job_aggregator');
|
||||
$qb->addGroupBy('job_aggregator');
|
||||
$qb
|
||||
->leftJoin(
|
||||
"acp.userHistories",
|
||||
"{$p}_userHistory",
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->eq("{$p}_userHistory.accompanyingPeriod", "acp.id"),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->gte("COALESCE(acp.closingDate, CURRENT_TIMESTAMP())", "{$p}_userHistory.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_userHistory.endDate"),
|
||||
$qb->expr()->lt("COALESCE(acp.closingDate, CURRENT_TIMESTAMP())", "{$p}_userHistory.endDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->leftJoin(
|
||||
UserJobHistory::class,
|
||||
"{$p}_jobHistory",
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->eq("{$p}_jobHistory.user", "{$p}_userHistory.user"),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_jobHistory.startDate", "{$p}_userHistory.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_jobHistory.endDate"),
|
||||
$qb->expr()->gt("{$p}_jobHistory.endDate", "{$p}_userHistory.startDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_jobHistory.job) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -43,10 +78,8 @@ final readonly class UserJobAggregator implements AggregatorInterface
|
||||
return Declarations::ACP_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// no form
|
||||
}
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
@ -63,7 +96,9 @@ final readonly class UserJobAggregator implements AggregatorInterface
|
||||
return '';
|
||||
}
|
||||
|
||||
$j = $this->jobRepository->find($value);
|
||||
if (null === $j = $this->jobRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
@ -73,11 +108,11 @@ final readonly class UserJobAggregator implements AggregatorInterface
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['job_aggregator'];
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Group by user job';
|
||||
return 'export.aggregator.course.by_user_job.Group by user job';
|
||||
}
|
||||
}
|
||||
|
@ -11,17 +11,30 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators;
|
||||
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Repository\UserJobRepository;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverter;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
final readonly class JobAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private UserJobRepository $jobRepository, private TranslatableStringHelper $translatableStringHelper) {}
|
||||
private const PREFIX = 'acp_work_action_agg_user_job';
|
||||
|
||||
public function __construct(
|
||||
private UserJobRepository $jobRepository,
|
||||
private TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -30,12 +43,20 @@ final readonly class JobAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acpwuser', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('acpw.referrers', 'acpwuser');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb->addSelect('IDENTITY(acpwuser.userJob) as job_aggregator')
|
||||
->addGroupBy('job_aggregator');
|
||||
$qb
|
||||
->leftJoin(
|
||||
UserJob::class,
|
||||
"{$p}_job",
|
||||
Expr\Join::WITH,
|
||||
'EXISTS (SELECT 1 FROM ' . AccompanyingPeriodWorkReferrerHistory::class . " {$p}_ref_history
|
||||
JOIN {$p}_ref_history.user {$p}_ref_history_user JOIN {$p}_ref_history_user.jobHistories {$p}_job_history
|
||||
WHERE {$p}_ref_history.accompanyingPeriodWork = acpw AND IDENTITY({$p}_job_history.job) = {$p}_job.id AND {$p}_job_history.startDate <= {$p}_ref_history.startDate
|
||||
AND ({$p}_job_history.endDate IS NULL or {$p}_job_history.endDate >= {$p}_ref_history.startDate))"
|
||||
)
|
||||
->addSelect("{$p}_job.id as {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -43,10 +64,8 @@ final readonly class JobAggregator implements AggregatorInterface
|
||||
return Declarations::SOCIAL_WORK_ACTION_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// no form
|
||||
}
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
@ -63,7 +82,9 @@ final readonly class JobAggregator implements AggregatorInterface
|
||||
return '';
|
||||
}
|
||||
|
||||
$j = $this->jobRepository->find($value);
|
||||
if (null === $j = $this->jobRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
@ -73,11 +94,11 @@ final readonly class JobAggregator implements AggregatorInterface
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['job_aggregator'];
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Group by treating agent job';
|
||||
return 'export.aggregator.course_work.by_agent_job.Group by treating agent job';
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,10 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators;
|
||||
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Repository\UserRepository;
|
||||
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;
|
||||
@ -21,7 +24,13 @@ use function in_array;
|
||||
|
||||
final readonly class ReferrerAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private UserRepository $userRepository, private UserRender $userRender) {}
|
||||
private const PREFIX = 'acpw_referrer_aggregator';
|
||||
|
||||
public function __construct(
|
||||
private UserRepository $userRepository,
|
||||
private UserRender $userRender,
|
||||
private RollingDateConverterInterface $rollingDateConverter
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -30,11 +39,16 @@ final readonly class ReferrerAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acpwuser', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('acpw.referrers', 'acpwuser');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb->addSelect('acpwuser.id AS referrer_aggregator');
|
||||
$qb
|
||||
->leftJoin('acpw.referrersHistory', $p . "_acpwusers_history")
|
||||
->andWhere("{$p}_acpwusers_history.startDate <= :{$p}_calc_date AND ({$p}_acpwusers_history.endDate IS NULL or {$p}_acpwusers_history.endDate > :{$p}_calc_date)");
|
||||
|
||||
$qb->setParameter("{$p}_calc_date", $this->rollingDateConverter->convert(
|
||||
$data['referrer_at'] ?? new RollingDate(RollingDate::T_TODAY)
|
||||
));
|
||||
$qb->addSelect("IDENTITY({$p}_acpwusers_history.user) AS referrer_aggregator");
|
||||
$qb->addGroupBy('referrer_aggregator');
|
||||
}
|
||||
|
||||
@ -45,11 +59,16 @@ final readonly class ReferrerAggregator implements AggregatorInterface
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// no form
|
||||
$builder->add('referrer_at', PickRollingDateType::class, [
|
||||
'label' => 'export.aggregator.course_work.by_treating_agent.Calc date'
|
||||
]);
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
return [
|
||||
'referrer_at' => new RollingDate(RollingDate::T_TODAY),
|
||||
];
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
@ -65,7 +84,7 @@ final readonly class ReferrerAggregator implements AggregatorInterface
|
||||
|
||||
$r = $this->userRepository->find($value);
|
||||
|
||||
return $this->userRender->renderString($r, []);
|
||||
return $this->userRender->renderString($r, ['absence' => false, 'user_job' => false, 'main_scope' => false]);
|
||||
};
|
||||
}
|
||||
|
||||
@ -76,6 +95,6 @@ final readonly class ReferrerAggregator implements AggregatorInterface
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Group by treating agent';
|
||||
return 'export.aggregator.course_work.by_treating_agent.Group by treating agent';
|
||||
}
|
||||
}
|
||||
|
@ -11,17 +11,30 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators;
|
||||
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Repository\ScopeRepository;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverter;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
final readonly class ScopeAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private ScopeRepository $scopeRepository, private TranslatableStringHelper $translatableStringHelper) {}
|
||||
private const PREFIX = 'acp_work_action_agg_user_scope';
|
||||
|
||||
public function __construct(
|
||||
private ScopeRepository $scopeRepository,
|
||||
private TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -30,12 +43,20 @@ final readonly class ScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acpwuser', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('acpw.referrers', 'acpwuser');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb->addSelect('IDENTITY(acpwuser.mainScope) as scope_aggregator');
|
||||
$qb->addGroupBy('scope_aggregator');
|
||||
$qb
|
||||
->leftJoin(
|
||||
Scope::class,
|
||||
"{$p}_scope",
|
||||
Expr\Join::WITH,
|
||||
'EXISTS (SELECT 1 FROM ' . AccompanyingPeriodWorkReferrerHistory::class . " {$p}_ref_history
|
||||
JOIN {$p}_ref_history.user {$p}_ref_history_user JOIN {$p}_ref_history_user.scopeHistories {$p}_scope_history
|
||||
WHERE {$p}_ref_history.accompanyingPeriodWork = acpw AND IDENTITY({$p}_scope_history.scope) = {$p}_scope.id AND {$p}_scope_history.startDate <= {$p}_ref_history.startDate
|
||||
AND ({$p}_scope_history.endDate IS NULL or {$p}_scope_history.endDate >= {$p}_ref_history.startDate))"
|
||||
)
|
||||
->addSelect("{$p}_scope.id as {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -43,10 +64,8 @@ final readonly class ScopeAggregator implements AggregatorInterface
|
||||
return Declarations::SOCIAL_WORK_ACTION_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// no form
|
||||
}
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
@ -63,7 +82,9 @@ final readonly class ScopeAggregator implements AggregatorInterface
|
||||
return '';
|
||||
}
|
||||
|
||||
$s = $this->scopeRepository->find($value);
|
||||
if (null === $s = $this->scopeRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
@ -73,11 +94,11 @@ final readonly class ScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['scope_aggregator'];
|
||||
return [self::PREFIX . '_select'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Group by treating agent scope';
|
||||
return 'export.aggregator.course_work.by_agent_scope.Group by treating agent scope';
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
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;
|
||||
@ -70,6 +71,7 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac
|
||||
'personsName',
|
||||
'thirdParties',
|
||||
'handlingThierParty',
|
||||
//'acpwReferrers',
|
||||
'referrers',
|
||||
'createdAt',
|
||||
'createdBy',
|
||||
@ -77,7 +79,20 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac
|
||||
'updatedBy',
|
||||
];
|
||||
|
||||
public function __construct(private readonly EntityManagerInterface $entityManager, private readonly DateTimeHelper $dateTimeHelper, private readonly UserHelper $userHelper, private readonly LabelPersonHelper $personHelper, private readonly LabelThirdPartyHelper $thirdPartyHelper, private readonly TranslatableStringExportLabelHelper $translatableStringExportLabelHelper, private readonly SocialIssueRender $socialIssueRender, private readonly SocialIssueRepository $socialIssueRepository, private readonly SocialActionRender $socialActionRender, private readonly RollingDateConverterInterface $rollingDateConverter, private readonly AggregateStringHelper $aggregateStringHelper, private readonly SocialActionRepository $socialActionRepository) {}
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly DateTimeHelper $dateTimeHelper,
|
||||
private readonly UserHelper $userHelper,
|
||||
private readonly LabelPersonHelper $personHelper,
|
||||
private readonly LabelThirdPartyHelper $thirdPartyHelper,
|
||||
private readonly TranslatableStringExportLabelHelper $translatableStringExportLabelHelper,
|
||||
private readonly SocialIssueRender $socialIssueRender,
|
||||
private readonly SocialIssueRepository $socialIssueRepository,
|
||||
private readonly SocialActionRender $socialActionRender,
|
||||
private readonly RollingDateConverterInterface $rollingDateConverter,
|
||||
private readonly AggregateStringHelper $aggregateStringHelper,
|
||||
private readonly SocialActionRepository $socialActionRepository
|
||||
) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
@ -142,6 +157,7 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac
|
||||
},
|
||||
'createdBy', 'updatedBy', 'acp_user' => $this->userHelper->getLabel($key, $values, 'export.list.acpw.' . $key),
|
||||
'referrers' => $this->userHelper->getLabel($key, $values, 'export.list.acpw.' . $key),
|
||||
//'acpwReferrers' => $this->userHelper->getLabelMulti($key, $values, 'export.list.acpw.' . $key),
|
||||
'personsName' => $this->personHelper->getLabelMulti($key, $values, 'export.list.acpw.' . $key),
|
||||
'handlingThierParty' => $this->thirdPartyHelper->getLabel($key, $values, 'export.list.acpw.' . $key),
|
||||
'thirdParties' => $this->thirdPartyHelper->getLabelMulti($key, $values, 'export.list.acpw.' . $key),
|
||||
@ -168,7 +184,7 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR);
|
||||
return dump($query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR));
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
@ -272,9 +288,20 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac
|
||||
|
||||
// referrers => at date XXXX
|
||||
$qb
|
||||
->addSelect('(SELECT IDENTITY(history.user) FROM ' . UserHistory::class . ' history ' .
|
||||
->addSelect('(SELECT JSON_BUILD_OBJECT(\'uid\', IDENTITY(history.user), \'d\', history.startDate) FROM ' . UserHistory::class . ' history ' .
|
||||
'WHERE history.accompanyingPeriod = acp AND history.startDate <= :calcDate AND (history.endDate IS NULL OR history.endDate > :calcDate)) AS referrers');
|
||||
|
||||
/*
|
||||
// acpwReferrers at date XXX
|
||||
$qb
|
||||
->addSelect('(
|
||||
SELECT IDENTITY(acpw_ref_history.accompanyingPeriodWork) AS acpw_ref_history_id,
|
||||
JSON_BUILD_OBJECT(\'uid\', IDENTITY(acpw_ref_history.user), \'d\', acpw_ref_history.startDate)
|
||||
FROM ' . AccompanyingPeriodWorkReferrerHistory::class . ' acpw_ref_history ' .
|
||||
'WHERE acpw_ref_history.accompanyingPeriodWork = acpw AND acpw_ref_history.startDate <= :calcDate AND (acpw_ref_history.endDate IS NULL or acpw_ref_history.endDate > :calcDate) GROUP BY acpw_ref_history_id) AS acpwReferrers'
|
||||
);
|
||||
*/
|
||||
|
||||
// thirdparties
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(tp.id) FROM ' . ThirdParty::class . ' tp '
|
||||
|
@ -133,7 +133,7 @@ class ListEvaluation implements ListInterface, GroupedExportInterface
|
||||
);
|
||||
},
|
||||
'createdBy', 'updatedBy', 'acpw_acp_user' => $this->userHelper->getLabel($key, $values, 'export.list.eval.' . $key),
|
||||
'acpw_referrers' => $this->userHelper->getLabel($key, $values, 'export.list.eval.' . $key),
|
||||
'acpw_referrers' => $this->userHelper->getLabelMulti($key, $values, 'export.list.eval.' . $key),
|
||||
'acpw_persons_id' => $this->aggregateStringHelper->getLabelMulti($key, $values, 'export.list.eval.' . $key),
|
||||
'acpw_persons' => $this->personHelper->getLabelMulti($key, $values, 'export.list.eval.' . $key),
|
||||
'eval_title' => $this->translatableStringExportLabelHelper
|
||||
@ -255,8 +255,8 @@ class ListEvaluation implements ListInterface, GroupedExportInterface
|
||||
|
||||
// referrers => at date XXXX
|
||||
$qb
|
||||
->addSelect('(SELECT IDENTITY(history.user) FROM ' . UserHistory::class . ' history ' .
|
||||
'WHERE history.accompanyingPeriod = acp AND history.startDate <= :calc_date AND (history.endDate IS NULL OR history.endDate > :calc_date)) AS acpw_referrers');
|
||||
->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');
|
||||
|
||||
// persons
|
||||
$qb
|
||||
|
@ -11,19 +11,25 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
|
||||
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
class CreatorJobFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(private readonly TranslatableStringHelper $translatableStringHelper, private readonly UserJobRepositoryInterface $userJobRepository) {}
|
||||
private const PREFIX = 'acp_filter_creator_job';
|
||||
|
||||
public function __construct(
|
||||
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||
private readonly UserJobRepositoryInterface $userJobRepository
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -32,13 +38,44 @@ class CreatorJobFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acp_creator', $qb->getAllAliases(), true)) {
|
||||
$qb->join('acp.createdBy', 'acp_creator');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->andWhere($qb->expr()->in('acp_creator.userJob', ':creator_job'))
|
||||
->setParameter('creator_job', $data['creator_job']);
|
||||
->join(
|
||||
"acp.userHistories",
|
||||
"{$p}_userHistory",
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->eq("{$p}_userHistory.accompanyingPeriod", "acp.id"),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->gte("COALESCE(acp.closingDate, CURRENT_TIMESTAMP())", "{$p}_userHistory.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_userHistory.endDate"),
|
||||
$qb->expr()->lt("COALESCE(acp.closingDate, CURRENT_TIMESTAMP())", "{$p}_userHistory.endDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->leftJoin(
|
||||
UserJobHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->eq("{$p}_history.user", "{$p}_userHistory.createdBy"),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", "{$p}_userHistory.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", "{$p}_userHistory.startDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->andWhere($qb->expr()->in("{$p}_history.job", ":{$p}_jobs"))
|
||||
->setParameter(
|
||||
"{$p}_jobs",
|
||||
$data["creator_job"],
|
||||
);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -48,20 +85,17 @@ class CreatorJobFilter implements FilterInterface
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('creator_job', EntityType::class, [
|
||||
'class' => UserJob::class,
|
||||
'choices' => $this->userJobRepository->findAllActive(),
|
||||
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
'label' => 'Job',
|
||||
]);
|
||||
}
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
$builder
|
||||
->add('creator_job', EntityType::class, [
|
||||
'class' => UserJob::class,
|
||||
'choices' => $this->userJobRepository->findAllActive(),
|
||||
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
'label' => 'Job',
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
@ -79,8 +113,15 @@ class CreatorJobFilter implements FilterInterface
|
||||
]];
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [
|
||||
'creator_job' => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Filter by creator job';
|
||||
return 'export.filter.course.creator_job.Filter by creator job';
|
||||
}
|
||||
}
|
||||
|
@ -11,17 +11,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodInfo;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
@ -34,12 +32,54 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||
*/
|
||||
readonly class JobWorkingOnCourseFilter implements FilterInterface
|
||||
{
|
||||
private const PREFIX = 'acp_filter_user_job_working_on_course';
|
||||
|
||||
public function __construct(
|
||||
private UserJobRepositoryInterface $userJobRepository,
|
||||
private RollingDateConverterInterface $rollingDateConverter,
|
||||
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data): void
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
"SELECT 1 FROM " . AccompanyingPeriodInfo::class . " {$p}_info "
|
||||
. "JOIN {$p}_info.user {$p}_user "
|
||||
. "JOIN " . UserJobHistory::class . " {$p}_history WITH {$p}_history.user = {$p}_user "
|
||||
. "WHERE IDENTITY({$p}_info.accompanyingPeriod) = acp.id "
|
||||
. "AND {$p}_history.job IN (:{$p}_jobs) "
|
||||
// job_at based on _info.infoDate
|
||||
. "AND {$p}_history.startDate <= {$p}_info.infoDate "
|
||||
. "AND ({$p}_history.endDate IS NULL OR {$p}_history.endDate > {$p}_info.infoDate) "
|
||||
. "AND {$p}_info.infoDate >= :{$p}_start and {$p}_info.infoDate < :{$p}_end"
|
||||
)
|
||||
)
|
||||
->setParameter("{$p}_jobs", $data['jobs'])
|
||||
->setParameter(
|
||||
"{$p}_start",
|
||||
$this->rollingDateConverter->convert($data['start_date']),
|
||||
)
|
||||
->setParameter(
|
||||
"{$p}_end",
|
||||
$this->rollingDateConverter->convert($data['end_date'])
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACP_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder): void
|
||||
{
|
||||
$jobs = $this->userJobRepository->findAllActive();
|
||||
@ -59,23 +99,14 @@ readonly class JobWorkingOnCourseFilter implements FilterInterface
|
||||
->add('end_date', PickRollingDateType::class, [
|
||||
'label' => 'export.filter.course.by_job_working.Job working before'
|
||||
])
|
||||
//->add('job_at', PickRollingDateType::class, [
|
||||
// 'label' => 'bla',
|
||||
// 'help' => 'bli',
|
||||
// 'required' => false,
|
||||
//])
|
||||
;
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [
|
||||
'jobs' => [],
|
||||
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
|
||||
'end_date' => new RollingDate(RollingDate::T_TODAY),
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.filter.course.by_job_working.title';
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
{
|
||||
return [
|
||||
@ -93,37 +124,18 @@ readonly class JobWorkingOnCourseFilter implements FilterInterface
|
||||
];
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return null;
|
||||
return [
|
||||
'jobs' => [],
|
||||
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
|
||||
'end_date' => new RollingDate(RollingDate::T_TODAY),
|
||||
//'job_at' => null,
|
||||
];
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data): void
|
||||
public function getTitle(): string
|
||||
{
|
||||
$ai_alias = 'jobs_working_on_course_filter_acc_info';
|
||||
$ai_user_alias = 'jobs_working_on_course_filter_user';
|
||||
$ai_jobs = 'jobs_working_on_course_filter_jobs';
|
||||
$start = 'acp_jobs_work_on_start';
|
||||
$end = 'acp_jobs_work_on_end';
|
||||
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
"SELECT 1 FROM " . AccompanyingPeriod\AccompanyingPeriodInfo::class . " {$ai_alias} JOIN {$ai_alias}.user {$ai_user_alias} " .
|
||||
"WHERE IDENTITY({$ai_alias}.accompanyingPeriod) = acp.id
|
||||
AND {$ai_user_alias}.userJob IN (:{$ai_jobs})
|
||||
AND {$ai_alias}.infoDate >= :{$start} and {$ai_alias}.infoDate < :{$end}
|
||||
"
|
||||
)
|
||||
)
|
||||
->setParameter($ai_jobs, $data['jobs'])
|
||||
->setParameter($start, $this->rollingDateConverter->convert($data['start_date']))
|
||||
->setParameter($end, $this->rollingDateConverter->convert($data['end_date']))
|
||||
;
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACP_TYPE;
|
||||
return 'export.filter.course.by_job_working.title';
|
||||
}
|
||||
}
|
||||
|
@ -12,18 +12,14 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
|
||||
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodInfo;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
@ -36,12 +32,54 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||
*/
|
||||
readonly class ScopeWorkingOnCourseFilter implements FilterInterface
|
||||
{
|
||||
private const PREFIX = 'acp_filter_user_scope_working_on_course';
|
||||
|
||||
public function __construct(
|
||||
private ScopeRepositoryInterface $scopeRepository,
|
||||
private RollingDateConverterInterface $rollingDateConverter,
|
||||
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data): void
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
"SELECT 1 FROM " . AccompanyingPeriodInfo::class . " {$p}_info "
|
||||
. "JOIN {$p}_info.user {$p}_user "
|
||||
. "JOIN " . UserScopeHistory::class . " {$p}_history WITH {$p}_history.user = {$p}_user "
|
||||
. "WHERE IDENTITY({$p}_info.accompanyingPeriod) = acp.id "
|
||||
. "AND {$p}_history.scope IN ( :{$p}_scopes ) "
|
||||
// scope_at based on _info.infoDate
|
||||
. "AND {$p}_history.startDate <= {$p}_info.infoDate "
|
||||
. "AND ({$p}_history.endDate IS NULL OR {$p}_history.endDate > {$p}_info.infoDate) "
|
||||
. "AND {$p}_info.infoDate >= :{$p}_start AND {$p}_info.infoDate < :{$p}_end"
|
||||
)
|
||||
)
|
||||
->setParameter("{$p}_scopes", $data["scopes"])
|
||||
->setParameter(
|
||||
"{$p}_start",
|
||||
$this->rollingDateConverter->convert($data["start_date"]),
|
||||
)
|
||||
->setParameter(
|
||||
"{$p}_end",
|
||||
$this->rollingDateConverter->convert($data["end_date"])
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACP_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder): void
|
||||
{
|
||||
$scopes = $this->scopeRepository->findAllActive();
|
||||
@ -64,20 +102,6 @@ readonly class ScopeWorkingOnCourseFilter implements FilterInterface
|
||||
;
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [
|
||||
'scopes' => [],
|
||||
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
|
||||
'end_date' => new RollingDate(RollingDate::T_TODAY),
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.filter.course.by_scope_working.title';
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
{
|
||||
return [
|
||||
@ -95,37 +119,17 @@ readonly class ScopeWorkingOnCourseFilter implements FilterInterface
|
||||
];
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return null;
|
||||
return [
|
||||
'scopes' => [],
|
||||
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
|
||||
'end_date' => new RollingDate(RollingDate::T_TODAY),
|
||||
];
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data): void
|
||||
public function getTitle(): string
|
||||
{
|
||||
$ai_alias = 'scopes_working_on_course_filter_acc_info';
|
||||
$ai_user_alias = 'scopes_working_on_course_filter_user';
|
||||
$ai_scopes = 'scopes_working_on_course_filter_scopes';
|
||||
$start = 'acp_scopes_work_on_start';
|
||||
$end = 'acp_scopes_work_on_end';
|
||||
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
"SELECT 1 FROM " . AccompanyingPeriod\AccompanyingPeriodInfo::class . " {$ai_alias} JOIN {$ai_alias}.user {$ai_user_alias} " .
|
||||
"WHERE IDENTITY({$ai_alias}.accompanyingPeriod) = acp.id
|
||||
AND {$ai_user_alias}.mainScope IN (:{$ai_scopes})
|
||||
AND {$ai_alias}.infoDate >= :{$start} and {$ai_alias}.infoDate < :{$end}
|
||||
"
|
||||
)
|
||||
)
|
||||
->setParameter($ai_scopes, $data['scopes'])
|
||||
->setParameter($start, $this->rollingDateConverter->convert($data['start_date']))
|
||||
->setParameter($end, $this->rollingDateConverter->convert($data['end_date']))
|
||||
;
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACP_TYPE;
|
||||
return 'export.filter.course.by_scope_working.title';
|
||||
}
|
||||
}
|
||||
|
@ -11,30 +11,25 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
|
||||
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
class UserJobFilter implements FilterInterface
|
||||
{
|
||||
private const A = 'acp_ujob_filter_uhistory';
|
||||
private const PREFIX = 'acp_filter_user_job';
|
||||
|
||||
private const AU = 'acp_ujob_filter_uhistory_user';
|
||||
|
||||
private const P = 'acp_ujob_filter_date';
|
||||
|
||||
private const PJ = 'acp_ujob_filter_job';
|
||||
|
||||
public function __construct(private readonly TranslatableStringHelper $translatableStringHelper, private readonly UserJobRepositoryInterface $userJobRepository, private readonly RollingDateConverterInterface $rollingDateConverter) {}
|
||||
public function __construct(
|
||||
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -43,26 +38,45 @@ class UserJobFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->join('acp.userHistories', self::A)
|
||||
->andWhere(
|
||||
->leftJoin(
|
||||
"acp.userHistories",
|
||||
"{$p}_userHistory",
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte(self::A . '.startDate', ':' . self::P),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull(self::A . '.endDate'),
|
||||
$qb->expr()->gt(self::A . '.endDate', ':' . self::P)
|
||||
$qb->expr()->eq("{$p}_userHistory.accompanyingPeriod", "acp.id"),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->gte("COALESCE(acp.closingDate, CURRENT_TIMESTAMP())", "{$p}_userHistory.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_userHistory.endDate"),
|
||||
$qb->expr()->lt("COALESCE(acp.closingDate, CURRENT_TIMESTAMP())", "{$p}_userHistory.endDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->leftJoin(
|
||||
UserJobHistory::class,
|
||||
"{$p}_jobHistory",
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->eq("{$p}_jobHistory.user", "{$p}_userHistory.user"),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_jobHistory.startDate", "{$p}_userHistory.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_jobHistory" . ".endDate"),
|
||||
$qb->expr()->gt("{$p}_jobHistory.endDate", "{$p}_userHistory.startDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->andWhere($qb->expr()->in("{$p}_jobHistory.job", ":{$p}_job"))
|
||||
->setParameter(
|
||||
self::P,
|
||||
$this->rollingDateConverter->convert($data['date_calc'])
|
||||
"{$p}_job",
|
||||
$data["jobs"],
|
||||
)
|
||||
->join(self::A . '.user', self::AU)
|
||||
->andWhere(
|
||||
$qb->expr()->in(self::AU . '.userJob', ':' . self::PJ)
|
||||
)
|
||||
->setParameter(self::PJ, $data['jobs']);
|
||||
;
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
@ -80,21 +94,13 @@ class UserJobFilter implements FilterInterface
|
||||
'expanded' => true,
|
||||
'choice_label' => fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
|
||||
'label' => 'Job',
|
||||
])
|
||||
->add('date_calc', PickRollingDateType::class, [
|
||||
'label' => 'export.filter.course.by_user_scope.Computation date for referrer',
|
||||
'required' => true,
|
||||
]);
|
||||
}
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return ['date_calc' => new RollingDate(RollingDate::T_TODAY)];
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
return [
|
||||
'Filtered by user job: only %job%', [
|
||||
'export.filter.course.by_user_job.Filtered by user job: only %job%', [
|
||||
'%job%' => implode(
|
||||
', ',
|
||||
array_map(
|
||||
@ -106,8 +112,15 @@ class UserJobFilter implements FilterInterface
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return 'Filter by user job';
|
||||
return [
|
||||
'jobs' => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.filter.course.by_user_job.Filter by user job';
|
||||
}
|
||||
}
|
||||
|
@ -12,30 +12,24 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
|
||||
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
class UserScopeFilter implements FilterInterface
|
||||
{
|
||||
private const A = 'acp_uscope_filter_uhistory';
|
||||
private const PREFIX = 'acp_filter_main_scope';
|
||||
|
||||
private const AU = 'acp_uscope_filter_uhistory_user';
|
||||
|
||||
private const P = 'acp_uscope_filter_date';
|
||||
|
||||
private const PS = 'acp_uscope_filter_scopes';
|
||||
|
||||
public function __construct(private readonly ScopeRepositoryInterface $scopeRepository, private readonly Security $security, private readonly TranslatableStringHelper $translatableStringHelper, private readonly RollingDateConverterInterface $rollingDateConverter) {}
|
||||
public function __construct(
|
||||
private readonly ScopeRepositoryInterface $scopeRepository,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -44,29 +38,48 @@ class UserScopeFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->join('acp.userHistories', self::A)
|
||||
->andWhere(
|
||||
->join(
|
||||
"acp.userHistories",
|
||||
"{$p}_userHistory",
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte(self::A . '.startDate', ':' . self::P),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull(self::A . '.endDate'),
|
||||
$qb->expr()->gt(self::A . '.endDate', ':' . self::P)
|
||||
$qb->expr()->eq("{$p}_userHistory.accompanyingPeriod", "acp.id"),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->gte("COALESCE(acp.closingDate, CURRENT_TIMESTAMP())", "{$p}_userHistory.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_userHistory.endDate"),
|
||||
$qb->expr()->lt("COALESCE(acp.closingDate, CURRENT_TIMESTAMP())", "{$p}_userHistory.endDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->join(
|
||||
UserScopeHistory::class,
|
||||
"{$p}_scopeHistory",
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->eq("{$p}_scopeHistory.user", "{$p}_userHistory.user"),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_scopeHistory.startDate", "{$p}_userHistory.startDate"),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_scopeHistory.endDate"),
|
||||
$qb->expr()->gt("{$p}_scopeHistory.endDate", "{$p}_userHistory.startDate")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->andWhere($qb->expr()->in("{$p}_scopeHistory.scope", ":{$p}_scopes"))
|
||||
->setParameter(
|
||||
self::P,
|
||||
$this->rollingDateConverter->convert($data['date_calc'])
|
||||
"{$p}_scopes",
|
||||
$data["scopes"],
|
||||
)
|
||||
->join(self::A . '.user', self::AU)
|
||||
->andWhere(
|
||||
$qb->expr()->in(self::AU . '.mainScope', ':' . self::PS)
|
||||
)
|
||||
->setParameter(self::PS, $data['scopes']);
|
||||
;
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACP_TYPE;
|
||||
}
|
||||
@ -80,21 +93,13 @@ class UserScopeFilter implements FilterInterface
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
])
|
||||
->add('date_calc', PickRollingDateType::class, [
|
||||
'label' => 'export.filter.course.by_user_scope.Computation date for referrer',
|
||||
'required' => true,
|
||||
]);
|
||||
}
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return ['date_calc' => new RollingDate(RollingDate::T_TODAY)];
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
return [
|
||||
'Filtered by user main scope: only %scope%', [
|
||||
'export.filter.course.by_user_scope.Filtered by user main scope: only %scope%', [
|
||||
'%scope%' => implode(
|
||||
', ',
|
||||
array_map(
|
||||
@ -106,16 +111,15 @@ class UserScopeFilter implements FilterInterface
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return 'Filter by user scope';
|
||||
return [
|
||||
'scopes' => [],
|
||||
];
|
||||
}
|
||||
|
||||
private function getUserMainScope(): Scope
|
||||
public function getTitle(): string
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->security->getUser();
|
||||
|
||||
return $user->getMainScope();
|
||||
return 'export.filter.course.by_user_scope.Filter by user scope';
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Export\Filter\SocialWorkFilters;
|
||||
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverter;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
@ -24,7 +30,12 @@ use function in_array;
|
||||
|
||||
class JobFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(protected TranslatorInterface $translator, private readonly TranslatableStringHelper $translatableStringHelper) {}
|
||||
private const PREFIX = 'acp_work_action_filter_user_job';
|
||||
|
||||
public function __construct(
|
||||
protected TranslatorInterface $translator,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -33,42 +44,35 @@ class JobFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acpwuser', $qb->getAllAliases(), true)) {
|
||||
$qb->join('acpw.referrers', 'acpwuser');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$where = $qb->getDQLPart('where');
|
||||
$clause = $qb->expr()->in('acpwuser.userJob', ':job');
|
||||
$qb->andWhere(
|
||||
'EXISTS (SELECT 1 FROM ' . AccompanyingPeriodWorkReferrerHistory::class . " {$p}_ref_history
|
||||
JOIN {$p}_ref_history.user {$p}_ref_history_user JOIN {$p}_ref_history_user.jobHistories {$p}_job_history
|
||||
WHERE {$p}_ref_history.accompanyingPeriodWork = acpw AND {$p}_job_history.job IN (:{$p}_job) AND {$p}_job_history.startDate <= {$p}_ref_history.startDate
|
||||
AND ({$p}_job_history.endDate IS NULL or {$p}_job_history.endDate >= {$p}_ref_history.startDate))"
|
||||
);
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter('job', $data['job']);
|
||||
$qb->setParameter("{$p}_job", $data["job"]);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::SOCIAL_WORK_ACTION_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('job', EntityType::class, [
|
||||
'class' => UserJob::class,
|
||||
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
$builder
|
||||
->add('job', EntityType::class, [
|
||||
'class' => UserJob::class,
|
||||
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
@ -81,13 +85,21 @@ class JobFilter implements FilterInterface
|
||||
);
|
||||
}
|
||||
|
||||
return ['Filtered by treating agent job: only %jobs%', [
|
||||
return ['export.filter.work.by_user_job.Filtered by treating agent job: only %jobs%', [
|
||||
'%jobs%' => implode(', ', $userjobs),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [
|
||||
'job' => [],
|
||||
'job_at' => new RollingDate(RollingDate::T_TODAY)
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Filter by treating agent job';
|
||||
return 'export.filter.work.by_user_job.Filter by treating agent job';
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,10 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Export\Filter\SocialWorkFilters;
|
||||
|
||||
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\Query\Expr\Andx;
|
||||
@ -20,8 +23,12 @@ use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
class ReferrerFilter implements FilterInterface
|
||||
final readonly class ReferrerFilter implements FilterInterface
|
||||
{
|
||||
private const PREFIX = 'acpw_referrer_filter';
|
||||
|
||||
public function __construct(private RollingDateConverterInterface $rollingDateConverter) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
@ -29,21 +36,19 @@ class ReferrerFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acpwuser', $qb->getAllAliases(), true)) {
|
||||
$qb->join('acpw.referrers', 'acpwuser');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$where = $qb->getDQLPart('where');
|
||||
$clause = $qb->expr()->in('acpwuser', ':agents');
|
||||
$qb
|
||||
->leftJoin('acpw.referrersHistory', $p . "_acpwusers_history")
|
||||
->andWhere("{$p}_acpwusers_history.startDate <= :{$p}_calc_date AND ({$p}_acpwusers_history.endDate IS NULL or {$p}_acpwusers_history.endDate > :{$p}_calc_date)")
|
||||
->andWhere("{$p}_acpwusers_history.user IN (:{$p}_agents)");
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter('agents', $data['accepted_agents']);
|
||||
$qb
|
||||
->setParameter("{$p}_agents", $data['accepted_agents'])
|
||||
->setParameter("{$p}_calc_date", $this->rollingDateConverter->convert(
|
||||
$data['agent_at'] ?? new RollingDate(RollingDate::T_TODAY)
|
||||
))
|
||||
;
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -53,13 +58,23 @@ class ReferrerFilter implements FilterInterface
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('accepted_agents', PickUserDynamicType::class, [
|
||||
'multiple' => true,
|
||||
]);
|
||||
$builder
|
||||
->add('accepted_agents', PickUserDynamicType::class, [
|
||||
'multiple' => true,
|
||||
'label' => 'export.filter.work.by_treating_agent.Accepted agents'
|
||||
])
|
||||
->add('agent_at', PickRollingDateType::class, [
|
||||
'label' => 'export.filter.work.by_treating_agent.Calc date',
|
||||
'help' => 'export.filter.work.by_treating_agent.calc_date_help',
|
||||
])
|
||||
;
|
||||
}
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
return [
|
||||
'accepted_agents' => [],
|
||||
'agent_at' => new RollingDate(RollingDate::T_TODAY),
|
||||
];
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
@ -71,13 +86,14 @@ class ReferrerFilter implements FilterInterface
|
||||
}
|
||||
|
||||
return [
|
||||
'Filtered by treating agent: only %agents%', [
|
||||
'%agents' => implode(', ', $users),
|
||||
'exports.filter.work.by_treating_agent.Filtered by treating agent at date', [
|
||||
'agents' => implode(', ', $users),
|
||||
'agent_at' => $this->rollingDateConverter->convert($data['agent_at'] ?? new RollingDate(RollingDate::T_TODAY)),
|
||||
], ];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Filter by treating agent';
|
||||
return 'export.filter.work.by_treating_agent.Filter by treating agent';
|
||||
}
|
||||
}
|
||||
|
@ -12,19 +12,28 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Export\Filter\SocialWorkFilters;
|
||||
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverter;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function in_array;
|
||||
|
||||
class ScopeFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(protected TranslatorInterface $translator, private readonly TranslatableStringHelper $translatableStringHelper) {}
|
||||
private const PREFIX = 'acp_work_action_filter_user_scope';
|
||||
|
||||
public function __construct(
|
||||
protected TranslatorInterface $translator,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@ -33,21 +42,15 @@ class ScopeFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acpwuser', $qb->getAllAliases(), true)) {
|
||||
$qb->join('acpw.referrers', 'acpwuser');
|
||||
}
|
||||
$p = self::PREFIX;
|
||||
|
||||
$where = $qb->getDQLPart('where');
|
||||
$clause = $qb->expr()->in('acpwuser.mainScope', ':scope');
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter('scope', $data['scope']);
|
||||
$qb->andWhere(
|
||||
'EXISTS (SELECT 1 FROM ' . AccompanyingPeriodWorkReferrerHistory::class . " {$p}_ref_history
|
||||
JOIN {$p}_ref_history.user {$p}_ref_history_user JOIN {$p}_ref_history_user.scopeHistories {$p}_scope_history
|
||||
WHERE {$p}_ref_history.accompanyingPeriodWork = acpw AND {$p}_scope_history.scope IN (:{$p}_scope) AND {$p}_scope_history.startDate <= {$p}_ref_history.startDate
|
||||
AND ({$p}_scope_history.endDate IS NULL or {$p}_scope_history.endDate >= {$p}_ref_history.startDate))"
|
||||
)
|
||||
->setParameter("{$p}_scope", $data["scope"]);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
@ -57,21 +60,19 @@ class ScopeFilter implements FilterInterface
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('scope', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
$builder
|
||||
->add('scope', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
{
|
||||
$scopes = [];
|
||||
|
||||
@ -81,13 +82,21 @@ class ScopeFilter implements FilterInterface
|
||||
);
|
||||
}
|
||||
|
||||
return ['Filtered by treating agent scope: only %scopes%', [
|
||||
return ['export.filter.work.by_user_scope.Filtered by treating agent scope: only %scopes%', [
|
||||
'%scopes%' => implode(', ', $scopes),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return 'Filter by treating agent scope';
|
||||
return [
|
||||
'scope' => [],
|
||||
'scope_at' => new RollingDate(RollingDate::T_TODAY)
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.filter.work.by_user_scope.Filter by treating agent scope';
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\Address;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Export\Helper\DateTimeHelper;
|
||||
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\Household\PersonHouseholdAddress;
|
||||
@ -59,8 +60,10 @@ final readonly class ListAccompanyingPeriodHelper
|
||||
'scopes',
|
||||
'socialIssues',
|
||||
'acpCreatedAt',
|
||||
'acpCreatedBy_id',
|
||||
'acpCreatedBy',
|
||||
'acpUpdatedAt',
|
||||
'acpUpdatedBy_id',
|
||||
'acpUpdatedBy',
|
||||
];
|
||||
|
||||
@ -75,6 +78,7 @@ final readonly class ListAccompanyingPeriodHelper
|
||||
private SocialIssueRender $socialIssueRender,
|
||||
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||
private TranslatorInterface $translator,
|
||||
private UserHelper $userHelper,
|
||||
) {}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
@ -104,6 +108,7 @@ 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),
|
||||
'locationPersonName', 'requestorPerson' => function ($value) use ($key) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.list.acp.' . $key;
|
||||
@ -204,11 +209,11 @@ final readonly class ListAccompanyingPeriodHelper
|
||||
|
||||
// add the field which are simple association
|
||||
$qb
|
||||
->leftJoin('acp.createdBy', "acp_created_by_t")
|
||||
->addSelect('acp_created_by_t.label AS acpCreatedBy');
|
||||
->addSelect('IDENTITY(acp.createdBy) AS acpCreatedBy_id')
|
||||
->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(acp.createdBy), \'d\', acp.createdAt) AS acpCreatedBy');
|
||||
$qb
|
||||
->leftJoin('acp.updatedBy', "acp_updated_by_t")
|
||||
->addSelect('acp_updated_by_t.label AS acpUpdatedBy');
|
||||
->addSelect('IDENTITY(acp.updatedBy) AS acpUpdatedBy_id')
|
||||
->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(acp.updatedBy), \'d\', acp.updatedAt) AS acpUpdatedBy');
|
||||
|
||||
foreach (['origin' => 'label', 'closingMotive' => 'name', 'job' => 'label', 'administrativeLocation' => 'name'] as $entity => $field) {
|
||||
$qb
|
||||
@ -230,7 +235,7 @@ final readonly class ListAccompanyingPeriodHelper
|
||||
|
||||
// referree at date
|
||||
$qb
|
||||
->addSelect('referrer_t.label AS referrer')
|
||||
->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(userHistory.user), \'d\', userHistory.startDate) AS referrer')
|
||||
->addSelect('userHistory.startDate AS referrerSince')
|
||||
->leftJoin('acp.userHistories', 'userHistory')
|
||||
->leftJoin('userHistory.user', 'referrer_t')
|
||||
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@ -91,7 +92,9 @@ class AccompanyingPeriodWorkEvaluationRepository implements ObjectRepository
|
||||
$qb->expr()->gte(':now', $qb->expr()->diff('e.maxDate', 'e.warningInterval')),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->eq('period.user', ':user'),
|
||||
$qb->expr()->isMemberOf(':user', 'work.referrers')
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM ' . AccompanyingPeriodWork::class . ' subw JOIN subw.referrersHistory subw_ref_history WHERE subw.id = work.id AND subw_ref_history.user = :user'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -132,10 +132,10 @@ final readonly class AccompanyingPeriodWorkRepository implements ObjectRepositor
|
||||
|
||||
// set limit and offset
|
||||
$sql .= " ORDER BY
|
||||
CASE WHEN enddate IS NULL THEN '-infinity'::timestamp ELSE 'infinity'::timestamp END ASC,
|
||||
startdate DESC,
|
||||
enddate DESC,
|
||||
id DESC";
|
||||
CASE WHEN w.enddate IS NULL THEN '-infinity'::timestamp ELSE 'infinity'::timestamp END ASC,
|
||||
w.startdate DESC,
|
||||
w.enddate DESC,
|
||||
w.id DESC";
|
||||
|
||||
$sql .= " LIMIT :limit OFFSET :offset";
|
||||
|
||||
@ -239,7 +239,9 @@ final readonly class AccompanyingPeriodWorkRepository implements ObjectRepositor
|
||||
$qb->expr()->lte('w.startDate', ':until'),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->eq('period.user', ':user'),
|
||||
$qb->expr()->isMemberOf(':user', 'w.referrers')
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM ' . AccompanyingPeriodWork::class . ' subw JOIN subw.referrersHistory subw_ref_history WHERE subw.id = w.id AND subw_ref_history.user = :user and subw.ref_history.endDate IS NULL'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -54,7 +54,7 @@ class AccompanyingPeriodWorkEndQueryPartForAccompanyingPeriodInfo implements Acc
|
||||
public function getFromStatement(): string
|
||||
{
|
||||
return 'chill_person_accompanying_period_work w
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr on w.id = cpapwr.accompanyingperiodwork_id';
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON w.id = cpapwr.accompanyingperiodwork_id AND daterange(cpapwr.startDate, cpapwr.endDate) @> w.endDate';
|
||||
}
|
||||
|
||||
public function getWhereClause(): string
|
||||
|
@ -11,10 +11,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoQueryPart;
|
||||
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
use Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface;
|
||||
|
||||
class AccompanyingPeriodWorkEvaluationWarningDateQueryPartForAccompanyingPeriodInfo implements AccompanyingPeriodInfoUnionQueryPartInterface
|
||||
class AccompanyingPeriodWorkEvaluationCreationQueryPartForAccompanyingPeriodInfo implements AccompanyingPeriodInfoUnionQueryPartInterface
|
||||
{
|
||||
public function getAccompanyingPeriodIdColumn(): string
|
||||
{
|
||||
@ -33,12 +34,12 @@ class AccompanyingPeriodWorkEvaluationWarningDateQueryPartForAccompanyingPeriodI
|
||||
|
||||
public function getUserIdColumn(): string
|
||||
{
|
||||
return 'cpapwr.user_id';
|
||||
return 'e.createdBy_id';
|
||||
}
|
||||
|
||||
public function getDateTimeColumn(): string
|
||||
{
|
||||
return 'e.maxDate';
|
||||
return 'e.createdAt';
|
||||
}
|
||||
|
||||
public function getMetadataColumn(): string
|
||||
@ -48,18 +49,18 @@ class AccompanyingPeriodWorkEvaluationWarningDateQueryPartForAccompanyingPeriodI
|
||||
|
||||
public function getDiscriminator(): string
|
||||
{
|
||||
return 'accompanying_period_work_evaluation_max';
|
||||
return 'accompanying_period_work_evaluation_creation';
|
||||
}
|
||||
|
||||
public function getFromStatement(): string
|
||||
{
|
||||
return 'chill_person_accompanying_period_work_evaluation e
|
||||
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id';
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id AND daterange(cpapwr.startDate, cpapwr.endDate) @> e.startDate';
|
||||
}
|
||||
|
||||
public function getWhereClause(): string
|
||||
{
|
||||
return 'e.maxDate IS NOT NULL';
|
||||
return 'e.createdAt IS NOT NULL';
|
||||
}
|
||||
}
|
@ -56,7 +56,7 @@ class AccompanyingPeriodWorkEvaluationMaxQueryPartForAccompanyingPeriodInfo impl
|
||||
{
|
||||
return 'chill_person_accompanying_period_work_evaluation e
|
||||
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id';
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id AND daterange(cpapwr.startDate, cpapwr.endDate) @> e.maxDate';
|
||||
}
|
||||
|
||||
public function getWhereClause(): string
|
||||
|
@ -56,11 +56,11 @@ class AccompanyingPeriodWorkEvaluationStartQueryPartForAccompanyingPeriodInfo im
|
||||
{
|
||||
return 'chill_person_accompanying_period_work_evaluation e
|
||||
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id';
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id AND daterange(cpapwr.startDate, cpapwr.endDate) @> e.startDate';
|
||||
}
|
||||
|
||||
public function getWhereClause(): string
|
||||
{
|
||||
return '';
|
||||
return 'e.startDate IS NOT NULL';
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
<?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\Service\EntityInfo\AccompanyingPeriodInfoQueryPart;
|
||||
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface;
|
||||
|
||||
class AccompanyingPeriodWorkNewReferrerQueryPartForAccompanyingPeriodInfo implements AccompanyingPeriodInfoUnionQueryPartInterface
|
||||
{
|
||||
public function getAccompanyingPeriodIdColumn(): string
|
||||
{
|
||||
return 'w.accompanyingperiod_id';
|
||||
}
|
||||
|
||||
public function getRelatedEntityColumn(): string
|
||||
{
|
||||
return AccompanyingPeriodWork::class;
|
||||
}
|
||||
|
||||
public function getRelatedEntityIdColumn(): string
|
||||
{
|
||||
return 'w.id';
|
||||
}
|
||||
|
||||
public function getUserIdColumn(): string
|
||||
{
|
||||
return 'cpapwr.user_id';
|
||||
}
|
||||
|
||||
public function getDateTimeColumn(): string
|
||||
{
|
||||
return 'cpapwr.startDate';
|
||||
}
|
||||
|
||||
public function getMetadataColumn(): string
|
||||
{
|
||||
return "'{}'::jsonb";
|
||||
}
|
||||
|
||||
public function getDiscriminator(): string
|
||||
{
|
||||
return 'accompanying_period_work_referrer_new';
|
||||
}
|
||||
|
||||
public function getFromStatement(): string
|
||||
{
|
||||
return 'chill_person_accompanying_period_work w
|
||||
JOIN chill_person_accompanying_period_work_referrer cpapwr on w.id = cpapwr.accompanyingperiodwork_id';
|
||||
}
|
||||
|
||||
public function getWhereClause(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
@ -54,7 +54,7 @@ class AccompanyingPeriodWorkStartQueryPartForAccompanyingPeriodInfo implements A
|
||||
public function getFromStatement(): string
|
||||
{
|
||||
return 'chill_person_accompanying_period_work w
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr on w.id = cpapwr.accompanyingperiodwork_id';
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr on w.id = cpapwr.accompanyingperiodwork_id AND daterange(cpapwr.startDate, cpapwr.endDate) @> w.startDate';
|
||||
}
|
||||
|
||||
public function getWhereClause(): string
|
||||
|
@ -24,6 +24,7 @@ use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\Relationships\Relation;
|
||||
use Chill\PersonBundle\Entity\Relationships\Relationship;
|
||||
use Chill\PersonBundle\Repository\Person\PersonCenterHistoryInterface;
|
||||
use Chill\PersonBundle\Repository\PersonRepository;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@ -44,6 +45,8 @@ class PersonMoveTest extends KernelTestCase
|
||||
|
||||
private CenterRepositoryInterface $centerRepository;
|
||||
|
||||
private PersonCenterHistoryInterface $personCenterHistory;
|
||||
|
||||
/**
|
||||
* @var list<array{0: class-string, 1: int}>
|
||||
*/
|
||||
@ -56,6 +59,7 @@ class PersonMoveTest extends KernelTestCase
|
||||
$this->personMoveManager = self::$container->get(PersonMoveManager::class);
|
||||
$this->eventDispatcher = self::$container->get(EventDispatcherInterface::class);
|
||||
$this->centerRepository = self::$container->get(CenterRepositoryInterface::class);
|
||||
$this->personCenterHistory = self::$container->get(PersonCenterHistoryInterface::class);
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass(): void
|
||||
@ -146,18 +150,22 @@ class PersonMoveTest extends KernelTestCase
|
||||
$personB = $this->em->find(Person::class, $personB->getId());
|
||||
$message = 'Move persons with overlapping center histories';
|
||||
|
||||
$this->em->refresh($personB);
|
||||
|
||||
self::assertCount(0, $personsByIdOfA);
|
||||
self::assertNotNull($personB?->getId(), $message);
|
||||
|
||||
$centerHistoriesB = $personB->getCenterHistory();
|
||||
$oldestDate = new \DateTimeImmutable('2023-01-01');
|
||||
$centersHistories = $this->personCenterHistory->findBy(['person' => $personB]);
|
||||
|
||||
$this->em->refresh($centerHistoriesB->first());
|
||||
// compute the oldest center history
|
||||
$oldestCenterHistory = null;
|
||||
foreach ($centersHistories as $centerHistory) {
|
||||
$this->em->refresh($centerHistory);
|
||||
if (null === $oldestCenterHistory || ($oldestCenterHistory instanceof Person\PersonCenterHistory && $oldestCenterHistory->getStartDate() >= $centerHistory->getStartDate())) {
|
||||
$oldestCenterHistory = $centerHistory;
|
||||
}
|
||||
}
|
||||
|
||||
self::assertCount(2, $centerHistoriesB);
|
||||
self::assertEquals($oldestDate, $centerHistoriesB->first()->getStartDate());
|
||||
self::assertCount(2, $centersHistories);
|
||||
self::assertEquals('2023-01-01', $oldestCenterHistory?->getStartDate()->format('Y-m-d'));
|
||||
|
||||
self::$entitiesToDelete[] = [Person::class, $personA];
|
||||
self::$entitiesToDelete[] = [Person::class, $personB];
|
||||
@ -218,11 +226,11 @@ class PersonMoveTest extends KernelTestCase
|
||||
$this->em->persist($memberA);
|
||||
$this->em->persist($memberB);
|
||||
|
||||
self::$entitiesToDelete[] = [Person::class, $personA];
|
||||
self::$entitiesToDelete[] = [Person::class, $personB];
|
||||
self::$entitiesToDelete[] = [HouseholdMember::class, $memberA];
|
||||
self::$entitiesToDelete[] = [HouseholdMember::class, $memberB];
|
||||
self::$entitiesToDelete[] = [Household::class, $household];
|
||||
self::$entitiesToDelete[] = [Person::class, $personA];
|
||||
self::$entitiesToDelete[] = [Person::class, $personB];
|
||||
|
||||
yield [$personA, $personB, "move 2 people having the same household at the same time"];
|
||||
|
||||
|
@ -52,16 +52,6 @@ final class AccompanyingCourseApiControllerTest extends WebTestCase
|
||||
|
||||
private ?int $personId = null;
|
||||
|
||||
private KernelBrowser $client;
|
||||
|
||||
/**
|
||||
* Setup before each test method (see phpunit doc).
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->client = $this->getClientAuthenticated();
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
self::ensureKernelShutdown();
|
||||
@ -427,8 +417,9 @@ final class AccompanyingCourseApiControllerTest extends WebTestCase
|
||||
*/
|
||||
public function testAccompanyingCourseShow(int $personId, int $periodId)
|
||||
{
|
||||
$c = $this->client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', $periodId));
|
||||
$response = $this->client->getResponse();
|
||||
$client = $this->getClientAuthenticated();
|
||||
$client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', $periodId));
|
||||
$response = $client->getResponse();
|
||||
|
||||
$this->assertTrue(in_array($response->getStatusCode(), [200, 422], true));
|
||||
|
||||
@ -548,12 +539,13 @@ final class AccompanyingCourseApiControllerTest extends WebTestCase
|
||||
*/
|
||||
public function testReferralAvailable(int $personId, int $periodId)
|
||||
{
|
||||
$this->client->request(
|
||||
$client = $this->getClientAuthenticated();
|
||||
$client->request(
|
||||
Request::METHOD_POST,
|
||||
sprintf('/api/1.0/person/accompanying-course/%d/referrers-suggested.json', $periodId)
|
||||
);
|
||||
|
||||
$this->assertTrue(in_array($this->client->getResponse()->getStatusCode(), [200, 422], true));
|
||||
$this->assertTrue(in_array($client->getResponse()->getStatusCode(), [200, 422], true));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -732,8 +724,9 @@ final class AccompanyingCourseApiControllerTest extends WebTestCase
|
||||
|
||||
public function testShow404()
|
||||
{
|
||||
$this->client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', 99999));
|
||||
$response = $this->client->getResponse();
|
||||
$client = $this->getClientAuthenticated();
|
||||
$client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', 99999));
|
||||
$response = $client->getResponse();
|
||||
|
||||
$this->assertEquals(404, $response->getStatusCode(), "Test that the response of rest api has a status code 'not found' (404)");
|
||||
}
|
||||
|
@ -137,20 +137,17 @@ final class HouseholdApiControllerTest extends WebTestCase
|
||||
$qb = self::$container->get(EntityManagerInterface::class)
|
||||
->createQueryBuilder();
|
||||
|
||||
$period = $qb
|
||||
->select('ap')
|
||||
->from(AccompanyingPeriod::class, 'ap')
|
||||
$personIds = $qb
|
||||
->select("p.id AS pid")
|
||||
->from(Person::class, 'p')
|
||||
->where(
|
||||
$qb->expr()->gte('SIZE(ap.participations)', 2)
|
||||
$qb->expr()->gte('SIZE(p.accompanyingPeriodParticipations)', 2)
|
||||
)
|
||||
->getQuery()
|
||||
->setMaxResults(1)
|
||||
->getSingleResult();
|
||||
|
||||
$person = $period->getParticipations()
|
||||
->first()->getPerson();
|
||||
|
||||
yield [$person->getId()];
|
||||
yield [$personIds['pid']];
|
||||
|
||||
self::ensureKernelShutdown();
|
||||
}
|
||||
|
@ -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\Tests\Entity\AccompanyingPeriod;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class AccompanyingPeriodWorkTest extends TestCase
|
||||
{
|
||||
public function testReferrerHistory(): void
|
||||
{
|
||||
$work = new AccompanyingPeriodWork();
|
||||
$userA = new User();
|
||||
$userB = new User();
|
||||
$userC = new User();
|
||||
|
||||
self::assertCount(0, $work->getReferrers());
|
||||
|
||||
$work->addReferrer($userA);
|
||||
|
||||
self::assertCount(1, $work->getReferrers());
|
||||
self::assertContains($userA, $work->getReferrers());
|
||||
|
||||
$work->addReferrer($userB);
|
||||
|
||||
self::assertCount(2, $work->getReferrers());
|
||||
self::assertContains($userA, $work->getReferrers());
|
||||
self::assertContains($userB, $work->getReferrers());
|
||||
|
||||
$work->addReferrer($userC);
|
||||
$work->removeReferrer($userB);
|
||||
|
||||
self::assertCount(2, $work->getReferrers());
|
||||
self::assertContains($userA, $work->getReferrers());
|
||||
self::assertNotContains($userB, $work->getReferrers());
|
||||
self::assertContains($userC, $work->getReferrers());
|
||||
|
||||
$work->removeReferrer($userA);
|
||||
self::assertNotContains($userA, $work->getReferrers());
|
||||
self::assertNotContains($userB, $work->getReferrers());
|
||||
self::assertContains($userC, $work->getReferrers());
|
||||
}
|
||||
|
||||
public function testReferrerHistoryOnDifferentDays(): void
|
||||
{
|
||||
|
||||
$work = new AccompanyingPeriodWork();
|
||||
$userA = new User();
|
||||
$userB = new User();
|
||||
$userC = new User();
|
||||
|
||||
$work->addReferrer($userA);
|
||||
|
||||
$historyA = $work->getReferrersHistory()->first();
|
||||
$reflection = new \ReflectionClass($historyA);
|
||||
$startDateReflection = $reflection->getProperty('startDate');
|
||||
$startDateReflection->setAccessible(true);
|
||||
$startDateReflection->setValue($historyA, new \DateTimeImmutable('1 year ago'));
|
||||
|
||||
$work->addReferrer($userB);
|
||||
$work->addReferrer($userC);
|
||||
|
||||
$work->removeReferrer($userB);
|
||||
$work->removeReferrer($userA);
|
||||
|
||||
self::assertCount(1, $work->getReferrers());
|
||||
self::assertNotContains($userA, $work->getReferrers());
|
||||
self::assertNotContains($userB, $work->getReferrers());
|
||||
self::assertContains($userC, $work->getReferrers());
|
||||
|
||||
self::assertCount(2, $work->getReferrersHistory());
|
||||
|
||||
$historyA = $work->getReferrersHistory()
|
||||
->filter(fn (AccompanyingPeriodWorkReferrerHistory $h) => $userA === $h->getUser())
|
||||
->first();
|
||||
|
||||
self::assertNotFalse($historyA);
|
||||
self::assertSame($userA, $historyA->getUser());
|
||||
self::assertEquals((new \DateTimeImmutable())->format('Y-m-d'), $historyA->getEndDate()->format('Y-m-d'));
|
||||
}
|
||||
}
|
@ -71,7 +71,6 @@ final class ReferrerScopeAggregatorTest extends AbstractAggregatorTest
|
||||
$em->createQueryBuilder()
|
||||
->select('count(acp.id)')
|
||||
->from(AccompanyingPeriod::class, 'acp')
|
||||
->join('acp.scopes', 'acpscope'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators;
|
||||
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\UserJobAggregator;
|
||||
@ -39,7 +40,9 @@ final class UserJobAggregatorTest extends AbstractAggregatorTest
|
||||
public function getFormData(): array
|
||||
{
|
||||
return [
|
||||
[],
|
||||
[
|
||||
'job_at' => new RollingDate(RollingDate::T_FIXED_DATE, \DateTimeImmutable::createFromFormat('Y-m-d', '2020-01-01')),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators;
|
||||
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\JobAggregator;
|
||||
@ -54,7 +55,6 @@ final class JobAggregatorTest extends AbstractAggregatorTest
|
||||
->select('count(acp.id)')
|
||||
->from(AccompanyingPeriod::class, 'acp')
|
||||
->join('acp.works', 'acpw')
|
||||
->join('acpw.referrers', 'acpwuser'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators;
|
||||
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\ReferrerAggregator;
|
||||
@ -39,7 +40,10 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest
|
||||
public function getFormData(): array
|
||||
{
|
||||
return [
|
||||
[],
|
||||
[], // there are previous saved export which does not contains any data
|
||||
[
|
||||
'referrer_at' => new RollingDate(RollingDate::T_TODAY)
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators;
|
||||
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\ScopeAggregator;
|
||||
@ -54,7 +55,6 @@ final class ScopeAggregatorTest extends AbstractAggregatorTest
|
||||
->select('count(acp.id)')
|
||||
->from(AccompanyingPeriod::class, 'acp')
|
||||
->join('acp.works', 'acpw')
|
||||
->join('acpw.referrers', 'acpwuser'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user