Merge pull request #6 from finnlabs/feature/rails3_fix_cukes

pull/6827/head
sschu 12 years ago
commit e519b043a5
  1. 51
      app/controllers/cost_types_controller.rb
  2. 2
      app/views/cost_objects/_labor_budget_item.html.erb
  3. 2
      app/views/cost_objects/_material_budget_item.html.erb
  4. 2
      app/views/cost_types/_rate.html.erb
  5. 4
      app/views/cost_types/edit.html.erb
  6. 19
      app/views/cost_types/index.html.erb
  7. 2
      app/views/hourly_rates/_rate.html.erb
  8. 2
      app/views/hourly_rates/edit.html.erb
  9. 8
      config/routes.rb
  10. 6
      features/create_budget.feature
  11. 4
      features/credit_unit_costs.feature
  12. 10
      features/step_definitions/cost_object_steps.rb
  13. 18
      features/step_definitions/cost_steps.rb
  14. 1
      features/view_own_rates.feature
  15. 4
      lib/open_project/costs/engine.rb
  16. 41
      lib/open_project/costs/patches/i18n_patch.rb
  17. 4
      lib/open_project/costs/patches/permitted_params_patch.rb
  18. 8
      spec/models/permitted_params_spec.rb
  19. 16
      spec/routing/cost_types_routing_spec.rb

@ -3,10 +3,7 @@ class CostTypesController < ApplicationController
# Allow only admins here # Allow only admins here
before_filter :require_admin before_filter :require_admin
before_filter :find_cost_type, :only => [:set_rate, :toggle_delete] before_filter :find_cost_type, :only => [:edit, :update, :set_rate, :toggle_delete]
before_filter :find_optional_cost_type, :only => [:edit, :update]
verify :method => :post, :only => [:set_rate, :toggle_delete], :redirect_to => { :action => :index }
helper :sort helper :sort
include SortHelper include SortHelper
@ -34,38 +31,18 @@ class CostTypesController < ApplicationController
end end
def edit def edit
if !@cost_type render :action => "edit", :layout => !request.xhr?
@cost_type = CostType.new()
end
@cost_type.attributes = permitted_params.cost_type if params[:cost_type]
if request.post? && @cost_type.save
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default(:action => 'index')
else
@cost_type.rates.build({:valid_from => Date.today}) if @cost_type.rates.empty?
render :action => "edit", :layout => !request.xhr?
end
rescue ActiveRecord::StaleObjectError
# Optimistic locking exception
flash.now[:error] = l(:notice_locking_conflict)
end end
def update def update
# TODO: method is copied over from edit # TODO: method is copied over from edit
# remove code as appropriate # remove code as appropriate
if !@cost_type @cost_type.attributes = permitted_params.cost_type
@cost_type = CostType.new()
end
@cost_type.attributes = permitted_params.cost_type if params[:cost_type]
if @cost_type.save if @cost_type.save
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_back_or_default(:action => 'index') redirect_back_or_default(:action => 'index')
else else
@cost_type.rates.build({:valid_from => Date.today}) if @cost_type.rates.empty?
render :action => "edit", :layout => !request.xhr? render :action => "edit", :layout => !request.xhr?
end end
rescue ActiveRecord::StaleObjectError rescue ActiveRecord::StaleObjectError
@ -76,13 +53,19 @@ class CostTypesController < ApplicationController
def new def new
# TODO: method is copied over from edit # TODO: method is copied over from edit
# remove code as appropriate # remove code as appropriate
if !@cost_type @cost_type = CostType.new()
@cost_type = CostType.new()
end
@cost_type.attributes = permitted_params.cost_type if params[:cost_type] @cost_type.rates.build({:valid_from => Date.today}) if @cost_type.rates.empty?
if request.post? && @cost_type.save render :action => "edit", :layout => !request.xhr?
end
def create
# TODO: method is copied over from edit
# remove code as appropriate
@cost_type = CostType.new(permitted_params.cost_type)
if @cost_type.save
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_back_or_default(:action => 'index') redirect_back_or_default(:action => 'index')
else else
@ -130,12 +113,6 @@ private
render_404 render_404
end end
def find_optional_cost_type
if !params[:id].blank?
@cost_type = CostType.find(params[:id])
end
end
def default_breadcrumb def default_breadcrumb
l(:caption_cost_type_plural) l(:caption_cost_type_plural)
end end

