diff --git a/migrations/Version20260206170000.php b/migrations/Version20260206170000.php new file mode 100644 index 0000000..c2d4a49 --- /dev/null +++ b/migrations/Version20260206170000.php @@ -0,0 +1,31 @@ +addSql("ALTER TABLE contrats ADD reservation_state VARCHAR(50) DEFAULT 'pending' NOT NULL"); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE contrats DROP reservation_state'); + } +} diff --git a/migrations/Version20260206180000.php b/migrations/Version20260206180000.php new file mode 100644 index 0000000..44f1355 --- /dev/null +++ b/migrations/Version20260206180000.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE devis ADD prestataire_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE devis ADD CONSTRAINT FK_8B27C52BBE30DA2F7 FOREIGN KEY (prestataire_id) REFERENCES prestaire (id)'); + $this->addSql('CREATE INDEX IDX_8B27C52BBE30DA2F7 ON devis (prestataire_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE devis DROP FOREIGN KEY FK_8B27C52BBE30DA2F7'); + $this->addSql('DROP INDEX IDX_8B27C52BBE30DA2F7 ON devis'); + $this->addSql('ALTER TABLE devis DROP prestataire_id'); + } +} diff --git a/src/Controller/Dashboard/ContratsController.php b/src/Controller/Dashboard/ContratsController.php index 5a3f493..b80cda0 100644 --- a/src/Controller/Dashboard/ContratsController.php +++ b/src/Controller/Dashboard/ContratsController.php @@ -75,7 +75,8 @@ class ContratsController extends AbstractController // 2. Paiement Manuel (Accompte / Solde / Caution) if ($type = $request->query->get('type')) { - return $this->handleManualPayment($contrat, $type); + $method = $request->query->get('method', 'Autre'); + return $this->handleManualPayment($contrat, $type, $method); } // 3. Calculs financiers pour l'affichage @@ -231,7 +232,7 @@ class ContratsController extends AbstractController return $this->redirectToRoute('app_crm_contrats_view', ['id' => $contrat->getId()]); } - private function handleManualPayment(Contrats $contrat, string $type): RedirectResponse + private function handleManualPayment(Contrats $contrat, string $type, string $paymentMethod = 'Autre'): RedirectResponse { // Calculs basiques pour le montant (à affiner selon vos besoins réels si différent de la vue) $totalHt = 0; @@ -274,6 +275,7 @@ class ContratsController extends AbstractController ->setValidateAt(new \DateTimeImmutable()) ->setUpdateAt(new \DateTimeImmutable()) ->setCard(['type' => 'manuel']) + ->setTypePayment($paymentMethod) ->setPaymentId("MANUAL-" . uniqid()); // Génération PDF Reçu diff --git a/src/Controller/Dashboard/DevisController.php b/src/Controller/Dashboard/DevisController.php index a506251..46f2e31 100644 --- a/src/Controller/Dashboard/DevisController.php +++ b/src/Controller/Dashboard/DevisController.php @@ -14,6 +14,7 @@ use App\Repository\DevisLineRepository; use App\Repository\DevisOptionsRepository; use App\Repository\DevisRepository; use App\Repository\OptionsRepository; +use App\Repository\PrestaireRepository; use App\Repository\ProductRepository; use App\Service\Pdf\DevisPdfService; use App\Service\Signature\Client; @@ -41,7 +42,8 @@ class DevisController extends AbstractController private readonly DevisRepository $devisRepository, private readonly ProductRepository $productRepository, private readonly CustomerAddressRepository $customerAddressRepository, - private readonly CustomerRepository $customerRepository + private readonly CustomerRepository $customerRepository, + private readonly PrestaireRepository $prestaireRepository ) { } @@ -107,7 +109,8 @@ class DevisController extends AbstractController 'optionsList' => $optionsRepository->findAll(), 'shipAddress' => [], 'billAddress' => [], - 'devis' => $devis + 'devis' => $devis, + 'prestataires' => $this->prestaireRepository->findAll(), ]); } @@ -185,6 +188,7 @@ class DevisController extends AbstractController 'optionsList' => $optionsRepository->findAll(), 'shipAddress' => $shipAddress, 'billAddress' => $billAddress, + 'prestataires' => $this->prestaireRepository->findAll(), ]); } @@ -255,6 +259,9 @@ class DevisController extends AbstractController if (!empty($devisData['bill_address'])) $devis->setBillAddress($this->customerAddressRepository->find($devisData['bill_address'])); if (!empty($devisData['ship_address'])) $devis->setAddressShip($this->customerAddressRepository->find($devisData['ship_address'])); if (!empty($formData['customer'])) $devis->setCustomer($this->customerRepository->find($formData['customer'])); + + if (!empty($devisData['paymentMethod'])) $devis->setPaymentMethod($devisData['paymentMethod']); + if (!empty($devisData['prestataire'])) $devis->setPrestataire($this->prestaireRepository->find($devisData['prestataire'])); // Calcul durée $day = 1; diff --git a/src/Controller/SignatureController.php b/src/Controller/SignatureController.php index 52c0414..f073d7f 100644 --- a/src/Controller/SignatureController.php +++ b/src/Controller/SignatureController.php @@ -65,6 +65,11 @@ class SignatureController extends AbstractController if ($submission['status'] === "completed") { $contrats->setIsSigned(true); + // Update Reservation State if Acompte is already paid + if ($contrats->isAccompte()) { + $contrats->setReservationState('ready'); + } + $auditUrl = $submission['audit_log_url']; $signedDocUrl = $submission['documents'][0]['url']; diff --git a/src/Controller/Webhooks.php b/src/Controller/Webhooks.php index 326e538..35eafd7 100644 --- a/src/Controller/Webhooks.php +++ b/src/Controller/Webhooks.php @@ -71,6 +71,13 @@ class Webhooks extends AbstractController $pl->setPaymentSignedFile(new UploadedFile($tmpSigned, "confirmed-certificate-" . $pl->getId() . ".pdf", "application/pdf", null, true)); $pl->setUpdateAt(new \DateTimeImmutable('now')); $entityManager->persist($pl); + + // Update Reservation State if Accompte and Signed + if ($pl->getType() === 'accompte' && $contrat->isSigned()) { + $contrat->setReservationState('ready'); + $entityManager->persist($contrat); + } + $entityManager->flush(); // --- ENVOI DES EMAILS --- diff --git a/src/Entity/Contrats.php b/src/Entity/Contrats.php index aec84da..f7d9664 100644 --- a/src/Entity/Contrats.php +++ b/src/Entity/Contrats.php @@ -143,6 +143,9 @@ class Contrats #[ORM\Column(length: 255, nullable: true)] private ?string $cautionState = null; + #[ORM\Column(length: 50, options: ['default' => 'pending'])] + private string $reservationState = 'pending'; + #[ORM\OneToOne(mappedBy: 'contrat', cascade: ['persist', 'remove'])] private ?EtatLieux $etatLieux = null; @@ -162,6 +165,18 @@ class Contrats return $this->id; } + public function getReservationState(): string + { + return $this->reservationState; + } + + public function setReservationState(string $reservationState): static + { + $this->reservationState = $reservationState; + + return $this; + } + public function getCustomer(): ?Customer { return $this->customer; diff --git a/src/Entity/ContratsPayments.php b/src/Entity/ContratsPayments.php index 24b2268..e808065 100644 --- a/src/Entity/ContratsPayments.php +++ b/src/Entity/ContratsPayments.php @@ -24,6 +24,9 @@ class ContratsPayments #[ORM\Column(length: 255)] private ?string $type = null; + #[ORM\Column(length: 255, nullable: true)] + private ?string $typePayment = null; + #[ORM\Column(length: 255)] private ?string $paymentId = null; @@ -89,6 +92,18 @@ class ContratsPayments return $this; } + public function getTypePayment(): ?string + { + return $this->typePayment; + } + + public function setTypePayment(?string $typePayment): static + { + $this->typePayment = $typePayment; + + return $this; + } + public function getPaymentId(): ?string { return $this->paymentId; diff --git a/src/Entity/Devis.php b/src/Entity/Devis.php index 7d930bd..17a237c 100644 --- a/src/Entity/Devis.php +++ b/src/Entity/Devis.php @@ -101,6 +101,8 @@ class Devis #[ORM\Column(options: ['default' => false])] private ?bool $isNotAddCaution = false; + #[ORM\ManyToOne(inversedBy: 'devis')] + private ?Prestaire $prestataire = null; /** * @var Collection @@ -174,6 +176,18 @@ class Devis return $this; } + public function getPrestataire(): ?Prestaire + { + return $this->prestataire; + } + + public function setPrestataire(?Prestaire $prestataire): static + { + $this->prestataire = $prestataire; + + return $this; + } + public function getId(): ?int { return $this->id; diff --git a/src/Entity/Prestaire.php b/src/Entity/Prestaire.php index 85807e2..ddec406 100644 --- a/src/Entity/Prestaire.php +++ b/src/Entity/Prestaire.php @@ -37,6 +37,12 @@ class Prestaire implements UserInterface, PasswordAuthenticatedUserInterface #[ORM\OneToMany(targetEntity: OrderSession::class, mappedBy: 'prestataire')] private Collection $orderSessions; + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: Devis::class, mappedBy: 'prestataire')] + private Collection $devis; + #[ORM\Column(length: 255)] private ?string $email = null; @@ -63,6 +69,7 @@ class Prestaire implements UserInterface, PasswordAuthenticatedUserInterface $this->etatLieuxes = new ArrayCollection(); $this->contrats = new ArrayCollection(); $this->orderSessions = new ArrayCollection(); + $this->devis = new ArrayCollection(); } public function getId(): ?int @@ -70,6 +77,36 @@ class Prestaire implements UserInterface, PasswordAuthenticatedUserInterface return $this->id; } + /** + * @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->setPrestataire($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->getPrestataire() === $this) { + $devi->setPrestataire(null); + } + } + + return $this; + } + /** * @return Collection */ diff --git a/templates/dashboard/contrats/view.twig b/templates/dashboard/contrats/view.twig index 6b6eafd..81b105c 100644 --- a/templates/dashboard/contrats/view.twig +++ b/templates/dashboard/contrats/view.twig @@ -141,10 +141,15 @@
Acompte {% if not acompteOk %} - - Marquer réglé - +
+ {% for method in ['Carte Bancaire', 'Chèque', 'Espèces', 'Virement'] %} + + {{ method|slice(0, 4) }}. + + {% endfor %} +
{% else %}

