Files
redmine-budget-plugin/app/models/budget.rb
T

132 lines
3.8 KiB
Ruby

# Budget is a meta class that is used to calculate summary data
# for all the deliverables on a project. Think of it akin to:
# has_many :deliverables
# belongs_to :project
#
class Budget
attr_reader :project
def initialize(project_id)
@project = Project.find(project_id)
end
# Gets the next due date from the deliverables
def next_due_date
del = self.deliverables
return nil unless del.size > 0
dates = del.collect(&:due).delete_if { |d| d.blank?}
return dates.sort[0]
end
# Gets the last due date of the deliverables
def final_due_date
del = self.deliverables
return nil unless del.size > 0
dates = del.collect(&:due).delete_if { |d| d.blank?}
return dates.sort[-1]
end
# Deliverables assigned to this +Budget+
def deliverables
return Deliverable.find_all_by_project_id(@project.id)
end
# Total budget all of the deliverables
def budget
return self.deliverables.collect(&:budget).delete_if { |d| d.blank?}.inject { |sum, n| sum + n} || 0.0
end
# Total labor budget all of the deliverables
def labor_budget
return self.deliverables.collect(&:labor_budget).delete_if { |d| d.blank?}.inject { |sum, n| sum + n} || 0.0
end
# Amount of the budget spent. Expressed as as a percentage whole number
def budget_ratio
budget = self.budget # cache result
if budget > 0.0
return ((self.spent / budget) * 100).round
else
self.progress
end
end
# Total amount spent for all the deliverables
def spent
self.deliverables.collect(&:spent).delete_if { |d| d.blank?}.inject { |sum, n| sum + n } || 0.0
end
# Amount of budget left on the deliverables
def left
return self.budget - self.spent
end
# Amount of labor budget left on the deliverables
def labor_budget_left
return self.labor_budget - self.spent
end
# Amount spent over the budget
def overruns
if self.left >= 0
return 0
else
return self.left * -1
end
end
# Completation progress, expressed as a percentage whole number
def progress
return 100 unless self.deliverables.size > 0
return 100 if self.budget == 0.0
balance = 0.0
self.deliverables.each do |deliverable|
balance += deliverable.budget * deliverable.progress
end
return (balance / self.budget).round
end
# Budget score. Will range from 100 (everything done with no money spent) to -100 (nothing done, all the money spent)
def score
return self.progress - self.budget_ratio
end
# Total profit of the deliverables. This is *not* calculated based on the amount spent and total budget but is the total of the profit amount for the deliverables.
def profit
return 0.0 unless self.deliverables.size > 0
# Covers fixed and percentage profit though the +profit+ method being overloaded on the Deliverable types
return self.deliverables.collect(&:profit).delete_if { |d| d.blank?}.inject { |sum, n| sum + n } || 0.0
end
# Dollar amount of time that has been logged to the project itself
def amount_missing_on_issues
time_logs = TimeEntry.find_all_by_project_id_and_issue_id(self.project.id, nil)
return time_logs.collect(&:cost).sum
end
# Dollar amount of time that has been logged to issues that are not assigned to deliverables
def amount_missing_on_deliverables
# Bisect the issues because NOT IN isn't reliable
all_issues = self.project.issues.find(:all)
return 0 if all_issues.empty?
deliverable_issues = self.project.issues.find(:all, :conditions => ["deliverable_id IN (?)", self.deliverables.collect(&:id)])
missing_issues = all_issues - deliverable_issues
time_logs = missing_issues.collect(&:time_entries).flatten
return time_logs.collect(&:cost).sum
end
end