src/Controller/EntrepriseController.php line 664

  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\CashJournal;
  4. use App\Entity\Entreprise;
  5. use App\Entity\Subscription;
  6. use App\Entity\SubscriptionPayment;
  7. use App\Entity\Invoice;
  8. use App\Entity\Pay;
  9. use App\Entity\Payment;
  10. use App\Entity\Residence;
  11. use App\Entity\Template;
  12. use App\Entity\TemplateDefault;
  13. use App\Entity\TemplateType;
  14. use App\Entity\User;
  15. use App\Entity\UserEntreprise;
  16. use App\Form\EntrepriseType;
  17. use App\Form\ResidenceType;
  18. use App\Repository\AdminRepository;
  19. use App\Repository\CountryRepository;
  20. use App\Repository\EntrepriseRepository;
  21. use App\Repository\HabitatRepository;
  22. use App\Repository\PackageRepository;
  23. use App\Repository\PaymentRepository;
  24. use App\Repository\PayRepository;
  25. use App\Repository\RoleRepository;
  26. use App\Repository\SubscriptionRepository;
  27. use App\Repository\SubscriptionPaymentRepository;
  28. use App\Repository\UserRepository;
  29. use App\Service\CashJournalService;
  30. use App\Service\CodeService;
  31. use App\Service\CSRFProtectionService;
  32. use App\Service\EntrepriseService;
  33. use App\Service\ImageUploaderService;
  34. use App\Service\InvoiceService;
  35. use App\Service\MailService;
  36. use App\Service\ResidenceService;
  37. use App\Service\SmsService;
  38. use App\Service\TemplatesService;
  39. use App\Service\UploaderService;
  40. use Doctrine\ORM\EntityManagerInterface;
  41. use Doctrine\Persistence\ManagerRegistry;
  42. use Doctrine\Persistence\ObjectManager;
  43. use Exception;
  44. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  45. use Symfony\Component\HttpClient\HttpClient;
  46. use Symfony\Component\HttpFoundation\File\UploadedFile;
  47. use Symfony\Component\HttpFoundation\RedirectResponse;
  48. use Symfony\Component\HttpFoundation\Request;
  49. use Symfony\Component\HttpFoundation\Response;
  50. use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
  51. use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  52. use Symfony\Component\Routing\Annotation\Route;
  53. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  54. use Symfony\Component\Security\Http\Attribute\IsGranted;
  55. use Symfony\Contracts\HttpClient\HttpClientInterface;
  56. #[Route('/admin/entreprise')]
  57. class EntrepriseController extends AbstractController
  58. {
  59.     private EntityManagerInterface $em;
  60.     private MailService $mailService;
  61.     private CashJournalService $cjs;
  62.     public function __construct(EntityManagerInterface $emMailService $mailServiceCashJournalService $cjs)
  63.     {
  64.         $this->em $em;
  65.         $this->mailService $mailService;
  66.         $this->cjs $cjs;
  67.     }
  68.     private int $perPage 50;
  69.     #[Route('/lists'name'app_entreprise')]
  70.     public function index(
  71.         Request $request,
  72.         SubscriptionRepository $subscriptionRepository,
  73.         PackageRepository $packageRepository,
  74.         HabitatRepository $habitatRepository
  75.     ): Response
  76.     {
  77.         $this->syncExpiredSubscriptions($subscriptionRepository);
  78.         $search $request->query->get('search''');
  79.         $status $request->query->get('status''all');
  80.         $packageId $request->query->get('package');
  81.         $page max(1, (int) $request->query->get('page'1));
  82.         $normalizedStatus in_array($status, ['all''active''inactive'], true) ? $status 'all';
  83.         $packageId ctype_digit((string) $packageId) ? (int) $packageId null;
  84.         $entries $this->em->getRepository(Entreprise::class)->findByFilter(
  85.             $search,
  86.             $normalizedStatus,
  87.             $packageId,
  88.             $page,
  89.             $this->perPage
  90.         );
  91.         $pages ceil($entries->count() / $this->perPage);
  92.         $entrepriseIds = [];
  93.         $active 0;
  94.         $inactive 0;
  95.         foreach ($entries as $e){
  96.             if ($e->getId() !== null) {
  97.                 $entrepriseIds[] = $e->getId();
  98.             }
  99.             if ($e->isStatus()){$active += 1;}else{$inactive += 1;}
  100.         }
  101.         $habitatCounts $habitatRepository->countByEntrepriseIds($entrepriseIds);
  102.         return $this->render('entreprise/index.html.twig', [
  103.             'entreprises' => $entries,
  104.             'page' => $page,
  105.             'pages' => $pages,
  106.             'search' => $search,
  107.             'statusFilter' => $normalizedStatus,
  108.             'packageFilter' => $packageId,
  109.             'packages' => $packageRepository->findBy(['publish' => true], ['name' => 'ASC']),
  110.             'active' => $active,
  111.             'inactive' => $inactive,
  112.             'habitatCounts' => $habitatCounts,
  113.         ]);
  114.     }
  115.     #[Route('/add/new/entreprise'name'app_new_entreprise'methods: ['GET','POST'])]
  116.     #[IsGranted('IS_AUTHENTICATED_FULLY')]
  117.     public function newEntreprise(Request $requestAdminRepository $adminRepositoryRoleRepository $roleRepositoryCodeService $codeServiceSmsService $smsServiceUserPasswordHasherInterface $passwordHasherCountryRepository $countryRepositoryUserRepository $userRepositoryPackageRepository $packageRepositoryImageUploaderService $imageUploaderService
  118.     ): Response
  119.     {
  120.         $currentUser $this->getUser();
  121.         if (!$currentUser) {
  122.             return $this->redirectToRoute('app_login');
  123.         }
  124.         $error null;
  125.         $entreprise null;
  126.         if ($request->isMethod('POST')) {
  127.             // ---- Collect & normalize inputs
  128.             $companyName   trim((string) $request->request->get('name'));
  129.             $pseudo        trim((string) $request->request->get('pseudo'));
  130.             $nbreBien      = (int) $request->request->get('nbreBien'0);
  131.             $companyEmail  trim((string) $request->request->get('user-email'));
  132.             $phoneFull     trim((string) $request->request->get('user-phone'));
  133.             ;
  134.             $adminId       $request->request->get('admin');
  135.             $countryId     $request->request->get('country');
  136.             // optional company fields
  137.             $rccm          trim((string) $request->request->get('rccm'));
  138.             $idnat         trim((string) $request->request->get('idnat'));
  139.             $numeroImpot   trim((string) $request->request->get('numeroimpot'));
  140.             $adress        trim((string) $request->request->get('adress'));
  141.             $city          trim((string) $request->request->get('city'));
  142.             $province      trim((string) $request->request->get('province'));
  143.             // bailleur
  144.             $ownerFirst    trim((string) $request->request->get('user-name'));
  145.             $ownerLast     trim((string) $request->request->get('user-lastname'));
  146.             $ownerEmail    $companyEmail// you use the bailleur email as company email
  147.             $useSameAddr   $request->request->get('checkbox') === 'on';
  148.             $ownerAdress   trim((string) $request->request->get('user-adress'));
  149.             $ownerCity     trim((string) $request->request->get('user-city'));
  150.             $ownerProvince trim((string) $request->request->get('user-province'));
  151.             $companyCountryId $request->request->get('country');        // enterprise country
  152.             $ownerCountryId   $request->request->get('user-country');    // bailleur country
  153.             $useSameAddr      = (bool) $request->request->get('useSameAddress'); // checkbox
  154.             $error null;
  155.             // ---- Basic validation
  156.             if ($companyName === '' || $pseudo === '' || $nbreBien || $ownerEmail === '' || $phoneFull === '') {
  157.                 $error 'Veuillez remplir tous les champs obligatoires.';
  158.             }
  159.             // Email uniqueness (use findOneBy; comparing to null is enough)
  160.             if (!$error) {
  161.                 $existing $userRepository->findOneBy(['email' => $ownerEmail]);
  162.                 if ($existing) {
  163.                     $error 'Cet e-mail est déjà utilisé. Essayez un autre.';
  164.                 }
  165.             }
  166.             // Country validation
  167.             // --- Company country (required) ---
  168.             if (!ctype_digit((string)$companyCountryId)) {
  169.                 $error "Pays de l’entreprise obligatoire.";
  170.             } else {
  171.                 $companyCountry $countryRepository->find((int)$companyCountryId);
  172.                 if (!$companyCountry) {
  173.                     $error "Pays de l’entreprise introuvable.";
  174.                 }
  175.             }
  176.             // --- Owner country ---
  177.             $ownerCountry null;
  178.             if (!$error) {
  179.                 if ($useSameAddr) {
  180.                     // if same address, reuse company country
  181.                     $ownerCountry $companyCountry;
  182.                 } else {
  183.                     // only validate/lookup when not using same address
  184.                     if (!ctype_digit((string)$ownerCountryId)) {
  185.                         $error "Pays du bailleur obligatoire.";
  186.                     } else {
  187.                         $ownerCountry $countryRepository->find((int)$ownerCountryId);
  188.                         if (!$ownerCountry) {
  189.                             $error "Pays du bailleur introuvable.";
  190.                         }
  191.                     }
  192.                 }
  193.             }
  194.             if (!$error) {
  195.                 try {
  196.                     // ---- Create Entreprise
  197.                     $entreprise = new Entreprise();
  198.                     $entreprise->setCode(md5((string) microtime(true))); // better: use a UUID if possible
  199.                     $entreprise->setName($companyName);
  200.                     $entreprise->setEmail($ownerEmail);
  201.                     $entreprise->setPhone($phoneFull);
  202.                     $entreprise->setPseudo($pseudo);
  203.                     $entreprise->setNbrebiensimmobiliers($nbreBien);
  204.                     $entreprise->setStatus(false);
  205.                     $entreprise->setSmsservice(true);
  206.                     $entreprise->setInvoiceservice(false);
  207.                     $entreprise->setRecallpaymentnotification(false);
  208.                     // relations
  209.                     if ($adminId) {
  210.                         $adminOwner $adminRepository->find($adminId);
  211.                         $entreprise->setAdmin($adminOwner ?? $currentUser);
  212.                     } else {
  213.                         $entreprise->setAdmin($currentUser);
  214.                     }
  215.                     $entreprise->setCountry($companyCountry);
  216.                     // optionals
  217.                     if ($adress !== '')   { $entreprise->setAdress($adress); }
  218.                     if ($city !== '')     { $entreprise->setCity($city); }
  219.                     if ($province !== '') { $entreprise->setProvince($province); }
  220.                     if ($rccm !== '')     { $entreprise->setRccm($rccm); }
  221.                     if ($idnat !== '')    { $entreprise->setIdnat($idnat); }
  222.                     if ($numeroImpot !== '') { $entreprise->setNumeroImpot($numeroImpot); }
  223.                     $this->em->persist($entreprise);
  224.                     // ---- Create Bailleur user
  225.                     $user = new User();
  226.                     $user->setEntreprise($entreprise);
  227.                     $user->setCode(md5((string) microtime(true)) . '_' uniqid(''true));
  228.                     // Prefer finding by role code instead of hard id=1
  229.                     $roleAdmin $roleRepository->findOneBy(['id' => 1]); // adjust to your needs
  230.                     $user->setRole($roleAdmin);
  231.                     $user->setUsername($ownerEmail);
  232.                     $user->setEmail($ownerEmail);
  233.                     $user->setName($ownerFirst);
  234.                     $user->setLastname($ownerLast);
  235.                     $user->setPhone($phoneFull);
  236.                     if ($useSameAddr) {
  237.                         $user->setAdress($entreprise->getAdress());
  238.                         $user->setCity($entreprise->getCity());
  239.                         $user->setProvince($entreprise->getProvince());
  240.                         $user->setCountry($entreprise->getCountry());
  241.                     } else {
  242.                         $user->setAdress($ownerAdress);
  243.                         $user->setCity($ownerCity);
  244.                         $user->setProvince($ownerProvince);
  245.                         $user->setCountry($ownerCountry);
  246.                     }
  247.                     //entreprise profil
  248.                     if ($request->files->get('icon-'.$entreprise->getId())){
  249.                         /** @var UploadedFile|null $file */
  250.                         $file $request->files->get('icon-'.$entreprise->getId());
  251.                         $imageUrl $imageUploaderService->uploadAndResizeImageToS3($file);
  252.                         $entreprise->setIcon($imageUrl);
  253.                     }
  254.                     // base flags
  255.                     $user->setReset(false);
  256.                     $user->setStatus(true);
  257.                     $user->setNotif(true);
  258.                     $user->setLevel(2);
  259.                     $user->setProfil(1);
  260.                     // password
  261.                     $plain strtoupper($codeService->PasswordCode(8));
  262.                     $user->setPassword($passwordHasher->hashPassword($user$plain));
  263.                     // notify via SMS; if SMS fails (==0), send email
  264.                     $message 'Votre mot de passe ' $entreprise->getName() . ' : ' $plain;
  265.                     $smsService->smsService($phoneFull$message);
  266.                     $this->em->persist($user);
  267.                     $this->em->flush();
  268.                     //add enteprise owner
  269.                     $entreprise->setOwner($user);
  270.                     //add to mutilple entreprises
  271.                     $link = new UserEntreprise();
  272.                     $link->setUser($user);
  273.                     $link->setEntreprise($user->getEntreprise());
  274.                     $link->setRole('ADMIN');
  275.                     $link->setStatus(true);
  276.                     $this->em->persist($link);
  277.                     $this->em->flush();
  278.                     //add template
  279.                     $this->addTemplates($entreprise);
  280.                     //add journal
  281.                     $this->addJournal($entreprise);
  282.                     $this->addFlash('success''Entreprise et bailleur créés avec succès.');
  283.                     return $this->redirectToRoute('app_entreprise_detail', ['code' => $entreprise->getCode()]);
  284.                 } catch (\Throwable $e) {
  285.                     $error 'Une erreur est survenue lors de l’enregistrement. Veuillez réessayer plus tard.';
  286.                 }
  287.             }
  288.         }
  289.         // GET or POST w/ error → render
  290.         $countries $countryRepository->findBy(['status' => true]);
  291.         $packages  $packageRepository->findBy(['publish' => true]);
  292.         return $this->render('entreprise/add_entreprise.html.twig', [
  293.             'admin'      => $currentUser->getId(),
  294.             'error'      => $error,
  295.             'countries'  => $countries,
  296.             'entreprise' => $entreprise,
  297.             'packages'   => $packages,
  298.         ]);
  299.     }
  300.     #[Route('/details/{code}'name'app_entreprise_detail')]
  301.     public function indexMarchandsDetails(
  302.         Entreprise $entreprise,
  303.         UserRepository $userRepository,
  304.         RoleRepository $roleRepository,
  305.         SubscriptionPaymentRepository $subscriptionPaymentRepository
  306.     ): Response
  307.     {
  308.         if(!$entreprise->getOwner()){
  309.             return  $this->redirectToRoute('app_entreprise_only_detail', ['code' => $entreprise->getCode()]);
  310.         }
  311.         $subscriptionPayments $subscriptionPaymentRepository->findBy(
  312.             ['entreprise' => $entreprise],
  313.             ['id' => 'DESC']
  314.         );
  315.         return $this->render('entreprise/detail_entreprise.html.twig', [
  316.             'entreprise' => $entreprise,
  317.             'bailleur' => $entreprise->getOwner(),
  318.             'subscriptionPayments' => $subscriptionPayments,
  319.         ]);
  320.     }
  321.     #[Route('/subscription/payment/{id}/delete'name'app_subscription_payment_delete'methods: ['POST'])]
  322.     public function deleteSubscriptionPayment(SubscriptionPayment $paymentRequest $request): Response
  323.     {
  324.         $entreprise $payment->getEntreprise();
  325.         $redirectCode $entreprise?->getCode();
  326.         $csrfToken = (string) $request->request->get('_csrf_token');
  327.         if (!$this->isCsrfTokenValid('delete_subscription_payment_' $payment->getId(), $csrfToken)) {
  328.             $this->addFlash('danger''Jeton CSRF invalide.');
  329.             return $this->redirectToEntrepriseDetailOrList($redirectCode);
  330.         }
  331.         if ($payment->isPaid() === true) {
  332.             $this->addFlash('warning''Impossible de supprimer une facture déjà payée.');
  333.             return $this->redirectToEntrepriseDetailOrList($redirectCode);
  334.         }
  335.         $subscription $payment->getSubscription();
  336.         if ($subscription && $subscription->isPaid() !== true && $subscription->getSubscriptionPayments()->count() <= 1) {
  337.             $this->em->remove($subscription);
  338.         }
  339.         $this->em->remove($payment);
  340.         $this->em->flush();
  341.         $this->addFlash('success''Facture supprimée.');
  342.         return $this->redirectToEntrepriseDetailOrList($redirectCode);
  343.     }
  344.     private function redirectToEntrepriseDetailOrList(?string $code): Response
  345.     {
  346.         if ($code) {
  347.             return $this->redirectToRoute('app_entreprise_detail', ['code' => $code]);
  348.         }
  349.         return $this->redirectToRoute('app_entreprise');
  350.     }
  351.     private function syncExpiredSubscriptions(SubscriptionRepository $subscriptionRepository): void
  352.     {
  353.         $now = new \DateTimeImmutable();
  354.         $expiredSubscriptions $subscriptionRepository->findExpiredActiveSubscriptions($now);
  355.         if (!$expiredSubscriptions) {
  356.             return;
  357.         }
  358.         $entreprisesToCheck = [];
  359.         foreach ($expiredSubscriptions as $subscription) {
  360.             $subscription->setStatus(false);
  361.             $entreprise $subscription->getEntreprise();
  362.             if ($entreprise) {
  363.                 $key $entreprise->getId() ?? spl_object_id($entreprise);
  364.                 $entreprisesToCheck[$key] = $entreprise;
  365.             }
  366.         }
  367.         foreach ($entreprisesToCheck as $entreprise) {
  368.             if (!$subscriptionRepository->hasActiveSubscriptionForEntreprise($entreprise$now)) {
  369.                 $entreprise->setStatus(false);
  370.             }
  371.         }
  372.         $this->em->flush();
  373.     }
  374.     #[Route('/bailleur/edit/{code}'name'app_entreprise_edit')]
  375.     public function editBailleur(Entreprise $entrepriseRequest $requestManagerRegistry $doctrineRoleRepository $roleRepositoryPackageRepository $packageRepositoryCountryRepository $countryRepositoryUserRepository $userRepositoryImageUploaderService $imageUploaderService): Response
  376.     {
  377.         $error null;
  378.         $em $doctrine->getManager();
  379.         $role $roleRepository->findOneBy(['id' => 1]);
  380.         $user $userRepository->findOneBy(['role' => $role'entreprise' => $entreprise]);
  381.         if ($request->isMethod('post')) {
  382.             try {
  383.                 /* entreprise */
  384.                 $entreprise->setName($request->get('name-'.$entreprise->getId()));
  385.                 $entreprise->setEmail($request->get('email-'.$entreprise->getId()));
  386.                 $entreprise->setPhone($request->get('phone-'.$entreprise->getId()));
  387.                 $entreprise->setPseudo($request->get('pseudo-'.$entreprise->getId()));
  388.                 $entreprise->setNbrebiensimmobiliers($request->get('nbreBien-'.$entreprise->getId()));
  389.                 $country_ $countryRepository->findOneBy(['id' => $request->get('country-'.$entreprise->getId())]);
  390.                 $entreprise->setCountry($country_);
  391.                 if ($request->request->get('adress-'.$entreprise->getId()) != null){
  392.                     $entreprise->setAdress($request->get('adress-'.$entreprise->getId()));
  393.                 }
  394.                 if ($request->request->get('city-'.$entreprise->getId()) != null){
  395.                     $entreprise->setCity($request->get('city-'.$entreprise->getId()));
  396.                 }
  397.                 if ($request->request->get('province-'.$entreprise->getId()) != null){
  398.                     $entreprise->setProvince($request->get('province-'.$entreprise->getId()));
  399.                 }
  400.                 if ($request->request->get('rccm-'.$entreprise->getId()) != null){
  401.                     $entreprise->setRccm($request->get('rccm-'.$entreprise->getId()));
  402.                 }
  403.                 if ($request->request->get('idnat-'.$entreprise->getId()) != null){
  404.                     $entreprise->setIdnat($request->get('idnat-'.$entreprise->getId()));
  405.                 }
  406.                 if ($request->request->get('numeroimpot-'.$entreprise->getId()) != null){
  407.                     $entreprise->setNumeroimpot($request->get('numeroimpot-'.$entreprise->getId()));
  408.                 }
  409.                 //entreprise profil
  410.                 if ($request->files->get('icon-'.$entreprise->getId())){
  411.                     /** @var UploadedFile|null $file */
  412.                     $file $request->files->get('icon-'.$entreprise->getId());
  413.                     $imageUrl $imageUploaderService->uploadAndResizeImageToS3($file);
  414.                     $entreprise->setIcon($imageUrl);
  415.                 }
  416.                 $em->persist($entreprise);
  417.                 /* user */
  418.                 $user->setUsername($request->get('user-email-'.$user->getId()));
  419.                 $user->setEmail($request->get('user-email-'.$user->getId()));
  420.                 $user->setName($request->get('user-name-'.$user->getId()));
  421.                 $user->setLastname($request->get('user-lastname-'.$user->getId()));
  422.                 $user->setPhone($request->get('user-phone-'.$user->getId()));
  423.                 if ($request->get('checkbox') !== null){
  424.                     $user->setAdress($entreprise->getAdress());
  425.                     $user->setCity($entreprise->getCity());
  426.                     $user->setProvince($entreprise->getProvince());
  427.                     $user->setCountry($entreprise->getCountry());
  428.                 }else{
  429.                     $country_ $countryRepository->findOneBy(['id' => $request->get('user-country-'.$user->getId())]);
  430.                     $user->setCountry($country_);
  431.                     $user->setAdress($request->get('user-adress-'.$user->getId()));
  432.                     $user->setCity($request->get('user-city-'.$user->getId()));
  433.                     $user->setProvince($request->get('user-province-'.$user->getId()));
  434.                 }
  435.                 $em->persist($user);
  436.                 $em->flush();
  437.                 return $this->redirectToRoute('app_entreprise_detail', ['code' => $entreprise->getCode()]);
  438.             }catch (\Exception $e){
  439.                 $error 'Une erreur est survenu lors de l\'enregistrement, Veuillez réessayer plus tard.';
  440.             }
  441.         }
  442.         $countries $countryRepository->findBy(['status' => true]);
  443.         $packages $packageRepository->findBy(['publish' => true]);
  444.         return $this->render('entreprise/edit_entreprise.html.twig', [
  445.             'error' => $error,
  446.             'countries' => $countries,
  447.             'entreprise' => $entreprise,
  448.             'bailleur' => $user,
  449.             'packages' => $packages
  450.         ]);
  451.     }
  452.     #[Route('/bailleur/unable/bailleur/{id}'name'app_unable_entreprise')]
  453.     public function unableEntreprise(Entreprise $entreprise): Response
  454.     {
  455.         return $this->render('entreprise/unable_entreprise.html.twig', [
  456.             'entreprise' => $entreprise,
  457.         ]);
  458.     }
  459.     #[Route('/bailleur/unable/{id}/save/changement'name'app_unable_entreprise_save_change'methods: ['POST'])]
  460.     public function unableEntrepriseSave($idManagerRegistry $doctrineCSRFProtectionService $csrfRequest $requestEntrepriseRepository $entrepriseRepository)
  461.     {
  462.         $em $doctrine->getManager();
  463.         if ($csrf->validateCSRFToken($request)) {
  464.             /* @var $entreprise Entreprise */
  465.             $entreprise $entrepriseRepository->find($id);
  466.             $entreprise->setStatus(false);
  467.             $em->persist($entreprise);
  468.             $em->flush();
  469.             foreach ($entreprise->getUsers() as $user){
  470.                 $user->setStatus(false);
  471.                 $em->persist($user);
  472.                 $em->flush();
  473.             }
  474.         }
  475.         return $this->render('feedback.html.twig', [
  476.             'error' => false,
  477.         ]);
  478.     }
  479.     #[Route('/bailleur/membership/{code}'name'app_membership')]
  480.     public function entrepriseMembership(Entreprise $entreprisePackageRepository $packageRepositoryManagerRegistry $doctrineCSRFProtectionService $csrfRequest $requestEntrepriseRepository $entrepriseRepository)
  481.     {
  482.         $packages $packageRepository->findAll();
  483.         if ($entreprise->getPackage() == null){
  484.             $package $packageRepository->findOneBy(['id' => 4]);
  485.             $entreprise->setPackage($package);
  486.             $em $doctrine->getManager();
  487.             $em->persist($entreprise);
  488.             $em->flush();
  489.         }
  490.         return $this->render('entreprise/membership_entreprise.html.twig', [
  491.             'entreprise' => $entreprise,
  492.             'packages' => $packages,
  493.             'months' => 1
  494.         ]);
  495.     }
  496.     #[Route('/bailleur/membership/{entreprise}/{package}'name'app_membership_change_membership')]
  497.     public function entrepriseChangeStatus($entreprise$packagePackageRepository $packageRepositoryManagerRegistry $doctrineCSRFProtectionService $csrfRequest $requestEntrepriseRepository $entrepriseRepository)
  498.     {
  499.         $entrepriseGet $entrepriseRepository->findOneBy(['id' => $entreprise]);
  500.         $packageGet $packageRepository->findOneBy(['id' => $package]);
  501.         $entrepriseGet->setPackage($packageGet);
  502.         $entrepriseGet->setPeriod('monthly');
  503.         $em $doctrine->getManager();
  504.         $em->persist($entrepriseGet);
  505.         $em->flush();
  506.         return $this->redirectToRoute('app_membership', ['code' => $entrepriseGet->getCode()]);
  507.     }
  508.     #[Route('/membership/update/subscription/price'name'app_membership_subscription_price_cycle'methods: ['POST'])]
  509.     public function updatePrice(Request $requestEntrepriseRepository $entrepriseRepositoryEntityManagerInterface $em): Response
  510.     {
  511.         $entreprise $entrepriseRepository->find($request->request->get('entreprise'));
  512.         $months max(1, (int) $request->request->get('monthsToPay'1));
  513.         if ($entreprise) {
  514.             $entreprise->setPeriod('monthly');
  515.             $em->flush();
  516.         }
  517.         return $this->render('entreprise/membership_price.html.twig', [
  518.             'entreprise' => $entreprise,
  519.             'months' => $months
  520.         ]);
  521.     }
  522.     #[Route('/membership/set/for/free/trail/price/{id}'name'app_membership_subscription_for_free_trail')]
  523.     #[IsGranted('IS_AUTHENTICATED_FULLY')]
  524.     public function startFreeTrial(Entreprise $entrepriseEntityManagerInterface $em): Response {
  525.         // Guard: entreprise must have a selected package
  526.         $package $entreprise->getPackage();
  527.         if (!$package) {
  528.             $this->addFlash('danger'"Aucun forfait n'est associé à cette entreprise.");
  529.             return $this->redirectToRoute('app_entreprise_detail', ['code' => $entreprise->getCode()]);
  530.         }
  531.         // Optional idempotency guard: if already flagged trial or an active sub overlaps, skip creating a new one
  532.         // (Uncomment/adjust if you have a repository method to check overlap)
  533.         // if ($entreprise->isFreeTrial() || $subscriptionRepo->hasActiveTrial($entreprise)) { ... }
  534.         // Dates: 2 months free trial from today (00:00)
  535.         $startAt = new \DateTimeImmutable('today');
  536.         $endAt   $startAt->modify('+2 months');
  537.         // Robust order number / code
  538.         $orderNumber bin2hex(random_bytes(16));       // 32-hex chars
  539.         $subCode     bin2hex(random_bytes(8));        // 16-hex chars
  540.         $sub = new Subscription();
  541.         $sub->setCode($subCode);
  542.         $sub->setOrderNumber($orderNumber);
  543.         $sub->setEntreprise($entreprise);
  544.         $sub->setPackage($package);
  545.         // A trial is free and not paid
  546.         $sub->setAmount('0.00');
  547.         $sub->setCurrency('USD');
  548.         $sub->setPaymenttype('Trial');
  549.         $sub->setStatus(true);   // active
  550.         $sub->setPaid(false);    // not a paid invoice
  551.         $sub->setStartAt($startAt);
  552.         $sub->setEndAt($endAt);
  553.         // Mark entreprise as active and in free trial
  554.         $entreprise->setStatus(true);
  555.         if (method_exists($entreprise'setFreeTrial')) {
  556.             $entreprise->setFreeTrial(true);
  557.         } elseif (method_exists($entreprise'setFreeTrail')) {
  558.             // If your entity still has the old typo, keep this fallback
  559.             $entreprise->setFreeTrail(true);
  560.         }
  561.         $em->persist($sub);
  562.         $em->flush();
  563.         $this->addFlash('success'"Période d’essai de 2 mois activée. Fin d’essai le " $endAt->format('d/m/Y') . ".");
  564.         return $this->redirectToRoute('app_entreprise_detail', ['code' => $entreprise->getCode()]);
  565.     }
  566.     #[Route('/bailleur/reset/password/{code}'name'app_entreprise_edit_bailleur_password')]
  567.     public function resetBailleur(Entreprise $entrepriseManagerRegistry $doctrineRoleRepository $roleRepositoryUserRepository $userRepositoryCodeService $codeServiceSmsService $smsServiceUserPasswordHasherInterface $passwordHasher): Response
  568.     {
  569.         $role $roleRepository->findOneBy(['id' => 1]);
  570.         $user $userRepository->findOneBy(['role' => $role'entreprise' => $entreprise]);
  571.         // password
  572.         $plain strtoupper($codeService->PasswordCode(8));
  573.         $user->setPassword($passwordHasher->hashPassword($user$plain));
  574.         $this->em->flush();
  575.         // notify via SMS; if SMS fails (==0), send email
  576.         $message 'Votre mot de passe ' $entreprise->getName() . ' : ' $plain;
  577.         $smsOk $smsService->smsService($user->getPhone(), $message);
  578.         if ($smsOk === 0) {
  579.             $this->addFlash('danger''Erreur lors de l\'envoi. réessayer');
  580.         }else{
  581.             $this->addFlash('success''Le mot de passe a été envoyé avec succès');
  582.         }
  583.         if($user->getEmail()){
  584.              $this->mailService->sendTemplatedMail(
  585.                  $user->getEmail(),
  586.                  'Votre mot de passe Bailleur',
  587.                  'entreprise/email_password.html.twig',
  588.                  ['message' => $message]
  589.              );
  590.         }
  591.         return $this->redirectToRoute('app_entreprise_detail', ['code' => $entreprise->getCode()]);
  592.     }
  593.     public function addTemplates(Entreprise $entreprise): void
  594.     {
  595.         $templateTypeInvoice $this->em->getRepository(TemplateType::class)->findOneBy(['name' => 'TEMPLATE_INVOICE_PDF']);
  596.         $templateDefaultInvoice $this->em->getRepository(TemplateDefault::class)->find(1);
  597.         $template = new Template();
  598.         $template->setParams('{"orientation": "P", "marginLeft": 25, "marginRight": 20, "marginTop": 20, "marginBottom": 20, "marginHeader": 9, "marginFooter": 9}');
  599.         $template->setIsDefault(true);
  600.         $template->setName('PDF Quittance');
  601.         $template->setTemplateType($templateTypeInvoice);
  602.         $template->setMarchand($entreprise);
  603.         $template->setText($templateDefaultInvoice->getText());
  604.         $this->em->persist($template);
  605.         $this->em->flush();
  606.         $templateTypeJournal $this->em->getRepository(TemplateType::class)->findOneBy(['name' => 'TEMPLATE_CASHJOURNAL_PDF']);
  607.         $templateDefaultJournal $this->em->getRepository(TemplateDefault::class)->find(2);
  608.         $template = new Template();
  609.         $template->setParams('{"orientation": "P", "marginLeft": 25, "marginRight": 20, "marginTop": 20, "marginBottom": 20, "marginHeader": 9, "marginFooter": 9}');
  610.         $template->setIsDefault(true);
  611.         $template->setName('PDF Livre de caisse');
  612.         $template->setTemplateType($templateTypeJournal);
  613.         $template->setMarchand($entreprise);
  614.         $template->setText($templateDefaultJournal->getText());
  615.         $this->em->persist($template);
  616.         $this->em->flush();
  617.     }
  618.     public function addJournal(Entreprise $entreprise): void
  619.     {
  620.         $year date('Y');
  621.         $month = (new \DateTime('first day of this month'))->format('n');
  622.         $journal $this->em->getRepository(CashJournal::class)->findOneBy(['cashYear' => $year,'cashMonth' => $month'marchand' => $entreprise]);
  623.         $youngestJ $this->em->getRepository(CashJournal::class)->getYoungestJournal($entreprise);
  624.         if (!$journal){
  625.             $journal = new CashJournal();
  626.             if ($youngestJ instanceof CashJournal) {
  627.                 // set last cash end as new cash start
  628.                 $journal->setCashStart($youngestJ->getCashEnd());
  629.                 // set new year and month based on the last entry
  630.                 if (12 == $youngestJ->getCashMonth()) {
  631.                     $journal->setCashYear($youngestJ->getCashYear() + 1);
  632.                     $journal->setCashMonth(1);
  633.                 } else {
  634.                     $journal->setCashYear($youngestJ->getCashYear());
  635.                     $journal->setCashMonth($youngestJ->getCashMonth() + 1);
  636.                 }
  637.             }else{
  638.                 $journal->setCashYear($year);
  639.                 $journal->setCashMonth($month);
  640.             }
  641.             $journal->setMarchand($entreprise);
  642.             $journal->setCashEnd(0.00);
  643.             $journal->setCashStart(0.00);
  644.             $journal->setIsClosed(false);
  645.             $journal->setIsBooked(false);
  646.             $this->em->persist($journal);
  647.             $this->em->flush();
  648.             // update cash end of journal
  649.             $this->cjs->recalculateCashEnd($journal);
  650.         }
  651.     }
  652. }