diff --git a/app/models/track.rb b/app/models/track.rb index c624cab8..f2b1ac95 100644 --- a/app/models/track.rb +++ b/app/models/track.rb @@ -31,9 +31,11 @@ class Track < ActiveRecord::Base validates :description, presence: true, if: :self_organized? validate :valid_dates validate :valid_room, if: :self_organized_and_accepted_or_confirmed? + validate :overlapping before_validation :capitalize_color + scope :accepted, -> { where(state: 'accepted') } scope :confirmed, -> { where(state: 'confirmed') } scope :cfp_active, -> { where(cfp_active: true) } @@ -209,4 +211,20 @@ class Track < ActiveRecord::Base errors.add(:room, "must be a room of #{program.conference.venue.name}") end end + + ## + # Check that there is no other track in the same room with overlapping dates + def overlapping + return unless start_date && end_date && room && program.try(:tracks) + (program.tracks.accepted + program.tracks.confirmed - [self]).each do |other_track| + if other_track.room == room && + other_track.start_date && other_track.end_date && + (other_track.start_date <= start_date && other_track.end_date >= start_date || + other_track.start_date <= end_date && other_track.end_date >= end_date || + start_date <= other_track.start_date && other_track.end_date <= end_date) + errors.add(:track, 'has overlapping dates with a confirmed or accepted track in the same room') + break + end + end + end end diff --git a/spec/models/track_spec.rb b/spec/models/track_spec.rb index 1ad54839..f9c53653 100644 --- a/spec/models/track_spec.rb +++ b/spec/models/track_spec.rb @@ -136,9 +136,97 @@ describe Track do end end end + + describe '#overlapping' do + before :each do + @conference = create(:conference, start_date: Date.current - 1.day, end_date: Date.current + 2.days) + @conference.venue = create(:venue) + @room = create(:room, venue: @conference.venue) + end + + context 'is valid' do + it 'when the tracks are in different rooms' do + other_room = create(:room, venue: @conference.venue) + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: other_room, start_date: Date.current, end_date: Date.current) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) + expect(track.valid?).to eq true + end + + it 'when it ends before the other tracks' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current - 1.day, end_date: Date.current - 1.day) + expect(track.valid?).to eq true + end + + it 'when it starts after the other tracks' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current + 1.day, end_date: Date.current + 1.day) + expect(track.valid?).to eq true + end + end + + context 'is invalid' do + it 'when it starts or ends with another track in the same room' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) + expect(track.valid?).to eq false + expect(track.errors[:track]).to eq ['has overlapping dates with a confirmed or accepted track in the same room'] + end + + it 'when it starts before another track and ends after the other starts and before it ends' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current + 2.days) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current - 1.day, end_date: Date.current + 1.day) + expect(track.valid?).to eq false + expect(track.errors[:track]).to eq ['has overlapping dates with a confirmed or accepted track in the same room'] + end + + it 'when it starts after another track and before it ends and ends after the other' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current + 2.days) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current + 1.day, end_date: Date.current + 3.days) + expect(track.valid?).to eq false + expect(track.errors[:track]).to eq ['has overlapping dates with a confirmed or accepted track in the same room'] + end + + it 'when it starts after another track and ends before the other' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current + 2.days) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current + 1.day, end_date: Date.current + 1.day) + expect(track.valid?).to eq false + expect(track.errors[:track]).to eq ['has overlapping dates with a confirmed or accepted track in the same room'] + end + + it 'when it starts before another track and ends after the other' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current - 1.day, end_date: Date.current + 1.day) + expect(track.valid?).to eq false + expect(track.errors[:track]).to eq ['has overlapping dates with a confirmed or accepted track in the same room'] + end + end + end end describe 'scope' do + describe '#accepted' do + before :each do + @program = create(:program) + end + + context 'includes' do + it 'when track is accepted' do + accepted_track = create(:track, state: 'accepted', program: @program) + expect(@program.tracks.accepted.include?(accepted_track)).to eq true + end + end + + context 'excludes' do + %w[new to_accept confirmed to_reject rejected canceled withdrawn].each do |state| + it "when track is #{state.humanize}" do + not_accepted_track = create(:track, state: state, program: @program) + expect(@program.tracks.accepted.include?(not_accepted_track)).to eq false + end + end + end + end + describe '#confirmed' do before :each do @program = create(:program)