MIF_E31230892/sim-pkpps/app/Models/Kepulangan.php

487 lines
15 KiB
PHP

<?php
// app/Models/Kepulangan.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
class Kepulangan extends Model
{
use HasFactory;
protected $table = 'kepulangan';
protected $fillable = [
'id_kepulangan',
'id_santri',
'tanggal_izin',
'tanggal_pulang',
'tanggal_kembali',
'durasi_izin',
'alasan',
'status',
'approved_by',
'approved_at',
'catatan',
];
protected $casts = [
'tanggal_izin' => 'date',
'tanggal_pulang' => 'date',
'tanggal_kembali' => 'date',
'approved_at' => 'datetime',
];
/**
* Boot method - Auto generate ID & calculate durasi
*/
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
// Generate ID Kepulangan
if (empty($model->id_kepulangan)) {
$last = Kepulangan::orderBy('id', 'desc')->first();
$num = $last ? intval(substr($last->id_kepulangan, 2)) + 1 : 1;
$model->id_kepulangan = 'KP' . str_pad($num, 3, '0', STR_PAD_LEFT);
}
// PENTING: Hitung durasi_izin otomatis
if ($model->tanggal_pulang && $model->tanggal_kembali) {
$model->durasi_izin = $model->hitungDurasiIzin(
$model->tanggal_pulang,
$model->tanggal_kembali
);
}
// Set tanggal_izin jika kosong
if (empty($model->tanggal_izin)) {
$model->tanggal_izin = now();
}
});
static::updating(function ($model) {
// Recalculate durasi_izin saat update tanggal
if ($model->isDirty(['tanggal_pulang', 'tanggal_kembali'])) {
$model->durasi_izin = $model->hitungDurasiIzin(
$model->tanggal_pulang,
$model->tanggal_kembali
);
}
});
}
/**
* Method untuk menghitung durasi izin dalam hari
* Formula: (tanggal_kembali - tanggal_pulang) + 1
*/
private function hitungDurasiIzin($tanggalPulang, $tanggalKembali)
{
$pulang = Carbon::parse($tanggalPulang);
$kembali = Carbon::parse($tanggalKembali);
// +1 karena hari pertama juga dihitung
return $pulang->diffInDays($kembali) + 1;
}
/**
* Relasi ke Santri
*/
public function santri()
{
return $this->belongsTo(Santri::class, 'id_santri', 'id_santri');
}
/**
* Accessor: Format tanggal
*/
public function getTanggalIzinFormattedAttribute()
{
return $this->tanggal_izin ? $this->tanggal_izin->format('d F Y') : '-';
}
public function getTanggalPulangFormattedAttribute()
{
return $this->tanggal_pulang ? $this->tanggal_pulang->format('d F Y') : '-';
}
public function getTanggalKembaliFormattedAttribute()
{
return $this->tanggal_kembali ? $this->tanggal_kembali->format('d F Y') : '-';
}
public function getApprovedAtFormattedAttribute()
{
return $this->approved_at ? $this->approved_at->format('d F Y H:i') : '-';
}
/**
* Accessor: Durasi izin calculated (untuk backward compatibility)
*/
public function getDurasiIzinCalculatedAttribute()
{
return $this->durasi_izin;
}
/**
* Accessor: Status badge
*/
public function getStatusBadgeAttribute()
{
$badges = [
'Menunggu' => 'badge-warning',
'Disetujui' => 'badge-success',
'Ditolak' => 'badge-danger',
'Selesai' => 'badge-secondary',
];
return $badges[$this->status] ?? 'badge-secondary';
}
/**
* Accessor: Apakah sedang dalam periode izin
*/
public function getIsAktifAttribute()
{
$today = Carbon::today();
return $this->status === 'Disetujui'
&& $today->between($this->tanggal_pulang, $this->tanggal_kembali);
}
/**
* Accessor: Apakah terlambat kembali
*/
public function getIsTerlambatAttribute()
{
if ($this->status !== 'Disetujui') {
return false;
}
return Carbon::today()->greaterThan($this->tanggal_kembali);
}
/**
* Scopes
*/
public function scopeStatus($query, $status)
{
return $query->where('status', $status);
}
public function scopeSantri($query, $idSantri)
{
return $query->where('id_santri', $idSantri);
}
public function scopeAktif($query)
{
$today = Carbon::today();
return $query->where('status', 'Disetujui')
->whereDate('tanggal_pulang', '<=', $today)
->whereDate('tanggal_kembali', '>=', $today);
}
public function scopeTerlambat($query)
{
return $query->where('status', 'Disetujui')
->whereDate('tanggal_kembali', '<', Carbon::today());
}
public function scopeSearch($query, $search)
{
return $query->where(function($q) use ($search) {
$q->where('id_kepulangan', 'like', "%{$search}%")
->orWhere('alasan', 'like', "%{$search}%")
->orWhereHas('santri', function($sq) use ($search) {
$sq->where('nama_lengkap', 'like', "%{$search}%")
->orWhere('id_santri', 'like', "%{$search}%")
->orWhere('nis', 'like', "%{$search}%");
});
});
}
/**
* ========================================
* FITUR KUOTA TAHUNAN
* ========================================
*/
/**
* Get settings kepulangan (kuota, periode)
*/
public static function getSettings()
{
$settings = DB::table('kepulangan_settings')->latest()->first();
if (!$settings) {
// Create default settings
DB::table('kepulangan_settings')->insert([
'kuota_maksimal' => 12,
'periode_mulai' => now()->startOfYear()->format('Y-m-d'),
'periode_akhir' => now()->endOfYear()->format('Y-m-d'),
'created_at' => now(),
'updated_at' => now(),
]);
$settings = DB::table('kepulangan_settings')->latest()->first();
}
return (object) [
'id' => $settings->id,
'kuota_maksimal' => $settings->kuota_maksimal,
'periode_mulai' => Carbon::parse($settings->periode_mulai),
'periode_akhir' => Carbon::parse($settings->periode_akhir),
'terakhir_reset' => $settings->terakhir_reset ? Carbon::parse($settings->terakhir_reset) : null,
'reset_by' => $settings->reset_by,
];
}
/**
* Update settings kepulangan
*/
public static function updateSettings($kuotaMaksimal, $periodeMulai, $periodeAkhir)
{
$existing = DB::table('kepulangan_settings')->latest()->first();
if ($existing) {
DB::table('kepulangan_settings')
->where('id', $existing->id)
->update([
'kuota_maksimal' => $kuotaMaksimal,
'periode_mulai' => $periodeMulai,
'periode_akhir' => $periodeAkhir,
'updated_at' => now(),
]);
} else {
DB::table('kepulangan_settings')->insert([
'kuota_maksimal' => $kuotaMaksimal,
'periode_mulai' => $periodeMulai,
'periode_akhir' => $periodeAkhir,
'created_at' => now(),
'updated_at' => now(),
]);
}
}
/**
* Get total hari izin santri dalam periode tertentu
*/
public static function getTotalHariIzinSantri($idSantri, $periodeMulai = null, $periodeAkhir = null)
{
if (!$periodeMulai || !$periodeAkhir) {
$settings = self::getSettings();
$periodeMulai = $settings->periode_mulai;
$periodeAkhir = $settings->periode_akhir;
}
return self::where('id_santri', $idSantri)
->whereIn('status', ['Disetujui', 'Selesai'])
->whereBetween('tanggal_pulang', [$periodeMulai, $periodeAkhir])
->sum('durasi_izin');
}
/**
* Get detail kuota santri
*/
public static function getSisaKuotaSantri($idSantri)
{
$settings = self::getSettings();
$totalTerpakai = self::getTotalHariIzinSantri(
$idSantri,
$settings->periode_mulai,
$settings->periode_akhir
);
$sisaKuota = $settings->kuota_maksimal - $totalTerpakai;
$persentase = $settings->kuota_maksimal > 0 ?
($totalTerpakai / $settings->kuota_maksimal) * 100 : 0;
// Tentukan status berdasarkan persentase
$status = 'aman'; // 0-80%
if ($persentase >= 80 && $persentase < 100) {
$status = 'hampir_habis'; // 80-100%
} elseif ($persentase >= 100) {
$status = 'melebihi'; // >100%
}
// Warna badge
$badgeColor = 'success'; // Hijau
if ($persentase >= 80 && $persentase < 100) {
$badgeColor = 'warning'; // Kuning
} elseif ($persentase >= 100) {
$badgeColor = 'danger'; // Merah
}
return [
'kuota_maksimal' => $settings->kuota_maksimal,
'total_terpakai' => $totalTerpakai,
'sisa_kuota' => max(0, $sisaKuota),
'persentase' => round($persentase, 1),
'status' => $status,
'badge_color' => $badgeColor,
'periode_mulai' => $settings->periode_mulai->format('d M Y'),
'periode_akhir' => $settings->periode_akhir->format('d M Y'),
'terakhir_reset' => $settings->terakhir_reset ?
$settings->terakhir_reset->format('d M Y') : '-',
];
}
/**
* Check apakah santri over limit
*/
public static function isOverLimit($idSantri)
{
$kuota = self::getSisaKuotaSantri($idSantri);
return $kuota['status'] === 'melebihi';
}
/**
* Get list santri yang over limit
*/
public static function getSantriOverLimit()
{
$settings = self::getSettings();
$santriIds = Santri::where('status', 'Aktif')->pluck('id_santri');
$overLimitList = [];
foreach ($santriIds as $idSantri) {
$totalHari = self::getTotalHariIzinSantri(
$idSantri,
$settings->periode_mulai,
$settings->periode_akhir
);
if ($totalHari > $settings->kuota_maksimal) {
$overLimitList[$idSantri] = $totalHari;
}
}
return $overLimitList;
}
/**
* ========================================
* FITUR RESET KUOTA
* ========================================
*/
/**
* Reset kuota untuk satu santri
*/
public static function resetKuotaSantri($idSantri, $resetBy, $catatan = null)
{
$settings = self::getSettings();
// Hitung total hari sebelum reset
$totalHariSebelumReset = self::getTotalHariIzinSantri(
$idSantri,
$settings->periode_mulai,
$settings->periode_akhir
);
// Catat log reset
DB::table('kepulangan_reset_logs')->insert([
'id_santri' => $idSantri,
'total_hari_sebelum_reset' => $totalHariSebelumReset,
'periode_mulai' => $settings->periode_mulai,
'periode_akhir' => $settings->periode_akhir,
'kuota_tahunan' => $settings->kuota_maksimal,
'jenis_reset' => 'individual',
'reset_by' => $resetBy,
'catatan' => $catatan,
'created_at' => now(),
'updated_at' => now(),
]);
// Update semua kepulangan santri yang Disetujui menjadi Selesai
// Ini cara "reset" dengan menandai semua izin lama sebagai selesai
self::where('id_santri', $idSantri)
->where('status', 'Disetujui')
->whereBetween('tanggal_pulang', [$settings->periode_mulai, $settings->periode_akhir])
->update(['status' => 'Selesai']);
return [
'success' => true,
'total_hari_direset' => $totalHariSebelumReset,
];
}
/**
* Reset kuota untuk semua santri
*/
public static function resetKuotaSemuaSantri($resetBy, $catatan = null)
{
$settings = self::getSettings();
// Get semua santri aktif
$santriIds = Santri::where('status', 'Aktif')->pluck('id_santri');
$totalSantri = $santriIds->count();
$totalHariDireset = 0;
foreach ($santriIds as $idSantri) {
$totalHari = self::getTotalHariIzinSantri(
$idSantri,
$settings->periode_mulai,
$settings->periode_akhir
);
$totalHariDireset += $totalHari;
}
// Catat log reset massal
DB::table('kepulangan_reset_logs')->insert([
'id_santri' => null, // Null untuk reset massal
'total_hari_sebelum_reset' => $totalHariDireset,
'periode_mulai' => $settings->periode_mulai,
'periode_akhir' => $settings->periode_akhir,
'kuota_tahunan' => $settings->kuota_maksimal,
'jenis_reset' => 'massal',
'reset_by' => $resetBy,
'catatan' => $catatan ?? "Reset massal untuk {$totalSantri} santri",
'created_at' => now(),
'updated_at' => now(),
]);
// Update semua kepulangan yang Disetujui menjadi Selesai
$jumlahDiupdate = self::whereIn('id_santri', $santriIds)
->where('status', 'Disetujui')
->whereBetween('tanggal_pulang', [$settings->periode_mulai, $settings->periode_akhir])
->update(['status' => 'Selesai']);
// Update tanggal terakhir reset di settings
DB::table('kepulangan_settings')
->where('id', $settings->id)
->update([
'terakhir_reset' => now(),
'reset_by' => $resetBy,
'updated_at' => now(),
]);
return [
'success' => true,
'total_santri' => $totalSantri,
'total_hari_direset' => $totalHariDireset,
'jumlah_izin_diupdate' => $jumlahDiupdate,
];
}
/**
* Get history reset logs
*/
public static function getResetLogs($limit = 20)
{
return DB::table('kepulangan_reset_logs')
->leftJoin('santris', 'kepulangan_reset_logs.id_santri', '=', 'santris.id_santri')
->select(
'kepulangan_reset_logs.*',
'santris.nama_lengkap'
)
->orderBy('kepulangan_reset_logs.created_at', 'desc')
->limit($limit)
->get();
}
}