Features:
- Custom workflow creation per project/tracker
- Step-by-step workflow definition
- Assignees per step (user, role group, department)
- Next/Previous step navigation
- Reject to first step
- Skip step (admin only)
- Step deadline settings
- Workflow dashboard
- Group member selection when proceeding
🤖 Generated with Claude Code
178 lines
5.7 KiB
Ruby
178 lines
5.7 KiB
Ruby
class IssueWorkflowsController < ApplicationController
|
|
before_action :find_issue
|
|
before_action :find_or_create_workflow_state
|
|
|
|
def show
|
|
@steps = @workflow_state.custom_workflow.workflow_steps.ordered
|
|
@current_step = @workflow_state.current_step
|
|
end
|
|
|
|
def next_step
|
|
if @workflow_state.can_proceed?(User.current)
|
|
selected_assignee_id = params[:assignee_id]
|
|
if @workflow_state.proceed!(User.current, selected_assignee_id)
|
|
flash[:notice] = '다음 단계로 이동했습니다.'
|
|
else
|
|
flash[:error] = '다음 단계로 이동할 수 없습니다.'
|
|
end
|
|
else
|
|
flash[:error] = '이 단계를 완료할 권한이 없습니다.'
|
|
end
|
|
redirect_to issue_path(@issue)
|
|
end
|
|
|
|
def prev_step
|
|
if @workflow_state.can_go_back?(User.current)
|
|
if @workflow_state.go_back!(User.current)
|
|
flash[:notice] = '이전 단계로 되돌렸습니다.'
|
|
else
|
|
flash[:error] = '이전 단계로 이동할 수 없습니다.'
|
|
end
|
|
else
|
|
flash[:error] = '이전 단계로 되돌릴 권한이 없습니다.'
|
|
end
|
|
redirect_to issue_path(@issue)
|
|
end
|
|
|
|
def complete
|
|
current_step = @workflow_state.current_step
|
|
if current_step&.is_end && (User.current.admin? || current_step.assignee?(User.current))
|
|
@issue.init_journal(User.current, "워크플로우 완료 처리")
|
|
@issue.status_id = 5 # 완료 상태
|
|
if @issue.save(validate: false)
|
|
@workflow_state.update(completed_at: Time.current, completed_by: User.current)
|
|
flash[:notice] = '이슈가 완료 처리되었습니다.'
|
|
else
|
|
flash[:error] = '완료 처리에 실패했습니다.'
|
|
end
|
|
else
|
|
flash[:error] = '완료 처리 권한이 없습니다.'
|
|
end
|
|
redirect_to issue_path(@issue)
|
|
end
|
|
|
|
def reject
|
|
current_step = @workflow_state.current_step
|
|
reason = params[:reason].to_s.strip
|
|
|
|
unless current_step && !current_step.is_start && (User.current.admin? || current_step.assignee?(User.current))
|
|
flash[:error] = '반려 권한이 없습니다.'
|
|
redirect_to issue_path(@issue)
|
|
return
|
|
end
|
|
|
|
if reason.blank?
|
|
flash[:error] = '반려 사유를 입력해주세요.'
|
|
redirect_to issue_path(@issue)
|
|
return
|
|
end
|
|
|
|
# 항상 최초 단계로 반려
|
|
first_step = @workflow_state.custom_workflow.workflow_steps.ordered.first
|
|
|
|
if first_step
|
|
# 이력 남기기
|
|
@issue.init_journal(User.current, "[반려] #{current_step.name} → #{first_step.name}\n사유: #{reason}")
|
|
|
|
# 상태 변경 (최초 단계의 상태로)
|
|
if first_step.issue_status_id.present?
|
|
@issue.status_id = first_step.issue_status_id
|
|
end
|
|
|
|
# 담당자 변경 - 개인 사용자일 때만, 그룹/부서는 미지정
|
|
user_assignees = first_step.workflow_step_assignees.where(assignee_type: 'user')
|
|
if user_assignees.any?
|
|
@issue.assigned_to_id = user_assignees.first.assignee_id
|
|
elsif first_step.workflow_step_assignees.any?
|
|
@issue.assigned_to_id = nil
|
|
end
|
|
|
|
@issue.save(validate: false)
|
|
@workflow_state.update(current_step: first_step, completed_at: nil, completed_by: nil)
|
|
|
|
flash[:notice] = "#{first_step.name} 단계로 반려되었습니다."
|
|
else
|
|
flash[:error] = '반려 대상 단계를 찾을 수 없습니다.'
|
|
end
|
|
|
|
redirect_to issue_path(@issue)
|
|
end
|
|
|
|
def skip_step
|
|
# 관리자만 단계 건너뛰기 가능
|
|
unless User.current.admin?
|
|
flash[:error] = '관리자만 단계를 건너뛸 수 있습니다.'
|
|
redirect_to issue_path(@issue)
|
|
return
|
|
end
|
|
|
|
current_step = @workflow_state.current_step
|
|
target_step_id = params[:target_step_id]
|
|
reason = params[:reason].to_s.strip
|
|
|
|
if target_step_id.blank?
|
|
flash[:error] = '이동할 단계를 선택해주세요.'
|
|
redirect_to issue_path(@issue)
|
|
return
|
|
end
|
|
|
|
target_step = WorkflowStep.find_by(id: target_step_id)
|
|
|
|
if target_step && target_step.custom_workflow_id == @workflow_state.custom_workflow_id
|
|
# 이력 남기기
|
|
note = "[단계 건너뛰기] #{current_step&.name || '(시작)'} → #{target_step.name}"
|
|
note += "\n사유: #{reason}" if reason.present?
|
|
@issue.init_journal(User.current, note)
|
|
|
|
# 상태 변경
|
|
if target_step.issue_status_id.present?
|
|
@issue.status_id = target_step.issue_status_id
|
|
end
|
|
|
|
# 담당자 변경 - 개인 사용자일 때만, 그룹/부서는 미지정
|
|
user_assignees = target_step.workflow_step_assignees.where(assignee_type: 'user')
|
|
if user_assignees.any?
|
|
@issue.assigned_to_id = user_assignees.first.assignee_id
|
|
elsif target_step.workflow_step_assignees.any?
|
|
@issue.assigned_to_id = nil
|
|
end
|
|
|
|
@issue.save(validate: false)
|
|
@workflow_state.update(current_step: target_step, completed_at: nil, completed_by: nil)
|
|
|
|
flash[:notice] = "#{target_step.name} 단계로 이동했습니다."
|
|
else
|
|
flash[:error] = '이동할 단계를 찾을 수 없습니다.'
|
|
end
|
|
|
|
redirect_to issue_path(@issue)
|
|
end
|
|
|
|
private
|
|
|
|
def find_issue
|
|
@issue = Issue.find(params[:issue_id])
|
|
rescue ActiveRecord::RecordNotFound
|
|
render_404
|
|
end
|
|
|
|
def find_or_create_workflow_state
|
|
@workflow_state = IssueWorkflowState.find_by(issue_id: @issue.id)
|
|
|
|
unless @workflow_state
|
|
workflow = CustomWorkflow.find_for_issue(@issue)
|
|
if workflow
|
|
@workflow_state = IssueWorkflowState.create!(
|
|
issue: @issue,
|
|
custom_workflow: workflow,
|
|
current_step: workflow.start_step,
|
|
started_at: Time.current
|
|
)
|
|
else
|
|
flash[:warning] = '이 이슈에 적용할 워크플로우가 없습니다.'
|
|
redirect_to issue_path(@issue)
|
|
end
|
|
end
|
|
end
|
|
end
|