456c6682b0
GET /api/qualimat_carriers?search= pour la saisie assistee du nom (RG-4.01, spec-back § 4.7) : recherche fuzzy sur name (+ siret), restreinte aux lignes actives (is_active = true), triee name ASC, paginee (regle n°13). - QualimatCarrierRepositoryInterface + DoctrineQualimatCarrierRepository : QueryBuilder de recherche (forcage is_active cote serveur, fuzzy multi-champs). - QualimatCarrierSearchProvider : provider de la GetCollection (pagination Hydra + echappatoire ?pagination=false), branche uniquement sur la collection. - ApiResource : provider custom sur GetCollection, retrait des ApiFilter natifs (incapables d'unifier name/siret sous ?search= ni d'imposer l'actif). Mapping ORM inchange (schema:update reste no-op). Aucune ecriture exposee. - Tests : actifs seuls, tri name, match siret, pagination Hydra, 403 sans perm.
176 lines
5.6 KiB
PHP
176 lines
5.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Module\Transport\Domain\Entity;
|
|
|
|
use ApiPlatform\Metadata\ApiResource;
|
|
use ApiPlatform\Metadata\Get;
|
|
use ApiPlatform\Metadata\GetCollection;
|
|
use App\Module\Transport\Infrastructure\ApiPlatform\State\Provider\QualimatCarrierSearchProvider;
|
|
use DateTimeImmutable;
|
|
use Doctrine\ORM\Mapping as ORM;
|
|
use Symfony\Component\Serializer\Attribute\Groups;
|
|
use Symfony\Component\Serializer\Attribute\SerializedName;
|
|
|
|
/**
|
|
* Mapping ORM LECTURE SEULE sur la table existante `qualimat_carrier`
|
|
* (referentiel des transporteurs agrees QUALIMAT, ERP-39). La table est
|
|
* alimentee/soft-deletee EXCLUSIVEMENT par la commande console `app:qualimat:sync` ;
|
|
* cette entite n'expose donc AUCUNE ecriture (ni Post/Patch/Delete).
|
|
*
|
|
* Role M4 (ERP-155/157) :
|
|
* - cible de la FK editable `carrier.qualimat_carrier_id` (§ 2.5) ;
|
|
* - embarquee (groupe `qualimat:read`) dans la liste et le detail Carrier pour
|
|
* afficher statut + date de validite QUALIMAT (RG-4.04) ;
|
|
* - endpoint de recherche `GET /api/qualimat_carriers?search=` pour la saisie
|
|
* assistee du nom (§ 4.7) — fuzzy name (+ siret), SEULEMENT les lignes actives,
|
|
* tri name ASC, paginee ; logique portee par QualimatCarrierSearchProvider.
|
|
*
|
|
* La table reste hors `schema_filter` Doctrine (doctrine.yaml) : c'est la
|
|
* migration modulaire Version20260612150000 qui possede son DDL et ses COMMENT
|
|
* (pas l'ORM). Lecture seule + referentiel synchronise => exclue de
|
|
* EntitiesAreTimestampableBlamableTest et non #[Auditable].
|
|
*/
|
|
#[ApiResource(
|
|
operations: [
|
|
// Saisie assistee (§ 4.7 / RG-4.01) : ?search= fuzzy name (+ siret),
|
|
// SEULEMENT les lignes actives, tri name ASC, paginee. La logique vit
|
|
// dans le provider (forcage is_active + recherche multi-champs) car un
|
|
// SearchFilter natif ne sait ni unifier name/siret sous un seul ?search=,
|
|
// ni imposer cote serveur le filtre actif.
|
|
new GetCollection(
|
|
security: "is_granted('transport.carriers.view')",
|
|
provider: QualimatCarrierSearchProvider::class,
|
|
normalizationContext: ['groups' => ['qualimat:read', 'default:read']],
|
|
),
|
|
new Get(
|
|
security: "is_granted('transport.carriers.view')",
|
|
normalizationContext: ['groups' => ['qualimat:read', 'default:read']],
|
|
),
|
|
],
|
|
)]
|
|
#[ORM\Entity]
|
|
// Mapping reproduisant a l'identique le DDL de la migration ERP-39
|
|
// (Version20260612150000) pour que `schema:update --force` reste un no-op :
|
|
// contrainte d'unicite siret + index is_active.
|
|
#[ORM\Table(name: 'qualimat_carrier')]
|
|
#[ORM\UniqueConstraint(name: 'uq_qualimat_carrier_siret', columns: ['siret'])]
|
|
#[ORM\Index(name: 'idx_qualimat_carrier_active', columns: ['is_active'])]
|
|
class QualimatCarrier
|
|
{
|
|
#[ORM\Id]
|
|
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
|
|
#[ORM\Column(type: 'bigint')]
|
|
#[Groups(['qualimat:read'])]
|
|
private ?string $id = null;
|
|
|
|
#[ORM\Column(length: 20)]
|
|
#[Groups(['qualimat:read'])]
|
|
private ?string $siret = null;
|
|
|
|
#[ORM\Column(length: 255)]
|
|
#[Groups(['qualimat:read'])]
|
|
private ?string $name = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
#[Groups(['qualimat:read'])]
|
|
private ?string $address = null;
|
|
|
|
#[ORM\Column(name: 'postal_code', length: 10, nullable: true)]
|
|
#[Groups(['qualimat:read'])]
|
|
private ?string $postalCode = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
#[Groups(['qualimat:read'])]
|
|
private ?string $city = null;
|
|
|
|
#[ORM\Column(length: 32, nullable: true)]
|
|
#[Groups(['qualimat:read'])]
|
|
private ?string $phone = null;
|
|
|
|
#[ORM\Column(length: 64, nullable: true)]
|
|
#[Groups(['qualimat:read'])]
|
|
private ?string $department = null;
|
|
|
|
#[ORM\Column(length: 32)]
|
|
#[Groups(['qualimat:read'])]
|
|
private ?string $status = null;
|
|
|
|
#[ORM\Column(name: 'validity_date', type: 'date_immutable', nullable: true)]
|
|
#[Groups(['qualimat:read'])]
|
|
private ?DateTimeImmutable $validityDate = null;
|
|
|
|
#[ORM\Column(name: 'is_active', options: ['default' => true])]
|
|
#[Groups(['qualimat:read'])]
|
|
#[SerializedName('isActive')]
|
|
private bool $isActive = true;
|
|
|
|
// Colonne technique de synchro (soft-delete) — mappee pour completude, non
|
|
// serialisee. Alimentee par app:qualimat:sync. columnDefinition pin la
|
|
// precision TIMESTAMP(6) du DDL ERP-39 pour eviter un ALTER de schema:update
|
|
// (le datetime_immutable par defaut mapperait sur TIMESTAMP(0)).
|
|
#[ORM\Column(name: 'last_synced_at', type: 'datetime_immutable', columnDefinition: 'TIMESTAMP(6) WITHOUT TIME ZONE NOT NULL')]
|
|
private ?DateTimeImmutable $lastSyncedAt = null;
|
|
|
|
public function getId(): ?string
|
|
{
|
|
return $this->id;
|
|
}
|
|
|
|
public function getSiret(): ?string
|
|
{
|
|
return $this->siret;
|
|
}
|
|
|
|
public function getName(): ?string
|
|
{
|
|
return $this->name;
|
|
}
|
|
|
|
public function getAddress(): ?string
|
|
{
|
|
return $this->address;
|
|
}
|
|
|
|
public function getPostalCode(): ?string
|
|
{
|
|
return $this->postalCode;
|
|
}
|
|
|
|
public function getCity(): ?string
|
|
{
|
|
return $this->city;
|
|
}
|
|
|
|
public function getPhone(): ?string
|
|
{
|
|
return $this->phone;
|
|
}
|
|
|
|
public function getDepartment(): ?string
|
|
{
|
|
return $this->department;
|
|
}
|
|
|
|
public function getStatus(): ?string
|
|
{
|
|
return $this->status;
|
|
}
|
|
|
|
public function getValidityDate(): ?DateTimeImmutable
|
|
{
|
|
return $this->validityDate;
|
|
}
|
|
|
|
public function isActive(): bool
|
|
{
|
|
return $this->isActive;
|
|
}
|
|
|
|
public function getLastSyncedAt(): ?DateTimeImmutable
|
|
{
|
|
return $this->lastSyncedAt;
|
|
}
|
|
}
|