Aller au contenu

Module Declarations prealables

Objectif

Le module Declarations prealables gere des demandes administratives structurees par type. Chaque type definit :

  • les documents attendus ;
  • les questions a renseigner ;
  • les champs obligatoires ;
  • l'icone et le libelle affiches.

Une societe cree une demande en brouillon ou la depose pour examen. Les administrateurs suivent ensuite la demande, changent son statut, archivent/suppriment et recoivent/envoient des notifications.

Carte technique

flowchart TD
    Router["app/core/Router.php"] --> Controller["DeclarationPrealableController"]
    Router --> TypeController["admin/DeclarationPrealableTypeController"]

    Controller --> Model["DeclarationPrealableModel"]
    TypeController --> TypeModel["DeclarationPrealableTypeModel"]

    Model --> Tables["dp_instances / dp_instance_*"]
    TypeModel --> TypeTables["dp_types / dp_type_*"]
    Model --> Notifications["DeclarationPrealableNotificationService"]
    NoteModel["NoteModel"] --> Notifications
    Notifications --> Mailer["MailerService"]
    Cron["app/cron/dp_notifications_cron.php"] --> Notifications

    View["declarations-prealables.php"] --> Details["declaration-prealable-details.php"]
    AdminView["admin/declaration-prealable-types.php"] --> TypeModal["ajouter-modifier-pour-declaration-prealable-type.php"]

Fichiers a connaitre

Role Fichiers
Routes app/core/Router.php
Controleur demandes app/controllers/DeclarationPrealableController.php
Modele demandes app/models/DeclarationPrealableModel.php
Controleur types admin app/controllers/admin/DeclarationPrealableTypeController.php
Modele types admin app/models/admin/DeclarationPrealableTypeModel.php
Notifications app/services/DeclarationPrealableNotificationService.php, app/cron/dp_notifications_cron.php
Notes liees app/models/NoteModel.php
Vues app/views/declarations-prealables.php, app/views/partials/details/declaration-prealable-details.php
Admin types app/views/admin/declaration-prealable-types.php, app/views/partials/modals/ajouter-modifier-pour-declaration-prealable-type.php
SQL app/bd/declarations-prealables.sql

Routes demandes

Route Methode Acces routeur Controleur Usage
declarations-prealables GET FEATURE_GERER_DECLARATION_PREALABLE index() Liste et page principale.
declaration-prealable/get POST FEATURE_GERER_DECLARATION_PREALABLE get() Detail complet d'une demande.
declaration-prealable/save POST FEATURE_GERER_DECLARATION_PREALABLE save() Brouillon ou depot.
declaration-prealable/change-status POST admin changeStatus() Changement de statut.
declaration-prealable/archive POST admin archive() Archivage logique.
declaration-prealable/unarchive POST admin unarchive() Desarchivage.
declaration-prealable/delete POST admin delete() Suppression logique.

Routes admin des types

Route Methode Acces Controleur Usage
declaration-prealable-types GET admin index() Liste des types.
declaration-prealable-type/add POST admin add() Creation d'un type.
declaration-prealable-type/update POST admin update() Modification d'un type.
declaration-prealable-type/delete POST admin delete() Suppression d'un type.

Droits et plans

Le routeur controle la feature FEATURE_GERER_DECLARATION_PREALABLE.

Le modele refait un controle via checkFeatureAccess(), puis un controle de plan via checkRequestActionAccess() pour les actions de creation/modification.

Plan minimal pour creer ou modifier une demande :

  • MINIMUM_REQUIRED_PLAN_NAME = TPE ;
  • admins : rang force a 999, donc bypass de plan ;
  • GRATUIT est insuffisant.

Permissions utilisees :

  • liste/detail : PermissionMiddleware::require('déclarations préalable', 'view_all') ;
  • creation : déclarations préalable/create ;
  • edition : déclarations préalable/edit.

Les constantes de permissions contiennent le module déclarations préalable.

Schema de donnees

dp_types

Types administrables :

  • name unique ;
  • icon ;
  • timestamps.

dp_type_documents

Documents attendus par type :

  • dp_type_id ;
  • label ;
  • required ;
  • sort_order.

Suppression en cascade quand le type est supprime.

dp_type_questions

Questions attendues par type :

  • dp_type_id ;
  • label ;
  • required ;
  • sort_order.

Suppression en cascade quand le type est supprime.

dp_instances

Demande concrete d'une societe.

