diff --git a/config/packages/vich_uploader.yaml b/config/packages/vich_uploader.yaml index 9a6938c..526cb9d 100644 --- a/config/packages/vich_uploader.yaml +++ b/config/packages/vich_uploader.yaml @@ -5,3 +5,19 @@ vich_uploader: uri_prefix: /images/image_product upload_destination: '%kernel.project_dir%/public/images/image_product' namer: Vich\UploaderBundle\Naming\SmartUniqueNamer + devis_file: + uri_prefix: /images/devis_file + upload_destination: '%kernel.project_dir%/public/images/devis_file' + namer: Vich\UploaderBundle\Naming\SmartUniqueNamer + devis_docuseal: + uri_prefix: /images/devis_docusign + upload_destination: '%kernel.project_dir%/public/images/devis_docusign' + namer: Vich\UploaderBundle\Naming\SmartUniqueNamer + devis_signed: + uri_prefix: /images/devis_signed + upload_destination: '%kernel.project_dir%/public/images/devis_signed' + namer: Vich\UploaderBundle\Naming\SmartUniqueNamer + devis_audit: + uri_prefix: /images/devis_audit + upload_destination: '%kernel.project_dir%/public/images/devis_audit' + namer: Vich\UploaderBundle\Naming\SmartUniqueNamer diff --git a/migrations/Version20260116140149.php b/migrations/Version20260116140149.php new file mode 100644 index 0000000..8df445b --- /dev/null +++ b/migrations/Version20260116140149.php @@ -0,0 +1,37 @@ +addSql('CREATE TABLE devis (id SERIAL NOT NULL, customer_id INT DEFAULT NULL, num VARCHAR(255) NOT NULL, create_a TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, update_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, state VARCHAR(255) NOT NULL, devis_file_name VARCHAR(255) DEFAULT NULL, devis_file_size INT DEFAULT NULL, devis_docu_seal_file_name VARCHAR(255) DEFAULT NULL, devis_docu_seal_file_size INT DEFAULT NULL, devis_signed_file_name VARCHAR(255) DEFAULT NULL, devis_signed_file_size INT DEFAULT NULL, devis_audit_file_name VARCHAR(255) DEFAULT NULL, devis_audit_file_size INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_8B27C52B9395C3F3 ON devis (customer_id)'); + $this->addSql('COMMENT ON COLUMN devis.create_a IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN devis.update_at IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE devis ADD CONSTRAINT FK_8B27C52B9395C3F3 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 devis DROP CONSTRAINT FK_8B27C52B9395C3F3'); + $this->addSql('DROP TABLE devis'); + } +} diff --git a/src/Controller/Dashboard/DevisController.php b/src/Controller/Dashboard/DevisController.php index ac629bc..1c69fe8 100644 --- a/src/Controller/Dashboard/DevisController.php +++ b/src/Controller/Dashboard/DevisController.php @@ -4,7 +4,11 @@ namespace App\Controller\Dashboard; use App\Logger\AppLogger; use App\Repository\AccountRepository; +use App\Repository\DevisRepository; +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; @@ -14,13 +18,17 @@ class DevisController extends AbstractController * Liste des administrateurs */ #[Route(path: '/crm/devis', name: 'app_crm_devis', options: ['sitemap' => false], methods: ['GET'])] - public function devis(AccountRepository $accountRepository, AppLogger $appLogger): Response + public function devis(DevisRepository $devisRepository,AppLogger $appLogger,PaginatorInterface $paginator,Request $request): Response { $appLogger->record('VIEW', 'Consultation de la liste des devis'); return $this->render('dashboard/devis/list.twig',[ - 'devis' => [], + 'quotes' => $paginator->paginate($devisRepository->findBy([],['createA'=>'asc']),$request->get('page', 1),20), ]); } + #[Route(path: '/crm/devis/add', name: 'app_crm_devis_add', options: ['sitemap' => false], methods: ['GET'])] + public function devisAdd(AccountRepository $accountRepository, AppLogger $appLogger): Response + { + } } diff --git a/src/Entity/Customer.php b/src/Entity/Customer.php index f2654f8..f36a7fc 100644 --- a/src/Entity/Customer.php +++ b/src/Entity/Customer.php @@ -45,9 +45,16 @@ class Customer #[ORM\OneToMany(targetEntity: CustomerAddress::class, mappedBy: 'customer')] private Collection $customerAddresses; + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: Devis::class, mappedBy: 'customer')] + private Collection $devis; + public function __construct() { $this->customerAddresses = new ArrayCollection(); + $this->devis = new ArrayCollection(); } public function getId(): ?int @@ -180,4 +187,34 @@ class Customer return $this; } + + /** + * @return Collection + */ + public function getDevis(): Collection + { + return $this->devis; + } + + public function addDevi(Devis $devi): static + { + if (!$this->devis->contains($devi)) { + $this->devis->add($devi); + $devi->setCustomer($this); + } + + return $this; + } + + public function removeDevi(Devis $devi): static + { + if ($this->devis->removeElement($devi)) { + // set the owning side to null (unless already changed) + if ($devi->getCustomer() === $this) { + $devi->setCustomer(null); + } + } + + return $this; + } } diff --git a/src/Entity/Devis.php b/src/Entity/Devis.php new file mode 100644 index 0000000..ca92816 --- /dev/null +++ b/src/Entity/Devis.php @@ -0,0 +1,340 @@ +id; + } + + public function getCustomer(): ?Customer + { + return $this->customer; + } + + public function setCustomer(?Customer $customer): static + { + $this->customer = $customer; + + return $this; + } + + public function getNum(): ?string + { + return $this->num; + } + + public function setNum(string $num): static + { + $this->num = $num; + + return $this; + } + + public function getCreateA(): ?\DateTimeImmutable + { + return $this->createA; + } + + public function setCreateA(\DateTimeImmutable $createA): static + { + $this->createA = $createA; + + return $this; + } + + public function getUpdateAt(): ?\DateTimeImmutable + { + return $this->updateAt; + } + + public function setUpdateAt(\DateTimeImmutable $updateAt): static + { + $this->updateAt = $updateAt; + + return $this; + } + + public function getState(): ?string + { + return $this->state; + } + + public function setState(string $state): static + { + $this->state = $state; + + return $this; + } + + /** + * @param File|null $devisFile + */ + public function setDevisFile(?File $devisFile): void + { + $this->devisFile = $devisFile; + if (null !== $devisFile) { + // It is required that at least one field changes if you are using doctrine + // otherwise the event listeners won't be called and the file is lost + $this->updateAt = new \DateTimeImmutable(); + } + } + + /** + * @param File|null $devisDocuSealFile + */ + public function setDevisDocuSealFile(?File $devisDocuSealFile): void + { + $this->devisDocuSealFile = $devisDocuSealFile; + if (null !== $devisDocuSealFile) { + // It is required that at least one field changes if you are using doctrine + // otherwise the event listeners won't be called and the file is lost + $this->updateAt = new \DateTimeImmutable(); + } + } + + /** + * @param File|null $devisSignFile + */ + public function setDevisSignFile(?File $devisSignFile): void + { + $this->devisSignFile = $devisSignFile; + if (null !== $devisSignFile) { + // It is required that at least one field changes if you are using doctrine + // otherwise the event listeners won't be called and the file is lost + $this->updateAt = new \DateTimeImmutable(); + } + } + + /** + * @param File|null $devisAuditFile + */ + public function setDevisAuditFile(?File $devisAuditFile): void + { + $this->devisAuditFile = $devisAuditFile; + if (null !== $devisAuditFile) { + // It is required that at least one field changes if you are using doctrine + // otherwise the event listeners won't be called and the file is lost + $this->updateAt = new \DateTimeImmutable(); + } + } + + /** + * @param int|null $devisSignedFileSize + */ + public function setDevisSignedFileSize(?int $devisSignedFileSize): void + { + $this->devisSignedFileSize = $devisSignedFileSize; + } + + /** + * @param string|null $devisSignedFileName + */ + public function setDevisSignedFileName(?string $devisSignedFileName): void + { + $this->devisSignedFileName = $devisSignedFileName; + } + + /** + * @param int|null $devisFileSize + */ + public function setDevisFileSize(?int $devisFileSize): void + { + $this->devisFileSize = $devisFileSize; + } + + /** + * @param int|null $devisDocuSealFileSize + */ + public function setDevisDocuSealFileSize(?int $devisDocuSealFileSize): void + { + $this->devisDocuSealFileSize = $devisDocuSealFileSize; + } + + /** + * @param string|null $devisDocuSealFileName + */ + public function setDevisDocuSealFileName(?string $devisDocuSealFileName): void + { + $this->devisDocuSealFileName = $devisDocuSealFileName; + } + + /** + * @param string|null $devisFileName + */ + public function setDevisFileName(?string $devisFileName): void + { + $this->devisFileName = $devisFileName; + } + + /** + * @param int|null $devisAuditFileSize + */ + public function setDevisAuditFileSize(?int $devisAuditFileSize): void + { + $this->devisAuditFileSize = $devisAuditFileSize; + } + + /** + * @param string|null $devisAuditFileName + */ + public function setDevisAuditFileName(?string $devisAuditFileName): void + { + $this->devisAuditFileName = $devisAuditFileName; + } + + /** + * @return File|null + */ + public function getDevisAuditFile(): ?File + { + return $this->devisAuditFile; + } + + /** + * @return string|null + */ + public function getDevisAuditFileName(): ?string + { + return $this->devisAuditFileName; + } + + /** + * @return int|null + */ + public function getDevisAuditFileSize(): ?int + { + return $this->devisAuditFileSize; + } + + /** + * @return File|null + */ + public function getDevisDocuSealFile(): ?File + { + return $this->devisDocuSealFile; + } + + /** + * @return string|null + */ + public function getDevisDocuSealFileName(): ?string + { + return $this->devisDocuSealFileName; + } + + /** + * @return int|null + */ + public function getDevisDocuSealFileSize(): ?int + { + return $this->devisDocuSealFileSize; + } + + /** + * @return File|null + */ + public function getDevisFile(): ?File + { + return $this->devisFile; + } + + /** + * @return string|null + */ + public function getDevisFileName(): ?string + { + return $this->devisFileName; + } + + /** + * @return int|null + */ + public function getDevisFileSize(): ?int + { + return $this->devisFileSize; + } + + /** + * @return string|null + */ + public function getDevisSignedFileName(): ?string + { + return $this->devisSignedFileName; + } + + /** + * @return int|null + */ + public function getDevisSignedFileSize(): ?int + { + return $this->devisSignedFileSize; + } + + /** + * @return File|null + */ + public function getDevisSignFile(): ?File + { + return $this->devisSignFile; + } + +} diff --git a/src/Entity/Product.php b/src/Entity/Product.php index e057099..94bb1fa 100644 --- a/src/Entity/Product.php +++ b/src/Entity/Product.php @@ -5,10 +5,11 @@ namespace App\Entity; use App\Repository\ProductRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\HttpFoundation\File\File; -use Vich\UploaderBundle\Mapping\Annotation as Vich; +use Vich\UploaderBundle\Mapping\Attribute\Uploadable; +use Vich\UploaderBundle\Mapping\Attribute\UploadableField; #[ORM\Entity(repositoryClass: ProductRepository::class)] -#[Vich\Uploadable()] +#[Uploadable] class Product { #[ORM\Id] @@ -38,7 +39,7 @@ class Product private ?float $caution = null; - #[Vich\UploadableField(mapping: 'image_product', fileNameProperty: 'imageName', size: 'imageSize')] + #[UploadableField(mapping: 'image_product', fileNameProperty: 'imageName', size: 'imageSize')] private ?File $imageFile = null; #[ORM\Column(nullable: true)] private ?string $imageName = null; diff --git a/src/Repository/DevisRepository.php b/src/Repository/DevisRepository.php new file mode 100644 index 0000000..d01a04f --- /dev/null +++ b/src/Repository/DevisRepository.php @@ -0,0 +1,43 @@ + + */ +class DevisRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Devis::class); + } + + // /** + // * @return Devis[] Returns an array of Devis objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('d') + // ->andWhere('d.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('d.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?Devis + // { + // return $this->createQueryBuilder('d') + // ->andWhere('d.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/templates/dashboard/devis/list.twig b/templates/dashboard/devis/list.twig index 683040e..a23ec4e 100644 --- a/templates/dashboard/devis/list.twig +++ b/templates/dashboard/devis/list.twig @@ -1 +1,157 @@ {% extends 'dashboard/base.twig' %} + +{% block title %}Catalogue Devis{% endblock %} +{% block title_header %}Gestion des Devis & Offres{% endblock %} + +{% block actions %} + +{% endblock %} + +{% block body %} +
+
+ + + + + + + + + + + + + {% for quote in quotes %} + + {# RÉFÉRENCE #} + + + {# CLIENT #} + + + {# DATE #} + + + {# STATUT DYNAMIQUE #} + + + {# MONTANT #} + + + {# ACTIONS #} + + + {% else %} + + + + {% endfor %} + +
RéférenceClientDateStatutTotal TTCActions
+
+ + {{ quote.ref|default('DEV-' ~ quote.id|upper) }} + + ID: #{{ quote.id }} +
+
+
+ + {{ quote.customer.surname|upper }} {{ quote.customer.name }} + + + {{ quote.customer.phone|default(quote.customer.email) }} + +
+
+ + {{ quote.createdAt|date('d/m/Y') }} + + + {% set statusClasses = { + 'brouillon': 'text-slate-400 bg-slate-500/10 border-slate-500/20', + 'crée': 'text-indigo-400 bg-indigo-500/10 border-indigo-500/20', + 'envoyée': 'text-blue-400 bg-blue-500/10 border-blue-500/20', + 'en attends de signature': 'text-amber-400 bg-amber-500/10 border-amber-500/20', + 'refusée': 'text-rose-400 bg-rose-500/10 border-rose-500/20', + 'signée': 'text-emerald-400 bg-emerald-500/10 border-emerald-500/20' + } %} + + {% set statusLabels = { + 'brouillon': 'Brouillon', + 'crée': 'Créé', + 'envoyée': 'Envoyé', + 'en attends de signature': 'Attente Signature', + 'refusée': 'Refusé', + 'signée': 'Signé' + } %} + + {% set currentStatus = quote.status|lower %} + + + {% if currentStatus == 'en attends de signature' %} + + {% elseif currentStatus == 'signée' %} + + {% endif %} + {{ statusLabels[currentStatus] ?? currentStatus }} + + + + {{ quote.totalTtc|number_format(2, ',', ' ') }}€ + + +
+ {# Modifier #} + + + + + {# PDF #} + + + + + {# Delete #} + + + +
+
+

Aucun devis trouvé

+
+
+
+ + {# PAGINATION #} + {% if quotes.getTotalItemCount is defined and quotes.getTotalItemCount > quotes.getItemNumberPerPage %} +
+ {{ knp_pagination_render(quotes) }} +
+ {% endif %} + + +{% endblock %}