redmine-workflow-engine/app/models/issue_workflow_state.rb
ioresponse e67fb92189 Initial commit: Redmine Workflow Engine Plugin
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
2025-12-23 00:16:43 +09:00

112 lines
3.4 KiB
Ruby

class IssueWorkflowState < ActiveRecord::Base
belongs_to :issue
belongs_to :custom_workflow
belongs_to :current_step, class_name: 'WorkflowStep', optional: true
belongs_to :completed_by, class_name: 'User', optional: true
validates :issue_id, presence: true, uniqueness: true
validates :custom_workflow_id, presence: true
def current_step_name
current_step&.name || '(시작 전)'
end
def progress_percent
return 0 unless current_step
total = custom_workflow.step_count
return 100 if current_step.last_step?
current_position = current_step.position + 1
(current_position.to_f / total * 100).round
end
def can_proceed?(user)
return false unless current_step
return false if completed?
return true if user.admin? # 관리자는 항상 가능
return true if current_step.first_step? && issue.author == user # 첫 단계는 작성자가 진행 가능
return true if current_step.workflow_step_assignees.empty? # 담당자 없으면 누구나 가능
current_step.assignee?(user)
end
def can_go_back?(user)
return false unless current_step
return false if current_step.first_step?
# 관리자이거나 이전 단계 담당자면 가능
user.admin? || current_step.prev_step&.assignee?(user)
end
def proceed!(user, selected_assignee_id = nil)
return false unless can_proceed?(user)
next_step = current_step.next_step
if next_step
update(current_step: next_step)
update_issue_for_step(next_step, selected_assignee_id)
else
# 마지막 단계 완료
update(completed_at: Time.current, completed_by: user)
# 마지막 단계의 상태로 변경
update_issue_for_step(current_step)
end
true
end
def go_back!(user)
return false unless can_go_back?(user)
prev_step = current_step.prev_step
if prev_step
update(current_step: prev_step, completed_at: nil, completed_by: nil)
update_issue_for_step(prev_step)
end
true
end
def completed?
completed_at.present?
end
def visible_to?(user)
return true if user.admin?
return true if issue.author == user
return true if current_step&.assignee?(user)
# 모든 단계의 담당자인지 확인
custom_workflow.workflow_steps.any? { |step| step.assignee?(user) }
end
private
def update_issue_for_step(step, selected_assignee_id = nil)
return unless step
# 이력 남기기 위해 journal 초기화
issue.init_journal(User.current, "워크플로우: #{step.name} 단계로 이동")
# 상태 변경
if step.issue_status_id.present?
issue.status_id = step.issue_status_id
end
# 담당자 변경
if selected_assignee_id.present?
# 선택된 담당자가 있으면 그 사람으로 할당
issue.assigned_to_id = selected_assignee_id
else
# 개인 사용자가 지정된 경우 첫번째 사용자로 할당
user_assignees = step.workflow_step_assignees.where(assignee_type: 'user')
if user_assignees.any?
issue.assigned_to_id = user_assignees.first.assignee_id
elsif step.workflow_step_assignees.any?
# 그룹/부서만 있고 선택 안된 경우 첫번째 멤버로 할당
assignees = step.all_assignee_users
issue.assigned_to_id = assignees.first&.id
end
end
# validate: false로 저장 (프로젝트 멤버 체크 우회, 이력은 남김)
issue.save(validate: false)
end
end