feat: 급여 관리 API 및 더미 시더 확장

- 급여 관리 API 추가 (SalaryController, SalaryService, Salary 모델)
  - 급여 목록/상세/등록/수정/삭제
  - 상태 변경 (scheduled/completed)
  - 일괄 상태 변경
  - 통계 조회
- 더미 시더 확장
  - 근태, 휴가, 부서, 사용자 시더 추가
  - 기존 시더 tenant_id/created_by/updated_by 필드 추가
- 부서 서비스 개선 (tree 조회 기능 추가)
- 카드 서비스 수정

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-25 03:48:32 +09:00
parent 0508282e58
commit 638e87b05d
35 changed files with 2057 additions and 24 deletions

View File

@@ -46,6 +46,18 @@ class User extends Authenticatable
'deleted_at',
];
protected $appends = [
'has_account',
];
/**
* 시스템 계정(비밀번호) 보유 여부
*/
public function getHasAccountAttribute(): bool
{
return ! empty($this->password);
}
public function userTenants()
{
return $this->hasMany(UserTenant::class);

View File

@@ -9,11 +9,12 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\Permission\Traits\HasRoles;
class Department extends Model
{
use HasRoles, ModelTrait; // 부서도 권한/역할을 가짐
use HasRoles, ModelTrait, SoftDeletes; // 부서도 권한/역할을 가짐
protected $table = 'departments';

View File

@@ -0,0 +1,165 @@
<?php
namespace App\Models\Tenants;
use App\Models\Members\User;
use App\Traits\BelongsToTenant;
use App\Traits\ModelTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* 급여 모델
*
* @property int $id
* @property int $tenant_id
* @property int $employee_id
* @property int $year
* @property int $month
* @property float $base_salary
* @property float $total_allowance
* @property float $total_overtime
* @property float $total_bonus
* @property float $total_deduction
* @property float $net_payment
* @property array|null $allowance_details
* @property array|null $deduction_details
* @property string|null $payment_date
* @property string $status
* @property int|null $created_by
* @property int|null $updated_by
* @property int|null $deleted_by
*/
class Salary extends Model
{
use BelongsToTenant, ModelTrait, SoftDeletes;
protected $table = 'salaries';
protected $fillable = [
'tenant_id',
'employee_id',
'year',
'month',
'base_salary',
'total_allowance',
'total_overtime',
'total_bonus',
'total_deduction',
'net_payment',
'allowance_details',
'deduction_details',
'payment_date',
'status',
'created_by',
'updated_by',
'deleted_by',
];
protected $casts = [
'base_salary' => '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');
}
// =========================================================================
// 헬퍼 메서드
// =========================================================================
/**
* 지급 완료 여부
*/
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);
}
}