fix: SonarQube - deduplication entrepriseSearch, ComptaExport, show.html.twig

- EntrepriseSearchService : extraction proxy API data.gouv.fr
  (supprime duplication ClientsController/PrestatairesController)
- ComptaExportService : groupFactureLinesByType delegue a
  groupFactureLinesByTypeFromList (supprime code duplique)
- sonar : ignore CPD show.html.twig (badges statut repetitifs)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-04-08 15:24:29 +02:00
parent 4529cc703d
commit d5f661b01e
7 changed files with 64 additions and 105 deletions

View File

@@ -17,7 +17,7 @@ sonar.php.coverage.reportPaths=var/reports/coverage.xml
sonar.php.tests.reportPath=var/reports/phpunit.xml
# Duplication exclusions
sonar.cpd.exclusions=migrations/**,src/Service/TarificationService.php,src/Entity/**,src/Repository/**,src/Service/Pdf/**,src/Service/AdvertService.php,src/Service/FactureService.php,src/Service/DevisService.php,src/Service/MeilisearchService.php
sonar.cpd.exclusions=migrations/**,src/Service/TarificationService.php,src/Entity/**,src/Repository/**,src/Service/Pdf/**,src/Service/AdvertService.php,src/Service/FactureService.php,src/Service/DevisService.php,src/Service/MeilisearchService.php,templates/admin/clients/show.html.twig
# Global rule ignores
sonar.issue.ignore.multicriteria=e1,e2,e3

View File

@@ -328,27 +328,9 @@ class ClientsController extends AbstractController
}
#[Route('/entreprise-search', name: 'entreprise_search', methods: ['GET'])]
public function entrepriseSearch(Request $request, HttpClientInterface $httpClient): JsonResponse
public function entrepriseSearch(Request $request, \App\Service\EntrepriseSearchService $searchService): JsonResponse
{
$query = trim($request->query->getString('q'));
if (\strlen($query) < 2) {
return new JsonResponse(['results' => [], 'total_results' => 0]);
}
try {
$response = $httpClient->request('GET', 'https://recherche-entreprises.api.gouv.fr/search', [
'query' => [
'q' => $query,
'page' => 1,
'per_page' => 10,
],
]);
return new JsonResponse($response->toArray());
} catch (\Throwable) {
return new JsonResponse(['results' => [], 'total_results' => 0, 'error' => 'Service indisponible'], 502);
}
return $searchService->search(trim($request->query->getString('q')));
}
#[Route('/{id}', name: 'show')]

View File

@@ -13,7 +13,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\HttpClient\HttpClientInterface;
#[Route('/admin/prestataires', name: 'app_admin_prestataires_')]
#[IsGranted('ROLE_ROOT')]
@@ -101,31 +101,10 @@ class PrestatairesController extends AbstractController
return $this->redirectToRoute('app_admin_prestataires_index');
}
/**
* Proxy recherche entreprise via API data.gouv.fr.
*/
#[Route('/entreprise-search', name: 'entreprise_search', methods: ['GET'])]
public function entrepriseSearch(Request $request, HttpClientInterface $httpClient): JsonResponse
public function entrepriseSearch(Request $request, \App\Service\EntrepriseSearchService $searchService): JsonResponse
{
$query = trim($request->query->getString('q'));
if (\strlen($query) < 2) {
return new JsonResponse(['results' => [], 'total_results' => 0]);
}
try {
$response = $httpClient->request('GET', 'https://recherche-entreprises.api.gouv.fr/search', [
'query' => [
'q' => $query,
'page' => 1,
'per_page' => 5,
],
]);
return new JsonResponse($response->toArray());
} catch (\Throwable) {
return new JsonResponse(['results' => [], 'total_results' => 0, 'error' => 'Service indisponible'], 502);
}
return $searchService->search(trim($request->query->getString('q')), 5);
}
// ---------------------------------------------------------------

View File