@ -12,7 +12,7 @@
-%> -%>
<% unless error_messages.blank? %><tr><td colspan="5"><%= error_messages %></td></tr><% end %> <% unless error_messages.blank? %><tr><td colspan="5"><%= error_messages %></td></tr><% end %>
<% fields_for prefix, labor_budget_item do |cost_form| %> <%= fields_for prefix, labor_budget_item do |cost_form| %>
<tr class="cost_entry <%= classes %>" id="<%= id_prefix %>"> <tr class="cost_entry <%= classes %>" id="<%= id_prefix %>">
<td class="units"> <td class="units">
<label class="hidden-for-sighted" for="<%= id_prefix %>_units"><%= l(:field_hours) %></label> <label class="hidden-for-sighted" for="<%= id_prefix %>_units"><%= l(:field_hours) %></label>

@ -12,7 +12,7 @@
-%> -%>
<% unless error_messages.blank? %><tr><td colspan="5"><%= error_messages %></td></tr><% end %> <% unless error_messages.blank? %><tr><td colspan="5"><%= error_messages %></td></tr><% end %>
<% fields_for prefix, material_budget_item do |cost_form| %> <%= fields_for prefix, material_budget_item do |cost_form| %>
<tr class="cost_entry <%= classes %>" id="<%= id_prefix %>"> <tr class="cost_entry <%= classes %>" id="<%= id_prefix %>">
<td class="units"> <td class="units">
<label class="hidden-for-sighted" for="<%= id_prefix %>_units"><%= l(:field_units) %></label> <label class="hidden-for-sighted" for="<%= id_prefix %>_units"><%= l(:field_units) %></label>

@ -13,7 +13,7 @@
<% unless error_messages.blank? %><tr><td colspan="3"><%= error_messages %></td></tr><% end %> <% unless error_messages.blank? %><tr><td colspan="3"><%= error_messages %></td></tr><% end %>
<% fields_for prefix, rate do |rate_form| %> <%= fields_for prefix, rate do |rate_form| %>
<tr class="<%= classes %>" id="<%= id_prefix %>"> <tr class="<%= classes %>" id="<%= id_prefix %>">
<td> <td>
<label class="hidden-for-sighted", for="<%= "#{id_prefix}_valid_from" %>"><%= l(:caption_valid_from) %></label> <label class="hidden-for-sighted", for="<%= "#{id_prefix}_valid_from" %>"><%= l(:caption_valid_from) %></label>

@ -1,7 +1,7 @@
<%= render :partial => 'shared/costs_header' %> <%= render :partial => 'shared/costs_header' %>
<h2><%= l(:caption_cost_type) %></h2> <h2><%= l(:caption_cost_type) %></h2>
<%= labelled_tabular_form_for @cost_type, :url => {:action => 'update'}, :method => :put do |f| %> <%= labelled_tabular_form_for @cost_type do |f| %>
<%= error_messages_for 'cost_type' %> <%= error_messages_for 'cost_type' %>
<%= back_url_hidden_field_tag %> <%= back_url_hidden_field_tag %>
@ -13,7 +13,7 @@
</div> </div>
<h3><%= l :caption_rate_history %></h3> <h3><%= l :caption_rate_history %></h3>
<% javascript_tag do -%> <%= javascript_tag do -%>
RatesForm = new Subform('<%= escape_javascript(render(:partial => "rate", :object => CostRate.new )) %>',<%= @cost_type.rates.length %>,'rates_body'); RatesForm = new Subform('<%= escape_javascript(render(:partial => "rate", :object => CostRate.new )) %>',<%= @cost_type.rates.length %>,'rates_body');
<% end -%> <% end -%>
<table class="list" style="width:auto"> <table class="list" style="width:auto">

