<?php
namespace App\Controller;
use App\Entity\Facture;
use App\Form\FactureType;
use App\Repository\FactureRepository;
use App\Repository\EntrepriseRepository;
use App\Repository\ContratRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Repository\ElectricMeterRepository;
use App\Repository\GasMeterRepository;
use App\Repository\UserRepository;
use App\Repository\ChargeRepository;
use App\Entity\Entreprise;
use App\Entity\ElectricMeter;
use App\Entity\GasMeter;
use App\Entity\User;
use App\Entity\Contrat;
use App\Entity\Charge;
use App\Form\ContratType;
use App\Form\ChargeType;
use App\Service\PricingService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* @Route("/compta")
*/
class ComptaController extends AbstractController
{
private $pricingService;
public function __construct(PricingService $pricingService)
{
$this->pricingService = $pricingService;
}
/**
* @Route("/", name="app_compta")
*/
public function index(EntrepriseRepository $entrepriseRepository): Response
{
$entreprises = $entrepriseRepository->findAll();
$totalEntreprises = count($entreprises);
$elecricMeters = $this->getDoctrine()->getRepository(ElectricMeter::class)->findAll();
$gasMeters = $this->getDoctrine()->getRepository(GasMeter::class)->findAll();
$totalElectricMeters = count($elecricMeters);
$totalGasMeters = count($gasMeters);
return $this->render('compta/index.html.twig', [
'total_entreprises' => $totalEntreprises,
'total_electric_meters' => $totalElectricMeters,
'total_gas_meters' => $totalGasMeters,
]);
}
/**
* @Route("/charges", name="app_compta_charge")
*/
public function charges(ChargeRepository $chargeRepository, UserRepository $userRepository): Response
{
$charges = $chargeRepository->findAll();
// Filter users to only get those with ROLE_TEAM
$agents = $userRepository->createQueryBuilder('u')
->where('u.roles LIKE :role')
->setParameter('role', '%ROLE_TEAM%')
->orderBy('u.username', 'ASC')
->getQuery()
->getResult();
// Calculate totals
$totalHT = 0;
$totalTVA = 0;
$totalTTC = 0;
// Get unique categories
$categories = [];
foreach ($charges as $charge) {
if ($charge->getCategoriePersonnalisee()) {
$categories[$charge->getCategoriePersonnalisee()] = $charge->getCategoriePersonnalisee();
}
$totalHT += floatval($charge->getMontantHT());
$totalTVA += floatval($charge->getMontantTVA());
$totalTTC += floatval($charge->getMontantTTC());
}
// Sort categories alphabetically
sort($categories);
// Vérifier les charges mensuelles manquantes
$missingCharges = $chargeRepository->checkMissingMonthlyCharges();
return $this->render('compta/charges.html.twig', [
'charges' => $charges,
'agents' => $agents,
'categories' => $categories,
'totalHT' => $totalHT,
'totalTVA' => $totalTVA,
'totalTTC' => $totalTTC,
'missingCharges' => $missingCharges,
]);
}
/**
* @Route("/charge/new", name="app_compta_charge_new", methods={"GET","POST"})
*/
public function newCharge(Request $request, EntityManagerInterface $entityManager, ChargeRepository $chargeRepository): Response
{
$charge = new Charge();
// If this is a monthly charge being added from the missing charges list
if ($request->query->has('monthly_charge_id')) {
$originalCharge = $chargeRepository->find($request->query->get('monthly_charge_id'));
if ($originalCharge) {
$charge->setAgent($originalCharge->getAgent());
$charge->setDesignation($originalCharge->getDesignation());
$charge->setCategorieDesignation($originalCharge->getCategorieDesignation());
$charge->setCategoriePersonnalisee($originalCharge->getCategoriePersonnalisee());
$charge->setMontantHT($originalCharge->getMontantHT());
$charge->setTauxTVA($originalCharge->getTauxTVA());
$charge->setMontantTVA($originalCharge->getMontantTVA());
$charge->setMontantTTC($originalCharge->getMontantTTC());
$charge->setIsEntrepriseCharge($originalCharge->getIsEntrepriseCharge());
$charge->setIsMensuel($originalCharge->getIsMensuel());
// Set the expected date from the query parameter if provided
if ($request->query->has('expected_date')) {
$expectedDate = new \DateTime($request->query->get('expected_date'));
$charge->setDate($expectedDate);
}
}
}
$form = $this->createForm(ChargeType::class, $charge);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
try {
// Ensure proper number formatting
$montantHT = floatval(str_replace(',', '.', $charge->getMontantHT()));
$montantTVA = floatval(str_replace(',', '.', $charge->getMontantTVA()));
$montantTTC = floatval(str_replace(',', '.', $charge->getMontantTTC()));
$charge->setMontantHT(number_format($montantHT, 2, '.', ''));
$charge->setMontantTVA(number_format($montantTVA, 2, '.', ''));
$charge->setMontantTTC(number_format($montantTTC, 2, '.', ''));
$entityManager->persist($charge);
$entityManager->flush();
$this->addFlash('success', 'La charge a été créée avec succès.');
return $this->redirectToRoute('app_compta_charge');
} catch (\Exception $e) {
$this->addFlash('error', 'Une erreur est survenue lors de la création de la charge : ' . $e->getMessage());
}
}
return $this->render('compta/charge/new.html.twig', [
'charge' => $charge,
'form' => $form->createView(),
]);
}
/**
* @Route("/charge/{id}/edit", name="app_compta_charge_edit", methods={"GET","POST"})
*/
public function editCharge(Request $request, Charge $charge, EntityManagerInterface $entityManager): Response
{
$form = $this->createForm(ChargeType::class, $charge);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
try {
// Ensure proper number formatting
$montantHT = floatval(str_replace(',', '.', $charge->getMontantHT()));
$montantTVA = floatval(str_replace(',', '.', $charge->getMontantTVA()));
$montantTTC = floatval(str_replace(',', '.', $charge->getMontantTTC()));
$charge->setMontantHT(number_format($montantHT, 2, '.', ''));
$charge->setMontantTVA(number_format($montantTVA, 2, '.', ''));
$charge->setMontantTTC(number_format($montantTTC, 2, '.', ''));
$entityManager->flush();
$this->addFlash('success', 'La charge a été modifiée avec succès.');
return $this->redirectToRoute('app_compta_charge');
} catch (\Exception $e) {
$this->addFlash('error', 'Une erreur est survenue lors de la modification de la charge : ' . $e->getMessage());
}
}
return $this->render('compta/charge/edit.html.twig', [
'charge' => $charge,
'form' => $form->createView(),
]);
}
/**
* @Route("/charge/{id}/duplicate", name="app_compta_charge_duplicate", methods={"GET"})
*/
public function duplicateCharge(Charge $charge): Response
{
return $this->redirectToRoute('app_compta_charge_new', [
'monthly_charge_id' => $charge->getId()
]);
}
/**
* @Route("/charge/{id}", name="app_compta_charge_delete", methods={"POST"})
*/
public function deleteCharge(Request $request, Charge $charge, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('delete'.$charge->getId(), $request->request->get('_token'))) {
try {
$entityManager->remove($charge);
$entityManager->flush();
$this->addFlash('success', 'La charge a été supprimée avec succès.');
} catch (\Exception $e) {
$this->addFlash('error', 'Une erreur est survenue lors de la suppression de la charge : ' . $e->getMessage());
}
}
return $this->redirectToRoute('app_compta_charge');
}
/**
* @Route("/contrats", name="app_compta_contrat")
*/
public function contrats(ContratRepository $contratRepository, ElectricMeterRepository $electricMeterRepository, GasMeterRepository $gasMeterRepository): Response
{
$contrats = $contratRepository->findAll();
// Get meters for type determination
$meters = [];
foreach ($electricMeterRepository->findAll() as $meter) {
$meters[$meter->getPDL()] = ['type' => 'Elec', 'profil' => $meter->getProfil()];
}
foreach ($gasMeterRepository->findAll() as $meter) {
$meters[$meter->getPDL()] = ['type' => 'Gaz', 'profil' => $meter->getProfil()];
}
return $this->render('compta/contrats.html.twig', [
'contrats' => $contrats,
'meters' => $meters,
]);
}
/**
* @Route("/contrat/new", name="app_compta_contrat_new", methods={"GET","POST"})
*/
public function newContrat(Request $request, EntityManagerInterface $entityManager): Response
{
$contrat = new Contrat();
$pdlChoices = $this->getPDLChoicesForForm($entityManager);
$form = $this->createForm(ContratType::class, $contrat, [
'pdl_choices' => $pdlChoices
]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if (!$contrat->getDuree()) {
$duration = $this->calculateDurationInMonths($contrat->getDateDebut(), $contrat->getDateFin());
$contrat->setDuree($duration);
}
// Use PricingService to calculate the contract value
$contratValue = $this->pricingService->calculateContractValue($contrat);
$contrat->setValeur($contratValue);
if ($contrat->getEntreprise() && $contrat->getEntreprise()->getUtilisateur()) {
$user = $entityManager->getRepository(User::class)->find($contrat->getEntreprise()->getUtilisateur());
if ($user) {
$contrat->setCollaborateur($user);
}
}
$entityManager->persist($contrat);
$entityManager->flush();
// Return JSON response for AJAX handling
if ($request->isXmlHttpRequest()) {
return new JsonResponse([
'success' => true,
'contractId' => $contrat->getId(),
'message' => 'Le contrat a été créé avec succès.'
]);
}
$this->addFlash('success', 'Le contrat a été créé avec succès.');
return $this->redirectToRoute('app_compta_contrat');
}
return $this->render('compta/new_contrat.html.twig', [
'contrat' => $contrat,
'form' => $form->createView(),
]);
}
/**
* @Route("/contrat/{id}/edit", name="app_compta_contrat_edit", methods={"GET","POST"})
*/
public function editContrat(Request $request, Contrat $contrat, EntityManagerInterface $entityManager): Response
{
$pdlChoices = $this->getPDLChoicesForForm($entityManager);
$form = $this->createForm(ContratType::class, $contrat, [
'pdl_choices' => $pdlChoices
]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if (!$contrat->getDuree()) {
$duration = $this->calculateDurationInMonths($contrat->getDateDebut(), $contrat->getDateFin());
$contrat->setDuree($duration);
}
// Use PricingService to calculate the contract value
$contratValue = $this->pricingService->calculateContractValue($contrat);
$contrat->setValeur($contratValue);
if ($contrat->getEntreprise() && $contrat->getEntreprise()->getUtilisateur()) {
$user = $entityManager->getRepository(User::class)->find($contrat->getEntreprise()->getUtilisateur());
if ($user) {
$contrat->setCollaborateur($user);
}
}
$entityManager->flush();
return $this->redirectToRoute('app_compta_contrat');
}
return $this->render('compta/edit_contrat.html.twig', [
'contrat' => $contrat,
'form' => $form->createView(),
]);
}
/**
* @Route("/contrat/{id}", name="app_compta_contrat_delete", methods={"POST"})
*/
public function deleteContrat(Request $request, Contrat $contrat, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('delete'.$contrat->getId(), $request->request->get('_token'))) {
$entityManager->remove($contrat);
$entityManager->flush();
}
return $this->redirectToRoute('app_compta_contrat');
}
/**
* @Route("/suivi_commercial", name="app_compta_suivit_commercial")
*/
public function suivi_commercial(Request $request, UserRepository $userRepository, ContratRepository $contratRepository): Response
{
// Récupérer uniquement les utilisateurs avec ROLE_TEAM
$collaborators = $userRepository->createQueryBuilder('u')
->where('u.roles LIKE :role')
->setParameter('role', '%ROLE_TEAM%')
->orderBy('u.username', 'ASC')
->getQuery()
->getResult();
// Récupérer les dates de la période
$dateDebut = $request->query->get('date_debut') ? new \DateTime($request->query->get('date_debut')) : new \DateTime();
$dateFin = $request->query->get('date_fin') ? new \DateTime($request->query->get('date_fin')) : new \DateTime();
// Périodes pour la durée des contrats
$contractPeriods = [12, 24, 36];
$selectedContractPeriod = (int)$request->query->get('contract_period', 12);
// Calculer les données commerciales
$commercialData = $this->calculateCommercialData(
$collaborators,
$contratRepository,
$selectedContractPeriod,
$dateDebut,
$dateFin
);
return $this->render('compta/suivi_commercial.html.twig', [
'commercial_data' => $commercialData,
'contract_periods' => $contractPeriods,
'selected_contract_period' => $selectedContractPeriod,
'date_debut' => $dateDebut,
'date_fin' => $dateFin
]);
}
private function calculateCommercialData($collaborators, ContratRepository $contratRepository, $contractPeriod, \DateTime $dateDebut, \DateTime $dateFin): array
{
$data = [];
foreach ($collaborators as $collaborator) {
$totalPrestations = 0;
// Utiliser la méthode du repository avec les dates spécifiques
$contrats = $contratRepository->findContratsByPeriod($collaborator, $dateDebut, $dateFin);
foreach ($contrats as $contrat) {
if (!$contrat->getDateSignature()) {
continue;
}
// La valeur du contrat est maintenant annuelle
$valeurAnnuelle = $contrat->getValeur();
// Calculer la date de fin en ajoutant la durée à la date de signature
$dateFinContrat = clone $contrat->getDateSignature();
$dateFinContrat->modify('+' . $contrat->getDuree() . ' months');
// Calculer le nombre d'années à prendre en compte
$annees = $contractPeriod / 12;
// Si la durée du contrat est inférieure à la période demandée,
// on ajuste la valeur proportionnellement
if ($contrat->getDuree() < $contractPeriod) {
$annees = $contrat->getDuree() / 12;
}
// Ajouter le montant total des prestations pour la période demandée
$totalPrestations += $valeurAnnuelle * $annees;
}
$data[] = [
'name' => $collaborator->getUsername(),
'total_prestations' => $totalPrestations
];
}
return $data;
}
/**
* @Route("/suivi_comptable", name="app_compta_suivit_compta")
*/
public function suivi_comptable(): Response
{
return $this->render('compta/suivi_comptable.html.twig', [
'controller_name' => 'ComptaController',
]);
}
private function calculateDurationInMonths(?\DateTimeInterface $dateDebut, ?\DateTimeInterface $dateFin): ?int
{
if (!$dateDebut || !$dateFin) {
return null;
}
$interval = $dateDebut->diff($dateFin);
return $interval->y * 12 + $interval->m;
}
/**
* @Route("/get-pdl-options/{entreprise}", name="app_compta_get_pdl_options", methods={"GET"})
*/
public function getPdlOptions(
Entreprise $entreprise,
ElectricMeterRepository $electricMeterRepository,
GasMeterRepository $gasMeterRepository
): JsonResponse
{
$pdlOptions = [];
// Fetch electric meters
$electricMeters = $electricMeterRepository->findBy(['entreprise_id' => $entreprise->getId()]);
foreach ($electricMeters as $meter) {
$pdlOptions[] = [
'pdl' => $meter->getPDL(),
'type' => 'electric',
'car' => $meter->getCAR(),
'prix_moyen' => $meter->getPrix(),
'fournisseur' => $meter->getFournisseur(),
'date_debut' => $meter->getDateDebut() ? $meter->getDateDebut()->format('d/m/Y') : null,
'date_fin' => $meter->getDateFin() ? $meter->getDateFin()->format('d/m/Y') : null,
];
}
// Fetch gas meters
$gasMeters = $gasMeterRepository->findBy(['entreprise_id' => $entreprise->getId()]);
foreach ($gasMeters as $meter) {
$pdlOptions[] = [
'pdl' => $meter->getPDL(),
'type' => 'gas',
'car' => $meter->getCAR(),
'prix_moyen' => $meter->getPrix(),
'fournisseur' => $meter->getFournisseur(),
'date_debut' => $meter->getDateDebut() ? $meter->getDateDebut()->format('d/m/Y') : null,
'date_fin' => $meter->getDateFin() ? $meter->getDateFin()->format('d/m/Y') : null,
];
}
return new JsonResponse($pdlOptions);
}
private function getPDLChoicesForForm(EntityManagerInterface $entityManager): array
{
$pdlChoices = [];
$electricMeters = $entityManager->getRepository(ElectricMeter::class)->findAll();
foreach ($electricMeters as $meter) {
$pdlChoices[$meter->getPDL()] = $meter->getPDL();
}
$gasMeters = $entityManager->getRepository(GasMeter::class)->findAll();
foreach ($gasMeters as $meter) {
$pdlChoices[$meter->getPDL()] = $meter->getPDL();
}
return $pdlChoices;
}
/**
* @Route("/get-contrats/{entreprise}", name="app_compta_get_contrats", methods={"GET"})
*/
public function getContrats(Entreprise $entreprise, ContratRepository $contratRepository): JsonResponse
{
$contrats = $contratRepository->findBy(['entreprise' => $entreprise]);
$data = [];
foreach ($contrats as $contrat) {
$data[] = [
'id' => $contrat->getId(),
'pdl' => $contrat->getPdl() ?? 'PDL non défini',
'fournisseur' => $contrat->getFournisseur() ?? 'Fournisseur non défini',
'car' => $contrat->getCar() ? number_format($contrat->getCar(), 0, ',', ' ') : '0',
'dateDebut' => $contrat->getDateDebut() ? $contrat->getDateDebut()->format('d/m/Y') : 'N/A',
'dateFin' => $contrat->getDateFin() ? $contrat->getDateFin()->format('d/m/Y') : 'N/A'
];
}
return new JsonResponse($data);
}
/**
* @Route("/factures", name="app_compta_facture")
*/
public function factures(Request $request, FactureRepository $factureRepository, EntrepriseRepository $entrepriseRepository, ContratRepository $contratRepository): Response
{
$filters = [
'dateDebut' => $request->query->get('dateDebut') ? \DateTime::createFromFormat('d/m/Y', $request->query->get('dateDebut')) : null,
'dateFin' => $request->query->get('dateFin') ? \DateTime::createFromFormat('d/m/Y', $request->query->get('dateFin')) : null,
'entreprise' => $request->query->get('entreprise'),
'contrat' => $request->query->get('contrat'),
'statut' => $request->query->get('statut'),
'sort' => $request->query->get('sort', 'dateEmission'),
'direction' => $request->query->get('direction', 'DESC')
];
return $this->render('compta/factures.html.twig', [
'factures' => $factureRepository->findByFilters($filters),
'entreprises' => $entrepriseRepository->findAll(),
'contrats' => $contratRepository->findAll(),
]);
}
/**
* @Route("/facture/new", name="app_compta_facture_new", methods={"GET","POST"})
*/
public function newFacture(Request $request, EntityManagerInterface $entityManager): Response
{
$facture = new Facture();
$form = $this->createForm(FactureType::class, $facture);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
try {
$entityManager->persist($facture);
$entityManager->flush();
$this->addFlash('success', 'La facture a été créée avec succès.');
return $this->redirectToRoute('app_compta_facture');
} catch (\Exception $e) {
$this->addFlash('error', 'Une erreur est survenue lors de la création de la facture : ' . $e->getMessage());
}
}
return $this->render('compta/facture/new.html.twig', [
'facture' => $facture,
'form' => $form->createView(),
]);
}
/**
* @Route("/facture/{id}", name="app_compta_facture_show", methods={"GET"})
*/
public function showFacture(Facture $facture): Response
{
return $this->render('compta/facture/show.html.twig', [
'facture' => $facture,
]);
}
/**
* @Route("/facture/{id}/edit", name="app_compta_facture_edit", methods={"GET","POST"})
*/
public function editFacture(Request $request, Facture $facture, EntityManagerInterface $entityManager): Response
{
$form = $this->createForm(FactureType::class, $facture);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->flush();
$this->addFlash('success', 'La facture a été modifiée avec succès.');
return $this->redirectToRoute('app_compta_facture');
}
return $this->render('compta/facture/edit.html.twig', [
'facture' => $facture,
'form' => $form->createView(),
]);
}
/**
* @Route("/facture/{id}", name="app_compta_facture_delete", methods={"POST"})
*/
public function deleteFacture(Request $request, Facture $facture, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('delete'.$facture->getId(), $request->request->get('_token'))) {
$entityManager->remove($facture);
$entityManager->flush();
$this->addFlash('success', 'La facture a été supprimée avec succès.');
}
return $this->redirectToRoute('app_compta_facture');
}
/**
* @Route("/facture/new-from-contract/{id}", name="app_compta_facture_new_from_contract", methods={"GET","POST"})
*/
public function newFactureFromContract(Request $request, Contrat $contrat, EntityManagerInterface $entityManager): Response
{
$facture = new Facture();
// Pre-fill invoice data from contract
$facture->setEntreprise($contrat->getEntreprise());
$facture->setContrat($contrat);
$facture->setDateEmission(new \DateTime());
// Set due date to 30 days after emission
$dueDate = new \DateTime();
$dueDate->modify('+30 days');
$facture->setDateEcheance($dueDate);
// Calculate monthly amount (annual value divided by 12)
$montantHT = $contrat->getValeur() / 12;
$montantTVA = $montantHT * 0.20; // 20% TVA
$montantTTC = $montantHT + $montantTVA;
$facture->setMontantHT(number_format($montantHT, 2, '.', ''));
$facture->setMontantTVA(number_format($montantTVA, 2, '.', ''));
$facture->setMontantTTC(number_format($montantTTC, 2, '.', ''));
// Generate invoice number (YYYYMM-XXX)
$numero = date('Ym') . '-' . sprintf('%03d', rand(1, 999));
$facture->setNumero($numero);
// Set initial status
$facture->setStatut('En attente');
$form = $this->createForm(FactureType::class, $facture);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->persist($facture);
$entityManager->flush();
$this->addFlash('success', 'La facture a été créée avec succès.');
return $this->redirectToRoute('app_compta_facture');
}
return $this->render('compta/facture/new.html.twig', [
'facture' => $facture,
'form' => $form->createView(),
'from_contract' => true
]);
}
}