Merge branch 'release/4.3' into feature/rails4

Signed-off-by: Alex Coles <alex@alexbcoles.com>
pull/6827/head
Alex Coles 9 years ago
commit 1dc0fc082a
  1. 1
      app/controllers/cost_types_controller.rb
  2. 25
      app/controllers/hourly_rates_controller.rb
  3. 2
      app/views/cost_objects/_edit.html.erb
  4. 5
      app/views/cost_objects/edit.html.erb
  5. 3
      app/views/cost_objects/index.html.erb
  6. 7
      app/views/cost_objects/new.html.erb
  7. 32
      app/views/cost_objects/show.html.erb
  8. 4
      app/views/cost_types/edit.html.erb
  9. 20
      app/views/cost_types/index.html.erb
  10. 2
      app/views/costlog/_date_range.html.erb
  11. 2
      app/views/costlog/edit.html.erb
  12. 14
      app/views/costlog/index.html.erb
  13. 4
      app/views/hourly_rates/edit.html.erb
  14. 2
      app/views/hourly_rates/show.html.erb
  15. 3
      config/locales/de.yml
  16. 57
      config/locales/en.yml
  17. 6
      features/manage_budget.feature
  18. 38
      lib/open_project/costs/engine.rb
  19. 1
      spec/factories/default_hourly_rate_factory.rb
  20. 71
      spec/features/hourly_rates_feature.rb
  21. 68
      spec/models/time_entry_spec.rb
  22. 1
      spec/requests/api/cost_entries/cost_entries_by_work_package_resource_spec.rb

@ -23,6 +23,7 @@ class CostTypesController < ApplicationController
# Allow only admins here
before_filter :require_admin
before_filter :find_cost_type, only: [:edit, :update, :set_rate, :destroy, :restore]
layout 'admin'
helper :sort
include SortHelper

@ -83,8 +83,14 @@ class HourlyRatesController < ApplicationController
return deny_access unless User.current.admin?
end
@user.add_rates(@project, permitted_params.user_rates[:new_rate_attributes])
@user.set_existing_rates(@project, permitted_params.user_rates[:existing_rate_attributes])
if params.include? 'user'
update_rates @user,
@project,
permitted_params.user_rates[:new_rate_attributes],
permitted_params.user_rates[:existing_rate_attributes]
else
delete_rates @user, @project
end
if @user.save
flash[:notice] = l(:notice_successful_update)
@ -130,8 +136,21 @@ class HourlyRatesController < ApplicationController
end
end
private
def update_rates(user, project, added_rates, changed_rates)
user.add_rates(project, added_rates)
user.set_existing_rates(project, changed_rates)
end
def delete_rates(user, project)
if project.present?
user.rates.delete_all
else
user.default_rates.delete_all
end
end
private
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound

@ -29,5 +29,5 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<fieldset><legend><%= l(:label_attachment_plural )%></legend>
<p><%= render :partial => 'attachments/form' %></p>
</fieldset>
<%= submit_tag l(:button_submit), class: 'button -highlight' %>
<%= styled_button_tag l(:button_submit), class: '-with-icon icon-yes' %>
<% end %>

@ -18,9 +18,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++#%>
<% html_title "#{l(:label_edit)} #{l(:label_cost_object_id, id: @cost_object.id)}: #{@cost_object.subject}" %>
<%= render :partial => 'shared/costs_header' %>
<h2><%= l(:label_cost_object_id, id: @cost_object.id) %></h2>
<% html_title "#{l(:label_edit)} #{l(:label_cost_object_id, id: @cost_object.id)}: #{@cost_object.subject}" %>
<%= toolbar title: l(:label_cost_object_id, id: @cost_object.id) %>
<%= render :partial => 'edit' %>
<%= javascript_tag "Form.Element.focus('cost_object_subject');" %>

@ -21,8 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<%= render :partial => 'shared/costs_header' %>
<% html_title(l(:label_cost_object_plural)) %>
<h2><%=l(:label_cost_object_plural)%></h2>
<%= toolbar title: l(:label_cost_object_plural) %>
<% if @cost_objects.empty? %>
<p class="nodata"><%= l(:label_no_data) %></p>