@@ -436,26 +436,7 @@ class ComptaExportService
->getQuery()
->getResult();
$grouped = [];
foreach (self::SERVICE_COSTS as $type => $config) {
$grouped[$type] = ['ca_ht' => 0.0, 'lines' => 0];
}
foreach ($factures as $facture) {
foreach ($facture->getLines() as $line) {
$type = $line->getType() ?? 'other';
if (!isset($grouped[$type])) {
$type = 'other';
}
$grouped[$type]['ca_ht'] += (float) $line->getPriceHt();
$title = $line->getTitle();
if ('ndd' !== $type || str_contains($title, 'Renouvellement') || str_contains($title, 'Depot')) {
++$grouped[$type]['lines'];
}
}
}
return $grouped;
return $this->groupFactureLinesByTypeFromList($factures);
}
/**

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Service;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class EntrepriseSearchService
{
private const API_URL = 'https://recherche-entreprises.api.gouv.fr/search';
public function __construct(
private HttpClientInterface $httpClient,
) {
}
public function search(string $query, int $perPage = 10): JsonResponse
{
if (\strlen($query) < 2) {
return new JsonResponse(['results' => [], 'total_results' => 0]);
}
try {
$response = $this->httpClient->request('GET', self::API_URL, [
'query' => [
'q' => $query,
'page' => 1,
'per_page' => $perPage,
],
]);
return new JsonResponse($response->toArray());
} catch (\Throwable) {
return new JsonResponse(['results' => [], 'total_results' => 0, 'error' => 'Service indisponible'], 502);
}
}
}

View File

@@ -193,38 +193,32 @@ class ClientsControllerTest extends TestCase
public function testEntrepriseSearchTooShort(): void
{
$svc = $this->createMock(\App\Service\EntrepriseSearchService::class);
$svc->method('search')->willReturn(new JsonResponse(['results' => [], 'total_results' => 0]));
$controller = new ClientsController();
$request = new Request(['q' => 'a']);
$response = $controller->entrepriseSearch($request, $this->createStub(HttpClientInterface::class));
$this->assertInstanceOf(JsonResponse::class, $response);
$response = $controller->entrepriseSearch(new Request(['q' => 'a']), $svc);
$this->assertStringContainsString('"total_results":0', $response->getContent());
}
public function testEntrepriseSearchSuccess(): void
{
$apiResponse = $this->createStub(ResponseInterface::class);
$apiResponse->method('toArray')->willReturn(['results' => [['siren' => '123456789']], 'total_results' => 1]);
$httpClient = $this->createStub(HttpClientInterface::class);
$httpClient->method('request')->willReturn($apiResponse);
$svc = $this->createMock(\App\Service\EntrepriseSearchService::class);
$svc->method('search')->willReturn(new JsonResponse(['results' => [['siren' => '123456789']], 'total_results' => 1]));
$controller = new ClientsController();
$request = new Request(['q' => 'siteconseil']);
$response = $controller->entrepriseSearch($request, $httpClient);
$this->assertInstanceOf(JsonResponse::class, $response);
$response = $controller->entrepriseSearch(new Request(['q' => 'siteconseil']), $svc);
$this->assertStringContainsString('123456789', $response->getContent());
}
public function testEntrepriseSearchApiError(): void
{
$httpClient = $this->createStub(HttpClientInterface::class);
$httpClient->method('request')->willThrowException(new \RuntimeException('API down'));
$svc = $this->createMock(\App\Service\EntrepriseSearchService::class);
$svc->method('search')->willReturn(new JsonResponse(['error' => 'Service indisponible'], 502));
$controller = new ClientsController();
$request = new Request(['q' => 'test']);
$response = $controller->entrepriseSearch($request, $httpClient);
$response = $controller->entrepriseSearch(new Request(['q' => 'test']), $svc);
$this->assertSame(502, $response->getStatusCode());
$this->assertStringContainsString('Service indisponible', $response->getContent());
}
public function testToggle(): void

View File

@@ -396,36 +396,25 @@ class PrestatairesControllerTest extends TestCase
public function testEntrepriseSearchReturnsEmptyWhenQueryTooShort(): void
{
$httpClient = $this->createStub(HttpClientInterface::class);
$svc = $this->createMock(\App\Service\EntrepriseSearchService::class);
$svc->method('search')->willReturn(new JsonResponse(['results' => [], 'total_results' => 0]));
$controller = $this->buildController();
$response = $controller->entrepriseSearch(new Request(['q' => 'a']), $svc);
$request = new Request(['q' => 'a']);
$response = $controller->entrepriseSearch($request, $httpClient);
$this->assertInstanceOf(JsonResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
$data = json_decode($response->getContent(), true);
$this->assertSame([], $data['results']);
$this->assertSame(0, $data['total_results']);
}
public function testEntrepriseSearchForwardsApiResponse(): void
{
$apiData = ['results' => [['nom_complet' => 'ACME SA']], 'total_results' => 1];
$httpResponse = $this->createStub(HttpResponseInterface::class);
$httpResponse->method('toArray')->willReturn($apiData);
$httpClient = $this->createStub(HttpClientInterface::class);
$httpClient->method('request')->willReturn($httpResponse);
$svc = $this->createMock(\App\Service\EntrepriseSearchService::class);
$svc->method('search')->willReturn(new JsonResponse(['results' => [['nom_complet' => 'ACME SA']], 'total_results' => 1]));
$controller = $this->buildController();
$response = $controller->entrepriseSearch(new Request(['q' => 'ACME']), $svc);
$request = new Request(['q' => 'ACME']);
$response = $controller->entrepriseSearch($request, $httpClient);
$this->assertInstanceOf(JsonResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
$data = json_decode($response->getContent(), true);
$this->assertSame(1, $data['total_results']);
@@ -433,15 +422,12 @@ class PrestatairesControllerTest extends TestCase
public function testEntrepriseSearchHandlesHttpError(): void
{
$httpClient = $this->createStub(HttpClientInterface::class);
$httpClient->method('request')->willThrowException(new \RuntimeException('Network error'));
$svc = $this->createMock(\App\Service\EntrepriseSearchService::class);
$svc->method('search')->willReturn(new JsonResponse(['error' => 'Service indisponible'], 502));
$controller = $this->buildController();
$response = $controller->entrepriseSearch(new Request(['q' => 'ACME']), $svc);
$request = new Request(['q' => 'ACME']);
$response = $controller->entrepriseSearch($request, $httpClient);
$this->assertInstanceOf(JsonResponse::class, $response);
$this->assertSame(502, $response->getStatusCode());
}