Rewrite rate and edit views

pull/6827/head
Oliver Günther 8 years ago
parent cd8a1319c2
commit 4e49c8800d
No known key found for this signature in database
GPG Key ID: 88872239EB414F99
  1. 9
      app/controllers/cost_objects_controller.rb
  2. 2
      app/views/cost_objects/items/_material_budget_item.html.erb
  3. 2
      app/views/cost_objects/subform/_labor_budget_subform.html.erb
  4. 2
      app/views/cost_objects/subform/_material_budget_subform.html.erb
  5. 32
      app/views/costlog/edit.html.erb
  6. 6
      app/views/hourly_rates/_rate.html.erb
  7. 22
      app/views/hourly_rates/edit.html.erb
  8. 1
      app/views/shared/_costs_header.html.erb
  9. 4
      frontend/app/components/budget/cost-budget-subform.directive.ts
  10. 92
      frontend/app/components/subform/cost-subform.directive.ts
  11. 3
      spec/features/members_hourly_rates_spec.rb
  12. 46
      spec/features/update_budget_spec.rb

@ -173,9 +173,9 @@ class CostObjectsController < ApplicationController
end
def update_material_budget_item
@element_id = params[:element_id] if params.has_key? :element_id
@element_id = params[:element_id] if params[:element_id].present?
@cost_type = CostType.find(params[:cost_type_id]) if params.has_key? :cost_type_id
@cost_type = CostType.find(params[:cost_type_id]) if params[:cost_type_id].present?
@units = BigDecimal.new(Rate.clean_currency(params[:units]))
@costs = (@units * @cost_type.rate_at(params[:fixed_date]).rate rescue 0.0)
@ -186,9 +186,8 @@ class CostObjectsController < ApplicationController
end
def update_labor_budget_item
@element_id = params[:element_id] if params.has_key? :element_id
@user = User.find(params[:user_id])
@element_id = params[:element_id] if params[:element_id].present?
@user = User.find(params[:user_id]) if params[:user_id].present?
@hours = params[:hours].to_hours
@costs = @hours * @user.rate_at(params[:fixed_date], @project).rate rescue 0.0

@ -72,7 +72,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
</a>
</td>
<td class="delete budget-table--fields buttons -short">
<a class="no-decoration-on-hover" title="<%= t(:button_delete) %>" ng-click="$ctrl.removeRow(<%= id_prefix %>)" href="">
<a class="delete-budget-item no-decoration-on-hover" title="<%= t(:button_delete) %>">
<span class="icon-context icon-delete"></span>
<span class="hidden-for-sighted"><%= t(:button_delete) %></span>
</a>

@ -93,7 +93,7 @@ end -%>
</div>
</div>
<div class="generic-table--action-buttons">
<a class="budget-add-row button -alt-highlight -with-icon icon-context icon-add" ng-click="$ctrl.addBudgetItem()">
<a class="budget-add-row button -alt-highlight -with-icon icon-context icon-add">
<%= t(:button_add_budget_item) %>
</a>
</div>

@ -100,7 +100,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
</div>
</div>
<div class="generic-table--action-buttons">
<a class="budget-add-row button -alt-highlight -with-icon icon-context icon-add">
<a href="javascript:" class="budget-add-row button -alt-highlight -with-icon icon-context icon-add">
<%= t(:button_add_budget_item) %>
</a>
</div>