@ -21,8 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<%= render :partial => 'shared/costs_header' %>
<% html_title(l(:label_cost_object_new)) %>
<h2><%=l(:label_cost_object_new)%></h2>
<%= toolbar title: l(:label_cost_object_new) %>
<%= labelled_tabular_form_for @cost_object,
:url => projects_cost_objects_path(@project),
@ -30,8 +29,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
:html => {:multipart => true, :id => 'cost_object_form'} do |f| %>
<%= error_messages_for 'cost_object' %>
<%= render :partial => 'form', :locals => {:f => f} %>
<%= submit_tag l(:button_create), class: 'button -highlight' %>
<%= submit_tag l(:button_create_and_continue), :name => 'continue',
<%= styled_button_tag l(:button_create), class: '-with-icon icon-yes' %>
<%= styled_button_tag l(:button_create_and_continue), :name => 'continue',
class: 'button -highlight'%>
<%= javascript_tag "Form.Element.focus('cost_object_subject');" %>

@ -19,17 +19,31 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++#%>
<%= render :partial => 'shared/costs_header' %>
<% content_for :action_menu_specific do %>
<%= link_to_if_authorized l(:button_update), {:controller => 'cost_objects', :action => 'edit', :id => @cost_object}, :class => 'icon icon-edit', :accesskey => accesskey(:edit) %>
<%= link_to_if_authorized l(:button_copy), {:controller => 'cost_objects', :action => 'copy', :id => @cost_object }, :class => 'icon icon-copy' %>
<%= link_to_if_authorized l(:button_delete), {:controller => 'cost_objects', :action => 'destroy', :id => @cost_object}, :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-delete' %>
<% end %>
<% html_title "#{l(:label_cost_object_id, id: @cost_object.id)}: #{@cost_object.subject}" %>
<h2><%= l(:label_cost_object_id, :id => @cost_object.id) %></h2>
<%= render :partial => 'layouts/action_menu_specific' %>
<%= toolbar title: l(:label_cost_object_id, id: @cost_object.id) do %>
<% if authorize_for(:cost_objects, :edit) %>
<li class="toolbar-item">
<%= link_to({ controller: 'cost_objects', action: 'edit', id: @cost_object }, class: 'button', accesskey: accesskey(:edit)) do %>
<i class="button--icon icon-edit"></i> <%= l(:button_update) %>
<% end %>
</li>
<% end %>
<% if authorize_for(:cost_objects, :copy) %>
<li class="toolbar-item">
<%= link_to({ controller: 'cost_objects', action: 'copy', id: @cost_object }, class: 'button') do %>
<i class="button--icon icon-copy"></i> <%= l(:button_copy) %>
<% end %>
</li>
<% end %>
<% if authorize_for(:cost_objects, :copy) %>
<li class="toolbar-item">
<%= link_to({ controller: 'cost_objects', action: 'destroy', id: @cost_object }, class: 'button', method: :delete, data: { confirm: l(:text_are_you_sure)}) do %>
<i class="button--icon icon-delete"></i> <%= l(:button_delete) %>
<% end %>
</li>
<% end %>
<% end %>
<div class="<%= @cost_object.css_classes %> details">
<h3><%=h @cost_object.subject %></h3>

@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<% html_title l(:label_cost_type_specific, id: @cost_type.id, name: @cost_type.name) %>
<h2><%= CostType.model_name.human %></h2>
<%= toolbar title: CostType.model_name.human %>
<%= labelled_tabular_form_for @cost_type do |f| %>
<%= error_messages_for 'cost_type' %>
@ -73,7 +73,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<%= link_to_function l(:button_add_rate), "addRate($('add_rate_date'))", {:class => "button icon icon-add"} %>
</div>
<hr class="form--separator">
<%= submit_tag l(:button_save), class: 'button -highlight' %>
<%= styled_button_tag l(:button_save), class: '-with-icon icon-yes' %>
<% end %>

