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