From fd043536d1bd1e34044cbec3e99257c20e36460c Mon Sep 17 00:00:00 2001 From: Tom Udding Date: Sat, 20 Jul 2024 18:55:48 +0200 Subject: [PATCH 1/2] fix: comparisons for discharge/abrogation dates --- module/Decision/src/Mapper/Member.php | 28 ++++++++++---- module/Decision/src/Mapper/Organ.php | 38 +++++++++++++------ module/Decision/src/Model/Member.php | 6 +-- .../Permissions/Assertion/IsOrganMember.php | 2 +- 4 files changed, 50 insertions(+), 24 deletions(-) diff --git a/module/Decision/src/Mapper/Member.php b/module/Decision/src/Mapper/Member.php index 3071ba2795..4760049f29 100644 --- a/module/Decision/src/Mapper/Member.php +++ b/module/Decision/src/Mapper/Member.php @@ -5,6 +5,7 @@ namespace Decision\Mapper; use Application\Mapper\BaseMapper; +use DateTime; use Decision\Model\Member as MemberModel; use Decision\Model\Organ as OrganModel; use Decision\Model\OrganMember as OrganMemberModel; @@ -128,9 +129,13 @@ public function findOrgans(MemberModel $member): array ->join('o.members', 'om') ->join('om.member', 'm') ->where('m.lidnr = :lidnr') - ->andWhere('om.dischargeDate IS NULL'); + ->andWhere($qb->expr()->orX( + $qb->expr()->isNull('om.dischargeDate'), + $qb->expr()->gt('om.dischargeDate', ':now') + )); - $qb->setParameter('lidnr', $member->getLidnr()); + $qb->setParameter('lidnr', $member->getLidnr()) + ->setParameter('now', new DateTime()); return $qb->getQuery()->getResult(); } @@ -147,10 +152,14 @@ public function findCurrentInstallations(MemberModel $member): array ->from(OrganMemberModel::class, 'om') ->leftJoin('om.organ', 'o') ->where('om.member = :member') - ->andWhere('om.installDate <= CURRENT_TIMESTAMP()') - ->andWhere('om.dischargeDate IS NULL OR om.dischargeDate > CURRENT_TIMESTAMP()'); + ->andWhere('om.installDate <= :now') + ->andWhere($qb->expr()->orX( + $qb->expr()->isNull('om.dischargeDate'), + $qb->expr()->gt('om.dischargeDate', ':now') + )); - $qb->setParameter('member', $member); + $qb->setParameter('member', $member) + ->setParameter('now', new DateTime()); return $qb->getQuery()->getResult(); } @@ -167,10 +176,13 @@ public function findHistoricalInstallations(MemberModel $member): array ->from(OrganMemberModel::class, 'om') ->leftJoin('om.organ', 'o') ->where('om.member = :member') - ->andWhere('om.dischargeDate IS NOT NULL') - ->andWhere('om.dischargeDate <= CURRENT_TIMESTAMP()'); + ->andWhere($qb->expr()->andX( + $qb->expr()->isNotNull('om.dischargeDate'), + $qb->expr()->lte('om.dischargeDate', ':now') + )); - $qb->setParameter('member', $member); + $qb->setParameter('member', $member) + ->setParameter('now', new DateTime()); return $qb->getQuery()->getResult(); } diff --git a/module/Decision/src/Mapper/Organ.php b/module/Decision/src/Mapper/Organ.php index 5d4b6b9be7..adbdbbcc41 100644 --- a/module/Decision/src/Mapper/Organ.php +++ b/module/Decision/src/Mapper/Organ.php @@ -5,6 +5,7 @@ namespace Decision\Mapper; use Application\Mapper\BaseMapper; +use DateTime; use Decision\Model\Enums\OrganTypes; use Decision\Model\Organ as OrganModel; use Doctrine\ORM\NonUniqueResultException; @@ -26,15 +27,19 @@ class Organ extends BaseMapper */ public function findActive(?OrganTypes $type = null): array { - $criteria = [ - 'abrogationDate' => null, - ]; + $qb = $this->getRepository()->createQueryBuilder('o'); + $qb->where($qb->expr()->orX( + $qb->expr()->isNull('o.abrogationDate'), + $qb->expr()->gt('o.abrogationDate', ':now'), + )) + ->setParameter('now', new DateTime()); if (null !== $type) { - $criteria['type'] = $type; + $qb->andWhere('o.type = :type') + ->setParameter('type', $type); } - return $this->getRepository()->findBy($criteria); + return $qb->getQuery()->getResult(); } /** @@ -42,12 +47,17 @@ public function findActive(?OrganTypes $type = null): array */ public function findActiveById(int $id): ?OrganModel { - return $this->getRepository()->findOneBy( - [ - 'id' => $id, - 'abrogationDate' => null, - ], - ); + $qb = $this->getRepository()->createQueryBuilder('o'); + $qb->where('o.id = :id') + ->andWhere($qb->expr()->orX( + $qb->expr()->isNull('o.abrogationDate'), + $qb->expr()->gt('o.abrogationDate', ':now') + )); + + $qb->setParameter('id', $id) + ->setParameter('now', new DateTime()); + + return $qb->getQuery()->getOneOrNullResult(); } /** @@ -58,7 +68,11 @@ public function findActiveById(int $id): ?OrganModel public function findAbrogated(?OrganTypes $type = null): array { $qb = $this->getRepository()->createQueryBuilder('o'); - $qb->where('o.abrogationDate IS NOT NULL') + $qb->where($qb->expr()->andX( + $qb->expr()->isNotNull('o.abrogationDate'), + $qb->expr()->lte('o.abrogationDate', ':now') + )) + ->setParameter('now', new DateTime()) ->orderBy('o.abrogationDate', 'DESC'); if (null !== $type) { diff --git a/module/Decision/src/Model/Member.php b/module/Decision/src/Model/Member.php index a53733a161..50350be289 100644 --- a/module/Decision/src/Model/Member.php +++ b/module/Decision/src/Model/Member.php @@ -668,7 +668,7 @@ static function (BoardMember $boardMember) use ($today) { $dischargeDate = $boardMember->getDischargeDate(); // Keep installation if not discharged or discharged in the future - return null === $dischargeDate || $dischargeDate >= $today; + return null === $dischargeDate || $dischargeDate > $today; }, ); @@ -855,12 +855,12 @@ protected function isCurrentBoard(BoardMember $boardMember): bool // Installation was (before) today. if ( null === $releaseDate - || $releaseDate >= $now + || $releaseDate > $now ) { // Not yet released or the release is the in the future. if ( null === $dischargeDate - || $dischargeDate >= $now + || $dischargeDate > $now ) { // Not yet discharged or the discharge is in the future. return true; diff --git a/module/User/src/Permissions/Assertion/IsOrganMember.php b/module/User/src/Permissions/Assertion/IsOrganMember.php index 8a791f894c..057b14fb67 100644 --- a/module/User/src/Permissions/Assertion/IsOrganMember.php +++ b/module/User/src/Permissions/Assertion/IsOrganMember.php @@ -68,6 +68,6 @@ protected function isCurrentMember(OrganMember $organMember): bool $now = new DateTime(); return $organMember->getInstallDate() <= $now && - (null === $organMember->getDischargeDate() || $organMember->getDischargeDate() >= $now); + (null === $organMember->getDischargeDate() || $organMember->getDischargeDate() > $now); } } From ca1687518c4227ed4a7404820e5054cdf7e60517 Mon Sep 17 00:00:00 2001 From: Tom Udding Date: Wed, 11 Sep 2024 16:30:35 +0200 Subject: [PATCH 2/2] DRAFT: feat: improved --- .../src/Controller/ActivityController.php | 4 +- .../src/Controller/AdminController.php | 10 +- module/Activity/src/Model/Activity.php | 2 +- module/Activity/src/Service/Activity.php | 6 +- .../Activity/src/Service/ActivityCalendar.php | 2 +- .../admin-approval/view-proposal.phtml | 4 +- .../view/activity/admin-approval/view.phtml | 4 +- .../Activity/view/activity/admin/list.phtml | 6 +- .../src/Model/Enums/ApprovableStatus.php | 9 + .../src/Model/Traits/ApprovableTrait.php | 16 +- .../Model/Traits/UpdateProposableTrait.php | 10 + module/Application/view/partial/admin.phtml | 94 ++++++---- .../Controller/CompanyAccountController.php | 2 +- module/Company/src/Model/Company.php | 9 +- module/Company/src/Model/Job.php | 1 + .../Company/src/Model/Proposals/JobUpdate.php | 12 +- module/Company/src/Service/Company.php | 2 +- .../view/company/admin-approval/index.phtml | 2 +- .../company/admin-approval/job-approval.phtml | 6 +- .../company/admin-approval/job-proposal.phtml | 6 +- .../view/company/admin/edit-company.phtml | 4 +- .../view/company/admin/edit-package.phtml | 5 +- .../view/company/company-account/jobs.phtml | 6 +- module/Decision/config/module.config.php | 77 +++++++- .../OrganAdminApprovalControllerFactory.php | 29 +++ .../Factory/OrganAdminControllerFactory.php | 3 + .../OrganAdminApprovalController.php | 67 +++++++ .../src/Controller/OrganAdminController.php | 80 +++++++- module/Decision/src/Form/OrganInformation.php | 28 +-- .../src/Hydrator/OrganInformation.php | 65 +++++++ module/Decision/src/Mapper/Member.php | 45 ++++- .../Decision/src/Mapper/OrganInformation.php | 45 +++++ .../src/Model/DecisionLocalisedText.php | 16 ++ module/Decision/src/Model/Organ.php | 55 +++++- .../Decision/src/Model/OrganInformation.php | 141 +++++++------- .../Proposals/OrganInformationUpdate.php | 76 ++++++++ module/Decision/src/Module.php | 14 +- module/Decision/src/Service/AclService.php | 9 +- module/Decision/src/Service/Organ.php | 173 ++++++++++-------- .../organ-admin-approval/approval.phtml | 0 .../decision/organ-admin-approval/index.phtml | 0 .../organ-admin-approval/proposal.phtml | 0 .../view/decision/organ-admin/edit.phtml | 135 +++++++++----- .../view/decision/organ-admin/index.phtml | 26 +-- .../view/frontpage/organ/organ.phtml | 41 ++--- .../Frontpage/view/partial/organ-card.phtml | 2 +- 46 files changed, 996 insertions(+), 353 deletions(-) create mode 100644 module/Decision/src/Controller/Factory/OrganAdminApprovalControllerFactory.php create mode 100644 module/Decision/src/Controller/OrganAdminApprovalController.php create mode 100644 module/Decision/src/Hydrator/OrganInformation.php create mode 100644 module/Decision/src/Mapper/OrganInformation.php create mode 100644 module/Decision/src/Model/DecisionLocalisedText.php create mode 100644 module/Decision/src/Model/Proposals/OrganInformationUpdate.php create mode 100644 module/Decision/view/decision/organ-admin-approval/approval.phtml create mode 100644 module/Decision/view/decision/organ-admin-approval/index.phtml create mode 100644 module/Decision/view/decision/organ-admin-approval/proposal.phtml diff --git a/module/Activity/src/Controller/ActivityController.php b/module/Activity/src/Controller/ActivityController.php index 99f7ca476d..47fb3747d8 100644 --- a/module/Activity/src/Controller/ActivityController.php +++ b/module/Activity/src/Controller/ActivityController.php @@ -79,7 +79,7 @@ public function viewAction(): mixed } // If the Activity has a sign-up list always display it by redirecting the request. - if (0 !== $activity->getSignupLists()->count()) { + if (!$activity->getSignupLists()->isEmpty()) { return $this->forward()->dispatch( self::class, [ @@ -263,7 +263,7 @@ public function signupAction(): Response|ViewModel // Let user edit subscription details if (null !== ($signup = $this->signupMapper->getSignUp($signupList, $identity))) { - if (0 === $signupList->getFields()->count()) { + if ($signupList->getFields()->isEmpty()) { return $this->redirect()->toRoute( 'activity/view/signuplist', [ diff --git a/module/Activity/src/Controller/AdminController.php b/module/Activity/src/Controller/AdminController.php index ad38b2201b..c54c6156f5 100644 --- a/module/Activity/src/Controller/AdminController.php +++ b/module/Activity/src/Controller/AdminController.php @@ -96,7 +96,7 @@ public function updateAction(): Response|ViewModel throw new NotAllowedException($this->translator->translate('You are not allowed to update this activity')); } - if (0 !== $activity->getSignupLists()->count()) { + if (!$activity->getSignupLists()->isEmpty()) { $openingDates = []; $participants = 0; @@ -144,11 +144,11 @@ public function updateAction(): Response|ViewModel } } - $updateProposal = $activity->getUpdateProposal(); + $updateProposals = $activity->getUpdateProposals(); - if (0 !== $updateProposal->count()) { + if (!$updateProposals->isEmpty()) { // If there already is an update proposal for this activity, show that instead of the original activity. - $activity = $updateProposal->first()->getNew(); + $activity = $updateProposals->first()->getNew(); } $activityData = $activity->toArray(); @@ -213,7 +213,7 @@ public function participantsAction(): ViewModel // If the activity does not have any sign-up lists there is no need // to check the participants or any sign-up lists. - if (0 === $activity->getSignupLists()->count()) { + if ($activity->getSignupLists()->isEmpty()) { return $this->notFoundAction(); } diff --git a/module/Activity/src/Model/Activity.php b/module/Activity/src/Model/Activity.php index de69664014..1004ca3fc5 100644 --- a/module/Activity/src/Model/Activity.php +++ b/module/Activity/src/Model/Activity.php @@ -274,7 +274,7 @@ public function setStatus(int $status): void /** * @return Collection */ - public function getUpdateProposal(): Collection + public function getUpdateProposals(): Collection { return $this->updateProposal; } diff --git a/module/Activity/src/Service/Activity.php b/module/Activity/src/Service/Activity.php index bf2a04b738..a92e046914 100644 --- a/module/Activity/src/Service/Activity.php +++ b/module/Activity/src/Service/Activity.php @@ -140,7 +140,7 @@ protected function findOrgan(int $organId): OrganModel { $organ = $this->organService->getOrgan($organId); - if (!$this->organService->canEditOrgan($organ)) { + if (!$this->organService->canUseOrgan($organ)) { throw new NotAllowedException( $this->translator->translate('You are not allowed to create an activity for this organ'), ); @@ -446,8 +446,8 @@ public function createUpdateProposal( $em = $this->entityManager; - if (0 !== $currentActivity->getUpdateProposal()->count()) { - $proposal = $currentActivity->getUpdateProposal()->first(); + if (0 !== $currentActivity->getUpdateProposals()->count()) { + $proposal = $currentActivity->getUpdateProposals()->first(); //Remove old update proposal $oldUpdate = $proposal->getNew(); $proposal->setNew($newActivity); diff --git a/module/Activity/src/Service/ActivityCalendar.php b/module/Activity/src/Service/ActivityCalendar.php index 31ad91d787..5e129c0f0f 100644 --- a/module/Activity/src/Service/ActivityCalendar.php +++ b/module/Activity/src/Service/ActivityCalendar.php @@ -230,7 +230,7 @@ protected function canDeleteOption(ActivityCalendarOptionModel $option): bool $organ = $option->getProposal()->getOrgan(); - return null !== $organ && $this->organService->canEditOrgan($organ); + return null !== $organ && $this->organService->canUseOrgan($organ); } /** diff --git a/module/Activity/view/activity/admin-approval/view-proposal.phtml b/module/Activity/view/activity/admin-approval/view-proposal.phtml index e9577cb3dd..55c1404835 100644 --- a/module/Activity/view/activity/admin-approval/view-proposal.phtml +++ b/module/Activity/view/activity/admin-approval/view-proposal.phtml @@ -240,7 +240,7 @@ $this->breadcrumbs()
- getCategories()->count() !== 0 || $new->getCategories()->count() !== 0): ?> + getCategories()->isEmpty() || !$new->getCategories()->isEmpty()): ?>

translate('Activity Categories') ?>

@@ -267,7 +267,7 @@ $this->breadcrumbs() - getSignupLists()->count() !== 0 || $new->getSignupLists()->count() !== 0): ?> + getSignupLists()->isEmpty() || !$new->getSignupLists()->isEmpty()): ?>

translate('Sign-up Lists') ?>

diff --git a/module/Activity/view/activity/admin-approval/view.phtml b/module/Activity/view/activity/admin-approval/view.phtml index 9d64ec9825..9085f0839e 100644 --- a/module/Activity/view/activity/admin-approval/view.phtml +++ b/module/Activity/view/activity/admin-approval/view.phtml @@ -173,7 +173,7 @@ $this->breadcrumbs()
- getCategories()->count() > 0): ?> + getCategories()->isEmpty()): ?>