@ -30,13 +30,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
:post :
:put %>
<remote-field-updater url="<%= url_for(controller: :cost_objects, action: :update_material_budget_item, project_id: @cost_entry.project) %>"
method="POST">
<%= labelled_tabular_form_for @cost_entry, url: url, html: { method: method } do |f| %>
<%= error_messages_for 'cost_entry' %>
<%= back_url_hidden_field_tag %>
<%= f.hidden_field :element_id, value: 'cost_entry', class: 'remote-field--input', data: { :'remote-field-key' =>'element_id' } %>
<div class="box">
<div class="form--field">
<%= f.text_field :work_package_id, size: 6, required: true %>
<%= f.text_field :work_package_id, size: 6, required: true, autofocus: true %>
<% if @cost_entry.work_package %>
<div class="form--field-instructions">
@ -45,7 +48,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<% end %>
</div>
<div class="form--field">
<%= f.text_field :spent_on, size: 10, required: true %>
<%= f.text_field :spent_on,
size: 10,
required: true,
class: 'remote-field--input',
data: { :'remote-field-key' =>'fixed_date' } %>
<label for="cost_entry_spent_on" class="hidden-for-sighted"><%= l(:label_date) %> <%=l(:text_hint_date_format) %></label>
<%= calendar_for('cost_entry_spent_on') %>
</div>
@ -59,7 +66,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<% end %>
<div class="form--field">
<%# see above %>
<%= f.select :cost_type_id, cost_types_collection_for_select_options, required: true, prompt: true %></p>
<%= f.select :cost_type_id,
cost_types_collection_for_select_options,
{ prompt: true },
{
required: true,
class: 'remote-field--input',
data: { :'remote-field-key' => 'cost_type_id' }
} %></p>
</div>
<div class="form--field">
<% if @cost_entry.cost_type.nil? %>
@ -70,11 +84,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
size: 6,
required: true,
suffix: h(suffix),
suffix_id: 'cost_entry_unit_name' %>
suffix_id: 'cost_entry_unit_name',
class: 'remote-field--input',
data: { :'remote-field-key' => 'units' } %>
<% end %>
<%= observe_field( "cost_entry_cost_type_id", url: {controller: :cost_objects, action: :update_material_budget_item, project_id: @cost_entry.project}, with: "'cost_type_id=' + encodeURIComponent(value) + '&units=' + encodeURIComponent(document.getElementById('cost_entry_units').value) + '&fixed_date=' + encodeURIComponent(document.getElementById('cost_entry_spent_on').value) + '&element_id=cost_entry'") %>
<%= observe_field( "cost_entry_units", frequency: 1, url: {controller: :cost_objects, action: :update_material_budget_item, project_id: @cost_entry.project}, with: "'cost_type_id=' + encodeURIComponent(document.getElementById('cost_entry_cost_type_id').value) + '&units=' + encodeURIComponent(value) + '&fixed_date=' + encodeURIComponent(document.getElementById('cost_entry_spent_on').value) + '&element_id=cost_entry'") %>
<%= observe_field( "cost_entry_spent_on", frequency: 1, url: {controller: :cost_objects, action: :update_material_budget_item, project_id: @cost_entry.project}, with: "'cost_type_id=' + encodeURIComponent(document.getElementById('cost_entry_cost_type_id').value) + '&units=' + encodeURIComponent(document.getElementById('cost_entry_units').value) + '&fixed_date=' + encodeURIComponent(value) + '&element_id=cost_entry'") %>
</div>
<div class="form--field">
@ -84,10 +97,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<a href="javascript:;" id="cost_entry_costs" class="icon-context icon-edit" title="<%= t(:help_click_to_edit) %>">
<%= number_to_currency(@cost_entry.calculated_costs) %>
</a>
<%= update_page_tag do |page|
page << "makeEditable('cost_entry_costs', 'cost_entry[overridden_costs]');"
page << "edit($('cost_entry_costs'), 'cost_entry[overridden_costs]', '#{number_to_currency(@cost_entry.overridden_costs)}');" if @cost_entry.overridden_costs
end %>
<% else %>
<span id="cost_entry_costs_editor" class="form--text-field-container">
<input class="currency form--text-field" value="<%= number_to_currency(@cost_entry.overridden_costs, unit: "").strip if @cost_entry.overridden_costs %>" size="7" name="cost_entry[overridden_costs]" id="cost_entry_costs_edit"/> <%= Setting.plugin_openproject_costs['costs_currency'] %>
@ -104,3 +113,4 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<%= styled_button_tag t(:button_save), class: '-with-icon icon-checkmark' %>
<% end %>
</remote-field-updater>

@ -19,6 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++#%>
<%-
templated ||= false
index ||= "INDEX"
new_or_existing = rate.new_record? ? 'new' : 'existing'
id_or_index = rate.new_record? ? index : rate.id
@ -26,10 +27,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
id_prefix = "user_#{new_or_existing}_rate_attributes_#{id_or_index}"
name_prefix = "user[#{new_or_existing}_rate_attributes][#{id_or_index}]"
classes ||= ""
classes += " subform-row-template" if templated
-%>
<%= fields_for prefix, rate do |rate_form| %>
<tr class="<%= classes %>" id="<%= id_prefix %>">
<tr class="subform-row <%= classes %>" id="<%= id_prefix %>">
<td>
<label class="hidden-for-sighted" for="<%= "#{id_prefix}_valid_from" %>"><%= Rate.human_attribute_name(:valid_from) %></label>
<%= rate_form.text_field :valid_from, :size => 10, :class => 'date', :index => id_or_index %><%= calendar_for("#{id_prefix}_valid_from") %>
@ -44,7 +46,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
</span>
</td>
<td class="buttons">
<a href="#" class="delete-rate no-decoration-on-hover"><%= icon_wrapper('icon-context icon-delete', I18n.t(:button_delete)) %></a>
<a href="javascript:" class="delete-row-button no-decoration-on-hover"><%= icon_wrapper('icon-context icon-delete', I18n.t(:button_delete)) %></a>
</td>
</tr>
<% end %>

