Entite Echeancier : - MAJORATION_RATE = 0.05 constante - getMajoration() : montant de la majoration - getTotalWithMajoration() : total creance + 5% - getMonthlyAmount() : calcule sur le total majore Controller create : - Echeances calculees sur le total majore (pas le total brut) PDF EcheancierPdf : - Bloc resume : creance, majoration (rouge), total a payer - Tableau total : "TOTAL (creance + majoration 5%)" Email proposition : - Lignes creance, majoration +5% (rouge), total a payer Page process (client) : - 4 colonnes : creance, majoration 5%, total a payer, mensualite Admin show : - Carte "Creance + majoration 5%" avec detail Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
289 lines
10 KiB
PHP
289 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Service\Pdf;
|
|
|
|
use App\Entity\Echeancier;
|
|
use setasign\Fpdi\Fpdi;
|
|
use Symfony\Component\HttpKernel\KernelInterface;
|
|
|
|
if (!\defined('EURO')) {
|
|
\define('EURO', \chr(128)); // @codeCoverageIgnore
|
|
}
|
|
|
|
class EcheancierPdf extends Fpdi
|
|
{
|
|
public function __construct(
|
|
private readonly KernelInterface $kernel,
|
|
private readonly Echeancier $echeancier,
|
|
) {
|
|
parent::__construct();
|
|
$this->SetTitle($this->enc('Echeancier de paiement - '.$this->echeancier->getCustomer()->getFullName()));
|
|
$this->SetAuthor($this->enc('Association E-Cosplay'));
|
|
}
|
|
|
|
public function generate(): void
|
|
{
|
|
$this->AliasNbPages();
|
|
$this->AddPage();
|
|
|
|
$this->writeHeader();
|
|
$this->writeContextBlock();
|
|
$this->writeEcheancesTable();
|
|
$this->writeConditions();
|
|
$this->writeSignatures();
|
|
}
|
|
|
|
/** @codeCoverageIgnore */
|
|
public function Header(): void
|
|
{
|
|
}
|
|
|
|
/** @codeCoverageIgnore */
|
|
public function Footer(): void
|
|
{
|
|
$this->SetY(-22);
|
|
$this->SetDrawColor(253, 140, 4);
|
|
$this->Line(15, $this->GetY(), 195, $this->GetY());
|
|
$this->Ln(3);
|
|
$this->SetFont('Arial', '', 7);
|
|
$this->SetTextColor(0, 0, 0);
|
|
$this->Cell(190, 3, $this->enc('42 rue de Saint-Quentin - 02800 BEAUTOR - Tel: 06 79 34 88 02 - contact@e-cosplay.fr'), 0, 1, 'C');
|
|
$this->Cell(190, 3, $this->enc('Association E-Cosplay - N SIRET 943 121 517 00011 - CODE APE 9329Z - RNA W022006988'), 0, 1, 'C');
|
|
$this->SetFont('Arial', 'I', 7);
|
|
$this->SetTextColor(150, 150, 150);
|
|
$this->Cell(190, 3, $this->enc('Page ').$this->PageNo().' / {nb}', 0, 0, 'C');
|
|
}
|
|
|
|
/** @codeCoverageIgnore */
|
|
private function writeHeader(): void
|
|
{
|
|
$logo = $this->kernel->getProjectDir().'/public/logo.jpg';
|
|
if (file_exists($logo)) {
|
|
$this->Image($logo, 10, 8, 45);
|
|
}
|
|
|
|
$this->SetFont('Arial', 'B', 16);
|
|
$this->SetXY(60, 10);
|
|
$this->Cell(0, 8, $this->enc('ECHEANCIER DE PAIEMENT'), 0, 1, 'L');
|
|
|
|
$formatter = new \IntlDateFormatter(
|
|
'fr_FR',
|
|
\IntlDateFormatter::FULL,
|
|
\IntlDateFormatter::NONE,
|
|
'Europe/Paris',
|
|
\IntlDateFormatter::GREGORIAN
|
|
);
|
|
|
|
$this->SetFont('Arial', '', 10);
|
|
$this->SetXY(60, 19);
|
|
$this->Cell(0, 5, $this->enc('Emis a Beautor, le '.$formatter->format($this->echeancier->getCreatedAt())), 0, 1, 'L');
|
|
|
|
// Client
|
|
$this->SetFont('Arial', 'B', 11);
|
|
$customer = $this->echeancier->getCustomer();
|
|
$y = 35;
|
|
$this->SetXY(120, $y);
|
|
$name = $customer->getRaisonSociale() ?: $customer->getFullName();
|
|
$this->Cell(0, 5, $this->enc($name), 0, 1, 'L');
|
|
|
|
if ($address = $customer->getAddress()) {
|
|
$y += 5;
|
|
$this->SetXY(120, $y);
|
|
$this->SetFont('Arial', '', 10);
|
|
$this->Cell(0, 5, $this->enc($address), 0, 1, 'L');
|
|
}
|
|
|
|
$y += 5;
|
|
$this->SetXY(120, $y);
|
|
$this->SetFont('Arial', '', 10);
|
|
$cityLine = ($customer->getZipCode() ?? '').' '.($customer->getCity() ?? '');
|
|
$this->Cell(0, 5, $this->enc(trim($cityLine)), 0, 1, 'L');
|
|
|
|
if ($customer->getEmail()) {
|
|
$y += 5;
|
|
$this->SetXY(120, $y);
|
|
$this->Cell(0, 5, $this->enc($customer->getEmail()), 0, 1, 'L');
|
|
}
|
|
|
|
$this->Ln(10);
|
|
}
|
|
|
|
/** @codeCoverageIgnore */
|
|
private function writeContextBlock(): void
|
|
{
|
|
$this->SetY(65);
|
|
|
|
$this->SetDrawColor(200, 200, 200);
|
|
$this->Cell(0, 0.5, '', 'T', 1, 'L');
|
|
$this->Ln(3);
|
|
|
|
$this->SetFont('Arial', 'B', 11);
|
|
$this->Cell(0, 6, $this->enc('OBJET DE L\'ECHEANCIER'), 0, 1, 'L');
|
|
$this->Ln(2);
|
|
|
|
$this->SetFont('Arial', '', 10);
|
|
$this->MultiCell(0, 5, $this->enc($this->echeancier->getDescription()), 0, 'L');
|
|
$this->Ln(3);
|
|
|
|
$labelW = 55;
|
|
|
|
$this->SetFont('Arial', '', 10);
|
|
$this->Cell($labelW, 6, $this->enc('Montant de la creance :'), 0, 0, 'L');
|
|
$this->SetFont('Arial', 'B', 10);
|
|
$this->Cell(0, 6, number_format((float) $this->echeancier->getTotalAmountHt(), 2, ',', ' ').' '.EURO, 0, 1, 'L');
|
|
|
|
$this->SetFont('Arial', '', 10);
|
|
$this->Cell($labelW, 6, $this->enc('Majoration (5%) :'), 0, 0, 'L');
|
|
$this->SetFont('Arial', 'B', 10);
|
|
$this->SetTextColor(220, 38, 38);
|
|
$this->Cell(0, 6, '+ '.number_format($this->echeancier->getMajoration(), 2, ',', ' ').' '.EURO, 0, 1, 'L');
|
|
$this->SetTextColor(0, 0, 0);
|
|
|
|
$this->SetFont('Arial', '', 10);
|
|
$this->Cell($labelW, 6, $this->enc('Total a payer :'), 0, 0, 'L');
|
|
$this->SetFont('Arial', 'B', 11);
|
|
$this->Cell(0, 6, number_format($this->echeancier->getTotalWithMajoration(), 2, ',', ' ').' '.EURO, 0, 1, 'L');
|
|
|
|
$this->SetFont('Arial', '', 10);
|
|
$this->Cell($labelW, 6, $this->enc('Nombre d\'echeances :'), 0, 0, 'L');
|
|
$this->SetFont('Arial', 'B', 10);
|
|
$this->Cell(0, 6, (string) $this->echeancier->getNbLines().' mois', 0, 1, 'L');
|
|
|
|
$this->SetFont('Arial', '', 10);
|
|
$this->Cell($labelW, 6, $this->enc('Mensualite :'), 0, 0, 'L');
|
|
$this->SetFont('Arial', 'B', 10);
|
|
$this->Cell(0, 6, number_format($this->echeancier->getMonthlyAmount(), 2, ',', ' ').' '.EURO.'/mois', 0, 1, 'L');
|
|
|
|
$this->Ln(2);
|
|
$this->Cell(0, 0.5, '', 'T', 1, 'L');
|
|
$this->Ln(3);
|
|
}
|
|
|
|
/** @codeCoverageIgnore */
|
|
private function writeEcheancesTable(): void
|
|
{
|
|
$this->SetFont('Arial', 'B', 11);
|
|
$this->Cell(0, 6, $this->enc('TABLEAU DES ECHEANCES'), 0, 1, 'L');
|
|
$this->Ln(2);
|
|
|
|
// En-tete tableau
|
|
$this->SetFont('Arial', 'B', 9);
|
|
$this->SetFillColor(35, 35, 35);
|
|
$this->SetTextColor(255, 255, 255);
|
|
$this->Cell(15, 7, $this->enc('N'), 1, 0, 'C', true);
|
|
$this->Cell(55, 7, $this->enc('Date de prelevement'), 1, 0, 'C', true);
|
|
$this->Cell(50, 7, $this->enc('Montant HT'), 1, 0, 'C', true);
|
|
$this->Cell(50, 7, $this->enc('Statut'), 1, 1, 'C', true);
|
|
$this->SetTextColor(0, 0, 0);
|
|
|
|
$this->SetFont('Arial', '', 9);
|
|
$fill = false;
|
|
$months = [1 => 'Janvier', 'Fevrier', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Aout', 'Septembre', 'Octobre', 'Novembre', 'Decembre'];
|
|
|
|
foreach ($this->echeancier->getLines() as $line) {
|
|
$this->SetFillColor(245, 245, 240);
|
|
|
|
$monthName = $months[(int) $line->getScheduledAt()->format('n')] ?? '';
|
|
$dateLabel = $line->getScheduledAt()->format('d').' '.$monthName.' '.$line->getScheduledAt()->format('Y');
|
|
|
|
$this->Cell(15, 6, (string) $line->getPosition(), 'B', 0, 'C', $fill);
|
|
$this->Cell(55, 6, $this->enc($dateLabel), 'B', 0, 'L', $fill);
|
|
$this->Cell(50, 6, number_format((float) $line->getAmount(), 2, ',', ' ').' '.EURO, 'B', 0, 'R', $fill);
|
|
$this->Cell(50, 6, $this->enc('A prelever'), 'B', 1, 'C', $fill);
|
|
|
|
$fill = !$fill;
|
|
}
|
|
|
|
// Total avec majoration
|
|
$this->SetFont('Arial', 'B', 10);
|
|
$this->SetFillColor(253, 191, 4);
|
|
$this->Cell(70, 8, $this->enc(' TOTAL (creance + majoration 5%)'), 0, 0, 'L', true);
|
|
$this->Cell(50, 8, number_format($this->echeancier->getTotalWithMajoration(), 2, ',', ' ').' '.EURO, 0, 0, 'R', true);
|
|
$this->Cell(50, 8, '', 0, 1, 'C', true);
|
|
|
|
$this->Ln(5);
|
|
}
|
|
|
|
/** @codeCoverageIgnore */
|
|
private function writeConditions(): void
|
|
{
|
|
$this->SetFont('Arial', 'B', 10);
|
|
$this->Cell(0, 6, $this->enc('CONDITIONS'), 0, 1, 'L');
|
|
$this->Ln(1);
|
|
|
|
$this->SetFont('Arial', '', 9);
|
|
$conditions = [
|
|
'Le prelevement sera effectue automatiquement a chaque date prevue via Stripe.',
|
|
'En cas d\'echec de prelevement, une relance sera envoyee par email.',
|
|
'Apres 2 echecs consecutifs, l\'echeancier sera considere en defaut.',
|
|
'Le client peut contacter contact@e-cosplay.fr pour toute question.',
|
|
'Majoration de 5% du montant total conformement aux CGV (article 11).',
|
|
];
|
|
|
|
foreach ($conditions as $i => $condition) {
|
|
$this->Cell(5, 5, '', 0, 0);
|
|
$this->Cell(5, 5, ($i + 1).'.', 0, 0, 'R');
|
|
$this->Cell(0, 5, $this->enc(' '.$condition), 0, 1, 'L');
|
|
}
|
|
|
|
$this->Ln(5);
|
|
}
|
|
|
|
/** @codeCoverageIgnore */
|
|
private function writeSignatures(): void
|
|
{
|
|
// S'assurer qu'on a assez de place
|
|
if ($this->GetY() + 50 > $this->GetPageHeight() - 25) {
|
|
$this->AddPage();
|
|
}
|
|
|
|
$this->SetDrawColor(200, 200, 200);
|
|
$this->Cell(0, 0.5, '', 'T', 1, 'L');
|
|
$this->Ln(3);
|
|
|
|
$formatter = new \IntlDateFormatter(
|
|
'fr_FR',
|
|
\IntlDateFormatter::LONG,
|
|
\IntlDateFormatter::NONE,
|
|
'Europe/Paris',
|
|
\IntlDateFormatter::GREGORIAN
|
|
);
|
|
|
|
$this->SetFont('Arial', '', 9);
|
|
$this->Cell(0, 5, $this->enc('Fait a Beautor, le '.$formatter->format(new \DateTime())), 0, 1, 'L');
|
|
$this->Ln(3);
|
|
|
|
// 2 colonnes de signature
|
|
$colWidth = 85;
|
|
$signY = $this->GetY();
|
|
|
|
// Signature E-Cosplay (gauche) - auto-signee
|
|
$this->SetFont('Arial', 'B', 9);
|
|
$this->SetXY(15, $signY);
|
|
$this->Cell($colWidth, 5, $this->enc('Pour Association E-Cosplay :'), 0, 1, 'L');
|
|
$this->SetXY(15, $signY + 7);
|
|
$this->SetFont('Arial', '', 10);
|
|
$this->Cell($colWidth, 20, '{{Sign;type=signature;role=Company}}', 0, 0, 'L');
|
|
|
|
// Signature Client (droite)
|
|
$this->SetFont('Arial', 'B', 9);
|
|
$this->SetXY(110, $signY);
|
|
$customerName = $this->echeancier->getCustomer()->getRaisonSociale() ?: $this->echeancier->getCustomer()->getFullName();
|
|
$this->Cell($colWidth, 5, $this->enc('Pour '.$customerName.' :'), 0, 1, 'L');
|
|
$this->SetXY(110, $signY + 7);
|
|
$this->SetFont('Arial', '', 10);
|
|
$this->Cell($colWidth, 20, '{{SignClient;type=signature;role=First Party}}', 0, 0, 'L');
|
|
|
|
$this->SetY($signY + 35);
|
|
$this->SetFont('Arial', 'I', 8);
|
|
$this->SetTextColor(150, 150, 150);
|
|
$this->Cell(0, 4, $this->enc('Signature electronique via DocuSeal - Valeur juridique (reglement eIDAS, art. 1367 Code civil)'), 0, 1, 'C');
|
|
$this->SetTextColor(0, 0, 0);
|
|
}
|
|
|
|
private function enc(string $text): string
|
|
{
|
|
return mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
|
|
}
|
|
}
|