# == Schema Information # # Table name: shifts # # id :integer not null, primary key # day_of_week :integer # start_time :time # end_time :time # hours :time # schedule_id :integer # created_at :datetime # updated_at :datetime # due_date :date # absence :boolean default(FALSE) # class Shift < ActiveRecord::Base belongs_to :schedule belongs_to :job belongs_to :shift_offs has_one :swap, :dependent => :destroy has_one :cancellation, :dependent => :destroy has_one :lateness, :dependent => :destroy has_one :unavailable, :dependent => :destroy #validate :should_real_time validate :should_have_no_time_off #validate :detect_overlap #validates_presence_of :schedule validates_presence_of :job # validates_inclusion_of :day_of_week, :in => 0..6 # validates_uniqueness_of :day_of_week, :scope => :schedule #validates_uniqueness_of :due_date, :scope => :schedule validates_time :start_time validates_time :end_time, :after => :start_time validates_date :due_date after_create :calculate_schedule_hours after_save :calculate_schedule_hours after_destroy { |record| check_schedule_for_deletion(record.schedule_id) } def job_name self.job.name end def user_name self.schedule.user.name end def get_week_hours(user) if user and user != 0 user = User.find(user) user.shifts.where("due_date >= ? AND due_date <= ?",self.due_date.beginning_of_week,self.due_date.end_of_week ).sum(:hours).to_i else self.schedule.shifts.where("due_date >= ? AND due_date <= ?",self.due_date.beginning_of_week,self.due_date.end_of_week ).sum(:hours).to_i end end def if_swap_approved(user) if user and user != 0 swapped = Candidate.find_by_alert_type_id(self.id) return (get_week_hours(user) + swapped.swap.shift.hours.strftime('%H').to_i) else swapped = Candidate.find_by_alert_type_id(self.id) return (get_week_hours(0) + swapped.swap.shift.hours.strftime('%H').to_i) end end def formatted_due_date "#{due_date.strftime('%m')}/#{due_date.strftime('%d')}/#{due_date.strftime('%Y')}" end def status state = 'normal' if swap && swap.pending? state = 'swap_pending' end if cancellation && cancellation.pending? state = 'cancellation_pending' end state end def shift_detail "AS #{self.job_name} from #{self.start_time.strftime('%H:%M')} to #{self.end_time.strftime('%H:%M')} on #{self.formatted_due_date}" end def published? self.schedule.published? end def is_timeoff? timeoff = Timeoff.where(:user_id => self.schedule.user_id, :off_date=>self.due_date,:status=>"approved") timeoff.size > 0 end def timeoff_reason timeoff = self.schedule.user.timeoffs.where(:off_date => self.due_date,:status=>"approved").first "The Reason : #{timeoff.reason}, All day : #{timeoff.all_day}, Vacation : #{timeoff.vacation} " if timeoff end def is_cancelled? cancellation = Cancellation.where('shift_id = ? AND (status = ? OR status = ?)',self.id,"approved excused","approved unexcused") cancellation.size > 0 end def is_swapped? swap = Swap.where(:shift_id=>self.id,:status=>"approved") swap.size > 0 end def swap_shift dest_sch, org, date curr = self.schedule dates = Date.parse(dest_sch[:due_date]) if dest_sch[:schedule_id].present? new_schedule = Schedule.find_by_id(dest_sch[:schedule_id]) else new_schedule = Schedule.new(user_id: dest_sch[:employee_id], organization_id: org.id, total_hours: '0:0', start_date: curr.start_date, end_date: curr.end_date) unless new_schedule.save! return new_schedule end end self.update_attributes!(schedule_id: new_schedule.id, day_of_week: dest_sch[:day_of_week], due_date: dates) curr.destroy! if curr.shifts.count == 0 return self end def has_issue? if swp = swap and swp.status == "pending" return { "status"=> true, "type"=> "swap pending" } elsif unavail = unavailable and unavail.status == "pending" return { "status"=> true, "type"=> "unavailable pending" } elsif cancel = cancellation and cancel.status == "pending" return { "status"=> true, "type"=> "cancel pending" } elsif Timeoff.where('user_id = ? AND off_date = ? AND status = ?',self.schedule.user_id, self.due_date, "pending").count > 0 return { "status"=> true, "type"=> "timeoff pending" } elsif late = lateness return { "status"=> true, "type"=> "lateness" } end return { "status"=> false } end def can_edit? edit_possible = true if self.swap && swap.approved? edit_possible = true elsif self.cancellation && self.cancellation.approved? edit_possible = true end edit_possible end def avail? self.published? && self.can_edit? end #private #def detect_overlap # if self.schedule_id # schedule = Schedule.find(self.schedule_id) # if schedule # overlap = schedule.user.shifts.where(:due_date => self.due_date).first # if overlap && overlap.id != self.id # errors.add(:due_date, "can't create because already user has shift #{self.due_date} ") # end # end # end #end #private #def should_real_time # if self.start_time > self.end_time # errors.add(:start_time, "can't be in later than end time") # end #end private def should_have_no_time_off if self.schedule_id schedule = Schedule.find(self.schedule_id) if schedule timeoff = schedule.user.timeoffs.where(:off_date => self.due_date).first if timeoff && timeoff.approved? if timeoff.all_day_absence? errors.add(:due_date, "can't create because already user requested time off on #{self.due_date} all day.") elsif timeoff.interfere?(self.start_time, self.end_time) errors.add(:due_date, "can't create because already user requested time off on #{self.due_date} #{self.start_time} #{self.end_time}") end end end end end def check_schedule_for_deletion schedule_id schedule = Schedule.find_by_id(schedule_id) if schedule and schedule.shifts.count == 0 schedule.destroy else schedule.calculate_total_hours end end def self.create_with_json(schedule, data,changed) data.each do |shift| if shift["hours"] == "" shift["hours"] = "0:0" end if shift["training_hours"] == "" shift["training_hours"] = "0:0" end if shift["id"].nil? # create new one new_shift = schedule.shifts.new(:start_time => shift["start_time"], :end_time => shift["end_time"], :day_of_week => shift["day_of_week"], :hours => shift["hours"], :training_hours => shift["training_hours"], :due_date => shift["due_date"], :job_id => shift["job_id"], :shift_off_id => shift["shift_off_id"] ) unless new_shift.save return new_shift.errors end else # modify existing one ext_shift = Shift.find(shift["id"]) if ext_shift ext_shift.start_time = shift["start_time"] ext_shift.end_time = shift["end_time"] ext_shift.job_id = shift["job_id"] ext_shift.day_of_week = shift["day_of_week"] ext_shift.hours = shift["hours"] ext_shift.due_date = shift["due_date"] ext_shift.job_id = shift["job_id"] ext_shift.shift_off_id = shift["shift_off_id"] shift_changed = ext_shift.changed? result = ext_shift.save if shift_changed changed = true end unless result return ext_shift.errors end end end end # if changed and schedule.publish? # schedule.notify_employee # end true end def self.week_shifts schedule, user, org, date w_shifts = [] shifts = schedule.try(:shifts) daily_shifts = shifts.sort_by(&:start_time).group_by(&:day_of_week) if shifts.present? weekdaysrange(org.m_to_s).each do |i| if daily_shifts.present? && daily_shifts[i].present? w_shifts << self.format_shift(schedule, daily_shifts[i], i) else w_shifts << self.fill_shift(schedule, user, i, org, date) end end w_shifts end def self.day(day, m_to_s) days = m_to_s ? ["Mon","Tue","Wed","Thur","Fri","Sat", "Sun"] : ["Sun", "Mon","Tue","Wed","Thur","Fri","Sat"]; days[day] end def self.days(m_to_s, date) days = [] currdate = Date.parse(date).at_beginning_of_week currdate = m_to_s ? currdate - 1 : currdate - 2 ; (0..6).each do |day_of_week| currdate = currdate + 1 obj = {'month'=> currdate.month,'date'=> currdate.day,'year'=> currdate.year, 'day'=> day(day_of_week, m_to_s)} days.push(obj) end days end def self.get_day m_to_s, day_of_week, date dates = Date.parse(date).at_beginning_of_week + day_of_week dates = dates - 1 if m_to_s dates end def self.weekdaysrange(m_to_s) m_to_s ? (1..7) : (0..6) end def self.format_shift schedule, shifts, day d_shifts = [] shifts.each do |shift| dshift = {} job = shift.job dshift["id"] = shift.id dshift["job_id"] = job.id dshift["job_name"] = job.name dshift["start_time"] = shift.try(:start_time).try(:strftime, '%I:%M%P') || 0.0 dshift["end_time"] = shift.try(:end_time).try(:strftime,'%I:%M%P') || 0.0 dshift["hours"] = shift.try(:hours).try(:strftime,'%H:%M') || 0.0 dshift["training_hours"] = shift.try(:training_hours).try(:strftime,'%H:%M') || 0.0 dshift["shift_off_id"] = shift.shift_off_id dshift["due_date"] = shift.due_date dshift["is_timeoff"] = shift.is_timeoff? dshift["is_cancelled"] = shift.is_cancelled? dshift["cancel_reason"] = shift.cancellation.try(:reason) dshift["timeoff_reason"] = shift.timeoff_reason dshift["is_swapped"] = shift.is_swapped? dshift["color"] = job.color dshift["has_issue"] = shift.has_issue? dshift["day_of_week"] = day%7 dshift["schedule_id"] = schedule.try(:id) dshift["employee_id"] = schedule.user_id dshift["shift_off"] = ShiftOff.where(:id => shift.shift_off_id).first.name if shift.try(:shift_off_id) d_shifts << dshift end d_shifts end def self.fill_shift schedule, emp, day, org, date shift = {} shift["id"] = nil shift["hours"] = "" shift["start_time"] = "" shift["end_time"] = "" shift["shift_off_id"] = nil shift["day_of_week"] = day shift["employee_id"] = emp.id shift["due_date"] = get_day(org.m_to_s, day, date) shift["training_hours"] = "" shift["job_id"] = nil shift["schedule_id"] = schedule.try(:id) return [shift] end def self.daily schedules, date filtered_shifts = [] shifts = self.where(:schedule_id => schedules, :due_date => date) shifts.each do |shift| user = shift.schedule.user filtered_shifts << shift.attributes.merge(:user_id => user.id, :user_name => user.name) end filtered_shifts end def calculate_schedule_hours if self.schedule self.schedule.calculate_total_hours end end end