137 lines
6.4 KiB
PHP
137 lines
6.4 KiB
PHP
<?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;
|