feat: ajout champs submitter + PDFs Vich sur Devis + protection uploads

src/Entity/Devis.php:
- submitterSiteconseilId (int nullable): ID du soumetteur cote SITECONSEIL
  dans DocuSeal apres signature
- submitterCustomerId (int nullable): ID du soumetteur cote client
  dans DocuSeal apres signature
- unsignedPdf (string nullable) + unsignedPdfFile (Vich): PDF non signe
- signedPdf (string nullable) + signedPdfFile (Vich): PDF signe
- auditPdf (string nullable) + auditPdfFile (Vich): certificat d'audit
- updatedAt (DateTimeImmutable nullable): mis a jour automatiquement
  a chaque upload de fichier via les setters *File()
- Annotation #[Vich\Uploadable] sur la classe
- Les 3 champs fichier utilisent le mapping 'devis_pdf'

config/packages/vich_uploader.yaml:
- Nouveau mapping devis_pdf: stockage dans public/uploads/devis
  avec SmartUniqueNamer pour eviter les collisions de noms

config/packages/security.yaml:
- Nouvelle regle access_control: /uploads/devis requiert ROLE_USER
  (empeche l'acces aux PDF de devis sans etre connecte)

migrations/Version20260402203334.php:
- Ajout colonnes submitter_siteconseil_id, submitter_customer_id,
  unsigned_pdf, signed_pdf, audit_pdf, updated_at sur la table devis

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-04-02 22:33:47 +02:00
parent cdd5c656a9
commit 09148b5b33
4 changed files with 170 additions and 0 deletions

View File

@@ -48,6 +48,7 @@ security:
# Note: Only the *first* matching rule is applied
access_control:
- { path: ^/uploads/devis, roles: ROLE_USER }
- { path: ^/2fa, role: IS_AUTHENTICATED_2FA_IN_PROGRESS }
- { path: ^/admin, roles: ROLE_EMPLOYE }
- { path: ^/espace-client, roles: ROLE_CUSTOMER }

View File

@@ -6,3 +6,7 @@ vich_uploader:
uri_prefix: /uploads/avatars
upload_destination: '%kernel.project_dir%/public/uploads/avatars'
namer: Vich\UploaderBundle\Naming\SmartUniqueNamer
devis_pdf:
uri_prefix: /uploads/devis
upload_destination: '%kernel.project_dir%/public/uploads/devis'
namer: Vich\UploaderBundle\Naming\SmartUniqueNamer

View File

@@ -0,0 +1,41 @@
<?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 Version20260402203334 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('ALTER TABLE devis ADD submitter_siteconseil_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE devis ADD submitter_customer_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE devis ADD unsigned_pdf VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE devis ADD signed_pdf VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE devis ADD audit_pdf VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE devis ADD updated_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE devis DROP submitter_siteconseil_id');
$this->addSql('ALTER TABLE devis DROP submitter_customer_id');
$this->addSql('ALTER TABLE devis DROP unsigned_pdf');
$this->addSql('ALTER TABLE devis DROP signed_pdf');
$this->addSql('ALTER TABLE devis DROP audit_pdf');
$this->addSql('ALTER TABLE devis DROP updated_at');
}
}

View File

@@ -6,8 +6,11 @@ use App\Repository\DevisRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
#[ORM\Entity(repositoryClass: DevisRepository::class)]
#[Vich\Uploadable]
class Devis
{
#[ORM\Id]
@@ -22,9 +25,36 @@ class Devis
#[ORM\Column(length: 128)]
private string $hmac;
#[ORM\Column(nullable: true)]
private ?int $submitterSiteconseilId = null;
#[ORM\Column(nullable: true)]
private ?int $submitterCustomerId = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $unsignedPdf = null;
#[Vich\UploadableField(mapping: 'devis_pdf', fileNameProperty: 'unsignedPdf')]
private ?File $unsignedPdfFile = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $signedPdf = null;
#[Vich\UploadableField(mapping: 'devis_pdf', fileNameProperty: 'signedPdf')]
private ?File $signedPdfFile = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $auditPdf = null;
#[Vich\UploadableField(mapping: 'devis_pdf', fileNameProperty: 'auditPdf')]
private ?File $auditPdfFile = null;
#[ORM\Column]
private \DateTimeImmutable $createdAt;
#[ORM\Column(nullable: true)]
private ?\DateTimeImmutable $updatedAt = null;
/** @var Collection<int, Advert> */
#[ORM\OneToMany(targetEntity: Advert::class, mappedBy: 'devis')]
private Collection $adverts;
@@ -52,11 +82,105 @@ class Devis
return $this->hmac;
}
public function getSubmitterSiteconseilId(): ?int
{
return $this->submitterSiteconseilId;
}
public function setSubmitterSiteconseilId(?int $submitterSiteconseilId): void
{
$this->submitterSiteconseilId = $submitterSiteconseilId;
}
public function getSubmitterCustomerId(): ?int
{
return $this->submitterCustomerId;
}
public function setSubmitterCustomerId(?int $submitterCustomerId): void
{
$this->submitterCustomerId = $submitterCustomerId;
}
public function getUnsignedPdf(): ?string
{
return $this->unsignedPdf;
}
public function setUnsignedPdf(?string $unsignedPdf): void
{
$this->unsignedPdf = $unsignedPdf;
}
public function getUnsignedPdfFile(): ?File
{
return $this->unsignedPdfFile;
}
public function setUnsignedPdfFile(?File $unsignedPdfFile): void
{
$this->unsignedPdfFile = $unsignedPdfFile;
if (null !== $unsignedPdfFile) {
$this->updatedAt = new \DateTimeImmutable();
}
}
public function getSignedPdf(): ?string
{
return $this->signedPdf;
}
public function setSignedPdf(?string $signedPdf): void
{
$this->signedPdf = $signedPdf;
}
public function getSignedPdfFile(): ?File
{
return $this->signedPdfFile;
}
public function setSignedPdfFile(?File $signedPdfFile): void
{
$this->signedPdfFile = $signedPdfFile;
if (null !== $signedPdfFile) {
$this->updatedAt = new \DateTimeImmutable();
}
}
public function getAuditPdf(): ?string
{
return $this->auditPdf;
}
public function setAuditPdf(?string $auditPdf): void
{
$this->auditPdf = $auditPdf;
}
public function getAuditPdfFile(): ?File
{
return $this->auditPdfFile;
}
public function setAuditPdfFile(?File $auditPdfFile): void
{
$this->auditPdfFile = $auditPdfFile;
if (null !== $auditPdfFile) {
$this->updatedAt = new \DateTimeImmutable();
}
}
public function getCreatedAt(): \DateTimeImmutable
{
return $this->createdAt;
}
public function getUpdatedAt(): ?\DateTimeImmutable
{
return $this->updatedAt;
}
/** @return Collection<int, Advert> */
public function getAdverts(): Collection
{