feat(field_sales) : fondations du module Tournées + VisitableInterface + RBAC (ERP-123)
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 2m26s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Failing after 13s

- Module FieldSales (ID field_sales, REQUIRED false) avec 2 permissions
  field_sales.tours.view / .manage (scope V0.2, pas de reports.*), active
  dans config/modules.php.
- Contrat partage VisitableInterface (getId/getDisplayName/getVisitableType)
  implemente par Client (client) et Supplier (supplier) sans import inter-module.
  Note doctrine.yaml : contrat polymorphe (2 implementations) donc resolu par
  service via (tier_type, tier_id), pas via resolve_target_entities.
- 3 miroirs RBAC alignes : sidebar.php (section Tournées, item /tours, i18n
  sidebar.field_sales.*), personas.ts et SeedE2ECommand.php (user-full) ;
  matrice metier RbacSeeder (Commerciale = view+manage, Bureau = view, Compta
  exclue, Admin bypass).
This commit is contained in:
Matthieu
2026-06-11 14:51:52 +02:00
parent de4aaa1d64
commit be9204eca7
12 changed files with 214 additions and 2 deletions
+20 -1
View File
@@ -17,6 +17,7 @@ use App\Shared\Domain\Contract\BlamableInterface;
use App\Shared\Domain\Contract\CategoryInterface;
use App\Shared\Domain\Contract\SiteInterface;
use App\Shared\Domain\Contract\TimestampableInterface;
use App\Shared\Domain\Contract\VisitableInterface;
use App\Shared\Domain\Trait\TimestampableBlamableTrait;
use DateTimeImmutable;
use Doctrine\Common\Collections\ArrayCollection;
@@ -135,7 +136,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
#[ORM\Index(name: 'idx_client_created_by', columns: ['created_by'])]
#[ORM\Index(name: 'idx_client_updated_by', columns: ['updated_by'])]
#[Auditable]
class Client implements TimestampableInterface, BlamableInterface
class Client implements TimestampableInterface, BlamableInterface, VisitableInterface
{
use TimestampableBlamableTrait;
@@ -321,6 +322,24 @@ class Client implements TimestampableInterface, BlamableInterface
return $this;
}
/**
* Libelle affichable du Tiers pour le module FieldSales (carte/etapes).
* La raison sociale est NotBlank (RG M1), le fallback chaine vide ne sert
* qu'a honorer le type non-nullable du contrat VisitableInterface.
*/
public function getDisplayName(): string
{
return $this->companyName ?? '';
}
/**
* Type stable porte par tour_stop.tier_type pour un Client (cf. M6 § 3.1).
*/
public function getVisitableType(): string
{
return 'client';
}
public function getDistributor(): ?Client
{
return $this->distributor;