Encaissé

{% endif %} @@ -200,10 +205,15 @@ {# 3. SINON, C'EST QU'ELLE N'EST PAS ENCORE ENREGISTRÉE #} {% else %} - - Marquer reçue - +
+ {% for method in ['Carte Bancaire', 'Chèque', 'Espèces', 'Virement'] %} + + {{ method|slice(0, 4) }}. + + {% endfor %} +
{% endif %}
@@ -222,10 +232,15 @@
Solde Final {% if not soldeOk %} - - Régler le solde - +
+ {% for method in ['Carte Bancaire', 'Chèque', 'Espèces', 'Virement'] %} + + {{ method|slice(0, 4) }}. + + {% endfor %} +
{% else %}

Totalité payée

{% endif %} diff --git a/templates/dashboard/devis/add.twig b/templates/dashboard/devis/add.twig index a7dd3c5..61234b3 100644 --- a/templates/dashboard/devis/add.twig +++ b/templates/dashboard/devis/add.twig @@ -107,6 +107,36 @@
+ {# --- BLOC 04.5 : LIVRAISON & PAIEMENT --- #} +
+

+ 05 + Logistique & Paiement +

+
+
+ + +
+
+ + +
+
+
+ {# SECTION REPEATER #}
diff --git a/templates/reservation/contrat/view.twig b/templates/reservation/contrat/view.twig index de18755..f920be8 100644 --- a/templates/reservation/contrat/view.twig +++ b/templates/reservation/contrat/view.twig @@ -178,6 +178,22 @@

{{ totalTTC|number_format(2, ',', ' ') }}€

{% endif %} + + {# INFO CAUTION #} + {% if totalCaution > 0 %} +
+
+ +
+
+

Information Caution

+

+ Un chèque de caution vous sera demandé le jour de livraison d'un montant de {{ totalCaution|number_format(2, ',', ' ') }}€. +

+
+
+ {% endif %} + {# SOLDE FINAL - Bloc Principal avec saisie du montant #}
@@ -240,8 +256,9 @@
{% endif %} - {# 2. ACOMPTE #} - {% if not contratPaymentPay(contrat, 'accompte') %} + {% if not (contrat.devis and 'Chorus' in contrat.devis.paymentMethod) %} + {# 2. ACOMPTE #} + {% if not contratPaymentPay(contrat, 'accompte') %}
@@ -284,6 +301,64 @@
{% endif %} + {# 3. SOLDE #} + {% if solde > 0 %} +
+
+
+

Solde restant

+
+
+

{{ solde|number_format(2, ',', ' ') }}€

+ + {% if contrat.signed and contratPaymentPay(contrat, 'accompte') %} +
+ +
+ +
+ +
+
+
+ +
+ {% else %} +
+ + {% if not contrat.signed %}Attente signature{% else %}Attente acompte{% endif %} + +
+ {% endif %} +
+
+ {% else %} +
+
+
+

Solde Réglé

+
+
+ {% for payment in paymentCtaList %} +
+
+

Paiement

+

{{ payment.amount|number_format(2, ',', ' ') }}€

+
+

Le {{ payment.validateAt|date('d/m/Y') }}

+
+ {% endfor %} +
+ Dossier à jour +
+
+
+ {% endif %} + {% endif %} +
{# ... (Garder tout le code précédent inchangé jusqu'à la fin de la grille 3 colonnes) ... #}