Files
sam-manage/app/Models/HR/Employee.php
김보곤 eab39e0b29 feat: [hr] 사원관리 연봉정보 탭 생성
- 사원 상세/수정 페이지에 연봉정보 입력 섹션 추가
- 특수 권한 사용자만 열람/수정 가능한 접근 제어 적용
- 연봉 변경 시 자동 이력 기록
- 일반 API 응답에서 연봉 데이터 노출 방지 (toArray 오버라이드)
2026-03-11 16:27:49 +09:00

258 lines
7.1 KiB
PHP

<?php
namespace App\Models\HR;
use App\Models\Tenants\Department;
use App\Models\User;
use App\Traits\ModelTrait;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Employee extends Model
{
use ModelTrait;
const EMPLOYMENT_TYPES = [
'regular' => '정규직',
'contract' => '계약직',
'daily' => '일용직',
'freelancer' => '프리랜서',
];
protected $table = 'tenant_user_profiles';
protected $fillable = [
'tenant_id',
'user_id',
'department_id',
'position_key',
'job_title_key',
'work_location_key',
'employment_type_key',
'employee_status',
'worker_type',
'manager_user_id',
'json_extra',
'profile_photo_path',
'display_name',
];
protected static function booted(): void
{
static::addGlobalScope('employee', function (Builder $builder) {
$builder->where(function ($q) {
$q->where('worker_type', 'employee')
->orWhereNull('worker_type');
});
});
}
protected $casts = [
'json_extra' => 'array',
'tenant_id' => 'int',
'user_id' => 'int',
'department_id' => 'int',
'manager_user_id' => 'int',
];
protected $appends = [
'hire_date',
'resign_date',
'position_label',
'job_title_label',
];
// =========================================================================
// 관계 정의
// =========================================================================
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
public function department(): BelongsTo
{
return $this->belongsTo(Department::class, 'department_id');
}
public function manager(): BelongsTo
{
return $this->belongsTo(User::class, 'manager_user_id');
}
// =========================================================================
// json_extra Accessor
// =========================================================================
public function getHireDateAttribute(): ?string
{
return $this->json_extra['hire_date'] ?? null;
}
public function getResignDateAttribute(): ?string
{
return $this->json_extra['resign_date'] ?? null;
}
public function getAddressAttribute(): ?string
{
return $this->json_extra['address'] ?? null;
}
public function getEmergencyContactAttribute(): ?string
{
return $this->json_extra['emergency_contact'] ?? null;
}
public function getResidentNumberAttribute(): ?string
{
return $this->json_extra['resident_number'] ?? null;
}
public function getPersonalEmailAttribute(): ?string
{
return $this->json_extra['personal_email'] ?? null;
}
public function getBankAccountAttribute(): ?array
{
return $this->json_extra['bank_account'] ?? null;
}
public function getDependentsAttribute(): array
{
return $this->json_extra['dependents'] ?? [];
}
public function getPositionLabelAttribute(): ?string
{
if (! $this->position_key || ! $this->tenant_id) {
return $this->position_key;
}
$position = Position::where('tenant_id', $this->tenant_id)
->where('type', Position::TYPE_RANK)
->where('key', $this->position_key)
->first();
return $position?->name ?? $this->position_key;
}
public function getJobTitleLabelAttribute(): ?string
{
if (! $this->job_title_key || ! $this->tenant_id) {
return $this->job_title_key;
}
$position = Position::where('tenant_id', $this->tenant_id)
->where('type', Position::TYPE_TITLE)
->where('key', $this->job_title_key)
->first();
return $position?->name ?? $this->job_title_key;
}
// =========================================================================
// json_extra 헬퍼
// =========================================================================
public function getJsonExtraValue(string $key, mixed $default = null): mixed
{
return $this->json_extra[$key] ?? $default;
}
public function setJsonExtraValue(string $key, mixed $value): void
{
$extra = $this->json_extra ?? [];
if ($value === null) {
unset($extra[$key]);
} else {
$extra[$key] = $value;
}
$this->json_extra = $extra;
}
// =========================================================================
// 연봉 정보 (salary_info) — 민감 데이터, 별도 접근 제어
// =========================================================================
public function getSalaryInfo(): array
{
return $this->json_extra['salary_info'] ?? [
'annual_salary' => null,
'effective_date' => null,
'notes' => null,
'history' => [],
];
}
public function setSalaryInfo(array $data): void
{
$current = $this->getSalaryInfo();
$history = $current['history'] ?? [];
// 기존 연봉이 있으면 이력에 추가
if ($current['annual_salary'] !== null) {
$history[] = [
'annual_salary' => $current['annual_salary'],
'effective_date' => $current['effective_date'],
'notes' => $current['notes'],
'recorded_at' => now()->format('Y-m-d H:i:s'),
'recorded_by' => auth()->user()?->name ?? '-',
];
}
$this->setJsonExtraValue('salary_info', [
'annual_salary' => $data['annual_salary'] ?? null,
'effective_date' => $data['effective_date'] ?? null,
'notes' => $data['notes'] ?? null,
'history' => $history,
]);
}
/**
* toArray 시 salary_info 제거 (일반 API 응답에서 연봉 정보 노출 방지)
*/
public function toArray(): array
{
$array = parent::toArray();
if (isset($array['json_extra']['salary_info'])) {
unset($array['json_extra']['salary_info']);
}
return $array;
}
// =========================================================================
// 스코프
// =========================================================================
public function scopeForTenant($query, ?int $tenantId = null)
{
$tenantId = $tenantId ?? session('selected_tenant_id');
if ($tenantId) {
return $query->where('tenant_id', $tenantId);
}
return $query;
}
public function scopeActiveEmployees($query)
{
return $query->where('employee_status', 'active');
}
public function scopeOnLeave($query)
{
return $query->where('employee_status', 'leave');
}
public function scopeResigned($query)
{
return $query->where('employee_status', 'resigned');
}
}