feat(field_sales) : carte interactive Leaflet + écran de planification de tournée (ERP-127)
- 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:
@@ -146,6 +146,49 @@ final class TourRouteApiTest extends AbstractFieldSalesApiTestCase
|
||||
self::assertArrayHasKey('tourDate', $this->violationsByPath($response->toArray(false)));
|
||||
}
|
||||
|
||||
/**
|
||||
* /reorder applique l'ordre fourni (drag & drop) en renumerotant les
|
||||
* positions sans heurter l'unique (tour_id, position), puis recompute.
|
||||
*/
|
||||
public function testReorderAppliesGivenOrderAndRecomputes(): void
|
||||
{
|
||||
$client = $this->authenticatedClient('admin', 'admin');
|
||||
$admin = $this->getUserByUsername('admin');
|
||||
$tour = $this->seedTour($admin);
|
||||
|
||||
$a = $this->seedCustomStop($tour, 0, 47.0, -1.0, 'A');
|
||||
$b = $this->seedCustomStop($tour, 1, 47.1, -1.0, 'B');
|
||||
$c = $this->seedCustomStop($tour, 2, 47.2, -1.0, 'C');
|
||||
|
||||
// Ordre inverse demande : C, B, A.
|
||||
$body = $client->request('POST', '/api/tours/'.$tour->getId().'/reorder', [
|
||||
'headers' => ['Content-Type' => self::LD, 'Accept' => self::LD],
|
||||
'json' => ['stopIds' => [$c->getId(), $b->getId(), $a->getId()]],
|
||||
])->toArray();
|
||||
|
||||
$stops = $this->stopsByPosition($body);
|
||||
self::assertSame([0, 1, 2], array_map(static fn (array $s) => $s['position'], $stops));
|
||||
self::assertSame(['C', 'B', 'A'], array_map(static fn (array $s) => $s['customLabel'], $stops));
|
||||
// Recompute applique : la 1re etape est le depart (leg nul).
|
||||
self::assertSame(0, $stops[0]['legDistanceM']);
|
||||
self::assertNotNull($body['totalDistanceM']);
|
||||
}
|
||||
|
||||
public function testReorderRequiresStopIds(): void
|
||||
{
|
||||
$client = $this->authenticatedClient('admin', 'admin');
|
||||
$admin = $this->getUserByUsername('admin');
|
||||
$tour = $this->seedTour($admin);
|
||||
|
||||
$response = $client->request('POST', '/api/tours/'.$tour->getId().'/reorder', [
|
||||
'headers' => ['Content-Type' => self::LD, 'Accept' => self::LD],
|
||||
'json' => [],
|
||||
]);
|
||||
|
||||
self::assertResponseStatusCodeSame(422);
|
||||
self::assertArrayHasKey('stopIds', $this->violationsByPath($response->toArray(false)));
|
||||
}
|
||||
|
||||
public function testComputeRequiresManagePermission(): void
|
||||
{
|
||||
$creds = $this->createUserWithPermissions(['field_sales.tours.view']);
|
||||
|
||||
Reference in New Issue
Block a user