From 0fb4191338a37d58fbc4ed9fe071577cf57d0997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 10 Nov 2024 00:19:58 +0100 Subject: [PATCH 1/8] Added new datastructures to store additional data about a check performed by StuRa finance members --- migrations/Version20241109231434.php | 49 ++++++++++++++ src/Entity/Embeddable/Check.php | 99 ++++++++++++++++++++++++++++ src/Entity/PaymentOrder.php | 60 ++++++++--------- 3 files changed, 177 insertions(+), 31 deletions(-) create mode 100644 migrations/Version20241109231434.php create mode 100644 src/Entity/Embeddable/Check.php diff --git a/migrations/Version20241109231434.php b/migrations/Version20241109231434.php new file mode 100644 index 0000000..a2dc1c6 --- /dev/null +++ b/migrations/Version20241109231434.php @@ -0,0 +1,49 @@ +addSql('ALTER TABLE bank_accounts_audit CHANGE diffs diffs JSON DEFAULT NULL'); + $this->addSql('ALTER TABLE confirmation_token_audit CHANGE diffs diffs JSON DEFAULT NULL'); + $this->addSql('ALTER TABLE confirmer_audit CHANGE diffs diffs JSON DEFAULT NULL'); + $this->addSql('ALTER TABLE departments_audit CHANGE diffs diffs JSON DEFAULT NULL'); + $this->addSql('ALTER TABLE payment_orders ADD mathematically_correct_checked TINYINT(1) NOT NULL, ADD mathematically_correct_timestamp DATETIME DEFAULT NULL, ADD mathematically_correct_confirmer_name VARCHAR(255) DEFAULT NULL, ADD mathematically_correct_confirmer_id INT DEFAULT NULL, ADD mathematically_correct_remark LONGTEXT DEFAULT NULL, ADD factually_correct_checked TINYINT(1) NOT NULL, ADD factually_correct_timestamp DATETIME DEFAULT NULL, ADD factually_correct_confirmer_name VARCHAR(255) DEFAULT NULL, ADD factually_correct_confirmer_id INT DEFAULT NULL, ADD factually_correct_remark LONGTEXT DEFAULT NULL'); + + //Move the old data from mathematically_correct and factually_correct to the new fields + $this->addSql('UPDATE payment_orders SET mathematically_correct_checked = mathematically_correct, factually_correct_checked = factually_correct'); + + //Drop the old columns + $this->addSql('ALTER TABLE payment_orders DROP mathematically_correct, DROP factually_correct'); + $this->addSql('ALTER TABLE payment_orders_audit CHANGE diffs diffs JSON DEFAULT NULL'); + $this->addSql('ALTER TABLE user_audit CHANGE diffs diffs JSON DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE bank_accounts_audit CHANGE diffs diffs LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE confirmation_token_audit CHANGE diffs diffs LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE confirmer_audit CHANGE diffs diffs LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE departments_audit CHANGE diffs diffs LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE payment_orders ADD mathematically_correct TINYINT(1) NOT NULL, ADD factually_correct TINYINT(1) NOT NULL, DROP mathematically_correct_checked, DROP mathematically_correct_timestamp, DROP mathematically_correct_confirmer_name, DROP mathematically_correct_confirmer_id, DROP mathematically_correct_remark, DROP factually_correct_checked, DROP factually_correct_timestamp, DROP factually_correct_confirmer_name, DROP factually_correct_confirmer_id, DROP factually_correct_remark'); + $this->addSql('ALTER TABLE payment_orders_audit CHANGE diffs diffs LONGTEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE user_audit CHANGE diffs diffs LONGTEXT DEFAULT NULL'); + } +} diff --git a/src/Entity/Embeddable/Check.php b/src/Entity/Embeddable/Check.php new file mode 100644 index 0000000..e240c57 --- /dev/null +++ b/src/Entity/Embeddable/Check.php @@ -0,0 +1,99 @@ +checked; + } + + public function setChecked(bool $checked): Check + { + $this->checked = $checked; + return $this; + } + + public function getTimestamp(): ?\DateTime + { + return $this->timestamp; + } + + public function setTimestamp(?\DateTime $timestamp): Check + { + $this->timestamp = $timestamp; + return $this; + } + + public function getConfirmerName(): ?string + { + return $this->confirmerName; + } + + public function setConfirmerName(?string $confirmerName): Check + { + $this->confirmerName = $confirmerName; + return $this; + } + + public function getConfirmerID(): ?int + { + return $this->confirmerID; + } + + public function setConfirmerID(?int $confirmerID): Check + { + $this->confirmerID = $confirmerID; + return $this; + } + + public function getRemark(): ?string + { + return $this->remark; + } + + public function setRemark(?string $remark): Check + { + $this->remark = $remark; + return $this; + } + +} \ No newline at end of file diff --git a/src/Entity/PaymentOrder.php b/src/Entity/PaymentOrder.php index 0b380b5..0da14cb 100644 --- a/src/Entity/PaymentOrder.php +++ b/src/Entity/PaymentOrder.php @@ -20,6 +20,7 @@ use App\Entity\Contracts\DBElementInterface; use App\Entity\Contracts\TimestampedElementInterface; +use App\Entity\Embeddable\Check; use App\Entity\Embeddable\Confirmation; use App\Entity\Embeddable\PayeeInfo; use App\EntityListener\PaymentOrderChangedFieldsListener; @@ -227,19 +228,21 @@ class PaymentOrder implements DBElementInterface, TimestampedElementInterface, \ *******************************************************************************************************************/ /** - * @var bool "mathematisch richtig" + * @var Check "mathematisch richtig" */ - #[ORM\Column(type: Types::BOOLEAN)] - private bool $mathematically_correct = false; - - #[ORM\Column(type: Types::BOOLEAN)] - private bool $exported = false; + #[ORM\Embedded(Check::class)] + #[Assert\Valid] + private Check $mathematically_correct; /** - * @var bool "sachlich richtig" + * @var Check "sachlich richtig" */ + #[ORM\Embedded(Check::class)] + #[Assert\Valid] + private Check $factually_correct; + #[ORM\Column(type: Types::BOOLEAN)] - private bool $factually_correct = false; + private bool $exported = false; /** * @var Confirmation The first confirmation for this payment order @@ -297,6 +300,9 @@ public function __construct() $this->confirmation1 = new Confirmation(); $this->confirmation2 = new Confirmation(); + + $this->mathematically_correct = new Check(); + $this->factually_correct = new Check(); } public function getId(): ?int @@ -468,48 +474,41 @@ public function getAmountString(): ?string } /** - * Returns whether this payment order was checked as mathematically correct. - * This means it was checked that the amount and data in this payment order matches the data on the invoice. + * Returns the check object for "mathematically correct". + * @return Check */ - public function isMathematicallyCorrect(): bool + public function getMathematicallyCorrect(): Check { return $this->mathematically_correct; } /** - * Sets whether this payment order was checked as mathematically correct. + * Returns whether this payment order was checked as mathematically correct. * This means it was checked that the amount and data in this payment order matches the data on the invoice. */ - public function setMathematicallyCorrect(bool $mathematically_correct): PaymentOrder + public function isMathematicallyCorrectChecked(): bool { - $this->mathematically_correct = $mathematically_correct; - - return $this; + return $this->mathematically_correct->isChecked(); } + /** - * Returns whether this payment order was checked as factually correct. - * This means it was checked that this payment is really needed. In our context it also means that an payment order - * was payed out and is finished. + * Returns the check object for "factually correct". + * @return Check */ - public function isFactuallyCorrect(): bool + public function getFactuallyCorrect(): Check { return $this->factually_correct; } /** - * Sets whether this payment order was checked as factually correct. + * Returns whether this payment order was checked as factually correct. * This means it was checked that this payment is really needed. In our context it also means that an payment order * was payed out and is finished. */ - public function setFactuallyCorrect(bool $factually_correct): PaymentOrder + public function isFactuallyCorrectChecked(): bool { - $this->factually_correct = $factually_correct; - - //Update the status of booking date - $this->booking_date = $factually_correct ? new \DateTime() : null; - - return $this; + return $this->factually_correct->isChecked(); } /** @@ -706,7 +705,7 @@ public function setSubmitterEmail(string $submitter_email): PaymentOrder } /** - * Returns whether this is an payment order for an resolution of the FSR-Kom (these are handled differently). + * Returns whether this is a payment order for a resolution of the FSR-Kom (these are handled differently). */ public function isFsrKomResolution(): bool { @@ -714,7 +713,7 @@ public function isFsrKomResolution(): bool } /** - * Sets whether this is an payment order for an resolution of the FSR-Kom (these are handled differently). + * Sets whether this is a payment order for a resolution of the FSR-Kom (these are handled differently). */ public function setFsrKomResolution(bool $fsr_kom_resolution): PaymentOrder { @@ -758,7 +757,6 @@ public function getBookingDate(): ?DateTime /** * Manually set the datetime when this payment was booked in banking. * Set to null if payment_order was not booked yet. - * The value is set automatically to now when the "factually_checked" field is set. */ public function setBookingDate(?DateTime $booking_date): PaymentOrder { From 1f19965bc49b26f4b3e0ab380237e9066d1e79f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 10 Nov 2024 00:31:15 +0100 Subject: [PATCH 2/8] Fixed admin fields for our new datastructures --- src/Admin/Filter/CheckFilter.php | 38 +++++++++++++++++++ .../Admin/PaymentOrderCrudController.php | 18 ++++++--- 2 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 src/Admin/Filter/CheckFilter.php diff --git a/src/Admin/Filter/CheckFilter.php b/src/Admin/Filter/CheckFilter.php new file mode 100644 index 0000000..37d88ff --- /dev/null +++ b/src/Admin/Filter/CheckFilter.php @@ -0,0 +1,38 @@ +setFilterFqcn(__CLASS__) + ->setProperty($propertyName) + ->setLabel($label) + ->setFormType(BooleanFilterType::class) + ->setFormTypeOption('translation_domain', 'EasyAdminBundle'); + } + + public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void + { + $property = $filterDataDto->getProperty() . '.checked'; + + $queryBuilder + ->andWhere(sprintf('%s.%s %s :%s', $filterDataDto->getEntityAlias(), $property, $filterDataDto->getComparison(), $filterDataDto->getParameterName())) + ->setParameter($filterDataDto->getParameterName(), $filterDataDto->getValue()); + } +} \ No newline at end of file diff --git a/src/Controller/Admin/PaymentOrderCrudController.php b/src/Controller/Admin/PaymentOrderCrudController.php index a9c51c2..3f9860c 100644 --- a/src/Controller/Admin/PaymentOrderCrudController.php +++ b/src/Controller/Admin/PaymentOrderCrudController.php @@ -21,9 +21,11 @@ use App\Admin\Field\ConfirmationField; use App\Admin\Field\FieldChangesField; use App\Admin\Field\VichyFileField; +use App\Admin\Filter\CheckFilter; use App\Admin\Filter\ConfirmedFilter; use App\Admin\Filter\DepartmentTypeFilter; use App\Admin\Filter\MoneyAmountFilter; +use App\Entity\Embeddable\Check; use App\Entity\FieldChanges; use App\Entity\PaymentOrder; use App\Entity\User; @@ -64,7 +66,13 @@ final class PaymentOrderCrudController extends AbstractCrudController { - public function __construct(private readonly PaymentOrderMailLinkGenerator $mailToGenerator, private readonly DashboardControllerRegistry $dashboardControllerRegistry, private EntityManagerInterface $entityManager, private readonly ConfirmationEmailSender $confirmationEmailSender, private readonly AdminUrlGenerator $adminURLGenerator, private readonly MessageBusInterface $messageBus) + public function __construct( + private readonly PaymentOrderMailLinkGenerator $mailToGenerator, + private EntityManagerInterface $entityManager, + private readonly ConfirmationEmailSender $confirmationEmailSender, + private readonly AdminUrlGenerator $adminURLGenerator, + private readonly MessageBusInterface $messageBus + ) { } @@ -163,9 +171,9 @@ public function configureFilters(Filters $filters): Filters ->add(EntityFilter::new('department', 'payment_order.department.label')) ->add(DepartmentTypeFilter::new('department_type', 'payment_order.department_type.label')) ->add(MoneyAmountFilter::new('amount', 'payment_order.amount.label')) - ->add(BooleanFilter::new('factually_correct', 'payment_order.factually_correct.label')) + ->add(CheckFilter::new('factually_correct', 'payment_order.factually_correct.label')) + ->add(CheckFilter::new('mathematically_correct', 'payment_order.mathematically_correct.label')) ->add(BooleanFilter::new('exported', 'payment_order.exported.label')) - ->add(BooleanFilter::new('mathematically_correct', 'payment_order.mathematically_correct.label')) ->add(ConfirmedFilter::new('confirmed', 'payment_order.confirmed.label')) ->add(TextFilter::new('funding_id', 'payment_order.funding_id.label')) ->add(DateTimeFilter::new('creation_date', 'creation_date')) @@ -431,7 +439,7 @@ public function configureFields(string $pageName): iterable //Status informations $statusPanel = FormField::addPanel('payment_order.group.status'); - $mathematicallyCorrect = BooleanField::new('mathematically_correct', 'payment_order.mathematically_correct.label') + $mathematicallyCorrect = BooleanField::new('mathematically_correct.checked', 'payment_order.mathematically_correct.label') ->setHelp('payment_order.mathematically_correct.help') //Disable fields (and show coloumns as read only tags) if user does not have proper permissions to change //factually and mathematically correct status @@ -439,7 +447,7 @@ public function configureFields(string $pageName): iterable ->renderAsSwitch($this->isGranted('ROLE_PO_MATHEMATICALLY')); $exported = BooleanField::new('exported', 'payment_order.exported.label') ->setHelp('payment_order.exported.help'); - $factuallyCorrect = BooleanField::new('factually_correct', 'payment_order.factually_correct.label') + $factuallyCorrect = BooleanField::new('factually_correct.checked', 'payment_order.factually_correct.label') ->setHelp('payment_order.factually_correct.help') ->setFormTypeOption('disabled', !$this->isGranted('ROLE_PO_FACTUALLY')) ->renderAsSwitch($this->isGranted('ROLE_PO_FACTUALLY')); From 1706292fca858bf9070e4cbeb3cd1a73f5addbd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 10 Nov 2024 00:43:02 +0100 Subject: [PATCH 3/8] Fixed actions for payment orders --- src/Controller/Admin/PaymentOrderCrudController.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Controller/Admin/PaymentOrderCrudController.php b/src/Controller/Admin/PaymentOrderCrudController.php index 3f9860c..9fc5c87 100644 --- a/src/Controller/Admin/PaymentOrderCrudController.php +++ b/src/Controller/Admin/PaymentOrderCrudController.php @@ -329,10 +329,6 @@ public function configureActions(Actions $actions): Actions //Hide action if no contact emails are associated with department $emailAction->displayIf(fn(PaymentOrder $paymentOrder): bool => null !== $this->mailToGenerator->generateContactMailLink($paymentOrder)); - $hhv_action = Action::new('contactHHV', 'payment_order.action.contact_hhv', 'fas fa-comment-dots') - ->linkToUrl(fn(PaymentOrder $paymentOrder): string => $this->mailToGenerator->getHHVMailLink($paymentOrder)) - ->setCssClass('btn btn-secondary text-dark'); - $resend_confirmation_action = Action::new('resendConfirmation', 'payment_order.action.resend_confirmation', 'fas fa-redo') ->linkToCrudAction('resendConfirmationEmail') ->displayIf(fn(PaymentOrder $paymentOrder): bool => $this->isGranted('ROLE_EDIT_PAYMENT_ORDERS') && !$paymentOrder->isConfirmed()) @@ -342,15 +338,15 @@ public function configureActions(Actions $actions): Actions ->linkToCrudAction('checkMathematicallyCorrect') ->displayIf(fn(PaymentOrder $paymentOrder): bool => $this->isGranted('ROLE_PO_MATHEMATICALLY') && $paymentOrder->isConfirmed() - && !$paymentOrder->isMathematicallyCorrect()) + && !$paymentOrder->isMathematicallyCorrectChecked()) ->setCssClass('btn btn-success'); $factually_correct_action = Action::new('factuallyCorrect', 'payment_order.action.factually_correct', 'fas fa-check') ->linkToCrudAction('checkFactuallyCorrect') ->displayIf(fn(PaymentOrder $paymentOrder): bool => $this->isGranted('ROLE_PO_FACTUALLY') && $paymentOrder->isConfirmed() - && !$paymentOrder->isFactuallyCorrect() - && $paymentOrder->isMathematicallyCorrect()) + && !$paymentOrder->isFactuallyCorrectChecked() + && $paymentOrder->isMathematicallyCorrectChecked()) ->setCssClass('btn btn-success'); $manual_confirmation = Action::new('manual_confirmation', 'payment_order.action.manual_confirmation', 'fas fa-exclamation-triangle') @@ -364,8 +360,6 @@ public function configureActions(Actions $actions): Actions $actions->add(Crud::PAGE_EDIT, $emailAction); $actions->add(Crud::PAGE_DETAIL, $emailAction); - $actions->add(Crud::PAGE_EDIT, $hhv_action); - $actions->add(Crud::PAGE_DETAIL, $hhv_action); $actions->disable(Crud::PAGE_NEW); From 7391c6cb352c512e8ae6f81fad63b7921b53fd17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 10 Nov 2024 01:00:54 +0100 Subject: [PATCH 4/8] Use pretty routes for admin ressources --- config/routes/easyadmin.yaml | 3 +++ src/Controller/Admin/DashboardController.php | 2 +- src/Controller/Admin/PaymentOrderManualConfirmController.php | 4 ++-- src/Controller/SecurityController.php | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 config/routes/easyadmin.yaml diff --git a/config/routes/easyadmin.yaml b/config/routes/easyadmin.yaml new file mode 100644 index 0000000..a88552e --- /dev/null +++ b/config/routes/easyadmin.yaml @@ -0,0 +1,3 @@ +easyadmin: + resource: . + type: easyadmin.routes \ No newline at end of file diff --git a/src/Controller/Admin/DashboardController.php b/src/Controller/Admin/DashboardController.php index 14f1c56..2ff6de9 100644 --- a/src/Controller/Admin/DashboardController.php +++ b/src/Controller/Admin/DashboardController.php @@ -53,7 +53,7 @@ public function configureDashboard(): Dashboard ->setTitle('StuRa Finanzen'); } - #[Route(path: '/admin', name: 'admin_dashboard')] + #[Route(path: '/admin', name: 'admin')] public function index(): Response { return $this->render('admin/dashboard.html.twig'); diff --git a/src/Controller/Admin/PaymentOrderManualConfirmController.php b/src/Controller/Admin/PaymentOrderManualConfirmController.php index 28d5537..9560064 100644 --- a/src/Controller/Admin/PaymentOrderManualConfirmController.php +++ b/src/Controller/Admin/PaymentOrderManualConfirmController.php @@ -46,7 +46,7 @@ public function manualConfirmation( if ($paymentOrder->isConfirmed()) { $this->addFlash('error', 'payment_order.manual_confirm.already_confirmed'); - return $this->redirectToRoute('admin_dashboard'); + return $this->redirectToRoute('admin'); } $form = $this->createForm(PaymentOrderManualConfirmationType::class); @@ -60,7 +60,7 @@ public function manualConfirmation( //Show a success flash notification $this->addFlash('success', 'payment_order.manual_confirm.success'); - return $this->redirectToRoute('admin_dashboard'); + return $this->redirectToRoute('admin'); } return $this->render('admin/payment_order/manual_confirm.html.twig', [ diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php index d3e7b77..fac0963 100644 --- a/src/Controller/SecurityController.php +++ b/src/Controller/SecurityController.php @@ -38,7 +38,7 @@ public function login(AuthenticationUtils $authenticationUtils): Response 'last_username' => $lastUsername, 'error' => $error, 'csrf_token_intention' => 'authenticate', - 'target_path' => $this->generateUrl('admin_dashboard'), + 'target_path' => $this->generateUrl('admin'), 'forgot_password_enabled' => true, 'forgot_password_path' => $this->generateUrl('app_forgot_password_request'), From 902d3838b5295c783bc5b0019df1a7e3f2e99269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 10 Nov 2024 14:37:06 +0100 Subject: [PATCH 5/8] Allow to check payment orders factually and mathematically using the new system --- .../Admin/PaymentOrderCrudController.php | 38 +----- ...r.php => PaymentOrderHelperController.php} | 66 +++++++++- .../PaymentOrderChangedFieldsListener.php | 2 +- src/Services/PaymentOrder/CheckHelper.php | 118 ++++++++++++++++++ templates/admin/payment_order/check.html.twig | 17 +++ .../payment_order/manual_confirm.html.twig | 26 +--- .../admin/payment_order/tables.macro.twig | 32 +++++ translations/messages.de.xlf | 6 + 8 files changed, 243 insertions(+), 62 deletions(-) rename src/Controller/Admin/{PaymentOrderManualConfirmController.php => PaymentOrderHelperController.php} (53%) create mode 100644 src/Services/PaymentOrder/CheckHelper.php create mode 100644 templates/admin/payment_order/check.html.twig create mode 100644 templates/admin/payment_order/tables.macro.twig diff --git a/src/Controller/Admin/PaymentOrderCrudController.php b/src/Controller/Admin/PaymentOrderCrudController.php index 9fc5c87..a8b376f 100644 --- a/src/Controller/Admin/PaymentOrderCrudController.php +++ b/src/Controller/Admin/PaymentOrderCrudController.php @@ -199,40 +199,6 @@ public function resendConfirmationEmail(AdminContext $context): Response return $this->redirect($context->getReferrer() ?? '/admin'); } - /** - * Handler for action if user click "check mathematically" button in admin page. - */ - public function checkMathematicallyCorrect(AdminContext $context): Response - { - $this->denyAccessUnlessGranted('ROLE_PO_MATHEMATICALLY'); - - /** @var PaymentOrder $payment_order */ - $payment_order = $context->getEntity() - ->getInstance(); - $payment_order->setMathematicallyCorrect(true); - $this->entityManager->flush(); - $this->addFlash('success', 'payment_order.action.mathematically_correct.success'); - - return $this->redirect($context->getReferrer() ?? '/admin'); - } - - /** - * Handler for action if user click "check factually" button in admin page. - */ - public function checkFactuallyCorrect(AdminContext $context): Response - { - $this->denyAccessUnlessGranted('ROLE_PO_FACTUALLY'); - - /** @var PaymentOrder $payment_order */ - $payment_order = $context->getEntity() - ->getInstance(); - $payment_order->setFactuallyCorrect(true); - $this->entityManager->flush(); - $this->addFlash('success', 'payment_order.action.factually_correct.success'); - - return $this->redirect($context->getReferrer() ?? '/admin'); - } - public function configureAssets(Assets $assets): Assets { return $assets @@ -335,14 +301,14 @@ public function configureActions(Actions $actions): Actions ->setCssClass('btn btn-secondary text-dark'); $mathematically_correct_action = Action::new('mathematicallyCorrect', 'payment_order.action.mathematically_correct', 'fas fa-check') - ->linkToCrudAction('checkMathematicallyCorrect') + ->linkToRoute('payment_order_check', fn (PaymentOrder $paymentOrder) => ['type' => 'mathematically_correct', 'id' => $paymentOrder->getId()]) ->displayIf(fn(PaymentOrder $paymentOrder): bool => $this->isGranted('ROLE_PO_MATHEMATICALLY') && $paymentOrder->isConfirmed() && !$paymentOrder->isMathematicallyCorrectChecked()) ->setCssClass('btn btn-success'); $factually_correct_action = Action::new('factuallyCorrect', 'payment_order.action.factually_correct', 'fas fa-check') - ->linkToCrudAction('checkFactuallyCorrect') + ->linkToRoute('payment_order_check', fn (PaymentOrder $paymentOrder) => ['type' => 'factually_correct', 'id' => $paymentOrder->getId()]) ->displayIf(fn(PaymentOrder $paymentOrder): bool => $this->isGranted('ROLE_PO_FACTUALLY') && $paymentOrder->isConfirmed() && !$paymentOrder->isFactuallyCorrectChecked() diff --git a/src/Controller/Admin/PaymentOrderManualConfirmController.php b/src/Controller/Admin/PaymentOrderHelperController.php similarity index 53% rename from src/Controller/Admin/PaymentOrderManualConfirmController.php rename to src/Controller/Admin/PaymentOrderHelperController.php index 9560064..b23533b 100644 --- a/src/Controller/Admin/PaymentOrderManualConfirmController.php +++ b/src/Controller/Admin/PaymentOrderHelperController.php @@ -21,16 +21,26 @@ use App\Entity\PaymentOrder; use App\Form\PaymentOrderManualConfirmationType; use App\Services\EmailConfirmation\ManualConfirmationHelper; +use App\Services\PaymentOrder\CheckHelper; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +/** + * This controller contains various helping methods for the PaymentOrder entity. + */ #[Route(path: '/admin/payment_order')] -final class PaymentOrderManualConfirmController extends AbstractController +final class PaymentOrderHelperController extends AbstractController { + public function __construct(private readonly EntityManagerInterface $entityManager) + { + } + #[Route(path: '/{id}/confirm', name: 'payment_order_manual_confirm')] public function manualConfirmation( PaymentOrder $paymentOrder, @@ -69,4 +79,58 @@ public function manualConfirmation( 'form' => $form->createView(), ]); } + + /** + * This method show a detail form where the user can check the factually_correct or mathematically_correct field + * with a optional remark + * @param PaymentOrder $paymentOrder + * @param string $type + * @param Request $request + * @return void + */ + #[Route(path: '/{id}/check/{type}', name: 'payment_order_check')] + public function doCheck( + PaymentOrder $paymentOrder, + string $type, + Request $request, + CheckHelper $checkHelper, + + ): Response { + //Ensure that the type is valid + if (!in_array($type, CheckHelper::ALLOWED_FIELDS, true)) { + throw $this->createNotFoundException(); + } + + //Check if the current user is allowed to check the field + if ($type === CheckHelper::FACTUALLY_CORRECT) { + $this->denyAccessUnlessGranted('ROLE_PO_FACTUALLY'); + } + if ($type === CheckHelper::MATHEMATICALLY_CORRECT) { + $this->denyAccessUnlessGranted('ROLE_PO_MATHEMATICALLY'); + } + + //Create the form + $form = $this->createFormBuilder() + ->add('remark', TextareaType::class, ['required' => false, 'label' => 'Anmerkung']) + ->add('submit', SubmitType::class, ['label' => 'Prüfen']) + ->getForm(); + + $form->handleRequest($request); + if ($form->isSubmitted() && $form->isValid()) { + $remark = $form->get('remark')->getData(); + $checkHelper->check($paymentOrder, $type, $remark); + + $this->entityManager->flush(); + + $this->addFlash('success', 'payment_order.check.success'); + + return $this->redirectToRoute('admin_payment_order_detail', ['entityId' => $paymentOrder->getId()]); + } + + return $this->render('admin/payment_order/check.html.twig', [ + 'entity' => $paymentOrder, + 'type' => $type, + 'form' => $form->createView(), + ]); + } } diff --git a/src/EntityListener/PaymentOrderChangedFieldsListener.php b/src/EntityListener/PaymentOrderChangedFieldsListener.php index 8097ea3..96e1202 100644 --- a/src/EntityListener/PaymentOrderChangedFieldsListener.php +++ b/src/EntityListener/PaymentOrderChangedFieldsListener.php @@ -50,7 +50,7 @@ public function __construct(private readonly Security $security, private readonl public function preUpdate(PaymentOrder $paymentOrder, PreUpdateEventArgs $eventArgs): void { - //Ensure that we have an logged in user. We do not track other changes + //Ensure that we have a logged-in user. We do not track other changes $user = $this->security->getUser(); if (!$user instanceof User) { return; diff --git a/src/Services/PaymentOrder/CheckHelper.php b/src/Services/PaymentOrder/CheckHelper.php new file mode 100644 index 0000000..fbd2714 --- /dev/null +++ b/src/Services/PaymentOrder/CheckHelper.php @@ -0,0 +1,118 @@ +security->isGranted('ROLE_PO_FACTUALLY')) { + throw new AccessDeniedException('You are not allowed to check this field'); + } + if ($field === self::MATHEMATICALLY_CORRECT && !$this->security->isGranted('ROLE_PO_MATHEMATICALLY')) { + throw new AccessDeniedException('You are not allowed to check this field'); + } + + //Retrieve the check object + $check = $field === self::FACTUALLY_CORRECT ? $paymentOrder->getFactuallyCorrect() : $paymentOrder->getMathematicallyCorrect(); + + //Ensure that check is not already checked + if ($check->isChecked()) { + throw new \LogicException('This field is already checked. Use uncheck() to uncheck it'); + } + + //Retrieve the user object + $user = $this->security->getUser(); + if (!$user instanceof User) { + throw new \LogicException('This method only works with logged in users'); + } + + //Fill in the data from the currently logged in user + $check->setChecked(true) + ->setConfirmerID($user->getId()) + ->setConfirmerName($user->getFullName()) + ->setTimestamp(new \DateTime()) + ->setRemark($remark); + + //If the field is factually_correct, mark the paymentOrder as booked + if ($field === self::FACTUALLY_CORRECT) { + if ($paymentOrder->getBookingDate() === null) { + $paymentOrder->setBookingDate(new \DateTime()); + } + } + + //TODO: Add an event if both checks are checked + } + + /** + * Uncheck the given field of the given PaymentOrder (factually_correct or mathematically_correct) and remove the data + * from the currently logged in user. If the field is not allowed, an exception is thrown. + * @param PaymentOrder $paymentOrder + * @param string $field + * @return void + */ + public function uncheck(PaymentOrder $paymentOrder, string $field): void + { + if (!in_array($field, self::ALLOWED_FIELDS, true)) { + throw new \InvalidArgumentException('Invalid field'); + } + + //Check if the current user is allowed to check the field + if ($field === self::FACTUALLY_CORRECT && !$this->security->isGranted('ROLE_PO_FACTUALLY')) { + throw new AccessDeniedException('You are not allowed to check this field'); + } + if ($field === self::MATHEMATICALLY_CORRECT && !$this->security->isGranted('ROLE_PO_MATHEMATICALLY')) { + throw new AccessDeniedException('You are not allowed to check this field'); + } + + //Retrieve the check object + $check = $field === self::FACTUALLY_CORRECT ? $paymentOrder->getFactuallyCorrect() : $paymentOrder->getMathematicallyCorrect(); + + //Remove all data from the check object + $check->setChecked(false) + ->setConfirmerID(null) + ->setConfirmerName(null) + ->setTimestamp(null) + ->setRemark(null); + } +} \ No newline at end of file diff --git a/templates/admin/payment_order/check.html.twig b/templates/admin/payment_order/check.html.twig new file mode 100644 index 0000000..6cd7c6c --- /dev/null +++ b/templates/admin/payment_order/check.html.twig @@ -0,0 +1,17 @@ +{% extends "@EasyAdmin/page/content.html.twig" %} +{% form_theme form "@EasyAdmin/crud/form_theme.html.twig" %} +{% import "admin/payment_order/tables.macro.twig" as tables %} + +{% block page_title %} + {% if type == 'factually_correct' %}Sachliche{% else %}Rechnerische{% endif %} + Prüfung von {{ entity.iDString }}{% endblock %} + +{% block page_content %} + + {{ tables.paymentOrder(entity) }} + +

