diff --git a/migrations/Version20260409061033.php b/migrations/Version20260409061033.php new file mode 100644 index 0000000..07c9aa3 --- /dev/null +++ b/migrations/Version20260409061033.php @@ -0,0 +1,34 @@ +addSql('CREATE TABLE contrat (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, email VARCHAR(255) NOT NULL, raison_sociale VARCHAR(255) NOT NULL, type VARCHAR(50) NOT NULL, state VARCHAR(20) DEFAULT \'draft\' NOT NULL, submission_id VARCHAR(255) DEFAULT NULL, submitter_company_id INT DEFAULT NULL, submitter_customer_id INT DEFAULT NULL, pdf_unsigned VARCHAR(255) DEFAULT NULL, pdf_signed VARCHAR(255) DEFAULT NULL, pdf_audit VARCHAR(255) DEFAULT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, signed_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, updated_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, customer_id INT DEFAULT NULL, PRIMARY KEY (id))'); + $this->addSql('CREATE INDEX IDX_603499939395C3F3 ON contrat (customer_id)'); + $this->addSql('ALTER TABLE contrat ADD CONSTRAINT FK_603499939395C3F3 FOREIGN KEY (customer_id) REFERENCES customer (id) ON DELETE SET NULL NOT DEFERRABLE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE contrat DROP CONSTRAINT FK_603499939395C3F3'); + $this->addSql('DROP TABLE contrat'); + } +} diff --git a/src/Controller/Admin/ContratController.php b/src/Controller/Admin/ContratController.php index aa9d183..4ece6b6 100644 --- a/src/Controller/Admin/ContratController.php +++ b/src/Controller/Admin/ContratController.php @@ -3,12 +3,20 @@ namespace App\Controller\Admin; use App\Entity\Contrat; +use App\Service\DocuSealService; +use App\Service\MailerService; +use App\Service\Pdf\ContratMigrationSiteconseilPdf; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Http\Attribute\IsGranted; +use Twig\Environment; #[Route('/admin/contrats', name: 'app_admin_contrats_')] #[IsGranted('ROLE_EMPLOYE')] @@ -30,7 +38,7 @@ class ContratController extends AbstractController } #[Route('/create', name: 'create', methods: ['POST'])] - public function create(Request $request): Response + public function create(Request $request, KernelInterface $kernel): Response { $email = trim($request->request->getString('email')); $raisonSociale = trim($request->request->getString('raisonSociale')); @@ -52,7 +60,10 @@ class ContratController extends AbstractController $this->em->persist($contrat); $this->em->flush(); - $this->addFlash('success', 'Contrat '.$contrat->getReference().' cree.'); + // Generer le PDF selon le type + $this->generateContratPdf($contrat, $kernel); + + $this->addFlash('success', 'Contrat '.$contrat->getReference().' cree avec PDF.'); return $this->redirectToRoute('app_admin_contrats_show', ['id' => $contrat->getId()]); } @@ -70,6 +81,124 @@ class ContratController extends AbstractController ]); } + #[Route('/{id}/generate-pdf', name: 'generate_pdf', requirements: ['id' => '\d+'], methods: ['POST'])] + public function generatePdf(int $id, KernelInterface $kernel): Response + { + $contrat = $this->em->getRepository(Contrat::class)->find($id); + if (null === $contrat) { + throw $this->createNotFoundException('Contrat introuvable'); + } + + $this->generateContratPdf($contrat, $kernel); + $this->addFlash('success', 'PDF regenere.'); + + return $this->redirectToRoute('app_admin_contrats_show', ['id' => $id]); + } + + /** + * Envoie le contrat pour signature DocuSeal (2 parties : Company auto-signe + Client signe). + */ + #[Route('/{id}/send-signature', name: 'send_signature', requirements: ['id' => '\d+'], methods: ['POST'])] + public function sendSignature( + int $id, + DocuSealService $docuSeal, + MailerService $mailer, + Environment $twig, + UrlGeneratorInterface $urlGenerator, + #[Autowire(env: 'DOCUSEAL_URL')] string $docuSealUrl = '', + #[Autowire('%kernel.project_dir%')] string $projectDir = '', + ): Response { + $contrat = $this->em->getRepository(Contrat::class)->find($id); + if (null === $contrat) { + throw $this->createNotFoundException('Contrat introuvable'); + } + + if (null === $contrat->getPdfUnsigned()) { + $this->addFlash('error', 'Le PDF doit etre genere avant l\'envoi.'); + + return $this->redirectToRoute('app_admin_contrats_show', ['id' => $id]); + } + + $pdfPath = $projectDir.'/public/uploads/contrats/'.$contrat->getPdfUnsigned(); + if (!file_exists($pdfPath)) { + $this->addFlash('error', 'Fichier PDF introuvable.'); + + return $this->redirectToRoute('app_admin_contrats_show', ['id' => $id]); + } + + $signedRedirectUrl = $urlGenerator->generate('app_admin_contrats_show', [ + 'id' => $contrat->getId(), + ], UrlGeneratorInterface::ABSOLUTE_URL); + + try { + $pdfBase64 = base64_encode(file_get_contents($pdfPath)); + + $result = $docuSeal->getApi()->createSubmissionFromPdf([ + 'name' => 'Contrat '.$contrat->getReference().' - '.$contrat->getRaisonSociale(), + 'send_email' => false, + 'flatten' => true, + 'documents' => [[ + 'name' => 'contrat-'.$contrat->getReference().'.pdf', + 'file' => 'data:application/pdf;base64,'.$pdfBase64, + ]], + 'submitters' => [ + [ + 'email' => 'contact@e-cosplay.fr', + 'name' => 'Association E-Cosplay', + 'role' => 'Company', + 'completed' => true, + 'send_email' => false, + 'values' => ['Sign' => $docuSeal->getLogoBase64()], + 'metadata' => ['doc_type' => 'contrat', 'contrat_id' => $contrat->getId()], + ], + [ + 'email' => $contrat->getEmail(), + 'name' => $contrat->getRaisonSociale(), + 'role' => 'First Party', + 'send_email' => false, + 'completed_redirect_url' => $signedRedirectUrl, + 'metadata' => ['doc_type' => 'contrat', 'contrat_id' => $contrat->getId()], + ], + ], + ]); + + $submitterId = $result['submitters'][1]['id'] ?? ($result[1]['id'] ?? null); + $companySubmitterId = $result['submitters'][0]['id'] ?? ($result[0]['id'] ?? null); + + if (null !== $submitterId) { + $contrat->setSubmissionId((string) $submitterId); + $contrat->setSubmitterCompanyId(null !== $companySubmitterId ? (int) $companySubmitterId : null); + $contrat->setSubmitterCustomerId((int) $submitterId); + $contrat->setState(Contrat::STATE_SEND); + $this->em->flush(); + + // Envoyer email au client avec le lien + $slug = $docuSeal->getSubmitterSlug($submitterId); + $signUrl = null !== $slug ? rtrim($docuSealUrl, '/').'/s/'.$slug : null; + + $mailer->sendEmail( + $contrat->getEmail(), + 'Contrat a signer - '.$contrat->getTypeLabel().' - '.$contrat->getReference(), + $twig->render('emails/contrat_signature.html.twig', [ + 'contrat' => $contrat, + 'signUrl' => $signUrl, + ]), + null, + null, + false, + ); + + $this->addFlash('success', 'Contrat envoye pour signature a '.$contrat->getEmail().'.'); + } else { + $this->addFlash('error', 'Erreur DocuSeal : aucun submitter retourne.'); + } + } catch (\Throwable $e) { + $this->addFlash('error', 'Erreur DocuSeal : '.$e->getMessage()); + } + + return $this->redirectToRoute('app_admin_contrats_show', ['id' => $id]); + } + #[Route('/{id}/cancel', name: 'cancel', requirements: ['id' => '\d+'], methods: ['POST'])] public function cancel(int $id): Response { @@ -85,4 +214,29 @@ class ContratController extends AbstractController return $this->redirectToRoute('app_admin_contrats_index'); } + + private function generateContratPdf(Contrat $contrat, KernelInterface $kernel): void + { + $pdf = match ($contrat->getType()) { + Contrat::TYPE_MIGRATION_SITECONSEIL => new ContratMigrationSiteconseilPdf($kernel, $contrat), + default => throw new \RuntimeException('Type de contrat non supporte : '.$contrat->getType()), + }; + + $pdf->generate(); + + $tmpPath = tempnam(sys_get_temp_dir(), 'contrat_').'.pdf'; + $pdf->Output('F', $tmpPath); + + $contrat->setPdfUnsignedFile(new UploadedFile( + $tmpPath, + 'contrat-'.$contrat->getReference().'.pdf', + 'application/pdf', + null, + true, + )); + $contrat->setUpdatedAt(new \DateTimeImmutable()); + $this->em->flush(); + + @unlink($tmpPath); + } } diff --git a/src/Controller/MoveFromSiteconseilController.php b/src/Controller/MoveFromSiteconseilController.php new file mode 100644 index 0000000..591e424 --- /dev/null +++ b/src/Controller/MoveFromSiteconseilController.php @@ -0,0 +1,16 @@ +render('move/siteconseil.html.twig'); + } +} diff --git a/src/Controller/WebhookDocuSealController.php b/src/Controller/WebhookDocuSealController.php index bea49c9..54ba04f 100644 --- a/src/Controller/WebhookDocuSealController.php +++ b/src/Controller/WebhookDocuSealController.php @@ -64,6 +64,10 @@ class WebhookDocuSealController extends AbstractController } // Dispatch par type de document + if ('contrat' === $docType) { + return $this->handleContratEvent($eventType, $data, $metadata, $mailer, $twig, $em, $projectDir); + } + if ('attestation_custom' === $docType) { return $this->handleAttestationCustomEvent($eventType, $data, $metadata, $em, $projectDir); } @@ -183,6 +187,119 @@ class WebhookDocuSealController extends AbstractController } } + /** + * @param array $data + * @param array $metadata + */ + private function handleContratEvent( + string $eventType, + array $data, + array $metadata, + MailerService $mailer, + Environment $twig, + EntityManagerInterface $em, + string $projectDir, + ): JsonResponse { + if ('form.completed' !== $eventType) { + return new JsonResponse(['status' => 'ok', 'event' => $eventType, 'doc_type' => 'contrat']); + } + + $contratId = $metadata['contrat_id'] ?? null; + if (null === $contratId) { + return new JsonResponse(['status' => 'ignored', 'reason' => 'contrat_id missing']); + } + + $contrat = $em->getRepository(\App\Entity\Contrat::class)->find((int) $contratId); + if (null === $contrat) { + return new JsonResponse(['status' => 'ignored', 'reason' => 'contrat not found']); + } + + // Telecharger les PDFs signes + $tmpFiles = []; + + $documents = $data['documents'] ?? []; + $pdfUrl = $documents[0]['url'] ?? null; + if (null !== $pdfUrl) { + $content = @file_get_contents($pdfUrl); + if (false !== $content && str_starts_with($content, '%PDF')) { + $tmp = tempnam(sys_get_temp_dir(), 'ctr_signed_').'.pdf'; + file_put_contents($tmp, $content); + $contrat->setPdfSignedFile(new \Symfony\Component\HttpFoundation\File\UploadedFile($tmp, 'contrat-signe-'.$contrat->getReference().'.pdf', 'application/pdf', null, true)); + $tmpFiles[] = $tmp; + } + } + + $auditUrl = $data['audit_log_url'] ?? null; + if (null !== $auditUrl) { + $auditContent = @file_get_contents($auditUrl); + if (false !== $auditContent) { + $tmp = tempnam(sys_get_temp_dir(), 'ctr_audit_').'.pdf'; + file_put_contents($tmp, $auditContent); + $contrat->setPdfAuditFile(new \Symfony\Component\HttpFoundation\File\UploadedFile($tmp, 'audit-'.$contrat->getReference().'.pdf', 'application/pdf', null, true)); + $tmpFiles[] = $tmp; + } + } + + $contrat->setState(\App\Entity\Contrat::STATE_SIGNED); + $contrat->setSignedAt(new \DateTimeImmutable()); + $em->flush(); + + foreach ($tmpFiles as $f) { + @unlink($f); + } + + // Pieces jointes + $attachments = []; + if (null !== $contrat->getPdfSigned()) { + $signedPath = $projectDir.'/public/uploads/contrats/signed/'.$contrat->getPdfSigned(); + if (file_exists($signedPath)) { + $attachments[] = ['path' => $signedPath, 'name' => 'contrat-signe-'.$contrat->getReference().'.pdf']; + } + } + if (null !== $contrat->getPdfAudit()) { + $auditPath = $projectDir.'/public/uploads/contrats/audit/'.$contrat->getPdfAudit(); + if (file_exists($auditPath)) { + $attachments[] = ['path' => $auditPath, 'name' => 'audit-'.$contrat->getReference().'.pdf']; + } + } + + // Mail client + try { + $mailer->sendEmail( + $contrat->getEmail(), + 'Contrat '.$contrat->getReference().' signe - '.$contrat->getTypeLabel(), + $twig->render('emails/contrat_signed.html.twig', [ + 'contrat' => $contrat, + ]), + null, + null, + false, + $attachments, + ); + } catch (\Throwable) { + // silencieux + } + + // Mail admin + try { + $mailer->sendEmail( + self::MONITOR_EMAIL, + 'Contrat '.$contrat->getReference().' signe par '.$contrat->getRaisonSociale(), + $twig->render('emails/contrat_signed.html.twig', [ + 'contrat' => $contrat, + ]), + null, + null, + false, + $attachments, + ); + } catch (\Throwable) { + // silencieux + } + + return new JsonResponse(['status' => 'ok', 'event' => 'contrat_signed', 'reference' => $contrat->getReference()]); + } + /** * @param array $data * @param array $metadata diff --git a/src/Service/Pdf/ContratMigrationSiteconseilPdf.php b/src/Service/Pdf/ContratMigrationSiteconseilPdf.php new file mode 100644 index 0000000..011c42e --- /dev/null +++ b/src/Service/Pdf/ContratMigrationSiteconseilPdf.php @@ -0,0 +1,291 @@ +SetTitle($this->enc('Contrat Migration '.$this->contrat->getReference().' - '.$this->contrat->getRaisonSociale())); + $this->SetAuthor($this->enc('Association E-Cosplay')); + } + + public function generate(): void + { + $this->AliasNbPages(); + $this->AddPage(); + + $this->writeHeader(); + $this->writePreambule(); + $this->writeArticles(); + $this->writeSignatures(); + } + + /** @codeCoverageIgnore */ + public function Header(): void + { + } + + /** @codeCoverageIgnore */ + public function Footer(): void + { + $this->SetY(-22); + $this->SetDrawColor(253, 140, 4); + $this->Line(15, $this->GetY(), 195, $this->GetY()); + $this->Ln(3); + $this->SetFont('Arial', '', 7); + $this->SetTextColor(0, 0, 0); + $this->Cell(190, 3, $this->enc('42 rue de Saint-Quentin - 02800 BEAUTOR - Tel: 06 79 34 88 02 - contact@e-cosplay.fr'), 0, 1, 'C'); + $this->Cell(190, 3, $this->enc('Association E-Cosplay - N SIRET 943 121 517 00011 - CODE APE 9329Z - RNA W022006988'), 0, 1, 'C'); + $this->SetFont('Arial', 'I', 7); + $this->SetTextColor(150, 150, 150); + $this->Cell(190, 3, $this->enc('Page ').$this->PageNo().' / {nb}', 0, 0, 'C'); + } + + /** @codeCoverageIgnore */ + private function writeHeader(): void + { + $logo = $this->kernel->getProjectDir().'/public/logo.jpg'; + if (file_exists($logo)) { + $this->Image($logo, 10, 8, 45); + } + + $this->SetFont('Arial', 'B', 14); + $this->SetXY(60, 10); + $this->Cell(0, 7, $this->enc('CONTRAT DE MIGRATION'), 0, 1, 'L'); + + $this->SetFont('Arial', 'B', 10); + $this->SetXY(60, 17); + $this->SetTextColor(253, 140, 4); + $this->Cell(0, 5, $this->enc('Transfert de services SARL SITECONSEIL'), 0, 1, 'L'); + $this->SetTextColor(0, 0, 0); + + $this->SetFont('Arial', '', 9); + $this->SetXY(60, 23); + $this->SetTextColor(150, 150, 150); + $this->Cell(0, 5, $this->enc('Ref. : '.$this->contrat->getReference()), 0, 1, 'L'); + $this->SetTextColor(0, 0, 0); + + $formatter = new \IntlDateFormatter( + 'fr_FR', + \IntlDateFormatter::FULL, + \IntlDateFormatter::NONE, + 'Europe/Paris', + \IntlDateFormatter::GREGORIAN + ); + + $this->SetFont('Arial', '', 10); + $this->SetXY(60, 29); + $this->Cell(0, 5, $this->enc('Emis a Beautor, le '.$formatter->format($this->contrat->getCreatedAt())), 0, 1, 'L'); + + // Parties + $this->SetY(42); + + // Partie 1 : Association + $this->SetFont('Arial', 'B', 9); + $this->Cell(0, 5, $this->enc('ENTRE LES SOUSSIGNES :'), 0, 1, 'L'); + $this->Ln(2); + $this->SetFont('Arial', '', 9); + $this->MultiCell(0, 4, $this->enc( + 'L\'Association E-Cosplay, association loi 1901, immatriculee sous le numero ' + .'SIRET 943 121 517 00011, dont le siege social est situe au 42 rue de Saint-Quentin, ' + .'02800 BEAUTOR, representee par son/sa President(e),' + ), 0, 'L'); + $this->SetFont('Arial', 'I', 9); + $this->Cell(0, 5, $this->enc('Ci-apres denommee "le Prestataire",'), 0, 1, 'L'); + $this->Ln(3); + + // Partie 2 : Client + $this->SetFont('Arial', 'B', 9); + $this->Cell(0, 5, $this->enc('ET :'), 0, 1, 'L'); + $this->Ln(2); + $this->SetFont('Arial', '', 9); + $this->MultiCell(0, 4, $this->enc( + $this->contrat->getRaisonSociale().', ' + .'joignable a l\'adresse email '.$this->contrat->getEmail().',' + ), 0, 'L'); + $this->SetFont('Arial', 'I', 9); + $this->Cell(0, 5, $this->enc('Ci-apres denomme(e) "le Client",'), 0, 1, 'L'); + $this->Ln(5); + } + + /** @codeCoverageIgnore */ + private function writePreambule(): void + { + $this->SetFont('Arial', 'B', 11); + $this->SetFillColor(250, 191, 4); + $this->Cell(0, 8, $this->enc(' PREAMBULE'), 0, 1, 'L', true); + $this->Ln(4); + + $this->SetFont('Arial', '', 9); + $paragraphs = [ + 'Dans le cadre de la cessation d\'activite de la SARL SITECONSEIL, l\'Association E-Cosplay ' + .'assure depuis plusieurs mois la gestion de l\'infrastructure technique et des services ' + .'precedemment operes par la SARL SITECONSEIL, dans un objectif de continuite de service ' + .'pour l\'ensemble des clients concernes.', + + 'La SARL SITECONSEIL a decide de transmettre la gestion de vos services a l\'Association ' + .'E-Cosplay. Cette transmission necessite la signature d\'un nouveau contrat de prestation ' + .'entre vous et l\'Association E-Cosplay.', + + 'Le present contrat definit les conditions dans lesquelles l\'Association E-Cosplay ' + .'poursuivra la fourniture des services dont vous beneficiez jusqu\'a present.', + ]; + + foreach ($paragraphs as $p) { + $this->MultiCell(0, 5, $this->enc($p), 0, 'J'); + $this->Ln(2); + } + + // Avertissement important + $this->Ln(2); + $this->SetFillColor(255, 247, 237); + $this->SetDrawColor(234, 88, 12); + $this->Rect($this->GetX(), $this->GetY(), 180, 18, 'DF'); + + $this->SetFont('Arial', 'B', 9); + $this->SetTextColor(154, 52, 18); + $this->SetX(15); + $this->Cell(0, 6, $this->enc('ATTENTION :'), 0, 1, 'L'); + $this->SetFont('Arial', '', 8); + $this->SetX(15); + $this->MultiCell(170, 4, $this->enc( + 'L\'Association E-Cosplay ne prendra pas en compte votre anciennete avec la SARL SITECONSEIL, ' + .'ni les accords ou conditions particulieres que vous auriez pu conclure avec cette derniere. ' + .'Le present contrat constitue un engagement nouveau et independant.' + ), 0, 'J'); + $this->SetTextColor(0, 0, 0); + $this->SetDrawColor(200, 200, 200); + $this->Ln(5); + } + + /** @codeCoverageIgnore */ + private function writeArticles(): void + { + $articles = [ + 'OBJET DU CONTRAT' => [ + 'Le present contrat a pour objet de definir les conditions dans lesquelles l\'Association ' + .'E-Cosplay fournira au Client les services informatiques precedemment assures par la ' + .'SARL SITECONSEIL, incluant sans s\'y limiter : l\'hebergement web, la gestion des noms ' + .'de domaine, la messagerie electronique, et tout autre service technique associe.', + ], + 'TRANSFERT DES SERVICES' => [ + 'Le Client reconnait que les services qui lui etaient fournis par la SARL SITECONSEIL ' + .'sont desormais operes par l\'Association E-Cosplay.', + 'Le Client accepte que le transfert de ses services soit effectue dans les conditions ' + .'du present contrat, sans garantie de reprise des conditions anterieures.', + ], + 'CONDITIONS TARIFAIRES' => [ + 'Les tarifs applicables seront ceux en vigueur au sein de l\'Association E-Cosplay ' + .'au moment de la signature du present contrat.', + 'Les tarifs precedemment appliques par la SARL SITECONSEIL ne sont pas opposables ' + .'a l\'Association E-Cosplay.', + 'Les devis et factures seront emis par l\'Association E-Cosplay selon sa propre grille tarifaire.', + ], + 'DUREE ET RESILIATION' => [ + 'Le present contrat prend effet a la date de sa signature et est conclu pour une duree indeterminee.', + 'Chacune des parties peut resilier le contrat a tout moment, sous reserve d\'un preavis ' + .'de 30 jours, notifie par email avec accuse de reception.', + ], + 'ANCIENNETE ET DROITS ANTERIEURS' => [ + 'Le Client reconnait expressement que l\'Association E-Cosplay ne reprend aucun ' + .'engagement, garantie, condition particuliere ou anciennete lies aux relations ' + .'commerciales entre le Client et la SARL SITECONSEIL.', + 'Tout litige relatif aux prestations anterieures de la SARL SITECONSEIL devra etre ' + .'dirige exclusivement vers cette derniere.', + ], + 'RESPONSABILITE' => [ + 'L\'Association E-Cosplay s\'engage a assurer la continuite des services dans la mesure ' + .'du possible. Elle ne saurait etre tenue responsable des interruptions de service ' + .'liees a la transition depuis la SARL SITECONSEIL.', + ], + 'DONNEES PERSONNELLES' => [ + 'L\'Association E-Cosplay s\'engage a traiter les donnees personnelles du Client ' + .'conformement au Reglement General sur la Protection des Donnees (RGPD) et a la ' + .'loi Informatique et Libertes.', + ], + 'DROIT APPLICABLE ET JURIDICTION' => [ + 'Le present contrat est soumis au droit francais. En cas de litige, les parties ' + .'s\'engagent a rechercher une solution amiable. A defaut, les tribunaux competents ' + .'de Laon seront seuls competents.', + ], + ]; + + $articleNum = 1; + foreach ($articles as $title => $contents) { + if ($this->GetY() + 25 > $this->GetPageHeight() - 25) { + $this->AddPage(); + } + + $this->SetFont('Arial', 'B', 10); + $this->Cell(0, 6, $this->enc('Article '.$articleNum.' - '.$title), 0, 1, 'L'); + $this->Ln(1); + + $this->SetFont('Arial', '', 9); + foreach ($contents as $content) { + $this->MultiCell(0, 4, $this->enc($content), 0, 'J'); + $this->Ln(2); + } + + $this->Ln(2); + ++$articleNum; + } + } + + /** @codeCoverageIgnore */ + private function writeSignatures(): void + { + if ($this->GetY() + 50 > $this->GetPageHeight() - 25) { + $this->AddPage(); + } + + $this->SetDrawColor(200, 200, 200); + $this->Cell(0, 0.5, '', 'T', 1, 'L'); + $this->Ln(3); + + $formatter = new \IntlDateFormatter( + 'fr_FR', + \IntlDateFormatter::LONG, + \IntlDateFormatter::NONE, + 'Europe/Paris', + \IntlDateFormatter::GREGORIAN + ); + + $this->SetFont('Arial', '', 9); + $this->Cell(0, 5, $this->enc('Fait a Beautor, le '.$formatter->format(new \DateTime()).', en deux exemplaires.'), 0, 1, 'L'); + $this->Ln(5); + + $colWidth = 85; + + // Labels + $this->SetFont('Arial', 'B', 9); + $this->Cell($colWidth, 5, $this->enc('Pour l\'Association E-Cosplay :'), 0, 0, 'L'); + $this->Cell(10, 5, '', 0, 0); + $this->Cell($colWidth, 5, $this->enc('Le Client ('.$this->contrat->getRaisonSociale().') :'), 0, 1, 'L'); + $this->Ln(2); + + // Signatures DocuSeal + $this->SetFont('Arial', '', 10); + $this->Cell($colWidth, 20, '{{Sign;type=signature;role=Company}}', 0, 0, 'L'); + $this->Cell(10, 20, '', 0, 0); + $this->Cell($colWidth, 20, '{{SignClient;type=signature;role=First Party}}', 0, 1, 'L'); + + $this->Ln(5); + $this->SetFont('Arial', 'I', 8); + $this->SetTextColor(150, 150, 150); + $this->Cell(0, 4, $this->enc('Signature electronique via DocuSeal - Valeur juridique (reglement eIDAS, art. 1367 Code civil)'), 0, 1, 'C'); + $this->SetTextColor(0, 0, 0); + } + + private function enc(string $text): string + { + return mb_convert_encoding($text, 'Windows-1252', 'UTF-8'); + } +} diff --git a/templates/admin/contrats/show.html.twig b/templates/admin/contrats/show.html.twig index 1ab3c40..e9f7715 100644 --- a/templates/admin/contrats/show.html.twig +++ b/templates/admin/contrats/show.html.twig @@ -56,12 +56,28 @@ {# Actions #}
- {% if contrat.pdfUnsigned %} + {% if contrat.state == 'draft' %} + {% if contrat.pdfUnsigned %} +
+ +
+ {% endif %} + {% endif %} + {% if contrat.pdfUnsigned and contrat.state in ['draft', 'send'] %} Voir PDF {% endif %} + {% if contrat.state == 'draft' and contrat.pdfUnsigned %} +
+ +
+ {% elseif contrat.state == 'send' and contrat.pdfUnsigned %} +
+ +
+ {% endif %} {% if contrat.pdfSigned %} diff --git a/templates/emails/contrat_signature.html.twig b/templates/emails/contrat_signature.html.twig new file mode 100644 index 0000000..4f021f2 --- /dev/null +++ b/templates/emails/contrat_signature.html.twig @@ -0,0 +1,60 @@ +{% extends 'email/base.html.twig' %} + +{% block content %} + + + + +
+

