readBool($request->query->get('includeArchived')); $search = $request->query->getString('search') ?: null; $categoryType = $request->query->getString('categoryType') ?: null; /** @var list $clients */ $clients = $this->repository ->createListQueryBuilder($includeArchived, $search, $categoryType) ->getQuery() ->getResult() ; $withSiren = $this->security->isGranted('commercial.clients.accounting.view'); $binary = $this->exporter->export( 'Répertoire clients', $this->buildHeaders($withSiren), $this->buildRows($clients, $withSiren), ); return $this->buildResponse($binary); } /** * Colonnes dans l'ordre impose par la spec § 4.6. SIREN inseree avant la * date de creation, uniquement si l'utilisateur a accounting.view. * * @return list */ private function buildHeaders(bool $withSiren): array { $headers = [ 'Nom entreprise', 'Nom contact principal', 'Prénom', 'Téléphone principal', 'Téléphone secondaire', 'Email', 'Catégories', 'Sites', ]; if ($withSiren) { $headers[] = 'SIREN'; } $headers[] = 'Date de création'; return $headers; } /** * @param list $clients * * @return iterable> */ private function buildRows(array $clients, bool $withSiren): iterable { foreach ($clients as $client) { $row = [ $client->getCompanyName(), $client->getLastName(), $client->getFirstName(), $client->getPhonePrimary(), $client->getPhoneSecondary(), $client->getEmail(), $this->formatCategories($client), $this->formatSites($client), ]; if ($withSiren) { $row[] = $client->getSiren(); } $row[] = $client->getCreatedAt()?->format('d/m/Y'); yield $row; } } /** * Libelles des categories du client, dedupliques, tries, joints par virgule. */ private function formatCategories(Client $client): string { $names = []; foreach ($client->getCategories() as $category) { // @var CategoryInterface $category $name = $category->getName(); if (null !== $name && '' !== $name) { $names[$name] = true; } } return $this->joinSorted($names); } /** * Le Client ne porte pas de sites en propre : ils sont rattaches aux * adresses (RG-1.10). La colonne « Sites » agrege donc l'union distincte des * sites de toutes les adresses du client (decision validee 01/06). */ private function formatSites(Client $client): string { $names = []; foreach ($client->getAddresses() as $address) { foreach ($address->getSites() as $site) { // @var SiteInterface $site $name = $site->getName(); if (null !== $name && '' !== $name) { $names[$name] = true; } } } return $this->joinSorted($names); } /** * @param array $names ensemble de libelles (cles) */ private function joinSorted(array $names): string { $list = array_keys($names); sort($list); return implode(', ', $list); } private function buildResponse(string $binary): Response { $filename = sprintf('repertoire-clients-%s.xlsx', new DateTimeImmutable()->format('Ymd')); $response = new Response($binary); $response->headers->set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); $response->headers->set('Content-Disposition', sprintf('attachment; filename="%s"', $filename)); return $response; } /** * Lit un flag booleen issu des query params. Accepte true / "true" / "1". * Aligne sur ClientProvider pour un comportement identique a la liste. */ private function readBool(mixed $raw): bool { return is_string($raw) && in_array(strtolower($raw), ['true', '1'], true); } }