Commit 1527feab by Yvon Kerdoncuff

Merge branch '5735-allowance-based-on-household' into 'ssa-gironde'

create new method to distribute TAV allowance, based on household

See merge request cooperatic/kohinos-tav!70
parents 3a401fc8 fa204e43
...@@ -111,7 +111,7 @@ legend.required:after { ...@@ -111,7 +111,7 @@ legend.required:after {
margin-bottom: 20px; margin-bottom: 20px;
} }
.formEncaisserCotisationAdherent-no-profile { .formEncaisserCotisationAdherent-no-cotisation-amount {
font-style: italic; font-style: italic;
color: #ff4136; color: #ff4136;
} }
......
...@@ -624,20 +624,20 @@ $(function() { ...@@ -624,20 +624,20 @@ $(function() {
if (cotisationmontant === undefined) { if (cotisationmontant === undefined) {
$("#formEncaisserCotisationAdherent-montant-container").hide(); $("#formEncaisserCotisationAdherent-montant-container").hide();
$("#formEncaisserCotisationAdherent-no-profile").hide(); $("#formEncaisserCotisationAdherent-no-cotisation-amount").hide();
return; return;
} }
if (cotisationmontant !== null) { if (cotisationmontant !== null) {
$("#formEncaisserCotisationAdherent-montant-display").text(`${cotisationmontant} €`); $("#formEncaisserCotisationAdherent-montant-display").text(`${cotisationmontant} €`);
$("#formEncaisserCotisationAdherent-montant-container").show(); $("#formEncaisserCotisationAdherent-montant-container").show();
$("#formEncaisserCotisationAdherent-no-profile").hide(); $("#formEncaisserCotisationAdherent-no-cotisation-amount").hide();
$("#formEncaisserCotisationAdherent_save").prop("disabled",false); $("#formEncaisserCotisationAdherent_save").prop("disabled",false);
} else { } else {
// no cotisation profile // no cotisation profile
$("#formEncaisserCotisationAdherent-montant-container").hide(); $("#formEncaisserCotisationAdherent-montant-container").hide();
$("#formEncaisserCotisationAdherent-no-profile").show(); $("#formEncaisserCotisationAdherent-no-cotisation-amount").show();
$("#formEncaisserCotisationAdherent_save").prop("disabled",true); $("#formEncaisserCotisationAdherent_save").prop("disabled",true);
} }
......
...@@ -267,6 +267,7 @@ services: ...@@ -267,6 +267,7 @@ services:
- [ setUserManager, ['@fos_user.user_manager']] - [ setUserManager, ['@fos_user.user_manager']]
- [ setSecurity, ['@security.helper']] - [ setSecurity, ['@security.helper']]
- [ setEventDispatcher, ['@event_dispatcher']] - [ setEventDispatcher, ['@event_dispatcher']]
- [ setTavCotisationUtils, ['@app.utils.tav_cotisations']]
admin.all.cotisations: admin.all.cotisations:
class: App\Admin\CotisationAdmin class: App\Admin\CotisationAdmin
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
"app": { "app": {
"js": [ "js": [
"/build/runtime.6ad5c9da.js", "/build/runtime.6ad5c9da.js",
"/build/app.6ea8ce63.js" "/build/app.27091a64.js"
], ],
"css": [ "css": [
"/build/app.04bd0e32.css" "/build/app.5cdc32e6.css"
] ]
}, },
"admin": { "admin": {
......
{ {
"build/app.css": "/build/app.04bd0e32.css", "build/app.css": "/build/app.5cdc32e6.css",
"build/app.js": "/build/app.6ea8ce63.js", "build/app.js": "/build/app.27091a64.js",
"build/admin.css": "/build/admin.4de55830.css", "build/admin.css": "/build/admin.4de55830.css",
"build/admin.js": "/build/admin.86a2d986.js", "build/admin.js": "/build/admin.86a2d986.js",
"build/runtime.js": "/build/runtime.6ad5c9da.js", "build/runtime.js": "/build/runtime.6ad5c9da.js",
......
...@@ -18,6 +18,7 @@ use App\Exporter\CustomDoctrineORMQuerySourceIterator; ...@@ -18,6 +18,7 @@ use App\Exporter\CustomDoctrineORMQuerySourceIterator;
use App\Form\Type\DependentChildFormType; use App\Form\Type\DependentChildFormType;
use App\Form\Type\GeolocFormType; use App\Form\Type\GeolocFormType;
use App\Form\Type\UserFormType; use App\Form\Type\UserFormType;
use App\Utils\TAVCotisationUtils;
use Doctrine\ORM\Query; use Doctrine\ORM\Query;
use FOS\UserBundle\Event\UserEvent; use FOS\UserBundle\Event\UserEvent;
use Knp\Menu\ItemInterface as MenuItemInterface; use Knp\Menu\ItemInterface as MenuItemInterface;
...@@ -40,6 +41,7 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType; ...@@ -40,6 +41,7 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvents;
...@@ -59,6 +61,7 @@ class AdherentAdmin extends AbstractAdmin ...@@ -59,6 +61,7 @@ class AdherentAdmin extends AbstractAdmin
protected $baseRouteName = 'adherent'; protected $baseRouteName = 'adherent';
protected $baseRoutePattern = 'adherent'; protected $baseRoutePattern = 'adherent';
protected $security; protected $security;
protected $tavCotisationUtils;
protected $datagridValues = [ protected $datagridValues = [
// reverse order (default = 'ASC') // reverse order (default = 'ASC')
...@@ -74,6 +77,11 @@ class AdherentAdmin extends AbstractAdmin ...@@ -74,6 +77,11 @@ class AdherentAdmin extends AbstractAdmin
$this->security = $security; $this->security = $security;
} }
public function setTavCotisationUtils(TAVCotisationUtils $tavCotisationUtils)
{
$this->tavCotisationUtils = $tavCotisationUtils;
}
public function configure() public function configure()
{ {
parent::configure(); parent::configure();
...@@ -149,6 +157,11 @@ class AdherentAdmin extends AbstractAdmin ...@@ -149,6 +157,11 @@ class AdherentAdmin extends AbstractAdmin
if (null == $adherent->getGeoloc()) { if (null == $adherent->getGeoloc()) {
$adherent->setGeoloc(new Geoloc()); $adherent->setGeoloc(new Geoloc());
} }
// params
$tav_env = $this->getConfigurationPool()->getContainer()->getParameter('tav_env');
$household_based_allowance = $this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance');
$formMapper $formMapper
->tab('General') ->tab('General')
->with('Identité', ['class' => 'col-md-7']) ->with('Identité', ['class' => 'col-md-7'])
...@@ -162,12 +175,8 @@ class AdherentAdmin extends AbstractAdmin ...@@ -162,12 +175,8 @@ class AdherentAdmin extends AbstractAdmin
'required' => true, 'required' => true,
'with_geoloc' => false, 'with_geoloc' => false,
'with_latlon' => false, 'with_latlon' => false,
'with_subterritory' => 'with_subterritory' => $tav_env && $household_based_allowance,
$this->getConfigurationPool()->getContainer()->getParameter('tav_env') 'with_quartier' => $tav_env && $household_based_allowance
&& $this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance'),
'with_quartier' =>
$this->getConfigurationPool()->getContainer()->getParameter('tav_env')
&& $this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance')
]) ])
->end() ->end()
->with('Groupe', ['class' => 'col-md-5']) ->with('Groupe', ['class' => 'col-md-5'])
...@@ -182,8 +191,13 @@ class AdherentAdmin extends AbstractAdmin ...@@ -182,8 +191,13 @@ class AdherentAdmin extends AbstractAdmin
->end() ->end()
; ;
if ($this->getConfigurationPool()->getContainer()->getParameter('tav_env')) { /**
if ($this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance')) { * In TAV env, 2 allowance processes possible:
* - household based (if param set)
* - cotisation profile and rate based (default)
*/
if ($tav_env) {
if ($household_based_allowance) {
$formMapper $formMapper
->tab('General') ->tab('General')
->with('Foyer', ['class' => 'col-md-7']) ->with('Foyer', ['class' => 'col-md-7'])
...@@ -223,7 +237,24 @@ class AdherentAdmin extends AbstractAdmin ...@@ -223,7 +237,24 @@ class AdherentAdmin extends AbstractAdmin
]) ])
->end() ->end()
->end(); ->end();
}
// Add cotisation info
$formMapper
->tab('General')
->with('Informations de cotisation', ['class' => 'col-md-5'])
->add('cotisationAmount', NumberType::class, [
'label' => 'Montant de la cotisation (en €)',
'help' => 'Montant minimum : 10€ par foyer + 5€/personne supplémentaire du foyer'
])
->add('allocationAmount', NumberType::class, [
'label' => 'Montant d\'allocation prévu en fonction du foyer (en MonA)',
'disabled' => true,
'required' => false,
'help' => 'Le montant de l\'allocation sera calculé automatiquement en fonction des informations du foyer une fois les informations sauvegardées.'
])
->end()
->end();
} else {
// For Comptoir role in edit mode, hide profile choice // For Comptoir role in edit mode, hide profile choice
$displayProfilChoice = true; $displayProfilChoice = true;
$isComptoirOnly = $isComptoirOnly =
...@@ -308,6 +339,7 @@ class AdherentAdmin extends AbstractAdmin ...@@ -308,6 +339,7 @@ class AdherentAdmin extends AbstractAdmin
]) ])
->end() ->end()
->end(); ->end();
}
if (!empty($adherent) && !empty($adherent->getEmlcAccount()) ) { if (!empty($adherent) && !empty($adherent->getEmlcAccount()) ) {
$em = $this->getConfigurationPool()->getContainer()->get('doctrine')->getManager(); $em = $this->getConfigurationPool()->getContainer()->get('doctrine')->getManager();
...@@ -333,6 +365,8 @@ class AdherentAdmin extends AbstractAdmin ...@@ -333,6 +365,8 @@ class AdherentAdmin extends AbstractAdmin
$em = $this->getConfigurationPool()->getContainer()->get('doctrine')->getManager(); $em = $this->getConfigurationPool()->getContainer()->get('doctrine')->getManager();
$formMapper->getFormBuilder()->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use ($em) { $formMapper->getFormBuilder()->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use ($em) {
$adherent = $event->getData(); $adherent = $event->getData();
// Check user email
$user = $adherent->getUser(); $user = $adherent->getUser();
if (!$user || null === $user->getId()) { if (!$user || null === $user->getId()) {
$repo = $em->getRepository(User::class); $repo = $em->getRepository(User::class);
...@@ -343,6 +377,17 @@ class AdherentAdmin extends AbstractAdmin ...@@ -343,6 +377,17 @@ class AdherentAdmin extends AbstractAdmin
$user->setUsername($user->getEmail()); $user->setUsername($user->getEmail());
} }
} }
// check cotisation amount
if ($this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance')) {
$adultsCount = $adherent->getHouseholdAdultCount();
$dependentChildrenCount = count($adherent->getDependentChildren());
$minCotisationAmount = 10 + 5 * ( $adultsCount - 1 ) + 5 * $dependentChildrenCount;
if ($adherent->getCotisationAmount() < $minCotisationAmount) {
$event->getForm()->get('cotisationAmount')->addError(new FormError('Le montant minimum est de ' . $minCotisationAmount . '€ (selon les données du foyer indiquées)'));
}
}
}); });
parent::configureFormFields($formMapper); parent::configureFormFields($formMapper);
} }
...@@ -384,6 +429,11 @@ class AdherentAdmin extends AbstractAdmin ...@@ -384,6 +429,11 @@ class AdherentAdmin extends AbstractAdmin
$adherent->addAccount($account); $adherent->addAccount($account);
$em->persist($account); $em->persist($account);
} }
if ($this->getConfigurationPool()->getContainer()->getParameter('household_based_allowance')) {
$this->tavCotisationUtils->calculateAllowanceAccordingToHousehold($adherent);
}
$em->persist($adherent->getUser()); $em->persist($adherent->getUser());
$em->persist($adherent); $em->persist($adherent);
$em->flush(); $em->flush();
......
...@@ -136,8 +136,6 @@ class UserAdherentController extends FluxController ...@@ -136,8 +136,6 @@ class UserAdherentController extends FluxController
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
// TODO: set CB payment when the functionality is validated
$flux = $form->getData(); $flux = $form->getData();
// Look for existing cotisation // Look for existing cotisation
...@@ -166,12 +164,13 @@ class UserAdherentController extends FluxController ...@@ -166,12 +164,13 @@ class UserAdherentController extends FluxController
$flux->setDon(null); $flux->setDon(null);
} }
// TODO redirect to paiement // Redirect to payment
return $this->forward('App\Controller\PaymentController::preparePaymentAction', [ return $this->forward('App\Controller\PaymentController::preparePaymentAction', [
'form' => $form, 'form' => $form,
'type' => Payment::TYPE_PAIEMENT_COTISATION_TAV // TODO 'type' => Payment::TYPE_PAIEMENT_COTISATION_TAV
]); ]);
/* For test purposes, comment redirection and uncomment following part to skip payment */
// $this->em->persist($flux); // $this->em->persist($flux);
// $this->operationUtils->executeOperations($flux); // $this->operationUtils->executeOperations($flux);
......
...@@ -215,6 +215,34 @@ class UserComptoirController extends FluxController ...@@ -215,6 +215,34 @@ class UserComptoirController extends FluxController
} }
$destinataire = $flux->getDestinataire(); $destinataire = $flux->getDestinataire();
if ($this->getParameter('household_based_allowance') == true) {
/* Process: allowance based on household */
$cotisationAmount = $destinataire->getCotisationAmount();
// Verifications
if (is_null($cotisationAmount) || is_null($destinataire->getHouseholdAdultCount())) {
$this->addFlash(
'error',
$this->translator->trans("Opération impossible : le profil de l'habitant.e est incomplet, veuillez le compléter dans l'interface d'administration.")
);
return $this->redirectToRoute('index');
}
if (is_null($destinataire->getAllocationAmount())) {
$this->tavCotisationUtils->calculateAllowanceAccordingToHousehold($destinataire);
$this->em->persist($destinataire);
}
$flux->setMontant($cotisationAmount);
$this->em->persist($flux);
$this->operationUtils->executeOperations($flux);
// Apply cotisation rate, create new flux
$this->tavCotisationsUtils->applyHouseholdAllowance($flux);
} else {
/* Process: allowance based on cotisation profile with cotisation rate */
$profile = $destinataire->getProfilDeCotisation(); $profile = $destinataire->getProfilDeCotisation();
if (is_null($profile)) { if (is_null($profile)) {
...@@ -234,6 +262,7 @@ class UserComptoirController extends FluxController ...@@ -234,6 +262,7 @@ class UserComptoirController extends FluxController
// Apply cotisation rate, create new flux // Apply cotisation rate, create new flux
$this->tavCotisationsUtils->applyTauxCotisation($flux); $this->tavCotisationsUtils->applyTauxCotisation($flux);
}
$this->em->flush(); $this->em->flush();
......
...@@ -129,6 +129,21 @@ class Adherent extends AccountableObject implements AccountableInterface ...@@ -129,6 +129,21 @@ class Adherent extends AccountableObject implements AccountableInterface
*/ */
private $householdAdultCount; private $householdAdultCount;
/**
* On household based allowance process, define a cotisation amount for each adherent.
*
* @ORM\Column(type="float", nullable=true)
*/
private $cotisationAmount;
/**
* On household based allowance process, the allowance amountis calculated based on household data.
* Calculate and save the allocation amount when the household data is updated.
*
* @ORM\Column(type="float", nullable=true)
*/
private $allocationAmount;
public function __construct() public function __construct()
{ {
...@@ -407,4 +422,28 @@ class Adherent extends AccountableObject implements AccountableInterface ...@@ -407,4 +422,28 @@ class Adherent extends AccountableObject implements AccountableInterface
return $this; return $this;
} }
public function getCotisationAmount(): ?float
{
return $this->cotisationAmount;
}
public function setCotisationAmount(?float $cotisationAmount): self
{
$this->cotisationAmount = $cotisationAmount;
return $this;
}
public function getAllocationAmount(): ?float
{
return $this->allocationAmount;
}
public function setAllocationAmount(?float $allocationAmount): self
{
$this->allocationAmount = $allocationAmount;
return $this;
}
} }
...@@ -7,16 +7,19 @@ use App\Utils\OperationFactory; ...@@ -7,16 +7,19 @@ use App\Utils\OperationFactory;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* Application du taux de cotisation lors du paiement d'une cotisation (au sens TAV). * Application du calcul de l'allocation (emlc reçues) lors du paiement d'une cotisation (au sens TAV).
* *
* Au paiement d'une cotisation: * Au paiement d'une cotisation:
* - Un premier Flux est enregistré, correspondant à l'achat/vente d'emlc. * - Un premier Flux est enregistré, correspondant à l'achat/vente d'emlc.
* - Puis on applique le taux défini dans le ProfilDeCotisation de l'adhérent, * - On crée un nouveau flux pour compléter la cotisation, en fonction du système de calcul choisi.
* on crée un nouveau flux pour compléter la cositsation. *
* Systèmes de calculs possibles :
* - On applique le taux défini dans le ProfilDeCotisation de l'adhérent
* - On calcule le montant à recevoir en fonction du foyer de l'adhérent
* *
* @ORM\Entity * @ORM\Entity
*/ */
class TauxCotisationApplication extends Flux class CotisationTavApplication extends Flux
{ {
const TYPE_REVERSEMENT_COTISATION_ADHERENT = 'reversement_cotisation_adherent'; const TYPE_REVERSEMENT_COTISATION_ADHERENT = 'reversement_cotisation_adherent';
const TYPE_PRELEVEMENT_COTISATION_ADHERENT = 'prelevement_cotisation_adherent'; const TYPE_PRELEVEMENT_COTISATION_ADHERENT = 'prelevement_cotisation_adherent';
...@@ -26,7 +29,7 @@ class TauxCotisationApplication extends Flux ...@@ -26,7 +29,7 @@ class TauxCotisationApplication extends Flux
*/ */
public function getParenttype(): string public function getParenttype(): string
{ {
return parent::TYPE_APPLICATION_TAUX_COTISATION; return parent::TYPE_APPLICATION_COTISATION_TAV;
} }
public function getAllOperations($em) public function getAllOperations($em)
......
...@@ -7,12 +7,16 @@ use App\Utils\OperationFactory; ...@@ -7,12 +7,16 @@ use App\Utils\OperationFactory;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* En cas de taux < 1, l'adhérent•e reçoit moins d'emlc que ce qu'elle•il paye en € : * Dans les cas suivants :
* - [Profil de Cotisation] La taux est inférieur à 1
* - [Allocation selon foyer] Le montant reçu calculé est inférieur au montant payé
*
* L'adhérent•e reçoit moins d'emlc que ce qu'elle•il paye en €,
* un second flux est créé pour prélever le complément de la cotisation. * un second flux est créé pour prélever le complément de la cotisation.
* *
* @ORM\Entity * @ORM\Entity
*/ */
class TauxCotisationPrelevement extends TauxCotisationApplication class CotisationTavPrelevement extends CotisationTavApplication
{ {
/** /**
* @ORM\OneToOne(targetEntity="Adherent") * @ORM\OneToOne(targetEntity="Adherent")
......
...@@ -7,12 +7,16 @@ use App\Utils\OperationFactory; ...@@ -7,12 +7,16 @@ use App\Utils\OperationFactory;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* En cas de taux > 1, l'adhérent•e reçoit plus d'emlc que ce qu'elle•il paye en € : * * Dans les cas suivants :
* - [Profil de Cotisation] La taux est supérieur à 1
* - [Allocation selon foyer] Le montant reçu calculé est supérieur au montant payé
*
* L'adhérent•e reçoit plus d'emlc que ce qu'elle•il paye en €,
* un second flux est créé pour reverser le complément de la cotisation. * un second flux est créé pour reverser le complément de la cotisation.
* *
* @ORM\Entity * @ORM\Entity
*/ */
class TauxCotisationReversement extends TauxCotisationApplication class CotisationTavReversement extends CotisationTavApplication
{ {
/** /**
* @ORM\OneToOne(targetEntity="Siege") * @ORM\OneToOne(targetEntity="Siege")
......
...@@ -54,9 +54,9 @@ use Symfony\Component\Validator\Constraints as Assert; ...@@ -54,9 +54,9 @@ use Symfony\Component\Validator\Constraints as Assert;
* "ticket_fix" = "TicketFix", * "ticket_fix" = "TicketFix",
* "ticket_fix_print" = "TicketFixPrint", * "ticket_fix_print" = "TicketFixPrint",
* "ticket_fix_destroy" = "TicketFixDestroy", * "ticket_fix_destroy" = "TicketFixDestroy",
* "application_taux_cotisation" = "TauxCotisationApplication", * "application_cotisation_tav" = "CotisationTavApplication",
* "reversement_cotisation_adherent" = "TauxCotisationReversement", * "reversement_cotisation_adherent" = "CotisationTavReversement",
* "prelevement_cotisation_adherent" = "TauxCotisationPrelevement", * "prelevement_cotisation_adherent" = "CotisationTavPrelevement",
* }) * })
*/ */
abstract class Flux implements FluxInterface abstract class Flux implements FluxInterface
...@@ -74,7 +74,7 @@ abstract class Flux implements FluxInterface ...@@ -74,7 +74,7 @@ abstract class Flux implements FluxInterface
const TYPE_VENTE = 'vente'; const TYPE_VENTE = 'vente';
const TYPE_VENTE_EMLC = 'vente_emlc'; const TYPE_VENTE_EMLC = 'vente_emlc';
const TYPE_TICKET_FIX = 'ticket_fix'; const TYPE_TICKET_FIX = 'ticket_fix';
const TYPE_APPLICATION_TAUX_COTISATION = 'application_taux_cotisation'; const TYPE_APPLICATION_COTISATION_TAV = 'application_cotisation_tav';
/** /**
* @var \Ramsey\Uuid\UuidInterface * @var \Ramsey\Uuid\UuidInterface
......
...@@ -125,7 +125,7 @@ class ProfilDeCotisation ...@@ -125,7 +125,7 @@ class ProfilDeCotisation
} }
/** /**
* setContacts. * setBeneficiaires.
* *
* @param [type] $beneficiaires [description] * @param [type] $beneficiaires [description]
*/ */
......
...@@ -36,12 +36,23 @@ class AchatMonnaieFormType extends FluxFormType ...@@ -36,12 +36,23 @@ class AchatMonnaieFormType extends FluxFormType
if ($this->container->getParameter('tav_env')) { if ($this->container->getParameter('tav_env')) {
$montant = 0; $montant = 0;
if ($this->container->getParameter('household_based_allowance')) {
$cosisationMontant = $this->security->getUser()->getAdherent()->getCotisationAmount();
if (null != $cosisationMontant) {
$montant = $cosisationMontant;
} else {
$montant = false;
}
} else {
$profilDeCotisation = $this->security->getUser()->getAdherent()->getProfilDeCotisation(); $profilDeCotisation = $this->security->getUser()->getAdherent()->getProfilDeCotisation();
if (null != $profilDeCotisation) { if (null != $profilDeCotisation) {
$montant = $profilDeCotisation->getMontant(); $montant = $profilDeCotisation->getMontant();
} else { } else {
$montant = false; $montant = false;
} }
}
$builder $builder
->add('montant', HiddenType::class, [ ->add('montant', HiddenType::class, [
......
...@@ -20,13 +20,17 @@ class EncaisserCotisationAdherentFormType extends VenteEmlcAdherentFormType ...@@ -20,13 +20,17 @@ class EncaisserCotisationAdherentFormType extends VenteEmlcAdherentFormType
// Get the cotisation amount for each adherent. Used for display purposes on the front end. // Get the cotisation amount for each adherent. Used for display purposes on the front end.
$adherents = $this->em->getRepository(Adherent::class)->findOrderByName(); $adherents = $this->em->getRepository(Adherent::class)->findOrderByName();
$adherentsProfiles = []; $adherentsCotisationAmounts = [];
foreach ($adherents as $adh) { foreach ($adherents as $adh) {
$montant = null; $montant = null;
if (!is_null($adh->getProfilDeCotisation()) ) {
if ($this->container->getParameter('household_based_allowance') && !is_null($adh->getCotisationAmount())) {
$montant = $adh->getCotisationAmount();
} else if (!$this->container->getParameter('household_based_allowance') && !is_null($adh->getProfilDeCotisation())) {
$montant = $adh->getProfilDeCotisation()->getMontant(); $montant = $adh->getProfilDeCotisation()->getMontant();
} }
$adherentsProfiles[strval($adh->getId())] = $montant;
$adherentsCotisationAmounts[strval($adh->getId())] = $montant;
} }
$builder $builder
...@@ -37,7 +41,7 @@ class EncaisserCotisationAdherentFormType extends VenteEmlcAdherentFormType ...@@ -37,7 +41,7 @@ class EncaisserCotisationAdherentFormType extends VenteEmlcAdherentFormType
'data' => 0 'data' => 0
]) ])
->add('cotisationMontants', HiddenType::class, [ ->add('cotisationMontants', HiddenType::class, [
'data' => json_encode($adherentsProfiles), 'data' => json_encode($adherentsCotisationAmounts),
'mapped' => false 'mapped' => false
]) ])
; ;
......
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240313125437 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE adherent ADD cotisation_amount DOUBLE PRECISION DEFAULT NULL, ADD allocation_amount DOUBLE PRECISION DEFAULT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE adherent DROP cotisation_amount, DROP allocation_amount');
}
}
...@@ -5,8 +5,8 @@ namespace App\Utils; ...@@ -5,8 +5,8 @@ namespace App\Utils;
use App\Entity\Adherent; use App\Entity\Adherent;
use App\Entity\Siege; use App\Entity\Siege;
use App\Entity\Flux; use App\Entity\Flux;
use App\Entity\TauxCotisationReversement; use App\Entity\CotisationTavReversement;
use App\Entity\TauxCotisationPrelevement; use App\Entity\CotisationTavPrelevement;
use App\Enum\MoyenEnum; use App\Enum\MoyenEnum;
use App\Utils\CustomEntityManager; use App\Utils\CustomEntityManager;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
...@@ -45,6 +45,9 @@ class TAVCotisationUtils ...@@ -45,6 +45,9 @@ class TAVCotisationUtils
} }
/** /**
* First method to calculate allowance:
* according to a contribution rate defined in user's profile (ProfilDeCotisation).
*
* Apply the cotisation profile rate to the amount paid * Apply the cotisation profile rate to the amount paid
* and register the complement as a new flux (only if rate != 1) * and register the complement as a new flux (only if rate != 1)
* *
...@@ -72,14 +75,14 @@ class TAVCotisationUtils ...@@ -72,14 +75,14 @@ class TAVCotisationUtils
if ($amountDiff > 0) { if ($amountDiff > 0) {
// User should receive more than he•she paid: send a new flux to the user to complete its cotisation // User should receive more than he•she paid: send a new flux to the user to complete its cotisation
$fluxCotis = new TauxCotisationReversement(); $fluxCotis = new CotisationTavReversement();
$fluxCotis->setExpediteur($siege); $fluxCotis->setExpediteur($siege);
$fluxCotis->setDestinataire($flux->getDestinataire()); $fluxCotis->setDestinataire($flux->getDestinataire());
$fluxCotis->setMontant($amountDiff); $fluxCotis->setMontant($amountDiff);
$fluxCotis->setReference("Reversement cotisation après paiement de " . $cotisationAmount . "€ et application du taux " . $cotisationTaux); $fluxCotis->setReference("Reversement cotisation après paiement de " . $cotisationAmount . "€ et application du taux " . $cotisationTaux);
} else { } else {
// User should receive less than he•she paid: fetch the difference from his account // User should receive less than he•she paid: fetch the difference from his account
$fluxCotis = new TauxCotisationPrelevement(); $fluxCotis = new CotisationTavPrelevement();
$fluxCotis->setExpediteur($flux->getDestinataire()); $fluxCotis->setExpediteur($flux->getDestinataire());
$fluxCotis->setDestinataire($siege); $fluxCotis->setDestinataire($siege);
$fluxCotis->setMontant(-$amountDiff); $fluxCotis->setMontant(-$amountDiff);
...@@ -95,6 +98,93 @@ class TAVCotisationUtils ...@@ -95,6 +98,93 @@ class TAVCotisationUtils
} }
/** /**
* Second method to calculate allowance:
* allowance based on user's household.
*
* Rules are as follow:
* - 150 emlc for the first person in user's household
* - 75 emlc for each other adult
* - 75 emlc amount for each dependant child, with a percentage applied if the child is in shared custody:
* 25%, 50% or 75% depending on the shared custody arrangement
*
* Once the full amount is calculated, cap user's balance.
* User account balance is capped at twice the amount previously calculated.
*
* @param Adherent $adherent (by ref)
*/
public function calculateAllowanceAccordingToHousehold(&$adherent) {
// TODO base amounts to param in .env, or in global params ?
// base allowance, for one adult
$mlcAllowanceAmount = 150;
$adultsCount = $adherent->getHouseholdAdultCount();
if ($adultsCount == null) {
return;
}
// increment for each other adult in the household
$mlcAllowanceAmount += 75 * ($adultsCount - 1);
// increment allowance for each dependant child, depending on the shared custody arrangement
$dependentChildren = $adherent->getDependentChildren();
foreach ($dependentChildren as $child) {
$childAllowanceAmount = 75;
$sharedCustodyPercentage = $child->getSharedCustodyPercentage();
if ($sharedCustodyPercentage != null) {
$childAllowanceAmount = $childAllowanceAmount * $sharedCustodyPercentage;
}
$mlcAllowanceAmount += $childAllowanceAmount;
}
$adherent->setAllocationAmount($mlcAllowanceAmount);
}
/**
* Method called to create Flux based on allowance amount (for household based allowance).
*/
public function applyHouseholdAllowance(Flux $flux) {
// get allowance
$adherent = $flux->getDestinataire();
$cotisationAmount = $flux->getMontant();
// get the mlc amount the user is supposed to receive
$mlcAllowanceAmount = $adherent->getAllocationAmount();
if ($flux->getExpediteur() instanceof Siege) {
$siege = $flux->getExpediteur();
} else {
$siege = $flux->getExpediteur()->getGroupe()->getSiege();
}
// get the difference between what the user paid and what he•she's supposed to receive
$amountDiff = $mlcAllowanceAmount - $cotisationAmount;
if ($amountDiff > 0) {
// User should receive more than he•she paid: send a new flux to the user to complete its cotisation
$fluxCotis = new CotisationTavReversement();
$fluxCotis->setExpediteur($siege);
$fluxCotis->setDestinataire($adherent);
$fluxCotis->setMontant($amountDiff);
$fluxCotis->setReference("Reversement du complément de cotisation après paiement de " . $cotisationAmount . "€ pour une allocation de " . $mlcAllowanceAmount . " MonA.");
} else {
// User should receive less than he•she paid: fetch the difference from his account
$fluxCotis = new CotisationTavPrelevement();
$fluxCotis->setExpediteur($adherent);
$fluxCotis->setDestinataire($siege);
$fluxCotis->setMontant(-$amountDiff);
$fluxCotis->setReference("Prélèvement du complément de cotisation après paiement de " . $cotisationAmount . "€ pour une allocation de " . $mlcAllowanceAmount . " MonA.");
}
$fluxCotis->setOperateur($flux->getOperateur());
$fluxCotis->setRole($flux->getRole());
$fluxCotis->setMoyen(MoyenEnum::MOYEN_EMLC);
$this->em->persist($fluxCotis);
$this->operationUtils->executeOperations($fluxCotis);
}
/**
* Get the last cotisation of an adhérent * Get the last cotisation of an adhérent
* *
* @param Adherent $adherent * @param Adherent $adherent
......
...@@ -6,9 +6,12 @@ ...@@ -6,9 +6,12 @@
{% block blockcontent %} {% block blockcontent %}
{% set form = getPayerCotisationTAVForm(app.user) %} {% set form = getPayerCotisationTAVForm(app.user) %}
{% if form.montant.vars.value == false %} {% if form.montant.vars.value == false and not household_based_allowance %}
<p>{{ 'Vous n\'avez pas de profil de cotisation associé, vous ne pouvez donc pas payer de cotisation.'|trans }}</p> <p>{{ 'Vous n\'avez pas de profil de cotisation associé, vous ne pouvez donc pas payer de cotisation.'|trans }}</p>
<p>{{ 'Veuillez contacter un administrateur.'|trans }}</p> <p>{{ 'Veuillez contacter un•e administrateur•rice.'|trans }}</p>
{% elseif form.montant.vars.value == false and household_based_allowance %}
<p>{{ 'Vous n\'avez pas de montant de cotisation renseigné dans votre profil, vous ne pouvez donc pas payer de cotisation.'|trans }}</p>
<p>{{ 'Veuillez contacter un•e administrateur•rice.'|trans }}</p>
{% else %} {% else %}
<p> <p>
{{ 'Montant de la cotisation à payer'|trans }} : <span class="paiement_cotisation_montant">{{ form.montant.vars.value }}</span> {{ 'Montant de la cotisation à payer'|trans }} : <span class="paiement_cotisation_montant">{{ form.montant.vars.value }}</span>
......
...@@ -9,9 +9,13 @@ ...@@ -9,9 +9,13 @@
<h5> <b>Montant de la cotisation : <span id="formEncaisserCotisationAdherent-montant-display"></span></b></h5> <h5> <b>Montant de la cotisation : <span id="formEncaisserCotisationAdherent-montant-display"></span></b></h5>
<br/> <br/>
</div> </div>
<div id="formEncaisserCotisationAdherent-no-profile" style="display:none"> <div id="formEncaisserCotisationAdherent-no-cotisation-amount" style="display:none">
<p class="formEncaisserCotisationAdherent-no-profile"> <p class="formEncaisserCotisationAdherent-no-cotisation-amount">
{% if household_based_allowance %}
L'habitant•e n'a pas de montant de cotisation renseigné, impossible de l'encaisser.
{% else %}
L'habitant•e n'a pas de profil de cotisation affecté, impossible de l'encaisser. L'habitant•e n'a pas de profil de cotisation affecté, impossible de l'encaisser.
{% endif %}
</p> </p>
</div> </div>
{% set form = getComptoirEncaisserCotisationForm(app.user) %} {% set form = getComptoirEncaisserCotisationForm(app.user) %}
......
...@@ -89,6 +89,7 @@ des_vente_adherent_email_subject: 'Vente de billets à un adhérent' ...@@ -89,6 +89,7 @@ des_vente_adherent_email_subject: 'Vente de billets à un adhérent'
des_vente_prestataire_email_subject: 'Vente de billets à un prestataire' des_vente_prestataire_email_subject: 'Vente de billets à un prestataire'
des_vente_emlc_adherent_email_subject: 'Vente de monnaie numérique à un adhérent' des_vente_emlc_adherent_email_subject: 'Vente de monnaie numérique à un adhérent'
des_vente_emlc_prestataire_email_subject: 'Vente de monnaie numérique à un prestataire' des_vente_emlc_prestataire_email_subject: 'Vente de monnaie numérique à un prestataire'
des_application_cotisation_tav: 'Complément de cotisation'
confirmation-cotisation-title: 'Cotisation bien reçue !' confirmation-cotisation-title: 'Cotisation bien reçue !'
confirmation-cotisation-content: 'Cotisation bien reçue, merci !' confirmation-cotisation-content: 'Cotisation bien reçue, merci !'
confirmation-cotisation-footer-cta: 'acheter de la monnaie locale numérique' confirmation-cotisation-footer-cta: 'acheter de la monnaie locale numérique'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment