siteByCode('86'); $http = $this->authManageOnSite($site); $clientEntity = $this->seedTestClient('Negoce'); $created = $this->postTicket($http, $this->validClientTicketPayload($clientEntity)); self::assertResponseStatusCodeSame(201); $createdBody = $created->toArray(); $id = (int) $createdBody['id']; $number = (string) $createdBody['number']; $detail = $http->request('GET', '/api/weighing_tickets/'.$id, ['headers' => ['Accept' => self::LD]])->toArray(); $list = $http->request('GET', '/api/weighing_tickets?search='.$number, ['headers' => ['Accept' => self::LD]])->toArray(); // Enveloppe Hydra AP4 (member/totalItems sans prefixe hydra:). self::assertArrayHasKey('member', $list); self::assertArrayNotHasKey('hydra:member', $list); $row = $this->memberById($list, $id); self::assertNotNull($row, 'Le ticket cree doit apparaitre dans la liste filtree.'); // === Piege #1 : relations embarquees en OBJET (pas IRI nu) === self::assertIsArray($row['client'], 'client doit etre un objet embarque (client:read), pas un IRI nu.'); self::assertArrayHasKey('companyName', $row['client']); // supplier null sur une contrepartie Client (cle potentiellement omise par // skip_null_values — tolerant aux deux cas, jamais un IRI nu). self::assertNull($row['supplier'] ?? null); // === Piege #2 : booleen plateFreeFormat present === self::assertArrayHasKey('plateFreeFormat', $row); self::assertFalse($row['plateFreeFormat']); // === Piege #3 : number formate {siteCode}-TP-{NNNN} === self::assertArrayHasKey('number', $row); self::assertMatchesRegularExpression('/^86-TP-\d{4}$/', $row['number']); // === Piege #4 : netWeight = full - empty (14300 - 7150) === self::assertSame(7150, $row['netWeight']); // displayDate (date du ticket = fullDate ?? emptyDate) expose en liste. self::assertArrayHasKey('displayDate', $row); // === DETAIL : site embarque (avec code), immatriculation, les 2 pesees === self::assertIsArray($detail['site']); self::assertSame('86', $detail['site']['code']); self::assertSame('AB-123-CD', $detail['immatriculation']); self::assertSame(7150, $detail['emptyWeight']); self::assertSame(14300, $detail['fullWeight']); self::assertSame(7150, $detail['netWeight']); self::assertIsArray($detail['client']); self::assertArrayHasKey('companyName', $detail['client']); $this->dumpDodIfRequested($list, $detail); } /** * Piege #1 symetrique (spec § 4.0.bis) : sur une contrepartie FOURNISSEUR, * `supplier` doit sortir en OBJET embarque (supplier:read) et `client` etre * null (jamais un IRI nu). Le cas Client est couvert ci-dessus ; ce test * verrouille l'autre branche pour qu'un drift de read-group cote Supplier ne * passe pas inapercu. */ public function testSupplierCounterpartyEmbedsSupplier(): void { $site = $this->siteByCode('86'); $http = $this->authManageOnSite($site); $supplierEntity = $this->seedTestSupplier('Ferraille'); $created = $this->postTicket($http, $this->validSupplierTicketPayload($supplierEntity)); self::assertResponseStatusCodeSame(201); $createdBody = $created->toArray(); $id = (int) $createdBody['id']; $number = (string) $createdBody['number']; $detail = $http->request('GET', '/api/weighing_tickets/'.$id, ['headers' => ['Accept' => self::LD]])->toArray(); $list = $http->request('GET', '/api/weighing_tickets?search='.$number, ['headers' => ['Accept' => self::LD]])->toArray(); $row = $this->memberById($list, $id); self::assertNotNull($row, 'Le ticket fournisseur cree doit apparaitre dans la liste filtree.'); // Liste : supplier embarque en objet, client omis/null (skip_null_values). self::assertIsArray($row['supplier'], 'supplier doit etre un objet embarque (supplier:read), pas un IRI nu.'); self::assertArrayHasKey('companyName', $row['supplier']); self::assertNull($row['client'] ?? null); self::assertSame('FOURNISSEUR', $row['counterpartyType']); // Detail : meme contrat cote item. self::assertIsArray($detail['supplier']); self::assertArrayHasKey('companyName', $detail['supplier']); self::assertNull($detail['client'] ?? null); } /** * DoD (§ 4.0.bis) : ecrit les corps JSON reels sous /tmp si WEIGHING_TICKET_DOD_DUMP * est positionnee (sinon no-op). A coller dans spec-back.md § 4.0.bis. * * @param array $list * @param array $detail */ private function dumpDodIfRequested(array $list, array $detail): void { if (false === getenv('WEIGHING_TICKET_DOD_DUMP')) { return; } $flags = JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES; file_put_contents('/tmp/weighing-ticket-dod-list.json', json_encode($list, $flags)); file_put_contents('/tmp/weighing-ticket-dod-detail.json', json_encode($detail, $flags)); } }