Commit ac19947d by Damien Moulard

Merge branch '6232-sprint5-core-story' into 'ssa-gironde'

create two commands : one triggers ccas reconversion, the other sends an email…

See merge request cooperatic/kohinos-tav!98
parents 838fee71 272fbd00
......@@ -67,6 +67,10 @@ templates/themes/custom/
#INTELLIJ
.idea/
#dir containg exported reconversions xml
#dir containing exported reconversions xml
reconversions/*
!reconversions/.versionme
#dir containing ccas transactions csv
ccastransactions/*
!ccastransactions/.versionme
\ No newline at end of file
......@@ -168,6 +168,27 @@ EN CAS D'ERREUR 500 :
Pour cela on peut utiliser la commande suivante :
**tail -f var/log/prod.xxx.log | grep CRITICAL**
## Mettre en place les CRON jobs
Ecrire dans un fichier tav dans /etc/cron.d :
Quand TAV_ENV = 1, pour envoyer les rappels de cotisation :
42 0 * * * kohinos php /home/kohinos/kohinos/bin/console kohinos:tav:mail-rappel-cotisation
Quand AUTOMATISATION_RECONVERSION = 1, pour que les reconversions aient lieu les dimanches soir qui précèdent les 14 et/ou 28 :
15 23 * * 0 kohinos [ "$(date +\%d)" -ge 08 -a "$(date +\%d)" -le 14 ] && php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires twice_a_month
15 23 * * 0 kohinos [ "$(date +\%d)" -ge 22 -a "$(date +\%d)" -le 28 ] && php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires twice_a_month
17 23 * * 0 kohinos [ "$(date +\%d)" -ge 22 -a "$(date +\%d)" -le 28 ] && php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires once_a_month
19 23 * 1,3,5,7,9,11 0 kohinos [ "$(date +\%d)" -ge 22 -a "$(date +\%d)" -le 28 ] && php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires once_every_two_month
Quand CCAS_MODE = 1, pour que les reconversions CCAS soient effectuées et les exports envoyés puis effacés du disque le 1 du mois :
47 1 1 * * kohinos php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-ccas-prestataires
57 1 1 * * kohinos php /home/kohinos/kohinos/bin/console kohinos:ssa:export-ccas-transactions
27 2 1 * * kohinos rm -f /home/kohinos/kohinos/ccastransactions/*
## Lancer le Kohinos en local
Installer le client symfony
......@@ -188,4 +209,4 @@ Compiler les assets (css & js) après modification pour tests
**$ yarn run encore dev**
Compiler les assets avant de commit :
**$ yarn run encore prod**
**$ yarn run encore prod**
\ No newline at end of file
<?php
declare(strict_types=1);
namespace App\Command;
use App\Entity\Flux;
use App\Entity\GlobalParameter;
use App\Entity\Prestataire;
use App\Entity\Reconversion;
use App\Entity\Siege;
use App\Enum\MoyenEnum;
use App\Utils\CustomEntityManager;
use App\Utils\OperationUtils;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Twig\Environment;
/**
* This command is part of the CCAS reconversion process.
* @see SendCcasTransactionsExportToPrestataire for more comments.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
class ReconversionCcasMonaPrestatairesCommand extends Command
{
protected static $defaultName = 'kohinos:ssa:reconversion-ccas-prestataires';
protected $em;
protected $mailer;
protected $templating;
protected $io;
protected $param;
protected $operationUtils;
public function __construct(
CustomEntityManager $em,
\Swift_Mailer $mailer,
Environment $templating,
OperationUtils $operationUtils
) {
$this->em = $em;
$this->mailer = $mailer;
$this->templating = $templating;
$this->operationUtils = $operationUtils;
parent::__construct();
}
protected function configure()
{
$this->setDescription('SSA : générer les flux de reconversion ccas du mois précédent');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->io = new SymfonyStyle($input, $output);
$this->io->title('START. Reconversions CCAS');
$prestas = $this->em->getRepository(Prestataire::class)->findAll();
$firstDayOfPreviousMonth = new \DateTime('first day of previous month');
foreach ($prestas as $p) {
$sumOfCcasTransactionsOfPreviousMonth = $this->em->getRepository(Flux::class)->getSumOfValidCcasTransactionsByPrestaAndMonth(
$p,
intval($firstDayOfPreviousMonth->format('m')),
$firstDayOfPreviousMonth->format('Y'),
);
$montant = round(floatval($sumOfCcasTransactionsOfPreviousMonth),2);
if ($montant <= 0) {
continue;
}
/* @var Prestataire $p */
$flux = new Reconversion();
$flux->setReconversionCcas(true);
//do not fill operator as it is automated process $entity->setOperateur();
$flux->setExpediteur($p);
$flux->setMontant($montant);
$flux->setDestinataire($this->em->getRepository(Siege::class)->getTheOne());
$flux->setMoyen(MoyenEnum::MOYEN_AUTRE);
$flux->setTauxreconversion(
!empty($p->getTauxreconversion()) ?
$p->getTauxreconversion()
: floatval(
$this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::RECONVERSION_PRESTATAIRE)
)
);
$now = new \DateTime();
$flux->setReference(
'Reconversion CCAS automatique du ' . $now->format('d/m/Y')
);
$this->operationUtils->executeOperations($flux);
}
$this->io->success('End');
$memoryUsage = memory_get_usage(true) / 1024 / 1024;
$this->io->text("Batch finished with memory: ${memoryUsage}M");
return 0;
}
}
......@@ -24,15 +24,6 @@ use Twig\Environment;
*/
class ReconversionMonaPrestatairesCommand extends Command
{
//Les reconversions doivent avoir lieu les dimanches soir qui précèdent les 14 et 28.
// On utilise les crons suivants :
//15 23 * * 0 kohinos [ "$(date +\%d)" -ge 08 -a "$(date +\%d)" -le 14 ] && php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires twice_a_month
//15 23 * * 0 kohinos [ "$(date +\%d)" -ge 22 -a "$(date +\%d)" -le 28 ] && php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires twice_a_month
//17 23 * * 0 kohinos [ "$(date +\%d)" -ge 22 -a "$(date +\%d)" -le 28 ] && php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires once_a_month
//19 23 * 1,3,5,7,9,11 0 kohinos [ "$(date +\%d)" -ge 22 -a "$(date +\%d)" -le 28 ] && php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires once_every_two_month
protected static $defaultName = 'kohinos:ssa:reconversion-prestataires';
protected $em;
......
<?php
declare(strict_types=1);
namespace App\Command;
use App\Entity\Flux;
use App\Entity\GlobalParameter;
use App\Entity\Prestataire;
use App\Utils\CustomEntityManager;
use App\Utils\OperationUtils;
use IntlDateFormatter;
use NumberFormatter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\HttpKernel\KernelInterface;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
/**
* This command is part of the CCAS reconversion process.
* It is used in conjonction with ReconversionCcasMonaPrestataires.
* It has been separated from ReconversionCcasMonaPrestataires
* so that we can replay it in case of mailing issue
* without performing the "Demande de reconversion" again.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
class SendCcasTransactionsExportToPrestatairesCommand extends Command
{
protected static $defaultName = 'kohinos:ssa:export-ccas-transactions';
protected $em;
protected $mailer;
protected $templating;
protected $io;
protected $param;
protected $operationUtils;
public function __construct(
CustomEntityManager $em,
\Swift_Mailer $mailer,
Environment $templating,
OperationUtils $operationUtils
) {
$this->em = $em;
$this->mailer = $mailer;
$this->templating = $templating;
$this->operationUtils = $operationUtils;
parent::__construct();
}
/**
* @param Prestataire $p
* @param $monthYearStr
* @param $montant
* @param $path
*
* @throws LoaderError
* @throws RuntimeError
* @throws SyntaxError
*/
public function prepareMail(Prestataire $p, $monthYearStr, $montant): \Swift_Message
{
$this->io->text('Envoi du mail pour le prestataire ' . $p->getRaison());
$subject = "Expérimentation de Sécurité Sociale de l’Alimentation en Gironde – Facture CCAS $monthYearStr";
$globalParamRepo = $this->em->getRepository(GlobalParameter::class);
$mail = (new \Swift_Message($subject))
->setFrom($globalParamRepo->val(GlobalParameter::MLC_NOTIF_EMAIL))
->setTo($p->getGestionnairesEmailsArray())
->setCc($globalParamRepo->getMailOfGestionnaireDeGroupeOrDefaultContact($p))
->setBody(
$this->templating->render(
'@kohinos/email/tav/ccas_transactions.html.twig',
[
'subject' => "Facture CCAS $monthYearStr",
'montant' => $montant,
'monthyear' => $monthYearStr
]
),
'text/html'
);
return $mail;
}
/**
* @param $year
* @param int $month
* @param Prestataire $p
* @param string $dir
* @param $data
* @param NumberFormatter $nf
*
* @return array
*/
public function createExportFile($year, int $month, Prestataire $p, string $dir, $data, NumberFormatter $nf): array
{
$filename = sprintf(
'ssagir_ccastrans_%s_%s.%s',
$year . '-' . $month,
$p->getSlug(),
'csv'
);
$path = $dir . '/' . $filename;
$file = fopen($path, 'w');
//Write header
fputcsv($file, [
"Numéro d'anonymisation",
'Montant (€)',
'Montant en lettres',
'Date',
]);
//Init data used in loop to write intermediate totals
$prestaTotal = 0;
$clientTotal = 0;
$previousAnonymousToken = $data[0]['anonymous_token'];
//Write body
foreach ($data as $row) {
//Write intermediate total line before moving to next client
if ($previousAnonymousToken !== $row['anonymous_token']) {
fputcsv($file, [
'TOTAL ' . $row['anonymous_token'],
$clientTotal,
$nf->format($clientTotal),
'',
]);
$clientTotal = 0;
}
//Write transaction line
fputcsv($file, [
$row['anonymous_token'],
$row['montant'],
$this->currencySpellout($nf, $row['montant']),
$row['created_at'],
]);
$clientTotal += $row['montant'];
$prestaTotal += $row['montant'];
}
//Write last intermediate total line
fputcsv($file, [
'TOTAL ' . $row['anonymous_token'],
number_format($clientTotal, 2),
$this->currencySpellout($nf, $clientTotal),
'',
]);
//Write final total line
fputcsv($file, [
'TOTAL ' . $p->getRaison(),
number_format($prestaTotal, 2),
$this->currencySpellout($nf, $prestaTotal),
'',
]);
fclose($file);
return [$path, $prestaTotal];
}
protected function configure()
{
$this
->setDescription('SSA : envoyer des mails avec export permettant aux prestataires de facturer le ccas')
->addOption(
'prestaid',
null,
InputOption::VALUE_REQUIRED,
"Effectuer l'action pour un seul prestataire."
)
->addOption(
'yearmonth',
null,
InputOption::VALUE_REQUIRED,
"Effectuer l'action pour le mois YYYYMM."
);
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->io = new SymfonyStyle($input, $output);
$prestaid = $input->hasOption('prestaid') ? $input->getOption('prestaid') : null;
$yearmonth = $input->hasOption('yearmonth') ? $input->getOption('yearmonth') : null;
$prestaRepo = $this->em->getRepository(Prestataire::class);
$prestas = $prestaid ? [0 => $prestaRepo->find($prestaid)] : $prestaRepo->findAll();
$dateFormatter = new IntlDateFormatter('fr_FR', IntlDateFormatter::NONE, IntlDateFormatter::NONE);
$dateFormatter->setPattern("MMMM Y");
$date = new \DateTime();
if ($yearmonth) {
$year = substr($yearmonth, 0, 4);
$month = intval(substr($yearmonth, 4, 2));
$date->setDate(intval($year),$month,1);
$monthYearStr = $dateFormatter->format($date);
} else {
$date->modify('previous month');
$year = $date->format('Y');
$month = intval($date->format('m'));
$monthYearStr = $dateFormatter->format($date);
}
$this->io->title(
'START. Préparation des exports pour ' . count($prestas) . ' prestataires '
. 'contenant les transactions CCAS pour ' . $monthYearStr
);
//projectDir is composer.json
/** @var KernelInterface $kernel */
$kernel = $this->getApplication()->getKernel();
$rootDir = $kernel->getContainer()->getParameter('kernel.project_dir');
$dir = $rootDir . '/ccastransactions';
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
$nf = new NumberFormatter('fr', NumberFormatter::SPELLOUT);
foreach ($prestas as $p) {
/* @var Prestataire $p */
$data = $this->em->getRepository(Flux::class)->getValidCcasTransactionsByPrestaAndMonth($p, $month, $year);
//only create export file if there are data
$path = null;
$prestaTotal = 0;
if ($data) {
list($path, $prestaTotal) = $this->createExportFile($year, $month, $p, $dir, $data, $nf);
}
//Send mail as soon as there are data or prestataire is CCAS OK
//If there is no data, just inform there is nothing to do
if ($data || $p->getCcasOk()) {
$mail = $this->prepareMail($p, $monthYearStr, $prestaTotal);
if ($path) {
$mail->attach(\Swift_Attachment::fromPath($path));
}
$this->mailer->send($mail);
}
}
$this->io->success('End');
$memoryUsage = memory_get_usage(true) / 1024 / 1024;
$this->io->text("Batch finished with memory: ${memoryUsage}M");
return 0;
}
private function currencySpellout($nf, $montant)
{
$amountInCents = round($montant * 100);
$cents = round($amountInCents % 100, 2);
$euros = ($amountInCents - $cents) / 100;
$eurosText = $nf->format($euros) . ' euro' . ($euros > 1 ? 's' : '');
$centsText = $cents ? ' et ' . $nf->format($cents) . ' centime' . ($cents > 0.01 ? 's' : '') : '';
return $eurosText . $centsText;
}
}
......@@ -388,20 +388,16 @@ class IndexController extends AbstractController
private function notifyGestionnaireAfterPrestaquizSent(Prestataire $presta, $type="submit")
{
$user = $this->security->getUser();
$gestionnaires = $presta->getGroupe()->getGestionnaires();
if(!$gestionnaires || $gestionnaires->isEmpty() || !$gestionnaires->first()->getEmail()) {
$to = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_CONTACT_EMAIL);
} else {
$to = $gestionnaires->first()->getEmail();
}
$globalParamRepo = $this->em->getRepository(GlobalParameter::class);
$to = $globalParamRepo->getMailOfGestionnaireDeGroupeOrDefaultContact($presta);
$subjectKeyWord = $type == "submit" ? "Réception" : "Édition";
$subject = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NAME_SMALL)
$subject = $globalParamRepo->val(GlobalParameter::MLC_NAME_SMALL)
. ' : ' . $subjectKeyWord . ' d\'un questionnaire de point de vente';
$contentKeyWord = $type == "submit" ? "soumettre" : "modifier";
$mail = (new \Swift_Message($subject))
->setFrom($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NOTIF_EMAIL)) //using $from here sometimes fails with 550 5.7.1 Sender mismatch
->setFrom($globalParamRepo->val(GlobalParameter::MLC_NOTIF_EMAIL)) //using $from here sometimes fails with 550 5.7.1 Sender mismatch
->setTo($to)
->setBody("L'utilisateur " . $user->getUsername()
. " vient de " . $contentKeyWord . " un questionnaire d'auto-évaluation pour le point de vente " . $presta->getRaison() . "."
......
......@@ -71,7 +71,7 @@ class AccountPrestataire extends Account
/**
* Can be negative amount.
*
* @param float $montant Used during a transaction to ncrement or decrement balance of CCAS transactions
* @param float $montant Used during a transaction to increment or decrement balance of CCAS transactions
*/
public function addAmountCcas(float $montant): self
{
......
......@@ -813,9 +813,14 @@ class Prestataire extends AccountableObject implements AccountableInterface
public function getGestionnairesEmailsString()
{
return join(' - ', array_map(function ($user) {
return join(' - ', $this->getGestionnairesEmailsArray());
}
public function getGestionnairesEmailsArray()
{
return array_map(function ($user) {
return $user->getEmail();
}, $this->users->getValues()));
}, $this->users->getValues());
}
public function getCaissiersString()
......
......@@ -39,6 +39,13 @@ class Reconversion extends Flux
*/
protected $reconverti = false;
/**
* Identify CCAS reconversions.
*
* @ORM\Column(type="boolean", options={"default": false})
*/
protected $reconversionCcas = false;
public function __construct()
{
parent::__construct();
......@@ -179,4 +186,16 @@ class Reconversion extends Flux
'destinataires' => ['siege', 'tresorier'],
];
}
public function getReconversionCcas(): ?bool
{
return $this->reconversionCcas;
}
public function setReconversionCcas(bool $reconversionCcas): self
{
$this->reconversionCcas = $reconversionCcas;
return $this;
}
}
<?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 Version20240508145048 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 flux ADD reconversion_ccas TINYINT(1) DEFAULT \'0\'');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE flux DROP reconversion_ccas');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8 DEFAULT NULL COLLATE `utf8_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
......@@ -7,6 +7,7 @@ use App\Entity\Comptoir;
use App\Entity\Flux;
use App\Entity\Groupe;
use App\Entity\Prestataire;
use App\Entity\Transaction;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
......@@ -29,37 +30,142 @@ class FluxRepository extends ServiceEntityRepository
/**
* @param Prestataire $presta [description]
* @param string $parenttype Parent type of flux (cotisation, transfert, transaction, vente...)
* @param string $type Type of flux (cotisation, transfert, transaction, vente...)
* @param string $from Date from which to fetch the flux
* @param array $filter For each element of array, add condition f.key = value
*
* @return Query Returns a query fo finding an array of Flux
* @return Query Returns an array of flux ids matching conditions
*/
public function getQueryByPrestataire(Prestataire $presta, string $parenttype = null, string $type = null, $from = null)
private function getQueryByPrestataireCore(Prestataire $presta, $filter = null, $from = null, $to = null)
{
$sqlQuery = "SELECT f.id FROM {$this->tableName} f WHERE (f.prestataire_id = :id OR f.prestataire_dest_id = :id)";
if (null != $parenttype) {
$sqlQuery .= ' AND f.parenttype = :type';
}
if ($type != null) {
$sqlQuery .= " AND f.type = :type";
}
if ($from != null) {
$sqlQuery .= " AND f.created_at >= :from";
}
$statement = $this->connection->prepare($sqlQuery);
if (null != $parenttype) {
$statement->bindValue(':type', $parenttype);
if ($to != null) {
$sqlQuery .= " AND f.created_at <= :to";
}
if ($type != null) {
$statement->bindValue(':type', $type);
$i = 0;
foreach ($filter as $key => $value) {
if($value === null) { //for convenience
$sqlQuery .= " AND f." . $key . " IS NULL";
} else {
$sqlQuery .= " AND f." . $key . " = :param_$i";
}
$i++;
}
$statement = $this->connection->prepare($sqlQuery);
if ($from != null) {
$statement->bindValue(':from', $from);
}
if ($to != null) {
$statement->bindValue(':to', $to);
}
$i = 0;
foreach ($filter as $key => $value) {
if($value !== null) {
$statement->bindValue(":param_$i", $value);
}
$i++;
}
$statement->bindValue(':id', $presta->getId());
$statement->execute();
$results = $statement->fetchAll();
return $statement->fetchAll();
}
/**
* @param Prestataire $presta [description]
* @param integer $m From 1 to 12
* @param integer $Y Format YYYY
*/
public function getValidCcasTransactionsByPrestaAndMonth(Prestataire $presta, $m, $Y)
{
//It's easier to write full SQL query because we want to join adherent to select and sort by anonymous token
$sqlQuery = "
SELECT
a.anonymous_token as anonymous_token,
f.montant as montant,
f.created_at as created_at
FROM {$this->tableName} f
JOIN adherent a ON f.adherent_id = a.id
WHERE (f.prestataire_id = :id OR f.prestataire_dest_id = :id)
AND f.created_at >= :from
AND f.created_at <= :to
AND f.cancellerflux_id IS NULL
AND f.is_ccas = :is_ccas
AND f.type = :type
ORDER BY a.anonymous_token ASC, f.created_at ASC
";
$statement = $this->connection->prepare($sqlQuery);
$statement->bindValue(':id', $presta->getId());
$from = new \DateTime();
$from->setDate($Y,$m,1)->setTime(0,0);
$statement->bindValue(':from', $from->format("Y-m-d H:i:s"));
$to = new \DateTime();
$to->setDate($Y,$m+1,0)->setTime(23,59,59);
$statement->bindValue(':to', $to->format("Y-m-d H:i:s"));
$statement->bindValue(":is_ccas", true);
$statement->bindValue(":type", Transaction::TYPE_TRANSACTION_ADHERENT_PRESTATAIRE);
$statement->execute();
return $statement->fetchAll();
}
/**
* @param Prestataire $presta [description]
* @param integer $m From 1 to 12
* @param integer $Y Format YYYY
*/
public function getSumOfValidCcasTransactionsByPrestaAndMonth(Prestataire $presta, $m, $Y)
{
$filter = array();
$filter['is_ccas'] = true;
$filter['cancellerflux_id'] = null; //exclude cancelled transactions
$filter['type'] = Transaction::TYPE_TRANSACTION_ADHERENT_PRESTATAIRE; //exclude cancellers
$from = new \DateTime();
$from->setDate($Y,$m,1)->setTime(0,0);
$to = new \DateTime();
$to->setDate($Y,$m+1,0)->setTime(23,59,59);
$ids = $this->getQueryByPrestataireCore(
$presta,
$filter,
$from->format("Y-m-d H:i:s"),
$to->format("Y-m-d H:i:s")
);
$qb = $this->createQueryBuilder('f');
return $qb
->select('sum(f.montant)')
->where($qb->expr()->in('f.id', ':ids'))
->setParameter('ids', $ids)
->orderBy('f.createdAt', 'DESC')
->getQuery()
->getSingleScalarResult();
}
/**
* @param Prestataire $presta [description]
* @param string $parenttype Parent type of flux (cotisation, transfert, transaction, vente...)
* @param string $type Type of flux (cotisation, transfert, transaction, vente...)
* @param string $from Date from which to fetch the flux
*
* @return Query Returns a query for finding an array of Flux
*/
public function getQueryByPrestataire(Prestataire $presta, string $parenttype = null, string $type = null, $from = null)
{
$filter = array();
if ($parenttype) {
$filter['parenttype'] = $parenttype;
}
if ($type) {
$filter['type'] = $type;
}
$results = $this->getQueryByPrestataireCore($presta,$filter,$from);
$qb = $this->createQueryBuilder('f');
return $qb
......
......@@ -49,4 +49,14 @@ class GlobalParameterRepository extends ServiceEntityRepository
return $global ? $global->getValue() : '';
}
public function getMailOfGestionnaireDeGroupeOrDefaultContact($presta)
{
$gestionnaires = $presta->getGroupe()->getGestionnaires();
if (!$gestionnaires || $gestionnaires->isEmpty() || !$gestionnaires->first()->getEmail()) {
return $this->val(GlobalParameter::MLC_CONTACT_EMAIL);
} else {
return $gestionnaires->first()->getEmail();
}
}
}
......@@ -16,6 +16,7 @@ use App\Entity\OperationGroupe;
use App\Entity\OperationPrestataire;
use App\Entity\OperationSiege;
use App\Entity\Prestataire;
use App\Entity\Reconversion;
use App\Entity\Siege;
use App\Entity\Transaction;
use App\Enum\CurrencyEnum;
......@@ -134,12 +135,22 @@ class OperationUtils
LockMode::PESSIMISTIC_WRITE
);
// $account->addOperation($operation);
/*
* Apply operations on account amounts.
* One flux between two acounts is composed of two operations.
* These operations are configured by getAllOperations methods.
* These operations can be positive or negative.
* For example, a flux of type Reconversion will decrease
* accounts of prestataire and siege (see Reconversion.php).
*/
$account->addAmount($operation->getMontant());
if(
$flux instanceof Transaction //only process $flux with getIsCcas method
&& $flux->getIsCcas() //check if this flux should increment ccas balance
&& $account instanceof AccountPrestataire //only process the prestataire side of the flux
) {
//Update special account "balance ccas" for some operations only
$updateBalanceCcas =
//only process isCcas or reconversionCcas $flux
( $flux instanceof Transaction && $flux->getIsCcas() || $flux instanceof Reconversion && $flux->getReconversionCcas() )
//only process the prestataire side of the flux
&& $account instanceof AccountPrestataire;
if ($updateBalanceCcas) {
$account->addAmountCcas($operation->getMontant());
}
$em->persist($operation);
......
{% extends '@kohinos/email/email_layout.html.twig' %}
{% set title %}{% spaceless %}
{{ subject }}
{% endspaceless %}
{% endset %}
{% block content %}
<p>
Cher point de vente,
</p>
{% if montant > 0 %}
<p>
Vous trouverez ci-joint l’état mensuel des dépenses en MonA en
{{ monthyear }} dans votre point de vente par les allocataires du dispositif de Sécurité Sociale
de l'Alimentation en Gironde, pour un montant de {{ montant }} euros.
</p>
<p>
Pour rappel, il s’agit uniquement des foyers éligibles aux
aides facultatives du CCAS, désignés par leur code client : les autres dépenses sont
converties en euros par Acclimat’action en direct.
</p>
<p>
Pour permettre au CCAS de reconvertir ces MonA en euros, nous vous prions de bien vouloir adresser par mail au
Service des Aides Financières du CCAS (di.saf@mairie-bordeaux.fr), au plus tard le 7 de ce mois, les justificatifs suivants :
</p>
<ul>
<li>L’état mensuel des dépenses par code client (ci-joint) signé</li>
<li>Une facture détaillée mensuelle de votre point de vente comprenant l’état mensuel des dépenses, respectant le formalisme suivant :
<ul>
<li>Entête : Facture XXXX<br/><br/></li>
<li>Emetteur :<br/>
Nom du tiers agréé<br/>
Adresse<br/>
Coordonnées téléphoniques du référent comptable<br/>
Coordonnées bancaires du tiers agréé<br/><br/></li>
<li>Destinataire :<br/>
CCAS de Bordeaux<br/>
Cité municipale, 4 rue Claude Bonnier<br/>
33045 Bordeaux Cedex<br/><br/></li>
<li>Détail : code client, montant mensuel dépensé par code client (en chiffres et en lettres),
date de la période couverte, montant total des dépenses dans le point de vente (en chiffres et en lettres)<br/><br/></li>
<li>Date de la facture + cachet et signature</li>
</ul>
</li>
</ul>
{% else %}
<p>
Pour information, en {{ monthyear }},
vous n'avez pas réalisé de transaction éligible aux reconversions CCAS et vous n'avez donc rien à faire de plus.
</p>
{% endif %}
<p>
Merci encore pour votre participation à cette expérimentation de Sécurité Sociale de l’Alimentation en Gironde !
</p>
<p>
Acclimat’action
</p>
{% endblock %}
\ No newline at end of file
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