feat: 휴가 부여현황 API 추가

- leave_grants 테이블 마이그레이션 추가
- LeaveGrant 모델 생성 (annual/monthly/reward/condolence/other 유형)
- LeaveService에 getGrants, storeGrant, destroyGrant 메서드 추가
- LeaveController에 grants, storeGrant, destroyGrant 엔드포인트 추가
- GET/POST/DELETE /api/v1/leaves/grants 라우트 추가
- 연차/월차 부여 시 LeaveBalance total_days 자동 갱신
This commit is contained in:
2025-12-24 19:39:33 +09:00
parent 3988372ca4
commit 01d9ccaf57
6 changed files with 363 additions and 0 deletions

View File

@@ -4,6 +4,7 @@
use App\Models\Tenants\Leave;
use App\Models\Tenants\LeaveBalance;
use App\Models\Tenants\LeaveGrant;
use App\Models\Tenants\TenantUserProfile;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
@@ -443,4 +444,142 @@ private function getOrCreateBalance(int $tenantId, int $userId, int $year): Leav
return $balance;
}
// =========================================================================
// 휴가 부여 관련 메서드
// =========================================================================
/**
* 휴가 부여 이력 목록 조회
*/
public function getGrants(array $params): LengthAwarePaginator
{
$tenantId = $this->tenantId();
$query = LeaveGrant::query()
->where('tenant_id', $tenantId)
->with([
'user:id,name,email',
'user.tenantProfile' => function ($q) use ($tenantId) {
$q->where('tenant_id', $tenantId)
->with('department:id,name');
},
'creator:id,name',
]);
// 사용자 필터
if (! empty($params['user_id'])) {
$query->where('user_id', $params['user_id']);
}
// 부여 유형 필터
if (! empty($params['grant_type'])) {
$query->where('grant_type', $params['grant_type']);
}
// 날짜 범위 필터
if (! empty($params['date_from'])) {
$query->where('grant_date', '>=', $params['date_from']);
}
if (! empty($params['date_to'])) {
$query->where('grant_date', '<=', $params['date_to']);
}
// 연도 필터
if (! empty($params['year'])) {
$query->whereYear('grant_date', $params['year']);
}
// 부서 필터
if (! empty($params['department_id'])) {
$query->whereHas('user.tenantProfile', function ($q) use ($params, $tenantId) {
$q->where('tenant_id', $tenantId)
->where('department_id', $params['department_id']);
});
}
// 검색 (사용자명)
if (! empty($params['search'])) {
$query->whereHas('user', function ($q) use ($params) {
$q->where('name', 'like', '%'.$params['search'].'%');
});
}
// 정렬
$sortBy = $params['sort_by'] ?? 'grant_date';
$sortDir = $params['sort_dir'] ?? 'desc';
$query->orderBy($sortBy, $sortDir);
// 페이지네이션
$perPage = $params['per_page'] ?? 20;
return $query->paginate($perPage);
}
/**
* 휴가 부여
*/
public function storeGrant(array $data): LeaveGrant
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
return DB::transaction(function () use ($data, $tenantId, $userId) {
$grant = LeaveGrant::create([
'tenant_id' => $tenantId,
'user_id' => $data['user_id'],
'grant_type' => $data['grant_type'],
'grant_date' => $data['grant_date'],
'grant_days' => $data['grant_days'],
'reason' => $data['reason'] ?? null,
'created_by' => $userId,
]);
// 연차/월차인 경우 LeaveBalance의 total_days에 추가
if (in_array($data['grant_type'], [LeaveGrant::TYPE_ANNUAL, LeaveGrant::TYPE_MONTHLY])) {
$year = \Carbon\Carbon::parse($data['grant_date'])->year;
$balance = $this->getOrCreateBalance($tenantId, $data['user_id'], $year);
$balance->total_days += $data['grant_days'];
$balance->save();
}
return $grant->fresh(['user:id,name,email', 'creator:id,name']);
});
}
/**
* 휴가 부여 삭제 (soft delete)
*/
public function destroyGrant(int $id): bool
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
return DB::transaction(function () use ($id, $tenantId, $userId) {
$grant = LeaveGrant::query()
->where('tenant_id', $tenantId)
->findOrFail($id);
// 연차/월차인 경우 LeaveBalance의 total_days에서 차감
if (in_array($grant->grant_type, [LeaveGrant::TYPE_ANNUAL, LeaveGrant::TYPE_MONTHLY])) {
$year = \Carbon\Carbon::parse($grant->grant_date)->year;
$balance = LeaveBalance::query()
->where('tenant_id', $tenantId)
->where('user_id', $grant->user_id)
->where('year', $year)
->first();
if ($balance) {
$balance->total_days = max(0, $balance->total_days - $grant->grant_days);
$balance->save();
}
}
$grant->deleted_by = $userId;
$grant->save();
$grant->delete();
return true;
});
}
}