Merge branch '4477-fixed-expenses'

This commit is contained in:
Eric Davis
2010-09-22 11:54:41 -07:00
24 changed files with 751 additions and 64 deletions
+1
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
+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)
+2 -2
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,7 +104,7 @@
<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, deliverable.fixed_budget_total_spent, deliverable.fixed_budget_total, :class => 'fixed') %>
<% end %>
<tr id="deliverable_details_<%= h(deliverable.id) %>" class="ign">
+18 -2
View File
@@ -62,8 +62,24 @@
<td class="overhead_budget_total"><%= h(format_value_field_for_contracts(deliverable.overhead_budget_total(validated_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"><%= h(format_value_field_for_contracts(fixed_budget.budget_spent)) %></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"><%= h(format_value_field_for_contracts(deliverable.fixed_markup_budget_total_spent(period))) %></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(validated_period))) %></td>
@@ -62,6 +62,46 @@
</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">
<%= fixed_budget.label(:paid, l(:field_paid), :style => 'display: none;') %><%# Hidden label :| %>
<%= l(:field_paid) %>
</p>
<%= fixed_budget.check_box(:paid) %>
<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>
+5 -3
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"
@@ -70,4 +70,6 @@ en:
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
field_paid: Paid
+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
@@ -0,0 +1,10 @@
class AddPaidToFixedBudgets < ActiveRecord::Migration
def self.up
add_column :fixed_budgets, :paid, :boolean
add_index :fixed_budgets, :paid
end
def self.down
remove_column :fixed_budgets, :paid
end
end
+73
View File
@@ -210,6 +210,79 @@ 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 "show the total fixed budget spent for a Deliverable" do
@manager = User.generate!
@deliverable1 = FixedDeliverable.generate!(:contract => @contract, :manager => @manager)
FixedBudget.generate!(:deliverable => @deliverable1, :budget => '$1,000', :markup => '$100', :paid => true)
FixedBudget.generate!(:deliverable => @deliverable1, :budget => '$2,000', :markup => '200%')
visit_contract_page(@contract)
assert_select "table#deliverables" do
assert_select "td.fixed.spent-amount", :text => /1,000/
end
end
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', :paid => true)
@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 => '1,000'
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', :paid => true)
@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 => '100'
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)
+74 -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,17 @@ class DeliverablesEditTest < ActionController::IntegrationTest
# * overhead hidden month
# * overhead hours
# * overhead amount
# * fixed hidden year
# * fixed hidden month
# * fixed title
# * fixed budget
# * fixed markup
# * fixed paid checkbox
# * fixed paid hidden field
# * total (hidden)
assert_select ".date-2010-01" do
assert_select "input", :count => 9
assert_select "input", :count => 16
assert_select "textarea.wiki-edit", :count => 1 # Fixed description
end
@@ -371,6 +425,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 +448,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
+53 -2
View File
@@ -247,11 +247,62 @@ class ContractTest < ActiveSupport::TestCase
TimeEntry.generate!(:hours => 4, :issue => @issue2, :project => @project,
:activity => @non_billable_activity,
:user => @manager)
@deliverable_2.fixed_budgets << FixedBudget.spawn(:budget => 200, :markup => '$100', :paid => true)
assert_equal 875, @deliverable_1.profit_left
assert_equal 1125, @deliverable_2.profit_left
assert_equal 2000, contract.profit_left
assert_equal 825, @deliverable_2.profit_left
assert_equal 1700, contract.profit_left
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 "sum all fixed budget amounts on the Deliverables which are paid" do
contract = Contract.generate!
contract.deliverables << @deliverable_1 = FixedDeliverable.generate!
FixedBudget.generate!(:deliverable => @deliverable_1, :budget => '$1,000', :paid => true)
contract.deliverables << @deliverable_2 = HourlyDeliverable.generate!
FixedBudget.generate!(:deliverable => @deliverable_2, :budget => '$2,000')
assert_equal 1000, contract.fixed_spent
end
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 "sum all fixed budget markup values on the Deliverables which are paid" do
contract = Contract.generate!
contract.deliverables << @deliverable_1 = FixedDeliverable.generate!
FixedBudget.generate!(:deliverable => @deliverable_1, :budget => '$1,000', :markup => '$100', :paid => true)
contract.deliverables << @deliverable_2 = HourlyDeliverable.generate!
FixedBudget.generate!(:deliverable => @deliverable_2, :budget => '$2,000', :markup => '200%')
assert_equal (100) + (0), contract.fixed_markup_spent
end
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
+148
View File
@@ -476,4 +476,152 @@ 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_budget_total_spent" 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, :paid => true)
@deliverable.fixed_budgets << FixedBudget.spawn(:budget => 2000)
@deliverable.save!
assert_equal 1000 * 3, @deliverable.fixed_budget_total_spent
end
context "with a empty period" do
should "use all periods" do
assert_equal 3000, @deliverable.fixed_budget_total_spent(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_spent(Date.new(2011,1,1))
end
end
context "with an invalid period" do
should "return 0" do
assert_equal 0, @deliverable.fixed_budget_total_spent('1')
end
end
context "with a period in the retainer range" do
should "filter the records" do
assert_equal 1000, @deliverable.fixed_budget_total_spent(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
context "#fixed_markup_budget_total_spent" 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%', :paid => true)
@deliverable.fixed_budgets << FixedBudget.spawn(:budget => 2000, :markup => '$1000')
@deliverable.save!
assert_equal (500) * 3, @deliverable.fixed_markup_budget_total_spent
end
context "with a empty period" do
should "use all periods" do
assert_equal 1500, @deliverable.fixed_markup_budget_total_spent(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_spent(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_spent('1')
end
end
context "with a period in the retainer range" do
should "filter the records" do
assert_equal 500, @deliverable.fixed_markup_budget_total_spent(Date.new(2010,2,1))
end
end
end
end