@ -19,15 +19,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++#%>
<%= render :partial => 'shared/costs_header' %>
<% content_for :action_menu_specific do %>
<%= link_to l(:button_add_cost_type), new_cost_type_path, :class => 'icon icon-add' %>
<% end %>
<% html_title l(:cost_types_title) %>
<h2><%= CostType.model_name.human(:count => 2) %></h2>
<%= render :partial => 'layouts/action_menu_specific' %>
<% html_title l(:label_cost_type_plural) %>
<%= toolbar title: CostType.model_name.human(count: 2) do %>
<li class="toolbar-item">
<%= link_to new_cost_type_path, class: 'button -alt-highlight' do%>
<i class="button--icon icon-add"></i> <%= l(:button_add_cost_type) %>
<% end %>
</li>
<% end %>
<%= styled_form_tag(cost_types_path, { :method => :get, :id => 'query_form' }) do %>
<fieldset id="filters">
@ -47,13 +47,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
{ :update => "content",
:with => "Form.serialize('query_form')",
:method => :get
}, :class => 'button -highlight' %>
}, :class => 'button -with-icon icon-yes' %>
<%= link_to_remote l(:button_clear),
{ :url => { :clear_filter => true },
:method => :get,
:update => "content",
}, :class => 'button' %>
}, :class => 'button icon-context icon-undo' %>
<% end %>
<div id="cost_type_flash_notice_outer" style="display:none">

@ -45,5 +45,5 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<%= calendar_for :to %>
</div>
</div>
<%= submit_tag l(:button_apply), :name => nil, class: 'button -highlight' %>
<%= styled_button_tag l(:button_apply), class: '-with-icon icon-yes' %>
</fieldset>

@ -100,7 +100,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
</div>
</div>
<%= submit_tag l(:button_save), class: 'button -highlight' %>
<%= styled_button_tag l(:button_save), class: '-with-icon icon-yes' %>
<% end %>
<% content_for :header_tags do %>

@ -20,15 +20,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<%= render :partial => 'shared/costs_header' %>
<% content_for :action_menu_specific do %>
<%= link_to_if_authorized l(:button_log_costs), {:controller => '/costlog', :action => 'new', :work_package_id => @work_package}, :class => 'icon-context icon-unit' %>
<% end %>
<%= render_costlog_breadcrumb %>
<h2><%= WorkPackage.human_attribute_name(:spent_costs) %></h2>
<%= render :partial => 'layouts/action_menu_specific' %>
<%= toolbar title: WorkPackage.human_attribute_name(:spent_costs) do %>
<% if authorize_for(:costlog, :new) %>
<%= link_to({:controller => '/costlog', :action => 'new', :work_package_id => @work_package }, class: 'button') do %>
<i class="button--icon icon-unit"></i> <%= l(:button_log_costs) %>
<% end %>
<% end %>
<% end %>
<%= form_remote_tag( :url => {}, :method => :get, :update => 'content' ) do %>
<%= hidden_field_tag('project_id', params[:project_id]) if @project %>

@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<% html_title l(:label_administration), "#{l(:label_edit)} #{l((@project.nil? ? :caption_default_rate_history_for : :caption_rate_history_for), user: ' ')} #{@user.name}" %>
<h2><%= @project.nil? ? l(:caption_default_rate_history_for, :user => @user.name) : l(:caption_rate_history_for_project, :user => @user.name, :project => @project.name) %></h2>
<%= toolbar title: (@project.nil? ? l(:caption_default_rate_history_for, :user => @user.name) : l(:caption_rate_history_for_project, :user => @user.name, :project => @project.name) ) %>
<%- default_rate = @user.current_default_rate -%>
<% if default_rate%>
@ -56,5 +56,5 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<label class="hidden-for-sighted", for="add_rate_date" %>"><%= l(:description_date_for_new_rate) %></label>
<%= link_to_function l(:button_add_rate), "addRate($('add_rate_date'))", {:class => "button icon icon-add"} %>
</div>
<div><%= submit_tag l(:button_save), class: 'button -highlight' %></div>
<div><%= styled_button_tag l(:button_save), class: '-with-icon icon-yes' %></div>
<% end %>

@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<% html_title l(:label_administration), l(:caption_rate_history_for, user: @user.name) %>
<h2><%= l(:caption_rate_history_for, :user => @user.name) %></h2>
<%= toolbar title: l(:caption_rate_history_for, :user => @user.name) %>
<% if @project %>
<% html_title l(:caption_rate_history_for, user: @user.name) %>

