diff --git a/src/Controller/Api/SignalementController.php b/src/Controller/Api/SignalementController.php new file mode 100644 index 000000000..ffdf2eca9 --- /dev/null +++ b/src/Controller/Api/SignalementController.php @@ -0,0 +1,119 @@ + []]], + tags: ['Signalements'], + )] + #[OA\Parameter( + name: 'limit', + description: 'Nombre de signalements à retourner (défaut : 20, max : 100)', + in: 'query', + required: false, + schema: new OA\Schema(type: 'limit', example: '10') + )] + #[OA\Response( + response: Response::HTTP_OK, + description: 'Une liste de signalements', + content: new OA\JsonContent( + type: 'array', + items: new OA\Items(ref: new Model(type: SignalementResponse::class)) + ) + )] + public function getSignalementList( + SignalementRepository $signalementRepository, + SignalementDesordresProcessor $signalementDesordresProcessor, + UrlGeneratorInterface $urlGenerator, + #[MapQueryParameter] int $limit = 20, + ): JsonResponse { + if ($limit > 100) { + $limit = 100; + } + $signalements = $signalementRepository->findForAPI(user: $this->getUser(), limit: $limit); + $resources = []; + foreach ($signalements as $signalement) { + $resources[] = new SignalementResponse($signalement, $signalementDesordresProcessor, $urlGenerator); // sinon comment acceder à ses services pour generer les responses ? + } + + return new JsonResponse($resources, Response::HTTP_OK); + } + + #[Route('/signalements/{uuid}', name: 'api_signalement_uuid', methods: ['GET'])] + #[OA\Get( + path: '/api/signalements/{uuid}', + description: 'Retourne un signalement récupéré par son UUID', + summary: 'Signalement par UUID', + security: [['bearerAuth' => []]], + tags: ['Signalements'] + )] + #[OA\Response( + response: Response::HTTP_OK, + description: 'Un signalement', + content: new OA\JsonContent(ref: '#/components/schemas/SignalementResponse') + )] + public function getSignalementByUuid( + SignalementRepository $signalementRepository, + SignalementDesordresProcessor $signalementDesordresProcessor, + UrlGeneratorInterface $urlGenerator, + string $uuid, + ): JsonResponse { + $signalements = $signalementRepository->findForAPI(user : $this->getUser(), uuid : $uuid); + if (!count($signalements)) { + return new JsonResponse(['message' => 'Signalement introuvable'], Response::HTTP_NOT_FOUND); + } + $resource = new SignalementResponse($signalements[0], $signalementDesordresProcessor, $urlGenerator); + + return new JsonResponse($resource, Response::HTTP_OK); + } + + #[Route('/signalements/reference/{reference}', name: 'api_signalement_reference', methods: ['GET'])] + #[OA\Get( + path: '/api/signalements/reference/{reference}', + description: 'Retourne un signalement récupéré par sa reference', + summary: 'Signalement par référence', + security: [['bearerAuth' => []]], + tags: ['Signalements'] + )] + #[OA\Response( + response: Response::HTTP_OK, + description: 'Un signalement', + content: new OA\JsonContent(ref: '#/components/schemas/SignalementResponse') + )] + public function getSignalementByReference( + SignalementRepository $signalementRepository, + SignalementDesordresProcessor $signalementDesordresProcessor, + UrlGeneratorInterface $urlGenerator, + string $reference, + ): JsonResponse { + $signalements = $signalementRepository->findForAPI(user : $this->getUser(), reference : $reference); + if (!count($signalements)) { + return new JsonResponse(['message' => 'Signalement introuvable'], Response::HTTP_NOT_FOUND); + } + $resource = new SignalementResponse($signalements[0], $signalementDesordresProcessor, $urlGenerator); + + return new JsonResponse($resource, Response::HTTP_OK); + } +} diff --git a/src/DataFixtures/Files/User.yml b/src/DataFixtures/Files/User.yml index 5be78c0c9..a663d4ec9 100644 --- a/src/DataFixtures/Files/User.yml +++ b/src/DataFixtures/Files/User.yml @@ -2,7 +2,7 @@ users: - email: api-01@histologe.fr roles: "[\"ROLE_API_USER\"]" - partner: "Administrateurs Histologe ALL" + partner: "Partenaire 13-01" statut: 1 is_generique: 0 is_mailing_active: 0 diff --git a/src/Dto/Api/Response/AffectationResponse.php b/src/Dto/Api/Response/AffectationResponse.php new file mode 100644 index 000000000..47f6f2ec7 --- /dev/null +++ b/src/Dto/Api/Response/AffectationResponse.php @@ -0,0 +1,26 @@ +dateCreation = $affectation->getCreatedAt()->format(\DATE_ATOM); + $this->dateReponse = $affectation->getAnsweredAt()?->format(\DATE_ATOM); + $this->statut = $affectation->getStatut(); // envoyer un libellé ? + $this->partnerResponse = new PartnerResponse($affectation->getPartner()); + $this->motifCloture = $affectation->getMotifCloture()?->label(); + $this->motifRefus = $affectation->getMotifRefus()?->label(); + } +} diff --git a/src/Dto/Api/Response/DesordreResponse.php b/src/Dto/Api/Response/DesordreResponse.php new file mode 100644 index 000000000..e29e2232d --- /dev/null +++ b/src/Dto/Api/Response/DesordreResponse.php @@ -0,0 +1,31 @@ +categorie = $categorie; + $this->zone = $zone; + foreach ($data as $label => $unused) { + $details = $label; + if ($unused instanceof DesordrePrecision && $unused->getLabel()) { + $details .= ' : '.$unused->getLabel(); + } elseif ($unused instanceof Criticite && $unused->getLabel()) { + $details .= ' : '.$unused->getLabel(); + } + $this->details[] = $details; + } + } +} diff --git a/src/Dto/Api/Response/FileResponse.php b/src/Dto/Api/Response/FileResponse.php new file mode 100644 index 000000000..4aff00030 --- /dev/null +++ b/src/Dto/Api/Response/FileResponse.php @@ -0,0 +1,25 @@ +uuid = $file->getUuid(); + $this->titre = $file->getTitle(); + $this->documentType = $file->getDocumentType()->value; + $this->url = $urlGenerator->generate('show_file', ['uuid' => $file->getUuid()], UrlGeneratorInterface::ABSOLUTE_URL); + // besoin d'exposer plus d'élements ? + } +} diff --git a/src/Dto/Api/Response/GeolocalisationResponse.php b/src/Dto/Api/Response/GeolocalisationResponse.php new file mode 100644 index 000000000..2602ad939 --- /dev/null +++ b/src/Dto/Api/Response/GeolocalisationResponse.php @@ -0,0 +1,10 @@ +dateIntervention = $intervention->getScheduledAt()->format(\DATE_ATOM); + $this->type = $intervention->getType()?->label(); + $this->statut = $intervention->getStatus(); + $this->partner = $intervention->getPartner() ? new PartnerResponse($intervention->getPartner()) : null; + $this->details = $intervention->getDetails(); // traitement de suppression du html + $this->conclusions = $intervention->getConcludeProcedure() ?? []; + $this->occupantPresent = $intervention->isOccupantPresent(); + $this->proprietairePresent = $intervention->isProprietairePresent(); + // besoin d'exposer plus d'élements ? + } +} diff --git a/src/Dto/Api/Response/PartnerResponse.php b/src/Dto/Api/Response/PartnerResponse.php new file mode 100644 index 000000000..cab5963a9 --- /dev/null +++ b/src/Dto/Api/Response/PartnerResponse.php @@ -0,0 +1,21 @@ +nom = $partner->getNom(); + $this->type = $partner->getType()?->label(); + $this->competences = $partner->getCompetence() ?? []; + // besoin d'exposer plus d'élements ? + } +} diff --git a/src/Dto/Api/Response/SignalementResponse.php b/src/Dto/Api/Response/SignalementResponse.php new file mode 100644 index 000000000..2c54c527a --- /dev/null +++ b/src/Dto/Api/Response/SignalementResponse.php @@ -0,0 +1,316 @@ +uuid = $signalement->getUuid(); + $this->reference = $signalement->getReference(); + $this->dateCreation = $signalement->getCreatedAt()->format(\DATE_ATOM); + $this->statut = $signalement->getStatut(); // envoyer un libellé ? + $this->dateValidation = $signalement->getValidatedAt()?->format(\DATE_ATOM); + $this->dateCloture = $signalement->getClosedAt()?->format(\DATE_ATOM); + $this->motifCloture = $signalement->getMotifCloture()?->label(); + $this->motifRefus = $signalement->getMotifRefus()?->label(); + $this->abandonProcedureUsager = $signalement->getIsUsagerAbandonProcedure(); + // type declarant et details + $this->typeDeclarant = $signalement->getProfileDeclarant()?->label(); + $this->precisionTypeSiBailleur = $signalement->getTypeProprio()?->label(); + $this->lienDeclarantOccupantSiTiers = $signalement->getLienDeclarantOccupant(); + $this->details = $signalement->getDetails(); // renomer ? + // infos logement + $this->natureLogement = $signalement->getNatureLogement(); + $this->precisionNatureLogement = $signalement->getTypeCompositionLogement()?->getTypeLogementNatureAutrePrecision(); + $this->logementSocial = $signalement->getIsLogementSocial(); + $this->superficie = $signalement->getSuperficie(); + $this->pieceUnique = $this->stringToBool($signalement->getTypeCompositionLogement()?->getCompositionLogementPieceUnique()); + $this->nbPieces = $signalement->getTypeCompositionLogement()?->getCompositionLogementNbPieces() ?? $signalement->getNbPiecesLogement(); + $this->anneeConstruction = $signalement->getInformationComplementaire()?->getInformationsComplementairesLogementAnneeConstruction() ?? $signalement->getAnneeConstruction(); + $this->constructionAvant1949 = $signalement->getIsConstructionAvant1949(); + $this->nbNiveaux = $signalement->getInformationComplementaire()?->getInformationsComplementairesLogementNombreEtages() ?? $signalement->getNbNiveauxLogement(); + $this->rezDeChaussee = $this->stringToBool($signalement->getTypeCompositionLogement()?->getTypeLogementRdc()); + $this->dernierEtage = $this->stringToBool($signalement->getTypeCompositionLogement()?->getTypeLogementDernierEtage()); + $this->sousSolSansFenetre = $this->stringToBool($signalement->getTypeCompositionLogement()?->getTypeLogementSousSolSansFenetre()); + $this->sousCombleSansFenetre = $this->stringToBool($signalement->getTypeCompositionLogement()?->getTypeLogementSousCombleSansFenetre()); + $this->pieceAVivreSuperieureA9m = $this->stringToBool($signalement->getTypeCompositionLogement()?->getTypeLogementCommoditesPieceAVivre9m()); + $this->cuisine = $this->stringToBool($signalement->getTypeCompositionLogement()?->getTypeLogementCommoditesCuisine()); + $this->cuisineCollective = $this->stringToBool($signalement->getTypeCompositionLogement()?->getTypeLogementCommoditesCuisineCollective()); + $this->salleDeBain = $this->stringToBool($signalement->getTypeCompositionLogement()?->getTypeLogementCommoditesSalleDeBain()); + $this->salleDeBainCollective = $this->stringToBool($signalement->getTypeCompositionLogement()?->getTypeLogementCommoditesSalleDeBainCollective()); + $this->wc = $this->stringToBool($signalement->getTypeCompositionLogement()?->getTypeLogementCommoditesWc()); + $this->wcDansCuisine = $this->stringToBool($signalement->getTypeCompositionLogement()?->getTypeLogementCommoditesWcCuisine()); + $this->wcCollectif = $this->stringToBool($signalement->getTypeCompositionLogement()?->getTypeLogementCommoditesWcCollective()); + $this->hauteurSuperieureA2metres = $this->stringToBool($signalement->getTypeCompositionLogement()?->getCompositionLogementHauteur()); + $this->dpeExistant = $this->stringToBool($signalement->getTypeCompositionLogement()?->getBailDpeDpe()); + $this->geoLocalisation = new GeolocalisationResponse($signalement->getGeoloc()['lat'] ?? null, $signalement->getGeoloc()['lng'] ?? null); + // infos declarant + $this->structureDeclarant = $signalement->getStructureDeclarant(); + $this->nomDeclarant = $signalement->getNomDeclarant(); + $this->prenomDeclarant = $signalement->getPrenomDeclarant(); + $this->telephoneDeclarant = $signalement->getTelDeclarantDecoded(); + $this->telephoneSecondaireDeclarant = $signalement->getTelDeclarantSecondaireDecoded(); + $this->mailDeclarant = $signalement->getMailDeclarant(); + $this->estTravailleurSocialPourOccupant = $this->stringToBool($signalement->getSituationFoyer()?->getTravailleurSocialAccompagnementDeclarant()); + // infos occupants + $this->civiliteOccupant = $signalement->getCiviliteOccupant(); + $this->nomOccupant = $signalement->getNomOccupant(); + $this->prenomOccupant = $signalement->getPrenomOccupant(); + $this->telephoneOccupant = $signalement->getTelOccupantDecoded(); + $this->telephoneSecondaireOccupant = $signalement->getTelOccupantBisDecoded(); + $this->mailOccupant = $signalement->getMailOccupant(); + $this->adresseOccupant = $signalement->getAdresseOccupant(); + $this->codePostalOccupant = $signalement->getCpOccupant(); + $this->villeOccupant = $signalement->getVilleOccupant(); + $this->codeInseeOccupant = $signalement->getInseeOccupant(); + $this->etageOccupant = $signalement->getEtageOccupant(); + $this->escalierOccupant = $signalement->getEscalierOccupant(); + $this->numAppartOccupant = $signalement->getNumAppartOccupant(); + $this->adresseAutreOccupant = $signalement->getAdresseAutreOccupant(); + $this->dateNaissanceOccupant = $signalement->getDateNaissanceOccupant()?->format('Y-m-d') ?? $signalement->getInformationComplementaire()?->getInformationsComplementairesSituationOccupantsDateNaissance(); + $this->dateEntreeLogement = $signalement->getDateEntree()?->format('Y-m-d'); + $this->nbOccupantsLogement = $signalement->getNbOccupantsLogement(); + $this->enfantsDansLogement = $this->stringToBool($signalement->getTypeCompositionLogement()?->getCompositionLogementEnfants()); + $this->assuranceContactee = $this->stringToBool($signalement->getInformationProcedure()?->getInfoProcedureAssuranceContactee()); + $this->reponseAssurance = $signalement->getInformationProcedure()?->getInfoProcedureReponseAssurance(); + $this->souhaiteQuitterLogement = $this->stringToBool($signalement->getSituationFoyer()?->getTravailleurSocialQuitteLogement()); + $this->souhaiteQuitterLogementApresTravaux = $this->stringToBool($signalement->getInformationProcedure()?->getInfoProcedureDepartApresTravaux()); + $this->suiviParTravailleurSocial = $this->stringToBool($signalement->getSituationFoyer()?->getTravailleurSocialAccompagnement()); + $this->revenuFiscalOccupant = $signalement->getInformationComplementaire()?->getInformationsComplementairesSituationOccupantsRevenuFiscal(); + // infos proprietaire + $this->nomProprietaire = $signalement->getNomProprio(); + $this->prenomProprietaire = $signalement->getPrenomProprio(); + $this->adresseProprietaire = $signalement->getAdresseProprio(); + $this->codePostalProprietaire = $signalement->getCodePostalProprio(); + $this->villeProprietaire = $signalement->getVilleProprio(); + $this->telephoneProprietaire = $signalement->getTelProprioDecoded(); + $this->telephoneSecondaireProprietaire = $signalement->getTelProprioSecondaireDecoded(); + $this->mailProprietaire = $signalement->getMailProprio(); + $this->proprietaireDateNaissance = $signalement->getInformationComplementaire()?->getInformationsComplementairesSituationBailleurDateNaissance(); + $this->proprietaireRevenuFiscal = $signalement->getInformationComplementaire()?->getInformationsComplementairesSituationBailleurRevenuFiscal() ?: null; + $this->proprietaireBeneficiaireRsa = $this->stringToBool($signalement->getInformationComplementaire()?->getInformationsComplementairesSituationBailleurBeneficiaireRsa()); + $this->proprietaireBeneficiaireFsl = $this->stringToBool($signalement->getInformationComplementaire()?->getInformationsComplementairesSituationBailleurBeneficiaireFsl()); + // infos location + $this->proprietaireAverti = $signalement->getIsProprioAverti(); + $this->loyer = $signalement->getLoyer(); + $this->bailEnCours = $signalement->getIsBailEnCours(); + $this->bailExistant = $this->stringToBool($signalement->getTypeCompositionLogement()?->getBailDpeBail()); + $this->etatDesLieuxExistant = $this->stringToBool($signalement->getTypeCompositionLogement()?->getBailDpeEtatDesLieux()); + $this->preavisDepartTransmis = $signalement->getisPreavisDepart(); + $this->demandeRelogementEffectuee = $signalement->getIsRelogement(); + $this->loyersPayes = $this->stringToBool($signalement->getInformationComplementaire()?->getinformationsComplementairesSituationOccupantsLoyersPayes()); + $this->dateEffetBail = $signalement->getInformationComplementaire()?->getInformationsComplementairesSituationBailleurDateEffetBail() ?? $this->dateEntreeLogement; + // infos allocataire + $this->allocataire = in_array($signalement->getIsAllocataire(), [null, '']) ? null : (bool) $signalement->getIsAllocataire(); // valeurs possibles : null, '', 0, 1, 'CAF', 'MSA' + $this->typeAllocataire = in_array($signalement->getIsAllocataire(), ['MSA', 'CAF']) ? $signalement->getIsAllocataire() : null; + $this->numAllocataire = $signalement->getNumAllocataire(); + $this->montantAllocation = $signalement->getSituationFoyer()?->getLogementSocialMontantAllocation() ?? $signalement->getMontantAllocation(); + $this->beneficiaireRSA = $this->stringToBool($signalement->getInformationComplementaire()?->getInformationsComplementairesSituationOccupantsBeneficiaireRsa()) ?? $signalement->getIsRsa(); + $this->beneficiaireFSL = $this->stringToBool($signalement->getInformationComplementaire()?->getInformationsComplementairesSituationOccupantsBeneficiaireFsl()) ?? $signalement->getIsFondSolidariteLogement(); + // désordres + $desordresInfos = $signalementDesordresProcessor->process($signalement); + if (!$signalement->getCreatedFrom()) { + foreach ($desordresInfos['criticitesArranged'] as $label => $data) { + $this->desordres[] = new DesordreResponse($label, $data); + } + } else { + foreach (DesordreCritereZone::getLabelList() as $zone => $unused) { + if (isset($desordresInfos['criticitesArranged'][$zone])) { + foreach ($desordresInfos['criticitesArranged'][$zone] as $label => $data) { + $this->desordres[] = new DesordreResponse($label, $data, $zone); + } + } + } + } + $this->score = $signalement->getScore(); + $this->scoreBatiment = $signalement->getScoreBatiment(); + $this->scoreLogement = $signalement->getScoreLogement(); + // tags, qualifications, suivis, affectations, visites, files + foreach ($signalement->getTags() as $tag) { + $this->tags[] = $tag->getLabel(); + } + foreach ($signalement->getSignalementQualifications() as $qualification) { + if (!$qualification->isPostVisite()) { + $this->qualifications[] = $qualification->getStatus()->label(); + } + } + foreach ($signalement->getSuivis() as $suivi) { + $this->suivis[] = new SuiviResponse($suivi); + } + foreach ($signalement->getAffectations() as $affectation) { + $this->affectations[] = new AffectationResponse($affectation); + } + foreach ($signalement->getInterventions() as $visite) { + $this->interventions[] = new InterventionResponse($visite); + } + foreach ($signalement->getFiles() as $file) { + $this->files[] = new FileResponse($file, $urlGenerator); + } + // divers + $this->territoire = $signalement->getTerritory()?->getName(); // retirer car ce sera toujours le même pour un utilisateur ? + $this->signalementImporte = $signalement->getIsImported(); + } + + private function stringToBool(?string $value): ?bool + { + if (in_array($value, ['oui', 'piece_unique'])) { + return true; + } + if (in_array($value, ['non', 'plusieurs_pieces'])) { + return false; + } + + return null; + } +} diff --git a/src/Dto/Api/Response/SuiviResponse.php b/src/Dto/Api/Response/SuiviResponse.php new file mode 100644 index 000000000..b5841ad65 --- /dev/null +++ b/src/Dto/Api/Response/SuiviResponse.php @@ -0,0 +1,25 @@ +id = $suivi->getId(); + $this->dateCreation = $suivi->getCreatedAt()->format(\DATE_ATOM); + $this->description = $suivi->getDescription(); // traitement de suppression du html ? comment gérer les bouton/doc qui sont présent en dur dans le contenu ? + $this->public = $suivi->getIsPublic(); + $this->type = $suivi->getType(); // envoyer un libellé ? + // exposer "createdBy" sous quelle forme ? + } +} diff --git a/src/Entity/ApiUserToken.php b/src/Entity/ApiUserToken.php index f13c91fab..943a17ce8 100644 --- a/src/Entity/ApiUserToken.php +++ b/src/Entity/ApiUserToken.php @@ -12,7 +12,7 @@ class ApiUserToken implements EntityHistoryInterface { // Constant values to update later - public const string EXPIRATION_TIME = '+2 minutes'; + public const string EXPIRATION_TIME = '+200 minutes'; public const string CLEAN_EXPIRATION_PERIOD = '-3 minutes'; #[ORM\Id] diff --git a/src/Repository/SignalementRepository.php b/src/Repository/SignalementRepository.php index afb00efd8..363cd0ca1 100755 --- a/src/Repository/SignalementRepository.php +++ b/src/Repository/SignalementRepository.php @@ -1388,4 +1388,37 @@ public function findSignalementsBetweenDates(\DateTimeImmutable $startDate, \Dat ->getQuery() ->getResult(); } + + public function findForAPI(User $user, int $limit = 1, ?string $uuid = null, ?string $reference = null): array + { + $territory = $user->getPartner()->getTerritory(); // TODO : impact multi territoire + $qb = $this->createQueryBuilder('s') + ->select('s', 'desordrePrecisions', 'desordreCategories', 'desordreCriteres', 'signalementQualifications', + 'files', 'tags', 'suivi', 'affectations', 'interventions', 'territory') + ->leftJoin('s.desordrePrecisions', 'desordrePrecisions') + ->leftJoin('s.desordreCategories', 'desordreCategories') + ->leftJoin('s.desordreCriteres', 'desordreCriteres') + ->leftJoin('s.signalementQualifications', 'signalementQualifications') + ->leftJoin('s.files', 'files') + ->leftJoin('s.tags', 'tags') + ->leftJoin('s.suivis', 'suivi') + ->leftJoin('s.affectations', 'affectations') + ->leftJoin('s.interventions', 'interventions') + ->leftJoin('s.territory', 'territory') + ->where('s.territory = :territory') + ->setParameter('territory', $territory) + ->orderBy('s.createdAt', 'DESC') + ->setMaxResults($limit); + if ($uuid) { + $qb->andWhere('s.uuid = :uuid') + ->setParameter('uuid', $uuid); + } + if ($reference) { + $qb->andWhere('s.reference = :reference') + ->setParameter('reference', $reference); + } + + return $qb->getQuery() + ->getResult(); + } } diff --git a/tests/Functional/Service/Signalement/VisiteNotifierTest.php b/tests/Functional/Service/Signalement/VisiteNotifierTest.php index 59138424b..0d5e919ec 100644 --- a/tests/Functional/Service/Signalement/VisiteNotifierTest.php +++ b/tests/Functional/Service/Signalement/VisiteNotifierTest.php @@ -44,7 +44,7 @@ public function testNotifyVisiteToConclude() $intervention = $signalement->getInterventions()[0]; $nbNotified = $this->visiteNotifier->notifyVisiteToConclude($intervention); - $this->assertEquals($nbNotified, 3); + $this->assertEquals($nbNotified, 4); } public function testNotifyVisiteToConclude69()