Initial commit: Proyek HRIS Laravel

This commit is contained in:
jouel88 2026-03-19 21:16:06 +07:00
commit cc98e0761e
268 changed files with 34318 additions and 0 deletions

18
.editorconfig Normal file
View File

@ -0,0 +1,18 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[compose.yaml]
indent_size = 4

65
.env.example Normal file
View File

@ -0,0 +1,65 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database
# PHP_CLI_SERVER_WORKERS=4
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
# CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

11
.gitattributes vendored Normal file
View File

@ -0,0 +1,11 @@
* text=auto eol=lf
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore

12
.github/workflows/issues.yml vendored Normal file
View File

@ -0,0 +1,12 @@
name: Issues
on:
issues:
types: [labeled]
permissions:
issues: write
jobs:
help-wanted:
uses: laravel/.github/.github/workflows/issues.yml@main

12
.github/workflows/pull-requests.yml vendored Normal file
View File

@ -0,0 +1,12 @@
name: Pull Requests
on:
pull_request_target:
types: [opened]
permissions:
pull-requests: write
jobs:
uneditable:
uses: laravel/.github/.github/workflows/pull-requests.yml@main

47
.github/workflows/tests.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: Tests
on:
push:
branches:
- master
- '*.x'
pull_request:
schedule:
- cron: '0 0 * * *'
permissions:
contents: read
jobs:
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
php: [8.2, 8.3, 8.4]
name: PHP ${{ matrix.php }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite
coverage: none
- name: Install Composer dependencies
run: composer install --prefer-dist --no-interaction --no-progress
- name: Copy environment file
run: cp .env.example .env
- name: Generate app key
run: php artisan key:generate
- name: Execute tests
run: php artisan test

13
.github/workflows/update-changelog.yml vendored Normal file
View File

@ -0,0 +1,13 @@
name: Update Changelog
on:
release:
types: [released]
permissions: {}
jobs:
update:
permissions:
contents: write
uses: laravel/.github/.github/workflows/update-changelog.yml@main

27
.gitignore vendored Normal file
View File

@ -0,0 +1,27 @@
*.log
.DS_Store
.env
.env.backup
.env.production
.phpactor.json
.phpunit.result.cache
/.fleet
/.idea
/.nova
/.phpunit.cache
/.vscode
/.zed
/auth.json
/node_modules
/public/build
/public/hot
/public/storage
/storage/*.key
/storage/pail
/vendor
Homestead.json
Homestead.yaml
Thumbs.db
_ide_helper.php
.phpstorm.meta.php

753
_ide_helper_models.php Normal file
View File

@ -0,0 +1,753 @@
<?php
// @formatter:off
// phpcs:ignoreFile
/**
* A helper file for your Eloquent Models
* Copy the phpDocs from this file to the correct Model,
* And remove them from this file, to prevent double declarations.
*
* @author Barry vd. Heuvel <barryvdh@gmail.com>
*/
namespace App\Models{
/**
* @property string $id_approval
* @property string $id_surat
* @property int $id_approver
* @property string|null $id_ttd_approver
* @property int $tahap 1=Manajer, 2=HRD
* @property string $status
* @property string|null $catatan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\User $approver
* @property-read \App\Models\SuratIzin $suratIzin
* @property-read \App\Models\TandaTangan|null $tandaTanganApprover
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereCatatan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereIdApproval($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereIdApprover($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereIdSurat($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereIdTtdApprover($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereTahap($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereUpdatedAt($value)
* @mixin \Eloquent
*/
class ApprovalSurat extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id
* @property int $id_user
* @property string|null $path_model_yml
* @property int $is_verified 0:Pending, 1:Approved, 2:Rejected
* @property string|null $last_updated
* @property string|null $encoding_wajah
* @property string $tanggal_latih
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\User $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereEncodingWajah($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereIsVerified($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereLastUpdated($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah wherePathModelYml($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereTanggalLatih($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereUpdatedAt($value)
* @mixin \Eloquent
*/
class DataWajah extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id
* @property string $id_penggunaan
* @property int $id_poin_sumber
* @property int $jumlah_diambil
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\PenggunaanPoin $penggunaan
* @property-read \App\Models\PoinLembur $poinSumber
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin whereIdPenggunaan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin whereIdPoinSumber($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin whereJumlahDiambil($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin whereUpdatedAt($value)
* @mixin \Eloquent
*/
class DetailPenggunaanPoin extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_divisi
* @property string $nama_divisi
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi whereIdDivisi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi whereNamaDivisi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Divisi extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id
* @property string $tanggal
* @property string $keterangan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|HariLibur newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|HariLibur newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|HariLibur query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|HariLibur whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|HariLibur whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|HariLibur whereKeterangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|HariLibur whereTanggal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|HariLibur whereUpdatedAt($value)
* @mixin \Eloquent
*/
class HariLibur extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_jabatan
* @property string $nama_jabatan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan whereIdJabatan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan whereNamaJabatan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Jabatan extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_jadwal
* @property int|null $id_user
* @property string|null $tanggal
* @property int|null $id_shift
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\PenggunaanPoin|null $penggunaanPoin
* @property-read \App\Models\ShiftKerja|null $shift
* @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja whereIdJadwal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja whereIdShift($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja whereTanggal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja whereUpdatedAt($value)
* @mixin \Eloquent
*/
class JadwalKerja extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_jenis_izin
* @property string $nama_izin
* @property string|null $created_at
* @property string|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin whereIdJenisIzin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin whereNamaIzin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin whereUpdatedAt($value)
* @mixin \Eloquent
*/
class JenisIzin extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_kompensasi
* @property string $nama_kompensasi
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi whereIdKompensasi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi whereNamaKompensasi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi whereUpdatedAt($value)
* @mixin \Eloquent
*/
class JenisKompensasi extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_pengurangan
* @property string $nama_pengurangan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\PenggunaanPoin> $penggunaanPoin
* @property-read int|null $penggunaan_poin_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan whereIdPengurangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan whereNamaPengurangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan whereUpdatedAt($value)
* @mixin \Eloquent
*/
class JenisPengurangan extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_kantor
* @property string $nama_kantor
* @property string|null $alamat
* @property numeric|null $latitude
* @property numeric|null $longitude
* @property int $radius
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\User> $users
* @property-read int|null $users_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereAlamat($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereIdKantor($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereLatitude($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereLongitude($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereNamaKantor($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereRadius($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Kantor extends \Eloquent {}
}
namespace App\Models{
/**
* @property string $id_lembur
* @property int|null $id_user
* @property \Illuminate\Support\Carbon $tanggal_lembur
* @property string $jam_mulai
* @property string $jam_selesai
* @property int|null $durasi_menit
* @property string|null $keterangan
* @property int|null $jumlah_poin
* @property int|null $id_kompensasi
* @property int $id_status
* @property string|null $alasan_penolakan
* @property \Illuminate\Support\Carbon $tanggal_diajukan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\JenisKompensasi|null $kompensasi
* @property-read \App\Models\StatusPengajuan $status
* @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereAlasanPenolakan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereDurasiMenit($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereIdKompensasi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereIdLembur($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereIdStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereJamMulai($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereJamSelesai($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereJumlahPoin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereKeterangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereTanggalDiajukan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereTanggalLembur($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Lembur extends \Eloquent {}
}
namespace App\Models{
/**
* @property string $id_izin
* @property int|null $id_user
* @property string $tanggal_mulai
* @property string $tanggal_selesai
* @property int|null $id_jenis_izin
* @property string|null $alasan
* @property string|null $bukti_file
* @property int $id_status
* @property string|null $alasan_penolakan
* @property string $tanggal_diajukan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\JenisIzin|null $jenisIzin
* @property-read \App\Models\StatusPengajuan $statusPengajuan
* @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereAlasan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereAlasanPenolakan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereBuktiFile($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereIdIzin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereIdJenisIzin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereIdStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereTanggalDiajukan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereTanggalMulai($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereTanggalSelesai($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereUpdatedAt($value)
* @mixin \Eloquent
*/
class PengajuanIzin extends \Eloquent {}
}
namespace App\Models{
/**
* @property string $id_penggunaan
* @property int|null $id_user
* @property \Illuminate\Support\Carbon $tanggal_penggunaan
* @property int|null $jumlah_poin
* @property string|null $jam_masuk_custom
* @property string|null $jam_pulang_custom
* @property int|null $id_pengurangan
* @property int $id_status
* @property string|null $alasan_penolakan
* @property \Illuminate\Support\Carbon $tanggal_diajukan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\JenisPengurangan|null $jenisPengurangan
* @property-read \App\Models\StatusPengajuan $status
* @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereAlasanPenolakan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereIdPenggunaan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereIdPengurangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereIdStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereJamMasukCustom($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereJamPulangCustom($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereJumlahPoin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereTanggalDiajukan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereTanggalPenggunaan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereUpdatedAt($value)
* @mixin \Eloquent
*/
class PenggunaanPoin extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_pengumuman
* @property string|null $judul
* @property string|null $isi
* @property \Illuminate\Support\Carbon|null $tanggal
* @property int|null $dibuat_oleh
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\User|null $pembuat
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereDibuatOleh($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereIdPengumuman($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereIsi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereJudul($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereTanggal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Pengumuman extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_permission
* @property string $nama_permission
* @property string $slug
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Role> $roles
* @property-read int|null $roles_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission whereIdPermission($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission whereNamaPermission($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Permission extends \Eloquent {}
}
namespace App\Models{
/**
* @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|Poin newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Poin newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Poin query()
* @mixin \Eloquent
*/
class Poin extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_poin
* @property int|null $id_user
* @property int|null $jumlah_poin
* @property int|null $sisa_poin
* @property string|null $id_lembur
* @property string|null $keterangan
* @property \Illuminate\Support\Carbon $tanggal
* @property \Illuminate\Support\Carbon|null $expired_at
* @property bool|null $is_fully_used
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\Lembur|null $lembur
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereExpiredAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereIdLembur($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereIdPoin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereIsFullyUsed($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereJumlahPoin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereKeterangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereSisaPoin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereTanggal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereUpdatedAt($value)
* @mixin \Eloquent
*/
class PoinLembur extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_presensi
* @property int|null $id_user
* @property string $tanggal
* @property string|null $jam_masuk
* @property string|null $jam_pulang
* @property numeric|null $lat_masuk
* @property numeric|null $lon_masuk
* @property numeric|null $lat_pulang
* @property numeric|null $lon_pulang
* @property string|null $foto_wajah_masuk
* @property string|null $foto_wajah_pulang
* @property int|null $id_status
* @property string|null $alasan_telat
* @property string|null $keterangan_pulang
* @property string|null $waktu_terlambat
* @property string|null $waktu_masuk_awal
* @property string|null $waktu_pulang_awal
* @property string|null $waktu_pulang_akhir
* @property int $verifikasi_wajah
* @property int $id_validasi
* @property string|null $keterangan_luar_radius
* @property string|null $alasan_penolakan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string|null $deleted_at
* @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereAlasanPenolakan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereAlasanTelat($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereDeletedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereFotoWajahMasuk($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereFotoWajahPulang($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereIdPresensi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereIdStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereIdValidasi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereJamMasuk($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereJamPulang($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereKeteranganLuarRadius($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereKeteranganPulang($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereLatMasuk($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereLatPulang($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereLonMasuk($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereLonPulang($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereTanggal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereVerifikasiWajah($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereWaktuMasukAwal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereWaktuPulangAkhir($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereWaktuPulangAwal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereWaktuTerlambat($value)
* @mixin \Eloquent
*/
class Presensi extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_riwayat
* @property int $id_user_1
* @property int $id_jadwal_1
* @property int $id_user_2
* @property int $id_jadwal_2
* @property string|null $keterangan
* @property int $created_by
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\User $execAdmin
* @property-read \App\Models\JadwalKerja $jadwal1
* @property-read \App\Models\JadwalKerja $jadwal2
* @property-read \App\Models\User $user1
* @property-read \App\Models\User $user2
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereCreatedBy($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereIdJadwal1($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereIdJadwal2($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereIdRiwayat($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereIdUser1($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereIdUser2($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereKeterangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereUpdatedAt($value)
* @mixin \Eloquent
*/
class RiwayatTukarShift extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_role
* @property string $nama_role
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Permission> $permissions
* @property-read int|null $permissions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\User> $users
* @property-read int|null $users_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role whereIdRole($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role whereNamaRole($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Role extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_shift
* @property string|null $nama_shift
* @property string|null $jam_mulai
* @property string|null $jam_selesai
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\JadwalKerja> $jadwalKerja
* @property-read int|null $jadwal_kerja_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja whereIdShift($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja whereJamMulai($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja whereJamSelesai($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja whereNamaShift($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja whereUpdatedAt($value)
* @mixin \Eloquent
*/
class ShiftKerja extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id_status
* @property string $nama_status
* @property string|null $created_at
* @property string|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan whereIdStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan whereNamaStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan whereUpdatedAt($value)
* @mixin \Eloquent
*/
class StatusPengajuan extends \Eloquent {}
}
namespace App\Models{
/**
* @property string $id_surat
* @property string $id_izin
* @property int $id_user
* @property string $nomor_surat
* @property string $isi_surat
* @property string|null $id_ttd_pengaju
* @property string $status_surat
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\ApprovalSurat|null $approvalHrd
* @property-read \App\Models\ApprovalSurat|null $approvalManajer
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ApprovalSurat> $approvals
* @property-read int|null $approvals_count
* @property-read \App\Models\PengajuanIzin $pengajuanIzin
* @property-read \App\Models\TandaTangan|null $tandaTanganPengaju
* @property-read \App\Models\User $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|SuratIzin newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|SuratIzin newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|SuratIzin query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|SuratIzin whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|SuratIzin whereIdIzin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|SuratIzin whereIdSurat($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|SuratIzin whereIdTtdPengaju($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|SuratIzin whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|SuratIzin whereIsiSurat($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|SuratIzin whereNomorSurat($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|SuratIzin whereStatusSurat($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|SuratIzin whereUpdatedAt($value)
* @mixin \Eloquent
*/
class SuratIzin extends \Eloquent {}
}
namespace App\Models{
/**
* @property string $id_tanda_tangan
* @property int $id_user
* @property string $file_ttd
* @property int $is_active
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\User $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|TandaTangan active()
* @method static \Illuminate\Database\Eloquent\Builder<static>|TandaTangan newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|TandaTangan newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|TandaTangan query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|TandaTangan whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|TandaTangan whereFileTtd($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|TandaTangan whereIdTandaTangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|TandaTangan whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|TandaTangan whereIsActive($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|TandaTangan whereUpdatedAt($value)
* @mixin \Eloquent
*/
class TandaTangan extends \Eloquent {}
}
namespace App\Models{
/**
* @property int $id
* @property string|null $nik
* @property string $nama_lengkap
* @property string $email
* @property string|null $no_telp
* @property string|null $alamat
* @property string|null $email_verified_at
* @property string $password
* @property int $sisa_cuti
* @property string|null $foto
* @property string|null $remember_token
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property int|null $id_kantor
* @property int|null $id_divisi
* @property int|null $id_jabatan
* @property int $status_aktif
* @property string|null $tgl_bergabung
* @property int $is_face_registered
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property-read \App\Models\DataWajah|null $dataWajah
* @property-read \App\Models\Divisi|null $divisi
* @property-read \App\Models\Jabatan|null $jabatan
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\JadwalKerja> $jadwalKerja
* @property-read int|null $jadwal_kerja_count
* @property-read \App\Models\Kantor|null $kantor
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Lembur> $lemburs
* @property-read int|null $lemburs_count
* @property-read \Illuminate\Notifications\DatabaseNotificationCollection<int, \Illuminate\Notifications\DatabaseNotification> $notifications
* @property-read int|null $notifications_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Role> $roles
* @property-read int|null $roles_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Laravel\Sanctum\PersonalAccessToken> $tokens
* @property-read int|null $tokens_count
* @method static \Database\Factories\UserFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User onlyTrashed()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereAlamat($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereDeletedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmailVerifiedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereFoto($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereIdDivisi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereIdJabatan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereIdKantor($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereIsFaceRegistered($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereNamaLengkap($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereNik($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereNoTelp($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User wherePassword($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereRememberToken($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereSisaCuti($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereStatusAktif($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereTglBergabung($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User withTrashed(bool $withTrashed = true)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User withoutTrashed()
* @mixin \Eloquent
*/
class User extends \Eloquent {}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class AccrueAnnualLeave extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:accrue-annual-leave';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Increment annual leave (sisa_cuti) for all active employees';
public function handle()
{
$this->info('Starting annual leave accrual process...');
$activeUsers = \App\Models\User::where('status_aktif', '=', 1)->get();
$count = 0;
foreach ($activeUsers as $user) {
$user->increment('sisa_cuti', 12);
$count++;
}
$this->success("Successfully accrued leave for {$count} active employees.");
}
}

View File

@ -0,0 +1,79 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\User;
use App\Models\Presensi;
use App\Models\JadwalKerja;
use Carbon\Carbon;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\DB;
class AutoPresensiJob extends Command
{
/**
* Nama dan signature dari perintah console.
*
* @var string
*/
protected $signature = 'presensi:auto-alpha';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Otomatis set Alpha untuk pegawai yang tidak absen & hapus foto lama';
public function handle()
{
$hariIni = Carbon::today()->format('Y-m-d');
$usersWithJadwal = JadwalKerja::where('tanggal', $hariIni)
->with('user')
->get()
->pluck('id_user');
$usersPresensi = Presensi::where('tanggal', $hariIni)->pluck('id_user');
$usersAlpha = $usersWithJadwal->diff($usersPresensi);
$idAlpha = DB::table('status_presensi')->where('nama_status', 'Alpha')->value('id_status') ?? 0;
foreach ($usersAlpha as $userId) {
Presensi::create([
'id_user' => $userId,
'tanggal' => $hariIni,
'id_status' => $idAlpha,
'id_validasi' => 1,
'alasan_telat' => 'Auto Alpha by System'
]);
}
$this->info('Auto Alpha processed for ' . $usersAlpha->count() . ' users.');
$dateLimit = Carbon::now()->subMonths(3);
$oldPresensi = Presensi::where('tanggal', '<', $dateLimit)->get();
$countDeleted = 0;
foreach ($oldPresensi as $p) {
if ($p->foto_wajah_masuk && Storage::disk('public')->exists($p->foto_wajah_masuk)) {
Storage::disk('public')->delete($p->foto_wajah_masuk);
$p->foto_wajah_masuk = null;
}
if ($p->foto_wajah_pulang && Storage::disk('public')->exists($p->foto_wajah_pulang)) {
Storage::disk('public')->delete($p->foto_wajah_pulang);
$p->foto_wajah_pulang = null;
}
$p->save();
$countDeleted++;
}
$this->info("Cleanup photos completed. Processed $countDeleted records.");
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class LaporanLemburExport implements FromCollection, WithHeadings, WithMapping, ShouldAutoSize, WithStyles
{
protected $pegawai;
protected $rekap;
protected $bulan;
protected $tahun;
public function __construct($pegawai, array $rekap, $bulan, $tahun)
{
$this->pegawai = $pegawai;
$this->rekap = $rekap;
$this->bulan = $bulan;
$this->tahun = $tahun;
}
public function collection()
{
return collect($this->pegawai);
}
public function headings(): array
{
return [
['LAPORAN REKAPITULASI LEMBUR PEGAWAI'],
['Periode: ' . \Carbon\Carbon::create()->month($this->bulan)->translatedFormat('F') . ' ' . $this->tahun],
[],
[
'No',
'NIK',
'Nama Lengkap',
'Divisi / Dept',
'Jabatan',
'Jumlah Hari Lembur',
'Total Waktu (Menit)',
'Durasi Jam',
'Poin Lembur Diperoleh'
]
];
}
public function map($row): array
{
static $no = 0;
$no++;
$dataRekap = $this->rekap[$row->id] ?? [
'total_menit' => 0,
'format_jam' => '0j 0m',
'jumlah_hari' => 0,
'poin_diperoleh' => 0,
];
return [
$no,
$row->nik ?? '-',
$row->nama_lengkap,
$row->divisi->nama_divisi ?? '-',
$row->jabatan->nama_jabatan ?? '-',
$dataRekap['jumlah_hari'] . ' Hari',
$dataRekap['total_menit'] . ' Menit',
$dataRekap['format_jam'],
$dataRekap['poin_diperoleh'] . ' Poin'
];
}
public function styles(Worksheet $sheet)
{
return [
1 => ['font' => ['bold' => true, 'size' => 14]],
2 => ['font' => ['italic' => true]],
4 => ['font' => ['bold' => true], 'fill' => ['fillType' => 'solid', 'color' => ['rgb' => 'E6F4EA']]],
];
}
}

View File

@ -0,0 +1,81 @@
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class LaporanPresensiExport implements FromCollection, WithHeadings, WithMapping, ShouldAutoSize, WithStyles
{
protected $rekap;
protected $bulan;
protected $tahun;
public function __construct(array $rekap, $bulan, $tahun)
{
$this->rekap = $rekap;
$this->bulan = $bulan;
$this->tahun = $tahun;
}
public function collection()
{
return collect($this->rekap);
}
public function headings(): array
{
return [
['LAPORAN REKAPITULASI PRESENSI PEGAWAI'],
['Periode: ' . \Carbon\Carbon::createFromDate($this->tahun, (int) $this->bulan, 1)->translatedFormat('F') . ' ' . $this->tahun],
[],
[
'No',
'NIK',
'Nama Lengkap',
'Jabatan',
'Divisi',
'Hadir',
'Izin / Cuti',
'Sakit',
'Alpha (Mangkir)',
'Terlambat',
'Poin Lembur Diperoleh'
]
];
}
public function map($row): array
{
static $no = 0;
$no++;
return [
$no,
$row['user']->nik ?? '-',
$row['user']->nama_lengkap,
$row['user']->jabatan->nama_jabatan ?? '-',
$row['user']->divisi->nama_divisi ?? '-',
$row['hadir'] . ' Hari',
$row['izin'] . ' Hari',
$row['sakit'] . ' Hari',
$row['alpha'] . ' Hari',
$row['terlambat'] . ' Kali',
$row['poin_lembur'] . ' Poin'
];
}
public function styles(Worksheet $sheet)
{
return [
1 => ['font' => ['bold' => true, 'size' => 14]],
2 => ['font' => ['italic' => true]],
4 => ['font' => ['bold' => true], 'fill' => ['fillType' => 'solid', 'color' => ['rgb' => 'E2E8F0']]],
];
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace App\Helpers;
class ApiResponse
{
public static function success($data = null, string $message = 'Berhasil', int $code = 200)
{
return response()->json([
'success' => true,
'message' => $message,
'data' => $data,
], $code);
}
public static function error(string $message = 'Terjadi kesalahan', int $code = 400, $errors = null)
{
$response = [
'success' => false,
'message' => $message,
];
if ($errors) {
$response['errors'] = $errors;
}
return response()->json($response, $code);
}
public static function validationError($errors, string $message = 'Validasi gagal')
{
return self::error($message, 422, $errors);
}
public static function notFound(string $message = 'Data tidak ditemukan')
{
return self::error($message, 404);
}
public static function unauthorized(string $message = 'Tidak memiliki akses')
{
return self::error($message, 401);
}
}

View File

@ -0,0 +1,148 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
class AuthController extends Controller
{
/**
* @OA\Post(
* path="/api/login",
* tags={"Authentication"},
* summary="User login",
* description="Login user and return token",
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* required={"email","password"},
* @OA\Property(property="email", type="string", format="email", example="pegawai@mpg.com"),
* @OA\Property(property="password", type="string", format="password", example="password")
* )
* ),
* @OA\Response(
* response=200,
* description="Login successful",
* @OA\JsonContent(
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="Login berhasil"),
* @OA\Property(property="data", type="object",
* @OA\Property(property="token", type="string"),
* @OA\Property(property="user", type="object")
* )
* )
* ),
* @OA\Response(
* response=401,
* description="Unauthorized",
* @OA\JsonContent(
* @OA\Property(property="success", type="boolean", example=false),
* @OA\Property(property="message", type="string", example="Email atau Password salah")
* )
* )
* )
*/
public function login(Request $request)
{
try {
$validate = Validator::make($request->all(), [
'email' => 'required|email',
'password' => 'required',
]);
if ($validate->fails()) {
return ApiResponse::validationError($validate->errors());
}
if (!Auth::attempt($request->only('email', 'password'))) {
return ApiResponse::unauthorized('Email atau Password salah');
}
$user = User::where('email', $request->email)
->with(['divisi', 'jabatan', 'kantor', 'roles'])
->first();
if ($user->status_aktif != 1) {
return ApiResponse::error('Akun Anda dinonaktifkan. Hubungi admin.', 403);
}
$token = $user->createToken('auth_token')->plainTextToken;
return ApiResponse::success([
'token' => $token,
'user' => $user
], 'Login berhasil');
} catch (\Exception $e) {
\Illuminate\Support\Facades\Log::error('Login Error: ' . $e->getMessage());
return ApiResponse::error('Terjadi kesalahan server: ' . $e->getMessage(), 500);
}
}
public function logout(Request $request)
{
try {
/** @var \Laravel\Sanctum\PersonalAccessToken $token */
$token = $request->user()->currentAccessToken();
$token->delete();
return ApiResponse::success(null, 'Logout berhasil');
} catch (\Exception $e) {
return ApiResponse::error('Gagal logout: ' . $e->getMessage(), 500);
}
}
public function updateProfile(Request $request)
{
try {
$user = $request->user();
$validate = Validator::make($request->all(), [
'nama_lengkap' => 'required|string|max:255',
'no_telp' => 'nullable|string|max:15',
'password' => 'nullable|min:6',
'foto' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
]);
if ($validate->fails()) {
return ApiResponse::validationError($validate->errors());
}
$data = [
'nama_lengkap' => $request->nama_lengkap,
'no_telp' => $request->no_telp,
];
if ($request->filled('password')) {
$data['password'] = Hash::make($request->password);
}
if ($request->hasFile('foto')) {
if ($user->foto) {
Storage::disk('public')->delete($user->foto);
}
$data['foto'] = $request->file('foto')->store('foto-profil', 'public');
}
$user->update($data);
return ApiResponse::success($user, 'Profil berhasil diperbarui');
} catch (\Exception $e) {
return ApiResponse::error('Gagal update profil: ' . $e->getMessage(), 500);
}
}
public function user(Request $request)
{
$user = $request->user()->load(['roles', 'divisi', 'jabatan', 'kantor']);
return ApiResponse::success($user);
}
}

View File

@ -0,0 +1,141 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Models\JadwalKerja;
use App\Models\Presensi;
use App\Models\PenggunaanPoin;
use App\Services\PoinService;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
class DashboardController extends Controller
{
public function index()
{
try {
$user = Auth::user()->load(['divisi', 'jabatan', 'kantor']);
$today = Carbon::now('Asia/Jakarta')->toDateString();
$jadwal = JadwalKerja::with(['shift'])
->where('id_user', $user->id)
->where('tanggal', $today)
->first();
$presensi = Presensi::where('id_user', $user->id)
->where('tanggal', $today)
->first();
$totalPoin = (new PoinService())->getActivePoints($user->id);
$jamMasuk = $jadwal ? $jadwal->shift->jam_mulai : '-';
$jamPulang = $jadwal ? $jadwal->shift->jam_selesai : '-';
$isAdjusted = false;
$adjustmentNote = null;
$statusJadwal = 'Normal';
if ($jadwal) {
$poinOverrides = PenggunaanPoin::where('id_user', $user->id)
->where('tanggal_penggunaan', $today)
->where('id_status', 2)
->get();
foreach ($poinOverrides as $poin) {
if ($poin->id_pengurangan == 4 && $poin->jam_masuk_custom) {
$jamMasuk = substr($poin->jam_masuk_custom, 0, 5) . ':00';
$isAdjusted = true;
$adjustmentNote = "Datang Siang (Via Poin)";
$statusJadwal = 'Masuk Siang';
}
if ($poin->id_pengurangan == 5 && $poin->jam_pulang_custom) {
$jamPulang = substr($poin->jam_pulang_custom, 0, 5) . ':00';
$isAdjusted = true;
$adjustmentNote = $adjustmentNote ? "Masuk Siang & Pulang Cepat" : "Pulang Cepat (Via Poin)";
$statusJadwal = $statusJadwal == 'Masuk Siang' ? 'Full Custom' : 'Pulang Cepat';
}
}
}
$ketMasuk = 'Belum Absen';
$ketPulang = null;
if ($presensi) {
if ($presensi->waktu_terlambat) {
$ketMasuk = 'Terlambat';
} else if ($presensi->waktu_masuk_awal) {
$ketMasuk = (str_contains($statusJadwal, 'Poin')) ? 'Masuk Siang (Poin) - Datang Awal' : 'Datang Awal';
} else {
$ketMasuk = (str_contains($statusJadwal, 'Poin')) ? 'Masuk Siang (Poin)' : 'Tepat Waktu';
}
if ($presensi->jam_pulang) {
if ($presensi->waktu_pulang_awal) {
$ketPulang = 'Pulang Awal';
} else if ($presensi->waktu_pulang_akhir) {
$ketPulang = (str_contains($statusJadwal, 'Poin')) ? 'Pulang Cepat (Poin) - Lembur' : 'Lembur / Pulang Akhir';
} else {
$ketPulang = (str_contains($statusJadwal, 'Poin')) ? 'Pulang Cepat (Poin)' : 'Tepat Waktu';
}
}
}
$dashboardData = [
'user' => [
'id' => $user->id,
'nama_lengkap' => $user->nama_lengkap,
'email' => $user->email,
'foto' => $user->foto,
'jabatan' => $user->jabatan->nama_jabatan ?? 'N/A',
'divisi' => $user->divisi->nama_divisi ?? 'N/A',
'sisa_cuti' => (int) ($user->sisa_cuti ?? 0),
'status_aktif' => $user->status_aktif ?? 'Aktif',
],
'poin' => (int) $totalPoin,
'statistik_absensi' => [
'izin' => Presensi::where('id_user', $user->id)
->whereMonth('tanggal', now()->month)
->whereYear('tanggal', now()->year)
->where('id_status', 3)
->count(),
'alpha' => Presensi::where('id_user', $user->id)
->whereMonth('tanggal', now()->month)
->whereYear('tanggal', now()->year)
->where('id_status', 5)
->count(),
],
'jadwal_hari_ini' => $jadwal ? [
'hari' => Carbon::parse($today, 'Asia/Jakarta')->translatedFormat('l, d F Y'),
'shift' => $jadwal->shift->nama_shift ?? '-',
'jam_masuk' => $jamMasuk,
'jam_pulang' => $jamPulang,
'is_adjusted' => $isAdjusted,
'note' => $adjustmentNote,
'status_jadwal' => $statusJadwal,
'kantor_nama' => $user->kantor->nama_kantor ?? '-',
'kantor_lat' => (float) ($user->kantor->latitude ?? 0),
'kantor_lon' => (float) ($user->kantor->longitude ?? 0),
'kantor_radius' => (float) ($user->kantor->radius ?? 200),
] : null,
'presensi_hari_ini' => $presensi ? [
'jam_masuk' => $presensi->jam_masuk,
'jam_pulang' => $presensi->jam_pulang,
'status' => $presensi->id_status,
'keterangan' => $ketMasuk,
'keterangan_pulang' => $ketPulang,
] : null,
];
return ApiResponse::success($dashboardData, 'Dashboard data berhasil dimuat');
} catch (\Exception $e) {
return ApiResponse::error('Gagal memuat dashboard: ' . $e->getMessage(), 500);
}
}
}

View File

@ -0,0 +1,115 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Services\FaceRecognitionService;
use App\Helpers\ApiResponse;
use Illuminate\Support\Facades\Log;
class FaceEnrollmentController extends Controller
{
protected $faceService;
public function __construct(FaceRecognitionService $faceService)
{
$this->faceService = $faceService;
}
public function enrollFace(Request $request)
{
$validator = \Validator::make($request->all(), [
'foto_depan' => 'required|image|mimes:jpeg,png,jpg|max:5120',
'foto_kanan' => 'required|image|mimes:jpeg,png,jpg|max:5120',
'foto_kiri' => 'required|image|mimes:jpeg,png,jpg|max:5120',
'foto_bawah' => 'required|image|mimes:jpeg,png,jpg|max:5120',
]);
if ($validator->fails()) {
return response()->json([
'success' => false,
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
try {
$user = $request->user();
$photos = [
'depan' => $request->file('foto_depan'),
'kanan' => $request->file('foto_kanan'),
'kiri' => $request->file('foto_kiri'),
'bawah' => $request->file('foto_bawah'),
];
$result = $this->faceService->enrollFace($user->id, $photos);
return response()->json([
'success' => true,
'message' => $result['message'],
'data' => null
], 200);
} catch (\Exception $e) {
Log::error('Face Enrollment Error: ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'Terjadi kesalahan sistem: ' . $e->getMessage()
], 500);
}
}
public function getFaceStatus(Request $request)
{
$user = $request->user();
$dataWajah = \DB::table('data_wajah')->where('id_user', $user->id)->first();
$status = 'not_registered';
if ($user->is_face_registered) {
$status = 'pending';
if ($dataWajah && $dataWajah->is_verified == 1) {
$status = 'verified';
} elseif ($dataWajah && $dataWajah->is_verified == 2) {
$status = 'rejected';
}
}
return response()->json([
'success' => true,
'data' => [
'status' => $status,
'is_registered' => (bool) $user->is_face_registered
]
]);
}
public function verifyFace(Request $request)
{
$request->validate([
'foto' => 'required|image|max:5120',
]);
try {
$user = $request->user();
$file = $request->file('foto');
$result = $this->faceService->verifyFace($user->id, $file);
return response()->json([
'success' => true,
'message' => 'Wajah terverifikasi',
'data' => $result
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Verifikasi Gagal: ' . $e->getMessage()
], 400);
}
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Helpers\ApiResponse;
use App\Models\JenisKompensasi;
class KompensasiController extends Controller
{
public function index()
{
try {
$kompensasi = JenisKompensasi::all();
return ApiResponse::success($kompensasi);
} catch (\Exception $e) {
return ApiResponse::error('Gagal memuat jenis kompensasi: ' . $e->getMessage(), 500);
}
}
}

View File

@ -0,0 +1,120 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Helpers\ApiResponse;
use App\Services\LemburService;
use App\Http\Requests\StoreLemburRequest;
use App\Models\Lembur;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LemburController extends Controller
{
protected $lemburService;
public function __construct(LemburService $lemburService)
{
$this->lemburService = $lemburService;
}
public function store(StoreLemburRequest $request)
{
try {
$user = Auth::user();
$this->lemburService->createLembur($user, $request->validated());
return ApiResponse::success(null, 'Pengajuan lembur berhasil dikirim.', 201);
} catch (\Exception $e) {
return ApiResponse::error('Gagal mengajukan lembur: ' . $e->getMessage(), 500);
}
}
/**
* @OA\Put(
* path="/api/lembur/{id}",
* tags={"Lembur"},
* summary="Update Overtime Request",
* security={{"bearerAuth":{}}},
* @OA\Parameter(
* name="id",
* in="path",
* required=true,
* @OA\Schema(type="integer")
* ),
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* required={"tanggal_lembur", "jam_mulai", "jam_selesai"},
* @OA\Property(property="tanggal_lembur", type="string", format="date", example="2023-10-25"),
* @OA\Property(property="jam_mulai", type="string", format="time", example="17:00"),
* @OA\Property(property="jam_selesai", type="string", format="time", example="20:00"),
* @OA\Property(property="keterangan", type="string", example="Updated reason"),
* @OA\Property(property="id_kompensasi", type="integer", example=1)
* )
* ),
* @OA\Response(
* response=200,
* description="Overtime request updated successfully",
* @OA\JsonContent(
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="Pengajuan lembur berhasil diperbarui.")
* )
* )
* )
*/
public function update(\App\Http\Requests\UpdateLemburRequest $request, $id)
{
try {
$lembur = Lembur::where('id_user', Auth::id())
->where('id_lembur', $id)
->where('id_status', 1)
->first();
if (!$lembur) {
return ApiResponse::error('Data lembur tidak ditemukan atau sudah diproses.', 404);
}
$lembur->update($request->validated());
return ApiResponse::success(null, 'Pengajuan lembur berhasil diperbarui.');
} catch (\Exception $e) {
return ApiResponse::error('Gagal memperbarui lembur: ' . $e->getMessage(), 500);
}
}
public function history(Request $request)
{
try {
$query = Lembur::with(['status', 'kompensasi'])
->where('id_user', Auth::id());
if ($request->has('status')) {
$statusParam = $request->get('status');
$statusId = match ($statusParam) {
'pending' => 1,
'approved' => 2,
'rejected' => 3,
default => null
};
if ($statusId) {
$query->where('id_status', $statusId);
}
}
$history = $query->orderBy('created_at', 'desc')->get();
return ApiResponse::success(['data' => $history], 'Riwayat lembur berhasil dimuat');
} catch (\Exception $e) {
return ApiResponse::error('Gagal memuat riwayat lembur: ' . $e->getMessage(), 500);
}
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Models\Pengumuman;
class PengumumanApiController extends Controller
{
public function index()
{
try {
$pengumuman = Pengumuman::with('pembuat.jabatan')
->orderBy('tanggal', 'desc')
->limit(5)
->get()
->map(function ($item) {
return [
'id' => $item->id_pengumuman,
'title' => $item->judul,
'description' => $item->isi,
'tanggal' => $item->tanggal?->format('Y-m-d'),
'jabatan' => $item->pembuat?->jabatan?->nama_jabatan ?? 'Admin',
'avatar_url' => $item->pembuat?->foto,
];
});
return ApiResponse::success($pengumuman, 'Pengumuman berhasil dimuat');
} catch (\Exception $e) {
return ApiResponse::error('Gagal memuat pengumuman: ' . $e->getMessage(), 500);
}
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Models\Poin;
use App\Models\PenggunaanPoin;
use App\Services\PoinService;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class PoinController extends Controller
{
public function getExpiringPoints()
{
try {
$userId = auth()->user()->id;
$poinService = new PoinService();
$poinExpiring = $poinService->getExpiringPoints($userId);
$activePoints = $poinService->getActivePoints($userId);
$firstExpiring = $poinExpiring->first();
$formattedExpiring = null;
if ($firstExpiring) {
$formattedExpiring = [
'poin' => (int) $firstExpiring->sisa_poin,
'tanggal_kadaluarsa' => $firstExpiring->expired_at->format('Y-m-d'),
];
}
return ApiResponse::success([
'expiring_points' => $formattedExpiring,
'total_poin' => (int) $activePoints
]);
} catch (\Exception $e) {
return ApiResponse::error('Terjadi kesalahan: ' . $e->getMessage(), 500);
}
}
public function getPointHistory()
{
try {
$userId = auth()->user()->id;
$poinService = new PoinService();
$history = $poinService->getPointHistory($userId);
return ApiResponse::success(['history' => $history]);
} catch (\Exception $e) {
return ApiResponse::error('Terjadi kesalahan: ' . $e->getMessage(), 500);
}
}
public function redeem(Request $request)
{
$validator = Validator::make($request->all(), [
'jumlah' => 'required|integer|min:1',
'keterangan' => 'required|string',
'id_pengurangan' => 'required|integer',
'jam_masuk_custom' => 'required_if:id_pengurangan,4|date_format:H:i|nullable',
'jam_pulang_custom' => 'required_if:id_pengurangan,5|date_format:H:i|nullable',
]);
if ($validator->fails()) {
return ApiResponse::validationError($validator->errors());
}
try {
$userId = auth()->user()->id;
$jumlah = $request->jumlah;
$keterangan = $request->keterangan;
$poinService = new PoinService();
$totalPoin = $poinService->getActivePoints($userId);
if ($totalPoin < $jumlah) {
return ApiResponse::error('Saldo poin tidak mencukupi', 400);
}
preg_match('/\[(\d{4}-\d{2}-\d{2})\]/', $keterangan, $matches);
$tanggalPenggunaan = isset($matches[1]) ? $matches[1] : now()->format('Y-m-d');
PenggunaanPoin::create([
'id_user' => $userId,
'tanggal_penggunaan' => $tanggalPenggunaan,
'jumlah_poin' => $jumlah,
'id_pengurangan' => $request->id_pengurangan,
'jam_masuk_custom' => $request->jam_masuk_custom,
'jam_pulang_custom' => $request->jam_pulang_custom,
'id_status' => 1,
'tanggal_diajukan' => now(),
]);
return ApiResponse::success(null, 'Pengajuan penukaran poin berhasil disimpan');
} catch (\Exception $e) {
return ApiResponse::error($e->getMessage(), 400);
}
}
}

View File

@ -0,0 +1,257 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Models\Presensi;
use App\Services\PresensiService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\PenggunaanPoin;
use Carbon\Carbon;
class PresensiController extends Controller
{
protected $presensiService;
public function __construct(PresensiService $presensiService)
{
$this->presensiService = $presensiService;
}
public function index()
{
$user = Auth::user();
$today = Carbon::now('Asia/Jakarta')->toDateString();
$jadwal = \App\Models\JadwalKerja::with(['shift', 'user'])
->where('id_user', $user->id)
->where('tanggal', $today)
->first();
if (!$jadwal) {
return response()->json([
'success' => false,
'message' => 'Tidak ada jadwal hari ini'
], 404);
}
$jamMasuk = $jadwal->shift->jam_mulai;
$jamPulang = $jadwal->shift->jam_selesai;
$infoJadwal = $jadwal->shift->nama_shift;
$poinOverrides = PenggunaanPoin::where('id_user', $user->id)
->whereDate('tanggal_penggunaan', $today)
->where('id_status', 2)
->get();
foreach ($poinOverrides as $poin) {
if ($poin->id_pengurangan == 4 && $poin->jam_masuk_custom) {
$jamMasuk = substr($poin->jam_masuk_custom, 0, 5) . ':00';
$infoJadwal .= ' (Masuk Siang)';
}
if ($poin->id_pengurangan == 5 && $poin->jam_pulang_custom) {
$jamPulang = substr($poin->jam_pulang_custom, 0, 5) . ':00';
$infoJadwal .= ' (Pulang Cepat)';
}
}
$presensi = Presensi::where('id_user', $user->id)
->where('tanggal', $today)
->first();
$ketMasuk = 'Belum Absen';
$ketPulang = null;
if ($presensi) {
if ($presensi->waktu_terlambat) {
$ketMasuk = 'Terlambat';
} else if ($presensi->waktu_masuk_awal) {
$ketMasuk = (str_contains($infoJadwal, 'Poin')) ? 'Masuk Siang (Poin) - Datang Awal' : 'Datang Awal';
} else {
$ketMasuk = (str_contains($infoJadwal, 'Poin')) ? 'Masuk Siang (Poin)' : 'Tepat Waktu';
}
if ($presensi->jam_pulang) {
if ($presensi->waktu_pulang_awal) {
$ketPulang = 'Pulang Awal';
} else if ($presensi->waktu_pulang_akhir) {
$ketPulang = (str_contains($infoJadwal, 'Poin')) ? 'Pulang Cepat (Poin) - Lembur' : 'Lembur / Pulang Akhir';
} else {
$ketPulang = (str_contains($infoJadwal, 'Poin')) ? 'Pulang Cepat (Poin)' : 'Tepat Waktu';
}
}
}
return response()->json([
'success' => true,
'data' => [
'jadwal' => [
'nama_shift' => $infoJadwal,
'jam_masuk' => $jamMasuk,
'jam_pulang' => $jamPulang,
'tanggal' => Carbon::parse($today)->translatedFormat('l, d F Y'),
],
'presensi' => [
'jam_masuk' => $presensi ? $presensi->jam_masuk : null,
'jam_pulang' => $presensi ? $presensi->jam_pulang : null,
'foto_masuk' => $presensi ? $presensi->foto_masuk : null,
'status_validasi' => $presensi ? $presensi->id_validasi : null,
'keterangan' => $presensi ? $ketMasuk : null,
'keterangan_pulang' => $presensi ? $ketPulang : null,
]
]
]);
}
public function store(Request $request)
{
try {
$request->validate([
'latitude' => 'required|numeric',
'longitude' => 'required|numeric',
'foto' => 'required|image|max:2048',
'status' => 'required|in:masuk,pulang',
'keterangan_luar_radius' => 'nullable|string|max:500',
]);
$user = Auth::user();
if ($request->status == 'masuk') {
$data = $this->presensiService->absenMasuk($user, $request);
return ApiResponse::success($data, 'Absen masuk berhasil');
} else {
$data = $this->presensiService->absenPulang($user, $request);
return ApiResponse::success($data, 'Absen pulang berhasil');
}
} catch (\Exception $e) {
$code = (int) $e->getCode();
if ($code < 100 || $code > 599)
$code = 500;
return ApiResponse::error($e->getMessage(), $code);
}
}
public function history(Request $request)
{
try {
$userId = Auth::id();
$history = Presensi::where('id_user', $userId)
->latest('tanggal')
->paginate(10);
$enriched = $history->getCollection()->map(function ($item) use ($userId) {
$jadwal = \App\Models\JadwalKerja::with('shift')
->where('id_user', $userId)
->where('tanggal', $item->tanggal)
->first();
$namaShift = $jadwal?->shift?->nama_shift ?? '-';
$statusMasuk = '-';
if ($item->jam_masuk) {
if ($item->waktu_terlambat) {
$statusMasuk = 'Terlambat';
} elseif ($item->waktu_masuk_awal) {
$statusMasuk = 'Datang Awal';
} else {
$statusMasuk = 'Tepat Waktu';
}
} else {
if ($item->alasan_telat && str_contains($item->alasan_telat, 'Cuti')) {
$statusMasuk = 'Cuti';
} elseif ($item->id_status == 3) {
$statusMasuk = 'Izin';
} elseif ($item->id_status == 4) {
$statusMasuk = 'Sakit';
} elseif ($item->id_status != null && $item->id_status != 1 && $item->id_status != 2) {
$statusStatus = \DB::table('status_presensi')->where('id_status', $item->id_status)->value('nama_status');
if ($statusStatus) {
$statusMasuk = $statusStatus;
}
}
}
$statusPulang = null;
if ($item->jam_pulang) {
if ($item->waktu_pulang_awal) {
$statusPulang = 'Pulang Awal';
} elseif ($item->waktu_pulang_akhir) {
$statusPulang = 'Lembur';
} else {
$statusPulang = 'Tepat Waktu';
}
}
$totalJam = '-';
if ($item->jam_masuk && $item->jam_pulang) {
try {
$masuk = Carbon::parse($item->tanggal . ' ' . $item->jam_masuk);
$pulang = Carbon::parse($item->tanggal . ' ' . $item->jam_pulang);
$diff = $masuk->diff($pulang);
$totalJam = $diff->h . 'j ' . $diff->i . 'm';
} catch (\Exception $e) {
$totalJam = '-';
}
}
return [
'id_presensi' => $item->id_presensi,
'tanggal' => Carbon::parse($item->tanggal)->translatedFormat('l, d F Y'),
'tanggal_raw' => $item->tanggal,
'jam_masuk' => $item->jam_masuk,
'jam_pulang' => $item->jam_pulang,
'shift' => $namaShift,
'status_masuk' => $statusMasuk,
'status_pulang' => $statusPulang,
'total_jam' => $totalJam,
'waktu_terlambat' => $item->waktu_terlambat,
'keterangan_luar_radius' => $item->keterangan_luar_radius,
'verifikasi_wajah' => (bool) $item->verifikasi_wajah,
'status_validasi' => $item->id_validasi,
'alasan_penolakan' => $item->alasan_penolakan,
];
});
$history->setCollection($enriched);
return ApiResponse::success($history);
} catch (\Exception $e) {
return ApiResponse::error($e->getMessage(), 500);
}
}
public function resubmit(Request $request, $id)
{
try {
$request->validate([
'keterangan' => 'required|string|max:500',
]);
$presensi = Presensi::where('id_presensi', $id)
->where('id_user', Auth::id())
->firstOrFail();
if ($presensi->id_validasi != 3) {
return ApiResponse::error('Hanya presensi yang ditolak yang bisa diajukan ulang.', 422);
}
$presensi->update([
'id_validasi' => 2,
'alasan_penolakan' => null,
'keterangan_luar_radius' => $request->keterangan,
]);
return ApiResponse::success(null, 'Presensi berhasil diajukan ulang. Menunggu validasi admin.');
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
return ApiResponse::error('Data presensi tidak ditemukan.', 404);
} catch (\Exception $e) {
return ApiResponse::error($e->getMessage(), 500);
}
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
class ProfileController extends Controller
{
public function update(Request $request)
{
try {
$user = Auth::user();
$request->validate([
'foto' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
'no_telp' => 'nullable|string|max:20',
'alamat' => 'nullable|string|max:500',
]);
if ($request->has('no_telp')) {
$user->no_telp = $request->no_telp;
}
if ($request->has('alamat')) {
$user->alamat = $request->alamat;
}
if ($request->hasFile('foto')) {
if ($user->foto && Storage::disk('public')->exists($user->foto)) {
Storage::disk('public')->delete($user->foto);
}
$path = $request->file('foto')->store('uploads/profil', 'public');
$user->foto = $path;
}
$user->save();
return ApiResponse::success($user->load(['divisi', 'jabatan', 'kantor']), 'Profil berhasil diperbarui.');
} catch (\Exception $e) {
return ApiResponse::error('Gagal update profil: ' . $e->getMessage(), 500);
}
}
public function password(Request $request)
{
try {
$request->validate([
'current_password' => 'required',
'new_password' => 'required|min:8|confirmed',
]);
$user = Auth::user();
if (!Hash::check($request->current_password, $user->password)) {
return ApiResponse::error('Password lama salah.', 400);
}
$user->password = Hash::make($request->new_password);
$user->save();
return ApiResponse::success(null, 'Kata sandi berhasil diubah.');
} catch (\Exception $e) {
return ApiResponse::error('Gagal ubah kata sandi: ' . $e->getMessage(), 500);
}
}
}

View File

@ -0,0 +1,255 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Models\JadwalKerja;
use App\Models\PenggunaanPoin;
use App\Models\Presensi;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Carbon\CarbonPeriod;
class ScheduleController extends Controller
{
public function today(Request $request)
{
try {
$user = Auth::user();
$today = Carbon::now('Asia/Jakarta')->toDateString();
$jadwal = JadwalKerja::with(['shift', 'user.kantor'])
->where('id_user', $user->id)
->where('tanggal', $today)
->first();
if (!$jadwal) {
return ApiResponse::success(null, 'Tidak ada jadwal hari ini');
}
$shift = $jadwal->shift;
$jamMasuk = $shift->jam_mulai ?? '00:00:00';
$jamPulang = $shift->jam_selesai ?? '00:00:00';
$namaShift = $shift->nama_shift ?? 'Unknown Shift';
$statusJadwal = 'Normal';
$note = null;
$poinList = PenggunaanPoin::where('id_user', $user->id)
->whereDate('tanggal_penggunaan', $today)
->where('id_status', 2)
->get();
foreach ($poinList as $poin) {
if ($poin->id_pengurangan == 4 && $poin->jam_masuk_custom) {
$jamMasuk = substr($poin->jam_masuk_custom, 0, 5) . ':00';
$statusJadwal = 'Masuk Siang (Poin)';
$note = 'Jadwal disesuaikan poin';
}
if ($poin->id_pengurangan == 5 && $poin->jam_pulang_custom) {
$jamPulang = substr($poin->jam_pulang_custom, 0, 5) . ':00';
if ($statusJadwal != 'Normal') {
$statusJadwal = 'Full Custom (Poin)';
} else {
$statusJadwal = 'Pulang Cepat (Poin)';
}
}
}
return ApiResponse::success([
'hari' => Carbon::parse($today, 'Asia/Jakarta')->translatedFormat('l, d F Y'),
'shift' => $namaShift,
'jam_masuk' => $jamMasuk,
'jam_pulang' => $jamPulang,
'status_jadwal' => $statusJadwal,
'note' => $note,
'kantor' => $jadwal->user->kantor->nama_kantor ?? '-',
'lokasi_kantor' => [
'lat' => $jadwal->user->kantor->latitude ?? 0,
'lon' => $jadwal->user->kantor->longitude ?? 0,
],
], 'Jadwal hari ini ditemukan');
} catch (\Exception $e) {
return ApiResponse::error('Terjadi kesalahan: ' . $e->getMessage(), 500);
}
}
public function checkScheduleByDate(Request $request)
{
$request->validate([
'tanggal' => 'required|date_format:Y-m-d',
]);
try {
$user = Auth::user();
$tanggal = $request->tanggal;
$jadwal = JadwalKerja::with(['shift'])
->where('id_user', $user->id)
->where('tanggal', $tanggal)
->first();
if (!$jadwal) {
return ApiResponse::error('Tidak ada jadwal kerja pada tanggal ini', 404);
}
$shift = $jadwal->shift;
$jamMasuk = $shift->jam_mulai ?? '00:00:00';
$jamPulang = $shift->jam_selesai ?? '00:00:00';
$statusJadwal = 'Normal';
$note = null;
$poinList = PenggunaanPoin::where('id_user', $user->id)
->whereDate('tanggal_penggunaan', $tanggal)
->where('id_status', 2)
->get();
foreach ($poinList as $poin) {
if ($poin->id_pengurangan == 4 && $poin->jam_masuk_custom) {
$jamMasuk = substr($poin->jam_masuk_custom, 0, 5) . ':00';
$statusJadwal = 'Masuk Siang (Poin)';
}
if ($poin->id_pengurangan == 5 && $poin->jam_pulang_custom) {
$jamPulang = substr($poin->jam_pulang_custom, 0, 5) . ':00';
$statusJadwal = ($statusJadwal == 'Masuk Siang (Poin)') ? 'Full Custom' : 'Pulang Cepat (Poin)';
}
}
return ApiResponse::success([
'tanggal' => Carbon::parse($tanggal, 'Asia/Jakarta')->translatedFormat('l, d F Y'),
'jam_masuk' => $jamMasuk,
'jam_pulang' => $jamPulang,
'jam_mulai' => $jamMasuk,
'jam_selesai' => $jamPulang,
'status_jadwal' => $statusJadwal,
'note' => $note,
], 'Jadwal ditemukan');
} catch (\Exception $e) {
return ApiResponse::error('Terjadi kesalahan: ' . $e->getMessage(), 500);
}
}
public function getMonthlySchedule(Request $request)
{
$month = $request->month ?? Carbon::now()->month;
$year = $request->year ?? Carbon::now()->year;
try {
$user = Auth::user();
$startDate = Carbon::createFromDate($year, $month, 1)->startOfMonth();
$endDate = Carbon::createFromDate($year, $month, 1)->endOfMonth();
$jadwals = JadwalKerja::with(['shift'])
->where('id_user', $user->id)
->whereBetween('tanggal', [$startDate->toDateString(), $endDate->toDateString()])
->get()
->keyBy('tanggal');
$poinOverrides = PenggunaanPoin::where('id_user', $user->id)
->whereBetween('tanggal_penggunaan', [$startDate->toDateString(), $endDate->toDateString()])
->where('id_status', 2)
->get()
->groupBy('tanggal_penggunaan');
$presensis = Presensi::where('id_user', $user->id)
->whereBetween('tanggal', [$startDate->toDateString(), $endDate->toDateString()])
->get()
->keyBy('tanggal');
$hariLiburs = \App\Models\HariLibur::whereBetween('tanggal', [$startDate->toDateString(), $endDate->toDateString()])
->where(function($query) use ($user) {
$query->whereNull('id_kantor')
->orWhere('id_kantor', $user->id_kantor);
})
->get()
->keyBy(function ($item) {
return Carbon::parse($item->tanggal)->toDateString();
});
$results = [];
$period = CarbonPeriod::create($startDate, $endDate);
foreach ($period as $date) {
$dateStr = $date->toDateString();
$jadwalItem = $jadwals[$dateStr] ?? null;
$presensiItem = $presensis[$dateStr] ?? null;
$hariLiburItem = $hariLiburs[$dateStr] ?? null;
$dataHari = [
'tanggal' => $dateStr,
'hari' => $date->translatedFormat('l'),
'is_hari_kerja' => false,
'is_hari_libur' => $hariLiburItem ? true : false,
'keterangan_libur' => $hariLiburItem ? $hariLiburItem->keterangan : null,
'shift_nama' => $hariLiburItem ? $hariLiburItem->keterangan : 'Libur / Off',
'jam_masuk' => '-',
'jam_pulang' => '-',
'status_poin' => null,
'status_presensi' => $presensiItem ? 'Hadir' : 'Belum Absen',
'warna_kalender' => $hariLiburItem ? '#EF4444' : '#E0E0E0'
];
if ($jadwalItem) {
$dataHari['is_hari_kerja'] = true;
$shift = $jadwalItem->shift;
$dataHari['shift_nama'] = $shift->nama_shift ?? 'Unknown Shift';
$dataHari['jam_masuk'] = $shift->jam_mulai ?? '00:00:00';
$dataHari['jam_pulang'] = $shift->jam_selesai ?? '00:00:00';
$dataHari['warna_kalender'] = '#3B82F6';
$poinFound = null;
foreach ($poinOverrides as $keyDate => $poins) {
if (Carbon::parse($keyDate)->toDateString() == $dateStr) {
$poinFound = $poins;
break;
}
}
if ($poinFound) {
foreach ($poinFound as $poin) {
if ($poin->id_pengurangan == 4 && $poin->jam_masuk_custom) {
$dataHari['jam_masuk'] = substr($poin->jam_masuk_custom, 0, 5) . ':00';
$dataHari['status_poin'] = 'Masuk Siang';
$dataHari['warna_kalender'] = '#F59E0B';
}
if ($poin->id_pengurangan == 5 && $poin->jam_pulang_custom) {
$dataHari['jam_pulang'] = substr($poin->jam_pulang_custom, 0, 5) . ':00';
$dataHari['status_poin'] = $dataHari['status_poin'] ? 'Full Custom' : 'Pulang Cepat';
$dataHari['warna_kalender'] = '#F59E0B';
}
}
}
if ($presensiItem) {
$dataHari['warna_kalender'] = '#10B981';
}
}
$results[] = $dataHari;
}
return ApiResponse::success($results, 'Data kalender berhasil dimuat');
} catch (\Exception $e) {
return ApiResponse::error('Terjadi kesalahan: ' . $e->getMessage(), 500);
}
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreSignatureRequest;
use App\Models\TandaTangan;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
class SignatureApiController extends Controller
{
public function show()
{
$ttd = TandaTangan::where('id_user', Auth::id())
->active()
->first();
if (!$ttd) {
return ApiResponse::notFound('Belum ada tanda tangan.');
}
return ApiResponse::success([
'id_tanda_tangan' => $ttd->id_tanda_tangan,
'file_ttd' => asset('storage/' . $ttd->file_ttd),
'is_active' => $ttd->is_active,
'created_at' => $ttd->created_at,
]);
}
public function store(StoreSignatureRequest $request)
{
try {
$base64 = $request->signature_data;
if (str_contains($base64, ',')) {
$base64 = explode(',', $base64)[1];
}
$imageData = base64_decode($base64);
if ($imageData === false) {
return ApiResponse::error('Data tanda tangan tidak valid.', 400);
}
$fileName = 'uploads/ttd/' . Auth::id() . '_' . time() . '.png';
Storage::disk('public')->put($fileName, $imageData);
TandaTangan::where('id_user', Auth::id())->update(['is_active' => false]);
$ttd = TandaTangan::create([
'id_user' => Auth::id(),
'file_ttd' => $fileName,
'is_active' => true,
]);
return ApiResponse::success([
'id_tanda_tangan' => $ttd->id_tanda_tangan,
'file_ttd' => asset('storage/' . $ttd->file_ttd),
], 'Tanda tangan berhasil disimpan.', 201);
} catch (\Exception $e) {
return ApiResponse::error('Gagal menyimpan tanda tangan: ' . $e->getMessage(), 500);
}
}
public function destroy($id)
{
$ttd = TandaTangan::where('id_user', Auth::id())
->where('id_tanda_tangan', $id)
->first();
if (!$ttd) {
return ApiResponse::notFound('Tanda tangan tidak ditemukan.');
}
if ($ttd->file_ttd && Storage::disk('public')->exists($ttd->file_ttd)) {
Storage::disk('public')->delete($ttd->file_ttd);
}
$ttd->delete();
return ApiResponse::success(null, 'Tanda tangan berhasil dihapus.');
}
}

View File

@ -0,0 +1,213 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Models\PengajuanIzin;
use App\Models\JenisIzin;
use App\Models\SuratIzin;
use App\Models\TandaTangan;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
class SubmissionController extends Controller
{
public function types()
{
$types = JenisIzin::all();
return ApiResponse::success($types);
}
public function store(\App\Http\Requests\StorePengajuanIzinRequest $request)
{
try {
$user = Auth::user();
$jenisIzin = JenisIzin::find($request->id_jenis_izin);
if ($jenisIzin && $jenisIzin->nama_izin == 'Cuti') {
$tanggalMulai = Carbon::parse($request->tanggal_mulai);
$minDate = Carbon::now()->addDays(7)->startOfDay();
if ($tanggalMulai->lt($minDate)) {
return ApiResponse::error('Pengajuan Cuti minimal H-7!', 400);
}
}
DB::beginTransaction();
$path = null;
if ($request->hasFile('bukti_file')) {
$path = $request->file('bukti_file')->store('uploads/izin', 'public');
}
$submission = PengajuanIzin::create([
'id_user' => $user->id,
'id_jenis_izin' => $request->id_jenis_izin,
'tanggal_mulai' => $request->tanggal_mulai,
'tanggal_selesai' => $request->tanggal_selesai,
'alasan' => $request->alasan,
'bukti_file' => $path,
'id_status' => 1
]);
$ttdAktif = TandaTangan::where('id_user', $user->id)->active()->first();
$namaJenisIzin = $jenisIzin->nama_izin ?? 'Izin';
$tglMulai = Carbon::parse($request->tanggal_mulai)->translatedFormat('d F Y');
$tglSelesai = Carbon::parse($request->tanggal_selesai)->translatedFormat('d F Y');
$isiSurat = "Dengan hormat,\n\n"
. "Saya yang bertanda tangan di bawah ini:\n"
. "Nama: {$user->nama_lengkap}\n"
. "NIK: {$user->nik}\n"
. "Jabatan: " . ($user->jabatan->nama_jabatan ?? '-') . "\n"
. "Divisi: " . ($user->divisi->nama_divisi ?? '-') . "\n\n"
. "Dengan ini mengajukan {$namaJenisIzin} mulai tanggal {$tglMulai} sampai dengan {$tglSelesai}.\n\n"
. "Alasan: {$request->alasan}\n\n"
. "Demikian surat ini saya buat dengan sebenar-benarnya. "
. "Atas perhatian dan persetujuannya, saya ucapkan terima kasih.";
$suratIzin = null;
if ($jenisIzin && $jenisIzin->nama_izin == 'Cuti') {
$suratIzin = SuratIzin::create([
'id_izin' => $submission->id_izin,
'id_user' => $user->id,
'id_ttd_pengaju' => $ttdAktif?->id_tanda_tangan,
'isi_surat' => $isiSurat,
'status_surat' => 'menunggu_manajer',
]);
}
DB::commit();
$responseData = $submission->toArray();
if ($suratIzin) {
$responseData['surat_izin'] = [
'id_surat' => $suratIzin->id_surat,
'nomor_surat' => $suratIzin->nomor_surat,
'status_surat' => $suratIzin->status_surat,
'has_ttd' => $ttdAktif !== null,
];
} else {
$responseData['surat_izin'] = null;
}
$message = 'Pengajuan & surat izin berhasil dibuat.';
if (!$ttdAktif) {
$message .= ' (TTD belum tersedia, silakan buat di menu Profil)';
}
return ApiResponse::success($responseData, $message, 201);
} catch (\Exception $e) {
DB::rollBack();
return ApiResponse::error('Gagal mengirim pengajuan: ' . $e->getMessage(), 500);
}
}
/**
* @OA\Put(
* path="/api/submission/{id}",
* tags={"Submission"},
* summary="Update Submission",
* security={{"bearerAuth":{}}},
* @OA\Parameter(
* name="id",
* in="path",
* required=true,
* @OA\Schema(type="integer")
* ),
* @OA\RequestBody(
* required=true,
* @OA\MediaType(
* mediaType="multipart/form-data",
* @OA\Schema(
* required={"id_jenis_izin", "tanggal_mulai", "tanggal_selesai", "alasan"},
* @OA\Property(property="id_jenis_izin", type="integer", example=1),
* @OA\Property(property="tanggal_mulai", type="string", format="date"),
* @OA\Property(property="tanggal_selesai", type="string", format="date"),
* @OA\Property(property="alasan", type="string"),
* @OA\Property(property="bukti_file", type="string", format="binary")
* )
* )
* ),
* @OA\Response(
* response=200,
* description="Submission updated",
* @OA\JsonContent(
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string")
* )
* )
* )
*/
public function update(\App\Http\Requests\UpdatePengajuanIzinRequest $request, $id)
{
try {
$submission = PengajuanIzin::where('id_user', Auth::id())
->where('id_izin', $id)
->where('id_status', 1)
->first();
if (!$submission) {
return ApiResponse::error('Pengajuan tidak ditemukan atau sudah diproses.', 404);
}
DB::beginTransaction();
$data = $request->validated();
if ($request->hasFile('bukti_file')) {
if ($submission->bukti_file && \Illuminate\Support\Facades\Storage::disk('public')->exists($submission->bukti_file)) {
\Illuminate\Support\Facades\Storage::disk('public')->delete($submission->bukti_file);
}
$data['bukti_file'] = $request->file('bukti_file')->store('uploads/izin', 'public');
}
$submission->update($data);
DB::commit();
return ApiResponse::success(null, 'Pengajuan berhasil diperbarui.');
} catch (\Exception $e) {
DB::rollBack();
return ApiResponse::error('Gagal memperbarui pengajuan: ' . $e->getMessage(), 500);
}
}
public function history(Request $request)
{
try {
$query = PengajuanIzin::with(['jenisIzin', 'statusPengajuan'])
->where('id_user', Auth::id());
if ($request->has('status')) {
$statusParam = $request->get('status');
$statusId = match ($statusParam) {
'pending' => 1,
'approved' => 2,
'rejected' => 3,
default => null
};
if ($statusId) {
$query->where('id_status', $statusId);
}
}
$history = $query->orderBy('created_at', 'desc')->paginate(10);
return ApiResponse::success($history);
} catch (\Exception $e) {
return ApiResponse::error('Gagal memuat riwayat: ' . $e->getMessage(), 500);
}
}
}

View File

@ -0,0 +1,157 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreSuratIzinRequest;
use App\Models\SuratIzin;
use App\Models\PengajuanIzin;
use App\Models\TandaTangan;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class SuratIzinApiController extends Controller
{
public function index()
{
try {
$surat = SuratIzin::with([
'pengajuanIzin.jenisIzin',
'tandaTanganPengaju',
'approvals.approver',
'approvals.tandaTanganApprover',
])
->where('id_user', Auth::id())
->orderBy('created_at', 'desc')
->paginate(10);
$data = $surat->through(function ($item) {
return $this->formatSurat($item);
});
return ApiResponse::success($data);
} catch (\Exception $e) {
return ApiResponse::error('Gagal memuat daftar surat: ' . $e->getMessage(), 500);
}
}
public function store(StoreSuratIzinRequest $request)
{
try {
$user = Auth::user();
$pengajuan = PengajuanIzin::with('jenisIzin')
->where('id_izin', $request->id_izin)
->where('id_user', $user->id)
->first();
if (!$pengajuan) {
return ApiResponse::notFound('Pengajuan izin tidak ditemukan.');
}
$existing = SuratIzin::where('id_izin', $request->id_izin)->first();
if ($existing) {
return ApiResponse::error('Surat izin untuk pengajuan ini sudah ada.', 400);
}
$ttd = TandaTangan::where('id_user', $user->id)->active()->first();
if (!$ttd) {
return ApiResponse::error('Anda belum memiliki tanda tangan digital. Silakan buat terlebih dahulu.', 400);
}
DB::beginTransaction();
$surat = SuratIzin::create([
'id_izin' => $pengajuan->id_izin,
'id_user' => $user->id,
'isi_surat' => $request->isi_surat,
'id_ttd_pengaju' => $ttd->id_tanda_tangan,
'status_surat' => 'menunggu_manajer',
]);
DB::commit();
$surat->load(['tandaTanganPengaju', 'approvals']);
return ApiResponse::success(
$this->formatSurat($surat),
'Surat izin berhasil dibuat.',
201
);
} catch (\Exception $e) {
DB::rollBack();
return ApiResponse::error('Gagal membuat surat izin: ' . $e->getMessage(), 500);
}
}
public function show($id)
{
try {
$surat = SuratIzin::with([
'pengajuanIzin.jenisIzin',
'user.jabatan',
'user.divisi',
'tandaTanganPengaju',
'approvals.approver.jabatan',
'approvals.tandaTanganApprover',
])
->where('id_user', Auth::id())
->where('id_surat', $id)
->first();
if (!$surat) {
return ApiResponse::notFound('Surat izin tidak ditemukan.');
}
return ApiResponse::success($this->formatSurat($surat, true));
} catch (\Exception $e) {
return ApiResponse::error('Gagal memuat detail surat: ' . $e->getMessage(), 500);
}
}
private function formatSurat(SuratIzin $surat, bool $detail = false): array
{
$data = [
'id_surat' => $surat->id_surat,
'nomor_surat' => $surat->nomor_surat,
'status_surat' => $surat->status_surat,
'jenis_izin' => $surat->pengajuanIzin?->jenisIzin?->nama_izin ?? 'N/A',
'created_at' => $surat->created_at?->toISOString(),
'ttd_pengaju' => $surat->tandaTanganPengaju ? asset('storage/' . $surat->tandaTanganPengaju->file_ttd) : null,
'approvals' => $surat->approvals->map(function ($approval) {
return [
'tahap' => $approval->tahap,
'tahap_label' => $approval->tahap == 1 ? 'Manajer' : 'HRD',
'status' => $approval->status,
'approver_nama' => $approval->approver?->nama_lengkap ?? 'N/A',
'approver_jabatan' => $approval->approver?->jabatan?->nama_jabatan ?? 'N/A',
'ttd_approver' => $approval->tandaTanganApprover ? asset('storage/' . $approval->tandaTanganApprover->file_ttd) : null,
'catatan' => $approval->catatan,
'created_at' => $approval->created_at?->toISOString(),
];
})->toArray(),
];
if ($detail) {
$data['isi_surat'] = $surat->isi_surat;
$data['pengaju'] = [
'nama' => $surat->user?->nama_lengkap,
'nik' => $surat->user?->nik,
'jabatan' => $surat->user?->jabatan?->nama_jabatan ?? 'N/A',
'divisi' => $surat->user?->divisi?->nama_divisi ?? 'N/A',
];
$data['pengajuan'] = [
'id_izin' => $surat->pengajuanIzin?->id_izin,
'tanggal_mulai' => $surat->pengajuanIzin?->tanggal_mulai,
'tanggal_selesai' => $surat->pengajuanIzin?->tanggal_selesai,
'alasan' => $surat->pengajuanIzin?->alasan,
];
}
return $data;
}
}

View File

@ -0,0 +1,72 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AuthController extends Controller
{
public function index()
{
if (Auth::check()) {
return redirect()->route('dashboard');
}
return view('auth.login');
}
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);
if (Auth::attempt($credentials)) {
$user = Auth::user();
if ($user->status_aktif == 0) {
Auth::logout();
if ($request->ajax() || $request->wantsJson()) {
return response()->json([
'success' => false,
'message' => 'Akun Anda telah dinonaktifkan.'
], 403);
}
return back()->with('error', 'Akun Anda telah dinonaktifkan.');
}
$request->session()->regenerate();
if ($request->ajax() || $request->wantsJson()) {
return response()->json([
'success' => true,
'message' => 'Login berhasil!',
'redirect' => route('dashboard')
]);
}
return redirect()->route('dashboard');
}
if ($request->ajax() || $request->wantsJson()) {
return response()->json([
'success' => false,
'message' => 'Email atau Password yang Anda masukkan salah.'
], 401);
}
return back()
->withInput($request->only('email'))
->with('error', 'Email atau Password yang Anda masukkan salah.')
->withErrors(['email' => 'Email atau Password yang Anda masukkan salah.']);
}
public function logout(Request $request)
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/login');
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Http\Controllers;
/**
* @OA\Info(
* version="1.0.0",
* title="MPG HRIS API",
* description="API Documentation"
* )
* @OA\Server(
* url="http://localhost/api",
* description="API Server"
* )
*/
class Controller
{
}

View File

@ -0,0 +1,58 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
class CutiController extends Controller
{
public function index(Request $request)
{
$search = $request->input('search');
$query = User::with(['jabatan', 'divisi'])->whereHas('roles', function($q) {
$q->where('nama_role', '!=', 'super_admin');
});
if ($search) {
$query->where('nama_lengkap', 'like', '%' . $search . '%')
->orWhere('nik', 'like', '%' . $search . '%');
}
$pegawai = $query->orderBy('nama_lengkap', 'asc')->paginate(15);
return view('cuti.index', compact('pegawai', 'search'));
}
public function update(Request $request, $id)
{
$request->validate([
'sisa_cuti' => 'required|integer|min:0',
'alasan' => 'nullable|string|max:255'
]);
$user = User::findOrFail($id);
$sisaLama = $user->sisa_cuti;
$user->update([
'sisa_cuti' => $request->sisa_cuti
]);
return redirect()->back()->with('success', 'Sisa cuti ' . $user->nama_lengkap . ' berhasil diperbarui.');
}
public function resetMassal(Request $request)
{
$request->validate([
'jumlah_hari' => 'required|integer|min:0'
]);
User::query()->update([
'sisa_cuti' => $request->jumlah_hari
]);
return redirect()->back()->with('success', 'Sisa cuti semua pegawai berhasil di-reset menjadi ' . $request->jumlah_hari . ' hari.');
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\Kantor;
use App\Models\Presensi;
use Illuminate\Http\Request;
use Carbon\Carbon;
class DashboardController extends Controller
{
public function index()
{
$hariIni = Carbon::today()->format('Y-m-d');
$totalPegawai = User::where('status_aktif', 1)->count();
$rekapRaw = Presensi::whereDate('tanggal', $hariIni)
->selectRaw("
SUM(CASE WHEN id_status IN (1,2) THEN 1 ELSE 0 END) as hadir,
SUM(CASE WHEN id_status = 2 THEN 1 ELSE 0 END) as terlambat,
SUM(CASE WHEN id_status = 3 THEN 1 ELSE 0 END) as izin,
SUM(CASE WHEN id_status = 4 THEN 1 ELSE 0 END) as sakit,
SUM(CASE WHEN id_status = 5 THEN 1 ELSE 0 END) as alpha
")->first();
$rekap = [
'hadir' => $rekapRaw->hadir ?? 0,
'izin' => $rekapRaw->izin ?? 0,
'sakit' => $rekapRaw->sakit ?? 0,
'alpha' => $rekapRaw->alpha ?? 0,
'terlambat' => $rekapRaw->terlambat ?? 0
];
$recentActivities = Presensi::with(['user.jabatan'])
->whereDate('tanggal', $hariIni)
->whereNotNull('jam_masuk')
->orderBy('jam_masuk', 'desc')
->take(5)
->get();
$pendingIzin = \App\Models\PengajuanIzin::with('user')->where('id_status', 1)->orderBy('created_at', 'desc')->take(5)->get();
$pendingSuratIzin = \App\Models\SuratIzin::with('user')->whereIn('status_surat', ['menunggu_manajer', 'menunggu_hrd'])->orderBy('created_at', 'desc')->take(5)->get();
$pendingLembur = \App\Models\Lembur::with('user')->where('id_status', 1)->orderBy('created_at', 'desc')->take(5)->get();
$pendingPoin = \App\Models\PenggunaanPoin::with('user')->where('id_status', 1)->orderBy('created_at', 'desc')->take(5)->get();
$pendingPresensi = Presensi::with('user')->where('id_validasi', 2)->orderBy('created_at', 'desc')->take(5)->get();
$pengajuanPending = [
'izin' => $pendingIzin,
'surat_izin' => $pendingSuratIzin,
'lembur' => $pendingLembur,
'poin' => $pendingPoin,
'presensi' => $pendingPresensi
];
$startDate = Carbon::today()->subDays(6)->format('Y-m-d');
$endDate = $hariIni;
$chartRaw = Presensi::whereBetween('tanggal', [$startDate, $endDate])
->whereIn('id_status', [1, 2])
->selectRaw("DATE(tanggal) as tgl, COUNT(*) as total")
->groupBy('tgl')
->orderBy('tgl')
->pluck('total', 'tgl');
$chartLabels = [];
$chartData = [];
for ($i = 6; $i >= 0; $i--) {
$date = Carbon::today()->subDays($i);
$chartLabels[] = $date->format('d/m');
$chartData[] = $chartRaw->get($date->format('Y-m-d'), 0);
}
return view('dashboard.index', compact('rekap', 'recentActivities', 'pengajuanPending', 'chartLabels', 'chartData', 'totalPegawai'));
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace App\Http\Controllers;
use App\Models\Divisi;
use App\Http\Requests\StoreDivisiRequest;
use App\Http\Requests\UpdateDivisiRequest;
class DivisiController extends Controller
{
public function index()
{
$divisi = Divisi::latest()->paginate(10);
return view('divisi.index', compact('divisi'));
}
public function store(StoreDivisiRequest $request)
{
Divisi::create($request->validated());
return redirect()->back()->with('success', 'Divisi berhasil ditambahkan.');
}
public function update(UpdateDivisiRequest $request, $id)
{
$divisi = Divisi::findOrFail($id);
$divisi->update($request->validated());
return redirect()->back()->with('success', 'Divisi berhasil diperbarui.');
}
public function destroy($id)
{
try {
Divisi::findOrFail($id)->delete();
return redirect()->back()->with('success', 'Divisi berhasil dihapus.');
} catch (\Illuminate\Database\QueryException $e) {
if ($e->getCode() === '23000') {
return redirect()->back()->with('error', 'Divisi tidak bisa dihapus karena masih digunakan oleh data pegawai.');
}
throw $e;
}
}
}

View File

@ -0,0 +1,83 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\DataWajah;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class FaceApprovalController extends Controller
{
public function index()
{
$users = User::whereHas('dataWajah', function($q) {
$q->where('is_verified', 0);
})
->with(['jabatan', 'divisi'])
->get();
$users->each(function ($user) {
$storagePath = "face_datasets/{$user->id}";
$poses = ['depan', 'kanan', 'kiri', 'bawah'];
$fotoList = [];
foreach ($poses as $pose) {
$extensions = ['jpg', 'jpeg', 'png'];
foreach ($extensions as $ext) {
$filePath = "{$storagePath}/user_{$user->id}_{$pose}.{$ext}";
if (Storage::disk('local')->exists($filePath)) {
$fotoList[$pose] = $filePath;
break;
}
}
}
$user->face_photos = $fotoList;
});
return view('face-approval.index', compact('users'));
}
public function approve($id)
{
$user = User::findOrFail($id);
if ($user->dataWajah) {
$user->dataWajah->update(['is_verified' => 1]);
}
return redirect()->back()->with('success', 'Wajah karyawan berhasil diverifikasi. Karyawan kini bisa melakukan presensi.');
}
public function reject($id)
{
$user = User::findOrFail($id);
if ($user->dataWajah) {
$user->dataWajah->delete();
}
$user->update(['is_face_registered' => 0]);
$storagePath = "face_datasets/{$user->id}";
if (Storage::disk('local')->exists($storagePath)) {
Storage::disk('local')->deleteDirectory($storagePath);
}
return redirect()->back()->with('success', 'Wajah ditolak. Karyawan diminta melakukan registrasi ulang.');
}
public function showPhoto($userId, $pose)
{
$extensions = ['jpg', 'jpeg', 'png'];
foreach ($extensions as $ext) {
$filePath = "face_datasets/{$userId}/user_{$userId}_{$pose}.{$ext}";
if (Storage::disk('local')->exists($filePath)) {
return response()->file(Storage::disk('local')->path($filePath));
}
}
abort(404);
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace App\Http\Controllers;
use App\Models\HariLibur;
use Illuminate\Http\Request;
use Carbon\Carbon;
class HariLiburController extends Controller
{
public function index(Request $request)
{
$query = HariLibur::with('kantor');
if ($request->filled('search')) {
$query->where('keterangan', 'like', '%' . $request->search . '%')
->orWhere('tanggal', 'like', '%' . $request->search . '%');
}
if ($request->filled('id_kantor')) {
$query->where('id_kantor', $request->id_kantor);
}
$hariLiburs = $query->orderBy('tanggal', 'desc')->paginate(10)->withQueryString();
$kantors = \App\Models\Kantor::all();
return view('hari_libur.index', compact('hariLiburs', 'kantors'));
}
public function store(Request $request)
{
$request->validate([
'tanggal' => 'required|date',
'keterangan' => 'required|string|max:255',
'id_kantor' => 'nullable|exists:kantor,id_kantor',
]);
HariLibur::create($request->all());
return redirect()->route('hari-libur.index')
->with('success', 'Hari libur berhasil ditambahkan.');
}
public function update(Request $request, $id)
{
$request->validate([
'tanggal' => 'required|date',
'keterangan' => 'required|string|max:255',
'id_kantor' => 'nullable|exists:kantor,id_kantor',
]);
$hariLibur = HariLibur::where('id', '=', $id)->first() ?? HariLibur::where('id_hari_libur', '=', $id)->firstOrFail();
$hariLibur->update($request->all());
return redirect()->route('hari-libur.index')
->with('success', 'Hari libur berhasil diperbarui.');
}
public function destroy($id)
{
$hariLibur = HariLibur::where('id', '=', $id)->first() ?? HariLibur::where('id_hari_libur', '=', $id)->firstOrFail();
$hariLibur->delete();
return redirect()->route('hari-libur.index')
->with('success', 'Hari libur berhasil dihapus.');
}
public function syncHolidays(Request $request)
{
$year = $request->get('year', date('Y'));
try {
$response = \Illuminate\Support\Facades\Http::withoutVerifying()->get("https://api-hari-libur.vercel.app/api?year={$year}");
if ($response->successful()) {
$body = $response->json();
$holidays = $body['data'] ?? [];
$count = 0;
foreach ($holidays as $h) {
if (isset($h['date'])) {
$tanggal = $h['date'];
$keterangan = $h['description'];
$exists = HariLibur::where('tanggal', '=', $tanggal)->exists();
if (!$exists) {
HariLibur::create(['tanggal' => $tanggal, 'keterangan' => $keterangan]);
$count++;
}
}
}
return redirect()->route('hari-libur.index')
->with('success', "Berhasil sinkronisasi {$count} hari libur baru untuk tahun {$year}.");
}
return redirect()->route('hari-libur.index')
->with('error', 'Gagal mengambil data dari API Hari Libur.');
} catch (\Exception $e) {
return redirect()->route('hari-libur.index')
->with('error', 'Terjadi kesalahan: ' . $e->getMessage());
}
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace App\Http\Controllers;
use App\Models\Jabatan;
use App\Http\Requests\StoreJabatanRequest;
use App\Http\Requests\UpdateJabatanRequest;
class JabatanController extends Controller
{
public function index()
{
$jabatan = Jabatan::latest()->paginate(10);
return view('jabatan.index', compact('jabatan'));
}
public function store(StoreJabatanRequest $request)
{
Jabatan::create($request->validated());
return redirect()->back()->with('success', 'Jabatan berhasil ditambahkan.');
}
public function update(UpdateJabatanRequest $request, $id)
{
$jabatan = Jabatan::findOrFail($id);
$jabatan->update($request->validated());
return redirect()->back()->with('success', 'Jabatan berhasil diperbarui.');
}
public function destroy($id)
{
try {
Jabatan::findOrFail($id)->delete();
return redirect()->back()->with('success', 'Jabatan berhasil dihapus.');
} catch (\Illuminate\Database\QueryException $e) {
if ($e->getCode() === '23000') {
return redirect()->back()->with('error', 'Jabatan tidak bisa dihapus karena masih digunakan oleh data pegawai.');
}
throw $e;
}
}
}

View File

@ -0,0 +1,352 @@
<?php
namespace App\Http\Controllers;
use App\Models\JadwalKerja;
use App\Models\ShiftKerja;
use App\Models\User;
use App\Models\Kantor;
use App\Models\Divisi;
use Illuminate\Http\Request;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use App\Models\PenggunaanPoin;
use App\Services\PoinService;
class JadwalController extends Controller
{
public function index()
{
$kantor = Kantor::all();
$divisi = Divisi::all();
$shifts = ShiftKerja::all()->map(function ($s) {
$s->color = $this->getShiftColor($s->nama_shift);
return $s;
});
$pegawai = User::where('status_aktif', 1)->orderBy('nama_lengkap', 'asc')->get();
return view('jadwal.index', compact('kantor', 'divisi', 'shifts', 'pegawai'));
}
public function getEvents(Request $request)
{
$start = Carbon::parse($request->start)->format('Y-m-d');
$end = Carbon::parse($request->end)->format('Y-m-d');
$query = JadwalKerja::with(['user.kantor', 'user.jabatan', 'shift'])
->whereDate('tanggal', '>=', $start)
->whereDate('tanggal', '<=', $end);
if ($request->filled('filter_kantor') && $request->filter_kantor != "") {
$query->whereHas('user', function ($q) use ($request) {
$q->where('id_kantor', $request->filter_kantor);
});
}
if ($request->filled('filter_divisi') && $request->filter_divisi != "") {
$query->whereHas('user', function ($q) use ($request) {
$q->where('id_divisi', $request->filter_divisi);
});
}
$jadwals = $query->get();
$poinUsed = PenggunaanPoin::with('jenisPengurangan')
->whereBetween('tanggal_penggunaan', [$start, $end])
->where('id_status', 2)
->get()
->groupBy(function ($item) {
return $item->id_user . '-' . $item->tanggal_penggunaan->format('Y-m-d');
});
$events = [];
foreach ($jadwals as $jadwalItem) {
$key = $jadwalItem->id_user . '-' . $jadwalItem->tanggal;
$shiftName = $jadwalItem->shift?->nama_shift ?? 'Shift ?';
$jamMulai = $jadwalItem->shift?->jam_mulai ?? '00:00:00';
$jamSelesai = $jadwalItem->shift?->jam_selesai ?? '23:59:00';
$originalJamMulai = $jamMulai;
$originalJamSelesai = $jamSelesai;
$color = $this->getShiftColor($shiftName);
$title = ($jadwalItem->user?->nama_lengkap ?? 'Unknown') . "\n(" . $shiftName . ")";
$description = "Jam: " . substr($jamMulai, 0, 5) . " - " . substr($jamSelesai, 0, 5);
$isPoin = false;
$poinInfo = [];
if (isset($poinUsed[$key])) {
$isPoin = true;
$listPenggunaan = $poinUsed[$key];
$color = '#f6c23e';
foreach ($listPenggunaan as $penggunaan) {
$jenis = $penggunaan->jenisPengurangan?->nama_pengurangan ?? 'Unknown';
if ($penggunaan->id_pengurangan == 4 && $penggunaan->jam_masuk_custom) {
$jamMulai = substr($penggunaan->jam_masuk_custom, 0, 5) . ':00';
$description .= "\n\n⚠️ DATANG TELAT (Poin)\nJam Masuk Baru: $jamMulai";
$poinInfo[] = "⚠️ Datang Terlambat (Menjadi $jamMulai)";
} elseif ($penggunaan->id_pengurangan == 5 && $penggunaan->jam_pulang_custom) {
$jamSelesai = substr($penggunaan->jam_pulang_custom, 0, 5) . ':00';
$description .= "\n\n⚠️ PULANG CEPAT (Poin)\nJam Pulang Baru: $jamSelesai";
$poinInfo[] = "⚠️ Pulang Cepat (Menjadi $jamSelesai)";
} else {
$description .= "\n[INFO] " . $jenis;
$poinInfo[] = $jenis;
}
}
}
$events[] = [
'id' => $jadwalItem->id_jadwal,
'title' => $title,
'start' => $jadwalItem->tanggal,
'backgroundColor' => $color,
'borderColor' => $color,
'extendedProps' => [
'description' => $description,
'is_poin' => $isPoin,
'is_holiday' => false,
'poin_info' => $poinInfo,
'nama_user' => $jadwalItem->user?->nama_lengkap ?? '-',
'nama_shift' => $shiftName,
'jam_mulai' => $jamMulai,
'jam_selesai' => $jamSelesai,
'original_jam_mulai' => $originalJamMulai,
'original_jam_selesai' => $originalJamSelesai,
'kantor' => $jadwalItem->user?->kantor?->nama_kantor ?? '-',
'jabatan' => $jadwalItem->user?->jabatan?->nama_jabatan ?? '-',
'id_shift' => $jadwalItem->id_shift,
'id_user' => $jadwalItem->id_user,
]
];
}
$hariLibursQuery = \App\Models\HariLibur::whereBetween('tanggal', [$start, $end]);
if ($request->filled('filter_kantor') && $request->filter_kantor != "") {
$hariLibursQuery->where(function($q) use ($request) {
$q->whereNull('id_kantor')->orWhere('id_kantor', $request->filter_kantor);
});
}
$hariLiburs = $hariLibursQuery->get();
foreach ($hariLiburs as $hl) {
$events[] = [
'id' => 'libur-' . $hl->id,
'title' => '🔴 ' . $hl->keterangan,
'start' => $hl->tanggal,
'backgroundColor' => '#ef4444',
'borderColor' => '#ef4444',
'display' => 'block',
'extendedProps' => [
'is_holiday' => true,
'is_poin' => false,
'keterangan' => $hl->keterangan,
'nama_user' => $hl->keterangan,
'nama_shift' => 'Hari Libur',
'jam_mulai' => '00:00',
'jam_selesai' => '00:00',
]
];
}
return response()->json($events);
}
private function getShiftColor($namaShift)
{
if (stripos($namaShift, 'Pagi') !== false)
return '#10b981';
if (stripos($namaShift, 'Siang') !== false)
return '#3b82f6';
if (stripos($namaShift, 'Sore') !== false)
return '#f59e0b';
if (stripos($namaShift, 'Malam') !== false)
return '#6b7280';
return '#6366f1';
}
public function create()
{
$shifts = ShiftKerja::all();
$kantor = Kantor::all();
$divisi = Divisi::all();
$pegawai = User::where('status_aktif', 1)
->with(['kantor', 'jabatan'])
->orderBy('nama_lengkap', 'asc')
->get();
return view('jadwal.create', compact('shifts', 'pegawai', 'kantor', 'divisi'));
}
public function store(Request $request)
{
$request->validate([
'tanggal_mulai' => 'required|date',
'tanggal_selesai' => 'required|date|after_or_equal:tanggal_mulai',
'id_shift' => 'required|exists:shift_kerja,id_shift',
'user_ids' => 'required|array',
'user_ids.*' => 'exists:users,id',
]);
$startDate = Carbon::parse($request->tanggal_mulai);
$endDate = Carbon::parse($request->tanggal_selesai);
$period = \Carbon\CarbonPeriod::create($startDate, $endDate);
$shiftId = $request->id_shift;
$userIds = $request->user_ids;
$count = 0;
$refundCount = 0;
$poinService = new PoinService();
$usersData = \App\Models\User::whereIn('id', $userIds)->get(['id', 'id_kantor'])->keyBy('id');
try {
DB::transaction(function () use ($period, $userIds, $shiftId, &$count, &$refundCount, $poinService, $usersData) {
foreach ($period as $date) {
$dateStr = $date->format('Y-m-d');
$libursDate = \App\Models\HariLibur::where('tanggal', $dateStr)->get();
foreach ($userIds as $userId) {
$user = $usersData->get($userId);
$isLibur = $libursDate->contains(function($libur) use ($user) {
return is_null($libur->id_kantor) || ($user && $libur->id_kantor == $user->id_kantor);
});
if ($isLibur) {
continue;
}
$conflictPoin = PenggunaanPoin::where('id_user', $userId)
->where('tanggal_penggunaan', $dateStr)
->where('id_status', 2)
->first();
if ($conflictPoin) {
$poinService->refundPoin($conflictPoin->id_penggunaan);
$refundCount++;
}
JadwalKerja::updateOrCreate(
['id_user' => $userId, 'tanggal' => $dateStr],
['id_shift' => $shiftId]
);
$count++;
}
}
});
$message = "Berhasil memproses $count jadwal kerja!";
if ($refundCount > 0) {
$message .= " ⚠️ PERINGATAN: $refundCount penggunaan poin dibatalkan otomatis & saldo dikembalikan karena konflik jadwal.";
return redirect()->route('jadwal.index')->with('warning', $message);
}
return redirect()->route('jadwal.index')->with('success', $message);
} catch (\Exception $e) {
return redirect()->back()->with('error', 'Terjadi kesalahan: ' . $e->getMessage());
}
}
public function update(Request $request, $id)
{
$request->validate([
'id_shift' => 'required|exists:shift_kerja,id_shift',
]);
$jadwal = JadwalKerja::findOrFail($id);
$warningMsg = null;
try {
DB::transaction(function () use ($jadwal, $request, &$warningMsg) {
$conflictPoin = PenggunaanPoin::where('id_user', $jadwal->id_user)
->where('tanggal_penggunaan', $jadwal->tanggal)
->where('id_status', 2)
->first();
if ($conflictPoin) {
$poinService = new PoinService();
$poinService->refundPoin($conflictPoin->id_penggunaan);
$warningMsg = "Jadwal diperbarui, namun penggunaan poin user pada tanggal ini telah DIBATALKAN otomatis.";
}
$jadwal->update(['id_shift' => $request->id_shift]);
});
return response()->json([
'success' => true,
'message' => $warningMsg ?? 'Jadwal berhasil diperbarui'
]);
} catch (\Exception $e) {
return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
}
}
public function destroy($id)
{
$jadwal = JadwalKerja::findOrFail($id);
$jadwal->delete();
return response()->json(['success' => true, 'message' => 'Jadwal berhasil dihapus']);
}
public function bulkDestroy(Request $request)
{
$request->validate([
'tanggal_mulai' => 'required|date',
'tanggal_selesai' => 'required|date|after_or_equal:tanggal_mulai',
'user_ids' => 'required|array',
'user_ids.*' => 'exists:users,id',
]);
try {
DB::transaction(function () use ($request) {
JadwalKerja::whereIn('id_user', $request->user_ids)
->whereBetween('tanggal', [$request->tanggal_mulai, $request->tanggal_selesai])
->delete();
});
return redirect()->back()->with('success', 'Jadwal kerja berhasil dihapus pada rentang tanggal tersebut.');
} catch (\Exception $e) {
return redirect()->back()->with('error', 'Terjadi kesalahan saat menghapus jadwal: ' . $e->getMessage());
}
}
public function checkConflicts(Request $request)
{
$userIds = $request->user_ids;
$startDate = $request->tanggal_mulai;
$endDate = $request->tanggal_selesai;
if (empty($userIds) || !$startDate || !$endDate) {
return response()->json(['has_conflict' => false]);
}
$conflicts = PenggunaanPoin::with('user')
->whereIn('id_user', $userIds)
->whereBetween('tanggal_penggunaan', [$startDate, $endDate])
->where('id_status', 2)
->get();
if ($conflicts->count() > 0) {
$details = $conflicts->map(function ($item) {
return "- " . $item->user->nama_lengkap . " (" . date('d M Y', strtotime($item->tanggal_penggunaan)) . ")";
})->join('<br>');
return response()->json([
'has_conflict' => true,
'message' => "Ditemukan <b>{$conflicts->count()} karyawan</b> yang sedang menggunakan Poin (Cuti/Pulang Cepat) pada tanggal ini:<br><br><small>$details</small><br><br>Jika dilanjutkan, <b>Penggunaan Poin akan DIBATALKAN otomatis</b> dan saldo dikembalikan."
]);
}
return response()->json(['has_conflict' => false]);
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace App\Http\Controllers;
use App\Models\Kantor;
use App\Http\Requests\StoreKantorRequest;
use App\Http\Requests\UpdateKantorRequest;
class KantorController extends Controller
{
public function index()
{
$kantor = Kantor::latest()->paginate(10);
return view('kantor.index', compact('kantor'));
}
public function store(StoreKantorRequest $request)
{
Kantor::create($request->validated());
return redirect()->back()->with('success', 'Lokasi kantor berhasil ditambahkan.');
}
public function update(UpdateKantorRequest $request, $id)
{
$kantor = Kantor::findOrFail($id);
$kantor->update($request->validated());
return redirect()->back()->with('success', 'Data kantor berhasil diperbarui.');
}
public function destroy($id)
{
try {
Kantor::findOrFail($id)->delete();
return redirect()->back()->with('success', 'Data kantor berhasil dihapus.');
} catch (\Illuminate\Database\QueryException $e) {
if ($e->getCode() === '23000') {
return redirect()->back()->with('error', 'Kantor tidak bisa dihapus karena masih digunakan oleh data pegawai.');
}
throw $e;
}
}
}

View File

@ -0,0 +1,144 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\Presensi;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\LaporanPresensiExport;
use Barryvdh\DomPDF\Facade\Pdf;
class LaporanController extends Controller
{
public function index(Request $request)
{
$bulan = $request->input('bulan', date('m'));
$tahun = $request->input('tahun', date('Y'));
$divisiId = $request->input('id_divisi');
$rekap = $this->buildRekap($bulan, $tahun, $divisiId);
$divisiList = \App\Models\Divisi::all();
return view('laporan.index', compact('rekap', 'bulan', 'tahun', 'divisiList', 'divisiId'));
}
public function cuti(Request $request)
{
$bulan = $request->input('bulan', date('m'));
$tahun = $request->input('tahun', date('Y'));
$search = $request->input('search');
$divisiId = $request->input('id_divisi');
$query = \App\Models\PengajuanIzin::with(['user.divisi', 'jenisIzin'])
->whereYear('tanggal_mulai', $tahun)
->whereMonth('tanggal_mulai', $bulan);
if ($search) {
$query->whereHas('user', function($q) use ($search) {
$q->where('nama_lengkap', 'like', "%{$search}%")
->orWhere('nik', 'like', "%{$search}%");
});
}
if ($divisiId) {
$query->whereHas('user', function($q) use ($divisiId) {
$q->where('id_divisi', $divisiId);
});
}
$izinList = $query->orderBy('tanggal_mulai', 'desc')->paginate(15)->withQueryString();
$divisiList = \App\Models\Divisi::all();
return view('laporan.izin', compact('izinList', 'bulan', 'tahun', 'search', 'divisiId', 'divisiList'));
}
public function exportExcel(Request $request)
{
$bulan = $request->input('bulan', date('m'));
$tahun = $request->input('tahun', date('Y'));
$divisiId = $request->input('id_divisi');
$rekap = $this->buildRekap($bulan, $tahun, $divisiId);
$filename = "Laporan_Presensi_{$bulan}_{$tahun}.xlsx";
return Excel::download(new LaporanPresensiExport($rekap, $bulan, $tahun), $filename);
}
public function exportPdf(Request $request)
{
$bulan = $request->input('bulan', date('m'));
$tahun = $request->input('tahun', date('Y'));
$divisiId = $request->input('id_divisi');
$rekap = $this->buildRekap($bulan, $tahun, $divisiId);
$pdf = Pdf::loadView('laporan.pdf', compact('rekap', 'bulan', 'tahun'));
$pdf->setPaper('a4', 'landscape');
return $pdf->download("Laporan_Presensi_{$bulan}_{$tahun}.pdf");
}
private function buildRekap(int $bulan, int $tahun, $divisiId = null): array
{
$idTepatWaktu = 1;
$idTerlambat = 2;
$idIzin = 3;
$idSakit = 4;
$idAlpha = 5;
$pegawaiQuery = User::with(['jabatan', 'divisi'])
->where('status_aktif', 1)
->select('id', 'nama_lengkap', 'nik', 'id_jabatan', 'id_divisi')
->orderBy('nama_lengkap', 'asc');
if ($divisiId) {
$pegawaiQuery->where('id_divisi', $divisiId);
}
$pegawai = $pegawaiQuery->get();
$presensiStats = Presensi::whereMonth('tanggal', $bulan)
->whereYear('tanggal', $tahun)
->selectRaw("
id_user,
SUM(CASE WHEN id_status IN (?, ?) THEN 1 ELSE 0 END) as hadir,
SUM(CASE WHEN id_status = ? THEN 1 ELSE 0 END) as terlambat,
SUM(CASE WHEN id_status = ? THEN 1 ELSE 0 END) as alpha,
SUM(CASE WHEN id_status = ? THEN 1 ELSE 0 END) as izin,
SUM(CASE WHEN id_status = ? THEN 1 ELSE 0 END) as sakit
", [$idTepatWaktu, $idTerlambat, $idTerlambat, $idAlpha, $idIzin, $idSakit])
->groupBy('id_user')
->get()
->keyBy('id_user');
$poinStats = DB::table('poin_lembur')
->whereMonth('tanggal', $bulan)
->whereYear('tanggal', $tahun)
->selectRaw('id_user, SUM(jumlah_poin) as total_poin')
->groupBy('id_user')
->pluck('total_poin', 'id_user');
$rekap = [];
foreach ($pegawai as $p) {
$stats = $presensiStats->get($p->id);
$rekap[] = [
'user' => $p,
'hadir' => $stats->hadir ?? 0,
'terlambat' => $stats->terlambat ?? 0,
'alpha' => $stats->alpha ?? 0,
'izin' => $stats->izin ?? 0,
'sakit' => $stats->sakit ?? 0,
'poin_lembur' => $poinStats->get($p->id, 0)
];
}
return $rekap;
}
}

View File

@ -0,0 +1,102 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\Lembur;
use Illuminate\Http\Request;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\LaporanLemburExport;
class LaporanLemburController extends Controller
{
public function index(Request $request)
{
$bulan = $request->input('bulan', date('m'));
$tahun = $request->input('tahun', date('Y'));
$search = $request->input('search');
$divisiId = $request->input('id_divisi');
$query = User::with(['divisi', 'jabatan', 'lemburs' => function($q) use ($bulan, $tahun) {
$q->whereYear('tanggal_lembur', $tahun)
->whereMonth('tanggal_lembur', $bulan)
->where('id_status', 2);
}])
->whereHas('roles', function($q) {
$q->where('nama_role', '!=', 'super_admin');
});
if ($search) {
$query->where(function($q) use ($search) {
$q->where('nama_lengkap', 'like', '%' . $search . '%')
->orWhere('nik', 'like', '%' . $search . '%');
});
}
if ($divisiId) {
$query->where('id_divisi', $divisiId);
}
$pegawai = $query->orderBy('nama_lengkap', 'asc')->paginate(15)->withQueryString();
$divisiList = \App\Models\Divisi::all();
$rekap = [];
foreach ($pegawai as $p) {
$totalMenit = $p->lemburs->sum('durasi_menit');
$jam = floor($totalMenit / 60);
$menit = $totalMenit % 60;
$rekap[$p->id] = [
'total_menit' => $totalMenit,
'format_jam' => "{$jam}j {$menit}m",
'jumlah_hari' => $p->lemburs->count(),
'poin_diperoleh' => $totalMenit,
];
}
return view('laporan-lembur.index', compact('pegawai', 'rekap', 'bulan', 'tahun', 'search', 'divisiId', 'divisiList'));
}
public function exportExcel(Request $request)
{
$bulan = $request->input('bulan', date('m'));
$tahun = $request->input('tahun', date('Y'));
$divisiId = $request->input('id_divisi');
$query = User::with(['divisi', 'jabatan', 'lemburs' => function($q) use ($bulan, $tahun) {
$q->whereYear('tanggal_lembur', $tahun)
->whereMonth('tanggal_lembur', $bulan)
->where('id_status', 2);
}])
->whereHas('roles', function($q) {
$q->where('nama_role', '!=', 'super_admin');
});
if ($divisiId) {
$query->where('id_divisi', $divisiId);
}
$pegawai = $query->orderBy('nama_lengkap', 'asc')->get();
$rekap = [];
foreach ($pegawai as $p) {
$totalMenit = $p->lemburs->sum('durasi_menit');
$jam = floor($totalMenit / 60);
$menit = $totalMenit % 60;
$rekap[$p->id] = [
'total_menit' => $totalMenit,
'format_jam' => "{$jam}j {$menit}m",
'jumlah_hari' => $p->lemburs->count(),
'poin_diperoleh' => $totalMenit,
];
}
$filename = "Laporan_Lembur_{$bulan}_{$tahun}.xlsx";
return Excel::download(new LaporanLemburExport($pegawai, $rekap, $bulan, $tahun), $filename);
}
}

View File

@ -0,0 +1,87 @@
<?php
namespace App\Http\Controllers;
use App\Models\Lembur;
use App\Models\User;
use App\Models\JenisKompensasi;
use App\Services\LemburService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class LemburController extends Controller
{
protected $lemburService;
public function __construct(LemburService $lemburService)
{
$this->lemburService = $lemburService;
}
public function index()
{
$lembur = Lembur::with(['user', 'status', 'kompensasi'])
->orderByRaw("CASE WHEN id_status = 1 THEN 0 ELSE 1 END")
->orderBy('created_at', 'desc')
->paginate(10);
return view('lembur.index', compact('lembur'));
}
public function create()
{
$pegawai = User::where('status_aktif', 1)->orderBy('nama_lengkap', 'asc')->get();
$kompensasi = JenisKompensasi::all();
return view('lembur.create', compact('pegawai', 'kompensasi'));
}
public function store(Request $request)
{
$request->validate([
'id_user' => 'required|exists:users,id',
'tanggal_lembur' => 'required|date',
'jam_mulai' => 'required',
'jam_selesai' => 'required',
'id_kompensasi' => 'required|exists:jenis_kompensasi,id_kompensasi',
'keterangan' => 'nullable|string'
]);
try {
DB::transaction(function () use ($request) {
$user = User::findOrFail($request->id_user);
$lembur = $this->lemburService->createLembur($user, $request->all());
$this->lemburService->approve($lembur);
});
return redirect()->route('lembur.index')->with('success', 'Data lembur manual berhasil ditambahkan dan disetujui otomatis.');
} catch (\Exception $e) {
return redirect()->back()->with('error', 'Gagal menambahkan lembur: ' . $e->getMessage())->withInput();
}
}
public function update(Request $request, Lembur $lembur)
{
$request->validate([
'action' => 'required|in:approve,reject',
'alasan_penolakan' => 'required_if:action,reject|nullable|string'
]);
try {
if ($request->action == 'approve') {
$this->lemburService->approve($lembur);
$message = 'Pengajuan lembur berhasil disetujui.';
if ($lembur->id_kompensasi == 2) {
$message .= ' Poin telah ditambahkan ke karyawan.';
}
} else {
$this->lemburService->reject($lembur, $request->alasan_penolakan);
$message = 'Pengajuan lembur berhasil ditolak.';
}
return redirect()->back()->with('success', $message);
} catch (\Exception $e) {
return redirect()->back()->with('error', 'Gagal memproses pengajuan: ' . $e->getMessage());
}
}
}

View File

@ -0,0 +1,186 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\Divisi;
use App\Models\Jabatan;
use App\Models\Kantor;
use App\Models\Role;
use App\Http\Requests\StorePegawaiRequest;
use App\Http\Requests\UpdatePegawaiRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
class PegawaiController extends Controller
{
public function index(Request $request)
{
$query = User::with(['divisi', 'jabatan', 'kantor', 'roles'])
->whereDoesntHave('roles', function ($q) {
$q->where('nama_role', 'admin');
});
if ($request->filled('filter_jabatan')) {
$query->where('id_jabatan', $request->filter_jabatan);
}
if ($request->filled('filter_kantor')) {
$query->where('id_kantor', $request->filter_kantor);
}
if ($request->filled('filter_status')) {
$status = $request->filter_status == 'active' ? 1 : 0;
$query->where('status_aktif', $status);
}
if ($request->filled('search')) {
$search = $request->search;
$query->where(function ($q) use ($search) {
$q->where('nama_lengkap', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%")
->orWhere('nik', 'like', "%{$search}%");
});
}
$pegawai = $query->latest()->paginate(10)->withQueryString();
$allKantor = Kantor::withCount([
'users as total_pegawai' => function ($q) {
$q->whereDoesntHave('roles', function ($sq) {
$sq->where('nama_role', 'admin');
});
}
])->get();
$stats = [
'kantor_list' => $allKantor,
'total' => User::whereDoesntHave('roles', function ($q) {
$q->where('nama_role', 'admin');
})->count(),
'active' => User::where('status_aktif', 1)
->whereDoesntHave('roles', function ($q) {
$q->where('nama_role', 'admin');
})->count(),
];
$allJabatan = Jabatan::all();
return view('pegawai.index', compact('pegawai', 'stats', 'allJabatan', 'allKantor'));
}
public function store(StorePegawaiRequest $request)
{
$data = $request->validated();
if ($request->hasFile('foto')) {
$data['foto'] = $request->file('foto')->store('foto-profil', 'public');
}
$data['password'] = Hash::make('Mpg123!');
$data['status_aktif'] = 1;
$data['sisa_cuti'] = 12;
$user = User::create($data);
if ($request->filled('id_role')) {
$user->roles()->attach($request->id_role);
}
return redirect()->route('pegawai.index')->with('success', 'Pegawai baru berhasil ditambahkan.');
}
public function update(UpdatePegawaiRequest $request, $id)
{
$pegawai = User::findOrFail($id);
$data = $request->validated();
if ($request->hasFile('foto')) {
if ($pegawai->foto) {
Storage::disk('public')->delete($pegawai->foto);
}
$data['foto'] = $request->file('foto')->store('foto-profil', 'public');
}
$pegawai->update($data);
if ($request->filled('id_role')) {
$pegawai->roles()->sync([$request->id_role]);
}
return redirect()->route('pegawai.index')->with('success', 'Data pegawai berhasil diperbarui.');
}
public function create()
{
$divisi = Divisi::all();
$jabatan = Jabatan::all();
$kantor = Kantor::all();
$roles = Role::all();
return view('pegawai.create', compact('divisi', 'jabatan', 'kantor', 'roles'));
}
public function show($id)
{
$pegawai = User::with(['divisi', 'jabatan', 'kantor', 'roles'])->findOrFail($id);
$sisaPoin = \App\Models\PoinLembur::where('id_user', $id)
->where('is_fully_used', false)
->whereDate('expired_at', '>=', now()->toDateString())
->sum('sisa_poin');
$historyTambah = \App\Models\PoinLembur::where('id_user', $id)
->select('id_poin as id', 'tanggal', 'jumlah_poin as jumlah', 'keterangan', \DB::raw("'penambahan' as tipe"))
->orderBy('tanggal', 'desc')
->take(10)
->get();
$historyKurang = \App\Models\PenggunaanPoin::where('id_user', $id)
->where('id_status', 1)
->select('id_penggunaan as id', 'tanggal_penggunaan as tanggal', 'jumlah_poin as jumlah', \DB::raw("'Penggunaan Poin' as keterangan"), \DB::raw("'pengurangan' as tipe"))
->orderBy('tanggal_penggunaan', 'desc')
->take(10)
->get();
$historyPoin = $historyTambah->concat($historyKurang)->sortByDesc('tanggal')->take(10);
$riwayatPresensi = \App\Models\Presensi::with(['status', 'jadwal.shift'])
->where('id_user', $id)
->whereDate('tanggal', '>=', now()->subDays(30)->toDateString())
->orderBy('tanggal', 'desc')
->get();
$riwayatIzin = \App\Models\PengajuanIzin::with('jenisIzin')
->where('id_user', $id)
->whereDate('tanggal_mulai', '>=', now()->subDays(30)->toDateString())
->orderBy('tanggal_mulai', 'desc')
->get();
return view('pegawai.show', compact('pegawai', 'sisaPoin', 'historyPoin', 'riwayatPresensi', 'riwayatIzin'));
}
public function edit($id)
{
$pegawai = User::findOrFail($id);
$divisi = Divisi::all();
$jabatan = Jabatan::all();
$kantor = Kantor::all();
$roles = Role::all();
return view('pegawai.edit', compact('pegawai', 'divisi', 'jabatan', 'kantor', 'roles'));
}
public function destroy($id)
{
$pegawai = User::findOrFail($id);
if ($pegawai->status_aktif == 0) {
$pegawai->delete();
return redirect()->route('pegawai.index')->with('success', 'Data pegawai berhasil dihapus (Soft Delete).');
}
$pegawai->update(['status_aktif' => 0]);
return redirect()->route('pegawai.index')->with('success', 'Status pegawai telah diubah menjadi Non-Aktif (Resigned). Histori data tetap tersimpan.');
}
}

View File

@ -0,0 +1,197 @@
<?php
namespace App\Http\Controllers;
use App\Models\PengajuanIzin;
use App\Models\Presensi;
use App\Models\JenisIzin;
use App\Models\User;
use App\Models\SuratIzin;
use App\Models\TandaTangan;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
class PengajuanIzinController extends Controller
{
public function index(Request $request)
{
$statusId = $request->get('status');
$query = PengajuanIzin::with(['user', 'jenisIzin', 'statusPengajuan', 'suratIzin']);
if ($statusId) {
$query->where('id_status', $statusId);
}
if ($request->has('search') && $request->search != '') {
$query->whereHas('user', function ($q) use ($request) {
$q->where('nama_lengkap', 'like', '%' . $request->search . '%');
});
}
$izin = $query->orderBy('created_at', 'desc')->paginate(15);
$statuses = \App\Models\StatusPengajuan::all();
$jenisIzin = \App\Models\JenisIzin::all();
$users = \App\Models\User::with(['kantor', 'divisi', 'jabatan'])->select('id', 'nama_lengkap', 'nik', 'id_kantor', 'id_divisi', 'id_jabatan')->orderBy('nama_lengkap', 'asc')->get();
$kantor = \App\Models\Kantor::all();
$divisi = \App\Models\Divisi::all();
return view('izin.index', compact('izin', 'statusId', 'statuses', 'jenisIzin', 'users', 'kantor', 'divisi'));
}
public function store(Request $request)
{
$request->validate([
'id_user' => 'required|exists:users,id',
'id_jenis_izin' => 'required|exists:jenis_izin,id_jenis_izin',
'tanggal_mulai' => 'required|date',
'tanggal_selesai' => 'required|date|after_or_equal:tanggal_mulai',
'alasan' => 'required|string',
'bukti_file' => 'required|image|mimes:jpeg,png,jpg,pdf|max:2048'
]);
$jenisIzin = JenisIzin::find($request->id_jenis_izin);
if ($jenisIzin && $jenisIzin->nama_izin == 'Cuti') {
$diffParams = Carbon::parse($request->tanggal_mulai, 'Asia/Jakarta')->diffInDays(Carbon::now('Asia/Jakarta'));
if (Carbon::parse($request->tanggal_mulai, 'Asia/Jakarta')->diffInDays(Carbon::now('Asia/Jakarta')) < 7) {
return back()->with('error', 'Pengajuan Cuti minimal H-7!');
}
}
try {
DB::beginTransaction();
$path = null;
if ($request->hasFile('bukti_file')) {
$path = $request->file('bukti_file')->store('uploads/izin', 'public');
}
$izin = PengajuanIzin::create([
'id_user' => $request->id_user,
'id_jenis_izin' => $request->id_jenis_izin,
'tanggal_mulai' => $request->tanggal_mulai,
'tanggal_selesai' => $request->tanggal_selesai,
'alasan' => $request->alasan,
'bukti_file' => $path,
'id_status' => 1
]);
if ($izin->id_jenis_izin == 2) {
$user = User::with(['jabatan', 'divisi'])->find($request->id_user);
$ttdAktif = TandaTangan::where('id_user', $user->id)->active()->first();
$tglMulai = Carbon::parse($request->tanggal_mulai)->translatedFormat('d F Y');
$tglSelesai = Carbon::parse($request->tanggal_selesai)->translatedFormat('d F Y');
$isiSurat = "Dengan hormat,\n\n"
. "Saya yang bertanda tangan di bawah ini:\n"
. "Nama: {$user->nama_lengkap}\n"
. "NIK: {$user->nik}\n"
. "Jabatan: " . ($user->jabatan->nama_jabatan ?? '-') . "\n"
. "Divisi: " . ($user->divisi->nama_divisi ?? '-') . "\n\n"
. "Dengan ini mengajukan Cuti mulai tanggal {$tglMulai} sampai dengan {$tglSelesai}.\n\n"
. "Alasan: {$request->alasan}\n\n"
. "Demikian surat ini saya buat dengan sebenar-benarnya. "
. "Atas perhatian dan persetujuannya, saya ucapkan terima kasih.";
SuratIzin::create([
'id_izin' => $izin->id_izin,
'id_user' => $user->id,
'id_ttd_pengaju' => $ttdAktif?->id_tanda_tangan,
'isi_surat' => $isiSurat,
'status_surat' => 'menunggu_manajer',
]);
}
DB::commit();
return redirect()->back()->with('success', 'Pengajuan izin berhasil dibuat.');
} catch (\Exception $e) {
DB::rollBack();
return redirect()->back()->with('error', 'Gagal membuat izin: ' . $e->getMessage());
}
}
public function approve($id)
{
try {
DB::beginTransaction();
$izin = PengajuanIzin::with('jenisIzin')->findOrFail($id);
if ($izin->id_status !== 1) {
return redirect()->back()->with('error', 'Pengajuan ini sudah diproses sebelumnya.');
}
if ($izin->id_jenis_izin == 2) {
return redirect()->back()->with('error', 'Pengajuan Cuti harus disetujui melalui menu Surat Izin (Manajer → HRD).');
}
$izin->update(['id_status' => 2]);
$statusPresensiId = 3;
if ($izin->id_jenis_izin == 1) {
$statusPresensiId = 4;
}
$startDate = Carbon::parse($izin->tanggal_mulai, 'Asia/Jakarta');
$endDate = Carbon::parse($izin->tanggal_selesai, 'Asia/Jakarta');
while ($startDate->lte($endDate)) {
$tanggal = $startDate->format('Y-m-d');
Presensi::updateOrCreate(
[
'id_user' => $izin->id_user,
'tanggal' => $tanggal
],
[
'id_status' => $statusPresensiId,
'jam_masuk' => null,
'jam_pulang' => null,
'id_validasi' => 1,
'alasan_telat' => $izin->jenisIzin->nama_izin . ': ' . $izin->alasan
]
);
$startDate->addDay();
}
if ($izin->id_jenis_izin == 2) {
$user = \App\Models\User::find($izin->id_user);
$jumlahHari = Carbon::parse($izin->tanggal_mulai)->diffInDays(Carbon::parse($izin->tanggal_selesai)) + 1;
$user->decrement('sisa_cuti', $jumlahHari);
}
DB::commit();
return redirect()->back()->with('success', 'Izin disetujui & data presensi diperbarui.');
} catch (\Exception $e) {
DB::rollBack();
return redirect()->back()->with('error', 'Gagal: ' . $e->getMessage());
}
}
public function reject(Request $request, $id)
{
$izin = PengajuanIzin::findOrFail($id);
if ($izin->id_status !== 1) {
return redirect()->back()->with('error', 'Pengajuan ini sudah diproses sebelumnya.');
}
if ($izin->id_jenis_izin == 2) {
return redirect()->back()->with('error', 'Pengajuan Cuti harus ditolak melalui menu Surat Izin.');
}
$izin->update([
'id_status' => 3
]);
return redirect()->back()->with('success', 'Pengajuan izin berhasil ditolak.');
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\Http\Controllers;
use App\Models\PenggunaanPoin;
use App\Services\PoinService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class PenggunaanPoinController extends Controller
{
protected $poinService;
public function __construct(PoinService $poinService)
{
$this->poinService = $poinService;
}
public function index()
{
$penggunaan = PenggunaanPoin::with(['user', 'jenisPengurangan', 'status'])
->orderByRaw("CASE WHEN id_status = 1 THEN 0 ELSE 1 END")
->orderBy('tanggal_diajukan', 'desc')
->paginate(10);
return view('penggunaan-poin.index', compact('penggunaan'));
}
public function update(Request $request, $id)
{
$request->validate([
'action' => 'required|in:approve,reject',
'alasan_penolakan' => 'required_if:action,reject|nullable|string'
]);
$penggunaan = PenggunaanPoin::findOrFail($id);
try {
if ($request->action == 'approve') {
DB::transaction(function () use ($penggunaan) {
$penggunaan->update(['id_status' => 2]);
$this->poinService->deductPoin(
$penggunaan->id_user,
$penggunaan->jumlah_poin,
$penggunaan->id_penggunaan
);
});
$message = 'Pengajuan berhasil disetujui dan poin telah dipotong.';
} else {
$penggunaan->update([
'id_status' => 3,
'alasan_penolakan' => $request->alasan_penolakan
]);
$message = 'Pengajuan berhasil ditolak.';
}
return redirect()->back()->with('success', $message);
} catch (\Exception $e) {
return redirect()->back()->with('error', 'Gagal memproses: ' . $e->getMessage());
}
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StorePengumumanRequest;
use App\Http\Requests\UpdatePengumumanRequest;
use App\Models\Pengumuman;
use Illuminate\Support\Facades\Auth;
class PengumumanController extends Controller
{
public function index()
{
$pengumuman = Pengumuman::with('pembuat')
->orderBy('tanggal', 'desc')
->paginate(10);
$title = 'Pengumuman';
return view('pengumuman.index', compact('pengumuman', 'title'));
}
public function create()
{
$title = 'Buat Pengumuman';
return view('pengumuman.create', compact('title'));
}
public function store(StorePengumumanRequest $request)
{
$data = $request->validated();
$data['dibuat_oleh'] = Auth::id();
Pengumuman::create($data);
return redirect()->route('pengumuman.index')
->with('success', 'Pengumuman berhasil ditambahkan.');
}
public function edit(Pengumuman $pengumuman)
{
$title = 'Edit Pengumuman';
return view('pengumuman.edit', compact('pengumuman', 'title'));
}
public function update(UpdatePengumumanRequest $request, Pengumuman $pengumuman)
{
$pengumuman->update($request->validated());
return redirect()->route('pengumuman.index')
->with('success', 'Pengumuman berhasil diperbarui.');
}
public function destroy(Pengumuman $pengumuman)
{
$pengumuman->delete();
return redirect()->route('pengumuman.index')
->with('success', 'Pengumuman berhasil dihapus.');
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers;
use App\Models\Role;
use App\Models\Permission;
use Illuminate\Http\Request;
class PermissionController extends Controller
{
public function index()
{
$permissions = Permission::all();
$roles = Role::with('permissions')->get();
return view('permission.index', compact('permissions', 'roles'));
}
public function store(Request $request)
{
$request->validate([
'nama_permission' => 'required|unique:permissions,nama_permission',
]);
Permission::create([
'nama_permission' => $request->nama_permission,
'slug' => \Illuminate\Support\Str::slug($request->nama_permission)
]);
return back()->with('success', 'Permission berhasil dibuat.');
}
public function sync(Request $request, $id_role)
{
$role = Role::findOrFail($id_role);
$role->permissions()->sync($request->permissions);
return back()->with('success', 'Hak akses role berhasil diperbarui.');
}
}

View File

@ -0,0 +1,471 @@
<?php
namespace App\Http\Controllers;
use App\Models\Presensi;
use App\Models\JadwalKerja;
use App\Models\Kantor;
use Illuminate\Http\Request;
use App\Http\Requests\StorePresensiRequest;
use App\Http\Requests\StoreManualPresensiRequest;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class PresensiController extends Controller
{
public function index(Request $request)
{
$tanggal = $request->get('tanggal', Carbon::today()->format('Y-m-d'));
$divisiId = $request->get('divisi_id');
$pendingDates = Presensi::where('id_validasi', 2)
->selectRaw('DATE(tanggal) as tgl, COUNT(*) as jumlah')
->groupBy('tgl')
->orderBy('tgl', 'desc')
->get();
$query = Presensi::with(['user.jabatan', 'user.kantor'])
->whereDate('tanggal', $tanggal);
if ($divisiId) {
$query->whereHas('user', function ($q) use ($divisiId) {
$q->where('id_divisi', $divisiId);
});
}
$filterStatus = $request->get('filter_status');
if ($filterStatus == 'tepat_waktu') {
$query->where('id_status', 1);
} elseif ($filterStatus == 'terlambat') {
$query->where('id_status', 2);
} elseif ($filterStatus == 'pending') {
$query->where('id_validasi', 2);
}
$presensi = $query->orderBy('jam_masuk', 'desc')->paginate(15);
$divisi = \App\Models\Divisi::all();
$userIds = $presensi->pluck('id_user')->unique();
$jadwalCollection = JadwalKerja::with('shift')
->whereIn('id_user', $userIds)
->where('tanggal', $tanggal)
->get()
->keyBy('id_user');
$poinCollection = \App\Models\PenggunaanPoin::with('jenisPengurangan')
->whereIn('id_user', $userIds)
->where('tanggal_penggunaan', $tanggal)
->where('id_status', 2)
->get()
->keyBy('id_user');
$presensi->map(function ($item) use ($jadwalCollection, $poinCollection) {
$jadwal = $jadwalCollection->get($item->id_user);
$poinUsed = $poinCollection->get($item->id_user);
$jamMasuk = $jadwal && $jadwal->shift ? $jadwal->shift->jam_mulai : '-';
$jamPulang = $jadwal && $jadwal->shift ? $jadwal->shift->jam_selesai : '-';
$item->jam_jadwal_masuk_original = $jamMasuk;
$item->jam_jadwal_pulang_original = $jamPulang;
$item->is_adjusted = false;
$item->adjustment_note = null;
if ($poinUsed) {
$menit = $poinUsed->jumlah_poin * 30;
$jenis = $poinUsed->jenisPengurangan->nama_pengurangan ?? '';
if ($jenis == 'Pulang Cepat' && $jamPulang != '-') {
$jamPulang = Carbon::parse($jamPulang)->subMinutes($menit)->format('H:i:s');
$item->is_adjusted = true;
$item->adjustment_note = "Pulang Cepat -{$menit}m";
} elseif ($jenis == 'Datang Terlambat' && $jamMasuk != '-') {
$jamMasuk = Carbon::parse($jamMasuk)->addMinutes($menit)->format('H:i:s');
$item->is_adjusted = true;
$item->adjustment_note = "Datang Telat +{$menit}m";
}
}
$item->jam_jadwal_masuk = $jamMasuk;
$item->jam_jadwal_pulang = $jamPulang;
$item->nama_shift = $jadwal && $jadwal->shift ? $jadwal->shift->nama_shift : 'Non-Shift';
$item->status_keterlambatan = 'Tepat Waktu';
$item->badge_color = 'success';
if ($item->id_status == 2) {
$item->status_keterlambatan = $item->alasan_telat ?? 'Terlambat';
$item->badge_color = 'rose';
} elseif ($item->id_status == 1) {
$item->status_keterlambatan = 'Tepat Waktu';
$item->badge_color = 'emerald';
}
$item->validasi_label = $item->id_validasi == 1 ? 'Disetujui' : ($item->id_validasi == 3 ? 'Ditolak' : 'Pending');
$item->validasi_color = $item->id_validasi == 1 ? 'emerald' : ($item->id_validasi == 3 ? 'rose' : 'amber');
$item->jarak_masuk = null;
if ($item->user && $item->user->kantor && $item->lat_masuk && $item->lon_masuk) {
$item->jarak_masuk = app(self::class)->calculateDistance(
$item->lat_masuk,
$item->lon_masuk,
$item->user->kantor->latitude,
$item->user->kantor->longitude
);
$item->jarak_masuk = round($item->jarak_masuk, 2);
}
$item->dalam_radius = false;
if ($item->jarak_masuk !== null && $item->user && $item->user->kantor) {
$item->dalam_radius = $item->jarak_masuk <= $item->user->kantor->radius;
}
return $item;
});
return view('presensi.index', compact('presensi', 'tanggal', 'divisi', 'pendingDates'));
}
public function approve($id)
{
$presensi = Presensi::findOrFail($id);
$presensi->update(['id_validasi' => 1]);
return redirect()->back()->with('success', 'Presensi berhasil disetujui.');
}
public function reject($id)
{
$presensi = Presensi::findOrFail($id);
$presensi->update(['id_validasi' => 3]);
return redirect()->back()->with('success', 'Presensi berhasil ditolak.');
}
public function bulkAction(Request $request)
{
$request->validate([
'presensi_ids' => 'required|array',
'action' => 'required|in:approve,reject'
]);
$statusValidasi = $request->action == 'approve' ? 1 : 3;
Presensi::whereIn('id_presensi', $request->presensi_ids)
->where('id_validasi', 2)
->update(['id_validasi' => $statusValidasi]);
$statusText = $request->action == 'approve' ? 'disetujui' : 'ditolak';
return redirect()->back()->with('success', count($request->presensi_ids) . " pengajuan presensi berhasil {$statusText}.");
}
public function create()
{
$pegawai = \App\Models\User::with('divisi')
->orderBy('nama_lengkap', 'asc')
->get();
$statuses = \Illuminate\Support\Facades\DB::table('status_presensi')->get();
return view('presensi.create', compact('pegawai', 'statuses'));
}
public function storeManual(StoreManualPresensiRequest $request)
{
$existing = Presensi::where('id_user', $request->id_user)
->where('tanggal', $request->tanggal)
->first();
if ($existing) {
return redirect()->back()
->withInput()
->with('error', 'Pegawai bersangkutan sudah memiliki record presensi pada tanggal tersebut. Gunakan fitur Edit (Koreksi) jika ingin mengubah data.');
}
$jamMasuk = $request->jam_masuk ? $request->jam_masuk . ':00' : null;
$jamPulang = $request->jam_pulang ? $request->jam_pulang . ':00' : null;
Presensi::create([
'id_user' => $request->id_user,
'tanggal' => $request->tanggal,
'id_status' => $request->id_status,
'jam_masuk' => in_array($request->id_status, [1, 2]) ? $jamMasuk : null,
'jam_pulang' => in_array($request->id_status, [1, 2]) ? $jamPulang : null,
'alasan_telat' => $request->alasan_telat ?? 'Input Manual oleh Admin',
'id_validasi' => 1,
'verifikasi_wajah' => 1,
'lat_masuk' => null,
'lon_masuk' => null,
'lat_pulang' => null,
'lon_pulang' => null,
'keterangan_luar_radius' => 'Input Manual oleh Admin',
'foto_wajah_masuk' => null,
'foto_wajah_pulang' => null,
]);
return redirect()->route('presensi.index', ['tanggal' => $request->tanggal])
->with('success', 'Presensi manual berhasil ditambahkan.');
}
public function edit($id)
{
$presensi = Presensi::with('user.divisi')->findOrFail($id);
$statuses = \Illuminate\Support\Facades\DB::table('status_presensi')->get();
return view('presensi.edit', compact('presensi', 'statuses'));
}
public function updateManual(Request $request, $id)
{
$request->validate([
'id_status' => 'required|exists:status_presensi,id_status',
'jam_masuk' => [
\Illuminate\Validation\Rule::requiredIf(fn () => in_array($request->id_status, [1, 2])),
'nullable',
'date_format:H:i'
],
'jam_pulang' => 'nullable|date_format:H:i',
'alasan_telat' => 'required|string|max:255'
], [
'alasan_telat.required' => 'Catatan koreksi wajib diisi sebagai dokumentasi log.',
'jam_masuk.required' => 'Jam masuk wajib diisi untuk kehadiran Hadir/Terlambat.'
]);
$presensi = Presensi::findOrFail($id);
$jamMasuk = $request->jam_masuk ? $request->jam_masuk . ':00' : null;
$jamPulang = $request->jam_pulang ? $request->jam_pulang . ':00' : null;
$isAdjusted = false;
$adjustmentNotes = [];
if ($presensi->jam_masuk != $jamMasuk && in_array($request->id_status, [1, 2])) {
$isAdjusted = true;
$adjustmentNotes[] = "Datang dari " . substr($presensi->jam_masuk ?? '-', 0, 5) . " ke " . substr($jamMasuk, 0, 5);
}
if ($presensi->jam_pulang != $jamPulang && in_array($request->id_status, [1, 2])) {
$isAdjusted = true;
$adjustmentNotes[] = "Pulang dari " . substr($presensi->jam_pulang ?? '-', 0, 5) . " ke " . substr($jamPulang, 0, 5);
}
$presensi->update([
'id_status' => $request->id_status,
'jam_masuk' => in_array($request->id_status, [1, 2]) ? $jamMasuk : null,
'jam_pulang' => in_array($request->id_status, [1, 2]) ? $jamPulang : null,
'alasan_telat' => $request->alasan_telat . ($isAdjusted ? " | [Koreksi Admin: " . implode(', ', $adjustmentNotes) . "]" : ""),
'id_validasi' => 1,
]);
return redirect()->route('presensi.index', ['tanggal' => $presensi->tanggal])
->with('success', 'Data presensi berhasil dikoreksi.');
}
public function store(StorePresensiRequest $request)
{
$user = Auth::user();
$hariIni = Carbon::today()->toDateString();
$jamSekarang = Carbon::now();
$status = $request->status;
if ($status === 'pulang') {
return $this->handlePulang($request, $user, $hariIni, $jamSekarang);
}
$jadwal = JadwalKerja::with('shift')
->where('id_user', $user->id)
->where('tanggal', $hariIni)
->first();
if (!$jadwal) {
return response()->json([
'success' => false,
'message' => 'Anda tidak memiliki jadwal hari ini.'
], 403);
}
$cekPresensi = Presensi::where('id_user', $user->id)
->where('tanggal', $hariIni)
->first();
if ($cekPresensi) {
return response()->json([
'success' => false,
'message' => 'Anda sudah melakukan absen masuk hari ini.'
], 400);
}
$kantor = $user->kantor;
if (!$kantor) {
return response()->json([
'success' => false,
'message' => 'Data kantor belum diatur untuk akun Anda.'
], 400);
}
$jarak = $this->calculateDistance(
$request->latitude,
$request->longitude,
$kantor->latitude,
$kantor->longitude
);
$radiusKantor = $kantor->radius;
$idValidasi = ($jarak <= $radiusKantor) ? 1 : 2;
$presensiService = new \App\Services\PresensiService();
$dynamicSchedule = $presensiService->getDynamicSchedule($user->id, $hariIni);
$jamMasukEfektif = $dynamicSchedule
? $dynamicSchedule['jam_masuk']
: $jadwal->shift->jam_mulai;
$statusJadwal = $dynamicSchedule['status_jadwal'] ?? 'Normal';
$jamMasukParsed = Carbon::parse($jamMasukEfektif, 'Asia/Jakarta');
$toleransi = 15;
$batasToleransi = $jamMasukParsed->copy()->addMinutes($toleransi);
$idStatus = 1;
$alasanTelat = null;
if ($jamSekarang->greaterThan($batasToleransi)) {
$idStatus = 2;
$selisihMenit = $jamSekarang->diffInMinutes($jamMasukParsed);
$alasanTelat = "Terlambat {$selisihMenit} menit";
}
$fotoFile = $request->file('foto');
$imageName = 'masuk_' . $user->id . '_' . time() . '.' . $fotoFile->getClientOriginalExtension();
$fotoFile->storeAs('uploads/absensi', $imageName, 'public');
$verifikasiWajah = 0;
$faceMessage = null;
try {
$faceService = new \App\Services\FaceRecognitionService();
$result = $faceService->verifyFace($user->id, $fotoFile);
if (isset($result['match']) && $result['match'] === true) {
$verifikasiWajah = 1;
}
$faceMessage = $result['message'] ?? null;
} catch (\Exception $e) {
}
$presensi = Presensi::create([
'id_user' => $user->id,
'tanggal' => $hariIni,
'jam_masuk' => $jamSekarang->format('H:i:s'),
'lat_masuk' => $request->latitude,
'lon_masuk' => $request->longitude,
'foto_wajah_masuk' => 'uploads/absensi/' . $imageName,
'id_status' => $idStatus,
'alasan_telat' => $alasanTelat,
'id_validasi' => $idValidasi,
'keterangan_luar_radius' => $request->keterangan_luar_radius,
'verifikasi_wajah' => $verifikasiWajah,
]);
return response()->json([
'success' => true,
'status' => 'success',
'message' => 'Absen masuk berhasil!',
'data' => [
'jam_masuk' => $jamSekarang->format('H:i'),
'status' => ($idStatus == 1) ? 'Tepat Waktu' : 'Terlambat',
'jarak' => round($jarak, 2) . ' meter',
'validasi_lokasi' => ($idValidasi == 1) ? 'Dalam Radius' : 'Di Luar Radius (Pending)',
'verifikasi_wajah' => $verifikasiWajah == 1 ? 'Terverifikasi' : 'Pending',
'face_message' => $faceMessage,
]
], 200);
}
private function handlePulang($request, $user, $hariIni, $jamSekarang)
{
$presensi = Presensi::where('id_user', $user->id)
->whereNull('jam_pulang')
->latest()
->first();
if (!$presensi) {
return response()->json([
'success' => false,
'message' => 'Anda belum absen masuk atau sudah absen pulang.'
], 404);
}
$fotoFile = $request->file('foto');
$imageName = 'pulang_' . $user->id . '_' . time() . '.' . $fotoFile->getClientOriginalExtension();
$fotoFile->storeAs('uploads/absensi', $imageName, 'public');
$verifikasiPulang = 0;
try {
$faceService = new \App\Services\FaceRecognitionService();
$result = $faceService->verifyFace($user->id, $fotoFile);
if (isset($result['match']) && $result['match'] === true) {
$verifikasiPulang = 1;
}
} catch (\Exception $e) {
}
$presensiService = new \App\Services\PresensiService();
$dynamicSchedule = $presensiService->getDynamicSchedule($user->id, $hariIni);
$jamPulangEfektif = $dynamicSchedule
? $dynamicSchedule['jam_pulang']
: null;
$waktuPulangAwal = null;
$waktuPulangAkhir = null;
if ($jamPulangEfektif) {
$jamPulangTarget = Carbon::parse($jamPulangEfektif, 'Asia/Jakarta');
if ($jamSekarang->lessThan($jamPulangTarget)) {
$waktuPulangAwal = $jamSekarang->diff($jamPulangTarget)->format('%H:%I:%S');
} else if ($jamSekarang->greaterThan($jamPulangTarget)) {
$waktuPulangAkhir = $jamSekarang->diff($jamPulangTarget)->format('%H:%I:%S');
}
}
$presensi->update([
'jam_pulang' => $jamSekarang->format('H:i:s'),
'lat_pulang' => $request->latitude,
'lon_pulang' => $request->longitude,
'foto_wajah_pulang' => 'uploads/absensi/' . $imageName,
'waktu_pulang_awal' => $waktuPulangAwal,
'waktu_pulang_akhir' => $waktuPulangAkhir,
]);
return response()->json([
'success' => true,
'status' => 'success',
'message' => 'Absen pulang berhasil. Hati-hati di jalan!',
'data' => [
'jam_pulang' => $jamSekarang->format('H:i'),
'verifikasi_wajah' => $verifikasiPulang == 1 ? 'Terverifikasi' : 'Pending',
]
], 200);
}
private function calculateDistance($lat1, $lon1, $lat2, $lon2)
{
$earthRadius = 6371000;
$dLat = deg2rad($lat2 - $lat1);
$dLon = deg2rad($lon2 - $lon1);
$a = sin($dLat / 2) * sin($dLat / 2) +
cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
sin($dLon / 2) * sin($dLon / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
return $earthRadius * $c;
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rule;
class ProfileController extends Controller
{
public function edit()
{
$user = Auth::user();
return view('profile.edit', compact('user'));
}
public function update(Request $request)
{
$user = Auth::user();
$request->validate([
'nama_lengkap' => 'required|string|max:255',
'email' => ['required', 'string', 'email', 'max:255', Rule::unique('users')->ignore($user->id)],
'foto' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048',
'password' => 'nullable|string|min:8|confirmed',
]);
$data = [
'nama_lengkap' => $request->nama_lengkap,
'email' => $request->email,
];
if ($request->filled('password')) {
$data['password'] = Hash::make($request->password);
}
if ($request->hasFile('foto')) {
if ($user->foto) {
Storage::disk('public')->delete($user->foto);
}
$data['foto'] = $request->file('foto')->store('foto-profil', 'public');
}
$user->update($data);
return redirect()->route('profile.edit')->with('success', 'Profil berhasil diperbarui.');
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace App\Http\Controllers;
use App\Models\Role;
use App\Models\User;
use App\Http\Requests\StoreRoleRequest;
use App\Http\Requests\UpdateRoleRequest;
use Illuminate\Http\Request;
class RoleController extends Controller
{
public function index(Request $request)
{
$query = Role::with(['permissions', 'users']);
if ($request->filled('search')) {
$query->where('nama_role', 'like', '%' . $request->search . '%');
}
$roles = $query->latest('id_role')->paginate(10)->withQueryString();
$allPermissions = \App\Models\Permission::orderBy('nama_permission', 'asc')->get();
return view('role.index', compact('roles', 'allPermissions'));
}
public function store(StoreRoleRequest $request)
{
$role = Role::create($request->validated());
if ($request->has('id_permissions')) {
$role->permissions()->sync($request->id_permissions);
}
return redirect()->route('role.index')
->with('success', 'Role baru berhasil ditambahkan.');
}
public function update(UpdateRoleRequest $request, $id)
{
$role = Role::findOrFail($id);
$role->update($request->validated());
if ($request->has('id_permissions')) {
$role->permissions()->sync($request->id_permissions);
} else {
$role->permissions()->detach();
}
return redirect()->route('role.index')
->with('success', 'Role berhasil diperbarui.');
}
public function destroy($id)
{
$role = Role::findOrFail($id);
if ($role->users()->exists()) {
return redirect()->back()->with('error', 'Role tidak bisa dihapus karena masih digunakan user.');
}
$role->delete();
return redirect()->route('role.index')
->with('success', 'Role berhasil dihapus.');
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace App\Http\Controllers;
use App\Models\ShiftKerja;
use App\Http\Requests\StoreShiftRequest;
use App\Http\Requests\UpdateShiftRequest;
use Illuminate\Http\Request;
class ShiftController extends Controller
{
public function index(Request $request)
{
$query = ShiftKerja::withCount('jadwalKerja');
if ($request->filled('search')) {
$query->where('nama_shift', 'like', '%' . $request->search . '%');
}
$shifts = $query->latest('id_shift')->paginate(10)->withQueryString();
return view('shift.index', compact('shifts'));
}
public function store(StoreShiftRequest $request)
{
ShiftKerja::create($request->validated());
return redirect()->route('shift.index')
->with('success', 'Shift kerja berhasil ditambahkan.');
}
public function update(UpdateShiftRequest $request, $id)
{
$shift = ShiftKerja::findOrFail($id);
$shift->update($request->validated());
return redirect()->route('shift.index')
->with('success', 'Shift kerja berhasil diperbarui.');
}
public function destroy($id)
{
$shift = ShiftKerja::findOrFail($id);
if ($shift->jadwalKerja()->exists()) {
return redirect()->back()
->with('error', 'Shift tidak bisa dihapus karena masih digunakan di jadwal.');
}
$shift->delete();
return redirect()->route('shift.index')
->with('success', 'Shift kerja berhasil dihapus.');
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreSignatureRequest;
use App\Models\TandaTangan;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
class SignatureController extends Controller
{
public function show()
{
$ttd = TandaTangan::where('id_user', Auth::id())->active()->first();
return view('signature.show', compact('ttd'));
}
public function store(StoreSignatureRequest $request)
{
try {
$base64 = $request->signature_data;
if (str_contains($base64, ',')) {
$base64 = explode(',', $base64)[1];
}
$imageData = base64_decode($base64);
if ($imageData === false) {
return back()->with('error', 'Data tanda tangan tidak valid.');
}
$fileName = 'uploads/ttd/' . Auth::id() . '_' . time() . '.png';
Storage::disk('public')->put($fileName, $imageData);
TandaTangan::where('id_user', Auth::id())->update(['is_active' => false]);
TandaTangan::create([
'id_user' => Auth::id(),
'file_ttd' => $fileName,
'is_active' => true,
]);
return back()->with('success', 'Tanda tangan berhasil disimpan.');
} catch (\Exception $e) {
return back()->with('error', 'Gagal menyimpan tanda tangan: ' . $e->getMessage());
}
}
}

View File

@ -0,0 +1,218 @@
<?php
namespace App\Http\Controllers;
use App\Models\SuratIzin;
use App\Models\ApprovalSurat;
use App\Models\TandaTangan;
use App\Models\Presensi;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
class SuratIzinController extends Controller
{
public function index(Request $request)
{
$query = SuratIzin::with(['user', 'pengajuanIzin.jenisIzin', 'approvals.approver']);
if ($request->filled('status')) {
$query->where('status_surat', $request->status);
}
if ($request->filled('search')) {
$query->whereHas('user', function ($q) use ($request) {
$q->where('nama_lengkap', 'like', '%' . $request->search . '%');
});
}
$suratList = $query->orderBy('created_at', 'desc')->paginate(15);
return view('surat-izin.index', compact('suratList'));
}
public function show($id)
{
$surat = SuratIzin::with([
'user.jabatan',
'user.divisi',
'pengajuanIzin.jenisIzin',
'tandaTanganPengaju',
'approvals.approver.jabatan',
'approvals.tandaTanganApprover',
])->findOrFail($id);
$user = Auth::user();
$canApprove = false;
$tahapApproval = null;
$isSuperAdmin = $user->roles->contains(function ($role) {
return in_array(strtolower($role->nama_role), ['admin', 'super admin', 'super_admin']);
});
if ($surat->status_surat === 'menunggu_manajer' && ($user->roles->contains('nama_role', 'manajer') || $isSuperAdmin)) {
$canApprove = true;
$tahapApproval = 1;
} elseif ($surat->status_surat === 'menunggu_hrd' && ($user->roles->contains('nama_role', 'hrd') || $isSuperAdmin)) {
$canApprove = true;
$tahapApproval = 2;
}
$ttdApprover = TandaTangan::where('id_user', $user->id)->active()->first();
return view('surat-izin.show', compact('surat', 'canApprove', 'tahapApproval', 'ttdApprover'));
}
public function approve(Request $request, $id)
{
try {
$surat = SuratIzin::findOrFail($id);
$user = Auth::user();
if (in_array($surat->status_surat, ['disetujui', 'ditolak'])) {
return redirect()->back()->with('error', 'Surat ini sudah diproses sebelumnya.');
}
$tahap = null;
$isSuperAdmin = $user->roles->contains(function ($role) {
return in_array(strtolower($role->nama_role), ['admin', 'super admin', 'super_admin']);
});
if ($surat->status_surat === 'menunggu_manajer' && ($user->roles->contains('nama_role', 'manajer') || $isSuperAdmin)) {
$tahap = 1;
} elseif ($surat->status_surat === 'menunggu_hrd' && ($user->roles->contains('nama_role', 'hrd') || $isSuperAdmin)) {
$tahap = 2;
}
if (!$tahap) {
return redirect()->back()->with('error', 'Anda tidak memiliki akses untuk menyetujui surat ini.');
}
$ttd = TandaTangan::where('id_user', $user->id)->active()->first();
if (!$ttd) {
return redirect()->back()->with('error', 'Anda belum memiliki tanda tangan digital.');
}
DB::beginTransaction();
ApprovalSurat::create([
'id_surat' => $surat->id_surat,
'id_approver' => $user->id,
'id_ttd_approver' => $ttd->id_tanda_tangan,
'tahap' => $tahap,
'status' => 'disetujui',
'catatan' => $request->catatan,
]);
if ($tahap === 1) {
$surat->update(['status_surat' => 'menunggu_hrd']);
} elseif ($tahap === 2) {
$surat->update(['status_surat' => 'disetujui']);
$this->prosesPersetujuanFinal($surat);
}
DB::commit();
$statusLabel = $tahap === 1 ? 'Menunggu HRD' : 'Disetujui';
return redirect()->back()->with('success', "Surat berhasil disetujui. Status: {$statusLabel}");
} catch (\Exception $e) {
DB::rollBack();
return redirect()->back()->with('error', 'Gagal: ' . $e->getMessage());
}
}
public function reject(Request $request, $id)
{
try {
$surat = SuratIzin::findOrFail($id);
$user = Auth::user();
if (in_array($surat->status_surat, ['disetujui', 'ditolak'])) {
return redirect()->back()->with('error', 'Surat ini sudah diproses sebelumnya.');
}
$tahap = null;
$isSuperAdmin = $user->roles->contains(function ($role) {
return in_array(strtolower($role->nama_role), ['admin', 'super admin', 'super_admin']);
});
if ($surat->status_surat === 'menunggu_manajer' && ($user->roles->contains('nama_role', 'manajer') || $isSuperAdmin)) {
$tahap = 1;
} elseif ($surat->status_surat === 'menunggu_hrd' && ($user->roles->contains('nama_role', 'hrd') || $isSuperAdmin)) {
$tahap = 2;
}
if (!$tahap) {
return redirect()->back()->with('error', 'Anda tidak memiliki akses.');
}
DB::beginTransaction();
ApprovalSurat::create([
'id_surat' => $surat->id_surat,
'id_approver' => $user->id,
'id_ttd_approver' => null,
'tahap' => $tahap,
'status' => 'ditolak',
'catatan' => $request->catatan,
]);
$surat->update(['status_surat' => 'ditolak']);
if ($surat->pengajuanIzin) {
$surat->pengajuanIzin->update(['id_status' => 3]);
}
DB::commit();
return redirect()->back()->with('success', 'Surat izin berhasil ditolak.');
} catch (\Exception $e) {
DB::rollBack();
return redirect()->back()->with('error', 'Gagal: ' . $e->getMessage());
}
}
private function prosesPersetujuanFinal(SuratIzin $surat)
{
$izin = $surat->pengajuanIzin;
if (!$izin)
return;
$izin->load('jenisIzin');
$izin->update(['id_status' => 2]);
$statusPresensiId = 3;
if ($izin->id_jenis_izin == 1) {
$statusPresensiId = 4;
}
$startDate = Carbon::parse($izin->tanggal_mulai, 'Asia/Jakarta');
$endDate = Carbon::parse($izin->tanggal_selesai, 'Asia/Jakarta');
while ($startDate->lte($endDate)) {
Presensi::updateOrCreate(
[
'id_user' => $izin->id_user,
'tanggal' => $startDate->format('Y-m-d'),
],
[
'id_status' => $statusPresensiId,
'jam_masuk' => null,
'jam_pulang' => null,
'id_validasi' => 1,
'alasan_telat' => $izin->jenisIzin->nama_izin . ': ' . $izin->alasan,
]
);
$startDate->addDay();
}
if ($izin->id_jenis_izin == 2) {
$user = \App\Models\User::find($izin->id_user, ['*']);
$jumlahHari = Carbon::parse($izin->tanggal_mulai)->diffInDays(Carbon::parse($izin->tanggal_selesai)) + 1;
$user->decrement('sisa_cuti', $jumlahHari);
}
}
}

View File

@ -0,0 +1,91 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\JadwalKerja;
use App\Models\RiwayatTukarShift;
use App\Http\Requests\StoreTukarShiftRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
class TukarShiftController extends Controller
{
public function index()
{
$riwayat = RiwayatTukarShift::with(['user1', 'user2', 'jadwal1.shift', 'jadwal2.shift', 'execAdmin'])
->latest()
->get();
return view('tukar-shift.index', compact('riwayat'));
}
public function create()
{
$pegawai = User::with('kantor')->whereDoesntHave('roles', function ($q) {
$q->where('nama_role', 'admin')->orWhere('nama_role', 'Admin');
})->get();
$kantor = \App\Models\Kantor::orderBy('nama_kantor', 'asc')->get();
return view('tukar-shift.create', compact('pegawai', 'kantor'));
}
public function store(StoreTukarShiftRequest $request)
{
DB::beginTransaction();
try {
$jadwal1 = JadwalKerja::findOrFail($request->id_jadwal_1);
$jadwal2 = JadwalKerja::findOrFail($request->id_jadwal_2);
$tempUser1 = $jadwal1->id_user;
$jadwal1->id_user = $jadwal2->id_user;
$jadwal2->id_user = $tempUser1;
$jadwal1->save();
$jadwal2->save();
RiwayatTukarShift::create([
'id_user_1' => $request->id_user_1,
'id_jadwal_1' => $jadwal1->id_jadwal,
'id_user_2' => $request->id_user_2,
'id_jadwal_2' => $jadwal2->id_jadwal,
'keterangan' => $request->keterangan,
'created_by' => Auth::id() ?? 1,
]);
DB::commit();
return redirect()->route('tukar-shift.index')
->with('success', 'Berhasil menukar shift kerja untuk kedua pegawai tersebut.');
} catch (\Exception $e) {
DB::rollBack();
return redirect()->back()
->withInput()
->with('error', 'Gagal menukar shift: ' . $e->getMessage());
}
}
public function getJadwalUser(Request $request)
{
$request->validate([
'id_user' => 'required|exists:users,id'
]);
$startOfMonth = Carbon::now()->startOfMonth()->toDateString();
$endOfMonth = Carbon::now()->addMonth()->endOfMonth()->toDateString();
$jadwal = JadwalKerja::with('shift')
->where('id_user', $request->id_user)
->whereBetween('tanggal', [$startOfMonth, $endOfMonth])
->orderBy('tanggal', 'asc')
->get();
return response()->json($jadwal);
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Facades\Auth;
class CheckRole
{
/**
* Menangani permintaan yang masuk.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, ...$roles): Response
{
if (!Auth::check()) {
return redirect('login');
}
$user = Auth::user();
foreach ($roles as $role) {
if ($user->roles()->where('nama_role', $role)->exists()) {
return $next($request);
}
}
abort(403, 'Unauthorized action.');
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreDivisiRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'nama_divisi' => ['required', 'string', 'max:255', 'unique:divisi,nama_divisi'],
];
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreHariLiburRequest extends FormRequest
{
public function authorize(): bool
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
];
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreJabatanRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'nama_jabatan' => ['required', 'string', 'max:255', 'unique:jabatan,nama_jabatan'],
];
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreKantorRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'nama_kantor' => ['required', 'string', 'max:255'],
'alamat' => ['nullable', 'string'],
'latitude' => ['required', 'numeric', 'between:-90,90'],
'longitude' => ['required', 'numeric', 'between:-180,180'],
'radius' => ['required', 'integer', 'min:10'],
];
}
}

View File

@ -0,0 +1,57 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreLemburRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'tanggal_lembur' => 'required|date',
'jam_mulai' => 'required|date_format:H:i',
'jam_selesai' => 'required|date_format:H:i|after:jam_mulai',
'keterangan' => 'nullable|string|max:255',
'id_kompensasi' => 'nullable|exists:jenis_kompensasi,id_kompensasi',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes()
{
return [
'tanggal_lembur' => 'tanggal lembur',
'jam_mulai' => 'jam mulai',
'jam_selesai' => 'jam selesai',
'id_kompensasi' => 'jenis kompensasi',
];
}
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages(): array
{
return [
'jam_selesai.after' => 'Jam selesai harus lebih dari jam mulai.',
];
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StoreManualPresensiRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->roles->contains(function ($role) {
return in_array(strtolower($role->nama_role), ['admin', 'super admin', 'super_admin']);
});
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'id_user' => 'required|exists:users,id',
'tanggal' => 'required|date',
'id_status' => 'required|exists:status_presensi,id_status',
'jam_masuk' => [
Rule::requiredIf(fn () => in_array($this->id_status, [1, 2])),
'nullable',
'date_format:H:i'
],
'jam_pulang' => 'nullable|date_format:H:i',
'alasan_telat' => 'nullable|string|max:255'
];
}
public function messages(): array
{
return [
'id_user.required' => 'Pegawai harus dipilih.',
'id_status.required' => 'Status kehadiran harus dipilih.',
'jam_masuk.required' => 'Jam masuk wajib diisi untuk status Hadir/Terlambat.',
];
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePegawaiRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'nik' => ['required', 'numeric', 'unique:users,nik'],
'nama_lengkap' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'unique:users,email'],
'no_telp' => ['nullable', 'string', 'max:20'],
'alamat' => ['nullable', 'string'],
'id_divisi' => ['required', 'exists:divisi,id_divisi'],
'id_jabatan' => ['required', 'exists:jabatan,id_jabatan'],
'id_kantor' => ['required', 'exists:kantor,id_kantor'],
'tgl_bergabung' => ['required', 'date'],
'foto' => ['nullable', 'image', 'max:5120'],
'sisa_cuti' => ['nullable', 'integer', 'min:0'],
'id_role' => ['required', 'exists:roles,id_role'],
];
}
public function messages(): array
{
return [
'nik.unique' => 'NIK sudah terdaftar di sistem.',
'email.unique' => 'Email ini sudah digunakan oleh pegawai lain.',
'id_divisi.exists' => 'Divisi yang dipilih tidak valid.',
'id_role.exists' => 'Role yang dipilih tidak valid.',
];
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePengajuanIzinRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'tanggal_mulai' => 'required|date',
'tanggal_selesai' => 'required|date|after_or_equal:tanggal_mulai',
'id_jenis_izin' => 'required|exists:jenis_izin,id_jenis_izin',
'alasan' => 'required|string|max:150',
'bukti_file' => 'nullable|file|mimes:pdf,jpg,jpeg,png|max:2048',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes()
{
return [
'tanggal_mulai' => 'tanggal mulai',
'tanggal_selesai' => 'tanggal selesai',
'id_jenis_izin' => 'jenis izin',
'bukti_file' => 'bukti file',
];
}
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages(): array
{
return [
'tanggal_selesai.after_or_equal' => 'Tanggal selesai harus sama atau setelah tanggal mulai.',
'bukti_file.max' => 'Ukuran file maksimal 2MB.',
'bukti_file.mimes' => 'Format file harus PDF, JPG, JPEG, atau PNG.',
];
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePengumumanRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'judul' => 'required|string|max:80',
'isi' => 'required|string|max:500',
'tanggal' => 'required|date',
];
}
public function messages(): array
{
return [
'judul.required' => 'Judul pengumuman wajib diisi.',
'judul.max' => 'Judul maksimal 80 karakter.',
'isi.required' => 'Isi pengumuman wajib diisi.',
'tanggal.required' => 'Tanggal tayang wajib diisi.',
];
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePresensiRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'latitude' => 'required|numeric',
'longitude' => 'required|numeric',
'foto' => 'required|file|mimes:jpg,jpeg,png|max:5120',
'status' => 'required|in:masuk,pulang',
'keterangan_luar_radius' => 'nullable|string|max:500',
];
}
public function messages(): array
{
return [
'foto.required' => 'Foto wajah wajib diambil.',
'foto.file' => 'Format foto tidak valid.',
'foto.mimes' => 'Foto harus berformat JPG atau PNG.',
'foto.max' => 'Ukuran foto maksimal 5MB.',
'status.required' => 'Status presensi wajib diisi.',
'status.in' => 'Status presensi harus masuk atau pulang.',
];
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreRoleRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'nama_role' => ['required', 'string', 'max:50', 'unique:roles,nama_role'],
'id_permissions' => ['nullable', 'array'],
'id_permissions.*' => ['exists:permissions,id_permission'],
];
}
public function messages(): array
{
return [
'nama_role.required' => 'Nama role wajib diisi.',
'nama_role.unique' => 'Nama role ini sudah ada.',
];
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreShiftRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'nama_shift' => ['required', 'string', 'max:50', 'unique:shift_kerja,nama_shift'],
'jam_mulai' => ['required', 'date_format:H:i'],
'jam_selesai' => ['required', 'date_format:H:i', 'after:jam_mulai'],
];
}
public function messages(): array
{
return [
'nama_shift.required' => 'Nama shift wajib diisi.',
'nama_shift.unique' => 'Nama shift ini sudah ada.',
'jam_mulai.required' => 'Jam mulai wajib diisi.',
'jam_selesai.required' => 'Jam selesai wajib diisi.',
'jam_selesai.after' => 'Jam selesai harus setelah jam mulai.',
];
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreSignatureRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'signature_data' => 'required|string',
];
}
public function messages(): array
{
return [
'signature_data.required' => 'Data tanda tangan wajib diisi.',
];
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreSuratIzinRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'id_izin' => 'required|string|exists:pengajuan_izin,id_izin',
'isi_surat' => 'required|string',
];
}
public function messages(): array
{
return [
'id_izin.required' => 'ID pengajuan izin wajib diisi.',
'id_izin.exists' => 'Pengajuan izin tidak ditemukan.',
'isi_surat.required' => 'Isi surat wajib diisi.',
];
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreTukarShiftRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'id_user_1' => 'required|exists:users,id',
'id_jadwal_1' => 'required|exists:jadwal_kerja,id_jadwal',
'id_user_2' => 'required|exists:users,id|different:id_user_1',
'id_jadwal_2' => 'required|exists:jadwal_kerja,id_jadwal|different:id_jadwal_1',
'keterangan' => 'nullable|string|max:1000'
];
}
public function messages(): array
{
return [
'id_user_1.required' => 'Pegawai pertama harus dipilih.',
'id_user_1.exists' => 'Pegawai pertama tidak valid.',
'id_jadwal_1.required' => 'Jadwal pegawai pertama harus dipilih.',
'id_jadwal_1.exists' => 'Jadwal pegawai pertama tidak valid.',
'id_user_2.required' => 'Pegawai kedua harus dipilih.',
'id_user_2.exists' => 'Pegawai kedua tidak valid.',
'id_user_2.different' => 'Pegawai kedua tidak boleh sama dengan pegawai pertama.',
'id_jadwal_2.required' => 'Jadwal pegawai kedua harus dipilih.',
'id_jadwal_2.exists' => 'Jadwal pegawai kedua tidak valid.',
'id_jadwal_2.different' => 'Jadwal pegawai kedua tidak boleh sama dengan jadwal pegawai pertama.',
];
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateDivisiRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
$id = $this->route('divisi');
return [
'nama_divisi' => ['required', 'string', 'max:255', 'unique:divisi,nama_divisi,'.$id.',id_divisi'],
];
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateJabatanRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
$id = $this->route('jabatan');
return [
'nama_jabatan' => ['required', 'string', 'max:255', 'unique:jabatan,nama_jabatan,'.$id.',id_jabatan'],
];
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateKantorRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'nama_kantor' => ['required', 'string', 'max:255'],
'alamat' => ['nullable', 'string'],
'latitude' => ['required', 'numeric', 'between:-90,90'],
'longitude' => ['required', 'numeric', 'between:-180,180'],
'radius' => ['required', 'integer', 'min:10'],
];
}
}

View File

@ -0,0 +1,57 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateLemburRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'tanggal_lembur' => 'required|date',
'jam_mulai' => 'required|date_format:H:i',
'jam_selesai' => 'required|date_format:H:i|after:jam_mulai',
'keterangan' => 'nullable|string|max:255',
'id_kompensasi' => 'nullable|exists:jenis_kompensasi,id_kompensasi',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes()
{
return [
'tanggal_lembur' => 'tanggal lembur',
'jam_mulai' => 'jam mulai',
'jam_selesai' => 'jam selesai',
'id_kompensasi' => 'jenis kompensasi',
];
}
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages(): array
{
return [
'jam_selesai.after' => 'Jam selesai harus lebih dari jam mulai.',
];
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UpdatePegawaiRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
$id = $this->route('pegawai');
return [
'nama_lengkap' => ['required', 'string', 'max:255'],
'email' => [
'required',
'email',
'unique:users,email,' . $this->route('pegawai')
],
'no_telp' => ['nullable', 'string', 'max:20'],
'alamat' => ['nullable', 'string'],
'id_divisi' => ['required', 'exists:divisi,id_divisi'],
'id_jabatan' => ['required', 'exists:jabatan,id_jabatan'],
'id_kantor' => ['required', 'exists:kantor,id_kantor'],
'status_aktif' => ['required', 'in:0,1'],
'tgl_bergabung' => ['required', 'date'],
'foto' => ['nullable', 'image', 'max:5120'],
'sisa_cuti' => ['nullable', 'integer', 'min:0'],
'id_role' => ['required', 'exists:roles,id_role'],
];
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdatePengajuanIzinRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'tanggal_mulai' => 'required|date',
'tanggal_selesai' => 'required|date|after_or_equal:tanggal_mulai',
'id_jenis_izin' => 'required|exists:jenis_izin,id_jenis_izin',
'alasan' => 'required|string|max:150',
'bukti_file' => 'nullable|file|mimes:pdf,jpg,jpeg,png|max:2048',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes()
{
return [
'tanggal_mulai' => 'tanggal mulai',
'tanggal_selesai' => 'tanggal selesai',
'id_jenis_izin' => 'jenis izin',
'bukti_file' => 'bukti file',
];
}
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages(): array
{
return [
'tanggal_selesai.after_or_equal' => 'Tanggal selesai harus sama atau setelah tanggal mulai.',
'bukti_file.max' => 'Ukuran file maksimal 2MB.',
'bukti_file.mimes' => 'Format file harus PDF, JPG, JPEG, atau PNG.',
];
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdatePengumumanRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'judul' => 'required|string|max:80',
'isi' => 'required|string|max:500',
'tanggal' => 'required|date',
];
}
public function messages(): array
{
return [
'judul.required' => 'Judul pengumuman wajib diisi.',
'judul.max' => 'Judul maksimal 80 karakter.',
'isi.required' => 'Isi pengumuman wajib diisi.',
'tanggal.required' => 'Tanggal tayang wajib diisi.',
];
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateRoleRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'nama_role' => ['required', 'string', 'max:50', 'unique:roles,nama_role,' . $this->route('role') . ',id_role'],
'id_permissions' => ['nullable', 'array'],
'id_permissions.*' => ['exists:permissions,id_permission'],
];
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateShiftRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'nama_shift' => ['required', 'string', 'max:50', 'unique:shift_kerja,nama_shift,' . $this->route('shift') . ',id_shift'],
'jam_mulai' => ['required', 'date_format:H:i'],
'jam_selesai' => ['required', 'date_format:H:i', 'after:jam_mulai'],
];
}
public function messages(): array
{
return [
'nama_shift.required' => 'Nama shift wajib diisi.',
'nama_shift.unique' => 'Nama shift ini sudah ada.',
'jam_mulai.required' => 'Jam mulai wajib diisi.',
'jam_selesai.required' => 'Jam selesai wajib diisi.',
'jam_selesai.after' => 'Jam selesai harus setelah jam mulai.',
];
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
/**
* @property string $id_approval
* @property string $id_surat
* @property int $id_approver
* @property string|null $id_ttd_approver
* @property int $tahap 1=Manajer, 2=HRD
* @property string $status
* @property string|null $catatan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\User $approver
* @property-read \App\Models\SuratIzin $suratIzin
* @property-read \App\Models\TandaTangan|null $tandaTanganApprover
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereCatatan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereIdApproval($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereIdApprover($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereIdSurat($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereIdTtdApprover($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereTahap($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ApprovalSurat whereUpdatedAt($value)
* @mixin \Eloquent
*/
class ApprovalSurat extends Model
{
protected $table = 'approval_surat';
protected $primaryKey = 'id_approval';
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'id_surat',
'id_approver',
'id_ttd_approver',
'tahap',
'status',
'catatan',
];
protected static function booted()
{
static::creating(function ($model) {
if (empty($model->{$model->getKeyName()})) {
$prefix = 'APR-' . date('ym') . '-';
$model->{$model->getKeyName()} = $prefix . strtoupper(Str::random(5));
}
});
}
public function suratIzin()
{
return $this->belongsTo(SuratIzin::class, 'id_surat', 'id_surat');
}
public function approver()
{
return $this->belongsTo(User::class, 'id_approver');
}
public function tandaTanganApprover()
{
return $this->belongsTo(TandaTangan::class, 'id_ttd_approver', 'id_tanda_tangan');
}
}

39
app/Models/DataWajah.php Normal file
View File

@ -0,0 +1,39 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property int $id_user
* @property string|null $path_model_yml
* @property int $is_verified 0:Pending, 1:Approved, 2:Rejected
* @property string|null $last_updated
* @property string|null $encoding_wajah
* @property string $tanggal_latih
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\User $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereEncodingWajah($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereIsVerified($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereLastUpdated($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah wherePathModelYml($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereTanggalLatih($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DataWajah whereUpdatedAt($value)
* @mixin \Eloquent
*/
class DataWajah extends Model
{
protected $table = 'data_wajah';
protected $guarded = [];
public function user()
{
return $this->belongsTo(User::class, 'id_user', 'id');
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string $id_penggunaan
* @property int $id_poin_sumber
* @property int $jumlah_diambil
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\PenggunaanPoin $penggunaan
* @property-read \App\Models\PoinLembur $poinSumber
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin whereIdPenggunaan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin whereIdPoinSumber($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin whereJumlahDiambil($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DetailPenggunaanPoin whereUpdatedAt($value)
* @mixin \Eloquent
*/
class DetailPenggunaanPoin extends Model
{
use HasFactory;
protected $table = 'detail_penggunaan_poin';
protected $primaryKey = 'id';
protected $fillable = [
'id_penggunaan',
'id_poin_sumber',
'jumlah_diambil'
];
public function penggunaan()
{
return $this->belongsTo(PenggunaanPoin::class, 'id_penggunaan', 'id_penggunaan');
}
public function poinSumber()
{
return $this->belongsTo(PoinLembur::class, 'id_poin_sumber', 'id_poin');
}
}

32
app/Models/Divisi.php Normal file
View File

@ -0,0 +1,32 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id_divisi
* @property string $nama_divisi
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi whereIdDivisi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi whereNamaDivisi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Divisi whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Divisi extends Model
{
use HasFactory;
protected $table = 'divisi';
protected $primaryKey = 'id_divisi';
protected $fillable = [
'nama_divisi'
];
}

25
app/Models/HariLibur.php Normal file
View File

@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class HariLibur extends Model
{
use HasFactory;
protected $table = 'hari_libur';
protected $primaryKey = 'id';
protected $fillable = [
'tanggal',
'keterangan',
'id_kantor',
];
public function kantor()
{
return $this->belongsTo(Kantor::class, 'id_kantor');
}
}

32
app/Models/Jabatan.php Normal file
View File

@ -0,0 +1,32 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id_jabatan
* @property string $nama_jabatan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan whereIdJabatan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan whereNamaJabatan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Jabatan whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Jabatan extends Model
{
use HasFactory;
protected $table = 'jabatan';
protected $primaryKey = 'id_jabatan';
protected $fillable = [
'nama_jabatan'
];
}

View File

@ -0,0 +1,60 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;
/**
* @property int $id_jadwal
* @property int|null $id_user
* @property string|null $tanggal
* @property int|null $id_shift
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\PenggunaanPoin|null $penggunaanPoin
* @property-read \App\Models\ShiftKerja|null $shift
* @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja whereIdJadwal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja whereIdShift($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja whereTanggal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JadwalKerja whereUpdatedAt($value)
* @mixin \Eloquent
*/
class JadwalKerja extends Model
{
use HasFactory;
protected $table = 'jadwal_kerja';
protected $primaryKey = 'id_jadwal';
protected $fillable = [
'id_user',
'id_shift',
'tanggal',
];
public function user()
{
return $this->belongsTo(User::class, 'id_user');
}
public function shift()
{
return $this->belongsTo(ShiftKerja::class, 'id_shift');
}
public function penggunaanPoin()
{
return $this->hasOne(PenggunaanPoin::class, 'id_user', 'id_user')
->whereColumn('tanggal_penggunaan', 'tanggal')
->where('id_status', 2);
}
}

28
app/Models/JenisIzin.php Normal file
View File

@ -0,0 +1,28 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id_jenis_izin
* @property string $nama_izin
* @property string|null $created_at
* @property string|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin whereIdJenisIzin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin whereNamaIzin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisIzin whereUpdatedAt($value)
* @mixin \Eloquent
*/
class JenisIzin extends Model
{
protected $table = 'jenis_izin';
protected $primaryKey = 'id_jenis_izin';
public $timestamps = false;
protected $fillable = ['id_jenis_izin', 'nama_izin'];
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id_kompensasi
* @property string $nama_kompensasi
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi whereIdKompensasi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi whereNamaKompensasi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisKompensasi whereUpdatedAt($value)
* @mixin \Eloquent
*/
class JenisKompensasi extends Model
{
protected $table = 'jenis_kompensasi';
protected $primaryKey = 'id_kompensasi';
const UANG = 1;
const POIN = 2;
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id_pengurangan
* @property string $nama_pengurangan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\PenggunaanPoin> $penggunaanPoin
* @property-read int|null $penggunaan_poin_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan whereIdPengurangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan whereNamaPengurangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|JenisPengurangan whereUpdatedAt($value)
* @mixin \Eloquent
*/
class JenisPengurangan extends Model
{
use HasFactory;
protected $table = 'jenis_pengurangan';
protected $primaryKey = 'id_pengurangan';
protected $fillable = [
'nama_pengurangan'
];
public function penggunaanPoin()
{
return $this->hasMany(PenggunaanPoin::class, 'id_pengurangan');
}
}

53
app/Models/Kantor.php Normal file
View File

@ -0,0 +1,53 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id_kantor
* @property string $nama_kantor
* @property string|null $alamat
* @property numeric|null $latitude
* @property numeric|null $longitude
* @property int $radius
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\User> $users
* @property-read int|null $users_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereAlamat($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereIdKantor($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereLatitude($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereLongitude($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereNamaKantor($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereRadius($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Kantor whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Kantor extends Model
{
use HasFactory;
protected $table = 'kantor';
protected $primaryKey = 'id_kantor';
protected $fillable = [
'nama_kantor',
'tipe',
'alamat',
'latitude',
'longitude',
'radius'
];
public function users()
{
return $this->hasMany(User::class, 'id_kantor');
}
}

97
app/Models/Lembur.php Normal file
View File

@ -0,0 +1,97 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* @property string $id_lembur
* @property int|null $id_user
* @property \Illuminate\Support\Carbon $tanggal_lembur
* @property string $jam_mulai
* @property string $jam_selesai
* @property int|null $durasi_menit
* @property string|null $keterangan
* @property int|null $jumlah_poin
* @property int|null $id_kompensasi
* @property int $id_status
* @property string|null $alasan_penolakan
* @property \Illuminate\Support\Carbon $tanggal_diajukan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\JenisKompensasi|null $kompensasi
* @property-read \App\Models\StatusPengajuan $status
* @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereAlasanPenolakan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereDurasiMenit($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereIdKompensasi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereIdLembur($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereIdStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereJamMulai($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereJamSelesai($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereJumlahPoin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereKeterangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereTanggalDiajukan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereTanggalLembur($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Lembur whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Lembur extends Model
{
use HasFactory;
protected $table = 'lembur';
protected $primaryKey = 'id_lembur';
public $incrementing = false;
protected $keyType = 'string';
protected static function booted()
{
static::creating(function ($model) {
if (empty($model->{$model->getKeyName()})) {
$prefix = 'LMB-' . date('ym') . '-';
$model->{$model->getKeyName()} = $prefix . strtoupper(\Illuminate\Support\Str::random(5));
}
});
}
protected $fillable = [
'id_user',
'tanggal_lembur',
'jam_mulai',
'jam_selesai',
'durasi_menit',
'keterangan',
'jumlah_poin',
'id_kompensasi',
'id_status',
'alasan_penolakan',
'tanggal_diajukan'
];
protected $casts = [
'tanggal_lembur' => 'date',
'tanggal_diajukan' => 'datetime',
];
public function user()
{
return $this->belongsTo(User::class, 'id_user');
}
public function kompensasi()
{
return $this->belongsTo(JenisKompensasi::class, 'id_kompensasi');
}
public function status()
{
return $this->belongsTo(StatusPengajuan::class, 'id_status');
}
}

View File

@ -0,0 +1,90 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* @property string $id_izin
* @property int|null $id_user
* @property string $tanggal_mulai
* @property string $tanggal_selesai
* @property int|null $id_jenis_izin
* @property string|null $alasan
* @property string|null $bukti_file
* @property int $id_status
* @property string|null $alasan_penolakan
* @property string $tanggal_diajukan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\JenisIzin|null $jenisIzin
* @property-read \App\Models\StatusPengajuan $statusPengajuan
* @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereAlasan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereAlasanPenolakan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereBuktiFile($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereIdIzin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereIdJenisIzin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereIdStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereTanggalDiajukan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereTanggalMulai($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereTanggalSelesai($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PengajuanIzin whereUpdatedAt($value)
* @mixin \Eloquent
*/
class PengajuanIzin extends Model
{
use HasFactory;
protected $table = 'pengajuan_izin';
protected $primaryKey = 'id_izin';
public $incrementing = false;
protected $keyType = 'string';
protected static function booted()
{
static::creating(function ($model) {
if (empty($model->{$model->getKeyName()})) {
$prefix = 'IZN-' . date('ym') . '-';
$model->{$model->getKeyName()} = $prefix . strtoupper(\Illuminate\Support\Str::random(5));
}
});
}
protected $fillable = [
'id_user',
'id_jenis_izin',
'id_status',
'tanggal_mulai',
'tanggal_selesai',
'alasan',
'bukti_file',
];
public function user()
{
return $this->belongsTo(User::class, 'id_user');
}
public function jenisIzin()
{
return $this->belongsTo(JenisIzin::class, 'id_jenis_izin', 'id_jenis_izin');
}
public function statusPengajuan()
{
return $this->belongsTo(StatusPengajuan::class, 'id_status', 'id_status');
}
public function suratIzin()
{
return $this->hasOne(SuratIzin::class, 'id_izin', 'id_izin');
}
}

View File

@ -0,0 +1,91 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* @property string $id_penggunaan
* @property int|null $id_user
* @property \Illuminate\Support\Carbon $tanggal_penggunaan
* @property int|null $jumlah_poin
* @property string|null $jam_masuk_custom
* @property string|null $jam_pulang_custom
* @property int|null $id_pengurangan
* @property int $id_status
* @property string|null $alasan_penolakan
* @property \Illuminate\Support\Carbon $tanggal_diajukan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\JenisPengurangan|null $jenisPengurangan
* @property-read \App\Models\StatusPengajuan $status
* @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereAlasanPenolakan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereIdPenggunaan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereIdPengurangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereIdStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereJamMasukCustom($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereJamPulangCustom($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereJumlahPoin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereTanggalDiajukan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereTanggalPenggunaan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PenggunaanPoin whereUpdatedAt($value)
* @mixin \Eloquent
*/
class PenggunaanPoin extends Model
{
use HasFactory;
protected $table = 'penggunaan_poin';
protected $primaryKey = 'id_penggunaan';
public $incrementing = false;
protected $keyType = 'string';
protected static function booted()
{
static::creating(function ($model) {
if (empty($model->{$model->getKeyName()})) {
$prefix = 'PNP-' . date('ym') . '-';
$model->{$model->getKeyName()} = $prefix . strtoupper(\Illuminate\Support\Str::random(5));
}
});
}
protected $fillable = [
'id_user',
'tanggal_penggunaan',
'jumlah_poin',
'id_pengurangan',
'jam_masuk_custom',
'jam_pulang_custom',
'id_status',
'alasan_penolakan',
'tanggal_diajukan'
];
protected $casts = [
'tanggal_penggunaan' => 'date',
'tanggal_diajukan' => 'datetime',
];
public function user()
{
return $this->belongsTo(User::class, 'id_user');
}
public function jenisPengurangan()
{
return $this->belongsTo(JenisPengurangan::class, 'id_pengurangan');
}
public function status()
{
return $this->belongsTo(StatusPengajuan::class, 'id_status', 'id_status');
}
}

51
app/Models/Pengumuman.php Normal file
View File

@ -0,0 +1,51 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id_pengumuman
* @property string|null $judul
* @property string|null $isi
* @property \Illuminate\Support\Carbon|null $tanggal
* @property int|null $dibuat_oleh
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\User|null $pembuat
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereDibuatOleh($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereIdPengumuman($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereIsi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereJudul($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereTanggal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Pengumuman whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Pengumuman extends Model
{
use HasFactory;
protected $table = 'pengumuman';
protected $primaryKey = 'id_pengumuman';
protected $fillable = [
'judul',
'isi',
'tanggal',
'dibuat_oleh'
];
protected $casts = [
'tanggal' => 'date',
];
public function pembuat()
{
return $this->belongsTo(User::class, 'dibuat_oleh', 'id');
}
}

36
app/Models/Permission.php Normal file
View File

@ -0,0 +1,36 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
/**
* @property int $id_permission
* @property string $nama_permission
* @property string $slug
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Role> $roles
* @property-read int|null $roles_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission whereIdPermission($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission whereNamaPermission($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Permission whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Permission extends Model
{
protected $table = 'permissions';
protected $primaryKey = 'id_permission';
protected $fillable = ['nama_permission', 'slug'];
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class, 'detail_role_permissions', 'id_permission', 'id_role');
}
}

30
app/Models/Poin.php Normal file
View File

@ -0,0 +1,30 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|Poin newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Poin newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Poin query()
* @mixin \Eloquent
*/
class Poin extends Model
{
protected $table = 'poin';
protected $primaryKey = 'id_poin';
protected $fillable = [
'id_user',
'jumlah_poin',
'sumber',
'tgl_kadaluarsa',
];
public function user()
{
return $this->belongsTo(User::class, 'id_user', 'id');
}
}

65
app/Models/PoinLembur.php Normal file
View File

@ -0,0 +1,65 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id_poin
* @property int|null $id_user
* @property int|null $jumlah_poin
* @property int|null $sisa_poin
* @property string|null $id_lembur
* @property string|null $keterangan
* @property \Illuminate\Support\Carbon $tanggal
* @property \Illuminate\Support\Carbon|null $expired_at
* @property bool|null $is_fully_used
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\Lembur|null $lembur
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereExpiredAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereIdLembur($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereIdPoin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereIsFullyUsed($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereJumlahPoin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereKeterangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereSisaPoin($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereTanggal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|PoinLembur whereUpdatedAt($value)
* @mixin \Eloquent
*/
class PoinLembur extends Model
{
use HasFactory;
protected $table = 'poin_lembur';
protected $primaryKey = 'id_poin';
protected $fillable = [
'id_user',
'id_lembur',
'jumlah_poin',
'tanggal',
'keterangan',
'sisa_poin',
'expired_at',
'is_fully_used'
];
protected $casts = [
'tanggal' => 'datetime',
'expired_at' => 'date',
'is_fully_used' => 'boolean',
];
public function lembur()
{
return $this->belongsTo(Lembur::class, 'id_lembur');
}
}

97
app/Models/Presensi.php Normal file
View File

@ -0,0 +1,97 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id_presensi
* @property int|null $id_user
* @property string $tanggal
* @property string|null $jam_masuk
* @property string|null $jam_pulang
* @property numeric|null $lat_masuk
* @property numeric|null $lon_masuk
* @property numeric|null $lat_pulang
* @property numeric|null $lon_pulang
* @property string|null $foto_wajah_masuk
* @property string|null $foto_wajah_pulang
* @property int|null $id_status
* @property string|null $alasan_telat
* @property string|null $keterangan_pulang
* @property string|null $waktu_terlambat
* @property string|null $waktu_masuk_awal
* @property string|null $waktu_pulang_awal
* @property string|null $waktu_pulang_akhir
* @property int $verifikasi_wajah
* @property int $id_validasi
* @property string|null $keterangan_luar_radius
* @property string|null $alasan_penolakan
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string|null $deleted_at
* @property-read \App\Models\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereAlasanPenolakan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereAlasanTelat($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereDeletedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereFotoWajahMasuk($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereFotoWajahPulang($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereIdPresensi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereIdStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereIdUser($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereIdValidasi($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereJamMasuk($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereJamPulang($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereKeteranganLuarRadius($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereKeteranganPulang($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereLatMasuk($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereLatPulang($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereLonMasuk($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereLonPulang($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereTanggal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereVerifikasiWajah($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereWaktuMasukAwal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereWaktuPulangAkhir($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereWaktuPulangAwal($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Presensi whereWaktuTerlambat($value)
* @mixin \Eloquent
*/
class Presensi extends Model
{
protected $table = 'presensi';
protected $primaryKey = 'id_presensi';
protected $fillable = [
'id_user',
'tanggal',
'jam_masuk',
'jam_pulang',
'lat_masuk',
'lon_masuk',
'lat_pulang',
'lon_pulang',
'foto_wajah_masuk',
'foto_wajah_pulang',
'id_status',
'alasan_telat',
'verifikasi_wajah',
'id_validasi',
'alasan_penolakan',
'keterangan_luar_radius',
'keterangan_pulang',
'waktu_terlambat',
'waktu_masuk_awal',
'waktu_pulang_awal',
'waktu_pulang_akhir'
];
public function user()
{
return $this->belongsTo(User::class, 'id_user');
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id_riwayat
* @property int $id_user_1
* @property int $id_jadwal_1
* @property int $id_user_2
* @property int $id_jadwal_2
* @property string|null $keterangan
* @property int $created_by
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\User $execAdmin
* @property-read \App\Models\JadwalKerja $jadwal1
* @property-read \App\Models\JadwalKerja $jadwal2
* @property-read \App\Models\User $user1
* @property-read \App\Models\User $user2
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereCreatedBy($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereIdJadwal1($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereIdJadwal2($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereIdRiwayat($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereIdUser1($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereIdUser2($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereKeterangan($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|RiwayatTukarShift whereUpdatedAt($value)
* @mixin \Eloquent
*/
class RiwayatTukarShift extends Model
{
protected $table = 'riwayat_tukar_shift';
protected $primaryKey = 'id_riwayat';
protected $fillable = [
'id_user_1',
'id_jadwal_1',
'id_user_2',
'id_jadwal_2',
'keterangan',
'created_by',
];
public function user1()
{
return $this->belongsTo(User::class, 'id_user_1', 'id');
}
public function jadwal1()
{
return $this->belongsTo(JadwalKerja::class, 'id_jadwal_1', 'id_jadwal');
}
public function user2()
{
return $this->belongsTo(User::class, 'id_user_2', 'id');
}
public function jadwal2()
{
return $this->belongsTo(JadwalKerja::class, 'id_jadwal_2', 'id_jadwal');
}
public function execAdmin()
{
return $this->belongsTo(User::class, 'created_by', 'id');
}
}

41
app/Models/Role.php Normal file
View File

@ -0,0 +1,41 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
/**
* @property int $id_role
* @property string $nama_role
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Permission> $permissions
* @property-read int|null $permissions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\User> $users
* @property-read int|null $users_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role whereIdRole($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role whereNamaRole($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Role whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Role extends Model
{
protected $table = 'roles';
protected $primaryKey = 'id_role';
protected $fillable = ['nama_role'];
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class, 'detail_user_roles', 'id_role', 'id_user');
}
public function permissions(): BelongsToMany
{
return $this->belongsToMany(Permission::class, 'detail_role_permissions', 'id_role', 'id_permission');
}
}

46
app/Models/ShiftKerja.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @property int $id_shift
* @property string|null $nama_shift
* @property string|null $jam_mulai
* @property string|null $jam_selesai
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\JadwalKerja> $jadwalKerja
* @property-read int|null $jadwal_kerja_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja whereIdShift($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja whereJamMulai($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja whereJamSelesai($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja whereNamaShift($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShiftKerja whereUpdatedAt($value)
* @mixin \Eloquent
*/
class ShiftKerja extends Model
{
use HasFactory;
protected $table = 'shift_kerja';
protected $primaryKey = 'id_shift';
protected $fillable = [
'nama_shift',
'jam_mulai',
'jam_selesai',
];
public function jadwalKerja(): HasMany
{
return $this->hasMany(JadwalKerja::class, 'id_shift', 'id_shift');
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id_status
* @property string $nama_status
* @property string|null $created_at
* @property string|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan whereIdStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan whereNamaStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|StatusPengajuan whereUpdatedAt($value)
* @mixin \Eloquent
*/
class StatusPengajuan extends Model
{
protected $table = 'status_pengajuan';
protected $primaryKey = 'id_status';
public $timestamps = false;
protected $fillable = ['id_status', 'nama_status'];
}

Some files were not shown because too many files have changed in this diff Show More