Permalink
Browse files

Major rework of WorkDay materialization.

  • Loading branch information...
1 parent 8a4da1e commit 2fd3420f47e7aa3e80654521122b7847b21f24e9 @huerlisi huerlisi committed Jan 19, 2012
View
@@ -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
@@ -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(':')
@@ -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
@@ -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
View
@@ -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
@@ -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
@@ -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
@@ -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}
@@ -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.