Skip to content

ETQ usager, je peux déposer mon dossier même si la vérification du SIRET est temporairement indisponible#13435

Open
E-L-T wants to merge 10 commits into
mainfrom
feat/dossier-submission-with-external-data-pending-no-kind
Open

ETQ usager, je peux déposer mon dossier même si la vérification du SIRET est temporairement indisponible#13435
E-L-T wants to merge 10 commits into
mainfrom
feat/dossier-submission-with-external-data-pending-no-kind

Conversation

@E-L-T

@E-L-T E-L-T commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Résumé

Voir #12997 (périmètre SIRET)

  • La validation du champ SIRET ne bloque plus le dépôt que sur un 404 (SIRET inexistant) ; les fetchs en cours et les erreurs techniques sont non bloquants, avec une bannière informative pour l'usager.
  • Les pannes techniques d'API Entreprise (retries épuisés, échec non-retryable) convergent vers le mode dégradé : etablissement stub, acceptation bloquée par any_etablissement_as_degraded_mode?, données rattrapées par les jobs de backfill existants.
  • Un champ SIRET dont une colonne alimente une condition conserve la validation bloquante historique (condition non évaluable sans les données).

Pour tester

  • Si nécessaire, ajouter un token API entreprise dans votre profil admin (http://localhost:3000/profil?context=administrateur)
  • Créer une démarche avec un champ siret
  • Commencer un nouveau dossier
  • Là deux options possibles, pour produire un dossier en erreur technique, qui va pouvoir être déposé :
    • Couper l'accès à l'API en ajoutant 127.0.0.1 entreprise.api.gouv.fr dans /etc/hosts. Saisir un SIRET valide, puis attendre ~2 minutes (3 tentatives du job avec backoff : ~3 s, ~18 s, ~83 s). À l'épuisement, le hook converge vers le mode dégradé. Penser à retirer la ligne de /etc/hosts ensuite.
    • Ou juste en console :
    champ = Dossier.find(ID).champs.find(&:siret?)
    champ.update_columns(external_id: NUMERO_DU_SIRET, external_state: 'waiting_for_job')
    champ.handle_exhausted_external_data_retries!
    
  • Pour reproduire en console un dossier avec une erreur 404, qui ne peut pas être déposé :
champ.update_columns(
  value: NUMERO_DU_SIRET,
  external_id: NUMERO_DU_SIRET,
  etablissement_id: nil,
  external_state: 'external_error',
  fetch_external_data_exceptions: [ExternalDataException.new(error: 'not found', code: 404)]
)

Ce qu'on doit observer pour un dossier en erreur technique

  1. Côté usager : le message « La vérification automatique est temporairement indisponible. Vous pouvez poursuivre le dépôt… » doit s'afficher sous le champ, et le dépôt du dossier doit fonctionner.
  2. En base : champ fetched, etablissement créé avec seulement le siret (as_degraded_mode? → true).
  3. Côté instructeur : le dossier peut passer en instruction, mais accepter/refuser/classer sans suite sont refusés (can_terminer? → false tant que l'etablissement est dégradé).
  4. Rattrapage : une fois l'API rétablie, les données arrivent via le job planifié à +30 min — ou immédiatement en console : APIEntreprise::EtablissementJob.perform_now(etablissement.id, procedure.id). L'acceptation redevient alors possible.

@E-L-T
E-L-T force-pushed the feat/dossier-submission-with-external-data-pending-no-kind branch from 3f58eb0 to 5a370cc Compare July 7, 2026 14:06
@E-L-T
E-L-T force-pushed the feat/dossier-submission-with-external-data-pending-no-kind branch 5 times, most recently from e09ec54 to 80e128d Compare July 8, 2026 08:54
@E-L-T
E-L-T marked this pull request as ready for review July 8, 2026 09:10

@mfo mfo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, je te propose 3 patches :

  1. côté remontée quand l'API est down/429, je pense qu'on peut rester sur l'input_status_message_component, c'est son role
  2. plus discutable (a prendre ou aps), pas certains qu'on souhatie laisser passer des 401/403 etc...
  3. il y avait un angle mort côté dependant_conditions, je pense que tu peux embarquer ce patch les yeux fermer

Comment thread app/models/champs/siret_champ.rb
Comment thread app/components/dsfr/input_status_message_component.rb Outdated
@@ -34,17 +52,33 @@ def ready_for_external_call?

def fetch_external_data
case APIEntrepriseService.create_etablissement_with_fallback(self, external_id.delete(" "), dossier.user&.id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on creait déjà l'etb en mode degradé quand api down ou 429, j'ai donc pas trop compris ces changements (on gere les 401/403/409/422/451...). Or dans ces cas la, je ne suis pas certains qu'on souhaite passer le dossier (a la limite, le 409 arrive quand on requete le meme objet au mm moment, limite faudrait remonter le cas dans APIEntrepriseService.create_etablissement_with_fallback).

Propale pour simplifier :
0001-simplify-siret_champ-remove-redundant-degraded-mode-.patch

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effectivement, c'est peut-être pas une bonne idée de laisser passer sans distinction tout ce qui est 401/403/409/422/451...
Peut-être qu'il faut traiter différemment selon les erreurs.
Vu que le but de la fonctionnalité est de diminuer la gêne pour les usagers dans des cas où ça peut être rattrapé ensuite, je verrais bien :

  • 401 (Unauthorized) / 403 (Forbidden) / 409 (collision de requête) -> dépôt possible avec degraded_mode (par ex. via create_etablissement_with_fallback) + sentry -> rattrapé par les jobs mais surtout par le cron Cron::BackfillSiretDegradedModeJob une fois que le problème est réparé côté administration.
  • 451 (indispo pour raison légale) / 422 (requête invalide) : blocage du dépôt + sentry

Comment thread app/models/concerns/champ_external_data_concern.rb Outdated
Comment thread app/models/champs/siret_champ.rb Outdated
Comment thread app/validators/external_data_champ_validator.rb Outdated
Comment thread app/models/champs/siret_champ.rb Outdated
Comment thread app/components/editable_champ/siret_component/siret_component.html.haml Outdated
Comment thread app/jobs/champ_fetch_external_data_job.rb
transitions from: [:fetching], to: :fetched
# waiting_for_job: a champ can converge to fetched from the retry_on
# exhaustion block (see #handle_exhausted_external_data_retries!)
transitions from: [:fetching, :waiting_for_job], to: :fetched

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je suis un peu embété d'avoir un état fetched pour un champ ... qui ne l'ai pas. De plus, on va chercher a étendre cette mécanique à d'autres champs connectés.

Du coup, je te proposerai bien, dans la machine à état, d'introduire un nouvelle état degraded et une nouvelle transition exhausted_retry! qui irait de waiting_for_job -> degraded

@E-L-T
E-L-T force-pushed the feat/dossier-submission-with-external-data-pending-no-kind branch from cc757c1 to 4ebf9ab Compare July 9, 2026 08:34
E-L-T and others added 9 commits July 9, 2026 10:39
Legacy services produce a Failure without a code key, which raised
NoMatchingPatternKeyError in handle_result. Persist the exception
with a nil code and move the champ to external_error instead.
Lenient validation for Champs::SiretChamp: only a 404 (nonexistent
SIRET) blocks submission; pending and technical errors do not, and a
syntactically valid SIRET satisfies the mandatory check. Ref #12997
…n is unavailable

SiretChamp#external_data_status_message returns :technical_error on a
non-404 fetch failure; the SIRET component renders an informative
banner. The old red warning on external_error is removed.
Instead of converting the degraded Success into a retryable 503, keep
the champ fetched: submission works naturally and instructeurs are
already blocked by Dossier#any_etablissement_as_degraded_mode?.
…ition

A condition can target a column of a SIRET champ (value_json), so it
cannot be evaluated until the data is fetched. In that case the
historical blocking validation is restored via dependent_conditions?.
Exhausted retries or a non-retryable non-404 failure now create a
degraded etablissement (champ fetched) so acceptance stays blocked and
backfill jobs recover the data. Only a 404 ends in external_error.
OCRService was the only fetch_external_data producer returning a
Failure without a code key. Fix it at the source and remove the
code-less pattern-match arm from ChampExternalDataConcern.
…mponent

The degraded-mode message was rendered directly in SiretComponent,
bypassing the component that owns the a11y contract (fr-messages-group,
aria-describedby). Move it into the :siret branch, keeping it hidden
when the champ feeds a condition since submission is then blocked.

Co-authored-by: mfo <mfo@users.noreply.github.com>
@E-L-T
E-L-T force-pushed the feat/dossier-submission-with-external-data-pending-no-kind branch from 4ebf9ab to 4999040 Compare July 9, 2026 08:40
Same structure, clearer vocabulary:
lenient_validate -> permissive_validate, legacy_validate -> strict_validate,
lenient_external_data_validation? -> permissive_external_data_validation?.
@E-L-T
E-L-T force-pushed the feat/dossier-submission-with-external-data-pending-no-kind branch from 4999040 to 29472ae Compare July 9, 2026 09:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants