feat: [approval] 전자결재 모듈 API 구현
- 마이그레이션 4개 (approval_forms, approval_lines, approvals, approval_steps) - 모델 4개 (ApprovalForm, ApprovalLine, Approval, ApprovalStep) - ApprovalService 비즈니스 로직 (양식/결재선 CRUD, 기안함/결재함/참조함, 결재 액션) - 컨트롤러 3개 (ApprovalFormController, ApprovalLineController, ApprovalController) - FormRequest 13개 (양식/결재선/문서 검증) - Swagger 문서 3개 (26개 엔드포인트) - i18n 메시지/에러 키 추가 - 라우트 26개 등록
This commit is contained in:
181
app/Models/Tenants/ApprovalStep.php
Normal file
181
app/Models/Tenants/ApprovalStep.php
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Tenants;
|
||||
|
||||
use App\Models\Members\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* 결재 단계 모델
|
||||
*
|
||||
* @property int $id
|
||||
* @property int $approval_id
|
||||
* @property int $step_order
|
||||
* @property string $step_type
|
||||
* @property int $approver_id
|
||||
* @property string $status
|
||||
* @property string|null $comment
|
||||
* @property \Carbon\Carbon|null $acted_at
|
||||
* @property bool $is_read
|
||||
* @property \Carbon\Carbon|null $read_at
|
||||
*/
|
||||
class ApprovalStep extends Model
|
||||
{
|
||||
protected $table = 'approval_steps';
|
||||
|
||||
protected $casts = [
|
||||
'step_order' => 'integer',
|
||||
'acted_at' => 'datetime',
|
||||
'is_read' => 'boolean',
|
||||
'read_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'approval_id',
|
||||
'step_order',
|
||||
'step_type',
|
||||
'approver_id',
|
||||
'status',
|
||||
'comment',
|
||||
'acted_at',
|
||||
'is_read',
|
||||
'read_at',
|
||||
];
|
||||
|
||||
protected $attributes = [
|
||||
'status' => 'pending',
|
||||
'is_read' => false,
|
||||
];
|
||||
|
||||
// =========================================================================
|
||||
// 상태 상수
|
||||
// =========================================================================
|
||||
|
||||
public const STATUS_PENDING = 'pending'; // 대기
|
||||
|
||||
public const STATUS_APPROVED = 'approved'; // 승인
|
||||
|
||||
public const STATUS_REJECTED = 'rejected'; // 반려
|
||||
|
||||
public const STATUS_SKIPPED = 'skipped'; // 건너뜀
|
||||
|
||||
public const STATUSES = [
|
||||
self::STATUS_PENDING,
|
||||
self::STATUS_APPROVED,
|
||||
self::STATUS_REJECTED,
|
||||
self::STATUS_SKIPPED,
|
||||
];
|
||||
|
||||
// =========================================================================
|
||||
// 관계 정의
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 결재 문서
|
||||
*/
|
||||
public function approval(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Approval::class, 'approval_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 결재자/참조자
|
||||
*/
|
||||
public function approver(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'approver_id');
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 스코프
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 대기 중
|
||||
*/
|
||||
public function scopePending($query)
|
||||
{
|
||||
return $query->where('status', self::STATUS_PENDING);
|
||||
}
|
||||
|
||||
/**
|
||||
* 승인됨
|
||||
*/
|
||||
public function scopeApproved($query)
|
||||
{
|
||||
return $query->where('status', self::STATUS_APPROVED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 결재자
|
||||
*/
|
||||
public function scopeByApprover($query, int $userId)
|
||||
{
|
||||
return $query->where('approver_id', $userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 결재 단계만 (참조 제외)
|
||||
*/
|
||||
public function scopeApprovalOnly($query)
|
||||
{
|
||||
return $query->whereIn('step_type', [ApprovalLine::STEP_TYPE_APPROVAL, ApprovalLine::STEP_TYPE_AGREEMENT]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 참조만
|
||||
*/
|
||||
public function scopeReferenceOnly($query)
|
||||
{
|
||||
return $query->where('step_type', ApprovalLine::STEP_TYPE_REFERENCE);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 헬퍼 메서드
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 결재 가능 여부
|
||||
*/
|
||||
public function isActionable(): bool
|
||||
{
|
||||
return $this->status === self::STATUS_PENDING
|
||||
&& in_array($this->step_type, [ApprovalLine::STEP_TYPE_APPROVAL, ApprovalLine::STEP_TYPE_AGREEMENT]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 참조인지 확인
|
||||
*/
|
||||
public function isReference(): bool
|
||||
{
|
||||
return $this->step_type === ApprovalLine::STEP_TYPE_REFERENCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태 라벨
|
||||
*/
|
||||
public function getStatusLabelAttribute(): string
|
||||
{
|
||||
return match ($this->status) {
|
||||
self::STATUS_PENDING => '대기',
|
||||
self::STATUS_APPROVED => '승인',
|
||||
self::STATUS_REJECTED => '반려',
|
||||
self::STATUS_SKIPPED => '건너뜀',
|
||||
default => $this->status,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 단계 유형 라벨
|
||||
*/
|
||||
public function getStepTypeLabelAttribute(): string
|
||||
{
|
||||
return match ($this->step_type) {
|
||||
ApprovalLine::STEP_TYPE_APPROVAL => '결재',
|
||||
ApprovalLine::STEP_TYPE_AGREEMENT => '합의',
|
||||
ApprovalLine::STEP_TYPE_REFERENCE => '참조',
|
||||
default => $this->step_type,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user