Merge branch 'master' into VSR-issues

This commit is contained in:
Julien Fastré 2023-01-24 15:36:22 +01:00
commit 6b90a7d2a7
Signed by: julienfastre
GPG Key ID: BDE2190974723FCB
201 changed files with 2768 additions and 708 deletions

View File

@ -9,18 +9,22 @@
],
"require": {
"php": "^7.4",
"ext-json": "*",
"ext-openssl": "*",
"ext-redis": "*",
"champs-libres/async-uploader-bundle": "dev-sf4#d57134aee8e504a83c902ff0cf9f8d36ac418290",
"champs-libres/wopi-bundle": "dev-master#6dd8e0a14e00131eb4b889ecc30270ee4a0e5224",
"champs-libres/wopi-lib": "dev-master#8615f4a45a39fc2b6a98765ea835fcfd39618787",
"champs-libres/wopi-bundle": "dev-master@dev",
"champs-libres/wopi-lib": "dev-master@dev",
"doctrine/doctrine-bundle": "^2.1",
"doctrine/doctrine-migrations-bundle": "^3.0",
"doctrine/orm": "^2.7",
"doctrine/orm": "^2.13.0",
"erusev/parsedown": "^1.7",
"graylog2/gelf-php": "^1.5",
"knplabs/knp-menu-bundle": "^3.0",
"knplabs/knp-time-bundle": "^1.12",
"knpuniversity/oauth2-client-bundle": "^2.10",
"league/csv": "^9.7.1",
"lexik/jwt-authentication-bundle": "^2.16",
"nyholm/psr7": "^1.4",
"ocramius/package-versions": "^1.10 || ^2",
"odolbeau/phone-number-bundle": "^3.6",
@ -29,12 +33,12 @@
"ramsey/uuid-doctrine": "^1.7",
"sensio/framework-extra-bundle": "^5.5",
"spomky-labs/base64url": "^2.0",
"symfony/asset": "^4.4",
"symfony/browser-kit": "^4.4",
"symfony/css-selector": "^4.4",
"symfony/expression-language": "^4.4",
"symfony/form": "^4.4",
"symfony/framework-bundle": "^4.4",
"symfony/http-client": "^4.4 || ^5",
"symfony/http-foundation": "^4.4",
"symfony/intl": "^4.4",
"symfony/mailer": "^5.4",
@ -48,7 +52,6 @@
"symfony/translation": "^4.4",
"symfony/twig-bundle": "^4.4",
"symfony/validator": "^4.4",
"symfony/web-link": "*",
"symfony/webpack-encore-bundle": "^1.11",
"symfony/workflow": "^4.4",
"symfony/yaml": "^4.4",
@ -72,8 +75,7 @@
"symfony/maker-bundle": "^1.20",
"symfony/phpunit-bridge": "^4.4",
"symfony/stopwatch": "^4.4",
"symfony/var-dumper": "^4.4",
"symfony/web-profiler-bundle": "^4.4"
"symfony/var-dumper": "^4.4"
},
"conflict": {
"symfony/symfony": "*"

View File

@ -43,6 +43,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
final class ActivityController extends AbstractController
@ -73,6 +74,8 @@ final class ActivityController extends AbstractController
private ThirdPartyRepository $thirdPartyRepository;
private TranslatorInterface $translator;
private UserRepositoryInterface $userRepository;
public function __construct(
@ -89,7 +92,8 @@ final class ActivityController extends AbstractController
LoggerInterface $logger,
SerializerInterface $serializer,
UserRepositoryInterface $userRepository,
CenterResolverManagerInterface $centerResolver
CenterResolverManagerInterface $centerResolver,
TranslatorInterface $translator
) {
$this->activityACLAwareRepository = $activityACLAwareRepository;
$this->activityTypeRepository = $activityTypeRepository;
@ -105,6 +109,7 @@ final class ActivityController extends AbstractController
$this->serializer = $serializer;
$this->userRepository = $userRepository;
$this->centerResolver = $centerResolver;
$this->translator = $translator;
}
/**
@ -160,7 +165,7 @@ final class ActivityController extends AbstractController
$this->entityManager->remove($activity);
$this->entityManager->flush();
$this->addFlash('success', $this->get('translator')
$this->addFlash('success', $this->translator
->trans('The activity has been successfully removed.'));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
@ -245,7 +250,7 @@ final class ActivityController extends AbstractController
);
}
$this->addFlash('success', $this->get('translator')->trans('Success : activity updated!'));
$this->addFlash('success', $this->translator->trans('Success : activity updated!'));
return $this->redirectToRoute('chill_activity_activity_show', $params);
}
@ -363,6 +368,7 @@ final class ActivityController extends AbstractController
if ($person instanceof Person) {
$entity->setPerson($person);
$entity->getPersons()->add($person);
}
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
@ -472,7 +478,7 @@ final class ActivityController extends AbstractController
);
}
$this->addFlash('success', $this->get('translator')->trans('Success : activity created!'));
$this->addFlash('success', $this->translator->trans('Success : activity created!'));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);

View File

@ -13,15 +13,24 @@ namespace Chill\ActivityBundle\Controller;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Form\ActivityReasonType;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* ActivityReason controller.
*/
class ActivityReasonController extends AbstractController
{
private ActivityReasonRepository $activityReasonRepository;
public function __construct(ActivityReasonRepository $activityReasonRepository)
{
$this->activityReasonRepository = $activityReasonRepository;
}
/**
* Creates a new ActivityReason entity.
*/
@ -56,8 +65,8 @@ class ActivityReasonController extends AbstractController
$entity = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityReason::class)->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find ActivityReason entity.');
if (null === $entity) {
throw new NotFoundHttpException('Unable to find ActivityReason entity.');
}
$editForm = $this->createEditForm($entity);
@ -75,7 +84,7 @@ class ActivityReasonController extends AbstractController
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityReason::class)->findAll();
$entities = $this->activityReasonRepository->findAll();
return $this->render('ChillActivityBundle:ActivityReason:index.html.twig', [
'entities' => $entities,

View File

@ -63,10 +63,8 @@ class ActivityReason
/**
* Get category.
*
* @return ActivityReasonCategory
*/
public function getCategory()
public function getCategory(): ?ActivityReasonCategory
{
return $this->category;
}
@ -107,6 +105,11 @@ class ActivityReason
return $this->name;
}
public function isActiveAndParentActive(): bool
{
return $this->active && null !== $this->getCategory() && $this->getCategory()->getActive();
}
/**
* Set active.
*

View File

@ -60,7 +60,7 @@ class ByCreatorAggregator implements AggregatorInterface
return 'Created by';
}
if (null === $value) {
if (null === $value || '' === $value) {
return '';
}

View File

@ -65,7 +65,7 @@ class BySocialActionAggregator implements AggregatorInterface
return 'Social action';
}
if (null === $value) {
if (null === $value || '' === $value) {
return '';
}

View File

@ -65,7 +65,7 @@ class BySocialIssueAggregator implements AggregatorInterface
return 'Social issues';
}
if (null === $value) {
if (null === $value || '' === $value) {
return '';
}

View File

@ -65,7 +65,7 @@ class ByThirdpartyAggregator implements AggregatorInterface
return 'Accepted thirdparty';
}
if (null === $value) {
if (null === $value || '' === $value) {
return '';
}

View File

@ -65,7 +65,7 @@ class CreatorScopeAggregator implements AggregatorInterface
return 'Scope';
}
if (null === $value) {
if (null === $value || '' === $value) {
return '';
}

View File

@ -65,11 +65,13 @@ class LocationTypeAggregator implements AggregatorInterface
return 'Accepted locationtype';
}
if (null === $value) {
if (null === $value || '' === $value) {
return '';
}
$lt = $this->locationTypeRepository->find($value);
if (null === $lt = $this->locationTypeRepository->find($value)) {
return '';
}
return $this->translatableStringHelper->localize(
$lt->getTitle()

View File

@ -71,7 +71,7 @@ class ActivityTypeAggregator implements AggregatorInterface
return 'Activity type';
}
if (null === $value) {
if (null === $value || '' === $value) {
return '';
}

View File

@ -66,7 +66,7 @@ class ActivityUserAggregator implements AggregatorInterface
return 'Activity user';
}
if (null === $value) {
if (null === $value || '' === $value) {
return '';
}

View File

@ -64,7 +64,7 @@ class ActivityUsersAggregator implements AggregatorInterface
return 'Activity users';
}
if (null === $value) {
if (null === $value || '' === $value) {
return '';
}

View File

@ -63,7 +63,7 @@ class ActivityUsersJobAggregator implements \Chill\MainBundle\Export\AggregatorI
return 'Users \'s job';
}
if (null === $value) {
if (null === $value || '' === $value) {
return '';
}

View File

@ -63,7 +63,7 @@ class ActivityUsersScopeAggregator implements \Chill\MainBundle\Export\Aggregato
return 'Users \'s scope';
}
if (null === $value) {
if (null === $value || '' === $value) {
return '';
}

View File

@ -134,6 +134,10 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
return 'reasons' === $data['level'] ? 'Group by reasons' : 'Group by categories of reason';
}
if (null === $value || '' === $value) {
return '';
}
switch ($data['level']) {
case 'reasons':
$r = $this->activityReasonRepository->find($value);

View File

@ -57,6 +57,7 @@ class SentReceivedAggregator implements AggregatorInterface
switch ($value) {
case null:
case '':
return '';
case 'sent':

View File

@ -17,11 +17,9 @@ use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class ActivityTypeFilter implements FilterInterface
{
@ -44,14 +42,13 @@ class ActivityTypeFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('activity', $qb->getAllAliases(), true)) {
$qb->join(Activity::class, 'activity', Expr\Join::WITH, 'activity.accompanyingPeriod = acp');
}
$clause = $qb->expr()->in('activity.activityType', ':selected_activity_types');
$qb->andWhere($clause);
$qb->setParameter('selected_activity_types', $data['types']);
$qb->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . Activity::class . ' act_type_filter_activity
WHERE act_type_filter_activity.activityType IN (:act_type_filter_activity_types) AND act_type_filter_activity.accompanyingPeriod = acp'
)
);
$qb->setParameter('act_type_filter_activity_types', $data['accepted_activitytypes']);
}
public function applyOn()

View File

@ -11,7 +11,8 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Form;
use Chill\ActivityBundle\Form\Type\TranslatableActivityReasonCategory;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Form\Type\TranslatableActivityReasonCategoryType;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
@ -25,13 +26,13 @@ class ActivityReasonType extends AbstractType
$builder
->add('name', TranslatableStringFormType::class)
->add('active', CheckboxType::class, ['required' => false])
->add('category', TranslatableActivityReasonCategory::class);
->add('category', TranslatableActivityReasonCategoryType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'Chill\ActivityBundle\Entity\ActivityReason',
'data_class' => ActivityReason::class,
]);
}

View File

@ -13,7 +13,7 @@ namespace Chill\ActivityBundle\Form;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityPresence;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Form\Type\PickActivityReasonType;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\DocStoreBundle\Form\StoredObjectType;
use Chill\MainBundle\Entity\Center;
@ -229,19 +229,10 @@ class ActivityType extends AbstractType
}
if ($activityType->isVisible('reasons')) {
$builder->add('reasons', EntityType::class, [
$builder->add('reasons', PickActivityReasonType::class, [
'label' => $activityType->getLabel('reasons'),
'required' => $activityType->isRequired('reasons'),
'class' => ActivityReason::class,
'multiple' => true,
'choice_label' => function (ActivityReason $activityReason) {
return $this->translatableStringHelper->localize($activityReason->getName());
},
'attr' => ['class' => 'select2 '],
'query_builder' => static function (EntityRepository $er) {
return $er->createQueryBuilder('a')
->where('a.active = true');
},
]);
}

View File

@ -12,9 +12,9 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Form\Type;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\ActivityBundle\Templating\Entity\ActivityReasonRender;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
@ -22,31 +22,29 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* FormType to choose amongst activity reasons.
*/
class TranslatableActivityReason extends AbstractType
class PickActivityReasonType extends AbstractType
{
/**
* @var ActivityReasonRender
*/
protected $reasonRender;
private ActivityReasonRepository $activityReasonRepository;
/**
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
private ActivityReasonRender $reasonRender;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
TranslatableStringHelper $translatableStringHelper,
ActivityReasonRender $reasonRender
ActivityReasonRepository $activityReasonRepository,
ActivityReasonRender $reasonRender,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->activityReasonRepository = $activityReasonRepository;
$this->reasonRender = $reasonRender;
$this->translatableStringHelper = $translatableStringHelper;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'class' => 'ChillActivityBundle:ActivityReason',
'class' => ActivityReason::class,
'choice_label' => function (ActivityReason $choice) {
return $this->reasonRender->renderString($choice, []);
},
@ -57,10 +55,7 @@ class TranslatableActivityReason extends AbstractType
return null;
},
'query_builder' => static function (EntityRepository $er) {
return $er->createQueryBuilder('r')
->where('r.active = true');
},
'choices' => $this->activityReasonRepository->findAll(),
'attr' => ['class' => ' select2 '],
]
);

View File

@ -1,59 +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\ActivityBundle\Form\Type;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Description of TranslatableActivityReasonCategory.
*/
class TranslatableActivityReasonCategory extends AbstractType
{
/**
* @var RequestStack
*/
private $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function configureOptions(OptionsResolver $resolver)
{
$locale = $this->requestStack->getCurrentRequest()->getLocale();
$resolver->setDefaults(
[
'class' => 'ChillActivityBundle:ActivityReasonCategory',
'choice_label' => 'name[' . $locale . ']',
'query_builder' => static function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->where('c.active = true');
},
]
);
}
public function getBlockPrefix()
{
return 'translatable_activity_reason_category';
}
public function getParent()
{
return EntityType::class;
}
}

View File

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Form\Type;
use Chill\ActivityBundle\Entity\ActivityReasonCategory;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Description of TranslatableActivityReasonCategory.
*/
class TranslatableActivityReasonCategoryType extends AbstractType
{
private TranslatableStringHelperInterface $translatableStringHelper;
private TranslatorInterface $translator;
public function __construct(TranslatableStringHelperInterface $translatableStringHelper, TranslatorInterface $translator)
{
$this->translatableStringHelper = $translatableStringHelper;
$this->translator = $translator;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'class' => ActivityReasonCategory::class,
'choice_label' => function (ActivityReasonCategory $category) {
return $this->translatableStringHelper->localize($category->getName())
. (!$category->getActive() ? ' (' . $this->translator->trans('inactive') . ')' : '');
},
]
);
}
public function getBlockPrefix()
{
return 'translatable_activity_reason_category';
}
public function getParent()
{
return EntityType::class;
}
}

