<?php declare(strict_types=1); namespace App\Command; use App\Entity\Flux; use App\Entity\GlobalParameter; use App\Entity\Prestataire; use App\Entity\User; 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); //Send to prestataire //Send copy to tresoriers + gestionnaire de groupe (or default contact if linked gestionnaire de groupe does not exists) $copyTo = array(); $users = $this->em->getRepository(User::class)->findByRole('ROLE_TRESORIER'); foreach ($users as $userTresorier) { $copyTo[] = $userTresorier->getEmail(); } $copyTo[] = $globalParamRepo->getMailOfGestionnaireDeGroupeOrDefaultContact($p); $mail = (new \Swift_Message($subject)) ->setFrom($globalParamRepo->val(GlobalParameter::MLC_NOTIF_EMAIL)) ->setTo($p->getGestionnairesEmailsArray()) ->setCc($copyTo) ->setBody( $this->templating->render( '@kohinos/email/tav/ccas_transactions.html.twig', [ 'subject' => "Facture CCAS $monthYearStr", 'montant' => $montant, 'monthyear' => $monthYearStr ] ), 'text/html' ); return $mail; } private function fputcsvSeparatedBySemicolon($file, $arr) { fputcsv($file, $arr, ";"); } /** * @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 $this->fputcsvSeparatedBySemicolon($file, [ "Numero 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']) { $this->fputcsvSeparatedBySemicolon($file, [ 'TOTAL ' . $row['anonymous_token'], $clientTotal, $nf->format($clientTotal), '', ]); $clientTotal = 0; } //Write transaction line $this->fputcsvSeparatedBySemicolon($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 $this->fputcsvSeparatedBySemicolon($file, [ 'TOTAL ' . $row['anonymous_token'], number_format($clientTotal, 2), $this->currencySpellout($nf, $clientTotal), '', ]); //Write final total line $this->fputcsvSeparatedBySemicolon($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; } }