From ad10a2ef16593a21a735e61b520a37ed8f89dfde Mon Sep 17 00:00:00 2001 From: afornerot Date: Tue, 16 Sep 2025 20:45:51 +0200 Subject: [PATCH] svg --- src/Controller/BillController.php | 126 +++++++++++++++++++ src/Entity/Accounting.php | 6 +- src/Entity/Bill.php | 152 ++++++++++++++++++++++ src/Entity/BillDetail.php | 126 +++++++++++++++++++ src/Entity/Company.php | 61 ++++----- src/Entity/Operation.php | 6 +- src/Entity/Year.php | 6 +- src/Form/BillDetailType.php | 46 +++++++ src/Form/BillType.php | 64 ++++++++++ src/Form/CompanyType.php | 3 +- src/Repository/BillDetailRepository.php | 18 +++ src/Repository/BillRepository.php | 18 +++ templates/bill/edit.html.twig | 159 ++++++++++++++++++++++++ templates/bill/list.html.twig | 49 ++++++++ templates/bill/print.html.twig | 79 ++++++++++++ 15 files changed, 873 insertions(+), 46 deletions(-) create mode 100644 src/Controller/BillController.php create mode 100644 src/Entity/Bill.php create mode 100644 src/Entity/BillDetail.php create mode 100644 src/Form/BillDetailType.php create mode 100644 src/Form/BillType.php create mode 100644 src/Repository/BillDetailRepository.php create mode 100644 src/Repository/BillRepository.php create mode 100644 templates/bill/edit.html.twig create mode 100644 templates/bill/list.html.twig create mode 100644 templates/bill/print.html.twig diff --git a/src/Controller/BillController.php b/src/Controller/BillController.php new file mode 100644 index 0000000..ad19921 --- /dev/null +++ b/src/Controller/BillController.php @@ -0,0 +1,126 @@ +getSession()->get('company'); + $bills = $billRepository->findBy(['company' => $company]); + + return $this->render('bill/list.html.twig', [ + 'usemenu' => true, + 'usesidebar' => false, + 'title' => 'Devis / Factures', + 'routesubmit' => 'app_user_bill_submit', + 'routeupdate' => 'app_user_bill_update', + 'routeprint' => 'app_user_bill_print', + 'bills' => $bills, + ]); + } + + #[Route('/user/bill/print/{id}', name: 'app_user_bill_print')] + public function print(int $id, Request $request, EntityManagerInterface $em): Response + { + $company = $request->getSession()->get('company'); + $bill = $em->getRepository(Bill::class)->find($id); + if (!$bill || $bill->getCompany() != $company) { + return $this->redirectToRoute('app_user_bill'); + } + + return $this->render('bill/print.html.twig', [ + 'usemenu' => false, + 'usesidebar' => false, + 'bill' => $bill, + ]); + } + + #[Route('/user/bill/submit', name: 'app_user_bill_submit')] + public function submit(Request $request, EntityManagerInterface $em): Response + { + $company = $request->getSession()->get('company'); + $bill = new Bill(); + $bill->setCompany($company); + $bill->setBillDate(new \DateTime()); + + $form = $this->createForm(BillType::class, $bill, ['mode' => 'submit']); + $form->handleRequest($request); + if ($form->isSubmitted() && $form->isValid()) { + $em->persist($bill); + $em->flush(); + + return $this->redirectToRoute('app_user_bill'); + } + + return $this->render('bill/edit.html.twig', [ + 'usemenu' => true, + 'usesidebar' => false, + 'title' => 'Création Compagnie', + 'routecancel' => 'app_user_bill', + 'routedelete' => 'app_user_bill_delete', + 'mode' => 'submit', + 'form' => $form, + ]); + } + + #[Route('/user/bill/update/{id}', name: 'app_user_bill_update')] + public function update(int $id, Request $request, EntityManagerInterface $em): Response + { + $company = $request->getSession()->get('company'); + $bill = $em->getRepository(Bill::class)->find($id); + if (!$bill || $bill->getCompany() != $company) { + return $this->redirectToRoute('app_user_bill'); + } + + $form = $this->createForm(BillType::class, $bill, ['mode' => 'update']); + $form->handleRequest($request); + if ($form->isSubmitted() && $form->isValid()) { + $em->flush(); + + return $this->redirectToRoute('app_user_bill'); + } + + return $this->render('bill/edit.html.twig', [ + 'usemenu' => true, + 'usesidebar' => false, + 'title' => 'Modification = '.$bill->isBill() ? 'Facture' : 'Devis', + 'routecancel' => 'app_user_bill', + 'routedelete' => 'app_user_bill_delete', + 'mode' => 'update', + 'form' => $form, + ]); + } + + #[Route('/user/bill/delete/{id}', name: 'app_user_bill_delete')] + public function delete(int $id, Request $request, EntityManagerInterface $em): Response + { + $company = $request->getSession()->get('company'); + $bill = $em->getRepository(Bill::class)->find($id); + if (!$bill) { + return $this->redirectToRoute('app_user_bill'); + } + + // Tentative de suppression + try { + $em->remove($bill); + $em->flush(); + } catch (\Exception $e) { + $this->addflash('error', $e->getMessage()); + + return $this->redirectToRoute('app_user_bill_update', ['id' => $id]); + } + + return $this->redirectToRoute('app_user_bill'); + } +} diff --git a/src/Entity/Accounting.php b/src/Entity/Accounting.php index a0427ad..f0bf29c 100644 --- a/src/Entity/Accounting.php +++ b/src/Entity/Accounting.php @@ -44,7 +44,7 @@ class Accounting #[ORM\ManyToOne(inversedBy: 'accountings')] #[ORM\JoinColumn(nullable: false)] - private ?Company $company = null; + private Company $company; /** * @var Collection @@ -129,12 +129,12 @@ class Accounting return $this; } - public function getCompany(): ?Company + public function getCompany(): Company { return $this->company; } - public function setCompany(?Company $company): static + public function setCompany(Company $company): static { $this->company = $company; diff --git a/src/Entity/Bill.php b/src/Entity/Bill.php new file mode 100644 index 0000000..2931edb --- /dev/null +++ b/src/Entity/Bill.php @@ -0,0 +1,152 @@ +billDetails = new ArrayCollection(); + } + + public function getId(): int + { + return $this->id; + } + + public function getBillDate(): \DateTimeInterface + { + return $this->billDate; + } + + public function setBillDate(\DateTimeInterface $billDate): static + { + $this->billDate = $billDate; + + return $this; + } + + public function getClientName(): string + { + return $this->clientName; + } + + public function setClientName(string $clientName): static + { + $this->clientName = $clientName; + + return $this; + } + + public function getClientAddress(): string + { + return $this->clientAddress; + } + + public function setClientAddress(string $clientAddress): static + { + $this->clientAddress = $clientAddress; + + return $this; + } + + public function isBill(): ?bool + { + return $this->bill; + } + + public function setBill(bool $bill): static + { + $this->bill = $bill; + + return $this; + } + + public function getCompany(): Company + { + return $this->company; + } + + public function setCompany(Company $company): static + { + $this->company = $company; + + return $this; + } + + public function getTotal(): int + { + $total = 0; + foreach ($this->billDetails as $billDetail) { + $total += $billDetail->getTotal(); + } + + return $total; + } + + public function setTotal(int $total): static + { + $this->total = $total; + + return $this; + } + + /** + * @return Collection + */ + public function getBillDetails(): Collection + { + return $this->billDetails; + } + + public function addBillDetail(BillDetail $billDetail): static + { + if (!$this->billDetails->contains($billDetail)) { + $this->billDetails->add($billDetail); + $billDetail->setBill($this); + } + + return $this; + } + + public function removeBillDetail(BillDetail $billDetail): static + { + if ($this->billDetails->removeElement($billDetail)) { + } + + return $this; + } +} diff --git a/src/Entity/BillDetail.php b/src/Entity/BillDetail.php new file mode 100644 index 0000000..20a60d3 --- /dev/null +++ b/src/Entity/BillDetail.php @@ -0,0 +1,126 @@ +id; + } + + public function getBill(): Bill + { + return $this->bill; + } + + public function setBill(Bill $bill): static + { + $this->bill = $bill; + + return $this; + } + + public function getRowOrder(): int + { + return $this->rowOrder; + } + + public function setRowOrder(int $rowOrder): static + { + $this->rowOrder = $rowOrder; + + return $this; + } + + public function getDescription(): string + { + return $this->description; + } + + public function setDescription(string $description): static + { + $this->description = $description; + + return $this; + } + + public function getPrice(): int + { + return $this->price; + } + + public function setPrice(int $price): static + { + $this->price = $price; + + return $this; + } + + public function getQuantity(): int + { + return $this->quantity; + } + + public function setQuantity(int $quantity): static + { + $this->quantity = $quantity; + + return $this; + } + + public function getDiscount(): int + { + return $this->discount; + } + + public function setDiscount(int $discount): static + { + $this->discount = $discount; + + return $this; + } + + public function getTotal(): int + { + return $this->quantity * $this->price * (100 - $this->discount) / 100; + } + + public function setTotal(int $total): static + { + $this->total = $total; + + return $this; + } +} diff --git a/src/Entity/Company.php b/src/Entity/Company.php index 6e64800..ebb62c0 100644 --- a/src/Entity/Company.php +++ b/src/Entity/Company.php @@ -57,6 +57,12 @@ class Company #[ORM\Column(length: 255, nullable: true)] private ?string $bankbic = null; + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: Bill::class, mappedBy: 'company', orphanRemoval: true)] + private Collection $bills; + /** * @var Collection */ @@ -83,6 +89,7 @@ class Company public function __construct() { + $this->bills = new ArrayCollection(); $this->accountings = new ArrayCollection(); $this->users = new ArrayCollection(); $this->years = new ArrayCollection(); @@ -238,6 +245,24 @@ class Company return $this; } + /** + * @return Collection + */ + public function getBills(): Collection + { + return $this->bills; + } + + public function addBill(Bill $bill): static + { + if (!$this->bills->contains($bill)) { + $this->bills->add($bill); + $bill->setCompany($this); + } + + return $this; + } + /** * @return Collection */ @@ -256,18 +281,6 @@ class Company return $this; } - public function removeAccounting(Accounting $accounting): static - { - if ($this->accountings->removeElement($accounting)) { - // set the owning side to null (unless already changed) - if ($accounting->getCompany() === $this) { - $accounting->setCompany(null); - } - } - - return $this; - } - /** * @return Collection */ @@ -313,18 +326,6 @@ class Company return $this; } - public function removeYear(Year $year): static - { - if ($this->years->removeElement($year)) { - // set the owning side to null (unless already changed) - if ($year->getCompany() === $this) { - $year->setCompany(null); - } - } - - return $this; - } - /** * @return Collection */ @@ -342,16 +343,4 @@ class Company return $this; } - - public function removeOperation(Operation $operation): static - { - if ($this->operations->removeElement($operation)) { - // set the owning side to null (unless already changed) - if ($operation->getCompany() === $this) { - $operation->setCompany(null); - } - } - - return $this; - } } diff --git a/src/Entity/Operation.php b/src/Entity/Operation.php index ebff3eb..bb59d5b 100644 --- a/src/Entity/Operation.php +++ b/src/Entity/Operation.php @@ -33,7 +33,7 @@ class Operation #[ORM\ManyToOne(inversedBy: 'operations')] #[ORM\JoinColumn(nullable: false)] - private ?Company $company = null; + private Company $company; public function getId(): ?int { @@ -100,12 +100,12 @@ class Operation return $this; } - public function getCompany(): ?Company + public function getCompany(): Company { return $this->company; } - public function setCompany(?Company $company): static + public function setCompany(Company $company): static { $this->company = $company; diff --git a/src/Entity/Year.php b/src/Entity/Year.php index f09d4f9..83e4e14 100644 --- a/src/Entity/Year.php +++ b/src/Entity/Year.php @@ -22,7 +22,7 @@ class Year #[ORM\ManyToOne(inversedBy: 'years')] #[ORM\JoinColumn(nullable: false)] - private ?Company $company = null; + private Company $company; public function getId(): ?int { @@ -53,12 +53,12 @@ class Year return $this; } - public function getCompany(): ?Company + public function getCompany(): Company { return $this->company; } - public function setCompany(?Company $company): static + public function setCompany(Company $company): static { $this->company = $company; diff --git a/src/Form/BillDetailType.php b/src/Form/BillDetailType.php new file mode 100644 index 0000000..2acbf1c --- /dev/null +++ b/src/Form/BillDetailType.php @@ -0,0 +1,46 @@ +add('rowOrder', IntegerType::class, [ + 'label' => 'Ordre', + 'attr' => ['min' => 0], + ]) + ->add('description', TextareaType::class, [ + 'label' => 'Description', + ]) + ->add('price', IntegerType::class, [ + 'label' => 'Prix unitaire', + ]) + ->add('quantity', IntegerType::class, [ + 'label' => 'Quantité', + ]) + ->add('discount', IntegerType::class, [ + 'label' => 'Remise (%)', + ]) + ->add('total', IntegerType::class, [ + 'label' => 'Total', + 'disabled' => true, + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => BillDetail::class, + ]); + } +} diff --git a/src/Form/BillType.php b/src/Form/BillType.php new file mode 100644 index 0000000..2e50348 --- /dev/null +++ b/src/Form/BillType.php @@ -0,0 +1,64 @@ +add('submit', SubmitType::class, [ + 'label' => 'Valider', + 'attr' => ['class' => 'btn btn-success no-print'], + ]) + + ->add('billDate', DateType::class, [ + 'widget' => 'single_text', + 'label' => 'Date', + ]) + ->add('clientName', TextType::class, [ + 'label' => 'Nom du client', + ]) + ->add('clientAddress', TextareaType::class, [ + 'label' => 'Adresse du client', + ]) + ->add('bill', CheckboxType::class, [ + 'label' => 'Est-ce une facture ?', + 'required' => false, + ]) + ->add('billDetails', CollectionType::class, [ + 'entry_type' => BillDetailType::class, + 'entry_options' => ['label' => false], + 'allow_add' => true, + 'allow_delete' => true, + 'by_reference' => false, + 'prototype' => true, + 'label' => false, + ]) + ->add('total', IntegerType::class, [ + 'label' => 'Total', + 'disabled' => true, + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => Bill::class, + 'mode' => 'string', + ]); + } +} diff --git a/src/Form/CompanyType.php b/src/Form/CompanyType.php index 5a3bf44..db7291a 100644 --- a/src/Form/CompanyType.php +++ b/src/Form/CompanyType.php @@ -9,6 +9,7 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\EmailType; use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -34,7 +35,7 @@ class CompanyType extends AbstractType ->add('logo', HiddenType::class) - ->add('adress', TextType::class, [ + ->add('adress', TextareaType::class, [ 'label' => 'Adresse', 'required' => false, ]) diff --git a/src/Repository/BillDetailRepository.php b/src/Repository/BillDetailRepository.php new file mode 100644 index 0000000..2aded10 --- /dev/null +++ b/src/Repository/BillDetailRepository.php @@ -0,0 +1,18 @@ + + */ +class BillDetailRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, BillDetail::class); + } +} diff --git a/src/Repository/BillRepository.php b/src/Repository/BillRepository.php new file mode 100644 index 0000000..94c1db8 --- /dev/null +++ b/src/Repository/BillRepository.php @@ -0,0 +1,18 @@ + + */ +class BillRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Bill::class); + } +} diff --git a/templates/bill/edit.html.twig b/templates/bill/edit.html.twig new file mode 100644 index 0000000..91d1c39 --- /dev/null +++ b/templates/bill/edit.html.twig @@ -0,0 +1,159 @@ +{% extends 'base.html.twig' %} + +{% block title %} = {{title}}{% endblock %} + +{% block localstyle %} + + +{% endblock %} + +{% block body %} +

