le geocodage auto reprend ; * - bornes WGS84 (Assert\Range) -> 422 avec message FR par champ. * * @internal */ final class AddressGeolocationTest extends AbstractSupplierApiTestCase { public function testClientAddressIsGeocodedOnCreate(): void { $this->skipIfSitesModuleDisabled(); $client = $this->createAdminClient(); $seed = $this->seedClient('Geo Create'); $data = $client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [ 'headers' => ['Content-Type' => self::LD], 'json' => $this->clientAddressPayload(), ])->toArray(); self::assertResponseStatusCodeSame(201); self::assertSame(InMemoryGeocoder::LATITUDE, $data['latitude']); self::assertSame(InMemoryGeocoder::LONGITUDE, $data['longitude']); self::assertFalse($data['geoManual']); self::assertNotNull($data['geocodedAt']); } public function testSupplierAddressIsGeocodedOnCreate(): void { $this->skipIfSitesModuleDisabled(); $client = $this->createAdminClient(); $seed = $this->seedSupplier('Geo Supplier Create'); $category = $this->supplierCategory('NEGOCIANT'); $data = $client->request('POST', '/api/suppliers/'.$seed->getId().'/addresses', [ 'headers' => ['Content-Type' => self::LD], 'json' => [ 'addressType' => 'DEPART', 'postalCode' => '86100', 'city' => 'Châtellerault', 'street' => '1 rue du Test', 'sites' => [$this->firstSiteIri()], 'categories' => ['/api/categories/'.$category->getId()], ], ])->toArray(); self::assertResponseStatusCodeSame(201); self::assertSame(InMemoryGeocoder::LATITUDE, $data['latitude']); self::assertSame(InMemoryGeocoder::LONGITUDE, $data['longitude']); self::assertFalse($data['geoManual']); self::assertNotNull($data['geocodedAt']); } public function testManualPinIsPersistedViaPatch(): void { $this->skipIfSitesModuleDisabled(); $client = $this->createAdminClient(); $addressId = $this->createClientAddress($client, 'Geo Pin'); // Pin deplace a la main cote front : PATCH coordonnees + geoManual. $data = $client->request('PATCH', '/api/client_addresses/'.$addressId, [ 'headers' => ['Content-Type' => self::MERGE], 'json' => [ 'latitude' => '48.1234567', 'longitude' => '-1.6543217', 'geoManual' => true, ], ])->toArray(); self::assertResponseStatusCodeSame(200); self::assertSame('48.1234567', $data['latitude']); self::assertSame('-1.6543217', $data['longitude']); self::assertTrue($data['geoManual']); } public function testManualPinIsNotOverwrittenByAutoGeocoding(): void { $this->skipIfSitesModuleDisabled(); $client = $this->createAdminClient(); $addressId = $this->createClientAddress($client, 'Geo RG-608'); $client->request('PATCH', '/api/client_addresses/'.$addressId, [ 'headers' => ['Content-Type' => self::MERGE], 'json' => ['latitude' => '48.1234567', 'longitude' => '-1.6543217', 'geoManual' => true], ]); self::assertResponseStatusCodeSame(200); // RG-6.08 : une modification metier ulterieure (rue changee) ne doit PAS // relancer le geocodage — l'InMemoryGeocoder renverrait Poitiers, ce qui // trahirait une reecriture indue du pin manuel. $data = $client->request('PATCH', '/api/client_addresses/'.$addressId, [ 'headers' => ['Content-Type' => self::MERGE], 'json' => ['street' => '2 rue du Pin Manuel'], ])->toArray(); self::assertResponseStatusCodeSame(200); self::assertSame('48.1234567', $data['latitude']); self::assertSame('-1.6543217', $data['longitude']); self::assertTrue($data['geoManual']); } public function testClearingManualFlagTriggersRegeocoding(): void { $this->skipIfSitesModuleDisabled(); $client = $this->createAdminClient(); $addressId = $this->createClientAddress($client, 'Geo Regeocode'); $client->request('PATCH', '/api/client_addresses/'.$addressId, [ 'headers' => ['Content-Type' => self::MERGE], 'json' => ['latitude' => '48.1234567', 'longitude' => '-1.6543217', 'geoManual' => true], ]); self::assertResponseStatusCodeSame(200); // Bouton « Re-geocoder depuis l'adresse » : geoManual repasse a false -> // le processor regeocode depuis l'adresse postale. $data = $client->request('PATCH', '/api/client_addresses/'.$addressId, [ 'headers' => ['Content-Type' => self::MERGE], 'json' => ['geoManual' => false], ])->toArray(); self::assertResponseStatusCodeSame(200); self::assertSame(InMemoryGeocoder::LATITUDE, $data['latitude']); self::assertSame(InMemoryGeocoder::LONGITUDE, $data['longitude']); self::assertFalse($data['geoManual']); self::assertNotNull($data['geocodedAt']); } public function testOutOfRangeCoordinatesAreRejected(): void { $this->skipIfSitesModuleDisabled(); $client = $this->createAdminClient(); $addressId = $this->createClientAddress($client, 'Geo Bounds'); $body = $client->request('PATCH', '/api/client_addresses/'.$addressId, [ 'headers' => ['Content-Type' => self::MERGE], 'json' => ['latitude' => '95', 'longitude' => '200', 'geoManual' => true], ])->toArray(false); self::assertResponseStatusCodeSame(422); $byPath = $this->violationsByPath($body); self::assertSame('La latitude doit être comprise entre -90 et 90.', $byPath['latitude'] ?? null); self::assertSame('La longitude doit être comprise entre -180 et 180.', $byPath['longitude'] ?? null); } /** Cree un client + une adresse valide, et retourne l'id de l'adresse. */ private function createClientAddress(\ApiPlatform\Symfony\Bundle\Test\Client $client, string $companyName): int { $seed = $this->seedClient($companyName); $data = $client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [ 'headers' => ['Content-Type' => self::LD], 'json' => $this->clientAddressPayload(), ])->toArray(); self::assertResponseStatusCodeSame(201); return (int) $data['id']; } /** Payload minimal valide d'une adresse client (livraison, 1 site, 1 categorie). */ private function clientAddressPayload(): array { $category = $this->createCategory('SECTEUR'); return [ 'isDelivery' => true, 'postalCode' => '86100', 'city' => 'Châtellerault', 'street' => '1 rue du Test', 'sites' => [$this->firstSiteIri()], 'categories' => ['/api/categories/'.$category->getId()], ]; } /** Retourne l'IRI du premier site seede (fixtures Sites). */ private function firstSiteIri(): string { $site = $this->getEm()->getRepository(Site::class)->findOneBy([]); self::assertNotNull($site, 'Aucun site seede : impossible de tester les adresses.'); return '/api/sites/'.$site->getId(); } }