commit
ef4847fe97
@ -1,64 +0,0 @@ |
||||
#-- 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. |
||||
#++ |
||||
|
||||
Feature: Managing Budgets |
||||
|
||||
Background: |
||||
Given there is 1 User with: |
||||
| Login | testuser | |
||||
| firstname | Chuck | |
||||
| lastname | Testa | |
||||
| default rate | 37 | |
||||
And there is 1 Project with the following: |
||||
| name | project1 | |
||||
| identifier | project1 | |
||||
And there is a role "manager" |
||||
And the role "manager" may have the following rights: |
||||
| edit_cost_objects | |
||||
| view_cost_rates | |
||||
| view_hourly_rates | |
||||
And there is 1 cost type with the following: |
||||
| name | cost_type_1 | |
||||
| unit | single_unit | |
||||
| unit_plural | multi_unit | |
||||
| cost_rate | 40 | |
||||
And the user "testuser" is a "manager" in the project "project1" |
||||
And I am already logged in as "testuser" |
||||
|
||||
@javascript |
||||
Scenario: Budgets can be copied |
||||
Given there is a budget with the following: |
||||
| subject | budget1 | |
||||
| author | testuser | |
||||
| project | project1 | |
||||
And the budget "budget1" has the following material items: |
||||
| units | comment | cost_type | |
||||
| 10 | materialtestcomment | cost_type_1 | |
||||
| 6 | materialtestcomment2 | cost_type_1 | |
||||
And the budget "budget1" has the following labor items: |
||||
| hours | comment | user | |
||||
| 8 | labortestcomment | testuser | |
||||
| 5 | labortestcomment2 | testuser | |
||||
And I go to the show page of the budget "budget1" |
||||
When I click on "Copy" |
||||
Then I should see "New budget" |
||||
And the planned material costs in row 1 should be "400.00 EUR" |
||||
And the planned labor costs in row 1 should be "296.00 EUR" |
||||
And the planned material costs in row 2 should be "240.00 EUR" |
||||
And the planned labor costs in row 2 should be "185.00 EUR" |
@ -0,0 +1,107 @@ |
||||
// -- copyright
|
||||
// OpenProject is a project management system.
|
||||
// Copyright (C) 2012-2015 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.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// See doc/COPYRIGHT.rdoc for more details.
|
||||
// ++
|
||||
|
||||
export class CostUnitSubformController { |
||||
|
||||
public objId: string; |
||||
public objName: string; |
||||
|
||||
constructor(public $element) { |
||||
// Add new row handler
|
||||
$element.find('#'+this.objId).click(() => { |
||||
this.makeEditable('#'+this.objId, this.objName); |
||||
}); |
||||
} |
||||
|
||||
private getCurrencyValue(str) { |
||||
var result = str.match(/^\s*(([0-9]+[.,])+[0-9]+) (.+)\s*/); |
||||
return result ? new Array(result[1], result[3]) : new Array(str, ""); |
||||
} |
||||
|
||||
public makeEditable(id, name){ |
||||
var obj = jQuery(id); |
||||
|
||||
jQuery(id).on('click', this.edit_and_focus(obj, name)); |
||||
} |
||||
|
||||
private edit_and_focus(obj, name) { |
||||
this.edit(obj, name); |
||||
|
||||
jQuery('#'+obj[0].id+'_edit').focus(); |
||||
jQuery('#'+obj[0].id+'_edit').select(); |
||||
} |
||||
|
||||
private edit(obj, name, obj_value) { |
||||
obj.hide(); |
||||
|
||||
var obj_value = typeof(obj_value) != 'undefined' ? obj_value : obj[0].innerHTML; |
||||
var parsed = this.getCurrencyValue(obj_value); |
||||
var value = parsed[0]; |
||||
var currency = parsed[1]; |
||||
|
||||
var form_start = '<section class="form--section" id="'+obj[0].id+ |
||||
'_section"><div class="form--field"><div class="form--field-container">'; |
||||
var button = '<div id="'+obj[0].id+ |
||||
'_cancel" class="form--field-affix -transparent icon icon-close"></div>'; |
||||
var span = '<div id="'+obj[0].id+'_editor" class="form--text-field-container">'; |
||||
span += '<input id="'+obj[0].id+'_edit" class="form--text-field" name="'+name+'" value="'+value+'" class="currency" type="text" /> '; |
||||
span += '</div>'; |
||||
|
||||
var affix = '<div class="form--field-affix" id="'+obj[0].id+'_affix">' + |
||||
currency + |
||||
'</div>'; |
||||
var form_end = '</div></div></section>'; |
||||
|
||||
jQuery(form_start + button + span + affix + form_end).insertAfter(obj); |
||||
|
||||
var that = this; |
||||
jQuery('#'+obj[0].id+'_cancel').on('click', function() { |
||||
that.cleanUp(obj) |
||||
}); |
||||
} |
||||
|
||||
private cleanUp(obj){ |
||||
jQuery('#'+obj[0].id+'_section').remove(); |
||||
obj.show(); |
||||
} |
||||
} |
||||
|
||||
function costUnitSubform() { |
||||
return { |
||||
restrict: 'E', |
||||
scope: { |
||||
objId: '@', |
||||
objName: '@' |
||||
}, |
||||
bindToController: true, |
||||
controller: CostUnitSubformController, |
||||
controllerAs: '$ctrl' |
||||
}; |
||||
} |
||||
|
||||
angular.module('openproject').directive('costUnitSubform', costUnitSubform); |
@ -0,0 +1,70 @@ |
||||
#-- 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 'Work Package table cost sums', type: :feature, js: true do |
||||
let(:project) { FactoryGirl.create :project } |
||||
let(:user) { FactoryGirl.create :admin } |
||||
|
||||
let(:parent) { FactoryGirl.create :work_package, project: project } |
||||
let(:work_package) { FactoryGirl.create :work_package, project: project, parent: parent } |
||||
let(:hourly_rate) { FactoryGirl.create :default_hourly_rate, user: user, rate: 1.00 } |
||||
|
||||
let!(:time_entry1) { |
||||
FactoryGirl.create :time_entry, |
||||
user: user, |
||||
work_package: parent, |
||||
project: project, |
||||
hours: 10 |
||||
} |
||||
|
||||
let!(:time_entry2) { |
||||
FactoryGirl.create :time_entry, |
||||
user: user, |
||||
work_package: work_package, |
||||
project: project, |
||||
hours: 2.50 |
||||
} |
||||
|
||||
let(:wp_table) { ::Pages::WorkPackagesTable.new(project) } |
||||
let!(:query) do |
||||
query = FactoryGirl.build(:query, user: user, project: project) |
||||
query.column_names = %w(id subject spent_hours) |
||||
|
||||
query.save! |
||||
query |
||||
end |
||||
|
||||
before do |
||||
login_as(user) |
||||
|
||||
wp_table.visit_query(query) |
||||
wp_table.expect_work_package_listed(parent) |
||||
wp_table.expect_work_package_listed(work_package) |
||||
end |
||||
|
||||
it 'shows the correct sum of the time entries' do |
||||
parent_row = wp_table.row(parent) |
||||
wp_row = wp_table.row(work_package) |
||||
|
||||
expect(parent_row).to have_selector('.wp-edit-field.spentTime', text: '12.5 hours') |
||||
expect(wp_row).to have_selector('.wp-edit-field.spentTime', text: '2.5 hours') |
||||
end |
||||
end |
@ -0,0 +1,123 @@ |
||||
#-- 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 'spec_helper' |
||||
|
||||
describe WorkPackage, 'cost eager loading', type: :model do |
||||
let(:project) do |
||||
work_package.project |
||||
end |
||||
let(:role) do |
||||
FactoryGirl.create(:role, |
||||
permissions: [:view_work_packages, |
||||
:view_cost_entries, |
||||
:view_cost_rates, |
||||
:view_time_entries, |
||||
:log_time, |
||||
:log_costs, |
||||
:view_hourly_rates]) |
||||
end |
||||
let(:user) do |
||||
FactoryGirl.create(:user, |
||||
member_in_project: project, |
||||
member_through_role: role) |
||||
end |
||||
|
||||
let(:cost_type) do |
||||
FactoryGirl.create(:cost_type) |
||||
end |
||||
let(:work_package) do |
||||
FactoryGirl.create(:work_package) |
||||
end |
||||
let(:cost_entry1) do |
||||
FactoryGirl.create(:cost_entry, |
||||
cost_type: cost_type, |
||||
user: user, |
||||
work_package: work_package, |
||||
project: project) |
||||
end |
||||
let(:cost_entry2) do |
||||
FactoryGirl.create(:cost_entry, |
||||
cost_type: cost_type, |
||||
user: user, |
||||
work_package: work_package, |
||||
project: project) |
||||
|
||||
end |
||||
let(:time_entry1) do |
||||
FactoryGirl.create(:time_entry, |
||||
user: user, |
||||
project: project, |
||||
work_package: work_package) |
||||
end |
||||
let(:time_entry2) do |
||||
FactoryGirl.create(:time_entry, |
||||
user: user, |
||||
project: project, |
||||
work_package: work_package) |
||||
end |
||||
let(:user_rates) do |
||||
FactoryGirl.create(:hourly_rate, |
||||
user: user, |
||||
project: project) |
||||
end |
||||
let(:cost_rate) do |
||||
FactoryGirl.create(:cost_rate, |
||||
cost_type: cost_type) |
||||
end |
||||
|
||||
context "combining core's and cost's eager loading" do |
||||
let(:scope) do |
||||
|
||||
scope = WorkPackage |
||||
.include_spent_hours(user) |
||||
.where(id: [work_package.id]) |
||||
|
||||
OpenProject::Costs::Engine::EagerLoadedCosts.join_costs(scope) |
||||
end |
||||
|
||||
before do |
||||
allow(User) |
||||
.to receive(:current) |
||||
.and_return(user) |
||||
|
||||
user_rates |
||||
project.reload |
||||
cost_rate |
||||
cost_entry1 |
||||
cost_entry2 |
||||
time_entry1 |
||||
time_entry2 |
||||
end |
||||
|
||||
subject { scope.first } |
||||
|
||||
it 'correctly calculates spent time' do |
||||
expect(scope.to_a.first.hours).to eql time_entry1.hours + time_entry2.hours |
||||
end |
||||
|
||||
it 'correctly calculates labor costs' do |
||||
expect(scope.first.labor_costs).to eql (user_rates.rate * (time_entry1.hours + time_entry2.hours)).to_f |
||||
end |
||||
|
||||
it 'correctly calculates material costs' do |
||||
expect(scope.first.material_costs).to eql (cost_entry1.costs + cost_entry2.costs).to_f |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue