PYBS (Personel Yönetim Bilgi Sistemi) / modules/admin-mesai-ekle.php
admin-mesai-ekle.php 450 satır • 21.38 KB
<?php
// modules/admin-mesai-ekle.php
session_start();
require_once '../config/db.php';
require_once '../config/functions.php';

// Sadece Yetkililer
yetkiKontrol(['root', 'muhasebe', 'insan_kaynaklari']);

include '../includes/header.php';
include '../includes/menu.php';

$mesaj = '';
$kullanici_id = $_SESSION['kullanici_id'];
$hedef = 'tekil'; // Varsayılan: Tekil

// Resmi Tatil Listesi
$tatiller = $pdo->query("SELECT tarih FROM resmi_tatiller")->fetchAll(PDO::FETCH_COLUMN);
$tatiller_json = json_encode($tatiller);

// SAAT SEÇENEKLERİ (tam ve buçuk)
$zaman_secenekleri = "";
for ($saat = 0; $saat < 24; $saat++) {
    foreach (['00', '30'] as $dakika) {
        $deger = sprintf('%02d:%s', $saat, $dakika); 
        $zaman_secenekleri .= "<option value='$deger'>$deger</option>";
    }
}

// İŞLEM
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    csrfKontrol($_POST['csrf_token']);

    $hedef = $_POST['hedef_kitle'];
    
    $eklenecekler = [];
    if ($hedef == 'toplu') {
        $eklenecekler = $pdo->query("SELECT id FROM kullanicilar WHERE durum=1 AND rol != 'root'")->fetchAll(PDO::FETCH_COLUMN);
    } elseif ($hedef == 'coklu') {
        $eklenecekler = $_POST['personel_id_coklu'] ?? [];
    } elseif ($hedef == 'tekil') {
        if (!empty($_POST['personel_id_tekil'])) {
            $eklenecekler[] = $_POST['personel_id_tekil'];
        }
    }

    $tarih = $_POST['tarih'];
    $bas_saat = $_POST['baslangic_saati'];
    $bit_saat = $_POST['bitis_saati'];
    $mesai_turu = $_POST['mesai_turu'];
    $aciklama = guvenlik($_POST['aciklama']);

    // Brüt Süre Hesapla
    $t1 = strtotime($bas_saat);
    $t2 = strtotime($bit_saat);
    if ($t2 < $t1) $t2 += 24 * 60 * 60;
    
    $fark = ($t2 - $t1) / 3600;
    
    // Mola Düş (Otomatik)
    $mola = 0;
    if ($fark > 7.5) $mola = 1; elseif ($fark > 4) $mola = 0.5;
    
    $net_saat_brut = $fark - $mola;
    if($net_saat_brut < 0) $net_saat_brut = 0;

    // Döngü ile Ekle
    $sayac = 0;
    $hata_mesajlari = [];
    $sql = "INSERT INTO mesai_hareketleri (calisan_id, hedef_yonetici_id, onaylayan_id, tarih, baslangic_saati, bitis_saati, toplam_saat, mesai_turu, aciklama, durum, onaylanma_tarihi) 
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'onaylandi', NOW())";
    $stmt = $pdo->prepare($sql);

    if (!empty($eklenecekler)) {
        // Personel verilerini topluca çek
        $personel_data_stmt = $pdo->prepare("SELECT id, ad, soyad, gunluk_calisma_saati FROM kullanicilar WHERE id IN (" . implode(',', $eklenecekler) . ")");
        $personel_data_stmt->execute();
        // HATA DÜZELTME: FETCH_KEY_PAIR hatası giderildi, ID'ye göre gruplayarak ilişkisel dizi çekildi.
        $personel_bilgileri = $personel_data_stmt->fetchAll(PDO::FETCH_ASSOC | PDO::FETCH_GROUP); 
        
        foreach ($eklenecekler as $pid) {
            $pid = (int)$pid;
            $personel_info = $personel_bilgileri[$pid][0] ?? null;

            if (!$personel_info) continue; // Personel bulunamazsa atla
            
            $personel_ad = $personel_info['ad'] . ' ' . $personel_info['soyad'];
            $gunluk_mesai = (float)($personel_info['gunluk_calisma_saati'] ?? 9.00); // Varsayılan 9 saat

            // 1. Kontrol: Onaylanmış mevcut mesai kaydı var mı?
            $kontrol_mesai = $pdo->prepare("SELECT count(*) FROM mesai_hareketleri WHERE calisan_id = ? AND tarih = ? AND durum = 'onaylandi'");
            $kontrol_mesai->execute([$pid, $tarih]);
            
            // 2. Kontrol: Onaylanmış TAM GÜN izin kaydı var mı?
            $kontrol_izin_tam = $pdo->prepare("
                SELECT count(*) FROM izin_talepleri 
                WHERE calisan_id = ? 
                AND durum = 'onaylandi' 
                AND ? BETWEEN DATE(baslangic_tarihi) AND DATE(bitis_tarihi)
                AND izin_turu != 'saatlik'
            ");
            $kontrol_izin_tam->execute([$pid, $tarih]);

            
            $hata = false;

            if ($kontrol_mesai->fetchColumn() > 0) {
                $hata_mesajlari[] = "({$personel_ad}) $tarih tarihinde zaten onaylanmış <strong>MESAİ</strong> kaydı var.";
                $hata = true;
            } elseif ($kontrol_izin_tam->fetchColumn() > 0) {
                 $hata_mesajlari[] = "({$personel_ad}) $tarih tarihinde onaylanmış <strong>TAM GÜN İZİN</strong> kaydı var. Mesai eklenemez.";
                 $hata = true;
            } 
            
            // Eğer tam gün veya mesai çakışması yoksa, saatlik izin borcunu hesapla
            if (!$hata) {
                
                // 3. Saatlik İzin Borcunu Hesapla
                $stmt_saatlik_izin = $pdo->prepare("
                    SELECT SUM(saatlik_sure) as toplam_izin 
                    FROM izin_talepleri 
                    WHERE calisan_id = ? 
                    AND durum = 'onaylandi' 
                    AND DATE(baslangic_tarihi) = ?
                    AND izin_turu = 'saatlik'
                ");
                $stmt_saatlik_izin->execute([$pid, $tarih]);
                $toplam_saatlik_izin = (float)$stmt_saatlik_izin->fetchColumn() ?? 0.0;
                
                $net_mesai_kayit = $net_saat_brut;
                
                // Eğer saatlik izin varsa ve bu mesai borç kapatmak için kullanılıyorsa
                if ($toplam_saatlik_izin > 0) {
                    
                    // Yeni mesai süresi = Girilen süre - (Borç olan izin süresi)
                    $gercek_fazla_mesai = $net_saat_brut - $toplam_saatlik_izin;
                    
                    if ($gercek_fazla_mesai > 0) {
                        $net_mesai_kayit = round($gercek_fazla_mesai, 2);
                        
                        // İzin borcunun kapatıldığı bilgisini uyarı olarak ekle
                         $hata_mesajlari[] = "({$personel_ad}) $tarih tarihinde {$toplam_saatlik_izin} saatlik izin borcu kapatıldı. Fazla mesai süresi: **{$net_mesai_kayit} Saat** olarak kaydedildi.";
                    } else {
                        // Fazla mesai, izin borcunu tamamen kapatıyorsa ve geriye bir şey kalmıyorsa, mesaiyi kaydetme
                        $hata_mesajlari[] = "({$personel_ad}) $tarih tarihinde {$toplam_saatlik_izin} saatlik izin borcu olduğu için, girilen {$net_saat_brut} saat mesai kaydı tamamen izin borcunu kapatmıştır. Fazla mesai **0.0 Saat** olarak kaydedilmedi.";
                        $hata = true; // <-- KAYDI ENGELLE
                        
                    }
                    
                }

                if (!$hata) {
                    try {
                        $gercek_mesai_turu = $mesai_turu;
                        if ($mesai_turu === 'vardiya_gece') {
                            $gercek_mesai_turu = 'fazla_mesai';
                        }
                        
                        // Güncellenmiş net mesai süresini kullan
                        $stmt->execute([$pid, $kullanici_id, $kullanici_id, $tarih, $bas_saat, $bit_saat, $net_mesai_kayit, $gercek_mesai_turu, $aciklama]);
                        $sayac++;
                    } catch (PDOException $e) {
                        $hata_mesajlari[] = "({$personel_ad}) Veritabanı Hatası: " . $e->getMessage();
                    }
                }
            }
        }
    }

    $mesaj_html = "";
    if ($sayac > 0) {
        $mesaj_html .= '<div class="alert alert-success border-success border-5 border-start shadow-sm">
                          <h5 class="alert-heading fw-bold"><i class="fas fa-check-circle me-2"></i>Başarılı!</h5>
                          Toplam <strong>'.$sayac.'</strong> personele mesai girişi yapıldı.
                        </div>';
    }
    if (!empty($hata_mesajlari)) {
        // Hata mesajlarını modalda göstermek için hazırlıyoruz
        $hata_listesi = '<ul>';
        foreach ($hata_mesajlari as $hata) {
            $hata_listesi .= '<li>'.$hata.'</li>';
        }
        $hata_listesi .= '</ul>';

        // Mesajı modalı tetikleyecek şekilde ayarlıyoruz
        $mesaj_html .= '
            <div class="alert alert-warning border-warning border-5 border-start shadow-sm">
                <h5 class="alert-heading fw-bold"><i class="fas fa-exclamation-triangle me-2"></i>Mesai İşlemi Uyarıları/Detayları!</h5>
                Bazı personel için süreler ayarlandı/kayıt engellendi. Detaylar için aşağıdaki butona tıklayın:
                <button type="button" class="btn btn-sm btn-warning mt-2" data-bs-toggle="modal" data-bs-target="#hataModal">
                    <i class="fas fa-search me-1"></i> Detayları Gör
                </button>
            </div>
            
            <div class="modal fade" id="hataModal" tabindex="-1" aria-labelledby="hataModalLabel" aria-hidden="true">
              <div class="modal-dialog modal-dialog-centered modal-lg">
                <div class="modal-content">
                  <div class="modal-header bg-warning text-dark">
                    <h5 class="modal-title" id="hataModalLabel"><i class="fas fa-exclamation-triangle me-2"></i> Mesai İşlemi Detayları</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                  </div>
                  <div class="modal-body">
                    <p class="fw-bold">Aşağıdaki personel için izin/mesai çakışması kontrol edildi. Mesai süreleri ayarlandı veya kayıt engellendi:</p>
                    <div class="alert alert-light border-warning">
                        '.$hata_listesi.'
                    </div>
                    <p class="small text-muted">Tam gün izin veya mevcut mesai kaydı olanlara işlem yapılmamıştır.</p>
                  </div>
                  <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Kapat</button>
                  </div>
                </div>
              </div>
            </div>';
    }
    if ($sayac == 0 && empty($hata_mesajlari)) {
        $mesaj_html .= '<div class="alert alert-danger">Lütfen işlem yapılacak personel seçin.</div>';
    }
    $mesaj = $mesaj_html;
}

// Personel Listesi
$personeller = $pdo->query("SELECT id, ad, soyad FROM kullanicilar WHERE durum=1 AND rol != 'root' ORDER BY ad ASC")->fetchAll();
?>

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
    <title>Yönetici Mesai Girişi</title>
    <style>
        body { background-color: #f8f9fa; }
        .card { border: none; border-radius: 1.5rem; overflow: hidden; }
        .card-header { background: linear-gradient(135deg, #0d6efd, #0dcaf0); color: white; }
        .form-control, .form-select { border-radius: 0.75rem; }
        .btn-primary { border-radius: 0.75rem; padding: 0.85rem; font-weight: 600; }
        .btn-check:checked + .btn-outline-primary { background-color: #0d6efd; border-color: #0d6efd; }
        .alert-important { border-left-width: 6px; }
        @media (max-width: 576px) {
            .container-fluid { padding: 1rem; }
            h3 { font-size: 1.4rem; }
        }
    </style>
</head>
<body>

<div class="container-fluid py-4 py-md-5">
    <div class="row justify-content-center">
        <div class="col-12 col-lg-10 col-xl-8">

            <h3 class="mb-4 text-center text-primary fw-bold">
                <i class="fas fa-clock me-2"></i>Yönetici Mesai Girişi
            </h3>

            <div class="alert alert-info alert-important mb-4" role="alert">
                <h6 class="alert-heading fw-bold"><i class="fas fa-info-circle me-2"></i>Bilgi</h6>
                Bu sayfadan yapılan mesai girişleri <strong>otomatik onaylı</strong> olarak sisteme işlenir.<br>
                Aynı güne onaylı tam gün izin veya mevcut mesai kaydı olanlara işlem **yapılmaz**. Saatlik izin borcu varsa, girilen fazla mesai süresi öncelikle bu borcu kapatır.
            </div>

            <div class="card shadow-lg">
                <div class="card-header py-4 text-center">
                    <h5 class="mb-0 text-white fw-bold">Mesai Bilgilerini Giriniz</h5>
                </div>
                <div class="card-body p-4 p-md-5">

                    <?php echo $mesaj; ?>

                    <form method="POST" id="mesaiForm" novalidate>
                        <input type="hidden" name="csrf_token" value="<?php echo csrfTokenOlustur(); ?>">

                        <div class="mb-4">
                            <label class="form-label fw-semibold">
                                <i class="fas fa-users me-2 text-primary"></i>Kime Mesai Eklenecek? <span class="text-danger">*</span>
                            </label>
                            <div class="btn-group w-100" role="group" aria-label="Hedef kitle">
                                <input type="radio" class="btn-check" name="hedef_kitle" id="toplu" value="toplu" onchange="togglePersonel()">
                                <label class="btn btn-outline-primary rounded-start" for="toplu">Toplu (Tüm Personel)</label>

                                <input type="radio" class="btn-check" name="hedef_kitle" id="coklu" value="coklu" onchange="togglePersonel()">
                                <label class="btn btn-outline-primary" for="coklu">Çoklu Seçim</label>

                                <input type="radio" class="btn-check" name="hedef_kitle" id="tekil" value="tekil" checked onchange="togglePersonel()">
                                <label class="btn btn-outline-primary rounded-end" for="tekil">Tek Personel</label>
                            </div>
                        </div>

                        <div class="mb-4" id="tekilDiv">
                            <label for="personel_id_tekil" class="form-label fw-semibold">
                                <i class="fas fa-user me-2 text-primary"></i>Personel Seçiniz
                            </label>
                            <select name="personel_id_tekil" id="personel_id_tekil" class="form-select form-select-lg">
                                <option value="">Seçiniz...</option>
                                <?php foreach($personeller as $p): ?>
                                    <option value="<?php echo $p['id']; ?>">
                                        <?php echo htmlspecialchars($p['ad'].' '.$p['soyad']); ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                        </div>

                        <div class="mb-4 d-none" id="cokluDiv">
                            <label for="personel_id_coklu" class="form-label fw-semibold">
                                <i class="fas fa-users-cog me-2 text-primary"></i>Personelleri Seçiniz (Çoklu)
                            </label>
                            <select name="personel_id_coklu[]" id="personel_id_coklu" class="form-select form-select-lg" multiple size="8">
                                <?php foreach($personeller as $p): ?>
                                    <option value="<?php echo $p['id']; ?>">
                                        <?php echo htmlspecialchars($p['ad'].' '.$p['soyad']); ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                            <div class="form-text mt-2">
                                <i class="fas fa-info-circle me-2"></i>
                                Birden fazla seçim için <kbd>Ctrl</kbd> (Windows) veya <kbd>Cmd</kbd> (Mac) tuşunu basılı tutun.
                            </div>
                        </div>

                        <hr class="my-5">

                        <div class="row g-3 mb-4">
                            <div class="col-md-6">
                                <label for="tarih" class="form-label fw-semibold">
                                    <i class="fas fa-calendar-day me-2 text-primary"></i>Tarih <span class="text-danger">*</span>
                                </label>
                                <input type="date" name="tarih" id="mesaiTarihInput" class="form-control form-control-lg" 
                                       value="<?php echo date('Y-m-d'); ?>" required onchange="mesaiTuruOtoSec()">
                            </div>
                            <div class="col-md-6">
                                <label for="mesai_turu" class="form-label fw-semibold">
                                    <i class="fas fa-tags me-2 text-primary"></i>Mesai Türü <span class="text-danger">*</span>
                                </label>
                                <select name="mesai_turu" id="mesaiTuruSelect" class="form-select form-select-lg bg-light fw-bold">
                                    <option value="fazla_mesai">Fazla Mesai (Hafta İçi / Cumartesi)</option>
                                    <option value="resmi_tatil_mesaisi">Resmi Tatil / Bayram (2.0x)</option>
                                    <option value="vardiya_gece">Vardiya Girişi (18:00 - 03:00)</option>
                                </select>
                            </div>
                        </div>

                        <div class="row g-3 mb-4">
                            <div class="col-md-6">
                                <label for="baslangic_saati" class="form-label fw-semibold">
                                    <i class="fas fa-clock me-2 text-primary"></i>Başlangıç Saati <span class="text-danger">*</span>
                                </label>
                                <select name="baslangic_saati" id="baslangic_saati" class="form-select form-select-lg" required>
                                    <option value="">Seçiniz</option>
                                    <?php echo $zaman_secenekleri; ?>
                                </select>
                            </div>
                            <div class="col-md-6">
                                <label for="bitis_saati" class="form-label fw-semibold">
                                    <i class="far fa-clock me-2 text-primary"></i>Bitiş Saati <span class="text-danger">*</span>
                                </label>
                                <select name="bitis_saati" id="bitis_saati" class="form-select form-select-lg" required>
                                    <option value="">Seçiniz</option>
                                    <?php echo $zaman_secenekleri; ?>
                                </select>
                            </div>
                        </div>

                        <div class="mb-4">
                            <label for="aciklama" class="form-label fw-semibold">
                                <i class="fas fa-comment-dots me-2 text-primary"></i>Açıklama (İsteğe Bağlı)
                            </label>
                            <input type="text" name="aciklama" id="aciklama" class="form-control form-control-lg" 
                                   placeholder="Örn: Ek üretim vardiyası, bayram mesaisi...">
                        </div>

                        <div class="d-grid">
                            <button type="submit" class="btn btn-primary btn-lg shadow">
                                <i class="fas fa-save me-2"></i> KAYDET VE İŞLE
                            </button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
const resmiTatiller = <?php echo $tatiller_json; ?>;

function togglePersonel() {
    const hedef = document.querySelector('input[name="hedef_kitle"]:checked').value;
    const tekilDiv = document.getElementById('tekilDiv');
    const cokluDiv = document.getElementById('cokluDiv');
    const tekilSelect = document.getElementById('personel_id_tekil');
    const cokluSelect = document.getElementById('personel_id_coklu');

    tekilDiv.classList.toggle('d-none', hedef !== 'tekil');
    cokluDiv.classList.toggle('d-none', hedef !== 'coklu');

    // Required ayarı
    if (hedef === 'tekil') {
        tekilSelect.required = true;
        cokluSelect.required = false;
    } else if (hedef === 'coklu') {
        tekilSelect.required = false;
        cokluSelect.required = true;
    } else { // toplu
        tekilSelect.required = false;
        cokluSelect.required = false;
    }
}

function mesaiTuruOtoSec() {
    const tarihInput = document.getElementById('mesaiTarihInput');
    const mesaiSelect = document.getElementById('mesaiTuruSelect');
    const tarih = tarihInput.value;

    if (!tarih) {
        mesaiSelect.value = 'fazla_mesai';
        return;
    }

    const date = new Date(tarih + 'T00:00:00');
    const day = date.getDay(); // 0=Pazar, 6=Cumartesi

    let tur = 'fazla_mesai';

    if (resmiTatiller.includes(tarih) || day === 0) {
        tur = 'resmi_tatil_mesaisi';
    }

    mesaiSelect.value = tur;
}

// Sayfa yüklendiğinde
document.addEventListener('DOMContentLoaded', () => {
    togglePersonel();
    mesaiTuruOtoSec();
    
    // PHP'den gelen hatayı kontrol et ve modalı tetikle
    const hataModal = document.getElementById('hataModal');
    if (hataModal) {
        const bsHataModal = new bootstrap.Modal(hataModal);
        bsHataModal.show();
    }
});
</script>

<?php include '../includes/footer.php'; ?>
</body>
</html>