mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge branch '111_exports_suite' into 641_issues_with_children
This commit is contained in:
commit
8928664f87
@ -16,6 +16,8 @@ and this project adheres to
|
||||
* [activity][export] DX/Feature: use of an `ActivityTypeRepositoryInterface` instead of the old-style EntityRepository
|
||||
* [person][export] Fixed: some inconsistency with date filter on accompanying courses
|
||||
* [person][export] Fixed: use left join for related entities in accompanying course aggregators
|
||||
* [workflow] Feature: allow user to copy and send manually the access link for the workflow
|
||||
* [workflow] Feature: show the email addresses that received an access link for the workflow
|
||||
|
||||
## Test releases
|
||||
|
||||
@ -32,8 +34,12 @@ and this project adheres to
|
||||
* [person-thirdparty]: fix quick-add of names that consist of multiple parts (eg. De Vlieger) within onthefly modal person/thirdparty
|
||||
* [search]: Order of birthdate fields changed in advanced search to avoid confusion.
|
||||
* [workflow]: Constraint added to workflow (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/675)
|
||||
* [social_action]: only show active objectives (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/625)
|
||||
* [household]: Reposition and cut button for enfant hors menage have been deleted (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/620)
|
||||
* [admin]: Add crud for composition type in admin (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/611)
|
||||
* [social_action]: only show active objectives (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/625)
|
||||
|
||||
## Test releases
|
||||
|
||||
### 2022-05-30
|
||||
|
||||
|
@ -17,12 +17,13 @@ These are alias conventions :
|
||||
| | Scope::class | acp.scopes | acpscope |
|
||||
| | SocialIssue::class | acp.socialIssues | acpsocialissue |
|
||||
| | User::class | acp.user | acpuser |
|
||||
| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories |
|
||||
| AccompanyingPeriodWork::class | | | acpw |
|
||||
| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval |
|
||||
| | User::class | acpw.referrers | acpwuser |
|
||||
| | SocialAction::class | acpw.socialAction | acpwsocialaction |
|
||||
| | Goal::class | acpw.goals | goal |
|
||||
| | Result::class | acpw.results | result |
|
||||
| | Result::class | acpw.results | result |
|
||||
| AccompanyingPeriodParticipation::class | | | acppart |
|
||||
| | Person::class | acppart.person | partperson |
|
||||
| AccompanyingPeriodWorkEvaluation::class | | | workeval |
|
||||
@ -47,7 +48,7 @@ These are alias conventions :
|
||||
| | HouseholdComposition::class | household.compositions | composition |
|
||||
| Activity::class | | | activity |
|
||||
| | Person::class | activity.person | actperson |
|
||||
| | AccompanyingPeriod::class | activity.accompanyingPeriod | acp |
|
||||
| | AccompanyingPeriod::class | activity.accompanyingPeriod | acp |
|
||||
| | Person::class | activity\_person\_having\_activity.person | person\_person\_having\_activity |
|
||||
| | ActivityReason::class | activity\_person\_having\_activity.reasons | reasons\_person\_having\_activity |
|
||||
| | ActivityType::class | activity.activityType | acttype |
|
||||
@ -59,6 +60,7 @@ These are alias conventions :
|
||||
| | User::class | activity.users | actusers |
|
||||
| | ActivityReason::class | activity.reasons | actreasons |
|
||||
| | Center::class | actperson.center | actcenter |
|
||||
| | Person::class | activity.createdBy | actcreator |
|
||||
| ActivityReason::class | | | actreasons |
|
||||
| | ActivityReasonCategory::class | actreason.category | actreasoncat |
|
||||
| Calendar::class | | | cal |
|
||||
|
@ -13,6 +13,10 @@ namespace Chill\ActivityBundle\Entity;
|
||||
|
||||
use Chill\ActivityBundle\Validator\Constraints as ActivityValidator;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
|
||||
use Chill\MainBundle\Entity\Embeddable\PrivateCommentEmbeddable;
|
||||
@ -55,8 +59,12 @@ use Symfony\Component\Validator\Constraints as Assert;
|
||||
* getUserFunction="getUser",
|
||||
* path="scope")
|
||||
*/
|
||||
class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface, HasCentersInterface, HasScopesInterface
|
||||
class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface, HasCentersInterface, HasScopesInterface, TrackCreationInterface, TrackUpdateInterface
|
||||
{
|
||||
use TrackCreationTrait;
|
||||
|
||||
use TrackUpdateTrait;
|
||||
|
||||
public const SENTRECEIVED_RECEIVED = 'received';
|
||||
|
||||
public const SENTRECEIVED_SENT = 'sent';
|
||||
|
@ -13,20 +13,19 @@ namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\UserRepository;
|
||||
use Chill\MainBundle\Repository\UserRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
class ByUserAggregator implements AggregatorInterface
|
||||
class ByCreatorAggregator implements AggregatorInterface
|
||||
{
|
||||
private UserRender $userRender;
|
||||
|
||||
private UserRepository $userRepository;
|
||||
private UserRepositoryInterface $userRepository;
|
||||
|
||||
public function __construct(
|
||||
UserRepository $userRepository,
|
||||
UserRepositoryInterface $userRepository,
|
||||
UserRender $userRender
|
||||
) {
|
||||
$this->userRepository = $userRepository;
|
||||
@ -40,12 +39,8 @@ class ByUserAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('actusers', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('activity.users', 'actusers');
|
||||
}
|
||||
|
||||
$qb->addSelect('actusers.id AS users_aggregator');
|
||||
$qb->addGroupBy('users_aggregator');
|
||||
$qb->addSelect('IDENTITY(activity.createdBy) AS creator_aggregator');
|
||||
$qb->addGroupBy('creator_aggregator');
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -62,7 +57,7 @@ class ByUserAggregator implements AggregatorInterface
|
||||
{
|
||||
return function ($value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'Accepted users';
|
||||
return 'Created by';
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
@ -77,11 +72,11 @@ class ByUserAggregator implements AggregatorInterface
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['users_aggregator'];
|
||||
return ['creator_aggregator'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Group activity by linked users';
|
||||
return 'Group activity by creator';
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
class UserScopeAggregator implements AggregatorInterface
|
||||
class CreatorScopeAggregator implements AggregatorInterface
|
||||
{
|
||||
private ScopeRepository $scopeRepository;
|
||||
|
||||
@ -40,12 +40,12 @@ class UserScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('actuser', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('activity.user', 'actuser');
|
||||
if (!in_array('actcreator', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('activity.createdBy', 'actcreator');
|
||||
}
|
||||
|
||||
$qb->addSelect('IDENTITY(actuser.mainScope) AS userscope_aggregator');
|
||||
$qb->addGroupBy('userscope_aggregator');
|
||||
$qb->addSelect('IDENTITY(actcreator.mainScope) AS creatorscope_aggregator');
|
||||
$qb->addGroupBy('creatorscope_aggregator');
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -79,11 +79,11 @@ class UserScopeAggregator implements AggregatorInterface
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['userscope_aggregator'];
|
||||
return ['creatorscope_aggregator'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Group activity by userscope';
|
||||
return 'Group activity by creator scope';
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\ActivityBundle\Export\Aggregator;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\UserRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
class ActivityUsersAggregator implements AggregatorInterface
|
||||
{
|
||||
private UserRender $userRender;
|
||||
|
||||
private UserRepositoryInterface $userRepository;
|
||||
|
||||
public function __construct(UserRepositoryInterface $userRepository, UserRender $userRender)
|
||||
{
|
||||
$this->userRepository = $userRepository;
|
||||
$this->userRender = $userRender;
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('actusers', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('activity.users', 'actusers');
|
||||
}
|
||||
|
||||
$qb
|
||||
->addSelect('actusers.id AS activity_users_aggregator')
|
||||
->addGroupBy('activity_users_aggregator');
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACTIVITY;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// nothing to add on the form
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
return function ($value) {
|
||||
if ('_header' === $value) {
|
||||
return 'Activity users';
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$u = $this->userRepository->find($value);
|
||||
|
||||
return $this->userRender->renderString($u, []);
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
{
|
||||
return ['activity_users_aggregator'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Aggregate by activity users';
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\ActivityBundle\Export\Aggregator;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
class ActivityUsersJobAggregator implements \Chill\MainBundle\Export\AggregatorInterface
|
||||
{
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
|
||||
private UserJobRepositoryInterface $userJobRepository;
|
||||
|
||||
public function __construct(UserJobRepositoryInterface $userJobRepository, TranslatableStringHelperInterface $translatableStringHelper)
|
||||
{
|
||||
$this->userJobRepository = $userJobRepository;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('actusers', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('activity.users', 'actusers');
|
||||
}
|
||||
|
||||
$qb
|
||||
->addSelect('IDENTITY(actusers.userJob) AS activity_users_job_aggregator')
|
||||
->addGroupBy('activity_users_job_aggregator');
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ACTIVITY;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// nothing to add in the form
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
return function ($value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'Users \'s job';
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$j = $this->userJobRepository->find($value);
|
||||
|
||||
return $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['activity_users_job_aggregator'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Aggregate by users job';
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\ActivityBundle\Export\Aggregator;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
class ActivityUsersScopeAggregator implements \Chill\MainBundle\Export\AggregatorInterface
|
||||
{
|
||||
private ScopeRepositoryInterface $scopeRepository;
|
||||
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
|
||||
public function __construct(ScopeRepositoryInterface $scopeRepository, TranslatableStringHelperInterface $translatableStringHelper)
|
||||
{
|
||||
$this->scopeRepository = $scopeRepository;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('actusers', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('activity.users', 'actusers');
|
||||
}
|
||||
|
||||
$qb
|
||||
->addSelect('IDENTITY(actusers.mainScope) AS activity_users_main_scope_aggregator')
|
||||
->addGroupBy('activity_users_main_scope_aggregator');
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ACTIVITY;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// nothing to add in the form
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
return function ($value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'Users \'s scope';
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$s = $this->scopeRepository->find($value);
|
||||
|
||||
return $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['activity_users_main_scope_aggregator'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Aggregate by users scope';
|
||||
}
|
||||
}
|
@ -15,12 +15,10 @@ use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
class ByUserFilter implements FilterInterface
|
||||
class ByCreatorFilter implements FilterInterface
|
||||
{
|
||||
private UserRender $userRender;
|
||||
|
||||
@ -36,22 +34,11 @@ class ByUserFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$where = $qb->getDQLPart('where');
|
||||
|
||||
if (!in_array('actusers', $qb->getAllAliases(), true)) {
|
||||
$qb->join('activity.users', 'actusers');
|
||||
}
|
||||
|
||||
$clause = $qb->expr()->in('actusers.id', ':users');
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter('users', $data['accepted_users']);
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->in('activity.createdBy', ':users')
|
||||
)
|
||||
->setParameter('users', $data['accepted_users']);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -74,13 +61,13 @@ class ByUserFilter implements FilterInterface
|
||||
$users[] = $this->userRender->renderString($u, []);
|
||||
}
|
||||
|
||||
return ['Filtered activity by linked users: only %users%', [
|
||||
return ['Filtered activity by creator: only %users%', [
|
||||
'%users%' => implode(', ou ', $users),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Filter activity by linked users';
|
||||
return 'Filter activity by creator';
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
<?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\Filter;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class ActivityUsersFilter implements FilterInterface
|
||||
{
|
||||
private UserRender $userRender;
|
||||
|
||||
public function __construct(UserRender $userRender)
|
||||
{
|
||||
$this->userRender = $userRender;
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$orX = $qb->expr()->orX();
|
||||
|
||||
foreach ($data['accepted_users'] as $key => $user) {
|
||||
$orX->add($qb->expr()->isMemberOf(':activity_users_filter_u' . $key, 'activity.users'));
|
||||
$qb->setParameter('activity_users_filter_u' . $key, $user);
|
||||
}
|
||||
|
||||
$qb->andWhere($orX);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ACTIVITY;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('accepted_users', PickUserDynamicType::class, [
|
||||
'multiple' => true,
|
||||
'label' => 'Users',
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
$users = [];
|
||||
|
||||
foreach ($data['accepted_users'] as $u) {
|
||||
$users[] = $this->userRender->renderString($u, []);
|
||||
}
|
||||
|
||||
return ['Filtered activity by users: only %users%', [
|
||||
'%users%' => implode(', ', $users),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Filter activity by users';
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\ActivityBundle\Tests\Export\Aggregator\ACPAggregators;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByUserAggregator;
|
||||
use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByCreatorAggregator;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
@ -22,7 +22,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
*/
|
||||
final class ByUserAggregatorTest extends AbstractAggregatorTest
|
||||
{
|
||||
private ByUserAggregator $aggregator;
|
||||
private ByCreatorAggregator $aggregator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\ActivityBundle\Tests\Export\Aggregator\ACPAggregators;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\UserScopeAggregator;
|
||||
use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\CreatorScopeAggregator;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
@ -22,7 +22,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
*/
|
||||
final class UserScopeAggregatorTest extends AbstractAggregatorTest
|
||||
{
|
||||
private UserScopeAggregator $aggregator;
|
||||
private CreatorScopeAggregator $aggregator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\ActivityBundle\Tests\Export\Filter\ACPFilters;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\ActivityBundle\Export\Filter\ACPFilters\ByUserFilter;
|
||||
use Chill\ActivityBundle\Export\Filter\ACPFilters\ByCreatorFilter;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@ -23,7 +23,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
*/
|
||||
final class ByUserFilterTest extends AbstractFilterTest
|
||||
{
|
||||
private ByUserFilter $filter;
|
||||
private ByCreatorFilter $filter;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
|
@ -55,6 +55,10 @@ services:
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'activity_date_filter' }
|
||||
|
||||
Chill\ActivityBundle\Export\Filter\ActivityUsersFilter:
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'activity_users_filter' }
|
||||
|
||||
chill.activity.export.reason_filter:
|
||||
class: Chill\ActivityBundle\Export\Filter\PersonFilters\ActivityReasonFilter
|
||||
tags:
|
||||
@ -77,10 +81,9 @@ services:
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'activity_locationtype_filter' }
|
||||
|
||||
chill.activity.export.byuser_filter: # TMS (M2M)
|
||||
class: Chill\ActivityBundle\Export\Filter\ACPFilters\ByUserFilter
|
||||
Chill\ActivityBundle\Export\Filter\ACPFilters\ByCreatorFilter:
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'activity_byuser_filter' }
|
||||
- { name: chill.export_filter, alias: 'activity_bycreator_filter' }
|
||||
|
||||
chill.activity.export.emergency_filter:
|
||||
class: Chill\ActivityBundle\Export\Filter\ACPFilters\EmergencyFilter
|
||||
@ -138,10 +141,9 @@ services:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_date_aggregator }
|
||||
|
||||
chill.activity.export.byuser_aggregator:
|
||||
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByUserAggregator
|
||||
Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByCreatorAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_byuser_aggregator }
|
||||
- { name: chill.export_aggregator, alias: activity_by_creator_aggregator }
|
||||
|
||||
chill.activity.export.bythirdparty_aggregator:
|
||||
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByThirdpartyAggregator
|
||||
@ -158,7 +160,18 @@ services:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_bysocialissue_aggregator }
|
||||
|
||||
chill.activity.export.userscope_aggregator:
|
||||
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\UserScopeAggregator
|
||||
Chill\ActivityBundle\Export\Aggregator\ACPAggregators\CreatorScopeAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_userscope_aggregator }
|
||||
- { name: chill.export_aggregator, alias: activity_creator_scope_aggregator }
|
||||
|
||||
Chill\ActivityBundle\Export\Aggregator\ActivityUsersAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_users_aggregator }
|
||||
|
||||
Chill\ActivityBundle\Export\Aggregator\ActivityUsersScopeAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_users_scope_aggregator }
|
||||
|
||||
Chill\ActivityBundle\Export\Aggregator\ActivityUsersJobAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_users_job_aggregator }
|
||||
|
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\Migrations\Activity;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20221014130554 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE activity DROP updatedAt');
|
||||
$this->addSql('ALTER TABLE activity DROP createdAt');
|
||||
$this->addSql('ALTER TABLE activity DROP updatedBy_id');
|
||||
$this->addSql('ALTER TABLE activity DROP createdBy_id');
|
||||
|
||||
// rename some indexes on activity
|
||||
$this->addSql('ALTER INDEX idx_ac74095a217bbb47 RENAME TO idx_55026b0c217bbb47');
|
||||
$this->addSql('ALTER INDEX idx_ac74095a682b5931 RENAME TO idx_55026b0c682b5931');
|
||||
$this->addSql('ALTER INDEX idx_ac74095aa76ed395 RENAME TO idx_55026b0ca76ed395');
|
||||
$this->addSql('ALTER INDEX idx_ac74095ac54c8c93 RENAME TO idx_55026b0cc54c8c93');
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Track update and create on activity';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE activity ADD updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE activity ADD createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE activity ADD updatedBy_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE activity ADD createdBy_id INT DEFAULT NULL');
|
||||
$this->addSql('COMMENT ON COLUMN activity.updatedAt IS \'(DC2Type:datetime_immutable)\'');
|
||||
$this->addSql('COMMENT ON COLUMN activity.createdAt IS \'(DC2Type:datetime_immutable)\'');
|
||||
$this->addSql('ALTER TABLE activity ADD CONSTRAINT FK_AC74095A65FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE activity ADD CONSTRAINT FK_AC74095A3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_AC74095A65FF1AEC ON activity (updatedBy_id)');
|
||||
$this->addSql('CREATE INDEX IDX_AC74095A3174800F ON activity (createdBy_id)');
|
||||
|
||||
// rename some indexes on activity
|
||||
$this->addSql('ALTER INDEX idx_55026b0cc54c8c93 RENAME TO IDX_AC74095AC54C8C93');
|
||||
$this->addSql('ALTER INDEX idx_55026b0c217bbb47 RENAME TO IDX_AC74095A217BBB47');
|
||||
$this->addSql('ALTER INDEX idx_55026b0c682b5931 RENAME TO IDX_AC74095A682B5931');
|
||||
$this->addSql('ALTER INDEX idx_55026b0ca76ed395 RENAME TO IDX_AC74095AA76ED395');
|
||||
|
||||
$this->addSql('UPDATE activity SET updatedBy_id=user_id, createdBy_id=user_id, createdAt="date", updatedAt="date"');
|
||||
}
|
||||
}
|
@ -252,8 +252,6 @@ Filter by activity type: Filtrer les activités par type
|
||||
Filter activity by locationtype: Filtrer les activités par type de localisation
|
||||
'Filtered activity by locationtype: only %types%': "Filtré par type de localisation: uniquement %types%"
|
||||
Accepted locationtype: Types de localisation
|
||||
Filter activity by linked users: Filtrer les activités par TMS
|
||||
'Filtered activity by linked users: only %users%': "Filtré par TMS: uniquement %users%"
|
||||
Accepted users: TMS(s)
|
||||
Filter activity by emergency: Filtrer les activités par urgence
|
||||
'Filtered activity by emergency: only %emergency%': "Filtré par urgence: uniquement si %emergency%"
|
||||
@ -269,7 +267,11 @@ Filter activity by linked socialaction: Filtrer les activités par action liée
|
||||
Filter activity by linked socialissue: Filtrer les activités par problématique liée
|
||||
'Filtered activity by linked socialissue: only %issues%': "Filtré par problématique liée: uniquement %issues%"
|
||||
Filter activity by user: Filtrer les activités par créateur
|
||||
'Filtered activity by user: only %users%': "Filtré par créateur: uniquement %users%"
|
||||
Filter activity by users: Filtrer les activités par utilisateur participant
|
||||
Filter activity by creator: Filtrer les activités par créateur de l'échange
|
||||
'Filtered activity by user: only %users%': "Filtré par référent: uniquement %users%"
|
||||
'Filtered activity by users: only %users%': "Filtré par utilisateurs participants: uniquement %users%"
|
||||
'Filtered activity by creator: only %users%': "Filtré par créateur: uniquement %users%"
|
||||
Creators: Créateurs
|
||||
Filter activity by userscope: Filtrer les activités par service du créateur
|
||||
'Filtered activity by userscope: only %scopes%': "Filtré par service du créateur: uniquement %scopes%"
|
||||
@ -282,9 +284,14 @@ By reason: Par sujet
|
||||
By category of reason: Par catégorie de sujet
|
||||
Reason's level: Niveau du sujet
|
||||
Group by reasons: Sujet d'activité
|
||||
Aggregate by activity user: Grouper les activités par utilisateur
|
||||
Aggregate by activity user: Grouper les activités par référent
|
||||
Aggregate by activity users: Grouper les activités par utilisateurs participants
|
||||
Aggregate by activity type: Grouper les activités par type
|
||||
Aggregate by activity reason: Grouper les activités par sujet
|
||||
Aggregate by users scope: Grouper les activités par service principal de l'utilisateur
|
||||
Users 's scope: Service principal des utilisateurs participants à l'activité
|
||||
Aggregate by users job: Grouper les activités par métier des utilisateurs participants
|
||||
Users 's job: Métier des utilisateurs participants à l'activité
|
||||
|
||||
Group activity by locationtype: Grouper les activités par type de localisation
|
||||
Group activity by date: Grouper les activités par date
|
||||
@ -294,7 +301,8 @@ by week: Par semaine
|
||||
for week: Semaine
|
||||
by year: Par année
|
||||
in year: En
|
||||
Group activity by linked users: Grouper les activités par TMS impliqué
|
||||
Group activity by creator: Grouper les activités par créateur de l'échange
|
||||
Group activity by creator scope: Grouper les activités par service du créateur de l'échange
|
||||
Group activity by linked thirdparties: Grouper les activités par tiers impliqué
|
||||
Accepted thirdparty: Tiers impliqué
|
||||
Group activity by linked socialaction: Grouper les activités par action liée
|
||||
|
@ -14,7 +14,7 @@ namespace Chill\MainBundle\Controller;
|
||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Repository\CountryRepository;
|
||||
use Chill\MainBundle\Repository\PostalCodeRepository;
|
||||
use Chill\MainBundle\Repository\PostalCodeRepositoryInterface;
|
||||
use Chill\MainBundle\Serializer\Model\Collection;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@ -30,11 +30,11 @@ final class PostalCodeAPIController extends ApiController
|
||||
|
||||
private PaginatorFactory $paginatorFactory;
|
||||
|
||||
private PostalCodeRepository $postalCodeRepository;
|
||||
private PostalCodeRepositoryInterface $postalCodeRepository;
|
||||
|
||||
public function __construct(
|
||||
CountryRepository $countryRepository,
|
||||
PostalCodeRepository $postalCodeRepository,
|
||||
PostalCodeRepositoryInterface $postalCodeRepository,
|
||||
PaginatorFactory $paginatorFactory
|
||||
) {
|
||||
$this->countryRepository = $countryRepository;
|
||||
|
@ -22,6 +22,7 @@ use Chill\MainBundle\Controller\UserController;
|
||||
use Chill\MainBundle\Controller\UserJobApiController;
|
||||
use Chill\MainBundle\Controller\UserJobController;
|
||||
use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
|
||||
use Chill\MainBundle\Doctrine\DQL\Age;
|
||||
use Chill\MainBundle\Doctrine\DQL\Extract;
|
||||
use Chill\MainBundle\Doctrine\DQL\GetJsonFieldByKey;
|
||||
use Chill\MainBundle\Doctrine\DQL\JsonAggregate;
|
||||
@ -243,6 +244,7 @@ class ChillMainExtension extends Extension implements
|
||||
'datetime_functions' => [
|
||||
'EXTRACT' => Extract::class,
|
||||
'TO_CHAR' => ToChar::class,
|
||||
'AGE' => Age::class,
|
||||
],
|
||||
],
|
||||
'hydrators' => [
|
||||
|
54
src/Bundle/ChillMainBundle/Doctrine/DQL/Age.php
Normal file
54
src/Bundle/ChillMainBundle/Doctrine/DQL/Age.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?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\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
|
||||
class Age extends FunctionNode
|
||||
{
|
||||
private $value1;
|
||||
|
||||
private $value2;
|
||||
|
||||
public function getSql(SqlWalker $sqlWalker)
|
||||
{
|
||||
if (null !== $this->value2) {
|
||||
return sprintf(
|
||||
'AGE(%s, %s)',
|
||||
$this->value1->dispatch($sqlWalker),
|
||||
$this->value2->dispatch($sqlWalker)
|
||||
);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'AGE(%s)',
|
||||
$this->value1->dispatch($sqlWalker),
|
||||
);
|
||||
}
|
||||
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->value1 = $parser->SimpleArithmeticExpression();
|
||||
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
|
||||
$this->value2 = $parser->SimpleArithmeticExpression();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
@ -28,6 +28,11 @@ use Symfony\Component\Serializer\Annotation\Groups;
|
||||
*/
|
||||
class Scope
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="boolean", nullable=false, options={"default": true})
|
||||
*/
|
||||
private bool $active = true;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(name="id", type="integer")
|
||||
@ -88,6 +93,18 @@ class Scope
|
||||
return $this->roleScopes;
|
||||
}
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
public function setActive(bool $active): Scope
|
||||
{
|
||||
$this->active = $active;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
|
@ -0,0 +1,55 @@
|
||||
<?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\Form\Type\DataTransformer;
|
||||
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Chill\MainBundle\Repository\PostalCodeRepositoryInterface;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use function gettype;
|
||||
use function is_int;
|
||||
|
||||
class PostalCodeToIdTransformer implements DataTransformerInterface
|
||||
{
|
||||
private PostalCodeRepositoryInterface $postalCodeRepository;
|
||||
|
||||
public function __construct(PostalCodeRepositoryInterface $postalCodeRepository)
|
||||
{
|
||||
$this->postalCodeRepository = $postalCodeRepository;
|
||||
}
|
||||
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value || trim('') === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!is_int((int) $value)) {
|
||||
throw new TransformationFailedException('Cannot transform ' . gettype($value));
|
||||
}
|
||||
|
||||
return $this->postalCodeRepository->find((int) $value);
|
||||
}
|
||||
|
||||
public function transform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($value instanceof PostalCode) {
|
||||
return $value->getId();
|
||||
}
|
||||
|
||||
throw new TransformationFailedException('Could not reverseTransform ' . gettype($value));
|
||||
}
|
||||
}
|
49
src/Bundle/ChillMainBundle/Form/Type/PickPostalCodeType.php
Normal file
49
src/Bundle/ChillMainBundle/Form/Type/PickPostalCodeType.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?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\Form\Type;
|
||||
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Chill\MainBundle\Form\Type\DataTransformer\PostalCodeToIdTransformer;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class PickPostalCodeType extends AbstractType
|
||||
{
|
||||
private PostalCodeToIdTransformer $postalCodeToIdTransformer;
|
||||
|
||||
public function __construct(PostalCodeToIdTransformer $postalCodeToIdTransformer)
|
||||
{
|
||||
$this->postalCodeToIdTransformer = $postalCodeToIdTransformer;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->addViewTransformer($this->postalCodeToIdTransformer);
|
||||
}
|
||||
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars['uniqid'] = $view->vars['attr']['data-input-postal-code'] = uniqid('input_pick_postal_code_');
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver
|
||||
->setDefault('class', PostalCode::class)
|
||||
->setDefault('multiple', false)
|
||||
->setAllowedTypes('multiple', ['bool'])
|
||||
->setDefault('compound', false);
|
||||
}
|
||||
}
|
@ -15,9 +15,9 @@ use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Form\DataMapper\ScopePickerDataMapper;
|
||||
use Chill\MainBundle\Repository\ScopeRepository;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use RuntimeException;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
@ -26,11 +26,9 @@ use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
use function array_map;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
@ -44,47 +42,37 @@ use function count;
|
||||
*/
|
||||
class ScopePickerType extends AbstractType
|
||||
{
|
||||
protected AuthorizationHelperInterface $authorizationHelper;
|
||||
private AuthorizationHelperInterface $authorizationHelper;
|
||||
|
||||
/**
|
||||
* @var ScopeRepository
|
||||
*/
|
||||
protected $scopeRepository;
|
||||
private Security $security;
|
||||
|
||||
protected Security $security;
|
||||
|
||||
/**
|
||||
* @var TokenStorageInterface
|
||||
*/
|
||||
protected $tokenStorage;
|
||||
|
||||
/**
|
||||
* @var TranslatableStringHelper
|
||||
*/
|
||||
protected $translatableStringHelper;
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
|
||||
public function __construct(
|
||||
AuthorizationHelperInterface $authorizationHelper,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
ScopeRepository $scopeRepository,
|
||||
Security $security,
|
||||
TranslatableStringHelper $translatableStringHelper
|
||||
TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
$this->scopeRepository = $scopeRepository;
|
||||
$this->security = $security;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$items = $this->authorizationHelper->getReachableScopes(
|
||||
$this->security->getUser(),
|
||||
$options['role'] instanceof Role ? $options['role']->getRole() : $options['role'],
|
||||
$options['center']
|
||||
$items = array_filter(
|
||||
$this->authorizationHelper->getReachableScopes(
|
||||
$this->security->getUser(),
|
||||
$options['role'] instanceof Role ? $options['role']->getRole() : $options['role'],
|
||||
$options['center']
|
||||
),
|
||||
static function (Scope $s) { return $s->isActive(); }
|
||||
);
|
||||
|
||||
if (0 === count($items)) {
|
||||
throw new RuntimeException('no scopes are reachable. This form should not be shown to user');
|
||||
}
|
||||
|
||||
if (1 !== count($items)) {
|
||||
$builder->add('scope', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
@ -123,35 +111,4 @@ class ScopePickerType extends AbstractType
|
||||
->setRequired('role')
|
||||
->setAllowedTypes('role', ['string', Role::class]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|Center|Center[] $center
|
||||
* @param string $role
|
||||
*
|
||||
* @return \Doctrine\ORM\QueryBuilder
|
||||
*/
|
||||
protected function buildAccessibleScopeQuery($center, $role)
|
||||
{
|
||||
$roles = $this->authorizationHelper->getParentRoles($role);
|
||||
$roles[] = $role;
|
||||
$centers = $center instanceof Center ? [$center] : $center;
|
||||
|
||||
$qb = $this->scopeRepository->createQueryBuilder('s');
|
||||
$qb
|
||||
// jointure to center
|
||||
->join('s.roleScopes', 'rs')
|
||||
->join('rs.permissionsGroups', 'pg')
|
||||
->join('pg.groupCenters', 'gc')
|
||||
// add center constraint
|
||||
->where($qb->expr()->in('IDENTITY(gc.center)', ':centers'))
|
||||
->setParameter('centers', array_map(static fn (Center $c) => $c->getId(), $centers))
|
||||
// role constraints
|
||||
->andWhere($qb->expr()->in('rs.role', ':roles'))
|
||||
->setParameter('roles', $roles)
|
||||
// user contraint
|
||||
->andWhere(':user MEMBER OF gc.users')
|
||||
->setParameter('user', $this->tokenStorage->getToken()->getUser());
|
||||
|
||||
return $qb;
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,9 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use RuntimeException;
|
||||
|
||||
final class PostalCodeRepository implements ObjectRepository
|
||||
final class PostalCodeRepository implements PostalCodeRepositoryInterface
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
@ -29,7 +28,7 @@ final class PostalCodeRepository implements ObjectRepository
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->repository = $entityManager->getRepository(PostalCode::class);
|
||||
$this->repository = $entityManager->getRepository($this->getClassName());
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
@ -51,20 +50,11 @@ final class PostalCodeRepository implements ObjectRepository
|
||||
return $this->repository->find($id, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PostalCode[]
|
||||
*/
|
||||
public function findAll(): array
|
||||
{
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
*
|
||||
* @return PostalCode[]
|
||||
*/
|
||||
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
|
||||
{
|
||||
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||
@ -95,7 +85,7 @@ final class PostalCodeRepository implements ObjectRepository
|
||||
return $this->repository->findOneBy($criteria, $orderBy);
|
||||
}
|
||||
|
||||
public function getClassName()
|
||||
public function getClassName(): string
|
||||
{
|
||||
return PostalCode::class;
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
<?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;
|
||||
|
||||
use Chill\MainBundle\Entity\Country;
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
interface PostalCodeRepositoryInterface extends ObjectRepository
|
||||
{
|
||||
public function countByPattern(string $pattern, ?Country $country): int;
|
||||
|
||||
public function find($id, $lockMode = null, $lockVersion = null): ?PostalCode;
|
||||
|
||||
/**
|
||||
* @return PostalCode[]
|
||||
*/
|
||||
public function findAll(): array;
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
*
|
||||
* @return PostalCode[]
|
||||
*/
|
||||
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array;
|
||||
|
||||
public function findByPattern(string $pattern, ?Country $country, ?int $start = 0, ?int $limit = 50): array;
|
||||
|
||||
public function findOneBy(array $criteria, ?array $orderBy = null): ?PostalCode;
|
||||
|
||||
public function getClassName(): string;
|
||||
}
|
@ -43,6 +43,15 @@ final class ScopeRepository implements ScopeRepositoryInterface
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
|
||||
public function findAllActive(): array
|
||||
{
|
||||
$qb = $this->repository->createQueryBuilder('s');
|
||||
|
||||
$qb->where('s.active = \'TRUE\'');
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
|
@ -22,10 +22,15 @@ interface ScopeRepositoryInterface extends ObjectRepository
|
||||
public function find($id, $lockMode = null, $lockVersion = null): ?Scope;
|
||||
|
||||
/**
|
||||
* @return Scope[]
|
||||
* @return array|Scope[]
|
||||
*/
|
||||
public function findAll(): array;
|
||||
|
||||
/**
|
||||
* @return array|Scope[]
|
||||
*/
|
||||
public function findAllActive(): array;
|
||||
|
||||
/**
|
||||
* @param null|mixed $limit
|
||||
* @param null|mixed $offset
|
||||
|
@ -14,9 +14,8 @@ namespace Chill\MainBundle\Repository;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
class UserJobRepository implements ObjectRepository
|
||||
class UserJobRepository implements UserJobRepositoryInterface
|
||||
{
|
||||
private EntityRepository $repository;
|
||||
|
||||
@ -38,6 +37,11 @@ class UserJobRepository implements ObjectRepository
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
|
||||
public function findAllActive(): array
|
||||
{
|
||||
return $this->repository->findBy(['active' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
@ -49,12 +53,12 @@ class UserJobRepository implements ObjectRepository
|
||||
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||
}
|
||||
|
||||
public function findOneBy(array $criteria)
|
||||
public function findOneBy(array $criteria): ?UserJob
|
||||
{
|
||||
return $this->repository->findOneBy($criteria);
|
||||
}
|
||||
|
||||
public function getClassName()
|
||||
public function getClassName(): string
|
||||
{
|
||||
return UserJob::class;
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
<?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;
|
||||
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
interface UserJobRepositoryInterface extends ObjectRepository
|
||||
{
|
||||
public function find($id): ?UserJob;
|
||||
|
||||
/**
|
||||
* @return array|UserJob[]
|
||||
*/
|
||||
public function findAll(): array;
|
||||
|
||||
/**
|
||||
* @return array|UserJob[]
|
||||
*/
|
||||
public function findAllActive(): array;
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
*
|
||||
* @return array|object[]|UserJob[]
|
||||
*/
|
||||
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null);
|
||||
|
||||
public function findOneBy(array $criteria): ?UserJob;
|
||||
|
||||
public function getClassName(): string;
|
||||
}
|
@ -16,11 +16,10 @@ use Chill\MainBundle\Entity\User;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
use function count;
|
||||
|
||||
final class UserRepository implements ObjectRepository
|
||||
final class UserRepository implements UserRepositoryInterface
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
@ -171,7 +170,7 @@ final class UserRepository implements ObjectRepository
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
public function getClassName()
|
||||
public function getClassName(): string
|
||||
{
|
||||
return User::class;
|
||||
}
|
||||
|
@ -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\MainBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
interface UserRepositoryInterface extends ObjectRepository
|
||||
{
|
||||
public function countBy(array $criteria): int;
|
||||
|
||||
public function countByActive(): int;
|
||||
|
||||
public function countByUsernameOrEmail(string $pattern): int;
|
||||
|
||||
public function find($id, $lockMode = null, $lockVersion = null): ?User;
|
||||
|
||||
/**
|
||||
* @return User[]
|
||||
*/
|
||||
public function findAll(): array;
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
*
|
||||
* @return User[]
|
||||
*/
|
||||
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array;
|
||||
|
||||
/**
|
||||
* @return array|User[]
|
||||
*/
|
||||
public function findByActive(?array $orderBy = null, ?int $limit = null, ?int $offset = null): array;
|
||||
|
||||
public function findByUsernameOrEmail(string $pattern, ?array $orderBy = [], ?int $limit = null, ?int $offset = null): array;
|
||||
|
||||
public function findOneBy(array $criteria, ?array $orderBy = null): ?User;
|
||||
|
||||
public function findOneByUsernameOrEmail(string $pattern);
|
||||
|
||||
/**
|
||||
* Get the users having a specific flags.
|
||||
*
|
||||
* If provided, only the users amongst "filtered users" are searched. This
|
||||
* allows to make a first search amongst users based on role and center
|
||||
* and, then filter those users having some flags.
|
||||
*
|
||||
* @param \Chill\MainBundle\Entity\User[] $amongstUsers
|
||||
* @param mixed $flag
|
||||
*/
|
||||
public function findUsersHavingFlags($flag, array $amongstUsers = []): array;
|
||||
|
||||
public function getClassName(): string;
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
import { createApp } from 'vue';
|
||||
import PickPostalCode from 'ChillMainAssets/vuejs/PickPostalCode/PickPostalCode';
|
||||
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
|
||||
import { appMessages } from 'ChillMainAssets/vuejs/PickEntity/i18n';
|
||||
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
|
||||
|
||||
const i18n = _createI18n(appMessages);
|
||||
|
||||
|
||||
function loadOnePicker(el, input, uniqId, city) {
|
||||
const app = createApp({
|
||||
template: '<pick-postal-code @select-city="onCitySelected" @removeCity="onCityRemoved" :picked="city"></pick-postal-code>',
|
||||
components: {
|
||||
PickPostalCode,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
city: city,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onCitySelected(city) {
|
||||
this.city = city;
|
||||
input.value = city.id;
|
||||
},
|
||||
onCityRemoved(city) {
|
||||
this.city = null;
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
})
|
||||
.use(i18n)
|
||||
.mount(el);
|
||||
}
|
||||
|
||||
function loadDynamicPickers(element) {
|
||||
|
||||
let apps = element.querySelectorAll('[data-module="pick-postal-code"]');
|
||||
|
||||
apps.forEach(function(el) {
|
||||
|
||||
const
|
||||
uniqId = el.dataset.uniqid,
|
||||
input = document.querySelector(`input[data-input-uniqid="${uniqId}"]`),
|
||||
cityIdValue = input.value === '' ? null : input.value
|
||||
;
|
||||
|
||||
if (cityIdValue !== null) {
|
||||
makeFetch('GET', `/api/1.0/main/postal-code/${cityIdValue}.json`).then(city => {
|
||||
loadOnePicker(el, input, uniqId, city);
|
||||
})
|
||||
} else {
|
||||
loadOnePicker(el, input, uniqId, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function(e) {
|
||||
loadDynamicPickers(document)
|
||||
})
|
@ -0,0 +1,29 @@
|
||||
# Pickpostalcode
|
||||
|
||||
Allow to pick a postal code.
|
||||
|
||||
In use with module `mod_pick_postal_code`, associated with `PickPostalCodeType` in php.
|
||||
|
||||
## Usage
|
||||
|
||||
`<pick-postal-code @select-city="onCitySelected" @removeCity="onCityRemoved" :picked="city"></pick-postal-code>`
|
||||
|
||||
## Props
|
||||
|
||||
* `picked`: the city picked. A javascript object (a city). Null if empty.
|
||||
* `country`: country to restraint search on picked. May be null.
|
||||
|
||||
## Emits
|
||||
|
||||
### `selectCity`
|
||||
|
||||
When a city is onCitySelected.
|
||||
|
||||
Argument: a js object, representing a city
|
||||
|
||||
### `removeCity`
|
||||
|
||||
When a city is removed.
|
||||
|
||||
|
||||
Argument: a js object, representing a city
|
@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div class="PickPostalCode">
|
||||
<vue-multiselect
|
||||
id="citySelector"
|
||||
@search-change="listenInputSearch"
|
||||
ref="citySelector"
|
||||
v-model="internalPicked"
|
||||
@select="selectCity"
|
||||
@remove="remove"
|
||||
name=""
|
||||
track-by="id"
|
||||
label="value"
|
||||
:custom-label="transName"
|
||||
:placeholder="$t('select_city')"
|
||||
:select-label="$t('multiselect.select_label')"
|
||||
:deselect-label="$t('multiselect.deselect_label')"
|
||||
:selected-label="$t('multiselect.selected_label')"
|
||||
:taggable="true"
|
||||
:multiple="false"
|
||||
:internal-search="false"
|
||||
:loading="isLoading"
|
||||
:options="cities"></vue-multiselect>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
|
||||
import VueMultiselect from "vue-multiselect";
|
||||
import {reactive, defineProps, onMounted} from "vue";
|
||||
import {fetchCities, searchCities} from "./api";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VueMultiselect,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cities: [],
|
||||
internalPicked: null,
|
||||
isLoading: false,
|
||||
abortControllers: [],
|
||||
}
|
||||
},
|
||||
emits: ['pickCity', 'removeCity'],
|
||||
props: {
|
||||
picked: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
country: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.picked !== null) {
|
||||
this.internalPicked = this.picked;
|
||||
this.cities.push(this.picked);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
transName(value) {
|
||||
return (value.code && value.name) ? `${value.name} (${value.code})` : '';
|
||||
},
|
||||
selectCity(city) {
|
||||
this.$emit('selectCity', city);
|
||||
},
|
||||
listenInputSearch(query) {
|
||||
if (query.length <= 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
let c = this.abortControllers.pop();
|
||||
|
||||
while (typeof c !== 'undefined') {
|
||||
c.abort();
|
||||
c = this.abortControllers.pop();
|
||||
}
|
||||
|
||||
this.isLoading = true;
|
||||
let controller = new AbortController();
|
||||
this.abortControllers.push(controller);
|
||||
|
||||
searchCities(query, this.country, controller).then(
|
||||
newCities => {
|
||||
this.cities = this.cities.filter(city => city.id === this.picked);
|
||||
newCities.forEach(item => {
|
||||
this.cities.push(item);
|
||||
})
|
||||
this.isLoading = false;
|
||||
|
||||
return Promise.resolve();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error); //TODO better error handling
|
||||
this.isLoading = false;
|
||||
});
|
||||
},
|
||||
remove(item) {
|
||||
this.$emit('removeCity', item);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
@ -0,0 +1,3 @@
|
||||
.PickPostalCode {
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import {makeFetch, fetchResults} from 'ChillMainAssets/lib/api/apiMethods';
|
||||
|
||||
/**
|
||||
* Endpoint chill_api_single_postal_code__index
|
||||
* method GET, get Cities Object
|
||||
* @params {object} a country object
|
||||
* @returns {Promise} a promise containing all Postal Code objects filtered with country
|
||||
*/
|
||||
const fetchCities = (country) => {
|
||||
// warning: do not use fetchResults (in apiMethods): we need only a **part** of the results in the db
|
||||
const params = new URLSearchParams({item_per_page: 100});
|
||||
|
||||
if (country !== null) {
|
||||
params.append('country', country.id);
|
||||
}
|
||||
|
||||
return makeFetch('GET', `/api/1.0/main/postal-code.json?${params.toString()}`).then(r => Promise.resolve(r.results));
|
||||
};
|
||||
|
||||
/**
|
||||
* Endpoint chill_main_postalcodeapi_search
|
||||
* method GET, get Cities Object
|
||||
* @params {string} search a search string
|
||||
* @params {object} country a country object
|
||||
* @params {AbortController} an abort controller
|
||||
* @returns {Promise} a promise containing all Postal Code objects filtered with country and a search string
|
||||
*/
|
||||
const searchCities = (search, country, controller) => {
|
||||
const url = '/api/1.0/main/postal-code/search.json?';
|
||||
const params = new URLSearchParams({q: search});
|
||||
|
||||
if (country !== null) {
|
||||
Object.assign('country', country.id);
|
||||
}
|
||||
|
||||
return makeFetch('GET', url + params, null, {signal: controller.signal})
|
||||
.then(result => Promise.resolve(result.results));
|
||||
};
|
||||
|
||||
export {
|
||||
fetchCities,
|
||||
searchCities,
|
||||
};
|
@ -238,3 +238,9 @@
|
||||
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %} data-input-uniqid="{{ form.vars['uniqid'] }}"/>
|
||||
<div data-module="pick-dynamic" data-types="{{ form.vars['types']|json_encode }}" data-multiple="{{ form.vars['multiple'] }}" data-uniqid="{{ form.vars['uniqid'] }}"></div>
|
||||
{% endblock %}
|
||||
|
||||
{% block pick_postal_code_widget %}
|
||||
{{ form_help(form)}}
|
||||
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %} data-input-uniqid="{{ form.vars['uniqid'] }}"/>
|
||||
<div data-module="pick-postal-code" data-uniqid="{{ form.vars['uniqid'] }}"></div>
|
||||
{% endblock %}
|
||||
|
@ -95,6 +95,15 @@
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if entity_workflow.currentStep.destEmail|length > 0 %}
|
||||
<p><b>{{ 'workflow.An access key was also sent to those addresses'|trans }} :</b></p>
|
||||
<ul>
|
||||
{% for e in entity_workflow.currentStep.destEmail -%}
|
||||
<li><a href="mailto:{{ e|escape('html_attr') }}">{{ e }}</a></li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if entity_workflow.currentStep.destUserByAccessKey|length > 0 %}
|
||||
<p><b>{{ 'workflow.Those users are also granted to apply a transition by using an access key'|trans }} :</b></p>
|
||||
<ul>
|
||||
@ -103,6 +112,21 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if is_granted('CHILL_MAIN_WORKFLOW_LINK_SHOW', entity_workflow) %}
|
||||
<p><b>{{ 'workflow.This link grant any user to apply a transition'|trans }} :</b></p>
|
||||
|
||||
{% set link = absolute_url(path('chill_main_workflow_grant_access_by_key', {'id': entity_workflow.currentStep.id, 'accessKey': entity_workflow.currentStep.accessKey})) %}
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" readonly value="{{ link|e('html_attr') }}" class="form-control">
|
||||
<button class="btn btn-secondary" type="button" id="button-copy" onclick="navigator.clipboard.writeText('{{ link|e('html_attr') }}').then(() => { window.alert({{ ('"' ~ 'workflow.Access link copied'|trans ~ ' !"') |e('html_attr') }})});"><i class="fa fa-files-o"></i></button>
|
||||
<a class="btn btn-secondary" type="button" id="button-email"
|
||||
href="mailto:?body={{ ((('workflow.The workflow may be accssed through this link'|trans)~':')|e('url')) ~ '%0D%0A%0D%0A' ~ link|e('url') }}"><i class="fa fa-envelope"></i></a>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
@ -81,6 +81,15 @@
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if entity_workflow.currentStep.destEmail|length > 0 %}
|
||||
<p><b>{{ 'workflow.An access key was also sent to those addresses'|trans }} :</b></p>
|
||||
<ul>
|
||||
{% for e in entity_workflow.currentStep.destEmail -%}
|
||||
<li><a href="mailto:{{ e|escape('html_attr') }}">{{ e }}</a></li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if step.destUserByAccessKey|length > 0 %}
|
||||
<p><b>{{ 'workflow.Those users are also granted to apply a transition by using an access key'|trans }} :</b></p>
|
||||
<ul>
|
||||
|
@ -27,6 +27,8 @@ class EntityWorkflowVoter extends Voter
|
||||
|
||||
public const SEE = 'CHILL_MAIN_WORKFLOW_SEE';
|
||||
|
||||
public const SHOW_ENTITY_LINK = 'CHILL_MAIN_WORKFLOW_LINK_SHOW';
|
||||
|
||||
private EntityWorkflowManager $manager;
|
||||
|
||||
private Security $security;
|
||||
@ -80,6 +82,19 @@ class EntityWorkflowVoter extends Voter
|
||||
case self::DELETE:
|
||||
return $subject->getStep() === 'initial';
|
||||
|
||||
case self::SHOW_ENTITY_LINK:
|
||||
if ($subject->getStep() === 'initial') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentStep = $subject->getCurrentStepChained();
|
||||
|
||||
if ($currentStep->isFinal()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $currentStep->getPrevious()->getTransitionBy() === $this->security->getUser();
|
||||
|
||||
default:
|
||||
throw new UnexpectedValueException("attribute {$attribute} not supported");
|
||||
}
|
||||
@ -91,6 +106,7 @@ class EntityWorkflowVoter extends Voter
|
||||
self::SEE,
|
||||
self::CREATE,
|
||||
self::DELETE,
|
||||
self::SHOW_ENTITY_LINK,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -108,12 +108,9 @@ abstract class AbstractAggregatorTest extends KernelTestCase
|
||||
abstract public function getQueryBuilders();
|
||||
|
||||
/**
|
||||
* Compare aliases array before and after that aggregator alter query
|
||||
* Compare aliases array before and after that aggregator alter query.
|
||||
*
|
||||
* @dataProvider dataProviderAliasDidNotDisappears
|
||||
*
|
||||
* @param QueryBuilder $qb
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function testAliasDidNotDisappears(QueryBuilder $qb, array $data)
|
||||
|
@ -100,12 +100,9 @@ abstract class AbstractFilterTest extends KernelTestCase
|
||||
abstract public function getQueryBuilders();
|
||||
|
||||
/**
|
||||
* Compare aliases array before and after that filter alter query
|
||||
* Compare aliases array before and after that filter alter query.
|
||||
*
|
||||
* @dataProvider dataProviderAliasDidNotDisappears
|
||||
*
|
||||
* @param QueryBuilder $qb
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function testAliasDidNotDisappears(QueryBuilder $qb, array $data)
|
||||
|
@ -17,6 +17,7 @@ namespace Chill\MainBundle\Test;
|
||||
* **Usage : ** You must set up trait with `setUpTrait` before use
|
||||
* and use tearDownTrait after usage.
|
||||
*
|
||||
* @deprecated use @see{\Prophecy\PhpUnit\ProphecyTrait} instead
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @deprecated use @class{Prophecy\PhpUnit\ProphecyTrait} instead
|
||||
|
79
src/Bundle/ChillMainBundle/Tests/Doctrine/DQL/AgeTest.php
Normal file
79
src/Bundle/ChillMainBundle/Tests/Doctrine/DQL/AgeTest.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?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 DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class AgeTest extends KernelTestCase
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$this->entityManager = self::$container->get(EntityManagerInterface::class);
|
||||
}
|
||||
|
||||
public function generateQueries(): iterable
|
||||
{
|
||||
yield [
|
||||
'SELECT AGE(a.validFrom, a.validTo) FROM ' . Address::class . ' a',
|
||||
[],
|
||||
];
|
||||
|
||||
yield [
|
||||
'SELECT AGE(:date0, :date1) FROM ' . Address::class . ' a',
|
||||
[
|
||||
'date0' => new DateTimeImmutable('now'),
|
||||
'date1' => new DateTimeImmutable('2020-01-01'),
|
||||
],
|
||||
];
|
||||
|
||||
yield [
|
||||
'SELECT AGE(a.validFrom, :date1) FROM ' . Address::class . ' a',
|
||||
[
|
||||
'date1' => new DateTimeImmutable('now'),
|
||||
],
|
||||
];
|
||||
|
||||
yield [
|
||||
'SELECT AGE(:date0, a.validFrom) FROM ' . Address::class . ' a',
|
||||
[
|
||||
'date0' => new DateTimeImmutable('now'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider generateQueries
|
||||
*/
|
||||
public function testWorking(string $dql, array $args)
|
||||
{
|
||||
$dql = $this->entityManager->createQuery($dql)->setMaxResults(3);
|
||||
|
||||
foreach ($args as $key => $value) {
|
||||
$dql->setParameter($key, $value);
|
||||
}
|
||||
|
||||
$results = $dql->getResult();
|
||||
|
||||
$this->assertIsArray($results);
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
<?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 Form\Type;
|
||||
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Chill\MainBundle\Form\Type\DataTransformer\PostalCodeToIdTransformer;
|
||||
use Chill\MainBundle\Form\Type\PickPostalCodeType;
|
||||
use Chill\MainBundle\Repository\PostalCodeRepositoryInterface;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use ReflectionClass;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\PreloadedExtension;
|
||||
use Symfony\Component\Form\Test\TypeTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class PickPostalCodeTypeTest extends TypeTestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
public function testSubmitValidData(): void
|
||||
{
|
||||
$builder = $this->factory->createBuilder(FormType::class, ['postal_code' => null]);
|
||||
$builder->add('postal_code', PickPostalCodeType::class);
|
||||
$form = $builder->getForm();
|
||||
|
||||
$form->submit(['postal_code' => '1']);
|
||||
|
||||
$this->assertTrue($form->isSynchronized());
|
||||
|
||||
$this->assertEquals(1, $form['postal_code']->getData()->getId());
|
||||
}
|
||||
|
||||
protected function getExtensions()
|
||||
{
|
||||
$postalCodeRepository = $this->prophesize(PostalCodeRepositoryInterface::class);
|
||||
$postalCodeRepository->find(Argument::any())
|
||||
->will(static function ($args) {
|
||||
$postalCode = new PostalCode();
|
||||
$reflectionClass = new ReflectionClass($postalCode);
|
||||
$id = $reflectionClass->getProperty('id');
|
||||
$id->setAccessible(true);
|
||||
$id->setValue($postalCode, (int) $args[0]);
|
||||
|
||||
return $postalCode;
|
||||
});
|
||||
|
||||
$type = new PickPostalCodeType(
|
||||
new PostalCodeToIdTransformer(
|
||||
$postalCodeRepository->reveal()
|
||||
)
|
||||
);
|
||||
|
||||
return [
|
||||
new PreloadedExtension([$type], []),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
<?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 Form\Type;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Form\Type\ScopePickerType;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\PreloadedExtension;
|
||||
use Symfony\Component\Form\Test\TypeTestCase;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class ScopePickerTypeTest extends TypeTestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
public function testBuildOneScopeIsSuccessful()
|
||||
{
|
||||
$form = $this->factory->create(ScopePickerType::class, null, [
|
||||
'center' => new Center(),
|
||||
'role' => 'ONE_SCOPE',
|
||||
]);
|
||||
|
||||
$view = $form->createView();
|
||||
|
||||
$this->assertContains('hidden', $view['scope']->vars['block_prefixes']);
|
||||
}
|
||||
|
||||
public function testBuildThreeScopesIsSuccessful()
|
||||
{
|
||||
$form = $this->factory->create(ScopePickerType::class, null, [
|
||||
'center' => new Center(),
|
||||
'role' => 'THREE_SCOPE',
|
||||
]);
|
||||
|
||||
$view = $form->createView();
|
||||
|
||||
$this->assertContains('entity', $view['scope']->vars['block_prefixes']);
|
||||
}
|
||||
|
||||
public function testBuildTwoScopesIsSuccessful()
|
||||
{
|
||||
$form = $this->factory->create(ScopePickerType::class, null, [
|
||||
'center' => new Center(),
|
||||
'role' => 'TWO_SCOPE',
|
||||
]);
|
||||
|
||||
$view = $form->createView();
|
||||
|
||||
$this->assertContains('entity', $view['scope']->vars['block_prefixes']);
|
||||
}
|
||||
|
||||
protected function getExtensions()
|
||||
{
|
||||
$user = new User();
|
||||
$role1Scope = 'ONE_SCOPE';
|
||||
$role2Scope = 'TWO_SCOPE';
|
||||
$role3Scope = 'THREE_SCOPE';
|
||||
$scopeA = (new Scope())->setName(['fr' => 'scope a']);
|
||||
$scopeB = (new Scope())->setName(['fr' => 'scope b']);
|
||||
$scopeC = (new Scope())->setName(['fr' => 'scope b'])->setActive(false);
|
||||
|
||||
$authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class);
|
||||
$authorizationHelper->getReachableScopes($user, $role1Scope, Argument::any())
|
||||
->willReturn([$scopeA]);
|
||||
$authorizationHelper->getReachableScopes($user, $role2Scope, Argument::any())
|
||||
->willReturn([$scopeA, $scopeB]);
|
||||
$authorizationHelper->getReachableScopes($user, $role3Scope, Argument::any())
|
||||
->willReturn([$scopeA, $scopeB, $scopeC]);
|
||||
|
||||
$security = $this->prophesize(Security::class);
|
||||
$security->getUser()->willReturn($user);
|
||||
|
||||
$translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class);
|
||||
$translatableStringHelper->localize(Argument::type('array'))->will(
|
||||
static function ($args) { return $args[0]['fr']; }
|
||||
);
|
||||
|
||||
$type = new ScopePickerType(
|
||||
$authorizationHelper->reveal(),
|
||||
$security->reveal(),
|
||||
$translatableStringHelper->reveal()
|
||||
);
|
||||
|
||||
// add the mocks for creating EntityType
|
||||
$entityManager = DoctrineTestHelper::createTestEntityManager();
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->getClassMetadata(Scope::class)->willReturn($entityManager->getClassMetadata(Scope::class));
|
||||
$em->contains(Argument::type(Scope::class))->willReturn(true);
|
||||
$em->initializeObject(Argument::type(Scope::class))->will(static fn ($o) => $o);
|
||||
$emRevealed = $em->reveal();
|
||||
$managerRegistry = $this->prophesize(ManagerRegistry::class);
|
||||
$managerRegistry->getManager(Argument::any())->willReturn($emRevealed);
|
||||
$managerRegistry->getManagerForClass(Scope::class)->willReturn($emRevealed);
|
||||
|
||||
$entityType = $this->prophesize(EntityType::class);
|
||||
$entityType->getParent()->willReturn(ChoiceType::class);
|
||||
|
||||
return [
|
||||
new PreloadedExtension([$type], []),
|
||||
new DoctrineOrmExtension($managerRegistry->reveal()),
|
||||
];
|
||||
}
|
||||
}
|
@ -70,6 +70,7 @@ module.exports = function(encore, entries)
|
||||
encore.addEntry('mod_entity_workflow_subscribe', __dirname + '/Resources/public/module/entity-workflow-subscribe/index.js');
|
||||
encore.addEntry('mod_entity_workflow_pick', __dirname + '/Resources/public/module/entity-workflow-pick/index.js');
|
||||
encore.addEntry('mod_wopi_link', __dirname + '/Resources/public/module/wopi-link/index.js');
|
||||
encore.addEntry('mod_pick_postal_code', __dirname + '/Resources/public/module/pick-postal-code/index.js');
|
||||
|
||||
// Vue entrypoints
|
||||
encore.addEntry('vue_address', __dirname + '/Resources/public/vuejs/Address/index.js');
|
||||
|
@ -0,0 +1,33 @@
|
||||
<?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 Version20221010142417 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE scopes DROP active');
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Allow a scope to be desactivated';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE scopes ADD active BOOLEAN DEFAULT true NOT NULL');
|
||||
}
|
||||
}
|
@ -455,7 +455,6 @@ workflow:
|
||||
Delete workflow: Supprimer le workflow
|
||||
Steps is not waiting for transition. Maybe someone apply the transition before you ?: L'étape que vous cherchez a déjà été modifiée par un autre utilisateur. Peut-être quelqu'un a-t-il modifié cette étape avant vous ?
|
||||
You get access to this step: Vous avez acquis les droits pour appliquer une transition sur ce workflow.
|
||||
Those users are also granted to apply a transition by using an access key: Ces utilisateurs peuvent également valider cette étape, grâce à un lien d'accès
|
||||
dest by email: Liens d'autorisation par email
|
||||
dest by email help: Les adresses email mentionnées ici recevront un lien d'accès. Ce lien d'accès permettra à l'utilisateur de valider cette étape.
|
||||
Add an email: Ajouter une adresse email
|
||||
@ -467,6 +466,11 @@ workflow:
|
||||
Previous workflow transitionned help: Workflows où vous avez exécuté une action.
|
||||
For: Pour
|
||||
You must select a next step, pick another decision if no next steps are available: Il faut une prochaine étape. Choissisez une autre décision si nécessaire.
|
||||
An access key was also sent to those addresses: Un lien d'accès a été envoyé à ces addresses
|
||||
Those users are also granted to apply a transition by using an access key: Ces utilisateurs ont obtennu l'accès grâce au lien reçu par email
|
||||
Access link copied: Lien d'accès copié
|
||||
This link grant any user to apply a transition: Le lien d'accès suivant permet d'appliquer une transition
|
||||
The workflow may be accssed through this link: Une transition peut être appliquée sur ce workflow grâce au lien d'accès suivant
|
||||
|
||||
|
||||
Subscribe final: Recevoir une notification à l'étape finale
|
||||
|
@ -62,6 +62,7 @@ class UserRefEventSubscriber implements EventSubscriberInterface
|
||||
&& $period->getUser() !== $this->security->getUser()
|
||||
&& null !== $period->getUser()
|
||||
&& $period->getStep() !== AccompanyingPeriod::STEP_DRAFT
|
||||
&& !$period->isPreventUserIsChangedNotification()
|
||||
) {
|
||||
$this->generateNotificationToUser($period);
|
||||
}
|
||||
|
@ -11,7 +11,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Form\Type\PickPostalCodeType;
|
||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Repository\UserRepository;
|
||||
@ -92,12 +94,14 @@ class ReassignAccompanyingPeriodController extends AbstractController
|
||||
$form->handleRequest($request);
|
||||
|
||||
$userFrom = $form['user']->getData();
|
||||
$postalCodes = $form['postal_code']->getData() instanceof PostalCode ? [$form['postal_code']->getData()] : [];
|
||||
|
||||
$total = $this->accompanyingPeriodACLAwareRepository->countByUserOpenedAccompanyingPeriod($userFrom);
|
||||
$paginator = $this->paginatorFactory->create($total);
|
||||
$periods = $this->accompanyingPeriodACLAwareRepository
|
||||
->findByUserOpenedAccompanyingPeriod(
|
||||
->findByUserAndPostalCodesOpenedAccompanyingPeriod(
|
||||
$userFrom,
|
||||
$postalCodes,
|
||||
['openingDate' => 'ASC'],
|
||||
$paginator->getItemsPerPage(),
|
||||
$paginator->getCurrentPageFirstItemNumber()
|
||||
@ -123,7 +127,7 @@ class ReassignAccompanyingPeriodController extends AbstractController
|
||||
$period = $this->courseRepository->find($periodId);
|
||||
|
||||
if ($period->getUser() === $userFrom) {
|
||||
$period->setUser($userTo);
|
||||
$period->setUser($userTo, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +152,9 @@ class ReassignAccompanyingPeriodController extends AbstractController
|
||||
{
|
||||
$data = [
|
||||
'user' => null,
|
||||
'postal_code' => null,
|
||||
];
|
||||
|
||||
$builder = $this->formFactory->createBuilder(FormType::class, $data, [
|
||||
'method' => 'get', 'csrf_protection' => false, ]);
|
||||
|
||||
@ -158,12 +164,17 @@ class ReassignAccompanyingPeriodController extends AbstractController
|
||||
'label' => 'reassign.Current user',
|
||||
'required' => false,
|
||||
'help' => 'reassign.Choose a user and click on "Filter" to apply',
|
||||
])
|
||||
->add('postal_code', PickPostalCodeType::class, [
|
||||
'label' => 'reassign.Filter by postal code',
|
||||
'required' => false,
|
||||
'help' => 'reassign.Filter course which are located inside a postal code',
|
||||
]);
|
||||
|
||||
return $builder->getForm();
|
||||
}
|
||||
|
||||
private function buildReassignForm(array $periodIds, ?User $userFrom): FormInterface
|
||||
private function buildReassignForm(array $periodIds, ?User $userFrom = null): FormInterface
|
||||
{
|
||||
$defaultData = [
|
||||
'userFrom' => $userFrom,
|
||||
|
@ -13,6 +13,7 @@ namespace Chill\PersonBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Serializer\Model\Collection;
|
||||
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
|
||||
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
@ -39,9 +40,11 @@ class SocialWorkEvaluationApiController extends AbstractController
|
||||
*/
|
||||
public function listEvaluationBySocialAction(SocialAction $action): Response
|
||||
{
|
||||
$pagination = $this->paginatorFactory->create($action->getEvaluations()->count());
|
||||
$evaluations = $action->getEvaluations()->filter(static fn (Evaluation $eval) => $eval->isActive());
|
||||
|
||||
$evaluations = $action->getEvaluations()->slice(
|
||||
$pagination = $this->paginatorFactory->create($evaluations->count());
|
||||
|
||||
$evaluations = $evaluations->slice(
|
||||
$pagination->getCurrentPageFirstItemNumber(),
|
||||
$pagination->getItemsPerPage()
|
||||
);
|
||||
|
@ -22,6 +22,7 @@ use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodLocationHistory;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodStepHistory;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
|
||||
@ -265,6 +266,8 @@ class AccompanyingPeriod implements
|
||||
*/
|
||||
private ?Comment $pinnedComment = null;
|
||||
|
||||
private bool $preventUserIsChangedNotification = false;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text")
|
||||
* @Groups({"read", "write"})
|
||||
@ -334,6 +337,12 @@ class AccompanyingPeriod implements
|
||||
*/
|
||||
private string $step = self::STEP_DRAFT;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=AccompanyingPeriodStepHistory::class,
|
||||
* mappedBy="period", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||
*/
|
||||
private Collection $stepHistories;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime", nullable=true, options={"default": NULL})
|
||||
*/
|
||||
@ -388,7 +397,6 @@ class AccompanyingPeriod implements
|
||||
*/
|
||||
public function __construct(?DateTime $dateOpening = null)
|
||||
{
|
||||
$this->setOpeningDate($dateOpening ?? new DateTime('now'));
|
||||
$this->participations = new ArrayCollection();
|
||||
$this->scopes = new ArrayCollection();
|
||||
$this->socialIssues = new ArrayCollection();
|
||||
@ -397,6 +405,8 @@ class AccompanyingPeriod implements
|
||||
$this->resources = new ArrayCollection();
|
||||
$this->userHistories = new ArrayCollection();
|
||||
$this->locationHistories = new ArrayCollection();
|
||||
$this->stepHistories = new ArrayCollection();
|
||||
$this->setOpeningDate($dateOpening ?? new DateTime('now'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -964,6 +974,11 @@ class AccompanyingPeriod implements
|
||||
return $this->step;
|
||||
}
|
||||
|
||||
public function getStepHistories(): Collection
|
||||
{
|
||||
return $this->stepHistories;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
@ -1048,6 +1063,11 @@ class AccompanyingPeriod implements
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isPreventUserIsChangedNotification(): bool
|
||||
{
|
||||
return $this->preventUserIsChangedNotification;
|
||||
}
|
||||
|
||||
public function isRequestorAnonymous(): bool
|
||||
{
|
||||
return $this->requestorAnonymous;
|
||||
@ -1227,7 +1247,11 @@ class AccompanyingPeriod implements
|
||||
*/
|
||||
public function setOpeningDate($openingDate)
|
||||
{
|
||||
$this->openingDate = $openingDate;
|
||||
if ($this->openingDate !== $openingDate) {
|
||||
$this->openingDate = $openingDate;
|
||||
|
||||
$this->ensureStepContinuity();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -1326,6 +1350,14 @@ class AccompanyingPeriod implements
|
||||
$this->bootPeriod();
|
||||
}
|
||||
|
||||
if (self::STEP_DRAFT !== $this->step && $previous !== $step) {
|
||||
// we create a new history
|
||||
$history = new AccompanyingPeriodStepHistory();
|
||||
$history->setStep($this->step)->setStartDate(new DateTimeImmutable('now'));
|
||||
|
||||
$this->addStepHistory($history);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -1343,11 +1375,12 @@ class AccompanyingPeriod implements
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setUser(?User $user): self
|
||||
public function setUser(?User $user, bool $preventNotification = false): self
|
||||
{
|
||||
if ($this->user !== $user) {
|
||||
$this->userPrevious = $this->user;
|
||||
$this->userIsChanged = true;
|
||||
$this->preventUserIsChangedNotification = $preventNotification;
|
||||
|
||||
foreach ($this->userHistories as $history) {
|
||||
if (null === $history->getEndDate()) {
|
||||
@ -1365,6 +1398,17 @@ class AccompanyingPeriod implements
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function addStepHistory(AccompanyingPeriodStepHistory $stepHistory): self
|
||||
{
|
||||
if (!$this->stepHistories->contains($stepHistory)) {
|
||||
$this->stepHistories[] = $stepHistory;
|
||||
$stepHistory->setPeriod($this);
|
||||
$this->ensureStepContinuity();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function bootPeriod(): void
|
||||
{
|
||||
// first location history
|
||||
@ -1376,6 +1420,43 @@ class AccompanyingPeriod implements
|
||||
$this->addLocationHistory($locationHistory);
|
||||
}
|
||||
|
||||
private function ensureStepContinuity(): void
|
||||
{
|
||||
// ensure continuity of histories
|
||||
$criteria = new Criteria();
|
||||
$criteria->orderBy(['startDate' => Criteria::ASC, 'id' => Criteria::ASC]);
|
||||
|
||||
/** @var Iterator $steps */
|
||||
$steps = $this->getStepHistories()->matching($criteria)->getIterator();
|
||||
$steps->rewind();
|
||||
|
||||
// we set the start date of the first step as the opening date, only if it is
|
||||
// not greater than the end date
|
||||
/** @var AccompanyingPeriodStepHistory $current */
|
||||
$current = $steps->current();
|
||||
|
||||
if (null === $current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->getOpeningDate()->format('Y-m-d') !== $current->getStartDate()->format('Y-m-d')
|
||||
&& ($this->getOpeningDate() <= $current->getEndDate() || null === $current->getEndDate())) {
|
||||
$current->setStartDate(DateTimeImmutable::createFromMutable($this->getOpeningDate()));
|
||||
}
|
||||
|
||||
// then we set all the end date to the start date of the next one
|
||||
do {
|
||||
/** @var AccompanyingPeriodStepHistory $current */
|
||||
$current = $steps->current();
|
||||
$steps->next();
|
||||
|
||||
if ($steps->valid()) {
|
||||
$next = $steps->current();
|
||||
$current->setEndDate($next->getStartDate());
|
||||
}
|
||||
} while ($steps->valid());
|
||||
}
|
||||
|
||||
private function setRequestorPerson(?Person $requestorPerson = null): self
|
||||
{
|
||||
$this->requestorPerson = $requestorPerson;
|
||||
|
@ -0,0 +1,115 @@
|
||||
<?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\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table("chill_person_accompanying_period_step_history")
|
||||
*/
|
||||
class AccompanyingPeriodStepHistory implements TrackCreationInterface, TrackUpdateInterface
|
||||
{
|
||||
use TrackCreationTrait;
|
||||
|
||||
use TrackUpdateTrait;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
|
||||
*/
|
||||
private ?DateTimeImmutable $endDate = null;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class)
|
||||
*/
|
||||
private AccompanyingPeriod $period;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="date_immutable")
|
||||
*/
|
||||
private ?DateTimeImmutable $startDate = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", nullable=false)
|
||||
*/
|
||||
private string $step;
|
||||
|
||||
public function getEndDate(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getPeriod(): AccompanyingPeriod
|
||||
{
|
||||
return $this->period;
|
||||
}
|
||||
|
||||
public function getStartDate(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
public function getStep(): string
|
||||
{
|
||||
return $this->step;
|
||||
}
|
||||
|
||||
public function setEndDate(?DateTimeImmutable $endDate): self
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal use AccompanyingPeriod::addLocationHistory
|
||||
*/
|
||||
public function setPeriod(AccompanyingPeriod $period): self
|
||||
{
|
||||
$this->period = $period;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setStartDate(?DateTimeImmutable $startDate): self
|
||||
{
|
||||
$this->startDate = $startDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setStep(string $step): AccompanyingPeriodStepHistory
|
||||
{
|
||||
$this->step = $step;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ class Comment implements TrackCreationInterface, TrackUpdateInterface
|
||||
* inversedBy="comments")
|
||||
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
private ?AccompanyingPeriod $accompanyingPeriod;
|
||||
private ?AccompanyingPeriod $accompanyingPeriod = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text")
|
||||
|
@ -26,6 +26,11 @@ use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
*/
|
||||
class Evaluation
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="boolean", nullable=false, options={"default": true})
|
||||
*/
|
||||
private bool $active = true;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="dateinterval", nullable=true, options={"default": null})
|
||||
* @Serializer\Groups({"read"})
|
||||
@ -72,6 +77,9 @@ class Evaluation
|
||||
$this->socialActions = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal do use @see{SocialAction::addEvaluation}
|
||||
*/
|
||||
public function addSocialAction(SocialAction $socialAction): self
|
||||
{
|
||||
if (!$this->socialActions->contains($socialAction)) {
|
||||
@ -111,6 +119,16 @@ class Evaluation
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*
|
||||
* @internal do use @see{SocialAction::removeEvaluation}
|
||||
*/
|
||||
public function removeSocialAction(SocialAction $socialAction): self
|
||||
{
|
||||
if ($this->socialActions->contains($socialAction)) {
|
||||
@ -120,6 +138,13 @@ class Evaluation
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setActive(bool $active): Evaluation
|
||||
{
|
||||
$this->active = $active;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDelay(?DateInterval $delay): self
|
||||
{
|
||||
$this->delay = $delay;
|
||||
|
@ -112,6 +112,7 @@ class SocialAction
|
||||
{
|
||||
if (!$this->evaluations->contains($evaluation)) {
|
||||
$this->evaluations[] = $evaluation;
|
||||
$evaluation->addSocialAction($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
@ -332,6 +333,7 @@ class SocialAction
|
||||
public function removeEvaluation(Evaluation $evaluation): self
|
||||
{
|
||||
$this->evaluations->removeElement($evaluation);
|
||||
$evaluation->removeSocialAction($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -13,12 +13,21 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
|
||||
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use LogicException;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
final class DurationAggregator implements AggregatorInterface
|
||||
{
|
||||
private const CHOICES = [
|
||||
'month',
|
||||
'week',
|
||||
'day',
|
||||
];
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(TranslatorInterface $translator)
|
||||
@ -33,19 +42,31 @@ final class DurationAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$qb->addSelect(
|
||||
'
|
||||
(acp.closingDate - acp.openingDate +15) *12/365
|
||||
AS duration_aggregator'
|
||||
);
|
||||
switch ($data['precision']) {
|
||||
case 'day':
|
||||
$qb->addSelect('(COALESCE(acp.closingDate, :now) - acp.openingDate) AS duration_aggregator');
|
||||
|
||||
// TODO Pour avoir un interval plus précis (nécessaire ?):
|
||||
// adapter la fonction extract pour pouvoir l'utiliser avec des intervals: extract(month from interval)
|
||||
// et ajouter une fonction custom qui calcule plus précisément les intervals, comme doctrineum/date-interval
|
||||
// https://packagist.org/packages/doctrineum/date-interval#3.1.0 (mais composer fait un conflit de dépendance)
|
||||
break;
|
||||
|
||||
$qb->addGroupBy('duration_aggregator');
|
||||
$qb->addOrderBy('duration_aggregator');
|
||||
case 'week':
|
||||
$qb->addSelect('(COALESCE(acp.closingDate, :now) - acp.openingDate) / 7 AS duration_aggregator');
|
||||
|
||||
break;
|
||||
|
||||
case 'month':
|
||||
$qb->addSelect('(EXTRACT (MONTH FROM AGE(COALESCE(acp.closingDate, :now), acp.openingDate)) * 12 +
|
||||
EXTRACT (MONTH FROM AGE(COALESCE(acp.closingDate, :now), acp.openingDate))) AS duration_aggregator');
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new LogicException('precision not supported: ' . $data['precision']);
|
||||
}
|
||||
|
||||
$qb
|
||||
->setParameter('now', new DateTimeImmutable('now'))
|
||||
->addGroupBy('duration_aggregator')
|
||||
->addOrderBy('duration_aggregator');
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -55,25 +76,27 @@ final class DurationAggregator implements AggregatorInterface
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// no form
|
||||
$builder->add('precision', ChoiceType::class, [
|
||||
'choices' => array_combine(self::CHOICES, self::CHOICES),
|
||||
'label' => 'export.aggregator.course.duration.Precision',
|
||||
'choice_label' => static fn (string $c) => 'export.aggregator.course.duration.' . $c,
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
return function ($value): ?string {
|
||||
return static function ($value) use ($data) {
|
||||
if ('_header' === $value) {
|
||||
return 'Rounded month duration';
|
||||
return 'export.aggregator.course.duration.' . $data['precision'];
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return $this->translator->trans('current duration'); // when closingDate is null
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (0 === $value) {
|
||||
return $this->translator->trans('duration 0 month');
|
||||
}
|
||||
|
||||
return $value . $this->translator->trans(' months');
|
||||
return $value;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -12,15 +12,20 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
|
||||
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Repository\UserRepository;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
final class ReferrerAggregator implements AggregatorInterface
|
||||
{
|
||||
private const A = 'acp_ref_agg_uhistory';
|
||||
|
||||
private const P = 'acp_ref_agg_date';
|
||||
|
||||
private UserRender $userRender;
|
||||
|
||||
private UserRepository $userRepository;
|
||||
@ -40,12 +45,23 @@ final class ReferrerAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acpuser', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('acp.user', 'acpuser');
|
||||
}
|
||||
|
||||
$qb->addSelect('acpuser.id AS referrer_aggregator');
|
||||
$qb->addGroupBy('referrer_aggregator');
|
||||
$qb
|
||||
->addSelect('IDENTITY(' . self::A . '.user) AS referrer_aggregator')
|
||||
->addGroupBy('referrer_aggregator')
|
||||
->leftJoin('acp.userHistories', self::A)
|
||||
->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull(self::A),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte(self::A . '.startDate', ':' . self::P),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull(self::A . '.endDate'),
|
||||
$qb->expr()->gt(self::A . '.endDate', ':' . self::P)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->setParameter(self::P, $data['date_calc']);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -55,7 +71,13 @@ final class ReferrerAggregator implements AggregatorInterface
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// no form
|
||||
$builder
|
||||
->add('date_calc', ChillDateType::class, [
|
||||
'input' => 'datetime_immutable',
|
||||
'data' => new DateTimeImmutable('now'),
|
||||
'label' => 'export.aggregator.course.by_referrer.Computation date for referrer',
|
||||
'required' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
|
@ -12,20 +12,21 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
|
||||
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
//use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use DateTime;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use LogicException;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function in_array;
|
||||
|
||||
final class StepAggregator implements AggregatorInterface //, FilterInterface
|
||||
final class StepAggregator implements AggregatorInterface
|
||||
{
|
||||
private const A = 'acpstephistories';
|
||||
|
||||
private const P = 'acp_step_agg_date';
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(
|
||||
@ -41,30 +42,26 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$qb->addSelect('acp.step AS step_aggregator');
|
||||
$qb->addGroupBy('step_aggregator');
|
||||
|
||||
/*
|
||||
// add date in where clause
|
||||
$where = $qb->getDQLPart('where');
|
||||
|
||||
$clause = $qb->expr()->andX(
|
||||
$qb->expr()->lte('acp.openingDate', ':ondate'),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->gt('acp.closingDate', ':ondate'),
|
||||
$qb->expr()->isNull('acp.closingDate')
|
||||
)
|
||||
);
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
if (!in_array(self::A, $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('acp.stepHistories', self::A);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE);
|
||||
*/
|
||||
$qb
|
||||
->addSelect(self::A . '.step AS step_aggregator')
|
||||
->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull(self::A . '.step'),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte(self::A . '.startDate', ':' . self::P),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull(self::A . '.endDate'),
|
||||
$qb->expr()->lt(self::A . '.endDate', ':' . self::P)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->setParameter(self::P, $data['on_date'])
|
||||
->addGroupBy('step_aggregator');
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -95,8 +92,11 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface
|
||||
case '_header':
|
||||
return 'Step';
|
||||
|
||||
case null:
|
||||
return '';
|
||||
|
||||
default:
|
||||
throw new LogicException(sprintf('The value %s is not valid', $value));
|
||||
return $value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function in_array;
|
||||
|
||||
final class CountryOfBirthAggregator implements AggregatorInterface, ExportElementValidatedInterface
|
||||
{
|
||||
@ -83,7 +84,9 @@ final class CountryOfBirthAggregator implements AggregatorInterface, ExportEleme
|
||||
. ' is not known.');
|
||||
}
|
||||
|
||||
$qb->leftJoin('person.countryOfBirth', 'countryOfBirth');
|
||||
if (!in_array('countryOfBirth', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('person.countryOfBirth', 'countryOfBirth');
|
||||
}
|
||||
|
||||
// add group by
|
||||
$qb->addGroupBy('country_of_birth_aggregator');
|
||||
|
@ -99,6 +99,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface
|
||||
$qb = $this->repository->createQueryBuilder('acp');
|
||||
|
||||
$qb
|
||||
->andWhere('acp.step != :count_acp_step')
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part
|
||||
@ -107,6 +108,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface
|
||||
'
|
||||
)
|
||||
)
|
||||
->setParameter('count_acp_step', AccompanyingPeriod::STEP_DRAFT)
|
||||
->setParameter('authorized_centers', $centers);
|
||||
|
||||
$qb->select('COUNT(DISTINCT acp.id) AS export_result');
|
||||
|
@ -50,9 +50,6 @@ class AdministrativeLocationFilter implements FilterInterface
|
||||
{
|
||||
$builder->add('accepted_locations', PickUserLocationType::class, [
|
||||
'label' => 'Accepted locations',
|
||||
'label_attr' => [
|
||||
//'class' => 'd-none'
|
||||
],
|
||||
'multiple' => true,
|
||||
]);
|
||||
}
|
||||
|
@ -1,90 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
class CurrentUserJobFilter implements FilterInterface
|
||||
{
|
||||
private Security $security;
|
||||
|
||||
private TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
public function __construct(
|
||||
TranslatableStringHelper $translatableStringHelper,
|
||||
Security $security
|
||||
) {
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->security = $security;
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$where = $qb->getDQLPart('where');
|
||||
$clause = $qb->expr()->eq('acp.job', ':userjob');
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter('userjob', $this->getUserJob());
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ACP_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
return [
|
||||
'Filtered by user job: only %job%', [
|
||||
'%job%' => $this->translatableStringHelper->localize(
|
||||
$this->getUserJob()->getLabel()
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Filter by user job';
|
||||
}
|
||||
|
||||
private function getUserJob(): UserJob
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->security->getUser();
|
||||
|
||||
return $user->getUserJob();
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
|
||||
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use function in_array;
|
||||
|
||||
class CurrentUserScopeFilter implements FilterInterface
|
||||
{
|
||||
private Security $security;
|
||||
|
||||
private TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
public function __construct(
|
||||
TranslatableStringHelper $translatableStringHelper,
|
||||
Security $security
|
||||
) {
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->security = $security;
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acpscope', $qb->getAllAliases(), true)) {
|
||||
$qb->join('acp.scopes', 'acpscope');
|
||||
}
|
||||
|
||||
$where = $qb->getDQLPart('where');
|
||||
$clause = $qb->expr()->eq('acpscope.id', ':userscope');
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter('userscope', $this->getUserMainScope());
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ACP_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
return [
|
||||
'Filtered by user main scope: only %scope%', [
|
||||
'%scope%' => $this->translatableStringHelper->localize(
|
||||
$this->getUserMainScope()->getName()
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Filter by user scope';
|
||||
}
|
||||
|
||||
private function getUserMainScope(): Scope
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->security->getUser();
|
||||
|
||||
return $user->getMainScope();
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Chill\PersonBundle\Repository\SocialWork\EvaluationRepositoryInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@ -23,11 +23,15 @@ use function in_array;
|
||||
|
||||
class EvaluationFilter implements FilterInterface
|
||||
{
|
||||
private EvaluationRepositoryInterface $evaluationRepository;
|
||||
|
||||
private TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
public function __construct(
|
||||
EvaluationRepositoryInterface $evaluationRepository,
|
||||
TranslatableStringHelper $translatableStringHelper
|
||||
) {
|
||||
$this->evaluationRepository = $evaluationRepository;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
@ -50,16 +54,8 @@ class EvaluationFilter implements FilterInterface
|
||||
$qb->join('workeval.evaluation', 'eval');
|
||||
}
|
||||
|
||||
$where = $qb->getDQLPart('where');
|
||||
$clause = $qb->expr()->in('eval.id', ':evaluations');
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->andWhere($clause);
|
||||
$qb->setParameter('evaluations', $data['accepted_evaluations']);
|
||||
}
|
||||
|
||||
@ -72,11 +68,13 @@ class EvaluationFilter implements FilterInterface
|
||||
{
|
||||
$builder->add('accepted_evaluations', EntityType::class, [
|
||||
'class' => Evaluation::class,
|
||||
'choices' => $this->evaluationRepository->findAllActive(),
|
||||
'choice_label' => function (Evaluation $ev) {
|
||||
return $this->translatableStringHelper->localize($ev->getTitle());
|
||||
},
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
'expanded' => false,
|
||||
'attr' => ['class' => 'select2'],
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -11,17 +11,23 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class ReferrerFilter implements FilterInterface
|
||||
{
|
||||
private const A = 'acp_referrer_filter_uhistory';
|
||||
|
||||
private const P = 'acp_referrer_filter_date';
|
||||
|
||||
private const PU = 'acp_referrer_filter_users';
|
||||
|
||||
private UserRender $userRender;
|
||||
|
||||
public function __construct(UserRender $userRender)
|
||||
@ -36,17 +42,22 @@ class ReferrerFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$where = $qb->getDQLPart('where');
|
||||
$clause = $qb->expr()->in('acp.user', ':referrers');
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter('referrers', $data['accepted_referrers']);
|
||||
$qb
|
||||
->join('acp.userHistories', self::A)
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte(self::A . '.startDate', ':' . self::P),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull(self::A . '.endDate'),
|
||||
$qb->expr()->gt(self::A . '.endDate', ':' . self::P)
|
||||
)
|
||||
)
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->in(self::A . '.user', ':' . self::PU)
|
||||
)
|
||||
->setParameter(self::PU, $data['accepted_referrers'])
|
||||
->setParameter(self::P, $data['date_calc']);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@ -56,14 +67,16 @@ class ReferrerFilter implements FilterInterface
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('accepted_referrers', EntityType::class, [
|
||||
'class' => User::class,
|
||||
'choice_label' => function (User $u) {
|
||||
return $this->userRender->renderString($u, []);
|
||||
},
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
$builder
|
||||
->add('accepted_referrers', PickUserDynamicType::class, [
|
||||
'multiple' => true,
|
||||
])
|
||||
->add('date_calc', ChillDateType::class, [
|
||||
'input' => 'datetime_immutable',
|
||||
'data' => new DateTimeImmutable('now'),
|
||||
'label' => 'export.filter.course.by_referrer.Computation date for referrer',
|
||||
'required' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
|
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
class UserJobFilter implements FilterInterface
|
||||
{
|
||||
private const A = 'acp_ujob_filter_uhistory';
|
||||
|
||||
private const AU = 'acp_ujob_filter_uhistory_user';
|
||||
|
||||
private const P = 'acp_ujob_filter_date';
|
||||
|
||||
private const PJ = 'acp_ujob_filter_job';
|
||||
|
||||
private Security $security;
|
||||
|
||||
private TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
private UserJobRepositoryInterface $userJobRepository;
|
||||
|
||||
public function __construct(
|
||||
Security $security,
|
||||
TranslatableStringHelper $translatableStringHelper,
|
||||
UserJobRepositoryInterface $userJobRepository
|
||||
) {
|
||||
$this->security = $security;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->userJobRepository = $userJobRepository;
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$qb
|
||||
->join('acp.userHistories', self::A)
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte(self::A . '.startDate', ':' . self::P),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull(self::A . '.endDate'),
|
||||
$qb->expr()->gt(self::A . '.endDate', ':' . self::P)
|
||||
)
|
||||
)
|
||||
)
|
||||
->setParameter(self::P, $data['date_calc'])
|
||||
->join(self::A . '.user', self::AU)
|
||||
->andWhere(
|
||||
$qb->expr()->in(self::AU . '.userJob', ':' . self::PJ)
|
||||
)
|
||||
->setParameter(self::PJ, $data['jobs']);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ACP_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder
|
||||
->add('jobs', EntityType::class, [
|
||||
'class' => UserJob::class,
|
||||
'choices' => $this->userJobRepository->findAllActive(),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
'choice_label' => fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
|
||||
'label' => 'Job',
|
||||
])
|
||||
->add('date_calc', ChillDateType::class, [
|
||||
'input' => 'datetime_immutable',
|
||||
'data' => new DateTimeImmutable('now'),
|
||||
'label' => 'export.filter.course.by_user_scope.Computation date for referrer',
|
||||
'required' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
return [
|
||||
'Filtered by user job: only %job%', [
|
||||
'%job%' => implode(
|
||||
', ',
|
||||
array_map(
|
||||
fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
|
||||
$data['jobs']->toArray()
|
||||
)
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Filter by user job';
|
||||
}
|
||||
|
||||
private function getUserJob(): UserJob
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->security->getUser();
|
||||
|
||||
return $user->getUserJob();
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
|
||||
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
class UserScopeFilter implements FilterInterface
|
||||
{
|
||||
private const A = 'acp_uscope_filter_uhistory';
|
||||
|
||||
private const AU = 'acp_uscope_filter_uhistory_user';
|
||||
|
||||
private const P = 'acp_uscope_filter_date';
|
||||
|
||||
private const PS = 'acp_uscope_filter_scopes';
|
||||
|
||||
private ScopeRepositoryInterface $scopeRepository;
|
||||
|
||||
private Security $security;
|
||||
|
||||
private TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
public function __construct(
|
||||
ScopeRepositoryInterface $scopeRepository,
|
||||
Security $security,
|
||||
TranslatableStringHelper $translatableStringHelper
|
||||
) {
|
||||
$this->scopeRepository = $scopeRepository;
|
||||
$this->security = $security;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$qb
|
||||
->join('acp.userHistories', self::A)
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte(self::A . '.startDate', ':' . self::P),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull(self::A . '.endDate'),
|
||||
$qb->expr()->gt(self::A . '.endDate', ':' . self::P)
|
||||
)
|
||||
)
|
||||
)
|
||||
->setParameter(self::P, $data['date_calc'])
|
||||
->join(self::A . '.user', self::AU)
|
||||
->andWhere(
|
||||
$qb->expr()->in(self::AU . '.mainScope', ':' . self::PS)
|
||||
)
|
||||
->setParameter(self::PS, $data['scopes']);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ACP_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,
|
||||
])
|
||||
->add('date_calc', ChillDateType::class, [
|
||||
'input' => 'datetime_immutable',
|
||||
'data' => new DateTimeImmutable('now'),
|
||||
'label' => 'export.filter.course.by_user_scope.Computation date for referrer',
|
||||
'required' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
return [
|
||||
'Filtered by user main scope: only %scope%', [
|
||||
'%scope%' => implode(
|
||||
', ',
|
||||
array_map(
|
||||
fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
|
||||
$data['scopes']->toArray()
|
||||
)
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Filter by user scope';
|
||||
}
|
||||
|
||||
private function getUserMainScope(): Scope
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->security->getUser();
|
||||
|
||||
return $user->getMainScope();
|
||||
}
|
||||
}
|
@ -35,7 +35,7 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface
|
||||
$where = $qb->getDQLPart('where');
|
||||
|
||||
$min = null !== $data['min_age'] ? $data['min_age'] : 0;
|
||||
$max = null !== $data['max_age'] ? $data['max_age'] : 3000;
|
||||
$max = null !== $data['max_age'] ? $data['max_age'] : 150;
|
||||
$calc = $data['date_calc'];
|
||||
|
||||
$minDate = $calc->sub(new DateInterval('P' . $max . 'Y'));
|
||||
|
@ -12,12 +12,9 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Export\Filter\PersonFilters;
|
||||
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Entity\MaritalStatus;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
|
||||
class MaritalStatusFilter implements FilterInterface
|
||||
@ -37,25 +34,10 @@ class MaritalStatusFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
|
||||
{
|
||||
$where = $qb->getDQLPart('where');
|
||||
|
||||
$clause = $qb->expr()->andX(
|
||||
$qb->expr()->in('person.maritalStatus', ':maritalStatus'),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->eq('person.maritalStatusDate', ':calc_date'),
|
||||
$qb->expr()->isNull('person.maritalStatusDate')
|
||||
)
|
||||
$qb->andWhere(
|
||||
$qb->expr()->in('person.maritalStatus', ':maritalStatus')
|
||||
);
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter('maritalStatus', $data['maritalStatus']);
|
||||
$qb->setParameter('calc_date', $data['calc_date']);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
@ -75,11 +57,6 @@ class MaritalStatusFilter implements FilterInterface
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
|
||||
$builder->add('calc_date', ChillDateType::class, [
|
||||
'label' => 'Marital status at this time',
|
||||
'data' => new DateTime(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
|
@ -16,6 +16,7 @@ use Chill\MainBundle\Form\Type\TranslatableStringFormType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\UrlType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
@ -52,6 +53,14 @@ class EvaluationType extends AbstractType
|
||||
->add('notificationDelay', DateIntervalType::class, [
|
||||
'label' => 'evaluation.notificationDelay',
|
||||
'required' => false,
|
||||
])
|
||||
->add('active', ChoiceType::class, [
|
||||
'label' => 'active',
|
||||
'choices' => [
|
||||
'active' => true,
|
||||
'inactive' => false,
|
||||
],
|
||||
'required' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\MainBundle\Entity\Location;
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
@ -19,10 +21,14 @@ use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
||||
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use DateTime;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use function count;
|
||||
@ -49,7 +55,12 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
|
||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
||||
}
|
||||
|
||||
public function buildQueryOpenedAccompanyingCourseByUser(?User $user)
|
||||
/**
|
||||
* @param array|PostalCode[]
|
||||
*
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
public function buildQueryOpenedAccompanyingCourseByUser(?User $user, array $postalCodes = [])
|
||||
{
|
||||
$qb = $this->accompanyingPeriodRepository->createQueryBuilder('ap');
|
||||
|
||||
@ -65,6 +76,37 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
|
||||
->setParameter('now', new DateTime('now'))
|
||||
->setParameter('draft', AccompanyingPeriod::STEP_DRAFT);
|
||||
|
||||
if ([] !== $postalCodes) {
|
||||
$qb->join('ap.locationHistories', 'location_history')
|
||||
->leftJoin(PersonHouseholdAddress::class, 'person_address', Join::WITH, 'IDENTITY(location_history.personLocation) = IDENTITY(person_address.person)')
|
||||
->join(
|
||||
Address::class,
|
||||
'address',
|
||||
Join::WITH,
|
||||
'COALESCE(IDENTITY(location_history.addressLocation), IDENTITY(person_address.address)) = address.id'
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('person_address'),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte('person_address.validFrom', ':now'),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('person_address.validTo'),
|
||||
$qb->expr()->lt('person_address.validTo', ':now')
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->isNull('location_history.endDate')
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->in('address.postcode', ':postal_codes')
|
||||
)
|
||||
->setParameter('now', new DateTimeImmutable('now'), Types::DATE_IMMUTABLE)
|
||||
->setParameter('postal_codes', $postalCodes);
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
@ -77,6 +119,18 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
|
||||
return $qb->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function countByUserAndPostalCodesOpenedAccompanyingPeriod(?User $user, array $postalCodes): int
|
||||
{
|
||||
if (null === $user) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->buildQueryOpenedAccompanyingCourseByUser($user, $postalCodes)
|
||||
->select('COUNT(ap)')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function countByUserOpenedAccompanyingPeriod(?User $user): int
|
||||
{
|
||||
if (null === $user) {
|
||||
@ -158,6 +212,24 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
public function findByUserAndPostalCodesOpenedAccompanyingPeriod(?User $user, array $postalCodes, array $orderBy = [], int $limit = 0, int $offset = 50): array
|
||||
{
|
||||
if (null === $user) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$qb = $this->buildQueryOpenedAccompanyingCourseByUser($user);
|
||||
|
||||
$qb->setFirstResult($offset)
|
||||
->setMaxResults($limit);
|
||||
|
||||
foreach ($orderBy as $field => $direction) {
|
||||
$qb->addOrderBy('ap.' . $field, $direction);
|
||||
}
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|AccompanyingPeriod[]
|
||||
*/
|
||||
|
@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
@ -25,6 +26,11 @@ interface AccompanyingPeriodACLAwareRepositoryInterface
|
||||
*/
|
||||
public function countByUnDispatched(array $jobs, array $services, array $administrativeLocations): int;
|
||||
|
||||
/**
|
||||
* @param array|PostalCode[] $postalCodes
|
||||
*/
|
||||
public function countByUserAndPostalCodesOpenedAccompanyingPeriod(?User $user, array $postalCodes): int;
|
||||
|
||||
public function countByUserOpenedAccompanyingPeriod(?User $user): int;
|
||||
|
||||
public function findByPerson(
|
||||
@ -43,5 +49,10 @@ interface AccompanyingPeriodACLAwareRepositoryInterface
|
||||
*/
|
||||
public function findByUnDispatched(array $jobs, array $services, array $administrativeLocations, ?int $limit = null, ?int $offset = null): array;
|
||||
|
||||
/**
|
||||
* @param array|PostalCode[] $postalCodes
|
||||
*/
|
||||
public function findByUserAndPostalCodesOpenedAccompanyingPeriod(?User $user, array $postalCodes, array $orderBy = [], int $limit = 0, int $offset = 50): array;
|
||||
|
||||
public function findByUserOpenedAccompanyingPeriod(?User $user, array $orderBy = [], int $limit = 0, int $offset = 50): array;
|
||||
}
|
||||
|
@ -14,9 +14,8 @@ namespace Chill\PersonBundle\Repository\SocialWork;
|
||||
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
final class EvaluationRepository implements ObjectRepository
|
||||
final class EvaluationRepository implements EvaluationRepositoryInterface
|
||||
{
|
||||
private EntityRepository $repository;
|
||||
|
||||
@ -38,6 +37,11 @@ final class EvaluationRepository implements ObjectRepository
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
|
||||
public function findAllActive(): array
|
||||
{
|
||||
return $this->findBy(['active' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
|
@ -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\PersonBundle\Repository\SocialWork;
|
||||
|
||||
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
interface EvaluationRepositoryInterface extends ObjectRepository
|
||||
{
|
||||
public function find($id, ?int $lockMode = null, ?int $lockVersion = null): ?Evaluation;
|
||||
|
||||
/**
|
||||
* @return array<int, Evaluation>
|
||||
*/
|
||||
public function findAll(): array;
|
||||
|
||||
/**
|
||||
* @return array<int, Evaluation>
|
||||
*/
|
||||
public function findAllActive(): array;
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
*
|
||||
* @return array<int, Evaluation>
|
||||
*/
|
||||
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array;
|
||||
|
||||
public function findOneBy(array $criteria, ?array $orderBy = null): ?Evaluation;
|
||||
|
||||
/**
|
||||
* @return class-string
|
||||
*/
|
||||
public function getClassName(): string;
|
||||
}
|
@ -13,6 +13,7 @@ namespace Chill\PersonBundle\Repository\SocialWork;
|
||||
|
||||
use Chill\PersonBundle\Entity\SocialWork\Goal;
|
||||
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
@ -62,17 +63,21 @@ final class GoalRepository implements ObjectRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $orderBy
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
*
|
||||
* @return Goal[]
|
||||
*/
|
||||
public function findBySocialActionWithDescendants(SocialAction $action, $orderBy = null, $limit = null, $offset = null): array
|
||||
public function findBySocialActionWithDescendants(SocialAction $action, array $orderBy = [], ?int $limit = null, ?int $offset = null): array
|
||||
{
|
||||
$qb = $this->buildQueryBySocialActionWithDescendants($action);
|
||||
$qb->select('g');
|
||||
|
||||
$qb->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('g.desactivationDate'),
|
||||
$qb->expr()->gt('g.desactivationDate', ':now')
|
||||
)
|
||||
)
|
||||
->setParameter('now', new DateTime('now'));
|
||||
|
||||
foreach ($orderBy as $sort => $order) {
|
||||
$qb->addOrderBy('g.' . $sort, $order);
|
||||
}
|
||||
|
@ -96,13 +96,9 @@ final class ResultRepository implements ObjectRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $orderBy
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
*
|
||||
* @return Result[]
|
||||
*/
|
||||
public function findBySocialActionWithDescendants(SocialAction $action, $orderBy = null, $limit = null, $offset = null): array
|
||||
public function findBySocialActionWithDescendants(SocialAction $action, array $orderBy = [], ?int $limit = null, ?int $offset = null): array
|
||||
{
|
||||
$qb = $this->buildQueryBySocialActionWithDescendants($action);
|
||||
$qb->select('r');
|
||||
|
@ -76,7 +76,7 @@
|
||||
<div class="flex-table">
|
||||
{% for period in periods %}
|
||||
{% include '@ChillPerson/AccompanyingPeriod/_list_item.html.twig' with {'period': period,
|
||||
'recordAction': m.period_actions(period), 'itemMeta': m.period_meta(period) } %}
|
||||
'recordAction': m.period_actions(period), 'itemMeta': m.period_meta(period), 'show_address': true } %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -113,6 +113,16 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if show_address|default(false) and period.location is not null %}
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title"><h3>{{ 'Accompanying course location'|trans }}</h3></div>
|
||||
<div class="wl-col list">
|
||||
<p class="wl-item">
|
||||
{{ period.location|chill_entity_render_string }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -5,11 +5,13 @@
|
||||
{% block js %}
|
||||
{{ encore_entry_script_tags('mod_set_referrer') }}
|
||||
{{ encore_entry_script_tags('mod_pickentity_type') }}
|
||||
{{ encore_entry_script_tags('mod_pick_postal_code') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ encore_entry_link_tags('mod_set_referrer') }}
|
||||
{{ encore_entry_link_tags('mod_pickentity_type') }}
|
||||
{{ encore_entry_link_tags('mod_pick_postal_code') }}
|
||||
{% endblock %}
|
||||
|
||||
{% macro period_meta(period) %}
|
||||
@ -48,6 +50,8 @@
|
||||
{{ form_start(form) }}
|
||||
{{ form_label(form.user ) }}
|
||||
{{ form_widget(form.user, {'attr': {'class': 'select2'}}) }}
|
||||
{{ form_label(form.postal_code) }}
|
||||
{{ form_widget(form.postal_code) }}
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<button type="submit" class="btn btn-misc">
|
||||
@ -87,7 +91,7 @@
|
||||
<div class="flex-table">
|
||||
{% for period in periods %}
|
||||
{% include '@ChillPerson/AccompanyingPeriod/_list_item.html.twig' with {'period': period,
|
||||
'recordAction': m.period_actions(period), 'itemMeta': m.period_meta(period) } %}
|
||||
'recordAction': m.period_actions(period), 'itemMeta': m.period_meta(period), 'show_address': true } %}
|
||||
{% else %}
|
||||
{% if userFrom is same as(null) %}
|
||||
<p class="chill-no-data-statement">{{ 'period_by_user_list.Pick a user'|trans }}</p>
|
||||
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Service\DocGenerator;
|
||||
|
||||
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithAdminFormInterface;
|
||||
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface;
|
||||
use Chill\DocGeneratorBundle\Context\Exception\UnexpectedTypeException;
|
||||
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
|
||||
use Chill\DocGeneratorBundle\Service\Context\BaseContextData;
|
||||
@ -19,46 +20,72 @@ use Chill\DocStoreBundle\Entity\DocumentCategory;
|
||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\DocStoreBundle\Repository\DocumentCategoryRepository;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Form\Type\ScopePickerType;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use LogicException;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use function array_key_exists;
|
||||
use function count;
|
||||
|
||||
class PersonContext implements DocGeneratorContextWithAdminFormInterface
|
||||
class PersonContext implements DocGeneratorContextWithAdminFormInterface, DocGeneratorContextWithPublicFormInterface
|
||||
{
|
||||
private AuthorizationHelperInterface $authorizationHelper;
|
||||
|
||||
private BaseContextData $baseContextData;
|
||||
|
||||
private CenterResolverManagerInterface $centerResolverManager;
|
||||
|
||||
private DocumentCategoryRepository $documentCategoryRepository;
|
||||
|
||||
private EntityManagerInterface $em;
|
||||
|
||||
private NormalizerInterface $normalizer;
|
||||
|
||||
private Security $security;
|
||||
|
||||
private bool $showScopes;
|
||||
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(
|
||||
AuthorizationHelperInterface $authorizationHelper,
|
||||
BaseContextData $baseContextData,
|
||||
CenterResolverManagerInterface $centerResolverManager,
|
||||
DocumentCategoryRepository $documentCategoryRepository,
|
||||
NormalizerInterface $normalizer,
|
||||
TranslatableStringHelperInterface $translatableStringHelper,
|
||||
EntityManagerInterface $em,
|
||||
NormalizerInterface $normalizer,
|
||||
ParameterBagInterface $parameterBag,
|
||||
Security $security,
|
||||
TranslatorInterface $translator,
|
||||
BaseContextData $baseContextData
|
||||
TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {
|
||||
$this->documentCategoryRepository = $documentCategoryRepository;
|
||||
$this->normalizer = $normalizer;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->em = $em;
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->centerResolverManager = $centerResolverManager;
|
||||
$this->baseContextData = $baseContextData;
|
||||
$this->documentCategoryRepository = $documentCategoryRepository;
|
||||
$this->em = $em;
|
||||
$this->normalizer = $normalizer;
|
||||
$this->security = $security;
|
||||
$this->showScopes = $parameterBag->get('chill_main')['acl']['form_show_scopes'];
|
||||
$this->translator = $translator;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function adminFormReverseTransform(array $data): array
|
||||
@ -105,6 +132,18 @@ class PersonContext implements DocGeneratorContextWithAdminFormInterface
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Person $entity
|
||||
*/
|
||||
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
|
||||
{
|
||||
$builder->add('scope', ScopePickerType::class, [
|
||||
'center' => $this->centerResolverManager->resolveCenters($entity),
|
||||
'role' => PersonDocumentVoter::CREATE,
|
||||
'label' => 'Scope',
|
||||
]);
|
||||
}
|
||||
|
||||
public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
|
||||
{
|
||||
if (!$entity instanceof Person) {
|
||||
@ -156,6 +195,14 @@ class PersonContext implements DocGeneratorContextWithAdminFormInterface
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Person $entity
|
||||
*/
|
||||
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool
|
||||
{
|
||||
return $this->isScopeNecessary($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Person $entity
|
||||
*/
|
||||
@ -178,6 +225,36 @@ class PersonContext implements DocGeneratorContextWithAdminFormInterface
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->isScopeNecessary($entity)) {
|
||||
$doc->setScope($contextGenerationData['scope']);
|
||||
} elseif ($this->showScopes) {
|
||||
// in this case, it should have only one scope possible, we get it through AuthorizationHelper::getReachableScopes
|
||||
$scopes = $this->authorizationHelper->getReachableScopes(
|
||||
$this->security->getUser(),
|
||||
PersonDocumentVoter::CREATE,
|
||||
$this->centerResolverManager->resolveCenters($entity)
|
||||
);
|
||||
|
||||
if (1 !== count($scopes)) {
|
||||
throw new LogicException('at this step, it should have only one scope');
|
||||
}
|
||||
|
||||
$doc->setScope($scopes[0]);
|
||||
}
|
||||
|
||||
$this->em->persist($doc);
|
||||
}
|
||||
|
||||
private function isScopeNecessary(Person $person): bool
|
||||
{
|
||||
if ($this->showScopes && 1 < $this->authorizationHelper->getReachableScopes(
|
||||
$this->security->getUser(),
|
||||
PersonDocumentVoter::CREATE,
|
||||
$this->centerResolverManager->resolveCenters($person)
|
||||
)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ final class SocialWorkMetadata implements SocialWorkMetadataInterface
|
||||
/** @var Evaluation $eval */
|
||||
$eval = $this->getOrCreateEntity($this->evaluationRepository, 'title', ['fr' => $evaluationTitle]);
|
||||
$eval->setTitle(['fr' => $evaluationTitle]);
|
||||
$eval->addSocialAction($socialAction);
|
||||
$socialAction->addEvaluation($eval);
|
||||
|
||||
$this->entityManager->persist($eval);
|
||||
|
||||
|
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Tests\Controller;
|
||||
|
||||
use Chill\MainBundle\Test\PrepareClientTrait;
|
||||
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
|
||||
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class SocialWorkEvaluationApiControllerTest extends WebTestCase
|
||||
{
|
||||
use PrepareClientTrait;
|
||||
|
||||
private EntityManagerInterface $em;
|
||||
|
||||
private ?Evaluation $evaluationToReset = null;
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
if (null === $this->evaluationToReset) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::bootKernel();
|
||||
|
||||
$em = self::$container->get(EntityManagerInterface::class);
|
||||
$evaluation = $em->find(Evaluation::class, $this->evaluationToReset->getId());
|
||||
|
||||
$evaluation->setActive(true);
|
||||
$em->flush();
|
||||
}
|
||||
|
||||
public function dataGenerateSocialActionWithEvaluations(): iterable
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$this->em = self::$container->get(EntityManagerInterface::class);
|
||||
|
||||
/** @var SocialAction $socialAction */
|
||||
$socialAction = $this->em->createQuery(
|
||||
'SELECT s FROM ' . SocialAction::class . ' s WHERE SIZE(s.evaluations) >= 2'
|
||||
)
|
||||
->setMaxResults(1)
|
||||
->getSingleResult();
|
||||
|
||||
// set the first evaluation as inactive and save
|
||||
$this->evaluationToReset = $socialAction->getEvaluations()->first();
|
||||
$this->evaluationToReset->setActive(false);
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
yield [$socialAction, $this->evaluationToReset];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataGenerateSocialActionWithEvaluations
|
||||
*/
|
||||
public function testListEvaluationBySocialAction(SocialAction $action, Evaluation $inactiveEvaluation): void
|
||||
{
|
||||
$client = $this->getClientAuthenticated();
|
||||
|
||||
$client->request('GET', sprintf('/api/1.0/person/social-work/evaluation/by-social-action/%d.json', $action->getId()));
|
||||
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$content = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$ids = array_map(static fn (array $item) => $item['id'], $content['results']);
|
||||
|
||||
$this->assertNotContains($inactiveEvaluation->getId(), $ids);
|
||||
}
|
||||
}
|
@ -29,6 +29,38 @@ use function count;
|
||||
*/
|
||||
final class AccompanyingPeriodTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testChangeStepKeepHistory()
|
||||
{
|
||||
$period = new AccompanyingPeriod();
|
||||
|
||||
$this->assertCount(0, $period->getStepHistories(), 'at initialization, period should not have any step history');
|
||||
|
||||
$period->setStep(AccompanyingPeriod::STEP_DRAFT);
|
||||
|
||||
$this->assertCount(0, $period->getStepHistories(), 're applying a draft should not create a history');
|
||||
|
||||
$period->setStep(AccompanyingPeriod::STEP_CONFIRMED);
|
||||
|
||||
$this->assertCount(1, $period->getStepHistories());
|
||||
$this->assertEquals(AccompanyingPeriod::STEP_CONFIRMED, $period->getStepHistories()->first()->getStep());
|
||||
|
||||
$period->setOpeningDate($aMonthAgo = new DateTime('1 month ago'));
|
||||
|
||||
$this->assertCount(1, $period->getStepHistories());
|
||||
$this->assertEquals($aMonthAgo, $period->getStepHistories()->first()->getStartDate(), 'when changing the opening date, the start date of the first history should change');
|
||||
|
||||
$period->setOpeningDate($tenDaysAgo = new DateTime('10 days ago'));
|
||||
|
||||
$this->assertCount(1, $period->getStepHistories());
|
||||
$this->assertEquals($tenDaysAgo, $period->getStepHistories()->first()->getStartDate(), 'when changing the opening date, the start date of the first history should change');
|
||||
|
||||
$period->setStep(AccompanyingPeriod::STEP_CLOSED);
|
||||
$this->assertCount(2, $period->getStepHistories());
|
||||
|
||||
$period->setOpeningDate($tomorrow = new DateTime('tomorrow'));
|
||||
$this->assertEquals($tenDaysAgo, $period->getStepHistories()->first()->getStartDate(), 'when changing the opening date to a later one and no history after, start date should change');
|
||||
}
|
||||
|
||||
public function testClosingEqualOpening()
|
||||
{
|
||||
$datetime = new DateTime('now');
|
||||
|
@ -39,7 +39,9 @@ final class DurationAggregatorTest extends AbstractAggregatorTest
|
||||
public function getFormData(): array
|
||||
{
|
||||
return [
|
||||
[],
|
||||
['precision' => 'day'],
|
||||
['precision' => 'week'],
|
||||
['precision' => 'month'],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregato
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\GeographicalUnitStatAggregator;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
@ -38,8 +39,9 @@ final class GeographicalUnitStatAggregatorTest extends AbstractAggregatorTest
|
||||
|
||||
public function getFormData(): array
|
||||
{
|
||||
// TODO: add geographical unit stat into fixtures and provide a level
|
||||
return [
|
||||
[],
|
||||
['date_calc' => new DateTimeImmutable('today'), 'level' => null],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregato
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ReferrerAggregator;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
@ -39,7 +40,7 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest
|
||||
public function getFormData(): array
|
||||
{
|
||||
return [
|
||||
[],
|
||||
['date_calc' => new DateTimeImmutable('now')],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters;
|
||||
|
||||
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||
use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserJobFilter;
|
||||
use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserJobFilter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
@ -21,7 +21,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
*/
|
||||
final class CurrentUserJobFilterTest extends AbstractFilterTest
|
||||
{
|
||||
private CurrentUserJobFilter $filter;
|
||||
private UserJobFilter $filter;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters;
|
||||
|
||||
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||
use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserScopeFilter;
|
||||
use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserScopeFilter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
@ -21,7 +21,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
*/
|
||||
final class CurrentUserScopeFilterTest extends AbstractFilterTest
|
||||
{
|
||||
private CurrentUserScopeFilter $filter;
|
||||
private UserScopeFilter $filter;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
|
@ -0,0 +1,46 @@
|
||||
<?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\Serializer\Normalizer;
|
||||
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class AccompanyingPeriodOriginNormalizerTest extends KernelTestCase
|
||||
{
|
||||
private NormalizerInterface $normalizer;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->normalizer = self::$container->get(NormalizerInterface::class);
|
||||
}
|
||||
|
||||
public function testNormalization()
|
||||
{
|
||||
$o = new Origin();
|
||||
|
||||
$normalized = $this->normalizer->normalize(
|
||||
$o,
|
||||
'json',
|
||||
['groups' => ['read']]
|
||||
);
|
||||
|
||||
$this->assertIsArray($normalized);
|
||||
$this->assertArrayHasKey('type', $normalized);
|
||||
$this->assertEquals('origin', $normalized['type']);
|
||||
}
|
||||
}
|
@ -60,7 +60,7 @@ final class AccompanyingPeriodWorkDocGenNormalizerTest extends KernelTestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testNormlalize()
|
||||
public function testNormalize()
|
||||
{
|
||||
$work = new AccompanyingPeriodWork();
|
||||
$work
|
||||
|
@ -0,0 +1,46 @@
|
||||
<?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\Serializer\Normalizer;
|
||||
|
||||
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class SocialActionNormalizerTest extends KernelTestCase
|
||||
{
|
||||
private NormalizerInterface $normalizer;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->normalizer = self::$container->get(NormalizerInterface::class);
|
||||
}
|
||||
|
||||
public function testNormalization()
|
||||
{
|
||||
$sa = new SocialAction();
|
||||
|
||||
$normalized = $this->normalizer->normalize(
|
||||
$sa,
|
||||
'json',
|
||||
['groups' => ['read']]
|
||||
);
|
||||
|
||||
$this->assertIsArray($normalized);
|
||||
$this->assertArrayHasKey('type', $normalized);
|
||||
$this->assertEquals('social_work_social_action', $normalized['type']);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
<?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\Serializer\Normalizer;
|
||||
|
||||
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class SocialIssueNormalizerTest extends KernelTestCase
|
||||
{
|
||||
private NormalizerInterface $normalizer;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->normalizer = self::$container->get(NormalizerInterface::class);
|
||||
}
|
||||
|
||||
public function testNormalization()
|
||||
{
|
||||
$si = new SocialIssue();
|
||||
|
||||
$normalized = $this->normalizer->normalize(
|
||||
$si,
|
||||
'json',
|
||||
['groups' => ['read']]
|
||||
);
|
||||
|
||||
$this->assertIsArray($normalized);
|
||||
$this->assertArrayHasKey('type', $normalized);
|
||||
$this->assertEquals('social_issue', $normalized['type']);
|
||||
}
|
||||
}
|
@ -0,0 +1,265 @@
|
||||
<?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 Service\DocGenerator;
|
||||
|
||||
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
|
||||
use Chill\DocGeneratorBundle\Service\Context\BaseContextData;
|
||||
use Chill\DocStoreBundle\Entity\DocumentCategory;
|
||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\DocStoreBundle\Repository\DocumentCategoryRepository;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Service\DocGenerator\PersonContext;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Exception\Prediction\FailedPredictionException;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class PersonContextTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
/**
|
||||
* Test that the build person context works in the case when 'form_show_scope' is false.
|
||||
*/
|
||||
public function testScopeDoNotShowScopeInForms()
|
||||
{
|
||||
$person = new Person();
|
||||
$docGen = (new DocGeneratorTemplate())
|
||||
->setName(['fr' => 'template']);
|
||||
|
||||
$parameter = new ParameterBag(['chill_main' => ['acl' => ['form_show_scopes' => false]]]);
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->persist(Argument::type(PersonDocument::class))
|
||||
->should(static function ($calls, $object, $method) {
|
||||
if (1 !== count($calls)) {
|
||||
throw new FailedPredictionException(sprintf('the persist should be called exactly once, %d receivved', count($calls)));
|
||||
}
|
||||
|
||||
/** @var PersonDocument $personDocument */
|
||||
$personDocument = $calls[0]->getArguments()[0];
|
||||
|
||||
if (null !== $personDocument->getScope()) {
|
||||
throw new FailedPredictionException('the person document should not have any scope');
|
||||
}
|
||||
});
|
||||
|
||||
$personContext = $this->buildPersonContext(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
$em->reveal(),
|
||||
null,
|
||||
$parameter
|
||||
);
|
||||
|
||||
$this->assertFalse($personContext->hasPublicForm($docGen, $person));
|
||||
|
||||
$personContext->storeGenerated(
|
||||
$docGen,
|
||||
new StoredObject(),
|
||||
$person,
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
public function testScopeScopeMustBeShownInFormsAndUserAccessMultipleScope()
|
||||
{
|
||||
$person = new Person();
|
||||
$docGen = (new DocGeneratorTemplate())
|
||||
->setName(['fr' => 'template']);
|
||||
$scope = new Scope();
|
||||
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->persist(Argument::type(PersonDocument::class))
|
||||
->should(static function ($calls, $object, $method) use ($scope) {
|
||||
if (1 !== count($calls)) {
|
||||
throw new FailedPredictionException(sprintf('the persist should be called exactly once, %d receivved', count($calls)));
|
||||
}
|
||||
|
||||
/** @var PersonDocument $personDocument */
|
||||
$personDocument = $calls[0]->getArguments()[0];
|
||||
|
||||
if ($personDocument->getScope() !== $scope) {
|
||||
throw new FailedPredictionException('the person document should show the exactly prepared scope');
|
||||
}
|
||||
});
|
||||
|
||||
$authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class);
|
||||
$authorizationHelper->getReachableScopes(Argument::type(UserInterface::class), PersonDocumentVoter::CREATE, Argument::type('array'))
|
||||
->willReturn([$scope, new Scope()]);
|
||||
|
||||
$personContext = $this->buildPersonContext(
|
||||
$authorizationHelper->reveal(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
$em->reveal(),
|
||||
);
|
||||
|
||||
$this->assertTrue($personContext->hasPublicForm($docGen, $person));
|
||||
|
||||
$personContext->storeGenerated(
|
||||
$docGen,
|
||||
new StoredObject(),
|
||||
$person,
|
||||
['scope' => $scope]
|
||||
);
|
||||
}
|
||||
|
||||
public function testScopeScopeMustBeShownInFormsAndUserAccessOneScope()
|
||||
{
|
||||
$person = new Person();
|
||||
$docGen = (new DocGeneratorTemplate())
|
||||
->setName(['fr' => 'template']);
|
||||
$scope = new Scope();
|
||||
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->persist(Argument::type(PersonDocument::class))
|
||||
->should(static function ($calls, $object, $method) use ($scope) {
|
||||
if (1 !== count($calls)) {
|
||||
throw new FailedPredictionException(sprintf('the persist should be called exactly once, %d receivved', count($calls)));
|
||||
}
|
||||
|
||||
/** @var PersonDocument $personDocument */
|
||||
$personDocument = $calls[0]->getArguments()[0];
|
||||
|
||||
if ($personDocument->getScope() !== $scope) {
|
||||
throw new FailedPredictionException('the person document should show the exactly prepared scope');
|
||||
}
|
||||
});
|
||||
|
||||
$authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class);
|
||||
$authorizationHelper->getReachableScopes(Argument::type(UserInterface::class), PersonDocumentVoter::CREATE, Argument::type('array'))
|
||||
->willReturn([$scope]);
|
||||
|
||||
$personContext = $this->buildPersonContext(
|
||||
$authorizationHelper->reveal(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
$em->reveal(),
|
||||
);
|
||||
|
||||
$this->assertTrue($personContext->hasPublicForm($docGen, $person));
|
||||
|
||||
$personContext->storeGenerated(
|
||||
$docGen,
|
||||
new StoredObject(),
|
||||
$person,
|
||||
['scope' => $scope]
|
||||
);
|
||||
}
|
||||
|
||||
private function buildPersonContext(
|
||||
?AuthorizationHelperInterface $authorizationHelper = null,
|
||||
?BaseContextData $baseContextData = null,
|
||||
?CenterResolverManagerInterface $centerResolverManager = null,
|
||||
?DocumentCategoryRepository $documentCategoryRepository = null,
|
||||
?EntityManagerInterface $em = null,
|
||||
?NormalizerInterface $normalizer = null,
|
||||
?ParameterBagInterface $parameterBag = null,
|
||||
?Security $security = null,
|
||||
?TranslatorInterface $translator = null,
|
||||
?TranslatableStringHelperInterface $translatableStringHelper = null
|
||||
): PersonContext {
|
||||
if (null === $authorizationHelper) {
|
||||
$authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class)->reveal();
|
||||
}
|
||||
|
||||
if (null === $baseContextData) {
|
||||
$baseContextData = $this->prophesize(BaseContextData::class)->reveal();
|
||||
}
|
||||
|
||||
if (null === $centerResolverManager) {
|
||||
$centerResolverManager = $this->prophesize(CenterResolverManagerInterface::class);
|
||||
$centerResolverManager->resolveCenters(Argument::any(), Argument::any())
|
||||
->willReturn([new Center()]);
|
||||
$centerResolverManager = $centerResolverManager->reveal();
|
||||
}
|
||||
|
||||
if (null === $documentCategoryRepository) {
|
||||
$documentCategoryRepository = $this->prophesize(DocumentCategoryRepository::class);
|
||||
$documentCategoryRepository->find(Argument::type('integer'))->willReturn(
|
||||
new DocumentCategory(PersonDocument::class, 1)
|
||||
);
|
||||
$documentCategoryRepository = $documentCategoryRepository->reveal();
|
||||
}
|
||||
|
||||
if (null === $em) {
|
||||
$em = $this->prophesize(EntityManagerInterface::class)->reveal();
|
||||
}
|
||||
|
||||
if (null === $normalizer) {
|
||||
$normalizer = $this->prophesize(NormalizerInterface::class);
|
||||
$normalizer->normalize(Argument::type(Person::class), 'docgen', Argument::any())
|
||||
->willReturn(['type' => 'person']);
|
||||
$normalizer = $normalizer->reveal();
|
||||
}
|
||||
|
||||
if (null === $parameterBag) {
|
||||
$parameterBag = new ParameterBag(['chill_main' => ['acl' => ['form_show_scopes' => true]]]);
|
||||
}
|
||||
|
||||
if (null === $security) {
|
||||
$security = $this->prophesize(Security::class);
|
||||
$security->getUser()->willReturn(new User());
|
||||
$security = $security->reveal();
|
||||
}
|
||||
|
||||
if (null === $translator) {
|
||||
$translator = $this->prophesize(TranslatorInterface::class)->reveal();
|
||||
}
|
||||
|
||||
if (null === $translatableStringHelper) {
|
||||
$translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class);
|
||||
// return only the 'fr' key
|
||||
$translatableStringHelper->localize(Argument::type('array'))->will(static function ($args) {
|
||||
return $args[0]['fr'];
|
||||
});
|
||||
$translatableStringHelper = $translatableStringHelper->reveal();
|
||||
}
|
||||
|
||||
return new PersonContext(
|
||||
$authorizationHelper,
|
||||
$baseContextData,
|
||||
$centerResolverManager,
|
||||
$documentCategoryRepository,
|
||||
$em,
|
||||
$normalizer,
|
||||
$parameterBag,
|
||||
$security,
|
||||
$translator,
|
||||
$translatableStringHelper
|
||||
);
|
||||
}
|
||||
}
|
@ -16,19 +16,17 @@ services:
|
||||
- { name: chill.export, alias: avg_accompanyingcourse_duration }
|
||||
|
||||
## Filters
|
||||
chill.person.export.filter_current_userscope:
|
||||
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserScopeFilter
|
||||
Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserScopeFilter:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: accompanyingcourse_current_userscope_filter }
|
||||
- { name: chill.export_filter, alias: accompanyingcourse_userscope_filter }
|
||||
|
||||
chill.person.export.filter_current_userjob:
|
||||
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserJobFilter
|
||||
Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserJobFilter:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: accompanyingcourse_current_userjob_filter }
|
||||
- { name: chill.export_filter, alias: accompanyingcourse_userjob_filter }
|
||||
|
||||
chill.person.export.filter_socialissue:
|
||||
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\SocialIssueFilter
|
||||
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\Migrations\Person;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20221013131221 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE chill_person_social_work_evaluation DROP active');
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add an active column on evaluation';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE chill_person_social_work_evaluation ADD active BOOLEAN DEFAULT true NOT NULL');
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\Migrations\Person;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20221014115500 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP SEQUENCE chill_person_accompanying_period_step_history_id_seq CASCADE');
|
||||
$this->addSql('DROP TABLE chill_person_accompanying_period_step_history');
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add step history on accompanying periods';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE SEQUENCE chill_person_accompanying_period_step_history_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE chill_person_accompanying_period_step_history (id INT NOT NULL, period_id INT DEFAULT NULL,
|
||||
endDate DATE DEFAULT NULL, startDate DATE NOT NULL, step TEXT NOT NULL, createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL,
|
||||
updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, createdBy_id INT DEFAULT NULL,
|
||||
updatedBy_id INT DEFAULT NULL, PRIMARY KEY(id))
|
||||
');
|
||||
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CHECK (startDate <= endDate)');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT ' .
|
||||
'chill_internal_acp_steps_not_overlaps EXCLUDE USING GIST(
|
||||
-- extension btree_gist required to include comparaison with integer
|
||||
period_id WITH =,
|
||||
daterange(startDate, endDate, \'[)\') WITH &&
|
||||
)
|
||||
INITIALLY DEFERRED');
|
||||
|
||||
$this->addSql('CREATE INDEX IDX_84D514ACEC8B7ADE ON chill_person_accompanying_period_step_history (period_id)');
|
||||
$this->addSql('CREATE INDEX IDX_84D514AC3174800F ON chill_person_accompanying_period_step_history (createdBy_id)');
|
||||
$this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.endDate IS \'(DC2Type:date_immutable)\'');
|
||||
$this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.startDate IS \'(DC2Type:date_immutable)\'');
|
||||
$this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.createdAt IS \'(DC2Type:datetime_immutable)\'');
|
||||
$this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.updatedAt IS \'(DC2Type:datetime_immutable)\'');
|
||||
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT FK_84D514ACEC8B7ADE FOREIGN KEY (period_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT FK_84D514AC3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_84D514AC65FF1AEC ON chill_person_accompanying_period_step_history (updatedBy_id)');
|
||||
|
||||
// fill the tables with current state
|
||||
$this->addSql(
|
||||
'INSERT INTO chill_person_accompanying_period_step_history (id, period_id, startDate, endDate, step, createdAt, updatedAt)
|
||||
SELECT nextval(\'chill_person_accompanying_period_step_history_id_seq\'), id, openingDate, null, step, NOW(), NOW() FROM chill_person_accompanying_period WHERE step = \'CONFIRMED\'
|
||||
UNION
|
||||
SELECT nextval(\'chill_person_accompanying_period_step_history_id_seq\'), id, openingDate, closingDate, \'CONFIRMED\', NOW(), NOW() FROM chill_person_accompanying_period WHERE step = \'CLOSED\'
|
||||
UNION
|
||||
SELECT nextval(\'chill_person_accompanying_period_step_history_id_seq\'), id, closingDate, null, \'CLOSED\', NOW(), NOW() FROM chill_person_accompanying_period WHERE step = \'CLOSED\'
|
||||
'
|
||||
);
|
||||
}
|
||||
}
|
@ -939,6 +939,8 @@ reassign:
|
||||
All periods on this list will be reassigned to this user, excepted the one you manually reassigned before: Tous les parcours visibles sur cette page seront assignés à cet utilisateur, sauf ceux que vous aurez assigné à un utilisateur manuellement.
|
||||
Reassign: Assigner le référent
|
||||
List periods to be able to reassign them: Choisissez un utilisateur et cliquez sur "Filtrer" pour visualiser ses parcours. Vous pourrez ensuite les réassigner.
|
||||
Filter by postal code: Filtrer par code postal
|
||||
Filter course which are located inside a postal code: Afficher uniquement les parcours localisés auprès de ce code postal (une commune peut comporter plusieurs codes postaux).
|
||||
|
||||
notification:
|
||||
Notify referrer: Notifier le référent
|
||||
@ -947,14 +949,27 @@ notification:
|
||||
export:
|
||||
aggregator:
|
||||
course:
|
||||
by_referrer:
|
||||
Computation date for referrer: Date à laquelle le référent était actif
|
||||
by_user_scope:
|
||||
Group course by referrer's scope: Grouper les parcours par service du référent
|
||||
Computation date for referrer: Date à laquelle le référent était actif
|
||||
Referrer's scope: Service du référent de parcours
|
||||
|
||||
duration:
|
||||
day: Durée du parcours en jours
|
||||
week: Durée du parcours en semaines
|
||||
month: Durée du parcours en mois
|
||||
Precision: Unité de la durée
|
||||
filter:
|
||||
course:
|
||||
by_user_scope:
|
||||
Computation date for referrer: Date à laquelle le référent était actif
|
||||
by_referrer:
|
||||
Computation date for referrer: Date à laquelle le référent était actif
|
||||
|
||||
social_action:
|
||||
and children: et dérivés
|
||||
|
||||
social_issue:
|
||||
and children: et dérivés
|
||||
and children: et dérivés
|
||||
|
Loading…
x
Reference in New Issue
Block a user