@ -18,22 +18,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++#%>
<%= render :partial => 'shared/costs_header' %>
<%= render partial: 'shared/costs_header' %>
<% html_title t(:label_administration), "#{t(:label_edit)} #{l((@project.nil? ? :caption_default_rate_history_for : :caption_rate_history_for), user: ' ')} #{@user.name}" %>
<%= toolbar title: (@project.nil? ? t(:caption_default_rate_history_for, :user => @user.name) : t(:caption_rate_history_for_project, :user => @user.name, :project => @project.name) ) %>
<%= toolbar title: (@project.nil? ? t(:caption_default_rate_history_for, user: @user.name) : t(:caption_rate_history_for_project, user: @user.name, project: @project.name) ) %>
<%- default_rate = @user.current_default_rate -%>
<% if default_rate%>
<p><strong><%= t(:label_current_default_rate) %>:</strong> <%= number_to_currency(default_rate.rate)%></p>
<% end %>
<%= javascript_tag do -%>
RatesForm = new Subform('<%= escape_javascript(render(:partial => "rate", :object => HourlyRate.new )) %>',<%= @rates.length %>,'rates_body');
<% end -%>
<%= labelled_tabular_form_for @user, :url => {:action => 'update', :project_id => @project}, :method => :put do |f| %>
<% template_object = %>
<costs-subform item-count="<%= @rates.count %>">
<%= labelled_tabular_form_for @user, url: {action: 'update', project_id: @project}, method: :put do |f| %>
<%= back_url_hidden_field_tag %>
<%= error_messages_for 'user' %>
<%- @rates.each do |rate| -%>
@ -71,9 +69,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<th></th>
</tr>
</thead>
<tbody id="rates_body">
<tbody id="rates_body" class="subform-container">
<%= render partial: "hourly_rates/rate", object: HourlyRate.new, locals: { templated: true } %>
<%- @rates.each_with_index do |rate, index| -%>
<%= render :partial => 'rate', :object => rate, :locals => {:index => index} %>
<%= render partial: 'rate', object: rate, locals: {index: index} %>
<%- end -%>
</tbody>
</table>
@ -82,7 +81,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
</div>
<div class="generic-table--action-buttons">
<label class="hidden-for-sighted" for="add_rate_date" %>"><%= t(:description_date_for_new_rate) %></label>
<%= link_to_function t(:button_add_rate), "addRate($('add_rate_date'))", {class: "button -alt-highlight -with-icon icon-add"} %>
<a href="javascript:" class="add-row-button button -alt-highlight -with-icon icon-add" title="<%= t(:button_add_rate) %>">
<%= t(:button_add_rate) %>
</a>
<%= styled_button_tag t(:button_save), class: '-with-icon icon-checkmark' %>
</div>
<% end %>
</costs-subform>

@ -20,5 +20,4 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<% content_for :header_tags do %>
<%= stylesheet_link_tag 'costs/costs.css' %>
<%= javascript_include_tag 'costs/costs.js' %>
<% end %>

