feat(companies): resolve and set SIREN via VigieProcure - #11
Merged
Conversation
Ajoute deux procedures tRPC (companies.resolveSiren, companies.setSiren) qui appellent GET /api/v1/companies/resolve cote api_v2 avec un JWT de service (VIGIEPROCURE_API_JWT/VIGIEPROCURE_API_URL, meme doctrine que VIGIEPROCURE_WEBHOOK_URL/_SECRET -- absent = fonctionnalite degradee, jamais un appel non authentifie). Sur la fiche company : affiche le SIREN existant, ou propose de le resoudre sur clic explicite. Un seul candidat "exact" ecrit automatiquement (exception ciblee et reversible, validee par Franck -- jamais depuis un effet au chargement ni un backfill de masse). Sinon, liste les candidats pour choix manuel. Conflit d'unicite (P2002 sur Company.siren) traduit en erreur utilisateur nommant la fiche en conflit, jamais un 500 nu. Inclut la regeneration de apps/api/src/generated/server.ts, qui corrige au passage une dette preexistante (excludeEmail/restoreEmail declares dans activities.router.ts depuis le commit 044e350 mais jamais regeneres). A provisionner separement (hors perimetre de ce chantier) : VIGIEPROCURE_API_URL, VIGIEPROCURE_API_JWT sur crm-api en production. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LtJccFqpSuP8tQ3Pwg1dYH
- companySetSirenInput.siren: .length(9) laissait passer 9 caracteres non numeriques alors que le message annoncait "9 digits" -- .regex le fait respecter reellement. - companySetSirenOutput (conflict): conflictingCompanyId/Name n'etaient consommes nulle part cote frontend (seul `reason` est lu), et le fallback `conflicting?.id ?? ""` etait un `z.string()` non-nullable rempli d'une chaine vide dans un cas quasi mort (le findFirst est scope par la meme contrainte unique partielle que le P2002 qui declenche ce chemin). Retire les deux champs plutot que de les rendre nullable pour un consommateur qui n'existe pas. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LtJccFqpSuP8tQ3Pwg1dYH
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LtJccFqpSuP8tQ3Pwg1dYH
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
companies.resolveSiren,companies.setSiren) qui appellentGET /api/v1/companies/resolvecote api_v2, avec un JWT de service lu depuis l'environnement (jamais un appel non authentifie -- meme doctrine quevigieProcureBridge()/bridge()).apps/app) : affiche le SIREN existant, ou propose de le resoudre sur clic explicite. Un seul candidatconfidence: "exact"ecrit automatiquement -- toujours declenche par une action utilisateur (jamais un effet au chargement ni un backfill de masse), doctrine validee par Franck. Sinon, liste les candidats pour choix manuel.P2002surCompany.siren, contrainte scopeearchivedAt: null) traduit en erreur utilisateur nommant la fiche en conflit, jamais un 500 nu -- procedure dediee avec son proprecatch, ne passe pas parcompanies.update/translate()(qui aurait mappe le conflit sur le mauvais message, celui du domaine).Variables d'environnement a provisionner (hors perimetre de cette PR -- Franck)
VIGIEPROCURE_API_URL(ex.https://api.vigieproc.fr)VIGIEPROCURE_API_JWT-- jeton de service long-lived mint� coteapi_v2(issue_jwt("service:crm-api", ttl_min=...), voirapi_v2/services/auth.py), pas genere par cette branche.Absentes toutes les deux : la fonctionnalite se degrade proprement (
outcome: "not-configured", message explicite dans l'UI), jamais un crash ni un appel non authentifie.Ecart notable
Le diff inclut la regeneration de
apps/api/src/generated/server.ts. Cette regeneration corrige au passage une dette preexistante et sans rapport avec ce chantier :excludeEmail/restoreEmailsont declares dansactivities.router.tsdepuis le commit044e350(branchefeat/exclude-email-thread, deja mergee dansrelease) mais le fichier genere n'avait jamais ete rejoue depuis. Verifie en isolant la regeneration surorigin/releasenon modifie -- meme diff, avant tout changement de ce chantier.Test plan
bun run check-types(13/13 packages, monorepo entier) -- cleanbun run lint:slop(oxlint anti-slop) -- cleanbun run lint(biome, via le hook pre-push) -- cleanapps/api/test/vigieprocure-companies-client.spec.ts(7 tests : env absent ->not-configured, 401 ->unauthorized, corps malforme -> degradation propre, mapping d'un candidatexact) -- ne requiert pas de Postgresapps/api+apps/appavec Postgres jetable (docker compose up -d+db:test --reset) -- 445/445 et 169/169 respectivement (verifie aussi queAuth (e2e) > lets the sign-in page read what it may offer, un test sensible a l'ordre de chargement d'env preexistant, echoue identiquement surorigin/releasenon modifie -- pas une regression de cette PR)/mnt/hermes-extrasur vigiep1 a 97% plein)🤖 Generated with Claude Code
https://claude.ai/code/session_01LtJccFqpSuP8tQ3Pwg1dYH