module Ecom module Core class CrewTime < ApplicationRecord # Time Ranges MORNING = :morning AFTERNOON = :afternoon FULL_DAY = :full_day # TIME_RANGE = { # MORNING => { start: Time.zone.parse('5:00 AM'), end: Time.zone.parse('9:00 AM') }, # AFTERNOON => { start: Time.zone.parse('10:00 AM'), end: Time.zone.parse('2:00 PM') }, # BOTH => { start: Time.zone.parse('5:00 AM'), end: Time.zone.parse('2:00 PM') } # }.freeze belongs_to :attendance_sheet_entry belongs_to :revision_to, class_name: 'Ecom::Core::CrewTime', optional: true belongs_to :created_by, class_name: 'Ecom::Core::User' has_one :revision, class_name: 'Ecom::Core::CrewTime', foreign_key: :revision_to_id validates :checkin_time, presence: true, if: :checkout_time scope :by_attendance, lambda { |id| joins(:attendance_sheet_entry).where(ecom_core_attendance_sheet_entries: { attendance_sheet_id: id }) } scope :revised, ->(revised) { where(revised: revised) } before_save :calculate_hours after_save :calculate_total def calculate_hours self.hours = if checkout_time.nil? || checkin_time.nil? 0 else compute_hours end end def calculate_total compute_total_for_entry if saved_change_to_hours? end def compute_total_for_entry attendance_sheet_entry.total_hours = Ecom::Core::CrewTime.where( attendance_sheet_entry: attendance_sheet_entry, revised: false ).sum(:hours) attendance_sheet_entry.save end # A method to get the available time ranges at a given point. # We cannot define the range variables as a constant because # the parsing should be done at the exact moment we are about # to do time range calculations to avoid errors caused by date # mismatches def define_range morning = { start: Time.zone.parse('5:00 AM'), finish: Time.zone.parse('9:00 AM') } afternoon = { start: Time.zone.parse('10:00 AM'), finish: Time.zone.parse('2:00 PM') } full_day = { start: Time.zone.parse('5:00 AM'), finish: Time.zone.parse('2:00 PM') } { morning: morning, afternoon: afternoon, full_day: full_day } end # A method to check if checkin and checkout range falls in the morning, # afternoon, or both. def find_range(start, finish) range = define_range if start.before?(range[:morning][:finish]) && finish.before?(range[:afternoon][:start]) :morning elsif start.after?(range[:morning][:finish]) && finish.after?(range[:afternoon][:start]) :afternoon else :full_day end end # A method to adjust time ranges by trimming any time value outside # of the defined morning and afternoon ranges def compute_hours # Reparse time to avoid errors caused by date differences range = define_range start = Time.zone.parse(checkin_time.strftime('%I:%M%p')) finish = Time.zone.parse(checkout_time.strftime('%I:%M%p')) day_part = find_range(start, finish) left = start.before?(range[day_part][:start]) ? range[day_part][:start] : start right = finish.after?(range[day_part][:finish]) ? range[day_part][:finish] : finish time = (right - left) / 1.hour time -= 1 if day_part == FULL_DAY time end end end end