diff --git a/composer.json b/composer.json index 3d9f9a0..3dda365 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,7 @@ "prefer-stable": true, "require": { "php": ">=8.0.0", + "doctrine/orm": "2.11.*", "knplabs/knp-snappy": "^1.4", "murph/murph-core": "^1.13,>=1.13.0", "sabre/dav": "^4.3" diff --git a/config/packages/security.yaml b/config/packages/security.yaml index ab24957..1131f5d 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -14,6 +14,7 @@ security: role_hierarchy: ROLE_WRITER: ROLE_USER ROLE_MANAGER: ROLE_WRITER + ROLE_TREASURER: ROLE_USER ROLE_ADMIN: ROLE_MANAGER firewalls: diff --git a/src/Controller/ExpenseReportAdminController.php b/src/Controller/ExpenseReportAdminController.php new file mode 100644 index 0000000..62bc8f1 --- /dev/null +++ b/src/Controller/ExpenseReportAdminController.php @@ -0,0 +1,242 @@ +fileUpload = $fileUpload; + $this->fs = $fs; + } + + /** + * @Route("/admin/expense_report/{page}", name="admin_expense_report_index", methods={"GET"}, requirements={"page":"\d+"}) + */ + public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response + { + if (!$this->isGranted('ROLE_TREASURER')) { + $query->filterByUser($this->getUser()); + } + + return $this->doIndex($page, $query, $request, $session); + } + + /** + * @Route("/admin/expense_report/new", name="admin_expense_report_new", methods={"GET", "POST"}) + */ + public function new(Factory $factory, EntityManager $entityManager, Request $request): Response + { + return $this->doNew($factory->create($this->getUser()), $entityManager, $request); + } + + /** + * @Route("/admin/expense_report/show/{entity}", name="admin_expense_report_show", methods={"GET"}) + */ + public function show(Entity $entity): Response + { + if (!$this->isGranted('ROLE_TREASURER')) { + if ($entity->getUser()->getId() !== $entity->getUser()->getId()) { + throw $this->createAccessDeniedException(); + } + } + + return $this->doShow($entity); + } + + /** + * @Route("/admin/expense_report/filter", name="admin_expense_report_filter", methods={"GET"}) + */ + public function filter(Session $session): Response + { + return $this->doFilter($session); + } + + /** + * @Route("/admin/expense_report/edit/{entity}", name="admin_expense_report_edit", methods={"GET", "POST"}) + */ + public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response + { + if (!$this->isGranted('ROLE_TREASURER')) { + if ($entity->getUser()->getId() !== $entity->getUser()->getId()) { + throw $this->createAccessDeniedException(); + } + } + + return $this->doEdit($entity, $entityManager, $request, [$this, 'beforeUpdate']); + } + + protected function beforeUpdate(EntityInterface $entity, FormInterface $form, Request $request) + { + $deleteBills = $request->request->get($form->getName())['deleteBills']['bills'] ?? []; + $bills = $entity->getBills(); + + foreach ($bills as $key => $value) { + if (in_array($value, $deleteBills)) { + unset($bills[$key]); + $this->fs->remove($value); + } + } + + $directory = sprintf( + 'uploads/Notes de frais/%d/%d', + $this->getUser()->getId(), + $entity->getId() + ); + + $newBills = $request->files->get($form->getName())['newBills'] ?? []; + + foreach ($newBills as $data) { + $this->fileUpload->handleForm( + $data['file'], + $directory, + function ($filename) use ($directory, &$bills) { + $bills[] = $directory.'/'.$filename; + }, + true + ); + } + + $entity->setBills($bills); + } + + /** + * @Route("/admin/expense_report/sort/{page}", name="admin_expense_report_sort", methods={"POST"}, requirements={"page":"\d+"}) + */ + public function sort(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response + { + return $this->doSort($page, $query, $entityManager, $request, $session); + } + + /** + * @Route("/admin/expense_report/batch/{page}", name="admin_expense_report_batch", methods={"POST"}, requirements={"page":"\d+"}) + */ + public function batch(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response + { + if (!$this->isGranted('ROLE_TREASURER')) { + $query->filterByUser($this->getUser()); + } + + return $this->doBatch($page, $query, $entityManager, $request, $session); + } + + /** + * @Route("/admin/expense_report/delete/{entity}", name="admin_expense_report_delete", methods={"DELETE"}) + */ + public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response + { + if (!$this->isGranted('ROLE_TREASURER')) { + if ($entity->getUser()->getId() !== $entity->getUser()->getId()) { + throw $this->createAccessDeniedException(); + } + } + + return $this->doDelete($entity, $entityManager, $request); + } + + protected function getConfiguration(): CrudConfiguration + { + return CrudConfiguration::create() + ->setPageTitle('index', 'Notes de frais') + ->setPageTitle('edit', 'Note de frais {id}') + ->setPageTitle('new', 'Nouvelle note de frais') + ->setPageTitle('show', 'Note de frais {id}') + + ->setPageRoute('index', 'admin_expense_report_index') + ->setPageRoute('new', 'admin_expense_report_new') + ->setPageRoute('edit', 'admin_expense_report_edit') + ->setPageRoute('show', 'admin_expense_report_show') + ->setPageRoute('sort', 'admin_expense_report_sort') + ->setPageRoute('batch', 'admin_expense_report_batch') + ->setPageRoute('delete', 'admin_expense_report_delete') + ->setPageRoute('filter', 'admin_expense_report_filter') + + ->setFormOptions('new', [ + 'is_treasurer' => $this->isGranted('ROLE_TREASURER'), + ]) + ->setFormOptions('edit', [ + 'is_treasurer' => $this->isGranted('ROLE_TREASURER'), + ]) + ->setView('form', 'admin/expense_report/_form.html.twig') + + ->setForm('edit', Type::class, []) + ->setForm('new', Type::class) + // ->setForm('filter', Type::class) + + // ->setMaxPerPage('index', 20) + + // ->setIsSortableCollection('index', false) + // ->setSortableCollectionProperty('sortOrder') + + // ->setAction('index', 'new', true) + // ->setAction('index', 'show', true) + // ->setAction('index', 'edit', true) + // ->setAction('index', 'delete', true) + + // ->setAction('edit', 'back', true) + // ->setAction('edit', 'show', true) + // ->setAction('edit', 'delete', true) + + // ->setAction('show', 'back', true) + // ->setAction('show', 'edit', true) + + ->setDefaultSort('index', 'dateTo', 'desc') + + ->setField('index', 'Personne', Field\TextField::class, [ + 'property_builder' => function (EntityInterface $entity) { + return $entity->getUser()->getDisplayName(); + }, + 'attr' => ['class' => 'col-md-3'], + ]) + ->setField('index', 'Date from', Field\DateField::class, [ + 'property' => 'dateFrom', + 'sort' => ['dateFrom', '.dateFrom'], + 'format' => 'd/m/Y', + 'attr' => ['class' => 'col-md-3'], + ]) + ->setField('index', 'Date to', Field\DateField::class, [ + 'property' => 'dateTo', + 'sort' => ['dateTo', '.dateTo'], + 'format' => 'd/m/Y', + 'attr' => ['class' => 'col-md-3'], + ]) + ->setField('index', 'Payée', Field\ButtonField::class, [ + 'property_builder' => function (EntityInterface $entity) { + return $entity->getIsPaid() ? 'Yes' : 'No'; + }, + 'attr' => ['class' => 'col-md-3'], + 'button_attr_builder' => function (EntityInterface $entity) { + return ['class' => 'btn btn-sm btn-'.($entity->getIsPaid() ? 'success' : 'info')]; + }, + ]) + // ->setBatchAction('index', 'delete', 'Delete', function (EntityInterface $entity, EntityManager $manager) { + // $manager->delete($entity); + // }) + ; + } + + protected function getSection(): string + { + return 'expense_report'; + } +} diff --git a/src/Entity/ExpenseReport.php b/src/Entity/ExpenseReport.php new file mode 100644 index 0000000..4742a6e --- /dev/null +++ b/src/Entity/ExpenseReport.php @@ -0,0 +1,179 @@ +id; + } + + public function getUser(): ?User + { + return $this->user; + } + + public function setUser(?User $user): self + { + $this->user = $user; + + return $this; + } + + public function getMoves(): ?array + { + return $this->moves; + } + + public function setMoves(array $moves): self + { + $this->moves = $moves; + + return $this; + } + + public function getVariousPayments(): ?array + { + return $this->variousPayments; + } + + public function setVariousPayments(array $variousPayments): self + { + $this->variousPayments = $variousPayments; + + return $this; + } + + public function getIsPaid(): ?bool + { + return $this->isPaid; + } + + public function setIsPaid(bool $isPaid): self + { + $this->isPaid = $isPaid; + + return $this; + } + + public function getPaidAt(): ?\DateTimeInterface + { + return $this->paidAt; + } + + public function setPaidAt(?\DateTimeInterface $paidAt): self + { + $this->paidAt = $paidAt; + + return $this; + } + + public function getBills(): ?array + { + return $this->bills; + } + + public function setBills(array $bills): self + { + $this->bills = $bills; + + return $this; + } + + public function getDateFrom(): ?\DateTimeInterface + { + return $this->dateFrom; + } + + public function setDateFrom(\DateTimeInterface $dateFrom): self + { + $this->dateFrom = $dateFrom; + + return $this; + } + + public function getDateTo(): ?\DateTimeInterface + { + return $this->dateTo; + } + + public function setDateTo(\DateTimeInterface $dateTo): self + { + $this->dateTo = $dateTo; + + return $this; + } + + public function getScalePerKilometer(): ?float + { + return $this->scalePerKilometer; + } + + public function setScalePerKilometer(float $scalePerKilometer): self + { + $this->scalePerKilometer = $scalePerKilometer; + + return $this; + } +} diff --git a/src/Entity/User.php b/src/Entity/User.php index 7f6a251..27c8860 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -5,6 +5,8 @@ namespace App\Entity; use App\Core\Doctrine\Timestampable; use App\Core\Entity\EntityInterface; use App\Repository\UserRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; @@ -75,8 +77,19 @@ class User implements PasswordAuthenticatedUserInterface, UserInterface, TwoFact */ protected $isManager; + /** + * @ORM\Column(type="boolean", nullable=true) + */ + protected $isTreasurer; + + /** + * @ORM\OneToMany(targetEntity=ExpenseReport::class, mappedBy="user", orphanRemoval=true) + */ + private $expenseReports; + public function __construct() { + $this->expenseReports = new ArrayCollection(); } public function __toString() @@ -127,6 +140,10 @@ class User implements PasswordAuthenticatedUserInterface, UserInterface, TwoFact $roles[] = 'ROLE_MANAGER'; } + if ($this->getIsTreasurer()) { + $roles[] = 'ROLE_TREASURER'; + } + return array_unique($roles); } @@ -268,4 +285,46 @@ class User implements PasswordAuthenticatedUserInterface, UserInterface, TwoFact return $this; } + + public function getIsTreasurer(): ?bool + { + return $this->isTreasurer; + } + + public function setIsTreasurer(?bool $isTreasurer): self + { + $this->isTreasurer = $isTreasurer; + + return $this; + } + + /** + * @return Collection + */ + public function getExpenseReports(): Collection + { + return $this->expenseReports; + } + + public function addExpenseReport(ExpenseReport $expenseReport): self + { + if (!$this->expenseReports->contains($expenseReport)) { + $this->expenseReports[] = $expenseReport; + $expenseReport->setUser($this); + } + + return $this; + } + + public function removeExpenseReport(ExpenseReport $expenseReport): self + { + if ($this->expenseReports->removeElement($expenseReport)) { + // set the owning side to null (unless already changed) + if ($expenseReport->getUser() === $this) { + $expenseReport->setUser(null); + } + } + + return $this; + } } diff --git a/src/EventSubscriber/ExpenseReportEventSubscriber.php b/src/EventSubscriber/ExpenseReportEventSubscriber.php new file mode 100644 index 0000000..dd11b4c --- /dev/null +++ b/src/EventSubscriber/ExpenseReportEventSubscriber.php @@ -0,0 +1,50 @@ +manager = $manager; + } + + public function supports(EntityInterface $entity) + { + return $entity instanceof ExpenseReport; + } + + public function onPreCreate(EntityManagerEvent $event) + { + if (!$this->supports($event->getEntity())) { + return; + } + + $this->addScalePerKilometer($event->getEntity()); + } + + public function onPreUpdate(EntityManagerEvent $event) + { + if (!$this->supports($event->getEntity())) { + return; + } + + $this->addScalePerKilometer($event->getEntity()); + } + + protected function addScalePerKilometer(EntityInterface $entity) + { + if (null === $entity->getScalePerKilometer() || 0.0 === $entity->getScalePerKilometer()) { + $scale = $this->manager->get('expense_report_scale_per_kilometer'); + $entity->setScalePerKilometer($scale->getValue()); + } + } +} diff --git a/src/EventSubscriber/SettingEventSubscriber.php b/src/EventSubscriber/SettingEventSubscriber.php index 21d5d92..50161ea 100644 --- a/src/EventSubscriber/SettingEventSubscriber.php +++ b/src/EventSubscriber/SettingEventSubscriber.php @@ -5,8 +5,9 @@ namespace App\EventSubscriber; use App\Core\Event\Setting\SettingEvent; use App\Core\EventSubscriber\SettingEventSubscriber as EventSubscriber; use App\Core\Setting\SettingManager; -use Symfony\Component\Form\Extension\Core\Type\CheckboxType; -use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\Extension\Core\Type\NumberType; +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\Range; /** * class SettingEventSubscriber. @@ -24,8 +25,12 @@ class SettingEventSubscriber extends EventSubscriber public function onInit(SettingEvent $event) { - // $this->manager->init('myapp_foo', 'My app', 'Foo', 'Default value'); - // $this->manager->init('myapp_bar', 'My app', 'Bar', true); + $this->manager->init( + 'expense_report_scale_per_kilometer', + 'Note de frais', + 'Barème au kilomètre', + 0 + ); } public function onFormInit(SettingEvent $event) @@ -34,25 +39,24 @@ class SettingEventSubscriber extends EventSubscriber $builder = $data['builder']; $entity = $data['entity']; - // if ('myapp_foo' === $entity->getCode()) { - // $builder->add( - // 'value', - // TextType::class, - // [ - // 'label' => $entity->getLabel(), - // ] - // ); - // } - // - // if ('myapp_bar' === $entity->getCode()) { - // $builder->add( - // 'value', - // CheckboxType::class, - // [ - // 'label' => $entity->getLabel(), - // 'required' => false, - // ] - // ); - // } + if ('expense_report_scale_per_kilometer' === $entity->getCode()) { + $builder->add( + 'value', + NumberType::class, + [ + 'html5' => true, + 'required' => true, + 'label' => $entity->getLabel(), + 'scale' => 2, + 'attr' => [ + 'step' => 0.01, + ], + 'constraints' => [ + new NotBlank(), + new Range(['min' => 0]), + ], + ] + ); + } } } diff --git a/src/Factory/ExpenseReportFactory.php b/src/Factory/ExpenseReportFactory.php new file mode 100644 index 0000000..04557fe --- /dev/null +++ b/src/Factory/ExpenseReportFactory.php @@ -0,0 +1,18 @@ +setUser($user); + + return $entity; + } +} diff --git a/src/Form/ExpenseReportDeleteBillType.php b/src/Form/ExpenseReportDeleteBillType.php new file mode 100644 index 0000000..811504a --- /dev/null +++ b/src/Form/ExpenseReportDeleteBillType.php @@ -0,0 +1,42 @@ +add( + 'bills', + ChoiceType::class, + [ + 'label' => false, + 'required' => false, + 'multiple' => true, + 'choices' => $choices, + 'expanded' => true, + ] + ); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'bills' => [], + ]); + } +} diff --git a/src/Form/ExpenseReportMoveType.php b/src/Form/ExpenseReportMoveType.php new file mode 100644 index 0000000..c2db236 --- /dev/null +++ b/src/Form/ExpenseReportMoveType.php @@ -0,0 +1,107 @@ +add('date', DateType::class, [ + 'html5' => true, + 'required' => true, + 'widget' => 'single_text', + 'row_attr' => [ + 'class' => 'col-md-6 pr-1', + ], + 'constraints' => [ + new NotBlank(), + ], + ]) + ->add('event', TextType::class, [ + 'row_attr' => [ + 'class' => 'col-md-6', + ], + 'constraints' => [ + new NotBlank(), + ], + ]) + ->add('addressFrom', TextType::class, [ + 'row_attr' => [ + 'class' => 'col-md-6 pr-1', + ], + 'constraints' => [ + new NotBlank(), + ], + ]) + ->add('addressTo', TextType::class, [ + 'row_attr' => [ + 'class' => 'col-md-6', + ], + 'constraints' => [ + new NotBlank(), + ], + ]) + ->add('distance', NumberType::class, [ + 'html5' => true, + 'scale' => 0, + 'row_attr' => [ + 'class' => 'col-6 pr-1', + ], + 'constraints' => [ + new NotBlank(), + new Range(['min' => 0]), + ], + ]) + ->add('isRoundTrip', ChoiceType::class, [ + 'row_attr' => [ + 'class' => 'col-md-6', + ], + 'choices' => [ + 'Non' => false, + 'Oui' => true, + ], + 'required' => true, + ]) + ->add('highwayPay', NumberType::class, [ + 'row_attr' => [ + 'class' => 'col-md-6 pr-1', + ], + 'attr' => [ + 'step' => 0.01, + ], + 'scale' => 2, + 'required' => false, + 'html5' => true, + ]) + ->add('parkingPay', NumberType::class, [ + 'row_attr' => [ + 'class' => 'col-md-6', + ], + 'attr' => [ + 'step' => 0.01, + ], + 'scale' => 2, + 'required' => false, + 'html5' => true, + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + // Configure your form options here + ]); + } +} diff --git a/src/Form/ExpenseReportNewBillType.php b/src/Form/ExpenseReportNewBillType.php new file mode 100644 index 0000000..2a7c0e8 --- /dev/null +++ b/src/Form/ExpenseReportNewBillType.php @@ -0,0 +1,36 @@ +add('file', FileType::class, [ + 'required' => true, + 'label' => false, + 'row_attr' => [ + 'class' => 'col-md-12', + ], + 'constraints' => [ + new NotBlank(), + ], + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + // Configure your form options here + ]); + } +} diff --git a/src/Form/ExpenseReportType.php b/src/Form/ExpenseReportType.php new file mode 100644 index 0000000..e91bfb9 --- /dev/null +++ b/src/Form/ExpenseReportType.php @@ -0,0 +1,122 @@ +add('dateFrom', null, [ + 'html5' => true, + 'widget' => 'single_text', + ]) + ->add('dateTo', null, [ + 'html5' => true, + 'widget' => 'single_text', + ]) + ->add('moves', CollectionType::class, [ + 'entry_type' => ExpenseReportMoveType::class, + 'collection_name' => 'moves', + 'prototype' => true, + 'allow_add' => true, + 'allow_delete' => true, + 'row_attr' => [ + 'class' => 'mb-3 pb-3 pl-3 pr-3 pt-1 bg-light', + ], + 'label_attr' => [ + 'class' => 'font-weight-bold', + ], + 'attr' => [ + 'class' => 'mb-3 row', + ], + ]) + ->add('variousPayments', CollectionType::class, [ + 'entry_type' => ExpenseReportVariousPaymentType::class, + 'collection_name' => 'variousPayments', + 'prototype' => true, + 'allow_add' => true, + 'allow_delete' => true, + 'row_attr' => [ + 'class' => 'mb-3 pb-3 pl-3 pr-3 pt-1 bg-light', + ], + 'label_attr' => [ + 'class' => 'font-weight-bold', + ], + 'attr' => [ + 'class' => 'mb-3 row', + ], + ]) + ; + + if ($builder->getData()->getId()) { + $builder + ->add('newBills', CollectionType::class, [ + 'entry_type' => ExpenseReportNewBillType::class, + 'mapped' => false, + 'label' => 'Nouvelles factures', + 'collection_name' => 'newBills', + 'prototype' => true, + 'allow_add' => true, + 'allow_delete' => true, + 'row_attr' => [ + 'class' => 'mb-3 pb-3 pl-3 pr-3 pt-1 bg-light', + ], + 'label_attr' => [ + 'class' => 'font-weight-bold', + ], + 'attr' => [ + 'class' => 'mb-3 row', + ], + ]) + ; + } + + if (count($builder->getData()->getBills())) { + $builder + ->add('deleteBills', ExpenseReportDeleteBillType::class, [ + 'mapped' => false, + 'label' => 'Factures à supprimer', + 'bills' => $builder->getData()->getBills(), + 'row_attr' => [ + 'class' => 'mb-3 p-3 bg-light', + ], + 'label_attr' => [ + 'class' => 'font-weight-bold', + ], + 'attr' => [ + 'class' => 'row mb-3', + ], + ]) + ; + } + + if ($options['is_treasurer']) { + $builder + ->add('isPaid', CheckboxType::class, [ + 'required' => false, + ]) + ->add('paidAt', null, [ + 'required' => false, + 'html5' => true, + 'widget' => 'single_text', + ]) + ; + } + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => ExpenseReport::class, + 'is_treasurer' => false, + ]); + } +} diff --git a/src/Form/ExpenseReportVariousPaymentType.php b/src/Form/ExpenseReportVariousPaymentType.php new file mode 100644 index 0000000..bdb9e41 --- /dev/null +++ b/src/Form/ExpenseReportVariousPaymentType.php @@ -0,0 +1,58 @@ +add('date', DateType::class, [ + 'html5' => true, + 'required' => true, + 'widget' => 'single_text', + 'row_attr' => [ + 'class' => 'col-md-4 pr-1', + ], + 'constraints' => [ + new NotBlank(), + ], + ]) + ->add('label', TextType::class, [ + 'row_attr' => [ + 'class' => 'col-md-4 pr-1', + ], + 'constraints' => [ + new NotBlank(), + ], + ]) + ->add('amount', NumberType::class, [ + 'required' => true, + 'html5' => true, + 'row_attr' => [ + 'class' => 'col-md-4 pr-1', + ], + 'constraints' => [ + new NotBlank(), + new Range(['min' => 0]), + ], + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + // Configure your form options here + ]); + } +} diff --git a/src/Form/UserType.php b/src/Form/UserType.php index eefa9e8..910e956 100644 --- a/src/Form/UserType.php +++ b/src/Form/UserType.php @@ -25,5 +25,17 @@ class UserType extends BaseUserType ] ); + $builder->add( + 'isTreasurer', + CheckboxType::class, + [ + 'label' => 'Trésorier⋅ière', + 'required' => false, + 'attr' => [ + ], + 'constraints' => [ + ], + ] + ); } } diff --git a/src/Repository/ExpenseReportRepository.php b/src/Repository/ExpenseReportRepository.php new file mode 100644 index 0000000..788bd6f --- /dev/null +++ b/src/Repository/ExpenseReportRepository.php @@ -0,0 +1,76 @@ +_em->persist($entity); + if ($flush) { + $this->_em->flush(); + } + } + + /** + * @throws ORMException + * @throws OptimisticLockException + */ + public function remove(ExpenseReport $entity, bool $flush = true): void + { + $this->_em->remove($entity); + if ($flush) { + $this->_em->flush(); + } + } + + // /** + // * @return ExpenseReport[] Returns an array of ExpenseReport objects + // */ + /* + public function findByExampleField($value) + { + return $this->createQueryBuilder('e') + ->andWhere('e.exampleField = :val') + ->setParameter('val', $value) + ->orderBy('e.id', 'ASC') + ->setMaxResults(10) + ->getQuery() + ->getResult() + ; + } + */ + + /* + public function findOneBySomeField($value): ?ExpenseReport + { + return $this->createQueryBuilder('e') + ->andWhere('e.exampleField = :val') + ->setParameter('val', $value) + ->getQuery() + ->getOneOrNullResult() + ; + } + */ +} diff --git a/src/Repository/ExpenseReportRepositoryQuery.php b/src/Repository/ExpenseReportRepositoryQuery.php new file mode 100644 index 0000000..1143758 --- /dev/null +++ b/src/Repository/ExpenseReportRepositoryQuery.php @@ -0,0 +1,23 @@ +andWhere('.user = :user') + ->setParameter('user', $user->getId()); + } +} diff --git a/templates/admin/expense_report/_form.html.twig b/templates/admin/expense_report/_form.html.twig new file mode 100644 index 0000000..bd8bd8f --- /dev/null +++ b/templates/admin/expense_report/_form.html.twig @@ -0,0 +1,16 @@ +
+
+ {% for item in ['dateFrom', 'dateTo', 'isPaid', 'paidAt', 'newBills', 'deleteBills'] %} + {% if form[item] is defined %} + {% include(configuration.view('form_widget', '@Core/admin/crud/_form_widget.html.twig')) with {form: form[item]} %} + {% endif %} + {% endfor %} +
+
+ {% for item in ['moves', 'variousPayments', 'newBills'] %} + {% if form[item] is defined %} + {% include(configuration.view('form_widget', '@Core/admin/crud/_form_widget.html.twig')) with {form: form[item]} %} + {% endif %} + {% endfor %} +
+
diff --git a/templates/admin/menu.html.twig b/templates/admin/menu.html.twig index f9f6762..e5165e9 100644 --- a/templates/admin/menu.html.twig +++ b/templates/admin/menu.html.twig @@ -62,4 +62,15 @@ icon: 'fa fa-list-alt' }) }} + + {{ include('@Core/admin/module/_menu_section.html.twig', {label: 'Divers'}) }} + + {% endif %} diff --git a/templates/core/user/user_admin/_form.html.twig b/templates/core/user/user_admin/_form.html.twig index 3c280a5..a96a178 100644 --- a/templates/core/user/user_admin/_form.html.twig +++ b/templates/core/user/user_admin/_form.html.twig @@ -1,7 +1,7 @@
- {% for item in ['displayName', 'email', 'isAdmin', 'isWriter', 'isManager'] %} + {% for item in ['displayName', 'email', 'isAdmin', 'isWriter', 'isManager', 'isTreasurer'] %}
{{ form_row(form[item]) }}
diff --git a/templates/core/user/user_admin/_show.html.twig b/templates/core/user/user_admin/_show.html.twig new file mode 100644 index 0000000..55e6de6 --- /dev/null +++ b/templates/core/user/user_admin/_show.html.twig @@ -0,0 +1,27 @@ +
+
+
    +
  • + {{ 'Display name'|trans }} + + {{ entity.displayName }} +
  • +
  • + {{ 'E-mail'|trans }} + + {{ entity.email }} +
  • +
  • + {{ 'Permissions'|trans }} + + {{ entity.roles|join('
    ')|replace({ + ROLE_USER: 'User'|trans, + ROLE_WRITER: 'Writer'|trans, + ROLE_ADMIN: 'Administrator'|trans, + ROLE_TREASURER: 'ROLE_TREASURER'|trans, + ROLE_MANAGER: 'ROLE_MANAGER'|trans, + })|raw }} +
  • +
+
+
diff --git a/translations/messages.fr.yaml b/translations/messages.fr.yaml index ff5ed45..529a521 100644 --- a/translations/messages.fr.yaml +++ b/translations/messages.fr.yaml @@ -1,3 +1,5 @@ +"ROLE_MANAGER": "Gestionnaire" +"ROLE_TREASURER": "Trésorier⋅ière" "Address": "Adresse" "Zip code": "Code postal" "City": "Ville" @@ -26,3 +28,18 @@ "Caldav password": "Mot de passe du serveur Caldav" "Caldav calendar uri": "URI du calendrier Caldav" "Color": "Couleur" +"Moves": "Trajets" +"Highway pay": "Frais de péage (TTC)" +"Parking pay": "Frais de parking (TTC)" +"Various payments": "Paiements divers" +"Bills": "Factures" +"Date from": "Date de début" +"Date to": "Date de fin" +"Is paid": "Est payé(e)" +"Paid at": "Payé(e) le" +"Event": "Évènement" +"Address from": "Adresse de départ" +"Address to": "Adresse d'arrivée" +"Distance": "Distance (sans retour)" +"Is round trip": "Aller-retour" +"Amount": "Montant (TTC)"