Chez {{ contrat.raisonSociale }},

+ +

+ Dans le cadre de la cessation d'activite de la SARL SITECONSEIL, l'Association E-Cosplay assure desormais la gestion de vos services. Un nouveau contrat doit etre signe pour officialiser cette transition. +

+ +

+ Nous vous invitons a prendre connaissance de toutes les informations relatives a cette migration en consultant la page dediee : +

+ + + + + +
+ En savoir plus sur la migration +
+ + + + + + + + + + +
Reference{{ contrat.reference }}
Type{{ contrat.typeLabel }}
+ + {% if signUrl %} +

+ Une fois informe, veuillez signer le contrat en cliquant ci-dessous : +

+ + + + +
+ Signer le contrat +
+ {% endif %} + +
+

Important

+

L'Association E-Cosplay ne prendra pas en compte votre anciennete avec la SARL SITECONSEIL, ni les accords ou conditions particulieres conclus avec cette derniere.

+
+ +

+ Pour toute question : contact@e-cosplay.fr +

+
+{% endblock %} diff --git a/templates/emails/contrat_signed.html.twig b/templates/emails/contrat_signed.html.twig new file mode 100644 index 0000000..2d95675 --- /dev/null +++ b/templates/emails/contrat_signed.html.twig @@ -0,0 +1,44 @@ +{% extends 'email/base.html.twig' %} + +{% block content %} + + + + +
+