Es kann eine optionale Anmerkung zur Prüfung angegeben werden

+ + {{ form_start(form) }} + {{ form_end(form) }} +{% endblock %} \ No newline at end of file diff --git a/templates/admin/payment_order/manual_confirm.html.twig b/templates/admin/payment_order/manual_confirm.html.twig index aed0a8f..f9a9e8d 100644 --- a/templates/admin/payment_order/manual_confirm.html.twig +++ b/templates/admin/payment_order/manual_confirm.html.twig @@ -1,5 +1,6 @@ {% extends "@EasyAdmin/page/content.html.twig" %} {% form_theme form "@EasyAdmin/crud/form_theme.html.twig" %} +{% import "admin/payment_order/tables.macro.twig" as tables %} {% block page_title %}{% trans %}payment_order.manual_confirmation.title{% endtrans %} {{ entity.iDString }}{% endblock %} @@ -12,30 +13,7 @@

{% trans %}payment_order.manual_confirmation.additional_notifications_receiver{% endtrans %} {{ notifications_risky | join(', ') }}

{% endif %} - - - - - - - - - - - - - - - - - - - - - - - -
{% trans %}payment_order.notification_user.department{% endtrans %}:{{ entity.department.name }}
{% trans %}payment_order.notification_user.project_name{% endtrans %}: {{ entity.projectName }}
{% trans %}payment_order.notification_user.amount{% endtrans %}: {{ entity.amountString }} €
{% trans %}payment_order.notification_user.datetime{% endtrans %}{{ entity.creationDate | format_datetime }}
{% trans %}payment_order.notification_user.id{% endtrans %}: {{ entity.iDString }}
+ {{ tables.paymentOrder(entity) }}

{% trans %}payment_order.manual_confirmation.explanation3{% endtrans %}

diff --git a/templates/admin/payment_order/tables.macro.twig b/templates/admin/payment_order/tables.macro.twig new file mode 100644 index 0000000..fefcdd2 --- /dev/null +++ b/templates/admin/payment_order/tables.macro.twig @@ -0,0 +1,32 @@ +{% macro paymentOrder(entity) %} + {# @var \App\Entity\PaymentOrder entity #} + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{% trans %}payment_order.notification_user.department{% endtrans %}:{{ entity.department.name }}
{% trans %}payment_order.notification_user.project_name{% endtrans %}: {{ entity.projectName }}
{% trans %}payment_order.notification_user.amount{% endtrans %}: {{ entity.amountString }} €
Mittelfreigabe{{ entity.fundingId }} {% if entity.resolutionDate %}({{ entity.resolutionDate | format_date }}){% endif %}
{% trans %}payment_order.notification_user.datetime{% endtrans %}{{ entity.creationDate | format_datetime }}
{% trans %}payment_order.notification_user.id{% endtrans %}: {{ entity.iDString }}
+{% endmacro %} \ No newline at end of file diff --git a/translations/messages.de.xlf b/translations/messages.de.xlf index 117bffc..adb1fd0 100644 --- a/translations/messages.de.xlf +++ b/translations/messages.de.xlf @@ -2609,5 +2609,11 @@ Wenn du diese überschreiben möchtest, lösche die bestehende Vorlage und speic Beschlussdatum unterstützende Mittelfreigabe + + + payment_order.check.success + Zahlungsauftrag wurde erfolgreich geprüft + + From 25ba1a3bf3007cd01b97c4e4740674993fa53035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 10 Nov 2024 19:10:27 +0100 Subject: [PATCH 6/8] Show infos about checks in payment order info page --- src/Admin/Field/CheckField.php | 23 +++++++++++++++++++ .../Admin/PaymentOrderCrudController.php | 5 ++-- templates/admin/field/check.html.twig | 19 +++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 src/Admin/Field/CheckField.php create mode 100644 templates/admin/field/check.html.twig diff --git a/src/Admin/Field/CheckField.php b/src/Admin/Field/CheckField.php new file mode 100644 index 0000000..36bebde --- /dev/null +++ b/src/Admin/Field/CheckField.php @@ -0,0 +1,23 @@ +setProperty($propertyName) + ->setLabel($label) + ->setTemplatePath('admin/field/check.html.twig') + ->setTextAlign('center'); + } +} \ No newline at end of file diff --git a/src/Controller/Admin/PaymentOrderCrudController.php b/src/Controller/Admin/PaymentOrderCrudController.php index a8b376f..f37c8d1 100644 --- a/src/Controller/Admin/PaymentOrderCrudController.php +++ b/src/Controller/Admin/PaymentOrderCrudController.php @@ -18,6 +18,7 @@ namespace App\Controller\Admin; +use App\Admin\Field\CheckField; use App\Admin\Field\ConfirmationField; use App\Admin\Field\FieldChangesField; use App\Admin\Field\VichyFileField; @@ -491,9 +492,9 @@ public function configureFields(string $pageName): iterable FormField::addColumn(), FormField::addPanel('payment_order.section.status.review'), - $mathematicallyCorrect, + CheckField::new('mathematically_correct', 'payment_order.mathematically_correct.label'), + CheckField::new('factually_correct', 'payment_order.factually_correct.label'), $exported, - $factuallyCorrect, $booking_date, $references_exported, diff --git a/templates/admin/field/check.html.twig b/templates/admin/field/check.html.twig new file mode 100644 index 0000000..9c4977e --- /dev/null +++ b/templates/admin/field/check.html.twig @@ -0,0 +1,19 @@ +{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #} +{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #} +{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #} + +{# @var check \App\Entity\Embeddable\Check #} + +{% set check = field.value %} +{% if not check.checked %} + Noch nicht geprüft +{% else %} + Geprüft
+ {{ check.timestamp|format_datetime }}
+ {{ check.confirmerName }}
+ {% if check.remark %} + + {{ check.remark }} + + {% endif %} +{% endif %} \ No newline at end of file From 566da6b068438fbc20ce195d34b3028e3bb03a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 10 Nov 2024 19:36:33 +0100 Subject: [PATCH 7/8] Added an event subscriber to fill in the missing data for checks if just the check field is edited via EasyAdminBundle --- .../PaymentOrderCheckAddInfoSubscriber.php | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/EventSubscriber/PaymentOrderCheckAddInfoSubscriber.php diff --git a/src/EventSubscriber/PaymentOrderCheckAddInfoSubscriber.php b/src/EventSubscriber/PaymentOrderCheckAddInfoSubscriber.php new file mode 100644 index 0000000..f742925 --- /dev/null +++ b/src/EventSubscriber/PaymentOrderCheckAddInfoSubscriber.php @@ -0,0 +1,77 @@ +getEntityInstance(); + + //Only trigger for PaymentOrder entities + if (!($paymentOrder instanceof PaymentOrder)) { + return; + } + + //Check if the checked state of the "factually_correct" or "mathematically_correct" fields have changed + $old_data = $this->entityManager->getUnitOfWork()->getOriginalEntityData($paymentOrder); + + //If the "factually_checked" field has changed + if ($old_data['factually_correct.checked'] !== $paymentOrder->getFactuallyCorrect()->isChecked()) { + //Perform check if required (was not filled out before + if ($paymentOrder->getFactuallyCorrect()->isChecked() && $paymentOrder->getFactuallyCorrect()->getTimestamp() === null) { + //Uncheck the field temporarily to avoid the exception thrown by the checkHelper, if already checked + $paymentOrder->getFactuallyCorrect()->setChecked(false); + $this->checkHelper->check($paymentOrder, 'factually_correct'); + } + //Perform uncheck if required + elseif (!$paymentOrder->getFactuallyCorrect()->isChecked() && $paymentOrder->getFactuallyCorrect()->getTimestamp() !== null) { + //Check the field temporarily to avoid the exception thrown by the checkHelper, if already unchecked + $paymentOrder->getFactuallyCorrect()->setChecked(true); + $this->checkHelper->uncheck($paymentOrder, 'factually_correct'); + } + } + + //Do the same for the mathematically_correct field + if ($old_data['mathematically_correct.checked'] !== $paymentOrder->getMathematicallyCorrect()->isChecked()) { + //Perform check if required (was not filled out before + if ($paymentOrder->getMathematicallyCorrect()->isChecked() && $paymentOrder->getMathematicallyCorrect()->getTimestamp() === null) { + //Uncheck the field temporarily to avoid the exception thrown by the checkHelper, if already checked + $paymentOrder->getMathematicallyCorrect()->setChecked(false); + $this->checkHelper->check($paymentOrder, 'mathematically_correct'); + } + //Perform uncheck if required + elseif (!$paymentOrder->getMathematicallyCorrect()->isChecked() && $paymentOrder->getMathematicallyCorrect()->getTimestamp() !== null) { + //Check the field temporarily to avoid the exception thrown by the checkHelper, if already unchecked + $paymentOrder->getMathematicallyCorrect()->setChecked(true); + $this->checkHelper->uncheck($paymentOrder, 'mathematically_correct'); + } + } + } + + public static function getSubscribedEvents(): array + { + return [ + BeforeEntityUpdatedEvent::class => ['addCheckInfoToPaymentOrder'] + ]; + } +} \ No newline at end of file From bcce02584f65c241954a4dae12da8ebcc519e6f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 10 Nov 2024 19:44:04 +0100 Subject: [PATCH 8/8] Replaced call to deprecated function addPanel --- src/Controller/Admin/PaymentOrderCrudController.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Controller/Admin/PaymentOrderCrudController.php b/src/Controller/Admin/PaymentOrderCrudController.php index f37c8d1..a6f34a8 100644 --- a/src/Controller/Admin/PaymentOrderCrudController.php +++ b/src/Controller/Admin/PaymentOrderCrudController.php @@ -399,7 +399,7 @@ public function configureFields(string $pageName): iterable //$creationDate = TextField::new('creation_date', 'creation_date'); //Status informations - $statusPanel = FormField::addPanel('payment_order.group.status'); + $statusPanel = FormField::addFieldset('payment_order.group.status'); $mathematicallyCorrect = BooleanField::new('mathematically_correct.checked', 'payment_order.mathematically_correct.label') ->setHelp('payment_order.mathematically_correct.help') //Disable fields (and show coloumns as read only tags) if user does not have proper permissions to change @@ -421,14 +421,14 @@ public function configureFields(string $pageName): iterable $references_exported = BooleanField::new('references_exported', 'payment_order.references_exported.label'); //Payee informations - $payeePanel = FormField::addPanel('payment_order.group.receiver'); + $payeePanel = FormField::addFieldset('payment_order.group.receiver'); $bankInfoAccountOwner = TextField::new('bank_info.account_owner', 'bank_info.account_owner.label'); $bankInfoStreet = TextField::new('bank_info.street', 'bank_info.street.label'); $bankInfoZipCode = TextField::new('bank_info.zip_code', 'bank_info.zip_code.label'); $bankInfoCity = TextField::new('bank_info.city', 'bank_info.city.label'); //Payee bank account infos - $bankInfoPanel = FormField::addPanel('payment_order.group.bank_info'); + $bankInfoPanel = FormField::addFieldset('payment_order.group.bank_info'); $bankInfoIban = TextField::new('bank_info.iban', 'bank_info.iban.label'); $bankInfoBic = TextField::new('bank_info.bic', 'bank_info.bic.label') ->setRequired(false) @@ -484,21 +484,21 @@ public function configureFields(string $pageName): iterable FormField::addTab('payment_order.tab.status', 'fas fa-list-check'), //Status infos FormField::addColumn(), - FormField::addPanel('payment_order.section.status.confirmation'), + FormField::addFieldset('payment_order.section.status.confirmation'), BooleanField::new('confirmed', 'payment_order.confirmed.label'), $requiredConfirmations, $confirmation1, $confirmation2, FormField::addColumn(), - FormField::addPanel('payment_order.section.status.review'), + FormField::addFieldset('payment_order.section.status.review'), CheckField::new('mathematically_correct', 'payment_order.mathematically_correct.label'), CheckField::new('factually_correct', 'payment_order.factually_correct.label'), $exported, $booking_date, $references_exported, - FormField::addPanel('payment_order.section.edited_fields'), + FormField::addFieldset('payment_order.section.edited_fields'), FieldChangesField::new('field_changes', ""), ];