@ -5,28 +5,31 @@
<h2><%= l(:caption_cost_type_plural) %></h2> <h2><%= l(:caption_cost_type_plural) %></h2>
<%= form_tag({ :controller => 'cost_types', :action => 'index' }, :id => 'query_form') do %> <%= form_tag(cost_types_path, { :method => :get, :id => 'query_form' }) do %>
<fieldset id="filters"><legend><%= l(:label_filter_plural) %></legend> <fieldset id="filters">
<p> <legend><%= l(:label_filter_plural) %></legend>
<label for="fixed_date"><%= l(:label_fixed_date) %></label> <p>
<%= text_field_tag :fixed_date, @fixed_date, :size => 10 %><%= calendar_for('fixed_date') %> <label for="fixed_date"><%= l(:label_fixed_date) %></label>
</p> <%= text_field_tag :fixed_date, @fixed_date, :size => 10 %><%= calendar_for('fixed_date') %>
</p>
<p> <p>
<%= check_box_tag :include_deleted, "1", @include_deleted, :autocomplete => "off" %> <%= check_box_tag :include_deleted, "1", @include_deleted, :autocomplete => "off" %>
<label for="include_deleted"><%= l(:caption_show_locked) %></label> <label for="include_deleted"><%= l(:caption_show_locked) %></label>
</p> </p>
</fieldset>
<p class="buttons"> <p class="buttons">
<%= link_to_remote l(:button_apply), <%= link_to_remote l(:button_apply),
{ :update => "content", { :update => "content",
:with => "Form.serialize('query_form')" :with => "Form.serialize('query_form')",
:method => :get
}, :class => 'icon icon-checked' %> }, :class => 'icon icon-checked' %>
<%= link_to_remote l(:button_clear), <%= link_to_remote l(:button_clear),
{ :url => { :clear_filter => true }, { :url => { :clear_filter => true },
:method => :get,
:update => "content", :update => "content",
}, :class => 'icon icon-reload' %> }, :class => 'icon icon-reload' %>
</p> </p>
</fieldset>
<% end %> <% end %>
<div id="cost_type_flash_notice_outer" style="display:none"> <div id="cost_type_flash_notice_outer" style="display:none">

@ -13,7 +13,7 @@
<% unless error_messages.blank? %><tr><td colspan="3"><%= error_messages %></td></tr><% end %> <% unless error_messages.blank? %><tr><td colspan="3"><%= error_messages %></td></tr><% end %>
<% fields_for prefix, rate do |rate_form| %> <%= fields_for prefix, rate do |rate_form| %>
<tr class="<%= classes %>" id="<%= id_prefix %>"> <tr class="<%= classes %>" id="<%= id_prefix %>">
<td> <td>
<label class="hidden-for-sighted", for="<%= "#{id_prefix}_valid_from" %>"><%= l(:caption_valid_from) %></label> <label class="hidden-for-sighted", for="<%= "#{id_prefix}_valid_from" %>"><%= l(:caption_valid_from) %></label>

@ -6,7 +6,7 @@
<p><strong><%= l(:label_current_default_rate) %>:</strong> <%= number_to_currency(default_rate.rate)%></p> <p><strong><%= l(:label_current_default_rate) %>:</strong> <%= number_to_currency(default_rate.rate)%></p>
<% end %> <% end %>
<% javascript_tag do -%> <%= javascript_tag do -%>
RatesForm = new Subform('<%= escape_javascript(render(:partial => "rate", :object => HourlyRate.new )) %>',<%= @rates.length %>,'rates_body'); RatesForm = new Subform('<%= escape_javascript(render(:partial => "rate", :object => HourlyRate.new )) %>',<%= @rates.length %>,'rates_body');
<% end -%> <% end -%>

@ -20,11 +20,11 @@ OpenProject::Application.routes.draw do
post :preview, :on => :member post :preview, :on => :member
end end
resources :cost_types, :only => [:index, :new, :edit, :update] do resources :cost_types, :only => [:index, :new, :edit, :update, :create] do
# TODO: change to put or even better, replace with update method # TODO: check if this can be replaced with update method
post :set_rate, :on => :member put :set_rate, :on => :member
# TODO: change to destroy or even better, replace with destroy method # TODO: change to destroy or even better, replace with destroy method
post :toggle_delete, :on => :member put :toggle_delete, :on => :member
end end
# TODO: this is a duplicate from a route defined under project/:project_id, check whether we really want to do that # TODO: this is a duplicate from a route defined under project/:project_id, check whether we really want to do that

