Merge branch '4477-fixed-expenses'

This commit is contained in:
Eric Davis committed 2010-09-22 11:54:41 -07:00
commit 7c6c2797c4
24 files changed
+751 -64

No files matched your search

+20 -2
View File
@@ -36,8 +36,6 @@ class Contract < ActiveRecord::Base
named_scope :by_name, {:order => "#{Contract.table_name}.name ASC"}
[:status, :contract_type,
:fixed_spent, :fixed_budget,
:markup_spent, :markup_budget,
:discount_spent, :discount_budget
].each do |mthd|
define_method(mthd) { "TODO in later release" }
@@ -96,6 +94,26 @@ class Contract < ActiveRecord::Base
end
alias_method :profit_spent, :profit_left
# OPTIMIZE: N+1
def fixed_budget
deliverables.inject(0) {|total, deliverable| total += deliverable.fixed_budget_total }
end
# OPTIMIZE: N+1
def fixed_spent
deliverables.inject(0) {|total, deliverable| total += deliverable.fixed_budget_total_spent }
end
# OPTIMIZE: N+1
def fixed_markup_budget
deliverables.inject(0) {|total, deliverable| total += deliverable.fixed_markup_budget_total }
end
# OPTIMIZE: N+1
def fixed_markup_spent
deliverables.inject(0) {|total, deliverable| total += deliverable.fixed_markup_budget_total_spent }
end
def after_initialize
self.executed = false unless self.executed.present?
end
+20
View File
@@ -8,10 +8,12 @@ class Deliverable < ActiveRecord::Base
belongs_to :manager, :class_name => 'User', :foreign_key => 'manager_id'
has_many :labor_budgets
has_many :overhead_budgets
has_many :fixed_budgets
has_many :issues
accepts_nested_attributes_for :labor_budgets
accepts_nested_attributes_for :overhead_budgets
accepts_nested_attributes_for :fixed_budgets
# Validations
validates_presence_of :title
@@ -86,6 +88,24 @@ class Deliverable < ActiveRecord::Base
issues.inject(0) {|total, issue| total += issue.spent_hours }
end
def fixed_budget_total(date=nil)
fixed_budgets.sum(:budget)
end
def fixed_budget_total_spent(date=nil)
fixed_budgets.paid.sum(:budget)
end
# OPTIMIZE: N+1
def fixed_markup_budget_total(date=nil)
fixed_budgets.inject(0) {|total, fixed_budget| total += fixed_budget.markup_value }
end
# OPTIMIZE: N+1
def fixed_markup_budget_total_spent(date=nil)
fixed_budgets.paid.inject(0) {|total, fixed_budget| total += fixed_budget.markup_value }
end
def filter_by_date(date=nil, &block)
block.call
end
+65
View File
@@ -0,0 +1,65 @@
class FixedBudget < ActiveRecord::Base
unloadable
# Associations
belongs_to :deliverable
# Validations
# Accessors
def budget=(v)
if v.is_a? String
write_attribute(:budget, v.gsub(/[$ ,]/, ''))
else
write_attribute(:budget, v)
end
end
named_scope :by_period, lambda {|date|
if date
{
:conditions => {:year => date.year, :month => date.month}
}
end
}
named_scope :paid, {:conditions => {:paid => true}}
def markup_value
return 0 if budget.blank? || markup.blank?
case
when percent_markup?
percent = markup.gsub('%','').to_f
return budget.to_f * (percent / 100)
when straight_markup?
markup.gsub('$','').gsub(',','').to_f
else
0 # Invalid markup
end
end
def budget_spent
if paid?
budget
else
0
end
end
def percent_markup?
markup && markup.match(/%/)
end
def straight_markup?
markup && markup.match(/\$/)
end
# Is this a blank budget item. Retainers will create blank ones when
# they are copied. (RetainerDeliverable#create_budgets_for_periods)
def blank_record?
return true if new_record?
return title.blank? && budget.blank? && markup.blank?
end
end
+3 -3
View File
@@ -23,14 +23,14 @@ class FixedDeliverable < Deliverable
# The amount of profit that is budgeted for this deliverable.
# Profit = Total - ( Labor + Overhead + Fixed + Markup )
def profit_budget(date=nil)
budgets = labor_budget_total(date) + overhead_budget_total(date)
budgets = labor_budget_total(date) + overhead_budget_total(date) + fixed_budget_total(date) + fixed_markup_budget_total(date)
(total(date) || 0.0) - budgets
end
# The amount of money remaining after expenses have been taken out
# Profit left = Total - Labor spent - Overhead spent
# Profit left = Total - Labor spent - Overhead spent - Fixed - Markup
def profit_left(date=nil)
total_spent(date) - labor_budget_spent(date) - overhead_spent(date)
total_spent(date) - labor_budget_spent(date) - overhead_spent(date) - fixed_budget_total_spent(date) - fixed_markup_budget_total_spent(date)
end
# Hardcoded value used as a wrapper for the old Budget plugin API.
+6 -4
View File
@@ -14,12 +14,14 @@ class HourlyDeliverable < Deliverable
'H'
end
# Total = ( Labor Hours * Billing Rate ) + ( Fixed + Markup )
def total(date=nil)
return 0 if contract.nil?
return 0 if contract.billable_rate.blank?
return 0 if labor_budgets.count == 0 && overhead_budgets.count == 0
return contract.billable_rate * labor_budget_hours(date)
fixed_budget_amount = fixed_budget_total(date) + fixed_markup_budget_total(date)
return (contract.billable_rate * labor_budget_hours(date)) + fixed_budget_amount
end
# Total amount to be billed on the deliverable, using the total time logged
@@ -50,13 +52,13 @@ class HourlyDeliverable < Deliverable
# The amount of profit that is budgeted for this deliverable
# Profit = Total - ( Labor + Overhead + Fixed + Markup )
def profit_budget(date=nil)
budgets = labor_budget_total(date) + overhead_budget_total(date)
budgets = labor_budget_total(date) + overhead_budget_total(date) + fixed_budget_total(date) + fixed_markup_budget_total(date)
(total(date) || 0.0) - budgets
end
# The amount of money remaining after expenses have been taken out
# Profit left = Total - Labor spent - Overhead spent
# Profit left = Total - Labor spent - Overhead spent - Fixed - Markup
def profit_left(date=nil)
total_spent(date) - labor_budget_spent(date) - overhead_spent(date)
total_spent(date) - labor_budget_spent(date) - overhead_spent(date) - fixed_budget_total_spent(date) - fixed_markup_budget_total_spent(date)
end
end
+86 -4
View File
@@ -94,6 +94,12 @@ class RetainerDeliverable < HourlyDeliverable
budgets
end
def fixed_budgets_for_date(date)
budgets = fixed_budgets.all(:conditions => {:year => date.year, :month => date.month})
budgets = [fixed_budgets.build(:year => date.year, :month => date.month)] if budgets.empty?
budgets
end
def labor_budget_total(date=nil)
case scope_date_status(date)
when :in
@@ -127,6 +133,55 @@ class RetainerDeliverable < HourlyDeliverable
end
end
def fixed_budget_total(date=nil)
case scope_date_status(date)
when :in
fixed_budgets.sum(:budget, :conditions => {:year => date.year, :month => date.month})
when :out
0
else
super
end
end
def fixed_budget_total_spent(date=nil)
case scope_date_status(date)
when :in
fixed_budgets.paid.sum(:budget, :conditions => {:year => date.year, :month => date.month})
when :out
0
else
super
end
end
def fixed_markup_budget_total(date=nil)
case scope_date_status(date)
when :in
fixed_budgets.
all(:conditions => {:year => date.year, :month => date.month}).
inject(0) {|total, fixed_budget| total += fixed_budget.markup_value }
when :out
0
else
super
end
end
def fixed_markup_budget_total_spent(date=nil)
case scope_date_status(date)
when :in
fixed_budgets.
paid.
all(:conditions => {:year => date.year, :month => date.month}).
inject(0) {|total, fixed_budget| total += fixed_budget.markup_value }
when :out
0
else
super
end
end
def total_spent(date=nil)
case scope_date_status(date)
when :in
@@ -231,10 +286,16 @@ class RetainerDeliverable < HourlyDeliverable
undated_overhead_budgets.each do |template_budget|
overhead_budgets.create(template_budget.attributes.merge(:year => month.year, :month => month.month))
end
undated_fixed_budgets = fixed_budgets.all(:conditions => ["#{FixedBudget.table_name}.year IS NULL AND #{FixedBudget.table_name}.month IS NULL"])
undated_fixed_budgets.each do |template_budget|
fixed_budgets.create(template_budget.attributes.merge(:year => month.year, :month => month.month))
end
end
# Destroy origional un-dated budgets
labor_budgets.all(:conditions => ["#{LaborBudget.table_name}.year IS NULL AND #{LaborBudget.table_name}.month IS NULL"]).collect(&:destroy)
overhead_budgets.all(:conditions => ["#{OverheadBudget.table_name}.year IS NULL AND #{OverheadBudget.table_name}.month IS NULL"]).collect(&:destroy)
fixed_budgets.all(:conditions => ["#{FixedBudget.table_name}.year IS NULL AND #{FixedBudget.table_name}.month IS NULL"]).collect(&:destroy)
end
def check_for_extended_period
@@ -281,6 +342,17 @@ class RetainerDeliverable < HourlyDeliverable
end
end
fixed_budgets.all.each do |fixed_budget|
# Purge un-dated budgets, should not be saved at all
fixed_budget.destroy unless fixed_budget.year.present?
fixed_budget.destroy unless fixed_budget.month.present?
# Purge budgets outside the new beginning/ending range
unless (beginning_date..ending_date).to_a.include?(Date.new(fixed_budget.year, fixed_budget.month, 1))
fixed_budget.destroy
end
end
true
end
@@ -290,9 +362,10 @@ class RetainerDeliverable < HourlyDeliverable
old_end_date = end_date_change[0]
last_labor_budgets = labor_budgets.all(:conditions => {:year => old_end_date.year, :month => old_end_date.month})
last_overhead_budgets = overhead_budgets.all(:conditions => {:year => old_end_date.year, :month => old_end_date.month})
last_fixed_budgets = fixed_budgets.all(:conditions => {:year => old_end_date.year, :month => old_end_date.month})
months_after_date(old_end_date.end_of_month.to_date).each do |new_period|
create_budgets_for_new_period(new_period, last_labor_budgets, last_overhead_budgets)
create_budgets_for_new_period(new_period, last_labor_budgets, last_overhead_budgets, last_fixed_budgets)
end
end
@@ -302,14 +375,15 @@ class RetainerDeliverable < HourlyDeliverable
old_start_date = start_date_change[0]
first_labor_budgets = labor_budgets.all(:conditions => {:year => old_start_date.year, :month => old_start_date.month})
first_overhead_budgets = overhead_budgets.all(:conditions => {:year => old_start_date.year, :month => old_start_date.month})
first_fixed_budgets = fixed_budgets.all(:conditions => {:year => old_start_date.year, :month => old_start_date.month})
months_before_date(old_start_date.beginning_of_month.to_date).each do |new_period|
create_budgets_for_new_period(new_period, first_labor_budgets, first_overhead_budgets)
create_budgets_for_new_period(new_period, first_labor_budgets, first_overhead_budgets, first_fixed_budgets)
end
end
def create_budgets_for_new_period(new_period, labor_budgets_to_copy, overhead_budgets_to_copy)
def create_budgets_for_new_period(new_period, labor_budgets_to_copy, overhead_budgets_to_copy, fixed_budgets_to_copy)
labor_budgets_to_copy.each do |labor_budget_to_copy|
create_new_labor_budget_based_on_existing_budget(labor_budget_to_copy, 'year' => new_period.year, 'month' => new_period.month)
end
@@ -317,6 +391,10 @@ class RetainerDeliverable < HourlyDeliverable
overhead_budgets_to_copy.each do |overhead_budget_to_copy|
create_new_overhead_budget_based_on_existing_budget(overhead_budget_to_copy, 'year' => new_period.year, 'month' => new_period.month)
end
fixed_budgets_to_copy.each do |fixed_budget_to_copy|
create_new_fixed_budget_based_on_existing_budget(fixed_budget_to_copy, 'year' => new_period.year, 'month' => new_period.month)
end
end
def create_new_labor_budget_based_on_existing_budget(existing_labor_budget, attributes={})
@@ -327,6 +405,10 @@ class RetainerDeliverable < HourlyDeliverable
overhead_budgets.create(existing_overhead_budget.attributes.except('id').merge(attributes))
end
def create_new_fixed_budget_based_on_existing_budget(existing_fixed_budget, attributes={})
fixed_budgets.create(existing_fixed_budget.attributes.except('id').merge(attributes))
end
def scope_date_status(date)
if date
if within_date_range?(date)