@ -23,7 +23,7 @@ de:
attributes:
cost_entry:
work_package: "Ticket"
work_package: "Arbeitspacket"
work_package: "Arbeitspaket"
overridden_costs: "Überschriebene Kosten"
spent: "Gebucht"
spent_on: "Datum"
@ -104,7 +104,6 @@ de:
caption_set_rate: "Aktuellen Satz festlegen"
caption_show_locked: "Gesperrte Typen anzeigen"
cost_objects_title: "Budgets"
cost_types_title: "Kostenarten"
currency_delimiter: "."
currency_separator: ","

@ -22,7 +22,6 @@ en:
activerecord:
attributes:
cost_entry:
work_package: "WorkPackage"
work_package: "Work package"
overridden_costs: "Overridden costs"
spent: "Spent"
@ -32,17 +31,17 @@ en:
available: "Available"
budget: "Planned"
budget_ratio: "Spent (ratio)"
created_on: "Created On"
created_on: "Created on"
description: "Description"
fixed_date: "Fixed date"
spent: "Spent"
status: "Status"
subject: "Subject"
type: "Cost type"
updated_on: "Updated On"
updated_on: "Updated on"
cost_type:
unit: "Unit Name"
unit_plural: "Pluralized Unit Name"
unit: "Unit name"
unit_plural: "Pluralized unit name"
work_package:
cost_object_subject: "Budget title"
labor_costs: "Labor costs"
@ -55,8 +54,8 @@ en:
user:
default_rates: "Default rates"
variable_cost_object:
labor_budget: "Planned Labor Costs"
material_budget: "Planned Unit Costs"
labor_budget: "Planned labor costs"
material_budget: "Planned unit costs"
models:
cost_object: "Budget"
cost_type:
@ -80,8 +79,8 @@ en:
units: "Units"
valid_from: "Valid from"
button_add_budget_item: "Add Planned Costs"
button_add_cost_object: "Add Budget"
button_add_budget_item: "Add planned costs"
button_add_cost_object: "Add budget"
button_add_cost_type: "Add cost type"
button_add_rate: "Add rate"
button_cancel_edit_budget: "Cancel editing budget"
@ -89,22 +88,22 @@ en:
button_log_costs: "Log unit costs"
button_log_time: "Log time"
caption_booked_on_project: "Booked on Project"
caption_booked_on_project: "Booked on project"
caption_default: "Default"
caption_default_rate_history_for: "Default Rate History for %{user}"
caption_default_rate_history_for: "Default rate history for %{user}"
caption_labor: "Labor"
caption_labor_costs: "Actual Labor Costs"
caption_labor_costs: "Actual labor costs"
caption_locked_on: "Locked on"
caption_material_costs: "Actual Unit Costs"
caption_material_costs: "Actual unit costs"
caption_materials: "Units"
caption_rate_history: "Rate History"
caption_rate_history_for: "Rate History for %{user}"
caption_rate_history_for_project: "Rate History for %{user} in Project %{project}"
caption_rate_history: "Rate history"
caption_rate_history_for: "Rate history for %{user}"
caption_rate_history_for_project: "Rate history for %{user} in project %{project}"
caption_save_rate: "Save rate"
caption_set_rate: "Set Current Rate"
caption_set_rate: "Set current rate"
caption_show_locked: "Show locked types"
cost_objects_title: "Budgets"
cost_types_title: "Cost Types"
label_cost_type_plural: "Cost types"
currency_delimiter: ","
currency_separator: "."
@ -124,18 +123,18 @@ en:
label_costlog: "Logged unit costs"
label_cost_object: "Budget"
label_cost_object_id: "Budget #%{id}"
label_cost_object_new: "New Budget"
label_cost_object_new: "New budget"
label_cost_object_plural: "Budgets"
label_cost_plural: "Costs"
label_cost_report: "Cost Report"
label_cost_report: "Cost report"
label_cost_type_specific: "Budget #%{id}: %{name}"
label_cost_type_plural: "Cost Types"
label_cost_type_plural: "Cost types"
label_costs_per_page: "Costs per page"
label_currency: "Currency"
label_currency_format: "Format of currency"
label_current_default_rate: "Current default rate"
label_date_on: "on"
label_deleted_cost_types: "Deleted Cost Types"
label_deleted_cost_types: "Deleted cost types"
label_deliverable: "Budget"
label_display_cost_entries: "Display unit costs"
label_display_time_entries: "Display reported hours"
@ -143,7 +142,7 @@ en:
label_edit: "Edit"
label_fixed_cost_object: "Fixed budget"
label_fixed_date: "Fixed date"
label_generic_user: "Generic User"
label_generic_user: "Generic user"
label_greater_or_equal: ">="
label_group_by: "Group by"
label_group_by_add: "Add grouping field"
@ -164,7 +163,7 @@ en:
label_until: "until"
label_valid_from: "Valid from"
label_variable_cost_object: "Variable rate based budget"
label_view_all_cost_objects: "View all Budgets"
label_view_all_cost_objects: "View all budgets"
label_yes: "Yes"
notice_cost_object_conflict: "WorkPackages must be of the same project."
@ -173,7 +172,7 @@ en:
notice_successful_restore: "Successful restore."
permission_edit_cost_entries: "Edit booked unit costs"
permission_edit_cost_objects: "Edit Budgets"
permission_edit_cost_objects: "Edit budgets"
permission_edit_own_cost_entries: "Edit own booked unit costs"
permission_edit_hourly_rates: "Edit hourly rates"
permission_edit_own_hourly_rate: "Edit own hourly rates"
@ -181,13 +180,13 @@ en:
permission_log_costs: "Book unit costs"
permission_log_own_costs: "Book unit costs for oneself"
permission_view_cost_entries: "View booked costs"
permission_view_cost_objects: "View Budgets"
permission_view_cost_objects: "View budgets"
permission_view_cost_rates: "View cost rates"
permission_view_hourly_rates: "View all hourly rates"
permission_view_own_cost_entries: "View own booked costs"
permission_view_own_hourly_rate: "View own hourly rate"
permission_view_own_time_entries: "View own spent time"
project_module_costs_module: "Cost Control"
project_module_costs_module: "Cost control"
text_assign_time_and_cost_entries_to_project: "Assign reported hours and costs to the project"
text_cost_object_change_type_confirmation: "Are you sure? This operation will destroy information of the specific budget type."
@ -200,8 +199,8 @@ en:
week: "week"
x_entries:
one: "1 Entry"
other: "%{count} Entries"
one: "1 entry"
other: "%{count} entries"
js:
text_are_you_sure: "Are you sure?"