{{title}}

+ + + {{ form_start(form) }} + {{ form_widget(form.submit) }} + Annuler + {%if mode=="update" %}Supprimer{%endif%} + + {% include('include/error.html.twig') %} + +
+
+
+
Information
+
+ {{ form_row(form.billDate) }} + {{ form_row(form.clientName) }} + {{ form_row(form.clientAddress) }} + {{ form_row(form.bill) }} + {{ form_row(form.total) }} + +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + '> + + {% for detailForm in form.billDetails %} + + + + + + + + + + {% endfor %} + +
DescriptionQtePURemTotal
{{ form_widget(form.billDetails.vars.prototype.description)|e('html_attr') }}{{ form_widget(form.billDetails.vars.prototype.quantity)|e('html_attr') }}{{ form_widget(form.billDetails.vars.prototype.price)|e('html_attr') }}{{ form_widget(form.billDetails.vars.prototype.discount)|e('html_attr') }}{{ form_widget(form.billDetails.vars.prototype.total)|e('html_attr') }}
{{ form_widget(detailForm.rowOrder) }}{{ form_widget(detailForm.description) }}{{ form_widget(detailForm.quantity) }}{{ form_widget(detailForm.price) }}{{ form_widget(detailForm.discount) }}{{ form_widget(detailForm.total) }} + +
+ + +
+
+ + {{ form_rest(form) }} + + +
+ {{ form_rest(form) }} +
+ {{ form_end(form, { render_rest: false }) }} + +{% endblock %} + +{% block localscript %} + + + +{% endblock %} diff --git a/templates/bill/list.html.twig b/templates/bill/list.html.twig new file mode 100644 index 0000000..6e7c953 --- /dev/null +++ b/templates/bill/list.html.twig @@ -0,0 +1,49 @@ +{% extends 'base.html.twig' %} + +{% block title %} = {{title}}{% endblock %} + +{% block body %} +

