'array', 'deductions' => 'array', 'base_salary' => 'decimal:2', 'overtime_pay' => 'decimal:2', 'bonus' => 'decimal:2', 'gross_salary' => 'decimal:2', 'income_tax' => 'decimal:2', 'resident_tax' => 'decimal:2', 'health_insurance' => 'decimal:2', 'pension' => 'decimal:2', 'employment_insurance' => 'decimal:2', 'total_deductions' => 'decimal:2', 'net_salary' => 'decimal:2', 'confirmed_at' => 'datetime', 'paid_at' => 'datetime', 'pay_year' => 'integer', 'pay_month' => 'integer', ]; protected $fillable = [ 'tenant_id', 'user_id', 'pay_year', 'pay_month', 'base_salary', 'overtime_pay', 'bonus', 'allowances', 'gross_salary', 'income_tax', 'resident_tax', 'health_insurance', 'pension', 'employment_insurance', 'deductions', 'total_deductions', 'net_salary', 'status', 'confirmed_at', 'confirmed_by', 'paid_at', 'withdrawal_id', 'note', 'created_by', 'updated_by', 'deleted_by', ]; protected $attributes = [ 'status' => 'draft', 'base_salary' => 0, 'overtime_pay' => 0, 'bonus' => 0, 'gross_salary' => 0, 'income_tax' => 0, 'resident_tax' => 0, 'health_insurance' => 0, 'pension' => 0, 'employment_insurance' => 0, 'total_deductions' => 0, 'net_salary' => 0, ]; // ========================================================================= // 상태 상수 // ========================================================================= public const STATUS_DRAFT = 'draft'; // 작성중 public const STATUS_CONFIRMED = 'confirmed'; // 확정 public const STATUS_PAID = 'paid'; // 지급완료 public const STATUSES = [ self::STATUS_DRAFT, self::STATUS_CONFIRMED, self::STATUS_PAID, ]; // ========================================================================= // 관계 정의 // ========================================================================= /** * 급여 대상 사용자 */ public function user(): BelongsTo { return $this->belongsTo(User::class, 'user_id'); } /** * 확정자 */ public function confirmer(): BelongsTo { return $this->belongsTo(User::class, 'confirmed_by'); } /** * 출금 내역 */ public function withdrawal(): BelongsTo { return $this->belongsTo(Withdrawal::class, 'withdrawal_id'); } /** * 생성자 */ public function creator(): BelongsTo { return $this->belongsTo(User::class, 'created_by'); } /** * 수정자 */ public function updater(): BelongsTo { return $this->belongsTo(User::class, 'updated_by'); } // ========================================================================= // 스코프 // ========================================================================= /** * 특정 상태 */ public function scopeWithStatus($query, string $status) { return $query->where('status', $status); } /** * 작성중 */ public function scopeDraft($query) { return $query->where('status', self::STATUS_DRAFT); } /** * 확정 */ public function scopeConfirmed($query) { return $query->where('status', self::STATUS_CONFIRMED); } /** * 지급완료 */ public function scopePaid($query) { return $query->where('status', self::STATUS_PAID); } /** * 특정 연월 */ public function scopeForPeriod($query, int $year, int $month) { return $query->where('pay_year', $year)->where('pay_month', $month); } /** * 특정 사용자 */ public function scopeForUser($query, int $userId) { return $query->where('user_id', $userId); } // ========================================================================= // 헬퍼 메서드 // ========================================================================= /** * 수정 가능 여부 (작성중 상태만) */ public function isEditable(): bool { return $this->status === self::STATUS_DRAFT; } /** * 확정 가능 여부 */ public function isConfirmable(): bool { return $this->status === self::STATUS_DRAFT; } /** * 지급 가능 여부 */ public function isPayable(): bool { return $this->status === self::STATUS_CONFIRMED; } /** * 삭제 가능 여부 (작성중만) */ public function isDeletable(): bool { return $this->status === self::STATUS_DRAFT; } /** * 상태 라벨 */ public function getStatusLabelAttribute(): string { return match ($this->status) { self::STATUS_DRAFT => '작성중', self::STATUS_CONFIRMED => '확정', self::STATUS_PAID => '지급완료', default => $this->status, }; } /** * 급여 기간 문자열 */ public function getPeriodLabelAttribute(): string { return sprintf('%d년 %02d월', $this->pay_year, $this->pay_month); } /** * 수당 합계 */ public function getAllowancesTotalAttribute(): float { if (empty($this->allowances)) { return 0; } return collect($this->allowances)->sum('amount'); } /** * 공제 합계 (JSON) */ public function getDeductionsTotalAttribute(): float { if (empty($this->deductions)) { return 0; } return collect($this->deductions)->sum('amount'); } /** * 총지급액 계산 */ public function calculateGrossSalary(): float { return $this->base_salary + $this->overtime_pay + $this->bonus + $this->allowances_total; } /** * 총공제액 계산 */ public function calculateTotalDeductions(): float { return $this->income_tax + $this->resident_tax + $this->health_insurance + $this->pension + $this->employment_insurance + $this->deductions_total; } /** * 실수령액 계산 */ public function calculateNetSalary(): float { return $this->gross_salary - $this->total_deductions; } }