@ -13,10 +13,8 @@ Feature: Creating a Budget
And I am already logged in as "testuser" And I am already logged in as "testuser"
When I go to the overview page of the project called "project1" When I go to the overview page of the project called "project1"
And I toggle the "Budgets" submenu And I create a budget with the following:
And I follow "New Budget" within "#main-menu" | subject | budget1 |
And I fill in "cost_object_subject" with "budget1"
And I press "Create"
Then I should be on the show page for the budget "budget1" Then I should be on the show page for the budget "budget1"
And I should see "Successful creation" And I should see "Successful creation"

@ -17,8 +17,8 @@ Feature: Credit unit costs
Scenario: Crediting units costs to an issue Scenario: Crediting units costs to an issue
When I am already logged in as "manager" When I am already logged in as "manager"
And I go to the page of the issue "issue1" And I go to the page of the issue "issue1"
And I follow "More functions" within ".action_menu_main" And I select "Log unit costs" from the action menu
And I follow "Log unit costs" within ".action_menu_main"
And I fill in "cost_entry_units" with "100" And I fill in "cost_entry_units" with "100"
And I select "cost_type_1" from "Cost type"
And I press "Save" And I press "Save"
Then I should be on the page of the issue "issue1" Then I should be on the page of the issue "issue1"

@ -0,0 +1,10 @@
When(/^I create a budget with the following:$/) do |table|
rows = table.rows_hash
steps %Q{And I toggle the "Budgets" submenu
And I follow "New Budget" within "#main-menu"
And I fill in "Subject" with "#{rows['subject']}"}
click_button(I18n.t(:button_create), :exact => true)
end

