282 lines
8.9 KiB
PHP
282 lines
8.9 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Dashboard API - agregovaná data pro dashboard
|
|
*
|
|
* GET /api/admin/dashboard.php
|
|
* Vrací sekce dle oprávnění přihlášeného uživatele.
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once dirname(__DIR__) . '/config.php';
|
|
require_once dirname(__DIR__) . '/includes/JWTAuth.php';
|
|
require_once dirname(__DIR__) . '/includes/AuditLog.php';
|
|
require_once dirname(__DIR__) . '/includes/CnbRates.php';
|
|
|
|
setCorsHeaders();
|
|
setSecurityHeaders();
|
|
setNoCacheHeaders();
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
$authData = JWTAuth::requireAuth();
|
|
AuditLog::setUser($authData['user_id'], $authData['user']['username'] ?? 'unknown');
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
|
http_response_code(204);
|
|
exit;
|
|
}
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
|
errorResponse('Method not allowed', 405);
|
|
}
|
|
|
|
$pdo = db();
|
|
$result = [];
|
|
|
|
// --- Stav smeny aktualniho uzivatele (attendance.record) ---
|
|
$userId = $authData['user_id'];
|
|
if (hasPermission($authData, 'attendance.record')) {
|
|
$stmt = $pdo->prepare("
|
|
SELECT id FROM attendance
|
|
WHERE user_id = ? AND departure_time IS NULL AND (leave_type IS NULL OR leave_type = 'work')
|
|
ORDER BY created_at DESC LIMIT 1
|
|
");
|
|
$stmt->execute([$userId]);
|
|
$result['my_shift'] = [
|
|
'has_ongoing' => (bool) $stmt->fetch(),
|
|
];
|
|
}
|
|
|
|
// --- Docházka dnes (attendance.admin) ---
|
|
if (hasPermission($authData, 'attendance.admin')) {
|
|
// Poslední pracovní záznam per uživatel (vyloučit ty co mají leave dnes)
|
|
$stmt = $pdo->query("
|
|
SELECT u.id, CONCAT(u.first_name, ' ', u.last_name) as name,
|
|
CONCAT(LEFT(u.first_name, 1), LEFT(u.last_name, 1)) as initials,
|
|
a.arrival_time, a.departure_time, a.break_start, a.break_end
|
|
FROM users u
|
|
LEFT JOIN (
|
|
SELECT a1.*
|
|
FROM attendance a1
|
|
INNER JOIN (
|
|
SELECT user_id, MAX(id) as max_id
|
|
FROM attendance
|
|
WHERE shift_date = CURDATE()
|
|
AND (leave_type IS NULL OR leave_type = 'work')
|
|
GROUP BY user_id
|
|
) a2 ON a1.id = a2.max_id
|
|
) a ON u.id = a.user_id
|
|
WHERE u.is_active = 1
|
|
AND u.id NOT IN (
|
|
SELECT user_id FROM attendance
|
|
WHERE shift_date = CURDATE() AND leave_type IN ('vacation', 'sick', 'holiday', 'unpaid')
|
|
)
|
|
ORDER BY a.arrival_time IS NULL, a.arrival_time ASC
|
|
");
|
|
$users = $stmt->fetchAll();
|
|
|
|
$present = 0;
|
|
$away = 0;
|
|
$attendanceUsers = [];
|
|
|
|
foreach ($users as $u) {
|
|
$status = 'out';
|
|
$arrivedAt = null;
|
|
|
|
if ($u['arrival_time'] !== null) {
|
|
if ($u['departure_time'] !== null) {
|
|
$status = 'out';
|
|
} elseif ($u['break_start'] !== null && $u['break_end'] === null) {
|
|
$status = 'away';
|
|
$away++;
|
|
} else {
|
|
$status = 'in';
|
|
$present++;
|
|
}
|
|
$arrivedAt = date('H:i', strtotime($u['arrival_time']));
|
|
}
|
|
|
|
$attendanceUsers[] = [
|
|
'name' => $u['name'],
|
|
'initials' => $u['initials'],
|
|
'status' => $status,
|
|
'arrived_at' => $arrivedAt,
|
|
];
|
|
}
|
|
|
|
// Dnes na dovolene/nemocenske
|
|
$stmtLeave = $pdo->query("
|
|
SELECT CONCAT(u.first_name, ' ', u.last_name) as name,
|
|
CONCAT(LEFT(u.first_name, 1), LEFT(u.last_name, 1)) as initials,
|
|
a.leave_type
|
|
FROM attendance a
|
|
JOIN users u ON a.user_id = u.id
|
|
WHERE a.shift_date = CURDATE() AND a.leave_type IN ('vacation', 'sick', 'holiday', 'unpaid')
|
|
");
|
|
$onLeave = $stmtLeave->fetchAll();
|
|
|
|
foreach ($onLeave as $leave) {
|
|
$attendanceUsers[] = [
|
|
'name' => $leave['name'],
|
|
'initials' => $leave['initials'],
|
|
'status' => 'leave',
|
|
'arrived_at' => null,
|
|
'leave_type' => $leave['leave_type'],
|
|
];
|
|
}
|
|
|
|
$result['attendance'] = [
|
|
'present_today' => $present,
|
|
'away_today' => $away,
|
|
'total_active' => count($users),
|
|
'on_leave' => count($onLeave),
|
|
'users' => $attendanceUsers,
|
|
];
|
|
}
|
|
|
|
// --- Nabídky (offers.view) ---
|
|
if (hasPermission($authData, 'offers.view')) {
|
|
$stmt = $pdo->query("
|
|
SELECT
|
|
COUNT(*) as total,
|
|
SUM(CASE WHEN q.order_id IS NULL
|
|
AND (q.valid_until IS NULL OR q.valid_until >= CURDATE())
|
|
THEN 1 ELSE 0 END) as open_count,
|
|
SUM(CASE WHEN q.order_id IS NULL
|
|
AND q.valid_until < CURDATE()
|
|
THEN 1 ELSE 0 END) as expired_count,
|
|
SUM(CASE WHEN q.order_id IS NOT NULL
|
|
THEN 1 ELSE 0 END) as converted_count
|
|
FROM quotations q
|
|
");
|
|
$counts = $stmt->fetch();
|
|
|
|
$stmtMonth = $pdo->query("
|
|
SELECT COUNT(*) as count FROM quotations
|
|
WHERE YEAR(created_at) = YEAR(CURDATE()) AND MONTH(created_at) = MONTH(CURDATE())
|
|
");
|
|
$monthData = $stmtMonth->fetch();
|
|
|
|
$result['offers'] = [
|
|
'total' => (int) $counts['total'],
|
|
'open_count' => (int) $counts['open_count'],
|
|
'expired_count' => (int) $counts['expired_count'],
|
|
'converted_count' => (int) $counts['converted_count'],
|
|
'created_this_month' => (int) $monthData['count'],
|
|
];
|
|
}
|
|
|
|
// --- Projekty (projects.view) ---
|
|
if (hasPermission($authData, 'projects.view')) {
|
|
$stmt = $pdo->query("
|
|
SELECT p.id, p.name, p.status, c.name as customer_name
|
|
FROM projects p
|
|
LEFT JOIN customers c ON p.customer_id = c.id
|
|
WHERE p.status = 'aktivni'
|
|
ORDER BY p.modified_at DESC
|
|
LIMIT 5
|
|
");
|
|
$activeProjects = $stmt->fetchAll();
|
|
|
|
$stmtCounts = $pdo->query("
|
|
SELECT
|
|
SUM(CASE WHEN status = 'aktivni' THEN 1 ELSE 0 END) as active_count,
|
|
SUM(CASE WHEN status = 'dokonceny' THEN 1 ELSE 0 END) as completed_count
|
|
FROM projects WHERE status != 'deleted'
|
|
");
|
|
$projectCounts = $stmtCounts->fetch();
|
|
|
|
$result['projects'] = [
|
|
'active_count' => (int) ($projectCounts['active_count'] ?? 0),
|
|
'completed_count' => (int) ($projectCounts['completed_count'] ?? 0),
|
|
'active_projects' => $activeProjects,
|
|
];
|
|
}
|
|
|
|
// --- Faktury (invoices.view) ---
|
|
if (hasPermission($authData, 'invoices.view')) {
|
|
$stmt = $pdo->query("
|
|
SELECT
|
|
COUNT(*) as total,
|
|
SUM(CASE WHEN i.status = 'paid'
|
|
AND YEAR(i.paid_date) = YEAR(CURDATE())
|
|
AND MONTH(i.paid_date) = MONTH(CURDATE())
|
|
THEN 1 ELSE 0 END) as paid_this_month,
|
|
SUM(CASE WHEN i.status IN ('issued', 'overdue')
|
|
THEN 1 ELSE 0 END) as unpaid_count
|
|
FROM invoices i
|
|
");
|
|
$invCounts = $stmt->fetch();
|
|
|
|
// Tržby tento měsíc per faktura (pro kurz k datu vystaveni)
|
|
$stmtRevenue = $pdo->query("
|
|
SELECT i.id, i.currency, i.issue_date,
|
|
COALESCE(SUM(ii.quantity * ii.unit_price), 0) as revenue
|
|
FROM invoices i
|
|
JOIN invoice_items ii ON i.id = ii.invoice_id
|
|
WHERE i.status = 'paid'
|
|
AND YEAR(i.paid_date) = YEAR(CURDATE())
|
|
AND MONTH(i.paid_date) = MONTH(CURDATE())
|
|
GROUP BY i.id, i.currency, i.issue_date
|
|
ORDER BY revenue DESC
|
|
");
|
|
|
|
$revByCurrency = [];
|
|
$revCzkItems = [];
|
|
foreach ($stmtRevenue->fetchAll() as $row) {
|
|
$cur = $row['currency'];
|
|
$amt = (float) $row['revenue'];
|
|
$revByCurrency[$cur] = ($revByCurrency[$cur] ?? 0) + $amt;
|
|
$revCzkItems[] = [
|
|
'amount' => $amt,
|
|
'currency' => $cur,
|
|
'date' => $row['issue_date'],
|
|
];
|
|
}
|
|
|
|
$revenueByCurrency = [];
|
|
foreach ($revByCurrency as $cur => $total) {
|
|
$revenueByCurrency[] = [
|
|
'currency' => $cur,
|
|
'amount' => round($total, 2),
|
|
];
|
|
}
|
|
|
|
$cnb = CnbRates::getInstance();
|
|
|
|
$result['invoices'] = [
|
|
'total' => (int) $invCounts['total'],
|
|
'paid_this_month' => (int) $invCounts['paid_this_month'],
|
|
'unpaid_count' => (int) $invCounts['unpaid_count'],
|
|
'revenue_this_month' => $revenueByCurrency,
|
|
'revenue_czk' => $cnb->sumToCzk($revCzkItems),
|
|
];
|
|
}
|
|
|
|
// --- Čekající žádosti (attendance.approve) ---
|
|
if (hasPermission($authData, 'attendance.approve')) {
|
|
$stmt = $pdo->query("
|
|
SELECT COUNT(*) as count FROM leave_requests WHERE status = 'pending'
|
|
");
|
|
$pending = $stmt->fetch();
|
|
|
|
$result['leave_pending'] = [
|
|
'count' => (int) $pending['count'],
|
|
];
|
|
}
|
|
|
|
// --- Poslední aktivita (settings.roles = admin přehled) ---
|
|
if (hasPermission($authData, 'settings.roles')) {
|
|
$stmt = $pdo->query("
|
|
SELECT username, action, entity_type, description, created_at
|
|
FROM audit_logs
|
|
WHERE action IN ('create', 'update', 'delete', 'login')
|
|
ORDER BY created_at DESC
|
|
LIMIT 8
|
|
");
|
|
$result['recent_activity'] = $stmt->fetchAll();
|
|
}
|
|
|
|
jsonResponse($result);
|