Author SHA1 Message Date
Eric Davis f8270498f3 [#4477] Add a simple title/tooltip for the FixedBudget description 2010-09-17 15:37:30 -07:00
Eric Davis 2c04ef4f45 [#4477] Add FixedBudget items into the total and profit Deliverable calculations 2010-09-17 15:22:53 -07:00
Eric Davis d78e7b1dc3 [#4477] Scope fixed budget items by period too (for Retainers) 2010-09-17 14:51:48 -07:00
Eric Davis 67dd75a980 [#4477] Skip blank FixedBudget items in the deliverable details 2010-09-17 14:23:29 -07:00
Eric Davis 55c9f93c6c [#4477] Change Retainers to scope #fixed_budget_total and #fixed_markup_budget_total by date 2010-09-17 14:15:08 -07:00
Eric Davis 081df7874d [#4477] Added the total FixedBudget markup to the deliverable details 2010-09-17 13:43:35 -07:00
Eric Davis ed7536897e [#4477] Show each fixed budget item in the deliverable details 2010-09-17 13:38:44 -07:00
Eric Davis 86be29cecc [#4477] Show the total fixed budget in the deliverable row 2010-09-17 13:27:35 -07:00
Eric Davis 294c88f11d [#4477] Hook up FixedBudgets to Contract#fixed_budget and #fixed_markup_budget 2010-09-17 13:16:59 -07:00
Eric Davis edc8db7fe2 [#4477] Hook FixedBudgets into Retainer's periods. 2010-09-09 17:27:32 -07:00
Eric Davis feee5e8b43 [#4477] Added FixedBudgets to the Deliverable finance form. 2010-09-09 17:06:35 -07:00
Eric Davis 07da5d98d8 [#4477] Added the FixedBudget markup calculations. 2010-09-09 16:12:48 -07:00
Eric Davis 7f7e5b3042 [#4477] Added model and migration for FixedBudget. 2010-09-09 15:57:58 -07:00
Eric Davis d315252565 Refactor: use #scope_date_status. 2010-09-09 15:44:36 -07:00
Eric Davis 6b38dfca91 Refactor: use utility method. 2010-09-09 15:43:38 -07:00
Eric Davis 75046cfdd1 Refactor: extract utility method. 2010-09-09 15:42:12 -07:00
Eric Davis 12fffee314 Refactor: extract scope_date_status method to find how to scope the periods. 2010-09-09 15:39:25 -07:00
Eric Davis 362610dc77 [#4420] Updated the flash messages for Deliverables. 2010-09-09 15:12:01 -07:00
Eric Davis 1b08487a0d [#4420] Add a message for new Retainers that Finances are for a single month. 2010-09-09 15:04:26 -07:00
Eric Davis 5ef7d0271d [#4420] Display per period Retainer values on the Contract details page. 2010-09-09 14:56:20 -07:00
Eric Davis 0a1bb9169d [#4420] Hook up Retainer's total_spent and profit_left for periods. 2010-09-09 14:22:25 -07:00
Eric Davis 91d8c1818e [#4420] Hook up Retainer's overhead_spent and labor_budget_spent for periods 2010-09-09 13:50:50 -07:00
26 changed files with 1020 additions and 110 deletions

No files matched your search

+13 -4
View File
@@ -17,18 +17,18 @@ class DeliverablesController < InheritedResources::Base
if params[:deliverable] && params[:deliverable][:type] && Deliverable.valid_types.include?(params[:deliverable][:type])
@deliverable.type = params[:deliverable][:type]
end
create! { contract_url(@project, @contract) }
create!(:notice => l(:text_flash_deliverable_created, :name => @deliverable.title)) { contract_url(@project, @contract) }
end
def update
@deliverable = begin_of_association_chain.deliverables.find_by_id(params[:id])
params[:deliverable] = params[:fixed_deliverable] || params[:hourly_deliverable] || params[:retainer_deliverable]
update! { contract_url(@project, @contract) }
update!(:notice => l(:text_flash_deliverable_updated, :name => @deliverable.title)) { contract_url(@project, @contract) }
end
def show
if show_partial?
@period = params[:period]
@period = extract_period(params[:period])
render :partial => 'deliverables/details_row', :locals => {:contract => @contract, :deliverable => @contract.deliverables.find(params[:id]), :period => @period}
else
redirect_to contract_url(@project, @contract)
@@ -36,7 +36,7 @@ class DeliverablesController < InheritedResources::Base
end
def destroy
destroy! { contract_url(@project, @contract) }
destroy!(:notice => l(:text_flash_deliverable_destroyed, :name => resource.title)) { contract_url(@project, @contract) }
end
protected
@@ -57,4 +57,13 @@ class DeliverablesController < InheritedResources::Base
@project = @contract.project
end
def extract_period(param)
period = nil
if param.present? && param.match(/\A\d{4}-\d{2}\z/) # "YYYY-MM"
year, month = param.split('-')
period = Date.new(year.to_i, month.to_i, 1)
end
period
end
end
+9 -2
View File
@@ -3,6 +3,7 @@ module ContractsHelper
returning(deliverable) do |d|
d.labor_budgets.build if d.labor_budgets.empty?
d.overhead_budgets.build if d.overhead_budgets.empty?
d.fixed_budgets.build if d.fixed_budgets.empty?
end
end
@@ -86,12 +87,18 @@ module ContractsHelper
number_with_precision(value, :precision => Contract::ViewPrecision, :delimiter => ',')
end
def retainer_period_options(deliverable)
def retainer_period_options(deliverable, method_options={})
selected = method_options[:selected]
if selected && selected.is_a?(Date)
selected = selected.strftime("%Y-%m")
end
options = []
options << content_tag(:option, l(:label_all).capitalize, :value => '')
deliverable.months.collect do |month|
options << content_tag(:option, month.strftime("%B %Y"), :value => month.strftime("%Y-%m"))
value = month.strftime("%Y-%m")
options << content_tag(:option, month.strftime("%B %Y"), :value => value, :selected => (selected == value) ? 'selected' : nil)
end
options
+12 -2
View File
@@ -36,8 +36,8 @@ 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,
:fixed_spent,
:fixed_markup_spent,
:discount_spent, :discount_budget
].each do |mthd|
define_method(mthd) { "TODO in later release" }
@@ -96,6 +96,16 @@ 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_markup_budget
deliverables.inject(0) {|total, deliverable| total += deliverable.fixed_markup_budget_total }
end
def after_initialize
self.executed = false unless self.executed.present?
end
+16
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
@@ -38,6 +40,11 @@ class Deliverable < ActiveRecord::Base
''
end
# Deliverable's aren't dated. Subclasses may override this for period behavior.
def current_date
nil
end
def to_s
title
end
@@ -81,6 +88,15 @@ 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
# OPTIMIZE: N+1
def fixed_markup_budget_total(date=nil)
fixed_budgets.inject(0) {|total, fixed_budget| total += fixed_budget.markup_value }
end
def filter_by_date(date=nil, &block)
block.call
end
+55
View File
@@ -0,0 +1,55 @@
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
}
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 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
+4 -4
View File
@@ -16,21 +16,21 @@ class FixedDeliverable < Deliverable
end
# Fixed deliverables are always 100% spent
def total_spent
def total_spent(date=nil)
total
end
# 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
def profit_left
total_spent - labor_budget_spent - overhead_spent
def profit_left(date=nil)
total_spent(date) - labor_budget_spent(date) - overhead_spent(date)
end
# Hardcoded value used as a wrapper for the old Budget plugin API.
+7 -5
View File
@@ -14,17 +14,19 @@ 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
# and the contract rate
def total_spent
def total_spent(date=nil)
return 0 if contract.nil?
return 0 if contract.billable_rate.blank?
return 0 unless self.issues.count > 0
@@ -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
def profit_left
total_spent - labor_budget_spent - overhead_spent
def profit_left(date=nil)
total_spent(date) - labor_budget_spent(date) - overhead_spent(date)
end
end
+204 -24
View File
@@ -19,8 +19,12 @@ class RetainerDeliverable < HourlyDeliverable
'R'
end
def current_date
Date.today
end
def current_period
Date.today.strftime("%B %Y")
current_date.strftime("%B %Y")
end
def beginning_date
@@ -32,7 +36,11 @@ class RetainerDeliverable < HourlyDeliverable
end
def date_range
(beginning_date..ending_date)
if beginning_date && ending_date && beginning_date <= ending_date
(beginning_date..ending_date)
else
[]
end
end
def within_date_range?(date)
@@ -75,42 +83,159 @@ 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)
if date
if within_date_range?(date)
labor_budgets.sum(:budget, :conditions => {:year => date.year, :month => date.month})
else
0 # outside of range
end
case scope_date_status(date)
when :in
labor_budgets.sum(:budget, :conditions => {:year => date.year, :month => date.month})
when :out
0
else
super
end
end
def overhead_budget_total(date=nil)
if date
if within_date_range?(date)
overhead_budgets.sum(:budget, :conditions => {:year => date.year, :month => date.month})
else
0 # outside of range
end
case scope_date_status(date)
when :in
overhead_budgets.sum(:budget, :conditions => {:year => date.year, :month => date.month})
when :out
0
else
super
end
end
def labor_budget_hours(date=nil)
if date
if within_date_range?(date)
labor_budgets.sum(:hours, :conditions => {:year => date.year, :month => date.month})
else
0 # outside of range
end
case scope_date_status(date)
when :in
labor_budgets.sum(:hours, :conditions => {:year => date.year, :month => date.month})
when :out
0
else
super
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_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 total_spent(date=nil)
case scope_date_status(date)
when :in
# TODO: duplicated on HourlyDeliverable#total_spent
return 0 if contract.nil?
return 0 if contract.billable_rate.blank?
return 0 unless self.issues.count > 0
issue_ids = self.issues.collect(&:id)
time_logs = time_entries_for_date_and_issue_ids(date, issue_ids)
hours = time_logs.inject(0) {|total, time_entry|
total += time_entry.hours if time_entry.billable?
total
}
return hours * contract.billable_rate
when :out
0
else
super
end
end
# TODO: stolen directly from redmine_overhead but with a block option
def labor_budget_spent_with_filter(&block)
return 0.0 unless self.issues.size > 0
total = 0.0
# Get all timelogs assigned
if block_given?
time_logs = block.call
else
time_logs = self.issues.collect(&:time_entries).flatten
end
return time_logs.collect {|time_log|
if time_log.billable?
time_log.cost
else
0.0
end
}.sum
end
# TODO: stolen directly from redmine_overhead but with a block option
def overhead_spent_with_filter(&block)
if block_given?
time_logs = block.call
else
time_logs = issues.collect(&:time_entries).flatten
end
return time_logs.collect {|time_entry|
if time_entry.billable?
0
else
time_entry.cost
end
}.sum
end
def labor_budget_spent(date=nil)
case scope_date_status(date)
when :in
labor_budget_spent_with_filter do
issue_ids = self.issues.collect(&:id)
time_entries_for_date_and_issue_ids(date, issue_ids)
end
when :out
0
else
labor_budget_spent_with_filter
end
end
def overhead_spent(date=nil)
case scope_date_status(date)
when :in
overhead_spent_with_filter do
issue_ids = self.issues.collect(&:id)
time_entries_for_date_and_issue_ids(date, issue_ids)
end
when :out
0
else
overhead_spent_with_filter
end
end
def create_budgets_for_periods
# For each month in the time span
months.each do |month|
@@ -124,10 +249,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
@@ -174,6 +305,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
@@ -183,9 +325,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
@@ -195,14 +338,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
@@ -210,6 +354,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={})
@@ -219,4 +367,36 @@ class RetainerDeliverable < HourlyDeliverable
def create_new_overhead_budget_based_on_existing_budget(existing_overhead_budget, attributes={})
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)
status = :in
else
status = :out # outside of range
end
else
status = :no_date
end
status
end
def time_entries_for_date_and_issue_ids(date, issue_ids)
if issue_ids.present?
TimeEntry.all(:conditions => ["#{Issue.table_name}.id IN (:issue_ids) AND tyear = (:year) AND tmonth = (:month)",
{:issue_ids => issue_ids,
:year => date.year,
:month => date.month}
],
:include => :issue)
else
[]
end
end
end
+3 -3
View File
@@ -44,7 +44,7 @@
<%= show_budget_field(resource, :labor_spent, :labor_budget, :format => :format_value_field_for_contracts, :html_options => {:class => 'contract-labor'}, :label_html_options => {:width => '46%'}) %>
<%= show_budget_field(resource, :overhead_spent, :overhead_budget, :format => :format_value_field_for_contracts, :html_options => {:class => 'contract-overhead'}) %>
<%= show_budget_field(resource, :fixed_spent, :fixed_budget, :format => :format_value_field_for_contracts, :html_options => {:class => 'contract-fixed'}) %>
<%= show_budget_field(resource, :markup_spent, :markup_budget, :format => :format_value_field_for_contracts, :html_options => {:class => 'contract-markup'}) %>
<%= show_budget_field(resource, :fixed_markup_spent, :fixed_markup_budget, :format => :format_value_field_for_contracts, :html_options => {:class => 'contract-markup'}) %>
<%= show_budget_field(resource, :profit_spent, :profit_budget, :format => :format_value_field_for_contracts, :html_options => {:class => 'contract-profit'}) %>
<%= show_budget_field(resource, :discount_spent, :discount_budget, :format => :format_value_field_for_contracts, :html_options => {:class => 'contract-discount'}) %>
@@ -104,11 +104,11 @@
<td width="15%" class="manager"><%= h deliverable.manager.try(:name) %></td>
<%= format_budget_for_deliverable(deliverable, deliverable.labor_budget_spent, deliverable.labor_budget_total, :class => 'labor') %>
<%= format_budget_for_deliverable(deliverable, deliverable.overhead_spent, deliverable.overhead_budget_total, :class => 'overhead') %>
<%= format_budget_for_deliverable(deliverable, 0, 0) %><%# TODO: Release 2, Fixed Budgets %>
<%= format_budget_for_deliverable(deliverable, 0, deliverable.fixed_budget_total, :class => 'fixed') %>
<% end %>
<tr id="deliverable_details_<%= h(deliverable.id) %>" class="ign">
<%= render :partial => 'deliverables/details_row', :locals => {:deliverable => deliverable, :contract => resource} %>
<%= render :partial => 'deliverables/details_row', :locals => {:deliverable => deliverable, :contract => resource, :period => deliverable.current_date} %>
</tr>
<% end %>
</tbody>
+28 -12
View File
@@ -1,4 +1,4 @@
<% period ||= '' %>
<% period ||= nil %>
<td colspan="11" class="deliverable_details_outer_wrapper_<%= h(deliverable.id) %>">
@@ -16,7 +16,7 @@
<form method="get" action="<%= contract_deliverable_path(@project, contract, deliverable, :format => 'js', :as => 'deliverable_details_row') %>">
<fieldset>
<select name="period" id="retainer_period_change_<%= h(deliverable.id) %>" class="retainer_period_change">
<%= retainer_period_options(deliverable) %>
<%= retainer_period_options(deliverable, :selected => period) %>
</select>
</fieldset>
</form>
@@ -52,28 +52,44 @@
<tbody>
<tr>
<td class="l"><a href="#"><strong>Labor</strong></a></td>
<td class="labor_budget_spent"><%= h(format_value_field_for_contracts(deliverable.labor_budget_spent)) %></td>
<td class="labor_budget_total"><%= h(format_value_field_for_contracts(deliverable.labor_budget_total)) %></td>
<td class="labor_budget_spent"><%= h(format_value_field_for_contracts(deliverable.labor_budget_spent(period))) %></td>
<td class="labor_budget_total"><%= h(format_value_field_for_contracts(deliverable.labor_budget_total(period))) %></td>
<td> TODO: Release 2 / TODO hrs </td>
</tr>
<tr>
<td class="l"><a href="#"><strong>Overhead</strong></a></td>
<td class="overhead_budget_spent"><%= h(format_value_field_for_contracts(deliverable.overhead_spent)) %></td>
<td class="overhead_budget_total"><%= h(format_value_field_for_contracts(deliverable.overhead_budget_total)) %></td>
<td class="overhead_budget_spent"><%= h(format_value_field_for_contracts(deliverable.overhead_spent(period))) %></td>
<td class="overhead_budget_total"><%= h(format_value_field_for_contracts(deliverable.overhead_budget_total(period))) %></td>
<td> TODO: Release 2 / TODO hrs </td>
</tr>
<%# TODO: Release 2, Fixed %>
<%# TODO: Release 2, Markup %>
<% deliverable.fixed_budgets.by_period(period).each do |fixed_budget| %>
<% next if fixed_budget.blank_record? %>
<tr id="fixed_budget_<%= fixed_budget.id %>">
<td class="l fixed_title" title="<%= h(fixed_budget.description) %>"><%= h(fixed_budget.title) %></td>
<td class="fixed_budget_spent">0</td>
<td class="fixed_budget_total"><%= h(format_value_field_for_contracts(fixed_budget.budget)) %></td>
<td></td>
</tr>
<% end %>
<tr>
<td class="l"><%= l(:field_markup) %></td>
<td class="fixed_markup_budget_spent">0</td>
<td class="fixed_markup_budget_total"><%= h(format_value_field_for_contracts(deliverable.fixed_markup_budget_total(period))) %></td>
<td></td>
</tr>
<tr>
<td class="l">Profit</td>
<td><%= h(format_value_field_for_contracts(deliverable.profit_left)) %></td>
<td><%= h(format_value_field_for_contracts(deliverable.profit_budget)) %></td>
<td><%= h(format_value_field_for_contracts(deliverable.profit_left(period))) %></td>
<td><%= h(format_value_field_for_contracts(deliverable.profit_budget(period))) %></td>
<td></td>
</tr>
<tr class="total">
<td class="l"><strong>Total:</strong></td>
<td class="total_spent"><strong><%= h(format_value_field_for_contracts(deliverable.total_spent)) %></strong></td>
<td class="total"><strong><%= h(format_value_field_for_contracts(deliverable.total)) %></strong></td>
<td class="total_spent"><strong><%= h(format_value_field_for_contracts(deliverable.total_spent(period))) %></strong></td>
<td class="total"><strong><%= h(format_value_field_for_contracts(deliverable.total(period))) %></strong></td>
<td><strong>TODO: Release 2</strong></td>
</tr>
</tbody>
@@ -1,5 +1,9 @@
<% form.inputs :name => label, :class => "deliverable-finances #{fieldset_class}" do %>
<li style="display:none;" id='retainer-finances-message'>
<%= content_tag(:p, l(:text_retainer_monthly_message)) %>
</li>
<li class="numeric optional">
<%= content_tag(:label, l(:field_labor)) %>
<table id="deliverable-labor" class="deliverable_finance_table">
@@ -58,6 +62,40 @@
</table>
</li>
<li class="numeric optional">
<div id="deliverable-fixed" class="fixed-item-form">
<label for="contract_discount">Fixed</label>
<% form.fields_for :fixed_budgets, fixed_budgets do |fixed_budget| %>
<%= fixed_budget.hidden_field(:year) %>
<%= fixed_budget.hidden_field(:month) %>
<p class="inline-hints" style="display:none;"><%= fixed_budget.label(:title, l(:field_title))%></p>
<%= fixed_budget.text_field(:title) %>
<p class="inline-hints">
<%= fixed_budget.label(:budget, l(:field_budget), :style => 'display: none;')%> <%# Hidden label :| %>
<%= l(:text_dollar_sign) %>
</p>
<%= fixed_budget.text_field(:budget) %>
<p class="inline-hints">
<%= fixed_budget.label(:markup, l(:field_markup), :style => 'display: none;')%> <%# Hidden label :| %>
<%= l(:field_discount_hint) %>
</p>
<%= fixed_budget.text_field(:markup) %>
<p class="inline-hints" style="display:none;"><%= fixed_budget.label(:description, l(:field_description), :for => "fixed-description#{fixed_budget.object.object_id}")%></p>
<%= fixed_budget.text_area(:description, :class => 'wiki-edit', :rows => '5', :id => "fixed-description#{fixed_budget.object.object_id}") %>
<%= wikitoolbar_for "fixed-description#{fixed_budget.object.object_id}" %>
<p>Todo: Add button (Release 3)</p>
<% end %>
</div>
</li>
<%= form.input :total, :input_html => {:size => 10}, :wrapper_html => {:class => 'deliverable_total_input'}, :hint => l(:text_dollar_sign) %>
<% end %>
+3 -3
View File
@@ -3,7 +3,7 @@
<%= javascript_tag("var i18nChangedPeriodMessage = '#{l(:text_changed_period_message)}'") %>
<div class="box tabular">
<% form.inputs :name => l(:text_deliverable_details_legend) do %>
<% form.inputs :name => l(:text_deliverable_details_legend), :id => 'deliverable-details' do %>
<%# Used by jquery to check if this is a new or existing record %>
<%= hidden_field_tag('deliverable_id', h(resource.id), :id => 'deliverable_stored_id') %>
<%= form.input :title, :required => true %>
@@ -40,13 +40,13 @@
<% if resource.retainer? && resource.respond_to?(:months) %>
<% if resource.months.present? %>
<% resource.months.each do |month| %>
<%= render :partial => 'finance_form', :locals => {:form => form, :labor_budgets => resource.labor_budgets_for_date(month), :overhead_budgets => resource.overhead_budgets_for_date(month), :label => l(:text_deliverable_finances_date, :date => month.strftime("%B, %Y")), :fieldset_class => 'date-' + month.strftime('%Y-%m') } %>
<%= render :partial => 'finance_form', :locals => {:form => form, :labor_budgets => resource.labor_budgets_for_date(month), :overhead_budgets => resource.overhead_budgets_for_date(month), :fixed_budgets => resource.fixed_budgets_for_date(month), :label => l(:text_deliverable_finances_date, :date => month.strftime("%B, %Y")), :fieldset_class => 'date-' + month.strftime('%Y-%m') } %>
<% end %>
<% else %>
<%= content_tag(:p, l(:text_missing_period), :class => 'nodata') %>
<% end %>
<% else %>
<%= render :partial => 'finance_form', :locals => {:form => form, :labor_budgets => resource.labor_budgets, :overhead_budgets => resource.overhead_budgets, :label => l(:text_deliverable_finances), :fieldset_class => '' } %>
<%= render :partial => 'finance_form', :locals => {:form => form, :labor_budgets => resource.labor_budgets, :overhead_budgets => resource.overhead_budgets, :fixed_budgets => resource.fixed_budgets, :label => l(:text_deliverable_finances), :fieldset_class => '' } %>
<% end %>
</div>
+8
View File
@@ -48,12 +48,20 @@ jQuery(function($) {
if (deliverableType == 'FixedDeliverable') {
showDeliverableTotal();
hideDeliverableFrequency();
$('#retainer-finances-message').hide();
} else if(deliverableType == "HourlyDeliverable") {
hideDeliverableTotal();
hideDeliverableFrequency();
$('#retainer-finances-message').hide();
} else if(deliverableType == "RetainerDeliverable") {
hideDeliverableTotal();
showDeliverableFrequency();
if ($('form.deliverable #deliverable_stored_id').val() == '') {
$('#retainer-finances-message').show();
} else {
$('#retainer-finances-message').hide();
}
}
},
+8 -2
View File
@@ -40,8 +40,8 @@ en:
field_fixed_spent: "Fixed"
field_labor_budget: "Labor"
field_labor_spent: "Labor"
field_markup_budget: "Markup"
field_markup_spent: "Markup"
field_fixed_markup_budget: "Markup"
field_fixed_markup_spent: "Markup"
field_overhead_budget: "Overhead"
field_overhead_spent: "Overhead"
field_profit_budget: "Profit"
@@ -66,3 +66,9 @@ en:
text_missing_period: "This deliverable is missing a date range so it cannot have budget items. Please save start and end dates before adding any budget items."
text_changed_period_message: "The period for this deliverable has been changed. Would you like to expand/shrink the Deliverable Finances?"
field_current_period: "Current period"
text_retainer_monthly_message: "For a single month"
text_flash_deliverable_created: "Deliverable: {{name}} was successfully created."
text_flash_deliverable_updated: "Deliverable: {{name}} was successfully updated."
text_flash_deliverable_destroyed: "Deliverable: {{name}} was successfully destroyed."
field_budget: Budget
field_markup: Markup
+19
View File
@@ -0,0 +1,19 @@
class CreateFixedBudgets < ActiveRecord::Migration
def self.up
create_table :fixed_budgets do |t|
t.string :title
t.decimal :budget, :precision => 15, :scale => 4
t.string :markup
t.text :description
t.references :deliverable
t.timestamps
end
add_index :fixed_budgets, :deliverable_id
end
def self.down
drop_table :fixed_budgets
end
end
@@ -0,0 +1,14 @@
class AddYearAndMonthToFixedBudgets < ActiveRecord::Migration
def self.up
add_column :fixed_budgets, :year, :integer
add_index :fixed_budgets, :year
add_column :fixed_budgets, :month, :integer
add_index :fixed_budgets, :month
end
def self.down
remove_column :fixed_budgets, :year
remove_column :fixed_budgets, :month
end
end
+61
View File
@@ -210,6 +210,67 @@ class ContractsShowTest < ActionController::IntegrationTest
end
should "show the total fixed budget for a Deliverable" do
@manager = User.generate!
@deliverable1 = FixedDeliverable.generate!(:contract => @contract, :manager => @manager)
FixedBudget.generate!(:deliverable => @deliverable1, :budget => '$1,000', :markup => '$100')
FixedBudget.generate!(:deliverable => @deliverable1, :budget => '$2,000', :markup => '200%')
visit_contract_page(@contract)
assert_select "table#deliverables" do
assert_select "td.fixed", :text => /3,000/
end
end
should "QUESTION: show the total fixed budget spent for a Deliverable"
should "show each fixed budget item in the details for the Deliverable" do
@manager = User.generate!
@deliverable1 = FixedDeliverable.generate!(:contract => @contract, :manager => @manager)
@budget1 = FixedBudget.generate!(:deliverable => @deliverable1, :title => 'Item 1', :budget => '$1,000', :markup => '$100')
@budget2 = FixedBudget.generate!(:deliverable => @deliverable1, :title => 'Item 2', :budget => '$2,000', :markup => '200%')
visit_contract_page(@contract)
assert_select "table#deliverables" do
assert_select "#deliverable_details_#{@deliverable1.id}" do
assert_select "tr#fixed_budget_#{@budget1.id}" do
assert_select 'td.fixed_title', :text => /#{@budget1.title}/
assert_select 'td.fixed_budget_spent', :text => '0'
assert_select 'td.fixed_budget_total', :text => '1,000'
end
assert_select "tr#fixed_budget_#{@budget2.id}" do
assert_select 'td.fixed_title', :text => /#{@budget2.title}/
assert_select 'td.fixed_budget_spent', :text => '0'
assert_select 'td.fixed_budget_total', :text => '2,000'
end
end
end
end
should "show the total fixed markup budget in the details for the Deliverable" do
@manager = User.generate!
@deliverable1 = FixedDeliverable.generate!(:contract => @contract, :manager => @manager)
@budget1 = FixedBudget.generate!(:deliverable => @deliverable1, :title => 'Item 1', :budget => '$1,000', :markup => '$100')
@budget2 = FixedBudget.generate!(:deliverable => @deliverable1, :title => 'Item 2', :budget => '$2,000', :markup => '200%')
visit_contract_page(@contract)
assert_select "table#deliverables" do
assert_select "#deliverable_details_#{@deliverable1.id}" do
assert_select 'td.fixed_markup_budget_spent', :text => '0'
assert_select 'td.fixed_markup_budget_total', :text => '4,100'
end
end
end
should "show the current period for a Retainer" do
today_mock = Date.new(2010,2,15)
Date.stubs(:today).returns(today_mock)
+1 -2
View File
@@ -32,7 +32,6 @@ class DeliverableDetailsShowTest < ActionController::IntegrationTest
visit "/projects/#{@project.id}/contracts/#{@contract.id}/deliverables/#{@deliverable1.id}", :get, {:format => 'js', :as => 'deliverable_details_row', :period => '2010-02'}
puts response.body
assert_response :success
assert_select ".deliverable_details_outer_wrapper_#{@deliverable1.id}" do
assert_select "td.labor_budget_total", '100'
@@ -40,7 +39,7 @@ class DeliverableDetailsShowTest < ActionController::IntegrationTest
assert_select "td.total", '100'
assert_select "select.retainer_period_change" do
assert_select "option[selected=selected]", "Feburary 2010"
assert_select "option[selected=selected]", "February 2010"
end
end
+72 -9
View File
@@ -27,10 +27,11 @@ class DeliverablesEditTest < ActionController::IntegrationTest
assert_select "select#fixed_deliverable_type", :count => 0 # Not editable
assert js("jQuery('#fixed_deliverable_total_input').is(':visible')"), "Total is hidden when it should be visible"
fill_in "Title", :with => 'An updated title'
check "Feature Sign Off"
check "Warranty Sign Off"
within("#deliverable-details") do
fill_in "Title", :with => 'An updated title'
check "Feature Sign Off"
check "Warranty Sign Off"
end
click_button "Save"
assert_response :success
@@ -56,9 +57,11 @@ class DeliverablesEditTest < ActionController::IntegrationTest
assert_select "select#hourly_deliverable_type", :count => 0 # Not editable
assert js("jQuery('#hourly_deliverable_total_input').is(':hidden')"), "Total is visible when it should be hidden"
fill_in "Title", :with => 'An updated title'
check "Feature Sign Off"
check "Warranty Sign Off"
within("#deliverable-details") do
fill_in "Title", :with => 'An updated title'
check "Feature Sign Off"
check "Warranty Sign Off"
end
within("#deliverable-labor") do
fill_in "hrs", :with => '20'
@@ -95,6 +98,8 @@ class DeliverablesEditTest < ActionController::IntegrationTest
@retainer_deliverable = RetainerDeliverable.spawn(:contract => @contract, :manager => @manager, :title => "Retainer")
@retainer_deliverable.labor_budgets << @labor_budget = LaborBudget.spawn(:deliverable => @retainer_deliverable, :budget => 1000, :hours => 10)
@retainer_deliverable.overhead_budgets << @overhead_budget = OverheadBudget.spawn(:deliverable => @retainer_deliverable, :budget => 1000, :hours => 10)
@retainer_deliverable.fixed_budgets << @fixed_budget = FixedBudget.spawn(:deliverable => @retainer_deliverable, :title => 'Printing supplies', :budget => 100, :markup => 0)
@retainer_deliverable.start_date = '2010-01-01'
@retainer_deliverable.end_date = '2010-12-31'
@retainer_deliverable.save!
@@ -117,6 +122,13 @@ class DeliverablesEditTest < ActionController::IntegrationTest
fill_in "hrs", :with => '100'
fill_in "$", :with => '100'
end
within "#deliverable-fixed" do
fill_in "title", :with => 'Flight to NYC'
fill_in "budget", :with => '$600'
fill_in "markup", :with => '50%'
fill_in "description", :with => 'Need to fly to NYC for the week'
end
end
click_button "Save"
@@ -157,6 +169,24 @@ class DeliverablesEditTest < ActionController::IntegrationTest
end
end
@fixed_budgets = @retainer_deliverable.reload.fixed_budgets
assert_equal 12, @fixed_budgets.length
@fixed_budgets.each do |fixed_budget|
if fixed_budget.year == 2010 && fixed_budget.month == 1
# Specific month's budget updated?
assert_equal 600, fixed_budget.budget
assert_equal '50%', fixed_budget.markup
assert_equal 300, fixed_budget.markup_value
else
assert_equal 100, fixed_budget.budget
assert_equal '0', fixed_budget.markup
end
end
end
should "allow extending a Retainer's start and end months" do
@@ -175,6 +205,7 @@ class DeliverablesEditTest < ActionController::IntegrationTest
@retainer_deliverable.labor_budgets << @labor_budget = LaborBudget.spawn(:deliverable => @retainer_deliverable, :budget => labor_budget_amount_2, :hours => labor_budget_hours_2)
@retainer_deliverable.overhead_budgets << @overhead_budget = OverheadBudget.spawn(:deliverable => @retainer_deliverable, :budget => overhead_budget_amount_1, :hours => overhead_budget_hours_1)
@retainer_deliverable.overhead_budgets << @overhead_budget = OverheadBudget.spawn(:deliverable => @retainer_deliverable, :budget => overhead_budget_amount_2, :hours => overhead_budget_hours_2)
@retainer_deliverable.fixed_budgets << @fixed_budget = FixedBudget.spawn(:deliverable => @retainer_deliverable, :title => 'Printing supplies', :budget => 100, :markup => 0)
@retainer_deliverable.start_date = '2010-01-01'
@retainer_deliverable.end_date = '2010-12-31'
@retainer_deliverable.save!
@@ -256,6 +287,17 @@ class DeliverablesEditTest < ActionController::IntegrationTest
assert [overhead_budget_amount_1, overhead_budget_amount_2].include?(overhead_budget.budget), "Extended overhead budget dollars not matching template budget"
end
@fixed_budgets = @retainer_deliverable.reload.fixed_budgets
assert_equal 36, @fixed_budgets.length # 36 months * 1 record
@fixed_budgets_for_2009 = @fixed_budgets.select {|l| l.year == 2009 }
@fixed_budgets_for_2010 = @fixed_budgets.select {|l| l.year == 2010 }
@fixed_budgets_for_2011 = @fixed_budgets.select {|l| l.year == 2011 }
assert_equal 12, @fixed_budgets_for_2009.length
assert_equal 12, @fixed_budgets_for_2010.length
assert_equal 12, @fixed_budgets_for_2011.length
end
should "allow shrinking a Retainer's start and end months" do
@@ -264,6 +306,7 @@ class DeliverablesEditTest < ActionController::IntegrationTest
@retainer_deliverable.labor_budgets << @labor_budget = LaborBudget.spawn(:deliverable => @retainer_deliverable, :budget => 2000, :hours => 20)
@retainer_deliverable.overhead_budgets << @overhead_budget = OverheadBudget.spawn(:deliverable => @retainer_deliverable, :budget => 1000, :hours => 10)
@retainer_deliverable.overhead_budgets << @overhead_budget = OverheadBudget.spawn(:deliverable => @retainer_deliverable, :budget => 2000, :hours => 20)
@retainer_deliverable.fixed_budgets << @fixed_budget = FixedBudget.spawn(:deliverable => @retainer_deliverable, :title => 'Printing supplies', :budget => 100, :markup => 0)
@retainer_deliverable.start_date = '2010-01-01'
@retainer_deliverable.end_date = '2010-12-31'
@retainer_deliverable.save!
@@ -293,6 +336,9 @@ class DeliverablesEditTest < ActionController::IntegrationTest
@overhead_budgets = @retainer_deliverable.reload.overhead_budgets
assert_equal 12, @overhead_budgets.length # 6 months * 2 records
@fixed_budgets = @retainer_deliverable.reload.fixed_budgets
assert_equal 6, @fixed_budgets.length # 6 months * 1 records
end
should "allow editing a Retainer's start and end months inside the current period" do
@@ -346,7 +392,7 @@ class DeliverablesEditTest < ActionController::IntegrationTest
assert_response :success
assert_template 'deliverables/edit'
# Should show 6 inputs:
# Should show inputs:
# * labor hidden year
# * labor hidden month
# * labor hours
@@ -355,9 +401,15 @@ class DeliverablesEditTest < ActionController::IntegrationTest
# * overhead hidden month
# * overhead hours
# * overhead amount
# * fixed hidden year
# * fixed hidden month
# * fixed title
# * fixed budget
# * fixed markup
# * total (hidden)
assert_select ".date-2010-01" do
assert_select "input", :count => 9
assert_select "input", :count => 14
assert_select "textarea.wiki-edit", :count => 1 # Fixed description
end
@@ -371,6 +423,14 @@ class DeliverablesEditTest < ActionController::IntegrationTest
fill_in "hrs", :with => '100'
fill_in "$", :with => '100'
end
within "#deliverable-fixed" do
fill_in "title", :with => 'Flight to NYC'
fill_in "budget", :with => '$600'
fill_in "markup", :with => '50%'
fill_in "description", :with => 'Need to fly to NYC for the week'
end
end
click_button "Save"
@@ -386,5 +446,8 @@ class DeliverablesEditTest < ActionController::IntegrationTest
assert_equal 3, @retainer_deliverable.overhead_budgets.count
assert_equal [100, nil, nil], @retainer_deliverable.overhead_budgets.collect(&:hours)
assert_equal [100, nil, nil], @retainer_deliverable.overhead_budgets.collect(&:budget)
assert_equal 3, @retainer_deliverable.fixed_budgets.count
assert_equal [600, nil, nil], @retainer_deliverable.fixed_budgets.collect(&:budget)
end
end
+49 -25
View File
@@ -50,12 +50,15 @@ class DeliverablesNewTest < ActionController::IntegrationTest
click_link 'Add New'
assert_response :success
fill_in "Title", :with => 'A New Deliverable'
select "Fixed", :from => "Type"
select @manager.name, :from => "Manager"
fill_in "Start", :with => '2010-01-01'
fill_in "End Date", :with => '2010-12-31'
fill_in "Notes", :with => 'Some notes on the deliverable'
within("#deliverable-details") do
fill_in "Title", :with => 'A New Deliverable'
select "Fixed", :from => "Type"
select @manager.name, :from => "Manager"
fill_in "Start", :with => '2010-01-01'
fill_in "End Date", :with => '2010-12-31'
fill_in "Notes", :with => 'Some notes on the deliverable'
end
fill_in "Total", :with => '1,000.00'
# TODO: webrat can't trigger DOM events so it can't appear
# assert js("jQuery('#deliverable_total').is(':visible')"), "Total is hidden when it should be visible"
@@ -84,12 +87,14 @@ class DeliverablesNewTest < ActionController::IntegrationTest
click_link 'Add New'
assert_response :success
fill_in "Title", :with => 'A New Deliverable'
select "Hourly", :from => "Type"
select @manager.name, :from => "Manager"
fill_in "Start", :with => '2010-01-01'
fill_in "End Date", :with => '2010-12-31'
fill_in "Notes", :with => 'Some notes on the deliverable'
within("#deliverable-details") do
fill_in "Title", :with => 'A New Deliverable'
select "Hourly", :from => "Type"
select @manager.name, :from => "Manager"
fill_in "Start", :with => '2010-01-01'
fill_in "End Date", :with => '2010-12-31'
fill_in "Notes", :with => 'Some notes on the deliverable'
end
fill_in "Total", :with => '1,000.00'
# # Hide and clear the total
@@ -120,13 +125,15 @@ class DeliverablesNewTest < ActionController::IntegrationTest
click_link 'Add New'
assert_response :success
fill_in "Title", :with => 'A New Deliverable'
select "Retainer", :from => "Type"
select @manager.name, :from => "Manager"
fill_in "Start", :with => '2010-01-01'
fill_in "End Date", :with => '2010-12-31'
fill_in "Notes", :with => 'Some notes on the deliverable'
within("#deliverable-details") do
fill_in "Title", :with => 'A New Deliverable'
select "Retainer", :from => "Type"
select @manager.name, :from => "Manager"
fill_in "Start", :with => '2010-01-01'
fill_in "End Date", :with => '2010-12-31'
fill_in "Notes", :with => 'Some notes on the deliverable'
end
within("#deliverable-labor") do
fill_in "hrs", :with => '20'
fill_in "$", :with => '$2,000'
@@ -193,12 +200,14 @@ class DeliverablesNewTest < ActionController::IntegrationTest
click_link 'Add New'
assert_response :success
fill_in "Title", :with => 'A New Deliverable'
select "Hourly", :from => "Type"
select @manager.name, :from => "Manager"
fill_in "Start", :with => '2010-01-01'
fill_in "End Date", :with => '2010-12-31'
fill_in "Notes", :with => 'Some notes on the deliverable'
within("#deliverable-details") do
fill_in "Title", :with => 'A New Deliverable'
select "Hourly", :from => "Type"
select @manager.name, :from => "Manager"
fill_in "Start", :with => '2010-01-01'
fill_in "End Date", :with => '2010-12-31'
fill_in "Notes", :with => 'Some notes on the deliverable'
end
within("#deliverable-labor") do
fill_in "hrs", :with => '20'
@@ -210,6 +219,13 @@ class DeliverablesNewTest < ActionController::IntegrationTest
fill_in "$", :with => '$1,000'
end
within("#deliverable-fixed") do
fill_in "title", :with => 'Flight to NYC'
fill_in "budget", :with => '$600'
fill_in "markup", :with => '50%'
fill_in "description", :with => 'Need to fly to NYC for the week'
end
click_button "Save"
assert_response :success
@@ -226,6 +242,14 @@ class DeliverablesNewTest < ActionController::IntegrationTest
@overhead_budget = @deliverable.overhead_budgets.first
assert_equal 10, @overhead_budget.hours
assert_equal 1000.0, @overhead_budget.budget
assert_equal 1, @deliverable.fixed_budgets.count
@fixed_budget = @deliverable.fixed_budgets.first
assert_equal "Flight to NYC", @fixed_budget.title
assert_equal 600, @fixed_budget.budget
assert_equal "50%", @fixed_budget.markup
assert_equal 300, @fixed_budget.markup_value # 600 * 50%
end
end
+34
View File
@@ -254,4 +254,38 @@ class ContractTest < ActiveSupport::TestCase
end
end
context "#fixed_budget" do
should "sum all fixed budget amounts on the Deliverables" do
contract = Contract.generate!
contract.deliverables << @deliverable_1 = FixedDeliverable.generate!
FixedBudget.generate!(:deliverable => @deliverable_1, :budget => '$1,000')
contract.deliverables << @deliverable_2 = HourlyDeliverable.generate!
FixedBudget.generate!(:deliverable => @deliverable_2, :budget => '$2,000')
assert_equal 3000, contract.fixed_budget
end
end
context "#fixed_spent" do
should "QUESTION: how to compute the amount spent"
end
context "#fixed_markup_budget" do
should "sum all fixed budget markup values on the Deliverables" do
contract = Contract.generate!
contract.deliverables << @deliverable_1 = FixedDeliverable.generate!
FixedBudget.generate!(:deliverable => @deliverable_1, :budget => '$1,000', :markup => '$100')
contract.deliverables << @deliverable_2 = HourlyDeliverable.generate!
FixedBudget.generate!(:deliverable => @deliverable_2, :budget => '$2,000', :markup => '200%')
assert_equal (100) + (2.00 * 2000), contract.fixed_markup_budget
end
end
context "#fixed_markup_spent" do
should "QUESTION: how to compute the amount spent"
end
end
+1
View File
@@ -5,6 +5,7 @@ class DeliverableTest < ActiveSupport::TestCase
should_belong_to :manager
should_have_many :labor_budgets
should_have_many :overhead_budgets
should_have_many :fixed_budgets
should_have_many :issues
should_validate_presence_of :title
+33
View File
@@ -0,0 +1,33 @@
require File.dirname(__FILE__) + '/../test_helper'
class FixedBudgetTest < ActiveSupport::TestCase
should_belong_to :deliverable
context "#markup_value" do
setup do
@fixed_budget = FixedBudget.new(:budget => 1000)
end
context "with no markup" do
should "be 0" do
assert_equal nil, @fixed_budget.markup
assert_equal 0, @fixed_budget.markup_value
end
end
context "with a % markup" do
should "equal the budget times the %" do
@fixed_budget.markup = '50%'
assert_equal 500, @fixed_budget.markup_value
end
end
context "with a $ markup" do
should "equal the $ markup (straight markup)" do
@fixed_budget.markup = '$4,000.57'
assert_equal 4000.57, @fixed_budget.markup_value
end
end
end
end
+2 -1
View File
@@ -13,8 +13,9 @@ class FixedDeliverableTest < ActiveSupport::TestCase
LaborBudget.generate!(:deliverable => deliverable, :budget => 200)
LaborBudget.generate!(:deliverable => deliverable, :budget => 200)
OverheadBudget.generate!(:deliverable => deliverable, :budget => 200)
FixedBudget.generate!(:deliverable => deliverable, :budget => '$100', :markup => '50%') # $50 markup
assert_equal 400, deliverable.profit_budget
assert_equal 400 - 150, deliverable.profit_budget
end
should "be 0 if there is no total" do
+6 -4
View File
@@ -20,13 +20,14 @@ class HourlyDeliverableTest < ActiveSupport::TestCase
assert_equal 0, d.total
end
should "multiply the total number of labor budget hours by the contract billable rate" do
should "multiply the total number of labor budget hours by the contract billable rate and add the fixed budget and markup" do
contract = Contract.generate!(:billable_rate => 100.0)
d = HourlyDeliverable.generate!(:contract => contract)
d.labor_budgets << LaborBudget.generate!(:hours => 10)
d.overhead_budgets << OverheadBudget.generate!(:hours => 20)
d.fixed_budgets << FixedBudget.generate!(:budget => '$100', :markup => '50%') # $50 markup
assert_equal 100.0 * 10, d.total
assert_equal (100.0 * 10) + (100 + 50), d.total
end
end
@@ -86,9 +87,10 @@ class HourlyDeliverableTest < ActiveSupport::TestCase
LaborBudget.generate!(:deliverable => @deliverable, :hours => 5, :budget => 250)
LaborBudget.generate!(:deliverable => @deliverable, :hours => 5, :budget => 250)
OverheadBudget.generate!(:deliverable => @deliverable, :hours => 3, :budget => 225)
FixedBudget.generate!(:deliverable => @deliverable, :budget => '$100', :markup => '50%') # $50 markup
assert_equal 1500, @deliverable.total
assert_equal 1500 - (225 + 250 + 250), @deliverable.profit_budget
assert_equal 1650, @deliverable.total # has the FixedBudget items added to the total also
assert_equal 1650 - (225 + 250 + 250 + 100 + 50), @deliverable.profit_budget
end
end
+320 -8
View File
@@ -81,16 +81,66 @@ class RetainerDeliverableTest < ActiveSupport::TestCase
end
context "#labor_budget_spent" do
setup do
@project = Project.generate!
@contract = Contract.generate!(:billable_rate => 100, :project => @project)
@deliverable = RetainerDeliverable.generate!(:start_date => '2010-01-01', :end_date => '2010-03-31', :contract => @contract)
@deliverable.labor_budgets << LaborBudget.spawn(:budget => 100, :hours => 10)
@deliverable.overhead_budgets << OverheadBudget.spawn(:budget => 100, :hours => 10)
@deliverable.save!
@manager = User.generate!
@role = Role.generate!
User.add_to_project(@manager, @project, @role)
configure_overhead_plugin
@issue1 = Issue.generate_for_project!(@project)
@time_entry1 = TimeEntry.generate!(:issue => @issue1,
:project => @project,
:activity => @billable_activity,
:spent_on => Date.new(2010,1,2),
:hours => 10,
:user => @manager)
@time_entry2 = TimeEntry.generate!(:issue => @issue1,
:project => @project,
:activity => @billable_activity,
:spent_on => Date.new(2010,2,1),
:hours => 20,
:user => @manager)
@rate = Rate.generate!(:project => @project,
:user => @manager,
:date_in_effect => Date.new(2010,1,1),
:amount => 100)
@deliverable.issues << @issue1
end
context "with a empty period" do
should "use all periods"
should "use all periods" do
assert_equal (10+20) * 100, @deliverable.labor_budget_spent(nil)
end
end
context "with a period out of the retainer range" do
should "use all periods"
should "filter the records periods" do
assert_equal 0, @deliverable.labor_budget_spent(Date.new(2011,1,1))
end
end
context "with an invalid period" do
should "return 0" do
assert_equal 0, @deliverable.labor_budget_spent('1')
end
end
context "with a period in the retainer range" do
should "filter the records"
should "filter the records" do
assert_equal 20 * 100, @deliverable.labor_budget_spent(Date.new(2010,2,1))
end
end
end
@@ -126,8 +176,69 @@ class RetainerDeliverableTest < ActiveSupport::TestCase
end
end
# context "#overhead_spent"
context "#overhead_spent" do
setup do
@project = Project.generate!
@contract = Contract.generate!(:billable_rate => 100, :project => @project)
@deliverable = RetainerDeliverable.generate!(:start_date => '2010-01-01', :end_date => '2010-03-31', :contract => @contract)
@deliverable.overhead_budgets << OverheadBudget.spawn(:budget => 100, :hours => 10)
@deliverable.save!
@manager = User.generate!
@role = Role.generate!
User.add_to_project(@manager, @project, @role)
configure_overhead_plugin
@issue1 = Issue.generate_for_project!(@project)
@time_entry1 = TimeEntry.generate!(:issue => @issue1,
:project => @project,
:activity => @non_billable_activity,
:spent_on => Date.new(2010,1,2),
:hours => 10,
:user => @manager)
@time_entry2 = TimeEntry.generate!(:issue => @issue1,
:project => @project,
:activity => @non_billable_activity,
:spent_on => Date.new(2010,2,1),
:hours => 20,
:user => @manager)
@rate = Rate.generate!(:project => @project,
:user => @manager,
:date_in_effect => Date.new(2010,1,1),
:amount => 100)
@deliverable.issues << @issue1
end
context "with a empty period" do
should "use all periods" do
assert_equal (10+20) * 100, @deliverable.overhead_spent(nil)
end
end
context "with a period out of the retainer range" do
should "filter the records periods" do
assert_equal 0, @deliverable.overhead_spent(Date.new(2011,1,1))
end
end
context "with an invalid period" do
should "return 0" do
assert_equal 0, @deliverable.overhead_spent('1')
end
end
context "with a period in the retainer range" do
should "filter the records" do
assert_equal 20 * 100, @deliverable.overhead_spent(Date.new(2010,2,1))
end
end
end
context "#overhead_budget_total" do
setup do
@deliverable = RetainerDeliverable.generate!(:start_date => '2010-01-01', :end_date => '2010-03-31')
@@ -160,7 +271,71 @@ class RetainerDeliverableTest < ActiveSupport::TestCase
end
end
# context "#profit_left"
# (Labor used * contract rate) - (labor used * time rate) - (overhead used * time rate)
context "#profit_left" do
setup do
@project = Project.generate!
@contract = Contract.generate!(:billable_rate => 200, :project => @project)
@deliverable = RetainerDeliverable.generate!(:start_date => '2010-01-01', :end_date => '2010-03-31', :contract => @contract)
@deliverable.labor_budgets << LaborBudget.spawn(:budget => 100, :hours => 10)
@deliverable.overhead_budgets << OverheadBudget.spawn(:budget => 100, :hours => 10)
@deliverable.save!
@manager = User.generate!
@role = Role.generate!
User.add_to_project(@manager, @project, @role)
configure_overhead_plugin
@issue1 = Issue.generate_for_project!(@project)
@time_entry1 = TimeEntry.generate!(:issue => @issue1,
:project => @project,
:activity => @billable_activity,
:spent_on => Date.new(2010,1,2),
:hours => 10,
:user => @manager)
@time_entry2 = TimeEntry.generate!(:issue => @issue1,
:project => @project,
:activity => @non_billable_activity,
:spent_on => Date.new(2010,2,1),
:hours => 20,
:user => @manager)
@rate = Rate.generate!(:project => @project,
:user => @manager,
:date_in_effect => Date.new(2010,1,1),
:amount => 100)
@deliverable.issues << @issue1
end
context "with a empty period" do
should "use all periods" do
assert_equal (10 * 200) - (10 * 100) - (20 * 100), @deliverable.profit_left(nil)
end
end
context "with a period out of the retainer range" do
should "filter the records periods" do
assert_equal 0, @deliverable.profit_left(Date.new(2011,1,1))
end
end
context "with an invalid period" do
should "return 0" do
assert_equal 0, @deliverable.profit_left('1')
end
end
context "with a period in the retainer range" do
should "filter the records" do
assert_equal (0 * 200) - (0 * 100) - (20 * 100), @deliverable.profit_left(Date.new(2010,2,1))
end
end
end
context "#profit_budget" do
setup do
@@ -200,8 +375,71 @@ class RetainerDeliverableTest < ActiveSupport::TestCase
end
# context "#total_spent"
context "#total_spent" do
setup do
@project = Project.generate!
@contract = Contract.generate!(:billable_rate => 200, :project => @project)
@deliverable = RetainerDeliverable.generate!(:start_date => '2010-01-01', :end_date => '2010-03-31', :contract => @contract)
@deliverable.labor_budgets << LaborBudget.spawn(:budget => 100, :hours => 10)
@deliverable.overhead_budgets << OverheadBudget.spawn(:budget => 100, :hours => 10)
@deliverable.save!
@manager = User.generate!
@role = Role.generate!
User.add_to_project(@manager, @project, @role)
configure_overhead_plugin
@issue1 = Issue.generate_for_project!(@project)
@time_entry1 = TimeEntry.generate!(:issue => @issue1,
:project => @project,
:activity => @billable_activity,
:spent_on => Date.new(2010,1,2),
:hours => 10,
:user => @manager)
@time_entry2 = TimeEntry.generate!(:issue => @issue1,
:project => @project,
:activity => @billable_activity,
:spent_on => Date.new(2010,2,1),
:hours => 20,
:user => @manager)
@rate = Rate.generate!(:project => @project,
:user => @manager,
:date_in_effect => Date.new(2010,1,1),
:amount => 100)
@deliverable.issues << @issue1
end
context "with a empty period" do
should "use all periods" do
# Labor used * contract rate
assert_equal (10+20) * 200, @deliverable.total_spent(nil)
end
end
context "with a period out of the retainer range" do
should "filter the records periods" do
assert_equal 0, @deliverable.total_spent(Date.new(2011,1,1))
end
end
context "with an invalid period" do
should "return 0" do
assert_equal 0, @deliverable.total_spent('1')
end
end
context "with a period in the retainer range" do
should "filter the records" do
assert_equal 20 * 200, @deliverable.total_spent(Date.new(2010,2,1))
end
end
end
context "#total" do
setup do
@contract = Contract.generate!(:billable_rate => 100)
@@ -238,4 +476,78 @@ class RetainerDeliverableTest < ActiveSupport::TestCase
end
end
context "#fixed_budget_total" do
setup do
@contract = Contract.generate!(:billable_rate => 100)
@deliverable = RetainerDeliverable.generate!(:start_date => '2010-01-01', :end_date => '2010-03-31', :contract => @contract)
@deliverable.fixed_budgets << FixedBudget.spawn(:budget => 1000)
@deliverable.fixed_budgets << FixedBudget.spawn(:budget => 2000)
@deliverable.save!
assert_equal 3000 * 3, @deliverable.fixed_budget_total
end
context "with a empty period" do
should "use all periods" do
assert_equal 9000, @deliverable.fixed_budget_total(nil)
end
end
context "with a period out of the retainer range" do
should "filter the records" do
assert_equal 0, @deliverable.fixed_budget_total(Date.new(2011,1,1))
end
end
context "with an invalid period" do
should "return 0" do
assert_equal 0, @deliverable.fixed_budget_total('1')
end
end
context "with a period in the retainer range" do
should "filter the records" do
assert_equal 3000, @deliverable.fixed_budget_total(Date.new(2010,2,1))
end
end
end
context "#fixed_markup_budget_total" do
setup do
@contract = Contract.generate!(:billable_rate => 100)
@deliverable = RetainerDeliverable.generate!(:start_date => '2010-01-01', :end_date => '2010-03-31', :contract => @contract)
@deliverable.fixed_budgets << FixedBudget.spawn(:budget => 1000, :markup => '50%')
@deliverable.fixed_budgets << FixedBudget.spawn(:budget => 2000, :markup => '$1000')
@deliverable.save!
assert_equal (500 + 1000) * 3, @deliverable.fixed_markup_budget_total
end
context "with a empty period" do
should "use all periods" do
assert_equal 4500, @deliverable.fixed_markup_budget_total(nil)
end
end
context "with a period out of the retainer range" do
should "filter the records" do
assert_equal 0, @deliverable.fixed_markup_budget_total(Date.new(2011,1,1))
end
end
context "with an invalid period" do
should "return 0" do
assert_equal 0, @deliverable.fixed_markup_budget_total('1')
end
end
context "with a period in the retainer range" do
should "filter the records" do
assert_equal 1500, @deliverable.fixed_markup_budget_total(Date.new(2010,2,1))
end
end
end
end