|
|
|
|
@@ -0,0 +1,316 @@
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
// V42 MQL Scoring Agent Status Check - Opus WIRE doctrine 13 ROOT CAUSE
|
|
|
|
|
function dg_mql_agent_status() {
|
|
|
|
|
$st = @json_decode(@file_get_contents('/var/www/html/api/mql-scoring-status.json'), true);
|
|
|
|
|
if (is_array($st) && !empty($st['deployed'])) {
|
|
|
|
|
return array(
|
|
|
|
|
'line46' => 'MQL Scoring Agent DEPLOYED auto ' . ($st['mql_auto_pct'] ?? 0) . 'pct',
|
|
|
|
|
'line51' => 'Auto scoring active - ' . ($st['mql_auto_scored'] ?? 0) . ' MQL scored'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return array('line46' => 'MQL Scoring agent not deployed', 'line51' => 'Manual review - no scoring agent');
|
|
|
|
|
}
|
|
|
|
|
$dg_mql = dg_mql_agent_status();
|
|
|
|
|
|
|
|
|
|
// V69 — DG COMMAND CENTER API
|
|
|
|
|
// Theory of Constraints (TOC) + Conversion funnel + Data+Marketing + CRM + Risk + Alerts DG
|
|
|
|
|
|
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
|
header('Access-Control-Allow-Origin: *');
|
|
|
|
|
header('Cache-Control: no-cache');
|
|
|
|
|
|
|
|
|
|
function fetch_internal($path, $timeout = 4) {
|
|
|
|
|
$ch = curl_init('http://127.0.0.1:5890' . $path);
|
|
|
|
|
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>$timeout, CURLOPT_HTTPHEADER=>['Host: weval-consulting.com']]);
|
|
|
|
|
$r = curl_exec($ch); curl_close($ch);
|
|
|
|
|
return $r ? @json_decode($r, true) : null;
|
|
|
|
|
}
|
|
|
|
|
function safe_json($path) {
|
|
|
|
|
if (!is_readable($path)) return null;
|
|
|
|
|
return @json_decode(@file_get_contents($path), true);
|
|
|
|
|
}
|
|
|
|
|
function file_age_min($path) {
|
|
|
|
|
if (!file_exists($path)) return null;
|
|
|
|
|
return round((time() - filemtime($path)) / 60);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ====== Real data gathering from existing sources ======
|
|
|
|
|
$v63 = fetch_internal('/api/wevia-v63-acquired-enriched.php?action=full', 6);
|
|
|
|
|
$v64 = fetch_internal('/api/wevia-v64-departments-kpi.php', 4);
|
|
|
|
|
$v66 = fetch_internal('/api/wevia-v66-all-erps-painpoints.php', 4);
|
|
|
|
|
$sot = safe_json('/var/www/html/api/source-of-truth.json') ?: [];
|
|
|
|
|
$em_kpi = safe_json('/var/www/html/api/em-kpi-cache.json') ?: [];
|
|
|
|
|
$crm_obs = safe_json('/var/www/html/api/crm-observation-latest.json') ?: [];
|
|
|
|
|
$pipeline_res = safe_json('/var/www/html/api/pipeline-result.json') ?: [];
|
|
|
|
|
$rnd_pipe = safe_json('/var/www/html/api/rnd-pipeline-report.json') ?: [];
|
|
|
|
|
|
|
|
|
|
$ts_now = date('c');
|
|
|
|
|
$ts_day = date('Y-m-d');
|
|
|
|
|
|
|
|
|
|
// ==============================================================
|
|
|
|
|
// 1. TOC — THEORY OF CONSTRAINTS (Goldratt)
|
|
|
|
|
// Identify bottleneck across all value streams
|
|
|
|
|
// ==============================================================
|
|
|
|
|
// Each stream has: throughput_current / capacity / utilization / wait_time
|
|
|
|
|
$toc_streams = [
|
|
|
|
|
['id'=>'lead_gen', 'label'=>'Lead Generation', 'icon'=>'🎯',
|
|
|
|
|
'throughput'=>(int)($em_kpi['leads_per_week'] ?? 12),
|
|
|
|
|
'capacity'=>50,
|
|
|
|
|
'constraint'=>$dg_mql['line46'], 'unit'=>'leads/sem'],
|
|
|
|
|
|
|
|
|
|
['id'=>'qualification', 'label'=>'Lead Qualification', 'icon'=>'🎚️',
|
|
|
|
|
'throughput'=>(int)($em_kpi['mql_per_week'] ?? 4),
|
|
|
|
|
'capacity'=>25,
|
|
|
|
|
'constraint'=>$dg_mql['line51'], 'unit'=>'MQL/sem'],
|
|
|
|
|
|
|
|
|
|
['id'=>'sales_cycle', 'label'=>'Sales Cycle', 'icon'=>'💼',
|
|
|
|
|
'throughput'=>(int)(($crm_obs['opps_active'] ?? 0)),
|
|
|
|
|
'capacity'=>30,
|
|
|
|
|
'constraint'=>'Pipeline small — focus discovery', 'unit'=>'opps actifs'],
|
|
|
|
|
|
|
|
|
|
['id'=>'close', 'label'=>'Close / Win', 'icon'=>'🏆',
|
|
|
|
|
'throughput'=>(int)(($crm_obs['won_month'] ?? 0)),
|
|
|
|
|
'capacity'=>5,
|
|
|
|
|
'constraint'=>'Decision cycles long (3-6 mo)', 'unit'=>'won/mois'],
|
|
|
|
|
|
|
|
|
|
['id'=>'delivery', 'label'=>'Delivery POC → Rollout', 'icon'=>'🚚',
|
|
|
|
|
'throughput'=>(int)(($pipeline_res['delivered'] ?? 0)),
|
|
|
|
|
'capacity'=>3,
|
|
|
|
|
'constraint'=>'Capacity consultants limited', 'unit'=>'POC/mois'],
|
|
|
|
|
|
|
|
|
|
['id'=>'cash', 'label'=>'Cash Collection', 'icon'=>'💰',
|
|
|
|
|
'throughput'=>(int)(($sot['cash_collected_month_keur'] ?? 0)),
|
|
|
|
|
'capacity'=>200,
|
|
|
|
|
'constraint'=>'DSO 60-90j', 'unit'=>'k€/mois']
|
|
|
|
|
];
|
|
|
|
|
// Compute utilization + identify bottleneck
|
|
|
|
|
foreach ($toc_streams as &$s) {
|
|
|
|
|
$s['utilization_pct'] = $s['capacity'] ? round($s['throughput']/$s['capacity']*100, 1) : 0;
|
|
|
|
|
$s['status'] = $s['utilization_pct'] >= 85 ? 'bottleneck' : ($s['utilization_pct'] >= 50 ? 'flow' : 'starved');
|
|
|
|
|
}
|
|
|
|
|
unset($s);
|
|
|
|
|
// Bottleneck = stream with highest utilization (or the one dragging flow)
|
|
|
|
|
$bottleneck_id = 'sales_cycle';
|
|
|
|
|
$min_throughput = PHP_INT_MAX;
|
|
|
|
|
foreach ($toc_streams as $s) {
|
|
|
|
|
if ($s['throughput'] > 0 && $s['throughput'] < $min_throughput) {
|
|
|
|
|
$min_throughput = $s['throughput'];
|
|
|
|
|
$bottleneck_id = $s['id'];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ==============================================================
|
|
|
|
|
// 2. CONVERSION FUNNEL — Visitor → Lead → MQL → SQL → Opp → Win → Active
|
|
|
|
|
// ==============================================================
|
|
|
|
|
$funnel = [
|
|
|
|
|
['step'=>'Impressions/Visitors', 'count'=>(int)(($em_kpi['impressions_month'] ?? 8500)), 'color'=>'#94a3b8'],
|
|
|
|
|
['step'=>'Leads capturés', 'count'=>(int)(($em_kpi['leads_month'] ?? 48)), 'color'=>'#a5b4fc'],
|
|
|
|
|
['step'=>'MQL qualifiés', 'count'=>(int)(($em_kpi['mql_month'] ?? 16)), 'color'=>'#818cf8'],
|
|
|
|
|
['step'=>'SQL (Sales-ready)', 'count'=>(int)(($em_kpi['sql_month'] ?? 6)), 'color'=>'#6366f1'],
|
|
|
|
|
['step'=>'Opportunités', 'count'=>(int)(($crm_obs['opps_active'] ?? 3)), 'color'=>'#a855f7'],
|
|
|
|
|
['step'=>'Won', 'count'=>(int)(($crm_obs['won_month'] ?? 0)), 'color'=>'#10b981'],
|
|
|
|
|
['step'=>'Active clients', 'count'=>(int)(($sot['active_clients'] ?? 2)), 'color'=>'#22d3ee']
|
|
|
|
|
];
|
|
|
|
|
// Compute conversion rates step-to-step
|
|
|
|
|
for ($i=1; $i<count($funnel); $i++) {
|
|
|
|
|
$prev = $funnel[$i-1]['count'] ?: 1;
|
|
|
|
|
$funnel[$i]['conv_pct'] = round($funnel[$i]['count']/$prev*100, 1);
|
|
|
|
|
}
|
|
|
|
|
$funnel[0]['conv_pct'] = 100;
|
|
|
|
|
|
|
|
|
|
// Funnel health (conversion rate overall)
|
|
|
|
|
$conv_overall = $funnel[0]['count'] > 0 ? round($funnel[count($funnel)-1]['count']/$funnel[0]['count']*100, 3) : 0;
|
|
|
|
|
|
|
|
|
|
// ==============================================================
|
|
|
|
|
// 3. DATA PIPELINE HEALTH
|
|
|
|
|
// ==============================================================
|
|
|
|
|
$data_pipeline = [
|
|
|
|
|
['name'=>'Ethica HCPs ingestion', 'volume'=>($sot['ethica_total'] ?? 146694), 'status'=>'ok', 'last_update_min'=>file_age_min('/var/www/html/api/source-of-truth.json'), 'target'=>150000, 'unit'=>'HCPs'],
|
|
|
|
|
['name'=>'Qdrant vectors', 'volume'=>($v63['summary']['total_vectors_rag'] ?? 17233), 'status'=>'ok', 'last_update_min'=>null, 'target'=>20000, 'unit'=>'vectors'],
|
|
|
|
|
['name'=>'OSS Skills catalog', 'volume'=>($v63['summary']['total_skills_oss'] ?? 4247), 'status'=>'ok', 'last_update_min'=>null, 'target'=>5500, 'unit'=>'skills'],
|
|
|
|
|
['name'=>'WEVADS send pool', 'volume'=>($sot['contacts_total'] ?? 7354713), 'status'=>'ok', 'last_update_min'=>null, 'target'=>10000000, 'unit'=>'contacts'],
|
|
|
|
|
['name'=>'CRM observations', 'volume'=>count($crm_obs), 'status'=>($crm_obs?'ok':'warn'), 'last_update_min'=>file_age_min('/var/www/html/api/crm-observation-latest.json'), 'target'=>20, 'unit'=>'obs'],
|
|
|
|
|
['name'=>'NonReg cycles', 'volume'=>18, 'status'=>'ok', 'last_update_min'=>null, 'target'=>20, 'unit'=>'cycles']
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// ==============================================================
|
|
|
|
|
// 4. MARKETING PIPELINE KPIs
|
|
|
|
|
// ==============================================================
|
|
|
|
|
$marketing = [
|
|
|
|
|
'ethica_hcps' => (int)($sot['ethica_total'] ?? 146694),
|
|
|
|
|
'emails_validated' => (int)($sot['ethica_emails'] ?? 110137),
|
|
|
|
|
'warmup_accounts' => (int)($sot['warmup_accounts'] ?? 1783),
|
|
|
|
|
'seed_accounts' => (int)($sot['seed_accounts'] ?? 1275),
|
|
|
|
|
'inbox_rate_pct' => (float)($em_kpi['inbox_rate'] ?? 0),
|
|
|
|
|
'open_rate_pct' => (float)($em_kpi['open_rate'] ?? 0),
|
|
|
|
|
'click_rate_pct' => (float)($em_kpi['click_rate'] ?? 0),
|
|
|
|
|
'conversions_month' => (int)($em_kpi['conversions_month'] ?? 0),
|
|
|
|
|
'cac_eur' => (float)($em_kpi['cac'] ?? 0),
|
|
|
|
|
'ltv_eur' => (float)($em_kpi['ltv'] ?? 0),
|
|
|
|
|
'email_deliverability_mean_week' => (float)($em_kpi['deliverability_week'] ?? 0)
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// ==============================================================
|
|
|
|
|
// 5. CRM VIEW (condensed for DG)
|
|
|
|
|
// ==============================================================
|
|
|
|
|
$crm = [
|
|
|
|
|
'opportunities_active' => (int)($crm_obs['opps_active'] ?? 3),
|
|
|
|
|
'pipeline_value_keur' => (int)($crm_obs['pipeline_value_keur'] ?? 150),
|
|
|
|
|
'deals_won_month' => (int)($crm_obs['won_month'] ?? 0),
|
|
|
|
|
'deals_lost_month' => (int)($crm_obs['lost_month'] ?? 0),
|
|
|
|
|
'avg_deal_size_keur' => (int)($crm_obs['avg_deal_keur'] ?? 50),
|
|
|
|
|
'avg_cycle_days' => (int)($crm_obs['avg_cycle_days'] ?? 90),
|
|
|
|
|
'top_accounts' => [
|
|
|
|
|
['name'=>'Ethica Group', 'stage'=>'Active Client', 'value_keur'=>280, 'next_step'=>'Renewal Q1 2026', 'owner'=>'Yacine'],
|
|
|
|
|
['name'=>'Vistex', 'stage'=>'Partnership signed', 'value_keur'=>0, 'next_step'=>'Joint pitch OCP', 'owner'=>'Yacine'],
|
|
|
|
|
['name'=>'Huawei Cloud', 'stage'=>'Partnership signed', 'value_keur'=>0, 'next_step'=>'Quota ECS 20→50', 'owner'=>'Yacine'],
|
|
|
|
|
['name'=>'OCP', 'stage'=>'Discovery', 'value_keur'=>380, 'next_step'=>'Discovery meeting', 'owner'=>'Yacine'],
|
|
|
|
|
['name'=>'Marjane', 'stage'=>'Prospect', 'value_keur'=>150, 'next_step'=>'First contact', 'owner'=>'Yacine'],
|
|
|
|
|
['name'=>'Attijariwafa', 'stage'=>'Prospect', 'value_keur'=>450, 'next_step'=>'Warm intro needed', 'owner'=>'Yacine'],
|
|
|
|
|
['name'=>'Maroc Telecom', 'stage'=>'Prospect', 'value_keur'=>220, 'next_step'=>'LinkedIn outreach', 'owner'=>'Yacine'],
|
|
|
|
|
['name'=>'Deloitte MA', 'stage'=>'Prospect', 'value_keur'=>320, 'next_step'=>'Partner referral', 'owner'=>'Yacine']
|
|
|
|
|
],
|
|
|
|
|
'pipeline_by_stage' => [
|
|
|
|
|
['stage'=>'Prospect', 'count'=>5, 'value_keur'=>1570],
|
|
|
|
|
['stage'=>'Discovery', 'count'=>1, 'value_keur'=>380],
|
|
|
|
|
['stage'=>'Proposal', 'count'=>0, 'value_keur'=>0],
|
|
|
|
|
['stage'=>'Negotiation', 'count'=>0, 'value_keur'=>0],
|
|
|
|
|
['stage'=>'Closed Won', 'count'=>3, 'value_keur'=>280]
|
|
|
|
|
]
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// ==============================================================
|
|
|
|
|
// 6. RISK MANAGEMENT (WEVAL register)
|
|
|
|
|
// ==============================================================
|
|
|
|
|
$risks = [
|
|
|
|
|
['id'=>'RW01', 'title'=>'Pipeline commercial vide', 'category'=>'Commercial', 'likelihood'=>5, 'impact'=>5, 'priority'=>'critical', 'owner'=>'Yacine', 'mitigation'=>'MQL Scoring + Pipeline Agent + Discovery 5 clients Pharma/Banque'],
|
|
|
|
|
['id'=>'RW02', 'title'=>'Dépendance Ethica (1 seul client actif)', 'category'=>'Commercial', 'likelihood'=>4, 'impact'=>5, 'priority'=>'critical', 'owner'=>'Yacine', 'mitigation'=>'Diversifier 3 nouveaux clients Q2'],
|
|
|
|
|
['id'=>'RW03', 'title'=>'Dérive technique (trop de projets parallèles)','category'=>'Tech', 'likelihood'=>4, 'impact'=>4, 'priority'=>'high', 'owner'=>'Yacine', 'mitigation'=>'Plan-action 882L + NonReg 153/153 + WEVIA Master autonome'],
|
|
|
|
|
['id'=>'RW04', 'title'=>'Pas de revenue récurrent SaaS encore', 'category'=>'Commercial', 'likelihood'=>5, 'impact'=>4, 'priority'=>'critical', 'owner'=>'Yacine', 'mitigation'=>'POC V67 simulator aux prospects pour accélérer close'],
|
|
|
|
|
['id'=>'RW05', 'title'=>'GDPR non-conformité données HCP', 'category'=>'Compliance', 'likelihood'=>2, 'impact'=>5, 'priority'=>'high', 'owner'=>'Yacine', 'mitigation'=>'consent.wevup.app + audit trimestriel'],
|
|
|
|
|
['id'=>'RW06', 'title'=>'Infra S204/S95 single point of failure', 'category'=>'Infra', 'likelihood'=>3, 'impact'=>4, 'priority'=>'high', 'owner'=>'Yacine', 'mitigation'=>'Backups GOLD quotidiens + monitoring Docker'],
|
|
|
|
|
['id'=>'RW07', 'title'=>'Sovereign cascade dépendance providers', 'category'=>'Tech', 'likelihood'=>2, 'impact'=>3, 'priority'=>'medium', 'owner'=>'Yacine', 'mitigation'=>'13 providers + Ollama local fallback'],
|
|
|
|
|
['id'=>'RW08', 'title'=>'Cash burn personal salary delay', 'category'=>'Finance', 'likelihood'=>3, 'impact'=>4, 'priority'=>'high', 'owner'=>'Yacine', 'mitigation'=>'Accélérer close Ethica renewal + POC Marjane'],
|
|
|
|
|
['id'=>'RW09', 'title'=>'Pas de co-fondateur tech delegate', 'category'=>'Organisation', 'likelihood'=>4, 'impact'=>3, 'priority'=>'high', 'owner'=>'Yacine', 'mitigation'=>'WEVIA Master autonome comble partiellement'],
|
|
|
|
|
['id'=>'RW10', 'title'=>'Concurrence IA générale accélère', 'category'=>'Stratégie', 'likelihood'=>4, 'impact'=>3, 'priority'=>'high', 'owner'=>'Yacine', 'mitigation'=>'Souveraineté + niche ERP gap-fill = moat défensive'],
|
|
|
|
|
['id'=>'RW11', 'title'=>'Partnership Vistex/Huawei inactifs', 'category'=>'Partenariat', 'likelihood'=>3, 'impact'=>3, 'priority'=>'medium', 'owner'=>'Yacine', 'mitigation'=>'Joint pitch OCP + Vistex events Q2'],
|
|
|
|
|
['id'=>'RW12', 'title'=>'Burnout (1 fondateur, 38 crons, 19 Docker)','category'=>'Humain', 'likelihood'=>4, 'impact'=>5, 'priority'=>'critical', 'owner'=>'Yacine', 'mitigation'=>'Automation maximale + délégation WEVIA']
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// ==============================================================
|
|
|
|
|
// 7. ALERTS DG (à traiter maintenant — top priority)
|
|
|
|
|
// ==============================================================
|
|
|
|
|
$alerts_dg = [];
|
|
|
|
|
// Alert : pipeline vide
|
|
|
|
|
if ($crm['opportunities_active'] < 5) {
|
|
|
|
|
$alerts_dg[] = ['level'=>'critical', 'icon'=>'🔴', 'title'=>'Pipeline commercial anémié',
|
|
|
|
|
'detail'=>sprintf('%d opportunités actives (objectif >10). Action: lancer outreach 5 prospects Pharma/Banque cette semaine.', $crm['opportunities_active']),
|
|
|
|
|
'action_link'=>'/crm.html', 'owner'=>'Yacine', 'deadline'=>'cette semaine'];
|
|
|
|
|
}
|
|
|
|
|
// Alert : conversions = 0
|
|
|
|
|
if ($marketing['conversions_month'] === 0) {
|
|
|
|
|
$alerts_dg[] = ['level'=>'critical', 'icon'=>'🔴', 'title'=>'0 conversions réelles ce mois',
|
|
|
|
|
'detail'=>'Pipeline Send→Open→Click→Convert à vérifier. POC simulator V67 à présenter 3 prospects.',
|
|
|
|
|
'action_link'=>'/agent-roi-simulator.html', 'owner'=>'Yacine', 'deadline'=>'J+3'];
|
|
|
|
|
}
|
|
|
|
|
// Alert : cash
|
|
|
|
|
$alerts_dg[] = ['level'=>'critical', 'icon'=>'💰', 'title'=>'Cash collection ce mois',
|
|
|
|
|
'detail'=>'Facturation Ethica Q1 à relancer + POC pricing à proposer Marjane/OCP.',
|
|
|
|
|
'action_link'=>'/erp-gap-fill-offer.html', 'owner'=>'Yacine', 'deadline'=>'J+5'];
|
|
|
|
|
// Alert : partnerships dormants
|
|
|
|
|
$alerts_dg[] = ['level'=>'high', 'icon'=>'🤝', 'title'=>'Partnerships Vistex/Huawei à activer',
|
|
|
|
|
'detail'=>'Aucun deal joint depuis signature. Relancer Olga (Vistex) + Ray (Huawei) pour joint pitch OCP.',
|
|
|
|
|
'action_link'=>'#', 'owner'=>'Yacine', 'deadline'=>'Semaine'];
|
|
|
|
|
// Alert : docker containers
|
|
|
|
|
$docker_count = (int)($sot['docker_running'] ?? 19);
|
|
|
|
|
if ($docker_count < 18) {
|
|
|
|
|
$alerts_dg[] = ['level'=>'high', 'icon'=>'🐳', 'title'=>sprintf('Docker containers: %d (attendu 19)', $docker_count),
|
|
|
|
|
'detail'=>'Verifier containers down sur S204 + S95.',
|
|
|
|
|
'action_link'=>'/tasks-live.html', 'owner'=>'Yacine', 'deadline'=>'J+1'];
|
|
|
|
|
}
|
|
|
|
|
// Alert : Bottleneck TOC
|
|
|
|
|
$bottleneck_label = '';
|
|
|
|
|
foreach ($toc_streams as $s) {
|
|
|
|
|
if ($s['id'] === $bottleneck_id) { $bottleneck_label = $s['label']; break; }
|
|
|
|
|
}
|
|
|
|
|
$alerts_dg[] = ['level'=>'high', 'icon'=>'⚠️', 'title'=>sprintf('TOC Bottleneck: %s', $bottleneck_label),
|
|
|
|
|
'detail'=>'Subordonner toutes les autres activités à désengorger ce goulet. Méthode Goldratt: identifier, exploiter, subordonner, élever, répéter.',
|
|
|
|
|
'action_link'=>'#toc', 'owner'=>'Yacine', 'deadline'=>'permanent'];
|
|
|
|
|
// Alert : wiki + anti-régression
|
|
|
|
|
$alerts_dg[] = ['level'=>'medium', 'icon'=>'📖', 'title'=>'Plan-action 882 lignes — lire AVANT chaque intervention',
|
|
|
|
|
'detail'=>'Anti-conflit autres Claudes. Doctrine: lire wiki+vault+plan AVANT + update APRÈS.',
|
|
|
|
|
'action_link'=>'/wiki/plan-action-2026-04-17.md', 'owner'=>'Yacine', 'deadline'=>'permanent'];
|
|
|
|
|
// Alert : V67 simulator pas encore présenté clients
|
|
|
|
|
$alerts_dg[] = ['level'=>'medium', 'icon'=>'🎯', 'title'=>'ROI Simulator V67 prêt — pas encore utilisé',
|
|
|
|
|
'detail'=>'Outil commercial premium 17.36M€ savings max scalable. Utiliser en discovery meeting cette semaine.',
|
|
|
|
|
'action_link'=>'/agent-roi-simulator.html', 'owner'=>'Yacine', 'deadline'=>'J+7'];
|
|
|
|
|
|
|
|
|
|
// Sort alerts by level
|
|
|
|
|
// === V37_ALERT_ACK_FILTER 19avr · doctrine 13+14 · reading /tmp/dg-alerts-ack.json ===
|
|
|
|
|
$ack_file = '/tmp/dg-alerts-ack.json';
|
|
|
|
|
$acks = [];
|
|
|
|
|
if (file_exists($ack_file)) {
|
|
|
|
|
$acks_raw = @json_decode(@file_get_contents($ack_file), true);
|
|
|
|
|
if (is_array($acks_raw)) $acks = $acks_raw;
|
|
|
|
|
}
|
|
|
|
|
$alerts_dg = array_values(array_filter($alerts_dg, function($a) use ($acks) {
|
|
|
|
|
$key = md5($a['title'] ?? '');
|
|
|
|
|
if (isset($acks[$key])) {
|
|
|
|
|
$ack_ts = strtotime($acks[$key]['ts'] ?? '0');
|
|
|
|
|
$expires = (int)($acks[$key]['expires_h'] ?? 24) * 3600;
|
|
|
|
|
if (time() - $ack_ts < $expires) return false; // filtered out (acknowledged)
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}));
|
|
|
|
|
// === END V37_ALERT_ACK_FILTER ===
|
|
|
|
|
|
|
|
|
|
usort($alerts_dg, function($a, $b) {
|
|
|
|
|
$order = ['critical'=>0, 'high'=>1, 'medium'=>2, 'low'=>3];
|
|
|
|
|
return $order[$a['level']] <=> $order[$b['level']];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ==============================================================
|
|
|
|
|
// SUMMARY DG
|
|
|
|
|
// ==============================================================
|
|
|
|
|
$summary = [
|
|
|
|
|
'timestamp' => $ts_now,
|
|
|
|
|
'toc_bottleneck_id' => $bottleneck_id,
|
|
|
|
|
'toc_bottleneck_label' => $bottleneck_label,
|
|
|
|
|
'conversion_overall_pct' => $conv_overall,
|
|
|
|
|
'funnel_leaks_count' => count(array_filter($funnel, fn($f)=>($f['conv_pct'] ?? 100) < 30)),
|
|
|
|
|
'pipeline_value_keur' => $crm['pipeline_value_keur'],
|
|
|
|
|
'active_clients' => (int)($sot['active_clients'] ?? 2),
|
|
|
|
|
'risks_critical' => count(array_filter($risks, fn($r)=>$r['priority']==='critical')),
|
|
|
|
|
'risks_high' => count(array_filter($risks, fn($r)=>$r['priority']==='high')),
|
|
|
|
|
'alerts_dg_count' => count($alerts_dg),
|
|
|
|
|
'alerts_critical' => count(array_filter($alerts_dg, fn($a)=>$a['level']==='critical')),
|
|
|
|
|
'data_pipeline_health_pct' => 100
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// Response
|
|
|
|
|
echo json_encode([
|
|
|
|
|
'generated_at' => $ts_now,
|
|
|
|
|
'version' => 'V69',
|
|
|
|
|
'role' => 'DG Command Center — Real-time',
|
|
|
|
|
'summary' => $summary,
|
|
|
|
|
'toc' => [
|
|
|
|
|
'bottleneck' => $bottleneck_id,
|
|
|
|
|
'streams' => $toc_streams,
|
|
|
|
|
'method' => 'Goldratt 5FS: 1.Identify 2.Exploit 3.Subordinate 4.Elevate 5.Repeat'
|
|
|
|
|
],
|
|
|
|
|
'conversion_funnel' => $funnel,
|
|
|
|
|
'data_pipeline' => $data_pipeline,
|
|
|
|
|
'marketing' => $marketing,
|
|
|
|
|
'crm' => $crm,
|
|
|
|
|
'risks' => $risks,
|
|
|
|
|
'alerts_dg' => $alerts_dg,
|
|
|
|
|
'sources_used' => [
|
|
|
|
|
'v63_live' => !empty($v63),
|
|
|
|
|
'v64_live' => !empty($v64),
|
|
|
|
|
'v66_live' => !empty($v66),
|
|
|
|
|
'source_of_truth' => !empty($sot),
|
|
|
|
|
'em_kpi_cache' => !empty($em_kpi),
|
|
|
|
|
'crm_observation' => !empty($crm_obs)
|
|
|
|
|
]
|
|
|
|
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|