✨ feat(src/Service/Customer): Ajoute la génération de PDF pour impayés.
✨ feat(src/Command): Formatte les dates dans AutoCreatedAvisPaymentCommand. ✨ feat(templates): Ajoute un lien pour la liste des impayés. ✨ feat(src/Service/Docuseal): Corrige l'URL du fichier Docuseal. ✨ feat(src/Controller): Ajoute la génération de la liste des impayés. 📝 chore(translations): Ajoute des traductions pour les statuts de factures.
This commit is contained in:
2
.env
2
.env
@@ -75,5 +75,5 @@ AMAZON_SES_SECRET=BD63dADmgFJJPnjlT9utRDlvcOh8pRH3eOZXsyhNL/F3
|
||||
CLOUDFLARE_TOKEN=4mqx9d7ynvoeCaXonJA07U19rH8gGhctqp7j2Lch
|
||||
MAILCOW_KEY=DF0E7E-0FD059-16226F-8ECFF1-E558B3
|
||||
|
||||
DEV_URL=https://a8afd3b350a5.ngrok-free.app
|
||||
DEV_URL=https://086e682e904b.ngrok-free.app
|
||||
SENTRY_BACKEND=https://dcf4ed12f5844686f088838f26082bf0@o4510197735948288.ingest.de.sentry.io/4510197737979984
|
||||
|
||||
@@ -67,8 +67,8 @@ class AutoCreatedAvisPaymentCommand extends Command
|
||||
unset($content[0]);
|
||||
$desciption = implode("\n", $content);
|
||||
$desciption = str_replace("{ndd}",$value->getNdd(),$desciption);
|
||||
$desciption = str_replace("{date_start}",$expiredAt->format('M Y'),$desciption);
|
||||
$desciption = str_replace("{date_stop}",$newLimitAt->format('M Y'),$desciption);
|
||||
$desciption = str_replace("{date_start}",$expiredAt->format('d M Y'),$desciption);
|
||||
$desciption = str_replace("{date_stop}",$newLimitAt->format('d M Y'),$desciption);
|
||||
|
||||
$avisLine = new CustomerAdvertPaymentLine();
|
||||
$avisLine->setPos(0);
|
||||
@@ -85,8 +85,8 @@ class AutoCreatedAvisPaymentCommand extends Command
|
||||
unset($content[0]);
|
||||
$desciption = implode("\n", $content);
|
||||
$desciption = str_replace("{ndd}",$value->getNdd(),$desciption);
|
||||
$desciption = str_replace("{date_start}",$expiredAt->format('M Y'),$desciption);
|
||||
$desciption = str_replace("{date_stop}",$newLimitAt->format('M Y'),$desciption);
|
||||
$desciption = str_replace("{date_start}",$expiredAt->format('d M Y'),$desciption);
|
||||
$desciption = str_replace("{date_stop}",$newLimitAt->format('d M Y'),$desciption);
|
||||
|
||||
|
||||
$avisLine2 = new CustomerAdvertPaymentLine();
|
||||
|
||||
@@ -43,6 +43,7 @@ use App\Service\Customer\Billing\{
|
||||
use App\Service\Customer\{CreateAvisEvent,
|
||||
CreateCustomerNddEmailEvent,
|
||||
CreateFactureEvent,
|
||||
CreateListUnpaidEvent,
|
||||
CustomerSendPasswordEmail,
|
||||
DeleteCustomerEvent,
|
||||
EventSpaceCustomerCreated,
|
||||
@@ -673,7 +674,12 @@ class CustomerController extends AbstractController
|
||||
return $this->redirectToRoute('artemis_intranet_customer_view', ['id' => $customer->getId()]);
|
||||
}
|
||||
|
||||
|
||||
if($request->query->has('extract')) {
|
||||
if($request->query->get('extract') == "impaye") {
|
||||
$event = new CreateListUnpaidEvent($customer);
|
||||
$eventDispatcher->dispatch($event);
|
||||
}
|
||||
}
|
||||
return $this->render('artemis/intranet/customer/edit.twig', [
|
||||
'form' => $form->createView(),
|
||||
'formNdd' => $formNdd->createView(),
|
||||
|
||||
@@ -15,6 +15,7 @@ use App\Service\Mailer\Mailer;
|
||||
use App\Service\Pdf\DevisPdf;
|
||||
use App\Service\Pdf\FacturePdf;
|
||||
use App\Service\Pdf\PaymentPdf;
|
||||
use App\Service\Pdf\UnpaidPdf;
|
||||
use App\Service\Stancer\Client;
|
||||
use App\Service\Vault\VaultClient;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -39,6 +40,7 @@ use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
|
||||
#[AsEventListener(event: SiteconseilAdvertPaymentComplete::class, method: 'onSiteconseilAdvertPaymentComplete')]
|
||||
#[AsEventListener(event: CustomerAdvertPaymentComplete::class, method: 'onCustomerAdvertPaymentComplete')]
|
||||
#[AsEventListener(event: EventSpaceCustomerCreated::class, method: 'onEventSpaceCustomerCreated')]
|
||||
#[AsEventListener(event: CreateListUnpaidEvent::class, method: 'onCreateListUnpaidEvent')]
|
||||
class BillingEventSusbriber
|
||||
{
|
||||
|
||||
@@ -53,6 +55,20 @@ class BillingEventSusbriber
|
||||
private readonly KernelInterface $kernel
|
||||
){
|
||||
}
|
||||
public function onCreateListUnpaidEvent(CreateListUnpaidEvent $createListUnpaidEvent)
|
||||
{
|
||||
$customer = $createListUnpaidEvent->getCustomer();
|
||||
$advert = $customer->getCustomerAdvertPayments();
|
||||
$lists =[];
|
||||
foreach ($advert as $advertPayment) {
|
||||
if($advertPayment->getState() == "send_avis" || $advertPayment->getState() =="unpaid") {
|
||||
$lists[] = $advertPayment;
|
||||
}
|
||||
}
|
||||
$pdf = New UnpaidPdf($lists,$customer,$this->kernel);
|
||||
$pdf->generate();
|
||||
$pdf->Output('I');
|
||||
}
|
||||
|
||||
public function onEventSpaceCustomerCreated(EventSpaceCustomerCreated $event): void
|
||||
{
|
||||
@@ -178,7 +194,8 @@ class BillingEventSusbriber
|
||||
$this->entityManager->persist($order);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$this->onCreateFactureEventSend(new CreateFactureEventSend($order));
|
||||
if($isSend)
|
||||
$this->onCreateFactureEventSend(new CreateFactureEventSend($order));
|
||||
}
|
||||
public function onCreatedAvisEventSend(CreateAvisEventSend $createAvisEventSend)
|
||||
{
|
||||
|
||||
23
src/Service/Customer/CreateListUnpaidEvent.php
Normal file
23
src/Service/Customer/CreateListUnpaidEvent.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Customer;
|
||||
|
||||
use App\Entity\Customer;
|
||||
|
||||
class CreateListUnpaidEvent
|
||||
{
|
||||
private Customer $customer;
|
||||
|
||||
public function __construct(Customer $customer)
|
||||
{
|
||||
$this->customer = $customer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Customer
|
||||
*/
|
||||
public function getCustomer(): Customer
|
||||
{
|
||||
return $this->customer;
|
||||
}
|
||||
}
|
||||
@@ -91,12 +91,13 @@ class SignClient
|
||||
unlink($this->kernelInterface->getProjectDir()."/public/tmp-sign/facture.pdf");
|
||||
file_put_contents($this->kernelInterface->getProjectDir()."/public/tmp-sign/facture.pdf",$content);
|
||||
|
||||
|
||||
$submission = $this->docuseal->createSubmissionFromPdf([
|
||||
'name' => 'Facture N°' . $customerOrder->getNumOrder(),
|
||||
'documents' => [
|
||||
[
|
||||
'name' => 'facture_'.$customerOrder->getNumOrder(),
|
||||
'file' => $this->requestStack->getCurrentRequest()."/tmp-sign/facture.pdf",
|
||||
'file' => (($_ENV['APP_ENV'] == "dev")?$_ENV['DEV_URL']:$this->requestStack->getCurrentRequest()->getSchemeAndHttpHost())."/tmp-sign/facture.pdf",
|
||||
],
|
||||
],
|
||||
'submitters' => [
|
||||
@@ -109,6 +110,7 @@ class SignClient
|
||||
]);
|
||||
$submiter = $this->docuseal->getSubmitter($submission['submitters'][0]['id']);
|
||||
$path = $submiter['documents'][0]['url'];
|
||||
unlink($this->kernelInterface->getProjectDir()."/public/tmp-sign/facture.pdf");
|
||||
return $path;
|
||||
|
||||
}
|
||||
|
||||
197
src/Service/Pdf/UnpaidPdf.php
Normal file
197
src/Service/Pdf/UnpaidPdf.php
Normal file
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Pdf;
|
||||
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\CustomerAdvertPayment;
|
||||
use App\Kernel;
|
||||
use Fpdf\Fpdf;
|
||||
use IntlDateFormatter;
|
||||
|
||||
if (!defined('EURO_UNPAID')) {
|
||||
define('EURO_UNPAID', chr(128));
|
||||
}
|
||||
|
||||
class UnpaidPdf extends FPDF
|
||||
{
|
||||
private Customer $customer;
|
||||
/**
|
||||
* @var CustomerAdvertPayment[] $list
|
||||
*/
|
||||
private array $list;
|
||||
private Kernel $kernel;
|
||||
|
||||
private function e(string $string): string
|
||||
{
|
||||
// Convert string from UTF-8 (source) to ISO-8859-1 (target)
|
||||
return mb_convert_encoding($string, 'ISO-8859-1', 'UTF-8');
|
||||
}
|
||||
|
||||
public function __construct(array $list, Customer $customer, Kernel $kernel)
|
||||
{
|
||||
$this->list = $list;
|
||||
$this->customer = $customer;
|
||||
$this->kernel = $kernel;
|
||||
parent::__construct('P', 'mm', 'A4');
|
||||
}
|
||||
|
||||
public function Header(): void
|
||||
{
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Image($this->kernel->getProjectDir() . "/public/assets/logo_siteconseil.png", 90, 5, 25);
|
||||
$formatter = new IntlDateFormatter(
|
||||
'fr_FR',
|
||||
IntlDateFormatter::FULL,
|
||||
IntlDateFormatter::NONE,
|
||||
'Europe/Paris',
|
||||
IntlDateFormatter::GREGORIAN
|
||||
);
|
||||
$t = new \DateTimeImmutable();
|
||||
$dateText = $this->e("Saint-Quentin, " . $formatter->format($t));
|
||||
|
||||
$this->Text(15, 85, $dateText);
|
||||
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
|
||||
$y = 60;
|
||||
$customer = $this->customer;
|
||||
|
||||
$this->Text(110, $y, $this->e($customer->getRaisonSocial()));
|
||||
$y += 5;
|
||||
$this->Text(110, $y, $this->e($customer->getAddress()));
|
||||
|
||||
if ($address2 = $customer->getAddress2()) {
|
||||
$y += 5;
|
||||
$this->Text(110, $y, $this->e($address2));
|
||||
}
|
||||
|
||||
if ($address3 = $customer->getAddress3()) {
|
||||
$y += 5;
|
||||
$this->Text(110, $y, $this->e($address3));
|
||||
}
|
||||
|
||||
$y += 5;
|
||||
$cityLine = $customer->getZipcode() . " " . $customer->getCity();
|
||||
$this->Text(110, $y, $this->e($cityLine));
|
||||
|
||||
$this->body();
|
||||
}
|
||||
|
||||
private function body(): void
|
||||
{
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
// Les lignes décoratives sont maintenues
|
||||
$this->Line(0, 100, 5, 100);
|
||||
$this->Line(0, 200, 5, 200);
|
||||
}
|
||||
|
||||
private function drawUnpaidInvoicesTable(): void
|
||||
{
|
||||
// Largeurs des colonnes (à ajuster selon vos données)
|
||||
$w = [50, 50, 43, 43];
|
||||
$header = ['Date', 'N° Avis de paiement', 'Montant HT', 'Montant TTC'];
|
||||
$lineHeight = 7;
|
||||
$totalTTC = 0;
|
||||
|
||||
// Entête du tableau
|
||||
$this->SetFillColor(253, 130, 4); // Couleur de fond pour l'entête
|
||||
$this->SetTextColor(0);
|
||||
$this->SetDrawColor(128, 128, 128); // Couleur des bordures
|
||||
$this->SetLineWidth(.3);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
|
||||
// Dessiner l'entête
|
||||
for ($i = 0; $i < count($header); $i++) {
|
||||
$this->Cell($w[$i], $lineHeight, $this->e($header[$i]), 1, 0, 'C', true);
|
||||
}
|
||||
$this->Ln();
|
||||
|
||||
// Données du tableau
|
||||
$this->SetFillColor(231, 166, 100);
|
||||
$this->SetTextColor(0);
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$fill = false; // Alternance de couleurs
|
||||
|
||||
// Supposition de la structure des éléments dans $this->list
|
||||
// Chaque élément de $this->list doit être un tableau/objet
|
||||
// avec les clés 'date', 'numero', 'montant_ttc', 'a_regler_ttc'
|
||||
foreach ($this->list as $row) {
|
||||
|
||||
$totalHt = 0;
|
||||
$totalTTCTemp = 0;
|
||||
foreach ($row->getCustomerAdvertPaymentLines() as $line) {
|
||||
$totalHt = $line->getPriceHt();
|
||||
$totalTTCTemp = $line->getPriceHt()*1.20;
|
||||
}
|
||||
|
||||
$this->Cell($w[0], $lineHeight, $this->e($row->getCreateAt()->format('d/m/Y')), 'LR', 0, 'C', $fill);
|
||||
$this->Cell($w[1], $lineHeight, $this->e($row->getNumAvis()), 'LR', 0, 'C', $fill);
|
||||
$this->Cell($w[2], $lineHeight, $this->e($totalHt . " " ).EURO_UNPAID, 'LR', 0, 'R', $fill);
|
||||
$this->Cell($w[3], $lineHeight, $this->e($totalTTCTemp . " ").EURO_UNPAID, 'LR', 0, 'R', $fill);
|
||||
$this->Ln();
|
||||
|
||||
$totalTTC += $totalTTCTemp;
|
||||
$fill = !$fill; // Alterner la couleur de fond
|
||||
}
|
||||
|
||||
// Ligne de fin du tableau
|
||||
$this->Cell(array_sum($w), 0, '', 'T');
|
||||
$this->Ln();
|
||||
|
||||
// Ligne du total
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$totalText = "TOTAL À RÉGLER : ";
|
||||
$totalFormatted = number_format($totalTTC, 2, ',', ' ');
|
||||
|
||||
// Cellule vide pour aligner le total à droite
|
||||
$this->Cell($w[0] + $w[1] + $w[2], $lineHeight, '', 0, 0, 'R');
|
||||
// Cellule pour le texte du total
|
||||
$this->Cell($w[3] - 20, $lineHeight, $this->e($totalText), 0, 0, 'R');
|
||||
// Cellule pour le montant total
|
||||
$this->Cell(20, $lineHeight, $this->e($totalFormatted)." ".EURO_UNPAID, 0, 1, 'R');
|
||||
}
|
||||
|
||||
public function generate()
|
||||
{
|
||||
$this->AliasNbPages();
|
||||
$this->AddPage();
|
||||
$this->SetFont('Arial', '', 12);
|
||||
$startY = 110;
|
||||
$this->SetY($startY);
|
||||
|
||||
$this->Cell(0, 10, $this->e("Madame, Monsieur,"), 0, 1);
|
||||
$this->MultiCell(0, 5, $this->e("Sauf erreur ou omission de notre part, après vérification de vos comptes en nos livres, nous avons constaté que vous restez à nous devoir:"), 0, 1);
|
||||
|
||||
// --- Insertion du tableau des factures impayées ---
|
||||
$this->Ln(5);
|
||||
$this->drawUnpaidInvoicesTable();
|
||||
$this->Ln(5);
|
||||
// ---------------------------------------------------
|
||||
|
||||
$this->MultiCell(0, 5, $this->e("Nous pensons qu'il s'agit d'un oubli de votre part, et nous vous remercions de bien vouloir nous adresser par tout moyen à votre convenance le règlement correspondant."), 0, 'L');
|
||||
$this->Ln(2);
|
||||
|
||||
$this->MultiCell(0, 5, $this->e("Dans cette attente, nous vous prions de croire en nos salutations distinguées."), 0, 'L');
|
||||
$this->Ln(10);
|
||||
$this->MultiCell(0, 5, $this->e("Le Service Comptabilité."), 0, 'R');
|
||||
}
|
||||
|
||||
public function Footer()
|
||||
{
|
||||
$this->SetY(-40);
|
||||
$this->Ln(10);
|
||||
$this->SetFont('Arial', 'B', 8);
|
||||
$this->SetTextColor(253, 140, 4);
|
||||
$this->SetDrawColor(253, 140, 4);
|
||||
$this->Cell(190, 5, $this->e("Partenaire de vos projects de puis 1997"), 0, 1, 'C');
|
||||
$this->Line(15, $this->GetY(), 195, $this->GetY());
|
||||
$this->SetFont('Arial', '', 8);
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
$this->Ln(2);
|
||||
$this->Cell(190, 4, $this->e("27, rue le Sérurier - 02100 SAINT-QUENTIN - Tél: 03 23 05 62 43"), 0, 1, 'C');
|
||||
$this->Cell(190, 4, $this->e("e-mail : s.com@siteconseil.fr - www.siteconseil.fr"), 0, 1, 'C');
|
||||
// Correction de la constante ici
|
||||
$footerText = $this->e("S.A.R.L aux capital de 71400 ") . EURO_UNPAID . $this->e(" - N°SIRET 418 664 058 00025 - N° TVA FR 05 418 664 058 - CODE APE 6201 Z - R.C. St-Quentin 418 664 058");
|
||||
$this->Cell(190, 4, $footerText, 0, 0, 'C');
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,12 @@
|
||||
+ Crée un échéancier de paiement
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ path('artemis_intranet_customer_orderAdd',{id:customer.id}) }}" class="px-4 py-2 bg-blue-600 text-white font-medium rounded-md shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900">
|
||||
+ Crée un devis / avis de paiement / facture
|
||||
</a>
|
||||
<a href="{{ path('artemis_intranet_customer_orderAdd',{id:customer.id}) }}" class="px-4 py-2 bg-blue-600 text-white font-medium rounded-md shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900">
|
||||
+ Crée un devis / avis de paiement / facture
|
||||
</a>
|
||||
<a target="_blank" href="{{ path('artemis_intranet_customer_view',{id:customer.id,extract:'impaye',current:'order'}) }}" class="pl-2 px-4 py-2 bg-blue-600 text-white font-medium rounded-md shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900">
|
||||
Liste des impayée
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -83,3 +83,5 @@ dns_email: Nom de Domaine + Emails
|
||||
esyWeb_created: Créer en attends de validation
|
||||
esyWeb_validate: Validation - En Attends de déploiement
|
||||
vadvert-ndd : Renouvellement nom de domaine
|
||||
f-created: Facture Crée
|
||||
f-send: Facture envoyée
|
||||
|
||||
Reference in New Issue
Block a user