@ -43,7 +43,7 @@ export class CostBudgetSubformController {
// Updater URL for the rows contained here
public updateUrl: string;
constructor(public $element, public $http, public wpNotificationsService) {
constructor(public $element, public wpNotificationsService) {
this.container = $element.find('.budget-item-container');
this.rowIndex = parseInt(this.itemCount);
@ -121,7 +121,7 @@ function costsBudgetSubform() {
restrict: 'E',
scope: {
updateUrl: '@',
rowIndex: '@'
itemCount: '@'
},
link: (scope, element, attr, ctrl) => {
const template = element.find('.budget-row-template');

@ -0,0 +1,92 @@
// -- 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 CostSubformController {
// Container for rows
private container: ng.IAugmentedJQuery;
// Template for new rows to insert, is rendered with INDEX placeholder
private rowTemplate: string;
// Current row index
public rowIndex: number;
// subform item count as output by rails
public itemCount: string;
constructor(public $element) {
this.container = $element.find('.subform-container');
this.rowIndex = parseInt(this.itemCount);
$element.on('click', '.delete-row-button', (evt) => {
var row = angular.element(evt.target).closest('.subform-row');
row.remove();
});
// Add new row handler
$element.find('.add-row-button').click(() => {
this.addRow();
});
}
/**
* Adds a new empty budget item row with the correct index set
*/
public addRow() {
this.container.append(this.indexedTemplate);
this.rowIndex += 1;
this.container.find('.subform-row:last-child input:first').focus();
}
/**
* Return the next possible new row from rowTemplate,
* with the index set to the current last value.
*/
private get indexedTemplate() {
return this.rowTemplate.replace(/INDEX/g, this.rowIndex.toString());
}
}
function costsSubform() {
return {
restrict: 'E',
scope: { itemCount: '@' },
link: (scope, element, attr, ctrl) => {
const template = element.find('.subform-row-template');
ctrl.rowTemplate = template[0].outerHTML;
template.remove();
},
bindToController: true,
controller: CostSubformController,
controllerAs: '$ctrl'
};
}
angular.module('openproject').directive('costsSubform', costsSubform);

@ -48,6 +48,9 @@ describe 'hourly rates on a member', type: :feature, js: true do
fill_in 'Valid from', with: date.strftime('%Y-%m-%d') if date
fill_in 'Rate', with: rate
end
# Close the date picker if still open
find('.ui-datepicker-close').click rescue nil
end
def change_rate_date(from:, to:)

@ -25,7 +25,7 @@ describe 'updating a budget', type: :feature, js: true do
let(:budget) { FactoryGirl.create :cost_object, author: user, project: project }
before do
allow(User).to receive(:current).and_return user
login_as(user)
end
describe 'with new cost items' do
@ -33,7 +33,7 @@ describe 'updating a budget', type: :feature, js: true do
FactoryGirl.create :cost_type, name: 'Post-war', unit: 'cap', unit_plural: 'caps'
end
let(:page) { Pages::EditBudget.new budget.id }
let(:budget_page) { Pages::EditBudget.new budget.id }
before do
project.add_member! user, FactoryGirl.create(:role)
@ -43,22 +43,22 @@ describe 'updating a budget', type: :feature, js: true do
end
it 'creates the cost items' do
page.visit!
budget_page.visit!
click_on 'Update'
page.add_unit_costs! 3, comment: 'Stimpak'
page.add_labor_costs! 5, user_name: user.name, comment: 'treatment'
budget_page.add_unit_costs! 3, comment: 'Stimpak'
budget_page.add_labor_costs! 5, user_name: user.name, comment: 'treatment'
click_on 'Submit'
expect(page).to have_content('Successful update')
expect(budget_page).to have_content('Successful update')
page.toggle_unit_costs!
expect(page.unit_costs_at(1)).to have_content '150.00 EUR'
expect(page.overall_unit_costs).to have_content '150.00 EUR'
budget_page.toggle_unit_costs!
expect(budget_page.unit_costs_at(1)).to have_content '150.00 EUR'
expect(budget_page.overall_unit_costs).to have_content '150.00 EUR'
page.toggle_labor_costs!
expect(page.labor_costs_at(1)).to have_content '125.00 EUR'
expect(page.overall_labor_costs).to have_content '125.00 EUR'
budget_page.toggle_labor_costs!
expect(budget_page.labor_costs_at(1)).to have_content '125.00 EUR'
expect(budget_page.overall_labor_costs).to have_content '125.00 EUR'
end
end
@ -79,7 +79,7 @@ describe 'updating a budget', type: :feature, js: true do
cost_object: budget
end
let(:page) { Pages::EditBudget.new budget.id }
let(:budget_page) { Pages::EditBudget.new budget.id }
before do
project.add_member! user, FactoryGirl.create(:role)
@ -93,25 +93,25 @@ describe 'updating a budget', type: :feature, js: true do
end
it 'updates the cost items' do
page.visit!
budget_page.visit!
click_on 'Update'
page.edit_unit_costs! material_budget_item.id, units: 5,
budget_page.edit_unit_costs! material_budget_item.id, units: 5,
comment: 'updated num stimpaks'
page.edit_labor_costs! labor_budget_item.id, hours: 3,
budget_page.edit_labor_costs! labor_budget_item.id, hours: 3,
user_name: user.name,
comment: 'updated treatment duration'
click_on 'Submit'
expect(page).to have_content('Successful update')
expect(budget_page).to have_content('Successful update')
page.toggle_unit_costs!
expect(page.unit_costs_at(1)).to have_content '250.00 EUR'
expect(page.overall_unit_costs).to have_content '250.00 EUR'
budget_page.toggle_unit_costs!
expect(budget_page.unit_costs_at(1)).to have_content '250.00 EUR'
expect(budget_page.overall_unit_costs).to have_content '250.00 EUR'
page.toggle_labor_costs!
expect(page.labor_costs_at(1)).to have_content '75.00 EUR'
expect(page.overall_labor_costs).to have_content '75.00 EUR'
budget_page.toggle_labor_costs!
expect(budget_page.labor_costs_at(1)).to have_content '75.00 EUR'
expect(budget_page.overall_labor_costs).to have_content '75.00 EUR'
end
end
end

Loading…
Cancel
Save