createAdminClient(); $carrier = $this->seedCompleteCarrier('Price Alpha'); $response = $client->request('GET', $this->exportUrl($carrier)); self::assertResponseIsSuccessful(); $headers = $response->getHeaders(false); self::assertStringContainsString(self::XLSX_MIME, $headers['content-type'][0] ?? ''); $disposition = $headers['content-disposition'][0] ?? ''; self::assertStringContainsString('attachment; filename="prix-transporteur-', $disposition); self::assertMatchesRegularExpression( '/filename="prix-transporteur-\d+-\d{8}\.xlsx"/', $disposition, ); $headerRow = $this->gridFromResponse($response->getContent())[0]; self::assertSame('Type de contenant', $headerRow[0]); self::assertContains('Transporteurs', $headerRow); self::assertContains('Adresse APRO ou Adresse Sites', $headerRow); self::assertContains('Adresse livraisons', $headerRow); self::assertContains('Forfait €', $headerRow); self::assertContains('Tonne €', $headerRow); self::assertContains('Indexation', $headerRow); self::assertContains('État du prix', $headerRow); } /** * Le transporteur complet seede 2 prix : une branche CLIENT (Benne / Tonne / * 42.50 / Valide) et une branche FOURNISSEUR (Fond Mouvant / Forfait / 320.00 / * En cours). On verifie le regroupement par contenant, la ventilation * Forfait/Tonne, les libelles d'etat FR et les points de depart/livraison * cross-module (le prix CLIENT livre chez le client, le prix FOURNISSEUR part * de l'adresse du fournisseur). */ public function testExportRendersGroupedPriceRows(): void { $client = $this->createAdminClient(); $carrier = $this->seedCompleteCarrier('Price Grouping'); $grid = $this->gridFromResponse($client->request('GET', $this->exportUrl($carrier))->getContent()); $benne = $this->rowForContainer($grid, 'Benne'); self::assertNotNull($benne, 'Ligne « Benne » introuvable dans l\'export prix.'); self::assertSame($carrier->getName(), $benne[1]); // Branche CLIENT : prix en Tonne (42.50 -> 42.5 apres typage numerique du // classeur), colonne Forfait vide, etat « Valide », livraison chez le client. self::assertEmpty($benne[4]); self::assertEqualsWithDelta(42.5, (float) $benne[5], 0.001); self::assertSame('Validé', $benne[7]); self::assertStringContainsString('TESTCARRIERREF CLI', (string) $benne[3]); $fondMouvant = $this->rowForContainer($grid, 'Fond Mouvant'); self::assertNotNull($fondMouvant, 'Ligne « Fond Mouvant » introuvable dans l\'export prix.'); // Branche FOURNISSEUR : prix au Forfait (320.00 -> 320), colonne Tonne vide, // etat « En cours », depart depuis l'adresse du fournisseur (APRO). self::assertEqualsWithDelta(320.0, (float) $fondMouvant[4], 0.001); self::assertEmpty($fondMouvant[5]); self::assertSame('En cours', $fondMouvant[7]); self::assertStringContainsString('TESTCARRIERREF FRN', (string) $fondMouvant[2]); } public function testNotFoundForUnknownCarrier(): void { $client = $this->createAdminClient(); $client->request('GET', '/api/carriers/99999999/prices/export.xlsx'); self::assertResponseStatusCodeSame(404); } public function testForbiddenWithoutCarriersViewPermission(): void { $carrier = $this->seedCompleteCarrier('Price Forbidden'); $creds = $this->createUserWithPermission('core.users.view'); $client = $this->authenticatedClient($creds['username'], $creds['password']); $client->request('GET', $this->exportUrl($carrier)); self::assertResponseStatusCodeSame(403); } public function testUnauthorizedWhenAnonymous(): void { $carrier = $this->seedCompleteCarrier('Price Anonymous'); $client = self::createClient(); $client->request('GET', $this->exportUrl($carrier)); self::assertResponseStatusCodeSame(401); } private function exportUrl(Carrier $carrier): string { return sprintf('/api/carriers/%d/prices/export.xlsx', (int) $carrier->getId()); } /** * Relit le binaire XLSX d'une reponse et renvoie la grille de cellules. * * @return array> */ private function gridFromResponse(string $binary): array { $tmp = tempnam(sys_get_temp_dir(), 'xlsx_carrier_price_export_test_'); self::assertIsString($tmp); file_put_contents($tmp, $binary); try { return IOFactory::load($tmp)->getActiveSheet()->toArray(); } finally { @unlink($tmp); } } /** * Renvoie la 1re ligne de donnees dont la colonne « Type de contenant » * (1re colonne) vaut $container, ou null. * * @param array> $grid * * @return null|array */ private function rowForContainer(array $grid, string $container): ?array { foreach (array_slice($grid, 1) as $row) { if ((string) ($row[0] ?? '') === $container) { return $row; } } return null; } }