Skip to content

Commit

Permalink
Major rework of WorkDay materialization.
Browse files Browse the repository at this point in the history
  • Loading branch information
huerlisi committed Jan 19, 2012
1 parent 8a4da1e commit 2fd3420
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 56 deletions.
15 changes: 6 additions & 9 deletions app/models/activity.rb
Expand Up @@ -2,7 +2,6 @@ class Activity < ActiveRecord::Base
# Associations
belongs_to :project
belongs_to :person
belongs_to :work_day
validates :project, :presence => true, :allow_blank => false
validates :person, :presence => true, :allow_blank => false

Expand All @@ -14,8 +13,6 @@ class Activity < ActiveRecord::Base

validates_date :date, :allow_nil => false, :allow_blank => false

before_save :create_work_day

def duration=(value)
if value.match(/:/)
hours, minutes = value.split(':')
Expand All @@ -29,12 +26,12 @@ def to_s
"%s: %0.2fh" % [project.name, duration]
end

private

def create_work_day
work_day = WorkDay.where(:person_id => person_id, :date => date).first
work_day ||= WorkDay.create(:person_id => person_id, :date => date)
# Work day
belongs_to :work_day, :autosave => true
before_save :update_work_day

self.work_day = work_day
private
def update_work_day
WorkDay.create_or_update_upto(self.person, date)
end
end
1 change: 1 addition & 0 deletions app/models/bookyt_projects/person.rb
Expand Up @@ -4,6 +4,7 @@ module Person

included do
has_many :activities, :foreign_key => :person_id
has_many :work_days, :foreign_key => :person_id
end

module InstanceMethods
Expand Down
96 changes: 52 additions & 44 deletions app/models/work_day.rb
@@ -1,27 +1,42 @@
class WorkDay < ActiveRecord::Base

# Associations
belongs_to :person
validates :person, :presence => true

has_many :activities

before_create :current_daily_workload
# Order
default_scope order(:date)

def current_daily_workload
unless person && person.employments.current && person.employments.current.daily_workload
self.daily_workload = 0.0
else
self.daily_workload = person.employments.current(self.date).daily_workload
end
# Calculations
default_scope select('work_days.*, hours_worked - hours_due AS overtime')

def self.create_or_update(person, date)
work_day = person.work_days.where(:date => date).first
work_day ||= person.work_days.build(:date => date)

work_day.update_hours

work_day.save
end

def update_hours
self.hours_due = calculate_hours_due
self.hours_worked = calculate_hours_worked
end

def self.create_for_current_employment(employee)
if employee.employments.current
workdays = WorkDay.where(:person_id => employee.id)
start_date = workdays.last.date unless workdays.empty?
start_date ||= employee.employments.current.duration_from
def self.create_or_update_upto(person, end_date)
# Guard
if latest_work_day = person.work_days.last
start_date = latest_work_day.date
else
start_date = end_date
end

(start_date..Date.today).each do |date|
WorkDay.create(:person => employee, :date => date)
end if workdays.last && (workdays.last.date < start_date)
(start_date..end_date).each do |date|
transaction do
self.create_or_update(person, date)
end
end
end

Expand All @@ -33,35 +48,34 @@ def self.create_for_current_employment(employee)
# params:
# :employee: Employee to build WorkDay instances for
# :range: Date range giving first and last day
def self.for_range(employee, range)
def self.build_or_update(employee, date)
self.create_for_current_employment(employee)

range.inject([]) do |out, day|
range.collect do |day|
work_day = WorkDay.where(:person_id => employee.id, :date => day).first
work_day ||= WorkDay.create(:person => employee, :date => day)
out << work_day

out
end
end

# Get WorkDay instances for a month
# Get employment
#
# params:
# :employee: Employee to build WorkDay instances for
# :date_in_month: Any day in the requested month. Uses today by default.
def self.for_month(employee, date_in_month = nil)
# Assume today if no date given
date_in_month ||= Date.today

start_date = date_in_month.beginning_of_month
end_date = date_in_month.end_of_month
# Lookup the employment for this day.
def employment
person.employments.current(self.date)
end

self.for_range(employee, start_date..end_date)
# Get daily workload
#
# Lookup daily workload for person. Returns 0.0 if no
# employment is specified.
def daily_workload
employment.try(:daily_workload) || 0.0
end

# Working hours for this day
def hours_due
#
# Saturday and sunday are off, uses daily workload for
# all other days.
def calculate_hours_due
case date.wday
when 6, 0
# Saturday and sunday are off
Expand All @@ -76,18 +90,12 @@ def hours_due
#
# Calculates hours worked by summing up duration of all logged
# activities.
def hours_worked
activities.where(:date => date).to_a.sum(&:duration)
end

# Overtime
#
# Simply substract hours_due from hours_worked.
def overtime
hours_worked - hours_due
def calculate_hours_worked
activities.where(:date => date).sum('duration')
end

# Calculate accumulated overtime
def overall_overtime
WorkDay.where(:person_id => person.id).where('date <= ?', date).to_a.sum(&:overtime)
WorkDay.where('date <= ?', date).sum('hours_worked - hours_due')
end
end
6 changes: 3 additions & 3 deletions app/views/timesheets/_show.html.haml
Expand Up @@ -5,9 +5,9 @@
%th.number= t_attr :overtime, Activity
%th.number= t_attr :overall_overtime, Activity

- overall_overtime = 0
- WorkDay.for_month(@employee).each do |day|
- overall_overtime += day.overtime
- WorkDay.create_or_update_upto(@employee, Date.today.end_of_month)
- range = Date.today.beginning_of_month..Date.today.end_of_month
- @employee.work_days.where(:date => range).each do |day|
- tr_params = {:class => work_day_classes(day)}
- tr_params.merge!(:rel => 'popover', 'data-content' => h(render 'timesheets/activities_popover', :activities => day.activities), 'data-original-title' => 'Aktivitäten', 'data-html' => 'true') unless day.activities.empty?
%tr{tr_params}
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20120119102801_add_hours_due_to_activities.rb
@@ -0,0 +1,5 @@
class AddHoursDueToActivities < ActiveRecord::Migration
def change
add_column :activities, :hours_due, :decimal, :precision => 10, :scale => 2
end
end
@@ -0,0 +1,8 @@
class ChangeMaterializedColumnsInWorkDays < ActiveRecord::Migration
def up
add_column :work_days, :hours_due, :decimal, :precision => 10, :scale => 2
add_column :work_days, :hours_worked, :decimal, :precision => 10, :scale => 2

remove_column :work_days, :daily_workload
end
end

0 comments on commit 2fd3420

Please sign in to comment.