Compare commits
43
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
394236c741 | ||
|
|
82dec74eb7 | ||
|
|
c8b86db4c4 | ||
|
|
0cc2523084 | ||
|
|
7ed21f7fe2 | ||
|
|
ad6766fd2f | ||
|
|
d3052d7d54 | ||
|
|
9f8f1238aa | ||
|
|
df53f27114 | ||
|
|
6a0c6348ed | ||
|
|
dfab7b5714 | ||
|
|
abc4d30346 | ||
|
|
994e9b0332 | ||
|
|
e5853100d5 | ||
|
|
918561d667 | ||
|
|
ede645e359 | ||
|
|
9858b87fff | ||
|
|
0a33cdefbb | ||
|
|
8f92dcc870 | ||
|
|
cb27ae2cc8 | ||
|
|
57d3830084 | ||
|
|
f46d222f4a | ||
|
|
2969afeaf4 | ||
|
|
bd3cd89dd0 | ||
|
|
4b12d9d9c8 | ||
|
|
16f8152599 | ||
|
|
c12a33e25d | ||
|
|
0c38fe2c37 | ||
|
|
d48addbcb0 | ||
|
|
c13f31b1b5 | ||
|
|
c3c472b3b1 | ||
|
|
40d4029cd1 | ||
|
|
9a4b4f2b43 | ||
|
|
fc28f8351f | ||
|
|
7d43ab7d74 | ||
|
|
f899b4e623 | ||
|
|
4b162f85bf | ||
|
|
1120b39ed6 | ||
|
|
7d19c10714 | ||
|
|
3e30629d44 | ||
|
|
8321903598 | ||
|
|
f18d259c3c | ||
|
|
9d52bcfbe2 |
@@ -1 +1,2 @@
|
||||
webrat*
|
||||
tmp/
|
||||
|
||||
@@ -14,7 +14,7 @@ class DeliverablesController < InheritedResources::Base
|
||||
|
||||
def create
|
||||
@deliverable = begin_of_association_chain.deliverables.build(params[:deliverable])
|
||||
if params[:deliverable] && params[:deliverable][:type] && ['FixedDeliverable','HourlyDeliverable'].include?(params[:deliverable][:type])
|
||||
if params[:deliverable] && params[:deliverable][:type] && Deliverable.valid_types.include?(params[:deliverable][:type])
|
||||
@deliverable.type = params[:deliverable][:type]
|
||||
end
|
||||
create! { contract_url(@project, @contract) }
|
||||
@@ -22,12 +22,17 @@ class DeliverablesController < InheritedResources::Base
|
||||
|
||||
def update
|
||||
@deliverable = begin_of_association_chain.deliverables.find_by_id(params[:id])
|
||||
params[:deliverable] = params[:fixed_deliverable] || params[:hourly_deliverable]
|
||||
params[:deliverable] = params[:fixed_deliverable] || params[:hourly_deliverable] || params[:retainer_deliverable]
|
||||
update! { contract_url(@project, @contract) }
|
||||
end
|
||||
|
||||
def show
|
||||
redirect_to contract_url(@project, @contract)
|
||||
if show_partial?
|
||||
@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)
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@@ -39,6 +44,11 @@ class DeliverablesController < InheritedResources::Base
|
||||
def begin_of_association_chain
|
||||
@contract
|
||||
end
|
||||
|
||||
# Is only a partial requested?
|
||||
def show_partial?
|
||||
params[:format] == 'js' && params[:as] == 'deliverable_details_row'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
||||
@@ -85,4 +85,15 @@ module ContractsHelper
|
||||
def format_value_field_for_contracts(value)
|
||||
number_with_precision(value, :precision => Contract::ViewPrecision, :delimiter => ',')
|
||||
end
|
||||
|
||||
def retainer_period_options(deliverable)
|
||||
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"))
|
||||
end
|
||||
|
||||
options
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,6 +22,16 @@ class Deliverable < ActiveRecord::Base
|
||||
|
||||
delegate :name, :to => :contract, :prefix => true, :allow_nil => true
|
||||
|
||||
# Callbacks
|
||||
|
||||
# Register callbacks here, on new records the class isn't set so class-specific
|
||||
# callbacks don't fire.
|
||||
def after_save
|
||||
if type == "RetainerDeliverable"
|
||||
self.becomes(self.type.constantize).create_budgets_for_periods
|
||||
end
|
||||
end
|
||||
|
||||
named_scope :by_title, {:order => "#{Deliverable.table_name}.title ASC"}
|
||||
|
||||
def short_type
|
||||
@@ -44,14 +54,22 @@ class Deliverable < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def labor_budget_total
|
||||
def labor_budget_total(date=nil)
|
||||
labor_budgets.sum(:budget)
|
||||
end
|
||||
|
||||
def overhead_budget_total
|
||||
def overhead_budget_total(date=nil)
|
||||
overhead_budgets.sum(:budget)
|
||||
end
|
||||
|
||||
def profit_budget(date=nil)
|
||||
nil
|
||||
end
|
||||
|
||||
def labor_budget_hours(date=nil)
|
||||
labor_budgets.sum(:hours)
|
||||
end
|
||||
|
||||
# Total number of hours estimated in the Deliverable's budgets
|
||||
def estimated_hour_budget_total
|
||||
(labor_budgets.sum(:hours) || 0.0) +
|
||||
@@ -63,11 +81,56 @@ class Deliverable < ActiveRecord::Base
|
||||
issues.inject(0) {|total, issue| total += issue.spent_hours }
|
||||
end
|
||||
|
||||
# Wrapper for the old Budget plugins' API
|
||||
def filter_by_date(date=nil, &block)
|
||||
block.call
|
||||
end
|
||||
|
||||
def retainer?
|
||||
type == "RetainerDeliverable"
|
||||
end
|
||||
|
||||
def self.valid_types
|
||||
['FixedDeliverable','HourlyDeliverable','RetainerDeliverable']
|
||||
end
|
||||
|
||||
def self.valid_types_to_select
|
||||
valid_types.inject([]) do |types, type|
|
||||
types << [type.gsub(/Deliverable/i,''), type]
|
||||
types
|
||||
end
|
||||
end
|
||||
|
||||
# Accessors from the budget plugin that need to be wrapped
|
||||
def subject
|
||||
warn "[DEPRECATION] Deliverable#subject is deprecated. Please use Deliverable#title instead."
|
||||
title
|
||||
end
|
||||
|
||||
def due
|
||||
warn "[DEPRECATION] Deliverable#due is deprecated. Please use Deliverable#end_date instead."
|
||||
end_date
|
||||
end
|
||||
|
||||
def hours_used
|
||||
warn "[DEPRECATION] Deliverable#hours_used is deprecated. Please use Deliverable#hours_spent_total instead."
|
||||
hours_spent_total
|
||||
end
|
||||
|
||||
def spent
|
||||
warn "[DEPRECATION] Deliverable#spent is deprecated. Please use Deliverable#total_spent instead."
|
||||
total_spent
|
||||
end
|
||||
|
||||
def total_hours
|
||||
warn "[DEPRECATION] Deliverable#total_hours is deprecated. Please use Deliverable#estimated_hour_budget_total instead."
|
||||
estimated_hour_budget_total
|
||||
end
|
||||
|
||||
def labor_budget
|
||||
warn "[DEPRECATION] Deliverable#labor_budget is deprecated. Please use Deliverable#labor_budget_total instead."
|
||||
labor_budget_total
|
||||
end
|
||||
|
||||
if Rails.env.test?
|
||||
generator_for :title, :method => :next_title
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ class FixedDeliverable < Deliverable
|
||||
'F'
|
||||
end
|
||||
|
||||
def total
|
||||
def total(date=nil)
|
||||
read_attribute(:total) || 0.0
|
||||
end
|
||||
|
||||
@@ -22,9 +22,9 @@ class FixedDeliverable < Deliverable
|
||||
|
||||
# The amount of profit that is budgeted for this deliverable.
|
||||
# Profit = Total - ( Labor + Overhead + Fixed + Markup )
|
||||
def profit_budget
|
||||
budgets = labor_budget_total + overhead_budget_total
|
||||
(total || 0.0) - budgets
|
||||
def profit_budget(date=nil)
|
||||
budgets = labor_budget_total(date) + overhead_budget_total(date)
|
||||
(total(date) || 0.0) - budgets
|
||||
end
|
||||
|
||||
# The amount of money remaining after expenses have been taken out
|
||||
|
||||
@@ -14,12 +14,12 @@ class HourlyDeliverable < Deliverable
|
||||
'H'
|
||||
end
|
||||
|
||||
def total
|
||||
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_budgets.sum(:hours)
|
||||
return contract.billable_rate * labor_budget_hours(date)
|
||||
end
|
||||
|
||||
# Total amount to be billed on the deliverable, using the total time logged
|
||||
@@ -49,9 +49,9 @@ class HourlyDeliverable < Deliverable
|
||||
|
||||
# The amount of profit that is budgeted for this deliverable
|
||||
# Profit = Total - ( Labor + Overhead + Fixed + Markup )
|
||||
def profit_budget
|
||||
budgets = labor_budget_total + overhead_budget_total
|
||||
(total || 0.0) - budgets
|
||||
def profit_budget(date=nil)
|
||||
budgets = labor_budget_total(date) + overhead_budget_total(date)
|
||||
(total(date) || 0.0) - budgets
|
||||
end
|
||||
|
||||
# The amount of money remaining after expenses have been taken out
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
# A RetainerDeliverable is an HourlyDeliverable that is renewed at
|
||||
# regular calendar periods. The Company bills a regular number of
|
||||
# hours for a hourly rate whereby the budgets are reset over a
|
||||
# regular cyclical period (monthly).
|
||||
class RetainerDeliverable < HourlyDeliverable
|
||||
unloadable
|
||||
|
||||
# Associations
|
||||
|
||||
# Validations
|
||||
|
||||
# Accessors
|
||||
|
||||
# Callbacks
|
||||
before_update :check_for_extended_period
|
||||
before_update :check_for_shrunk_period
|
||||
|
||||
def short_type
|
||||
'R'
|
||||
end
|
||||
|
||||
def current_period
|
||||
Date.today.strftime("%B %Y")
|
||||
end
|
||||
|
||||
def beginning_date
|
||||
start_date && start_date.beginning_of_month.to_date
|
||||
end
|
||||
|
||||
def ending_date
|
||||
end_date && end_date.end_of_month.to_date
|
||||
end
|
||||
|
||||
def date_range
|
||||
(beginning_date..ending_date)
|
||||
end
|
||||
|
||||
def within_date_range?(date)
|
||||
date_range.include?(date)
|
||||
end
|
||||
|
||||
def months
|
||||
month_acc = []
|
||||
|
||||
current_date = beginning_date
|
||||
return [] if current_date.nil? || ending_date.nil?
|
||||
|
||||
while current_date < ending_date do
|
||||
month_acc << current_date
|
||||
current_date = current_date.advance(:months => 1)
|
||||
end
|
||||
|
||||
month_acc
|
||||
end
|
||||
|
||||
# Returns the months used by the Deliverable that are before date
|
||||
def months_before_date(date)
|
||||
months.select {|m| m < date }
|
||||
end
|
||||
|
||||
# Returns the months used by the Deliverable that are after date
|
||||
def months_after_date(date)
|
||||
months.select {|m| m > date }
|
||||
end
|
||||
|
||||
def labor_budgets_for_date(date)
|
||||
budgets = labor_budgets.all(:conditions => {:year => date.year, :month => date.month})
|
||||
budgets = [labor_budgets.build(:year => date.year, :month => date.month)] if budgets.empty?
|
||||
budgets
|
||||
end
|
||||
|
||||
def overhead_budgets_for_date(date)
|
||||
budgets = overhead_budgets.all(:conditions => {:year => date.year, :month => date.month})
|
||||
budgets = [overhead_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
|
||||
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
|
||||
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
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def create_budgets_for_periods
|
||||
# For each month in the time span
|
||||
months.each do |month|
|
||||
# Iterate over all un-dated budgets, created dated versions
|
||||
undated_labor_budgets = labor_budgets.all(:conditions => ["#{LaborBudget.table_name}.year IS NULL AND #{LaborBudget.table_name}.month IS NULL"])
|
||||
undated_labor_budgets.each do |template_budget|
|
||||
labor_budgets.create(template_budget.attributes.merge(:year => month.year, :month => month.month))
|
||||
end
|
||||
|
||||
undated_overhead_budgets = overhead_budgets.all(:conditions => ["#{OverheadBudget.table_name}.year IS NULL AND #{OverheadBudget.table_name}.month IS NULL"])
|
||||
undated_overhead_budgets.each do |template_budget|
|
||||
overhead_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)
|
||||
end
|
||||
|
||||
def check_for_extended_period
|
||||
# TODO: brute force. Alternative would be to check end_date_changes to see if the period actually shifted
|
||||
if end_date_changed?
|
||||
extend_period_to_new_end_date
|
||||
end
|
||||
|
||||
# TODO: brute force. Alternative would be to check start_date_changes to see if the period actually shifted
|
||||
if start_date_changed?
|
||||
extend_period_to_new_start_date
|
||||
end
|
||||
end
|
||||
|
||||
def check_for_shrunk_period
|
||||
if end_date_changed? || start_date_changed?
|
||||
shrink_budgets_to_new_period
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def shrink_budgets_to_new_period
|
||||
return if beginning_date.nil? || ending_date.nil?
|
||||
labor_budgets.all.each do |labor_budget|
|
||||
# Purge un-dated budgets, should not be saved at all
|
||||
labor_budget.destroy unless labor_budget.year.present?
|
||||
labor_budget.destroy unless labor_budget.month.present?
|
||||
|
||||
# Purge budgets outside the new beginning/ending range
|
||||
unless (beginning_date..ending_date).to_a.include?(Date.new(labor_budget.year, labor_budget.month, 1))
|
||||
labor_budget.destroy
|
||||
end
|
||||
end
|
||||
|
||||
overhead_budgets.all.each do |overhead_budget|
|
||||
# Purge un-dated budgets, should not be saved at all
|
||||
overhead_budget.destroy unless overhead_budget.year.present?
|
||||
overhead_budget.destroy unless overhead_budget.month.present?
|
||||
|
||||
# Purge budgets outside the new beginning/ending range
|
||||
unless (beginning_date..ending_date).to_a.include?(Date.new(overhead_budget.year, overhead_budget.month, 1))
|
||||
overhead_budget.destroy
|
||||
end
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def extend_period_to_new_end_date
|
||||
return if end_date_change[0].nil? # No previous end date, so it will not have budgets
|
||||
|
||||
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})
|
||||
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
||||
def extend_period_to_new_start_date
|
||||
return if start_date_change[0].nil? # No previous start date, so it will not have budgets
|
||||
|
||||
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})
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def create_budgets_for_new_period(new_period, labor_budgets_to_copy, overhead_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
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
def create_new_labor_budget_based_on_existing_budget(existing_labor_budget, attributes={})
|
||||
labor_budgets.create(existing_labor_budget.attributes.except('id').merge(attributes))
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
@@ -108,104 +108,7 @@
|
||||
<% end %>
|
||||
|
||||
<tr id="deliverable_details_<%= h(deliverable.id) %>" class="ign">
|
||||
<td colspan="11">
|
||||
|
||||
<div class="expanded">
|
||||
<div class="info">
|
||||
<div class="title">
|
||||
<%= link_to(l(:button_edit), edit_contract_deliverable_path(@project, resource, deliverable), :class => 'icon icon-edit') %>
|
||||
<%= link_to(l(:button_delete), contract_deliverable_path(@project, resource, deliverable), :method => :delete, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') %>
|
||||
</div>
|
||||
|
||||
<%= textilizable(deliverable, :notes) %>
|
||||
|
||||
<table>
|
||||
<%= show_field(deliverable, :start_date, :format => :format_date, :html_options => {:class => 'deliverable-start-date'}) %>
|
||||
<%= show_field(deliverable, :end_date, :format => :format_date, :html_options => {:class => 'deliverable-end-date'}) %>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<%# TODO: Release 2, skinning port %>
|
||||
<div class="finance">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th>Spent</th>
|
||||
<th>Budget</th>
|
||||
<th>Hours</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr class="fill">
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="l"><a href="#"><strong>Labor</strong></a></td>
|
||||
<td><%= h(format_value_field_for_contracts(deliverable.labor_budget_spent)) %></td>
|
||||
<td><%= h(format_value_field_for_contracts(deliverable.labor_budget_total)) %></td>
|
||||
<td> TODO: Release 2 / TODO hrs </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="l"><a href="#"><strong>Overhead</strong></a></td>
|
||||
<td><%= h(format_value_field_for_contracts(deliverable.overhead_spent)) %></td>
|
||||
<td><%= h(format_value_field_for_contracts(deliverable.overhead_budget_total)) %></td>
|
||||
<td> TODO: Release 2 / TODO hrs </td>
|
||||
</tr>
|
||||
<%# TODO: Release 2, Fixed %>
|
||||
<%# TODO: Release 2, Markup %>
|
||||
<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></td>
|
||||
</tr>
|
||||
<tr class="total">
|
||||
<td class="l"><strong>Total:</strong></td>
|
||||
<td><strong><%= h(format_value_field_for_contracts(deliverable.total_spent)) %></strong></td>
|
||||
<td><strong><%= h(format_value_field_for_contracts(deliverable.total)) %></strong></td>
|
||||
<td><strong>TODO: Release 2</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="issue_status">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2"><%= l(:label_issue_status_plural) %></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<%# TODO: Release 2 Issue status counters
|
||||
<tr>
|
||||
<td>Proposed</td>
|
||||
<td class="number">1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>On Hold</td>
|
||||
<td class="number">1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Complete</td>
|
||||
<td class="number">14</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>All</strong></td>
|
||||
<td class="number">16</td>
|
||||
</tr>
|
||||
%>
|
||||
</table>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</td>
|
||||
<%= render :partial => 'deliverables/details_row', :locals => {:deliverable => deliverable, :contract => resource} %>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
<% period ||= '' %>
|
||||
|
||||
<td colspan="11" class="deliverable_details_outer_wrapper_<%= h(deliverable.id) %>">
|
||||
|
||||
<div class="expanded">
|
||||
|
||||
<div class="info">
|
||||
<div class="title">
|
||||
<%= link_to(l(:button_edit), edit_contract_deliverable_path(@project, contract, deliverable), :class => 'icon icon-edit') %>
|
||||
<%= link_to(l(:button_delete), contract_deliverable_path(@project, contract, deliverable), :method => :delete, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') %>
|
||||
</div>
|
||||
|
||||
<%= textilizable(deliverable, :notes) %>
|
||||
|
||||
<% if deliverable.retainer? %>
|
||||
<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) %>
|
||||
</select>
|
||||
</fieldset>
|
||||
</form>
|
||||
<% end %>
|
||||
|
||||
<table>
|
||||
<%= show_field(deliverable, :current_period, :html_options => {:class => 'deliverable-current-period'}) if deliverable.retainer? %>
|
||||
<%= show_field(deliverable, :start_date, :format => :format_date, :html_options => {:class => 'deliverable-start-date'}) %>
|
||||
<%= show_field(deliverable, :end_date, :format => :format_date, :html_options => {:class => 'deliverable-end-date'}) %>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<%# TODO: Release 2, skinning port %>
|
||||
<div class="finance">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th>Spent</th>
|
||||
<th>Budget</th>
|
||||
<th>Hours</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr class="fill">
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<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> 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> TODO: Release 2 / TODO hrs </td>
|
||||
</tr>
|
||||
<%# TODO: Release 2, Fixed %>
|
||||
<%# TODO: Release 2, Markup %>
|
||||
<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></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><strong>TODO: Release 2</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="issue_status">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2"><%= l(:label_issue_status_plural) %></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<%# TODO: Release 2 Issue status counters
|
||||
<tr>
|
||||
<td>Proposed</td>
|
||||
<td class="number">1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>On Hold</td>
|
||||
<td class="number">1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Complete</td>
|
||||
<td class="number">14</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>All</strong></td>
|
||||
<td class="number">16</td>
|
||||
</tr>
|
||||
%>
|
||||
</table>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<% form.inputs :name => label, :class => "deliverable-finances #{fieldset_class}" do %>
|
||||
|
||||
<li class="numeric optional">
|
||||
<%= content_tag(:label, l(:field_labor)) %>
|
||||
<table id="deliverable-labor" class="deliverable_finance_table">
|
||||
|
||||
<% form.fields_for :labor_budgets, labor_budgets do |labor_budget| %>
|
||||
<%= labor_budget.hidden_field(:year) %>
|
||||
<%= labor_budget.hidden_field(:month) %>
|
||||
<tr>
|
||||
<%# TODO: Select field for the Time Entry Activity in a td %>
|
||||
<td>
|
||||
<select><option>TODO: Release 3</option></select>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= labor_budget.label(:hours, l(:text_short_hours)) %></p>
|
||||
<%= labor_budget.text_field(:hours, :value => format_deliverable_value_fields(labor_budget.object.hours), :size => 10) %>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= labor_budget.label(:budget, l(:text_dollar_sign)) %></p>
|
||||
<%= labor_budget.text_field(:budget, :value => format_deliverable_value_fields(labor_budget.object.budget), :size => 10) %>
|
||||
</td>
|
||||
<%# TODO: Green Add button for multiple records %>
|
||||
<td>
|
||||
Todo: Add button (Release 3)
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
</li>
|
||||
|
||||
<li class="numeric optional">
|
||||
<%= content_tag(:label, l(:field_overhead)) %>
|
||||
<table id="deliverable-overhead" class="deliverable_finance_table">
|
||||
|
||||
<% form.fields_for :overhead_budgets, overhead_budgets do |overhead_budget| %>
|
||||
<%= overhead_budget.hidden_field(:year) %>
|
||||
<%= overhead_budget.hidden_field(:month) %>
|
||||
<tr>
|
||||
<%# TODO: Select field for the Time Entry Activity in a td %>
|
||||
<td>
|
||||
<select><option>TODO: Release 3</option></select>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= overhead_budget.label(:hours, l(:text_short_hours)) %></p>
|
||||
<%= overhead_budget.text_field(:hours, :value => format_deliverable_value_fields(overhead_budget.object.hours),:size => 10) %>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= overhead_budget.label(:budget, l(:text_dollar_sign)) %></p>
|
||||
<%= overhead_budget.text_field(:budget, :value => format_deliverable_value_fields(overhead_budget.object.budget), :size => 10) %>
|
||||
</td>
|
||||
<%# TODO: Green Add button for multiple records %>
|
||||
<td>
|
||||
Todo: Add button (Release 3)
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
</li>
|
||||
|
||||
<%= form.input :total, :input_html => {:size => 10}, :wrapper_html => {:class => 'deliverable_total_input'}, :hint => l(:text_dollar_sign) %>
|
||||
|
||||
<% end %>
|
||||
@@ -1,14 +1,28 @@
|
||||
<%= javascript_tag("var i18nStartDateEmpty = '#{l(:text_start_date_empty)}'") %>
|
||||
<%= javascript_tag("var i18nEndDateEmpty = '#{l(:text_end_date_empty)}'") %>
|
||||
<%= javascript_tag("var i18nChangedPeriodMessage = '#{l(:text_changed_period_message)}'") %>
|
||||
|
||||
<div class="box tabular">
|
||||
<% form.inputs :name => l(:text_deliverable_details_legend) 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 %>
|
||||
<% if resource.new_record? %>
|
||||
<%= form.input(:type, :required => true, :as => :select, :collection => [["Fixed", "FixedDeliverable"],["Hourly", "HourlyDeliverable"]], :include_blank => false, :input_html => {:class => 'type'}) %>
|
||||
<li class="select required" id="deliverable_type_input">
|
||||
<%= form.label(:type, l(:field_type)) %>
|
||||
<%= form.select(:type, Deliverable.valid_types_to_select, {:include_blank => false}, {:class => 'type'}) %>
|
||||
</li>
|
||||
<% else %>
|
||||
<%= form.input :type, :as => :hidden, :class => 'type' %>
|
||||
<% end %>
|
||||
<%= form.input :manager, :required => true, :collection => @project.users.sort %>
|
||||
<%= form.input :start_date, :as => :string, :input_html => {:size => 10}, :hint => calendar_for('deliverable_start_date') %>
|
||||
<%= form.input :end_date, :as => :string, :input_html => {:size => 10}, :hint => calendar_for('deliverable_end_date') %>
|
||||
|
||||
<%= form.input :start_date, :as => :string, :input_html => {:size => 10, :class => 'start-date'}, :hint => calendar_for('deliverable_start_date') %>
|
||||
<%= hidden_field_tag('deliverable_stored_start_date', h(resource.start_date), :id => 'deliverable_stored_start_date') %>
|
||||
|
||||
<%= form.input :end_date, :as => :string, :input_html => {:size => 10, :class => 'end-date'}, :hint => calendar_for('deliverable_end_date') %>
|
||||
<%= hidden_field_tag('deliverable_stored_end_date', h(resource.end_date), :id => 'deliverable_stored_end_date') %>
|
||||
|
||||
<%= form.input :notes, :input_html => {:class => 'wiki-edit', :rows => '5'} %>
|
||||
|
||||
<% unless resource.new_record? %>
|
||||
@@ -23,64 +37,16 @@
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% form.inputs :name => l(:text_deliverable_finances), :id => 'deliverable-finances' do %>
|
||||
|
||||
<li class="numeric optional">
|
||||
<%= content_tag(:label, l(:field_labor)) %>
|
||||
<table id="deliverable-labor" class="deliverable_finance_table">
|
||||
|
||||
<% form.fields_for :labor_budgets do |labor_budget| %>
|
||||
<tr>
|
||||
<%# TODO: Select field for the Time Entry Activity in a td %>
|
||||
<td>
|
||||
<select><option>TODO: Release 3</option></select>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= labor_budget.label(:hours, l(:text_short_hours)) %></p>
|
||||
<%= labor_budget.text_field(:hours, :value => format_deliverable_value_fields(labor_budget.object.hours), :size => 10) %>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= labor_budget.label(:budget, l(:text_dollar_sign)) %></p>
|
||||
<%= labor_budget.text_field(:budget, :value => format_deliverable_value_fields(labor_budget.object.budget), :size => 10) %>
|
||||
</td>
|
||||
<%# TODO: Green Add button for multiple records %>
|
||||
<td>
|
||||
Todo: Add button (Release 3)
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
</li>
|
||||
|
||||
<li class="numeric optional">
|
||||
<%= content_tag(:label, l(:field_overhead)) %>
|
||||
<table id="deliverable-overhead" class="deliverable_finance_table">
|
||||
|
||||
<% form.fields_for :overhead_budgets do |overhead_budget| %>
|
||||
<tr>
|
||||
<%# TODO: Select field for the Time Entry Activity in a td %>
|
||||
<td>
|
||||
<select><option>TODO: Release 3</option></select>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= overhead_budget.label(:hours, l(:text_short_hours)) %></p>
|
||||
<%= overhead_budget.text_field(:hours, :value => format_deliverable_value_fields(overhead_budget.object.hours),:size => 10) %>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inline-hints"><%= overhead_budget.label(:budget, l(:text_dollar_sign)) %></p>
|
||||
<%= overhead_budget.text_field(:budget, :value => format_deliverable_value_fields(overhead_budget.object.budget), :size => 10) %>
|
||||
</td>
|
||||
<%# TODO: Green Add button for multiple records %>
|
||||
<td>
|
||||
Todo: Add button (Release 3)
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
</li>
|
||||
|
||||
<%= form.input :total, :input_html => {:size => 10}, :wrapper_html => {:class => 'deliverable_total_input'}, :hint => l(:text_dollar_sign) %>
|
||||
|
||||
<% 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') } %>
|
||||
<% 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 => '' } %>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
jQuery(function($) {
|
||||
$("#ajax-indicator").ajaxStart(function(){ $(this).show(); });
|
||||
$("#ajax-indicator").ajaxStop(function(){ $(this).hide(); });
|
||||
|
||||
var right_align = $('#contract-terms .finance tr td:nth-child ~ td, .c_overview table.right tr td:nth-child ~ td, #deliverables table tr.click td:nth-child(5) ~ td, .deliverable_finance_table tr.aright td:nth-child ~ td');
|
||||
|
||||
@@ -22,16 +24,37 @@ jQuery(function($) {
|
||||
$(this).toggleClass('alt');
|
||||
});
|
||||
|
||||
showDeliverableTotal = function() {
|
||||
$('.deliverable_total_input').show();
|
||||
},
|
||||
|
||||
hideDeliverableTotal = function() {
|
||||
$('.deliverable_total_input').
|
||||
children('input').val('').end().
|
||||
hide();
|
||||
},
|
||||
|
||||
showDeliverableFrequency = function() {
|
||||
$('#deliverable_frequency').show();
|
||||
},
|
||||
|
||||
hideDeliverableFrequency = function() {
|
||||
$('#deliverable_frequency').hide();
|
||||
},
|
||||
|
||||
toggleSpecificDeliverableFields = function(form) {
|
||||
var deliverableType = form.find('.type').val();
|
||||
|
||||
if (deliverableType == 'FixedDeliverable') {
|
||||
$('.deliverable_total_input').show();
|
||||
} else {
|
||||
$('.deliverable_total_input').
|
||||
children('input').val('').end().
|
||||
hide();
|
||||
}
|
||||
showDeliverableTotal();
|
||||
hideDeliverableFrequency();
|
||||
} else if(deliverableType == "HourlyDeliverable") {
|
||||
hideDeliverableTotal();
|
||||
hideDeliverableFrequency();
|
||||
} else if(deliverableType == "RetainerDeliverable") {
|
||||
hideDeliverableTotal();
|
||||
showDeliverableFrequency();
|
||||
}
|
||||
},
|
||||
|
||||
toggleSpecificDeliverableFields($('form.deliverable'));
|
||||
@@ -39,6 +62,34 @@ jQuery(function($) {
|
||||
$('select#deliverable_type').change(function() {
|
||||
toggleSpecificDeliverableFields($('form.deliverable'));
|
||||
});
|
||||
|
||||
$('form.deliverable').submit(function() {
|
||||
var deliverableType = $('form.deliverable').find('.type').val();
|
||||
|
||||
if (deliverableType == 'RetainerDeliverable') {
|
||||
if ($('form.deliverable .start-date[value!=""]').length == 0) {
|
||||
return confirm(i18nStartDateEmpty);
|
||||
}
|
||||
if ($('form.deliverable .end-date[value!=""]').length == 0) {
|
||||
return confirm(i18nEndDateEmpty);
|
||||
}
|
||||
|
||||
if ($('form.deliverable #deliverable_stored_id').val() != '') {
|
||||
if ($('form.deliverable .start-date').val() != $('#deliverable_stored_start_date').val()) {
|
||||
return confirm(i18nChangedPeriodMessage);
|
||||
}
|
||||
if ($('form.deliverable .end-date').val() != $('#deliverable_stored_end_date').val()) {
|
||||
return confirm(i18nChangedPeriodMessage);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
$('select.retainer_period_change').live('change', function() {
|
||||
var deliverable_url = $(this).closest('form').attr('action');
|
||||
$(this).closest('tr').load(deliverable_url, this.form.serialize());
|
||||
});
|
||||
});
|
||||
|
||||
/* Jquery Table Expander Plugin */
|
||||
|
||||
@@ -28,6 +28,7 @@ en:
|
||||
field_feature_sign_off: Feature Sign Off
|
||||
field_warranty_sign_off: Warranty Sign Off
|
||||
text_deliverable_finances: Deliverable Finances
|
||||
text_deliverable_finances_date: "Deliverable Finances - {{date}}"
|
||||
text_short_hours: hrs
|
||||
text_dollar_sign: '$'
|
||||
field_client_point_of_contact: "Point of Contact"
|
||||
@@ -60,3 +61,8 @@ en:
|
||||
field_deliverable_title: "Deliverable"
|
||||
field_contract_name: "Contract"
|
||||
field_contract: "Contract"
|
||||
text_start_date_empty: "The start date is empty. If this form is submitted, no budget items will be created."
|
||||
text_end_date_empty: "The end date is empty. If this form is submitted, no budget items will be created."
|
||||
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"
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class AddFrequencyToDeliverables < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :deliverables, :frequency, :string
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :deliverables, :frequency
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
class AddYearAndMonthToLaborBudgets < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :labor_budgets, :year, :integer
|
||||
add_index :labor_budgets, :year
|
||||
|
||||
add_column :labor_budgets, :month, :integer
|
||||
add_index :labor_budgets, :month
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :labor_budgets, :year
|
||||
remove_column :labor_budgets, :month
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
class AddYearAndMonthToOverheadBudgets < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :overhead_budgets, :year, :integer
|
||||
add_index :overhead_budgets, :year
|
||||
|
||||
add_column :overhead_budgets, :month, :integer
|
||||
add_index :overhead_budgets, :month
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :overhead_budgets, :year
|
||||
remove_column :overhead_budgets, :month
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
class RemoveFrequencyFromDeliverables < ActiveRecord::Migration
|
||||
def self.up
|
||||
remove_column :deliverables, :frequency
|
||||
end
|
||||
|
||||
def self.down
|
||||
add_column :deliverables, :frequency, :string
|
||||
end
|
||||
end
|
||||
@@ -209,4 +209,40 @@ class ContractsShowTest < ActionController::IntegrationTest
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
should "show the current period for a Retainer" do
|
||||
today_mock = Date.new(2010,2,15)
|
||||
Date.stubs(:today).returns(today_mock)
|
||||
|
||||
@manager = User.generate!
|
||||
@retainer_deliverable = RetainerDeliverable.generate!(:contract => @contract, :manager => @manager, :title => "Retainer", :start_date => '2010-01-01', :end_date => '2010-03-31')
|
||||
|
||||
visit_contract_page(@contract)
|
||||
|
||||
assert_select "table#deliverables" do
|
||||
assert_select "#deliverable_details_#{@retainer_deliverable.id}" do
|
||||
assert_select ".deliverable-current-period", :text => /February 2010/i
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
should "show a selector for changing the currently shown Retainer's period" do
|
||||
@manager = User.generate!
|
||||
@retainer_deliverable = RetainerDeliverable.generate!(:contract => @contract, :manager => @manager, :title => "Retainer", :start_date => '2010-01-01', :end_date => '2010-03-31')
|
||||
|
||||
visit_contract_page(@contract)
|
||||
|
||||
assert_select "table#deliverables" do
|
||||
assert_select "#deliverable_details_#{@retainer_deliverable.id}" do
|
||||
assert_select "select#retainer_period_change_#{@retainer_deliverable.id}" do
|
||||
assert_select "option", /All/
|
||||
assert_select "option", /January 2010/
|
||||
assert_select "option", /February 2010/
|
||||
assert_select "option", /March 2010/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
require 'test_helper'
|
||||
|
||||
class DeliverableDetailsShowTest < ActionController::IntegrationTest
|
||||
include Redmine::I18n
|
||||
|
||||
def setup
|
||||
@project = Project.generate!(:identifier => 'main').reload
|
||||
@contract = Contract.generate!(:project => @project, :billable_rate => 10)
|
||||
@manager = User.generate!
|
||||
@deliverable1 = RetainerDeliverable.spawn(:contract => @contract, :manager => @manager, :title => "Retainer", :start_date => '2010-01-01', :end_date => '2010-03-31')
|
||||
@deliverable1.labor_budgets << LaborBudget.spawn(:budget => 100, :hours => 10)
|
||||
@deliverable1.overhead_budgets << OverheadBudget.spawn(:budget => 200, :hours => 10)
|
||||
|
||||
@deliverable1.save!
|
||||
end
|
||||
|
||||
context "for a JS request" do
|
||||
should "render the details for the deliverable" do
|
||||
visit "/projects/#{@project.id}/contracts/#{@contract.id}/deliverables/#{@deliverable1.id}", :get, {:format => 'js', :as => 'deliverable_details_row'}
|
||||
|
||||
assert_response :success
|
||||
assert_select ".deliverable_details_outer_wrapper_#{@deliverable1.id}"
|
||||
assert_select "table#deliverables", :count => 0 # Not the full table
|
||||
assert_select "tr#deliverable_details_#{@deliverable1.id}", :count => 0 # Not the wrapper tr
|
||||
|
||||
end
|
||||
|
||||
should "filter the details based on the period" do
|
||||
assert_equal 300, @deliverable1.labor_budget_total
|
||||
assert_equal 600, @deliverable1.overhead_budget_total
|
||||
assert_equal 300, @deliverable1.total # Contract rate * 30 hours (labor)
|
||||
|
||||
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'
|
||||
assert_select "td.overhead_budget_total", '200'
|
||||
assert_select "td.total", '100'
|
||||
|
||||
assert_select "select.retainer_period_change" do
|
||||
assert_select "option[selected=selected]", "Feburary 2010"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -90,4 +90,301 @@ class DeliverablesEditTest < ActionController::IntegrationTest
|
||||
assert_equal 10, @overhead_budget.hours
|
||||
assert_equal 1000.0, @overhead_budget.budget
|
||||
end
|
||||
|
||||
should "show allow editing the Deliverable Finances section for each Retainer period" do
|
||||
@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.start_date = '2010-01-01'
|
||||
@retainer_deliverable.end_date = '2010-12-31'
|
||||
@retainer_deliverable.save!
|
||||
assert_equal 12, @retainer_deliverable.months.length
|
||||
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@retainer_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
assert_template 'deliverables/edit'
|
||||
|
||||
assert_select 'fieldset.deliverable-finances', :count => 12
|
||||
|
||||
within ".date-2010-01" do
|
||||
within "#deliverable-labor" do
|
||||
fill_in "hrs", :with => '20'
|
||||
fill_in "$", :with => '2000'
|
||||
end
|
||||
|
||||
within "#deliverable-overhead" do
|
||||
fill_in "hrs", :with => '100'
|
||||
fill_in "$", :with => '100'
|
||||
end
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
@labor_budgets = @retainer_deliverable.reload.labor_budgets
|
||||
assert_equal 12, @labor_budgets.length
|
||||
@labor_budgets.each do |labor_budget|
|
||||
if labor_budget.year == 2010 && labor_budget.month == 1
|
||||
|
||||
# Specific month's budget updated?
|
||||
assert_equal 20.0, labor_budget.hours
|
||||
assert_equal 2000.0, labor_budget.budget
|
||||
|
||||
else
|
||||
|
||||
assert_equal 10.0, labor_budget.hours
|
||||
assert_equal 1000.0, labor_budget.budget
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@overhead_budgets = @retainer_deliverable.reload.overhead_budgets
|
||||
assert_equal 12, @overhead_budgets.length
|
||||
@overhead_budgets.each do |overhead_budget|
|
||||
if overhead_budget.year == 2010 && overhead_budget.month == 1
|
||||
|
||||
# Specific month's budget updated?
|
||||
assert_equal 100.0, overhead_budget.hours
|
||||
assert_equal 100.0, overhead_budget.budget
|
||||
|
||||
else
|
||||
|
||||
assert_equal 10.0, overhead_budget.hours
|
||||
assert_equal 1000.0, overhead_budget.budget
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
should "allow extending a Retainer's start and end months" do
|
||||
labor_budget_hours_1 = 10
|
||||
labor_budget_hours_2 = 20
|
||||
labor_budget_amount_1 = 1000
|
||||
labor_budget_amount_2 = 2000
|
||||
overhead_budget_hours_1 = 10
|
||||
overhead_budget_hours_2 = 20
|
||||
overhead_budget_amount_1 = 1000
|
||||
overhead_budget_amount_2 = 2000
|
||||
|
||||
|
||||
@retainer_deliverable = RetainerDeliverable.spawn(:contract => @contract, :manager => @manager, :title => "Retainer")
|
||||
@retainer_deliverable.labor_budgets << @labor_budget = LaborBudget.spawn(:deliverable => @retainer_deliverable, :budget => labor_budget_amount_1, :hours => labor_budget_hours_1)
|
||||
@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.start_date = '2010-01-01'
|
||||
@retainer_deliverable.end_date = '2010-12-31'
|
||||
@retainer_deliverable.save!
|
||||
assert_equal 12, @retainer_deliverable.months.length
|
||||
assert_equal 24, @retainer_deliverable.reload.labor_budgets.count # 12 months * 2 records
|
||||
assert_equal 24, @retainer_deliverable.reload.overhead_budgets.count # 12 months * 2 records
|
||||
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@retainer_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
assert_template 'deliverables/edit'
|
||||
|
||||
# Extend the period
|
||||
fill_in "Start", :with => '2009-01-13' # 12 new months
|
||||
fill_in "End Date", :with => '2011-12-01' # 12 new months
|
||||
click_button "Save"
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
@retainer_deliverable.reload
|
||||
|
||||
assert_equal 36, @retainer_deliverable.months.length
|
||||
|
||||
@labor_budgets = @retainer_deliverable.reload.labor_budgets
|
||||
assert_equal 72, @labor_budgets.length # 36 months * 2 records
|
||||
|
||||
@labor_budgets_for_2009 = @labor_budgets.select {|l| l.year == 2009 }
|
||||
@labor_budgets_for_2010 = @labor_budgets.select {|l| l.year == 2010 }
|
||||
@labor_budgets_for_2011 = @labor_budgets.select {|l| l.year == 2011 }
|
||||
|
||||
assert_equal 24, @labor_budgets_for_2009.length
|
||||
assert_equal 24, @labor_budgets_for_2010.length
|
||||
assert_equal 24, @labor_budgets_for_2011.length
|
||||
|
||||
@labor_budgets_for_2009.each do |labor_budget|
|
||||
assert_equal 2009, labor_budget.year
|
||||
assert [labor_budget_hours_1, labor_budget_hours_2].include?(labor_budget.hours), "Extended labor budget hours not matching template budget"
|
||||
assert [labor_budget_amount_1, labor_budget_amount_2].include?(labor_budget.budget), "Extended labor budget dollars not matching template budget"
|
||||
end
|
||||
|
||||
@labor_budgets_for_2010.each do |labor_budget|
|
||||
assert_equal 2010, labor_budget.year
|
||||
assert [labor_budget_hours_1, labor_budget_hours_2].include?(labor_budget.hours), "Extended labor budget hours not matching template budget"
|
||||
assert [labor_budget_amount_1, labor_budget_amount_2].include?(labor_budget.budget), "Extended labor budget dollars not matching template budget"
|
||||
end
|
||||
|
||||
@labor_budgets_for_2011.each do |labor_budget|
|
||||
assert_equal 2011, labor_budget.year
|
||||
assert [labor_budget_hours_1, labor_budget_hours_2].include?(labor_budget.hours), "Extended labor budget hours not matching template budget"
|
||||
assert [labor_budget_amount_1, labor_budget_amount_2].include?(labor_budget.budget), "Extended labor budget dollars not matching template budget"
|
||||
end
|
||||
|
||||
@overhead_budgets = @retainer_deliverable.reload.overhead_budgets
|
||||
assert_equal 72, @overhead_budgets.length # 36 months * 2 records
|
||||
|
||||
@overhead_budgets_for_2009 = @overhead_budgets.select {|l| l.year == 2009 }
|
||||
@overhead_budgets_for_2010 = @overhead_budgets.select {|l| l.year == 2010 }
|
||||
@overhead_budgets_for_2011 = @overhead_budgets.select {|l| l.year == 2011 }
|
||||
|
||||
assert_equal 24, @overhead_budgets_for_2009.length
|
||||
assert_equal 24, @overhead_budgets_for_2010.length
|
||||
assert_equal 24, @overhead_budgets_for_2011.length
|
||||
|
||||
@overhead_budgets_for_2009.each do |overhead_budget|
|
||||
assert_equal 2009, overhead_budget.year
|
||||
assert [overhead_budget_hours_1, overhead_budget_hours_2].include?(overhead_budget.hours), "Extended overhead budget hours not matching template budget"
|
||||
assert [overhead_budget_amount_1, overhead_budget_amount_2].include?(overhead_budget.budget), "Extended overhead budget dollars not matching template budget"
|
||||
end
|
||||
|
||||
@overhead_budgets_for_2010.each do |overhead_budget|
|
||||
assert_equal 2010, overhead_budget.year
|
||||
assert [overhead_budget_hours_1, overhead_budget_hours_2].include?(overhead_budget.hours), "Extended overhead budget hours not matching template budget"
|
||||
assert [overhead_budget_amount_1, overhead_budget_amount_2].include?(overhead_budget.budget), "Extended overhead budget dollars not matching template budget"
|
||||
end
|
||||
|
||||
@overhead_budgets_for_2011.each do |overhead_budget|
|
||||
assert_equal 2011, overhead_budget.year
|
||||
assert [overhead_budget_hours_1, overhead_budget_hours_2].include?(overhead_budget.hours), "Extended overhead budget hours not matching template budget"
|
||||
assert [overhead_budget_amount_1, overhead_budget_amount_2].include?(overhead_budget.budget), "Extended overhead budget dollars not matching template budget"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
should "allow shrinking a Retainer's start and end months" do
|
||||
@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.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.start_date = '2010-01-01'
|
||||
@retainer_deliverable.end_date = '2010-12-31'
|
||||
@retainer_deliverable.save!
|
||||
assert_equal 12, @retainer_deliverable.months.length
|
||||
assert_equal 24, @retainer_deliverable.reload.labor_budgets.count # 12 months * 2 records
|
||||
assert_equal 24, @retainer_deliverable.reload.overhead_budgets.count # 12 months * 2 records
|
||||
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@retainer_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
assert_template 'deliverables/edit'
|
||||
|
||||
# Shrink the period to only 6 months
|
||||
fill_in "Start", :with => '2010-02-13'
|
||||
fill_in "End Date", :with => '2010-07-01'
|
||||
click_button "Save"
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
@retainer_deliverable.reload
|
||||
|
||||
assert_equal 6, @retainer_deliverable.months.length
|
||||
|
||||
@labor_budgets = @retainer_deliverable.reload.labor_budgets
|
||||
assert_equal 12, @labor_budgets.length # 6 months * 2 records
|
||||
|
||||
@overhead_budgets = @retainer_deliverable.reload.overhead_budgets
|
||||
assert_equal 12, @overhead_budgets.length # 6 months * 2 records
|
||||
|
||||
end
|
||||
|
||||
should "allow editing a Retainer's start and end months inside the current period" do
|
||||
@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.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.start_date = '2010-01-01'
|
||||
@retainer_deliverable.end_date = '2010-12-31'
|
||||
@retainer_deliverable.save!
|
||||
assert_equal 12, @retainer_deliverable.months.length
|
||||
assert_equal 24, @retainer_deliverable.reload.labor_budgets.count # 12 months * 2 records
|
||||
assert_equal 24, @retainer_deliverable.reload.overhead_budgets.count # 12 months * 2 records
|
||||
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@retainer_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
assert_template 'deliverables/edit'
|
||||
|
||||
# Edit the dates without changing the period
|
||||
fill_in "Start", :with => '2010-01-13'
|
||||
fill_in "End Date", :with => '2010-12-01'
|
||||
click_button "Save"
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
@retainer_deliverable.reload
|
||||
|
||||
assert_equal 12, @retainer_deliverable.months.length
|
||||
|
||||
@labor_budgets = @retainer_deliverable.reload.labor_budgets
|
||||
assert_equal 24, @labor_budgets.length # 12 months * 2 records
|
||||
|
||||
@overhead_budgets = @retainer_deliverable.reload.overhead_budgets
|
||||
assert_equal 24, @overhead_budgets.length # 12 months * 2 records
|
||||
|
||||
end
|
||||
|
||||
should "show empty budget fields for a Retainer that has missing budgets" do
|
||||
@retainer_deliverable = RetainerDeliverable.spawn(:contract => @contract, :manager => @manager, :title => "Retainer")
|
||||
@retainer_deliverable.start_date = '2010-01-01'
|
||||
@retainer_deliverable.end_date = '2010-03-31'
|
||||
@retainer_deliverable.save!
|
||||
assert_equal 3, @retainer_deliverable.months.length
|
||||
assert_equal 0, @retainer_deliverable.reload.labor_budgets.count # 3 months * 0 records
|
||||
assert_equal 0, @retainer_deliverable.reload.overhead_budgets.count # 3 months * 0 records
|
||||
|
||||
visit_contract_page(@contract)
|
||||
click_link_within "#deliverable_details_#{@retainer_deliverable.id}", 'Edit'
|
||||
assert_response :success
|
||||
assert_template 'deliverables/edit'
|
||||
|
||||
# Should show 6 inputs:
|
||||
# * labor hidden year
|
||||
# * labor hidden month
|
||||
# * labor hours
|
||||
# * labor amount
|
||||
# * overhead hidden year
|
||||
# * overhead hidden month
|
||||
# * overhead hours
|
||||
# * overhead amount
|
||||
# * total (hidden)
|
||||
assert_select ".date-2010-01" do
|
||||
assert_select "input", :count => 9
|
||||
end
|
||||
|
||||
|
||||
within ".date-2010-01" do
|
||||
within "#deliverable-labor" do
|
||||
fill_in "hrs", :with => '20'
|
||||
fill_in "$", :with => '2000'
|
||||
end
|
||||
|
||||
within "#deliverable-overhead" do
|
||||
fill_in "hrs", :with => '100'
|
||||
fill_in "$", :with => '100'
|
||||
end
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
@retainer_deliverable.reload
|
||||
|
||||
assert_equal 3, @retainer_deliverable.labor_budgets.count
|
||||
assert_equal [20, nil, nil], @retainer_deliverable.labor_budgets.collect(&:hours)
|
||||
assert_equal [2000, nil, nil], @retainer_deliverable.labor_budgets.collect(&:budget)
|
||||
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -92,9 +92,9 @@ class DeliverablesNewTest < ActionController::IntegrationTest
|
||||
fill_in "Notes", :with => 'Some notes on the deliverable'
|
||||
fill_in "Total", :with => '1,000.00'
|
||||
|
||||
# Hide and clear the total
|
||||
assert js("jQuery('#deliverable_total_input').is(':hidden')"),
|
||||
"Total is visible when it should be hidden"
|
||||
# # Hide and clear the total
|
||||
# assert js("jQuery('#deliverable_total_input').is(':hidden')"),
|
||||
# "Total is visible when it should be hidden"
|
||||
|
||||
click_button "Save"
|
||||
|
||||
@@ -111,6 +111,79 @@ class DeliverablesNewTest < ActionController::IntegrationTest
|
||||
|
||||
end
|
||||
|
||||
should "create a new Retainer deliverable" do
|
||||
@manager = User.generate!
|
||||
@role = Role.generate!
|
||||
User.add_to_project(@manager, @project, @role)
|
||||
|
||||
visit_contract_page(@contract)
|
||||
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-labor") do
|
||||
fill_in "hrs", :with => '20'
|
||||
fill_in "$", :with => '$2,000'
|
||||
end
|
||||
|
||||
within("#deliverable-overhead") do
|
||||
fill_in "hrs", :with => '10'
|
||||
fill_in "$", :with => '$1,000'
|
||||
end
|
||||
|
||||
click_button "Save"
|
||||
|
||||
assert_response :success
|
||||
assert_template 'contracts/show'
|
||||
|
||||
@deliverable = Deliverable.last
|
||||
assert_equal "A New Deliverable", @deliverable.title
|
||||
assert_equal @contract, @deliverable.contract
|
||||
assert_equal "RetainerDeliverable", @deliverable.type
|
||||
assert_equal '2010-01-01', @deliverable.start_date.to_s
|
||||
assert_equal '2010-12-31', @deliverable.end_date.to_s
|
||||
assert_equal @manager, @deliverable.manager
|
||||
|
||||
# Budget items, one per month
|
||||
labor_budgets = @deliverable.labor_budgets
|
||||
assert_equal 12, labor_budgets.length
|
||||
|
||||
labor_budgets.each do |budget|
|
||||
assert_equal 2000, budget.budget
|
||||
assert_equal 20, budget.hours
|
||||
end
|
||||
|
||||
# Budget dates
|
||||
labor_budgets.each do |budget|
|
||||
assert_equal 2010, budget.year
|
||||
end
|
||||
(1..12).each do |month_number|
|
||||
assert_equal 1, labor_budgets.select {|b| b.month == month_number}.length
|
||||
end
|
||||
|
||||
overhead_budgets = @deliverable.overhead_budgets
|
||||
assert_equal 12, overhead_budgets.length
|
||||
|
||||
overhead_budgets.each do |budget|
|
||||
assert_equal 1000, budget.budget
|
||||
assert_equal 10, budget.hours
|
||||
end
|
||||
|
||||
# Budget dates
|
||||
overhead_budgets.each do |budget|
|
||||
assert_equal 2010, budget.year
|
||||
end
|
||||
(1..12).each do |month_number|
|
||||
assert_equal 1, overhead_budgets.select {|b| b.month == month_number}.length
|
||||
end
|
||||
end
|
||||
|
||||
should "create new budget items for the deliverables" do
|
||||
@manager = User.generate!
|
||||
@role = Role.generate!
|
||||
|
||||
@@ -26,8 +26,6 @@ class ContractTest < ActiveSupport::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
should "QUESTION: name be unique"
|
||||
|
||||
should "default executed to false" do
|
||||
@contract = Contract.new
|
||||
|
||||
|
||||
@@ -33,4 +33,5 @@ class DeliverableTest < ActiveSupport::TestCase
|
||||
assert_equal 20100.00, d.total.to_f
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class RetainerDeliverableTest < ActiveSupport::TestCase
|
||||
should "be a subclass of HourlyDeliverable" do
|
||||
assert_equal HourlyDeliverable, RetainerDeliverable.superclass
|
||||
end
|
||||
|
||||
context "#months" do
|
||||
should "be an array of months the Deliverable is active in" do
|
||||
d = RetainerDeliverable.new(:start_date => Date.today.beginning_of_month,
|
||||
:end_date => 6.months.from_now)
|
||||
|
||||
assert_equal 7, d.months.length # 6 months + current month
|
||||
d.months.each do |month|
|
||||
assert_kind_of Date, month
|
||||
end
|
||||
end
|
||||
|
||||
should "return an empty array if start_date is missing" do
|
||||
d = RetainerDeliverable.new(:start_date => nil,
|
||||
:end_date => 6.months.from_now)
|
||||
assert_equal [], d.months
|
||||
end
|
||||
|
||||
should "return an empty array if end_date is missing" do
|
||||
d = RetainerDeliverable.new(:start_date => Date.today.beginning_of_month,
|
||||
:end_date => nil)
|
||||
assert_equal [], d.months
|
||||
end
|
||||
|
||||
should "return an empty array if the start_date and end_date are reversed" do
|
||||
d = RetainerDeliverable.new(:start_date => 6.months.from_now,
|
||||
:end_date => Date.today.beginning_of_month)
|
||||
assert_equal [], d.months
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "#create_budgets_for_periods" do
|
||||
setup do
|
||||
@deliverable = RetainerDeliverable.generate!(:start_date => '2010-01-10',
|
||||
:end_date => '2010-10-10')
|
||||
LaborBudget.generate!(:deliverable => @deliverable)
|
||||
OverheadBudget.generate!(:deliverable => @deliverable)
|
||||
OverheadBudget.generate!(:deliverable => @deliverable)
|
||||
end
|
||||
|
||||
should "create a dated labor budget for each month" do
|
||||
assert_equal 1, @deliverable.labor_budgets.count
|
||||
|
||||
@deliverable.reload.create_budgets_for_periods
|
||||
|
||||
assert_equal 10, @deliverable.labor_budgets.count
|
||||
labor_budgets = @deliverable.labor_budgets
|
||||
labor_budgets.each do |budget|
|
||||
assert_equal 2010, budget.year
|
||||
end
|
||||
|
||||
(1..10).each do |month_number|
|
||||
assert_equal 1, labor_budgets.select {|b| b.month == month_number}.length
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
should "create a dated overhead budget for each month" do
|
||||
assert_equal 2, @deliverable.overhead_budgets.count
|
||||
|
||||
@deliverable.reload.create_budgets_for_periods
|
||||
|
||||
assert_equal 20, @deliverable.overhead_budgets.count
|
||||
overhead_budgets = @deliverable.overhead_budgets
|
||||
overhead_budgets.each do |budget|
|
||||
assert_equal 2010, budget.year
|
||||
end
|
||||
|
||||
(1..10).each do |month_number|
|
||||
assert_equal 2, overhead_budgets.select {|b| b.month == month_number}.length
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
context "#labor_budget_spent" do
|
||||
context "with a empty period" do
|
||||
should "use all periods"
|
||||
end
|
||||
|
||||
context "with a period out of the retainer range" do
|
||||
should "use all periods"
|
||||
end
|
||||
|
||||
context "with a period in the retainer range" do
|
||||
should "filter the records"
|
||||
end
|
||||
end
|
||||
|
||||
context "#labor_budget_total" do
|
||||
setup do
|
||||
@deliverable = RetainerDeliverable.generate!(:start_date => '2010-01-01', :end_date => '2010-03-31')
|
||||
@deliverable.labor_budgets << LaborBudget.spawn(:budget => 100, :hours => 10)
|
||||
@deliverable.save!
|
||||
end
|
||||
|
||||
context "with a empty period" do
|
||||
should "use all periods" do
|
||||
assert_equal 300.0, @deliverable.labor_budget_total(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context "with a period out of the retainer range" do
|
||||
should "filter the records" do
|
||||
assert_equal 0, @deliverable.labor_budget_total(Date.new(2011,1,1))
|
||||
end
|
||||
end
|
||||
|
||||
context "with an invalid period" do
|
||||
should "return 0" do
|
||||
assert_equal 0, @deliverable.labor_budget_total('1')
|
||||
end
|
||||
end
|
||||
|
||||
context "with a period in the retainer range" do
|
||||
should "filter the records" do
|
||||
assert_equal 100.0, @deliverable.labor_budget_total(Date.new(2010,2,1))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# context "#overhead_spent"
|
||||
|
||||
context "#overhead_budget_total" do
|
||||
setup do
|
||||
@deliverable = RetainerDeliverable.generate!(:start_date => '2010-01-01', :end_date => '2010-03-31')
|
||||
@deliverable.overhead_budgets << OverheadBudget.spawn(:budget => 100, :hours => 10)
|
||||
@deliverable.save!
|
||||
end
|
||||
|
||||
context "with a empty period" do
|
||||
should "use all periods" do
|
||||
assert_equal 300.0, @deliverable.overhead_budget_total(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context "with a period out of the retainer range" do
|
||||
should "filter the records" do
|
||||
assert_equal 0, @deliverable.overhead_budget_total(Date.new(2011,1,1))
|
||||
end
|
||||
end
|
||||
|
||||
context "with an invalid period" do
|
||||
should "return 0" do
|
||||
assert_equal 0, @deliverable.overhead_budget_total('1')
|
||||
end
|
||||
end
|
||||
|
||||
context "with a period in the retainer range" do
|
||||
should "filter the records" do
|
||||
assert_equal 100.0, @deliverable.overhead_budget_total(Date.new(2010,2,1))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# context "#profit_left"
|
||||
|
||||
context "#profit_budget" 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.labor_budgets << LaborBudget.spawn(:budget => 100, :hours => 10)
|
||||
@deliverable.overhead_budgets << OverheadBudget.spawn(:budget => 100, :hours => 10)
|
||||
@deliverable.save!
|
||||
|
||||
assert_equal 100 * 30, @deliverable.total
|
||||
assert_equal 2400, @deliverable.profit_budget # 3000 - 300 - 300
|
||||
end
|
||||
|
||||
context "with a empty period" do
|
||||
should "use all periods" do
|
||||
assert_equal 2400, @deliverable.profit_budget(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context "with a period out of the retainer range" do
|
||||
should "filter the records" do
|
||||
assert_equal 0, @deliverable.profit_budget(Date.new(2011,1,1))
|
||||
end
|
||||
end
|
||||
|
||||
context "with an invalid period" do
|
||||
should "return 0" do
|
||||
assert_equal 0, @deliverable.profit_budget('1')
|
||||
end
|
||||
end
|
||||
|
||||
context "with a period in the retainer range" do
|
||||
should "filter the records" do
|
||||
assert_equal 1000 - 200, @deliverable.profit_budget(Date.new(2010,2,1))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# context "#total_spent"
|
||||
|
||||
context "#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.labor_budgets << LaborBudget.spawn(:budget => 100, :hours => 10)
|
||||
@deliverable.overhead_budgets << OverheadBudget.spawn(:budget => 100, :hours => 10)
|
||||
@deliverable.save!
|
||||
|
||||
assert_equal 100 * 30, @deliverable.total
|
||||
end
|
||||
|
||||
context "with a empty period" do
|
||||
should "use all periods" do
|
||||
assert_equal 3000, @deliverable.total(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context "with a period out of the retainer range" do
|
||||
should "filter the records" do
|
||||
assert_equal 0, @deliverable.total(Date.new(2011,1,1))
|
||||
end
|
||||
end
|
||||
|
||||
context "with an invalid period" do
|
||||
should "return 0" do
|
||||
assert_equal 0, @deliverable.total('1')
|
||||
end
|
||||
end
|
||||
|
||||
context "with a period in the retainer range" do
|
||||
should "filter the records" do
|
||||
assert_equal 1000, @deliverable.total(Date.new(2010,2,1))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user