Feature: allow to administrate budget resources and charges from the admin

This commit is contained in:
Julien Fastré 2023-01-23 20:40:01 +00:00
parent 99482edf0d
commit c4eb45edcc
57 changed files with 1546 additions and 49 deletions

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; 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 Chill\BudgetBundle\Security\Authorization\BudgetElementVoter;
use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder; 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 = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../config'));
$loader->load('services/config.yaml'); $loader->load('services/config.yaml');
$loader->load('services/form.yaml'); $loader->load('services/form.yaml');
$loader->load('services/repository.yaml');
$loader->load('services/security.yaml'); $loader->load('services/security.yaml');
$loader->load('services/controller.yaml'); $loader->load('services/controller.yaml');
$loader->load('services/templating.yaml'); $loader->load('services/templating.yaml');
@ -48,6 +53,7 @@ class ChillBudgetExtension extends Extension implements PrependExtensionInterfac
{ {
$this->prependAuthorization($container); $this->prependAuthorization($container);
$this->prependRoutes($container); $this->prependRoutes($container);
$this->prependCruds($container);
} }
/** (non-PHPdoc). /** (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) protected function storeConfig($position, array $config, ContainerBuilder $container)
{ {
$container $container

View File

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

View File

@ -39,6 +39,12 @@ class Charge extends AbstractElement implements HasCentersInterface
self::HELP_NOT_RELEVANT, self::HELP_NOT_RELEVANT,
]; ];
/**
* @ORM\ManyToOne(targetEntity=ChargeKind::class, inversedBy="AbstractElement")
* @ORM\JoinColumn
*/
private ?ChargeKind $charge = null;
/** /**
* @var string * @var string
* @ORM\Column(name="help", type="string", nullable=true) * @ORM\Column(name="help", type="string", nullable=true)
@ -66,6 +72,11 @@ class Charge extends AbstractElement implements HasCentersInterface
return $this->getHousehold()->getCurrentPersons()->map(static fn (Person $p) => $p->getCenter())->toArray(); return $this->getHousehold()->getCurrentPersons()->map(static fn (Person $p) => $p->getCenter())->toArray();
} }
public function getCharge(): ?ChargeKind
{
return $this->charge;
}
public function getHelp() public function getHelp()
{ {
return $this->help; return $this->help;
@ -91,6 +102,13 @@ class Charge extends AbstractElement implements HasCentersInterface
return false; return false;
} }
public function setCharge(?ChargeKind $charge): self
{
$this->charge = $charge;
return $this;
}
public function setHelp($help) public function setHelp($help)
{ {
$this->help = $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

@ -31,6 +31,12 @@ class Resource extends AbstractElement implements HasCentersInterface
*/ */
private ?int $id = null; private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity=ResourceKind::class, inversedBy="AbstractElement")
* @ORM\JoinColumn
*/
private ?ResourceKind $resource = null;
public function __construct() public function __construct()
{ {
$this->setStartDate(new DateTimeImmutable('today')); $this->setStartDate(new DateTimeImmutable('today'));
@ -55,6 +61,11 @@ class Resource extends AbstractElement implements HasCentersInterface
return $this->id; return $this->id;
} }
public function getResource(): ?ResourceKind
{
return $this->resource;
}
public function isCharge(): bool public function isCharge(): bool
{ {
return false; return false;
@ -64,4 +75,11 @@ class Resource extends AbstractElement implements HasCentersInterface
{ {
return true; 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\Config\ConfigRepository;
use Chill\BudgetBundle\Entity\Charge; use Chill\BudgetBundle\Entity\Charge;
use Chill\BudgetBundle\Entity\ChargeKind;
use Chill\BudgetBundle\Repository\ChargeKindRepository;
use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType; use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_flip; use function array_flip;
use function asort; use function asort;
@ -31,21 +34,35 @@ class ChargeType extends AbstractType
protected TranslatableStringHelperInterface $translatableStringHelper; protected TranslatableStringHelperInterface $translatableStringHelper;
private ChargeKindRepository $repository;
private TranslatorInterface $translator;
public function __construct( public function __construct(
ConfigRepository $configRepository, ConfigRepository $configRepository,
TranslatableStringHelperInterface $translatableStringHelper TranslatableStringHelperInterface $translatableStringHelper,
ChargeKindRepository $repository,
TranslatorInterface $translator
) { ) {
$this->configRepository = $configRepository; $this->configRepository = $configRepository;
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
$this->repository = $repository;
$this->translator = $translator;
} }
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder $builder
->add('type', ChoiceType::class, [ ->add('charge', EntityType::class, [
'choices' => $this->getTypes(), 'class' => ChargeKind::class,
'placeholder' => 'Choose a charge type', 'choices' => $this->repository->findAllActive(),
'attr' => ['class' => ' select2 '], '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) ->add('amount', MoneyType::class)
->add('comment', TextareaType::class, [ ->add('comment', TextareaType::class, [

View File

@ -13,15 +13,17 @@ namespace Chill\BudgetBundle\Form;
use Chill\BudgetBundle\Config\ConfigRepository; use Chill\BudgetBundle\Config\ConfigRepository;
use Chill\BudgetBundle\Entity\Resource; use Chill\BudgetBundle\Entity\Resource;
use Chill\BudgetBundle\Entity\ResourceKind;
use Chill\BudgetBundle\Repository\ResourceKindRepository;
use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType; 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\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_flip; use function array_flip;
class ResourceType extends AbstractType class ResourceType extends AbstractType
@ -30,22 +32,35 @@ class ResourceType extends AbstractType
protected TranslatableStringHelperInterface $translatableStringHelper; protected TranslatableStringHelperInterface $translatableStringHelper;
private ResourceKindRepository $repository;
private TranslatorInterface $translator;
public function __construct( public function __construct(
ConfigRepository $configRepository, ConfigRepository $configRepository,
TranslatableStringHelperInterface $translatableStringHelper TranslatableStringHelperInterface $translatableStringHelper,
ResourceKindRepository $repository,
TranslatorInterface $translator
) { ) {
$this->configRepository = $configRepository; $this->configRepository = $configRepository;
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
$this->repository = $repository;
$this->translator = $translator;
} }
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder $builder
->add('type', ChoiceType::class, [ ->add('resource', EntityType::class, [
'choices' => $this->getTypes(), 'class' => ResourceKind::class,
'placeholder' => 'Choose a resource type', 'choices' => $this->repository->findAllActive(),
'label' => 'Resource element type', 'label' => 'Resource type',
'attr' => ['class' => ' select2 '], '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) ->add('amount', MoneyType::class)
->add('comment', TextareaType::class, [ ->add('comment', TextareaType::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"> <td class="column-wide el-type">
<span class="badge-title"> <span class="badge-title">
<span class="title_label title_label_{{ family }}"></span> <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> </span>
</td> </td>
<td class="column-small">{{ f.amount|format_currency('EUR') }}</td> <td class="column-small">{{ f.amount|format_currency('EUR') }}</td>

View File

@ -3,13 +3,13 @@
{% set indexPage = 'chill_budget_elements_index' %} {% set indexPage = 'chill_budget_elements_index' %}
{% set activeRouteKey = '' %} {% set activeRouteKey = '' %}
{% set person = element.person %} {% 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 %} {% else %}
{% set template = '@ChillPerson/Household/layout.html.twig' %} {% set template = '@ChillPerson/Household/layout.html.twig' %}
{% set indexPage = 'chill_budget_elements_household_index' %} {% set indexPage = 'chill_budget_elements_household_index' %}
{% set activeRouteKey = '' %} {% set activeRouteKey = '' %}
{% set household = element.household %} {% 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 %} {% endif %}
{% extends template %} {% extends template %}

View File

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

View File

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

View File

@ -25,7 +25,7 @@
<div class="item-row"> <div class="item-row">
<h2 class="badge-title"> <h2 class="badge-title">
<span class="title_label title_label_charge"></span> <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> </h2>
</div> </div>
<div class="item-row separator"> <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 indexPage = 'chill_budget_elements_index' %}
{% set activeRouteKey = '' %} {% set activeRouteKey = '' %}
{% set person = element.person %} {% 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 %} {% else %}
{% set template = '@ChillPerson/Household/layout.html.twig' %} {% set template = '@ChillPerson/Household/layout.html.twig' %}
{% set indexPage = 'chill_budget_elements_household_index' %} {% set indexPage = 'chill_budget_elements_household_index' %}
{% set activeRouteKey = '' %} {% set activeRouteKey = '' %}
{% set household = element.household %} {% 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 %} {% endif %}
{% extends template %} {% extends template %}

View File

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

View File

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

View File

@ -25,7 +25,7 @@
<div class="item-row"> <div class="item-row">
<h2 class="badge-title"> <h2 class="badge-title">
<span class="title_label title_label_resource"></span> <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> </h2>
</div> </div>
<div class="item-row separator"> <div class="item-row separator">

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 autowire: true
resource: '../../Controller' resource: '../../Controller'
tags: ['controller.service_arguments'] tags: ['controller.service_arguments']
Chill\BudgetBundle\Controller\Admin\:
autowire: true
autoconfigure: true
resource: '../../Controller/Admin'

View File

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

View File

@ -1,8 +1,5 @@
services: services:
Chill\BudgetBundle\Menu\PersonMenuBuilder: Chill\BudgetBundle\Menu\:
autowire: true
autoconfigure: true
Chill\BudgetBundle\Menu\HouseholdMenuBuilder:
autowire: true autowire: true
autoconfigure: 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: services:
Chill\BudgetBundle\Templating\Twig: Chill\BudgetBundle\Templating\:
arguments: autowire: true
$configRepository: '@Chill\BudgetBundle\Config\ConfigRepository' autoconfigure: true
$translatableStringHelper: '@Chill\MainBundle\Templating\TranslatableStringHelper' resource: '../../Templating'
tags:
- { name: 'twig.extension' }

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 resource type: Choisissez un type de ressource
Choose a charge type: Choisissez un type de charge Choose a charge type: Choisissez un type de charge
Resource type: Type de ressource
Charge type: Type de charge
Amount: Montant Amount: Montant
Comment: Commentaire 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% until %endDate%: Valide depuis le %startDate% jusqu'au %endDate%
Valid since %startDate%: Valide depuis le %startDate% 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

@ -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\MainBundle\Controller;
use Chill\MainBundle\CRUD\Controller\CRUDController;
use Chill\MainBundle\Pagination\PaginatorInterface;
use Symfony\Component\HttpFoundation\Request;
class RegroupmentController extends CRUDController
{
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
{
$query->addOrderBy('e.id', 'ASC');
return parent::orderQuery($action, $query, $request, $paginator);
}
}

View File

@ -18,6 +18,7 @@ use Chill\MainBundle\Controller\CountryController;
use Chill\MainBundle\Controller\LanguageController; use Chill\MainBundle\Controller\LanguageController;
use Chill\MainBundle\Controller\LocationController; use Chill\MainBundle\Controller\LocationController;
use Chill\MainBundle\Controller\LocationTypeController; use Chill\MainBundle\Controller\LocationTypeController;
use Chill\MainBundle\Controller\RegroupmentController;
use Chill\MainBundle\Controller\UserController; use Chill\MainBundle\Controller\UserController;
use Chill\MainBundle\Controller\UserJobApiController; use Chill\MainBundle\Controller\UserJobApiController;
use Chill\MainBundle\Controller\UserJobController; use Chill\MainBundle\Controller\UserJobController;
@ -48,6 +49,7 @@ use Chill\MainBundle\Entity\Country;
use Chill\MainBundle\Entity\Language; use Chill\MainBundle\Entity\Language;
use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\LocationType; use Chill\MainBundle\Entity\LocationType;
use Chill\MainBundle\Entity\Regroupment;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Form\CivilityType; use Chill\MainBundle\Form\CivilityType;
@ -55,6 +57,7 @@ use Chill\MainBundle\Form\CountryType;
use Chill\MainBundle\Form\LanguageType; use Chill\MainBundle\Form\LanguageType;
use Chill\MainBundle\Form\LocationFormType; use Chill\MainBundle\Form\LocationFormType;
use Chill\MainBundle\Form\LocationTypeType; use Chill\MainBundle\Form\LocationTypeType;
use Chill\MainBundle\Form\RegroupmentType;
use Chill\MainBundle\Form\UserJobType; use Chill\MainBundle\Form\UserJobType;
use Chill\MainBundle\Form\UserType; use Chill\MainBundle\Form\UserType;
use Exception; use Exception;
@ -499,6 +502,27 @@ class ChillMainExtension extends Extension implements
], ],
], ],
], ],
[
'class' => Regroupment::class,
'name' => 'regroupment',
'base_path' => '/admin/regroupment',
'form_class' => RegroupmentType::class,
'controller' => RegroupmentController::class,
'actions' => [
'index' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillMain/Admin/Regroupment/index.html.twig',
],
'new' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillMain/Admin/Regroupment/new.html.twig',
],
'edit' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillMain/Admin/Regroupment/edit.html.twig',
],
],
],
], ],
'apis' => [ 'apis' => [
[ [

View File

@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="regroupment")
*/
class Regroupment
{
/**
* @var Center
* @ORM\ManyToMany(
* targetEntity="Chill\MainBundle\Entity\Center"
* )
* @ORM\Id
*/
private Collection $centers;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* @ORM\Column(type="boolean")
*/
private bool $isActive = true;
/**
* @ORM\Column(type="string", length=15, options={"default": ""}, nullable=false)
*/
private string $name = '';
public function __construct()
{
$this->centers = new ArrayCollection();
}
public function getCenters(): ?Collection
{
return $this->centers;
}
public function getId(): ?int
{
return $this->id;
}
public function getIsActive(): bool
{
return $this->isActive;
}
public function getName(): string
{
return $this->name;
}
public function setCenters(?Collection $centers): self
{
$this->centers = $centers;
return $this;
}
public function setIsActive(bool $isActive): self
{
$this->isActive = $isActive;
return $this;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
}

View File

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Form;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Regroupment;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class RegroupmentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, [
'label' => 'Nom',
])
->add('centers', EntityType::class, [
'class' => Center::class,
'multiple' => true,
'attr' => ['class' => 'select2'],
])
->add('isActive', CheckboxType::class, [
'label' => 'Actif ?',
'required' => false,
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefault('class', Regroupment::class);
}
}

View File

@ -0,0 +1,11 @@
{% 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_save_and_show %}{% endblock %}
{% endembed %}
{% endblock admin_content %}

View File

@ -0,0 +1,39 @@
{% extends '@ChillMain/CRUD/Admin/index.html.twig' %}
{% block admin_content %}
{% embed '@ChillMain/CRUD/_index.html.twig' %}
{% block table_entities_thead_tr %}
<th>{{ 'Label'|trans }}</th>
<th>{{ 'Active'|trans }}</th>
<th>&nbsp;</th>
{% endblock %}
{% block table_entities_tbody %}
{% for entity in entities %}
<tr>
<td>{{ entity.name }}</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="{{ chill_path_add_return_path('chill_crud_regroupment_edit', { 'id': entity.id }) }}" class="btn btn-edit"></a>
</li>
</ul>
</td>
</tr>
{% endfor %}
{% endblock %}
{% block actions_before %}
<li class='cancel'>
<a href="{{ path('chill_main_admin_central') }}" class="btn btn-cancel">{{'Back to the admin'|trans}}</a>
</li>
{% endblock %}
{% endembed %}
{% 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 admin_content %}

View File

@ -53,6 +53,10 @@ class AdminUserMenuBuilder implements LocalMenuBuilderInterface
'route' => 'admin_center', 'route' => 'admin_center',
])->setExtras(['order' => 1010]); ])->setExtras(['order' => 1010]);
$menu->addChild('Regroupements des centres', [
'route' => 'chill_crud_regroupment_index',
])->setExtras(['order' => 1015]);
$menu->addChild('List circles', [ $menu->addChild('List circles', [
'route' => 'admin_scope', 'route' => 'admin_scope',
])->setExtras(['order' => 1020]); ])->setExtras(['order' => 1020]);

View File

@ -33,3 +33,7 @@ services:
arguments: arguments:
$security: '@Symfony\Component\Security\Core\Security' $security: '@Symfony\Component\Security\Core\Security'
tags: ['controller.service_arguments'] tags: ['controller.service_arguments']
Chill\MainBundle\Controller\RegroupmentController:
autowire: true
autoconfigure: true

View File

@ -138,6 +138,10 @@ services:
autowire: true autowire: true
autoconfigure: true autoconfigure: true
Chill\MainBundle\Form\RegroupmentType:
autowire: true
autoconfigure: true
Chill\MainBundle\Form\DataTransformer\IdToLocationDataTransformer: ~ Chill\MainBundle\Form\DataTransformer\IdToLocationDataTransformer: ~
Chill\MainBundle\Form\DataTransformer\IdToUserDataTransformer: ~ Chill\MainBundle\Form\DataTransformer\IdToUserDataTransformer: ~
Chill\MainBundle\Form\DataTransformer\IdToUsersDataTransformer: ~ Chill\MainBundle\Form\DataTransformer\IdToUsersDataTransformer: ~

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\Migrations\Main;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20230111104315 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('DROP SEQUENCE regroupment_id_seq CASCADE');
$this->addSql('ALTER TABLE regroupment_center DROP CONSTRAINT FK_2BCCE2F9EC6D1029');
$this->addSql('ALTER TABLE regroupment_center DROP CONSTRAINT FK_2BCCE2F95932F377');
$this->addSql('DROP TABLE regroupment');
$this->addSql('DROP TABLE regroupment_center');
}
public function getDescription(): string
{
return 'Add regroupment admin entity';
}
public function up(Schema $schema): void
{
$this->addSql('CREATE SEQUENCE regroupment_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE TABLE regroupment (id INT NOT NULL, name VARCHAR(15) DEFAULT \'\' NOT NULL, isActive BOOLEAN NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE TABLE regroupment_center (regroupment_id INT NOT NULL, center_id INT NOT NULL, PRIMARY KEY(regroupment_id, center_id))');
$this->addSql('CREATE INDEX IDX_2BCCE2F9EC6D1029 ON regroupment_center (regroupment_id)');
$this->addSql('CREATE INDEX IDX_2BCCE2F95932F377 ON regroupment_center (center_id)');
$this->addSql('ALTER TABLE regroupment_center ADD CONSTRAINT FK_2BCCE2F9EC6D1029 FOREIGN KEY (regroupment_id) REFERENCES regroupment (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE regroupment_center ADD CONSTRAINT FK_2BCCE2F95932F377 FOREIGN KEY (center_id) REFERENCES centers (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
}
}

View File

@ -409,6 +409,12 @@ crud:
add_new: Ajouter une civilité add_new: Ajouter une civilité
title_new: Nouvelle civilité title_new: Nouvelle civilité
title_edit: Modifier une civilité title_edit: Modifier une civilité
regroupment:
index:
title: Liste des regroupements
add_new: Ajouter un regroupement
title_new: Nouveau regroupement
title_edit: Modifier un regroupement
No entities: Aucun élément No entities: Aucun élément

View File

@ -22,7 +22,7 @@ class ChillDocumentLockManager implements DocumentLockManagerInterface
private const LOCK_DURATION = 60 * 30; private const LOCK_DURATION = 60 * 30;
/** /**
* Number of seconds to keep the lock after the delete lock operation. * Number of seconds to keep the lock after the delete lock operation
*/ */
private const LOCK_GRACEFUL_DURATION_TIME = 3; private const LOCK_GRACEFUL_DURATION_TIME = 3;