'int', 'user_id' => 'int', 'pay_year' => 'int', 'pay_month' => 'int', 'base_salary' => 'decimal:0', 'overtime_pay' => 'decimal:0', 'bonus' => 'decimal:0', 'gross_salary' => 'decimal:0', 'income_tax' => 'decimal:0', 'resident_tax' => 'decimal:0', 'health_insurance' => 'decimal:0', 'long_term_care' => 'decimal:0', 'pension' => 'decimal:0', 'employment_insurance' => 'decimal:0', 'total_deductions' => 'decimal:0', 'net_salary' => 'decimal:0', 'allowances' => 'array', 'deductions' => 'array', 'confirmed_at' => 'datetime', 'paid_at' => 'datetime', 'options' => 'array', ]; protected $attributes = [ 'status' => 'draft', ]; public const STATUS_DRAFT = 'draft'; public const STATUS_CONFIRMED = 'confirmed'; public const STATUS_PAID = 'paid'; public const STATUS_MAP = [ 'draft' => '작성중', 'confirmed' => '확정', 'paid' => '지급완료', ]; public const STATUS_COLORS = [ 'draft' => 'amber', 'confirmed' => 'blue', 'paid' => 'emerald', ]; // ========================================================================= // 관계 정의 // ========================================================================= public function user(): BelongsTo { return $this->belongsTo(User::class, 'user_id'); } public function confirmer(): BelongsTo { return $this->belongsTo(User::class, 'confirmed_by'); } public function creator(): BelongsTo { return $this->belongsTo(User::class, 'created_by'); } // ========================================================================= // Accessor // ========================================================================= public function getStatusLabelAttribute(): string { return self::STATUS_MAP[$this->status] ?? $this->status; } public function getStatusColorAttribute(): string { return self::STATUS_COLORS[$this->status] ?? 'gray'; } public function getPeriodLabelAttribute(): string { return sprintf('%d년 %d월', $this->pay_year, $this->pay_month); } // ========================================================================= // 상태 헬퍼 // ========================================================================= public function isEditable(?bool $isSuperAdmin = null): bool { if ($isSuperAdmin && in_array($this->status, [self::STATUS_CONFIRMED, self::STATUS_PAID])) { return true; } return $this->status === self::STATUS_DRAFT; } public function isConfirmable(): bool { return $this->status === self::STATUS_DRAFT; } public function isUnconfirmable(): bool { return $this->status === self::STATUS_CONFIRMED; } public function isPayable(): bool { return $this->status === self::STATUS_CONFIRMED; } public function isDeletable(): bool { return $this->status === self::STATUS_DRAFT; } public function isUnpayable(): bool { return $this->status === self::STATUS_PAID; } // ========================================================================= // 스코프 // ========================================================================= public function scopeForTenant($query, ?int $tenantId = null) { $tenantId = $tenantId ?? session('selected_tenant_id', 1); return $query->where($this->table.'.tenant_id', $tenantId); } public function scopeForPeriod($query, int $year, int $month) { return $query->where('pay_year', $year)->where('pay_month', $month); } public function scopeWithStatus($query, string $status) { return $query->where('status', $status); } }