diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-15 12:07:59 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-15 12:07:59 +0300 |
commit | e5731d5194e20deb33725943248c5899e4fdf05d (patch) | |
tree | aac082e7b302151dfe38a8a58c0c90f1a66fa017 /app/models/iteration.rb | |
parent | 1e9d859394883d104191c51fa18c2353f7bcc1fd (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/models/iteration.rb')
-rw-r--r-- | app/models/iteration.rb | 82 |
1 files changed, 73 insertions, 9 deletions
diff --git a/app/models/iteration.rb b/app/models/iteration.rb index 630d25d3ec5..bcb25012973 100644 --- a/app/models/iteration.rb +++ b/app/models/iteration.rb @@ -5,9 +5,12 @@ class Iteration < ApplicationRecord self.table_name = 'sprints' - STATE_ID_MAP = { - active: 1, - closed: 2 + attr_accessor :skip_future_date_validation + + STATE_ENUM_MAP = { + upcoming: 1, + started: 2, + closed: 3 }.with_indifferent_access.freeze include AtomicInternalId @@ -21,16 +24,77 @@ class Iteration < ApplicationRecord has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.iterations&.maximum(:iid) } has_internal_id :iid, scope: :group, init: ->(s) { s&.group&.iterations&.maximum(:iid) } - state_machine :state, initial: :active do + validates :start_date, presence: true + validates :due_date, presence: true + + validate :dates_do_not_overlap, if: :start_or_due_dates_changed? + validate :future_date, if: :start_or_due_dates_changed?, unless: :skip_future_date_validation + + scope :upcoming, -> { with_state(:upcoming) } + scope :started, -> { with_state(:started) } + + state_machine :state_enum, initial: :upcoming do + event :start do + transition upcoming: :started + end + event :close do - transition active: :closed + transition [:upcoming, :started] => :closed end - event :activate do - transition closed: :active + state :upcoming, value: Iteration::STATE_ENUM_MAP[:upcoming] + state :started, value: Iteration::STATE_ENUM_MAP[:started] + state :closed, value: Iteration::STATE_ENUM_MAP[:closed] + end + + # Alias to state machine .with_state_enum method + # This needs to be defined after the state machine block to avoid errors + class << self + alias_method :with_state, :with_state_enum + alias_method :with_states, :with_state_enums + + def filter_by_state(iterations, state) + case state + when 'closed' then iterations.closed + when 'started' then iterations.started + when 'opened' then iterations.started.or(iterations.upcoming) + when 'all' then iterations + else iterations.upcoming + end end + end + + def state + STATE_ENUM_MAP.key(state_enum) + end - state :active, value: Iteration::STATE_ID_MAP[:active] - state :closed, value: Iteration::STATE_ID_MAP[:closed] + def state=(value) + self.state_enum = STATE_ENUM_MAP[value] + end + + private + + def start_or_due_dates_changed? + start_date_changed? || due_date_changed? + end + + # ensure dates do not overlap with other Iterations in the same group/project + def dates_do_not_overlap + return unless resource_parent.iterations.within_timeframe(start_date, due_date).exists? + + errors.add(:base, s_("Iteration|Dates cannot overlap with other existing Iterations")) + end + + # ensure dates are in the future + def future_date + if start_date_changed? + errors.add(:start_date, s_("Iteration|cannot be in the past")) if start_date < Date.today + errors.add(:start_date, s_("Iteration|cannot be more than 500 years in the future")) if start_date > 500.years.from_now + end + + if due_date_changed? + errors.add(:due_date, s_("Iteration|cannot be in the past")) if due_date < Date.today + errors.add(:due_date, s_("Iteration|cannot be more than 500 years in the future")) if due_date > 500.years.from_now + end end end |