@ -93,8 +93,7 @@ Feature: Managing Budgets
| project | project1 |
And I go to the show page of the budget "budget1"
When I click on "Update"
Then I should be able to update the budget "budget1"
When I create a material item in row 1 with the following:
And I create a material item in row 1 with the following:
| units | 10 |
| comment | materialtestcomment |
Then the planned material costs in row 1 should be "400.00 EUR"
@ -127,8 +126,7 @@ Feature: Managing Budgets
| 5 | labortestcomment2 | testuser |
And I go to the show page of the budget "budget1"
And I click on "Update"
Then I should be able to update the budget "budget1"
When I update the material item in row 1 with the following:
And I update the material item in row 1 with the following:
| units | 5 |
| comment | changed_materialtestcomment |
Then the planned material costs in row 1 should be "200.00 EUR"

@ -65,35 +65,35 @@ module OpenProject::Costs
end
# Menu extensions
menu :top_menu,
menu :admin_menu,
:cost_types,
{:controller => '/cost_types', :action => 'index'},
:caption => :cost_types_title,
:if => Proc.new { User.current.admin? }
{ controller: '/cost_types', action: 'index' },
html: { class: 'icon2 icon-tracker' },
caption: :label_cost_type_plural
menu :project_menu,
:cost_objects,
{:controller => '/cost_objects', :action => 'index'},
:param => :project_id,
:before => :settings,
:caption => :cost_objects_title,
:html => {:class => 'icon2 icon-budget'}
{ controller: '/cost_objects', action: 'index' },
param: :project_id,
before: :settings,
caption: :cost_objects_title,
html: { class: 'icon2 icon-budget' }
menu :project_menu,
:new_budget,
{:controller => '/cost_objects', :action => 'new' },
:param => :project_id,
:caption => :label_cost_object_new,
:parent => :cost_objects,
:html => {:class => 'icon2 icon-add'}
{ controller: '/cost_objects', action: 'new' },
param: :project_id,
caption: :label_cost_object_new,
parent: :cost_objects,
html: { class: 'icon2 icon-add' }
menu :project_menu,
:show_all,
{:controller => '/cost_objects', :action => 'index' },
:param => :project_id,
:caption => :label_view_all_cost_objects,
:parent => :cost_objects,
:html => {:class => 'icon2 icon-list-view1'}
{ controller: '/cost_objects', action: 'index' },
param: :project_id,
caption: :label_view_all_cost_objects,
parent: :cost_objects,
html: { class: 'icon2 icon-list-view1' }
Redmine::Activity.map do |activity|
activity.register :cost_objects, class_name: 'Activity::CostObjectActivityProvider', default: false

