test(commercial) : export fournisseurs — dedup F3 + gating SIREN via permission explicite (ERP-113)

This commit is contained in:
Matthieu
2026-06-08 10:08:07 +02:00
parent 5c8318781a
commit 86415bef7c
2 changed files with 93 additions and 14 deletions
@@ -15,8 +15,9 @@ use PhpOffice\PhpSpreadsheet\IOFactory;
* Couvre : reponse 200 (Content-Type + Content-Disposition), exclusion des
* archives par defaut, respect du filtre ?search, peuplement des colonnes
* contact principal / categories / sites, gating de la colonne SIREN selon
* commercial.suppliers.accounting.view, 403 sans commercial.suppliers.view,
* 401 anonyme.
* commercial.suppliers.accounting.view (admin ET user minimal a permission
* explicite), dedup F3 (fournisseur multi-categories rendu sur une seule ligne),
* 403 sans commercial.suppliers.view, 401 anonyme.
*
* @internal
*/
@@ -178,6 +179,60 @@ final class SupplierExportControllerTest extends AbstractSupplierApiTestCase
self::assertStringNotContainsString('987654321', $this->flatten($grid));
}
/**
* Gating SIREN prouve via une permission EXPLICITE (et non le bypass admin) :
* un user minimal portant uniquement commercial.suppliers.view +
* commercial.suppliers.accounting.view voit bien la colonne SIREN et sa
* valeur. Complement de testSirenColumnPresentWithAccountingView (admin), qui
* ne prouve pas que accounting.view SEULE suffit (l'admin bypasse le RBAC).
* Le pendant negatif (sans accounting.view -> colonne absente) est couvert par
* testSirenColumnAbsentWithoutAccountingView.
*/
public function testSirenColumnPresentForMinimalUserWithAccountingView(): void
{
// Seed via admin, puis relecture par un user non-admin a 2 permissions.
$this->createAdminClient();
$supplier = $this->seedSupplier('Gated Siren Co');
$em = $this->getEm();
$supplier->setSiren('456789123');
$em->flush();
$creds = $this->createUserWithPermissions([
'commercial.suppliers.view',
'commercial.suppliers.accounting.view',
]);
$viewer = $this->authenticatedClient($creds['username'], $creds['password']);
$grid = $this->gridFromResponse($viewer->request('GET', self::EXPORT_URL)->getContent());
self::assertContains('SIREN', $grid[0]);
self::assertStringContainsString('456789123', $this->flatten($grid));
}
/**
* Dedup F3 : un fournisseur portant >= 2 categories FOURNISSEUR est multiplie
* par la jointure (selection/hydratation des collections) ; l'export doit le
* rendre sur UNE SEULE ligne. On seede un fournisseur a 2 categories et on
* assert qu'il n'apparait qu'une fois dans la colonne « Nom fournisseur ».
*/
public function testExportDeduplicatesSupplierWithMultipleCategories(): void
{
$client = $this->createAdminClient();
$supplier = $this->seedSupplier('Multi Cat Co', false, 'NEGOCIANT');
// 2e categorie FOURNISSEUR sur le meme fournisseur (RG-2.10).
$supplier->addCategory($this->supplierCategory('GROSSISTE'));
$this->getEm()->flush();
$names = $this->companyNames($client->request('GET', self::EXPORT_URL)->getContent());
$occurrences = count(array_filter($names, static fn (string $name): bool => 'MULTI CAT CO' === $name));
self::assertSame(
1,
$occurrences,
'Un fournisseur multi-categories doit apparaitre sur une seule ligne (dedup F3).',
);
}
public function testForbiddenWithoutSuppliersViewPermission(): void
{
$creds = $this->createUserWithPermission('core.users.view');