- Auditable 트레이트 신규 생성 (bootAuditable 패턴) - creating: created_by/updated_by 자동 채우기 - updating: updated_by 자동 채우기 - deleting: deleted_by 채우기 + saveQuietly() - created/updated/deleted: audit_logs 자동 기록 - 기존 AuditLogger 패턴과 동일한 try/catch 조용한 실패 - 변경된 필드만 before/after 기록 (updated 이벤트) - auditExclude 프로퍼티로 모델별 제외 필드 설정 가능 - 제외 대상: Attendance, StockTransaction, TodayIssue 등 고빈도/시스템 모델 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
235 lines
6.2 KiB
PHP
235 lines
6.2 KiB
PHP
<?php
|
|
|
|
namespace App\Models\Tenants;
|
|
|
|
use App\Models\Members\User;
|
|
use App\Traits\Auditable;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
|
|
/**
|
|
* 테넌트별 사용자 프로필 (사원 정보)
|
|
*
|
|
* @property int $id
|
|
* @property int $tenant_id
|
|
* @property int $user_id
|
|
* @property int|null $department_id
|
|
* @property string $employee_status active|leave|resigned
|
|
* @property array|null $json_extra
|
|
*
|
|
* @mixin IdeHelperTenantUserProfile
|
|
*/
|
|
class TenantUserProfile extends Model
|
|
{
|
|
use Auditable;
|
|
protected $casts = [
|
|
'json_extra' => 'array',
|
|
];
|
|
|
|
protected $appends = [
|
|
'position_label',
|
|
'job_title_label',
|
|
'rank',
|
|
'hire_date',
|
|
];
|
|
|
|
protected $fillable = [
|
|
'tenant_id',
|
|
'user_id',
|
|
'department_id',
|
|
'position_key',
|
|
'job_title_key',
|
|
'work_location_key',
|
|
'employment_type_key',
|
|
'employee_status',
|
|
'manager_user_id',
|
|
'json_extra',
|
|
'profile_photo_path',
|
|
'display_name',
|
|
];
|
|
|
|
// =========================================================================
|
|
// 관계 정의
|
|
// =========================================================================
|
|
|
|
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');
|
|
}
|
|
|
|
/**
|
|
* 직급 (positions 테이블 type=rank)
|
|
*/
|
|
public function rankPosition(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Position::class, 'position_key', 'key')
|
|
->where('type', Position::TYPE_RANK)
|
|
->where('tenant_id', $this->tenant_id);
|
|
}
|
|
|
|
/**
|
|
* 직책 (positions 테이블 type=title)
|
|
*/
|
|
public function titlePosition(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Position::class, 'job_title_key', 'key')
|
|
->where('type', Position::TYPE_TITLE)
|
|
->where('tenant_id', $this->tenant_id);
|
|
}
|
|
|
|
// =========================================================================
|
|
// json_extra 헬퍼 메서드
|
|
// =========================================================================
|
|
|
|
/**
|
|
* json_extra에서 특정 키 값 가져오기
|
|
*/
|
|
public function getJsonExtraValue(string $key, mixed $default = null): mixed
|
|
{
|
|
return $this->json_extra[$key] ?? $default;
|
|
}
|
|
|
|
/**
|
|
* json_extra에 특정 키 값 설정
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 사원 정보 일괄 업데이트 (json_extra)
|
|
*/
|
|
public function updateEmployeeInfo(array $data): void
|
|
{
|
|
$allowedKeys = [
|
|
'employee_code',
|
|
'resident_number',
|
|
'gender',
|
|
'address',
|
|
'salary',
|
|
'hire_date',
|
|
'rank',
|
|
'bank_account',
|
|
'work_type',
|
|
'contract_info',
|
|
'emergency_contact',
|
|
'education',
|
|
'certifications',
|
|
];
|
|
|
|
$extra = $this->json_extra ?? [];
|
|
foreach ($allowedKeys as $key) {
|
|
if (array_key_exists($key, $data)) {
|
|
if ($data[$key] === null) {
|
|
unset($extra[$key]);
|
|
} else {
|
|
$extra[$key] = $data[$key];
|
|
}
|
|
}
|
|
}
|
|
$this->json_extra = $extra;
|
|
$this->save();
|
|
}
|
|
|
|
// =========================================================================
|
|
// json_extra Accessor (자주 사용하는 필드)
|
|
// =========================================================================
|
|
|
|
public function getEmployeeCodeAttribute(): ?string
|
|
{
|
|
return $this->json_extra['employee_code'] ?? null;
|
|
}
|
|
|
|
public function getHireDateAttribute(): ?string
|
|
{
|
|
return $this->json_extra['hire_date'] ?? null;
|
|
}
|
|
|
|
public function getAddressAttribute(): ?string
|
|
{
|
|
return $this->json_extra['address'] ?? null;
|
|
}
|
|
|
|
public function getEmergencyContactAttribute(): ?string
|
|
{
|
|
return $this->json_extra['emergency_contact'] ?? null;
|
|
}
|
|
|
|
/**
|
|
* 직급 레이블 (position_key → positions 테이블에서 name 조회)
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 직책 레이블 (job_title_key → positions 테이블에서 name 조회)
|
|
*/
|
|
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 내 직급 정보 (rank)
|
|
*/
|
|
public function getRankAttribute(): ?string
|
|
{
|
|
return $this->json_extra['rank'] ?? null;
|
|
}
|
|
|
|
// =========================================================================
|
|
// 스코프
|
|
// =========================================================================
|
|
|
|
public function scopeActive($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');
|
|
}
|
|
}
|