1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
<?php
namespace App\Utils;
use App\Entity\Adherent;
use App\Entity\CotisationTavPrelevementCorrectionSolde;
use App\Entity\CotisationTavPrelevementDepassementPlafond;
use App\Entity\CotisationTavReversementCorrectionSolde;
use App\Entity\Payment;
use App\Entity\Siege;
use App\Entity\Flux;
use App\Entity\CotisationTavReversement;
use App\Entity\CotisationTavPrelevement;
use App\Enum\MoyenEnum;
use App\Utils\CustomEntityManager;
use Payum\Core\Request\GetHumanStatus;
use Symfony\Component\Security\Core\Security;
class TAVCotisationUtils
{
private $em;
private $security;
private $operationUtils;
public function __construct (
CustomEntityManager $em,
Security $security,
OperationUtils $operationUtils
) {
$this->em = $em;
$this->security = $security;
$this->operationUtils = $operationUtils;
}
/**
* Check if cotisation already exist this month the the recipient of the given Flux.
*/
public function checkExistingCotisation(Flux $flux)
{
$first_day_this_month = date('Y-m-01');
$last_day_this_month = date('Y-m-t');
$existing = $this->em->getRepository(Flux::class)->getTavCotisationsBetweenDates(
$flux->getDestinataire(),
$first_day_this_month,
$last_day_this_month
);
return count($existing) > 0;
}
public function checkExistingRecurringPayment(Flux $flux)
{
$recurringPayments = $this->em->getRepository(Payment::class)->findBy([
'isRecurrent' => true,
'clientEmail' => $flux->getDestinataire()->getUser()->getEmail(),
]);
$res = "";
foreach($recurringPayments as $p) {
if (
$p->getStatus() !== GetHumanStatus::STATUS_FAILED
&& $p->getStatus() !== GetHumanStatus::STATUS_CANCELED
&& $p->getStatus() !== GetHumanStatus::STATUS_EXPIRED
&& $p->getDetails()
&& array_key_exists('vads_identifier',$p->getDetails()) //some payment without vads_identifier have status NEW but are not real recurring payments
) {
//Everytime payzen sends a recurring payment notification, notification is
//caught by notifyRecurringPaymentAction, which does not update payment status.
//This is why we can not rely on $p->getStatus to decide if a recurring
//payment is still active or ended or expired.
$reason = "";
if($p->isRecurringPaymentEndedOrExpired($reason) !== true) {
$res .= ($reason . " ");
}
}
}
return $res;
}
/**
* 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
* and register the complement as a new flux (only if rate != 1)
*
* Warning: EntityManager not flushed here.
*/
public function applyTauxCotisation(Flux $flux)
{
$profile = $flux->getDestinataire()->getProfilDeCotisation();
$cotisationTaux = $profile->getTauxCotisation();
// don't need to create an other Flux if the rate is 1
if ($cotisationTaux != 1) {
// calculate the mlc amount the user will receive
$cotisationAmount = $profile->getMontant();
$mlcAmount = round($cotisationAmount * $cotisationTaux);
// get the difference between what the user paid and what he•she's supposed to receive
$amountDiff = $mlcAmount - $cotisationAmount;
if ($flux->getExpediteur() instanceof Siege) {
$siege = $flux->getExpediteur();
} else {
$siege = $flux->getExpediteur()->getGroupe()->getSiege();
}
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($flux->getDestinataire());
$fluxCotis->setMontant($amountDiff);
$fluxCotis->setReference("Reversement cotisation après paiement de " . $cotisationAmount . "€ et application du taux " . $cotisationTaux);
} else {
// User should receive less than he•she paid: fetch the difference from his account
$fluxCotis = new CotisationTavPrelevement();
$fluxCotis->setExpediteur($flux->getDestinataire());
$fluxCotis->setDestinataire($siege);
$fluxCotis->setMontant(-$amountDiff);
$fluxCotis->setReference("Prélèvement cotisation après paiement de " . $cotisationAmount . "€ et application du taux " . $cotisationTaux);
}
$fluxCotis->setOperateur($flux->getOperateur());
$fluxCotis->setRole($flux->getRole());
$fluxCotis->setMoyen(MoyenEnum::MOYEN_EMLC);
$this->em->persist($fluxCotis);
$this->operationUtils->executeOperations($fluxCotis);
}
}
/**
* 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).
* Only create flux if amount paid != allowance amount.
*/
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();
// get the difference between what the user paid and what he•she's supposed to receive
$amountDiff = $mlcAllowanceAmount - $cotisationAmount;
// only create new flux if there is a difference
if ($amountDiff != 0) {
if ($flux->getExpediteur() instanceof Siege) {
$siege = $flux->getExpediteur();
} else {
$siege = $flux->getExpediteur()->getGroupe()->getSiege();
}
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("Versement de l'allocation complémentaire après paiement de " . $cotisationAmount . "€ pour atteindre 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("Réduction de l'allocation correspondant à un paiement de " . $cotisationAmount . "€ pour atteindre 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);
}
}
/**
* Method called to create Flux based on allowance amount (for household based allowance).
*/
public function withdrawDownToTheCeiling(Adherent $adherent)
{
$balance = $adherent->getEmlcAccount()->getBalance();
$ceiling = $adherent->getCeiling();
$siege = $this->em->getRepository(Siege::class)->getTheOne();
// get the amount we want to withdraw
$amountDiff = $ceiling - $balance;
if ($amountDiff >= 0) {
throw new \Exception("Impossible de prélèver : le solde de l'adhérent est inférieur ou égal au plafond.");
}
$flux = new CotisationTavPrelevementDepassementPlafond();
$flux->setExpediteur($adherent);
$flux->setDestinataire($siege);
$flux->setMontant(-$amountDiff);
$flux->setReference("Prélèvement pour ramener le solde de " . $balance . " MonA sous le plafond de " . $ceiling . " MonA.");
$flux->setOperateur($this->security->getUser());
$flux->setRole($this->security->getUser()->getGroups()[0]->__toString());
$flux->setMoyen(MoyenEnum::MOYEN_EMLC);
$this->em->persist($flux);
$this->operationUtils->executeOperations($flux);
return $amountDiff;
}
/**
* Method called to create Flux to fix balance (for household based allowance).
*/
public function fixBalance(Adherent $adherent, $fixedBalance, $justification)
{
$balance = $adherent->getEmlcAccount()->getBalance();
$siege = $this->em->getRepository(Siege::class)->getTheOne();
$amountDiff = $fixedBalance - $balance;
if ($amountDiff >= 0) {
//Accroissement du solde
$flux = new CotisationTavReversementCorrectionSolde();
$flux->setExpediteur($siege);
$flux->setDestinataire($adherent);
$flux->setReference(
"Reversement pour corriger le solde de " . $balance . " MonA à " . $fixedBalance . " MonA : " . $justification
);
} else {
//Réduction du solde
$flux = new CotisationTavPrelevementCorrectionSolde();
$flux->setExpediteur($adherent);
$flux->setDestinataire($siege);
$flux->setReference(
"Prélèvement pour corriger le solde de " . $balance . " MonA à " . $fixedBalance . " MonA : " . $justification
);
}
$flux->setMontant(abs($amountDiff));
$flux->setOperateur($this->security->getUser());
$flux->setRole($this->security->getUser()->getGroups()[0]->__toString());
$flux->setMoyen(MoyenEnum::MOYEN_EMLC);
$this->em->persist($flux);
$this->operationUtils->executeOperations($flux);
}
/**
* Get the last cotisation of an adhérent
*
* @param Adherent $adherent
*
* @return bool|date
*/
public function getLastTavCotisationForAdherent(?Adherent $adherent)
{
$cotisations = [];
if (null !== $adherent) {
$cotisations = $this->em->getRepository(Flux::class)->getLastTavCotisation($adherent);
}
if (count($cotisations) > 0) {
return $cotisations[0]["created_at"];
}
return false;
}
}