'decimal:2', 'total_allowance' => 'decimal:2', 'total_overtime' => 'decimal:2', 'total_bonus' => 'decimal:2', 'total_deduction' => 'decimal:2', 'net_payment' => 'decimal:2', 'allowance_details' => 'array', 'deduction_details' => 'array', 'payment_date' => 'date', ]; protected $attributes = [ 'status' => 'scheduled', 'base_salary' => 0, 'total_allowance' => 0, 'total_overtime' => 0, 'total_bonus' => 0, 'total_deduction' => 0, 'net_payment' => 0, ]; // ========================================================================= // 관계 정의 // ========================================================================= /** * 직원 */ public function employee(): BelongsTo { return $this->belongsTo(User::class, 'employee_id'); } /** * 생성자 */ public function creator(): BelongsTo { return $this->belongsTo(User::class, 'created_by'); } /** * 수정자 */ public function updater(): BelongsTo { return $this->belongsTo(User::class, 'updated_by'); } /** * 직원 프로필 (테넌트별) * 주의: eager loading 시 constrain 필요 * ->with(['employeeProfile' => fn($q) => $q->where('tenant_id', $tenantId)]) */ public function employeeProfile(): HasOne { return $this->hasOne(TenantUserProfile::class, 'user_id', 'employee_id'); } // ========================================================================= // 헬퍼 메서드 // ========================================================================= /** * 지급 완료 여부 */ public function isCompleted(): bool { return $this->status === 'completed'; } /** * 지급 예정 여부 */ public function isScheduled(): bool { return $this->status === 'scheduled'; } /** * 상태 변경 */ public function markAsCompleted(): void { $this->status = 'completed'; } /** * 상태 변경 */ public function markAsScheduled(): void { $this->status = 'scheduled'; } /** * 실지급액 계산 */ public function calculateNetPayment(): float { return $this->base_salary + $this->total_allowance + $this->total_overtime + $this->total_bonus - $this->total_deduction; } /** * 년월 표시 */ public function getPeriodLabel(): string { return sprintf('%d년 %d월', $this->year, $this->month); } }