feat(SepaController): Ajoute la fonctionnalité de mandat SEPA pour les clients.

This commit is contained in:
Serreau Jovann
2025-10-12 17:27:11 +02:00
parent 3b91fbd807
commit e2ba005d03
8 changed files with 270 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
<?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 Version20251012151847 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('CREATE TABLE customer_sepa (id SERIAL NOT NULL, customer_id INT DEFAULT NULL, iban VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE INDEX IDX_57C955EA9395C3F3 ON customer_sepa (customer_id)');
$this->addSql('ALTER TABLE customer_sepa ADD CONSTRAINT FK_57C955EA9395C3F3 FOREIGN KEY (customer_id) REFERENCES customer (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
$this->addSql('ALTER TABLE customer_sepa DROP CONSTRAINT FK_57C955EA9395C3F3');
$this->addSql('DROP TABLE customer_sepa');
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Controller\Artemis\Customer;
use App\Entity\Account;
use App\Entity\Customer;
use App\Entity\CustomerContact;
use App\Entity\CustomerSepa;
use App\Repository\CustomerContactRepository;
use App\Service\Stancer\Client;
use Doctrine\ORM\EntityManagerInterface;
use LasseRafn\InitialAvatarGenerator\InitialAvatar;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class SepaController extends AbstractController
{
#[Route('/artemis/customer/sepa', name: 'artemis_customer_sepa', methods: ['GET', 'POST'])]
public function artemisCustomerSepa(Request $request,EntityManagerInterface $entityManager,CustomerContactRepository $customerContactRepository,Client $client): Response
{
/** @var Account $customer */
$customer = $this->getUser();
$contact = $customerContactRepository->findOneBy(['email'=>$customer->getEmail()]);
if($request->isMethod('POST')) {
$data = $request->request->all();
$mandate = $data['mandate'];
$customer = $contact->getCustomer();
$id = $client->sepa($mandate);
$sepa = new CustomerSepa();
$sepa->setCustomer($customer);
$sepa->setIban($id->id);
$entityManager->persist($sepa);
$entityManager->flush();
}
return $this->render('artemis/customer/sepa.twig', [
'sepas' => $contact->getCustomer()->getCustomerSepas(),
]);
}
}

View File

@@ -117,6 +117,12 @@ class Customer
#[ORM\ManyToOne(inversedBy: 'customers')]
private ?Account $account = null;
/**
* @var Collection<int, CustomerSepa>
*/
#[ORM\OneToMany(targetEntity: CustomerSepa::class, mappedBy: 'customer')]
private Collection $customerSepas;
public function __clone(): void
{
@@ -140,6 +146,7 @@ class Customer
$this->customerOrders = new ArrayCollection();
$this->websites = new ArrayCollection();
$this->customerSplits = new ArrayCollection();
$this->customerSepas = new ArrayCollection();
}
public function getId(): ?int
@@ -596,4 +603,34 @@ class Customer
return $this;
}
/**
* @return Collection<int, CustomerSepa>
*/
public function getCustomerSepas(): Collection
{
return $this->customerSepas;
}
public function addCustomerSepa(CustomerSepa $customerSepa): static
{
if (!$this->customerSepas->contains($customerSepa)) {
$this->customerSepas->add($customerSepa);
$customerSepa->setCustomer($this);
}
return $this;
}
public function removeCustomerSepa(CustomerSepa $customerSepa): static
{
if ($this->customerSepas->removeElement($customerSepa)) {
// set the owning side to null (unless already changed)
if ($customerSepa->getCustomer() === $this) {
$customerSepa->setCustomer(null);
}
}
return $this;
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace App\Entity;
use App\Repository\CustomerSepaRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: CustomerSepaRepository::class)]
class CustomerSepa
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $iban = null;
#[ORM\ManyToOne(inversedBy: 'customerSepas')]
private ?Customer $customer = null;
public function getId(): ?int
{
return $this->id;
}
public function getIban(): ?string
{
return $this->iban;
}
public function setIban(string $iban): static
{
$this->iban = $iban;
return $this;
}
public function getCustomer(): ?Customer
{
return $this->customer;
}
public function setCustomer(?Customer $customer): static
{
$this->customer = $customer;
return $this;
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Repository;
use App\Entity\CustomerSepa;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<CustomerSepa>
*/
class CustomerSepaRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, CustomerSepa::class);
}
// /**
// * @return CustomerSepa[] Returns an array of CustomerSepa objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('c')
// ->andWhere('c.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('c.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?CustomerSepa
// {
// return $this->createQueryBuilder('c')
// ->andWhere('c.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

View File

@@ -6,6 +6,7 @@ use App\Entity\Customer as AppCustomer;
use Doctrine\ORM\EntityManagerInterface;
use Stancer\Config;
use Stancer\Customer;
use Stancer\Sepa;
class Client
{
@@ -49,4 +50,12 @@ class Client
return $stancerId;
}
public function sepa($param)
{
$sepa = new Sepa();
$sepa->setIban($param['iban']);
$sepa->setName($param['titulaire_compte']);
return$sepa->send();
}
}

View File

@@ -204,6 +204,12 @@
<span class="ml-3">Facilité de paiement</span>
</a>
</li>
<li class="px-4 py-2">
<a href="{{ path('artemis_customer_sepa') }}" class="flex items-center p-2 text-base font-normal text-gray-900 dark:text-white {% if app.request.get('_route') == "artemis_cc_split"%}bg-gray-200 dark:bg-gray-700{% endif%} rounded-lg">
<svg class="w-6 h-6 text-gray-500 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M2 10a8 8 0 018-8v8h8a8 8 0 11-16 0z"></path><path d="M12 2.252A8.014 8.014 0 0117.748 12H12V2.252z"></path></svg>
<span class="ml-3">Mendat de prévélevement</span>
</a>
</li>
</ul>
</nav>
</aside>

View File

@@ -0,0 +1,43 @@
{% extends 'artemis/base.twig' %}
{% block title %}Espace Client - Mandat de prélèvement{% endblock %}
{% block content %}
{% for sepa in sepas %}
{{ dump(sepa) }}
{% endfor %}
<h2 class="text-3xl font-semibold text-gray-800 dark:text-gray-200">Créer un Mandat de Prélèvement</h2>
<div class="mt-5 bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 space-y-6">
<p class="text-gray-700 dark:text-gray-300">
Veuillez renseigner vos informations bancaires pour autoriser le prélèvement automatique de vos factures.
</p>
<form method="POST" class="space-y-6">
<h3 class="text-xl font-semibold text-gray-900 dark:text-gray-100">Vos Coordonnées Bancaires</h3>
<div class="space-y-2">
<label for="iban" class="block text-sm font-medium text-gray-700 dark:text-gray-300">IBAN (International Bank Account Number) :</label>
<input type="text" id="iban" name="mandate[iban]" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100" required
placeholder="FRXX XXXX XXXX XXXX XXXX XXXX XXX"
pattern="[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}"
title="Veuillez entrer un IBAN valide.">
</div>
<div class="space-y-2">
<label for="titulaire_compte" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Nom et Prénom du titulaire du compte :</label>
<input type="text" id="titulaire_compte" name="mandate[titulaire_compte]" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
</div>
<div class="pt-4">
<button type="submit" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-lg font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Valider le Mandat de Prélèvement
</button>
</div>
</form>
</div>
{% endblock %}