From 8150cfcabc8b85c530ca8eda44dcac88064e6426 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Wed, 11 Jun 2008 09:19:51 -0700 Subject: [PATCH] Added rdoc comments --- app/controllers/deliverables_controller.rb | 9 ++++++- app/models/budget.rb | 15 ++++++++++-- app/models/deliverable.rb | 28 ++++++++++++++++++---- app/models/fixed_deliverable.rb | 9 ++++--- app/models/hourly_deliverable.rb | 7 +++++- app/models/member_spent.rb | 4 ++++ 6 files changed, 61 insertions(+), 11 deletions(-) diff --git a/app/controllers/deliverables_controller.rb b/app/controllers/deliverables_controller.rb index 0f95f97..f57f35c 100644 --- a/app/controllers/deliverables_controller.rb +++ b/app/controllers/deliverables_controller.rb @@ -5,7 +5,8 @@ class DeliverablesController < ApplicationController helper :sort include SortHelper - + + # Main deliverable list def index sort_init "#{Deliverable.table_name}.id", "desc" sort_update @@ -31,11 +32,13 @@ class DeliverablesController < ApplicationController end end + # Action to preview the Deliverable description def preview @text = params[:deliverable][:description] render :partial => 'common/preview' end + # Saves a new Deliverable def create if params[:deliverable][:type] == FixedDeliverable.name @deliverable = FixedDeliverable.new(params[:deliverable]) @@ -59,10 +62,12 @@ class DeliverablesController < ApplicationController end + # Builds the edit form for the Deliverable def edit @deliverable = Deliverable.find_by_id_and_project_id(params[:deliverable_id], params[:id]) end + # Updates an existing Deliverable, optionally changing it's type def update @deliverable = Deliverable.find(params[:deliverable_id]) @@ -82,6 +87,7 @@ class DeliverablesController < ApplicationController end + # Removes the Deliverable def destroy @deliverable = Deliverable.find_by_id_and_project_id(params[:deliverable_id], @project.id) @@ -102,6 +108,7 @@ class DeliverablesController < ApplicationController redirect_to :controller => 'issues', :action => 'index', :project_id => @project.id end + # Assigns issues to the Deliverable based on their Version def bulk_assign_issues @deliverable = Deliverable.find_by_id_and_project_id(params[:deliverable_id], @project.id) diff --git a/app/models/budget.rb b/app/models/budget.rb index fea630d..77dceda 100644 --- a/app/models/budget.rb +++ b/app/models/budget.rb @@ -10,7 +10,8 @@ class Budget 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 @@ -20,6 +21,7 @@ class Budget 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 @@ -29,14 +31,17 @@ class Budget 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).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 @@ -46,14 +51,17 @@ class Budget end end + # Total amount spent for all the deliverables def spent self.deliverables.collect(&:spent).inject { |sum, n| sum + n } || 0.0 end + # Amount of budget left on the deliverables def left return self.budget - self.spent end + # Amount spent over the budget def overruns if self.left >= 0 return 0 @@ -62,6 +70,7 @@ class Budget 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 @@ -75,10 +84,12 @@ class Budget 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 diff --git a/app/models/deliverable.rb b/app/models/deliverable.rb index d015d16..c5e55ca 100644 --- a/app/models/deliverable.rb +++ b/app/models/deliverable.rb @@ -1,3 +1,5 @@ +# A Deliverable is an item that is created as part of the project. These items +# contain a collection of issues. class Deliverable < ActiveRecord::Base unloadable validates_presence_of :subject @@ -5,6 +7,7 @@ class Deliverable < ActiveRecord::Base belongs_to :project has_many :issues + # Assign all the issues with +version_id+ to this Deliverable def assign_issues_by_version(version_id) version = Version.find_by_id(version_id) return 0 if version.nil? || version.fixed_issues.blank? @@ -16,6 +19,10 @@ class Deliverable < ActiveRecord::Base return version.fixed_issues.size end + # Change the Deliverable type to another type. Valid types are + # + # * FixedDeliverable + # * HourlyDeliverable def change_type(to) if [FixedDeliverable.name, HourlyDeliverable.name].include?(to) self.type = to @@ -26,16 +33,21 @@ class Deliverable < ActiveRecord::Base end end + # Adjusted score to show the status of the Deliverable. 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 + # Amount spent. Virtual accessor that is overriden by subclasses. def spent 0 end - # TODO LATER: Shouldn't require the default_done_ratio patch + # Percentage of the deliverable that is compelte based on the progress of the + # assigned issues. Currently requires the +default_done_ratio+ patch. def progress + # TODO LATER: Shouldn't require the default_done_ratio patch return 100 unless self.issues.size > 0 total ||= self.issues.collect(&:estimated_hours).delete_if {|e| e.nil? }.inject {|sum, n| sum + n} || 0 @@ -50,14 +62,13 @@ class Deliverable < ActiveRecord::Base return (balance / total).round end + # Amount of the budget spent. Expressed as as a percentage whole number def budget_ratio return 0.0 if self.budget.nil? || self.budget == 0.0 return ((self.spent / self.budget) * 100).round end - # - # These attributes can take a Dollar amount or a % - # + # Setter for the overhead to take an Dollar amount or a %. def overhead=(v) if v.match(/%/) # Clear amount since this is a % @@ -71,6 +82,7 @@ class Deliverable < ActiveRecord::Base end end + # Setter for the materials to take an Dollar amount or a %. def materials=(v) if v.match(/%/) # Clear amount since this is a % @@ -84,6 +96,7 @@ class Deliverable < ActiveRecord::Base end end + # Setter for the profit to take an Dollar amount or a %. def profit=(v) if v.match(/%/) # Clear amount since this is a % @@ -97,22 +110,27 @@ class Deliverable < ActiveRecord::Base end end + # Amount of the budget remaining to be spent def budget_remaining return self.budget - self.spent end + # Number of hours used. Virtual accessor that is overriden by subclasses. def hours_used 0 end + # Amount spent on members. Virtual accessor that is overriden by subclasses. def members_spent [] end + # Amount of the budget remaining def left return self.budget - self.spent end + # Amount spent over the total budget def overruns if self.left >= 0 return 0 @@ -121,10 +139,12 @@ class Deliverable < ActiveRecord::Base end end + # Budget of labor, without counting profit or overheads. Virtual accessor that is overriden by subclasses. def labor_budget 0 end + # Helper method to return an Hash of the trackers and number of issues assigned to each tracker. def issues_with_trackers trackers = self.project.trackers return { } if trackers.empty? diff --git a/app/models/fixed_deliverable.rb b/app/models/fixed_deliverable.rb index 5c1ff00..84dae4a 100644 --- a/app/models/fixed_deliverable.rb +++ b/app/models/fixed_deliverable.rb @@ -1,16 +1,18 @@ class FixedDeliverable < Deliverable unloadable - # Fixed rate bids should always have a budget score of 0. This is because the budget is managed by the contractor. + # FixedDeliverables should always have a budget score of 0. This is because the budget is managed by the contractor. def score 0 end - + + # Returns the amount spent. It will always be related to the progress of the + # FixedDeliverable because it is managed by the user def spent (self.progress.to_f / 100 ) * self.budget end - def profit + def profit # :nodoc: if read_attribute(:profit_percent).nil? return super else @@ -18,6 +20,7 @@ class FixedDeliverable < Deliverable end end + # Budget for the labor, excluding overhead, profit, and materials def labor_budget return read_attribute(:fixed_cost) end diff --git a/app/models/hourly_deliverable.rb b/app/models/hourly_deliverable.rb index a1147fd..9f91e72 100644 --- a/app/models/hourly_deliverable.rb +++ b/app/models/hourly_deliverable.rb @@ -1,6 +1,8 @@ class HourlyDeliverable < Deliverable unloadable + # Amount of money spent on the issues. Determined by the Member's rate and + # timelogs. def spent return 0 unless self.issues.size > 0 total = 0.0 @@ -17,17 +19,19 @@ class HourlyDeliverable < Deliverable return total end + # Number of hours used by Members on the Deliverable's issues def hours_used return 0 unless self.issues.size > 0 return self.issues.collect(&:time_entries).flatten.collect(&:hours).sum end + # Amount of time and money spent by the Members on the Deliverable's issues. def members_spent return MemberSpent.find_all_by_deliverable(self) end - def profit + def profit # :nodoc: if read_attribute(:profit_percent).nil? return super else @@ -35,6 +39,7 @@ class HourlyDeliverable < Deliverable end end + # Budget for the labor, excluding overhead, profit, and materials def labor_budget return read_attribute(:cost_per_hour).to_f * read_attribute(:total_hours).to_f end diff --git a/app/models/member_spent.rb b/app/models/member_spent.rb index c68e928..a3549ce 100644 --- a/app/models/member_spent.rb +++ b/app/models/member_spent.rb @@ -1,14 +1,18 @@ +# Plain Ruby class to help build a data structure that lists +# each member and the amount of time and money they spent. class MemberSpent attr_accessor :user attr_accessor :hours attr_accessor :spent + # New data structure to hold the Member's data def initialize(options = { }) self.user = options[:user] || nil self.hours = options[:hours] || 0.0 self.spent = options[:spent] || 0.0 end + # Get all the Members, their hours used, and their money spent def self.find_all_by_deliverable(deliverable) membership = [] return membership unless deliverable.issues.size > 0