View File

@ -14,17 +14,38 @@ namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityReason;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* @method ActivityReason|null find($id, $lockMode = null, $lockVersion = null)
* @method ActivityReason|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityReason[] findAll()
* @method ActivityReason[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityReasonRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
private RequestStack $requestStack;
public function __construct(
ManagerRegistry $registry,
RequestStack $requestStack
) {
parent::__construct($registry, ActivityReason::class);
$this->requestStack = $requestStack;
}
/**
* @return ActivityReason[]
*/
public function findAll(): array
{
$qb = $this->createQueryBuilder('ar');
$qb->select(['ar'])
->leftJoin('ar.category', 'category')
->addOrderBy('JSON_EXTRACT(category.name, :lang)')
->addOrderBy('JSON_EXTRACT(ar.name, :lang)')
->setParameter('lang', $this->requestStack->getCurrentRequest()->getLocale() ?? 'fr');
return $qb->getQuery()->getResult();
}
}

View File

@ -117,7 +117,8 @@ export default {
target: { //name, id
},
edit: false,
addressId: null
addressId: null,
defaults: window.addaddress
}
}
}

View File

@ -30,7 +30,7 @@ const store = createStore({
},
getters: {
suggestedEntities(state) {
if (typeof state.activity.accompanyingPeriod === "undefined") {
if (typeof state.activity.accompanyingPeriod === "undefined" || state.activity.accompanyingPeriod === null) {
return [];
}
const allEntities = [

View File

@ -39,6 +39,9 @@ const makeConcernedThirdPartiesLocation = (locationType, store) => {
return locations;
};
const makeAccompanyingPeriodLocation = (locationType, store) => {
if (store.state.activity.accompanyingPeriod === null) {
return {};
}
const accPeriodLocation = store.state.activity.accompanyingPeriod.location;
return {
type: 'location',

View File

@ -7,13 +7,25 @@
<thead>
<tr>
<th>{{ 'Name'|trans }}</th>
<th>{{ 'Active'|trans }}</th>
<th>{{ 'Actions'|trans }}</th>
</tr>
</thead>
<tbody>
{% for entity in entities %}
<tr class="{% if entity.active %}active{% else %}inactive{% endif %}">
<td><a href="{{ path('chill_activity_activityreason_show', { 'id': entity.id }) }}">{{ entity.name|localize_translatable_string }}</a></td>
<td>
{% if entity.category is not null -%}
{{ entity.category.name|localize_translatable_string }} >
{% endif -%}
{{ entity.name|localize_translatable_string }}
</td>
<td>
<i class="fa {% if entity.active %}fa-check-square-o{% else %}fa-square-o{% endif %}"></i>
{% if entity.active and not entity.isActiveAndParentActive %}
<span class="badge text-bg-danger text-white">{{ 'Associated activity reason category is inactive'|trans }}</span>
{% endif %}
</td>
<td>
<ul class="record_actions">
<li>

View File

@ -7,6 +7,7 @@
<thead>
<tr>
<th>{{ 'Name'|trans }}</th>
<th>{{ 'Active'|trans }}</th>
<th>{{ 'Actions'|trans }}</th>
</tr>
</thead>
@ -14,7 +15,11 @@
{% for entity in entities %}
<tr>
<td>
<a href="{{ path('chill_activity_activityreasoncategory_show', { 'id': entity.id }) }}">{{ entity.name|localize_translatable_string }}</a></td>
{{ entity.name|localize_translatable_string }}
</td>
<td>
<i class="fa {% if entity.active %}fa-check-square-o{% else %}fa-square-o{% endif %}"></i>
</td>
<td>
<ul class="record_actions">
<li>

View File

@ -11,7 +11,7 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Form\Type;
use Chill\ActivityBundle\Form\Type\TranslatableActivityReason;
use Chill\ActivityBundle\Form\Type\PickActivityReasonType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Component\Form\PreloadedExtension;
use Symfony\Component\Form\Test\TypeTestCase;
@ -36,7 +36,7 @@ final class TranslatableActivityReasonTest extends TypeTestCase
public function testSimple()
{
$translatableActivityReasonType = new TranslatableActivityReason(
$translatableActivityReasonType = new PickActivityReasonType(
$this->getTranslatableStringHelper()
);

View File

@ -1,4 +1,11 @@
services:
Chill\ActivityBundle\Controller\ActivityController:
_defaults:
autowire: true
autoconfigure: true
Chill\ActivityBundle\Controller\:
resource: '../../Controller/'
tags: ['controller.service_arguments']
Chill\ActivityBundle\Controller\ActivityController:
tags: ['controller.service_arguments']

View File

@ -1,19 +1,12 @@
---
services:
chill.activity.form.type.translatableactivityreasoncategory:
class: Chill\ActivityBundle\Form\Type\TranslatableActivityReasonCategory
arguments:
- "@request_stack"
tags:
- { name: form.type, alias: translatable_activity_reason_category }
Chill\ActivityBundle\Form\Type\TranslatableActivityReasonCategoryType:
autowire: true
autoconfigure: true
chill.activity.form.type.translatableactivityreason:
class: Chill\ActivityBundle\Form\Type\TranslatableActivityReason
arguments:
$translatableStringHelper: "@chill.main.helper.translatable_string"
$reasonRender: '@Chill\ActivityBundle\Templating\Entity\ActivityReasonRender'
tags:
- { name: form.type, alias: translatable_activity_reason }
Chill\ActivityBundle\Form\Type\PickActivityReasonType:
autowire: true
autoconfigure: true
chill.activity.form.type.translatableactivitytype:
class: Chill\ActivityBundle\Form\Type\TranslatableActivityType

View File

@ -1,5 +1,8 @@
---
services:
Chill\ActivityBundle\Repository\ActivityReasonRepository:
autowire: true
chill_activity.repository.activity_type: '@Chill\ActivityBundle\Repository\ActivityTypeRepository'
chill_activity.repository.reason: '@Chill\ActivityBundle\Repository\ActivityReasonRepository'
chill_activity.repository.reason_category: '@Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository'

View File

@ -117,6 +117,7 @@ Activity Reasons: Sujets d'une activité
Activity Reasons Category: Catégories de sujet d'activités
Activity Types Categories: Catégories des types d'activité
Activity Presences: Presences aux activités
Associated activity reason category is inactive: La catégorie de sujet attachée est inactive
# Crud

View File

@ -0,0 +1,26 @@
<?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\Controller\Admin;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class AdminController extends AbstractController
{
/**
* @Route("/{_locale}/admin/budget", name="chill_admin_budget")
*/
public function indexAdminAction()
{
return $this->render('@ChillBudget/Admin/index.html.twig');
}
}

View File

@ -0,0 +1,28 @@
<?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\Controller\Admin;
use Chill\MainBundle\CRUD\Controller\CRUDController;
use Chill\MainBundle\Pagination\PaginatorInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\HttpFoundation\Request;
class ChargeKindController extends CRUDController
{
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
{
/** @var QueryBuilder $query */
$query->addOrderBy('e.ordering', 'ASC');
return $query;
}
}

View File

@ -0,0 +1,28 @@
<?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\Controller\Admin;
use Chill\MainBundle\CRUD\Controller\CRUDController;
use Chill\MainBundle\Pagination\PaginatorInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\HttpFoundation\Request;
class ResourceKindController extends CRUDController
{
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
{
/** @var QueryBuilder $query */
$query->addOrderBy('e.ordering', 'ASC');
return $query;
}
}

View File

@ -11,6 +11,10 @@ declare(strict_types=1);
namespace Chill\BudgetBundle\DependencyInjection;
use Chill\BudgetBundle\Controller\Admin\ChargeKindController;
use Chill\BudgetBundle\Controller\Admin\ResourceKindController;
use Chill\BudgetBundle\Entity\ChargeKind;
use Chill\BudgetBundle\Entity\ResourceKind;
use Chill\BudgetBundle\Security\Authorization\BudgetElementVoter;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -33,6 +37,7 @@ class ChillBudgetExtension extends Extension implements PrependExtensionInterfac
$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');
$loader->load('services/controller.yaml');
$loader->load('services/templating.yaml');
@ -48,6 +53,7 @@ class ChillBudgetExtension extends Extension implements PrependExtensionInterfac
{
$this->prependAuthorization($container);
$this->prependRoutes($container);
$this->prependCruds($container);
}
/** (non-PHPdoc).
@ -75,6 +81,56 @@ class ChillBudgetExtension extends Extension implements PrependExtensionInterfac
]);
}
protected function prependCruds(ContainerBuilder $container)
{
$container->prependExtensionConfig('chill_main', [
'cruds' => [
[
'class' => ChargeKind::class,
'name' => 'charge_kind',
'base_path' => '/admin/budget/charge-kind',
'form_class' => \Chill\BudgetBundle\Form\Admin\ChargeKindType::class,
'controller' => ChargeKindController::class,
'actions' => [
'index' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillBudget/Admin/Charge/index.html.twig',
],
'new' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillBudget/Admin/Charge/new.html.twig',
],
'edit' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillBudget/Admin/Charge/edit.html.twig',
],
],
],
[
'class' => ResourceKind::class,
'name' => 'resource_kind',
'base_path' => '/admin/budget/resource-kind',
'form_class' => \Chill\BudgetBundle\Form\Admin\ResourceKindType::class,
'controller' => ResourceKindController::class,
'actions' => [
'index' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillBudget/Admin/Resource/index.html.twig',
],
'new' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillBudget/Admin/Resource/new.html.twig',
],
'edit' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillBudget/Admin/Resource/edit.html.twig',
],
],
],
],
]);
}
protected function storeConfig($position, array $config, ContainerBuilder $container)
{
$container

View File

@ -26,6 +26,7 @@ class Configuration implements ConfigurationInterface
// ressources
->arrayNode('resources')->defaultValue([])
->setDeprecated('Chill', '2.0', 'Since the introduction of budget admin entities, config is no longer used')
->arrayPrototype()
->children()
->scalarNode('key')->isRequired()->cannotBeEmpty()
@ -49,6 +50,7 @@ class Configuration implements ConfigurationInterface
->end()
->end()
->arrayNode('charges')->defaultValue([])
->setDeprecated('Chill', '2.0', 'Since the introduction of budget admin entities, config is no longer used')
->arrayPrototype()
->children()
->scalarNode('key')->isRequired()->cannotBeEmpty()

View File

@ -11,8 +11,8 @@ declare(strict_types=1);
namespace Chill\BudgetBundle\Entity;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\HasCentersInterface;
use Chill\PersonBundle\Entity\Person;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
@ -22,7 +22,7 @@ use Doctrine\ORM\Mapping as ORM;
* @ORM\Table(name="chill_budget.charge")
* @ORM\Entity(repositoryClass="Chill\BudgetBundle\Repository\ChargeRepository")
*/
class Charge extends AbstractElement implements HasCenterInterface
class Charge extends AbstractElement implements HasCentersInterface
{
public const HELP_ASKED = 'running';
@ -39,6 +39,12 @@ class Charge extends AbstractElement implements HasCenterInterface
self::HELP_NOT_RELEVANT,
];
/**
* @ORM\ManyToOne(targetEntity=ChargeKind::class, inversedBy="AbstractElement")
* @ORM\JoinColumn
*/
private ?ChargeKind $charge = null;
/**
* @var string
* @ORM\Column(name="help", type="string", nullable=true)
@ -46,22 +52,29 @@ class Charge extends AbstractElement implements HasCenterInterface
private $help = self::HELP_NOT_RELEVANT;
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
private ?int $id = null;
public function __construct()
{
$this->setStartDate(new DateTimeImmutable('today'));
}
public function getCenter(): ?Center
public function getCenters(): array
{
return $this->getPerson()->getCenter();
if (null !== $this->getPerson()) {
return [$this->getPerson()->getCenter()];
}
return $this->getHousehold()->getCurrentPersons()->map(static fn (Person $p) => $p->getCenter())->toArray();
}
public function getCharge(): ?ChargeKind
{
return $this->charge;
}
public function getHelp()
@ -89,6 +102,13 @@ class Charge extends AbstractElement implements HasCenterInterface
return false;
}
public function setCharge(?ChargeKind $charge): self
{
$this->charge = $charge;
return $this;
}
public function setHelp($help)
{
$this->help = $help;

View File

@ -0,0 +1,108 @@
<?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\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Type of charge.
*
* @ORM\Table(name="chill_budget.charge_type")
* @ORM\Entity
*/
class ChargeKind
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* @ORM\Column(type="boolean", options={"default": true})
*/
private bool $isActive = true;
/**
* @ORM\Column(type="string", length=255, options={"default": ""}, nullable=false)
*/
private string $kind = '';
/**
* @ORM\Column(type="json", length=255, options={"default": "[]"})
*/
private array $name = [];
/**
* @ORM\Column(type="float", options={"default": 0.00})
*/
private float $ordering = 0.00;
/**
* @ORM\Column(type="json", length=255, options={"default": "[]"})
*/
private array $tags = [];
public function getId(): ?int
{
return $this->id;
}
public function getIsActive(): bool
{
return $this->isActive;
}
public function getKind(): ?string
{
return $this->kind;
}
public function getName(): ?array
{
return $this->name;
}
public function getOrdering(): float
{
return $this->ordering;
}
public function setIsActive(bool $isActive): self
{
$this->isActive = $isActive;
return $this;
}
public function setKind(?string $kind): self
{
$this->kind = $kind;
return $this;
}
public function setName(array $name): self
{
$this->name = $name;
return $this;
}
public function setOrdering(float $ordering): ChargeKind
{
$this->ordering = $ordering;
return $this;
}
}

View File

@ -11,8 +11,8 @@ declare(strict_types=1);
namespace Chill\BudgetBundle\Entity;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\HasCentersInterface;
use Chill\PersonBundle\Entity\Person;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
@ -22,25 +22,33 @@ use Doctrine\ORM\Mapping as ORM;
* @ORM\Table(name="chill_budget.resource")
* @ORM\Entity(repositoryClass="Chill\BudgetBundle\Repository\ResourceRepository")
*/
class Resource extends AbstractElement implements HasCenterInterface
class Resource extends AbstractElement implements HasCentersInterface
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity=ResourceKind::class, inversedBy="AbstractElement")
* @ORM\JoinColumn
*/
private ?ResourceKind $resource = null;
public function __construct()
{
$this->setStartDate(new DateTimeImmutable('today'));
}
public function getCenter(): ?Center
public function getCenters(): array
{
return $this->getPerson()->getCenter();
if (null !== $this->getPerson()) {
return [$this->getPerson()->getCenter()];
}
return $this->getHousehold()->getCurrentPersons()->map(static fn (Person $p) => $p->getCenter())->toArray();
}
/**
@ -53,6 +61,11 @@ class Resource extends AbstractElement implements HasCenterInterface
return $this->id;
}
public function getResource(): ?ResourceKind
{
return $this->resource;
}
public function isCharge(): bool
{
return false;
@ -62,4 +75,11 @@ class Resource extends AbstractElement implements HasCenterInterface
{
return true;
}
public function setResource(?ResourceKind $resource): self
{
$this->resource = $resource;
return $this;
}
}

View File

@ -0,0 +1,108 @@
<?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\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Type of resource.
*
* @ORM\Table(name="chill_budget.resource_type")
* @ORM\Entity
*/
class ResourceKind
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* @ORM\Column(type="boolean", options={"default": true})
*/
private bool $isActive = true;
/**
* @ORM\Column(type="string", length=255, nullable=false, options={"default": ""})
*/
private string $kind = '';
/**
* @ORM\Column(type="json", length=255, options={"default": "[]"})
*/
private array $name = [];
/**
* @ORM\Column(type="float", options={"default": 0.00})
*/
private float $ordering = 0.00;
/**
* @ORM\Column(type="json", length=255, options={"default": "[]"})
*/
private array $tags = [];
public function getId(): ?int
{
return $this->id;
}
public function getIsActive(): bool
{
return $this->isActive;
}
public function getKind(): ?string
{
return $this->kind;
}
public function getName(): ?array
{
return $this->name;
}
public function getOrdering(): float
{
return $this->ordering;
}
public function setIsActive(bool $isActive): self
{
$this->isActive = $isActive;
return $this;
}
public function setKind(?string $kind): self
{
$this->kind = $kind;
return $this;
}
public function setName(array $name): self
{
$this->name = $name;
return $this;
}
public function setOrdering(float $ordering): self
{
$this->ordering = $ordering;
return $this;
}
}

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\BudgetBundle\Form\Admin;
use Chill\BudgetBundle\Entity\ChargeKind;
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\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ChargeKindType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TranslatableStringFormType::class, [
'label' => 'Nom',
])
->add('ordering', NumberType::class)
->add('isActive', CheckboxType::class, [
'label' => 'Actif ?',
'required' => false,
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefault('class', ChargeKind::class);
}
}

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\BudgetBundle\Form\Admin;
use Chill\BudgetBundle\Entity\ResourceKind;
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\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ResourceKindType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TranslatableStringFormType::class, [
'label' => 'Nom',
])
->add('ordering', NumberType::class)
->add('isActive', CheckboxType::class, [
'label' => 'Actif ?',
'required' => false,
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefault('class', ResourceKind::class);
}
}

View File

@ -13,15 +13,18 @@ namespace Chill\BudgetBundle\Form;
use Chill\BudgetBundle\Config\ConfigRepository;
use Chill\BudgetBundle\Entity\Charge;
use Chill\BudgetBundle\Entity\ChargeKind;
use Chill\BudgetBundle\Repository\ChargeKindRepository;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
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;
@ -31,20 +34,34 @@ class ChargeType extends AbstractType
protected TranslatableStringHelperInterface $translatableStringHelper;
private ChargeKindRepository $repository;
private TranslatorInterface $translator;
public function __construct(
ConfigRepository $configRepository,
TranslatableStringHelperInterface $translatableStringHelper
TranslatableStringHelperInterface $translatableStringHelper,
ChargeKindRepository $repository,
TranslatorInterface $translator
) {
$this->configRepository = $configRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->repository = $repository;
$this->translator = $translator;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('type', ChoiceType::class, [
'choices' => $this->getTypes(),
'placeholder' => 'Choose a charge type',
->add('charge', EntityType::class, [
'class' => ChargeKind::class,
'choices' => $this->repository->findAllActive(),
'label' => 'Charge type',
'required' => true,
'placeholder' => $this->translator->trans('admin.form.Choose the type of charge'),
'choice_label' => function (ChargeKind $resource) {
return $this->translatableStringHelper->localize($resource->getName());
},
'attr' => ['class' => 'select2'],
])
->add('amount', MoneyType::class)

View File

@ -13,15 +13,17 @@ namespace Chill\BudgetBundle\Form;
use Chill\BudgetBundle\Config\ConfigRepository;
use Chill\BudgetBundle\Entity\Resource;
use Chill\BudgetBundle\Entity\ResourceKind;
use Chill\BudgetBundle\Repository\ResourceKindRepository;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
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
@ -30,21 +32,34 @@ class ResourceType extends AbstractType
protected TranslatableStringHelperInterface $translatableStringHelper;
private ResourceKindRepository $repository;
private TranslatorInterface $translator;
public function __construct(
ConfigRepository $configRepository,
TranslatableStringHelperInterface $translatableStringHelper
TranslatableStringHelperInterface $translatableStringHelper,
ResourceKindRepository $repository,
TranslatorInterface $translator
) {
$this->configRepository = $configRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->repository = $repository;
$this->translator = $translator;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('type', ChoiceType::class, [
'choices' => $this->getTypes(),
'placeholder' => 'Choose a resource type',
'label' => 'Resource element type',
->add('resource', EntityType::class, [
'class' => ResourceKind::class,
'choices' => $this->repository->findAllActive(),
'label' => 'Resource type',
'required' => true,
'placeholder' => $this->translator->trans('admin.form.Choose the type of resource'),
'choice_label' => function (ResourceKind $resource) {
return $this->translatableStringHelper->localize($resource->getName());
},
'attr' => ['class' => 'select2'],
])
->add('amount', MoneyType::class)

View File

@ -0,0 +1,62 @@
<?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\Menu;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Knp\Menu\MenuItem;
use Symfony\Component\Security\Core\Security;
final class AdminMenuBuilder implements LocalMenuBuilderInterface
{
private Security $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
// all the entries below must have ROLE_ADMIN permissions
if (!$this->security->isGranted('ROLE_ADMIN')) {
return;
}
$menu->addChild('Budget', [
'route' => 'chill_admin_budget',
])
->setAttribute('class', 'list-group-item-header')
->setExtras([
'order' => 7050,
'explain' => 'Budget resource and charge type configuration',
]);
$menu
->addChild('admin.menu.Resource types', [
'route' => 'chill_crud_resource_kind_index',
])
->setExtras([
'order' => 7060,
]);
$menu
->addChild('admin.menu.Charge types', [
'route' => 'chill_crud_charge_kind_index',
])
->setExtras([
'order' => 7070,
]);
}
public static function getMenuIds(): array
{
return ['admin_section', 'admin_budget'];
}
}

View File

@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\BudgetBundle\Repository;
use Chill\BudgetBundle\Entity\ChargeKind;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
class ChargeKindRepository implements ObjectRepository
{
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(ChargeKind::class);
}
public function find($id): ?ChargeKind
{
return $this->repository->find($id);
}
/**
* @return ChargeType[]
*/
public function findAll(): array
{
return $this->repository->findAll();
}
/**
* @return ChargeType[]
*/
public function findAllActive(): array
{
$qb = $this->repository->createQueryBuilder('c');
return $qb
->select('c')
->where($qb->expr()->eq('c.isActive', 'true'))
->orderBy('c.ordering', 'ASC')
->getQuery()
->getResult();
}
/**
* @return ChargeType[]
*/
public function findAllByType(string $type): array
{
return $this->findBy(['elementType' => $type]);
}
/**
* @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
{
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
}
public function findOneBy(array $criteria): ?ChargeKind
{
return $this->repository->findOneBy($criteria);
}
public function getClassName(): string
{
return ChargeKind::class;
}
}

View File

@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\BudgetBundle\Repository;
use Chill\BudgetBundle\Entity\ResourceKind;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
class ResourceKindRepository implements ObjectRepository
{
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(ResourceKind::class);
}
public function find($id): ?ResourceKind
{
return $this->repository->find($id);
}
/**
* @return ResourceType[]
*/
public function findAll(): array
{
return $this->repository->findAll();
}
/**
* @return ResourceType[]
*/
public function findAllActive(): array
{
$qb = $this->repository->createQueryBuilder('r');
return $qb
->select('r')
->where($qb->expr()->eq('r.isActive', 'true'))
->orderBy('r.ordering', 'ASC')
->getQuery()
->getResult();
}
/**
* @return ResourceType[]
*/
public function findAllByType(string $type): array
{
return $this->findBy(['elementType' => $type]);
}
/**
* @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
{
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
}
public function findOneBy(array $criteria): ?ResourceKind
{
return $this->repository->findOneBy($criteria);
}
public function getClassName(): string
{
return ResourceKind::class;
}
}

View File

@ -0,0 +1,12 @@
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
{% block title %}
{% include('@ChillMain/CRUD/_edit_title.html.twig') %}
{% endblock %}
{% block admin_content %}
{% embed '@ChillMain/CRUD/_edit_content.html.twig' %}
{% block content_form_actions_view %}{% endblock %}
{% block content_form_actions_save_and_show %}{% endblock %}
{% endembed %}
{% endblock %}

View File

@ -0,0 +1,49 @@
{% extends '@ChillMain/Admin/layoutWithVerticalMenu.html.twig' %}
{% block title %}{{ 'admin.title.Charge Type List'|trans }}{% endblock title %}
{% block admin_content %}
<h1>{{ 'admin.title.Charge Type List'|trans }}</h1>
<table class="records_list table table-bordered border-dark">
<thead>
<tr>
<th>{{ 'Ordering'|trans }}</th>
<th>{{ 'Name'|trans }}</th>
<th>{{ 'Active'|trans }}</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
{% for entity in entities %}
<tr>
<td>{{ entity.ordering }}</td>
<td>{{ entity|chill_entity_render_box }}</td>
<td style="text-align:center;">
{%- if entity.isActive -%}
<i class="fa fa-check-square-o"></i>
{%- else -%}
<i class="fa fa-square-o"></i>
{%- endif -%}
</td>
<td>
<ul class="record_actions">
<li>
<a href="{{ path('chill_crud_charge_kind_edit', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a>
</li>
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<ul class="record_actions sticky-form-buttons">
<li>
<a href="{{ path('chill_crud_charge_kind_new') }}" class="btn btn-create">
{{ 'admin.new.Create a new charge type'|trans }}
</a>
</li>
</ul>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
{% block title %}
{% include('@ChillMain/CRUD/_new_title.html.twig') %}
{% endblock %}
{% block admin_content %}
{% embed '@ChillMain/CRUD/_new_content.html.twig' %}
{% block content_form_actions_save_and_show %}{% endblock %}
{% endembed %}
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
{% block title %}
{% include('@ChillMain/CRUD/_edit_title.html.twig') %}
{% endblock %}
{% block admin_content %}
{% embed '@ChillMain/CRUD/_edit_content.html.twig' %}
{% block content_form_actions_view %}{% endblock %}
{% block content_form_actions_save_and_show %}{% endblock %}
{% endembed %}
{% endblock %}

View File

@ -0,0 +1,49 @@
{% extends '@ChillMain/Admin/layoutWithVerticalMenu.html.twig' %}
{% block title %}{{ 'admin.title.Resource Type List'|trans }}{% endblock title %}
{% block admin_content %}
<h1>{{ 'admin.title.Resource Type List'|trans }}</h1>
<table class="records_list table table-bordered border-dark">
<thead>
<tr>
<th>{{ 'Ordering'|trans }}</th>
<th>{{ 'Name'|trans }}</th>
<th>{{ 'Active'|trans }}</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
{% for entity in entities %}
<tr>
<td>{{ entity.ordering }}</td>
<td>{{ entity|chill_entity_render_box }}</td>
<td style="text-align:center;">
{%- if entity.isActive -%}
<i class="fa fa-check-square-o"></i>
{%- else -%}
<i class="fa fa-square-o"></i>
{%- endif -%}
</td>
<td>
<ul class="record_actions">
<li>
<a href="{{ path('chill_crud_resource_kind_edit', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a>
</li>
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<ul class="record_actions sticky-form-buttons">
<li>
<a href="{{ path('chill_crud_resource_kind_new') }}" class="btn btn-create">
{{ 'admin.new.Create a new resource type'|trans }}
</a>
</li>
</ul>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
{% block title %}
{% include('@ChillMain/CRUD/_new_title.html.twig') %}
{% endblock %}
{% block admin_content %}
{% embed '@ChillMain/CRUD/_new_content.html.twig' %}
{% block content_form_actions_save_and_show %}{% endblock %}
{% endembed %}
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends "@ChillMain/Admin/layoutWithVerticalMenu.html.twig" %}
{% block vertical_menu_content %}
{{ chill_menu('admin_budget', {
'layout': '@ChillMain/Admin/menu_admin_section.html.twig',
}) }}
{% endblock %}
{% block layout_wvm_content %}
{% block admin_content %}
<!-- block content empty -->
<h1>{{ 'admin.title.Budget configuration'|trans }}</h1>
{% endblock %}
{% endblock %}

View File

@ -16,7 +16,11 @@
<td class="column-wide el-type">
<span class="badge-title">
<span class="title_label title_label_{{ family }}"></span>
<span class="title_action">{{ f.type|budget_element_type_display(family) }}<span>
{% if f.isResource %}
<span class="title_action">{{ f.resource.name|localize_translatable_string }}<span>
{% else %}
<span class="title_action">{{ f.charge.name|localize_translatable_string }}<span>
{% endif %}
</span>
</td>
<td class="column-small">{{ f.amount|format_currency('EUR') }}</td>

View File

@ -3,13 +3,13 @@
{% set indexPage = 'chill_budget_elements_index' %}
{% set activeRouteKey = '' %}
{% set person = element.person %}
{% set confirm_question = 'Are you sure you want to remove the charge "%type%" associated to "%name%" ?'|trans({ '%name%' : person.firstname ~ ' ' ~ person.lastname, '%type%': element.type|budget_element_type_display('charge') } ) %}
{% set confirm_question = 'Are you sure you want to remove the charge "%type%" associated to "%name%" ?'|trans({ '%name%' : person.firstname ~ ' ' ~ person.lastname, '%type%': element.charge.getName | localize_translatable_string } ) %}
{% else %}
{% set template = '@ChillPerson/Household/layout.html.twig' %}
{% set indexPage = 'chill_budget_elements_household_index' %}
{% set activeRouteKey = '' %}
{% set household = element.household %}
{% set confirm_question = 'Are you sure you want to remove the charge "%type%" associated to household "%household%" ?'|trans({ '%household%' : household.id, '%type%': element.type|budget_element_type_display('charge') } ) %}
{% set confirm_question = 'Are you sure you want to remove the charge "%type%" associated to household "%household%" ?'|trans({ '%household%' : household.id, '%type%': element.charge.getName | localize_translatable_string } ) %}
{% endif %}
{% extends template %}

View File

@ -21,7 +21,7 @@
{{ form_start(form) }}
{{ form_row(form.type) }}
{{ form_row(form.charge) }}
{{ form_row(form.amount) }}
{{ form_row(form.help) }}
{{ form_row(form.comment) }}

View File

@ -20,7 +20,7 @@
{{ form_start(form) }}
{{ form_row(form.type) }}
{{ form_row(form.charge) }}
{{ form_row(form.amount) }}
{{ form_row(form.help) }}
{{ form_row(form.comment) }}

View File

@ -25,7 +25,7 @@
<div class="item-row">
<h2 class="badge-title">
<span class="title_label title_label_charge"></span>
<span class="title_action">{{ element.type|budget_element_type_display('charge') }}</span>
<span class="title_action">{{ element.charge.getName | localize_translatable_string }}</span>
</h2>
</div>
<div class="item-row separator">

View File

@ -0,0 +1 @@
<span class="chill-entity">{{ entity.name|localize_translatable_string }}</span>

View File

@ -3,13 +3,13 @@
{% set indexPage = 'chill_budget_elements_index' %}
{% set activeRouteKey = '' %}
{% set person = element.person %}
{% set confirm_question = 'Are you sure you want to remove the ressource "%type%" associated to "%name%" ?'|trans({ '%name%' : person.firstname ~ ' ' ~ person.lastname, '%type%': element.type|budget_element_type_display('resource') } ) %}
{% set confirm_question = 'Are you sure you want to remove the ressource "%type%" associated to "%name%" ?'|trans({ '%name%' : person.firstname ~ ' ' ~ person.lastname, '%type%': element.resource.getName | localize_translatable_string } ) %}
{% else %}
{% set template = '@ChillPerson/Household/layout.html.twig' %}
{% set indexPage = 'chill_budget_elements_household_index' %}
{% set activeRouteKey = '' %}
{% set household = element.household %}
{% set confirm_question = 'Are you sure you want to remove the ressource "%type%" associated to household "%household%" ?'|trans({ '%household%' : household.id, '%type%': element.type|budget_element_type_display('resource') } ) %}
{% set confirm_question = 'Are you sure you want to remove the ressource "%type%" associated to household "%household%" ?'|trans({ '%household%' : household.id, '%type%': element.resource.getName | localize_translatable_string} ) %}
{% endif %}
{% extends template %}

View File

@ -23,7 +23,7 @@
{{ form_start(form) }}
{{ form_row(form.type) }}
{{ form_row(form.resource) }}
{{ form_row(form.amount) }}
{{ form_row(form.comment) }}
{{ form_row(form.startDate) }}

View File

@ -22,8 +22,7 @@
<h1>{{ title }}</h1>
{{ form_start(form) }}
{{ form_row(form.type) }}
{{ form_row(form.resource) }}
{{ form_row(form.amount) }}
{{ form_row(form.comment) }}
{{ form_row(form.startDate) }}

View File

@ -25,7 +25,7 @@
<div class="item-row">
<h2 class="badge-title">
<span class="title_label title_label_resource"></span>
<span class="title_action title_action">{{ element.type|budget_element_type_display('resource') }}</span>
<span class="title_action title_action">{{ element.resource.getName | localize_translatable_string }}</span>
</h2>
</div>
<div class="item-row separator">

View File

@ -20,7 +20,7 @@ use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use function in_array;
use UnexpectedValueException;
class BudgetElementVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
{
@ -68,12 +68,29 @@ class BudgetElementVoter extends AbstractChillVoter implements ProvideRoleHierar
protected function supports($attribute, $subject)
{
return (in_array($attribute, self::ROLES, true) && $subject instanceof AbstractElement)
|| (($subject instanceof Person || $subject instanceof Household) && in_array($attribute, [self::SEE, self::CREATE], true));
return $this->voter->supports($attribute, $subject);
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
return $this->voter->voteOnAttribute($attribute, $subject, $token);
if (
$subject instanceof Person
|| ($subject instanceof AbstractElement && null !== $person = $subject->getPerson())) {
return $this->voter->voteOnAttribute($attribute, $person ?? $subject, $token);
}
if (
$subject instanceof Household
|| ($subject instanceof AbstractElement && null !== $household = $subject->getHousehold())) {
foreach (($household ?? $subject)->getCurrentPersons() as $person) {
if ($this->voter->voteOnAttribute($attribute, $person, $token)) {
return true;
}
}
return false;
}
throw new UnexpectedValueException('This subject is not supported, or is an element not associated with person or household');
}
}

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\BudgetBundle\Templating;
use Chill\BudgetBundle\Entity\ChargeKind;
use Chill\BudgetBundle\Entity\ResourceKind;
use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Templating\EngineInterface;
final class BudgetElementTypeRender implements ChillEntityRenderInterface
{
private EngineInterface $engine;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(TranslatableStringHelperInterface $translatableStringHelper, EngineInterface $engine)
{
$this->translatableStringHelper = $translatableStringHelper;
$this->engine = $engine;
}
public function renderBox($entity, array $options): string
{
return $this->engine->render('@ChillBudget/Entity/budget_element_type.html.twig', [
'entity' => $entity,
'options' => $options,
]);
}
public function renderString($entity, array $options): string
{
$title = '';
if (null !== $entity->getName()) {
return $this->translatableStringHelper->localize($entity->getName());
}
return $title;
}
public function supports($entity, array $options): bool
{
return $entity instanceof ChargeKind || $entity instanceof ResourceKind;
}
}

View File

@ -3,3 +3,7 @@ services:
autowire: true
resource: '../../Controller'
tags: ['controller.service_arguments']
Chill\BudgetBundle\Controller\Admin\:
autowire: true
autoconfigure: true
resource: '../../Controller/Admin'

View File

@ -1,14 +1,9 @@
services:
Chill\BudgetBundle\Form\ResourceType:
arguments:
$configRepository: '@Chill\BudgetBundle\Config\ConfigRepository'
$translatableStringHelper: '@Chill\MainBundle\Templating\TranslatableStringHelper'
tags:
- { name: 'form.type' }
Chill\BudgetBundle\Form\ChargeType:
arguments:
$configRepository: '@Chill\BudgetBundle\Config\ConfigRepository'
$translatableStringHelper: '@Chill\MainBundle\Templating\TranslatableStringHelper'
Chill\BudgetBundle\Form\:
autowire: true
resource: '../../Form'
tags:
- { name: 'form.type' }
# Chill\BudgetBundle\Form\Admin\:
# autowire: true
# resource: '../../Form/Admin'

View File

@ -1,8 +1,5 @@
services:
Chill\BudgetBundle\Menu\PersonMenuBuilder:
autowire: true
autoconfigure: true
Chill\BudgetBundle\Menu\HouseholdMenuBuilder:
Chill\BudgetBundle\Menu\:
autowire: true
autoconfigure: true
resource: './../../Menu'

View File

@ -0,0 +1,5 @@
services:
Chill\BudgetBundle\Repository\:
autowire: true
autoconfigure: true
resource: './../../Repository'

View File

@ -1,7 +1,5 @@
services:
Chill\BudgetBundle\Templating\Twig:
arguments:
$configRepository: '@Chill\BudgetBundle\Config\ConfigRepository'
$translatableStringHelper: '@Chill\MainBundle\Templating\TranslatableStringHelper'
tags:
- { name: 'twig.extension' }
Chill\BudgetBundle\Templating\:
autowire: true
autoconfigure: true
resource: '../../Templating'

View File

@ -0,0 +1,39 @@
<?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;
final class Version20221116163445 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('DROP SEQUENCE chill_budget.charge_type_id_seq CASCADE');
$this->addSql('DROP SEQUENCE chill_budget.resource_type_id_seq CASCADE');
$this->addSql('DROP TABLE chill_budget.charge_type');
$this->addSql('DROP TABLE chill_budget.resource_type');
}
public function getDescription(): string
{
return 'Create resource type and charge type';
}
public function up(Schema $schema): void
{
$this->addSql('CREATE SEQUENCE chill_budget.charge_type_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE SEQUENCE chill_budget.resource_type_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE TABLE chill_budget.charge_type (id INT NOT NULL, isActive BOOLEAN DEFAULT TRUE NOT NULL, name JSONB DEFAULT \'{}\'::jsonb NOT NULL, ordering DOUBLE PRECISION DEFAULT \'0\' NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE TABLE chill_budget.resource_type (id INT NOT NULL, isActive BOOLEAN DEFAULT TRUE NOT NULL, name JSONB DEFAULT \'{}\'::jsonb NOT NULL, ordering DOUBLE PRECISION DEFAULT \'0\' NOT NULL, PRIMARY KEY(id))');
}
}

View File

@ -0,0 +1,41 @@
<?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;
final class Version20221130101659 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_budget.resource_type DROP kind');
$this->addSql('ALTER TABLE chill_budget.resource_type DROP tags');
$this->addSql('ALTER TABLE chill_budget.charge_type DROP kind');
$this->addSql('ALTER TABLE chill_budget.charge_type DROP tags');
}
public function getDescription(): string
{
return 'Add kind and tags property to charge and resource types';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_budget.charge_type ADD kind VARCHAR(255) DEFAULT \'\' NOT NULL');
$this->addSql('ALTER TABLE chill_budget.charge_type ADD tags JSONB DEFAULT \'{}\'::jsonb NOT NULL');
$this->addSql('COMMENT ON COLUMN chill_budget.charge_type.tags IS \'(DC2Type:jsonb)\'');
$this->addSql('ALTER TABLE chill_budget.resource_type ADD kind VARCHAR(255) DEFAULT \'\' NOT NULL');
$this->addSql('ALTER TABLE chill_budget.resource_type ADD tags JSONB DEFAULT \'{}\'::jsonb NOT NULL');
$this->addSql('COMMENT ON COLUMN chill_budget.resource_type.tags IS \'(DC2Type:jsonb)\'');
}
}

View File

@ -0,0 +1,41 @@
<?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;
final class Version20221202165608 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_budget.charge DROP CONSTRAINT FK_5C99D2C355284914');
$this->addSql('ALTER TABLE chill_budget.charge DROP charge_id');
$this->addSql('ALTER TABLE chill_budget.resource DROP CONSTRAINT FK_5E0A5E9789329D25');
$this->addSql('ALTER TABLE chill_budget.resource DROP resource_id');
}
public function getDescription(): string
{
return 'Integrate budget admin entity';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_budget.charge ADD charge_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE chill_budget.charge ADD CONSTRAINT FK_5C99D2C355284914 FOREIGN KEY (charge_id) REFERENCES chill_budget.charge_type (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('CREATE INDEX IDX_5C99D2C355284914 ON chill_budget.charge (charge_id)');
$this->addSql('ALTER TABLE chill_budget.resource ADD resource_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE chill_budget.resource ADD CONSTRAINT FK_5E0A5E9789329D25 FOREIGN KEY (resource_id) REFERENCES chill_budget.resource_type (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('CREATE INDEX IDX_5E0A5E9789329D25 ON chill_budget.resource (resource_id)');
}
}

View File

@ -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\Migrations\Budget;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\AbstractMigration;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
final class Version20221207105407 extends AbstractMigration implements ContainerAwareInterface
{
public ContainerInterface $container;
public function down(Schema $schema): void
{
$this->addSql('DELETE FROM chill_budget.resource_type;');
$this->addSql('DELETE FROM chill_budget.charge_type;');
}
public function getDescription(): string
{
return 'Use new budget admin entities';
}
public function setContainer(?ContainerInterface $container = null)
{
$this->container = $container;
}
public function up(Schema $schema): void
{
$resources = $this->container->getParameter('chill_budget.resources');
$charges = $this->container->getParameter('chill_budget.charges');
foreach ($resources as $value) {
$lang = $value['labels'][0]['lang'];
$label = $value['labels'][0]['label'];
$kind = $value['key'];
$this->addSql(
'INSERT INTO chill_budget.resource_type (id, isActive, name, ordering, kind) VALUES (
nextval(\'chill_budget.resource_type_id_seq\'), true, jsonb_build_object(:lang::text, :label::text), 0, :kind::text)',
['lang' => $lang, 'label' => $label, 'kind' => $kind],
['lang' => Types::STRING, 'label' => Types::STRING, 'kind' => Types::STRING]
);
$this->addSql(
'UPDATE chill_budget.resource SET resource_id = resource_type.id
FROM chill_budget.resource_type WHERE resource.type = :kind AND resource_type.kind = resource.type;',
['kind' => $kind],
['kind' => Types::STRING]
);
}
foreach ($charges as $value) {
$lang = $value['labels'][0]['lang'];
$label = $value['labels'][0]['label'];
$kind = $value['key'];
$this->addSql(
'INSERT INTO chill_budget.charge_type VALUES (nextval(\'chill_budget.charge_type_id_seq\'), true,
jsonb_build_object(:lang::text, :label::text), 0, :kind::text);',
['lang' => $lang, 'label' => $label, 'kind' => $kind],
['lang' => Types::STRING, 'label' => Types::STRING, 'kind' => Types::STRING]
);
$this->addSql(
'UPDATE chill_budget.charge SET charge_id = charge_type.id
FROM chill_budget.charge_type WHERE charge.type = :kind AND charge_type.kind = charge.type;',
['kind' => $kind],
['kind' => Types::STRING]
);
}
}
}

View File

@ -58,6 +58,8 @@ Charge updated: charge mise à jour
Choose a resource type: Choisissez un type de ressource
Choose a charge type: Choisissez un type de charge
Resource type: Type de ressource
Charge type: Type de charge
Amount: Montant
Comment: Commentaire
@ -74,3 +76,28 @@ 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%
## admin
crud:
resource_kind:
title_new: Nouveau type de ressource
title_edit: Modifier le type de ressource
charge_kind:
title_new: Nouveau type de charge
title_edit: Modifier le type de charge
admin:
menu:
Resource types: Types de ressource
Charge types: Types de charge
title:
Charge Type List: Liste des types de charge
Resource Type List: Liste des types de ressource
Budget configuration: Configuration des éléments de budget
new:
Create a new charge type: Créér un nouveau type de charge
Create a new resource type: Créér un nouveau type de ressource
form:
Choose the type of resource: Choisissez une type de ressource
Choose the type of charge: Choisissez une type de charge

View File

@ -45,6 +45,7 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class CalendarController extends AbstractController
{
@ -68,6 +69,8 @@ class CalendarController extends AbstractController
private TranslatableStringHelperInterface $translatableStringHelper;
private TranslatorInterface $translator;
private UserRepositoryInterface $userRepository;
public function __construct(
@ -81,7 +84,8 @@ class CalendarController extends AbstractController
TranslatableStringHelperInterface $translatableStringHelper,
PersonRepository $personRepository,
AccompanyingPeriodRepository $accompanyingPeriodRepository,
UserRepositoryInterface $userRepository
UserRepositoryInterface $userRepository,
TranslatorInterface $translator
) {
$this->calendarACLAwareRepository = $calendarACLAwareRepository;
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
@ -94,6 +98,7 @@ class CalendarController extends AbstractController
$this->personRepository = $personRepository;
$this->accompanyingPeriodRepository = $accompanyingPeriodRepository;
$this->userRepository = $userRepository;
$this->translator = $translator;
}
/**
@ -131,7 +136,7 @@ class CalendarController extends AbstractController
$em->remove($entity);
$em->flush();
$this->addFlash('success', $this->get('translator')
$this->addFlash('success', $this->translator
->trans('The calendar item has been successfully removed.'));
return new RedirectResponse($redirectRoute);
@ -190,7 +195,7 @@ class CalendarController extends AbstractController
if ($form->isSubmitted() && $form->isValid()) {
$em->flush();
$this->addFlash('success', $this->get('translator')->trans('Success : calendar item updated!'));
$this->addFlash('success', $this->translator->trans('Success : calendar item updated!'));
if ($form->get('save_and_upload_doc')->isClicked()) {
return $this->redirectToRoute('chill_calendar_calendardoc_new', ['id' => $entity->getId()]);
@ -210,7 +215,7 @@ class CalendarController extends AbstractController
}
if ($form->isSubmitted() && !$form->isValid()) {
$this->addFlash('error', $this->get('translator')->trans('This form contains errors'));
$this->addFlash('error', $this->translator->trans('This form contains errors'));
}
$entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
@ -369,7 +374,7 @@ class CalendarController extends AbstractController
$em->persist($entity);
$em->flush();
$this->addFlash('success', $this->get('translator')->trans('Success : calendar item created!'));
$this->addFlash('success', $this->translator->trans('Success : calendar item created!'));
if ($form->get('save_and_upload_doc')->isClicked()) {
return $this->redirectToRoute('chill_calendar_calendardoc_new', ['id' => $entity->getId()]);
@ -393,7 +398,7 @@ class CalendarController extends AbstractController
}
if ($form->isSubmitted() && !$form->isValid()) {
$this->addFlash('error', $this->get('translator')->trans('This form contains errors'));
$this->addFlash('error', $this->translator->trans('This form contains errors'));
}
if (null === $view) {

View File

@ -66,6 +66,10 @@ final class AgentAggregator implements AggregatorInterface
return 'Agent';
}
if (null === $value || '' === $value) {
return '';
}
$r = $this->userRepository->find($value);
return $this->userRender->renderString($r, []);

View File

@ -67,6 +67,10 @@ class CancelReasonAggregator implements AggregatorInterface
return 'Cancel reason';
}
if (null === $value || '' === $value) {
return '';
}
$j = $this->cancelReasonRepository->find($value);
return $this->translatableStringHelper->localize(

View File

@ -66,6 +66,10 @@ final class JobAggregator implements AggregatorInterface
return 'Job';
}
if (null === $value || '' === $value) {
return '';
}
$j = $this->jobRepository->find($value);
return $this->translatableStringHelper->localize(

View File

@ -60,6 +60,10 @@ final class LocationAggregator implements AggregatorInterface
return 'Location';
}
if (null === $value || '' === $value) {
return '';
}
$l = $this->locationRepository->find($value);
return $l->getName();

View File

@ -66,7 +66,13 @@ final class LocationTypeAggregator implements AggregatorInterface
return 'Location type';
}
$j = $this->locationTypeRepository->find($value);
if (null === $value || '' === $value) {
return '';
}
if (null === $j = $this->locationTypeRepository->find($value)) {
return '';
}
return $this->translatableStringHelper->localize(
$j->getTitle()

View File

@ -66,6 +66,10 @@ final class ScopeAggregator implements AggregatorInterface
return 'Scope';
}
if (null === $value || '' === $value) {
return '';
}
$s = $this->scopeRepository->find($value);
return $this->translatableStringHelper->localize(

View File

@ -1,6 +1,11 @@
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
{% import "@ChillDocStore/Macro/macro_mimeicon.html.twig" as mm %}
{% if document is null %}
<div class="alert alert-warning">
{{ 'workflow.Document deleted'|trans }}
</div>
{% else %}
<div class="flex-table accompanying-course-work">
<div class="item-bloc document-item bg-chill-llight-gray">
<div class="row justify-content-center my-4">
@ -17,7 +22,6 @@
{{ document.description }}
</blockquote>
{% endif %}
</div>
</div>
</div>
@ -44,14 +48,17 @@
<li>
{{ m.download_button(document.object, document.title) }}
</li>
<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 %}
@ -59,6 +66,6 @@
<a href="{{ chill_path_add_return_path('accompanying_course_document_show', {'course': document.course.id, 'id': document.id}) }}" class="btn btn-show"></a>
</li>
{% endif %}
</li>
</ul>
{% endif %}
{% endif %}

View File

@ -60,7 +60,7 @@ final class StoredObjectManager implements StoredObjectManagerInterface
$this
->tempUrlGenerator
->generate(
Request::METHOD_PUT,
Request::METHOD_HEAD,
$document->getFilename()
)
->url

View File

@ -65,6 +65,10 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
{
$doc = $this->getRelatedEntity($entityWorkflow);
if (null === $doc) {
return $this->translator->trans('workflow.Document deleted');
}
return $this->translator->trans('workflow.Document (n°%doc%)', ['%doc%' => $entityWorkflow->getRelatedEntityId()])
. ' - ' . $doc->getTitle();
}

View File

@ -63,3 +63,6 @@ Create new DocumentCategory: Créer une nouvelle catégorie de document
# WOPI EDIT
online_edit_document: Éditer en ligne
workflow:
Document deleted: Document supprimé

View File

@ -151,7 +151,7 @@ class PickEventType extends AbstractType
} else {
$centers = $this->authorizationHelper->getReachableCenters(
$this->user,
(string) $options['role']
(string) $options['role']->getRole()
);
}

View File

@ -5,6 +5,7 @@
{% block title 'Delete event'|trans %}
{% block event_content %}
<div class="col-10">
{{ include('@ChillMain/Util/confirmation_template.html.twig',
{
@ -15,6 +16,6 @@
'form' : delete_form
}
) }}
</div>
{% endblock %}

View File

@ -3,6 +3,7 @@
{% block title 'Event edit'|trans %}
{% block event_content -%}
<div class="col-10">
<h1>{{ 'Event edit'|trans }}</h1>
{{ form_start(edit_form) }}
@ -30,4 +31,6 @@
</ul>
{{ form_end(edit_form) }}
</div>
{% endblock %}

View File

@ -6,7 +6,7 @@
<p>{{ 'Results %start%-%end% of %total%'|trans({ '%start%' : start, '%end%': start + events|length, '%total%' : total } ) }}</p>
<table class="events">
<table class="table events">
<thead>
<tr>
<th class="chill-red">{{ 'Name'|trans }}</th>
@ -52,7 +52,7 @@
</li>
{% if preview == true and events|length < total %}
<li>
<a href="{{ path('chill_main_search', { "name": search_name, "q" : pattern }) }}" class="btn btn-next">
<a href="{{ path('chill_main_search', { "name": search_name, "q" : pattern }) }}" class="btn btn-misc">
{{ 'See all results'|trans }}
</a>
</li>
@ -63,3 +63,4 @@
{% if preview == false %}
{{ chill_pagination(paginator) }}
{% endif %}

View File

@ -44,41 +44,42 @@
<td>{{ participation.role.name|localize_translatable_string }}</td>
<td>{{ participation.status.name|localize_translatable_string }}</td>
<td>
<ul class="list-inline">
<div class="btn-group" role="group" aria-label="Button group actions">
{% set currentPath = path(app.request.attributes.get('_route'), app.request.attributes.get('_route_params')) %}
{% set returnLabel = 'Back to %person% events'|trans({ '%person%' : currentPerson } ) %}
{% if is_granted('CHILL_EVENT_SEE_DETAILS', participation.event) %}
<li class="list-inline-item">
<a href="{{ path('chill_event__event_show', { 'event_id' : participation.event.id, 'return_path' : currentPath, 'return_label' : returnLabel } ) }}"
class="btn btn-primary btn-sm" title="{{ 'See details of the event'|trans }}"><i class="fa fa-fw fa-eye"></i></a>
</li>
class="btn btn-primary btn-sm" title="{{ 'See details of the event'|trans }}">
<i class="fa fa-fw fa-eye"></i>
</a>
{% endif %}
{% if is_granted('CHILL_EVENT_UPDATE', participation.event)
and is_granted('CHILL_EVENT_PARTICIPATION_UPDATE', participation) %}
<li class="list-inline-item">
<div class="btn dropdown-toggle">
<a href="" class="btn btn-warning btn-sm"><i class="fa fa-fw fa-pencil"></i></a>
<div class="dropdown-menu">
<div class="btn-group" role="group">
<button class="btn btn-sm btn-warning dropdown-toggle" type="button" id="dropdownEdit" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa fa-pencil"></i>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownEdit">
<li>
<a href="{{ path('chill_event__event_edit', { 'event_id' : participation.event.id, 'return_path' : currentPath, 'return_label' : returnLabel }) }}"
class="btn btn-warning btn-sm">
class="dropdown-item">
{{ 'Edit the event'|trans }}
</a>
</li>
<li>
<a href="{{ path('chill_event_participation_edit', { 'participation_id' : participation.id, 'return_path' : currentPath, 'return_label' : returnLabel }) }}"
class="btn btn-warning btn-sm">
class="dropdown-item">
{{ 'Edit the participation'|trans }}
</a>
</div>
</div>
</li>
</ul>
</div>
{% else %}
<li class="list-inline-item">
{% if is_granted('CHILL_EVENT_UPDATE', participation.event) %}
<a href="{{ path('chill_event__event_edit', { 'event_id' : participation.event.id, 'return_path' : currentPath, 'return_label' : returnLabel }) }}"
class="btn btn-warning btn-sm">
@ -91,12 +92,11 @@
{{ 'Edit the participation'|trans }}
</a>
{% endif %}
</li>
{% endif %}
</ul>
</div>
</td>
</tr>
{% endfor %}
@ -108,31 +108,17 @@
{{ chill_pagination(paginator) }}
{% endif %}
<div class="input-group mb-3">
<div class="input-group mt-5">
{{ form_start(form_add_event_participation_by_person) }}
{#
<input type="text" class="form-control" placeholder="Recipient's username" aria-label="Recipient's username" aria-describedby="button-addon2">
#}
{{ form_widget(form_add_event_participation_by_person.event_id, { 'attr' : { 'class' : 'form-control' } } ) }}
<div class="input-group-append input-group-btn">
{#
<button class="btn btn-outline-secondary" type="button" id="button-addon2">Button</button>
#}
{{ form_widget(form_add_event_participation_by_person.submit, { 'attr' : { 'class' : 'btn btn-success' } } ) }}
{{ form_widget(form_add_event_participation_by_person.event_id, { 'attr' : {
'class' : 'custom-select',
'style': 'min-width: 15em; max-width: 18em; display: inline-block;'
}}) }}
<div class="input-group-append">
{{ form_widget(form_add_event_participation_by_person.submit, { 'attr' : { 'class' : 'btn btn-sm btn-save' } } ) }}
</div>
{{ form_rest(form_add_event_participation_by_person) }}
{{ form_end(form_add_event_participation_by_person) }}
</div>
{#
{{ form(form_add_event_participation_by_person) }}
#}
<div class="input-group mb-3">
<input class="form-control" placeholder="Recipient's username">
<div class="input-group-append">
<button class="btn btn-success">Button</button>
</div>
</div>
{% endblock %}

View File

@ -3,6 +3,7 @@
{% block title 'Event creation'|trans %}
{% block event_content -%}
<div class="col-10">
<h1>{{ 'Event creation'|trans }}</h1>
{{ form_start(form) }}
@ -26,4 +27,5 @@
</ul>
{{ form_end(form) }}
</div>
{% endblock %}

View File

@ -3,6 +3,7 @@
{% block title 'Event creation'|trans %}
{% block event_content -%}
<div class="col-10">
<h1>{{ 'Event creation'|trans }}</h1>
{{ form_start(form) }}
@ -22,5 +23,5 @@
</ul>
{{ form_end(form) }}
</div>
{% endblock %}

View File

@ -5,9 +5,10 @@
{% import 'ChillPersonBundle:Person:macro.html.twig' as person_macro %}
{% block event_content -%}
<div class="col-10">
<h1>{{ 'Details of an event'|trans }}</h1>
<table class="record_properties">
<table class="table record_properties">
<tbody>
<tr>
<th>{{ 'Name'|trans }}</th>
@ -69,7 +70,7 @@
<p>{% transchoice count %}%count% participations to this event{% endtranschoice %}</p>
{% if count > 0 %}
<table>
<table class="table">
<thead>
<tr>
<th>{{ 'Person'|trans }}</th>
@ -117,7 +118,7 @@
{% endif %}
</ul>
<div style="margin-bottom: 10em;">
<div class="row" style="margin-bottom: 10em;">
<div class="col-8">
{{ form_start(form_add_participation_by_person) }}
<div class="input-group">
@ -150,5 +151,5 @@
<div class="post_show">
{{ chill_delegated_block('block_footer_show', { 'event': event }) }}
</div>
</div>
{% endblock %}

View File

@ -5,7 +5,7 @@
{% block title 'Remove participation'|trans %}
{% block event_content %}
<div class="col-10">
{{ include('@ChillMain/Util/confirmation_template.html.twig',
{
'title' : 'Remove participation'|trans,
@ -15,6 +15,6 @@
'form' : delete_form
}
) }}
</div>
{% endblock %}

View File

@ -3,9 +3,10 @@
{% import 'ChillPersonBundle:Person:macro.html.twig' as person_macro %}
{% block event_content -%}
<div class="col-10">
<h1>{{ 'Participation Edit'|trans }}</h1>
<table>
<table class="table">
<tbody>
<tr>
<th>{{ 'Associated event'|trans }} </th>
@ -18,11 +19,11 @@
</tbody>
</table>
<h2>{{ 'Participations'|trans }}</h2>
<h2 class="mt-5">{{ 'Participations'|trans }}</h2>
{{ form_start(form) }}
<table>
<table class="table">
<thead>
<tr>
<th>{{ 'Person'|trans }}</th>
@ -59,4 +60,5 @@
</ul>
{{ form_end(form) }}
</div>
{% endblock %}

Some files were not shown because too many files have changed in this diff Show More