<?php
// modules/puantaj-cetveli.php
session_start();
require_once '../config/db.php';
require_once '../config/functions.php';
// Yetkiler: Root, Yönetici, Muhasebe, İK
yetkiKontrol(['root', 'yonetici', 'muhasebe', 'insan_kaynaklari']);
include '../includes/header.php';
include '../includes/menu.php';
// --- FİLTRELER ---
// GET parametrelerinden değerleri al ve XSS koruması uygula
$yil = guvenlik($_GET['yil'] ?? date('Y'));
$ay = guvenlik($_GET['ay'] ?? date('m'));
$rol_filtre = guvenlik($_GET['rol'] ?? 'tumu');
$personel_id = guvenlik($_GET['personel_id'] ?? 'tumu');
// Ayın Gün Sayısı (28, 29, 30, 31)
$gun_sayisi = cal_days_in_month(CAL_GREGORIAN, $ay, $yil);
// --- PERSONEL LİSTESİ ---
$sql_per = "SELECT * FROM kullanicilar WHERE durum=1";
if ($rol_filtre != 'tumu') $sql_per .= " AND rol = '$rol_filtre'";
// GÜVENLİK GÜNCELLEMESİ: personel_id int'e cast ediliyor
if ($personel_id != 'tumu') $sql_per .= " AND id = " . (int)$personel_id;
$sql_per .= " ORDER BY ad ASC";
$personeller = $pdo->query($sql_per)->fetchAll();
// --- SİSTEM VERİLERİ ---
// 1. Resmi Tatiller
$tatiller = $pdo->query("SELECT tarih FROM resmi_tatiller")->fetchAll(PDO::FETCH_COLUMN);
// 2. Standart Günlük Mesai (Ayarlardan)
$gunluk_saat_std = (float)($pdo->query("SELECT ayar_degeri FROM site_ayarlari WHERE ayar_anahtari = 'gunluk_mesai_saati'")->fetchColumn() ?: 9);
// 3. Tüm personeller (Filtre kutusu için)
$tum_personeller_query = $pdo->query("SELECT id, ad, soyad FROM kullanicilar WHERE durum=1 ORDER BY ad ASC")->fetchAll();
// --- YARDIMCI FONKSİYONLAR ---
function gunIsmi($tarih) {
$gunler = ['Mon'=>'Pzt', 'Tue'=>'Sal', 'Wed'=>'Çar', 'Thu'=>'Per', 'Fri'=>'Cum', 'Sat'=>'Cts', 'Sun'=>'Pzr'];
return $gunler[date('D', strtotime($tarih))];
}
// İzin Renkleri ve Etiketleri
function izinStili($tur) {
$stiller = [
'yillik' => ['bg'=>'bg-info text-white', 't'=>'Y.İZ'],
'mazeret' => ['bg'=>'bg-warning text-dark', 't'=>'MAZ'],
'hastalik' => ['bg'=>'bg-danger text-white', 't'=>'RAPOR'],
'evlilik' => ['bg'=>'bg-primary text-white', 't'=>'EVL'],
'babalik' => ['bg'=>'bg-primary text-white', 't'=>'BAB'],
'olum' => ['bg'=>'bg-dark text-white', 't'=>'ÖLM'],
'saatlik' => ['bg'=>'bg-warning bg-opacity-25 text-dark', 't'=>'S.İZ'],
'diger' => ['bg'=>'bg-danger bg-opacity-50 text-white', 't'=>'ÜCR.İZ']
];
return $stiller[$tur] ?? ['bg'=>'bg-secondary text-white', 't'=>strtoupper($tur)];
}
// --- PHP VERİ TOPLAMA VE JSON HAZIRLAMA (Aynı) ---
$puantaj_datasi = [];
foreach($personeller as $per) {
$personel_id = $per['id'];
// GÜVENLİK GÜNCELLEMESİ: Dinamik sorgular hazırlanmış ifadeye çevrilmeli
for($d=1; $d<=$gun_sayisi; $d++) {
$g_tarih = "$yil-$ay-" . sprintf('%02d', $d);
$haftanin_gunu = date('N', strtotime($g_tarih));
$is_tatil = in_array($g_tarih, $tatiller);
$gunluk_normal = ($haftanin_gunu >= 6 || $is_tatil) ? 0 : $gunluk_saat_std;
// DB Verilerini Çek
$stmt_izin = $pdo->prepare("SELECT * FROM izin_talepleri WHERE calisan_id=? AND durum='onaylandi' AND ? BETWEEN DATE(baslangic_tarihi) AND DATE(bitis_tarihi)");
$stmt_izin->execute([$personel_id, $g_tarih]);
$izin = $stmt_izin->fetch();
$stmt_mesai = $pdo->prepare("SELECT SUM(toplam_saat) FROM mesai_hareketleri WHERE calisan_id=? AND durum='onaylandi' AND tarih=? AND mesai_turu IN ('fazla_mesai', 'hafta_tatili', 'resmi_tatil_mesaisi')");
$stmt_mesai->execute([$personel_id, $g_tarih]);
$mesai_saat = $stmt_mesai->fetchColumn() ?: 0;
$kesinti_saati = 0;
$calisma_saati = $gunluk_normal;
$izinden_gelen_bilgi = null;
if ($izin) {
$izinden_gelen_bilgi = $izin;
$izinden_gelen_bilgi['stil'] = izinStili($izin['izin_turu']);
if (in_array($izin['izin_turu'], ['saatlik', 'diger'])) {
if ($izin['izin_turu'] == 'diger') {
$kesinti_saati = $gunluk_saat_std;
$calisma_saati = 0;
} elseif ($izin['izin_turu'] == 'saatlik') {
$bas_saat_str = date('H:i', strtotime($izin['baslangic_tarihi']));
$bit_saat_str = date('H:i', strtotime($izin['bitis_tarihi']));
if ($haftanin_gunu <= 5 && $bas_saat_str === '08:00' && $bit_saat_str === '12:00') {
$kesinti_saati = 4.0; $calisma_saati = 5.0;
} elseif ($haftanin_gunu <= 5 && $bas_saat_str === '13:00' && $bit_saat_str === '18:00') {
$kesinti_saati = 5.0; $calisma_saati = 4.0;
} else {
$kesinti_saati = (float)$izin['saatlik_sure'];
$calisma_saati = $gunluk_normal - $kesinti_saati;
}
}
} else {
$calisma_saati = 0;
}
}
// JSON Datasına Ekle
$puantaj_datasi[$personel_id][$g_tarih] = [
'gunluk_saat_std' => $gunluk_saat_std,
'is_tatil' => $is_tatil || $haftanin_gunu >= 6,
'izin' => $izinden_gelen_bilgi,
'mesai_saat' => (float)$mesai_saat,
'net_kesinti_saati' => (float)$kesinti_saati,
'net_calisma_saati' => (float)$calisma_saati
];
}
}
?>
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center mb-3">
<h3><i class="fas fa-table text-primary"></i> Puantaj Cetveli</h3>
</div>
<div class="card-box p-3 mb-3 border-top border-primary border-3">
<form method="GET" id="puantajForm" class="row g-2 align-items-end">
<div class="col-md-2">
<label class="fw-bold small">Yıl</label>
<select name="yil" class="form-select form-select-sm">
<?php for($y=2024; $y<=2030; $y++): ?>
<option value='<?php echo $y; ?>' <?php echo ($y == $yil) ? 'selected' : ''; ?>>
<?php echo $y; ?>
</option>
<?php endfor; ?>
</select>
</div>
<div class="col-md-2">
<label class="fw-bold small">Ay</label>
<select name="ay" class="form-select form-select-sm">
<?php
$aylar = ['01'=>'Ocak','02'=>'Şubat','03'=>'Mart','04'=>'Nisan','05'=>'Mayıs','06'=>'Haziran','07'=>'Temmuz','08'=>'Ağustos','09'=>'Eylül','10'=>'Ekim','11'=>'Kasım','12'=>'Aralık'];
foreach($aylar as $k=>$v):
?>
<option value='<?php echo $k; ?>' <?php echo ($k == $ay) ? 'selected' : ''; ?>>
<?php echo $v; ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-2">
<label class="fw-bold small">Departman</label>
<select name="rol" class="form-select form-select-sm">
<option value="tumu" <?php echo ('tumu' == $rol_filtre) ? 'selected' : ''; ?>>Tümü</option>
<option value="calisan" <?php echo ('calisan' == $rol_filtre) ? 'selected' : ''; ?>>Personel</option>
<option value="vardiya_amiri" <?php echo ('vardiya_amiri' == $rol_filtre) ? 'selected' : ''; ?>>Vardiya Amiri</option>
<option value="mudur" <?php echo ('mudur' == $rol_filtre) ? 'selected' : ''; ?>>Müdür</option>
</select>
</div>
<div class="col-md-3">
<label class="fw-bold small">Personel</label>
<select name="personel_id" class="form-select form-select-sm">
<option value="tumu" <?php echo ('tumu' == $personel_id) ? 'selected' : ''; ?>>Herkes</option>
<?php foreach($tum_personeller_query as $p): ?>
<option value='<?php echo $p['id']; ?>' <?php echo ($p['id'] == $personel_id) ? 'selected' : ''; ?>>
<?php echo $p['ad'] . ' ' . $p['soyad']; ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3 d-flex gap-1">
<button type="button" onclick="submitForm('')" class="btn btn-primary btn-sm flex-grow-1"><i class="fas fa-search"></i> Listele</button>
<div class="btn-group">
<button type="button" onclick="submitForm('excel')" class="btn btn-success btn-sm" title="Excel İndir"><i class="fas fa-file-excel"></i></button>
<button type="button" onclick="submitForm('print')" class="btn btn-dark btn-sm" title="Yazdır / PDF"><i class="fas fa-print"></i></button>
</div>
</div>
<input type="hidden" name="format" id="exportFormat" value="">
</form>
</div>
<div class="card border-0 shadow-sm overflow-auto">
<table class="table table-bordered table-sm text-center mb-0 align-middle" style="font-size: 0.7rem;">
<thead class="table-dark">
<tr>
<th rowspan="2" class="align-middle text-start ps-2" style="min-width: 150px; position: sticky; left: 0; z-index: 10;">Personel Adı</th>
<?php for($d=1; $d<=$gun_sayisi; $d++):
$gun_tarih = "$yil-$ay-" . sprintf('%02d', $d);
$gun_kisa = gunIsmi($gun_tarih);
$is_tatil = in_array($gun_tarih, $tatiller);
$renk = ($gun_kisa == 'Pzr' || $is_tatil) ? 'bg-danger text-white' : (($gun_kisa == 'Cts') ? 'bg-secondary text-white' : '');
?>
<th class="<?php echo $renk; ?>" style="min-width: 35px;">
<?php echo $d; ?><br><small><?php echo $gun_kisa; ?></small>
</th>
<?php endfor; ?>
<th class="bg-primary border-start" style="min-width: 50px;">N.M</th>
<th class="bg-success text-white" style="min-width: 50px;">F.M</th>
<th class="bg-warning text-dark" style="min-width: 50px;">İZ</th>
</tr>
<tr>
<th colspan="<?php echo $gun_sayisi; ?>" class="bg-dark text-white">GÜNLÜK VERİLER</th>
<th colspan="3" class="bg-primary border-start text-white">TOPLAMLAR</th>
</tr>
</thead>
<tbody>
<?php foreach($personeller as $per):
$toplam_normal = 0;
$toplam_fazla = 0;
$toplam_izin_gun = 0;
?>
<tr>
<td class="text-start ps-2 fw-bold bg-light" style="position: sticky; left: 0; z-index: 5;">
<?php echo $per['ad'].' '.$per['soyad']; ?>
</td>
<?php for($d=1; $d<=$gun_sayisi; $d++):
$g_tarih = "$yil-$ay-" . sprintf('%02d', $d);
$haftanin_gunu = date('N', strtotime($g_tarih));
$is_tatil = in_array($g_tarih, $tatiller);
$is_pazar = ($haftanin_gunu == 7);
$is_cumartesi = ($haftanin_gunu == 6);
// Veriyi JSON objesinden çek
$data = $puantaj_datasi[$per['id']][$g_tarih] ?? [];
// Hücre değişkenleri
$final_hucre = ""; $bg = ""; $title = "";
$gunluk_normal = $data['net_calisma_saati'] ?? (($haftanin_gunu >= 6 || $is_tatil) ? 0 : $gunluk_saat_std);
$mesai_saat = $data['mesai_saat'] ?? 0;
$net_kesinti_saati = $data['net_kesinti_saati'] ?? 0;
$izin = $data['izin'] ?? null;
// B. İzin Varsa Düşür/İşaretle
if ($izin) {
$stil = $izin['stil'];
$bg = $stil['bg'];
if (in_array($izin['izin_turu'], ['saatlik', 'diger'])) {
// SAATLİK veya ÜCRETSİZ İZİN (İstenen gösterim: -X)
$hucre_izin = "<span class='text-danger fw-bold'>- " . (float)$net_kesinti_saati . "</span>";
$title = "Kesinti Saati: {$net_kesinti_saati}s. Çalışma: {$gunluk_normal}s.";
$bg = 'bg-danger bg-opacity-10'; // Kesinti uyarısı
$final_hucre = $hucre_izin; // Başlangıçta sadece kesintiyi koy
} else {
// Tam Gün Ücretli İzin (Yıllık, Mazeret vb.)
$hucre_izin = "<span class='fw-bold' style='font-size:0.65rem;'>{$stil['t']}</span>";
$title = strtoupper($izin['izin_turu']) . " İZNİ. Kesinti: 0s.";
$toplam_izin_gun++;
$final_hucre = $hucre_izin; // Başlangıçta sadece izin etiketini koy
}
}
// C. Fazla Mesai Varsa Ekle
if ($mesai_saat > 0) {
$m_val = (float)$mesai_saat;
$toplam_fazla += $mesai_saat;
$hucre_mesai = "<span class='text-success fw-bold' style='font-size:0.65rem;'>+ {$m_val}</span>";
// Mesai Arka Planı (İzin yoksa veya izin öncelikli değilse)
if (empty($izin)) {
if ($is_pazar || $is_tatil) $bg = "bg-danger bg-opacity-10";
elseif ($is_cumartesi) $bg = "bg-warning bg-opacity-10";
else $bg = "bg-success bg-opacity-10";
$final_hucre = $hucre_mesai; // Sadece mesai varsa direkt mesaiyi göster
} elseif (in_array($izin['izin_turu'], ['saatlik', 'diger'])) {
// İstenen Durum: Saatlik İzin VE Fazla Mesai (Alt alta gösterim)
$final_hucre = $hucre_izin . "<br>" . $hucre_mesai;
} else {
// Tam Gün Ücretli İzin varsa, mesaiyi altına ekle (Yıllık İzin + Fazla Mesai)
$final_hucre .= "<br>" . $hucre_mesai;
}
}
// D. Tam Çalışma (Tik)
if (empty($final_hucre) && $gunluk_normal > 0) {
$final_hucre = '<i class="fas fa-check" style="font-size:0.6rem; color:#198754;"></i>';
$title = "Tam Çalışma ({$gunluk_saat_std} Saat)";
$bg = "bg-white";
}
// E. Arka Plan Renklendirmesi (Boş/Normal günler)
if (empty($final_hucre)) { // Hücre hâlâ boşsa (Tatil/Hafta Sonu)
if ($is_tatil) $bg = "bg-danger bg-opacity-10";
elseif ($is_pazar) $bg = "bg-secondary bg-opacity-25";
elseif ($is_cumartesi) $bg = "bg-secondary bg-opacity-10";
elseif ($haftanin_gunu <= 5) $bg = "bg-white";
}
// Toplama Ekle (Normal çalışma saatini her durumda toplar)
$toplam_normal += $gunluk_normal;
?>
<td class="<?php echo $bg; ?>" title="<?php echo $title; ?>"
data-bs-toggle="modal" data-bs-target="#detailModal"
data-personel-id="<?php echo $per['id']; ?>"
data-tarih="<?php echo $g_tarih; ?>"
style="cursor: pointer;">
<?php echo $final_hucre; ?>
</td>
<?php endfor; ?>
<td class="fw-bold border-start"><?php echo (float)$toplam_normal; ?></td>
<td class="fw-bold text-success">+<?php echo (float)$toplam_fazla; ?></td>
<td class="fw-bold text-warning"><?php echo (float)$toplam_izin_gun; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="mt-3 small text-muted border-top pt-2 d-flex flex-wrap gap-2 align-items-center">
<strong>Açıklamalar:</strong>
<span class="badge bg-white text-dark border"><i class="fas fa-check"></i></span> = Tam Çalışma (9 Saat)
<span class="badge bg-danger bg-opacity-10 text-danger">- X</span> = Maaş Kesinti Saati (Tek başına)
<span class="badge bg-danger bg-opacity-10 text-danger">- X</span><span class="badge bg-success bg-opacity-10 p-0 px-1 mt-1 text-success">+ Y</span> = Saatlik/Ücretsiz İzin Kesintisi (-X) ve Fazla Mesai (+Y)
<span class="badge bg-info text-white">Y.İZ</span> Yıllık İzin (Ücretli, Kesintisiz)
<span class="badge bg-success p-0 px-1 mt-1">+ X</span> Fazla Mesai Saati (Tek başına)
</div>
</div>
<div class="modal fade" id="detailModal" tabindex="-1" aria-labelledby="detailModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title" id="detailModalLabel"><i class="fas fa-search me-2"></i> Puantaj Detayı</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Kapat"></button>
</div>
<div class="modal-body">
<h6 id="modalPersonelAdi" class="fw-bold mb-3"></h6>
<div class="row">
<div class="col-md-6">
<div class="card card-box mb-3 p-3">
<h6 class="text-primary"><i class="fas fa-chart-bar me-1"></i> Günlük Puantaj Özeti</h6>
<ul class="list-unstyled small">
<li>**Gün:** <span id="modalGunAdi"></span></li>
<li>**Normal Çalışma Saati (Varsayım):** <span id="modalStdSaat"></span> saat</li>
<li>**Net Onaylı Çalışma (İzin Sonrası):** <span id="modalNetCalisma"></span> saat</li>
<li>**Fazla Mesai (Ek Kazanç):** <span id="modalFazlaMesai"></span> saat</li>
</ul>
</div>
</div>
<div class="col-md-6">
<div class="card card-box mb-3 p-3">
<h6 class="text-danger"><i class="fas fa-cut me-1"></i> İzin / Kesinti Bilgisi</h6>
<div id="modalIzinDetaylari">Veri Bulunamadı.</div>
</div>
</div>
</div>
<h6 class="mt-3 text-secondary"><i class="fas fa-gavel me-1"></i> Uygulanan Kurallar</h6>
<div id="modalKurallar" class="small alert alert-light border"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Kapat</button>
</div>
</div>
</div>
</div>
<script>
// PHP verisini JavaScript'e aktar
const PUANTAJ_DATA = <?php echo json_encode($puantaj_datasi); ?>;
const AY_YIL = "<?php echo $yil.'-'.$ay; ?>";
function getGunAdi(tarihStr) {
const date = new Date(tarihStr);
const gunler = ['Pzr', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cts'];
const aylar = ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'];
return `${date.getDate()} ${aylar[date.getMonth()]} ${date.getFullYear()} (${gunler[date.getDay()]})`;
}
// -----------------------------------------------------------
function submitForm(type) {
const form = document.getElementById('puantajForm');
const hiddenInput = document.getElementById('exportFormat');
hiddenInput.value = type;
// Filtreleme yapılıyorsa (type boşsa), formu normal olarak gönderir
if (type === 'excel' || type === 'print') {
form.action = 'puantaj-rapor.php';
form.target = '_blank';
} else {
// Filtreleme için (Listele butonu)
form.action = 'puantaj-cetveli.php'; // Sayfanın kendisine gönderir
form.target = '_self';
}
form.submit();
}
// -----------------------------------------------------------
document.addEventListener('DOMContentLoaded', function() {
const detailModal = document.getElementById('detailModal');
detailModal.addEventListener('show.bs.modal', function (event) {
const button = event.relatedTarget;
const personelId = button.getAttribute('data-personel-id');
const tarih = button.getAttribute('data-tarih');
const data = PUANTAJ_DATA[personelId][tarih];
if (!data) return; // Veri yoksa devam etme
const personelAdi = button.closest('tr').querySelector('td').textContent.trim();
// Modal Başlık
document.getElementById('modalPersonelAdi').textContent = `${personelAdi} (${getGunAdi(tarih)})`;
document.getElementById('modalGunAdi').textContent = getGunAdi(tarih);
document.getElementById('modalStdSaat').textContent = data.gunluk_saat_std;
document.getElementById('modalNetCalisma').textContent = data.net_calisma_saati;
document.getElementById('modalFazlaMesai').textContent = data.mesai_saat;
let izinDetayHTML = '';
let kurallarHTML = '';
if (data.izin) {
const izin = data.izin;
const stil = izin.stil;
izinDetayHTML += `<ul class="list-unstyled small">`;
izinDetayHTML += `<li>**İzin Türü:** <span class="badge ${stil.bg}">${stil.t} - ${izin.izin_turu.toUpperCase()}</span></li>`;
if (izin.izin_turu === 'saatlik') {
const basSaat = new Date(izin.baslangic_tarihi).toTimeString().substring(0, 5);
const bitSaat = new Date(izin.bitis_tarihi).toTimeString().substring(0, 5);
izinDetayHTML += `<li>**İzin Aralığı:** ${basSaat} - ${bitSaat}</li>`;
izinDetayHTML += `<li>**Talep Edilen Süre:** ${izin.saatlik_sure} saat</li>`;
if (data.net_kesinti_saati === 4 || data.net_kesinti_saati === 5) {
kurallarHTML += `<li><i class="fas fa-gavel text-danger me-1"></i> Özel Mola Kuralı uygulandı. (4857 sayılı kanun uyarınca)</li>`;
kurallarHTML += `<li>**Net Kesinti Saati:** ${data.net_kesinti_saati} saat (Mola Dikkate Alındı)</li>`;
kurallarHTML += `<li>**Net Çalışma Süresi:** ${data.net_calisma_saati} saat</li>`;
} else {
kurallarHTML += `<li><i class="fas fa-gavel text-info me-1"></i> Saatlik Kesinti Kuralı uygulandı.</li>`;
kurallarHTML += `<li>**Net Kesinti Saati:** ${data.net_kesinti_saati} saat</li>`;
}
} else if (izin.izin_turu === 'diger') {
kurallarHTML += `<li><i class="fas fa-gavel text-warning me-1"></i> Ücretsiz İzin (Diger) Kuralı uygulandı.</li>`;
izinDetayHTML += `<li>**Net Kesinti Saati:** ${data.net_kesinti_saati} saat</li>`;
} else {
kurallarHTML += `<li><i class="fas fa-gavel text-success me-1"></i> Ücretli İzin (Tam Gün) Kuralı uygulandı. Kesinti yapılmadı.</li>`;
}
if (izin.aciklama) {
izinDetayHTML += `<li>**Açıklama:** ${izin.aciklama}</li>`;
}
izinDetayHTML += `</ul>`;
} else {
izinDetayHTML = '<div class="alert alert-success small">Bu gün için onaylanmış izin kaydı bulunmamaktadır.</div>';
}
if (data.is_tatil) {
kurallarHTML += `<li><i class="fas fa-gavel text-info me-1"></i> Gün Tatil/Hafta Sonu olarak kabul edildi. Normal çalışma saati: 0s.</li>`;
}
document.getElementById('modalIzinDetaylari').innerHTML = izinDetayHTML;
document.getElementById('modalKurallar').innerHTML = kurallarHTML || '<div class="text-muted">Bu gün için özel bir kural uygulanmamıştır.</div>';
});
});
</script>
<?php include '../includes/footer.php'; ?>