```
✨ feat(Ag/MainVote): Ajoute relation ManyToOne vers Main et les votes.
Ajoute la relation ManyToOne entre MainVote et Main.
Ajoute les champs pour et contre dans la classe MainVote.
Ajoute les champs civ, name, surname dans la classe Members.
```
This commit is contained in:
@@ -17,9 +17,9 @@ vich_uploader:
|
||||
inject_on_load: true
|
||||
delete_on_update: true
|
||||
delete_on_remove: true
|
||||
product:
|
||||
uri_prefix: /storage/product
|
||||
upload_destination: '%kernel.project_dir%/public/storage/product'
|
||||
ag:
|
||||
uri_prefix: /storage/ag
|
||||
upload_destination: '%kernel.project_dir%/public/storage/ag'
|
||||
namer: Vich\UploaderBundle\Naming\UniqidNamer # Replaced namer
|
||||
inject_on_load: true
|
||||
delete_on_update: true
|
||||
|
||||
36
migrations/Version20251123161144.php
Normal file
36
migrations/Version20251123161144.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?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 Version20251123161144 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 ag_main_vote ADD main_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE ag_main_vote ADD CONSTRAINT FK_A5781BDE627EA78A FOREIGN KEY (main_id) REFERENCES ag_main (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_A5781BDE627EA78A ON ag_main_vote (main_id)');
|
||||
}
|
||||
|
||||
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 ag_main_vote DROP CONSTRAINT FK_A5781BDE627EA78A');
|
||||
$this->addSql('DROP INDEX IDX_A5781BDE627EA78A');
|
||||
$this->addSql('ALTER TABLE ag_main_vote DROP main_id');
|
||||
}
|
||||
}
|
||||
34
migrations/Version20251123165538.php
Normal file
34
migrations/Version20251123165538.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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 Version20251123165538 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 members ADD name VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE members ADD surname VARCHAR(255) DEFAULT NULL');
|
||||
}
|
||||
|
||||
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 members DROP name');
|
||||
$this->addSql('ALTER TABLE members DROP surname');
|
||||
}
|
||||
}
|
||||
32
migrations/Version20251123165554.php
Normal file
32
migrations/Version20251123165554.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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 Version20251123165554 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 members ADD civ VARCHAR(255) DEFAULT NULL');
|
||||
}
|
||||
|
||||
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 members DROP civ');
|
||||
}
|
||||
}
|
||||
43
migrations/Version20251123174110.php
Normal file
43
migrations/Version20251123174110.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?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 Version20251123174110 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 ag_main ADD ag_file_name VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE ag_main ADD ag_dimensions JSON DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE ag_main ADD ag_size VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE ag_main ADD ag_mine_type VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE ag_main ADD ag_original_name VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE ag_main ADD update_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL');
|
||||
$this->addSql('COMMENT ON COLUMN ag_main.update_at IS \'(DC2Type:datetime_immutable)\'');
|
||||
}
|
||||
|
||||
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 ag_main DROP ag_file_name');
|
||||
$this->addSql('ALTER TABLE ag_main DROP ag_dimensions');
|
||||
$this->addSql('ALTER TABLE ag_main DROP ag_size');
|
||||
$this->addSql('ALTER TABLE ag_main DROP ag_mine_type');
|
||||
$this->addSql('ALTER TABLE ag_main DROP ag_original_name');
|
||||
$this->addSql('ALTER TABLE ag_main DROP update_at');
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,13 @@ namespace App\Controller\Admin;
|
||||
use App\Dto\Ag\AgMembersType;
|
||||
use App\Dto\Ag\AgOrderType;
|
||||
use App\Dto\Ag\AgType;
|
||||
use App\Dto\Ag\AgVoteType;
|
||||
use App\Entity\Account;
|
||||
use App\Entity\AccountResetPasswordRequest;
|
||||
use App\Entity\Ag\Main;
|
||||
use App\Entity\Ag\MainMember;
|
||||
use App\Entity\Ag\MainOrder;
|
||||
use App\Entity\Ag\MainVote;
|
||||
use App\Entity\Members;
|
||||
use App\Entity\MembersCotisations;
|
||||
use App\Entity\Products;
|
||||
@@ -23,20 +25,24 @@ use App\Repository\MembersRepository;
|
||||
use App\Repository\ProductsRepository;
|
||||
use App\Service\Mailer\Mailer;
|
||||
use App\Service\Payments\PaymentClient;
|
||||
use App\Service\Pdf\AgGenerator;
|
||||
use App\Service\Pdf\CotaReceiptGenerator;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordConfirmEvent;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordEvent;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\Mime\Part\DataPart;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
use Twig\Environment;
|
||||
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
|
||||
|
||||
class AdminController extends AbstractController
|
||||
{
|
||||
@@ -232,9 +238,43 @@ class AdminController extends AbstractController
|
||||
|
||||
|
||||
#[Route(path: '/admin/ag', name: 'admin_ag', options: ['sitemap' => false], methods: ['GET'])]
|
||||
public function adminAg(MainRepository $mainRepository): Response
|
||||
public function adminAg(UploaderHelper $uploaderHelper,KernelInterface $kernel,EntityManagerInterface $entityManager,Request $request,MainRepository $mainRepository): Response
|
||||
{
|
||||
if($request->query->has('idSign')) {
|
||||
/** @var Main $main */
|
||||
$main = $mainRepository->find($request->query->get('id'));
|
||||
$url = $uploaderHelper->asset($main,'ag');
|
||||
|
||||
if($_ENV['APP_ENV'] == "prod")
|
||||
$url = $request->getContentTypeFormat().$url;
|
||||
else
|
||||
$url = $_ENV['DEV_URL'].$url;
|
||||
dd($url);
|
||||
}
|
||||
if($request->query->has('idGenerate')) {
|
||||
/** @var Main $main */
|
||||
$main = $mainRepository->find($request->query->get('idGenerate'));
|
||||
$ag = new AgGenerator();
|
||||
$ag->setData($main,$kernel,$entityManager);
|
||||
$ag->SetTitle("AG - E-Cosplay ".$main->getAgDateAt()->format('d/m/Y'));
|
||||
$ag->AliasNbPages();
|
||||
$ag->AddPage();
|
||||
$ag->writeConstitutionAndMembers();
|
||||
$ag->writeSpecificAgendaItems();
|
||||
$ag->writeBureauRenewalAndClosure();
|
||||
// 1. Récupération du contenu du PDF en chaîne de caractères
|
||||
$content = $ag->Output('S');
|
||||
$fileName = 'ag_ecosplay_' . $main->getAgDateAt()->format('Ymd') . '.pdf';
|
||||
$tempFilePath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $fileName;
|
||||
|
||||
file_put_contents($tempFilePath, $content);
|
||||
$file = new UploadedFile($tempFilePath,$fileName,"application/pdf",0,true);
|
||||
$main->setAg($file);
|
||||
$main->setIsClosed(true);
|
||||
$entityManager->persist($main);
|
||||
$entityManager->flush();
|
||||
return $this->redirectToRoute('admin_ag');
|
||||
}
|
||||
return $this->render('admin/ag.twig', [
|
||||
'ags' => $mainRepository->findBy([],['agDateAt'=>'DESC']),
|
||||
]);
|
||||
@@ -273,7 +313,7 @@ class AdminController extends AbstractController
|
||||
if($formOrder->isSubmitted() && $formOrder->isValid()){
|
||||
$entityManager->persist($agOrder);
|
||||
$entityManager->flush();
|
||||
return $this->redirectToRoute('admin_ag_edit',['id'=>$agOrder->getId()]);
|
||||
return $this->redirectToRoute('admin_ag_edit',['id'=>$ag->getId()]);
|
||||
}
|
||||
|
||||
$agMembers = new MainMember();
|
||||
@@ -307,10 +347,30 @@ class AdminController extends AbstractController
|
||||
$entityManager->flush();
|
||||
return $this->redirectToRoute('admin_ag_edit',['id'=>$ag->getId()]);
|
||||
}
|
||||
if($request->query->has('agVoteId')) {
|
||||
$agOders = $entityManager->getRepository(MainVote::class)->find($request->query->get('agVoteId'));
|
||||
$entityManager->remove($agOders);
|
||||
$entityManager->flush();
|
||||
return $this->redirectToRoute('admin_ag_edit',['id'=>$ag->getId()]);
|
||||
}
|
||||
|
||||
$agVote = new MainVote();
|
||||
$agVote->setMain($ag);
|
||||
$formMemberVote = $this->createForm(AgVoteType::class,$agVote);
|
||||
$formMemberVote->handleRequest($request);
|
||||
if($formMemberVote->isSubmitted() && $formMemberVote->isValid()){
|
||||
$rf = $entityManager->getRepository(Members::class)->find($request->request->all()['ag_vote']['member']);
|
||||
$agVote->setMember($rf);
|
||||
$entityManager->persist($agVote);
|
||||
$entityManager->flush();
|
||||
return $this->redirectToRoute('admin_ag_edit',['id'=>$ag->getId()]);
|
||||
}
|
||||
|
||||
return $this->render('admin/ag/edit.twig', [
|
||||
'ag' => $form->createView(),
|
||||
'agMain' => $ag,
|
||||
'agMembers' => $formMember->createView(),
|
||||
'agVote' => $formMemberVote->createView(),
|
||||
'agOrder' => $formOrder->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ class AgMembersType extends AbstractType
|
||||
$choices = [];
|
||||
|
||||
foreach ($this->membersRepository->findAll() as $member) {
|
||||
if(!is_null($main)) {
|
||||
$present = $main->getMainMembers()->filter(function (MainMember $mainMember) use ($member) {
|
||||
return $member->getId() == $mainMember->getMember()->getId();
|
||||
})->first();
|
||||
@@ -36,6 +37,7 @@ class AgMembersType extends AbstractType
|
||||
$choices[$member->getPseudo()] = $member->getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$builder
|
||||
->add('member', ChoiceType::class, [
|
||||
|
||||
78
src/Dto/Ag/AgVoteType.php
Normal file
78
src/Dto/Ag/AgVoteType.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Dto\Ag;
|
||||
|
||||
use App\Entity\Ag\Main;
|
||||
use App\Entity\Ag\MainMember;
|
||||
use App\Entity\Ag\MainVote;
|
||||
use App\Entity\Members;
|
||||
use App\Repository\MembersRepository;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class AgVoteType extends AbstractType
|
||||
{
|
||||
public function __construct(private MembersRepository $membersRepository)
|
||||
{
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
/** @var Main $main */
|
||||
$main = $options['data']->getMain();
|
||||
$choices = [];
|
||||
|
||||
foreach ($this->membersRepository->findAll() as $member) {
|
||||
if(!is_null($main)){
|
||||
$present = $main->getMainVote()->filter(function (MainVote $mainMember) use ($member) {
|
||||
return $member->getId() == $mainMember->getMember()->getId();
|
||||
})->first();
|
||||
if(!$present instanceof MainVote) {
|
||||
$choices[$member->getPseudo()] = $member->getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$builder
|
||||
->add('member', ChoiceType::class, [
|
||||
'label' => 'Membre',
|
||||
'choices' => $choices,
|
||||
'mapped' => false,
|
||||
])
|
||||
->add('role', ChoiceType::class, [
|
||||
'label' => 'Rôle au sein de l\'association',
|
||||
'choices' => [
|
||||
'Président(e)' => 'President',
|
||||
'Trésorier(e)' => 'Tresorier',
|
||||
'Secrétaire(e)' => 'Secretaire',
|
||||
'Vice-Président(e)' => 'VicePresident',
|
||||
'Trésorier(e) Adjoints' => 'TresorierAdjoint',
|
||||
'Secrétaire(e) Adjoints' => 'SecretaireAdjoint',
|
||||
],
|
||||
])
|
||||
->add('pour',NumberType::class,[
|
||||
'label' => 'Pour',
|
||||
'required' => true,
|
||||
'empty_data' => 0,
|
||||
])
|
||||
->add('contre',NumberType::class,[
|
||||
'label' => 'Contre',
|
||||
'required' => true,
|
||||
'empty_data' => 0,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefault('data_class',MainVote::class);
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,12 @@ use App\Repository\Ag\MainRepository;
|
||||
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: MainRepository::class)]
|
||||
#[ORM\Table(name: 'ag_main')]
|
||||
#[Vich\Uploadable()]
|
||||
class Main
|
||||
{
|
||||
#[ORM\Id]
|
||||
@@ -56,10 +59,50 @@ class Main
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?bool $isClosed = null;
|
||||
|
||||
/**
|
||||
* @var Collection<int, MainVote>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: MainVote::class, mappedBy: 'main')]
|
||||
private Collection $mainVote;
|
||||
|
||||
|
||||
#[Vich\UploadableField(mapping: 'ag',fileNameProperty: 'agFileName', size: 'agSize', mimeType: 'agMineType', originalName: 'agOriginalName',dimensions: 'agDimensions')]
|
||||
private ?File $ag = null;
|
||||
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?string $agFileName = null;
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?array $agDimensions = [];
|
||||
#[ORM\Column(length: 255,nullable: true)]
|
||||
private ?string $agSize = null;
|
||||
#[ORM\Column(length: 255,nullable: true)]
|
||||
private ?string $agMineType = null;
|
||||
#[ORM\Column(length: 255,nullable: true)]
|
||||
private ?string $agOriginalName = null;
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?\DateTimeImmutable $updateAt;
|
||||
/**
|
||||
* @return File|null
|
||||
*/
|
||||
public function getAg(): ?File
|
||||
{
|
||||
return $this->ag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param File|null $ag
|
||||
*/
|
||||
public function setAg(?File $ag): void
|
||||
{
|
||||
$this->ag = $ag;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->mainMembers = new ArrayCollection();
|
||||
$this->orders = new ArrayCollection();
|
||||
$this->mainVote = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
@@ -234,4 +277,138 @@ class Main
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, MainVote>
|
||||
*/
|
||||
public function getMainVote(): Collection
|
||||
{
|
||||
return $this->mainVote;
|
||||
}
|
||||
|
||||
public function addMainVote(MainVote $mainVote): static
|
||||
{
|
||||
if (!$this->mainVote->contains($mainVote)) {
|
||||
$this->mainVote->add($mainVote);
|
||||
$mainVote->setMain($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeMainVote(MainVote $mainVote): static
|
||||
{
|
||||
if ($this->mainVote->removeElement($mainVote)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($mainVote->getMain() === $this) {
|
||||
$mainVote->setMain(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeImmutable|null
|
||||
*/
|
||||
public function getUpdateAt(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->updateAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
public function getAgDimensions(): ?array
|
||||
{
|
||||
return $this->agDimensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|null
|
||||
*/
|
||||
public function getIsClosed(): ?bool
|
||||
{
|
||||
return $this->isClosed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getAgFileName(): ?string
|
||||
{
|
||||
return $this->agFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getAgMineType(): ?string
|
||||
{
|
||||
return $this->agMineType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getAgOriginalName(): ?string
|
||||
{
|
||||
return $this->agOriginalName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getAgSize(): ?string
|
||||
{
|
||||
return $this->agSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTimeImmutable|null $updateAt
|
||||
*/
|
||||
public function setUpdateAt(?\DateTimeImmutable $updateAt): void
|
||||
{
|
||||
$this->updateAt = $updateAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|null $agDimensions
|
||||
*/
|
||||
public function setAgDimensions(?array $agDimensions): void
|
||||
{
|
||||
$this->agDimensions = $agDimensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $agFileName
|
||||
*/
|
||||
public function setAgFileName(?string $agFileName): void
|
||||
{
|
||||
$this->agFileName = $agFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $agMineType
|
||||
*/
|
||||
public function setAgMineType(?string $agMineType): void
|
||||
{
|
||||
$this->agMineType = $agMineType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $agOriginalName
|
||||
*/
|
||||
public function setAgOriginalName(?string $agOriginalName): void
|
||||
{
|
||||
$this->agOriginalName = $agOriginalName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $agSize
|
||||
*/
|
||||
public function setAgSize(?string $agSize): void
|
||||
{
|
||||
$this->agSize = $agSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ class MainVote
|
||||
#[ORM\Column]
|
||||
private ?int $contre = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'mainVote')]
|
||||
private ?Main $main = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
@@ -79,4 +82,16 @@ class MainVote
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMain(): ?Main
|
||||
{
|
||||
return $this->main;
|
||||
}
|
||||
|
||||
public function setMain(?Main $main): static
|
||||
{
|
||||
$this->main = $main;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,15 @@ class Members
|
||||
#[ORM\OneToMany(targetEntity: MainVote::class, mappedBy: 'member')]
|
||||
private Collection $agVotes;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $name = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $surname = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $civ = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->membersCotisations = new ArrayCollection();
|
||||
@@ -493,4 +502,40 @@ class Members
|
||||
|
||||
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 getCiv(): ?string
|
||||
{
|
||||
return $this->civ;
|
||||
}
|
||||
|
||||
public function setCiv(?string $civ): static
|
||||
{
|
||||
$this->civ = $civ;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,23 @@ class MembersType extends AbstractType
|
||||
'placeholder' => 'Choisir un fichier...',
|
||||
]
|
||||
])
|
||||
->add('civ',ChoiceType::class, [
|
||||
'choices' => [
|
||||
'M' => 'M',
|
||||
'Mme' => 'Mme',
|
||||
],
|
||||
'required' => true,
|
||||
'empty_data'=>'M',
|
||||
'label'=> 'Civile',
|
||||
])
|
||||
->add('name', TextType::class, [
|
||||
'label' => 'Nom',
|
||||
'required' => true,
|
||||
])
|
||||
->add('surname', TextType::class, [
|
||||
'label' => 'Prenom',
|
||||
'required' => true,
|
||||
])
|
||||
->add('pseudo', TextType::class, [
|
||||
'label' => 'Pseudo du membre',
|
||||
'required' => true,
|
||||
|
||||
505
src/Service/Pdf/AgGenerator.php
Normal file
505
src/Service/Pdf/AgGenerator.php
Normal file
@@ -0,0 +1,505 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Pdf;
|
||||
|
||||
use App\Entity\Ag\Main;
|
||||
use App\Entity\Ag\MainMember;
|
||||
use App\Entity\Members;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Fpdf\Fpdf;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
class AgGenerator extends Fpdf
|
||||
{
|
||||
private Main $main;
|
||||
private KernelInterface $kernel;
|
||||
private EntityManagerInterface $em;
|
||||
private array $burreauMembers; // Ajout de l'initialisation de la propriété
|
||||
|
||||
private int $nbVote = 0;
|
||||
private int $nbVoteVoix = 0;
|
||||
public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4')
|
||||
{
|
||||
parent::__construct($orientation, $unit, $size);
|
||||
}
|
||||
|
||||
public function setData(Main $main, KernelInterface $kernel,EntityManagerInterface $em)
|
||||
{
|
||||
$this->main = $main;
|
||||
$this->kernel = $kernel;
|
||||
$this->em = $em;
|
||||
|
||||
$this->burreauMembers = [];
|
||||
foreach ($main->getMainMembers() as $member) {
|
||||
if($member->getMember()->getRole() == "President")
|
||||
$this->burreauMembers[] = $member;
|
||||
if($member->getMember()->getRole() == "Tresorier")
|
||||
$this->burreauMembers[] = $member;
|
||||
if($member->getMember()->getRole() == "Secretaire")
|
||||
$this->burreauMembers[] = $member;
|
||||
if($member->getMember()->getRole() == "VicePresident")
|
||||
$this->burreauMembers[] = $member;
|
||||
if($member->getMember()->getRole() == "TresorierAdjoint")
|
||||
$this->burreauMembers[] = $member;
|
||||
if($member->getMember()->getRole() == "SecretaireAdjoint")
|
||||
$this->burreauMembers[] = $member;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit l'en-tête de chaque page du document PDF.
|
||||
*/
|
||||
public function Header()
|
||||
{
|
||||
// --- 1. Définition des constantes de position ---
|
||||
$yPos = 10; // Marge haute par défaut
|
||||
$xPos = 10; // Marge gauche par défaut
|
||||
$logoWidth = 20; // Nouvelle valeur
|
||||
$logoHeight = 10; // Nouvelle valeur
|
||||
|
||||
// --- 2. Titre et Date (Top Centré) ---
|
||||
$this->SetY($yPos);
|
||||
|
||||
$agDate = $this->main->getAgDateAt() ? $this->main->getAgDateAt()->format('d/m/Y') : 'Date non spécifiée';
|
||||
|
||||
$titleLine1 = "PROCES-VERBAL DE L'ASSEMBLÉE GÉNÉRALE";
|
||||
$titleLine2 = "Séance du : " . $agDate;
|
||||
|
||||
// Titre Principal
|
||||
$this->SetFont('Arial', 'B', 14);
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
$this->Cell(0, 7, utf8_decode($titleLine1), 0, 1, 'C');
|
||||
|
||||
// Date
|
||||
$this->SetFont('Arial', '', 11);
|
||||
$this->Cell(0, 6, utf8_decode($titleLine2), 0, 1, 'C');
|
||||
|
||||
// Sauvegarde la position Y après le titre/date
|
||||
$yAfterTitle = $this->GetY();
|
||||
|
||||
// --- 3. Logo (Fixé Haut Gauche) ---
|
||||
$logoPath = $this->kernel->getProjectDir() . '/public/assets/images/logo.jpg';
|
||||
$this->Image($logoPath, $xPos, $yPos, $logoWidth, $logoHeight);
|
||||
|
||||
// --- 4. Bloc d'informations légales (Uniquement sur la première page) ---
|
||||
if ($this->PageNo() == 1) {
|
||||
$this->writeAgContextBlock();
|
||||
} else {
|
||||
// --- 5. Définir la position finale Y pour les pages suivantes ---
|
||||
$finalY = max($yAfterTitle, $yPos + $logoHeight);
|
||||
$this->SetY($finalY + 5);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit le pied de page (généralement le numéro de page).
|
||||
*/
|
||||
public function Footer()
|
||||
{
|
||||
// Positionnement à 15 mm du bas
|
||||
$this->SetY(-15);
|
||||
// Police Arial italique 8
|
||||
$this->SetFont('Arial', 'I', 8);
|
||||
// Numéro de page
|
||||
$this->Cell(0, 10, 'Page ' . $this->PageNo() . '/{nb}', 0, 0, 'C');
|
||||
}
|
||||
|
||||
/**
|
||||
* Écrit le bloc d'informations légales et contextuelles de l'AG.
|
||||
*/
|
||||
public function writeAgContextBlock()
|
||||
{
|
||||
// Récupération des données nécessaires
|
||||
$agDate = $this->main->getAgDateAt() ? $this->main->getAgDateAt()->format('d/m/Y') : 'Date non spécifiée';
|
||||
$agTime = $this->main->getAgDateAt() ? $this->main->getAgDateAt()->format('H\hi') : 'Heure non spécifiée';
|
||||
|
||||
// Utilisez une petite police pour les détails légaux
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
|
||||
// Largeur de la cellule de données (pour aligner les données)
|
||||
$dataCellWidth = 100;
|
||||
$labelCellWidth = 60;
|
||||
|
||||
// Ajout du cadre
|
||||
$this->Cell(0, 1, '', 'T', 1, 'L', false); // Ligne supérieure légère
|
||||
$this->Ln(2); // Petite marge après la ligne
|
||||
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(0, 5, utf8_decode('INFORMATIONS LÉGALES ET CONTEXTUELLES'), 0, 1, 'C');
|
||||
$this->Ln(1);
|
||||
|
||||
$this->SetFont('Arial', '', 10);
|
||||
|
||||
// Ligne 1: Association / RNA
|
||||
$this->Cell($labelCellWidth, 5, utf8_decode('Association :'), 0, 0, 'L');
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell($dataCellWidth, 5, utf8_decode('E-Cosplay Association loi 1901 – RNA N°W022006988'), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
|
||||
// Ligne 2: Siège social
|
||||
$this->Cell($labelCellWidth, 5, utf8_decode('Siège social :'), 0, 0, 'L');
|
||||
$this->Cell($dataCellWidth, 5, utf8_decode('42 rue de saint-quentin 02800 Beautor'), 0, 1, 'L');
|
||||
|
||||
// Ligne 3: Date de déclaration
|
||||
$this->Cell($labelCellWidth, 5, utf8_decode('Date de déclaration :'), 0, 0, 'L');
|
||||
$this->Cell($dataCellWidth, 5, utf8_decode($agDate), 0, 1, 'L');
|
||||
|
||||
// Ligne 4: Date de l'AG
|
||||
$this->Cell($labelCellWidth, 5, utf8_decode("Date de l'Assemblée Générale :"), 0, 0, 'L');
|
||||
$this->Cell($dataCellWidth, 5, utf8_decode($agDate), 0, 1, 'L');
|
||||
|
||||
// Ligne 5: Heure de début
|
||||
$this->Cell($labelCellWidth, 5, utf8_decode('Heure de début de la séance :'), 0, 0, 'L');
|
||||
$this->Cell($dataCellWidth, 5, utf8_decode($agTime), 0, 1, 'L');
|
||||
|
||||
// Ligne 6: Lieu (Dynamique)
|
||||
$this->Cell($labelCellWidth, 5, utf8_decode('Lieu :'), 0, 0, 'L');
|
||||
$this->Cell($dataCellWidth, 5, utf8_decode($this->main->getLocate() . " " . $this->main->getLocateZipcode() . " " . $this->main->getLocateCity()), 0, 1, 'L');
|
||||
|
||||
// Ligne 7: Nature de l'AG (Dynamique)
|
||||
$this->Cell($labelCellWidth, 5, utf8_decode("Nature de l'AG :"), 0, 0, 'L');
|
||||
$this->Cell($dataCellWidth, 5, utf8_decode($this->main->getType()), 0, 1, 'L');
|
||||
|
||||
// Fin de la mise en forme du bloc
|
||||
$this->Ln(2); // Petite marge avant la ligne
|
||||
$this->Cell(0, 1, '', 'T', 1, 'L', false); // Ligne inférieure légère
|
||||
$this->Ln(5); // Espacement final avant le corps du texte
|
||||
}
|
||||
|
||||
/**
|
||||
* Écrit la sous-section 1.2 : Présidence et Secrétariat de Séance.
|
||||
*/
|
||||
public function writePresidenceAndSecretariat()
|
||||
{
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
$this->Cell(0, 7, utf8_decode('1.2. Présidence et Secrétariat de Séance'), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Ln(3);
|
||||
|
||||
$text = utf8_decode("Conformément aux statuts de l'Association, la séance est présidée par le Président".(($this->main->getPresident()->getCiv() == "Mme")?"e":"")." en exercice, ".$this->main->getPresident()->getCiv()." ".$this->main->getPresident()->getName()." ".$this->main->getPresident()->getSurname().", et la fonction de Secrétair".(($this->main->getSecretaire()->getCiv() == "Mme")?"e":"")." de Séance est assurée par ".$this->main->getSecretaire()->getCiv()." ".$this->main->getSecretaire()->getName()." ".$this->main->getSecretaire()->getSurname()." désigné par l'Assemblée, après vote à main levée, ou par le Secrétaire de l'Association.");
|
||||
|
||||
$this->MultiCell(0, 5, $text);
|
||||
|
||||
$this->Ln(5);
|
||||
|
||||
// Cadre pour les noms à compléter manuellement ou via un autre champ
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(60, 6, utf8_decode('Président de Séance :'), 0, 0, 'L');
|
||||
$this->Cell(0, 6, utf8_decode($this->main->getPresident()->getCiv()." ".$this->main->getPresident()->getName()." ".$this->main->getPresident()->getSurname()), 'B', 1, 'L');
|
||||
|
||||
$this->Cell(60, 6, utf8_decode('Secrétaire de Séance :'), 0, 0, 'L');
|
||||
$this->Cell(0, 6, utf8_decode($this->main->getSecretaire()->getCiv()." ".$this->main->getSecretaire()->getName()." ".$this->main->getSecretaire()->getSurname()), 'B', 1, 'L');
|
||||
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Ln(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Écrit la sous-section 1.3 : Ouverture de la Séance.
|
||||
*/
|
||||
public function writeOuvertureSeance()
|
||||
{
|
||||
$agTime = $this->main->getAgDateAt() ? $this->main->getAgDateAt()->format('H\hi') : 'Heure non spécifiée';
|
||||
$presidentCiv = $this->main->getPresident()->getCiv();
|
||||
$presidentTitle = ($presidentCiv === 'Mme') ? 'Présidente' : 'Président';
|
||||
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
$this->Cell(0, 7, utf8_decode('1.3. Ouverture de la séance'), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Ln(3);
|
||||
|
||||
$text = utf8_decode("Le $presidentTitle de séance déclare l'Assemblée Générale ouverte à $agTime et rappelle l'ordre du jour, qui a été porté à la connaissance des membres lors de la convocation.");
|
||||
|
||||
$this->MultiCell(0, 5, $text);
|
||||
|
||||
$this->Ln(5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Écrit la liste spécifique des points de l'ordre du jour et les délibérations.
|
||||
*/
|
||||
public function writeSpecificAgendaItems()
|
||||
{
|
||||
// Récupération dynamique des points d'ordre du jour
|
||||
$agendaOrders = $this->main->getOrders();
|
||||
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
$this->Cell(0, 7, utf8_decode('2.1. Points de l\'Ordre du Jour et Délibérations'), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Ln(3);
|
||||
|
||||
$i = 1;
|
||||
foreach ($agendaOrders as $order) {
|
||||
|
||||
// Titre du point
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(0, 6, utf8_decode("Point $i : " . $order->getTitle()), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
|
||||
// Contenu de la délibération (Description du point de l'ordre du jour)
|
||||
$this->SetX(15); // Décalage pour l'indentation
|
||||
// Utilisation de la méthode MultiCell pour gérer les sauts de ligne si besoin
|
||||
$this->MultiCell(0, 5, utf8_decode($order->getDescription()));
|
||||
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Ln(5);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Écrit la section 2 complète : Ordre du Jour et Délibérations.
|
||||
*/
|
||||
public function writeAgendaAndDeliberations()
|
||||
{
|
||||
// 1. Titre de la section principale
|
||||
$this->SetFont('Arial', 'B', 14);
|
||||
$this->Ln(5);
|
||||
$this->Cell(0, 8, utf8_decode('2. ORDRE DU JOUR ET DÉLIBÉRATIONS'), 0, 1, 'L');
|
||||
$this->Ln(3);
|
||||
|
||||
// 2. Appel des sous-sections (le nouvel ordre du jour)
|
||||
$this->writeSpecificAgendaItems();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Écrit la section 3 complète : Renouvellement du Bureau et Clôture.
|
||||
*/
|
||||
public function writeBureauRenewalAndClosure()
|
||||
{
|
||||
$agTime = $this->main->getAgDateAt() ? $this->main->getAgDateAt()->format('H\hi') : 'Heure non spécifiée';
|
||||
$presidentCiv = $this->main->getPresident()->getCiv();
|
||||
$presidentTitle = ($presidentCiv === 'Mme') ? 'Présidente' : 'Président';
|
||||
$secretaireCiv = $this->main->getSecretaire()->getCiv();
|
||||
$secretaireTitle = ($secretaireCiv === 'Mme') ? 'Secrétaire' : 'Secrétaire';
|
||||
|
||||
// 1. Titre de la section principale
|
||||
$this->SetFont('Arial', 'B', 14);
|
||||
$this->Ln(5);
|
||||
$this->Cell(0, 8, utf8_decode('3. RENOUVELLEMENT DU BUREAU ET CLÔTURE DE SÉANCE'), 0, 1, 'L');
|
||||
$this->Ln(3);
|
||||
|
||||
// --- 3.1. Renouvellement des membres du Bureau ---
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
$this->Cell(0, 7, utf8_decode('3.1. Renouvellement des membres du Bureau'), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Ln(3);
|
||||
|
||||
// Rappel statutaire / Anciens membres
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(0, 5, utf8_decode('Rappel Statutaire (Ancien Bureau) :'), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
foreach ($this->burreauMembers as $member) {
|
||||
$this->SetX(15);
|
||||
$this->Cell(0, 5, utf8_decode($member->getMember()->getRole().' : '.(($member->getMember()->getCiv() == "M")?"M":"Mme").' '.$member->getMember()->getName()." ".$member->getMember()->getSurname()), 0, 1, 'L');
|
||||
}
|
||||
$this->Ln(5);
|
||||
|
||||
// Modalités de vote
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(0, 5, utf8_decode('Modalités de vote :'), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->SetX(15);
|
||||
$this->MultiCell(0, 5, utf8_decode("Il est procédé au vote à main levée pour le renouvellement des membres du Bureau."));
|
||||
$this->SetX(10);
|
||||
$this->Ln(3);
|
||||
|
||||
// Résultats et Validité
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(0, 5, utf8_decode('Résultats du Vote :'), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->SetX(15);
|
||||
$this->Cell(0, 5, utf8_decode(' Nombre de votants éligibles : '.$this->nbVote), 0, 1, 'L');
|
||||
$this->SetX(15);
|
||||
$this->Cell(0, 5, utf8_decode(' Majorité absolue requise pour élection : '.$this->nbVoteVoix.' voix'), 0, 1, 'L');
|
||||
$this->SetX(10);
|
||||
$this->Ln(3);
|
||||
|
||||
// Résolution: Nouveaux membres du Bureau
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(0, 5, utf8_decode('Résolution : Sont élus/réélus, en tant que membres du Bureau :'), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
foreach ($this->main->getMainVote() as $vote) {
|
||||
$this->SetX(15);
|
||||
$this->Cell(0, 5, utf8_decode('o Président'.(($vote->getMember()->getCiv() == "Mme")?"e":"").' : '.$vote->getMember()->getCiv().' '.$vote->getMember()->getName().' '.$vote->getMember()->getSurname().' ('.$vote->getPour().' Pour, '.$vote->getContre().' Contre, 0 Abstention)'), 0, 1, 'L');
|
||||
}
|
||||
|
||||
$this->SetX(10);
|
||||
$this->Ln(3);
|
||||
|
||||
// Acceptance
|
||||
$acceptanceText = utf8_decode("Les membres nouvellement élus (selon les résultats de l'élection du Bureau qui sera reporté ci-dessous) acceptent leurs fonctions pour une durée de 1 an.");
|
||||
$this->MultiCell(0, 5, $acceptanceText);
|
||||
$this->Ln(10);
|
||||
|
||||
|
||||
// --- 3.2. Clôture de la Séance et Signatures ---
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
$this->Cell(0, 7, utf8_decode('3.2. Clôture de la Séance'), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Ln(3);
|
||||
|
||||
// Mise à jour de l'heure de clôture à 16h00
|
||||
$this->MultiCell(0, 5, utf8_decode("L'ordre du jour étant épuisé et plus personne ne demandant la parole, le $presidentTitle de séance remercie l'Assemblée de sa participation et prononce la clôture de l'Assemblée Générale à ".$this->main->getClosedAt()->format('H:i')));
|
||||
|
||||
$this->Ln(10);
|
||||
|
||||
// Ajout de la mention "Fait à..."
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Cell(0, 5, utf8_decode('Fait à Beautor Le '.$this->main->getAgDateAt()->format('d M Y')), 0, 1, 'L');
|
||||
$this->Ln(5); // Espace après la date et avant les titres
|
||||
|
||||
// Signatures
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
// Utiliser Cell avec une largeur définie (95mm pour chaque signature sur A4)
|
||||
$this->Cell(95, 5, utf8_decode("Le $presidentTitle de Séance"), 0, 0, 'C');
|
||||
$this->Cell(95, 5, utf8_decode(""), 0, 1, 'C');
|
||||
|
||||
$this->Ln(5);
|
||||
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Cell(95, 5, utf8_decode("{{Sign;type=signature}}"), 0, 0, 'C');
|
||||
$this->Cell(95, 5, utf8_decode(""), 0, 1, 'C');
|
||||
$this->Ln(5);
|
||||
$this->Cell(95, 5, utf8_decode($this->main->getPresident()->getName()." ".$this->main->getPresident()->getSurname()), 0, 0, 'C');
|
||||
|
||||
$this->Ln(5);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Écrit la section 1 complète : Constitution et Membres, y compris le quorum.
|
||||
*/
|
||||
public function writeConstitutionAndMembers()
|
||||
{
|
||||
// 1. Titre de la section principale
|
||||
$this->SetFont('Arial', 'B', 14);
|
||||
$this->Ln(5); // Plus d'espace pour la nouvelle section
|
||||
$this->Cell(0, 8, utf8_decode('1. CONSTITUTION ET OUVERTURE DE SÉANCE'), 0, 1, 'L');
|
||||
$this->Ln(3);
|
||||
|
||||
// 2. Début de la sous-section 1.1
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
$this->Cell(0, 7, utf8_decode('1.1. Membres présents et absents'), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Ln(3);
|
||||
|
||||
// 3. Initialisation des listes et compteurs pour les listes
|
||||
$listPresent = [];
|
||||
$listAbsent = [];
|
||||
$countPresent = 0;
|
||||
$countAbsent = 0;
|
||||
|
||||
// Compteurs pour le Quorum et la Majorité
|
||||
$totalMembers = 0; // Tous les membres de l'association
|
||||
$totalVoters = 0; // Membres ayant le droit de vote (Présents ou Représentés)
|
||||
|
||||
// Récupérer tous les membres de l'association
|
||||
$allMembers = $this->em->getRepository(Members::class)->findAll();
|
||||
$totalMembers = count($allMembers);
|
||||
|
||||
// 4. Traitement et classification des membres
|
||||
foreach ($allMembers as $member) {
|
||||
/** @var MainMember $mainMemberStatus */
|
||||
$mainMemberStatus = $this->main->getMainMembers()->filter(function (MainMember $mainMember) use ($member) {
|
||||
// Correspondance de l'entité Members avec son statut MainMember pour cette AG
|
||||
return $member->getId() == $mainMember->getMember()->getId();
|
||||
})->first();
|
||||
|
||||
if($mainMemberStatus instanceof MainMember && $mainMemberStatus->isVotedAllow()) {
|
||||
// Membre ayant le droit de vote (Présent ou Représenté)
|
||||
// Liste des votants (présents/représentés) par pseudo
|
||||
$listPresent[] = $member->getPseudo();
|
||||
$countPresent++;
|
||||
$totalVoters++;
|
||||
} else {
|
||||
// Membre n'ayant pas le droit de vote (Absent ou non éligible)
|
||||
// Liste des absents ou non votants par nom complet (Civilité Nom Prénom)
|
||||
$listAbsent[] = $member->getCiv()." ".$member->getName()." ".$member->getSurname();
|
||||
$countAbsent++;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Affichage de la liste des Membres Présents / Votants
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(0, 5, utf8_decode("Membres ayant le droit de vote (Présents ou Représentés) ($countPresent) :"), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
|
||||
if ($countPresent > 0) {
|
||||
$this->SetX(20);
|
||||
$this->MultiCell(0, 5, utf8_decode(implode(" / ", $listPresent)));
|
||||
} else {
|
||||
$this->SetX(20);
|
||||
$this->Cell(0, 5, utf8_decode("Aucun membre ayant le droit de vote (absence de quorum ou de procuration)."), 0, 1, 'L');
|
||||
}
|
||||
$this->SetX(10);
|
||||
|
||||
$this->Ln(5);
|
||||
|
||||
// 6. Affichage de la liste des Membres Absents
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(0, 5, utf8_decode("Membres absents ou n'ayant pas le droit de vote ($countAbsent) :"), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
|
||||
if ($countAbsent > 0) {
|
||||
$this->SetX(20);
|
||||
$this->MultiCell(0, 5, utf8_decode(implode(" / ", $listAbsent)));
|
||||
} else {
|
||||
$this->SetX(20);
|
||||
$this->Cell(0, 5, utf8_decode("Aucun membre absent."), 0, 1, 'L');
|
||||
}
|
||||
$this->SetX(10);
|
||||
|
||||
$this->Ln(10);
|
||||
|
||||
// 7. Calcul et Affichage du Quorum et de la Majorité
|
||||
|
||||
// Hypothèses standards pour le Quorum et la Majorité (à ajuster selon les statuts)
|
||||
// Quorum : 1/3 des membres totaux pour une 1ère convocation d'AG ordinaire (exemple)
|
||||
$quorumRequired = (int) ceil($totalMembers / 3);
|
||||
|
||||
// Majorité requise pour une résolution (Majorité simple > 50% des votes exprimés)
|
||||
$majorityRequired = (int) floor($totalVoters / 2) + 1;
|
||||
$this->nbVote = $totalVoters;
|
||||
$this->nbVoteVoix = $majorityRequired;
|
||||
// Affichage des résultats
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(0, 5, utf8_decode("Synthèse du Quorum et du Vote :"), 0, 1, 'L');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
|
||||
// Ligne 1: Total des membres
|
||||
$this->Cell(80, 5, utf8_decode("Total des membres de l'association :"), 0, 0, 'L');
|
||||
$this->Cell(20, 5, $totalMembers, 0, 1, 'R');
|
||||
|
||||
// Ligne 2: Nombre de votants
|
||||
$this->Cell(80, 5, utf8_decode("Nombre de votants (Présents ou Représentés) :"), 0, 0, 'L');
|
||||
$this->Cell(20, 5, $totalVoters, 0, 1, 'R');
|
||||
|
||||
// Ligne 3: Majorité
|
||||
$this->Cell(80, 5, utf8_decode("Nombre de voix requis pour la majorité simple :"), 0, 0, 'L');
|
||||
$this->Cell(20, 5, $majorityRequired, 0, 1, 'R');
|
||||
|
||||
$this->Ln(2);
|
||||
|
||||
// Ligne 4: Quorum Statut
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$quorumStatus = ($totalVoters >= $quorumRequired)
|
||||
? utf8_decode("QUORUM ATTEINT (requis : $quorumRequired votants)")
|
||||
: utf8_decode("QUORUM NON ATTEINT (requis : $quorumRequired votants)");
|
||||
|
||||
$this->SetFillColor(220, 220, 220);
|
||||
$this->Cell(0, 7, $quorumStatus, 1, 1, 'C', true);
|
||||
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Ln(5); // Espace avant la section 1.2
|
||||
|
||||
// 8. Appel de la sous-section 1.2
|
||||
$this->writePresidenceAndSecretariat();
|
||||
|
||||
// 9. Appel de la nouvelle sous-section 1.3
|
||||
$this->writeOuvertureSeance();
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,10 @@
|
||||
{% if ag.closed == false or ag.closed == null %}
|
||||
{# Bloc Actions: SUPPRIMER / MODIFIER #}
|
||||
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-600 flex space-x-3">
|
||||
|
||||
<a href="{{ path('admin_ag', {'idGenerate': ag.id}) }}"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-cyan-600 hover:bg-cyan-700 dark:bg-cyan-500 dark:hover:bg-cyan-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
Générer le pdf
|
||||
</a>
|
||||
{# Bouton Modifier #}
|
||||
<a href="{{ path('admin_ag_edit', {'id': ag.id}) }}"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 dark:bg-indigo-500 dark:hover:bg-indigo-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
@@ -71,6 +74,13 @@
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-600 flex space-x-3">
|
||||
<a href="{{ path('admin_ag',{id:ag.id,idSign:true}) }}"
|
||||
class="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-cyan-600 hover:bg-cyan-700 dark:bg-cyan-500 dark:hover:bg-cyan-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
Signature le pdf
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
</div>
|
||||
|
||||
{# ============================================== #}
|
||||
{# BLOC ORDRES DU JOUR (ODJ) #}
|
||||
{# BLOC ORDRES DU JOUR (ODJ) (Non répété ici) #}
|
||||
{# ============================================== #}
|
||||
<div class="mt-10 pt-6 border-t border-gray-200 dark:border-gray-700">
|
||||
<h2 class="text-xl font-bold mb-4 text-gray-900 dark:text-gray-100">Ajouter un Ordre du Jour</h2>
|
||||
@@ -116,20 +116,16 @@
|
||||
|
||||
{# FORMULAIRE D'AJOUT D'ODJ #}
|
||||
{{ form_start(agOrder, {'attr': {'class': 'space-y-4 p-4 border rounded-lg bg-gray-50 dark:bg-gray-700 mb-6'}}) }}
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{{ form_row(agOrder.title) }}
|
||||
<div class="md:col-span-1 hidden md:block"></div>
|
||||
</div>
|
||||
|
||||
{{ form_row(agOrder.description) }}
|
||||
|
||||
<div class="flex justify-end pt-2">
|
||||
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-teal-600 hover:bg-teal-700 dark:bg-teal-500 dark:hover:bg-teal-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-teal-500">
|
||||
Ajouter l'Ordre du Jour
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{ form_end(agOrder) }}
|
||||
|
||||
{# LISTE DES ORDRES DU JOUR ACTUELS #}
|
||||
@@ -137,27 +133,21 @@
|
||||
<h3 class="text-lg font-semibold mt-8 mb-4 border-b pb-2 text-gray-700 dark:text-gray-300">
|
||||
Ordres du Jour ({{ ag.vars.value.orders|length }})
|
||||
</h3>
|
||||
|
||||
<ul class="space-y-3">
|
||||
{% for order in ag.vars.value.orders %}
|
||||
<li class="p-4 rounded-md bg-white dark:bg-gray-800 shadow border border-gray-200 dark:border-gray-700">
|
||||
|
||||
<div class="flex justify-between items-start">
|
||||
<span class="text-gray-900 dark:text-gray-100 font-medium text-base">
|
||||
{{ loop.index }}. {{ order.title }}
|
||||
</span>
|
||||
|
||||
{# Formulaire de Suppression de l'ODJ #}
|
||||
<form method="POST" action="{{ path('admin_ag_edit', {'id': agMain.id, 'orderId': order.id}) }}" onsubmit="return confirm('Êtes-vous sûr de vouloir supprimer cet Ordre du Jour : {{ order.title }} ?');">
|
||||
<form method="POST" action="{{ path('admin_ag_edit', {'id': ag.vars.value.id, 'orderId': order.id}) }}" onsubmit="return confirm('Êtes-vous sûr de vouloir supprimer cet Ordre du Jour : {{ order.title }} ?');">
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
|
||||
<button type="submit"
|
||||
class="inline-flex items-center px-3 py-1 text-xs font-medium rounded-md text-white bg-red-500 hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition duration-150 ml-4">
|
||||
Supprimer
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400 whitespace-pre-line">
|
||||
{{ order.description|default('Aucune description fournie.') }}
|
||||
</p>
|
||||
@@ -169,7 +159,73 @@
|
||||
Aucun Ordre du Jour n'a encore été défini pour cette Assemblée Générale.
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# ============================================== #}
|
||||
{# BLOC GESTION DES VOTES #}
|
||||
{# ============================================== #}
|
||||
<div class="mt-10 pt-6 border-t border-gray-200 dark:border-gray-700">
|
||||
<h2 class="text-xl font-bold mb-4 text-gray-900 dark:text-gray-100">Enregistrer et Gérer les Votes</h2>
|
||||
|
||||
{% form_theme agVote 'form_admin.twig' %}
|
||||
|
||||
{# FORMULAIRE D'ENREGISTREMENT DE VOTE #}
|
||||
{{ form_start(agVote, {'attr': {'class': 'space-y-4 p-4 border rounded-lg bg-gray-50 dark:bg-gray-700 mb-6'}}) }}
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||
{{ form_row(agVote.member) }}
|
||||
{{ form_row(agVote.role) }}
|
||||
{{ form_row(agVote.pour) }}
|
||||
{{ form_row(agVote.contre) }}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-2">
|
||||
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-pink-600 hover:bg-pink-700 dark:bg-pink-500 dark:hover:bg-pink-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-pink-500">
|
||||
Enregistrer le Vote
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{ form_end(agVote) }}
|
||||
|
||||
{# NOUVEAU: LISTE DES VOTES ACTUELS #}
|
||||
{% if ag.vars.value.mainVote is defined and ag.vars.value.mainVote|length > 0 %}
|
||||
<h3 class="text-lg font-semibold mt-8 mb-4 border-b pb-2 text-gray-700 dark:text-gray-300">
|
||||
Historique des Votes ({{ ag.vars.value.mainVote|length }})
|
||||
</h3>
|
||||
|
||||
<ul class="space-y-3">
|
||||
{% for vote in ag.vars.value.mainVote %}
|
||||
<li class="p-4 rounded-md bg-white dark:bg-gray-800 shadow border border-gray-200 dark:border-gray-700 flex justify-between items-center">
|
||||
|
||||
<div>
|
||||
<span class="text-gray-900 dark:text-gray-100 font-medium">
|
||||
{{ vote.member.pseudo }}
|
||||
</span>
|
||||
<span class="text-sm font-normal text-gray-600 dark:text-gray-400 ml-2">
|
||||
({{ vote.role }})
|
||||
</span>
|
||||
<p class="mt-1 text-sm">
|
||||
<span class="text-green-600 font-semibold">Pour: {{ vote.pour|default(0) }}</span> |
|
||||
<span class="text-red-600 font-semibold">Contre: {{ vote.contre|default(0) }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{# Formulaire de Suppression du Vote #}
|
||||
<form method="POST" action="{{ path('admin_ag_edit', {'id': agMain.id, 'agVoteId': vote.id}) }}" onsubmit="return confirm('Êtes-vous sûr de vouloir supprimer ce vote ?');">
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<button type="submit"
|
||||
class="inline-flex items-center px-3 py-1 text-xs font-medium rounded-md text-white bg-red-500 hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition duration-150">
|
||||
Supprimer
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="mt-4 p-4 text-center text-gray-500 dark:text-gray-400 border border-dashed rounded-lg">
|
||||
Aucun vote n'a encore été enregistré pour cette Assemblée Générale.
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -52,6 +52,15 @@
|
||||
<div>
|
||||
{{ form_row(form.email) }}
|
||||
</div>
|
||||
<div>
|
||||
{{ form_row(form.civ) }}
|
||||
</div>
|
||||
<div>
|
||||
{{ form_row(form.name) }}
|
||||
</div>
|
||||
<div>
|
||||
{{ form_row(form.surname) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# --- SECTION IMAGE DE PROFIL / UPLOAD --- #}
|
||||
|
||||
Reference in New Issue
Block a user