fix: SonarQube - ComptaExportService split 24->14 methodes + DocuSeal constantes

ComptaHelperService (nouveau) :
- 12 methodes extraites de ComptaExportService : resolvePeriod,
  exportResponse, getFactures, resolveCompteBanque, resolveLibelleBanque,
  resolveTrancheAge, resolveCustomerInfo, getServiceGroups,
  aggregateServiceGroup, resolveStatutRentabilite

ComptaExportService :
- 24 -> 14 methodes (sous la limite de 20)
- Injection ComptaHelperService dans constructeur
- Delegation des appels utilitaires vers helper

DocuSealService :
- PDF_BASE64_PREFIX constante (3 occurrences)
- ROLE_FIRST_PARTY constante (3 occurrences)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-04-08 14:28:12 +02:00
parent b9d179579c
commit a0832e05c3
5 changed files with 262 additions and 243 deletions

View File

@@ -5,6 +5,7 @@ namespace App\Controller\Admin;
use App\Entity\AdvertPayment;
use App\Entity\Facture;
use App\Service\ComptaExportService;
use App\Service\ComptaHelperService;
use App\Service\DocuSealService;
use App\Service\MailerService;
use App\Service\Pdf\ComptaPdf;
@@ -31,6 +32,7 @@ class ComptabiliteController extends AbstractController
#[Autowire(env: 'DOCUSEAL_URL')]
private string $docuSealUrl,
private ComptaExportService $exportService,
private ComptaHelperService $helper,
) {
}
@@ -46,10 +48,10 @@ class ComptabiliteController extends AbstractController
#[Route('/export/journal-ventes', name: 'export_journal_ventes')]
public function exportJournalVentes(Request $request): Response
{
[$from, $to] = $this->exportService->resolvePeriod($request);
[$from, $to] = $this->helper->resolvePeriod($request);
$format = $request->query->getString('format', 'csv');
return $this->exportService->exportResponse(
return $this->helper->exportResponse(
$this->exportService->buildJournalVentesData($from, $to),
'journal_ventes_'.$from->format('Ymd').'_'.$to->format('Ymd'),
$format,
@@ -62,10 +64,10 @@ class ComptabiliteController extends AbstractController
#[Route('/export/grand-livre', name: 'export_grand_livre')]
public function exportGrandLivre(Request $request): Response
{
[$from, $to] = $this->exportService->resolvePeriod($request);
[$from, $to] = $this->helper->resolvePeriod($request);
$format = $request->query->getString('format', 'csv');
return $this->exportService->exportResponse(
return $this->helper->exportResponse(
$this->exportService->buildGrandLivreData($from, $to),
'grand_livre_clients_'.$from->format('Ymd').'_'.$to->format('Ymd'),
$format,
@@ -78,10 +80,10 @@ class ComptabiliteController extends AbstractController
#[Route('/export/fec', name: 'export_fec')]
public function exportFec(Request $request): Response
{
[$from, $to] = $this->exportService->resolvePeriod($request);
[$from, $to] = $this->helper->resolvePeriod($request);
$format = $request->query->getString('format', 'csv');
return $this->exportService->exportResponse(
return $this->helper->exportResponse(
$this->exportService->buildFecData($from, $to),
'FEC_'.$from->format('Ymd').'_'.$to->format('Ymd'),
$format,
@@ -96,7 +98,7 @@ class ComptabiliteController extends AbstractController
{
$format = $request->query->getString('format', 'csv');
return $this->exportService->exportResponse(
return $this->helper->exportResponse(
$this->exportService->buildBalanceAgeeData(),
'balance_agee_'.(new \DateTimeImmutable())->format('Ymd'),
$format,
@@ -109,10 +111,10 @@ class ComptabiliteController extends AbstractController
#[Route('/export/reglements', name: 'export_reglements')]
public function exportReglements(Request $request): Response
{
[$from, $to] = $this->exportService->resolvePeriod($request);
[$from, $to] = $this->helper->resolvePeriod($request);
$format = $request->query->getString('format', 'csv');
return $this->exportService->exportResponse(
return $this->helper->exportResponse(
$this->exportService->buildReglementsData($from, $to),
'reglements_'.$from->format('Ymd').'_'.$to->format('Ymd'),
$format,
@@ -125,7 +127,7 @@ class ComptabiliteController extends AbstractController
#[Route('/export-pdf/{type}', name: 'export_pdf', requirements: ['type' => 'journal-ventes|grand-livre|fec|balance-agee|reglements|commissions-stripe|couts-services'])]
public function exportPdf(string $type, Request $request): Response
{
[$from, $to] = $this->exportService->resolvePeriod($request);
[$from, $to] = $this->helper->resolvePeriod($request);
$titleMap = $this->getExportTitleMap();
@@ -152,7 +154,7 @@ class ComptabiliteController extends AbstractController
#[Route('/export-pdf/{type}/sign', name: 'export_pdf_sign', requirements: ['type' => 'journal-ventes|grand-livre|fec|balance-agee|reglements|commissions-stripe|couts-services'])]
public function exportPdfSign(string $type, Request $request, DocuSealService $docuSeal): Response
{
[$from, $to] = $this->exportService->resolvePeriod($request);
[$from, $to] = $this->helper->resolvePeriod($request);
$titleMap = $this->getExportTitleMap();
@@ -244,10 +246,10 @@ class ComptabiliteController extends AbstractController
#[Route('/export/commissions-stripe', name: 'export_commissions_stripe')]
public function exportCommissionsStripe(Request $request): Response
{
[$from, $to] = $this->exportService->resolvePeriod($request);
[$from, $to] = $this->helper->resolvePeriod($request);
$format = $request->query->getString('format', 'csv');
return $this->exportService->exportResponse(
return $this->helper->exportResponse(
$this->exportService->buildCommissionsStripeData($from, $to),
'commissions_stripe_'.$from->format('Ymd').'_'.$to->format('Ymd'),
$format,
@@ -260,10 +262,10 @@ class ComptabiliteController extends AbstractController
#[Route('/export/couts-services', name: 'export_couts_services')]
public function exportCoutsServices(Request $request): Response
{
[$from, $to] = $this->exportService->resolvePeriod($request);
[$from, $to] = $this->helper->resolvePeriod($request);
$format = $request->query->getString('format', 'csv');
return $this->exportService->exportResponse(
return $this->helper->exportResponse(
$this->exportService->buildCoutsServicesData($from, $to),
'couts_services_'.$from->format('Ymd').'_'.$to->format('Ymd'),
$format,
@@ -278,7 +280,7 @@ class ComptabiliteController extends AbstractController
#[Route('/rapport-financier', name: 'rapport_financier')]
public function rapportFinancier(Request $request): Response
{
[$from, $to] = $this->exportService->resolvePeriod($request);
[$from, $to] = $this->helper->resolvePeriod($request);
$periodFrom = $from->format(ComptaExportService::DATE_FORMAT_FR);
$periodTo = $to->format(ComptaExportService::DATE_FORMAT_FR);
@@ -313,7 +315,7 @@ class ComptabiliteController extends AbstractController
#[Route('/rapport-financier/sign', name: 'rapport_financier_sign')]
public function rapportFinancierSign(Request $request, DocuSealService $docuSeal): Response
{
[$from, $to] = $this->exportService->resolvePeriod($request);
[$from, $to] = $this->helper->resolvePeriod($request);
$periodFrom = $from->format(ComptaExportService::DATE_FORMAT_FR);
$periodTo = $to->format(ComptaExportService::DATE_FORMAT_FR);

View File

@@ -7,11 +7,6 @@ use App\Entity\Facture;
use App\Entity\FacturePrestataire;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
class ComptaExportService
{
public const LABEL_JOURNAL_VENTES = 'Journal des ventes';
@@ -20,7 +15,6 @@ class ComptaExportService
public const DATE_FORMAT_FR = 'd/m/Y';
private const DQL_BETWEEN_DATES = 'f.createdAt BETWEEN :from AND :to';
private const DQL_IS_PAID = 'f.isPaid = true';
private const LABEL_CLIENT_DELETED = 'Client supprime';
private const PREFIX_FACTURE = 'Facture ';
public const SERVICE_COSTS = [
@@ -39,6 +33,7 @@ class ComptaExportService
private EntityManagerInterface $em,
#[Autowire('%env(bool:TVA_ENABLED)%')]
private bool $tvaEnabled,
private ComptaHelperService $helper,
) {
}
@@ -67,12 +62,12 @@ class ComptaExportService
public function buildJournalVentesData(\DateTimeImmutable $from, \DateTimeImmutable $to): array
{
$tvaEnabled = $this->tvaEnabled;
$factures = $this->getFactures($from, $to);
$factures = $this->helper->getFactures($from, $to);
$rows = [];
foreach ($factures as $facture) {
$customer = $facture->getCustomer();
[$codeComptable, $raisonSociale] = $this->resolveCustomerInfo($customer);
[$codeComptable, $raisonSociale] = $this->helper->resolveCustomerInfo($customer);
$row = [
'JournalCode' => 'VE',
@@ -115,13 +110,13 @@ class ComptaExportService
$rows[] = $rowClient;
if ($facture->isPaid() && null !== $facture->getPaidAt()) {
$compteBanque = $this->resolveCompteBanque($facture->getPaidMethod());
$compteBanque = $this->helper->resolveCompteBanque($facture->getPaidMethod());
$rowPay = $row;
$rowPay['JournalCode'] = 'BQ';
$rowPay['JournalLib'] = 'Journal de banque';
$rowPay['EcritureDate'] = $facture->getPaidAt()->format('Y-m-d');
$rowPay['CompteNum'] = $compteBanque;
$rowPay['CompteLib'] = $this->resolveLibelleBanque($facture->getPaidMethod());
$rowPay['CompteLib'] = $this->helper->resolveLibelleBanque($facture->getPaidMethod());
$rowPay['EcritureLib'] = 'Reglement Facture '.$facture->getInvoiceNumber().' - '.$raisonSociale;
$rowPay['Debit'] = $tvaEnabled ? $facture->getTotalTtc() : $facture->getTotalHt();
$rowPay['Credit'] = '0.00';
@@ -148,12 +143,12 @@ class ComptaExportService
public function buildGrandLivreData(\DateTimeImmutable $from, \DateTimeImmutable $to): array
{
$tvaEnabled = $this->tvaEnabled;
$factures = $this->getFactures($from, $to);
$factures = $this->helper->getFactures($from, $to);
$rows = [];
foreach ($factures as $facture) {
$customer = $facture->getCustomer();
[$codeComptable, $raisonSociale] = $this->resolveCustomerInfo($customer);
[$codeComptable, $raisonSociale] = $this->helper->resolveCustomerInfo($customer);
$montant = $tvaEnabled ? $facture->getTotalTtc() : $facture->getTotalHt();
$rows[] = [
@@ -184,13 +179,13 @@ class ComptaExportService
public function buildFecData(\DateTimeImmutable $from, \DateTimeImmutable $to): array
{
$tvaEnabled = $this->tvaEnabled;
$factures = $this->getFactures($from, $to);
$factures = $this->helper->getFactures($from, $to);
$rows = [];
$ecritureNum = 1;
foreach ($factures as $facture) {
$customer = $facture->getCustomer();
[$codeComptable, $raisonSociale] = $this->resolveCustomerInfo($customer);
[$codeComptable, $raisonSociale] = $this->helper->resolveCustomerInfo($customer);
$numEcriture = sprintf('VE%06d', $ecritureNum);
$rows[] = [
@@ -285,7 +280,7 @@ class ComptaExportService
$rows = [];
foreach ($factures as $facture) {
$customer = $facture->getCustomer();
[$codeComptable, $raisonSociale] = $this->resolveCustomerInfo($customer);
[$codeComptable, $raisonSociale] = $this->helper->resolveCustomerInfo($customer);
$joursRetard = $facture->getCreatedAt()->diff($now)->days;
$rows[] = [
@@ -297,7 +292,7 @@ class ComptaExportService
'MontantTVA' => $tvaEnabled ? $facture->getTotalTva() : '0.00',
'MontantTTC' => $tvaEnabled ? $facture->getTotalTtc() : $facture->getTotalHt(),
'JoursRetard' => (string) $joursRetard,
'Tranche' => $this->resolveTrancheAge($joursRetard),
'Tranche' => $this->helper->resolveTrancheAge($joursRetard),
'Statut' => $facture->getState(),
];
}
@@ -326,7 +321,7 @@ class ComptaExportService
$rows = [];
foreach ($factures as $facture) {
$customer = $facture->getCustomer();
[$codeComptable, $raisonSociale] = $this->resolveCustomerInfo($customer);
[$codeComptable, $raisonSociale] = $this->helper->resolveCustomerInfo($customer);
$rows[] = [
'DateReglement' => $facture->getPaidAt()->format('Y-m-d'),
@@ -337,7 +332,7 @@ class ComptaExportService
'MontantTVA' => $tvaEnabled ? $facture->getTotalTva() : '0.00',
'MontantTTC' => $tvaEnabled ? $facture->getTotalTtc() : $facture->getTotalHt(),
'MethodePaiement' => $facture->getPaidMethod() ?? 'inconnu',
'CompteBanque' => $this->resolveCompteBanque($facture->getPaidMethod()),
'CompteBanque' => $this->helper->resolveCompteBanque($facture->getPaidMethod()),
];
}
@@ -389,14 +384,14 @@ class ComptaExportService
public function buildCoutsServicesData(\DateTimeImmutable $from, \DateTimeImmutable $to): array
{
$grouped = $this->groupFactureLinesByType($from, $to);
$serviceGroups = $this->getServiceGroups();
$serviceGroups = $this->helper->getServiceGroups();
$rows = [];
$totalCout = 0.0;
$totalCa = 0.0;
foreach ($serviceGroups as $group) {
[$caHt, $cout] = $this->aggregateServiceGroup($group, $grouped);
[$caHt, $cout] = $this->helper->aggregateServiceGroup($group, $grouped);
$marge = $caHt - $cout;
$totalCout += $cout;
$totalCa += $caHt;
@@ -406,7 +401,7 @@ class ComptaExportService
'CA_HT' => number_format($caHt, 2, '.', ''),
'Cout_Ecosplay' => number_format($cout, 2, '.', ''),
'Marge' => number_format($marge, 2, '.', ''),
'Statut' => $this->resolveStatutRentabilite($caHt, $marge),
'Statut' => $this->helper->resolveStatutRentabilite($caHt, $marge),
];
}
@@ -528,49 +523,14 @@ class ComptaExportService
$grouped = $this->groupFactureLinesByTypeFromList($factures);
$cost = 0.0;
foreach ($this->getServiceGroups() as $group) {
[, $cout] = $this->aggregateServiceGroup($group, $grouped);
foreach ($this->helper->getServiceGroups() as $group) {
[, $cout] = $this->helper->aggregateServiceGroup($group, $grouped);
$cost += $cout;
}
return $cost;
}
/**
* @return array<string, array{types: list<string>, name: string}>
*/
public function getServiceGroups(): array
{
return [
'esite' => ['types' => ['website', 'hosting', 'maintenance'], 'name' => 'E-Site'],
'esymail' => ['types' => ['esymail'], 'name' => 'E-Mail'],
'ndd' => ['types' => ['ndd'], 'name' => 'Nom de domaine'],
'other' => ['types' => ['other'], 'name' => 'Autre'],
];
}
/**
* Calcule le CA HT et le cout pour un groupe de services.
*
* @param array<string, mixed> $group
* @param array<string, array{ca_ht: float, lines: int}> $grouped
*
* @return array{float, float} [caHt, cout]
*/
public function aggregateServiceGroup(array $group, array $grouped): array
{
$caHt = 0.0;
$cout = 0.0;
foreach ($group['types'] as $type) {
$caHt += $grouped[$type]['ca_ht'];
$config = self::SERVICE_COSTS[$type];
$cout += $config['cout'] + (($config['cout_par_ligne'] ?? 0.0) * $grouped[$type]['lines']);
}
return [$caHt, $cout];
}
/**
* Ajoute les lignes prestataires aux resultats et met a jour le cout total.
*
@@ -609,154 +569,4 @@ class ComptaExportService
}
}
/**
* Resout le statut de rentabilite (evite les ternaires imbriques).
*/
private function resolveStatutRentabilite(float $caHt, float $marge): string
{
if ($caHt <= 0) {
return 'Inactif';
}
return $marge >= 0 ? 'Rentable' : 'Negatif';
}
/**
* @return list<Facture>
*/
public function getFactures(\DateTimeImmutable $from, \DateTimeImmutable $to): array
{
return $this->em->createQueryBuilder()
->select('f')
->from(Facture::class, 'f')
->where(self::DQL_BETWEEN_DATES)
->andWhere('f.state != :cancel')
->setParameter('from', $from)
->setParameter('to', $to)
->setParameter('cancel', Facture::STATE_CANCEL)
->orderBy('f.createdAt', 'ASC')
->getQuery()
->getResult();
}
/**
* @return array{\DateTimeImmutable, \DateTimeImmutable}
*/
public function resolvePeriod(Request $request): array
{
$period = $request->query->getString('period', 'current');
$now = new \DateTimeImmutable();
if ('custom' === $period) {
$dateFrom = $request->query->getString('from', $now->format('Y-m-01'));
$dateTo = $request->query->getString('to', $now->format('Y-m-d'));
} elseif ('previous' === $period) {
$prev = $now->modify('first day of last month');
$dateFrom = $prev->format('Y-m-01');
$dateTo = $prev->modify('last day of this month')->format('Y-m-d');
} else {
// current
$dateFrom = $now->format('Y-m-01');
$dateTo = $now->format('Y-m-d');
}
return [
new \DateTimeImmutable($dateFrom),
new \DateTimeImmutable($dateTo.' 23:59:59'),
];
}
/**
* @param list<array<string, string>> $rows
*/
public function exportResponse(array $rows, string $filename, string $format): Response
{
if ('json' === $format) {
return new JsonResponse($rows, 200, [
'Content-Disposition' => 'attachment; filename="'.$filename.'.json"',
]);
}
// CSV compatible SAGE (separateur ;, encodage UTF-8 BOM)
// @codeCoverageIgnoreStart
$response = new StreamedResponse(function () use ($rows) {
$handle = fopen('php://output', 'w');
// BOM UTF-8 pour Excel/SAGE
fwrite($handle, "\xEF\xBB\xBF");
if (!empty($rows)) {
// En-tetes
fputcsv($handle, array_keys($rows[0]), ';');
// Donnees
foreach ($rows as $row) {
fputcsv($handle, $row, ';');
}
}
fclose($handle);
});
// @codeCoverageIgnoreEnd
$response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
$response->headers->set('Content-Disposition', 'attachment; filename="'.$filename.'.csv"');
return $response;
}
public function resolveCompteBanque(?string $method): string
{
return match ($method) {
'card' => '512100',
'sepa_debit' => '512100',
'paypal' => '512200',
'klarna' => '512300',
'transfer', 'virement' => '512000',
default => '512000',
};
}
public function resolveLibelleBanque(?string $method): string
{
return match ($method) {
'card' => 'Stripe CB',
'sepa_debit' => 'Stripe SEPA',
'paypal' => 'PayPal',
'klarna' => 'Klarna',
'transfer', 'virement' => 'Virement bancaire',
default => 'Banque',
};
}
/**
* Resout le code comptable et la raison sociale d'un client de facture.
*
* @return array{string, string} [codeComptable, raisonSociale]
*/
private function resolveCustomerInfo(?object $customer): array
{
$codeComptable = $customer?->getCodeComptable() ?? 'DIVERS';
$raisonSociale = $customer?->getRaisonSociale()
?? ($customer ? $customer->getFirstName().' '.$customer->getLastName() : self::LABEL_CLIENT_DELETED);
return [$codeComptable, $raisonSociale];
}
public function resolveTrancheAge(int $jours): string
{
$tranches = [
30 => '0-30 jours',
60 => '31-60 jours',
90 => '61-90 jours',
];
foreach ($tranches as $limit => $label) {
if ($jours <= $limit) {
return $label;
}
}
return '+90 jours';
}
}

View File

@@ -0,0 +1,195 @@
<?php
namespace App\Service;
use App\Entity\Facture;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* Helpers utilitaires pour les exports comptables (periodes, formats, labels).
*/
class ComptaHelperService
{
public const LABEL_CLIENT_DELETED = 'Client supprime';
public function __construct(
private EntityManagerInterface $em,
) {
}
/**
* @return array{\DateTimeImmutable, \DateTimeImmutable}
*/
public function resolvePeriod(Request $request): array
{
$period = $request->query->getString('period', 'current');
$now = new \DateTimeImmutable();
if ('custom' === $period) {
$dateFrom = $request->query->getString('from', $now->format('Y-m-01'));
$dateTo = $request->query->getString('to', $now->format('Y-m-d'));
} elseif ('previous' === $period) {
$prev = $now->modify('first day of last month');
$dateFrom = $prev->format('Y-m-01');
$dateTo = $prev->modify('last day of this month')->format('Y-m-d');
} else {
$dateFrom = $now->format('Y-m-01');
$dateTo = $now->format('Y-m-d');
}
return [
new \DateTimeImmutable($dateFrom),
new \DateTimeImmutable($dateTo.' 23:59:59'),
];
}
/**
* @param list<array<string, string>> $rows
*/
public function exportResponse(array $rows, string $filename, string $format): Response
{
if ('json' === $format) {
return new JsonResponse($rows, 200, [
'Content-Disposition' => 'attachment; filename="'.$filename.'.json"',
]);
}
// @codeCoverageIgnoreStart
$response = new StreamedResponse(function () use ($rows) {
$handle = fopen('php://output', 'w');
fwrite($handle, "\xEF\xBB\xBF");
if (!empty($rows)) {
fputcsv($handle, array_keys($rows[0]), ';');
foreach ($rows as $row) {
fputcsv($handle, $row, ';');
}
}
fclose($handle);
});
// @codeCoverageIgnoreEnd
$response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
$response->headers->set('Content-Disposition', 'attachment; filename="'.$filename.'.csv"');
return $response;
}
/**
* @return list<Facture>
*/
public function getFactures(\DateTimeImmutable $from, \DateTimeImmutable $to): array
{
return $this->em->createQueryBuilder()
->select('f')
->from(Facture::class, 'f')
->where('f.createdAt BETWEEN :from AND :to')
->andWhere('f.state != :cancel')
->setParameter('from', $from)
->setParameter('to', $to)
->setParameter('cancel', Facture::STATE_CANCEL)
->orderBy('f.createdAt', 'ASC')
->getQuery()
->getResult();
}
public function resolveCompteBanque(?string $method): string
{
return match ($method) {
'card' => '512100',
'sepa_debit' => '512100',
'paypal' => '512200',
'klarna' => '512300',
'transfer', 'virement' => '512000',
default => '512000',
};
}
public function resolveLibelleBanque(?string $method): string
{
return match ($method) {
'card' => 'Stripe CB',
'sepa_debit' => 'Stripe SEPA',
'paypal' => 'PayPal',
'klarna' => 'Klarna',
'transfer', 'virement' => 'Virement bancaire',
default => 'Banque',
};
}
public function resolveTrancheAge(int $jours): string
{
$tranches = [
30 => '0-30 jours',
60 => '31-60 jours',
90 => '61-90 jours',
];
foreach ($tranches as $limit => $label) {
if ($jours <= $limit) {
return $label;
}
}
return '+90 jours';
}
/**
* @return array{string, string} [codeComptable, raisonSociale]
*/
public function resolveCustomerInfo(?object $customer): array
{
$codeComptable = $customer?->getCodeComptable() ?? 'DIVERS';
$raisonSociale = $customer?->getRaisonSociale()
?? ($customer ? $customer->getFirstName().' '.$customer->getLastName() : self::LABEL_CLIENT_DELETED);
return [$codeComptable, $raisonSociale];
}
/**
* @return array<string, array{types: list<string>, name: string}>
*/
public function getServiceGroups(): array
{
return [
'esite' => ['types' => ['website', 'hosting', 'maintenance'], 'name' => 'E-Site'],
'esymail' => ['types' => ['esymail'], 'name' => 'E-Mail'],
'ndd' => ['types' => ['ndd'], 'name' => 'Nom de domaine'],
'other' => ['types' => ['other'], 'name' => 'Autre'],
];
}
/**
* @param array<string, mixed> $group
* @param array<string, array{ca_ht: float, lines: int}> $grouped
*
* @return array{float, float} [caHt, cout]
*/
public function aggregateServiceGroup(array $group, array $grouped): array
{
$caHt = 0.0;
$cout = 0.0;
foreach ($group['types'] as $type) {
$caHt += $grouped[$type]['ca_ht'];
$config = ComptaExportService::SERVICE_COSTS[$type];
$cout += $config['cout'] + (($config['cout_par_ligne'] ?? 0.0) * $grouped[$type]['lines']);
}
return [$caHt, $cout];
}
public function resolveStatutRentabilite(float $caHt, float $marge): string
{
if ($caHt <= 0) {
return 'Inactif';
}
return $marge >= 0 ? 'Rentable' : 'Negatif';
}
}

View File

@@ -12,6 +12,9 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
class DocuSealService
{
private const PDF_BASE64_PREFIX = 'data:application/pdf;base64,';
private const ROLE_FIRST_PARTY = 'First Party';
private Api $api;
public function __construct(
@@ -48,14 +51,14 @@ class DocuSealService
'documents' => [
[
'name' => 'attestation-'.$attestation->getReference().'.pdf',
'file' => 'data:application/pdf;base64,'.$pdfBase64,
'file' => self::PDF_BASE64_PREFIX.$pdfBase64,
],
],
'submitters' => [
[
'email' => 'contact@e-cosplay.fr',
'name' => 'Association E-Cosplay',
'role' => 'First Party',
'role' => self::ROLE_FIRST_PARTY,
'completed' => true,
'send_email' => false,
'values' => [
@@ -118,7 +121,7 @@ class DocuSealService
$submitter = [
'email' => $customer->getEmail(),
'name' => $customer->getFullName(),
'role' => 'First Party',
'role' => self::ROLE_FIRST_PARTY,
'send_email' => false,
'metadata' => [
'doc_type' => 'devis',
@@ -138,7 +141,7 @@ class DocuSealService
'documents' => [
[
'name' => 'devis-'.str_replace('/', '-', $numOrder).'.pdf',
'file' => 'data:application/pdf;base64,'.$pdfBase64,
'file' => self::PDF_BASE64_PREFIX.$pdfBase64,
],
],
'submitters' => [$submitter],
@@ -331,7 +334,7 @@ class DocuSealService
$submitter = [
'email' => $signerEmail,
'name' => $signerName,
'role' => 'First Party',
'role' => self::ROLE_FIRST_PARTY,
'send_email' => false,
'metadata' => [
'doc_type' => 'comptabilite',
@@ -352,7 +355,7 @@ class DocuSealService
'documents' => [
[
'name' => str_replace(' ', '_', $documentName).'.pdf',
'file' => 'data:application/pdf;base64,'.$pdfBase64,
'file' => self::PDF_BASE64_PREFIX.$pdfBase64,
],
],
'submitters' => [$submitter],

View File

@@ -61,14 +61,23 @@ class ComptabiliteControllerTest extends TestCase
return $kernel;
}
private function buildHelper(?EntityManagerInterface $em = null): \App\Service\ComptaHelperService
{
return new \App\Service\ComptaHelperService($em ?? $this->buildEmWithQueryBuilder());
}
private function buildExportService(?EntityManagerInterface $em = null): ComptaExportService
{
return new ComptaExportService($em ?? $this->buildEmWithQueryBuilder(), false);
$emInstance = $em ?? $this->buildEmWithQueryBuilder();
return new ComptaExportService($emInstance, false, new \App\Service\ComptaHelperService($emInstance));
}
private function buildExportServiceWithTva(?EntityManagerInterface $em = null): ComptaExportService
{
return new ComptaExportService($em ?? $this->buildEmWithQueryBuilder(), true);
$emInstance = $em ?? $this->buildEmWithQueryBuilder();
return new ComptaExportService($emInstance, true, new \App\Service\ComptaHelperService($emInstance));
}
/**
@@ -80,7 +89,7 @@ class ComptabiliteControllerTest extends TestCase
$em = $this->buildEmWithQueryBuilder();
$kernel = $this->buildKernel();
$svc = $exportService ?? $this->buildExportService($em);
$controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc);
$controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc, $this->buildHelper($em));
$session = new Session(new MockArraySessionStorage());
$stack = $this->createStub(RequestStack::class);
@@ -131,7 +140,7 @@ class ComptabiliteControllerTest extends TestCase
$kernel = $this->buildKernel();
$svc = $exportService ?? $this->buildExportService($em);
$controller = new ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc);
$controller = new ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc, $this->buildHelper($em));
$session = new Session(new MockArraySessionStorage());
$stack = $this->createStub(RequestStack::class);
@@ -553,8 +562,8 @@ class ComptabiliteControllerTest extends TestCase
{
$em = !empty($otherData) ? $this->buildEmWithData($emData, $otherData) : $this->buildEmWithData($emData);
$kernel = $this->buildKernel();
$svc = new ComptaExportService($em, false);
$controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc);
$svc = new ComptaExportService($em, false, new \App\Service\ComptaHelperService($em));
$controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc, $this->buildHelper($em));
$session = new Session(new MockArraySessionStorage());
$stack = $this->createStub(\Symfony\Component\HttpFoundation\RequestStack::class);
@@ -593,8 +602,8 @@ class ComptabiliteControllerTest extends TestCase
{
$em = $this->buildEmWithData($emData);
$kernel = $this->buildKernel();
$svc = new ComptaExportService($em, true);
$controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc);
$svc = new ComptaExportService($em, true, new \App\Service\ComptaHelperService($em));
$controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc, $this->buildHelper($em));
$session = new Session(new MockArraySessionStorage());
$stack = $this->createStub(\Symfony\Component\HttpFoundation\RequestStack::class);
@@ -782,8 +791,8 @@ class ComptabiliteControllerTest extends TestCase
$em->method('createQueryBuilder')->willReturn($qb);
$kernel = $this->buildKernel();
$svc = new ComptaExportService($em, false);
$controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc);
$svc = new ComptaExportService($em, false, new \App\Service\ComptaHelperService($em));
$controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc, $this->buildHelper($em));
$session = new Session(new MockArraySessionStorage());
$stack = $this->createStub(\Symfony\Component\HttpFoundation\RequestStack::class);
@@ -1397,8 +1406,8 @@ class ComptabiliteControllerTest extends TestCase
$em->method('createQueryBuilder')->willReturn($qb);
$kernel = $this->buildKernel();
$svc = new ComptaExportService($em, false);
$controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc);
$svc = new ComptaExportService($em, false, new \App\Service\ComptaHelperService($em));
$controller = new \App\Controller\Admin\ComptabiliteController($em, $kernel, 'http://docuseal.example', $svc, $this->buildHelper($em));
$session = new Session(new MockArraySessionStorage());
$stack = $this->createStub(\Symfony\Component\HttpFoundation\RequestStack::class);