docs/50-pilotage/99-technique.md —
ce document se lit aussi dans le dépôt, sans serveur.
Pilotage et outils — le côté technique
Les fichiers
| Rôle | Fichier |
|---|---|
| Tableau de bord | public/erp/index.php + public/erp/assets/js/pages/index-2.js |
| Compteurs d'en-tête | src/Http/Controllers/TopbarController.php |
| Analytics | public/erp/analytics.php, src/Http/Controllers/AnalyticsController.php |
| Données Analytics | src/Repository/AnalyticsRepository.php |
| La projection | src/Analytics/Projection.php |
| Attributions | public/erp/attributions.php, src/Http/Controllers/AttributionController.php |
| Données attributions | src/Repository/AttributionRepository.php |
| Export | public/erp/export.php, src/Http/Controllers/ExportController.php |
| Journal d'audit | public/erp/audit.php, src/Http/Controllers/AuditController.php |
| Écriture du journal | src/Service/AuditService.php |
| Recherche | src/Http/Controllers/SearchController.php |
| Filtrage par module | src/Http/ModuleAccess.php |
Les droits
| Écran | Droit |
|---|---|
| Analytics, attributions | Module analytics |
| Tableau de bord, recherche, compteurs | Module orders |
| Export | Module finance, et hr pour les fiches de paie |
| Journal d'audit | Administrateur |
Les dix vues d'Analytics
GET /v1/analytics/overview vue d'ensemble
GET /v1/analytics/sales-timeline ventes dans le temps
GET /v1/analytics/products produits
GET /v1/analytics/customers clients
GET /v1/analytics/attribution attribution
GET /v1/analytics/suppliers fournisseurs
GET /v1/analytics/margins marges
GET /v1/analytics/operations operations
GET /v1/analytics/cash tresorerie
GET /v1/analytics/quality qualite (irregularites par fournisseur)
L'export vérifie le module par type
private const TYPES = [
'bills' => [/* … */ 'module' => 'finance'],
'avoirs' => [/* … */ 'module' => 'finance'],
'salaires' => [/* … */ 'module' => 'hr'],
'bank-statements' => [/* … */ 'module' => 'finance'],
];
Ajouter un type sans son module le laisse accessible à tout détenteur de finance.
Rien n'est stocké côté lecture
audit_logs est écrite par AuditService et lue telle quelle. InnerPerf et la projection ne stockent rien non plus : tout se recalcule à chaque lecture.
Un chiffre stocké serait faux le jour où la règle qui le produit change, et personne ne saurait lequel des deux croire.
Les tests
tests/Unit/ProjectionTest.php, tests/Unit/DroitsDeModuleTest.php, tests/Unit/VocabulaireAuditTest.php, tests/Unit/LikePatternTest.php.
Sur MySQL : tests/Integration/AnalyticsAvanceeIntegrationTest.php.
Les recherches des clients
Pourquoi Darsy relève lui-même
L'API de statistiques de Doofinder est agrégée. clicks-with-users et inits-with-users comptent les utilisateurs, ils ne les nomment pas ; searches rend un total par jour. Un seul de leurs points d'entrée est individuel — /sales, qui donne le montant et la session de chaque vente.
Savoir ce qu'un client donné a cherché est donc hors de portée de leur API.
La capture observe le réseau, pas le DOM
Le module darsy-plugin/includes/recherches.php enfile un script qui enveloppe fetch et XMLHttpRequest et observe les appels vers le moteur de Doofinder.
La couche Doofinder n'expose que trois événements — doofinder.layer.load, .update, .carousel.load — et leur charge utile n'est pas documentée. Lire la valeur d'un champ de saisie au bon moment serait deviner, et casserait au premier changement de leur interface.
Ce que Doofinder demande est en revanche exactement ce que le visiteur a cherché. Et la réponse porte le nombre de résultats : les recherches sans résultat en sortent gratuitement.
Trois choses sont prises au passage :
| Sur | On lit |
|---|---|
…/_search | la requête et son nombre de résultats |
…/stats/click | l'article cliqué |
| n'importe quel appel de statistiques | l'identifiant de session de Doofinder |
Ce dernier n'est donc pas deviné : il est lu. Il permettra de rapprocher nos relevés de leur point /sales.
La jointure n'existe que parce qu'on la pose
Notre propre identifiant de session vit dans un cookie, posé dès la première page, et le plugin l'écrit dans la méta _darsy_recherche_session au paiement. Sans cette méta, les relevés resteraient une masse anonyme : on saurait ce que « quelqu'un » a cherché, jamais ce que ce client a cherché.
Notre identifiant est le maître, pas le leur. Celui de Doofinder peut manquer — visiteur qui n'ouvre jamais la recherche, script bloqué, changement de leur côté. La jointure ne dépend donc de personne d'autre que nous.
Le cookie plutôt qu'un champ caché du formulaire : il fonctionne pour la commande classique et pour les blocs, et il survit au cache de pages — un champ caché serait servi figé.
Rien n'est stocké dans Darsy
Les relevés vivent sur la boutique, là où ils s'écrivent. Darsy les demande quand un opérateur ouvre un écran.
Une lecture par consultation au lieu d'une copie permanente à synchroniser : une seule vérité, aucune tâche planifiée, et pas de route d'écriture publique sur l'ERP pour de la télémétrie.
L'écriture publique est bornée par sa forme, pas par une identité
La route POST darsy/v1/recherche est appelée par le navigateur du visiteur : elle ne peut pas exiger de clé, et un jeton de formulaire serait servi périmé par le cache de pages.
Elle est donc bornée autrement : deux cents caractères par texte, trois cents événements par session, et une réponse 204 identique en cas de succès comme de refus — ce qui retire tout intérêt à la sonder. Une requête inventée ne peut que polluer nos propres statistiques, et seulement jusqu'à ces plafonds.
Les routes
| Route | Droit | Ce qu'elle fait |
|---|---|---|
GET /v1/searches/session/{session} | commandes | La chronologie d'une session |
GET /v1/searches/report | analytics | Les deux listes, sur une période |
POST /v1/searches/purge | administrateur | Efface avant une date, et le journalise |
Côté plugin : POST darsy/v1/recherche (public, borné), GET darsy/v1/recherches/session/{session}, GET darsy/v1/recherches/rapport, POST darsy/v1/recherches/purge — les trois dernières derrière la clé partagée.
Le déploiement demande un geste de plus
Le module vit dans darsy-plugin/, qui est dans le dépôt Darsy mais s'exécute sur WordPress. Un git pull côté ERP ne le met donc pas en service : il faut copier darsy-plugin/ dans wp-content/plugins/ de la boutique. La table se crée toute seule au premier chargement — y compris sur un plugin déjà actif, qui ne rejouerait jamais son hook d'activation.