Commit 77c5af9c by Yvon

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

create two commands : one triggers ccas reconversion, the other sends an email with ccas transaction export
parent 838fee71
......@@ -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
<?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;
/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
class ReconversionCcasMonaPrestatairesCommand extends Command
{
//Les reconversions doivent avoir lieu le premier jour du mois
// On utilise le cron suivant :
//47 1 1 * * kohinos php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-ccas-prestataires
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;
}
}
<?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 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;
/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
class SendCcasTransactionsExportToPrestatairesCommand extends Command
{
//Les mails doivent partir le premier jour du mois
// On utilise le cron suivant :
//57 1 1 * * kohinos php /home/kohinos/kohinos/bin/console kohinos:ssa:export-ccas-transactions
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 int $month
* @param $year
* @param $montant
* @param $path
*
* @throws LoaderError
* @throws RuntimeError
* @throws SyntaxError
*/
public function prepareMail(Prestataire $p, int $month, $year, $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 $month/$year";
$globalParamRepo = $this->em->getRepository(GlobalParameter::class);
$mail = (new \Swift_Message($subject))
->setFrom($globalParamRepo->val(GlobalParameter::MLC_NOTIF_EMAIL))
//->setTo($p->getGestionnairesEmailsArray())
->setTo('yvon.kerdoncuff@gmail.com')
//->setCc($globalParamRepo->getMailOfGestionnaireDeGroupeOrDefaultContact($p))
->setBody(
$this->templating->render(
'@kohinos/email/tav/ccas_transactions.html.twig',
[
'subject' => "Facture CCAS $month/$year",
'montant' => $montant,
'month' => $month,
'year' => $year,
]
),
'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();
if ($yearmonth) {
$year = substr($yearmonth, 0, 4);
$month = intval(substr($yearmonth, 4, 2));
}
$this->io->title(
'START. Préparation des exports pour ' . count($prestas) . ' prestataires '
. 'contenant les transactions CCAS du mois ' . $month . ' de l\'année ' . $year
);
//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);
//see below why not used $filesToDelete = [];
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, $month, $year, $prestaTotal);
if ($path) {
$mail->attach(\Swift_Attachment::fromPath($path));
//see below why not used $filesToDelete[] = $path;
}
$this->mailer->send($mail);
}
}
//TODO : if we really want to delete files, we need to find another way
//TODO : as below code does not work probably due to mailer trying to access the file later.
//TODO : idea : https://stackoverflow.com/questions/15643027/swiftmailer-remove-attachment-after-send
/*
* In FileByteStream.php line 131:
* Unable to open file for reading [/.../ccastransactions/ssagir_ccastrans_2024-5_xxxx.csv]
*/
/*foreach ($filesToDelete as $ftd) {
unlink($ftd);
}*/
$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;
......@@ -135,11 +136,12 @@ class OperationUtils
);
// $account->addOperation($operation);
$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
) {
$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 réalisées au cours du mois
{{ month }}/{{ year }} 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, au cours du mois {{ month }}/{{ year }},
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