{{title}}

+ Ajouter + +
+ + + + + + + + + + + + {% for bill in bills %} + + + + + + + + {% endfor %} + +
ActionDateTypeClientMontant
+ + + {{ bill.billDate|date("Y-m-d") }}{{ bill.isBill?'Facture':'Devis' }}{{ bill.clientName }}{{ bill.total }}
+
+{% endblock %} + +{% block localscript %} + +{% endblock %} diff --git a/templates/bill/print.html.twig b/templates/bill/print.html.twig new file mode 100644 index 0000000..5eaf60f --- /dev/null +++ b/templates/bill/print.html.twig @@ -0,0 +1,79 @@ +{% extends 'base.html.twig' %} + +{% block localstyle %} + + +{% endblock %} + +{% block body %} +
+
+

{{bill.isBill?'Facture FA':'Devis DE' }}{{bill.billDate|date("Ymd")}}

+
+
+

{{bill.company.title}}

+ {{bill.company.adress|nl2br}}

+ {{bill.company.email}} +
+ +
+

{{bill.clientName}}

+ {{bill.clientAddress|nl2br}} +
+ + + + + + + + + + + + + {% for billDetail in bill.billDetails %} + + + + + + + + {% endfor %} + + + + + + + +
DescriptionQtePURemTotal
{{ billDetail.description }}{{ billDetail.quantity }}{{ billDetail.price }}{{ billDetail.discount }}{{ billDetail.total }}
Total{{bill.total}}
+ +{% endblock %} \ No newline at end of file