Champs principaux :

  • dp_type_id ;
  • societe_id ;
  • created_by_membre_id ;
  • reference ;
  • status ;
  • submitted_at ;
  • reviewed_at ;
  • reviewed_by_membre_id ;
  • review_reason ;
  • timestamps.

Statuts exacts dans l'enum SQL :

  • incomplète ;
  • en cours ;
  • déposée ;
  • non réalisable ;
  • terminée.

Colonnes de cycle de vie ajoutees au runtime par ensureLifecycleColumns() si absentes :

  • archived_at ;
  • archived_by_membre_id ;
  • deleted_at ;
  • deleted_by_membre_id.

dp_instance_documents

Documents d'une demande :

  • dp_instance_id ;
  • dp_type_document_id ;
  • label ;
  • required ;
  • file_path ;
  • original_name ;
  • mime_type ;
  • file_size ;
  • uploaded_at.

dp_instance_answers

Reponses d'une demande :

  • dp_instance_id ;
  • dp_type_question_id ;
  • label ;
  • answer.

Le detail general de la demande est stocke comme une reponse avec le libelle Description générale.

Flux : liste et visibilite

getPaginatedDeclarations() applique toujours deleted_at IS NULL.

En contexte admin :

  • seules les demandes deposees sont visibles (submitted_at IS NOT NULL) ;
  • filtre optionnel par societe_id.

En contexte societe :

  • filtre obligatoire sur societe_id = active_societe_id ;
  • les brouillons de la societe restent visibles.

Filtres supportes :

  • search : reference, nom du type, reponse Description générale ;
  • statut ;
  • created_by ;
  • societe_id pour admin ;
  • date_debut ;
  • date_fin ;
  • tri autorise : id, reference, status, created_at.

Une demande est editable cote societe seulement si :

  • l'utilisateur n'est pas admin ;
  • permission edit presente ;
  • pas archivee ;
  • statut incomplète ou en cours.

Flux : brouillon et depot

save() recoit :

  • declaration_id ;
  • type_id ;
  • reference ;
  • detail_demande ;
  • accepted_cgu ;
  • save_mode ;
  • documents ;
  • answers ;
  • fichiers $_FILES.

save_mode = draft :

  • statut incomplète ;
  • documents/reponses obligatoires peuvent manquer ;
  • submitted_at reste nul.

Depot :

  • statut en cours ;
  • reference obligatoire ;
  • detail_demande obligatoire ;
  • tous les documents obligatoires doivent etre presents ou deja existants ;
  • toutes les questions obligatoires doivent avoir une reponse ;
  • submitted_at est pose a la premiere soumission.

Notification admin :

  • envoyee seulement si la demande n'est pas un brouillon ;
  • et si c'est une nouvelle demande deposee ou une demande qui n'avait pas encore submitted_at.

Flux : documents uploades

Constantes :

  • module upload : dp ;
  • type de document : attachments ;
  • taille max : 5 Mo.

prepareInstanceDocument() :

  • conserve le fichier existant si aucun nouvel upload n'arrive ;
  • upload avec BaseModel::uploadFile($file, 'dp', 'attachments', 5242880, false) ;
  • stocke file_path sous attachments/<fichier> ;
  • calcule mime_type via mime_content_type() ;
  • stocke original_name, file_size, uploaded_at.

URL de lecture :

asset('uploads/dp/' . file_path)

Donc un fichier stocke en base comme attachments/x.pdf est servi via uploads/dp/attachments/x.pdf.

Flux : changement de statut

updateStatus() est reserve admin.

Statuts autorises par le modele :

  • en cours ;
  • incomplète ;
  • déposée ;
  • non réalisable ;
  • terminée.

Regles :

  • statut inconnu refuse ;
  • demande introuvable refusee ;
  • demande archivee refusee ;
  • demande deja terminée verrouillee ;
  • si le statut ne change pas, retour success sans notification ;
  • sinon mise a jour de status, reviewed_at, reviewed_by_membre_id, review_reason, updated_at ;
  • notification de changement de statut ensuite.

Archivage et suppression

Actions admin :

  • archive() renseigne archived_at et archived_by_membre_id ;
  • unarchive() remet ces champs a NULL ;
  • delete() renseigne deleted_at et deleted_by_membre_id.

La suppression est logique. Les listes excluent les lignes avec deleted_at.

Administration des types

DeclarationPrealableTypeModel::saveType() cree ou modifie un type et ses enfants dans une transaction.