translate('Activity Categories') ?>

@@ -187,7 +187,7 @@ $this->breadcrumbs() - getSignupLists()->count() > 0): ?> + getSignupLists()->isEmpty()): ?>

translate('Sign-up Lists') ?>

diff --git a/module/Activity/view/activity/admin/list.phtml b/module/Activity/view/activity/admin/list.phtml index 5d740a1823..fbd3bc3897 100644 --- a/module/Activity/view/activity/admin/list.phtml +++ b/module/Activity/view/activity/admin/list.phtml @@ -46,10 +46,10 @@ use Laminas\View\Renderer\PhpRenderer; getCompany()) ? $this->translate('None') : $this->escapeHtml($activity->getCompany()->getName()) ?> escapeHtml($activity->getCreator()->getFullName()) ?> - getUpdateProposal()->count() === 0 ? '(-)' : - '' . $this->translate('Update pending') . '' ?> + getUpdateProposals()->count() === 0 ? '(-)' : + '' . $this->translate('Update pending') . '' ?> - getUpdateProposal()->count() === 0 ? '(-)' : $this->translate('Update pending') ?> + getUpdateProposals()->count() === 0 ? '(-)' : $this->translate('Update pending') ?> diff --git a/module/Application/src/Model/Enums/ApprovableStatus.php b/module/Application/src/Model/Enums/ApprovableStatus.php index 206be67d22..8853307516 100644 --- a/module/Application/src/Model/Enums/ApprovableStatus.php +++ b/module/Application/src/Model/Enums/ApprovableStatus.php @@ -13,4 +13,13 @@ enum ApprovableStatus: int case Unapproved = 0; case Approved = 1; case Rejected = 2; + + public function getIcon(): string + { + return match ($this) { + self::Unapproved => 'fa-circle-question', + self::Approved => 'fa-circle-check', + self::Rejected => 'fa-circle-xmark', + }; + } } diff --git a/module/Application/src/Model/Traits/ApprovableTrait.php b/module/Application/src/Model/Traits/ApprovableTrait.php index c60bc1a9aa..19a885071a 100644 --- a/module/Application/src/Model/Traits/ApprovableTrait.php +++ b/module/Application/src/Model/Traits/ApprovableTrait.php @@ -70,14 +70,24 @@ enumType: ApprovableStatus::class, )] protected ?ApprovableTextModel $approvableText = null; - public function getApproved(): ApprovableStatus + public function getApproval(): ApprovableStatus { return $this->approved; } public function isApproved(): bool { - return ApprovableStatus::Approved === $this->getApproved(); + return ApprovableStatus::Approved === $this->getApproval(); + } + + public function isRejected(): bool + { + return ApprovableStatus::Rejected === $this->getApproval(); + } + + public function isUnapproved(): bool + { + return ApprovableStatus::Unapproved === $this->getApproval(); } public function setApproved(ApprovableStatus $approved): void @@ -122,7 +132,7 @@ public function toGdprArray(): array { return [ 'id' => $this->getId(), - 'approved' => $this->getApproved()->value, + 'approved' => $this->getApproval()->value, 'approvedAt' => $this->getApprovedAt()?->format(DateTimeInterface::ATOM), 'approvableText' => $this->getApprovableText()?->getMessage(), ]; diff --git a/module/Application/src/Model/Traits/UpdateProposableTrait.php b/module/Application/src/Model/Traits/UpdateProposableTrait.php index 629e563af2..ad8d8b4825 100644 --- a/module/Application/src/Model/Traits/UpdateProposableTrait.php +++ b/module/Application/src/Model/Traits/UpdateProposableTrait.php @@ -4,12 +4,15 @@ namespace Application\Model\Traits; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping\Column; /** * A trait which provides basic (repeated) functionality for proposed update entities. * * TODO: Make activities also use this trait. + * + * @template T of object */ trait UpdateProposableTrait { @@ -42,4 +45,11 @@ public function setIsUpdate(bool $isUpdate): void { $this->isUpdate = $isUpdate; } + + /** + * Get update proposals for this entity. + * + * @psalm-return Collection + */ + abstract public function getUpdateProposals(): Collection; } diff --git a/module/Application/view/partial/admin.phtml b/module/Application/view/partial/admin.phtml index 4023a30623..5b13efe90a 100644 --- a/module/Application/view/partial/admin.phtml +++ b/module/Application/view/partial/admin.phtml @@ -40,31 +40,30 @@ use Laminas\View\Renderer\PhpRenderer; @@ -75,15 +74,13 @@ use Laminas\View\Renderer\PhpRenderer; translate('Career') ?>