PYBS (Personel Yönetim Bilgi Sistemi) / config/functions.php
functions.php 329 satır • 11.00 KB
<?php
// config/functions.php
// Veritabanı bağlantısını içeri alıyoruz
require_once 'db.php';

// Oturumu başlat (Eğer başlamadıysa)
if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

/**
 * 1. GÜVENLİK FONKSİYONU (XSS Temizliği)
 * Kullanıcıdan gelen her veriyi bu fonksiyonla temizleyeceğiz.
 */
function guvenlik($veri) {
    $veri = trim($veri); // Baştaki sondaki boşlukları sil
    $veri = stripslashes($veri); // Ters eğik çizgileri temizle
    $veri = htmlspecialchars($veri); // HTML etiketlerini etkisiz hale getir (XSS koruması)
    return $veri;
}

/**
 * 2. DETAYLI LOGLAMA FONKSİYONU
 * Sistemdeki her hareketi veritabanına yazar.
 * @param int $kullanici_id İşlemi yapan kişi (Yoksa 0)
 * @param string $islem_tipi giris, ekleme, silme vb.
 * @param string $aciklama Okunabilir detay
 * @param string $tablo Hangi tabloda işlem yapıldı?
 * @param int $kayit_id Hangi kayıt üzerinde işlem yapıldı?
 */
function logKaydet($pdo, $kullanici_id, $islem_tipi, $aciklama, $tablo = null, $kayit_id = null, $eski_veri = null, $yeni_veri = null) {
    
    $ip = $_SERVER['REMOTE_ADDR'];
    $cihaz = $_SERVER['HTTP_USER_AGENT'];

    // GÜVENLİK GÜNCELLEMESİ: islem_tipi ve aciklama XSS'e karşı temizlenmeli
    $temiz_islem_tipi = guvenlik($islem_tipi);
    $temiz_aciklama = guvenlik($aciklama);

    $sql = "INSERT INTO sistem_loglari (kullanici_id, islem_tipi, aciklama, tablo_adi, kayit_id, eski_veri, yeni_veri, ip_adresi, cihaz_bilgisi) 
            VALUES (:kid, :tip, :acik, :tablo, :rid, :eski, :yeni, :ip, :cihaz)";
    
    $stmt = $pdo->prepare($sql);
    $stmt->execute([
        ':kid' => $kullanici_id,
        ':tip' => $temiz_islem_tipi,
        ':acik' => $temiz_aciklama,
        ':tablo' => $tablo,
        ':rid' => $kayit_id,
        ':eski' => $eski_veri ? json_encode($eski_veri, JSON_UNESCAPED_UNICODE) : null,
        ':yeni' => $yeni_veri ? json_encode($yeni_veri, JSON_UNESCAPED_UNICODE) : null,
        ':ip' => $ip,
        ':cihaz' => $cihaz
    ]);
}

/**
 * 3. YETKİ KONTROLÜ
 * Sayfanın başında çağrılır. Kullanıcının o sayfaya girme yetkisi var mı bakar.
 * @param array $izin_verilen_roller Bu sayfayı görebilecek rollerin listesi
 */
function yetkiKontrol($izin_verilen_roller) {
    // 1. Giriş yapılmış mı?
    if (!isset($_SESSION['kullanici_id']) || !isset($_SESSION['rol'])) {
        header("Location: ../index.php?hata=giris_yapilmali");
        exit;
    }

    // 2. Rol yetkisi var mı?
    // Eğer kullanıcının rolü, izin verilenler listesinde YOKSA at.
    if (!in_array($_SESSION['rol'], $izin_verilen_roller)) {
        // Log kaydı atalım: Yetkisiz erişim denemesi
        global $pdo; // $pdo nesnesini globalden çek
        logKaydet($pdo, $_SESSION['kullanici_id'], 'guvenlik_uyarisi', 'Yetkisiz sayfaya erişim denemesi: ' . $_SERVER['PHP_SELF']);
        
        // Ana sayfasına yönlendir veya hata göster
        header("Location: ../index.php?hata=yetkisiz_erisim");
        exit;
    }
}

/**
 * 4. CSRF TOKEN OLUŞTURMA (Form Güvenliği)
 * Formlara gizli input olarak ekleyeceğiz.
 */
