diff --git a/config/routing/admin_accounting.yml b/config/routing/admin_accounting.yml index 166746cfa..1f16b2835 100644 --- a/config/routing/admin_accounting.yml +++ b/config/routing/admin_accounting.yml @@ -50,6 +50,11 @@ admin_accounting_invoices_send_email: path: /invoices/send defaults: {_controller: AppBundle\Controller\Admin\Accounting\Invoice\SendInvoiceEmailAction} +admin_accounting_invoices_mark_sent: + path: /invoices/mark-sent + methods: [POST] + defaults: {_controller: AppBundle\Controller\Admin\Accounting\Invoice\MarkFactureAsSentAction} + admin_accounting_search: path: /search defaults: {_controller: AppBundle\Controller\Admin\Accounting\SearchAction} diff --git a/db/migrations/20260914090000_facture_envoi.php b/db/migrations/20260914090000_facture_envoi.php new file mode 100644 index 000000000..898afab95 --- /dev/null +++ b/db/migrations/20260914090000_facture_envoi.php @@ -0,0 +1,18 @@ +execute($sql); + } +} diff --git a/sources/AppBundle/Accounting/InvoicingMailer.php b/sources/AppBundle/Accounting/InvoicingMailer.php index 59d0d04d1..6217cde53 100644 --- a/sources/AppBundle/Accounting/InvoicingMailer.php +++ b/sources/AppBundle/Accounting/InvoicingMailer.php @@ -9,6 +9,7 @@ use AppBundle\Email\Mailer\Attachment; use AppBundle\Email\Mailer\Mailer; use AppBundle\Email\Mailer\MailUser; +use AppBundle\Email\Mailer\MailUserFactory; use AppBundle\Email\Mailer\Message; use Symfony\Component\DependencyInjection\Attribute\Autowire; @@ -43,6 +44,7 @@ public function sendInvoice(Invoicing $invoicing): bool new MailUser(MailUser::DEFAULT_SENDER_EMAIL, MailUser::DEFAULT_SENDER_NAME), new MailUser($invoicing->getEmail(), $invoicing->getLastname()), ); + $message->addCc(MailUserFactory::sponsors()); $message->addAttachment(new Attachment( $cheminFacture, 'facture-' . $invoiceNumber . '.pdf', diff --git a/sources/AppBundle/Accounting/Model/Invoicing.php b/sources/AppBundle/Accounting/Model/Invoicing.php index dd5c4fe8d..bd0863442 100644 --- a/sources/AppBundle/Accounting/Model/Invoicing.php +++ b/sources/AppBundle/Accounting/Model/Invoicing.php @@ -38,6 +38,8 @@ class Invoicing implements NotifyPropertyInterface private string $phone = ''; private InvoicingPaymentStatus $paymentStatus = InvoicingPaymentStatus::Waiting; private ?DateTime $paymentDate = null; + private ?DateTime $dateEnvoi = null; + private ?string $envoyePar = null; private ?InvoicingCurrency $currency = null; /** @var InvoicingDetail[] */ #[Assert\Valid] @@ -355,6 +357,32 @@ public function setPrice(?float $price): self return $this; } + public function getDateEnvoi(): ?DateTime + { + return $this->dateEnvoi; + } + + public function setDateEnvoi(?DateTime $dateEnvoi): self + { + $this->propertyChanged('dateEnvoi', $this->dateEnvoi, $dateEnvoi); + $this->dateEnvoi = $dateEnvoi; + + return $this; + } + + public function getEnvoyePar(): ?string + { + return $this->envoyePar; + } + + public function setEnvoyePar(?string $envoyePar): self + { + $this->propertyChanged('envoyePar', $this->envoyePar, $envoyePar); + $this->envoyePar = $envoyePar; + + return $this; + } + public function getPaymentUrlRef(): string { if (empty($this->getInvoiceNumber())) { diff --git a/sources/AppBundle/Accounting/Model/Repository/InvoicingRepository.php b/sources/AppBundle/Accounting/Model/Repository/InvoicingRepository.php index 8ffdcafc6..5da57a67e 100644 --- a/sources/AppBundle/Accounting/Model/Repository/InvoicingRepository.php +++ b/sources/AppBundle/Accounting/Model/Repository/InvoicingRepository.php @@ -51,7 +51,7 @@ public function getQuotationsByPeriodId(?int $periodId = null, string $sort = 'd ->where('acf.date_devis <= (select date_fin from compta_periode where id = :periodId)') ->bindValues(['periodId' => $periodId]); } - $builder->groupBy(['acf.id', 'date_devis', 'numero_devis', 'date_facture', 'numero_facture', 'societe', 'adresse', 'code_postal', 'ville', 'id_pays', 'email', 'observation', 'ref_clt1', 'ref_clt2', 'ref_clt3', 'nom', 'prenom', 'tel', 'etat_paiement', 'date_paiement', 'devise_facture']) + $builder->groupBy(['acf.id', 'date_devis', 'numero_devis', 'date_facture', 'numero_facture', 'societe', 'adresse', 'code_postal', 'ville', 'id_pays', 'email', 'observation', 'ref_clt1', 'ref_clt2', 'ref_clt3', 'nom', 'prenom', 'tel', 'etat_paiement', 'date_paiement', 'date_envoi', 'envoye_par', 'devise_facture']) ->orderBy(["$filter $direction"]); $hydrator = new HydratorSingleObject(); @@ -82,7 +82,7 @@ public function getInvoicesByPeriodId(?int $periodId = null, string $sort = 'dat ->where('acf.date_facture <= (select date_fin from compta_periode where id = :periodId)') ->bindValues(['periodId' => $periodId]); } - $builder->groupBy(['acf.id', 'date_devis', 'numero_devis', 'date_facture', 'numero_facture', 'societe', 'adresse', 'code_postal', 'ville', 'id_pays', 'email', 'observation', 'ref_clt1', 'ref_clt2', 'ref_clt3', 'nom', 'prenom', 'tel', 'etat_paiement', 'date_paiement', 'devise_facture']) + $builder->groupBy(['acf.id', 'date_devis', 'numero_devis', 'date_facture', 'numero_facture', 'societe', 'adresse', 'code_postal', 'ville', 'id_pays', 'email', 'observation', 'ref_clt1', 'ref_clt2', 'ref_clt3', 'nom', 'prenom', 'tel', 'etat_paiement', 'date_paiement', 'date_envoi', 'envoye_par', 'devise_facture']) ->orderBy(["$filter $direction"]); $hydrator = new HydratorSingleObject(); @@ -315,6 +315,21 @@ public static function initMetadata(SerializerFactoryInterface $serializerFactor 'unserialize' => ['enum' => InvoicingCurrency::class], ], ]) + ->addField([ + 'columnName' => 'date_envoi', + 'fieldName' => 'dateEnvoi', + 'type' => 'datetime', + 'serializer' => DateTime::class, + 'serializer_options' => [ + 'serialize' => ['format' => 'Y-m-d H:i:s'], + 'unserialize' => ['format' => 'Y-m-d H:i:s', 'unSerializeUseFormat' => true], + ], + ]) + ->addField([ + 'columnName' => 'envoye_par', + 'fieldName' => 'envoyePar', + 'type' => 'string', + ]) ; return $metadata; diff --git a/sources/AppBundle/Controller/Admin/Accounting/Invoice/MarkFactureAsSentAction.php b/sources/AppBundle/Controller/Admin/Accounting/Invoice/MarkFactureAsSentAction.php new file mode 100644 index 000000000..c51489f07 --- /dev/null +++ b/sources/AppBundle/Controller/Admin/Accounting/Invoice/MarkFactureAsSentAction.php @@ -0,0 +1,51 @@ +request->get('_token')); + if (!$this->csrfTokenManager->isTokenValid($token)) { + throw $this->createAccessDeniedException('Token CSRF invalide'); + } + + $invoiceRef = $request->request->get('ref'); + $invoice = $this->invoicingRepository->getOneByInvoiceNumber((string) $invoiceRef); + if ($invoice === null) { + throw new NotFoundHttpException("Cette facture n'existe pas"); + } + + $invoice->setDateEnvoi(new \DateTime()); + $user = $this->security->getUser(); + if ($user instanceof User) { + $invoice->setEnvoyePar($user->getUsername()); + } + $this->invoicingRepository->save($invoice); + $this->audit->log('Facture n°' . $invoiceRef . ' marquée comme envoyée manuellement'); + $this->addFlash('notice', 'La facture a été marquée comme envoyée'); + + return $this->redirectToRoute('admin_accounting_invoices_list'); + } +} diff --git a/sources/AppBundle/Controller/Admin/Accounting/Invoice/SendInvoiceEmailAction.php b/sources/AppBundle/Controller/Admin/Accounting/Invoice/SendInvoiceEmailAction.php index 1690914b6..6968291fd 100644 --- a/sources/AppBundle/Controller/Admin/Accounting/Invoice/SendInvoiceEmailAction.php +++ b/sources/AppBundle/Controller/Admin/Accounting/Invoice/SendInvoiceEmailAction.php @@ -6,10 +6,12 @@ use AppBundle\Accounting\InvoicingMailer; use AppBundle\Accounting\Model\Repository\InvoicingRepository; +use AppBundle\Association\Model\User; use AppBundle\AuditLog\Audit; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class SendInvoiceEmailAction extends AbstractController @@ -18,6 +20,7 @@ public function __construct( private readonly InvoicingMailer $invoicingMailer, private readonly InvoicingRepository $invoicingRepository, private readonly Audit $audit, + private readonly Security $security, ) {} public function __invoke(Request $request): Response @@ -29,6 +32,12 @@ public function __invoke(Request $request): Response } if ($this->invoicingMailer->sendInvoice($invoice)) { + $invoice->setDateEnvoi(new \DateTime()); + $user = $this->security->getUser(); + if ($user instanceof User) { + $invoice->setEnvoyePar($user->getUsername()); + } + $this->invoicingRepository->save($invoice); $this->audit->log('Envoi par email de la facture n°' . $invoiceRef); $this->addFlash('notice', 'La facture a été envoyée'); } else { diff --git a/templates/admin/accounting/invoice/list.html.twig b/templates/admin/accounting/invoice/list.html.twig index 2c831cedd..836830296 100644 --- a/templates/admin/accounting/invoice/list.html.twig +++ b/templates/admin/accounting/invoice/list.html.twig @@ -30,6 +30,7 @@