mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-21 06:05:00 +00:00
Compare commits
35 Commits
694-save-e
...
693-filter
Author | SHA1 | Date | |
---|---|---|---|
25cbb528ec
|
|||
2d013e110a
|
|||
9e63480c70
|
|||
988495df27 | |||
cc62c9cc4a | |||
40924d9d39 | |||
8b505410ca | |||
d535ec6cfb | |||
9029426d03 | |||
9ae2e51819 | |||
68f7a832b4 | |||
af5f27ff49 | |||
5e58d36e79 | |||
b0ab591cbd
|
|||
d8af7d455e
|
|||
5830c3e177
|
|||
88eefa698b
|
|||
c64ec89274 | |||
16ec858ee8 | |||
de55ff920f
|
|||
885256ac0d
|
|||
f53d3852c3 | |||
9f5b11e6cc | |||
e5bc74d11d | |||
5c0d89a88b | |||
56a17a0bcd | |||
9ffe1ff8a8 | |||
c790b22496 | |||
e54c2ca712 | |||
2f091a639b | |||
9ada19ef23 | |||
8ee184e665 | |||
c5f842076f | |||
8e3a83de85 | |||
050a4feab5 |
@@ -0,0 +1,74 @@
|
||||
<?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\ACPFilters;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickUserLocationType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class LocationFilter implements FilterInterface
|
||||
{
|
||||
private TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
public function __construct(TranslatableStringHelper $translatableStringHelper)
|
||||
{
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$qb->andWhere(
|
||||
$qb->expr()->in('activity.location', ':location')
|
||||
);
|
||||
|
||||
$qb->setParameter('location', $data['accepted_location']);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACTIVITY_ACP;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('accepted_location', PickUserLocationType::class, [
|
||||
'multiple' => true,
|
||||
'label' => 'pick location'
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
{
|
||||
$locations = [];
|
||||
|
||||
foreach ($data['accepted_location'] as $location) {
|
||||
$locations[] = $location->getName();
|
||||
}
|
||||
|
||||
return ['Filtered activity by location: only %locations%', [
|
||||
'%locations%' => implode(', ', $locations),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Filter activity by location';
|
||||
}
|
||||
}
|
@@ -168,7 +168,7 @@
|
||||
{% if entity.documents|length > 0 %}
|
||||
<ul>
|
||||
{% for d in entity.documents %}
|
||||
<li>{{ d.title }}{{ m.download_button(d) }}</li>
|
||||
<li>{{ d.title }} {{ d|chill_document_button_group() }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
|
@@ -8,12 +8,14 @@
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('mod_notification_toggle_read_status') }}
|
||||
{{ encore_entry_script_tags('mod_async_upload') }}
|
||||
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_notification_toggle_read_status') }}
|
||||
{{ encore_entry_link_tags('mod_async_upload') }}
|
||||
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% import 'ChillActivityBundle:ActivityReason:macro.html.twig' as m %}
|
||||
|
@@ -7,13 +7,13 @@
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('mod_notification_toggle_read_status') }}
|
||||
{{ encore_entry_link_tags('mod_async_upload') }}
|
||||
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_notification_toggle_read_status') }}
|
||||
{{ encore_entry_link_tags('mod_async_upload') }}
|
||||
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% import 'ChillActivityBundle:ActivityReason:macro.html.twig' as m %}
|
||||
|
@@ -79,6 +79,11 @@ services:
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'accompanyingcourse_activitytype_filter' }
|
||||
|
||||
chill.activity.export.location_filter:
|
||||
class: Chill\ActivityBundle\Export\Filter\ACPFilters\LocationFilter
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'activity_location_filter' }
|
||||
|
||||
chill.activity.export.locationtype_filter:
|
||||
class: Chill\ActivityBundle\Export\Filter\ACPFilters\LocationTypeFilter
|
||||
tags:
|
||||
|
@@ -252,6 +252,8 @@ Activity reasons for those activities: Sujets de ces activités
|
||||
|
||||
Filter by activity type: Filtrer les activités par type
|
||||
|
||||
Filter activity by location: Filtrer les activités par localisation
|
||||
'Filtered activity by location: only %locations%': "Filtré par localisation: uniquement %locations%"
|
||||
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
|
||||
|
@@ -53,19 +53,15 @@ class ByActivityTypeAggregator implements AggregatorInterface
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
$this->asideActivityCategoryRepository->findBy(['id' => $values]);
|
||||
|
||||
return function ($value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'export.aggregator.Aside activity type';
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
if (null === $value || null === $t = $this->asideActivityCategoryRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$t = $this->asideActivityCategoryRepository->find($value);
|
||||
|
||||
return $this->translatableStringHelper->localize($t->getTitle());
|
||||
};
|
||||
}
|
||||
|
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\AsideActivityBundle\Export\Aggregator;
|
||||
|
||||
use Chill\AsideActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
use function in_array;
|
||||
|
||||
class ByUserJobAggregator implements 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('aside_user', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('aside.agent', 'aside_user');
|
||||
}
|
||||
|
||||
$qb
|
||||
->addSelect('IDENTITY(aside_user.userJob) AS aside_activity_user_job_aggregator')
|
||||
->addGroupBy('aside_activity_user_job_aggregator');
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
||||
}
|
||||
|
||||
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 || '' === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$j = $this->userJobRepository->find($value);
|
||||
|
||||
return $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['aside_activity_user_job_aggregator'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'export.aggregator.Aggregate by user job';
|
||||
}
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\AsideActivityBundle\Export\Aggregator;
|
||||
|
||||
use Chill\AsideActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
use function in_array;
|
||||
|
||||
class ByUserScopeAggregator implements 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('aside_user', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('aside.agent', 'aside_user');
|
||||
}
|
||||
|
||||
$qb
|
||||
->addSelect('IDENTITY(aside_user.mainScope) AS aside_activity_user_scope_aggregator')
|
||||
->addGroupBy('aside_activity_user_scope_aggregator');
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
||||
}
|
||||
|
||||
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 || '' === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$s = $this->scopeRepository->find($value);
|
||||
|
||||
return $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['aside_activity_user_scope_aggregator'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'export.aggregator.Aggregate by user scope';
|
||||
}
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\AsideActivityBundle\Export\Export;
|
||||
|
||||
use Chill\AsideActivityBundle\Export\Declarations;
|
||||
use Chill\AsideActivityBundle\Repository\AsideActivityRepository;
|
||||
use Chill\AsideActivityBundle\Security\AsideActivityVoter;
|
||||
use Chill\MainBundle\Export\ExportInterface;
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Doctrine\ORM\Query;
|
||||
use LogicException;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class AvgAsideActivityDuration implements ExportInterface, GroupedExportInterface
|
||||
{
|
||||
private AsideActivityRepository $repository;
|
||||
|
||||
public function __construct(
|
||||
AsideActivityRepository $repository
|
||||
) {
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes(): array
|
||||
{
|
||||
return [FormatterInterface::TYPE_TABULAR];
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'export.Average aside activities duration';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'export.Exports of aside activities';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
if ('export_avg_aside_activity_duration' !== $key) {
|
||||
throw new LogicException("the key {$key} is not used by this export");
|
||||
}
|
||||
|
||||
return static fn ($value) => '_header' === $value ? 'Average duration aside activities' : $value;
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['export_avg_aside_activity_duration'];
|
||||
}
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.Average aside activities duration';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$qb = $this->repository->createQueryBuilder('aside');
|
||||
|
||||
$qb
|
||||
->select('AVG(aside.duration) as export_avg_aside_activity_duration')
|
||||
->andWhere($qb->expr()->isNotNull('aside.duration'));
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return AsideActivityVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers(): array
|
||||
{
|
||||
return [Declarations::ASIDE_ACTIVITY_TYPE];
|
||||
}
|
||||
}
|
@@ -11,12 +11,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\AsideActivityBundle\Export\Export;
|
||||
|
||||
use Chill\AsideActivityBundle\Export\Declarations;
|
||||
use Chill\AsideActivityBundle\Repository\AsideActivityRepository;
|
||||
use Chill\AsideActivityBundle\Security\AsideActivityVoter;
|
||||
use Chill\MainBundle\Export\ExportInterface;
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use ChillAsideActivityBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query;
|
||||
use LogicException;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -100,6 +100,8 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface
|
||||
|
||||
public function supportsModifiers(): array
|
||||
{
|
||||
return [];
|
||||
return [
|
||||
Declarations::ASIDE_ACTIVITY_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\AsideActivityBundle\Export\Export;
|
||||
|
||||
use Chill\AsideActivityBundle\Entity\AsideActivity;
|
||||
use Chill\AsideActivityBundle\Export\Declarations;
|
||||
use Chill\AsideActivityBundle\Form\AsideActivityCategoryType;
|
||||
use Chill\AsideActivityBundle\Repository\AsideActivityCategoryRepository;
|
||||
use Chill\AsideActivityBundle\Security\AsideActivityVoter;
|
||||
use Chill\AsideActivityBundle\Templating\Entity\CategoryRender;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Chill\MainBundle\Export\Helper\DateTimeHelper;
|
||||
use Chill\MainBundle\Export\Helper\UserHelper;
|
||||
use Chill\MainBundle\Export\ListInterface;
|
||||
use Chill\MainBundle\Repository\CenterRepositoryInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Closure;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
final class ListAsideActivity implements ListInterface, GroupedExportInterface
|
||||
{
|
||||
private EntityManagerInterface $em;
|
||||
|
||||
private UserHelper $userHelper;
|
||||
|
||||
private DateTimeHelper $dateTimeHelper;
|
||||
|
||||
private ScopeRepositoryInterface $scopeRepository;
|
||||
|
||||
private CenterRepositoryInterface $centerRepository;
|
||||
|
||||
private AsideActivityCategoryRepository $asideActivityCategoryRepository;
|
||||
|
||||
private CategoryRender $categoryRender;
|
||||
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $em,
|
||||
DateTimeHelper $dateTimeHelper,
|
||||
UserHelper $userHelper,
|
||||
ScopeRepositoryInterface $scopeRepository,
|
||||
CenterRepositoryInterface $centerRepository,
|
||||
AsideActivityCategoryRepository $asideActivityCategoryRepository,
|
||||
CategoryRender $categoryRender,
|
||||
TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {
|
||||
$this->em = $em;
|
||||
$this->dateTimeHelper = $dateTimeHelper;
|
||||
$this->userHelper = $userHelper;
|
||||
$this->scopeRepository = $scopeRepository;
|
||||
$this->centerRepository = $centerRepository;
|
||||
$this->asideActivityCategoryRepository = $asideActivityCategoryRepository;
|
||||
$this->categoryRender = $categoryRender;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes()
|
||||
{
|
||||
return [FormatterInterface::TYPE_LIST];
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return 'export.aside_activity.List of aside activities';
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'export.aside_activity.List of aside activities';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'export.Exports of aside activities';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
switch ($key) {
|
||||
case 'id':
|
||||
case 'note':
|
||||
return function ($value) use ($key) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.aside_activity.' . $key;
|
||||
}
|
||||
|
||||
return $value ?? '';
|
||||
};
|
||||
case 'duration':
|
||||
return function ($value) use ($key) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.aside_activity.' . $key;
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($value instanceof \DateTimeInterface) {
|
||||
return $value->format('H:i:s');
|
||||
}
|
||||
|
||||
return $value;
|
||||
};
|
||||
|
||||
case 'createdAt':
|
||||
case 'updatedAt':
|
||||
case 'date':
|
||||
return $this->dateTimeHelper->getLabel('export.aside_activity.'.$key);
|
||||
|
||||
case 'agent_id':
|
||||
case 'creator_id':
|
||||
return $this->userHelper->getLabel($key, $values, 'export.aside_activity.' . $key);
|
||||
|
||||
case 'aside_activity_type':
|
||||
return function ($value) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.aside_activity.aside_activity_type';
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value || null === $c = $this->asideActivityCategoryRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->categoryRender->renderString($c, []);
|
||||
};
|
||||
|
||||
case 'main_scope':
|
||||
return function ($value) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.aside_activity.main_scope';
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value || null === $c = $this->scopeRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->translatableStringHelper->localize($c->getName());
|
||||
};
|
||||
|
||||
case 'main_center':
|
||||
return function ($value) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.aside_activity.main_center';
|
||||
}
|
||||
|
||||
/** @var Center $c */
|
||||
if (null === $value || '' === $value || null === $c = $this->centerRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $c->getName();
|
||||
};
|
||||
|
||||
default:
|
||||
throw new \LogicException('this key is not supported : ' . $key);
|
||||
}
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
{
|
||||
return [
|
||||
'id',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'agent_id',
|
||||
'creator_id',
|
||||
'main_scope',
|
||||
'main_center',
|
||||
'aside_activity_type',
|
||||
'date',
|
||||
'duration',
|
||||
'note'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder $query
|
||||
* @param array $data
|
||||
*/
|
||||
public function getResult($query, $data): array
|
||||
{
|
||||
return $query->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$qb = $this->em->createQueryBuilder()
|
||||
->from(AsideActivity::class, 'aside')
|
||||
->leftJoin('aside.agent', 'agent')
|
||||
;
|
||||
|
||||
$qb
|
||||
->addSelect('aside.id AS id')
|
||||
->addSelect('aside.createdAt AS createdAt')
|
||||
->addSelect('aside.updatedAt AS updatedAt')
|
||||
->addSelect('IDENTITY(aside.agent) AS agent_id')
|
||||
->addSelect('IDENTITY(aside.createdBy) AS creator_id')
|
||||
->addSelect('IDENTITY(agent.mainScope) AS main_scope')
|
||||
->addSelect('IDENTITY(agent.mainCenter) AS main_center')
|
||||
->addSelect('IDENTITY(aside.type) AS aside_activity_type')
|
||||
->addSelect('aside.date')
|
||||
->addSelect('aside.duration')
|
||||
->addSelect('aside.note')
|
||||
;
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return AsideActivityVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers()
|
||||
{
|
||||
return [Declarations::ASIDE_ACTIVITY_TYPE];
|
||||
}
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\AsideActivityBundle\Export\Export;
|
||||
|
||||
use Chill\AsideActivityBundle\Export\Declarations;
|
||||
use Chill\AsideActivityBundle\Repository\AsideActivityRepository;
|
||||
use Chill\AsideActivityBundle\Security\AsideActivityVoter;
|
||||
use Chill\MainBundle\Export\ExportInterface;
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Doctrine\ORM\Query;
|
||||
use LogicException;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class SumAsideActivityDuration implements ExportInterface, GroupedExportInterface
|
||||
{
|
||||
private AsideActivityRepository $repository;
|
||||
|
||||
public function __construct(
|
||||
AsideActivityRepository $repository
|
||||
) {
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes(): array
|
||||
{
|
||||
return [FormatterInterface::TYPE_TABULAR];
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'export.Sum aside activities duration';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'export.Exports of aside activities';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
if ('export_sum_aside_activity_duration' !== $key) {
|
||||
throw new LogicException("the key {$key} is not used by this export");
|
||||
}
|
||||
|
||||
return static fn ($value) => '_header' === $value ? 'Sum duration aside activities' : $value;
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['export_sum_aside_activity_duration'];
|
||||
}
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.Sum aside activities duration';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$qb = $this->repository
|
||||
->createQueryBuilder('aside');
|
||||
|
||||
$qb->select('SUM(aside.duration) as export_sum_aside_activity_duration')
|
||||
->andWhere($qb->expr()->isNotNull('aside.duration'));
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return AsideActivityVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers(): array
|
||||
{
|
||||
return [Declarations::ASIDE_ACTIVITY_TYPE];
|
||||
}
|
||||
}
|
@@ -80,8 +80,8 @@ class ByActivityTypeFilter implements FilterInterface
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
{
|
||||
$types = array_map(
|
||||
fn (AsideActivityCategory $t): string => $this->translatableStringHelper->localize($t->getName()),
|
||||
$this->asideActivityTypeRepository->findBy(['id' => $data['types']->toArray()])
|
||||
fn (AsideActivityCategory $t): string => $this->translatableStringHelper->localize($t->getTitle()),
|
||||
$data['types']->toArray()
|
||||
);
|
||||
|
||||
return ['export.filter.Filtered by aside activity type: only %type%', [
|
||||
|
@@ -46,25 +46,18 @@ class ByDateFilter implements FilterInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$where = $qb->getDQLPart('where');
|
||||
$clause = $qb->expr()->between(
|
||||
'aside.date',
|
||||
':date_from',
|
||||
':date_to'
|
||||
);
|
||||
|
||||
if ($where instanceof Andx) {
|
||||
$where->add($clause);
|
||||
} else {
|
||||
$where = $qb->expr()->andX($clause);
|
||||
}
|
||||
$qb->andWhere($clause);
|
||||
|
||||
$qb->add('where', $where);
|
||||
$qb->setParameter(
|
||||
'date_from',
|
||||
$this->rollingDateConverter->convert($data['date_from'])
|
||||
);
|
||||
$qb->setParameter(
|
||||
)->setParameter(
|
||||
'date_to',
|
||||
$this->rollingDateConverter->convert($data['date_to'])
|
||||
);
|
||||
|
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\AsideActivityBundle\Export\Filter;
|
||||
|
||||
use Chill\AsideActivityBundle\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;
|
||||
|
||||
class ByUserFilter 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)
|
||||
{
|
||||
$clause = $qb->expr()->in('aside.agent', ':users');
|
||||
|
||||
$qb
|
||||
->andWhere($clause)
|
||||
->setParameter('users', $data['accepted_users']);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('accepted_users', PickUserDynamicType::class, [
|
||||
'multiple' => true,
|
||||
'label' => 'Creators',
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
{
|
||||
$users = [];
|
||||
|
||||
foreach ($data['accepted_users'] as $u) {
|
||||
$users[] = $this->userRender->renderString($u, []);
|
||||
}
|
||||
|
||||
return ['export.filter.Filtered aside activity by user: only %users%', [
|
||||
'%users%' => implode(', ', $users),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.filter.Filter aside activity by user';
|
||||
}
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\AsideActivityBundle\Export\Filter;
|
||||
|
||||
use Chill\AsideActivityBundle\Entity\AsideActivity;
|
||||
use Chill\AsideActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class ByUserJobFilter implements FilterInterface
|
||||
{
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
|
||||
public function __construct(TranslatableStringHelperInterface $translatableStringHelper)
|
||||
{
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM ' . AsideActivity::class . ' aside_activity_user_job_filter_act
|
||||
JOIN aside_activity_user_job_filter_act.agent aside_activity_user_job_filter_user WHERE aside_activity_user_job_filter_user.userJob IN (:aside_activity_user_job_filter_jobs) AND aside_activity_user_job_filter_act = aside'
|
||||
)
|
||||
)
|
||||
->setParameter('aside_activity_user_job_filter_jobs', $data['jobs']);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('jobs', EntityType::class, [
|
||||
'class' => UserJob::class,
|
||||
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize($j->getLabel()),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
return ['export.filter.Filtered aside activities by user jobs: only %jobs%', [
|
||||
'%jobs%' => implode(
|
||||
', ',
|
||||
array_map(
|
||||
fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
|
||||
$data['jobs']->toArray()
|
||||
)
|
||||
),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'export.filter.Filter by user jobs';
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\AsideActivityBundle\Export\Filter;
|
||||
|
||||
use Chill\AsideActivityBundle\Entity\AsideActivity;
|
||||
use Chill\AsideActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class ByUserScopeFilter implements FilterInterface
|
||||
{
|
||||
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)
|
||||
{
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM ' . AsideActivity::class . ' aside_activity_user_scope_filter_act
|
||||
JOIN aside_activity_user_scope_filter_act.agent aside_activity_user_scope_filter_user WHERE aside_activity_user_scope_filter_user.mainScope IN (:aside_activity_user_scope_filter_scopes) AND aside_activity_user_scope_filter_act = aside '
|
||||
)
|
||||
)
|
||||
->setParameter('aside_activity_user_scope_filter_scopes', $data['scopes']);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('scopes', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
'choices' => $this->scopeRepository->findAllActive(),
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
return ['export.filter.Filtered aside activities by user scope: only %scopes%', [
|
||||
'%scopes%' => implode(
|
||||
', ',
|
||||
array_map(
|
||||
fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
|
||||
$data['scopes']->toArray()
|
||||
)
|
||||
),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'export.filter.Filter by user scope';
|
||||
}
|
||||
}
|
@@ -20,33 +20,3 @@ services:
|
||||
resource: "../Controller"
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
|
||||
## Exports
|
||||
|
||||
# indicators
|
||||
Chill\AsideActivityBundle\Export\Export\CountAsideActivity:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags:
|
||||
- { name: chill.export, alias: count_asideactivity }
|
||||
|
||||
# filters
|
||||
Chill\AsideActivityBundle\Export\Filter\ByDateFilter:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: asideactivity_bydate_filter }
|
||||
|
||||
Chill\AsideActivityBundle\Export\Filter\ByActivityTypeFilter:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: asideactivity_activitytype_filter }
|
||||
|
||||
# aggregators
|
||||
Chill\AsideActivityBundle\Export\Aggregator\ByActivityTypeAggregator:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: asideactivity_activitytype_aggregator }
|
||||
|
@@ -3,11 +3,23 @@ services:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\AsideActivityBundle\Export\Export\ListAsideActivity:
|
||||
tags:
|
||||
- { name: chill.export, alias: 'list_aside_activity' }
|
||||
|
||||
## Indicators
|
||||
Chill\AsideActivityBundle\Export\Export\CountAsideActivity:
|
||||
tags:
|
||||
- { name: chill.export, alias: 'count_aside_activity' }
|
||||
|
||||
Chill\AsideActivityBundle\Export\Export\SumAsideActivityDuration:
|
||||
tags:
|
||||
- { name: chill.export, alias: 'sum_aside_activity_duration' }
|
||||
|
||||
Chill\AsideActivityBundle\Export\Export\AvgAsideActivityDuration:
|
||||
tags:
|
||||
- { name: chill.export, alias: 'avg_aside_activity_duration' }
|
||||
|
||||
## Filters
|
||||
chill.aside_activity.export.date_filter:
|
||||
class: Chill\AsideActivityBundle\Export\Filter\ByDateFilter
|
||||
@@ -19,9 +31,34 @@ services:
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'aside_activity_type_filter' }
|
||||
|
||||
chill.aside_activity.export.user_job_filter:
|
||||
class: Chill\AsideActivityBundle\Export\Filter\ByUserJobFilter
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'aside_activity_user_job_filter' }
|
||||
|
||||
chill.aside_activity.export.user_scope_filter:
|
||||
class: Chill\AsideActivityBundle\Export\Filter\ByUserScopeFilter
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'aside_activity_user_scope_filter' }
|
||||
|
||||
chill.aside_activity.export.user_filter:
|
||||
class: Chill\AsideActivityBundle\Export\Filter\ByUserFilter
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'aside_activity_user_filter' }
|
||||
|
||||
## Aggregators
|
||||
|
||||
chill.aside_activity.export.type_aggregator:
|
||||
class: Chill\AsideActivityBundle\Export\Aggregator\ByActivityTypeAggregator
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_type_aggregator }
|
||||
- { name: chill.export_aggregator, alias: activity_type_aggregator }
|
||||
|
||||
chill.aside_activity.export.user_job_aggregator:
|
||||
class: Chill\AsideActivityBundle\Export\Aggregator\ByUserJobAggregator
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: aside_activity_user_job_aggregator }
|
||||
|
||||
chill.aside_activity.export.user_scope_aggregator:
|
||||
class: Chill\AsideActivityBundle\Export\Aggregator\ByUserScopeAggregator
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: aside_activity_user_scope_aggregator }
|
||||
|
@@ -29,16 +29,16 @@ location: Lieu
|
||||
|
||||
# Crud
|
||||
crud:
|
||||
aside_activity:
|
||||
title_view: Détail de l'activité annexe
|
||||
title_new: Nouvelle activité annexe
|
||||
title_edit: Édition d'une activité annexe
|
||||
title_delete: Supprimer une activité annexe
|
||||
button_delete: Supprimer
|
||||
confirm_message_delete: Êtes-vous sûr de vouloir supprimer cette activité annexe?
|
||||
aside_activity_category:
|
||||
title_new: Nouvelle catégorie d'activité annexe
|
||||
title_edit: Édition d'une catégorie de type d'activité
|
||||
aside_activity:
|
||||
title_view: Détail de l'activité annexe
|
||||
title_new: Nouvelle activité annexe
|
||||
title_edit: Édition d'une activité annexe
|
||||
title_delete: Supprimer une activité annexe
|
||||
button_delete: Supprimer
|
||||
confirm_message_delete: Êtes-vous sûr de vouloir supprimer cette activité annexe?
|
||||
aside_activity_category:
|
||||
title_new: Nouvelle catégorie d'activité annexe
|
||||
title_edit: Édition d'une catégorie de type d'activité
|
||||
|
||||
#forms
|
||||
Create a new aside activity type: Nouvelle categorie d'activité annexe
|
||||
@@ -169,18 +169,43 @@ Aside activity configuration: Configuration des activités annexes
|
||||
|
||||
# exports
|
||||
export:
|
||||
Exports of aside activities: Exports des activités annexes
|
||||
Count aside activities: Nombre d'activités annexes
|
||||
Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères
|
||||
filter:
|
||||
Filter by aside activity date: Filtrer les activités annexes par date
|
||||
Filter by aside activity type: Filtrer les activités annexes par type d'activité
|
||||
'Filtered by aside activity type: only %type%': "Filtré par type d'activité annexe: uniquement %type%"
|
||||
This date should be after the date given in "Implied in an aside activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "activités annexes après cette date"
|
||||
Aside activities after this date: Actvitités annexes après cette date
|
||||
Aside activities before this date: Actvitités annexes avant cette date
|
||||
aggregator:
|
||||
Group by aside activity type: Grouper les activités annexes par type d'activité
|
||||
Aside activity type: Type d'activité annexe
|
||||
aside_activity:
|
||||
List of aside activities: Liste des activités annexes
|
||||
createdAt: Création
|
||||
updatedAt: Dernière mise à jour
|
||||
agent_id: Utilisateur
|
||||
creator_id: Créateur
|
||||
main_scope: Service principal de l'utilisateur
|
||||
main_center: Centre principal de l'utilisteur
|
||||
aside_activity_type: Catégorie d'activité annexe
|
||||
date: Date
|
||||
duration: Durée
|
||||
note: Note
|
||||
|
||||
Exports of aside activities: Exports des activités annexes
|
||||
Count aside activities: Nombre d'activités annexes
|
||||
Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères
|
||||
Average aside activities duration: Durée moyenne des activités annexes
|
||||
Sum aside activities duration: Durée des activités annexes
|
||||
filter:
|
||||
Filter by aside activity date: Filtrer les activités annexes par date
|
||||
Filter by aside activity type: Filtrer les activités annexes par type d'activité
|
||||
'Filtered by aside activity type: only %type%': "Filtré par type d'activité annexe: uniquement %type%"
|
||||
Filtered by aside activities between %dateFrom% and %dateTo%: Filtré par date d'activité annexe, entre %dateFrom% et %dateTo%
|
||||
This date should be after the date given in "Implied in an aside activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "activités annexes après cette date"
|
||||
Aside activities after this date: Actvitités annexes après cette date
|
||||
Aside activities before this date: Actvitités annexes avant cette date
|
||||
'Filtered aside activity by user: only %users%': "Filtré par utilisateur: uniquement %users%"
|
||||
Filter aside activity by user: Filtrer par utilisateur
|
||||
'Filtered aside activities by user jobs: only %jobs%': "Filtré par métier des utilisateurs: uniquement %jobs%"
|
||||
Filter by user jobs: Filtrer les activités annexes par métier des utilisateurs
|
||||
'Filtered aside activities by user scope: only %scopes%': "Filtré par service des utilisateur: uniquement %scopes%"
|
||||
Filter by user scope: Filtrer les activités annexes par service d'utilisateur
|
||||
aggregator:
|
||||
Group by aside activity type: Grouper les activités annexes par type d'activité
|
||||
Aside activity type: Type d'activité annexe
|
||||
Aggregate by user job: Grouper les activités annexes par métier des utilisateurs
|
||||
Aggregate by user scope: Grouper les activités annexes par service des utilisateurs
|
||||
|
||||
# ROLES
|
||||
CHILL_ASIDE_ACTIVITY_STATS: Statistiques pour les activités annexes
|
||||
|
@@ -1,102 +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\BudgetBundle\Config;
|
||||
|
||||
class ConfigRepository
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $charges;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $resources;
|
||||
|
||||
public function __construct($resources, $charges)
|
||||
{
|
||||
$this->resources = $resources;
|
||||
$this->charges = $charges;
|
||||
}
|
||||
|
||||
public function getChargesKeys(bool $onlyActive = false): array
|
||||
{
|
||||
return array_map(static function ($element) {
|
||||
return $element['key'];
|
||||
}, $this->getCharges($onlyActive));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array where keys are the resource'key and label the ressource label
|
||||
*/
|
||||
public function getChargesLabels(bool $onlyActive = false)
|
||||
{
|
||||
$charges = [];
|
||||
|
||||
foreach ($this->getCharges($onlyActive) as $definition) {
|
||||
$charges[$definition['key']] = $this->normalizeLabel($definition['labels']);
|
||||
}
|
||||
|
||||
return $charges;
|
||||
}
|
||||
|
||||
public function getResourcesKeys(bool $onlyActive = false): array
|
||||
{
|
||||
return array_map(static function ($element) {
|
||||
return $element['key'];
|
||||
}, $this->getResources($onlyActive));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array where keys are the resource'key and label the ressource label
|
||||
*/
|
||||
public function getResourcesLabels(bool $onlyActive = false)
|
||||
{
|
||||
$resources = [];
|
||||
|
||||
foreach ($this->getResources($onlyActive) as $definition) {
|
||||
$resources[$definition['key']] = $this->normalizeLabel($definition['labels']);
|
||||
}
|
||||
|
||||
return $resources;
|
||||
}
|
||||
|
||||
private function getCharges(bool $onlyActive = false): array
|
||||
{
|
||||
return $onlyActive ?
|
||||
array_filter($this->charges, static function ($el) {
|
||||
return $el['active'];
|
||||
})
|
||||
: $this->charges;
|
||||
}
|
||||
|
||||
private function getResources(bool $onlyActive = false): array
|
||||
{
|
||||
return $onlyActive ?
|
||||
array_filter($this->resources, static function ($el) {
|
||||
return $el['active'];
|
||||
})
|
||||
: $this->resources;
|
||||
}
|
||||
|
||||
private function normalizeLabel($labels)
|
||||
{
|
||||
$normalizedLabels = [];
|
||||
|
||||
foreach ($labels as $labelDefinition) {
|
||||
$normalizedLabels[$labelDefinition['lang']] = $labelDefinition['label'];
|
||||
}
|
||||
|
||||
return $normalizedLabels;
|
||||
}
|
||||
}
|
@@ -35,7 +35,6 @@ class ChillBudgetExtension extends Extension implements PrependExtensionInterfac
|
||||
$config = $this->processConfiguration($configuration, $configs);
|
||||
|
||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../config'));
|
||||
$loader->load('services/config.yaml');
|
||||
$loader->load('services/form.yaml');
|
||||
$loader->load('services/repository.yaml');
|
||||
$loader->load('services/security.yaml');
|
||||
|
@@ -75,7 +75,7 @@ abstract class AbstractElement
|
||||
/**
|
||||
* @ORM\Column(name="type", type="string", length=255)
|
||||
*/
|
||||
private string $type;
|
||||
private string $type = '';
|
||||
|
||||
/*Getters and Setters */
|
||||
|
||||
|
@@ -12,12 +12,17 @@ declare(strict_types=1);
|
||||
namespace Chill\BudgetBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
* Type of charge.
|
||||
*
|
||||
* @ORM\Table(name="chill_budget.charge_type")
|
||||
* @ORM\Table(name="chill_budget.charge_type",
|
||||
* uniqueConstraints={@ORM\UniqueConstraint(name="charge_kind_unique_type_idx", fields={"kind"})}
|
||||
* )
|
||||
* @ORM\Entity
|
||||
* @UniqueEntity(fields={"kind"})
|
||||
*/
|
||||
class ChargeKind
|
||||
{
|
||||
@@ -35,6 +40,8 @@ class ChargeKind
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, options={"default": ""}, nullable=false)
|
||||
* @Assert\Regex(pattern="/^[a-z0-9\-_]{1,}$/", message="budget.admin.form.kind.only_alphanumeric")
|
||||
* @Assert\Length(min=3)
|
||||
*/
|
||||
private string $kind = '';
|
||||
|
||||
|
@@ -12,12 +12,17 @@ declare(strict_types=1);
|
||||
namespace Chill\BudgetBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
* Type of resource.
|
||||
*
|
||||
* @ORM\Table(name="chill_budget.resource_type")
|
||||
* @ORM\Table(name="chill_budget.resource_type", uniqueConstraints={
|
||||
* @ORM\UniqueConstraint(name="resource_kind_unique_type_idx", fields={"kind"})
|
||||
* })
|
||||
* @ORM\Entity
|
||||
* @UniqueEntity(fields={"kind"})
|
||||
*/
|
||||
class ResourceKind
|
||||
{
|
||||
@@ -35,6 +40,8 @@ class ResourceKind
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=false, options={"default": ""})
|
||||
* @Assert\Regex(pattern="/^[a-z0-9\-_]{1,}$/", message="budget.admin.form.kind.only_alphanumeric")
|
||||
* @Assert\Length(min=3)
|
||||
*/
|
||||
private string $kind = '';
|
||||
|
||||
|
@@ -16,6 +16,7 @@ use Chill\MainBundle\Form\Type\TranslatableStringFormType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
@@ -25,7 +26,11 @@ class ChargeKindType extends AbstractType
|
||||
{
|
||||
$builder
|
||||
->add('name', TranslatableStringFormType::class, [
|
||||
'label' => 'Nom',
|
||||
'label' => 'Title',
|
||||
])
|
||||
->add('kind', TextType::class, [
|
||||
'label' => 'budget.admin.form.Charge_kind_key',
|
||||
'help' => 'budget.admin.form.This kind must contains only alphabeticals characters, and dashes. This string is in use during document generation. Changes may have side effect on document'
|
||||
])
|
||||
->add('ordering', NumberType::class)
|
||||
->add('isActive', CheckboxType::class, [
|
||||
|
@@ -16,6 +16,7 @@ use Chill\MainBundle\Form\Type\TranslatableStringFormType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
@@ -25,7 +26,11 @@ class ResourceKindType extends AbstractType
|
||||
{
|
||||
$builder
|
||||
->add('name', TranslatableStringFormType::class, [
|
||||
'label' => 'Nom',
|
||||
'label' => 'Title',
|
||||
])
|
||||
->add('kind', TextType::class, [
|
||||
'label' => 'budget.admin.form.Resource_kind_key',
|
||||
'help' => 'budget.admin.form.This kind must contains only alphabeticals characters, and dashes. This string is in use during document generation. Changes may have side effect on document'
|
||||
])
|
||||
->add('ordering', NumberType::class)
|
||||
->add('isActive', CheckboxType::class, [
|
||||
|
@@ -11,7 +11,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\BudgetBundle\Form;
|
||||
|
||||
use Chill\BudgetBundle\Config\ConfigRepository;
|
||||
use Chill\BudgetBundle\Entity\Charge;
|
||||
use Chill\BudgetBundle\Entity\ChargeKind;
|
||||
use Chill\BudgetBundle\Repository\ChargeKindRepository;
|
||||
@@ -25,13 +24,9 @@ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function array_flip;
|
||||
use function asort;
|
||||
|
||||
class ChargeType extends AbstractType
|
||||
{
|
||||
protected ConfigRepository $configRepository;
|
||||
|
||||
protected TranslatableStringHelperInterface $translatableStringHelper;
|
||||
|
||||
private ChargeKindRepository $repository;
|
||||
@@ -39,12 +34,10 @@ class ChargeType extends AbstractType
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(
|
||||
ConfigRepository $configRepository,
|
||||
TranslatableStringHelperInterface $translatableStringHelper,
|
||||
ChargeKindRepository $repository,
|
||||
TranslatorInterface $translator
|
||||
) {
|
||||
$this->configRepository = $configRepository;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->repository = $repository;
|
||||
$this->translator = $translator;
|
||||
@@ -116,19 +109,4 @@ class ChargeType extends AbstractType
|
||||
{
|
||||
return 'chill_budgetbundle_charge';
|
||||
}
|
||||
|
||||
private function getTypes()
|
||||
{
|
||||
$charges = $this->configRepository
|
||||
->getChargesLabels(true);
|
||||
|
||||
// rewrite labels to filter in language
|
||||
foreach ($charges as $key => $labels) {
|
||||
$charges[$key] = $this->translatableStringHelper->localize($labels);
|
||||
}
|
||||
|
||||
asort($charges);
|
||||
|
||||
return array_flip($charges);
|
||||
}
|
||||
}
|
||||
|
@@ -11,7 +11,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\BudgetBundle\Form;
|
||||
|
||||
use Chill\BudgetBundle\Config\ConfigRepository;
|
||||
use Chill\BudgetBundle\Entity\Resource;
|
||||
use Chill\BudgetBundle\Entity\ResourceKind;
|
||||
use Chill\BudgetBundle\Repository\ResourceKindRepository;
|
||||
@@ -24,12 +23,9 @@ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function array_flip;
|
||||
|
||||
class ResourceType extends AbstractType
|
||||
{
|
||||
protected ConfigRepository $configRepository;
|
||||
|
||||
protected TranslatableStringHelperInterface $translatableStringHelper;
|
||||
|
||||
private ResourceKindRepository $repository;
|
||||
@@ -37,12 +33,10 @@ class ResourceType extends AbstractType
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(
|
||||
ConfigRepository $configRepository,
|
||||
TranslatableStringHelperInterface $translatableStringHelper,
|
||||
ResourceKindRepository $repository,
|
||||
TranslatorInterface $translator
|
||||
) {
|
||||
$this->configRepository = $configRepository;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->repository = $repository;
|
||||
$this->translator = $translator;
|
||||
@@ -98,19 +92,4 @@ class ResourceType extends AbstractType
|
||||
{
|
||||
return 'chill_budgetbundle_resource';
|
||||
}
|
||||
|
||||
private function getTypes()
|
||||
{
|
||||
$resources = $this->configRepository
|
||||
->getResourcesLabels(true);
|
||||
|
||||
// rewrite labels to filter in language
|
||||
foreach ($resources as $key => $labels) {
|
||||
$resources[$key] = $this->translatableStringHelper->localize($labels);
|
||||
}
|
||||
|
||||
asort($resources);
|
||||
|
||||
return array_flip($resources);
|
||||
}
|
||||
}
|
||||
|
@@ -14,9 +14,8 @@ namespace Chill\BudgetBundle\Repository;
|
||||
use Chill\BudgetBundle\Entity\ChargeKind;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
class ChargeKindRepository implements ObjectRepository
|
||||
final class ChargeKindRepository implements ChargeKindRepositoryInterface
|
||||
{
|
||||
private EntityRepository $repository;
|
||||
|
||||
@@ -50,7 +49,8 @@ class ChargeKindRepository implements ObjectRepository
|
||||
->where($qb->expr()->eq('c.isActive', 'true'))
|
||||
->orderBy('c.ordering', 'ASC')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
->getResult()
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,6 +77,11 @@ class ChargeKindRepository implements ObjectRepository
|
||||
return $this->repository->findOneBy($criteria);
|
||||
}
|
||||
|
||||
public function findOneByKind(string $kind): ?ChargeKind
|
||||
{
|
||||
return $this->repository->findOneBy(['kind' => $kind]);
|
||||
}
|
||||
|
||||
public function getClassName(): string
|
||||
{
|
||||
return ChargeKind::class;
|
||||
|
@@ -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\BudgetBundle\Repository;
|
||||
|
||||
use Chill\BudgetBundle\Entity\ChargeKind;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
interface ChargeKindRepositoryInterface extends ObjectRepository
|
||||
{
|
||||
public function find($id): ?ChargeKind;
|
||||
|
||||
/**
|
||||
* @return ChargeType[]
|
||||
*/
|
||||
public function findAll(): array;
|
||||
|
||||
/**
|
||||
* @return ChargeType[]
|
||||
*/
|
||||
public function findAllActive(): array;
|
||||
|
||||
public function findOneByKind(string $kind): ?ChargeKind;
|
||||
|
||||
/**
|
||||
* @return ChargeType[]
|
||||
*/
|
||||
public function findAllByType(string $type): array;
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
*
|
||||
* @return ChargeType[]
|
||||
*/
|
||||
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array;
|
||||
|
||||
public function findOneBy(array $criteria): ?ChargeKind;
|
||||
|
||||
public function getClassName(): string;
|
||||
}
|
@@ -14,9 +14,8 @@ namespace Chill\BudgetBundle\Repository;
|
||||
use Chill\BudgetBundle\Entity\ResourceKind;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
class ResourceKindRepository implements ObjectRepository
|
||||
final class ResourceKindRepository implements ResourceKindRepositoryInterface
|
||||
{
|
||||
private EntityRepository $repository;
|
||||
|
||||
@@ -50,7 +49,8 @@ class ResourceKindRepository implements ObjectRepository
|
||||
->where($qb->expr()->eq('r.isActive', 'true'))
|
||||
->orderBy('r.ordering', 'ASC')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
->getResult()
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,6 +77,11 @@ class ResourceKindRepository implements ObjectRepository
|
||||
return $this->repository->findOneBy($criteria);
|
||||
}
|
||||
|
||||
public function findOneByKind(string $kind): ?ResourceKind
|
||||
{
|
||||
return $this->repository->findOneBy(['kind' => $kind]);
|
||||
}
|
||||
|
||||
public function getClassName(): string
|
||||
{
|
||||
return ResourceKind::class;
|
||||
|
@@ -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\BudgetBundle\Repository;
|
||||
|
||||
use Chill\BudgetBundle\Entity\ResourceKind;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
interface ResourceKindRepositoryInterface extends ObjectRepository
|
||||
{
|
||||
public function find($id): ?ResourceKind;
|
||||
|
||||
/**
|
||||
* @return ResourceType[]
|
||||
*/
|
||||
public function findAll(): array;
|
||||
|
||||
/**
|
||||
* @return ResourceType[]
|
||||
*/
|
||||
public function findAllActive(): array;
|
||||
|
||||
/**
|
||||
* @return ResourceType[]
|
||||
*/
|
||||
public function findAllByType(string $type): array;
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
*
|
||||
* @return ResourceType[]
|
||||
*/
|
||||
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array;
|
||||
|
||||
public function findOneBy(array $criteria): ?ResourceKind;
|
||||
|
||||
public function findOneByKind(string $kind): ?ResourceKind;
|
||||
|
||||
public function getClassName(): string;
|
||||
}
|
@@ -34,7 +34,7 @@ class ResourceRepository extends EntityRepository
|
||||
//->andWhere('c.startDate < :date')
|
||||
// TODO: there is a misconception here, the end date must be lower or null. startDate are never null
|
||||
//->andWhere('c.startDate < :date OR c.startDate IS NULL');
|
||||
;
|
||||
;
|
||||
|
||||
if (null !== $sort) {
|
||||
$qb->orderBy($sort);
|
||||
|
@@ -19,7 +19,10 @@
|
||||
{% for entity in entities %}
|
||||
<tr>
|
||||
<td>{{ entity.ordering }}</td>
|
||||
<td>{{ entity|chill_entity_render_box }}</td>
|
||||
<td>
|
||||
{{ entity|chill_entity_render_box }}<br/>
|
||||
<strong>{{ 'budget.admin.form.Charge_kind_key'|trans }} :</strong> <code>{{ entity.kind }}</code>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
{%- if entity.isActive -%}
|
||||
<i class="fa fa-check-square-o"></i>
|
||||
@@ -39,6 +42,8 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{ chill_pagination(paginator) }}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li>
|
||||
<a href="{{ path('chill_crud_charge_kind_new') }}" class="btn btn-create">
|
||||
|
@@ -19,7 +19,10 @@
|
||||
{% for entity in entities %}
|
||||
<tr>
|
||||
<td>{{ entity.ordering }}</td>
|
||||
<td>{{ entity|chill_entity_render_box }}</td>
|
||||
<td>
|
||||
{{ entity|chill_entity_render_box }}<br/>
|
||||
<strong>{{ 'budget.admin.form.Resource_kind_key'|trans }} :</strong> <code>{{ entity.kind }}</code>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
{%- if entity.isActive -%}
|
||||
<i class="fa fa-check-square-o"></i>
|
||||
@@ -39,6 +42,8 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{ chill_pagination(paginator) }}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li>
|
||||
<a href="{{ path('chill_crud_resource_kind_new') }}" class="btn btn-create">
|
||||
|
@@ -11,45 +11,52 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\BudgetBundle\Service\Summary;
|
||||
|
||||
use Chill\BudgetBundle\Config\ConfigRepository;
|
||||
use Chill\BudgetBundle\Entity\ChargeKind;
|
||||
use Chill\BudgetBundle\Entity\ResourceKind;
|
||||
use Chill\BudgetBundle\Repository\ChargeKindRepository;
|
||||
use Chill\BudgetBundle\Repository\ChargeKindRepositoryInterface;
|
||||
use Chill\BudgetBundle\Repository\ResourceKindRepository;
|
||||
use Chill\BudgetBundle\Repository\ResourceKindRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use LogicException;
|
||||
use RuntimeException;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Helps to find a summary of the budget: the sum of resources and charges.
|
||||
*/
|
||||
class SummaryBudget implements SummaryBudgetInterface
|
||||
final class SummaryBudget implements SummaryBudgetInterface
|
||||
{
|
||||
private const QUERY_CHARGE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.charge WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
|
||||
private const QUERY_CHARGE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, charge_id AS kind_id FROM chill_budget.charge WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY charge_id';
|
||||
|
||||
private const QUERY_CHARGE_BY_PERSON = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.charge WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
|
||||
private const QUERY_CHARGE_BY_PERSON = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, charge_id AS kind_id FROM chill_budget.charge WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY charge_id';
|
||||
|
||||
private const QUERY_RESOURCE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.resource WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
|
||||
private const QUERY_RESOURCE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, resource_id AS kind_id FROM chill_budget.resource WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY resource_id';
|
||||
|
||||
private const QUERY_RESOURCE_BY_PERSON = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.resource WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
|
||||
private const QUERY_RESOURCE_BY_PERSON = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, resource_id AS kind_id FROM chill_budget.resource WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY resource_id';
|
||||
|
||||
private array $chargeLabels;
|
||||
|
||||
private ConfigRepository $configRepository;
|
||||
private ChargeKindRepositoryInterface $chargeKindRepository;
|
||||
|
||||
private EntityManagerInterface $em;
|
||||
|
||||
private array $resourcesLabels;
|
||||
private ResourceKindRepositoryInterface $resourceKindRepository;
|
||||
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, ConfigRepository $configRepository, TranslatableStringHelperInterface $translatableStringHelper)
|
||||
{
|
||||
public function __construct(
|
||||
EntityManagerInterface $em,
|
||||
TranslatableStringHelperInterface $translatableStringHelper,
|
||||
ResourceKindRepositoryInterface $resourceKindRepository,
|
||||
ChargeKindRepositoryInterface $chargeKindRepository
|
||||
) {
|
||||
$this->em = $em;
|
||||
$this->configRepository = $configRepository;
|
||||
$this->chargeLabels = $configRepository->getChargesLabels();
|
||||
$this->resourcesLabels = $configRepository->getResourcesLabels();
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->resourceKindRepository = $resourceKindRepository;
|
||||
$this->chargeKindRepository = $chargeKindRepository;
|
||||
}
|
||||
|
||||
public function getSummaryForHousehold(?Household $household): array
|
||||
@@ -112,7 +119,7 @@ class SummaryBudget implements SummaryBudgetInterface
|
||||
$rsm = new ResultSetMapping();
|
||||
$rsm
|
||||
->addScalarResult('sum', 'sum')
|
||||
->addScalarResult('type', 'type')
|
||||
->addScalarResult('kind_id', 'kind_id')
|
||||
->addScalarResult('comment', 'comment');
|
||||
|
||||
return $rsm;
|
||||
@@ -120,51 +127,62 @@ class SummaryBudget implements SummaryBudgetInterface
|
||||
|
||||
private function getEmptyChargeArray(): array
|
||||
{
|
||||
$keys = $this->configRepository->getChargesKeys();
|
||||
$labels = $this->chargeLabels;
|
||||
$keys = array_map(static fn (ChargeKind $kind) => $kind->getKind(), $this->chargeKindRepository->findAll());
|
||||
|
||||
return array_combine($keys, array_map(function ($i) use ($labels) {
|
||||
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i]), 'comment' => ''];
|
||||
return array_combine($keys, array_map(function ($kind) {
|
||||
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->chargeKindRepository->findOneByKind($kind)->getName()), 'comment' => ''];
|
||||
}, $keys));
|
||||
}
|
||||
|
||||
private function getEmptyResourceArray(): array
|
||||
{
|
||||
$keys = $this->configRepository->getResourcesKeys();
|
||||
$labels = $this->resourcesLabels;
|
||||
$keys = array_map(static fn (ResourceKind $kind) => $kind->getKind(), $this->resourceKindRepository->findAll());
|
||||
|
||||
return array_combine($keys, array_map(function ($i) use ($labels) {
|
||||
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i]), 'comment' => ''];
|
||||
return array_combine($keys, array_map(function ($kind) {
|
||||
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->resourceKindRepository->findOneByKind($kind)->getName()), 'comment' => ''];
|
||||
}, $keys));
|
||||
}
|
||||
|
||||
private function rowToArray(array $rows, string $kind): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
switch ($kind) {
|
||||
case 'charge':
|
||||
$label = $this->chargeLabels;
|
||||
foreach ($rows as $row) {
|
||||
$chargeKind = $this->chargeKindRepository->find($row['kind_id']);
|
||||
|
||||
break;
|
||||
if (null === $chargeKind) {
|
||||
throw new RuntimeException('charge kind not found: ' . $row['kind_id']);
|
||||
}
|
||||
$result[$chargeKind->getKind()] = [
|
||||
'sum' => (float) $row['sum'],
|
||||
'label' => $this->translatableStringHelper->localize($chargeKind->getName()),
|
||||
'comment' => (string) $row['comment'],
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
case 'resource':
|
||||
$label = $this->resourcesLabels;
|
||||
foreach ($rows as $row) {
|
||||
$resourceKind = $this->resourceKindRepository->find($row['kind_id']);
|
||||
|
||||
break;
|
||||
if (null === $resourceKind) {
|
||||
throw new RuntimeException('charge kind not found: ' . $row['kind_id']);
|
||||
}
|
||||
|
||||
$result[$resourceKind->getKind()] = [
|
||||
'sum' => (float) $row['sum'],
|
||||
'label' => $this->translatableStringHelper->localize($resourceKind->getName()),
|
||||
'comment' => (string) $row['comment'],
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
default:
|
||||
throw new LogicException();
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$result[$row['type']] = [
|
||||
'sum' => (float) $row['sum'],
|
||||
'label' => $this->translatableStringHelper->localize($label[$row['type']]),
|
||||
'comment' => (string) $row['comment'],
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@@ -1,65 +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\BudgetBundle\Templating;
|
||||
|
||||
use Chill\BudgetBundle\Config\ConfigRepository;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
use UnexpectedValueException;
|
||||
|
||||
class Twig extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* @var ConfigRepository
|
||||
*/
|
||||
protected $configRepository;
|
||||
|
||||
/**
|
||||
* @var TranslatableStringHelper
|
||||
*/
|
||||
protected $translatableStringHelper;
|
||||
|
||||
public function __construct(
|
||||
ConfigRepository $configRepository,
|
||||
TranslatableStringHelper $translatableStringHelper
|
||||
) {
|
||||
$this->configRepository = $configRepository;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function displayLink($link, $family)
|
||||
{
|
||||
switch ($family) {
|
||||
case 'resource':
|
||||
return $this->translatableStringHelper->localize(
|
||||
$this->configRepository->getResourcesLabels()[$link]
|
||||
);
|
||||
|
||||
case 'charge':
|
||||
return $this->translatableStringHelper->localize(
|
||||
$this->configRepository->getChargesLabels()[$link]
|
||||
);
|
||||
|
||||
default:
|
||||
throw new UnexpectedValueException("This family of element: {$family} is not "
|
||||
. "supported. Supported families are 'resource', 'charge'");
|
||||
}
|
||||
}
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new TwigFilter('budget_element_type_display', [$this, 'displayLink'], ['is_safe' => ['html']]),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,158 @@
|
||||
<?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\BudgetBundle\Tests\Service\Summary;
|
||||
|
||||
use Chill\BudgetBundle\Entity\ChargeKind;
|
||||
use Chill\BudgetBundle\Entity\ResourceKind;
|
||||
use Chill\BudgetBundle\Repository\ChargeKindRepositoryInterface;
|
||||
use Chill\BudgetBundle\Repository\ResourceKindRepositoryInterface;
|
||||
use Chill\BudgetBundle\Service\Summary\SummaryBudget;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
final class SummaryBudgetTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
public function testGenerateSummaryForPerson(): void
|
||||
{
|
||||
$queryCharges = $this->prophesize(AbstractQuery::class);
|
||||
$queryCharges->getResult()->willReturn([
|
||||
[
|
||||
'sum' => 250.0,
|
||||
'comment' => '',
|
||||
'kind_id' => 1, // kind: rental
|
||||
],
|
||||
]);
|
||||
$queryCharges->setParameters(Argument::type('array'))
|
||||
->will(function ($args, $query) {
|
||||
return $query;
|
||||
})
|
||||
;
|
||||
|
||||
$queryResources = $this->prophesize(AbstractQuery::class);
|
||||
$queryResources->getResult()->willReturn([
|
||||
[
|
||||
'sum' => 1500.0,
|
||||
'comment' => '',
|
||||
'kind_id' => 2, // kind: 'salary',
|
||||
],
|
||||
]);
|
||||
$queryResources->setParameters(Argument::type('array'))
|
||||
->will(function ($args, $query) {
|
||||
return $query;
|
||||
})
|
||||
;
|
||||
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->createNativeQuery(Argument::type('string'), Argument::type(Query\ResultSetMapping::class))
|
||||
->will(function ($args) use ($queryResources, $queryCharges) {
|
||||
if (false !== strpos($args[0], 'chill_budget.resource')) {
|
||||
return $queryResources->reveal();
|
||||
}
|
||||
if (false !== strpos($args[0], 'chill_budget.charge')) {
|
||||
return $queryCharges->reveal();
|
||||
}
|
||||
throw new \RuntimeException('this query does not have a stub counterpart: '.$args[0]);
|
||||
})
|
||||
;
|
||||
|
||||
$chargeRepository = $this->prophesize(ChargeKindRepositoryInterface::class);
|
||||
$chargeRepository->findAll()->willReturn([
|
||||
$rental = (new ChargeKind())->setKind('rental')->setName(['fr' => 'Rental']),
|
||||
$other = (new ChargeKind())->setKind('other')->setName(['fr' => 'Other']),
|
||||
]);
|
||||
$chargeRepository->find(1)->willReturn($rental);
|
||||
$chargeRepository->findOneByKind('rental')->willReturn($rental);
|
||||
$chargeRepository->findOneByKind('other')->willReturn($other);
|
||||
|
||||
$resourceRepository = $this->prophesize(ResourceKindRepositoryInterface::class);
|
||||
$resourceRepository->findAll()->willReturn([
|
||||
$salary = (new ResourceKind())->setKind('salary')->setName(['fr' => 'Salary']),
|
||||
$misc = (new ResourceKind())->setKind('misc')->setName(['fr' => 'Misc']),
|
||||
]);
|
||||
$resourceRepository->find(2)->willReturn($salary);
|
||||
$resourceRepository->findOneByKind('salary')->willReturn($salary);
|
||||
$resourceRepository->findOneByKind('misc')->willReturn($misc);
|
||||
|
||||
$translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class);
|
||||
$translatableStringHelper->localize(Argument::type('array'))->will(function ($arg) {
|
||||
return $arg[0]['fr'];
|
||||
});
|
||||
|
||||
$person = new Person();
|
||||
$personReflection = new \ReflectionClass($person);
|
||||
$personIdReflection = $personReflection->getProperty('id');
|
||||
$personIdReflection->setAccessible(true);
|
||||
$personIdReflection->setValue($person, 1);
|
||||
|
||||
$household = new Household();
|
||||
$householdReflection = new \ReflectionClass($household);
|
||||
$householdId = $householdReflection->getProperty('id');
|
||||
$householdId->setAccessible(true);
|
||||
$householdId->setValue($household, 1);
|
||||
$householdMember = (new HouseholdMember())->setPerson($person)
|
||||
->setStartDate(new \DateTimeImmutable('1 month ago'))
|
||||
;
|
||||
$household->addMember($householdMember);
|
||||
|
||||
$summaryBudget = new SummaryBudget(
|
||||
$em->reveal(),
|
||||
$translatableStringHelper->reveal(),
|
||||
$resourceRepository->reveal(),
|
||||
$chargeRepository->reveal()
|
||||
);
|
||||
|
||||
$summary = $summaryBudget->getSummaryForPerson($person);
|
||||
$summaryForHousehold = $summaryBudget->getSummaryForHousehold($household);
|
||||
|
||||
// we check the structure for the summary. The structure is the same for household
|
||||
// and persons
|
||||
|
||||
$expected = [
|
||||
'charges' => [
|
||||
'rental' => ['sum' => 250.0, 'comment' => '', 'label' => 'Rental'],
|
||||
'other' => ['sum' => 0.0, 'comment' => '', 'label' => 'Other'],
|
||||
],
|
||||
'resources' => [
|
||||
'salary' => ['sum' => 1500.0, 'comment' => '', 'label' => 'Salary'],
|
||||
'misc' => ['sum' => 0.0, 'comment' => '', 'label' => 'Misc'],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ([$summaryForHousehold, $summary] as $summary) {
|
||||
$this->assertIsArray($summary);
|
||||
$this->assertEqualsCanonicalizing(['charges', 'resources'], array_keys($summary));
|
||||
$this->assertEqualsCanonicalizing(['rental', 'other'], array_keys($summary['charges']));
|
||||
$this->assertEqualsCanonicalizing(['salary', 'misc'], array_keys($summary['resources']));
|
||||
|
||||
foreach ($expected as $resCha => $contains) {
|
||||
foreach ($contains as $kind => $row) {
|
||||
$this->assertEqualsCanonicalizing($row, $summary[$resCha][$kind]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
services:
|
||||
Chill\BudgetBundle\Config\ConfigRepository:
|
||||
arguments:
|
||||
$resources: '%chill_budget.resources%'
|
||||
$charges: '%chill_budget.charges%'
|
@@ -0,0 +1,37 @@
|
||||
<?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\Budget;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20230201131008 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this shouldn't be undone.
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Fix the comment on tags column in resource and charge type';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('COMMENT ON COLUMN chill_budget.resource_type.tags IS \'(DC2Type:json)\'');
|
||||
$this->addSql('COMMENT ON COLUMN chill_budget.charge_type.tags IS \'(DC2Type:json)\'');
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Budget;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20230209161546 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Budget: add unique constraint on kind for charge_kind and resource_kind';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql("UPDATE chill_budget.resource_type SET kind=md5(random()::text) WHERE kind = ''");
|
||||
$this->addSql("UPDATE chill_budget.charge_type SET kind=md5(random()::text) WHERE kind = ''");
|
||||
$this->addSql('CREATE UNIQUE INDEX resource_kind_unique_type_idx ON chill_budget.resource_type (kind);');
|
||||
$this->addSql('CREATE UNIQUE INDEX charge_kind_unique_type_idx ON chill_budget.charge_type (kind);');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP INDEX resource_kind_unique_type_idx');
|
||||
$this->addSql('DROP INDEX charge_kind_unique_type_idx');
|
||||
}
|
||||
}
|
@@ -77,6 +77,20 @@ The balance: Différence entre ressources et charges
|
||||
Valid since %startDate% until %endDate%: Valide depuis le %startDate% jusqu'au %endDate%
|
||||
Valid since %startDate%: Valide depuis le %startDate%
|
||||
|
||||
budget:
|
||||
admin:
|
||||
form:
|
||||
Charge_kind_key: Clé d'identification
|
||||
Resource_kind_key: Clé d'identification
|
||||
This kind must contains only alphabeticals characters, and dashes. This string is in use during document generation. Changes may have side effect on document: Cette clé sert à identifier le type de charge ou de revenu lors de la génération de document. Seuls les caractères alpha-numériques sont autorisés. Modifier cette clé peut avoir un effet lors de la génération de nouveaux documents.
|
||||
|
||||
# ROLES
|
||||
Budget elements: Budget
|
||||
CHILL_BUDGET_ELEMENT_CREATE: Créer une ressource/charge
|
||||
CHILL_BUDGET_ELEMENT_DELETE: Supprimer une ressource/charge
|
||||
CHILL_BUDGET_ELEMENT_SEE: Voir les ressources/charges
|
||||
CHILL_BUDGET_ELEMENT_UPDATE: Modifier une ressource/charge
|
||||
|
||||
## admin
|
||||
|
||||
crud:
|
||||
|
@@ -1,2 +1,8 @@
|
||||
The amount cannot be empty: Le montant ne peut pas être vide ou égal à zéro
|
||||
The budget element's end date must be after the start date: La date de fin doit être après la date de début
|
||||
The budget element's end date must be after the start date: La date de fin doit être après la date de début
|
||||
|
||||
budget:
|
||||
admin:
|
||||
form:
|
||||
kind:
|
||||
only_alphanumeric
|
||||
|
@@ -272,8 +272,9 @@ final class DocGeneratorTemplateController extends AbstractController
|
||||
}
|
||||
|
||||
if ($isTest && isset($form) && $form['show_data']->getData()) {
|
||||
// very ugly hack...
|
||||
dd($context->getData($template, $entity, $contextGenerationData));
|
||||
return $this->render('@ChillDocGenerator/Generator/debug_value.html.twig', [
|
||||
'datas' => json_encode($context->getData($template, $entity, $contextGenerationData), JSON_PRETTY_PRINT)
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ 'Doc generator debug'|trans }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<pre>{{ datas }}</pre>
|
||||
</body>
|
||||
</html>
|
@@ -0,0 +1,35 @@
|
||||
import {_createI18n} from "../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n";
|
||||
import DocumentActionButtonsGroup from "../../vuejs/DocumentActionButtonsGroup.vue";
|
||||
import {createApp} from "vue";
|
||||
import {StoredObject} from "../../types";
|
||||
|
||||
const i18n = _createI18n({});
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function (e) {
|
||||
document.querySelectorAll<HTMLDivElement>('div[data-download-buttons]').forEach((el) => {
|
||||
const app = createApp({
|
||||
components: {DocumentActionButtonsGroup},
|
||||
data() {
|
||||
|
||||
const datasets = el.dataset as {
|
||||
filename: string,
|
||||
canEdit: string,
|
||||
storedObject: string,
|
||||
small: string,
|
||||
};
|
||||
|
||||
const
|
||||
storedObject = JSON.parse(datasets.storedObject),
|
||||
filename = datasets.filename,
|
||||
canEdit = datasets.canEdit === '1',
|
||||
small = datasets.small === '1'
|
||||
;
|
||||
|
||||
return { storedObject, filename, canEdit, small };
|
||||
},
|
||||
template: '<document-action-buttons-group :can-edit="canEdit" :filename="filename" :stored-object="storedObject" :small="small"></document-action-buttons-group>',
|
||||
});
|
||||
|
||||
app.use(i18n).mount(el);
|
||||
})
|
||||
});
|
25
src/Bundle/ChillDocStoreBundle/Resources/public/types.ts
Normal file
25
src/Bundle/ChillDocStoreBundle/Resources/public/types.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import {DateTime} from "../../../ChillMainBundle/Resources/public/types";
|
||||
|
||||
export interface StoredObject {
|
||||
id: number,
|
||||
|
||||
/**
|
||||
* filename of the object in the object storage
|
||||
*/
|
||||
filename: string,
|
||||
creationDate: DateTime,
|
||||
datas: object,
|
||||
iv: number[],
|
||||
keyInfos: object,
|
||||
title: string,
|
||||
type: string,
|
||||
uuid: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Function executed by the WopiEditButton component.
|
||||
*/
|
||||
export type WopiEditButtonExecutableBeforeLeaveFunction = {
|
||||
(): Promise<void>
|
||||
}
|
||||
|
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="dropdown">
|
||||
<button :class="Object.assign({'btn': true, 'btn-outline-primary': true, 'dropdown-toggle': true, small: props.small})" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Actions
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li v-if="props.canEdit && is_extension_editable(props.storedObject.type)">
|
||||
<wopi-edit-button :stored-object="props.storedObject" :classes="{'dropdown-item': true}" :execute-before-leave="props.executeBeforeLeave"></wopi-edit-button>
|
||||
</li>
|
||||
<li v-if="props.storedObject.type != 'application/pdf' && is_extension_viewable(props.storedObject.type) && props.canConvertPdf">
|
||||
<convert-button :stored-object="props.storedObject" :filename="filename" :classes="{'dropdown-item': true}"></convert-button>
|
||||
</li>
|
||||
<li v-if="props.canDownload">
|
||||
<download-button :stored-object="props.storedObject" :filename="filename" :classes="{'dropdown-item': true}"></download-button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
import ConvertButton from "./StoredObjectButton/ConvertButton.vue";
|
||||
import DownloadButton from "./StoredObjectButton/DownloadButton.vue";
|
||||
import WopiEditButton from "./StoredObjectButton/WopiEditButton.vue";
|
||||
import {is_extension_editable, is_extension_viewable} from "./StoredObjectButton/helpers";
|
||||
import {StoredObject, WopiEditButtonExecutableBeforeLeaveFunction} from "../types";
|
||||
|
||||
interface DocumentActionButtonsGroupConfig {
|
||||
storedObject: StoredObject,
|
||||
small?: boolean,
|
||||
canEdit?: boolean,
|
||||
canDownload?: boolean,
|
||||
canConvertPdf?: boolean,
|
||||
returnPath?: string,
|
||||
|
||||
/**
|
||||
* Will be the filename displayed to the user when he·she download the document
|
||||
* (the document will be saved on his disk with this name)
|
||||
*
|
||||
* If not set, 'document' will be used.
|
||||
*/
|
||||
filename?: string,
|
||||
|
||||
/**
|
||||
* If set, will execute this function before leaving to the editor
|
||||
*/
|
||||
executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction,
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<DocumentActionButtonsGroupConfig>(), {
|
||||
small: false,
|
||||
canEdit: true,
|
||||
canDownload: true,
|
||||
canConvertPdf: true,
|
||||
returnPath: window.location.pathname + window.location.search + window.location.hash,
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -0,0 +1,5 @@
|
||||
# About buttons and components available
|
||||
|
||||
## DocumentActionButtonsGroup
|
||||
|
||||
This is an component to use to render a group of button with actions linked to a document.
|
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<a :class="props.classes" @click="download_and_open($event)">
|
||||
<i class="fa fa-file-pdf-o"></i>
|
||||
Télécharger en pdf
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
import {build_convert_link, download_and_decrypt_doc, download_doc} from "./helpers";
|
||||
import mime from "mime";
|
||||
import {reactive} from "vue";
|
||||
import {StoredObject} from "../../types";
|
||||
|
||||
interface ConvertButtonConfig {
|
||||
storedObject: StoredObject,
|
||||
classes: { [key: string]: boolean},
|
||||
filename?: string,
|
||||
};
|
||||
|
||||
interface DownloadButtonState {
|
||||
content: null|string
|
||||
}
|
||||
|
||||
const props = defineProps<ConvertButtonConfig>();
|
||||
const state: DownloadButtonState = reactive({content: null});
|
||||
|
||||
async function download_and_open(event: Event): Promise<void> {
|
||||
const button = event.target as HTMLAnchorElement;
|
||||
|
||||
if (null === state.content) {
|
||||
event.preventDefault();
|
||||
|
||||
const raw = await download_doc(build_convert_link(props.storedObject.uuid));
|
||||
state.content = window.URL.createObjectURL(raw);
|
||||
|
||||
button.href = window.URL.createObjectURL(raw);
|
||||
button.type = 'application/pdf';
|
||||
|
||||
button.download = (props.filename + '.pdf') || 'document.pdf';
|
||||
}
|
||||
|
||||
button.click();
|
||||
}
|
||||
|
||||
</script>
|
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<a :class="props.classes" @click="download_and_open($event)">
|
||||
<i class="fa fa-download"></i>
|
||||
Télécharger
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {reactive} from "vue";
|
||||
import {build_download_info_link, download_and_decrypt_doc} from "./helpers";
|
||||
import mime from "mime";
|
||||
import {StoredObject} from "../../types";
|
||||
|
||||
interface DownloadButtonConfig {
|
||||
storedObject: StoredObject,
|
||||
classes: {[k: string]: boolean},
|
||||
filename?: string,
|
||||
}
|
||||
|
||||
interface DownloadButtonState {
|
||||
content: null|string
|
||||
}
|
||||
|
||||
const props = defineProps<DownloadButtonConfig>();
|
||||
const state: DownloadButtonState = reactive({content: null});
|
||||
|
||||
async function download_and_open(event: Event): Promise<void> {
|
||||
const button = event.target as HTMLAnchorElement;
|
||||
|
||||
if (null === state.content) {
|
||||
event.preventDefault();
|
||||
|
||||
const urlInfo = build_download_info_link(props.storedObject.filename);
|
||||
|
||||
const raw = await download_and_decrypt_doc(urlInfo, props.storedObject.keyInfos, new Uint8Array(props.storedObject.iv));
|
||||
state.content = window.URL.createObjectURL(raw);
|
||||
|
||||
button.href = window.URL.createObjectURL(raw);
|
||||
button.type = props.storedObject.type;
|
||||
|
||||
if (props.filename !== undefined) {
|
||||
button.download = props.filename || 'document';
|
||||
|
||||
const ext = mime.getExtension(props.storedObject.type);
|
||||
if (null !== ext) {
|
||||
button.download = button.download + '.' + ext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button.click();
|
||||
}
|
||||
</script>
|
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<a :class="Object.assign(props.classes, {'btn': true})" @click="beforeLeave($event)" :href="build_wopi_editor_link(props.storedObject.uuid, props.returnPath)">
|
||||
<i class="fa fa-paragraph"></i>
|
||||
Editer en ligne
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import WopiEditButton from "./WopiEditButton.vue";
|
||||
import {build_wopi_editor_link} from "./helpers";
|
||||
import {StoredObject, WopiEditButtonExecutableBeforeLeaveFunction} from "../../types";
|
||||
|
||||
interface WopiEditButtonConfig {
|
||||
storedObject: StoredObject,
|
||||
returnPath?: string,
|
||||
classes: {[k: string] : boolean},
|
||||
executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction,
|
||||
}
|
||||
|
||||
const props = defineProps<WopiEditButtonConfig>();
|
||||
|
||||
let executed = false;
|
||||
|
||||
async function beforeLeave(event: Event): Promise<true> {
|
||||
console.log(executed);
|
||||
if (props.executeBeforeLeave === undefined || executed === true) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
await props.executeBeforeLeave();
|
||||
executed = true;
|
||||
|
||||
const link = event.target as HTMLAnchorElement;
|
||||
link.click();
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="sass">
|
||||
</style>
|
||||
|
@@ -0,0 +1,179 @@
|
||||
|
||||
const MIMES_EDIT = new Set([
|
||||
'application/vnd.ms-powerpoint',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.oasis.opendocument.text',
|
||||
'application/vnd.oasis.opendocument.text-flat-xml',
|
||||
'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'application/vnd.oasis.opendocument.spreadsheet-flat-xml',
|
||||
'application/vnd.oasis.opendocument.presentation',
|
||||
'application/vnd.oasis.opendocument.presentation-flat-xml',
|
||||
'application/vnd.oasis.opendocument.graphics',
|
||||
'application/vnd.oasis.opendocument.graphics-flat-xml',
|
||||
'application/vnd.oasis.opendocument.chart',
|
||||
'application/msword',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.ms-powerpoint',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/vnd.ms-word.document.macroEnabled.12',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
|
||||
'application/vnd.ms-excel.sheet.macroEnabled.12',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
|
||||
'application/x-dif-document',
|
||||
'text/spreadsheet',
|
||||
'text/csv',
|
||||
'application/x-dbase',
|
||||
'text/rtf',
|
||||
'text/plain',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
|
||||
]);
|
||||
|
||||
|
||||
|
||||
const MIMES_VIEW = new Set([
|
||||
...MIMES_EDIT,
|
||||
[
|
||||
'image/svg+xml',
|
||||
'application/vnd.sun.xml.writer',
|
||||
'application/vnd.sun.xml.calc',
|
||||
'application/vnd.sun.xml.impress',
|
||||
'application/vnd.sun.xml.draw',
|
||||
'application/vnd.sun.xml.writer.global',
|
||||
'application/vnd.sun.xml.writer.template',
|
||||
'application/vnd.sun.xml.calc.template',
|
||||
'application/vnd.sun.xml.impress.template',
|
||||
'application/vnd.sun.xml.draw.template',
|
||||
'application/vnd.oasis.opendocument.text-master',
|
||||
'application/vnd.oasis.opendocument.text-template',
|
||||
'application/vnd.oasis.opendocument.text-master-template',
|
||||
'application/vnd.oasis.opendocument.spreadsheet-template',
|
||||
'application/vnd.oasis.opendocument.presentation-template',
|
||||
'application/vnd.oasis.opendocument.graphics-template',
|
||||
'application/vnd.ms-word.template.macroEnabled.12',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||
'application/vnd.ms-excel.template.macroEnabled.12',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.template',
|
||||
'application/vnd.ms-powerpoint.template.macroEnabled.12',
|
||||
'application/vnd.wordperfect',
|
||||
'application/x-aportisdoc',
|
||||
'application/x-hwp',
|
||||
'application/vnd.ms-works',
|
||||
'application/x-mswrite',
|
||||
'application/vnd.lotus-1-2-3',
|
||||
'image/cgm',
|
||||
'image/vnd.dxf',
|
||||
'image/x-emf',
|
||||
'image/x-wmf',
|
||||
'application/coreldraw',
|
||||
'application/vnd.visio2013',
|
||||
'application/vnd.visio',
|
||||
'application/vnd.ms-visio.drawing',
|
||||
'application/x-mspublisher',
|
||||
'application/x-sony-bbeb',
|
||||
'application/x-gnumeric',
|
||||
'application/macwriteii',
|
||||
'application/x-iwork-numbers-sffnumbers',
|
||||
'application/vnd.oasis.opendocument.text-web',
|
||||
'application/x-pagemaker',
|
||||
'application/x-fictionbook+xml',
|
||||
'application/clarisworks',
|
||||
'image/x-wpg',
|
||||
'application/x-iwork-pages-sffpages',
|
||||
'application/x-iwork-keynote-sffkey',
|
||||
'application/x-abiword',
|
||||
'image/x-freehand',
|
||||
'application/vnd.sun.xml.chart',
|
||||
'application/x-t602',
|
||||
'image/bmp',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/tiff',
|
||||
'image/jpg',
|
||||
'image/jpeg',
|
||||
'application/pdf',
|
||||
]
|
||||
])
|
||||
|
||||
function is_extension_editable(mimeType: string): boolean {
|
||||
return MIMES_EDIT.has(mimeType);
|
||||
}
|
||||
|
||||
function is_extension_viewable(mimeType: string): boolean {
|
||||
return MIMES_VIEW.has(mimeType);
|
||||
}
|
||||
|
||||
function build_convert_link(uuid: string) {
|
||||
return `/chill/wopi/convert/${uuid}`;
|
||||
}
|
||||
|
||||
function build_download_info_link(object_name: string) {
|
||||
return `/asyncupload/temp_url/generate/GET?object_name=${object_name}`;
|
||||
}
|
||||
|
||||
function build_wopi_editor_link(uuid: string, returnPath?: string) {
|
||||
if (returnPath === undefined) {
|
||||
returnPath = window.location.pathname + window.location.search + window.location.hash;
|
||||
}
|
||||
|
||||
return `/chill/wopi/edit/${uuid}?returnPath=` + encodeURIComponent(returnPath);
|
||||
}
|
||||
|
||||
function download_doc(url: string): Promise<Blob> {
|
||||
return window.fetch(url).then(r => {
|
||||
if (r.ok) {
|
||||
return r.blob()
|
||||
}
|
||||
|
||||
throw new Error('Could not download document');
|
||||
});
|
||||
}
|
||||
|
||||
async function download_and_decrypt_doc(urlGenerator: string, keyData: JsonWebKey, iv: Uint8Array): Promise<Blob>
|
||||
{
|
||||
const algo = 'AES-CBC';
|
||||
// get an url to download the object
|
||||
const downloadInfoResponse = await window.fetch(urlGenerator);
|
||||
|
||||
if (!downloadInfoResponse.ok) {
|
||||
throw new Error("error while downloading url " + downloadInfoResponse.status + " " + downloadInfoResponse.statusText);
|
||||
}
|
||||
|
||||
const downloadInfo = await downloadInfoResponse.json() as {url: string};
|
||||
const rawResponse = await window.fetch(downloadInfo.url);
|
||||
|
||||
if (!rawResponse.ok) {
|
||||
throw new Error("error while downloading raw file " + rawResponse.status + " " + rawResponse.statusText);
|
||||
}
|
||||
|
||||
if (iv.length === 0) {
|
||||
return rawResponse.blob();
|
||||
}
|
||||
|
||||
const rawBuffer = await rawResponse.arrayBuffer();
|
||||
|
||||
try {
|
||||
const key = await window.crypto.subtle
|
||||
.importKey('jwk', keyData, { name: algo }, false, ['decrypt']);
|
||||
const decrypted = await window.crypto.subtle
|
||||
.decrypt({ name: algo, iv: iv }, key, rawBuffer);
|
||||
|
||||
return Promise.resolve(new Blob([decrypted]));
|
||||
} catch (e) {
|
||||
console.error('get error while keys and decrypt operations');
|
||||
console.error(e);
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
build_convert_link,
|
||||
build_download_info_link,
|
||||
build_wopi_editor_link,
|
||||
download_and_decrypt_doc,
|
||||
download_doc,
|
||||
is_extension_editable,
|
||||
is_extension_viewable,
|
||||
};
|
@@ -46,21 +46,8 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
{{ m.download_button(document.object, document.title) }}
|
||||
{{ document.object|chill_document_button_group(document.title, not freezed) }}
|
||||
</li>
|
||||
{% if chill_document_is_editable(document.object) %}
|
||||
{% if not freezed %}
|
||||
<li>
|
||||
{{ document.object|chill_document_edit_button({'title': document.title|e('html') }) }}
|
||||
</li>
|
||||
{% else %}
|
||||
<li>
|
||||
<a class="btn btn-wopilink disabled" href="#" title="{{ 'workflow.freezed document'|trans }}">
|
||||
{{ 'Update document'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE', document) and document.course != null %}
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('accompanying_course_document_show', {'course': document.course.id, 'id': document.id}) }}" class="btn btn-show"></a>
|
||||
|
@@ -8,16 +8,16 @@
|
||||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('mod_async_upload') }}
|
||||
{{ encore_entry_script_tags('mod_docgen_picktemplate') }}
|
||||
{{ encore_entry_script_tags('mod_entity_workflow_pick') }}
|
||||
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_async_upload') }}
|
||||
{{ encore_entry_link_tags('mod_docgen_picktemplate') }}
|
||||
{{ encore_entry_link_tags('mod_entity_workflow_pick') }}
|
||||
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@@ -14,6 +14,7 @@
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_async_upload') }}
|
||||
{{ encore_entry_link_tags('mod_entity_workflow_pick') }}
|
||||
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@@ -61,13 +62,8 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
{{ m.download_button(document.object, document.title) }}
|
||||
{{ document.object|chill_document_button_group(document.title) }}
|
||||
</li>
|
||||
{% if chill_document_is_editable(document.object) %}
|
||||
<li>
|
||||
{{ document.object|chill_document_edit_button }}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% set workflows_frame = chill_entity_workflow_list('Chill\\DocStoreBundle\\Entity\\AccompanyingCourseDocument', document.id) %}
|
||||
{% if workflows_frame is not empty %}
|
||||
<li>
|
||||
@@ -86,4 +82,5 @@
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('mod_async_upload') }}
|
||||
{{ encore_entry_script_tags('mod_entity_workflow_pick') }}
|
||||
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
@@ -0,0 +1,7 @@
|
||||
{%- import "@ChillDocStore/Macro/macro.html.twig" as m -%}
|
||||
<div
|
||||
data-download-buttons
|
||||
data-stored-object="{{ document_json|json_encode|escape('html_attr') }}"
|
||||
data-can-edit="{{ can_edit ? '1' : '0' }}"
|
||||
{% if options['small'] is defined %}data-button-small="{{ options['small'] ? '1' : '0' }}"{% endif %}
|
||||
{% if title|default(document.title)|default(null) is not null %}data-filename="{{ title|default(document.title)|escape('html_attr') }}"{% endif %}></div>
|
@@ -53,15 +53,10 @@
|
||||
<li>
|
||||
<a href="{{ path('accompanying_course_document_edit', {'course': accompanyingCourse.id, 'id': document.id }) }}" class="btn btn-update"></a>
|
||||
</li>
|
||||
{% if chill_document_is_editable(document.object) %}
|
||||
<li>
|
||||
{{ document.object|chill_document_edit_button }}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE_DETAILS', document) %}
|
||||
<li>
|
||||
{{ m.download_button(document.object, document.title) }}
|
||||
{{ document.object|chill_document_button_group(document.title, is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', document)) }}
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('accompanying_course_document_show', {'course': accompanyingCourse.id, 'id': document.id}) }}" class="btn btn-show"></a>
|
||||
@@ -80,15 +75,10 @@
|
||||
<li>
|
||||
<a href="{{ path('person_document_edit', {'person': person.id, 'id': document.id}) }}" class="btn btn-update"></a>
|
||||
</li>
|
||||
{% if chill_document_is_editable(document.object) %}
|
||||
<li>
|
||||
{{ document.object|chill_document_edit_button }}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_SEE_DETAILS', document) %}
|
||||
<li>
|
||||
{{ m.download_button(document.object, document.title) }}
|
||||
{{ document.object|chill_document_button_group(document.title, is_granted('CHILL_PERSON_DOCUMENT_UPDATE', document)) }}
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('person_document_show', {'person': person.id, 'id': document.id}) }}" class="btn btn-show"></a>
|
||||
|
@@ -2,13 +2,13 @@
|
||||
{% if storedObject is null %}
|
||||
<!-- No document to download -->
|
||||
{% else %}
|
||||
<a class="btn btn-download"
|
||||
data-label-preparing="{{ ('Preparing'|trans ~ '...')|escape('html_attr') }}"
|
||||
data-label-ready="{{ 'Ready to show'|trans|escape('html_attr') }}"
|
||||
data-download-button
|
||||
data-key="{{ storedObject.keyInfos|json_encode|escape('html_attr') }}"
|
||||
data-iv="{{ storedObject.iv|json_encode|escape('html_attr') }}"
|
||||
data-temp-url-get-generator="{{ storedObject|generate_url|escape('html_attr') }}"
|
||||
<a class="btn btn-download"
|
||||
data-label-preparing="{{ ('Preparing'|trans ~ '...')|escape('html_attr') }}"
|
||||
data-label-ready="{{ 'Ready to show'|trans|escape('html_attr') }}"
|
||||
data-download-button
|
||||
data-key="{{ storedObject.keyInfos|json_encode|escape('html_attr') }}"
|
||||
data-iv="{{ storedObject.iv|json_encode|escape('html_attr') }}"
|
||||
data-temp-url-get-generator="{{ storedObject|generate_url|escape('html_attr') }}"
|
||||
data-mime-type="{{ storedObject.type|escape('html_attr') }}" {% if filename is not null %}data-filename="{{ filename|escape('html_attr') }}"{% endif %}>
|
||||
{{ 'Download'|trans }}</a>
|
||||
{% endif %}
|
||||
@@ -28,4 +28,21 @@
|
||||
data-mime-type="{{ storedObject.type|escape('html_attr') }}" {% if filename is not null %}data-filename="{{ filename|escape('html_attr') }}"{% endif %}>
|
||||
{{ 'Download'|trans }}</a>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro download_button_group(storedObject, canEdit = true, filename = null, options = {}) %}
|
||||
{% if storedObject is null %}
|
||||
<!-- No document to download -->
|
||||
{% else %}
|
||||
<div
|
||||
data-download-buttons
|
||||
data-uuid="{{ storedObject.uuid|escape('html_attr') }}"
|
||||
data-key="{{ storedObject.keyInfos|json_encode|escape('html_attr') }}"
|
||||
data-iv="{{ storedObject.iv|json_encode|escape('html_attr') }}"
|
||||
data-temp-url-generator="{{ storedObject|generate_url|escape('html_attr') }}"
|
||||
data-mime-type="{{ storedObject.type|escape('html_attr') }}"
|
||||
data-can-edit="{{ canEdit ? '1' : '0' }}"
|
||||
{% if options['small'] is defined %}data-button-small="{{ options['small'] ? '1' : '0' }}"{% endif %}
|
||||
{% if filename|default(storedObject.title)|default(null) is not null %}data-filename="{{ filename|default(storedObject.title)|escape('html_attr') }}"{% endif %}></div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
@@ -27,16 +27,16 @@
|
||||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('mod_async_upload') }}
|
||||
{{ encore_entry_script_tags('mod_docgen_picktemplate') }}
|
||||
{{ encore_entry_script_tags('mod_entity_workflow_pick') }}
|
||||
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_async_upload') }}
|
||||
{{ encore_entry_link_tags('mod_docgen_picktemplate') }}
|
||||
{{ encore_entry_link_tags('mod_entity_workflow_pick') }}
|
||||
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@@ -24,7 +24,11 @@
|
||||
{% block title %}{{ 'Detail of document of %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ encore_entry_script_tags('mod_async_upload') }}
|
||||
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@@ -70,6 +74,10 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<li>
|
||||
{{ document.object|chill_document_button_group(document.title, is_granted('CHILL_PERSON_DOCUMENT_UPDATE', document)) }}
|
||||
</li>
|
||||
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_UPDATE', document) %}
|
||||
<li>
|
||||
<a href="{{ path('person_document_edit', {'id': document.id, 'person': person.id}) }}" class="btn btn-edit">
|
||||
@@ -77,16 +85,4 @@
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<li>
|
||||
{{ m.download_button(document.object, document.title) }}
|
||||
</li>
|
||||
|
||||
{% if chill_document_is_editable(document.object) %}
|
||||
<li>
|
||||
{{ document.object|chill_document_edit_button }}
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{# {{ include('ChillDocStoreBundle:PersonDocument:_delete_form.html.twig') }} #}
|
||||
{% endblock %}
|
||||
|
@@ -24,6 +24,10 @@ class WopiEditTwigExtension extends AbstractExtension
|
||||
'needs_environment' => true,
|
||||
'is_safe' => ['html'],
|
||||
]),
|
||||
new TwigFilter('chill_document_button_group', [WopiEditTwigExtensionRuntime::class, 'renderButtonGroup'], [
|
||||
'needs_environment' => true,
|
||||
'is_safe' => ['html'],
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -13,6 +13,8 @@ namespace Chill\DocStoreBundle\Templating;
|
||||
|
||||
use ChampsLibres\WopiLib\Contract\Service\Discovery\DiscoveryInterface;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Twig\Environment;
|
||||
use Twig\Extension\RuntimeExtensionInterface;
|
||||
|
||||
@@ -112,20 +114,53 @@ final class WopiEditTwigExtensionRuntime implements RuntimeExtensionInterface
|
||||
'application/pdf',
|
||||
];
|
||||
|
||||
private const DEFAULT_OPTIONS_TEMPLATE_BUTTON_GROUP = [
|
||||
'small' => false,
|
||||
];
|
||||
|
||||
private const TEMPLATE = '@ChillDocStore/Button/wopi_edit_document.html.twig';
|
||||
|
||||
private const TEMPLATE_BUTTON_GROUP = '@ChillDocStore/Button/button_group.html.twig';
|
||||
|
||||
private DiscoveryInterface $discovery;
|
||||
|
||||
public function __construct(DiscoveryInterface $discovery)
|
||||
private NormalizerInterface $normalizer;
|
||||
|
||||
public function __construct(DiscoveryInterface $discovery, NormalizerInterface $normalizer)
|
||||
{
|
||||
$this->discovery = $discovery;
|
||||
$this->normalizer = $normalizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if the document is editable.
|
||||
*
|
||||
* **NOTE**: as the Vue button does have similar test, this is not required if in use with
|
||||
* the dedicated Vue component (GroupDownloadButton.vue, WopiEditButton.vue)
|
||||
*/
|
||||
public function isEditable(StoredObject $document): bool
|
||||
{
|
||||
return in_array($document->getType(), self::SUPPORTED_MIMES, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{small: boolean} $options
|
||||
*
|
||||
* @throws \Twig\Error\LoaderError
|
||||
* @throws \Twig\Error\RuntimeError
|
||||
* @throws \Twig\Error\SyntaxError
|
||||
*/
|
||||
public function renderButtonGroup(Environment $environment, StoredObject $document, ?string $title = null, bool $canEdit = true, array $options = []): string
|
||||
{
|
||||
return $environment->render(self::TEMPLATE_BUTTON_GROUP, [
|
||||
'document' => $document,
|
||||
'document_json' => $this->normalizer->normalize($document, 'json', [AbstractNormalizer::GROUPS => ['read']]),
|
||||
'title' => $title,
|
||||
'can_edit' => $canEdit,
|
||||
'options' => array_merge($options, self::DEFAULT_OPTIONS_TEMPLATE_BUTTON_GROUP),
|
||||
]);
|
||||
}
|
||||
|
||||
public function renderEditButton(Environment $environment, StoredObject $document, ?array $options = null): string
|
||||
{
|
||||
return $environment->render(self::TEMPLATE, [
|
||||
|
@@ -4,4 +4,5 @@ module.exports = function(encore)
|
||||
ChillDocStoreAssets: __dirname + '/Resources/public'
|
||||
});
|
||||
encore.addEntry('mod_async_upload', __dirname + '/Resources/public/module/async_upload/index.js');
|
||||
encore.addEntry('mod_document_action_buttons_group', __dirname + '/Resources/public/module/document_action_buttons_group/index');
|
||||
};
|
||||
|
@@ -66,3 +66,11 @@ online_edit_document: Éditer en ligne
|
||||
|
||||
workflow:
|
||||
Document deleted: Document supprimé
|
||||
|
||||
# ROLES
|
||||
accompanyingCourseDocument: Documents dans les parcours d'accompagnement
|
||||
CHILL_ACCOMPANYING_COURSE_DOCUMENT_CREATE: Créer un document
|
||||
CHILL_ACCOMPANYING_COURSE_DOCUMENT_DELETE: Supprimer un document
|
||||
CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE: Voir les documents
|
||||
CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE_DETAILS: Voir les détails d'un document
|
||||
CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE: Modifier un document
|
@@ -28,7 +28,7 @@ class LocationController extends CRUDController
|
||||
|
||||
protected function customizeQuery(string $action, Request $request, $query): void
|
||||
{
|
||||
$query->where('e.availableForUsers = true'); //TODO not working
|
||||
$query->where('e.availableForUsers = "TRUE"');
|
||||
}
|
||||
|
||||
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
|
||||
|
@@ -83,9 +83,9 @@ interface ExportInterface extends ExportElementInterface
|
||||
*
|
||||
* @param string $key The column key, as added in the query
|
||||
* @param mixed[] $values The values from the result. if there are duplicates, those might be given twice. Example: array('FR', 'BE', 'CZ', 'FR', 'BE', 'FR')
|
||||
* @param mixed $data The data from the export's form (as defined in `buildForm`
|
||||
* @param mixed $data The data from the export's form (as defined in `buildForm`)
|
||||
*
|
||||
* @return Closure where the first argument is the value, and the function should return the label to show in the formatted file. Example : `function($countryCode) use ($countries) { return $countries[$countryCode]->getName(); }`
|
||||
* @return pure-callable(null|string|int|float|'_header' $value):string|int|\DateTimeInterface where the first argument is the value, and the function should return the label to show in the formatted file. Example : `function($countryCode) use ($countries) { return $countries[$countryCode]->getName(); }`
|
||||
*/
|
||||
public function getLabels($key, array $values, $data);
|
||||
|
||||
|
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Export\Helper;
|
||||
|
||||
/**
|
||||
* This class provides support to transform aggregates datas in correct format string into column of exports.
|
||||
*/
|
||||
class AggregateStringHelper
|
||||
{
|
||||
public function getLabelMulti(string $key, array $values, string $header)
|
||||
{
|
||||
return static function ($value) use ($header) {
|
||||
if ('_header' === $value) {
|
||||
return $header;
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return implode(
|
||||
'|',
|
||||
json_decode($value, true)
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
@@ -35,6 +35,10 @@ class DateTimeHelper
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($value instanceof \DateTimeInterface) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// warning: won't work with DateTimeImmutable as we reset time a few lines later
|
||||
$date = DateTime::createFromFormat('Y-m-d', $value);
|
||||
$hasTime = false;
|
||||
|
@@ -45,9 +45,9 @@ class UserHelper
|
||||
|
||||
public function getLabelMulti($key, array $values, string $header): callable
|
||||
{
|
||||
return function ($value) {
|
||||
return function ($value) use ($header) {
|
||||
if ('_header' === $value) {
|
||||
return 'users name';
|
||||
return $header;
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
|
@@ -98,11 +98,6 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (typeof this.value.point !== 'undefined') {
|
||||
this.updateMapCenter(this.value.point);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
transName(value) {
|
||||
return value.streetNumber === undefined ? value.street : `${value.streetNumber}, ${value.street}`
|
||||
|
@@ -260,11 +260,11 @@
|
||||
|
||||
{% block pick_rolling_date_widget %}
|
||||
<div data-rolling-date="{{ form.vars['uniqid'] }}" class="row">
|
||||
<div class="roll-wrapper col-sm-6">
|
||||
<div class="roll-wrapper">
|
||||
{{ form_widget(form.roll, { 'attr': { 'data-roll-picker': 'data-roll-picker'}}) }}
|
||||
{{ form_errors(form.roll) }}
|
||||
</div>
|
||||
<div class="fixed-wrapper col-sm-6">
|
||||
<div class="fixed-wrapper">
|
||||
{{ form_widget(form.fixedDate) }}
|
||||
{{ form_errors(form.fixedDate) }}
|
||||
</div>
|
||||
|
@@ -11,6 +11,7 @@
|
||||
{{ encore_entry_script_tags('mod_entity_workflow_subscribe') }}
|
||||
{{ encore_entry_script_tags('page_workflow_show') }}
|
||||
{{ encore_entry_script_tags('mod_wopi_link') }}
|
||||
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
@@ -19,6 +20,7 @@
|
||||
{{ encore_entry_link_tags('mod_entity_workflow_subscribe') }}
|
||||
{{ encore_entry_link_tags('page_workflow_show') }}
|
||||
{{ encore_entry_link_tags('mod_wopi_link') }}
|
||||
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% import '@ChillMain/Workflow/macro_breadcrumb.html.twig' as macro %}
|
||||
|
@@ -229,6 +229,7 @@ Create a new circle: Créer un nouveau cercle
|
||||
|
||||
#admin section for location
|
||||
Location: Localisation
|
||||
pick location: Localisation
|
||||
Location type list: Liste des types de localisation
|
||||
Create a new location type: Créer un nouveau type de localisation
|
||||
Available for users: Disponible aux utilisateurs
|
||||
|
@@ -19,7 +19,7 @@ use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
final class JobAggregator implements AggregatorInterface
|
||||
final class UserJobAggregator implements AggregatorInterface
|
||||
{
|
||||
private UserJobRepository $jobRepository;
|
||||
|
||||
@@ -40,11 +40,11 @@ final class JobAggregator implements AggregatorInterface
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
if (!in_array('acpjob', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('acp.job', 'acpjob');
|
||||
if (!in_array('acpuser', $qb->getAllAliases(), true)) {
|
||||
$qb->leftJoin('acp.user', 'acpuser');
|
||||
}
|
||||
|
||||
$qb->addSelect('IDENTITY(acp.job) AS job_aggregator');
|
||||
$qb->addSelect('IDENTITY(acpuser.userJob) AS job_aggregator');
|
||||
$qb->addGroupBy('job_aggregator');
|
||||
}
|
||||
|
@@ -24,7 +24,7 @@ use Doctrine\ORM\QueryBuilder;
|
||||
use LogicException;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class CountSocialWorkActions implements ExportInterface, GroupedExportInterface
|
||||
class CountAccompanyingPeriodWork implements ExportInterface, GroupedExportInterface
|
||||
{
|
||||
protected EntityManagerInterface $em;
|
||||
|
@@ -19,7 +19,8 @@ use Chill\MainBundle\Export\Helper\DateTimeHelper;
|
||||
use Chill\MainBundle\Export\Helper\ExportAddressHelper;
|
||||
use Chill\MainBundle\Export\Helper\UserHelper;
|
||||
use Chill\MainBundle\Export\ListInterface;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
||||
@@ -87,6 +88,8 @@ class ListAccompanyingPeriod implements ListInterface, GroupedExportInterface
|
||||
|
||||
private PersonRepository $personRepository;
|
||||
|
||||
private RollingDateConverterInterface $rollingDateConverter;
|
||||
|
||||
private SocialIssueRender $socialIssueRender;
|
||||
|
||||
private SocialIssueRepository $socialIssueRepository;
|
||||
@@ -110,6 +113,7 @@ class ListAccompanyingPeriod implements ListInterface, GroupedExportInterface
|
||||
SocialIssueRepository $socialIssueRepository,
|
||||
SocialIssueRender $socialIssueRender,
|
||||
TranslatableStringHelperInterface $translatableStringHelper,
|
||||
RollingDateConverterInterface $rollingDateConverter,
|
||||
UserHelper $userHelper
|
||||
) {
|
||||
$this->addressHelper = $addressHelper;
|
||||
@@ -122,14 +126,14 @@ class ListAccompanyingPeriod implements ListInterface, GroupedExportInterface
|
||||
$this->thirdPartyRender = $thirdPartyRender;
|
||||
$this->thirdPartyRepository = $thirdPartyRepository;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->rollingDateConverter = $rollingDateConverter;
|
||||
$this->userHelper = $userHelper;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder
|
||||
->add('calc_date', ChillDateType::class, [
|
||||
'input' => 'datetime_immutable',
|
||||
->add('calc_date', PickRollingDateType::class, [
|
||||
'label' => 'export.list.acp.Date of calculation for associated elements',
|
||||
'help' => 'export.list.acp.The associated referree, localisation, and other elements will be valid at this date',
|
||||
'required' => true,
|
||||
@@ -306,7 +310,7 @@ class ListAccompanyingPeriod implements ListInterface, GroupedExportInterface
|
||||
->setParameter('list_acp_step', AccompanyingPeriod::STEP_DRAFT)
|
||||
->setParameter('authorized_centers', $centers);
|
||||
|
||||
$this->addSelectClauses($qb, $data['calc_date']);
|
||||
$this->addSelectClauses($qb, $this->rollingDateConverter->convert($data['calc_date']));
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Export\Export;
|
||||
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Chill\MainBundle\Export\Helper\AggregateStringHelper;
|
||||
use Chill\MainBundle\Export\Helper\DateTimeHelper;
|
||||
use Chill\MainBundle\Export\Helper\TranslatableStringExportLabelHelper;
|
||||
use Chill\MainBundle\Export\Helper\UserHelper;
|
||||
use Chill\MainBundle\Export\ListInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
|
||||
use Chill\PersonBundle\Entity\SocialWork\Goal;
|
||||
use Chill\PersonBundle\Entity\SocialWork\Result;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
|
||||
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
|
||||
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
|
||||
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Chill\ThirdPartyBundle\Export\Helper\LabelThirdPartyHelper;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterface
|
||||
{
|
||||
private const FIELDS = [
|
||||
'id',
|
||||
'socialActionId',
|
||||
'socialAction',
|
||||
'socialIssue',
|
||||
'acp_id',
|
||||
'acp_user',
|
||||
'startDate',
|
||||
'endDate',
|
||||
'goalsId',
|
||||
'goalsTitle',
|
||||
'goalResultsId',
|
||||
'goalResultsTitle',
|
||||
'resultsId',
|
||||
'resultsTitle',
|
||||
'evaluationsId',
|
||||
'evaluationsTitle',
|
||||
'note',
|
||||
'personsId',
|
||||
'personsName',
|
||||
'thirdParties',
|
||||
'handlingThierParty',
|
||||
'referrers',
|
||||
'createdAt',
|
||||
'createdBy',
|
||||
'updatedAt',
|
||||
'updatedBy',
|
||||
];
|
||||
|
||||
private AggregateStringHelper $aggregateStringHelper;
|
||||
|
||||
private DateTimeHelper $dateTimeHelper;
|
||||
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
private LabelPersonHelper $personHelper;
|
||||
|
||||
private RollingDateConverterInterface $rollingDateConverter;
|
||||
|
||||
private SocialActionRender $socialActionRender;
|
||||
|
||||
private SocialActionRepository $socialActionRepository;
|
||||
|
||||
private SocialIssueRender $socialIssueRender;
|
||||
|
||||
private SocialIssueRepository $socialIssueRepository;
|
||||
|
||||
private LabelThirdPartyHelper $thirdPartyHelper;
|
||||
|
||||
private TranslatableStringExportLabelHelper $translatableStringExportLabelHelper;
|
||||
|
||||
private UserHelper $userHelper;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $entityManager,
|
||||
DateTimeHelper $dateTimeHelper,
|
||||
UserHelper $userHelper,
|
||||
LabelPersonHelper $personHelper,
|
||||
LabelThirdPartyHelper $thirdPartyHelper,
|
||||
TranslatableStringExportLabelHelper $translatableStringExportLabelHelper,
|
||||
SocialIssueRender $socialIssueRender,
|
||||
SocialIssueRepository $socialIssueRepository,
|
||||
SocialActionRender $socialActionRender,
|
||||
RollingDateConverterInterface $rollingDateConverter,
|
||||
AggregateStringHelper $aggregateStringHelper,
|
||||
SocialActionRepository $socialActionRepository
|
||||
) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->dateTimeHelper = $dateTimeHelper;
|
||||
$this->userHelper = $userHelper;
|
||||
$this->personHelper = $personHelper;
|
||||
$this->thirdPartyHelper = $thirdPartyHelper;
|
||||
$this->translatableStringExportLabelHelper = $translatableStringExportLabelHelper;
|
||||
$this->socialIssueRender = $socialIssueRender;
|
||||
$this->socialIssueRepository = $socialIssueRepository;
|
||||
$this->socialActionRender = $socialActionRender;
|
||||
$this->rollingDateConverter = $rollingDateConverter;
|
||||
$this->aggregateStringHelper = $aggregateStringHelper;
|
||||
$this->socialActionRepository = $socialActionRepository;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder
|
||||
->add('calc_date', PickRollingDateType::class, [
|
||||
'label' => 'export.list.acpw.Date of calculation for associated elements',
|
||||
'help' => 'export.list.acpw.help_description',
|
||||
'required' => true,
|
||||
'data' => new RollingDate(RollingDate::T_TODAY),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes()
|
||||
{
|
||||
return [FormatterInterface::TYPE_LIST];
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'export.list.acpw.List description';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'Exports of social work actions';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
switch ($key) {
|
||||
case 'startDate':
|
||||
case 'endDate':
|
||||
case 'createdAt':
|
||||
case 'updatedAt':
|
||||
return $this->dateTimeHelper->getLabel('export.list.acpw.' . $key);
|
||||
|
||||
case 'socialAction':
|
||||
return function ($value) use ($key) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.list.acpw.' . $key;
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->socialActionRender->renderString(
|
||||
$this->socialActionRepository->find($value),
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
||||
case 'socialIssue':
|
||||
return function ($value) use ($key) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.list.acpw.' . $key;
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->socialIssueRender->renderString(
|
||||
$this->socialIssueRepository->find($value),
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
||||
case 'createdBy':
|
||||
case 'updatedBy':
|
||||
case 'acp_user':
|
||||
return $this->userHelper->getLabel($key, $values, 'export.list.acpw.' . $key);
|
||||
|
||||
case 'referrers':
|
||||
//$date = $this->rollDateConverter->convert($data['calc_date'])->format('d/m/Y');
|
||||
return $this->userHelper->getLabel($key, $values, 'export.list.acpw.' . $key);
|
||||
|
||||
case 'personsName':
|
||||
return $this->personHelper->getLabelMulti($key, $values, 'export.list.acpw.' . $key);
|
||||
|
||||
case 'handlingThierParty':
|
||||
return $this->thirdPartyHelper->getLabel($key, $values, 'export.list.acpw.' . $key);
|
||||
|
||||
case 'thirdParties':
|
||||
return $this->thirdPartyHelper->getLabelMulti($key, $values, 'export.list.acpw.' . $key);
|
||||
|
||||
case 'personsId':
|
||||
case 'goalsId':
|
||||
case 'goalResultsId':
|
||||
case 'resultsId':
|
||||
case 'evaluationsId':
|
||||
return $this->aggregateStringHelper->getLabelMulti($key, $values, 'export.list.acpw.' . $key);
|
||||
|
||||
case 'goalsTitle':
|
||||
case 'goalResultsTitle':
|
||||
case 'resultsTitle':
|
||||
case 'evaluationsTitle':
|
||||
return $this->translatableStringExportLabelHelper->getLabelMulti($key, $values, 'export.list.acpw.' . $key);
|
||||
|
||||
default:
|
||||
return static function ($value) use ($key) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.list.acpw.' . $key;
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
{
|
||||
return self::FIELDS;
|
||||
}
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.list.acpw.List of accompanying period works';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::SOCIAL_WORK_ACTION_TYPE;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static function ($el) {
|
||||
return $el['center'];
|
||||
}, $acl);
|
||||
|
||||
$qb = $this->entityManager->createQueryBuilder();
|
||||
|
||||
$qb
|
||||
->from(AccompanyingPeriodWork::class, 'acpw')
|
||||
->distinct()
|
||||
->select('acpw.id AS id')
|
||||
->join('acpw.accompanyingPeriod', 'acp')
|
||||
->join('acp.participations', 'acppart')
|
||||
->join('acppart.person', 'person')
|
||||
// ignore participation which didn't last one day, at least
|
||||
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
|
||||
// get participants at the given date
|
||||
->andWhere('acppart.startDate <= :calc_date AND (acppart.endDate > :calc_date OR acppart.endDate IS NULL)')
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM ' . PersonCenterHistory::class . ' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||
AND acl_count_person_history.center IN (:authorized_centers)
|
||||
'
|
||||
)
|
||||
)
|
||||
->setParameter('authorized_centers', $centers)
|
||||
->setParameter('calc_date', $this->rollingDateConverter->convert($data['calc_date']));
|
||||
|
||||
$this->addSelectClauses($qb, $this->rollingDateConverter->convert($data['calc_date']));
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return AccompanyingPeriodVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers(): array
|
||||
{
|
||||
return [
|
||||
Declarations::SOCIAL_WORK_ACTION_TYPE,
|
||||
Declarations::ACP_TYPE,
|
||||
Declarations::PERSON_TYPE,
|
||||
];
|
||||
}
|
||||
|
||||
private function addSelectClauses(QueryBuilder $qb, DateTimeImmutable $calcDate): void
|
||||
{
|
||||
// add regular fields
|
||||
foreach ([
|
||||
'startDate',
|
||||
'endDate',
|
||||
'note',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
] as $field) {
|
||||
$qb->addSelect(sprintf('acpw.%s AS %s', $field, $field));
|
||||
}
|
||||
|
||||
// those with identity
|
||||
foreach ([
|
||||
'createdBy',
|
||||
'updatedBy',
|
||||
'handlingThierParty',
|
||||
] as $field) {
|
||||
$qb->addSelect(sprintf('IDENTITY(acpw.%s) AS %s', $field, $field));
|
||||
}
|
||||
|
||||
// join socialaction
|
||||
$qb
|
||||
->join('acpw.socialAction', 'sa')
|
||||
->addSelect('sa.id AS socialActionId')
|
||||
->addSelect('sa.id AS socialAction')
|
||||
->addSelect('IDENTITY(sa.issue) AS socialIssue');
|
||||
|
||||
// join acp
|
||||
$qb
|
||||
->addSelect('acp.id AS acp_id')
|
||||
->addSelect('IDENTITY(acp.user) AS acp_user');
|
||||
|
||||
// persons
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(person_acpw_member.id) FROM ' . Person::class . ' person_acpw_member '
|
||||
. 'WHERE person_acpw_member MEMBER OF acpw.persons) AS personsId')
|
||||
->addSelect('(SELECT AGGREGATE(person1_acpw_member.id) FROM ' . Person::class . ' person1_acpw_member '
|
||||
. 'WHERE person1_acpw_member MEMBER OF acpw.persons) AS personsName');
|
||||
|
||||
// referrers => at date XXXX
|
||||
$qb
|
||||
->addSelect('(SELECT IDENTITY(history.user) FROM ' . UserHistory::class . ' history ' .
|
||||
'WHERE history.accompanyingPeriod = acp AND history.startDate <= :calcDate AND (history.endDate IS NULL OR history.endDate > :calcDate)) AS referrers');
|
||||
|
||||
// thirdparties
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(tp.id) FROM ' . ThirdParty::class . ' tp '
|
||||
. 'WHERE tp MEMBER OF acpw.thirdParties) AS thirdParties');
|
||||
|
||||
// goals
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(IDENTITY(goal.goal)) FROM ' . AccompanyingPeriodWorkGoal::class . ' goal '
|
||||
. 'WHERE goal MEMBER OF acpw.goals) AS goalsId')
|
||||
->addSelect('(SELECT AGGREGATE(g.title) FROM ' . AccompanyingPeriodWorkGoal::class . ' goal1 '
|
||||
. 'LEFT JOIN ' . Goal::class . ' g WITH goal1.goal = g.id WHERE goal1 MEMBER OF acpw.goals) AS goalsTitle');
|
||||
|
||||
// goals results
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(wr.id) FROM ' . Result::class . ' wr '
|
||||
. 'JOIN ' . AccompanyingPeriodWorkGoal::class . ' wg WITH wr MEMBER OF wg.results '
|
||||
. 'WHERE wg MEMBER OF acpw.goals) AS goalResultsId')
|
||||
->addSelect('(SELECT AGGREGATE(wr1.title) FROM ' . Result::class . ' wr1 '
|
||||
. 'JOIN ' . AccompanyingPeriodWorkGoal::class . ' wg1 WITH wr1 MEMBER OF wg1.results '
|
||||
. 'WHERE wg1 MEMBER OF acpw.goals) AS goalResultsTitle');
|
||||
|
||||
// results
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(result.id) FROM ' . Result::class . ' result '
|
||||
. 'WHERE result MEMBER OF acpw.results ) AS resultsId ')
|
||||
->addSelect('(SELECT AGGREGATE (result1.title) FROM ' . Result::class . ' result1 '
|
||||
. 'WHERE result1 MEMBER OF acpw.results ) AS resultsTitle ');
|
||||
|
||||
// evaluations
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(IDENTITY(we.evaluation)) FROM ' . AccompanyingPeriodWorkEvaluation::class . ' we '
|
||||
. 'WHERE we MEMBER OF acpw.accompanyingPeriodWorkEvaluations ) AS evaluationsId ')
|
||||
->addSelect('(SELECT AGGREGATE(ev.title) FROM ' . AccompanyingPeriodWorkEvaluation::class . ' we1 '
|
||||
. 'LEFT JOIN ' . Evaluation::class . ' ev WITH we1.evaluation = ev.id '
|
||||
. 'WHERE we1 MEMBER OF acpw.accompanyingPeriodWorkEvaluations ) AS evaluationsTitle ');
|
||||
|
||||
$qb->setParameter('calcDate', $calcDate);
|
||||
}
|
||||
}
|
336
src/Bundle/ChillPersonBundle/Export/Export/ListEvaluation.php
Normal file
336
src/Bundle/ChillPersonBundle/Export/Export/ListEvaluation.php
Normal file
@@ -0,0 +1,336 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Export\Export;
|
||||
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Chill\MainBundle\Export\Helper\AggregateStringHelper;
|
||||
use Chill\MainBundle\Export\Helper\DateTimeHelper;
|
||||
use Chill\MainBundle\Export\Helper\TranslatableStringExportLabelHelper;
|
||||
use Chill\MainBundle\Export\Helper\UserHelper;
|
||||
use Chill\MainBundle\Export\ListInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
//use Chill\MainBundle\Service\RollingDate\RollingDateConverter;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
|
||||
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
|
||||
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
|
||||
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class ListEvaluation implements ListInterface, GroupedExportInterface
|
||||
{
|
||||
private const FIELDS = [
|
||||
'id',
|
||||
'startDate',
|
||||
'endDate',
|
||||
'maxDate',
|
||||
'warningInterval',
|
||||
'acpw_id',
|
||||
'acpw_startDate',
|
||||
'acpw_endDate',
|
||||
'acpw_socialaction_id',
|
||||
'acpw_socialaction',
|
||||
'acpw_socialissue',
|
||||
'acpw_referrers',
|
||||
'acpw_note',
|
||||
'acpw_acp_id',
|
||||
'acpw_acp_user',
|
||||
'acpw_persons_id',
|
||||
'acpw_persons',
|
||||
'comment',
|
||||
'eval_title',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'createdBy',
|
||||
'updatedBy',
|
||||
];
|
||||
|
||||
private AggregateStringHelper $aggregateStringHelper;
|
||||
|
||||
private DateTimeHelper $dateTimeHelper;
|
||||
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
private LabelPersonHelper $personHelper;
|
||||
|
||||
private RollingDateConverterInterface $rollingDateConverter;
|
||||
|
||||
private SocialActionRender $socialActionRender;
|
||||
|
||||
private SocialActionRepository $socialActionRepository;
|
||||
|
||||
private SocialIssueRender $socialIssueRender;
|
||||
|
||||
private SocialIssueRepository $socialIssueRepository;
|
||||
|
||||
private TranslatableStringExportLabelHelper $translatableStringExportLabelHelper;
|
||||
|
||||
private UserHelper $userHelper;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $entityManager,
|
||||
SocialIssueRender $socialIssueRender,
|
||||
SocialIssueRepository $socialIssueRepository,
|
||||
SocialActionRender $socialActionRender,
|
||||
SocialActionRepository $socialActionRepository,
|
||||
UserHelper $userHelper,
|
||||
LabelPersonHelper $personHelper,
|
||||
DateTimeHelper $dateTimeHelper,
|
||||
TranslatableStringExportLabelHelper $translatableStringExportLabelHelper,
|
||||
AggregateStringHelper $aggregateStringHelper,
|
||||
RollingDateConverterInterface $rollingDateConverter
|
||||
) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->socialIssueRender = $socialIssueRender;
|
||||
$this->socialIssueRepository = $socialIssueRepository;
|
||||
$this->socialActionRender = $socialActionRender;
|
||||
$this->socialActionRepository = $socialActionRepository;
|
||||
$this->userHelper = $userHelper;
|
||||
$this->personHelper = $personHelper;
|
||||
$this->dateTimeHelper = $dateTimeHelper;
|
||||
$this->translatableStringExportLabelHelper = $translatableStringExportLabelHelper;
|
||||
$this->aggregateStringHelper = $aggregateStringHelper;
|
||||
$this->rollingDateConverter = $rollingDateConverter;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder
|
||||
->add('calc_date', PickRollingDateType::class, [
|
||||
'label' => 'export.list.eval.Date of calculation for associated elements',
|
||||
'help' => 'export.list.eval.help_description',
|
||||
'required' => true,
|
||||
'data' => new RollingDate(RollingDate::T_TODAY),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes()
|
||||
{
|
||||
return [FormatterInterface::TYPE_LIST];
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'export.list.eval.Generate a list of evaluations, filtered on different parameters';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'Exports of evaluations';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
switch ($key) {
|
||||
case 'startDate':
|
||||
case 'endDate':
|
||||
case 'maxDate':
|
||||
case 'acpw_startDate':
|
||||
case 'acpw_endDate':
|
||||
case 'createdAt':
|
||||
case 'updatedAt':
|
||||
return $this->dateTimeHelper->getLabel('export.list.eval.' . $key);
|
||||
|
||||
case 'acpw_socialaction':
|
||||
return function ($value) use ($key) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.list.eval.' . $key;
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->socialActionRender->renderString(
|
||||
$this->socialActionRepository->find($value),
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
||||
case 'acpw_socialissue':
|
||||
return function ($value) use ($key) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.list.eval.' . $key;
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->socialIssueRender->renderString(
|
||||
$this->socialIssueRepository->find($value),
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
||||
case 'createdBy':
|
||||
case 'updatedBy':
|
||||
case 'acpw_acp_user':
|
||||
return $this->userHelper->getLabel($key, $values, 'export.list.eval.' . $key);
|
||||
|
||||
case 'acpw_referrers':
|
||||
return $this->userHelper->getLabel($key, $values, 'export.list.eval.' . $key);
|
||||
|
||||
case 'acpw_persons_id':
|
||||
return $this->aggregateStringHelper->getLabelMulti($key, $values, 'export.list.eval.' . $key);
|
||||
|
||||
case 'acpw_persons':
|
||||
return $this->personHelper->getLabelMulti($key, $values, 'export.list.eval.' . $key);
|
||||
|
||||
case 'eval_title':
|
||||
return $this->translatableStringExportLabelHelper
|
||||
->getLabel($key, $values, 'export.list.eval.' . $key);
|
||||
|
||||
default:
|
||||
return static function ($value) use ($key) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.list.eval.' . $key;
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
{
|
||||
return self::FIELDS;
|
||||
}
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.list.eval.List of evaluations';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::EVAL_TYPE;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static function ($el) {
|
||||
return $el['center'];
|
||||
}, $acl);
|
||||
|
||||
$qb = $this->entityManager->createQueryBuilder();
|
||||
|
||||
$qb
|
||||
->from(AccompanyingPeriodWorkEvaluation::class, 'workeval')
|
||||
->distinct()
|
||||
->select('workeval.id AS id')
|
||||
->join('workeval.accompanyingPeriodWork', 'acpw')
|
||||
->join('acpw.accompanyingPeriod', 'acp')
|
||||
->join('acp.participations', 'acppart')
|
||||
->join('acppart.person', 'person')
|
||||
// ignore participation which didn't last one day, at least
|
||||
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
|
||||
// get participants at the given date
|
||||
->andWhere('acppart.startDate <= :calc_date AND (acppart.endDate > :calc_date OR acppart.endDate IS NULL)')
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM ' . PersonCenterHistory::class . ' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||
AND acl_count_person_history.center IN (:authorized_centers)
|
||||
'
|
||||
)
|
||||
)
|
||||
->setParameter('authorized_centers', $centers)
|
||||
->setParameter('calc_date', $this->rollingDateConverter->convert($data['calc_date']));
|
||||
|
||||
$this->addSelectClauses($qb, $this->rollingDateConverter->convert($data['calc_date']));
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return AccompanyingPeriodVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers(): array
|
||||
{
|
||||
return [
|
||||
Declarations::EVAL_TYPE,
|
||||
Declarations::SOCIAL_WORK_ACTION_TYPE,
|
||||
Declarations::ACP_TYPE,
|
||||
Declarations::PERSON_TYPE,
|
||||
];
|
||||
}
|
||||
|
||||
private function addSelectClauses(QueryBuilder $qb, DateTimeImmutable $calc_date): void
|
||||
{
|
||||
// add the regular fields
|
||||
foreach (['startDate', 'endDate', 'maxDate', 'warningInterval', 'comment', 'createdAt', 'updatedAt'] as $field) {
|
||||
$qb->addSelect(sprintf('workeval.%s AS %s', $field, $field));
|
||||
}
|
||||
|
||||
// those with identity
|
||||
foreach (['createdBy', 'updatedBy'] as $field) {
|
||||
$qb->addSelect(sprintf('IDENTITY(workeval.%s) AS %s', $field, $field));
|
||||
}
|
||||
|
||||
foreach (['id', 'startDate', 'endDate', 'note'] as $field) {
|
||||
$qb->addSelect(sprintf('acpw.%s AS %s', $field, 'acpw_' . $field));
|
||||
}
|
||||
|
||||
// join socialaction
|
||||
$qb
|
||||
->leftJoin('acpw.socialAction', 'sa')
|
||||
->addSelect('sa.id AS acpw_socialaction_id')
|
||||
->addSelect('sa.id AS acpw_socialaction')
|
||||
->addSelect('IDENTITY(sa.issue) AS acpw_socialissue');
|
||||
|
||||
// join acp
|
||||
$qb
|
||||
->addSelect('acp.id AS acpw_acp_id')
|
||||
->addSelect('IDENTITY(acp.user) AS acpw_acp_user');
|
||||
|
||||
// referrers => at date XXXX
|
||||
$qb
|
||||
->addSelect('(SELECT IDENTITY(history.user) FROM ' . UserHistory::class . ' history ' .
|
||||
'WHERE history.accompanyingPeriod = acp AND history.startDate <= :calc_date AND (history.endDate IS NULL OR history.endDate > :calc_date)) AS acpw_referrers');
|
||||
|
||||
// persons
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(person_acpw_member.id) FROM ' . Person::class . ' person_acpw_member '
|
||||
. 'WHERE person_acpw_member MEMBER OF acpw.persons) AS acpw_persons_id')
|
||||
->addSelect('(SELECT AGGREGATE(person1_acpw_member.id) FROM ' . Person::class . ' person1_acpw_member '
|
||||
. 'WHERE person1_acpw_member MEMBER OF acpw.persons) AS acpw_persons');
|
||||
|
||||
// join evaluation
|
||||
$qb
|
||||
->leftJoin('workeval.evaluation', 'eval')
|
||||
->addSelect('eval.title AS eval_title');
|
||||
}
|
||||
}
|
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Export\Export;
|
||||
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Chill\MainBundle\Export\Helper\AggregateStringHelper;
|
||||
use Chill\MainBundle\Export\Helper\ExportAddressHelper;
|
||||
use Chill\MainBundle\Export\Helper\TranslatableStringExportLabelHelper;
|
||||
use Chill\MainBundle\Export\ListInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdComposition;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Chill\PersonBundle\Security\Authorization\HouseholdVoter;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function strlen;
|
||||
|
||||
class ListHouseholdInPeriod implements ListInterface, GroupedExportInterface
|
||||
{
|
||||
private const FIELDS = [
|
||||
'id',
|
||||
'membersCount',
|
||||
'membersId',
|
||||
'membersName',
|
||||
'compositionNumberOfChildren',
|
||||
'compositionComment',
|
||||
'compositionType',
|
||||
];
|
||||
|
||||
private ExportAddressHelper $addressHelper;
|
||||
|
||||
private AggregateStringHelper $aggregateStringHelper;
|
||||
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
private RollingDateConverterInterface $rollingDateConverter;
|
||||
|
||||
private TranslatableStringExportLabelHelper $translatableStringHelper;
|
||||
|
||||
public function __construct(
|
||||
ExportAddressHelper $addressHelper,
|
||||
AggregateStringHelper $aggregateStringHelper,
|
||||
EntityManagerInterface $entityManager,
|
||||
RollingDateConverterInterface $rollingDateConverter,
|
||||
TranslatableStringExportLabelHelper $translatableStringHelper
|
||||
) {
|
||||
$this->addressHelper = $addressHelper;
|
||||
$this->aggregateStringHelper = $aggregateStringHelper;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->rollingDateConverter = $rollingDateConverter;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder
|
||||
->add('calc_date', PickRollingDateType::class, [
|
||||
'label' => 'export.list.household.Date of calculation for associated elements',
|
||||
'help' => 'export.list.household.help_description',
|
||||
'data' => new RollingDate(RollingDate::T_TODAY),
|
||||
'required' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes()
|
||||
{
|
||||
return [FormatterInterface::TYPE_LIST];
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'export.list.household.List description';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'Exports of households';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
if (substr($key, 0, strlen('address_fields')) === 'address_fields') {
|
||||
return $this->addressHelper->getLabel($key, $values, $data, 'address_fields');
|
||||
}
|
||||
|
||||
switch ($key) {
|
||||
case 'membersId':
|
||||
case 'membersName':
|
||||
return $this->aggregateStringHelper->getLabelMulti($key, $values, 'export.list.household.' . $key);
|
||||
|
||||
case 'compositionType':
|
||||
//dump($values);
|
||||
return $this->translatableStringHelper->getLabel($key, $values, 'export.list.household.' . $key);
|
||||
|
||||
default:
|
||||
return static function ($value) use ($key) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.list.household.' . $key;
|
||||
}
|
||||
|
||||
return (string) $value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return array_merge(
|
||||
self::FIELDS,
|
||||
$this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields')
|
||||
);
|
||||
}
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.list.household.List household associated with accompanying period title';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::HOUSEHOLD_TYPE;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static function ($el) {
|
||||
return $el['center'];
|
||||
}, $acl);
|
||||
|
||||
$qb = $this->entityManager->createQueryBuilder();
|
||||
|
||||
$qb
|
||||
->from(Household::class, 'household')
|
||||
->distinct()
|
||||
->select('household.id AS id')
|
||||
->join('household.members', 'hmember')
|
||||
->join('hmember.person', 'person')
|
||||
->join('person.accompanyingPeriodParticipations', 'acppart')
|
||||
->join('acppart.accompanyingPeriod', 'acp')
|
||||
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM ' . PersonCenterHistory::class . ' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||
AND acl_count_person_history.center IN (:authorized_centers)
|
||||
'
|
||||
)
|
||||
)
|
||||
->andWhere('hmember.startDate <= :count_household_at_date AND (hmember.endDate IS NULL OR hmember.endDate > :count_household_at_date)')
|
||||
->setParameter('authorized_centers', $centers)
|
||||
->setParameter('count_household_at_date', $this->rollingDateConverter->convert($data['calc_date']));
|
||||
|
||||
$this->addSelectClauses($qb, $this->rollingDateConverter->convert($data['calc_date']));
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return HouseholdVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers(): array
|
||||
{
|
||||
return [
|
||||
Declarations::HOUSEHOLD_TYPE,
|
||||
Declarations::ACP_TYPE,
|
||||
Declarations::PERSON_TYPE,
|
||||
];
|
||||
}
|
||||
|
||||
private function addSelectClauses(QueryBuilder $qb, DateTimeImmutable $calcDate): void
|
||||
{
|
||||
// members at date
|
||||
$qb
|
||||
->addSelect('(SELECT COUNT(members0) FROM ' . HouseholdMember::class . ' members0 '
|
||||
. 'WHERE members0.startDate <= :calcDate AND (members0.endDate IS NULL OR members0.endDate > :calcDate) '
|
||||
. 'AND members0 MEMBER OF household.members) AS membersCount')
|
||||
|
||||
->addSelect('(SELECT AGGREGATE(IDENTITY(members1.person)) FROM ' . HouseholdMember::class . ' members1 '
|
||||
. 'WHERE members1.startDate <= :calcDate AND (members1.endDate IS NULL OR members1.endDate > :calcDate) '
|
||||
. 'AND members1 MEMBER OF household.members) AS membersId')
|
||||
|
||||
->addSelect("(SELECT AGGREGATE(CONCAT(person2.firstName, ' ', person2.lastName)) FROM " . HouseholdMember::class . ' members2 '
|
||||
. 'JOIN members2.person person2 '
|
||||
. 'WHERE members2.startDate <= :calcDate AND (members2.endDate IS NULL OR members2.endDate > :calcDate) '
|
||||
. 'AND members2 MEMBER OF household.members) AS membersName');
|
||||
|
||||
// composition at date
|
||||
$qb
|
||||
->addSelect('(SELECT compo.numberOfChildren FROM ' . HouseholdComposition::class . ' compo '
|
||||
. 'WHERE compo.startDate <= :calcDate AND (compo.endDate IS NULL OR compo.endDate > :calcDate) '
|
||||
. 'AND compo MEMBER OF household.compositions) AS compositionNumberOfChildren')
|
||||
|
||||
->addSelect('(SELECT compo1.comment.comment FROM ' . HouseholdComposition::class . ' compo1 '
|
||||
. 'WHERE compo1.startDate <= :calcDate AND (compo1.endDate IS NULL OR compo1.endDate > :calcDate) '
|
||||
. 'AND compo1 MEMBER OF household.compositions) AS compositionComment')
|
||||
|
||||
->addSelect('(
|
||||
SELECT type2.label
|
||||
FROM ' . HouseholdComposition::class . ' compo2
|
||||
JOIN compo2.householdCompositionType type2
|
||||
WHERE compo2.startDate <= :calcDate AND (compo2.endDate IS NULL OR compo2.endDate > :calcDate)
|
||||
AND compo2 MEMBER OF household.compositions
|
||||
) AS compositionType');
|
||||
|
||||
// address at date
|
||||
$qb
|
||||
->leftJoin('household.addresses', 'addresses')
|
||||
->andWhere(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte('addresses.validFrom', ':calcDate'),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('addresses.validTo'),
|
||||
$qb->expr()->gt('addresses.validTo', ':calcDate')
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addressHelper->addSelectClauses(
|
||||
ExportAddressHelper::F_ALL,
|
||||
$qb,
|
||||
'addresses',
|
||||
'address_fields'
|
||||
);
|
||||
|
||||
// inject date parameter
|
||||
$qb->setParameter('calcDate', $calcDate);
|
||||
}
|
||||
}
|
@@ -72,6 +72,7 @@ class HasTemporaryLocationFilter implements FilterInterface
|
||||
{
|
||||
$builder
|
||||
->add('having_temporarily', ChoiceType::class, [
|
||||
'label' => 'export.filter.course.having_temporarily.label',
|
||||
'choices' => [
|
||||
'export.filter.course.having_temporarily.Having a temporarily location' => true,
|
||||
'export.filter.course.having_temporarily.Having a person\'s location' => false,
|
||||
|
@@ -126,12 +126,4 @@ class UserJobFilter implements FilterInterface
|
||||
{
|
||||
return 'Filter by user job';
|
||||
}
|
||||
|
||||
private function getUserJob(): UserJob
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->security->getUser();
|
||||
|
||||
return $user->getUserJob();
|
||||
}
|
||||
}
|
||||
|
@@ -11,13 +11,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Export\Filter\SocialWorkFilters;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use function in_array;
|
||||
|
||||
@@ -61,13 +60,8 @@ class ReferrerFilter implements FilterInterface
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('accepted_agents', EntityType::class, [
|
||||
'class' => User::class,
|
||||
'choice_label' => function (User $u) {
|
||||
return $this->userRender->renderString($u, []);
|
||||
},
|
||||
$builder->add('accepted_agents', PickUserDynamicType::class, [
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@@ -187,8 +187,12 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
|
||||
);
|
||||
|
||||
foreach ($scopes as $key => $scope) {
|
||||
$orx->add($qb->expr()->isMemberOf(':scope_' . $key, 'ap.scopes'));
|
||||
$orx->add($qb->expr()->orX(
|
||||
$qb->expr()->isMemberOf(':scope_' . $key, 'ap.scopes'),
|
||||
$qb->expr()->eq('ap.user', ':user')
|
||||
));
|
||||
$qb->setParameter('scope_' . $key, $scope);
|
||||
$qb->setParameter('user', $this->security->getUser());
|
||||
}
|
||||
$qb->andWhere($orx);
|
||||
|
||||
|
@@ -34,7 +34,7 @@
|
||||
|
||||
<script>
|
||||
import VueMultiselect from 'vue-multiselect';
|
||||
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
|
||||
import { fetchResults } from 'ChillMainAssets/lib/api/apiMethods';
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
@@ -58,23 +58,17 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getOptions() {
|
||||
const url = `/api/1.0/main/location.json`;
|
||||
makeFetch('GET', url)
|
||||
fetchResults(`/api/1.0/main/location.json`)
|
||||
.then(response => {
|
||||
let options = response.results;
|
||||
let uniqueLocationTypeId = [...new Set(options.map(o => o.locationType.id))];
|
||||
let uniqueLocationTypeId = [...new Set(response.map(o => o.locationType.id))];
|
||||
let results = [];
|
||||
for (let id of uniqueLocationTypeId) {
|
||||
results.push({
|
||||
locationCategories: options.filter(o => o.locationType.id === id)[0].locationType.title.fr,
|
||||
locations: options.filter(o => o.locationType.id === id)
|
||||
locationCategories: response.filter(o => o.locationType.id === id)[0].locationType.title.fr,
|
||||
locations: response.filter(o => o.locationType.id === id)
|
||||
})
|
||||
}
|
||||
this.options = results;
|
||||
return response;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.open({message: error.txt})
|
||||
})
|
||||
},
|
||||
updateAdminLocation(value) {
|
||||
|
@@ -111,14 +111,12 @@
|
||||
</add-async-upload>
|
||||
</li>
|
||||
<li>
|
||||
<add-async-upload-downloader
|
||||
:buttonTitle="$t('download')"
|
||||
:storedObject="d.storedObject"
|
||||
>
|
||||
</add-async-upload-downloader>
|
||||
</li>
|
||||
<li v-if="canEditDocument(d)">
|
||||
<a class="btn btn-wopilink" @click="submitBeforeEdit(d.storedObject)"></a>
|
||||
<document-action-buttons-group
|
||||
:stored-object="d.storedObject"
|
||||
:filename="d.title"
|
||||
:can-edit="true"
|
||||
:execute-before-leave="submitBeforeLeaveToEditor"
|
||||
></document-action-buttons-group>
|
||||
</li>
|
||||
<li v-if="d.workflows.length === 0">
|
||||
<a class="btn btn-delete" @click="removeDocument(d)">
|
||||
@@ -174,6 +172,7 @@ import AddAsyncUpload from 'ChillDocStoreAssets/vuejs/_components/AddAsyncUpload
|
||||
import AddAsyncUploadDownloader from 'ChillDocStoreAssets/vuejs/_components/AddAsyncUploadDownloader.vue';
|
||||
import ListWorkflowModal from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue';
|
||||
import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api.js';
|
||||
import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue";
|
||||
|
||||
const i18n = {
|
||||
messages: {
|
||||
@@ -212,6 +211,7 @@ export default {
|
||||
AddAsyncUpload,
|
||||
AddAsyncUploadDownloader,
|
||||
ListWorkflowModal,
|
||||
DocumentActionButtonsGroup,
|
||||
},
|
||||
i18n,
|
||||
data() {
|
||||
@@ -223,78 +223,6 @@ export default {
|
||||
maxPostSize: 15000000,
|
||||
required: false,
|
||||
},
|
||||
mime: [
|
||||
// TODO temporary hardcoded. to be replaced by twig extension or a collabora server query
|
||||
'application/clarisworks',
|
||||
'application/coreldraw',
|
||||
'application/macwriteii',
|
||||
'application/msword',
|
||||
'application/vnd.lotus-1-2-3',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
|
||||
'application/vnd.ms-excel.sheet.macroEnabled.12',
|
||||
'application/vnd.ms-excel.template.macroEnabled.12',
|
||||
'application/vnd.ms-powerpoint',
|
||||
'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
|
||||
'application/vnd.ms-powerpoint.template.macroEnabled.12',
|
||||
'application/vnd.ms-visio.drawing',
|
||||
'application/vnd.ms-word.document.macroEnabled.12',
|
||||
'application/vnd.ms-word.template.macroEnabled.12',
|
||||
'application/vnd.ms-works',
|
||||
'application/vnd.oasis.opendocument.chart',
|
||||
'application/vnd.oasis.opendocument.formula',
|
||||
'application/vnd.oasis.opendocument.graphics',
|
||||
'application/vnd.oasis.opendocument.graphics-flat-xml',
|
||||
'application/vnd.oasis.opendocument.graphics-template',
|
||||
'application/vnd.oasis.opendocument.presentation',
|
||||
'application/vnd.oasis.opendocument.presentation-flat-xml',
|
||||
'application/vnd.oasis.opendocument.presentation-template',
|
||||
'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'application/vnd.oasis.opendocument.spreadsheet-flat-xml',
|
||||
'application/vnd.oasis.opendocument.spreadsheet-template',
|
||||
'application/vnd.oasis.opendocument.text',
|
||||
'application/vnd.oasis.opendocument.text-flat-xml',
|
||||
'application/vnd.oasis.opendocument.text-master',
|
||||
'application/vnd.oasis.opendocument.text-master-template',
|
||||
'application/vnd.oasis.opendocument.text-template',
|
||||
'application/vnd.oasis.opendocument.text-web',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.template',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
'application/vnd.sun.xml.calc',
|
||||
'application/vnd.sun.xml.calc.template',
|
||||
'application/vnd.sun.xml.chart',
|
||||
'application/vnd.sun.xml.draw',
|
||||
'application/vnd.sun.xml.draw.template',
|
||||
'application/vnd.sun.xml.impress',
|
||||
'application/vnd.sun.xml.impress.template',
|
||||
'application/vnd.sun.xml.math',
|
||||
'application/vnd.sun.xml.writer',
|
||||
'application/vnd.sun.xml.writer.global',
|
||||
'application/vnd.sun.xml.writer.template',
|
||||
'application/vnd.visio',
|
||||
'application/vnd.visio2013',
|
||||
'application/vnd.wordperfect',
|
||||
'application/x-abiword',
|
||||
'application/x-aportisdoc',
|
||||
'application/x-dbase',
|
||||
'application/x-dif-document',
|
||||
'application/x-fictionbook+xml',
|
||||
'application/x-gnumeric',
|
||||
'application/x-hwp',
|
||||
'application/x-iwork-keynote-sffkey',
|
||||
'application/x-iwork-numbers-sffnumbers',
|
||||
'application/x-iwork-pages-sffpages',
|
||||
'application/x-mspublisher',
|
||||
'application/x-mswrite',
|
||||
'application/x-pagemaker',
|
||||
'application/x-sony-bbeb',
|
||||
'application/x-t602',
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -343,10 +271,6 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
ISOToDatetime,
|
||||
canEditDocument(document) {
|
||||
return 'storedObject' in document ?
|
||||
this.mime.includes(document.storedObject.type) : false;
|
||||
},
|
||||
listAllStatus() {
|
||||
console.log('load all status');
|
||||
let url = `/api/`;
|
||||
@@ -363,7 +287,13 @@ export default {
|
||||
return `/chill/wopi/edit/${document.storedObject.uuid}?returnPath=` + encodeURIComponent(
|
||||
window.location.pathname + window.location.search + window.location.hash);
|
||||
},
|
||||
submitBeforeEdit(storedObject) {
|
||||
submitBeforeLeaveToEditor() {
|
||||
console.log('submit beore edit 2');
|
||||
// empty callback
|
||||
const callback = () => null;
|
||||
return this.$store.dispatch('submit', callback).catch(e => { console.log(e); throw e; });
|
||||
},
|
||||
submitBeforeEdit(storedObject) {
|
||||
const callback = (data) => {
|
||||
let evaluation = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key);
|
||||
let document = evaluation.documents.find(d => d.storedObject.id === storedObject.id);
|
||||
|
@@ -142,7 +142,7 @@
|
||||
{{ mm.mimeIcon(d.storedObject.type) }}
|
||||
</div>
|
||||
<div class="col col-lg-4 text-end">
|
||||
{{ m.download_button_small(d.storedObject, d.title) }}
|
||||
{{ d.storedObject|chill_document_button_group(d.title, is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', w), {'small': true}) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
@@ -6,20 +6,20 @@
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_async_upload') }}
|
||||
{{ encore_entry_link_tags('mod_entity_workflow_pick') }}
|
||||
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('mod_async_upload') }}
|
||||
{{ encore_entry_script_tags('mod_entity_workflow_pick') }}
|
||||
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="accompanying-course-work">
|
||||
<h1>{{ block('title') }}</h1>
|
||||
|
||||
|
||||
<div class="flex-table mt-4">
|
||||
{% include '@ChillPerson/AccompanyingCourseWork/_item.html.twig' with {
|
||||
'w': work,
|
||||
@@ -29,7 +29,7 @@
|
||||
} %}
|
||||
<div class="p-3 mt-3">{{ macro.metadata(work) }}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a href="{{ path('chill_person_accompanying_period_work_list', { 'id': accompanyingCourse.id }) }}"
|
||||
@@ -51,7 +51,7 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
@@ -120,20 +120,13 @@
|
||||
</div>
|
||||
|
||||
{% if display_action is defined and display_action == true %}
|
||||
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', evaluation.accompanyingPeriodWork) %}
|
||||
<ul class="record_actions">
|
||||
<li>{{ m.download_button(doc.storedObject, doc.title) }}</li>
|
||||
{% if chill_document_is_editable(doc.storedObject) %}
|
||||
<li>
|
||||
{{ doc.storedObject|chill_document_edit_button }}
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>{{ doc.storedObject|chill_document_button_group(doc.title, is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', evaluation.accompanyingPeriodWork)) }}</li>
|
||||
<li>
|
||||
<a class="btn btn-show" href="{{ path('chill_person_accompanying_period_work_edit', {'id': evaluation.accompanyingPeriodWork.id}) }}">
|
||||
{{ 'Show'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
@@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Search;
|
||||
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Form\Type\ChillPhoneNumberType;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Search\AbstractSearch;
|
||||
use Chill\MainBundle\Search\HasAdvancedSearchFormInterface;
|
||||
@@ -24,7 +25,7 @@ use Chill\PersonBundle\Form\Type\GenderType;
|
||||
use Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface;
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TelType;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Templating\EngineInterface;
|
||||
@@ -94,7 +95,7 @@ class PersonSearch extends AbstractSearch implements HasAdvancedSearchFormInterf
|
||||
'label' => 'Birthdate before',
|
||||
'required' => false,
|
||||
])
|
||||
->add('phonenumber', TelType::class, [
|
||||
->add('phonenumber', ChillPhoneNumberType::class, [
|
||||
'required' => false,
|
||||
'label' => 'Part of the phonenumber',
|
||||
])
|
||||
@@ -116,7 +117,7 @@ class PersonSearch extends AbstractSearch implements HasAdvancedSearchFormInterf
|
||||
|
||||
$string .= empty($data['_default']) ? '' : $data['_default'] . ' ';
|
||||
|
||||
foreach (['firstname', 'lastname', 'gender', 'phonenumber', 'city'] as $key) {
|
||||
foreach (['firstname', 'lastname', 'gender', 'city'] as $key) {
|
||||
$string .= empty($data[$key]) ? '' : $key . ':' .
|
||||
// add quote if contains spaces
|
||||
(strpos($data[$key], ' ') !== false ? '"' . $data[$key] . '"' : $data[$key])
|
||||
@@ -130,6 +131,8 @@ class PersonSearch extends AbstractSearch implements HasAdvancedSearchFormInterf
|
||||
$key . ':' . $data[$key]->format('Y-m-d') . ' ';
|
||||
}
|
||||
|
||||
$string .= empty($data['phonenumber']) ? '' : 'phonenumber:' . $data['phonenumber']->getNationalNumber();
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
@@ -154,6 +157,18 @@ class PersonSearch extends AbstractSearch implements HasAdvancedSearchFormInterf
|
||||
$data[$key] = $date ?? null;
|
||||
}
|
||||
|
||||
if (array_key_exists('phonenumber', $terms)) {
|
||||
try {
|
||||
$phonenumber = new PhoneNumber();
|
||||
$phonenumber->setNationalNumber($terms['phonenumber']);
|
||||
} catch (Exception $ex) {
|
||||
throw new ParsingException("The date for {$key} is "
|
||||
. 'not parsable', 0, $ex);
|
||||
}
|
||||
|
||||
$data['phonenumber'] = $phonenumber ?? null;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@@ -62,7 +62,7 @@ class SimilarPersonMatcher
|
||||
|
||||
public function matchPerson(
|
||||
Person $person,
|
||||
float $precision = 0.15,
|
||||
float $precision = 0.30,
|
||||
string $orderBy = self::SIMILAR_SEARCH_ORDER_BY_SIMILARITY,
|
||||
bool $addYearComparison = false
|
||||
) {
|
||||
@@ -72,41 +72,50 @@ class SimilarPersonMatcher
|
||||
);
|
||||
$query = $this->em->createQuery();
|
||||
|
||||
$dql = 'SELECT p from ChillPersonBundle:Person p '
|
||||
. ' WHERE ('
|
||||
. ' SIMILARITY(p.fullnameCanonical, UNACCENT(LOWER(:fullName))) >= :precision '
|
||||
. ' ) '
|
||||
. ' AND p.center IN (:centers)';
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
|
||||
$qb->select('p')
|
||||
->from(Person::class, 'p')
|
||||
->where('SIMILARITY(p.fullnameCanonical, UNACCENT(LOWER(:fullName))) >= :precision')
|
||||
->andWhere($qb->expr()->in('p.center', ':centers'));
|
||||
|
||||
if (null !== $person->getBirthdate()) {
|
||||
$qb->andWhere($qb->expr()->orX(
|
||||
$qb->expr()->eq('p.birthdate', ':personBirthdate'),
|
||||
$qb->expr()->isNull('p.birthdate')
|
||||
));
|
||||
|
||||
$qb->setParameter('personBirthdate', $person->getBirthdate());
|
||||
}
|
||||
|
||||
if ($person->getId() !== null) {
|
||||
$dql .= ' AND p.id != :personId ';
|
||||
$notDuplicatePersons = $this->personNotDuplicateRepository->findNotDuplicatePerson($person);
|
||||
|
||||
$qb->andWhere($qb->expr()->neq('p.id', ':personId'));
|
||||
$query->setParameter('personId', $person->getId());
|
||||
|
||||
$notDuplicatePersons = $this->personNotDuplicateRepository->findNotDuplicatePerson($person);
|
||||
|
||||
if (count($notDuplicatePersons)) {
|
||||
$dql .= ' AND p.id not in (:notDuplicatePersons)';
|
||||
$qb->andWhere($qb->expr()->notIn('p.id', ':notDuplicatePersons'));
|
||||
$query->setParameter('notDuplicatePersons', $notDuplicatePersons);
|
||||
}
|
||||
}
|
||||
|
||||
switch ($orderBy) {
|
||||
case self::SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL:
|
||||
$dql .= ' ORDER BY p.fullnameCanonical ASC ';
|
||||
$qb->orderBy('p.fullnameCanonical', 'ASC');
|
||||
|
||||
break;
|
||||
|
||||
case self::SIMILAR_SEARCH_ORDER_BY_SIMILARITY:
|
||||
default:
|
||||
$dql .= ' ORDER BY SIMILARITY(p.fullnameCanonical, UNACCENT(LOWER(:fullName))) DESC ';
|
||||
$qb->orderBy('SIMILARITY(p.fullnameCanonical, UNACCENT(LOWER(:fullName)))', 'DESC');
|
||||
}
|
||||
|
||||
$query = $query
|
||||
->setDQL($dql)
|
||||
$qb
|
||||
->setParameter('fullName', $this->personRender->renderString($person, []))
|
||||
->setParameter('centers', $centers)
|
||||
->setParameter('precision', $precision);
|
||||
|
||||
return $query->getResult();
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
}
|
||||
|
@@ -11,16 +11,25 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Security\Authorization;
|
||||
|
||||
use Chill\MainBundle\Security\Authorization\ChillVoterInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use UnexpectedValueException;
|
||||
use function in_array;
|
||||
|
||||
class AccompanyingPeriodWorkEvaluationVoter extends Voter
|
||||
class AccompanyingPeriodWorkEvaluationVoter extends Voter implements ChillVoterInterface
|
||||
{
|
||||
public const ALL = [
|
||||
self::SEE,
|
||||
self::STATS,
|
||||
];
|
||||
|
||||
public const SEE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_EVALUATION_SHOW';
|
||||
|
||||
public const STATS = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_EVALUATION_STATS';
|
||||
|
||||
private Security $security;
|
||||
|
||||
public function __construct(Security $security)
|
||||
@@ -31,7 +40,7 @@ class AccompanyingPeriodWorkEvaluationVoter extends Voter
|
||||
protected function supports($attribute, $subject)
|
||||
{
|
||||
return $subject instanceof AccompanyingPeriodWorkEvaluation
|
||||
&& self::SEE === $attribute;
|
||||
&& in_array($attribute, self::ALL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,6 +50,9 @@ class AccompanyingPeriodWorkEvaluationVoter extends Voter
|
||||
protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
|
||||
{
|
||||
switch ($attribute) {
|
||||
case self::STATS:
|
||||
return $this->security->isGranted(AccompanyingPeriodWorkVoter::STATS, $subject);
|
||||
|
||||
case self::SEE:
|
||||
return $this->security->isGranted(AccompanyingPeriodWorkVoter::SEE, $subject->getAccompanyingPeriodWork());
|
||||
|
||||
|
@@ -11,8 +11,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Security\Authorization;
|
||||
|
||||
use Chill\MainBundle\Security\Authorization\ChillVoterInterface;
|
||||
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
@@ -20,8 +25,15 @@ use UnexpectedValueException;
|
||||
use function get_class;
|
||||
use function in_array;
|
||||
|
||||
class AccompanyingPeriodWorkVoter extends Voter
|
||||
class AccompanyingPeriodWorkVoter extends Voter implements ProvideRoleHierarchyInterface, ChillVoterInterface
|
||||
{
|
||||
public const ALL = [
|
||||
self::SEE,
|
||||
self::CREATE,
|
||||
self::UPDATE,
|
||||
self::DELETE,
|
||||
];
|
||||
|
||||
public const CREATE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_CREATE';
|
||||
|
||||
public const DELETE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_DELETE';
|
||||
@@ -32,9 +44,39 @@ class AccompanyingPeriodWorkVoter extends Voter
|
||||
|
||||
private Security $security;
|
||||
|
||||
public function __construct(Security $security)
|
||||
{
|
||||
private VoterHelperInterface $voterHelper;
|
||||
|
||||
public function __construct(
|
||||
Security $security,
|
||||
VoterHelperFactoryInterface $voterHelperFactory
|
||||
) {
|
||||
$this->security = $security;
|
||||
$this->voterHelper = $voterHelperFactory
|
||||
->generate(self::class)
|
||||
->addCheckFor(null, [self::CREATE])
|
||||
->addCheckFor(AccompanyingPeriod::class, [self::ALL])
|
||||
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||
->build();
|
||||
}
|
||||
|
||||
public function getRoles(): array
|
||||
{
|
||||
return [
|
||||
self::SEE,
|
||||
self::CREATE,
|
||||
self::UPDATE,
|
||||
self::DELETE,
|
||||
];
|
||||
}
|
||||
|
||||
public function getRolesWithHierarchy(): array
|
||||
{
|
||||
return ['Social actions' => $this->getRoles()];
|
||||
}
|
||||
|
||||
public function getRolesWithoutScope(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function supports($attribute, $subject): bool
|
||||
@@ -86,9 +128,4 @@ class AccompanyingPeriodWorkVoter extends Voter
|
||||
|
||||
throw new UnexpectedValueException(sprintf("attribute {$attribute} on instance %s is not supported", get_class($subject)));
|
||||
}
|
||||
|
||||
private function getRoles(): array
|
||||
{
|
||||
return [self::SEE, self::CREATE, self::UPDATE, self::DELETE];
|
||||
}
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Security\Authorization;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Security\Authorization\ChillVoterInterface;
|
||||
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||
@@ -23,7 +24,7 @@ use Symfony\Component\Security\Core\Security;
|
||||
use UnexpectedValueException;
|
||||
use function in_array;
|
||||
|
||||
class HouseholdVoter extends Voter implements ProvideRoleHierarchyInterface
|
||||
class HouseholdVoter extends Voter implements ProvideRoleHierarchyInterface, ChillVoterInterface
|
||||
{
|
||||
public const EDIT = 'CHILL_PERSON_HOUSEHOLD_EDIT';
|
||||
|
||||
@@ -37,7 +38,9 @@ class HouseholdVoter extends Voter implements ProvideRoleHierarchyInterface
|
||||
public const STATS = 'CHILL_PERSON_HOUSEHOLD_STATS';
|
||||
|
||||
private const ALL = [
|
||||
self::EDIT, self::SEE,
|
||||
self::SEE,
|
||||
self::EDIT,
|
||||
self::STATS,
|
||||
];
|
||||
|
||||
private VoterHelperInterface $helper;
|
||||
@@ -60,7 +63,7 @@ class HouseholdVoter extends Voter implements ProvideRoleHierarchyInterface
|
||||
|
||||
public function getRolesWithHierarchy(): array
|
||||
{
|
||||
return ['Person' => $this->getRoles()];
|
||||
return ['Household' => $this->getRoles()];
|
||||
}
|
||||
|
||||
public function getRolesWithoutScope(): array
|
||||
|
@@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Serializer\Normalizer;
|
||||
use Chill\BudgetBundle\Service\Summary\SummaryBudgetInterface;
|
||||
use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper;
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Civility;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
@@ -87,6 +88,7 @@ class PersonDocGenNormalizer implements
|
||||
$dateContext['docgen:expects'] = DateTimeInterface::class;
|
||||
$addressContext = array_merge($context, ['docgen:expects' => Address::class]);
|
||||
$phonenumberContext = array_merge($context, ['docgen:expects' => PhoneNumber::class]);
|
||||
$centerContext = array_merge($context, ['docgen:expects' => Center::class]);
|
||||
$personResourceContext = array_merge($context, [
|
||||
'docgen:expects' => Person\PersonResource::class,
|
||||
// we simplify the list of attributes for the embedded persons
|
||||
@@ -139,6 +141,7 @@ class PersonDocGenNormalizer implements
|
||||
'numberOfChildren' => (string) $person->getNumberOfChildren(),
|
||||
'address' => $this->normalizer->normalize($person->getCurrentPersonAddress(), $format, $addressContext),
|
||||
'resources' => $this->normalizer->normalize($person->getResources(), $format, $personResourceContext),
|
||||
'center' => $this->normalizer->normalize($person->getCenter(), $format, $centerContext),
|
||||
];
|
||||
|
||||
if ($context['docgen:person:with-household'] ?? false) {
|
||||
@@ -240,6 +243,7 @@ class PersonDocGenNormalizer implements
|
||||
|
||||
$attributes = [
|
||||
'id', 'firstName', 'lastName', 'age', 'altNames', 'text',
|
||||
'center' => Center::class,
|
||||
'civility' => Civility::class,
|
||||
'birthdate' => DateTimeInterface::class,
|
||||
'deathdate' => DateTimeInterface::class,
|
||||
|
@@ -13,16 +13,16 @@ namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregato
|
||||
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\JobAggregator;
|
||||
use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\UserJobAggregator;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class JobAggregatorTest extends AbstractAggregatorTest
|
||||
final class UserJobAggregatorTest extends AbstractAggregatorTest
|
||||
{
|
||||
private JobAggregator $aggregator;
|
||||
private UserJobAggregator $aggregator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
@@ -13,15 +13,15 @@ namespace Export\Export;
|
||||
|
||||
use Chill\MainBundle\Test\Export\AbstractExportTest;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Chill\PersonBundle\Export\Export\CountSocialWorkActions;
|
||||
use Chill\PersonBundle\Export\Export\CountAccompanyingPeriodWork;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class CountSocialWorkActionsTest extends AbstractExportTest
|
||||
final class CountAccompanyingPeriodWorkTest extends AbstractExportTest
|
||||
{
|
||||
private CountSocialWorkActions $export;
|
||||
private CountAccompanyingPeriodWork $export;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
@@ -40,6 +40,7 @@ final class PersonDocGenNormalizerTest extends KernelTestCase
|
||||
|
||||
private const BLANK = [
|
||||
'id' => '',
|
||||
'center' => '',
|
||||
'firstName' => '',
|
||||
'lastName' => '',
|
||||
'altNames' => '',
|
||||
@@ -64,6 +65,7 @@ final class PersonDocGenNormalizerTest extends KernelTestCase
|
||||
'numberOfChildren' => '',
|
||||
'age' => '@ignored',
|
||||
'resources' => [],
|
||||
'center' => '@ignored',
|
||||
];
|
||||
|
||||
private NormalizerInterface $normalizer;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user