function csrfTokenOlustur() {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

/**
 * 5. CSRF KONTROL
 * Form post edildiğinde çağrılır.
 */
function csrfKontrol($token) {
    if (!isset($_SESSION['csrf_token']) || $token !== $_SESSION['csrf_token']) {
        die("Güvenlik Uyarısı: CSRF doğrulaması başarısız. Lütfen sayfayı yenileyip tekrar deneyin.");
    }
}


/**
 * 6. HAFTALIK ÇALIŞMA SÜRESİ HESAPLA (Denkleştirme İçin)
 * Bir personelin, verilen tarihteki haftasının toplam onaylı saatini getirir.
 */
function haftalikToplamSureGetir($pdo, $calisan_id, $referans_tarih) {
    // Tarihin olduğu haftanın Pazartesi ve Pazar günlerini bul
    $dt = new DateTime($referans_tarih);
    // ISO-8601'e göre haftanın ilk günü (Pazartesi)
    $dt->modify('this week monday'); 
    $baslangic = $dt->format('Y-m-d');
    $dt->modify('this week sunday');
    $bitis = $dt->format('Y-m-d');

    // O haftaki ONAYLANMIŞ mesaileri topla
    $sql = "SELECT SUM(toplam_saat) as toplam 
            FROM mesai_hareketleri 
            WHERE calisan_id = ? 
            AND tarih BETWEEN ? AND ? 
            AND durum = 'onaylandi'";
            
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$calisan_id, $baslangic, $bitis]);
    return (float)$stmt->fetchColumn(); // Örn: 42.5
}

/**
 * 7. NET İZİN GÜNÜ HESAPLA
 * Pazar günlerini ve Resmi Tatilleri saymaz.
 * @param PDO $pdo Veritabanı bağlantısı
 * @param string $baslangic Y-m-d H:i
 * @param string $bitis Y-m-d H:i
 * @return float Hesaplanan net iş günü
 */
function netIzinGunSayisiHesapla($pdo, $baslangic, $bitis) {
    // Saatleri sıfırla, sadece gün bazlı hesaplayalım
    $start = new DateTime(substr($baslangic, 0, 10));
    $end = new DateTime(substr($bitis, 0, 10));
    // Bitiş günü de dahil edilmeli, o yüzden döngüde <= kullanacağız
    // Ama DateTime loop'unda bitişi dahil etmek için 1 gün ileri alıp < yaparız veya interval kullanırız.
    // En temizi: $end gününü de kontrol etmek.
    
    $toplam_gun = 0;
    
    // Resmi Tatilleri Çek (Performans için bir kere çekip array yapalım)
    // Sadece bu aralıktaki tatilleri çek
    $sql = "SELECT tarih FROM resmi_tatiller WHERE tarih BETWEEN ? AND ?";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$start->format('Y-m-d'), $end->format('Y-m-d')]);
    $tatiller = $stmt->fetchAll(PDO::FETCH_COLUMN); // ['2025-01-01', '2025-04-23'] gibi döner

    // Döngü: Başlangıçtan Bitişe kadar gün gün git
    $current = clone $start;
    while ($current <= $end) {
        $curr_date = $current->format('Y-m-d');
        $haftanin_gunu = $current->format('N'); // 1 (Pzt) - 7 (Paz)
        
        // KURAL 1: Pazar günü mü? (7) -> Sayma
        // KURAL 2: Resmi tatil mi? -> Sayma
        if ($haftanin_gunu != 7 && !in_array($curr_date, $tatiller)) {
            $toplam_gun++;
        }
        
        // Bir sonraki güne geç
        $current->modify('+1 day');
    }
    
    // Eğer başlangıç ve bitiş aynı günse ve o gün tatil değilse 1 saymalı
    if ($start == $end && $toplam_gun == 0) {
        // Ama tatilse 0 kalmalı. Yukarıdaki döngü bunu halleder.
    }
    
    return $toplam_gun;
}

/**
 * 8. TELEFON FORMATLAMA
 * Girdiyi temizler ve +905XXXXXXXXX formatına çevirir.
 */
function telefonFormatla($tel) {
    // Sadece rakamları al
    $tel = preg_replace('/[^0-9]/', '', $tel);
    
    // Boşsa boş dön
    if (empty($tel)) return '';

    // Uzunluğa göre düzenle
    // 532... (10 hane) -> +90532...
    if (strlen($tel) == 10) {
        return '+90' . $tel;
    }
    // 0532... (11 hane) -> +90532...
    if (strlen($tel) == 11 && substr($tel, 0, 1) == '0') {
        return '+9' . $tel;
    }
    // 90532... (12 hane) -> +90532...
    if (strlen($tel) == 12 && substr($tel, 0, 2) == '90') {
        return '+' . $tel;
    }
    
    // Format uymuyorsa olduğu gibi (veya başına + ekleyerek) döndür
    return '+' . $tel;
}
// config/functions.php (En alta ekleyin)

/**
 * 9. HAFTALIK ONAYLI SÜRE HESAPLA
 * Verilen tarihin ait olduğu haftadaki (Pzt-Paz) toplam onaylı saati döndürür.
 */
