diff --git a/application/YoshiKan/Application/Command/Member/AddFederation/AddFederation.php b/application/YoshiKan/Application/Command/Member/AddFederation/AddFederation.php index 749a515..4e06b86 100644 --- a/application/YoshiKan/Application/Command/Member/AddFederation/AddFederation.php +++ b/application/YoshiKan/Application/Command/Member/AddFederation/AddFederation.php @@ -21,6 +21,7 @@ private function __construct( protected string $code, protected string $name, protected int $yearlySubscriptionFee, + protected string $publicLabel, ) { } @@ -34,6 +35,7 @@ public static function hydrateFromJson($json): self trim($json->code), trim($json->name), intval($json->yearlySubscriptionFee), + trim($json->publicLabel), ); } @@ -55,4 +57,9 @@ public function getYearlySubscriptionFee(): int { return $this->yearlySubscriptionFee; } + + public function getPublicLabel(): string + { + return $this->publicLabel; + } } diff --git a/application/YoshiKan/Application/Command/Member/AddFederation/AddFederationHandler.php b/application/YoshiKan/Application/Command/Member/AddFederation/AddFederationHandler.php index 4a845ac..01505ff 100644 --- a/application/YoshiKan/Application/Command/Member/AddFederation/AddFederationHandler.php +++ b/application/YoshiKan/Application/Command/Member/AddFederation/AddFederationHandler.php @@ -36,7 +36,8 @@ public function go(AddFederation $command): bool 1, $command->getCode(), $command->getName(), - $command->getYearlySubscriptionFee() + $command->getYearlySubscriptionFee(), + $command->getPublicLabel() ); $this->federationRepo->save($model); diff --git a/application/YoshiKan/Application/Command/Member/ChangeFederation/ChangeFederation.php b/application/YoshiKan/Application/Command/Member/ChangeFederation/ChangeFederation.php index 7fbeb06..af5c3e7 100644 --- a/application/YoshiKan/Application/Command/Member/ChangeFederation/ChangeFederation.php +++ b/application/YoshiKan/Application/Command/Member/ChangeFederation/ChangeFederation.php @@ -22,6 +22,7 @@ private function __construct( protected string $code, protected string $name, protected int $yearlySubscriptionFee, + protected string $publicLabel, ) { } @@ -36,6 +37,7 @@ public static function hydrateFromJson($json): self trim($json->code), trim($json->name), intval($json->yearlySubscriptionFee), + trim($json->publicLabel) ); } @@ -62,4 +64,9 @@ public function getYearlySubscriptionFee(): int { return $this->yearlySubscriptionFee; } + + public function getPublicLabel(): string + { + return $this->publicLabel; + } } diff --git a/application/YoshiKan/Application/Command/Member/ChangeFederation/ChangeFederationHandler.php b/application/YoshiKan/Application/Command/Member/ChangeFederation/ChangeFederationHandler.php index 26931fe..1434599 100644 --- a/application/YoshiKan/Application/Command/Member/ChangeFederation/ChangeFederationHandler.php +++ b/application/YoshiKan/Application/Command/Member/ChangeFederation/ChangeFederationHandler.php @@ -28,13 +28,14 @@ public function __construct( // Handler // ————————————————————————————————————————————————————————————————————————— - public function Change(ChangeFederation $command): bool + public function go(ChangeFederation $command): bool { $model = $this->federationRepo->getById($command->getId()); $model->change( $command->getCode(), $command->getName(), - $command->getYearlySubscriptionFee() + $command->getYearlySubscriptionFee(), + $command->getPublicLabel() ); $this->federationRepo->save($model); diff --git a/application/YoshiKan/Application/Command/Member/ChangeMemberGrade/ChangeMemberGradeHandler.php b/application/YoshiKan/Application/Command/Member/ChangeMemberGrade/ChangeMemberGradeHandler.php index 35fa312..c467af9 100644 --- a/application/YoshiKan/Application/Command/Member/ChangeMemberGrade/ChangeMemberGradeHandler.php +++ b/application/YoshiKan/Application/Command/Member/ChangeMemberGrade/ChangeMemberGradeHandler.php @@ -14,9 +14,9 @@ namespace App\YoshiKan\Application\Command\Member\ChangeMemberGrade; use App\YoshiKan\Domain\Model\Member\GradeLog; +use App\YoshiKan\Domain\Model\Member\GradeLogRepository; use App\YoshiKan\Domain\Model\Member\GradeRepository; use App\YoshiKan\Domain\Model\Member\MemberRepository; -use App\YoshiKan\Infrastructure\Database\Member\GradeLogRepository; class ChangeMemberGradeHandler { diff --git a/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsCanceled/MarkSubscriptionAsCanceled.php b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsCanceled/MarkSubscriptionAsCanceled.php new file mode 100644 index 0000000..7685bc4 --- /dev/null +++ b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsCanceled/MarkSubscriptionAsCanceled.php @@ -0,0 +1,40 @@ +id, + ); + } + + // ————————————————————————————————————————————————————————————————————————— + // Getters + // ————————————————————————————————————————————————————————————————————————— + + public function getId(): int + { + return $this->id; + } +} diff --git a/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsCanceled/MarkSubscriptionAsCanceledHandler.php b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsCanceled/MarkSubscriptionAsCanceledHandler.php new file mode 100644 index 0000000..0910515 --- /dev/null +++ b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsCanceled/MarkSubscriptionAsCanceledHandler.php @@ -0,0 +1,37 @@ +subscriptionRepository->getById($command->getId()); + $result = false; + + if (SubscriptionStatus::NEW === $subscription->getStatus() + || SubscriptionStatus::AWAITING_PAYMENT === $subscription->getStatus()) { + $subscription->changeStatus(SubscriptionStatus::CANCELED); + $this->subscriptionRepository->save($subscription); + $result = true; + } + + return $result; + } +} diff --git a/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsCanceled/mark_subscription_as_canceled.php b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsCanceled/mark_subscription_as_canceled.php new file mode 100644 index 0000000..426b98d --- /dev/null +++ b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsCanceled/mark_subscription_as_canceled.php @@ -0,0 +1,22 @@ +permission->CheckRole(['ROLE_DEVELOPER', 'ROLE_ADMIN', 'ROLE_CHIEF_EDITOR']); + + $handler = new MarkSubscriptionAsCanceledHandler($this->subscriptionRepository); + $result = $handler->go($command); + $this->entityManager->flush(); + + return $result; + } +} diff --git a/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsFinished/MarkSubscriptionAsFinished.php b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsFinished/MarkSubscriptionAsFinished.php new file mode 100644 index 0000000..99a4b68 --- /dev/null +++ b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsFinished/MarkSubscriptionAsFinished.php @@ -0,0 +1,40 @@ +id, + ); + } + + // ————————————————————————————————————————————————————————————————————————— + // Getters + // ————————————————————————————————————————————————————————————————————————— + + public function getId(): int + { + return $this->id; + } +} diff --git a/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsFinished/MarkSubscriptionAsFinishedHandler.php b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsFinished/MarkSubscriptionAsFinishedHandler.php new file mode 100644 index 0000000..84b4e3c --- /dev/null +++ b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsFinished/MarkSubscriptionAsFinishedHandler.php @@ -0,0 +1,35 @@ +subscriptionRepository->getById($command->getId()); + + if (SubscriptionStatus::PAYED === $subscription->getStatus()) { + $subscription->changeStatus(SubscriptionStatus::COMPLETE); + $this->subscriptionRepository->save($subscription); + $result = true; + } + + return $result; + } +} diff --git a/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsFinished/mark_subscription_as_finished.php b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsFinished/mark_subscription_as_finished.php new file mode 100644 index 0000000..1a63cd4 --- /dev/null +++ b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsFinished/mark_subscription_as_finished.php @@ -0,0 +1,22 @@ +permission->CheckRole(['ROLE_DEVELOPER', 'ROLE_ADMIN', 'ROLE_CHIEF_EDITOR']); + + $handler = new MarkSubscriptionAsFinishedHandler($this->subscriptionRepository); + $result = $handler->go($command); + $this->entityManager->flush(); + + return $result; + } +} diff --git a/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsPayed/MarkSubscriptionAsPayed.php b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsPayed/MarkSubscriptionAsPayed.php index 2eff5bf..ccee86f 100644 --- a/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsPayed/MarkSubscriptionAsPayed.php +++ b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsPayed/MarkSubscriptionAsPayed.php @@ -4,4 +4,37 @@ class MarkSubscriptionAsPayed { + // ————————————————————————————————————————————————————————————————————————— + // Constructor + // ————————————————————————————————————————————————————————————————————————— + + private function __construct( + protected int $id, + ) { + } + + // ————————————————————————————————————————————————————————————————————————— + // Hydrate from a json command + // ————————————————————————————————————————————————————————————————————————— + + public static function make(int $subscriptionId): self + { + return new self($subscriptionId); + } + + public static function hydrateFromJson($json): self + { + return new self( + $json->id, + ); + } + + // ————————————————————————————————————————————————————————————————————————— + // Getters + // ————————————————————————————————————————————————————————————————————————— + + public function getId(): int + { + return $this->id; + } } diff --git a/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsPayed/MarkSubscriptionAsPayedHandler.php b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsPayed/MarkSubscriptionAsPayedHandler.php index 9cb9bbb..f98c9b0 100644 --- a/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsPayed/MarkSubscriptionAsPayedHandler.php +++ b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsPayed/MarkSubscriptionAsPayedHandler.php @@ -2,6 +2,63 @@ namespace App\YoshiKan\Application\Command\Member\MarkSubscriptionAsPayed; +use App\YoshiKan\Application\Command\Member\MarkSubscriptionAsCanceled\MarkSubscriptionAsCanceled; +use App\YoshiKan\Application\Command\Member\MarkSubscriptionAsCanceled\MarkSubscriptionAsCanceledHandler; +use App\YoshiKan\Domain\Model\Member\MemberRepository; +use App\YoshiKan\Domain\Model\Member\SubscriptionRepository; +use App\YoshiKan\Domain\Model\Member\SubscriptionStatus; + class MarkSubscriptionAsPayedHandler { + // ————————————————————————————————————————————————————————————————————————— + // Constructor + // ————————————————————————————————————————————————————————————————————————— + + public function __construct( + protected SubscriptionRepository $subscriptionRepository, + protected MemberRepository $memberRepository + ) { + } + + // ————————————————————————————————————————————————————————————————————————— + // Handler + // ————————————————————————————————————————————————————————————————————————— + + public function go(MarkSubscriptionAsPayed $command): bool + { + $result = false; + $subscription = $this->subscriptionRepository->getById($command->getId()); + + if (SubscriptionStatus::AWAITING_PAYMENT === $subscription->getStatus()) { + $subscription->changeStatus(SubscriptionStatus::PAYED); + $this->subscriptionRepository->save($subscription); + $result = true; + } + + // mark the subscription and license as paid on the actual member + if ($result && !is_null($subscription->getMember())) { + $member = $subscription->getMember(); + if ($subscription->isMemberSubscriptionIsPartSubscription()) { + $member->markSubscriptionAsPayed(); + } + if ($subscription->isLicenseIsPartSubscription()) { + $member->markLicenseAsPayed(); + } + $this->memberRepository->save($member); + } + + // cancel all other pending subscriptions for this member + if ($result && !is_null($subscription->getMember())) { + $cancelHandler = new MarkSubscriptionAsCanceledHandler($this->subscriptionRepository); + $subscriptions = $this->subscriptionRepository->getByMember($subscription->getMember()); + foreach ($subscriptions as $subscription) { + if ($subscription->getId() !== $command->getId()) { + $cancelCommand = MarkSubscriptionAsCanceled::make($subscription->getId()); + $cancelHandler->go($cancelCommand); + } + } + } + + return $result; + } } diff --git a/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsPayed/mark_subscription_as_paid.php b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsPayed/mark_subscription_as_paid.php index 34ab0d2..513a584 100644 --- a/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsPayed/mark_subscription_as_paid.php +++ b/application/YoshiKan/Application/Command/Member/MarkSubscriptionAsPayed/mark_subscription_as_paid.php @@ -2,6 +2,37 @@ namespace App\YoshiKan\Application\Command\Member\MarkSubscriptionAsPayed; +use App\YoshiKan\Application\Command\Member\MarkSubscriptionAsFinished\MarkSubscriptionAsFinished; +use App\YoshiKan\Application\Command\Member\MarkSubscriptionAsFinished\MarkSubscriptionAsFinishedHandler; + trait mark_subscription_as_paid { + /** + * @throws \Exception + */ + public function markSubscriptionAsPayed(\stdClass $jsonCommand): bool + { + $command = MarkSubscriptionAsPayed::hydrateFromJson($jsonCommand); + + $this->permission->CheckRole(['ROLE_DEVELOPER', 'ROLE_ADMIN', 'ROLE_CHIEF_EDITOR']); + + // -- mark as paid + $handler = new MarkSubscriptionAsPayedHandler( + $this->subscriptionRepository, + $this->memberRepository + ); + $result = $handler->go($command); + $this->entityManager->flush(); + + // -- finish the subscription + $resultFinished = false; + if ($result) { + $finishedCommand = MarkSubscriptionAsFinished::make($command->getId()); + $finishedHandler = new MarkSubscriptionAsFinishedHandler($this->subscriptionRepository); + $resultFinished = $finishedHandler->go($finishedCommand); + $this->entityManager->flush(); + } + + return $resultFinished; + } } diff --git a/application/YoshiKan/Application/Command/Member/MemberExtendSubscription/MemberExtendSubscriptionHandler.php b/application/YoshiKan/Application/Command/Member/MemberExtendSubscription/MemberExtendSubscriptionHandler.php index e8f01d2..2caa637 100644 --- a/application/YoshiKan/Application/Command/Member/MemberExtendSubscription/MemberExtendSubscriptionHandler.php +++ b/application/YoshiKan/Application/Command/Member/MemberExtendSubscription/MemberExtendSubscriptionHandler.php @@ -63,6 +63,11 @@ public function go(MemberExtendSubscription $command): \stdClass throw new \Exception('Membership extension command is not valid.'); } + $extraTraining = false; + if (3 === $command->getNumberOfTraining()) { + $extraTraining = true; + } + // -- make a subscription $subscription = Subscription::make( $this->subscriptionRepository->nextIdentity(), @@ -76,7 +81,7 @@ public function go(MemberExtendSubscription $command): \stdClass Gender::from($command->getGender()), SubscriptionType::from($command->getType()), $command->getNumberOfTraining(), - $command->isExtraTraining(), + $extraTraining, $command->isNewMember(), $command->isReductionFamily(), $command->isJudogiBelt(), @@ -99,6 +104,7 @@ public function go(MemberExtendSubscription $command): \stdClass $subscription->setMember($member); $subscription->changeStatus(SubscriptionStatus::AWAITING_PAYMENT); + $subscription->calculate(); $this->subscriptionRepository->save($subscription); // -- make some subscription lines ----------------------------------------------------------------------------- diff --git a/application/YoshiKan/Application/Command/Member/MemberExtendSubscriptionMail/member_extend_subscription_mail.php b/application/YoshiKan/Application/Command/Member/MemberExtendSubscriptionMail/member_extend_subscription_mail.php index d4b8d04..9920944 100644 --- a/application/YoshiKan/Application/Command/Member/MemberExtendSubscriptionMail/member_extend_subscription_mail.php +++ b/application/YoshiKan/Application/Command/Member/MemberExtendSubscriptionMail/member_extend_subscription_mail.php @@ -13,6 +13,8 @@ namespace App\YoshiKan\Application\Command\Member\MemberExtendSubscriptionMail; +use App\YoshiKan\Application\Settings; + trait member_extend_subscription_mail { public function sendMemberExtendSubscriptionMail(int $subscriptionId): bool @@ -21,8 +23,8 @@ public function sendMemberExtendSubscriptionMail(int $subscriptionId): bool $command = new MemberExtendSubscriptionMail( $subscriptionId, - 'Yoshi-Kan', - 'no-reply@yoshi-kan.be' + Settings::FROM_NAME->value, + Settings::FROM_EMAIL->value ); $handler = new MemberExtendSubscriptionMailHandler( diff --git a/application/YoshiKan/Application/Command/Member/NewMemberSubscription/NewMemberSubscriptionHandler.php b/application/YoshiKan/Application/Command/Member/NewMemberSubscription/NewMemberSubscriptionHandler.php index 25d4ee3..7daca1c 100644 --- a/application/YoshiKan/Application/Command/Member/NewMemberSubscription/NewMemberSubscriptionHandler.php +++ b/application/YoshiKan/Application/Command/Member/NewMemberSubscription/NewMemberSubscriptionHandler.php @@ -71,6 +71,11 @@ public function go(NewMemberSubscription $command): \stdClass // -- make a subscription -------------------------------------------------------------------------------------- + $extraTraining = false; + if (3 === $command->getNumberOfTraining()) { + $extraTraining = true; + } + $subscription = Subscription::make( $this->subscriptionRepository->nextIdentity(), $command->getContactFirstname(), @@ -83,7 +88,7 @@ public function go(NewMemberSubscription $command): \stdClass Gender::from($command->getGender()), SubscriptionType::from($command->getType()), $command->getNumberOfTraining(), - $command->isExtraTraining(), + $extraTraining, $command->isNewMember(), $command->isReductionFamily(), $command->isJudogiBelt(), @@ -114,6 +119,7 @@ public function go(NewMemberSubscription $command): \stdClass ); // -- flush the subscription and get the database id via the uuid + $subscription->calculate(); $this->subscriptionRepository->save($subscription); $this->entityManager->flush(); diff --git a/application/YoshiKan/Application/Command/Member/NewMemberSubscriptionMail/new_member_subscription_mail.php b/application/YoshiKan/Application/Command/Member/NewMemberSubscriptionMail/new_member_subscription_mail.php index 08d78fe..74465db 100644 --- a/application/YoshiKan/Application/Command/Member/NewMemberSubscriptionMail/new_member_subscription_mail.php +++ b/application/YoshiKan/Application/Command/Member/NewMemberSubscriptionMail/new_member_subscription_mail.php @@ -13,6 +13,8 @@ namespace App\YoshiKan\Application\Command\Member\NewMemberSubscriptionMail; +use App\YoshiKan\Application\Settings; + trait new_member_subscription_mail { public function sendMemberNewSubscriptionMail(int $subscriptionId): bool @@ -33,8 +35,8 @@ public function sendMemberNewSubscriptionMail(int $subscriptionId): bool $command = new NewMemberSubscriptionMail( $subscriptionId, - 'Yoshi-Kan', - 'no-reply@yoshi-kan.be' + Settings::FROM_NAME->value, + Settings::FROM_EMAIL->value ); $result = $handler->go($command); diff --git a/application/YoshiKan/Application/Command/Member/NewMemberWebSubscription/NewMemberWebSubscription.php b/application/YoshiKan/Application/Command/Member/NewMemberWebSubscription/NewMemberWebSubscription.php index 1d6c910..76b3d17 100644 --- a/application/YoshiKan/Application/Command/Member/NewMemberWebSubscription/NewMemberWebSubscription.php +++ b/application/YoshiKan/Application/Command/Member/NewMemberWebSubscription/NewMemberWebSubscription.php @@ -15,4 +15,216 @@ class NewMemberWebSubscription { + // ————————————————————————————————————————————————————————————————————————— + // Constructor + // ————————————————————————————————————————————————————————————————————————— + + private function __construct( + protected string $type, + protected int $federationId, + protected int $locationId, + + protected string $contactFirstname, + protected string $contactLastname, + protected string $contactEmail, + protected string $contactPhone, + + protected string $addressStreet, + protected string $addressNumber, + protected string $addressBox, + protected string $addressZip, + protected string $addressCity, + + protected string $firstname, + protected string $lastname, + protected string $nationalRegisterNumber, + protected \DateTimeImmutable $dateOfBirth, + protected string $gender, + + protected bool $memberSubscriptionIsHalfYear, + protected int $numberOfTraining, + protected bool $isExtraTraining, + protected bool $isNewMember, + protected bool $isReductionFamily, + + protected float $total, + protected string $remarks, + + protected bool $isJudogiBelt, + protected string $honeyPot, + ) { + } + + // ————————————————————————————————————————————————————————————————————————— + // Hydrate from a json command + // ————————————————————————————————————————————————————————————————————————— + + /** + * @throws \Exception + */ + public static function hydrateFromJson($json): self + { + return new self( + trim($json->type), + intval($json->federationId), + intval($json->locationId), + trim($json->contactFirstname), + trim($json->contactLastname), + trim($json->contactEmail), + trim($json->contactPhone), + trim($json->addressStreet), + trim($json->addressNumber), + trim($json->addressBox), + trim($json->addressZip), + trim($json->addressCity), + trim($json->firstname), + trim($json->lastname), + trim($json->nationalRegisterNumber), + new \DateTimeImmutable($json->dateOfBirth), + trim($json->gender), + boolval($json->memberSubscriptionIsHalfYear), + intval($json->numberOfTraining), + boolval($json->isExtraTraining), + boolval($json->isNewMember), + boolval($json->isReductionFamily), + floatval($json->total), + trim($json->remarks), + boolval($json->isJudogiBelt), + trim($json->honeyPot), + ); + } + + // ————————————————————————————————————————————————————————————————————————— + // Getters + // ————————————————————————————————————————————————————————————————————————— + + public function getType(): string + { + return $this->type; + } + + public function getFederationId(): int + { + return $this->federationId; + } + + public function getLocationId(): int + { + return $this->locationId; + } + + public function getContactFirstname(): string + { + return $this->contactFirstname; + } + + public function getContactLastname(): string + { + return $this->contactLastname; + } + + public function getContactEmail(): string + { + return $this->contactEmail; + } + + public function getContactPhone(): string + { + return $this->contactPhone; + } + + public function getAddressStreet(): string + { + return $this->addressStreet; + } + + public function getAddressNumber(): string + { + return $this->addressNumber; + } + + public function getAddressBox(): string + { + return $this->addressBox; + } + + public function getAddressZip(): string + { + return $this->addressZip; + } + + public function getAddressCity(): string + { + return $this->addressCity; + } + + public function getFirstname(): string + { + return $this->firstname; + } + + public function getLastname(): string + { + return $this->lastname; + } + + public function getNationalRegisterNumber(): string + { + return $this->nationalRegisterNumber; + } + + public function getDateOfBirth(): \DateTimeImmutable + { + return $this->dateOfBirth; + } + + public function getGender(): string + { + return $this->gender; + } + + public function getNumberOfTraining(): int + { + return $this->numberOfTraining; + } + + public function isExtraTraining(): bool + { + return $this->isExtraTraining; + } + + public function isNewMember(): bool + { + return $this->isNewMember; + } + + public function isReductionFamily(): bool + { + return $this->isReductionFamily; + } + + public function getTotal(): float + { + return $this->total; + } + + public function getRemarks(): string + { + return $this->remarks; + } + + public function isJudogiBelt(): bool + { + return $this->isJudogiBelt; + } + + public function isMemberSubscriptionIsHalfYear(): bool + { + return $this->memberSubscriptionIsHalfYear; + } + + public function getHoneyPot(): string + { + return $this->honeyPot; + } } diff --git a/application/YoshiKan/Application/Command/Member/NewMemberWebSubscription/NewMemberWebSubscriptionHandler.php b/application/YoshiKan/Application/Command/Member/NewMemberWebSubscription/NewMemberWebSubscriptionHandler.php index f326880..422d304 100644 --- a/application/YoshiKan/Application/Command/Member/NewMemberWebSubscription/NewMemberWebSubscriptionHandler.php +++ b/application/YoshiKan/Application/Command/Member/NewMemberWebSubscription/NewMemberWebSubscriptionHandler.php @@ -13,6 +13,148 @@ namespace App\YoshiKan\Application\Command\Member\NewMemberWebSubscription; +use App\YoshiKan\Application\Query\Member\Readmodel\SettingsReadModel; +use App\YoshiKan\Domain\Model\Member\FederationRepository; +use App\YoshiKan\Domain\Model\Member\Gender; +use App\YoshiKan\Domain\Model\Member\LocationRepository; +use App\YoshiKan\Domain\Model\Member\MemberRepository; +use App\YoshiKan\Domain\Model\Member\Settings; +use App\YoshiKan\Domain\Model\Member\SettingsCode; +use App\YoshiKan\Domain\Model\Member\SettingsRepository; +use App\YoshiKan\Domain\Model\Member\Subscription; +use App\YoshiKan\Domain\Model\Member\SubscriptionItemRepository; +use App\YoshiKan\Domain\Model\Member\SubscriptionRepository; +use App\YoshiKan\Domain\Model\Member\SubscriptionType; +use Doctrine\ORM\EntityManagerInterface; + class NewMemberWebSubscriptionHandler { + // ——————————————————————————————————————————————————————————————— + // Constructor + // ——————————————————————————————————————————————————————————————— + + public function __construct( + protected FederationRepository $federationRepository, + protected LocationRepository $locationRepository, + protected SettingsRepository $settingsRepository, + protected MemberRepository $memberRepository, + protected SubscriptionRepository $subscriptionRepository, + protected SubscriptionItemRepository $subscriptionItemRepository, + protected EntityManagerInterface $entityManager, + ) { + } + + // ——————————————————————————————————————————————————————————————— + // Handle + // ——————————————————————————————————————————————————————————————— + /** + * @throws \Exception + */ + public function go(NewMemberWebSubscription $command): \stdClass + { + if ('' === $command->getHoneyPot()) { + $federation = $this->federationRepository->getById($command->getFederationId()); + $location = $this->locationRepository->getById($command->getLocationId()); + $settings = $this->settingsRepository->findByCode(SettingsCode::ACTIVE->value); + + // -- validate the command + $commandIsValid = $this->isCommandValid($command, $settings); + if (!$commandIsValid) { + throw new \Exception('Membership extension command is not valid.'); + } + + // -- dates calculation ------------------------------------------------------------------------------------ + $currentDate = new \DateTimeImmutable(); + $membershipStart = $currentDate->modify('first day of this month'); + $membershipEnd = $membershipStart->modify('+1 year'); + if ($command->isMemberSubscriptionIsHalfYear()) { + $addAmount = 5; + $startMonth = intval($currentDate->format('m')); + if (2 == $startMonth + || 3 == $startMonth || 4 == $startMonth || 5 == $startMonth + || 6 == $startMonth || 7 == $startMonth || 8 == $startMonth + ) { + $addAmount = 7; + } + $membershipEnd = $membershipStart->modify('+'.$addAmount.' months'); + } + $licenseStart = $currentDate->modify('first day of this month'); + $licenseEnd = $licenseStart->modify('+1 year'); + + // -- make a subscription ---------------------------------------------------------------------------------- + $extraTraining = false; + if (3 === $command->getNumberOfTraining()) { + $extraTraining = true; + } + + $subscription = Subscription::make( + $this->subscriptionRepository->nextIdentity(), + $command->getContactFirstname(), + $command->getContactLastname(), + $command->getContactEmail(), + $command->getContactPhone(), + $command->getFirstname(), + $command->getLastname(), + $command->getDateOfBirth(), + Gender::from($command->getGender()), + SubscriptionType::from($command->getType()), + $command->getNumberOfTraining(), + $extraTraining, + $command->isNewMember(), + $command->isReductionFamily(), + $command->isJudogiBelt(), + $command->getRemarks(), + $location, + json_decode(json_encode(SettingsReadModel::hydrateFromModel($settings)), true), + $federation, + $membershipStart, + $membershipEnd, + 0, + true, + $command->isMemberSubscriptionIsHalfYear(), + false, + $licenseStart, + $licenseEnd, + 0, + true, + false, + ); + + $subscription->setNewMemberFields( + $command->getNationalRegisterNumber(), + $command->getAddressStreet(), + $command->getAddressNumber(), + $command->getAddressBox(), + $command->getAddressZip(), + $command->getAddressCity() + ); + + // -- flush the subscription and get the database id via the uuid + $subscription->calculate(); + $this->subscriptionRepository->save($subscription); + $this->entityManager->flush(); + + $subscription = $this->subscriptionRepository->getByUuid($subscription->getUuid()); + $subscriptionId = $subscription->getId(); + + // -- compile a result class ------------------------------------------------------------------------------- + $result = new \stdClass(); + $result->id = $subscriptionId; + $result->reference = 'YKS-'.$subscriptionId.': '.$command->getFirstName().' '.$command->getLastName(); + } else { + // -- the honey-pot field was not empty + $result = new \stdClass(); + $result->id = 0; + $result->reference = 'YKS-0'; + } + + return $result; + } + + private function isCommandValid(NewMemberWebSubscription $command, Settings $settings): bool + { + // todo + + return true; + } } diff --git a/application/YoshiKan/Application/Command/Member/NewMemberWebSubscription/new_member_web_subscription.php b/application/YoshiKan/Application/Command/Member/NewMemberWebSubscription/new_member_web_subscription.php index 892417e..2f46a59 100644 --- a/application/YoshiKan/Application/Command/Member/NewMemberWebSubscription/new_member_web_subscription.php +++ b/application/YoshiKan/Application/Command/Member/NewMemberWebSubscription/new_member_web_subscription.php @@ -15,4 +15,22 @@ trait new_member_web_subscription { + public function newMemberWebSubscription(\stdClass $jsonCommand): \stdClass + { + $command = NewMemberWebSubscription::hydrateFromJson($jsonCommand); + $handler = new NewMemberWebSubscriptionHandler( + $this->federationRepository, + $this->locationRepository, + $this->settingsRepository, + $this->memberRepository, + $this->subscriptionRepository, + $this->subscriptionItemRepository, + $this->entityManager, + ); + + $result = $handler->go($command); + $this->entityManager->flush(); + + return $result; + } } diff --git a/application/YoshiKan/Application/Command/Member/NewMemberWebSubscriptionMail/NewMemberWebSubscriptionMail.php b/application/YoshiKan/Application/Command/Member/NewMemberWebSubscriptionMail/NewMemberWebSubscriptionMail.php index b87520c..1edc498 100644 --- a/application/YoshiKan/Application/Command/Member/NewMemberWebSubscriptionMail/NewMemberWebSubscriptionMail.php +++ b/application/YoshiKan/Application/Command/Member/NewMemberWebSubscriptionMail/NewMemberWebSubscriptionMail.php @@ -15,4 +15,39 @@ class NewMemberWebSubscriptionMail { + // ————————————————————————————————————————————————————————————————————————— + // Constructor + // ————————————————————————————————————————————————————————————————————————— + + public function __construct( + protected int $subscriptionId, + protected string $fromName, + protected string $fromEmail, + protected string $contactEmail, + ) { + } + + // ————————————————————————————————————————————————————————————————————————— + // Getters + // ————————————————————————————————————————————————————————————————————————— + + public function getSubscriptionId(): int + { + return $this->subscriptionId; + } + + public function getFromName(): string + { + return $this->fromName; + } + + public function getFromEmail(): string + { + return $this->fromEmail; + } + + public function getContactEmail(): string + { + return $this->contactEmail; + } } diff --git a/application/YoshiKan/Application/Command/Member/NewMemberWebSubscriptionMail/NewMemberWebSubscriptionMailHandler.php b/application/YoshiKan/Application/Command/Member/NewMemberWebSubscriptionMail/NewMemberWebSubscriptionMailHandler.php index 1feb211..565773f 100644 --- a/application/YoshiKan/Application/Command/Member/NewMemberWebSubscriptionMail/NewMemberWebSubscriptionMailHandler.php +++ b/application/YoshiKan/Application/Command/Member/NewMemberWebSubscriptionMail/NewMemberWebSubscriptionMailHandler.php @@ -13,6 +13,44 @@ namespace App\YoshiKan\Application\Command\Member\NewMemberWebSubscriptionMail; +use App\YoshiKan\Domain\Model\Member\SubscriptionRepository; +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; +use Twig\Environment; + class NewMemberWebSubscriptionMailHandler { + public function __construct( + protected SubscriptionRepository $subscriptionRepository, + protected Environment $twig, + protected MailerInterface $mailer + ) { + } + + public function go(NewMemberWebSubscriptionMail $command): bool + { + $subscription = $this->subscriptionRepository->getById($command->getSubscriptionId()); + $subject = 'Yoshi-Kan: bevestiging inschrijving '.$subscription->getFirstname().' '.$subscription->getLastname(); + + $mailTemplate = $this->twig->render( + 'mail/web_confirmation_mail.html.twig', + [ + 'subject' => $subject, + 'subscription' => $subscription, + 'url' => $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'], + ] + ); + + $message = (new Email()) + ->subject($subject) + ->from(new Address($command->getFromEmail(), $command->getFromName())) + ->to(new Address($subscription->getContactEmail(), $subscription->getContactFirstname().' '.$subscription->getContactLastname())) + ->bcc(new Address($command->getContactEmail(), $command->getFromName())) + ->html($mailTemplate); + + $this->mailer->send($message); + + return true; + } } diff --git a/application/YoshiKan/Application/Command/Member/NewMemberWebSubscriptionMail/new_member_web_subscription_mail.php b/application/YoshiKan/Application/Command/Member/NewMemberWebSubscriptionMail/new_member_web_subscription_mail.php index 6f8e2c5..3ca6325 100644 --- a/application/YoshiKan/Application/Command/Member/NewMemberWebSubscriptionMail/new_member_web_subscription_mail.php +++ b/application/YoshiKan/Application/Command/Member/NewMemberWebSubscriptionMail/new_member_web_subscription_mail.php @@ -13,6 +13,24 @@ namespace App\YoshiKan\Application\Command\Member\NewMemberWebSubscriptionMail; +use App\YoshiKan\Application\Settings; + trait new_member_web_subscription_mail { + /** + * @throws \Exception + */ + public function newMemberWebSubscriptionMail(int $subscriptionId): bool + { + $command = new NewMemberWebSubscriptionMail( + $subscriptionId, + Settings::FROM_NAME->value, + Settings::FROM_EMAIL->value, + Settings::CONTACT_EMAIL->value, + ); + + $handler = new NewMemberWebSubscriptionMailHandler($this->subscriptionRepository, $this->twig, $this->mailer); + + return $handler->go($command); + } } diff --git a/application/YoshiKan/Application/Command/Member/OrderFederation/OrderFederationHandler.php b/application/YoshiKan/Application/Command/Member/OrderFederation/OrderFederationHandler.php index 3831637..c7441c6 100644 --- a/application/YoshiKan/Application/Command/Member/OrderFederation/OrderFederationHandler.php +++ b/application/YoshiKan/Application/Command/Member/OrderFederation/OrderFederationHandler.php @@ -10,7 +10,7 @@ class OrderFederationHandler // Constructor // ——————————————————————————————————————————————————————————————— - public function __construct(protected FederationRepository $repo) + public function __construct(protected FederationRepository $federationRepository) { } @@ -22,9 +22,9 @@ public function go(OrderFederation $command): bool { $sequence = 0; foreach ($command->getSequence() as $sequenceId) { - $model = $this->repo->getById($sequenceId); + $model = $this->federationRepository->getById($sequenceId); $model->setSequence($sequence); - $this->repo->save($model); + $this->federationRepository->save($model); ++$sequence; } diff --git a/application/YoshiKan/Application/Command/Member/SetupConfiguration/SetupConfigurationHandler.php b/application/YoshiKan/Application/Command/Member/SetupConfiguration/SetupConfigurationHandler.php index 3eb5758..bff5a8a 100644 --- a/application/YoshiKan/Application/Command/Member/SetupConfiguration/SetupConfigurationHandler.php +++ b/application/YoshiKan/Application/Command/Member/SetupConfiguration/SetupConfigurationHandler.php @@ -14,9 +14,9 @@ namespace App\YoshiKan\Application\Command\Member\SetupConfiguration; use App\YoshiKan\Domain\Model\Member\Period; +use App\YoshiKan\Domain\Model\Member\PeriodRepository; use App\YoshiKan\Domain\Model\Member\Settings; use App\YoshiKan\Domain\Model\Member\SettingsRepository; -use App\YoshiKan\Infrastructure\Database\Member\PeriodRepository; class SetupConfigurationHandler { diff --git a/application/YoshiKan/Application/CommandBus.php b/application/YoshiKan/Application/CommandBus.php index a11c99a..9c9b5cc 100644 --- a/application/YoshiKan/Application/CommandBus.php +++ b/application/YoshiKan/Application/CommandBus.php @@ -13,16 +13,17 @@ namespace App\YoshiKan\Application; +use App\YoshiKan\Application\Command\Member\NewMemberWebSubscription\new_member_web_subscription; +use App\YoshiKan\Application\Command\Member\NewMemberWebSubscriptionMail\new_member_web_subscription_mail; use App\YoshiKan\Application\Security\BasePermissionService; +use App\YoshiKan\Domain\Model\Member\FederationRepository; use App\YoshiKan\Domain\Model\Member\LocationRepository; +use App\YoshiKan\Domain\Model\Member\MemberRepository; +use App\YoshiKan\Domain\Model\Member\PeriodRepository; +use App\YoshiKan\Domain\Model\Member\SettingsRepository; +use App\YoshiKan\Domain\Model\Member\SubscriptionItemRepository; use App\YoshiKan\Domain\Model\Member\SubscriptionRepository; use App\YoshiKan\Domain\Model\Product\JudogiRepository; -use App\YoshiKan\Infrastructure\Database\Member\FederationRepository; -use App\YoshiKan\Infrastructure\Database\Member\MemberRepository; -use App\YoshiKan\Infrastructure\Database\Member\PeriodRepository; -use App\YoshiKan\Infrastructure\Database\Member\SettingsRepository; -use backend\Command\WebConfirmationMail\web_confirmation_mail; -use backend\Command\WebSubscribe\web_subscribe; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Security\Core\Security; @@ -34,8 +35,8 @@ class CommandBus // —— Traits // —————————————————————————————————————————————————————————————————————————— - use web_subscribe; - use web_confirmation_mail; + use new_member_web_subscription; + use new_member_web_subscription_mail; // —————————————————————————————————————————————————————————————————————————— // —— Security @@ -55,6 +56,7 @@ public function __construct( protected MailerInterface $mailer, protected string $uploadFolder, protected SubscriptionRepository $subscriptionRepository, + protected SubscriptionItemRepository $subscriptionItemRepository, protected LocationRepository $locationRepository, protected PeriodRepository $periodRepository, protected JudogiRepository $judogiRepository, diff --git a/application/YoshiKan/Application/MemberCommandBus.php b/application/YoshiKan/Application/MemberCommandBus.php index 51ea07c..8a8f0ae 100644 --- a/application/YoshiKan/Application/MemberCommandBus.php +++ b/application/YoshiKan/Application/MemberCommandBus.php @@ -27,6 +27,9 @@ use App\YoshiKan\Application\Command\Member\ChangeMemberRemarks\change_member_remarks; use App\YoshiKan\Application\Command\Member\ChangePeriod\change_period; use App\YoshiKan\Application\Command\Member\DeleteMemberImage\delete_member_image; +use App\YoshiKan\Application\Command\Member\MarkSubscriptionAsCanceled\mark_subscription_as_canceled; +use App\YoshiKan\Application\Command\Member\MarkSubscriptionAsFinished\mark_subscription_as_finished; +use App\YoshiKan\Application\Command\Member\MarkSubscriptionAsPayed\mark_subscription_as_paid; use App\YoshiKan\Application\Command\Member\MemberExtendSubscription\member_extend_subscription; use App\YoshiKan\Application\Command\Member\MemberExtendSubscriptionMail\member_extend_subscription_mail; use App\YoshiKan\Application\Command\Member\NewMemberSubscription\new_member_subscription; @@ -94,6 +97,9 @@ class MemberCommandBus use member_extend_subscription_mail; use new_member_subscription; use new_member_subscription_mail; + use mark_subscription_as_paid; + use mark_subscription_as_finished; + use mark_subscription_as_canceled; // -- member images --------------------------------------------------------- use upload_member_image; diff --git a/application/YoshiKan/Application/MemberQueryBus.php b/application/YoshiKan/Application/MemberQueryBus.php index f8ea6c7..44f2c65 100644 --- a/application/YoshiKan/Application/MemberQueryBus.php +++ b/application/YoshiKan/Application/MemberQueryBus.php @@ -19,7 +19,9 @@ use App\YoshiKan\Application\Query\Member\get_member_image; use App\YoshiKan\Application\Query\Member\get_subscription; use App\YoshiKan\Application\Query\Message\get_message; +use App\YoshiKan\Application\Query\Reporting\get_reporting; use App\YoshiKan\Application\Security\BasePermissionService; +use App\YoshiKan\Domain\Model\Member\FederationRepository; use App\YoshiKan\Domain\Model\Member\GradeRepository; use App\YoshiKan\Domain\Model\Member\GroupRepository; use App\YoshiKan\Domain\Model\Member\LocationRepository; @@ -30,7 +32,6 @@ use App\YoshiKan\Domain\Model\Member\SubscriptionRepository; use App\YoshiKan\Domain\Model\Message\MessageRepository; use App\YoshiKan\Domain\Model\Product\JudogiRepository; -use App\YoshiKan\Infrastructure\Database\Member\FederationRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Security\Core\Security; use Twig\Environment; @@ -47,6 +48,7 @@ class MemberQueryBus use get_message; use get_subscription; use download_due_payments; + use get_reporting; // —————————————————————————————————————————————————————————————————————————— // —— Security diff --git a/application/YoshiKan/Application/Query/Member/ExportSubscriptions.php b/application/YoshiKan/Application/Query/Member/ExportSubscriptions.php new file mode 100644 index 0000000..3c624e4 --- /dev/null +++ b/application/YoshiKan/Application/Query/Member/ExportSubscriptions.php @@ -0,0 +1,82 @@ +subscriptionRepository->getByListId($listIds); + $activePeriod = $this->periodRepository->getActive(); + + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setTitle($activePeriod->getName()); + + // -- header ---------------------------------------------------------------- + $rowCounter = 1; + $sheet->setCellValue([1, $rowCounter], 'Ref.'); + $sheet->setCellValue([2, $rowCounter], 'Naam'); + $sheet->setCellValue([3, $rowCounter], 'Voornaam'); + $sheet->setCellValue([4, $rowCounter], 'Geboortedatum'); + $sheet->setCellValue([5, $rowCounter], 'Geslacht'); + $sheet->setCellValue([6, $rowCounter], 'Lidnr.'); + $sheet->setCellValue([7, $rowCounter], 'Graad'); + $sheet->setCellValue([8, $rowCounter], 'Contact'); + $sheet->setCellValue([9, $rowCounter], 'Email'); + + // -- data ----------------------------------------------------------------- + ++$rowCounter; + foreach ($subscriptions as $subscription) { + $sheet->setCellValue([1, $rowCounter], 'YKS-'.$subscription->getId()); + $sheet->setCellValue([2, $rowCounter], strtoupper($subscription->getLastname())); + $sheet->setCellValue([3, $rowCounter], $subscription->getFirstname()); + $sheet->setCellValue([4, $rowCounter], $subscription->getDateOfBirth()->format('d/m/Y')); + $sheet->setCellValue([5, $rowCounter], $subscription->getGenderAsString()); + if (!is_null($subscription->getMember())) { + $sheet->setCellValue([6, $rowCounter], 'YK-'.$subscription->getMember()->getId()); + $sheet->setCellValue([7, $rowCounter], $subscription->getMember()->getGrade()->getName()); + } else { + $sheet->setCellValue([6, $rowCounter], 'n/a'); + $sheet->setCellValue([7, $rowCounter], 'n/a'); + } + $sheet->setCellValue([8, $rowCounter], $subscription->getContactFirstname().' '.$subscription->getContactLastname()); + $sheet->setCellValue([9, $rowCounter], $subscription->getContactEmail()); + ++$rowCounter; + } + + // -- first row bold ------------------------------------------------------ + $highestColumn = $sheet->getHighestColumn(); + $sheet->getStyle('A1:'.$highestColumn.'1')->getFont()->setBold(true); + $sheet->setSelectedCell('A1'); + + // -- autosize the columns ------------------------------------------------ + for ($i = 'A'; $i != $sheet->getHighestColumn(); ++$i) { + $sheet->getColumnDimension($i)->setAutoSize(true); + } + $sheet->getColumnDimension($sheet->getHighestColumn())->setAutoSize(true); + + return $spreadsheet; + } +} diff --git a/application/YoshiKan/Application/Query/Member/GetConfiguration.php b/application/YoshiKan/Application/Query/Member/GetConfiguration.php index b7257da..2736ed0 100644 --- a/application/YoshiKan/Application/Query/Member/GetConfiguration.php +++ b/application/YoshiKan/Application/Query/Member/GetConfiguration.php @@ -28,6 +28,7 @@ use App\YoshiKan\Application\Query\Member\Readmodel\WebConfigurationReadModel; use App\YoshiKan\Application\Query\Product\JudogiReadModel; use App\YoshiKan\Application\Query\Product\JudogiReadModelCollection; +use App\YoshiKan\Domain\Model\Member\FederationRepository; use App\YoshiKan\Domain\Model\Member\GradeRepository; use App\YoshiKan\Domain\Model\Member\GroupRepository; use App\YoshiKan\Domain\Model\Member\LocationRepository; @@ -35,7 +36,6 @@ use App\YoshiKan\Domain\Model\Member\SettingsCode; use App\YoshiKan\Domain\Model\Member\SettingsRepository; use App\YoshiKan\Domain\Model\Product\JudogiRepository; -use App\YoshiKan\Infrastructure\Database\Member\FederationRepository; class GetConfiguration { @@ -103,24 +103,29 @@ public function getFullConfiguration(): ConfigurationReadModel public function getWebConfiguration(): WebConfigurationReadModel { - $grades = $this->gradeRepository->getAll(); $locations = $this->locationRepository->getAll(); - $groups = $this->groupRepository->getAll(); $activePeriod = $this->periodRepository->getActive(); $settings = $this->settingsRepository->findByCode(SettingsCode::ACTIVE->value); + $federations = $this->federationRepository->getAll(); $locationCollection = new LocationReadModelCollection(); foreach ($locations as $location) { $locationCollection->addItem(LocationReadModel::hydrateFromModel($location)); } + $federationCollection = new FederationReadModelCollection(); + foreach ($federations as $federation) { + $federationCollection->addItem(FederationReadModel::hydrateFromModel($federation)); + } + $activePeriodReadModel = PeriodReadModel::hydrateFromModel($activePeriod); $settingsReadModel = SettingsReadModel::hydrateFromModel($settings); return new WebConfigurationReadModel( $locationCollection, $activePeriodReadModel, - $settingsReadModel + $settingsReadModel, + $federationCollection ); } } diff --git a/application/YoshiKan/Application/Query/Member/GetSubscription.php b/application/YoshiKan/Application/Query/Member/GetSubscription.php index 52a5e45..ade873f 100644 --- a/application/YoshiKan/Application/Query/Member/GetSubscription.php +++ b/application/YoshiKan/Application/Query/Member/GetSubscription.php @@ -17,6 +17,7 @@ use App\YoshiKan\Application\Query\Member\Readmodel\SubscriptionReadModelCollection; use App\YoshiKan\Domain\Model\Member\MemberRepository; use App\YoshiKan\Domain\Model\Member\SubscriptionRepository; +use App\YoshiKan\Domain\Model\Member\SubscriptionStatus; class GetSubscription { @@ -43,11 +44,14 @@ public function getByMemberId(int $memberId): SubscriptionReadModelCollection return $collection; } - public function getByStatus(string $status): SubscriptionReadModelCollection + public function getByStatus(SubscriptionStatus $status): SubscriptionReadModelCollection { $collection = new SubscriptionReadModelCollection(); - - // todo + $subscriptions = $this->subscriptionRepository->getByStatus($status); + $collection = new SubscriptionReadModelCollection(); + foreach ($subscriptions as $subscription) { + $collection->addItem(SubscriptionReadModel::hydrateFromModel($subscription)); + } return $collection; } diff --git a/application/YoshiKan/Application/Query/Member/PrintSubscriptions.php b/application/YoshiKan/Application/Query/Member/PrintSubscriptions.php new file mode 100644 index 0000000..360e8e3 --- /dev/null +++ b/application/YoshiKan/Application/Query/Member/PrintSubscriptions.php @@ -0,0 +1,64 @@ +subscriptionRepository->getByListId($listIds); + $renderList = new \stdClass(); + $renderList->generatedOn = new \DateTimeImmutable(); + $renderList->subscriptions = $subscriptions; + + $pdfHtml = $this->twig->render('pdf/subscription_detail.html.twig', ['list' => $renderList]); + + $options = new Options(); + $options->set('isRemoteEnabled', true); + $options->set('isPhpEnabled', true); + $dompdf = new Dompdf($options); + $dompdf->loadHtml($pdfHtml); + $dompdf->setPaper('A4', 'portrait'); + $dompdf->render(); + + $fileName = $renderList->generatedOn->format('YmdHis').'_yoshikan_inschrijvingen.pdf'; + + /** @var Subscription $subscription */ + foreach ($subscriptions as $subscription) { + $subscription->flagSubscriptionIsPrinted(); + $this->subscriptionRepository->save($subscription); + } + $this->entityManager->flush(); + + $dompdf->stream($fileName, ['Attachment' => false]); + + exit; + } +} diff --git a/application/YoshiKan/Application/Query/Member/Readmodel/FederationReadModel.php b/application/YoshiKan/Application/Query/Member/Readmodel/FederationReadModel.php index aea7250..7d02b7d 100644 --- a/application/YoshiKan/Application/Query/Member/Readmodel/FederationReadModel.php +++ b/application/YoshiKan/Application/Query/Member/Readmodel/FederationReadModel.php @@ -16,6 +16,7 @@ public function __construct( protected string $code, protected string $name, protected int $yearlySubscriptionFee, + protected string $publicLabel, ) { } @@ -30,6 +31,7 @@ public function jsonSerialize(): \stdClass $json->uuid = $this->getUuid(); $json->code = $this->getCode(); $json->name = $this->getName(); + $json->publicLabel = $this->getPublicLabel(); $json->yearlySubscriptionFee = $this->getYearlySubscriptionFee(); return $json; @@ -47,6 +49,7 @@ public static function hydrateFromModel(Federation $model): self $model->getCode(), $model->getName(), $model->getYearlySubscriptionFee(), + $model->getPublicLabel() ); } @@ -78,4 +81,9 @@ public function getYearlySubscriptionFee(): int { return $this->yearlySubscriptionFee; } + + public function getPublicLabel(): string + { + return $this->publicLabel; + } } diff --git a/application/YoshiKan/Application/Query/Member/Readmodel/SubscriptionReadModel.php b/application/YoshiKan/Application/Query/Member/Readmodel/SubscriptionReadModel.php index 334b969..61bc307 100644 --- a/application/YoshiKan/Application/Query/Member/Readmodel/SubscriptionReadModel.php +++ b/application/YoshiKan/Application/Query/Member/Readmodel/SubscriptionReadModel.php @@ -5,6 +5,7 @@ namespace App\YoshiKan\Application\Query\Member\Readmodel; use App\YoshiKan\Domain\Model\Member\Subscription; +use App\YoshiKan\Domain\Model\Message\Message; class SubscriptionReadModel implements \JsonSerializable { @@ -54,6 +55,7 @@ public function __construct( protected FederationReadModel $federation, protected array $subscriptionitems, protected ?int $memberId, + protected ?int $messageId, ) { } @@ -69,13 +71,14 @@ public function jsonSerialize(): \stdClass $json->status = $this->getStatus(); $json->type = $this->getType(); $json->memberId = $this->getMemberId(); + $json->messageId = $this->getMessageId(); $json->contactFirstname = $this->getContactFirstname(); $json->contactLastname = $this->getContactLastname(); $json->contactEmail = $this->getContactEmail(); $json->contactPhone = $this->getContactPhone(); $json->firstname = $this->getFirstname(); $json->lastname = $this->getLastname(); - $json->dateOfBirth = $this->getDateOfBirth(); + $json->dateOfBirth = $this->getDateOfBirth()->format(\DateTimeInterface::ATOM); $json->gender = $this->getGender(); $json->numberOfTraining = $this->getNumberOfTraining(); $json->isExtraTraining = $this->isExtraTraining(); @@ -125,6 +128,16 @@ public static function hydrateFromModel(Subscription $model): self $memberId = $model->getMember()->getId(); } + $messageId = null; + $messages = $model->getMessages(); + if (count($messages) > 0) { + /** @var Message $message */ + foreach ($messages as $message) { + $messageId = $message->getId(); + break; + } + } + return new self( $model->getId(), $model->getUuid()->toRfc4122(), @@ -166,7 +179,8 @@ public static function hydrateFromModel(Subscription $model): self LocationReadModel::hydrateFromModel($model->getLocation()), FederationReadModel::hydrateFromModel($model->getFederation()), $itemCollection->getCollection(), - $memberId + $memberId, + $messageId ); } @@ -378,4 +392,9 @@ public function getMemberId(): ?int { return $this->memberId; } + + public function getMessageId(): ?int + { + return $this->messageId; + } } diff --git a/application/YoshiKan/Application/Query/Member/Readmodel/WebConfigurationReadModel.php b/application/YoshiKan/Application/Query/Member/Readmodel/WebConfigurationReadModel.php index 15b4d4d..f13a888 100644 --- a/application/YoshiKan/Application/Query/Member/Readmodel/WebConfigurationReadModel.php +++ b/application/YoshiKan/Application/Query/Member/Readmodel/WebConfigurationReadModel.php @@ -18,7 +18,8 @@ class WebConfigurationReadModel implements \JsonSerializable public function __construct( protected LocationReadModelCollection $locations, protected PeriodReadModel $activePeriod, - protected SettingsReadModel $settings + protected SettingsReadModel $settings, + protected FederationReadModelCollection $federations ) { } @@ -32,6 +33,7 @@ public function jsonSerialize(): \stdClass $json->locations = $this->getLocations()->getCollection(); $json->activePeriod = $this->getActivePeriod(); $json->settings = $this->getSettings(); + $json->federations = $this->getFederations()->getCollection(); return $json; } @@ -54,4 +56,9 @@ public function getSettings(): SettingsReadModel { return $this->settings; } + + public function getFederations(): FederationReadModelCollection + { + return $this->federations; + } } diff --git a/application/YoshiKan/Application/Query/Member/get_subscription.php b/application/YoshiKan/Application/Query/Member/get_subscription.php index c6113db..4996cc6 100644 --- a/application/YoshiKan/Application/Query/Member/get_subscription.php +++ b/application/YoshiKan/Application/Query/Member/get_subscription.php @@ -15,6 +15,8 @@ use App\YoshiKan\Application\Query\Member\Readmodel\SubscriptionReadModel; use App\YoshiKan\Application\Query\Member\Readmodel\SubscriptionReadModelCollection; +use App\YoshiKan\Domain\Model\Member\SubscriptionStatus; +use PhpOffice\PhpSpreadsheet\Spreadsheet; trait get_subscription { @@ -41,4 +43,44 @@ public function getSubscriptionsByMemberId(int $memberId): SubscriptionReadModel return $query->getByMemberId($memberId); } + + public function getSubscriptionsByStatus(string $status): SubscriptionReadModelCollection + { + $this->permission->CheckRole(['ROLE_DEVELOPER', 'ROLE_ADMIN', 'ROLE_CHIEF_EDITOR']); + + $status = SubscriptionStatus::from($status); + $query = new GetSubscription( + $this->subscriptionRepository, + $this->memberRepository + ); + + return $query->getByStatus($status); + } + + public function exportSubscriptions(array $listIds): Spreadsheet + { + $this->permission->CheckRole(['ROLE_DEVELOPER', 'ROLE_ADMIN', 'ROLE_CHIEF_EDITOR']); + + $exporter = new ExportSubscriptions($this->subscriptionRepository, $this->periodRepository); + + return $exporter->exportSubscriptions($listIds); + } + + public function printSubscriptions(array $listIds): bool + { + $this->permission->CheckRole(['ROLE_DEVELOPER', 'ROLE_ADMIN', 'ROLE_CHIEF_EDITOR']); + + $document = new PrintSubscriptions( + $this->locationRepository, + $this->federationRepository, + $this->subscriptionRepository, + $this->twig, + $this->uploadFolder, + $this->entityManager, + ); + + $document->printOverview($listIds); + + return true; + } } diff --git a/application/YoshiKan/Application/Query/Message/GetMessage.php b/application/YoshiKan/Application/Query/Message/GetMessage.php index 170dfb8..e9ad964 100644 --- a/application/YoshiKan/Application/Query/Message/GetMessage.php +++ b/application/YoshiKan/Application/Query/Message/GetMessage.php @@ -15,9 +15,9 @@ use App\YoshiKan\Application\Query\Message\Readmodel\MessageReadModel; use App\YoshiKan\Application\Query\Message\Readmodel\MessageReadModelCollection; +use App\YoshiKan\Domain\Model\Member\MemberRepository; use App\YoshiKan\Domain\Model\Member\SubscriptionRepository; use App\YoshiKan\Domain\Model\Message\MessageRepository; -use App\YoshiKan\Infrastructure\Database\Member\MemberRepository; class GetMessage { diff --git a/application/YoshiKan/Application/Query/Reporting/GetReporting.php b/application/YoshiKan/Application/Query/Reporting/GetReporting.php new file mode 100644 index 0000000..e56d2fa --- /dev/null +++ b/application/YoshiKan/Application/Query/Reporting/GetReporting.php @@ -0,0 +1,114 @@ +federationRepository->getAll(); + $locations = $this->locationRepository->getAll(); + $period = $this->periodRepository->getActive(); + + $locationReports = []; + + $report = new DashboardNumbersReadmodel(PeriodReadModel::hydrateFromModel($period)); + + // set numbers about subscriptions + $webSubscriptions = $this->subscriptionRepository->getByStatus(SubscriptionStatus::NEW); + $duePaymentSubscriptions = $this->subscriptionRepository->getByStatus(SubscriptionStatus::AWAITING_PAYMENT); + $report->setNumbers(count($webSubscriptions), count($duePaymentSubscriptions)); + + /** @var Federation $federation */ + foreach ($federations as $federation) { + $federationRM = FederationReadModel::hydrateFromModel($federation); + $federationReport = new FederationReport($federationRM); + /** @var Location $location */ + foreach ($locations as $location) { + $locationRM = LocationReadModel::hydrateFromModel($location); + $activeMembers = $this->memberRepository->getActiveMembersByFederationAndLocation($federation, $location); + $duePayments = 0; + $totalAmount = 0; + foreach ($activeMembers as $activeMember) { + $subscriptions = $activeMember->getSubscriptions(); + /** @var Subscription $subscription */ + foreach ($subscriptions as $subscription) { + // -- count amount from the start of the active period + if ($subscription->getLicenseStart() >= $period->getStartDate() + || $subscription->getMemberSubscriptionStart() >= $period->getStartDate()) { + if (SubscriptionStatus::COMPLETE === $subscription->getStatus() + || SubscriptionStatus::PAYED === $subscription->getStatus() + || SubscriptionStatus::AWAITING_PAYMENT === $subscription->getStatus() + ) { + $totalAmount += $subscription->getTotal(); + if (SubscriptionStatus::AWAITING_PAYMENT === $subscription->getStatus()) { + $duePayments += $subscription->getTotal(); + } + } + } + } + } + $locationReport = new LocationReport($locationRM, count($activeMembers), $duePayments, $totalAmount); + $locationReports[] = $locationReport; + $federationReport->addLocationReport($locationReport); + } + $report->addFederationReport($federationReport); + } + /** @var LocationReport $locationReport */ + foreach ($this->mergeLocationReports($locationReports) as $locationReport) { + $report->addLocationReport($locationReport); + } + + return $report; + } + + private function mergeLocationReports(array $locationReports): array + { + $result = []; + $resultObject = []; + /** @var LocationReport $locationReport */ + foreach ($locationReports as $locationReport) { + if (isset($resultObject[$locationReport->getLocation()->getCode()])) { + $resultObject[$locationReport->getLocation()->getCode()]->activeMembers += $locationReport->getActiveMembers(); + $resultObject[$locationReport->getLocation()->getCode()]->duePayments += $locationReport->getDuePayments(); + $resultObject[$locationReport->getLocation()->getCode()]->totalAmount += $locationReport->getTotalAmount(); + } else { + $resultObject[$locationReport->getLocation()->getCode()] = new \stdClass(); + $resultObject[$locationReport->getLocation()->getCode()]->activeMembers = $locationReport->getActiveMembers(); + $resultObject[$locationReport->getLocation()->getCode()]->duePayments = $locationReport->getDuePayments(); + $resultObject[$locationReport->getLocation()->getCode()]->totalAmount = $locationReport->getTotalAmount(); + $resultObject[$locationReport->getLocation()->getCode()]->location = $locationReport->getLocation(); + } + } + foreach ($resultObject as $key => $value) { + $result[] = new LocationReport($value->location, $value->activeMembers, $value->duePayments, $value->totalAmount); + } + + return $result; + } +} diff --git a/application/YoshiKan/Application/Query/Reporting/Readmodel/DashboardNumbersReadmodel.php b/application/YoshiKan/Application/Query/Reporting/Readmodel/DashboardNumbersReadmodel.php new file mode 100644 index 0000000..72f446b --- /dev/null +++ b/application/YoshiKan/Application/Query/Reporting/Readmodel/DashboardNumbersReadmodel.php @@ -0,0 +1,106 @@ +federationReports[] = $federationReport; + $this->activeMembers += $federationReport->getActiveMembers(); + $this->duePayments += $federationReport->getDuePayments(); + $this->totalAmount += $federationReport->getTotalAmount(); + } + + public function addLocationReport(LocationReport $locationReport): void + { + $this->locationReports[] = $locationReport; + } + + public function setNumbers($numberOfWebSubscriptions, $numberOfDuePayments): void + { + $this->numberOfWebSubscriptions = $numberOfWebSubscriptions; + $this->numberOfDuePayments = $numberOfDuePayments; + } + + // ————————————————————————————————————————————————————————————————————————— + // What to render as json + // ————————————————————————————————————————————————————————————————————————— + + public function jsonSerialize(): \stdClass + { + $json = new \stdClass(); + $json->activePeriod = $this->activePeriod; + $json->federationReports = $this->getFederationReports(); + $json->locationReports = $this->getLocationReports(); + $json->activeMembers = $this->getActiveMembers(); + $json->duePayments = $this->getDuePayments(); + $json->totalAmount = $this->getTotalAmount(); + $json->numberOfWebSubscriptions = $this->getNumberOfWebSubscriptions(); + $json->numberOfDuePayments = $this->getNumberOfDuePayments(); + + return $json; + } + + // ————————————————————————————————————————————————————————————————————————— + // Getters + // ————————————————————————————————————————————————————————————————————————— + + public function getFederationReports(): array + { + return $this->federationReports; + } + + public function getLocationReports(): array + { + return $this->locationReports; + } + + public function getActiveMembers(): int + { + return $this->activeMembers; + } + + public function getDuePayments(): float + { + return $this->duePayments; + } + + public function getTotalAmount(): float + { + return $this->totalAmount; + } + + public function getActivePeriod(): PeriodReadModel + { + return $this->activePeriod; + } + + public function getNumberOfWebSubscriptions(): int + { + return $this->numberOfWebSubscriptions; + } + + public function getNumberOfDuePayments(): int + { + return $this->numberOfDuePayments; + } +} diff --git a/application/YoshiKan/Application/Query/Reporting/Readmodel/FederationReport.php b/application/YoshiKan/Application/Query/Reporting/Readmodel/FederationReport.php new file mode 100644 index 0000000..18c9175 --- /dev/null +++ b/application/YoshiKan/Application/Query/Reporting/Readmodel/FederationReport.php @@ -0,0 +1,74 @@ +locationReports[] = $locationReport; + $this->activeMembers += $locationReport->getActiveMembers(); + $this->duePayments += $locationReport->getDuePayments(); + $this->totalAmount += $locationReport->getTotalAmount(); + } + + // ————————————————————————————————————————————————————————————————————————— + // What to render as json + // ————————————————————————————————————————————————————————————————————————— + + public function jsonSerialize(): \stdClass + { + $json = new \stdClass(); + $json->federation = $this->getFederation(); + $json->locationReports = $this->getLocationReports(); + $json->activeMembers = $this->getActiveMembers(); + $json->duePayments = $this->getDuePayments(); + $json->totalAmount = $this->getTotalAmount(); + + return $json; + } + + // ————————————————————————————————————————————————————————————————————————— + // Getters + // ————————————————————————————————————————————————————————————————————————— + + public function getFederation(): FederationReadModel + { + return $this->federation; + } + + public function getLocationReports(): array + { + return $this->locationReports; + } + + public function getActiveMembers(): int + { + return $this->activeMembers; + } + + public function getDuePayments(): float + { + return $this->duePayments; + } + + public function getTotalAmount(): float + { + return $this->totalAmount; + } +} diff --git a/application/YoshiKan/Application/Query/Reporting/Readmodel/LocationReport.php b/application/YoshiKan/Application/Query/Reporting/Readmodel/LocationReport.php new file mode 100644 index 0000000..84ccc1f --- /dev/null +++ b/application/YoshiKan/Application/Query/Reporting/Readmodel/LocationReport.php @@ -0,0 +1,58 @@ +location = $this->getLocation(); + $json->activeMembers = $this->getActiveMembers(); + $json->duePayments = $this->getDuePayments(); + $json->totalAmount = $this->getTotalAmount(); + + return $json; + } + + // ————————————————————————————————————————————————————————————————————————— + // Getters + // ————————————————————————————————————————————————————————————————————————— + + public function getLocation(): LocationReadModel + { + return $this->location; + } + + public function getActiveMembers(): int + { + return $this->activeMembers; + } + + public function getDuePayments(): float + { + return $this->duePayments; + } + + public function getTotalAmount(): float + { + return $this->totalAmount; + } +} diff --git a/application/YoshiKan/Application/Query/Reporting/get_reporting.php b/application/YoshiKan/Application/Query/Reporting/get_reporting.php new file mode 100644 index 0000000..4124d92 --- /dev/null +++ b/application/YoshiKan/Application/Query/Reporting/get_reporting.php @@ -0,0 +1,26 @@ +permission->CheckRole(['ROLE_DEVELOPER', 'ROLE_ADMIN', 'ROLE_CHIEF_EDITOR']); + + $query = new GetReporting( + $this->memberRepository, + $this->locationRepository, + $this->federationRepository, + $this->subscriptionRepository, + $this->periodRepository + ); + + return $query->getDashboardNumbers(); + } +} diff --git a/application/YoshiKan/Application/QueryBus.php b/application/YoshiKan/Application/QueryBus.php index 8063dd5..992da46 100644 --- a/application/YoshiKan/Application/QueryBus.php +++ b/application/YoshiKan/Application/QueryBus.php @@ -16,6 +16,7 @@ use App\YoshiKan\Application\Query\Member\get_configuration; use App\YoshiKan\Application\Query\Member\get_member; use App\YoshiKan\Application\Security\BasePermissionService; +use App\YoshiKan\Domain\Model\Member\FederationRepository; use App\YoshiKan\Domain\Model\Member\GradeRepository; use App\YoshiKan\Domain\Model\Member\GroupRepository; use App\YoshiKan\Domain\Model\Member\LocationRepository; @@ -24,7 +25,6 @@ use App\YoshiKan\Domain\Model\Member\SettingsRepository; use App\YoshiKan\Domain\Model\Member\SubscriptionRepository; use App\YoshiKan\Domain\Model\Product\JudogiRepository; -use App\YoshiKan\Infrastructure\Database\Member\FederationRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Security\Core\Security; use Twig\Environment; diff --git a/application/YoshiKan/Application/Settings.php b/application/YoshiKan/Application/Settings.php new file mode 100644 index 0000000..ffa2042 --- /dev/null +++ b/application/YoshiKan/Application/Settings.php @@ -0,0 +1,10 @@ +uuid = $uuid; @@ -85,6 +97,7 @@ public static function make( string $code, string $name, int $yearlySubscriptionFee, + string $publicLabel, ): self { return new self( $uuid, @@ -92,6 +105,7 @@ public static function make( $code, $name, $yearlySubscriptionFee, + $publicLabel ); } @@ -99,10 +113,12 @@ public function change( string $code, string $name, int $yearlySubscriptionFee, + string $publicLabel, ): void { - $this->code = $code; - $this->name = $name; + $this->code = trim($code); + $this->name = trim($name); $this->yearlySubscriptionFee = $yearlySubscriptionFee; + $this->publicLabel = trim($publicLabel); } // ————————————————————————————————————————————————————————————————————————— @@ -113,16 +129,21 @@ public function change( // Getters // ————————————————————————————————————————————————————————————————————————— - public function getCode(): ?string + public function getCode(): string { return $this->code; } - public function getName(): ?string + public function getName(): string { return $this->name; } + public function getPublicLabel(): string + { + return $this->publicLabel; + } + public function getYearlySubscriptionFee(): ?int { return $this->yearlySubscriptionFee; diff --git a/application/YoshiKan/Domain/Model/Member/MemberRepository.php b/application/YoshiKan/Domain/Model/Member/MemberRepository.php index d560ba6..a7a6861 100644 --- a/application/YoshiKan/Domain/Model/Member/MemberRepository.php +++ b/application/YoshiKan/Domain/Model/Member/MemberRepository.php @@ -41,4 +41,6 @@ public function search( ): array; public function listActiveMembers(): array; + + public function getActiveMembersByFederationAndLocation(Federation $federation, Location $location): array; } diff --git a/application/YoshiKan/Domain/Model/Member/Subscription.php b/application/YoshiKan/Domain/Model/Member/Subscription.php index 547501c..5ee3a7e 100644 --- a/application/YoshiKan/Domain/Model/Member/Subscription.php +++ b/application/YoshiKan/Domain/Model/Member/Subscription.php @@ -628,9 +628,9 @@ public function isLicenseIsPayed(): bool return $this->licenseIsPayed; } - public function getMessages(): ?Collection + public function getMessages(): array { - return $this->messages; + return $this->messages->getValues(); } /** @@ -647,26 +647,59 @@ public function getItems(): array public function getSubscriptionFee(bool $withReduction = true): float { - $fee = 0; + $memberShipFee = 0; if ($this->memberSubscriptionIsHalfYear) { if (1 === $this->numberOfTraining) { - $fee = floatval($this->settings['halfYearlyFee1Training']); + $memberShipFee = floatval($this->settings['halfYearlyFee1Training']); } else { - $fee = floatval($this->settings['halfYearlyFee2Training']); + $memberShipFee = floatval($this->settings['halfYearlyFee2Training']); } } else { if (1 === $this->numberOfTraining) { - $fee = floatval($this->settings['yearlyFee1Training']); + $memberShipFee = floatval($this->settings['yearlyFee1Training']); } else { - $fee = floatval($this->settings['yearlyFee2Training']); + $memberShipFee = floatval($this->settings['yearlyFee2Training']); } } - if ($this->isReductionFamily && $withReduction) { - $reduction = floatval($this->settings['familyDiscount']) * $fee / 100; - $fee = ceil($fee - $reduction); + $reduction = floatval($this->settings['familyDiscount']) * $memberShipFee / 100; + $memberShipFee = ceil($memberShipFee - $reduction); + } + + return $memberShipFee; + } + + public function calculate(): float + { + $memberShipFee = $this->getSubscriptionFee(false); + + if ($this->isExtraTraining) { + $memberShipFee += floatval($this->settings['extraTrainingFee']); + } + if ($this->isReductionFamily) { + $reduction = floatval($this->settings['familyDiscount']) * $memberShipFee / 100; + $memberShipFee = ceil($memberShipFee - $reduction); + } + if ($this->isNewMember) { + $memberShipFee += floatval($this->settings['newMemberSubscriptionFee']); } - return $fee; + // -- set some total counts ------------------------------------------------------------------------------------ + + if ($this->isMemberSubscriptionIsPartSubscription()) { + $this->memberSubscriptionTotal = $memberShipFee; + } else { + $this->memberSubscriptionTotal = 0; + } + + if ($this->licenseIsPartSubscription()) { + $this->licenseTotal = $this->federation->getYearlySubscriptionFee(); + } else { + $this->licenseTotal = 0; + } + + $this->total = $this->memberSubscriptionTotal + $this->licenseTotal; + + return $this->total; } } diff --git a/application/YoshiKan/Domain/Model/Member/SubscriptionItemType.php b/application/YoshiKan/Domain/Model/Member/SubscriptionItemType.php index f3a49f6..5c5dbab 100644 --- a/application/YoshiKan/Domain/Model/Member/SubscriptionItemType.php +++ b/application/YoshiKan/Domain/Model/Member/SubscriptionItemType.php @@ -17,8 +17,9 @@ enum SubscriptionItemType: string { case LICENSE = 'license'; case MEMBERSHIP = 'membership'; - case EXTRA_TRAINING = 'extra_training'; + case EXTRA_TRAINING = 'extra-training'; case REDUCTION = 'reduction'; - case SUBSCRIPTION_WELCOME = 'subscription_welcome'; + case SUBSCRIPTION_WELCOME = 'subscription-welcome'; + case JUDOGI = 'judogi'; case OTHER = 'other'; } diff --git a/application/YoshiKan/Domain/Model/Member/SubscriptionStatus.php b/application/YoshiKan/Domain/Model/Member/SubscriptionStatus.php index 13ccd09..77c752f 100644 --- a/application/YoshiKan/Domain/Model/Member/SubscriptionStatus.php +++ b/application/YoshiKan/Domain/Model/Member/SubscriptionStatus.php @@ -14,7 +14,7 @@ enum SubscriptionStatus: string { case NEW = 'nieuw'; - case AWAITING_PAYMENT = 'wachtend op betaling'; + case AWAITING_PAYMENT = 'wachtend-op-betaling'; case PAYED = 'betaald'; case COMPLETE = 'afgewerkt'; case CANCELED = 'canceled'; diff --git a/application/YoshiKan/Domain/Model/Member/SubscriptionType.php b/application/YoshiKan/Domain/Model/Member/SubscriptionType.php index 85e5fa5..2837f44 100644 --- a/application/YoshiKan/Domain/Model/Member/SubscriptionType.php +++ b/application/YoshiKan/Domain/Model/Member/SubscriptionType.php @@ -13,8 +13,8 @@ enum SubscriptionType: string { - case NEW_SUBSCRIPTION = 'nieuwe_inschrijving'; - case RENEWAL_FULL = 'volledige_hernieuwing'; - case RENEWAL_MEMBERSHIP = 'hernieuwing_lidmaatschap'; - case RENEWAL_LICENSE = 'hernieuwing_vergunning'; + case NEW_SUBSCRIPTION = 'nieuwe-inschrijving'; + case RENEWAL_FULL = 'volledige-hernieuwing'; + case RENEWAL_MEMBERSHIP = 'hernieuwing-lidmaatschap'; + case RENEWAL_LICENSE = 'hernieuwing-vergunning'; } diff --git a/application/YoshiKan/Infrastructure/Database/Member/FederationRepository.php b/application/YoshiKan/Infrastructure/Database/Member/FederationRepository.php index aee01b4..fc105a6 100644 --- a/application/YoshiKan/Infrastructure/Database/Member/FederationRepository.php +++ b/application/YoshiKan/Infrastructure/Database/Member/FederationRepository.php @@ -84,7 +84,7 @@ public function getByUuid(Uuid $uuid): Federation public function getAll(): array { $q = $this->createQueryBuilder('t')->andWhere('0 = 0'); - $q->addOrderBy('t.id', 'DESC'); + $q->addOrderBy('t.sequence', 'ASC'); return $q->getQuery()->getResult(); } diff --git a/application/YoshiKan/Infrastructure/Database/Member/MemberRepository.php b/application/YoshiKan/Infrastructure/Database/Member/MemberRepository.php index 10a5dea..c3ca847 100644 --- a/application/YoshiKan/Infrastructure/Database/Member/MemberRepository.php +++ b/application/YoshiKan/Infrastructure/Database/Member/MemberRepository.php @@ -13,6 +13,7 @@ namespace App\YoshiKan\Infrastructure\Database\Member; +use App\YoshiKan\Domain\Model\Member\Federation; use App\YoshiKan\Domain\Model\Member\Grade; use App\YoshiKan\Domain\Model\Member\Location; use App\YoshiKan\Domain\Model\Member\Member; @@ -166,4 +167,18 @@ public function listActiveMembers(): array return $q->getQuery()->getResult(); } + + public function getActiveMembersByFederationAndLocation(Federation $federation, Location $location): array + { + $q = $this->createQueryBuilder('t') + ->where('t.status = :status') + ->setParameter('status', MemberStatus::ACTIVE->value) + ->andWhere('t.location = :locationId') + ->setParameter('locationId', $location->getId()) + ->andWhere('t.federation = :federationId') + ->setParameter('federationId', $federation->getId()) + ->addOrderBy('t.id', 'DESC'); + + return $q->getQuery()->getResult(); + } } diff --git a/application/YoshiKan/Infrastructure/Templates/mail/member_extendMemberSubscription_mail.html.twig b/application/YoshiKan/Infrastructure/Templates/mail/member_extendMemberSubscription_mail.html.twig index e7997f1..3cbdd05 100644 --- a/application/YoshiKan/Infrastructure/Templates/mail/member_extendMemberSubscription_mail.html.twig +++ b/application/YoshiKan/Infrastructure/Templates/mail/member_extendMemberSubscription_mail.html.twig @@ -10,13 +10,13 @@ Beste {{ subscription.contactFirstname }} {{ subscription.contactLastname }},
- {% if(subscription.type.value == 'hernieuwing_lidmaatschap') %} + {% if(subscription.type.value == 'hernieuwing-lidmaatschap') %} Het lidmaatschap {% endif %} - {% if(subscription.type.value == 'hernieuwing_vergunning') %} + {% if(subscription.type.value == 'hernieuwing-vergunning') %} De vergunning {% endif %} - {% if(subscription.type.value == 'volledige_hernieuwing') %} + {% if(subscription.type.value == 'volledige-hernieuwing') %} Het lidmaatschap en de vergunning {% endif %} is verlopen of staat op het punt binnenkort te verlopen. diff --git a/application/YoshiKan/Infrastructure/Templates/mail/web_confirmation_mail.html.twig b/application/YoshiKan/Infrastructure/Templates/mail/web_confirmation_mail.html.twig index 530bfe7..a114912 100644 --- a/application/YoshiKan/Infrastructure/Templates/mail/web_confirmation_mail.html.twig +++ b/application/YoshiKan/Infrastructure/Templates/mail/web_confirmation_mail.html.twig @@ -3,53 +3,46 @@ {% block title %}{{ subject }}{% endblock %} {% block body %} -
Dag {{ subscription.contactFirstname }},
+ + {# judoka ------------------------------------------------------------------------------------------------------ #} +Beste {{ subscription.contactLastname }} {{ subscription.contactFirstname }},
- Bedankt voor je {% if(subscription.isNewMember) %}{% else %}(her){% endif %}inschrijving van - {{ subscription.firstname }} {{ subscription.lastname }} - (° {{ subscription.dateOfBirth | date('d/m/Y') }} - {% if(subscription.isNewMember) %}- {{ subscription.gender.value }}{% endif %}) - {% if(subscription.isNewMember) %} - als nieuw lid. - {% else %} - . - {% endif %} + Bedankt voor je inschrijving van + {{ subscription.lastname | upper }} {{ subscription.firstname }} + (° {{ subscription.dateOfBirth | date('d/m/Y') }} - {{ subscription.gender.value }}) + als nieuw lid.
{# extra paragraph for a new member ----------------------------------------------------------------------------- #} - {% if(subscription.isNewMember) %} -
- Contact
-
{{ subscription.contactFirstname }} {{ subscription.contactLastname }}
-
{{ subscription.addressStreet }} {{ subscription.addressNumber }}
- {% if(subscription.addressBox) != '' %} bus {{ subscription.adressBox }}{% endif %}
-
{{ subscription.addressZip }} {{ subscription.addressCity }}
-
+ Contact
+
{{ subscription.contactLastname }} {{ subscription.contactFirstname }}
+
{{ subscription.addressStreet }} {{ subscription.addressNumber }}
+ {% if(subscription.addressBox) != '' %} bus {{ subscription.adressBox }}{% endif %}
+
{{ subscription.addressZip }} {{ subscription.addressCity }}
+
Referentie: YKS-{{ subscription.id }}
-
{{ subscription.period.name }}
- Locatie: {{ subscription.location.name }}
-
- Type: {{ subscription.type.value }},
- {% if(subscription.numberOfTraining == 1) %}
- 1 training per week
+
- Lidmaatschap:
+ {% if(subscription.memberSubscriptionIsHalfYear == 1) %}
+ Halfjaarlijks
{% else %}
- 2 trainingen per week
- {% endif %}
- {% if(subscription.isExtraTraining) %}
-
- Extra supplement 3 tot 5 trainingen per week.
- {% endif %}
- {% if(subscription.isNewMember) %}
-
- Inschrijvingspakket: judogids, judopaspoort, leskaart + sportzak voor judopak.
+ Jaarlijks
{% endif %}
- {% if(subscription.isJudogiBelt) %}
-
- Judopak + gordel
+ {% if(subscription.numberOfTraining == 1) %}
+
- 1 training per week
+ {% elseif(subscription.numberOfTraining == 2) %}
+
- 2 trainingen per week
+ {% else %}
+
- 3 tot 5 trainingen per week.
{% endif %}
+
- Inschrijvingspakket: judogids, judopaspoort, leskaart + sportzak voor judopak.
{% if(subscription.isReductionFamily) %}
- 10% Korting als 2e en 3e kind van éénzelfde familie.
{% endif %}
We kijken alles nog eens na. Van zodra alles in orde is, krijg je van ons een definitieve bevestiging en de betalingsinstructies via email. @@ -58,7 +51,7 @@ Tot binnenkort.
- Met vriendelijke groeten,
+ Met sportieve groeten,
Team Yoshi-Kan.
+
+
+
+
+
+ Door het ondertekenen of het uitvoeren van de betaling, gaat U akkoord met ons
+ reglement en privacy verklaring, terug te vinden op onze website www.yoshi-kan.be
+
+ |
+
+
+ JC Yoshi-Kan v.z.w.
+
+
+
+ Secr: Spekstraat 80 + 2220 Heist o/d Berg + + + G 0474 51 13 98 + E judo.yoshikan@gmail.com +
+
+ IBAN: BE37 7330 0101 8328 + BIC: KRED BE BB +
+
+
+
+ Totaal:
+ {{ subscription.total }} €
+
+
+
+ Handtekening ouder of voogd:
+
+ + |
+
We hebben je inschrijving goed ontvangen.
-
We hebben ook een email als bevestiging verzonden naar {{
+
We hebben ook een bevestiging verzonden naar {{
subscription.contactEmail
}}.
Dit is je referentie
@@ -372,36 +478,39 @@ import axios from "axios";
const step = ref(0);
const isSaving = ref(false);
const settings = ref({});
+
const subscription = ref({
- periodId: 0,
+ type: 'nieuwe-inschrijving',
+ federationId: 2,
+ locationId: 1,
contactFirstname: '',
contactLastname: '',
contactEmail: '',
contactPhone: '',
+ addressStreet: '',
+ addressNumber: '',
+ addressBox: '',
+ addressZip: '',
+ addressCity: '',
firstname: '',
lastname: '',
+ nationalRegisterNumber: '',
dateOfBirthDD: '',
dateOfBirthMM: '',
dateOfBirthYYYY: '',
dateOfBirth: new Date(),
- location: 1,
- type: 'full',
+ gender: 'X',
+ memberSubscriptionIsHalfYear: false,
numberOfTraining: 1,
- extraTraining: false,
- reductionFamily: false,
- judogiBelt: false,
+ isExtraTraining: false,
+ isNewMember: true,
+ isReductionFamily: false,
+ total: 0,
remarks: '',
+ isJudogiBelt: false,
honeyPot: '',
- // extra fields for a subscription (only for new members)
- newMember: true,
- gender: 'X',
- nationalRegisterNumber: '',
- addressStreet: '',
- addressNumber: '',
- addressBox: '',
- addressZip: '',
- addressCity: ''
});
+
const subscriptionResult = ref({});
const options = {
postProcess: val => {
@@ -447,8 +556,8 @@ function loadSettings() {
function loadSettingsHandler(data) {
settings.value = data;
step.value = 1;
- subscription.value.periodId = settings.value.activePeriod.id;
- subscription.value.location = settings.value.locations[0].id;
+ subscription.value.locationId = settings.value.locations[0].id;
+ subscription.value.federationId = settings.value.federations[0].id;
}
function subscribe() {
@@ -477,22 +586,19 @@ function resetSubscription() {
subscription.value.dateOfBirthMM = '';
subscription.value.dateOfBirthYYYY = '';
subscription.value.dateOfBirth = new Date();
- subscription.value.gender = 'M';
+ subscription.value.gender = 'X';
subscription.value.newMember = true;
- subscription.value.type = 'full';
subscription.value.numberOfTraining = 1;
subscription.value.extraTraining = false;
subscription.value.reductionFamily = false;
subscription.value.judogiBelt = false;
subscription.value.remarks = '';
- // reset the extra fields
subscription.value.nationalRegisterNumber = '';
subscription.value.addressStreet = '';
subscription.value.addressNumber = '';
subscription.value.addressBox = '';
subscription.value.addressZip = '';
subscription.value.addressCity = '';
-
step.value = 1;
}
@@ -522,14 +628,9 @@ const dateValidator = function (value) {
};
const nationalRegisterNumberValidator = function (value) {
- if (!subscription.value.newMember) return true;
return (subscription.value.nationalRegisterNumber.length === 15);
}
-function isNewMember() {
- return subscription.value.newMember;
-}
-
const rules = {
contactFirstname: {required},
contactLastname: {required},
@@ -538,13 +639,14 @@ const rules = {
lastname: {required},
dateOfBirthDD: {required, numeric, maxValueValue: maxValue(31), minValueValue: minValue(1), dateValidator},
dateOfBirthMM: {required, numeric, maxValueValue: maxValue(12), minValueValue: minValue(1), dateValidator},
- dateOfBirthYYYY: {required, numeric, minValueValue: minValue(1900), maxValueValue: maxValue(2200), dateValidator},
- location: {minValueValue: minValue(1)},
- nationalRegisterNumber: {requiredNewMember: requiredIf(isNewMember), nationalRegisterNumberValidator},
- addressStreet: {requiredNewMember: requiredIf(isNewMember)},
- addressNumber: {requiredNewMember: requiredIf(isNewMember)},
- addressZip: {requiredNewMember: requiredIf(isNewMember)},
- addressCity: {requiredNewMember: requiredIf(isNewMember)},
+ dateOfBirthYYYY: {required, numeric, minValueValue: minValue(1900), maxValueValue: maxValue(2400), dateValidator},
+ nationalRegisterNumber: {nationalRegisterNumberValidator},
+ addressStreet: {required},
+ addressNumber: {required},
+ addressZip: {required},
+ addressCity: {required},
+ locationId: {minValueValue: minValue(1)},
+ federationId: {minValueValue: minValue(1)},
};
const v$ = useVuelidate(rules, subscription);
diff --git a/composer.lock b/composer.lock
index 17475b7..f86cab2 100644
--- a/composer.lock
+++ b/composer.lock
@@ -8394,16 +8394,16 @@
},
{
"name": "symfony/cache",
- "version": "v5.4.28",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache.git",
- "reference": "62b7ae3bccc5b474a30fadc7ef6bbc362007d3f9"
+ "reference": "e29c5a97bc2d81269973c3e1d7ceb9d48b4d5151"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/cache/zipball/62b7ae3bccc5b474a30fadc7ef6bbc362007d3f9",
- "reference": "62b7ae3bccc5b474a30fadc7ef6bbc362007d3f9",
+ "url": "https://api.github.com/repos/symfony/cache/zipball/e29c5a97bc2d81269973c3e1d7ceb9d48b4d5151",
+ "reference": "e29c5a97bc2d81269973c3e1d7ceb9d48b4d5151",
"shasum": ""
},
"require": {
@@ -8471,7 +8471,7 @@
"psr6"
],
"support": {
- "source": "https://github.com/symfony/cache/tree/v5.4.28"
+ "source": "https://github.com/symfony/cache/tree/v5.4.29"
},
"funding": [
{
@@ -8487,7 +8487,7 @@
"type": "tidelift"
}
],
- "time": "2023-08-05T08:32:42+00:00"
+ "time": "2023-09-19T13:25:51+00:00"
},
{
"name": "symfony/cache-contracts",
@@ -8827,16 +8827,16 @@
},
{
"name": "symfony/dependency-injection",
- "version": "v5.4.28",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/dependency-injection.git",
- "reference": "addc22fed594f9ce04e73ef6a9d3e2416f77192d"
+ "reference": "338638ed8c9d5c7fcb136a73f5c7043465ae2f05"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/addc22fed594f9ce04e73ef6a9d3e2416f77192d",
- "reference": "addc22fed594f9ce04e73ef6a9d3e2416f77192d",
+ "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/338638ed8c9d5c7fcb136a73f5c7043465ae2f05",
+ "reference": "338638ed8c9d5c7fcb136a73f5c7043465ae2f05",
"shasum": ""
},
"require": {
@@ -8896,7 +8896,7 @@
"description": "Allows you to standardize and centralize the way objects are constructed in your application",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/dependency-injection/tree/v5.4.28"
+ "source": "https://github.com/symfony/dependency-injection/tree/v5.4.29"
},
"funding": [
{
@@ -8912,7 +8912,7 @@
"type": "tidelift"
}
],
- "time": "2023-08-14T10:47:38+00:00"
+ "time": "2023-09-20T06:23:43+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -9170,16 +9170,16 @@
},
{
"name": "symfony/error-handler",
- "version": "v5.4.26",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
- "reference": "b26719213a39c9ba57520cbc5e52bfcc5e8d92f9"
+ "reference": "328c6fcfd2f90b64c16efaf0ea67a311d672f078"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/error-handler/zipball/b26719213a39c9ba57520cbc5e52bfcc5e8d92f9",
- "reference": "b26719213a39c9ba57520cbc5e52bfcc5e8d92f9",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/328c6fcfd2f90b64c16efaf0ea67a311d672f078",
+ "reference": "328c6fcfd2f90b64c16efaf0ea67a311d672f078",
"shasum": ""
},
"require": {
@@ -9221,7 +9221,7 @@
"description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/error-handler/tree/v5.4.26"
+ "source": "https://github.com/symfony/error-handler/tree/v5.4.29"
},
"funding": [
{
@@ -9237,7 +9237,7 @@
"type": "tidelift"
}
],
- "time": "2023-07-16T16:48:57+00:00"
+ "time": "2023-09-06T21:54:06+00:00"
},
{
"name": "symfony/event-dispatcher",
@@ -9657,16 +9657,16 @@
},
{
"name": "symfony/form",
- "version": "v5.4.26",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/form.git",
- "reference": "ca99a6874695aa266044a28ff945176cf480d9bf"
+ "reference": "57baf5a73a68c24dfe12d0d93bec119ec71acb48"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/form/zipball/ca99a6874695aa266044a28ff945176cf480d9bf",
- "reference": "ca99a6874695aa266044a28ff945176cf480d9bf",
+ "url": "https://api.github.com/repos/symfony/form/zipball/57baf5a73a68c24dfe12d0d93bec119ec71acb48",
+ "reference": "57baf5a73a68c24dfe12d0d93bec119ec71acb48",
"shasum": ""
},
"require": {
@@ -9739,7 +9739,7 @@
"description": "Allows to easily create, process and reuse HTML forms",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/form/tree/v5.4.26"
+ "source": "https://github.com/symfony/form/tree/v5.4.29"
},
"funding": [
{
@@ -9755,20 +9755,20 @@
"type": "tidelift"
}
],
- "time": "2023-07-26T07:54:04+00:00"
+ "time": "2023-09-10T17:22:50+00:00"
},
{
"name": "symfony/framework-bundle",
- "version": "v5.4.28",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/framework-bundle.git",
- "reference": "b84ebb25405c7334976b5791bfbbe0e50f4e472c"
+ "reference": "63e4ad1386fd4f31a005d751cd4dc016f9f2346e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/b84ebb25405c7334976b5791bfbbe0e50f4e472c",
- "reference": "b84ebb25405c7334976b5791bfbbe0e50f4e472c",
+ "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/63e4ad1386fd4f31a005d751cd4dc016f9f2346e",
+ "reference": "63e4ad1386fd4f31a005d751cd4dc016f9f2346e",
"shasum": ""
},
"require": {
@@ -9889,7 +9889,7 @@
"description": "Provides a tight integration between Symfony components and the Symfony full-stack framework",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/framework-bundle/tree/v5.4.28"
+ "source": "https://github.com/symfony/framework-bundle/tree/v5.4.29"
},
"funding": [
{
@@ -9905,20 +9905,20 @@
"type": "tidelift"
}
],
- "time": "2023-08-08T11:21:07+00:00"
+ "time": "2023-09-27T13:49:58+00:00"
},
{
"name": "symfony/http-client",
- "version": "v5.4.26",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
- "reference": "19d48ef7f38e5057ed1789a503cd3eccef039bce"
+ "reference": "04784c66cbee613a827363ee1e65db65392893c1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-client/zipball/19d48ef7f38e5057ed1789a503cd3eccef039bce",
- "reference": "19d48ef7f38e5057ed1789a503cd3eccef039bce",
+ "url": "https://api.github.com/repos/symfony/http-client/zipball/04784c66cbee613a827363ee1e65db65392893c1",
+ "reference": "04784c66cbee613a827363ee1e65db65392893c1",
"shasum": ""
},
"require": {
@@ -9980,7 +9980,7 @@
"http"
],
"support": {
- "source": "https://github.com/symfony/http-client/tree/v5.4.26"
+ "source": "https://github.com/symfony/http-client/tree/v5.4.29"
},
"funding": [
{
@@ -9996,7 +9996,7 @@
"type": "tidelift"
}
],
- "time": "2023-07-03T12:14:50+00:00"
+ "time": "2023-09-14T20:49:15+00:00"
},
{
"name": "symfony/http-client-contracts",
@@ -10154,16 +10154,16 @@
},
{
"name": "symfony/http-kernel",
- "version": "v5.4.28",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "127a2322ca1828157901092518b8ea8e4e1109d4"
+ "reference": "f53265fc6bd2a7f3a4ed4e443b76e750348ac3f7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/127a2322ca1828157901092518b8ea8e4e1109d4",
- "reference": "127a2322ca1828157901092518b8ea8e4e1109d4",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f53265fc6bd2a7f3a4ed4e443b76e750348ac3f7",
+ "reference": "f53265fc6bd2a7f3a4ed4e443b76e750348ac3f7",
"shasum": ""
},
"require": {
@@ -10246,7 +10246,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-kernel/tree/v5.4.28"
+ "source": "https://github.com/symfony/http-kernel/tree/v5.4.29"
},
"funding": [
{
@@ -10262,7 +10262,7 @@
"type": "tidelift"
}
],
- "time": "2023-08-26T13:47:51+00:00"
+ "time": "2023-09-30T06:31:17+00:00"
},
{
"name": "symfony/intl",
@@ -10828,16 +10828,16 @@
},
{
"name": "symfony/password-hasher",
- "version": "v5.4.27",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/password-hasher.git",
- "reference": "6dd2daf41b44384752f6b59e8ad3e56ffb81e35c"
+ "reference": "57cd0369af1a92b87c1e960251ebdf9457225bfd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/password-hasher/zipball/6dd2daf41b44384752f6b59e8ad3e56ffb81e35c",
- "reference": "6dd2daf41b44384752f6b59e8ad3e56ffb81e35c",
+ "url": "https://api.github.com/repos/symfony/password-hasher/zipball/57cd0369af1a92b87c1e960251ebdf9457225bfd",
+ "reference": "57cd0369af1a92b87c1e960251ebdf9457225bfd",
"shasum": ""
},
"require": {
@@ -10882,7 +10882,7 @@
"password"
],
"support": {
- "source": "https://github.com/symfony/password-hasher/tree/v5.4.27"
+ "source": "https://github.com/symfony/password-hasher/tree/v5.4.29"
},
"funding": [
{
@@ -10898,7 +10898,7 @@
"type": "tidelift"
}
],
- "time": "2023-07-28T14:44:35+00:00"
+ "time": "2023-09-19T07:33:45+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -12129,16 +12129,16 @@
},
{
"name": "symfony/security-bundle",
- "version": "v5.4.26",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-bundle.git",
- "reference": "38d674b6150ebd638f7c517b19790ac631f8dc35"
+ "reference": "8f432fae02bd908c1decea70a359d85a9be37f2c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/security-bundle/zipball/38d674b6150ebd638f7c517b19790ac631f8dc35",
- "reference": "38d674b6150ebd638f7c517b19790ac631f8dc35",
+ "url": "https://api.github.com/repos/symfony/security-bundle/zipball/8f432fae02bd908c1decea70a359d85a9be37f2c",
+ "reference": "8f432fae02bd908c1decea70a359d85a9be37f2c",
"shasum": ""
},
"require": {
@@ -12211,7 +12211,7 @@
"description": "Provides a tight integration of the Security component into the Symfony full-stack framework",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/security-bundle/tree/v5.4.26"
+ "source": "https://github.com/symfony/security-bundle/tree/v5.4.29"
},
"funding": [
{
@@ -12227,20 +12227,20 @@
"type": "tidelift"
}
],
- "time": "2023-07-05T15:49:26+00:00"
+ "time": "2023-09-19T07:33:45+00:00"
},
{
"name": "symfony/security-core",
- "version": "v5.4.22",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-core.git",
- "reference": "a801d525c7545332e2ddf7f52c163959354b1650"
+ "reference": "f8421bb428f48655d2b327d4bf58870983eef84a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/security-core/zipball/a801d525c7545332e2ddf7f52c163959354b1650",
- "reference": "a801d525c7545332e2ddf7f52c163959354b1650",
+ "url": "https://api.github.com/repos/symfony/security-core/zipball/f8421bb428f48655d2b327d4bf58870983eef84a",
+ "reference": "f8421bb428f48655d2b327d4bf58870983eef84a",
"shasum": ""
},
"require": {
@@ -12304,7 +12304,7 @@
"description": "Symfony Security Component - Core Library",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/security-core/tree/v5.4.22"
+ "source": "https://github.com/symfony/security-core/tree/v5.4.29"
},
"funding": [
{
@@ -12320,7 +12320,7 @@
"type": "tidelift"
}
],
- "time": "2023-03-10T10:02:45+00:00"
+ "time": "2023-09-10T17:22:50+00:00"
},
{
"name": "symfony/security-csrf",
@@ -12550,16 +12550,16 @@
},
{
"name": "symfony/serializer",
- "version": "v5.4.28",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/serializer.git",
- "reference": "701e2b8d48a3a627ffe128b38fbe6c4cf3ddcb3c"
+ "reference": "b893175000155839170d589f3a3593f2ce601829"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/serializer/zipball/701e2b8d48a3a627ffe128b38fbe6c4cf3ddcb3c",
- "reference": "701e2b8d48a3a627ffe128b38fbe6c4cf3ddcb3c",
+ "url": "https://api.github.com/repos/symfony/serializer/zipball/b893175000155839170d589f3a3593f2ce601829",
+ "reference": "b893175000155839170d589f3a3593f2ce601829",
"shasum": ""
},
"require": {
@@ -12633,7 +12633,7 @@
"description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/serializer/tree/v5.4.28"
+ "source": "https://github.com/symfony/serializer/tree/v5.4.29"
},
"funding": [
{
@@ -12649,7 +12649,7 @@
"type": "tidelift"
}
],
- "time": "2023-08-24T14:14:18+00:00"
+ "time": "2023-09-20T07:40:46+00:00"
},
{
"name": "symfony/service-contracts",
@@ -12798,16 +12798,16 @@
},
{
"name": "symfony/string",
- "version": "v5.4.26",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "1181fe9270e373537475e826873b5867b863883c"
+ "reference": "e41bdc93def20eaf3bfc1537c4e0a2b0680a152d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/1181fe9270e373537475e826873b5867b863883c",
- "reference": "1181fe9270e373537475e826873b5867b863883c",
+ "url": "https://api.github.com/repos/symfony/string/zipball/e41bdc93def20eaf3bfc1537c4e0a2b0680a152d",
+ "reference": "e41bdc93def20eaf3bfc1537c4e0a2b0680a152d",
"shasum": ""
},
"require": {
@@ -12864,7 +12864,7 @@
"utf8"
],
"support": {
- "source": "https://github.com/symfony/string/tree/v5.4.26"
+ "source": "https://github.com/symfony/string/tree/v5.4.29"
},
"funding": [
{
@@ -12880,7 +12880,7 @@
"type": "tidelift"
}
],
- "time": "2023-06-28T12:46:07+00:00"
+ "time": "2023-09-13T11:47:41+00:00"
},
{
"name": "symfony/translation",
@@ -13059,16 +13059,16 @@
},
{
"name": "symfony/twig-bridge",
- "version": "v5.4.26",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bridge.git",
- "reference": "832461a5f556df7933fd82e75b097d76182c640b"
+ "reference": "8e94856da373b63e7ba69e51a6c4f834d991cd58"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/832461a5f556df7933fd82e75b097d76182c640b",
- "reference": "832461a5f556df7933fd82e75b097d76182c640b",
+ "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/8e94856da373b63e7ba69e51a6c4f834d991cd58",
+ "reference": "8e94856da373b63e7ba69e51a6c4f834d991cd58",
"shasum": ""
},
"require": {
@@ -13160,7 +13160,7 @@
"description": "Provides integration for Twig with various Symfony components",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/twig-bridge/tree/v5.4.26"
+ "source": "https://github.com/symfony/twig-bridge/tree/v5.4.29"
},
"funding": [
{
@@ -13176,7 +13176,7 @@
"type": "tidelift"
}
],
- "time": "2023-07-20T16:28:53+00:00"
+ "time": "2023-09-06T21:54:06+00:00"
},
{
"name": "symfony/twig-bundle",
@@ -13344,16 +13344,16 @@
},
{
"name": "symfony/validator",
- "version": "v5.4.28",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/validator.git",
- "reference": "0acdcb86a8fc5ffd71c3b060184d2ed20a76a2c9"
+ "reference": "287ef8c80d1c23d5d1db8c3e26a8ad56680d535b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/validator/zipball/0acdcb86a8fc5ffd71c3b060184d2ed20a76a2c9",
- "reference": "0acdcb86a8fc5ffd71c3b060184d2ed20a76a2c9",
+ "url": "https://api.github.com/repos/symfony/validator/zipball/287ef8c80d1c23d5d1db8c3e26a8ad56680d535b",
+ "reference": "287ef8c80d1c23d5d1db8c3e26a8ad56680d535b",
"shasum": ""
},
"require": {
@@ -13436,7 +13436,7 @@
"description": "Provides tools to validate values",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/validator/tree/v5.4.28"
+ "source": "https://github.com/symfony/validator/tree/v5.4.29"
},
"funding": [
{
@@ -13452,20 +13452,20 @@
"type": "tidelift"
}
],
- "time": "2023-08-14T13:04:17+00:00"
+ "time": "2023-09-10T17:22:50+00:00"
},
{
"name": "symfony/var-dumper",
- "version": "v5.4.28",
+ "version": "v5.4.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
- "reference": "684b36ff415e1381d4a943c3ca2502cd2debad73"
+ "reference": "6172e4ae3534d25ee9e07eb487c20be7760fcc65"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/684b36ff415e1381d4a943c3ca2502cd2debad73",
- "reference": "684b36ff415e1381d4a943c3ca2502cd2debad73",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6172e4ae3534d25ee9e07eb487c20be7760fcc65",
+ "reference": "6172e4ae3534d25ee9e07eb487c20be7760fcc65",
"shasum": ""
},
"require": {
@@ -13525,7 +13525,7 @@
"dump"
],
"support": {
- "source": "https://github.com/symfony/var-dumper/tree/v5.4.28"
+ "source": "https://github.com/symfony/var-dumper/tree/v5.4.29"
},
"funding": [
{
@@ -13541,7 +13541,7 @@
"type": "tidelift"
}
],
- "time": "2023-08-24T13:38:36+00:00"
+ "time": "2023-09-12T10:09:58+00:00"
},
{
"name": "symfony/var-exporter",
@@ -15060,16 +15060,16 @@
},
{
"name": "phpstan/phpstan",
- "version": "1.10.36",
+ "version": "1.10.37",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
- "reference": "ffa3089511121a672e62969404e4fddc753f9b15"
+ "reference": "058ba07e92f744d4dcf6061ae75283d0c6456f2e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ffa3089511121a672e62969404e4fddc753f9b15",
- "reference": "ffa3089511121a672e62969404e4fddc753f9b15",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/058ba07e92f744d4dcf6061ae75283d0c6456f2e",
+ "reference": "058ba07e92f744d4dcf6061ae75283d0c6456f2e",
"shasum": ""
},
"require": {
@@ -15118,7 +15118,7 @@
"type": "tidelift"
}
],
- "time": "2023-09-29T14:07:45+00:00"
+ "time": "2023-10-02T16:18:37+00:00"
},
{
"name": "phpstan/phpstan-doctrine",
diff --git a/frontends/member_module/package.json b/frontends/member_module/package.json
index 22805b2..2599ab1 100644
--- a/frontends/member_module/package.json
+++ b/frontends/member_module/package.json
@@ -29,6 +29,7 @@
"@zxcvbn-ts/language-common": "^2.0.0",
"@zxcvbn-ts/language-en": "^2.0.0",
"axios": "^0.27.2",
+ "chart.js": "^4.4.0",
"dropzone": "^6.0.0-beta.2",
"html-to-image": "^1.10.10",
"maska": "^2.1.7",
@@ -36,7 +37,7 @@
"nomnoml": "^1.5.2",
"pinia": "^2.0.21",
"primeicons": "^6.0.0",
- "primevue": "^3.17.0",
+ "primevue": "3.34.1",
"simple-syntax-highlighter": "^2.2.3",
"vue": "^3.2.38",
"vue-advanced-cropper": "^2.8.6",
diff --git a/frontends/member_module/src/App.vue b/frontends/member_module/src/App.vue
index 98063a9..05d6925 100644
--- a/frontends/member_module/src/App.vue
+++ b/frontends/member_module/src/App.vue
@@ -25,6 +25,12 @@