@ -12,10 +12,8 @@ Given /^there is 1 cost type with the following:$/ do |table|
ct = CostType.generate ct = CostType.generate
send_table_to_object(ct, table, { send_table_to_object(ct, table, {
:cost_rate => Proc.new do |o,v| :cost_rate => Proc.new do |o,v|
CostRate.generate.tap do |cr| FactoryGirl.create(:cost_rate, :rate => v,
cr.rate = v :cost_type => o)
cr.cost_type = o
end.save!
end, end,
:name => Proc.new do |o,v| :name => Proc.new do |o,v|
o.name = v o.name = v
@ -78,7 +76,7 @@ end
Given /^the issue "([^\"]+)" has (\d+) [Cc]ost(?: )?[Ee]ntr(?:ies|y) with the following:$/ do |issue, count, table| Given /^the issue "([^\"]+)" has (\d+) [Cc]ost(?: )?[Ee]ntr(?:ies|y) with the following:$/ do |issue, count, table|
i = Issue.find(:last, :conditions => ["subject = '#{issue}'"]) i = Issue.find(:last, :conditions => ["subject = '#{issue}'"])
as_admin count do as_admin count do
ce = Factory.build(:cost_entry, :spent_on => (table.rows_hash["date"] ? table.rows_hash["date"].to_date : Date.today), ce = FactoryGirl.build(:cost_entry, :spent_on => (table.rows_hash["date"] ? table.rows_hash["date"].to_date : Date.today),
:units => table.rows_hash["units"], :units => table.rows_hash["units"],
:project => i.project, :project => i.project,
:issue => i, :issue => i,
@ -95,9 +93,13 @@ Given /^there is a standard cost control project named "([^\"]*)"$/ do |name|
steps %Q{ steps %Q{
Given there is 1 project with the following: Given there is 1 project with the following:
| Name | #{name} | | Name | #{name} |
And the project "#{name}" has the following trackers:
| name |
| tracker1 |
And the project "#{name}" has 1 subproject And the project "#{name}" has 1 subproject
And the project "#{name}" has 1 issue with: And the project "#{name}" has 1 issue with:
| subject | #{name}issue | | subject | #{name}issue |
And there is a role "Manager"
And the role "Manager" may have the following rights: And the role "Manager" may have the following rights:
| view_own_hourly_rate | | view_own_hourly_rate |
| view_issues | | view_issues |
@ -107,8 +109,10 @@ Given /^there is a standard cost control project named "([^\"]*)"$/ do |name|
And there is a role "Controller" And there is a role "Controller"
And the role "Controller" may have the following rights: And the role "Controller" may have the following rights:
| View own cost entries | | View own cost entries |
And there is a role "Developer"
And the role "Developer" may have the following rights: And the role "Developer" may have the following rights:
| View own cost entries | | View own cost entries |
And there is a role "Reporter"
And the role "Reporter" may have the following rights: And the role "Reporter" may have the following rights:
| Create issues | | Create issues |
And there is a role "Supplier" And there is a role "Supplier"
@ -162,7 +166,7 @@ Given /^users have times and the cost type "([^\"]*)" logged on the issue "([^\"
end end
Given /^there is a variable cost object with the following:$/ do |table| Given /^there is a variable cost object with the following:$/ do |table|
cost_object = Factory.build(:variable_cost_object) cost_object = FactoryGirl.build(:variable_cost_object)
table_hash = table.rows_hash table_hash = table.rows_hash
@ -171,7 +175,7 @@ Given /^there is a variable cost object with the following:$/ do |table|
Time.now Time.now
cost_object.fixed_date = cost_object.created_on.to_date cost_object.fixed_date = cost_object.created_on.to_date
cost_object.project = (Project.find_by_identifier(table_hash["project"]) || Project.find_by_name(table_hash ["project"])) if table_hash.has_key? "project" cost_object.project = (Project.find_by_identifier(table_hash["project"]) || Project.find_by_name(table_hash ["project"])) if table_hash.has_key? "project"
cost_object.author = User.current cost_object.author = User.find_by_login(table_hash["author"]) || cost_object.project.members.first.principal
cost_object.subject = table_hash["subject"] if table_hash.has_key? "subject" cost_object.subject = table_hash["subject"] if table_hash.has_key? "subject"
cost_object.save! cost_object.save!

@ -53,7 +53,6 @@ Feature: Permission View Own hourly and cost rates
| Labor Costs | | Labor Costs |
| Unit Costs | | Unit Costs |
And I follow "Apply" And I follow "Apply"
And I wait for AJAX
Then I should see "24.00 EUR" Then I should see "24.00 EUR"
And I should see "10.00 EUR" And I should see "10.00 EUR"
And I should see "14.00 EUR" And I should see "14.00 EUR"

@ -1,10 +1,12 @@
require_dependency 'open_project/costs/patches/i18n_patch'
module OpenProject::Costs module OpenProject::Costs
class Engine < ::Rails::Engine class Engine < ::Rails::Engine
engine_name :openproject_costs engine_name :openproject_costs
def self.settings def self.settings
{ :default => { 'costs_currency' => 'EUR', { :default => { 'costs_currency' => 'EUR',
'costs_currency_format' => '%n %u' }, 'costs_currency_format' => '%n %u' },
:partial => 'settings/openproject_costs' } :partial => 'settings/openproject_costs' }
end end

@ -1,17 +1,30 @@
module ActionView::Helpers::NumberHelper module OpenProject::Costs::Patches
def number_to_currency_with_l10n(number, options = {}) module NumberHelper
options[:delimiter] = l(:currency_delimiter) unless options[:delimiter] def self.included(base) # :nodoc:
options[:separator] = l(:currency_separator) unless options[:separator] base.class_eval do
include InstanceMethods
options[:unit] = Setting.plugin_openproject_costs['costs_currency'] unless options[:unit] alias_method_chain :number_to_currency, :l10n
options[:format] = Setting.plugin_openproject_costs['costs_currency_format'] unless options[:format] end
end
# FIXME: patch ruby instead of this code
# this circumvents the broken BigDecimal#to_f on Siemens's ruby module InstanceMethods
number = number.to_s if number.is_a? BigDecimal def number_to_currency_with_l10n(number, options = {})
options_with_default = { unit: Setting.plugin_openproject_costs['costs_currency'],
number_to_currency_without_l10n(number, options) format: Setting.plugin_openproject_costs['costs_currency_format'],
delimiter: l(:currency_delimiter),
separator: l(:currency_separator) }.merge(options)
# FIXME: patch ruby instead of this code
# this circumvents the broken BigDecimal#to_f on Siemens's ruby
number = number.to_s if number.is_a? BigDecimal
number_to_currency_without_l10n(number, options_with_default)
end
end
end end
end
alias_method_chain :number_to_currency, :l10n unless ActionView::Helpers::NumberHelper.included_modules.include?(OpenProject::Costs::Patches::NumberHelper)
end ActionView::Helpers::NumberHelper.send(:include, OpenProject::Costs::Patches::NumberHelper)
end

@ -27,8 +27,8 @@ module OpenProject::Costs::Patches::PermittedParamsPatch
:unit, :unit,
:unit_plural, :unit_plural,
:default, :default,
:new_rate_attributes, { new_rate_attributes: [:valid_from, :rate] },
:existing_rate_attributes) { existing_rate_attributes: [:valid_from, :rate] })
end end
def labor_budget_item def labor_budget_item

@ -99,15 +99,15 @@ describe PermittedParams do
end end
it "should return new_rate_attributes" do it "should return new_rate_attributes" do
params = ActionController::Parameters.new(:cost_type => { "new_rate_attributes" => "new_rate_attributes_test" } ) params = ActionController::Parameters.new(:cost_type => { "new_rate_attributes" => { "0" => { "valid_from" => "2013-05-08", "rate" => "5002" }, "1" => { "valid_from" => "2013-05-10", "rate" => "5004" } } } )
PermittedParams.new(params, user).cost_type.should == { "new_rate_attributes" => "new_rate_attributes_test" } PermittedParams.new(params, user).cost_type.should == { "new_rate_attributes" => { "0" => { "valid_from" => "2013-05-08", "rate" => "5002" }, "1" => { "valid_from" => "2013-05-10", "rate" => "5004" } } }
end end
it "should return existing_rate_attributes" do it "should return existing_rate_attributes" do
params = ActionController::Parameters.new(:cost_type => { "existing_rate_attributes" => "new_rate_attributes_test" } ) params = ActionController::Parameters.new(:cost_type => { "existing_rate_attributes" => { "9" => { "valid_from" => "2013-05-05", "rate" => "50.0" } } } )
PermittedParams.new(params, user).cost_type.should == { "existing_rate_attributes" => "new_rate_attributes_test" } PermittedParams.new(params, user).cost_type.should == { "existing_rate_attributes" => { "9" => { "valid_from" => "2013-05-05", "rate" => "50.0" } } }
end end
it "should not return project_id" do it "should not return project_id" do

@ -5,6 +5,9 @@ describe CostTypesController do
it { get('/cost_types').should route_to(:controller => 'cost_types', it { get('/cost_types').should route_to(:controller => 'cost_types',
:action => 'index') } :action => 'index') }
it { post('/cost_types').should route_to(:controller => 'cost_types',
:action => 'create') }
it { get('/cost_types/new').should route_to(:controller => 'cost_types', it { get('/cost_types/new').should route_to(:controller => 'cost_types',
:action => 'new') } :action => 'new') }
@ -16,11 +19,12 @@ describe CostTypesController do
:action => 'update', :action => 'update',
:id => '5') } :id => '5') }
it { post('/cost_types/5/set_rate').should route_to(:controller => 'cost_types', it { put('/cost_types/5/set_rate').should route_to(:controller => 'cost_types',
:action => 'set_rate', :action => 'set_rate',
:id => '5') } :id => '5') }
it { post('/cost_types/5/toggle_delete').should route_to(:controller => 'cost_types',
:action => 'toggle_delete', it { put('/cost_types/5/toggle_delete').should route_to(:controller => 'cost_types',
:id => '5') } :action => 'toggle_delete',
:id => '5') }
end end
end end

Loading…
Cancel
Save