function haftalikOnayliSureGetir($pdo, $calisan_id, $referans_tarih) {
    $date = new DateTime($referans_tarih);
    // Haftanın günü (1:Pzt ... 7:Paz)
    $gun = $date->format('N');
    
    // Pazartesiye git
    $pazartesi = clone $date;
    $pazartesi->modify('-' . ($gun - 1) . ' days');
    
    // Pazara git
    $pazar = clone $pazartesi;
    $pazar->modify('+6 days');
    
    $bas = $pazartesi->format('Y-m-d');
    $bit = $pazar->format('Y-m-d');

    $sql = "SELECT SUM(toplam_saat) FROM mesai_hareketleri 
            WHERE calisan_id = ? AND durum = 'onaylandi' 
            AND tarih BETWEEN ? AND ?";
            
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$calisan_id, $bas, $bit]);
    
    return (float)$stmt->fetchColumn();
}


/**
 * 10. TÜRKÇE TARİH FORMATLAMA
 * Girdiyi alır, "18 Mayıs 2025" formatına çevirir.
 * @param string $tarih (Y-m-d veya datetime)
 * @param bool $sadece_ay_yil (True ise sadece 'Aralık 2025' döndürür)
 */
function tarihTurkce($tarih, $sadece_ay_yil = false) {
    if (!$tarih) return '-';
    
    $zaman = strtotime($tarih);
    
    $aylar_en = [
        'January', 'February', 'March', 'April', 'May', 'June', 
        'July', 'August', 'September', 'October', 'November', 'December'
    ];
    
    $aylar_tr = [
        'Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 
        'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'
    ];
    
    // Format belirle
    $format = $sadece_ay_yil ? 'F Y' : 'd F Y';
    
    $ingilizce_tarih = date($format, $zaman);
    
    // Değiştir ve döndür
    return str_replace($aylar_en, $aylar_tr, $ingilizce_tarih);
}

/**
 * 11. BİLDİRİM GÖNDER
 * Belirli bir role veya kişiye bildirim atar.
 */
function bildirimGonder($pdo, $hedef_kullanici_id, $baslik, $mesaj, $link) {
    $sql = "INSERT INTO bildirimler (kullanici_id, baslik, mesaj, link) VALUES (?, ?, ?, ?)";
    $pdo->prepare($sql)->execute([$hedef_kullanici_id, $baslik, $mesaj, $link]);
}

/**
 * 12. GRUP BİLDİRİMİ (Rol Bazlı)
 * Örn: Tüm 'muhasebe' ve 'insan_kaynaklari' rollerine gönderir.
 */
function rolBildirimGonder($pdo, $roller_dizisi, $baslik, $mesaj, $link) {
    // Rolleri virgülle ayırıp SQL'e sokalım ('muhasebe','insan_kaynaklari')
    $in  = str_repeat('?,', count($roller_dizisi) - 1) . '?';
    $sql = "SELECT id FROM kullanicilar WHERE rol IN ($in) AND durum = 1";
    $stmt = $pdo->prepare($sql);
    $stmt->execute($roller_dizisi);
    $kisiler = $stmt->fetchAll(PDO::FETCH_COLUMN);

    foreach($kisiler as $k_id) {
        bildirimGonder($pdo, $k_id, $baslik, $mesaj, $link);
    }
}

/**
 * 13. OKUNMAMIŞ BİLDİRİM SAYISI
 * GÜVENLİK GÜNCELLEMESİ: SQL Injection'a karşı koruma eklendi
 */
function bildirimSayisi($pdo, $uid) {
    $sql = "SELECT COUNT(*) FROM bildirimler WHERE kullanici_id = ? AND okundu = 0";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$uid]); // Parametre bağlama kullanıldı
    return $stmt->fetchColumn();
}

/**
 * 14. PERSONEL ANLIK DURUM SORGULA
 * Bugünün tarihine göre personelin onaylı bir izni var mı bakar.
 */
function personelDurumu($pdo, $calisan_id) {
    $bugun = date('Y-m-d');
    
    // Bugün tarih aralığında olan ONAYLI izin var mı?
    $sql = "SELECT COUNT(*) FROM izin_talepleri 
            WHERE calisan_id = ? 
            AND durum = 'onaylandi' 
            AND ? BETWEEN DATE(baslangic_tarihi) AND DATE(bitis_tarihi)";
            
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$calisan_id, $bugun]);
    $izinli_mi = $stmt->fetchColumn();

    if ($izinli_mi > 0) {
        return '<span class="badge bg-warning text-dark"><i class="fas fa-umbrella-beach"></i> İZİNLİ</span>';
    } else {
        return '<span class="badge bg-success"><i class="fas fa-briefcase"></i> MESAİDE</span>';
    }
}
?>