Files
2026-05-17 05:22:06 +03:00

137 lines
6.4 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
require_once __DIR__ . '/../../inc/auth.php';
require_once __DIR__ . '/../../inc/functions.php';
$session = auth_require('owner');
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !auth_csrf_verify($_POST['csrf_token'] ?? '')) {
header('Location: ceo.php');
exit;
}
$pdo = getDB();
$userId = $session['user_id'];
// ---- 1. SLA Stability (с учётом severity нарушений) ----
$stmt = $pdo->query("SELECT COUNT(*) as total, SUM(CASE WHEN sla_compliant=TRUE THEN 1 ELSE 0 END) as compliant FROM tasks WHERE closed_at IS NOT NULL");
$slaData = $stmt->fetch();
$slaCompliance = $slaData['total'] > 0 ? ($slaData['compliant'] / $slaData['total']) * 100 : 100;
// Breach severity from incidents this period
$stmt = $pdo->query("SELECT severity, COUNT(*) as cnt FROM incidents WHERE created_at > NOW() - INTERVAL '30 days' GROUP BY severity");
$breaches = ['P1' => 0, 'P2' => 0, 'P3' => 0];
while ($b = $stmt->fetch()) {
$breaches[$b['severity']] = (int)$b['cnt'];
}
$breachSeverity = calc_breach_severity($breaches['P1'], $breaches['P2'], $breaches['P3']);
$slaStability = calc_sla_stability_index($slaCompliance, $breachSeverity);
// ---- 2. Revenue Stability (через коэффициент вариации MRR за 6 месяцев) ----
$mrrHistory = [];
$stmt = $pdo->query("SELECT TO_CHAR(created_at, 'YYYY-MM') as ym, SUM(sla_price_monthly) as mrr FROM sla_contracts WHERE status='active' GROUP BY ym ORDER BY ym DESC LIMIT 6");
while ($m = $stmt->fetch()) {
$mrrHistory[] = (float)$m->mrr;
}
$mrrHistory = array_reverse($mrrHistory);
$currentMrr = !empty($mrrHistory) ? end($mrrHistory) : 0;
$revenueStability = calc_revenue_stability_index($mrrHistory);
// ---- 3. Retention ----
$stmt = $pdo->query("SELECT COUNT(*) FROM sla_contracts WHERE status='active'");
$activeSla = (int)$stmt->fetchColumn();
$stmt = $pdo->query("SELECT COUNT(DISTINCT object_id) FROM sla_contracts WHERE status IN ('active','expired','cancelled')");
$totalEver = (int)$stmt->fetchColumn();
$retention = $totalEver > 0 ? round(($activeSla / $totalEver) * 100, 2) : 100;
// ---- 4. Engineer Performance ----
$stmt = $pdo->query("SELECT AVG(engineer_score) FROM engineer_kpi WHERE period_end > NOW() - INTERVAL '30 days'");
$avgEs = (float)$stmt->fetchColumn();
$engineerPerformance = min(100, $avgEs ?: 70);
// ---- 5. Incident Stability (с весами severity) ----
$stmt = $pdo->query("SELECT severity, COUNT(*) as cnt FROM incidents WHERE created_at > NOW() - INTERVAL '30 days' GROUP BY severity");
$incSev = ['P1' => 0, 'P2' => 0, 'P3' => 0];
while ($i = $stmt->fetch()) {
$incSev[$i['severity']] = (int)$i['cnt'];
}
$stmt = $pdo->query("SELECT COUNT(*) FROM objects WHERE status='active'");
$objCount = max(1, (int)$stmt->fetchColumn());
$incidentStability = calc_incident_stability_index($incSev['P1'], $incSev['P2'], $incSev['P3'], $objCount);
// ---- 6. Sales Flow (аудиты → SLA конверсия) ----
$stmt = $pdo->query("SELECT COUNT(*) FROM questionnaire_sessions WHERE status='completed' AND created_at > NOW() - INTERVAL '90 days'");
$audits = (int)$stmt->fetchColumn();
$stmt = $pdo->query("SELECT COUNT(*) FROM sla_contracts WHERE created_at > NOW() - INTERVAL '90 days'");
$newSla = (int)$stmt->fetchColumn();
$salesFlow = calc_sales_flow_index($audits, $newSla);
// ---- 7. Operational Efficiency (выручка / (часы × стоимость)) ----
$stmt = $pdo->query("SELECT COUNT(*) FROM tasks WHERE closed_at IS NOT NULL AND closed_at > NOW() - INTERVAL '30 days'");
$closedTasks = (int)$stmt->fetchColumn();
$engineerHours = $closedTasks * 2; // approx 2h per task
$costPerHour = 1500; // руб/час
$operationalEfficiency = calc_operational_efficiency_index($currentMrr, $engineerHours, $costPerHour);
// ---- CEO SHS через calc_ceo_shs() ----
$ceoShsScore = calc_ceo_shs(
$revenueStability, /* MRR Growth proxy */
$slaStability, /* SLA Compliance */
$retention,
$engineerPerformance, /* Productivity */
$salesFlow, /* Conversion */
$incidentStability
);
// ---- SHS (7-факторный) через calc_shs() ----
$shsScore = calc_shs($slaStability, $revenueStability, $retention, $engineerPerformance, $incidentStability, $salesFlow, $operationalEfficiency);
$shsStatus = shs_status($shsScore);
// ---- ΔSHS (дельта от предыдущей записи) ----
$prevRecord = $pdo->query("SELECT shs_score FROM shs_records ORDER BY id DESC LIMIT 1 OFFSET 1")->fetch();
$shsDelta = shs_delta($shsScore, $prevRecord ? (float)$prevRecord['shs_score'] : null);
// ---- Automation Rules ----
$autoAlerts = check_automation_rules($shsScore, $slaStability, $retention);
// ---- Save SHS record ----
$periodStart = date('Y-m-01');
$periodEnd = date('Y-m-t');
$stmt = $pdo->prepare("INSERT INTO shs_records (recorded_by, period_start, period_end, sla_stability, revenue_stability, retention, engineer_performance, incident_stability, sales_flow, operational_efficiency, shs_score, shs_status, components_json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)");
$stmt->execute([
$userId, $periodStart, $periodEnd,
$slaStability, $revenueStability, $retention, $engineerPerformance,
$incidentStability, $salesFlow, $operationalEfficiency,
$shsScore, $shsStatus,
json_encode([
'sla_compliance_raw' => $slaCompliance,
'breach_severity' => $breachSeverity,
'sla_stability' => $slaStability,
'revenue_stability' => $revenueStability,
'mrr_history' => $mrrHistory,
'current_mrr' => $currentMrr,
'retention' => $retention,
'active_sla' => $activeSla,
'total_ever_sla' => $totalEver,
'engineer_perf' => $engineerPerformance,
'incident_p1' => $incSev['P1'],
'incident_p2' => $incSev['P2'],
'incident_p3' => $incSev['P3'],
'object_count' => $objCount,
'incident_stability' => $incidentStability,
'audits_90d' => $audits,
'new_sla_90d' => $newSla,
'sales_flow' => $salesFlow,
'closed_tasks_30d' => $closedTasks,
'engineer_hours' => $engineerHours,
'operational_eff' => $operationalEfficiency,
'shs_delta' => $shsDelta,
'ceo_shs_score' => $ceoShsScore,
'auto_alerts' => $autoAlerts,
])
]);
auth_log($userId, 'shs_calc', "SHS: $shsScore ($shsStatus), CEO SHS: $ceoShsScore, Δ: " . ($shsDelta['delta'] ?? 'N/A'));
header('Location: ceo.php?calc_ok=1');
exit;