@ -20,7 +20,6 @@
FactoryGirl.define do
factory :default_hourly_rate do
association :user, :factory => :user
association :project, :factory => :project
valid_from Date.today
rate 50.0
end

@ -0,0 +1,71 @@
#-- copyright
# OpenProject Costs Plugin
#
# Copyright (C) 2009 - 2014 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#++
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper.rb")
describe 'hourly rates', type: :feature, js: true do
let(:user) { FactoryGirl.create :admin }
def view_rates
visit edit_user_path(user)
click_on 'Rate History'
end
before do
allow(User).to receive(:current).and_return user
end
context 'with no rates' do
before do
view_rates
end
it 'shows no data message' do
expect(page).to have_text 'No data to display'
end
end
context 'with rates' do
let!(:rate) { FactoryGirl.create(:default_hourly_rate, user: user) }
before do
view_rates
end
it 'shows the rates' do
expect(page).to have_text 'Current rate'
end
describe 'deleting all rates' do
before do
click_link 'Update' # go to update view for rates
click_on 'Delete' # delete last existing rate
click_on 'Save' # save change
end
# regression test: clicking save used to result in a error
it 'leads back to the now empty rate overview' do
expect(page).to have_text /rate history/i
expect(page).to have_text 'No data to display'
expect(page).not_to have_text 'Current rate'
end
end
end
end

@ -351,5 +351,73 @@ describe TimeEntry, :type => :model do
it { expect(TimeEntry.visible(time_entry2.user, project).all).to match_array([time_entry2]) }
end
end
context 'calculate dates' do
let(:my_role) { FactoryGirl.create(:role, permissions: [:view_own_time_entries]) }
let(:user3) do
FactoryGirl.create(:user, member_in_project: project, member_through_role: my_role)
end
let(:late_time_entry) do
FactoryGirl.create(:time_entry,
project: project,
work_package: work_package,
spent_on: 2.days.ago,
hours: hours,
user: user3,
rate: hourly_one,
comments: 'ipsum')
end
let(:early_time_entry) do
FactoryGirl.create(:time_entry,
project: project,
work_package: work_package,
spent_on: 4.days.ago,
hours: hours,
user: user3,
rate: hourly_one,
comments: 'dolor')
end
let(:other_time_entry) do
FactoryGirl.create(:time_entry,
project: project,
work_package: work_package,
spent_on: 6.days.ago,
hours: hours,
user: user2,
rate: hourly_one,
comments: 'dolor')
end
let(:another_time_entry) do
FactoryGirl.create(:time_entry,
project: project,
work_package: work_package,
spent_on: 1.days.ago,
hours: hours,
user: user2,
rate: hourly_one,
comments: 'dolor')
end
before do
early_time_entry.save!
late_time_entry.save!
other_time_entry.save!
another_time_entry.save!
allow(User).to receive(:current).and_return(user3)
end
describe '#earliest_date_for_project' do
it 'returns the earliest date' do
expect(TimeEntry.earliest_date_for_project(project)).to eq(early_time_entry.spent_on)
end
end
describe '#latest_date_for_project' do
it 'returns the latest date' do
expect(TimeEntry.latest_date_for_project(project)).to eq(late_time_entry.spent_on)
end
end
end
end
end

@ -95,6 +95,7 @@ describe 'API v3 Cost Entry resource' do
context 'user can see own cost entries' do
let(:cost_entry_permissions) { [:view_own_cost_entries] }
it 'should return HTTP 200' do
expect(response.status).to eql(200)
end

Loading…
Cancel
Save