269 lines
6.5 KiB
PHP
269 lines
6.5 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Models\Tenants;
|
||
|
|
|
||
|
|
use App\Models\Members\User;
|
||
|
|
use App\Traits\BelongsToTenant;
|
||
|
|
use Illuminate\Database\Eloquent\Model;
|
||
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 근태 기록 모델
|
||
|
|
*
|
||
|
|
* @property int $id
|
||
|
|
* @property int $tenant_id
|
||
|
|
* @property int $user_id
|
||
|
|
* @property string $base_date
|
||
|
|
* @property string $status
|
||
|
|
* @property array|null $json_details
|
||
|
|
* @property string|null $remarks
|
||
|
|
* @property int|null $created_by
|
||
|
|
* @property int|null $updated_by
|
||
|
|
* @property int|null $deleted_by
|
||
|
|
*/
|
||
|
|
class Attendance extends Model
|
||
|
|
{
|
||
|
|
use BelongsToTenant, SoftDeletes;
|
||
|
|
|
||
|
|
protected $table = 'attendances';
|
||
|
|
|
||
|
|
protected $casts = [
|
||
|
|
'json_details' => 'array',
|
||
|
|
'base_date' => 'date',
|
||
|
|
];
|
||
|
|
|
||
|
|
protected $fillable = [
|
||
|
|
'tenant_id',
|
||
|
|
'user_id',
|
||
|
|
'base_date',
|
||
|
|
'status',
|
||
|
|
'json_details',
|
||
|
|
'remarks',
|
||
|
|
'created_by',
|
||
|
|
'updated_by',
|
||
|
|
'deleted_by',
|
||
|
|
];
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 기본값 설정
|
||
|
|
*/
|
||
|
|
protected $attributes = [
|
||
|
|
'status' => 'onTime',
|
||
|
|
];
|
||
|
|
|
||
|
|
// =========================================================================
|
||
|
|
// 관계 정의
|
||
|
|
// =========================================================================
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 사용자 관계
|
||
|
|
*/
|
||
|
|
public function user(): BelongsTo
|
||
|
|
{
|
||
|
|
return $this->belongsTo(User::class, 'user_id');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 생성자 관계
|
||
|
|
*/
|
||
|
|
public function creator(): BelongsTo
|
||
|
|
{
|
||
|
|
return $this->belongsTo(User::class, 'created_by');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 수정자 관계
|
||
|
|
*/
|
||
|
|
public function updater(): BelongsTo
|
||
|
|
{
|
||
|
|
return $this->belongsTo(User::class, 'updated_by');
|
||
|
|
}
|
||
|
|
|
||
|
|
// =========================================================================
|
||
|
|
// json_details 헬퍼 메서드 (Accessor)
|
||
|
|
// =========================================================================
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 출근 시간
|
||
|
|
*/
|
||
|
|
public function getCheckInAttribute(): ?string
|
||
|
|
{
|
||
|
|
return $this->json_details['check_in'] ?? null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 퇴근 시간
|
||
|
|
*/
|
||
|
|
public function getCheckOutAttribute(): ?string
|
||
|
|
{
|
||
|
|
return $this->json_details['check_out'] ?? null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* GPS 데이터
|
||
|
|
*/
|
||
|
|
public function getGpsDataAttribute(): ?array
|
||
|
|
{
|
||
|
|
return $this->json_details['gps_data'] ?? null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 외근 정보
|
||
|
|
*/
|
||
|
|
public function getExternalWorkAttribute(): ?array
|
||
|
|
{
|
||
|
|
return $this->json_details['external_work'] ?? null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 다중 출퇴근 기록 (여러 번 출퇴근)
|
||
|
|
*/
|
||
|
|
public function getMultipleEntriesAttribute(): ?array
|
||
|
|
{
|
||
|
|
return $this->json_details['multiple_entries'] ?? null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 근무 시간 (분 단위)
|
||
|
|
*/
|
||
|
|
public function getWorkMinutesAttribute(): ?int
|
||
|
|
{
|
||
|
|
return isset($this->json_details['work_minutes'])
|
||
|
|
? (int) $this->json_details['work_minutes']
|
||
|
|
: null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 초과 근무 시간 (분 단위)
|
||
|
|
*/
|
||
|
|
public function getOvertimeMinutesAttribute(): ?int
|
||
|
|
{
|
||
|
|
return isset($this->json_details['overtime_minutes'])
|
||
|
|
? (int) $this->json_details['overtime_minutes']
|
||
|
|
: null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 지각 시간 (분 단위)
|
||
|
|
*/
|
||
|
|
public function getLateMinutesAttribute(): ?int
|
||
|
|
{
|
||
|
|
return isset($this->json_details['late_minutes'])
|
||
|
|
? (int) $this->json_details['late_minutes']
|
||
|
|
: null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 조퇴 시간 (분 단위)
|
||
|
|
*/
|
||
|
|
public function getEarlyLeaveMinutesAttribute(): ?int
|
||
|
|
{
|
||
|
|
return isset($this->json_details['early_leave_minutes'])
|
||
|
|
? (int) $this->json_details['early_leave_minutes']
|
||
|
|
: null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 휴가 유형 (vacation 상태일 때)
|
||
|
|
*/
|
||
|
|
public function getVacationTypeAttribute(): ?string
|
||
|
|
{
|
||
|
|
return $this->json_details['vacation_type'] ?? null;
|
||
|
|
}
|
||
|
|
|
||
|
|
// =========================================================================
|
||
|
|
// json_details 업데이트 메서드
|
||
|
|
// =========================================================================
|
||
|
|
|
||
|
|
/**
|
||
|
|
* json_details에서 특정 키 값 설정
|
||
|
|
*/
|
||
|
|
public function setJsonDetailsValue(string $key, mixed $value): void
|
||
|
|
{
|
||
|
|
$jsonDetails = $this->json_details ?? [];
|
||
|
|
if ($value === null) {
|
||
|
|
unset($jsonDetails[$key]);
|
||
|
|
} else {
|
||
|
|
$jsonDetails[$key] = $value;
|
||
|
|
}
|
||
|
|
$this->json_details = $jsonDetails;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* json_details에서 특정 키 값 가져오기
|
||
|
|
*/
|
||
|
|
public function getJsonDetailsValue(string $key, mixed $default = null): mixed
|
||
|
|
{
|
||
|
|
return $this->json_details[$key] ?? $default;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 출퇴근 정보 일괄 업데이트
|
||
|
|
*/
|
||
|
|
public function updateAttendanceDetails(array $data): void
|
||
|
|
{
|
||
|
|
$jsonDetails = $this->json_details ?? [];
|
||
|
|
$allowedKeys = [
|
||
|
|
'check_in',
|
||
|
|
'check_out',
|
||
|
|
'gps_data',
|
||
|
|
'external_work',
|
||
|
|
'multiple_entries',
|
||
|
|
'work_minutes',
|
||
|
|
'overtime_minutes',
|
||
|
|
'late_minutes',
|
||
|
|
'early_leave_minutes',
|
||
|
|
'vacation_type',
|
||
|
|
];
|
||
|
|
|
||
|
|
foreach ($allowedKeys as $key) {
|
||
|
|
if (array_key_exists($key, $data)) {
|
||
|
|
if ($data[$key] === null) {
|
||
|
|
unset($jsonDetails[$key]);
|
||
|
|
} else {
|
||
|
|
$jsonDetails[$key] = $data[$key];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->json_details = $jsonDetails;
|
||
|
|
}
|
||
|
|
|
||
|
|
// =========================================================================
|
||
|
|
// 스코프
|
||
|
|
// =========================================================================
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 특정 날짜의 근태 조회
|
||
|
|
*/
|
||
|
|
public function scopeOnDate($query, string $date)
|
||
|
|
{
|
||
|
|
return $query->whereDate('base_date', $date);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 특정 기간의 근태 조회
|
||
|
|
*/
|
||
|
|
public function scopeBetweenDates($query, string $startDate, string $endDate)
|
||
|
|
{
|
||
|
|
return $query->whereBetween('base_date', [$startDate, $endDate]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 특정 사용자의 근태 조회
|
||
|
|
*/
|
||
|
|
public function scopeForUser($query, int $userId)
|
||
|
|
{
|
||
|
|
return $query->where('user_id', $userId);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 특정 상태의 근태 조회
|
||
|
|
*/
|
||
|
|
public function scopeWithStatus($query, string $status)
|
||
|
|
{
|
||
|
|
return $query->where('status', $status);
|
||
|
|
}
|
||
|
|
}
|