feat(CustomerController): Ajoute la création de clients avec formulaire et contact principal.

Renomme le controller Inranet en Intranet. Ajoute la gestion de la création de client avec formulaire et contact principal. Ajoute l'appel API auto customer.
This commit is contained in:
Serreau Jovann
2025-07-23 11:50:33 +02:00
parent 1edce18a6b
commit b341b06656
15 changed files with 759 additions and 56 deletions

View File

@@ -2,11 +2,13 @@ import './admin.scss'
import * as Turbo from "@hotwired/turbo"
import {AutoSubmit} from './class/AutoSubmit'
import {ServerCard} from './class/ServerCard'
import {AutoCustomer} from './class/AutoCustomer'
function script() {
customElements.define('auto-submit',AutoSubmit,{extends:'form'})
customElements.define('server-card',ServerCard,{extends:'div'})
customElements.define('auto-customer',AutoCustomer,{extends:'button'})
}

View File

@@ -13,3 +13,12 @@ label,span,input,{
.bg-STOPPED{
color:var(--color-red-700);
}
input {
background: oklch(21% 0.034 264.665);
color: white;
&::placeholder {
opacity: 0.5;
}
}

View File

@@ -0,0 +1,203 @@
export class AutoCustomer extends HTMLButtonElement {
connectedCallback() {
const modalHTML = `
<style>
.modal {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.85);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.modal.hidden {
display: none;
}
.modal-content {
background: #1f2937; /* gray-800 */
border-radius: 0.5rem;
padding: 1.5rem;
max-width: 28rem;
width: 100%;
position: relative;
box-shadow: 0 10px 15px -3px rgba(0,0,0,0.5),
0 4px 6px -2px rgba(0,0,0,0.4);
color: #f9fafb; /* gray-50 */
font-family: system-ui, sans-serif;
}
.close-btn {
position: absolute;
top: 0.5rem;
right: 0.75rem;
background: transparent;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: #9ca3af; /* gray-400 */
}
.close-btn:hover {
color: #f9fafb;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
color: #d1d5db; /* gray-300 */
}
input[type="text"] {
width: 100%;
padding: 0.5rem 0.75rem;
border: 1px solid #374151; /* gray-700 */
border-radius: 0.375rem;
margin-bottom: 1rem;
font-size: 1rem;
background-color: #111827; /* gray-900 */
color: #f9fafb; /* gray-50 */
box-sizing: border-box;
}
input[type="text"]::placeholder {
color: #6b7280; /* gray-500 */
}
button.submit-btn {
background-color: #3b82f6; /* blue-500 */
color: white;
padding: 0.5rem 1rem;
font-weight: 600;
border: none;
border-radius: 0.375rem;
cursor: pointer;
transition: background-color 0.3s ease;
}
button.submit-btn:hover {
background-color: #2563eb; /* blue-600 */
}
.result {
margin-top: 1rem;
background-color: #374151; /* gray-700 */
padding: 1rem;
border-radius: 0.375rem;
white-space: pre-wrap;
font-family: monospace, monospace;
max-height: 200px;
overflow-y: auto;
}
.error {
color: #f87171; /* red-400 */
margin-top: 1rem;
}
</style>
<div class="modal hidden" id="siret-modal">
<div class="modal-content" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<button class="close-btn" aria-label="Fermer la fenêtre modale">&times;</button>
<h2 id="modal-title" style="font-size:1.25rem; font-weight:700; margin-bottom:1rem;">Recherche SIRET</h2>
<form id="siret-form">
<label for="siret-input">Numéro SIRET :</label>
<input type="text" id="siret-input" name="siret" maxlength="14" pattern="\\d{14}" placeholder="Ex: 12345678901234" required />
<button type="submit" class="submit-btn">Rechercher</button>
</form>
<div class="result" id="result-container" aria-live="polite"></div>
</div>
</div>
`;
this.insertAdjacentHTML('afterend', modalHTML);
const modal = document.getElementById('siret-modal');
const closeBtn = modal.querySelector('.close-btn');
const form = modal.querySelector('#siret-form');
const input = modal.querySelector('#siret-input');
const resultContainer = modal.querySelector('#result-container');
this.addEventListener('click', e => {
e.preventDefault();
resultContainer.textContent = ''; // reset résultat
modal.classList.remove('hidden');
input.focus();
});
closeBtn.addEventListener('click', () => {
modal.classList.add('hidden');
});
modal.addEventListener('click', e => {
if (e.target === modal) {
modal.classList.add('hidden');
}
});
form.addEventListener('submit', async e => {
e.preventDefault();
const siret = input.value.trim();
resultContainer.textContent = '';
if (!/^\d{14}$/.test(siret)) {
resultContainer.innerHTML = `<div class="error">Veuillez entrer un numéro SIRET valide à 14 chiffres.</div>`;
return;
}
resultContainer.textContent = 'Recherche en cours...';
try {
const response = await fetch(`/api-interne/intranet/customer/auto/${siret}`, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Erreur API: ${response.status}`);
}
const data = await response.json();
if(data.error != undefined) {
resultContainer.innerHTML = `<div class="error">Erreur lors de la recherche : Aucune information non trouvée</div>`;
input.value = "";
input.setAttribute('input',"")
} else {
if(data.etat) {
let customer_raisonSocial = document.body.querySelector('#customer_raisonSocial');
customer_raisonSocial.setAttribute('value',data.name);
customer_raisonSocial.value = data.name;
let customer_siret = document.body.querySelector('#customer_siret');
customer_siret.setAttribute('value',data.siret);
customer_siret.value = data.siret;
let customer_ape = document.body.querySelector('#customer_ape');
customer_ape.setAttribute('value',data.ape);
customer_ape.value = data.ape;
let customer_rna = document.body.querySelector('#customer_rna');
customer_rna.setAttribute('value',data.rna);
customer_rna.value = data.rna;
let customer_address = document.body.querySelector('#customer_address');
customer_address.setAttribute('value',data.num);
customer_address.value = data.num;
let customer_zipcode = document.body.querySelector('#customer_zipcode');
customer_zipcode.setAttribute('value',data.zipcode);
customer_zipcode.value = data.zipcode;
let customer_city = document.body.querySelector('#customer_city');
customer_city.setAttribute('value',data.comune);
customer_city.value = data.comune;
modal.classList.add('hidden');
} else {
resultContainer.innerHTML = `<div class="error">Erreur lors de la recherche : Entreprise FERMER</div>`;
input.value = "";
input.setAttribute('input',"")
}
}
} catch (error) {
resultContainer.innerHTML = `<div class="error">Erreur lors de la recherche : ${error.message}</div>`;
}
});
}
}

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 Version20250723094205 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_contact (id SERIAL NOT NULL, customer_id INT DEFAULT NULL, is_main BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, surname VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, phone VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE INDEX IDX_50BF42869395C3F3 ON customer_contact (customer_id)');
$this->addSql('ALTER TABLE customer_contact ADD CONSTRAINT FK_50BF42869395C3F3 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_contact DROP CONSTRAINT FK_50BF42869395C3F3');
$this->addSql('DROP TABLE customer_contact');
}
}

View File

@@ -22,21 +22,27 @@ class CustomerController extends AbstractController
}
$siret = str_replace(" ","",$siret);
$sirene = new Sirene($_ENV['INSEE_KEYAPI']);
$content = $sirene->siret($siret)->getBody();
$company =[
'etat' => $content['uniteLegale']['etatAdministratifUniteLegale'] = "A"?true:false,
'name' => $content['uniteLegale']['denominationUniteLegale'],
'siret' => $siret,
'rna' => $content['uniteLegale']['identifiantAssociationUniteLegale'],
'ape' => $content['uniteLegale']['activitePrincipaleUniteLegale'],
'num' => $content['adresseEtablissement']['numeroVoieEtablissement']." ".
$content['adresseEtablissement']['typeVoieEtablissement']." ".
$content['adresseEtablissement']['libelleVoieEtablissement'],
'zipcode' => $content['adresseEtablissement']['codePostalEtablissement'],
'comune' => $content['adresseEtablissement']['libelleCommuneEtablissement'],
try {
$sirene = new Sirene($_ENV['INSEE_KEYAPI']);
$content = $sirene->siret($siret)->getBody();
$company = [
'etat' => $content['uniteLegale']['etatAdministratifUniteLegale'] = "A" ? true : false,
'name' => $content['uniteLegale']['denominationUniteLegale'],
'siret' => $siret,
'rna' => $content['uniteLegale']['identifiantAssociationUniteLegale'],
'ape' => $content['uniteLegale']['activitePrincipaleUniteLegale'],
'num' => $content['adresseEtablissement']['numeroVoieEtablissement'] . " " .
$content['adresseEtablissement']['typeVoieEtablissement'] . " " .
$content['adresseEtablissement']['libelleVoieEtablissement'],
'zipcode' => $content['adresseEtablissement']['codePostalEtablissement'],
'comune' => $content['adresseEtablissement']['libelleCommuneEtablissement'],
];
return $this->json($company);
];
return $this->json($company);
}catch (\Exception $e) {
return $this->json([
'error' => true
]);
}
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace App\Controller\Artemis\Inranet;
use App\Repository\CustomerRepository;
use Knp\Bundle\PaginatorBundle\KnpPaginatorBundle;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class CustomerController extends AbstractController
{
#[Route(path: '/artemis/intranet/customer',name: 'artemis_intranet_customer',methods: ['GET', 'POST'])]
public function customers(CustomerRepository $customerRepository,Request $request,PaginatorInterface $paginator): Response
{
return $this->render('artemis/intranet/customer.twig',[
'customers' => $paginator->paginate($customerRepository->searchCustomer($request),$request->get('page',1),20),
]);
}
#[Route(path: '/artemis/intranet/customer/add',name: 'artemis_intranet_customer_add',methods: ['GET', 'POST'])]
public function customerAdd(Request $request): Response
{
return $this->render('artemis/intranet/customer/add.twig',[
]);
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace App\Controller\Artemis\Intranet;
use App\Entity\Customer;
use App\Entity\CustomerContact;
use App\Form\Artemis\Intranet\CustomerType;
use App\Repository\CustomerRepository;
use App\Service\Logger\LoggerService;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Bundle\PaginatorBundle\KnpPaginatorBundle;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Uid\Uuid;
class CustomerController extends AbstractController
{
#[Route(path: '/artemis/intranet/customer',name: 'artemis_intranet_customer',methods: ['GET', 'POST'])]
public function customers(CustomerRepository $customerRepository,Request $request,PaginatorInterface $paginator): Response
{
return $this->render('artemis/intranet/customer.twig',[
'customers' => $paginator->paginate($customerRepository->searchCustomer($request),$request->get('page',1),20),
]);
}
#[Route(path: '/artemis/intranet/customer/add',name: 'artemis_intranet_customer_add',methods: ['GET', 'POST'])]
public function customerAdd(LoggerService $loggerService,EntityManagerInterface $entityManager,Request $request): Response
{
$customer = new Customer();
$customer->setCountry('FR');
$form = $this->createForm(CustomerType::class,$customer);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$customer->setUuid(Uuid::v4());
$customer->setCreateAt(new \DateTimeImmutable());
$customer->setUpdateAt(new \DateTimeImmutable());
$entityManager->persist($customer);
$customerContact = new CustomerContact();
$customerContact->setIsMain(true);
$customerContact->setEmail($_POST['customer']['contactEmail']);
$customerContact->setName($_POST['customer']['contactName']);
$customerContact->setSurname($_POST['customer']['contactFirstname']);
$customerContact->setPhone($_POST['customer']['contactPhone']);
$customerContact->setCustomer($customer);
$entityManager->persist($customerContact);
$entityManager->flush();
$loggerService->log('CREATE',"Création d'un client - ".$customerContact->getName(),$this->getUser());
$this->addFlash("success","Création du compte client effectuée");
return $this->redirectToRoute('artemis_intranet_customer');
}
return $this->render('artemis/intranet/customer/add.twig',[
'form' => $form->createView()
]);
}
}

View File

@@ -3,6 +3,8 @@
namespace App\Entity;
use App\Repository\CustomerRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;
@@ -62,6 +64,17 @@ class Customer
#[ORM\Column(type: 'uuid')]
private ?Uuid $uuid = null;
/**
* @var Collection<int, CustomerContact>
*/
#[ORM\OneToMany(targetEntity: CustomerContact::class, mappedBy: 'customer')]
private Collection $customerContacts;
public function __construct()
{
$this->customerContacts = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
@@ -258,4 +271,41 @@ class Customer
return $this;
}
/**
* @return Collection<int, CustomerContact>
*/
public function getCustomerContacts(): Collection
{
return $this->customerContacts;
}
public function addCustomerContact(CustomerContact $customerContact): static
{
if (!$this->customerContacts->contains($customerContact)) {
$this->customerContacts->add($customerContact);
$customerContact->setCustomer($this);
}
return $this;
}
public function removeCustomerContact(CustomerContact $customerContact): static
{
if ($this->customerContacts->removeElement($customerContact)) {
// set the owning side to null (unless already changed)
if ($customerContact->getCustomer() === $this) {
$customerContact->setCustomer(null);
}
}
return $this;
}
public function mainContact()
{
return $this->customerContacts->filter(function (CustomerContact $customerContact) {
return $customerContact->isMain() == true;
})->first();
}
}

View File

@@ -0,0 +1,111 @@
<?php
namespace App\Entity;
use App\Repository\CustomerContactRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: CustomerContactRepository::class)]
class CustomerContact
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne(inversedBy: 'customerContacts')]
private ?Customer $customer = null;
#[ORM\Column]
private ?bool $isMain = null;
#[ORM\Column(length: 255)]
private ?string $name = null;
#[ORM\Column(length: 255)]
private ?string $surname = null;
#[ORM\Column(length: 255)]
private ?string $email = null;
#[ORM\Column(length: 255)]
private ?string $phone = null;
public function getId(): ?int
{
return $this->id;
}
public function getCustomer(): ?Customer
{
return $this->customer;
}
public function setCustomer(?Customer $customer): static
{
$this->customer = $customer;
return $this;
}
public function isMain(): ?bool
{
return $this->isMain;
}
public function setIsMain(bool $isMain): static
{
$this->isMain = $isMain;
return $this;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): static
{
$this->name = $name;
return $this;
}
public function getSurname(): ?string
{
return $this->surname;
}
public function setSurname(string $surname): static
{
$this->surname = $surname;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): static
{
$this->email = $email;
return $this;
}
public function getPhone(): ?string
{
return $this->phone;
}
public function setPhone(string $phone): static
{
$this->phone = $phone;
return $this;
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace App\Form\Artemis\Intranet;
use App\Entity\Customer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CountryType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TelType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Country;
class CustomerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('raisonSocial',TextType::class,[
'required' => true,
'label' => 'Raison Sociale'
])
->add('type',ChoiceType::class,[
'label' => 'Type d\'entreprise',
'required' => true,
'choices' =>[
'Entreprise' => 'company',
'Association' => 'association',
'Entreprise Individuelle' => 'ei',
'Particulier'=> 'particulier',
'Mairie' => 'Mairie',
'Autre' => 'autre'
]
])
->add('siret',TextType::class,[
'required' => true,
'label' => 'Siret'
])
->add('ape',TextType::class,[
'required' => false,
'label' => 'Code Naf / APE'
])
->add('rcs',TextType::class,[
'required' => false,
'label' => 'Numéro RCS'
])
->add('rna',TextType::class,[
'required' => false,
'label' => 'Numéro RNA (si association)'
])
->add('address',TextType::class,[
'required' => true,
'label' => 'Adresse'
])
->add('address2',TextType::class,[
'required' => false,
'label' => 'Adresse 2'
])
->add('address3',TextType::class,[
'required' => false,
'label' => 'Adresse 3'
])
->add('zipcode',TextType::class,[
'required' => true,
'label' => 'Code Postale'
])
->add('city',TextType::class,[
'required' => true,
'label' => 'Ville'
])
->add('country',CountryType::class,[
'required' => true,
'label' => 'Pays'
])
->add('codeComptable',TextType::class,[
'required' => true,
'label' => 'Code Comptable'
])
//no mapping port
->add('contactName',TextType::class,[
'required' => true,
'label' => 'Nom du contact',
'mapped' => false,
])
->add('contactFirstname',TextType::class,[
'required' => true,
'label' => 'Prénom du contact',
'mapped' => false,
])
->add('contactEmail',EmailType::class,[
'required' => true,
'label' => 'Email du contact (utilisé pour la facturation)',
'mapped' => false,
])
->add('contactPhone',TelType::class,[
'required' => true,
'label' => 'Téléphone du contact',
'mapped' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('data_class',Customer::class);
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Repository;
use App\Entity\CustomerContact;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<CustomerContact>
*/
class CustomerContactRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, CustomerContact::class);
}
// /**
// * @return CustomerContact[] Returns an array of CustomerContact 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): ?CustomerContact
// {
// return $this->createQueryBuilder('c')
// ->andWhere('c.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Service\Customer;
use App\Entity\Customer;
class CreateCustomerEvent
{
private Customer $customer;
public function __construct(Customer $customer)
{
$this->customer = $customer;
}
/**
* @return Customer
*/
public function getCustomer(): Customer
{
return $this->customer;
}
}

View File

@@ -61,16 +61,16 @@
{% for customer in customers %}
<tr class="hover:bg-gray-700 transition">
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-semibold">Entreprise A</div>
<div class="text-xs text-gray-400">+33 1 23 45 67 89</div>
<div class="text-xs text-gray-400">contact@entreprisea.fr</div>
<div class="text-xs text-gray-400">Jean Dupont</div>
<div class="text-sm font-semibold">{{ customer.type|trans }} - {{ customer.raisonSocial }}</div>
<div class="text-xs text-gray-400"><a href="tel:{{ customer.mainContact.phone }}">{{ customer.mainContact.phone }}</a> </div>
<div class="text-xs text-gray-400"><a href="mailto:{{ customer.mainContact.email }}">{{ customer.mainContact.email }}</a> </div>
<div class="text-xs text-gray-400">{{ customer.mainContact.name }} {{ customer.mainContact.surname }}</div>
</td>
<td class="px-6 py-4 text-center text-sm">3</td>
<td class="px-6 py-4 text-center text-sm">5</td>
<td class="px-6 py-4 text-center text-sm">10</td>
<td class="px-6 py-4 text-center text-sm">2</td>
<td class="px-6 py-4 text-center text-sm text-red-500 font-bold">Oui</td>
<td class="px-6 py-4 text-center text-sm">0</td>
<td class="px-6 py-4 text-center text-sm">0</td>
<td class="px-6 py-4 text-center text-sm">0</td>
<td class="px-6 py-4 text-center text-sm">0</td>
<td class="px-6 py-4 text-center text-sm text-green-500 font-bold">Non</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-center">
<button class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded mr-2">Voir</button>
<button class="bg-yellow-500 hover:bg-yellow-600 text-white px-3 py-1 rounded mr-2">Modifier</button>

View File

@@ -4,11 +4,84 @@
<div class="flex justify-between items-center mb-6">
<h2 class="text-3xl font-semibold text-gray-800 dark:text-gray-200">Crée un client</h2>
<div>
<button 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">
<button is="auto-customer" 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">
Récupération automatique des informations du client via Siret
</button>
</div>
</div> <div class="mt-5 bg-gray-800 rounded-lg shadow-lg p-6 space-y-4">
</div>
<div class="mt-5 bg-gray-800 rounded-lg shadow-lg p-6 space-y-4">
{{ form_start(form) }}
<div class="flex space-x-4">
<div class="flex-1">
{{ form_row(form.raisonSocial) }}
</div>
<div class="flex-1">
{{ form_row(form.codeComptable) }}
</div>
<div class="flex-1">
{{ form_row(form.type) }}
</div>
</div>
<div class="flex space-x-4 mb-4">
<div class="flex-1">
{{ form_row(form.siret) }}
</div>
<div class="flex-1">
{{ form_row(form.ape) }}
</div>
<div class="flex-1">
{{ form_row(form.rcs) }}
</div>
<div class="flex-1">
{{ form_row(form.rna) }}
</div>
</div>
<div class="flex space-x-4 mb-4">
<div class="flex-1">
{{ form_row(form.address) }}
</div>
<div class="flex-1">
{{ form_row(form.address2) }}
</div>
<div class="flex-1">
{{ form_row(form.address3) }}
</div>
</div>
{# city + country sur la même ligne #}
<div class="flex space-x-4 mb-4">
<div class="flex-1">
{{ form_row(form.zipcode) }}
</div>
<div class="flex-1">
{{ form_row(form.city) }}
</div>
<div class="flex-1">
{{ form_row(form.country) }}
</div>
</div>
<div class="flex space-x-4">
<div class="flex-1">
{{ form_row(form.contactName) }}
</div>
<div class="flex-1">
{{ form_row(form.contactFirstname) }}
</div>
<div class="flex-1">
{{ form_row(form.contactEmail) }}
</div>
<div class="flex-1">
{{ form_row(form.contactPhone) }}
</div>
</div>
<button type="submit" class="w-full bg-purple-600 hover:bg-purple-700 text-white font-semibold px-4 py-2 rounded">Enregistrer</button>
{{ form_end(form) }}
</div>
{% endblock %}

View File

@@ -2,3 +2,9 @@ ROLE_ROOT: Super Administrateur
ROLE_ADMIN: Administrateur
ROLE_ARTEMIS: Artemiss
ROLE_USER: Utilisateur
company: Entreprise
association: Association
ei: Entreprise Individuelle
particulier: Particulier
Mairie: Mairie
autre: Autre