Regles utiles :

  • nom obligatoire ;
  • documents et questions sont remplaces par les valeurs postees ;
  • chaque enfant porte label, required, sort_order ;
  • la suppression d'un type supprime d'abord documents/questions puis dp_types.

Attention : dp_instances.dp_type_id a une contrainte ON DELETE RESTRICT. Supprimer un type utilise par une demande peut donc echouer selon l'etat des donnees.

Notifications

DeclarationPrealableNotificationService cree dynamiquement la table dp_notifications :

  • dp_instance_id ;
  • societe_id ;
  • source_type ;
  • payload_json ;
  • status : pending, processing, sent, failed ;
  • retry_count ;
  • next_retry_at ;
  • last_error ;
  • sent_at ;
  • timestamps.

Retries :

  • MAX_RETRIES = 3 ;
  • delai entre retries : 300 secondes ;
  • processPendingNotifications($limit) borne la limite entre 1 et 100.

Sources verifiees :

  • new_request : nouvelle demande deposee, notification aux admins ;
  • status_changed : changement de statut, notification a la societe ;
  • note_from_societe : note ajoutee par la societe, notification aux admins ;
  • note_from_admin : note ajoutee par un admin, notification a la societe.

queueNotification() envoie les sources new_request et note_from_societe vers queueAdminNotifications(), donc potentiellement plusieurs lignes de notification.

Le modele tente l'envoi immediat apres mise en file via sendNotificationById(). Le cron sert de rattrapage pour les notifications pending.

Cron :

php app/cron/dp_notifications_cron.php

Integration Notes

NoteModel appelle dispatchDeclarationNoteNotification() quand resource_type === 'dp'.

Le payload de note contient :

  • source_type : note_from_admin ou note_from_societe selon la session ;
  • dp_instance_id ;
  • societe_id ;
  • reference ;
  • type_name ;
  • societe_name ;
  • detail_demande ;
  • nom de la personne ayant ajoute la note ;
  • note_content ;
  • redirect_path.

Particularites runtime

Deux schemas sont crees/alteres au runtime :

  • DeclarationPrealableModel::__construct() appelle ensureLifecycleColumns() pour ajouter les colonnes archive/suppression ;
  • DeclarationPrealableNotificationService::__construct() appelle ensureStorageSchema() pour creer dp_notifications.

Impact :

  • l'utilisateur SQL applicatif doit pouvoir ALTER TABLE et CREATE TABLE ;
  • un environnement avec droits limites peut echouer tardivement ;
  • ces changements devraient idealement etre appliques par migration avant usage.

Points de vigilance

  1. Utiliser les statuts exacts avec accents. Une variante comme incomplete ou terminee ne correspond pas a l'enum.
  2. Les admins ne voient que les demandes deposees (submitted_at IS NOT NULL). Les brouillons restent cote societe.
  3. Une demande terminée, non réalisable, archivee ou supprimee ne doit pas etre modifiable cote societe.
  4. Le champ accepted_cgu est recu par le controleur mais aucune colonne equivalente n'a ete identifiee dans le schema inspecte.
  5. La creation de dp_notifications est dans le service, pas dans declarations-prealables.sql.
  6. La suppression de type peut etre bloquee par dp_instances via ON DELETE RESTRICT.
  7. Les documents obligatoires sont verifies deux fois dans save() : avant preparation, puis apres preparation. Garder ce double verrou si la logique upload evolue.

Verification conseillee

  1. Creer un type avec un document obligatoire et une question obligatoire.
  2. En societe TPE ou plus, creer un brouillon : statut incomplète, submitted_at nul.
  3. Tenter le depot sans document obligatoire : refus attendu.
  4. Ajouter les pieces et reponses, deposer : statut en cours, submitted_at renseigne, notification new_request.
  5. En admin, verifier que la demande apparait dans la liste.
  6. Changer le statut vers déposée, puis verifier reviewed_at, reviewed_by_membre_id, review_reason et notification status_changed.
  7. Ajouter une note cote societe et cote admin ; verifier les sources note_from_societe et note_from_admin.
  8. Lancer php app/cron/dp_notifications_cron.php pour traiter les notifications restantes.

Questions ouvertes

  • Aucun test automatise dedie aux Declarations prealables n'a ete trouve dans les fichiers inspectes.
  • accepted_cgu semble capture au POST mais n'est pas persiste dans les tables inspectees.
  • Les migrations runtime devraient etre clarifiees avant durcissement des droits SQL de production.