From 93b97106c729664dd89772eefe7dc52db7e117e0 Mon Sep 17 00:00:00 2001 From: vgreb Date: Mon, 5 Jan 2026 10:39:11 +0100 Subject: [PATCH] Refonte - class Cotisations --- sources/Afup/Association/Cotisations.php | 223 +++++------------- .../Afup/Association/CotisationsFactory.php | 3 + .../SubscriptionManagement.php | 9 +- .../UserMembership/SeniorityComputer.php | 21 +- .../UserMembership/UserService.php | 8 +- .../Controller/Website/Member/IndexAction.php | 5 +- .../Website/Membership/Fee/DownloadAction.php | 12 +- .../Website/Membership/Fee/IndexAction.php | 30 +-- .../Membership/GeneralMeeting/IndexAction.php | 2 +- .../Website/Membership/InvoiceAction.php | 5 +- .../Membership/PayboxCallbackAction.php | 5 +- .../Website/Membership/PaymentAction.php | 10 +- .../MembershipFee/Model/MembershipFee.php | 12 + .../Repository/MembershipFeeRepository.php | 54 +++++ .../membership/membershipfee.html.twig | 12 +- .../site/company_membership/payment.html.twig | 8 +- .../unit/Afup/Association/CotisationsTest.php | 6 +- 17 files changed, 195 insertions(+), 230 deletions(-) diff --git a/sources/Afup/Association/Cotisations.php b/sources/Afup/Association/Cotisations.php index 623af14be..5b438d0b8 100644 --- a/sources/Afup/Association/Cotisations.php +++ b/sources/Afup/Association/Cotisations.php @@ -20,6 +20,8 @@ use AppBundle\Email\Mailer\MailUser; use AppBundle\Email\Mailer\MailUserFactory; use AppBundle\Email\Mailer\Message; +use AppBundle\MembershipFee\Model\MembershipFee; +use AppBundle\MembershipFee\Model\Repository\MembershipFeeRepository; use Assert\Assertion; use DateInterval; use DateTime; @@ -41,38 +43,13 @@ class Cotisations { private ?CompanyMemberRepository $companyMemberRepository = null; + private ?MembershipFeeRepository $membershipFeeRepository = null; public function __construct( private readonly Base_De_Donnees $_bdd, private readonly ?Droits $_droits = null, ) {} - /** - * Renvoit la liste des cotisations concernant une personne - * - * @param int $id_personne Identifiant de la personne - * @param string $champs Champs à renvoyer - * @param string $ordre Tri des enregistrements - * @param bool $associatif Renvoyer un tableau associatif ? - * @return array|false - */ - public function obtenirListe(MemberType $type_personne, $id_personne, string $champs = '*', string $ordre = 'date_fin DESC', bool $associatif = false) - { - $requete = 'SELECT'; - $requete .= ' ' . $champs . ' '; - $requete .= 'FROM'; - $requete .= ' afup_cotisations '; - $requete .= 'WHERE'; - $requete .= ' type_personne=' . $type_personne->value; - $requete .= ' AND id_personne=' . $id_personne . ' '; - $requete .= 'ORDER BY ' . $ordre; - if ($associatif) { - return $this->_bdd->obtenirAssociatif($requete); - } else { - return $this->_bdd->obtenirTous($requete); - } - } - /** * Renvoit la cotisation demandée * @@ -143,51 +120,9 @@ public function ajouter(MemberType $type_personne, $id_personne, $montant, $type return $this->_bdd->executer($requete) !== false; } - /** - * Modifie une cotisation - * - * @param int $id Identifiant de la cotisation à modifier - * @param int $type_personne Type de la personne (morale ou physique) - * @param int $id_personne Identifiant de la personne - * @param float $montant Adresse de la personne - * @param int $type_reglement Type de règlement (espèces, chèque, virement) - * @param string $informations_reglement Informations concernant le règlement (numéro de chèque, de virement etc.) - * @param int $date_debut Date de début de la - * cotisation - * @param int $date_fin Date de fin de la cotisation - * @param string $commentaires Commentaires concernnant la cotisation - * @param string $referenceClient Reference client à mentionner sur la facture - * @return bool Succès de la modification - */ - public function modifier($id, $type_personne, $id_personne, $montant, $type_reglement, - $informations_reglement, $date_debut, $date_fin, $commentaires, $referenceClient): bool - { - $requete = 'UPDATE'; - $requete .= ' afup_cotisations '; - $requete .= 'SET'; - $requete .= ' type_personne=' . $type_personne . ','; - $requete .= ' id_personne=' . $id_personne . ','; - $requete .= ' montant=' . $montant . ','; - $requete .= ' type_reglement=' . $type_reglement . ','; - $requete .= ' informations_reglement=' . $this->_bdd->echapper($informations_reglement) . ','; - $requete .= ' date_debut=' . $date_debut . ','; - $requete .= ' date_fin=' . $date_fin . ','; - $requete .= ' commentaires=' . $this->_bdd->echapper($commentaires) . ','; - $requete .= ' reference_client=' . $this->_bdd->echapper($referenceClient) . ' '; - $requete .= 'WHERE'; - $requete .= ' id=' . $id; - return $this->_bdd->executer($requete) !== false; - } - - public function estDejaReglee($cmd) + public function isAlreadyPaid($cmd): bool { - $requete = 'SELECT'; - $requete .= ' 1 '; - $requete .= 'FROM'; - $requete .= ' afup_cotisations '; - $requete .= 'WHERE'; - $requete .= ' informations_reglement=' . $this->_bdd->echapper($cmd); - return $this->_bdd->obtenirUn($requete); + return $this->membershipFeeRepository->getOneBy(['paymentDetails' => $cmd]) instanceof MembershipFee; } public function notifierReglementEnLigneAuTresorier(string $cmd, float $total, string $autorisation, string $transaction, UserRepository $userRepository): ?bool @@ -196,8 +131,8 @@ public function notifierReglementEnLigneAuTresorier(string $cmd, float $total, s // Facture $invoiceNumber = substr($cmd, 1); $cotisation = $this->getByInvoice($invoiceNumber); - $type_personne = $cotisation['type_personne']; - $id_personne = $cotisation['id_personne']; + $type_personne = $cotisation->getUserType(); + $id_personne = $cotisation->getUserId(); } else { // Cotisation @@ -259,21 +194,22 @@ public function validerReglementEnLigne($cmd, $total, string $autorisation, stri $this ->updatePayment( - $cotisation['id'], - AFUP_COTISATIONS_REGLEMENT_ENLIGNE, "autorisation : " . $autorisation . " / transaction : " . $transaction, + $cotisation->getId(), + AFUP_COTISATIONS_REGLEMENT_ENLIGNE, + "autorisation : " . $autorisation . " / transaction : " . $transaction, ); - } elseif (substr(md5($reference), -3) === strtolower($verif) && !$this->estDejaReglee($cmd)) { + } elseif (substr(md5($reference), -3) === strtolower($verif) && !$this->isAlreadyPaid($cmd)) { [$ref, $date, $type_personne, $id_personne, $reste] = explode('-', (string) $cmd, 5); $date_debut = mktime(0, 0, 0, (int) substr($date, 2, 2), (int) substr($date, 0, 2), (int) substr($date, 4, 4)); - $cotisation = $this->obtenirDerniere(MemberType::from((int) $type_personne), $id_personne); - $date_fin_precedente = $cotisation === false ? 0 : $cotisation['date_fin']; + $cotisation = $this->getLastestByUserTypeAndId(MemberType::from((int) $type_personne), (int) $id_personne); + $date_fin_precedente = !$cotisation instanceof MembershipFee ? 0 : $cotisation->getEndDate()->getTimestamp(); if ($date_fin_precedente > 0) { - $date_debut = strtotime('+1day', (int) $date_fin_precedente); + $date_debut = strtotime('+1day', $date_fin_precedente); } - $date_fin = $this->finProchaineCotisation($cotisation)->format('U'); + $date_fin = $this->getNextSubscriptionExpiration($cotisation)->format('U'); $result = $this->ajouter( MemberType::from((int) $type_personne), $id_personne, @@ -308,13 +244,18 @@ public function getAccountFromCmd(string $cmd): array /** * Supprime une cotisation * - * @param int $id Identifiant de la cotisation à supprimer + * @param $id Identifiant de la cotisation à supprimer * @return bool Succès de la suppression */ - public function supprimer($id) + public function supprimer(int $id): bool { - $requete = 'DELETE FROM afup_cotisations WHERE id=' . $id; - return $this->_bdd->executer($requete); + try { + $cotisation = $this->membershipFeeRepository->get($id); + $this->membershipFeeRepository->delete($cotisation); + return true; + } catch (\Exception $exception) { + return false; + } } /** @@ -531,8 +472,8 @@ public function envoyerFacture($id_cotisation, Mailer $mailer, UserRepository $u $cheminFacture = AFUP_CHEMIN_RACINE . 'cache/fact' . $id_cotisation . '.pdf'; $numeroFacture = $this->genererFacture($id_cotisation, $cheminFacture); - $cotisation = $this->obtenirDerniere(MemberType::from((int) $personne['type_personne']), $personne['id_personne']); - $pattern = str_replace(' ', '', $patternPrefix) . '_' . $numeroFacture . '_' . date('dmY', (int) $cotisation['date_debut']) . '.pdf'; + $cotisation = $this->getLastestByUserTypeAndId(MemberType::from((int) $personne['type_personne']), (int) $personne['id_personne']); + $pattern = str_replace(' ', '', $patternPrefix) . '_' . $numeroFacture . '_' . date('dmY', $cotisation->getStartDate()->getTimestamp()) . '.pdf'; $message = new Message('Facture AFUP', null, new MailUser( $contactPhysique['email'], @@ -552,62 +493,15 @@ public function envoyerFacture($id_cotisation, Mailer $mailer, UserRepository $u /** * Retourne la dernière cotisation d'une personne morale - * @param int $id_personne Identifiant de la personne - * @return array|false */ - public function obtenirDerniere(MemberType $type_personne, $id_personne) + public function getLastestByUserTypeAndId(MemberType $type_personne, int $id_personne): ?MembershipFee { - $requete = 'SELECT'; - $requete .= ' * '; - $requete .= 'FROM'; - $requete .= ' afup_cotisations '; - $requete .= 'WHERE'; - $requete .= ' type_personne=' . $type_personne->value . ' '; - $requete .= ' AND id_personne=' . $id_personne . ' '; - $requete .= 'ORDER BY'; - $requete .= ' date_fin DESC '; - $requete .= 'LIMIT 0, 1 '; - return $this->_bdd->obtenirEnregistrement($requete); + return $this->membershipFeeRepository->getLastestByUserTypeAndId($type_personne, $id_personne); } - /** - * Retourne la date de début d'une cotisation. - * - * Cette date est déterminée par la date de fin de la cotisation précédente - * s'il y en a une ou alors sur la date du jour dans le cas contraire. - * - * @param int $type_personne Identifiant du type de personne - * @param int $id_personne Identifiant de la personne - * @return int Timestamp de la date de la cotisation - */ - public function obtenirDateDebut($type_personne, $id_personne) + public function getNextSubscriptionExpiration(?MembershipFee $cotisation = null): DateTime { - $requete = 'SELECT'; - $requete .= ' date_fin '; - $requete .= 'FROM'; - $requete .= ' afup_cotisations '; - $requete .= 'WHERE'; - $requete .= ' type_personne=' . $type_personne . ' '; - $requete .= 'AND'; - $requete .= ' id_personne=' . $id_personne . ' '; - $requete .= 'ORDER BY'; - $requete .= ' date_fin DESC'; - $date_debut = $this->_bdd->obtenirUn($requete); - - if ($date_debut !== false) { - return (int) $date_debut; - } else { - return time(); - } - } - - /** - * @param array|false $cotisation from Afup_Personnes_Physiques::obtenirDerniereCotisation - * @return DateTime Date of end of next subscription - */ - public function finProchaineCotisation($cotisation = false): DateTime - { - $endSubscription = $cotisation === false ? new DateTime() : new \DateTime('@' . $cotisation['date_fin']); + $endSubscription = $cotisation ? $cotisation->getEndDate() : new DateTime(); $base = $now = new DateTime(); $year = new DateInterval('P1Y'); @@ -623,71 +517,55 @@ public function finProchaineCotisation($cotisation = false): DateTime /** * Renvoit la cotisation demandée * - * @param string $invoiceId Identifiant de la facture - * @param string|null $token Token de la facture. Si null, pas de vérification - * @return array + * @param $invoiceId Identifiant de la facture + * @param $token Token de la facture. Si null, pas de vérification */ - public function getByInvoice(string $invoiceId, string $token = null) + public function getByInvoice(string $invoiceId, string $token = null): ?MembershipFee { - $requete = 'SELECT'; - $requete .= ' * '; - $requete .= 'FROM'; - $requete .= ' afup_cotisations '; - $requete .= 'WHERE'; - $requete .= ' numero_facture = ' . $this->_bdd->echapper($invoiceId); + $criterias = ['invoiceNumber' => $invoiceId]; if ($token !== null) { - $requete .= ' AND token = ' . $this->_bdd->echapper($token); + $criterias['token'] = $token; } - - return $this->_bdd->obtenirEnregistrement($requete); + return $this->membershipFeeRepository->getOneBy($criterias); } /** * Modifie une cotisation * - * @param int $id Identifiant de la cotisation à modifier - * @param int $type_reglement Type de règlement (espèces, chèque, virement) - * @param string $informations_reglement Informations concernant le règlement (numéro de chèque, de virement etc.) + * @param $id Identifiant de la cotisation à modifier + * @param $type_reglement Type de règlement (espèces, chèque, virement) + * @param $informations_reglement Informations concernant le règlement (numéro de chèque, de virement etc.) * @return bool Succès de la modification */ - public function updatePayment($id, $type_reglement, string $informations_reglement): bool + public function updatePayment(int $id, int $type_reglement, string $informations_reglement): bool { - $requete = 'UPDATE'; - $requete .= ' afup_cotisations '; - $requete .= 'SET'; - $requete .= ' type_reglement=' . $type_reglement . ','; - $requete .= ' informations_reglement=' . $this->_bdd->echapper($informations_reglement); - $requete .= ' WHERE'; - $requete .= ' id=' . $id; - return $this->_bdd->executer($requete) !== false; + return $this->membershipFeeRepository->updatePayment($id, $type_reglement, $informations_reglement) !== false; } - public function isCurrentUserAllowedToReadInvoice(string $invoiceId) + public function isCurrentUserAllowedToReadInvoice(string $invoiceId): bool { if (!$this->_droits) { throw new \RuntimeException('La variable $_droits ne doit pas être null.'); } - $sql = 'SELECT type_personne, id_personne FROM afup_cotisations WHERE id = ' . $this->_bdd->echapper($invoiceId); - $result = $this->_bdd->obtenirEnregistrement($sql); - - if (!$result) { + $cotisation = $this->membershipFeeRepository->get($invoiceId); + if (!$cotisation instanceof MembershipFee) { return false; } /** * si type_personne = 0, alors personne physique: id_personne doit être identique l'id de l'utilisateur connecté */ - if ($result['type_personne'] === "0") { - return $result['id_personne'] == $this->_droits->obtenirIdentifiant(); + if ($cotisation->getUserType() == MemberType::MemberPhysical) { + return $cotisation->getUserId() == $this->_droits->obtenirIdentifiant(); } /** * si type_personne = 1, alors personne morale: id_personne doit être égale à compagnyId de l'utilisateur connecté * qui doit aussi avoir le droit "ROLE_COMPAGNY_MANAGER" */ - if ($result['type_personne'] == MemberType::MemberCompany->value) { - return $this->_droits->verifierDroitManagerPersonneMorale($result['id_personne']); + if ($cotisation->getUserType() == MemberType::MemberCompany) { + return $this->_droits->verifierDroitManagerPersonneMorale($cotisation->getUserId()); } return false; @@ -697,4 +575,9 @@ public function setCompanyMemberRepository(CompanyMemberRepository $companyMembe { $this->companyMemberRepository = $companyMemberRepository; } + + public function setMembershipFeeRepository(MembershipFeeRepository $membershipFeeRepository): void + { + $this->membershipFeeRepository = $membershipFeeRepository; + } } diff --git a/sources/Afup/Association/CotisationsFactory.php b/sources/Afup/Association/CotisationsFactory.php index 804b0cb18..715905c3f 100644 --- a/sources/Afup/Association/CotisationsFactory.php +++ b/sources/Afup/Association/CotisationsFactory.php @@ -6,6 +6,7 @@ use Afup\Site\Utils\Utils; use AppBundle\Association\Model\Repository\CompanyMemberRepository; +use AppBundle\MembershipFee\Model\Repository\MembershipFeeRepository; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; @@ -15,6 +16,7 @@ public function __construct( private TokenStorageInterface $tokenStorage, private CompanyMemberRepository $companyMemberRepository, private AuthorizationCheckerInterface $authorizationChecker, + private MembershipFeeRepository $membershipFeeRepository, ) {} public function create(): Cotisations @@ -25,6 +27,7 @@ public function create(): Cotisations ); $cotisations->setCompanyMemberRepository($this->companyMemberRepository); + $cotisations->setMembershipFeeRepository($this->membershipFeeRepository); return $cotisations; } diff --git a/sources/AppBundle/Association/CompanyMembership/SubscriptionManagement.php b/sources/AppBundle/Association/CompanyMembership/SubscriptionManagement.php index 1c27c35c4..7886e8479 100644 --- a/sources/AppBundle/Association/CompanyMembership/SubscriptionManagement.php +++ b/sources/AppBundle/Association/CompanyMembership/SubscriptionManagement.php @@ -8,6 +8,7 @@ use Afup\Site\Utils\Utils; use AppBundle\Association\MemberType; use AppBundle\Association\Model\CompanyMember; +use AppBundle\MembershipFee\Model\MembershipFee; final readonly class SubscriptionManagement { @@ -15,7 +16,7 @@ public function __construct(private Cotisations $cotisations) {} public function createInvoiceForInscription(CompanyMember $company, $numberOfMembers): array { - $endSubscription = $this->cotisations->finProchaineCotisation(false); + $endSubscription = $this->cotisations->getNextSubscriptionExpiration(null); // Create the invoice $this->cotisations->ajouter( @@ -28,12 +29,12 @@ public function createInvoiceForInscription(CompanyMember $company, $numberOfMem $endSubscription->format('U'), '', ); - $subscriptionArray = $this->cotisations->obtenirDerniere(MemberType::MemberCompany, $company->getId()); + $subscription = $this->cotisations->getLastestByUserTypeAndId(MemberType::MemberCompany, $company->getId()); - if ($subscriptionArray === false) { + if (!$subscription instanceof MembershipFee) { throw new \RuntimeException('An error occured'); } - return ['invoice' => $subscriptionArray['numero_facture'], 'token' => $subscriptionArray['token']]; + return ['invoice' => $subscription->getInvoiceNumber(), 'token' => $subscription->getToken()]; } } diff --git a/sources/AppBundle/Association/UserMembership/SeniorityComputer.php b/sources/AppBundle/Association/UserMembership/SeniorityComputer.php index 371211a5d..39860457b 100644 --- a/sources/AppBundle/Association/UserMembership/SeniorityComputer.php +++ b/sources/AppBundle/Association/UserMembership/SeniorityComputer.php @@ -4,18 +4,20 @@ namespace AppBundle\Association\UserMembership; -use Afup\Site\Association\Cotisations; use AppBundle\Association\MemberType; use AppBundle\Association\Model\CompanyMember; use AppBundle\Association\Model\User; +use AppBundle\MembershipFee\Model\MembershipFee; +use AppBundle\MembershipFee\Model\Repository\MembershipFeeRepository; +use CCMBenchmark\Ting\Repository\Collection; class SeniorityComputer { - public function __construct(private readonly Cotisations $cotisations) {} + public function __construct(private readonly MembershipFeeRepository $membershipFeeRepository) {} public function computeCompany(CompanyMember $companyMember) { - $cotis = $this->cotisations->obtenirListe(MemberType::MemberCompany, $companyMember->getId()); + $cotis = $this->membershipFeeRepository->getListByUserTypeAndId(MemberType::MemberCompany, $companyMember->getId()); $infos = $this->computeFromCotisationsAndReturnInfos($cotis); @@ -24,7 +26,7 @@ public function computeCompany(CompanyMember $companyMember) public function computeCompanyAndReturnInfos(CompanyMember $companyMember): array { - $cotis = $this->cotisations->obtenirListe(MemberType::MemberCompany, $companyMember->getId()); + $cotis = $this->membershipFeeRepository->getListByUserTypeAndId(MemberType::MemberCompany, $companyMember->getId()); return $this->computeFromCotisationsAndReturnInfos($cotis); } @@ -38,20 +40,23 @@ public function compute(User $user) public function computeAndReturnInfos(User $user): array { - $cotis = $this->cotisations->obtenirListe(MemberType::MemberPhysical, $user->getId()); + $cotis = $this->membershipFeeRepository->getListByUserTypeAndId(MemberType::MemberPhysical, $user->getId()); return $this->computeFromCotisationsAndReturnInfos($cotis); } - private function computeFromCotisationsAndReturnInfos(array $cotisations): array + /** + * @param Collection $cotisations + */ + private function computeFromCotisationsAndReturnInfos(Collection $cotisations): array { $now = new \DateTime(); $diffs = []; $years = []; foreach ($cotisations as $coti) { - $from = new \DateTimeImmutable('@' . $coti['date_debut']); - $to = new \DateTimeImmutable('@' . $coti['date_fin']); + $from = $coti->getStartDate(); + $to = $coti->getEndDate(); $to = min($now, $to); $diffs[] = $from->diff($to); $years[] = $from->format('Y'); diff --git a/sources/AppBundle/Association/UserMembership/UserService.php b/sources/AppBundle/Association/UserMembership/UserService.php index 4942becde..7f26ede34 100644 --- a/sources/AppBundle/Association/UserMembership/UserService.php +++ b/sources/AppBundle/Association/UserMembership/UserService.php @@ -12,6 +12,7 @@ use AppBundle\Email\Mailer\MailUser; use AppBundle\Email\Mailer\MailUserFactory; use AppBundle\Email\Mailer\Message; +use AppBundle\MembershipFee\Model\MembershipFee; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; @@ -85,10 +86,7 @@ public function sendWelcomeEmail(User $user): bool return $this->mailer->send($message); } - /** - * @return array - */ - public function getLastSubscription(User $user) + public function getLastSubscription(User $user): ?MembershipFee { if ($user->getCompanyId()) { $id = $user->getCompanyId(); @@ -98,6 +96,6 @@ public function getLastSubscription(User $user) $personType = MemberType::MemberPhysical; } - return $this->cotisations->obtenirDerniere($personType, $id); + return $this->cotisations->getLastestByUserTypeAndId($personType, $id); } } diff --git a/sources/AppBundle/Controller/Website/Member/IndexAction.php b/sources/AppBundle/Controller/Website/Member/IndexAction.php index 897e1c9a0..4e9183d3a 100644 --- a/sources/AppBundle/Controller/Website/Member/IndexAction.php +++ b/sources/AppBundle/Controller/Website/Member/IndexAction.php @@ -9,6 +9,7 @@ use AppBundle\Association\UserMembership\BadgesComputer; use AppBundle\Association\UserMembership\UserService; use AppBundle\GeneralMeeting\GeneralMeetingRepository; +use AppBundle\MembershipFee\Model\MembershipFee; use AppBundle\Security\Authentication; use AppBundle\Twig\ViewRenderer; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -36,8 +37,8 @@ public function __invoke(): Response $cotisation = $userService->getLastSubscription($user); $dateFinCotisation = null; - if ($cotisation) { - $dateFinCotisation = new \DateTimeImmutable('@' . $cotisation['date_fin']); + if ($cotisation instanceof MembershipFee) { + $dateFinCotisation = $cotisation->getEndDate(); } $daysBeforeMembershipExpiration = $user->getDaysBeforeMembershipExpiration(); diff --git a/sources/AppBundle/Controller/Website/Membership/Fee/DownloadAction.php b/sources/AppBundle/Controller/Website/Membership/Fee/DownloadAction.php index 98c945198..06476bc87 100644 --- a/sources/AppBundle/Controller/Website/Membership/Fee/DownloadAction.php +++ b/sources/AppBundle/Controller/Website/Membership/Fee/DownloadAction.php @@ -12,6 +12,7 @@ use AppBundle\Association\Model\Repository\UserRepository; use AppBundle\Association\Model\User; use AppBundle\AuditLog\Audit; +use AppBundle\MembershipFee\Model\Repository\MembershipFeeRepository; use Assert\Assertion; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\BinaryFileResponse; @@ -23,6 +24,7 @@ final class DownloadAction extends AbstractController public function __construct( private readonly UserRepository $userRepository, private readonly CompanyMemberRepository $companyMemberRepository, + private readonly MembershipFeeRepository $membershipFeeRepository, private readonly Cotisations $cotisations, private readonly Droits $droits, private readonly Audit $audit, @@ -40,19 +42,19 @@ public function __invoke(Request $request): BinaryFileResponse $tempfile = tempnam(sys_get_temp_dir(), 'membership_fee_download'); $numeroFacture = $this->cotisations->genererFacture($id, $tempfile); - $cotisation = $this->cotisations->obtenir($id); + $membershipFee = $this->membershipFeeRepository->get($id); - if ($cotisation['type_personne'] == MemberType::MemberCompany->value) { - $company = $this->companyMemberRepository->get($cotisation['id_personne']); + if ($membershipFee->getUserType() == MemberType::MemberCompany) { + $company = $this->companyMemberRepository->get($membershipFee->getUserId()); Assertion::isInstanceOf($company, CompanyMember::class); $patternPrefix = $company->getCompanyName(); } else { - $user = $this->userRepository->get($cotisation['id_personne']); + $user = $this->userRepository->get($membershipFee->getUserId()); Assertion::isInstanceOf($user, User::class); $patternPrefix = $user->getLastName(); } - $pattern = str_replace(' ', '', $patternPrefix) . '_' . $numeroFacture . '_' . date('dmY', (int) $cotisation['date_debut']) . '.pdf'; + $pattern = str_replace(' ', '', $patternPrefix) . '_' . $numeroFacture . '_' . $membershipFee->getStartDate()->format('dmY') . '.pdf'; $response = new BinaryFileResponse($tempfile, Response::HTTP_OK, [], false); $response->deleteFileAfterSend(true); diff --git a/sources/AppBundle/Controller/Website/Membership/Fee/IndexAction.php b/sources/AppBundle/Controller/Website/Membership/Fee/IndexAction.php index 8b7738cad..e37e22320 100644 --- a/sources/AppBundle/Controller/Website/Membership/Fee/IndexAction.php +++ b/sources/AppBundle/Controller/Website/Membership/Fee/IndexAction.php @@ -13,6 +13,8 @@ use AppBundle\Association\Model\Repository\CompanyMemberRepository; use AppBundle\Association\Model\Repository\UserRepository; use AppBundle\Association\UserMembership\UserService; +use AppBundle\MembershipFee\Model\MembershipFee; +use AppBundle\MembershipFee\Model\Repository\MembershipFeeRepository; use AppBundle\Payment\PayboxBilling; use AppBundle\Payment\PayboxFactory; use AppBundle\Twig\ViewRenderer; @@ -29,6 +31,7 @@ public function __construct( private readonly UserService $userService, private readonly PayboxFactory $payboxFactory, private readonly Cotisations $cotisations, + private readonly MembershipFeeRepository $membershipFeeRepository, private readonly Droits $droits, ) {} @@ -43,34 +46,27 @@ public function __invoke(): Response $now = new \DateTime('now'); $isSubjectedToVat = Vat::isSubjectedToVat($now); - if (!$cotisation) { + if (!$cotisation instanceof MembershipFee) { $message = ''; } else { - $endSubscription = $this->cotisations->finProchaineCotisation($cotisation); + $endSubscription = $this->cotisations->getNextSubscriptionExpiration($cotisation); $message = sprintf( 'Votre dernière cotisation -- %s € -- est valable jusqu\'au %s.
Si vous renouvelez votre cotisation maintenant, celle-ci sera valable jusqu\'au %s.', - number_format((float) $cotisation['montant'], 2, ',', ' '), - date("d/m/Y", (int) $cotisation['date_fin']), + number_format((float) $cotisation->getAmount(), 2, ',', ' '), + $cotisation->getEndDate()->format('d/m/Y'), $endSubscription->format('d/m/Y'), ); } - $cotisations_physique = $this->cotisations->obtenirListe(MemberType::MemberPhysical, $user->getId()); - $cotisations_morale = $this->cotisations->obtenirListe(MemberType::MemberCompany, $user->getCompanyId()); + $cotisations_physique = $this->membershipFeeRepository->getListByUserTypeAndId(MemberType::MemberPhysical, $user->getId()); + $cotisations_morale = $this->membershipFeeRepository->getListByUserTypeAndId(MemberType::MemberCompany, $user->getCompanyId()); - if (is_array($cotisations_morale) && is_array($cotisations_physique)) { - $liste_cotisations = array_merge($cotisations_physique, $cotisations_morale); - } elseif (is_array($cotisations_morale)) { - $liste_cotisations = $cotisations_morale; - } elseif (is_array($cotisations_physique)) { - $liste_cotisations = $cotisations_physique; - } else { - $liste_cotisations = []; - } + /** @var array $liste_cotisations */ + $liste_cotisations = array_merge(iterator_to_array($cotisations_physique), iterator_to_array($cotisations_morale)); foreach ($liste_cotisations as $k => $cotisation) { - $liste_cotisations[$k]['telecharger_facture'] = $this->cotisations->isCurrentUserAllowedToReadInvoice($cotisation['id']); + $cotisation->setDownloadInvoice($this->cotisations->isCurrentUserAllowedToReadInvoice((string) $cotisation->getId())); } if ($user->getCompanyId() > 0) { @@ -112,7 +108,7 @@ public function __invoke(): Response 'isSubjectedToVat' => $isSubjectedToVat, 'title' => 'Ma cotisation', 'cotisations' => $liste_cotisations, - 'time' => time(), + 'time' => new \DateTime(), 'montant' => $montant, 'libelle' => $libelle, 'paybox' => $paybox, diff --git a/sources/AppBundle/Controller/Website/Membership/GeneralMeeting/IndexAction.php b/sources/AppBundle/Controller/Website/Membership/GeneralMeeting/IndexAction.php index 138553d69..b0f61fee6 100644 --- a/sources/AppBundle/Controller/Website/Membership/GeneralMeeting/IndexAction.php +++ b/sources/AppBundle/Controller/Website/Membership/GeneralMeeting/IndexAction.php @@ -49,7 +49,7 @@ public function __invoke(Request $request): Response $generalMeetingPlanned = $generalMeetingRepository->hasGeneralMeetingPlanned(); $cotisation = $userService->getLastSubscription($user); - $needsMembersheepFeePayment = $latestDate->getTimestamp() > strtotime("+14 day", (int) $cotisation['date_fin']); + $needsMembersheepFeePayment = $latestDate->getTimestamp() > strtotime("+14 day", $cotisation->getEndDate()->getTimestamp()); if ($needsMembersheepFeePayment) { return $this->view->render('admin/association/membership/generalmeeting_membersheepfee.html.twig', [ diff --git a/sources/AppBundle/Controller/Website/Membership/InvoiceAction.php b/sources/AppBundle/Controller/Website/Membership/InvoiceAction.php index 718290a78..3d59d6f19 100644 --- a/sources/AppBundle/Controller/Website/Membership/InvoiceAction.php +++ b/sources/AppBundle/Controller/Website/Membership/InvoiceAction.php @@ -5,6 +5,7 @@ namespace AppBundle\Controller\Website\Membership; use Afup\Site\Association\Cotisations; +use AppBundle\MembershipFee\Model\MembershipFee; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; @@ -18,12 +19,12 @@ public function __invoke(string $invoiceNumber, ?string $token): Response { $invoice = $this->cotisations->getByInvoice($invoiceNumber, $token); - if (!$invoice) { + if (!$invoice instanceof MembershipFee) { throw $this->createNotFoundException(sprintf('Could not find the invoice "%s" with token "%s"', $invoiceNumber, $token)); } ob_start(); - $this->cotisations->genererFacture($invoice['id']); + $this->cotisations->genererFacture($invoice->getId()); $pdf = ob_get_clean(); $response = new Response($pdf); diff --git a/sources/AppBundle/Controller/Website/Membership/PayboxCallbackAction.php b/sources/AppBundle/Controller/Website/Membership/PayboxCallbackAction.php index aef8d5b27..2e46680f4 100644 --- a/sources/AppBundle/Controller/Website/Membership/PayboxCallbackAction.php +++ b/sources/AppBundle/Controller/Website/Membership/PayboxCallbackAction.php @@ -10,6 +10,7 @@ use AppBundle\Association\Model\Repository\CompanyMemberRepository; use AppBundle\Association\Model\Repository\UserRepository; use AppBundle\AuditLog\Audit; +use AppBundle\MembershipFee\Model\MembershipFee; use AppBundle\Payment\PayboxResponseFactory; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; @@ -46,9 +47,9 @@ public function __invoke(Request $request): Response if ($etat == AFUP_COTISATIONS_PAIEMENT_REGLE) { $account = $this->cotisations->getAccountFromCmd($payboxResponse->getCmd()); - $lastCotisation = $this->cotisations->obtenirDerniere(MemberType::from($account['type']), $account['id']); + $lastCotisation = $this->cotisations->getLastestByUserTypeAndId(MemberType::from($account['type']), $account['id']); - if ($lastCotisation === false && $account['type'] == MemberType::MemberPhysical->value) { + if (!$lastCotisation instanceof MembershipFee && $account['type'] == MemberType::MemberPhysical->value) { $user = $this->userRepository->get($account['id']); $this->eventDispatcher->dispatch(new NewMemberEvent($user)); } diff --git a/sources/AppBundle/Controller/Website/Membership/PaymentAction.php b/sources/AppBundle/Controller/Website/Membership/PaymentAction.php index 50b09708c..9077977ba 100644 --- a/sources/AppBundle/Controller/Website/Membership/PaymentAction.php +++ b/sources/AppBundle/Controller/Website/Membership/PaymentAction.php @@ -7,6 +7,7 @@ use Afup\Site\Association\Cotisations; use AppBundle\Association\Model\Repository\CompanyMemberRepository; use AppBundle\Compta\BankAccount\BankAccountFactory; +use AppBundle\MembershipFee\Model\MembershipFee; use AppBundle\Payment\PayboxBilling; use AppBundle\Payment\PayboxFactory; use AppBundle\Twig\ViewRenderer; @@ -25,7 +26,10 @@ public function __construct( public function __invoke(string $invoiceNumber, ?string $token): Response { $invoice = $this->cotisations->getByInvoice($invoiceNumber, $token); - $company = $this->companyMemberRepository->get($invoice['id_personne']); + $company = null; + if ($invoice instanceof MembershipFee) { + $company = $this->companyMemberRepository->get($invoice->getUserId()); + } if (!$invoice || $company === null) { throw $this->createNotFoundException(sprintf('Could not find the invoice "%s" with token "%s"', $invoiceNumber, $token)); @@ -35,7 +39,7 @@ public function __invoke(string $invoiceNumber, ?string $token): Response $paybox = $this->payboxFactory->createPayboxForSubscription( 'F' . $invoiceNumber, - (float) $invoice['montant'], + (float) $invoice->getAmount(), $company->getEmail(), $payboxBilling, ); @@ -45,7 +49,7 @@ public function __invoke(string $invoiceNumber, ?string $token): Response return $this->view->render('site/company_membership/payment.html.twig', [ 'paybox' => $paybox, 'invoice' => $invoice, - 'bankAccount' => $bankAccountFactory->createApplyableAt(new \DateTimeImmutable('@' . $invoice['date_debut'])), + 'bankAccount' => $bankAccountFactory->createApplyableAt(new \DateTimeImmutable('@' . $invoice->getStartDate()->getTimestamp())), 'afup' => [ 'raison_sociale' => AFUP_RAISON_SOCIALE, 'adresse' => AFUP_ADRESSE, diff --git a/sources/AppBundle/MembershipFee/Model/MembershipFee.php b/sources/AppBundle/MembershipFee/Model/MembershipFee.php index 762a3c22e..60e300ed0 100644 --- a/sources/AppBundle/MembershipFee/Model/MembershipFee.php +++ b/sources/AppBundle/MembershipFee/Model/MembershipFee.php @@ -29,6 +29,8 @@ class MembershipFee implements NotifyPropertyInterface private ?int $nbReminders = null; private ?DateTime $lastReminderDate = null; + private bool $downloadInvoice = false; + public function getId(): ?int { return $this->id; @@ -196,4 +198,14 @@ public function setLastReminderDate(?DateTime $lastReminderDate): self $this->lastReminderDate = $lastReminderDate; return $this; } + + public function canDownloadInvoice(): bool + { + return $this->downloadInvoice; + } + + public function setDownloadInvoice(bool $hasPermission): bool + { + return $this->downloadInvoice = $hasPermission; + } } diff --git a/sources/AppBundle/MembershipFee/Model/Repository/MembershipFeeRepository.php b/sources/AppBundle/MembershipFee/Model/Repository/MembershipFeeRepository.php index 053aecea9..3f1a799e5 100644 --- a/sources/AppBundle/MembershipFee/Model/Repository/MembershipFeeRepository.php +++ b/sources/AppBundle/MembershipFee/Model/Repository/MembershipFeeRepository.php @@ -8,6 +8,8 @@ use AppBundle\Controller\Admin\Membership\MembershipFeePayment; use AppBundle\MembershipFee\Model\MembershipFee; use Aura\SqlQuery\Common\SelectInterface; +use CCMBenchmark\Ting\Repository\Collection; +use CCMBenchmark\Ting\Repository\HydratorSingleObject; use CCMBenchmark\Ting\Repository\Metadata; use CCMBenchmark\Ting\Repository\MetadataInitializer; use CCMBenchmark\Ting\Repository\Repository; @@ -15,6 +17,9 @@ use CCMBenchmark\Ting\Serializer\SerializerFactoryInterface; use DateTime; +/** + * @extends Repository + */ class MembershipFeeRepository extends Repository implements MetadataInitializer { public function getMembershipStartingDate(MemberType $memberType, int $idMember): DateTime @@ -53,6 +58,55 @@ public function generateInvoiceNumber(): string return 'COTIS-' . date('Y') . '-' . (is_null($result?->number) ? 1 : $result->number); } + public function updatePayment(int $id, int $paymentType, string $paymentInfos): bool + { + $sql = 'UPDATE'; + $sql .= ' afup_cotisations '; + $sql .= 'SET'; + $sql .= ' type_reglement= :paymentType,'; + $sql .= ' informations_reglement=:paymentInfos'; + $sql .= ' WHERE'; + $sql .= ' id=' . $id; + + return $this->getQuery($sql)->setParams(['paymentType' => $paymentType, 'paymentInfos' => $paymentInfos, 'id' => $id])->execute(); + } + + public function getLastestByUserTypeAndId(MemberType $type_personne, int $id_personne): ?MembershipFee + { + $sql = 'SELECT'; + $sql .= ' * '; + $sql .= 'FROM'; + $sql .= ' afup_cotisations '; + $sql .= 'WHERE'; + $sql .= ' type_personne=:userType '; + $sql .= ' AND id_personne=:userId '; + $sql .= 'ORDER BY'; + $sql .= ' date_fin DESC '; + $sql .= 'LIMIT 0, 1 '; + + $collection = $this->getQuery($sql)->setParams(['userType' => $type_personne->value, 'userId' => $id_personne])->query($this->getCollection(new HydratorSingleObject())); + if ($collection->count() === 0) { + return null; + } + return $collection->first(); + } + + /** + * @return Collection + */ + public function getListByUserTypeAndId(MemberType $memberType, int $memberId): Collection + { + $sql = 'SELECT * '; + $sql .= 'FROM'; + $sql .= ' afup_cotisations '; + $sql .= 'WHERE'; + $sql .= ' type_personne=:userType '; + $sql .= ' AND id_personne=:userId '; + $sql .= 'ORDER BY date_fin DESC'; + + return $this->getQuery($sql)->setParams(['userType' => $memberType->value, 'userId' => $memberId])->query($this->getCollection(new HydratorSingleObject())); + } + public static function initMetadata(SerializerFactoryInterface $serializerFactory, array $options = []) { $metadata = new Metadata($serializerFactory); diff --git a/templates/admin/association/membership/membershipfee.html.twig b/templates/admin/association/membership/membershipfee.html.twig index 3a278063f..2bead8f7a 100644 --- a/templates/admin/association/membership/membershipfee.html.twig +++ b/templates/admin/association/membership/membershipfee.html.twig @@ -38,18 +38,18 @@ {% for cotisation in cotisations %} - {{ cotisation.date_debut|date('d/m/Y') }} + {{ cotisation.startDate|date('d/m/Y') }} - {{ cotisation.date_fin|date('d/m/Y') }} - {% if cotisation.date_debut < time and cotisation.date_fin > time %} + {{ cotisation.endDate|date('d/m/Y') }} + {% if cotisation.startDate < time and cotisation.endDate > time %} (toujours valide) {% endif %} - {{ cotisation.montant|number_format(2, ',') }} € + {{ cotisation.amount|number_format(2, ',') }} € - {% if cotisation.telecharger_facture %} - + {% if cotisation.canDownloadInvoice %} + Télécharger la facture diff --git a/templates/site/company_membership/payment.html.twig b/templates/site/company_membership/payment.html.twig index fea0560e5..3bd992b88 100644 --- a/templates/site/company_membership/payment.html.twig +++ b/templates/site/company_membership/payment.html.twig @@ -7,17 +7,17 @@ Votre inscription a bien été enregistrée ! Il ne vous reste plus qu'à régler votre cotisation.

- Montant de la cotisation: {{ invoice.montant }} Euros + Montant de la cotisation: {{ invoice.amount|number_format(2, '.', ' ') }} Euros

Facture disponible

- Télécharger la facture + Télécharger la facture

Régler votre cotisation par Carte Bleue

{{ paybox|raw }}

Régler votre cotisation par chèque

-

Libellez le chèque à cet ordre: {{ afup.raison_sociale }}. Indiquez au dos le numéro de facture: {{ invoice.numero_facture }}

+

Libellez le chèque à cet ordre: {{ afup.raison_sociale }}. Indiquez au dos le numéro de facture: {{ invoice.invoiceNumber }}

{{ afup.raison_sociale }}
{{ afup.adresse }}
@@ -25,7 +25,7 @@

Régler votre cotisation par virement bancaire

- Lors d'un règlement par virement bancaire, indiquez ce libellé: {{ invoice.numero_facture }}. + Lors d'un règlement par virement bancaire, indiquez ce libellé: {{ invoice.invoiceNumber }}.

diff --git a/tests/unit/Afup/Association/CotisationsTest.php b/tests/unit/Afup/Association/CotisationsTest.php index 2a0712941..23b6a0c89 100644 --- a/tests/unit/Afup/Association/CotisationsTest.php +++ b/tests/unit/Afup/Association/CotisationsTest.php @@ -7,6 +7,7 @@ use Afup\Site\Association\Cotisations; use Afup\Site\Utils\Base_De_Donnees; use AppBundle\Association\MemberType; +use AppBundle\MembershipFee\Model\MembershipFee; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; @@ -41,7 +42,10 @@ public function testFinProchaineCotisation(\DateTimeInterface $dateFin, \DateTim $cotisations = new Cotisations($bdd); - $actual = $cotisations->finProchaineCotisation(['date_fin' => $dateFin->format('U')]); + $membershipFee = new MembershipFee(); + $membershipFee->setEndDate(new \DateTime('@' . $dateFin->format('U'))); + + $actual = $cotisations->getNextSubscriptionExpiration($membershipFee); self::assertEquals($expected->format('Y-m-d'), $actual->format('Y-m-d')); }