PYBS (Personel Yönetim Bilgi Sistemi) / Ramsa/modules/admin-mesai-ekle.php
admin-mesai-ekle.php 315 satır • 15.53 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İ
$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 = round(($t2 - $t1) / 3600, 2);
    $net_mesai_kayit = $fark; // Mola düşümü yok, net süre

    $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 çek
        $personel_data_stmt = $pdo->prepare("SELECT id, ad, soyad FROM kullanicilar WHERE id IN (" . implode(',', $eklenecekler) . ")");
        $personel_data_stmt->execute();
        $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_ad = $personel_info['ad'] . ' ' . $personel_info['soyad'];
            $hata = false;

            // 1. ZATEN MESAI VAR MI?
            $kontrol_mesai = $pdo->prepare("SELECT count(*) FROM mesai_hareketleri WHERE calisan_id = ? AND tarih = ? AND durum = 'onaylandi' AND ( (baslangic_saati < ? AND bitis_saati > ?) )");
            $kontrol_mesai->execute([$pid, $tarih, $bit_saat, $bas_saat]);
            
            if ($kontrol_mesai->fetchColumn() > 0) {
                $hata_mesajlari[] = "({$personel_ad}) $tarih tarihinde belirtilen saatlerde ($bas_saat - $bit_saat) zaten mesai kaydı var.";
                $hata = true;
            }

            // 2. İZİN ÇAKIŞMASI
            if (!$hata) {
                $kontrol_izin = $pdo->prepare("SELECT izin_turu, baslangic_tarihi, bitis_tarihi FROM izin_talepleri WHERE calisan_id = ? AND durum = 'onaylandi' AND ? BETWEEN DATE(baslangic_tarihi) AND DATE(bitis_tarihi)");
                $kontrol_izin->execute([$pid, $tarih]);
                $izin_kaydi = $kontrol_izin->fetch();

                if ($izin_kaydi) {
                    $i_bas_ts = strtotime($izin_kaydi['baslangic_tarihi']);
                    $i_bit_ts = strtotime($izin_kaydi['bitis_tarihi']);
                    
                    $izin_bas_gun = date('Y-m-d', $i_bas_ts);
                    $izin_bit_gun = date('Y-m-d', $i_bit_ts);
                    
                    if ($izin_bas_gun != $izin_bit_gun) {
                        $hata_mesajlari[] = "({$personel_ad}) Personel çok günlü izinli ({$izin_bas_gun} / {$izin_bit_gun}). Bu tarihte mesai yazılamaz.";
                        $hata = true;
                    } 
                    else {
                        $izin_bitis_saati = date('H:i', $i_bit_ts);
                        $mesai_baslangic_saati = $bas_saat;

                        if ($mesai_baslangic_saati < $izin_bitis_saati) {
                            $hata_mesajlari[] = "({$personel_ad}) Tek günlük izin devam ediyor. Mesai saati ($bas_saat), izin bitişinden ($izin_bitis_saati) sonra olmalıdır.";
                            $hata = true;
                        }
                    }
                }
            }
            
            if (!$hata) {
                try {
                    $gercek_mesai_turu = $mesai_turu;
                    if ($mesai_turu === 'vardiya_gece') $gercek_mesai_turu = 'fazla_mesai';
                    
                    $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_listesi = '<ul>';
        foreach ($hata_mesajlari as $hata) $hata_listesi .= '<li>'.$hata.'</li>';
        $hata_listesi .= '</ul>';

        $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ı!</h5>
                Bazı kayıtlar kurallar gereği engellendi:
                <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">
              <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">Engellenen İşlemler</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
                  <div class="modal-body"><div class="alert alert-light border-warning">'.$hata_listesi.'</div></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;
}

$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">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
    <style>
        body { background-color: #f8f9fa; }
        .card { border: none; border-radius: 1.5rem; overflow: hidden; }
        .card-header { background: linear-gradient(135deg, #0d6efd, #0dcaf0); color: white; }
        .flatpickr-day.resmi-tatil { background: #ffebee !important; border-color: #ffcdd2 !important; color: #c62828 !important; font-weight: bold; }
        .flatpickr-day.pazar-gunu { background: #fff3e0 !important; border-color: #ffe0b2 !important; color: #ef6c00 !important; font-weight: bold; }
    </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="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">
                        <input type="hidden" name="csrf_token" value="<?php echo csrfTokenOlustur(); ?>">

                        <div class="mb-4">
                            <label class="form-label fw-semibold">Kime Mesai Eklenecek?</label>
                            <div class="btn-group w-100">
                                <input type="radio" class="btn-check" name="hedef_kitle" id="toplu" value="toplu" onchange="togglePersonel()">
                                <label class="btn btn-outline-primary" for="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" for="tekil">Tek Personel</label>
                            </div>
                        </div>

                        <div class="mb-4" id="tekilDiv">
                            <label class="form-label fw-semibold">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) echo "<option value='{$p['id']}'>{$p['ad']} {$p['soyad']}</option>"; ?>
                            </select>
                        </div>

                        <div class="mb-4 d-none" id="cokluDiv">
                            <label class="form-label fw-semibold">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) echo "<option value='{$p['id']}'>{$p['ad']} {$p['soyad']}</option>"; ?>
                            </select>
                            <div class="form-text">Çoklu seçim için Ctrl tuşuna basılı tutun.</div>
                        </div>

                        <div class="row g-3 mb-4">
                            <div class="col-md-6">
                                <label class="form-label fw-semibold">Tarih</label>
                                <input type="text" name="tarih" id="mesaiTarihInput" class="form-control bg-white fw-bold" required value="<?php echo date('Y-m-d'); ?>">
                            </div>
                            <div class="col-md-6">
                                <label class="form-label fw-semibold">Mesai Türü</label>
                                <select name="mesai_turu" id="mesaiTuruSelect" class="form-select bg-light fw-bold">
                                    <option value="fazla_mesai">Fazla Mesai (1.5x)</option>
                                    <option value="resmi_tatil_mesaisi">Resmi Tatil / Bayram (2.0x)</option>
                                    <option value="vardiya_gece">Vardiya Girişi</option>
                                </select>
                            </div>
                        </div>

                        <div class="row g-3 mb-4">
                            <div class="col-md-6">
                                <label class="form-label fw-semibold">Başlangıç Saati</label>
                                <select name="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 class="form-label fw-semibold">Bitiş Saati</label>
                                <select name="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 class="form-label fw-semibold">Açıklama</label><input type="text" name="aciklama" class="form-control form-control-lg"></div>
                        <div class="d-grid"><button type="submit" class="btn btn-primary btn-lg shadow">KAYDET VE İŞLE</button></div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/l10n/tr.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
const resmiTatiller = <?php echo $tatiller_json; ?>;
document.addEventListener('DOMContentLoaded', function() {
    flatpickr("#mesaiTarihInput", {
        locale: "tr", 
        dateFormat: "Y-m-d", 
        altInput: true, 
        altFormat: "d F Y", 
        defaultDate: "today", 
        disableMobile: true,
        onChange: function(selectedDates, dateStr) { mesaiTuruOtoSec(dateStr); },
        onDayCreate: function(dObj, dStr, fp, dayElem){
            // DÜZELTME: toISOString() yerine Flatpickr'ın kendi formatlayıcısını kullanıyoruz
            // Bu sayede timezone (saat dilimi) kayması önlenir.
            let d = fp.formatDate(dayElem.dateObj, "Y-m-d");
            
            if (resmiTatiller.includes(d)) {
                dayElem.className += " resmi-tatil";
            } else if (dayElem.dateObj.getDay() === 0) {
                dayElem.className += " pazar-gunu";
            }
        }
    });
    togglePersonel();
    mesaiTuruOtoSec(new Date().toISOString().split('T')[0]);
    if(document.getElementById('hataModal')) new bootstrap.Modal(document.getElementById('hataModal')).show();
});

function togglePersonel() {
    const hedef = document.querySelector('input[name="hedef_kitle"]:checked').value;
    document.getElementById('tekilDiv').classList.toggle('d-none', hedef !== 'tekil');
    document.getElementById('cokluDiv').classList.toggle('d-none', hedef !== 'coklu');
    document.getElementById('personel_id_tekil').required = (hedef === 'tekil');
    document.getElementById('personel_id_coklu').required = (hedef === 'coklu');
}

function mesaiTuruOtoSec(dateStr) {
    if (!dateStr) return;
    const date = new Date(dateStr);
    const day = date.getDay();
    let tur = (resmiTatiller.includes(dateStr) || day === 0) ? 'resmi_tatil_mesaisi' : 'fazla_mesai';
    document.getElementById('mesaiTuruSelect').value = tur;
}
</script>
<?php include '../includes/footer.php'; ?>
</body>
</html>