fix(technique) : cloisonner par site les sous-ressources prestataire + RG-3.04 fonction (ERP-134, ERP-135)

Les operations Get/Patch/Delete des sous-ressources Contact/Adresse/RIB
passaient par le provider Doctrine par defaut (non cloisonne), et le POST
resolvait le parent sans controle de scope : un user cloisonne pouvait
lire/editer/supprimer une sous-ressource d'un prestataire hors de son site
(IBAN/BIC du RIB inclus). SiteScopedQueryExtension ne filtre que les
SiteAwareInterface, que ces entites ne sont pas.

- ProviderSiteScopeChecker : decision de cloisonnement centralisee (source
  unique), consommee par ProviderProvider (refactore), le provider decore
  et les processors.
- ProviderSubResourceItemProvider : decore le provider par defaut sur
  Get/Patch/Delete des 3 sous-ressources -> 404 si parent hors perimetre.
- Garde assertInScope au POST dans les 3 processors -> 404 si parent hors
  perimetre. ProviderOwnedInterface sur les 3 entites.

RG-3.04 : alignement code <-> spec (ligne 926). La Fonction (jobTitle) rend
desormais un contact valide a elle seule : ajout au validateName, au CHECK
chk_provider_contact_name et normalisation (normalizeText, vide -> null).

Tests : ProviderSubResourceSiteScopeTest (fuite cross-site, 7 cas) ;
RG-3.04 jobTitle reecrit. Spec § 2.13 corrigee (l'heritage n'etait pas
automatique). Suite back complete verte (685 tests).
This commit is contained in:
Matthieu
2026-06-12 16:12:00 +02:00
parent d6ed4f5faf
commit 8b6b4f2dbb
15 changed files with 410 additions and 61 deletions
+3 -3
View File
@@ -252,7 +252,7 @@ final class Version20260612100000 extends AbstractMigration
updated_by INT DEFAULT NULL,
PRIMARY KEY (id),
CONSTRAINT chk_provider_contact_name
CHECK (first_name IS NOT NULL OR last_name IS NOT NULL OR phone_primary IS NOT NULL OR email IS NOT NULL),
CHECK (first_name IS NOT NULL OR last_name IS NOT NULL OR job_title IS NOT NULL OR phone_primary IS NOT NULL OR email IS NOT NULL),
CONSTRAINT fk_provider_contact_provider
FOREIGN KEY (provider_id) REFERENCES provider (id) ON DELETE CASCADE,
CONSTRAINT fk_provider_contact_created_by
@@ -263,12 +263,12 @@ final class Version20260612100000 extends AbstractMigration
SQL);
$this->addSql('CREATE INDEX idx_provider_contact_provider ON provider_contact (provider_id)');
$this->comment('provider_contact', '_table', 'Contacts d un prestataire (1:n) — au moins un champ rempli parmi prenom/nom/telephone/email (RG-3.04, chk_provider_contact_name).');
$this->comment('provider_contact', '_table', 'Contacts d un prestataire (1:n) — au moins un champ rempli parmi prenom/nom/fonction/telephone/email (RG-3.04, chk_provider_contact_name).');
$this->comment('provider_contact', 'id', 'Identifiant interne auto-incremente.');
$this->comment('provider_contact', 'provider_id', 'FK -> provider.id, ON DELETE CASCADE — prestataire proprietaire du contact.');
$this->comment('provider_contact', 'first_name', 'Prenom du contact (capitalise serveur). Au moins un champ du contact requis (RG-3.04, chk_provider_contact_name).');
$this->comment('provider_contact', 'last_name', 'Nom du contact (capitalise serveur). Au moins un champ du contact requis (RG-3.04, chk_provider_contact_name).');
$this->comment('provider_contact', 'job_title', 'Fonction / intitule de poste du contact (≤ 120 caracteres).');
$this->comment('provider_contact', 'job_title', 'Fonction / intitule de poste du contact (≤ 120 caracteres). Au moins un champ du contact requis (RG-3.04, chk_provider_contact_name).');
$this->comment('provider_contact', 'phone_primary', 'Telephone principal du contact — chiffres uniquement (normalisation serveur).');
$this->comment('provider_contact', 'phone_secondary', 'Telephone secondaire du contact — chiffres uniquement (normalisation serveur).');
$this->comment('provider_contact', 'email', 'Email du contact (lowercase serveur).');