From a2c797ed40f5a3e44f061bd91b6d203a0757eed4 Mon Sep 17 00:00:00 2001 From: Tom Udding Date: Sat, 30 Sep 2023 20:39:32 +0200 Subject: [PATCH] Add `Reappointment` subdecision for installations in bodies This adds a new type of organ mutation, a reappointment. This mutation can be used to "prolong" the membership of a member in an organ. The subdecision refers to the original installation, just like a discharge. While a self-reference would have been nice (for consecutive reappointments), it introduces a lot of compexity due to the added recursion. As such, `Reappointment`s have a many-to-one relationship with `Installation`s. `Reappointment`s are processed before `Discharge`s and `Installation`s. In the Organ Mutations UI, only the `Lid` function can be directly reappointed. All other functions can be reappointed by checking the relevant functions in the model. This means that if a function is NOT checked it will not be reappointed, this means that a discharge for this particular function is created. Some of the JavaScript discharge functionality has been moved into a separate function to reduce code duplication. On top of that, the reappointments, discharges, and installations are now also sorted as such in the UI. --- module/Database/src/Form/Install.php | 12 + module/Database/src/Hydrator/Install.php | 19 +- module/Database/src/Model/SubDecision.php | 2 + .../src/Model/SubDecision/Installation.php | 45 ++- .../src/Model/SubDecision/Reappointment.php | 82 +++++ module/Database/src/Module.php | 13 +- module/Database/src/Service/Meeting.php | 52 ++-- .../view/database/meeting/installform.phtml | 290 +++++++++++++----- module/Report/src/Model/SubDecision.php | 2 + .../src/Model/SubDecision/Installation.php | 41 +++ .../src/Model/SubDecision/Reappointment.php | 64 ++++ module/Report/src/Service/Meeting.php | 12 +- psalm/psalm-baseline.xml | 5 + 13 files changed, 526 insertions(+), 113 deletions(-) create mode 100644 module/Database/src/Model/SubDecision/Reappointment.php create mode 100644 module/Report/src/Model/SubDecision/Reappointment.php diff --git a/module/Database/src/Form/Install.php b/module/Database/src/Form/Install.php index eeaad909..d2f01102 100644 --- a/module/Database/src/Form/Install.php +++ b/module/Database/src/Form/Install.php @@ -18,6 +18,7 @@ public function __construct( private readonly Translator $translator, MeetingFieldset $meeting, InstallationFieldset $install, + SubDecisionFieldset $reappointment, SubDecisionFieldset $discharge, SubDecisionFieldset $foundation, ) { @@ -44,6 +45,17 @@ public function __construct( ], ]); + $this->add([ + 'name' => 'reappointments', + 'type' => Collection::class, + 'options' => [ + 'label' => $this->translator->translate('Reappointments'), + 'count' => 1, + 'should_create_template' => true, + 'target_element' => $reappointment, + ], + ]); + $this->add([ 'name' => 'discharges', 'type' => Collection::class, diff --git a/module/Database/src/Hydrator/Install.php b/module/Database/src/Hydrator/Install.php index 8c98a298..0b2901b2 100644 --- a/module/Database/src/Hydrator/Install.php +++ b/module/Database/src/Hydrator/Install.php @@ -7,6 +7,7 @@ use Database\Model\Decision as DecisionModel; use Database\Model\SubDecision\Discharge as DischargeModel; use Database\Model\SubDecision\Installation as InstallationModel; +use Database\Model\SubDecision\Reappointment as ReappointmentModel; use InvalidArgumentException; class Install extends AbstractDecision @@ -34,8 +35,18 @@ public function hydrate( $num = 1; - // first add discharges - if (isset($data['discharges']) && !empty($data['discharges'])) { + // first do reappointments + if (!empty($data['reappointments'])) { + foreach ($data['reappointments'] as $install) { + $reappointment = new ReappointmentModel(); + $reappointment->setInstallation($install); + $reappointment->setNumber($num++); + $reappointment->setDecision($decision); + } + } + + // then add discharges + if (!empty($data['discharges'])) { foreach ($data['discharges'] as $install) { $discharge = new DischargeModel(); $discharge->setInstallation($install); @@ -44,8 +55,8 @@ public function hydrate( } } - // then add installations - if (isset($data['installations']) && !empty($data['installations'])) { + // finally add installations + if (!empty($data['installations'])) { foreach ($data['installations'] as $install) { $installation = new InstallationModel(); $installation->setNumber($num++); diff --git a/module/Database/src/Model/SubDecision.php b/module/Database/src/Model/SubDecision.php index 988343e4..58cf2cbb 100644 --- a/module/Database/src/Model/SubDecision.php +++ b/module/Database/src/Model/SubDecision.php @@ -18,6 +18,7 @@ use Database\Model\SubDecision\Key\Granting as KeyGranting; use Database\Model\SubDecision\Key\Withdrawal as KeyWithdrawal; use Database\Model\SubDecision\Other; +use Database\Model\SubDecision\Reappointment; use Database\Model\SubDecision\Reckoning; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\DiscriminatorColumn; @@ -42,6 +43,7 @@ 'foundation' => Foundation::class, 'abrogation' => Abrogation::class, 'installation' => Installation::class, + 'reappointment' => Reappointment::class, 'discharge' => Discharge::class, 'budget' => Budget::class, 'reckoning' => Reckoning::class, diff --git a/module/Database/src/Model/SubDecision/Installation.php b/module/Database/src/Model/SubDecision/Installation.php index bfe8f90f..2b50e4ad 100644 --- a/module/Database/src/Model/SubDecision/Installation.php +++ b/module/Database/src/Model/SubDecision/Installation.php @@ -5,10 +5,13 @@ namespace Database\Model\SubDecision; use Database\Model\Member; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToOne; +use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\OneToOne; /** @@ -36,6 +39,17 @@ class Installation extends FoundationReference )] protected Member $member; + /** + * Reappointment subdecisions if this installation was prolonged (can be done multiple times). + * + * @var Collection + */ + #[OneToMany( + targetEntity: Reappointment::class, + mappedBy: 'installation', + )] + protected Collection $reappointments; + /** * Discharges. */ @@ -45,6 +59,11 @@ class Installation extends FoundationReference )] protected ?Discharge $discharge = null; + public function __construct() + { + $this->reappointments = new ArrayCollection(); + } + /** * Get the function. */ @@ -78,17 +97,13 @@ public function setMember(Member $member): void } /** - * Get the content. + * Get the reappointments, if they exist. * - * Fixes Bor's greatest frustration + * @return Collection */ - public function getContent(): string + public function getReappointments(): Collection { - $member = $this->getMember()->getFullName(); - $text = $member . ' wordt geïnstalleerd als ' . $this->getFunction(); - $text .= ' van ' . $this->getFoundation()->getAbbr() . '.'; - - return $text; + return $this->reappointments; } /** @@ -98,4 +113,18 @@ public function getDischarge(): ?Discharge { return $this->discharge; } + + /** + * Get the content. + * + * Fixes Bor's greatest frustration + */ + public function getContent(): string + { + $member = $this->getMember()->getFullName(); + $text = $member . ' wordt geïnstalleerd als ' . $this->getFunction(); + $text .= ' van ' . $this->getFoundation()->getAbbr() . '.'; + + return $text; + } } diff --git a/module/Database/src/Model/SubDecision/Reappointment.php b/module/Database/src/Model/SubDecision/Reappointment.php new file mode 100644 index 00000000..e5619a4e --- /dev/null +++ b/module/Database/src/Model/SubDecision/Reappointment.php @@ -0,0 +1,82 @@ +installation; + } + + /** + * Set the original installation for this reappointment. + */ + public function setInstallation(Installation $installation): void + { + $this->installation = $installation; + } + + /** + * Get the textual content of this subdecision. + */ + public function getContent(): string + { + $installation = $this->getInstallation(); + $memberFullName = $installation->getMember()->getFullName(); + + return sprintf( + '%s wordt herbenoemd als %s van %s.', + $memberFullName, + $installation->getFunction(), + $installation->getFoundation()->getAbbr(), + ); + } +} diff --git a/module/Database/src/Module.php b/module/Database/src/Module.php index 978bc070..3a1f16b1 100644 --- a/module/Database/src/Module.php +++ b/module/Database/src/Module.php @@ -85,9 +85,10 @@ use Database\Model\Meeting as MeetingModel; use Database\Model\Member as MemberModel; use Database\Model\SubDecision\Board\Installation as BoardInstallationModel; +use Database\Model\SubDecision\Discharge as DischargeModel; use Database\Model\SubDecision\Foundation as FoundationModel; -use Database\Model\SubDecision\Installation as InstallationModel; use Database\Model\SubDecision\Key\Granting as KeyGrantingModel; +use Database\Model\SubDecision\Reappointment as ReappointmentModel; use Database\Service\Api as ApiService; use Database\Service\Factory\ApiFactory as ApiServiceFactory; use Database\Service\Factory\InstallationFunctionFactory as InstallationFunctionServiceFactory; @@ -245,6 +246,7 @@ public function getServiceConfig(): array $container->get(MvcTranslator::class), $container->get(MeetingFieldset::class), $container->get(InstallationFieldset::class), + $container->get('database_form_fieldset_subdecision_reappointment'), $container->get('database_form_fieldset_subdecision_discharge'), $container->get('database_form_fieldset_subdecision_foundation'), ); @@ -358,10 +360,17 @@ public function getServiceConfig(): array return $fieldset; }, + 'database_form_fieldset_subdecision_reappointment' => static function (ContainerInterface $container) { + $fieldset = new SubDecisionFieldset(); + $fieldset->setHydrator($container->get('database_hydrator_subdecision')); + $fieldset->setObject(new ReappointmentModel()); + + return $fieldset; + }, 'database_form_fieldset_subdecision_discharge' => static function (ContainerInterface $container) { $fieldset = new SubDecisionFieldset(); $fieldset->setHydrator($container->get('database_hydrator_subdecision')); - $fieldset->setObject(new InstallationModel()); + $fieldset->setObject(new DischargeModel()); return $fieldset; }, diff --git a/module/Database/src/Service/Meeting.php b/module/Database/src/Service/Meeting.php index 52524813..0608f610 100644 --- a/module/Database/src/Service/Meeting.php +++ b/module/Database/src/Service/Meeting.php @@ -550,14 +550,14 @@ public function installDecision(array $data): array $decision = $form->getData(); $meeting = $this->getMeeting( MeetingTypes::from($decision['meeting']['type']), - (int) $decision['meeting']['number'], + intval($decision['meeting']['number']), ); $subdecision = $this->getOrganMapper()->findSimple( MeetingTypes::from($decision['subdecision']['meeting_type']), - (int) $decision['subdecision']['meeting_number'], - (int) $decision['subdecision']['decision_point'], - (int) $decision['subdecision']['decision_number'], - (int) $decision['subdecision']['number'], + intval($decision['subdecision']['meeting_number']), + intval($decision['subdecision']['decision_point']), + intval($decision['subdecision']['decision_number']), + intval($decision['subdecision']['number']), ); if ( @@ -573,9 +573,10 @@ public function installDecision(array $data): array $decision['meeting'] = $meeting; $decision['subdecision'] = $subdecision; + // Prepare installations. $installations = []; array_walk($decision['installations'], function ($value) use (&$installations): void { - $member = $this->memberMapper->findSimple((int) $value['member']['lidnr']); + $member = $this->memberMapper->findSimple(intval($value['member']['lidnr'])); if (null === $member) { return; @@ -586,27 +587,28 @@ public function installDecision(array $data): array 'function' => $value['function'], ]; }); - $decision['installations'] = $installations; - $discharges = []; - array_walk($decision['discharges'], function ($value) use (&$discharges): void { - $decision = $this->getOrganMapper()->findInstallationDecision( - MeetingTypes::from($value['meeting_type']), - (int) $value['meeting_number'], - (int) $value['decision_point'], - (int) $value['decision_number'], - (int) $value['number'], - ); - - if (null === $decision) { - return; - } - - $discharges[] = $decision; - }); - - $decision['discharges'] = $discharges; + // Prepare reappointments and discharges. + foreach (['reappointments', 'discharges'] as $subDecisionType) { + $subDecisions = []; + array_walk($decision[$subDecisionType], function ($value) use (&$subDecisions): void { + $decision = $this->getOrganMapper()->findInstallationDecision( + MeetingTypes::from($value['meeting_type']), + intval($value['meeting_number']), + intval($value['decision_point']), + intval($value['decision_number']), + intval($value['number']), + ); + + if (null === $decision) { + return; + } + + $subDecisions[] = $decision; + }); + $decision[$subDecisionType] = $subDecisions; + } $decision = (new InstallHydrator())->hydrate($decision, new DecisionModel()); diff --git a/module/Database/view/database/meeting/installform.phtml b/module/Database/view/database/meeting/installform.phtml index 946548e4..186cc736 100644 --- a/module/Database/view/database/meeting/installform.phtml +++ b/module/Database/view/database/meeting/installform.phtml @@ -90,6 +90,8 @@ $(document).ready(function () { + ' translate('Discharge') ?>'; if ('Lid' === install.function) { + toAdd += ''; toAdd += ''; toAdd += '