feat(field_sales) : carte interactive Leaflet + écran de planification de tournée (ERP-127)
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Failing after 56s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Failing after 21s

- API visitable_tiers (provider DBAL bbox/q/type, paginé) pour les pins de la carte
- POST /tours/{id}/reorder (drag & drop) : renumérotation atomique + recompute
- Layer front field-sales : TourMap (pins, popup, polyline, sélection rectangle),
  liste d'étapes draggable (vuedraggable), composable de planification + Vitest
- Pages /tours, /tours/new, /tours/[id]/plan (split responsive, point custom géocodé)
- i18n FR, deep links Waze/Google/Apple, état 100% local
This commit is contained in:
Matthieu
2026-06-11 17:38:40 +02:00
parent f8f7571cc0
commit f8793ab359
23 changed files with 2721 additions and 2 deletions
@@ -6,6 +6,8 @@ namespace App\Tests\Module\FieldSales\Api;
use App\Module\Commercial\Domain\Entity\Client;
use App\Module\Commercial\Domain\Entity\ClientAddress;
use App\Module\Commercial\Domain\Entity\Supplier;
use App\Module\Commercial\Domain\Entity\SupplierAddress;
use App\Module\Core\Domain\Entity\Role;
use App\Module\Core\Domain\Entity\User;
use App\Module\FieldSales\Domain\Entity\Tour;
@@ -29,6 +31,7 @@ use DateTimeImmutable;
abstract class AbstractFieldSalesApiTestCase extends AbstractApiTestCase
{
protected const string TEST_CLIENT_PREFIX = 'TEST_FS_CLIENT_';
protected const string TEST_SUPPLIER_PREFIX = 'TEST_FS_SUPPLIER_';
protected function tearDown(): void
{
@@ -73,6 +76,40 @@ abstract class AbstractFieldSalesApiTestCase extends AbstractApiTestCase
return $address;
}
/**
* Seede un Fournisseur minimal (companyName uniquement).
*/
protected function seedSupplier(string $companyName): Supplier
{
$em = $this->getEm();
$supplier = new Supplier();
$supplier->setCompanyName(self::TEST_SUPPLIER_PREFIX.mb_strtoupper($companyName, 'UTF-8'));
$em->persist($supplier);
$em->flush();
return $supplier;
}
/**
* Seede une adresse geolocalisee rattachee a $supplier (type PROSPECT).
*/
protected function seedSupplierAddress(Supplier $supplier, float $lat = 47.218, float $lng = -1.553): SupplierAddress
{
$em = $this->getEm();
$address = new SupplierAddress();
$address->setSupplier($supplier);
$address->setAddressType('PROSPECT');
$address->setPostalCode('44000');
$address->setCity('NANTES');
$address->setStreet('2 rue de Test');
$address->setLatitude($lat);
$address->setLongitude($lng);
$em->persist($address);
$em->flush();
return $address;
}
/**
* Seede une tournee appartenant a $owner (sans passer par l'API).
*/
@@ -135,6 +172,16 @@ abstract class AbstractFieldSalesApiTestCase extends AbstractApiTestCase
'DELETE FROM '.Client::class.' c WHERE c.companyName LIKE :prefix',
)->setParameter('prefix', self::TEST_CLIENT_PREFIX.'%')->execute();
// Adresses puis fournisseurs de test (FK supplier_address.supplier_id CASCADE).
$em->createQuery(
'DELETE FROM '.SupplierAddress::class.' a WHERE a.supplier IN ('
.'SELECT s.id FROM '.Supplier::class.' s WHERE s.companyName LIKE :prefix)',
)->setParameter('prefix', self::TEST_SUPPLIER_PREFIX.'%')->execute();
$em->createQuery(
'DELETE FROM '.Supplier::class.' s WHERE s.companyName LIKE :prefix',
)->setParameter('prefix', self::TEST_SUPPLIER_PREFIX.'%')->execute();
$em->createQuery(
'DELETE FROM '.User::class.' u WHERE u.username LIKE :prefix',
)->setParameter('prefix', 'testuser_%')->execute();