Chez {{ contrat.raisonSociale }},

+ +
+

+ Contrat {{ contrat.reference }} signe avec succes +

+
+ +

+ Votre contrat a ete signe avec succes. Vous trouverez en piece jointe le contrat signe ainsi que le certificat d'audit de signature. +

+ + + + + + + + + + + + + + +
Reference{{ contrat.reference }}
Type{{ contrat.typeLabel }}
Signe le{{ contrat.signedAt ? contrat.signedAt|date('d/m/Y H:i') : 'Maintenant' }}
+ +

+ Votre espace client sera cree prochainement. Vous recevrez un email avec vos identifiants de connexion. +

+ +

+ Pour toute question : contact@e-cosplay.fr +

+
+{% endblock %} diff --git a/templates/move/siteconseil.html.twig b/templates/move/siteconseil.html.twig new file mode 100644 index 0000000..4e68093 --- /dev/null +++ b/templates/move/siteconseil.html.twig @@ -0,0 +1,170 @@ +{% extends 'base.html.twig' %} + +{% block title %}Migration SARL SITECONSEIL - Association E-Cosplay{% endblock %} + +{% block body %} +
+ + {# Header #} +
+
+
+ E-Cosplay +
+

Migration de vos services

+

Transfert depuis SARL SITECONSEIL vers Association E-Cosplay

+
+
+
+
+ +
+ + {# Introduction #} +
+

Pourquoi cette migration ?

+

+ Dans le cadre de la cessation d'activite de la SARL SITECONSEIL, l'Association E-Cosplay a pris en charge la gestion de l'infrastructure technique et des services qui vous etaient fournis par la SARL SITECONSEIL. +

+

+ Depuis plusieurs mois, notre equipe assure la continuite de vos services (sites internet, emails, noms de domaine, hebergement) afin de vous garantir une transition sans interruption. +

+

+ La SARL SITECONSEIL a decide de nous transmettre definitivement la gestion de vos services. Pour officialiser cette transition, un nouveau contrat doit etre signe entre vous et l'Association E-Cosplay. +

+
+ + {# Ce qui change #} +
+

Ce qui change

+
+
+

Votre nouveau prestataire

+
    +
  • + + L'Association E-Cosplay devient votre interlocuteur unique +
  • +
  • + + Nouveau contrat avec nos conditions generales +
  • +
  • + + Nouvelle facturation selon notre grille tarifaire +
  • +
  • + + Espace client dedie avec suivi en temps reel +
  • +
+
+
+

Ce qui ne change pas

+
    +
  • + + Vos sites internet restent en ligne et accessibles +
  • +
  • + + Vos emails continuent de fonctionner +
  • +
  • + + Vos noms de domaine sont preserves +
  • +
  • + + Vos donnees sont conservees et securisees +
  • +
+
+
+
+ + {# Avertissement important #} +
+

Information importante

+

+ L'Association E-Cosplay ne prendra pas en compte votre anciennete avec la SARL SITECONSEIL, ni les accords, conditions particulieres, tarifs preferentiels ou engagements que vous auriez pu conclure avec cette derniere. +

+

+ Le nouveau contrat constitue un engagement nouveau et independant. Les tarifs applicables seront ceux en vigueur au sein de l'Association E-Cosplay. +

+
+ + {# Etapes #} +
+

Comment ca se passe ?

+
+
+
1
+
+

Vous recevez un email avec le contrat

+

Le contrat de migration vous est envoye par email avec un lien de signature electronique.

+
+
+
+
2
+
+

Vous signez le contrat electroniquement

+

La signature est securisee via DocuSeal (valeur juridique, reglement eIDAS).

+
+
+
+
3
+
+

Votre espace client est cree

+

Un espace client vous est attribue avec acces a vos services, factures et support.

+
+
+
+
4
+
+

Vos services continuent normalement

+

Aucune interruption. Vos sites, emails et domaines restent actifs.

+
+
+
+
+ + {# FAQ #} +
+

Questions frequentes

+
+
+

Que se passe-t-il si je ne signe pas le contrat ?

+

Sans signature, nous ne pourrons pas maintenir vos services au-dela de la periode de transition. Vos services seront desactives.

+
+
+

Mes tarifs vont-ils changer ?

+

Les tarifs de l'Association E-Cosplay s'appliqueront. Ils peuvent differer de ceux de la SARL SITECONSEIL. Un devis vous sera transmis.

+
+
+

Mes donnees sont-elles en securite ?

+

Oui. Toutes vos donnees sont hebergees sur notre infrastructure securisee et traitees conformement au RGPD.

+
+
+

Puis-je recuperer mes donnees si je ne souhaite pas continuer ?

+

Oui. Vous pouvez demander l'export de vos donnees a tout moment en contactant notre support.

+
+
+

A qui m'adresser pour un litige avec SARL SITECONSEIL ?

+

Tout litige relatif aux prestations anterieures doit etre dirige vers la SARL SITECONSEIL. L'Association E-Cosplay n'est pas responsable des engagements passes.

+
+
+
+ + {# Contact #} +
+
+
+{% endblock %}