Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7e2e8ba86 | ||
|
|
f9906a563d | ||
|
|
d041809113 | ||
|
|
7104521c50 | ||
|
|
07e33f9401 | ||
|
|
0aeafe75ee | ||
|
|
7d57f10038 | ||
|
|
296d9f6d75 | ||
|
|
c8266b2b62 | ||
|
|
bc33dd1127 | ||
|
|
fba5042ba3 | ||
|
|
fd4b640c2f | ||
|
|
952646f485 | ||
|
|
6c37e97bee | ||
|
|
cce10b04d8 | ||
|
|
04d0be203e | ||
|
|
1d608a6c47 | ||
|
|
d3e7f2d2ae | ||
|
|
aa9649fde3 | ||
|
|
e25f841fe2 | ||
|
|
b3c625d9d9 | ||
|
|
4b1a4cd156 | ||
|
|
624b80a128 | ||
|
|
43862fc3b9 | ||
|
|
94afb7a1ca | ||
|
|
2125241f38 | ||
|
|
4b18f20eba | ||
|
|
a53ceb2804 | ||
|
|
d552aa7f6f | ||
|
|
aa541f42bb | ||
|
|
19968fa0b8 | ||
|
|
c7ac326959 | ||
|
|
46f53a9332 | ||
|
|
6692dffe0f | ||
|
|
6a9ead4ad0 | ||
|
|
642eb1f4ee | ||
|
|
a55227c9bb | ||
|
|
14a6aa2c28 |
@@ -0,0 +1,7 @@
|
|||||||
|
*.tar.gz
|
||||||
|
*.zip
|
||||||
|
.DS_Store
|
||||||
|
coverage
|
||||||
|
doc
|
||||||
|
pkg
|
||||||
|
rdoc
|
||||||
@@ -7,3 +7,29 @@ RedminePluginSupport::Base.setup do |plugin|
|
|||||||
plugin.project_name = 'budget_plugin'
|
plugin.project_name = 'budget_plugin'
|
||||||
plugin.default_task = [:spec]
|
plugin.default_task = [:spec]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
require 'jeweler'
|
||||||
|
Jeweler::Tasks.new do |s|
|
||||||
|
s.name = "budget_plugin"
|
||||||
|
s.summary = "A plugin for Redmine to manage the set of deliverables for each project, automatically calculating key performance indicators."
|
||||||
|
s.email = "edavis@littlestreamsoftware.com"
|
||||||
|
s.homepage = "https://projects.littlestreamsoftware.com/projects/redmine-budget"
|
||||||
|
s.description = "A plugin for Redmine to manage the set of deliverables for each project, automatically calculating key performance indicators."
|
||||||
|
s.authors = ["Eric Davis"]
|
||||||
|
s.rubyforge_project = "budget_plugin" # TODO
|
||||||
|
s.files = FileList[
|
||||||
|
"[A-Z]*",
|
||||||
|
"init.rb",
|
||||||
|
"rails/init.rb",
|
||||||
|
"{bin,generators,lib,test,app,assets,config,lang}/**/*",
|
||||||
|
'lib/jeweler/templates/.gitignore'
|
||||||
|
]
|
||||||
|
end
|
||||||
|
Jeweler::GemcutterTasks.new
|
||||||
|
Jeweler::RubyforgeTasks.new do |rubyforge|
|
||||||
|
rubyforge.doc_task = "rdoc"
|
||||||
|
end
|
||||||
|
rescue LoadError
|
||||||
|
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
||||||
|
end
|
||||||
|
|||||||
@@ -27,15 +27,17 @@ class DeliverablesController < ApplicationController
|
|||||||
|
|
||||||
@budget = Budget.new(@project.id)
|
@budget = Budget.new(@project.id)
|
||||||
|
|
||||||
|
@display_form = params[:new].present? || @deliverables.empty?
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render :action => 'index', :layout => !request.xhr? }
|
format.html { render :action => 'index', :layout => !request.xhr? }
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Action to preview the Deliverable description
|
# Action to preview the Deliverable description
|
||||||
def preview
|
def preview
|
||||||
@text = params[:deliverable][:description]
|
@text = params[:deliverable][:description]
|
||||||
render :partial => 'common/preview'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Saves a new Deliverable
|
# Saves a new Deliverable
|
||||||
@@ -54,9 +56,9 @@ class DeliverablesController < ApplicationController
|
|||||||
if @deliverable.save
|
if @deliverable.save
|
||||||
@flash = l(:notice_successful_create)
|
@flash = l(:notice_successful_create)
|
||||||
format.html { redirect_to :action => 'index' }
|
format.html { redirect_to :action => 'index' }
|
||||||
format.js { render :action => 'create.js.rjs'}
|
format.js
|
||||||
else
|
else
|
||||||
format.js { render :action => 'create_error.js.rjs'}
|
format.js
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -64,7 +66,7 @@ class DeliverablesController < ApplicationController
|
|||||||
|
|
||||||
# Builds the edit form for the Deliverable
|
# Builds the edit form for the Deliverable
|
||||||
def edit
|
def edit
|
||||||
@deliverable = Deliverable.find_by_id_and_project_id(params[:deliverable_id], params[:id])
|
@deliverable = Deliverable.find_by_id_and_project_id(params[:deliverable_id], @project.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Updates an existing Deliverable, optionally changing it's type
|
# Updates an existing Deliverable, optionally changing it's type
|
||||||
@@ -78,9 +80,9 @@ class DeliverablesController < ApplicationController
|
|||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @deliverable.update_attributes(params[:deliverable])
|
if @deliverable.update_attributes(params[:deliverable])
|
||||||
@flash = l(:notice_successful_create)
|
@flash = l(:notice_successful_create)
|
||||||
format.html { redirect_to :action => 'index', :id => @project.id }
|
format.html { redirect_to :action => 'index', :id => @project.identifier }
|
||||||
else
|
else
|
||||||
format.html { render :action => 'edit', :id => @project.id}
|
format.html { render :action => 'edit', :id => @project.identifier }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -95,7 +97,7 @@ class DeliverablesController < ApplicationController
|
|||||||
render_403 and return unless @deliverable.editable_by?(User.current)
|
render_403 and return unless @deliverable.editable_by?(User.current)
|
||||||
@deliverable.destroy
|
@deliverable.destroy
|
||||||
flash[:notice] = l(:notice_successful_delete)
|
flash[:notice] = l(:notice_successful_delete)
|
||||||
redirect_to :action => 'index', :id => @project.id
|
redirect_to :action => 'index', :id => @project.identifier
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create a query in the session and redirects to the issue list with that query
|
# Create a query in the session and redirects to the issue list with that query
|
||||||
@@ -111,7 +113,7 @@ class DeliverablesController < ApplicationController
|
|||||||
|
|
||||||
session[:query] = {:project_id => @query.project_id, :filters => @query.filters}
|
session[:query] = {:project_id => @query.project_id, :filters => @query.filters}
|
||||||
|
|
||||||
redirect_to :controller => 'issues', :action => 'index', :project_id => @project.id
|
redirect_to :controller => 'issues', :action => 'index', :project_id => @project.identifier
|
||||||
end
|
end
|
||||||
|
|
||||||
# Assigns issues to the Deliverable based on their Version
|
# Assigns issues to the Deliverable based on their Version
|
||||||
@@ -124,12 +126,12 @@ class DeliverablesController < ApplicationController
|
|||||||
number_updated = @deliverable.assign_issues_by_version(params[:version][:id])
|
number_updated = @deliverable.assign_issues_by_version(params[:version][:id])
|
||||||
|
|
||||||
flash[:notice] = l(:message_updated_issues, number_updated)
|
flash[:notice] = l(:message_updated_issues, number_updated)
|
||||||
redirect_to :action => 'index', :id => @project.id
|
redirect_to :action => 'index', :id => @project.identifier
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def find_project
|
def find_project
|
||||||
@project = Project.find(params[:id])
|
@project = Project.where(:identifier => params[:id]).first || Project.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_settings
|
def get_settings
|
||||||
@@ -149,13 +151,13 @@ class DeliverablesController < ApplicationController
|
|||||||
def sort_if_needed(deliverables)
|
def sort_if_needed(deliverables)
|
||||||
if session[@sort_name] && %w(score spent progress labor_budget).include?(session[@sort_name][:key])
|
if session[@sort_name] && %w(score spent progress labor_budget).include?(session[@sort_name][:key])
|
||||||
case session[@sort_name][:key]
|
case session[@sort_name][:key]
|
||||||
when "score":
|
when "score" then
|
||||||
sorted = deliverables.sort {|a,b| a.score <=> b.score}
|
sorted = deliverables.sort {|a,b| a.score <=> b.score}
|
||||||
when "spent":
|
when "spent" then
|
||||||
sorted = deliverables.sort {|a,b| a.spent <=> b.spent}
|
sorted = deliverables.sort {|a,b| a.spent <=> b.spent}
|
||||||
when "progress":
|
when "progress" then
|
||||||
sorted = deliverables.sort {|a,b| a.progress <=> b.progress}
|
sorted = deliverables.sort {|a,b| a.progress <=> b.progress}
|
||||||
when "labor_budget":
|
when "labor_budget" then
|
||||||
sorted = deliverables.sort {|a,b| a.labor_budget <=> b.labor_budget}
|
sorted = deliverables.sort {|a,b| a.labor_budget <=> b.labor_budget}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -165,5 +167,4 @@ class DeliverablesController < ApplicationController
|
|||||||
return deliverables
|
return deliverables
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module DeliverablesHelper
|
|||||||
# TODO Later: Refactor since observers are not used anymore
|
# TODO Later: Refactor since observers are not used anymore
|
||||||
def field_with_budget_observer_and_totals(form, object, field, percent_field, default_value='')
|
def field_with_budget_observer_and_totals(form, object, field, percent_field, default_value='')
|
||||||
content_tag(:tr,
|
content_tag(:tr,
|
||||||
content_tag(:td, "<label for='deliverable_#{field.to_s}'>#{l_field(field, 'field_')}</label>") +
|
content_tag(:td, "<label for='deliverable_#{field.to_s}'>#{l_field(field, 'field_')}</label>".html_safe) +
|
||||||
content_tag(:td, number_or_percent_field(object, field, percent_field, default_value, :size => 7)) +
|
content_tag(:td, number_or_percent_field(object, field, percent_field, default_value, :size => 7)) +
|
||||||
content_tag(:td,
|
content_tag(:td,
|
||||||
content_tag(:span,
|
content_tag(:span,
|
||||||
@@ -93,8 +93,8 @@ module DeliverablesHelper
|
|||||||
end
|
end
|
||||||
|
|
||||||
def number_or_percent(number_field, percent_field)
|
def number_or_percent(number_field, percent_field)
|
||||||
return number_to_currency(number_field, :precision => 0) unless number_field.blank?
|
return number_to_currency(number_field, :unit => l(:label_currency), :precision => 0) unless number_field.blank?
|
||||||
return number_to_percentage(percent_field, :precision => 0) unless percent_field.blank?
|
return number_to_percentage(percent_field, :precision => 0) unless percent_field.blank?
|
||||||
return "$0"
|
return number_to_currency(0, :unit => l(:label_currency), :precision => 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ class Deliverable < ActiveRecord::Base
|
|||||||
validates_presence_of :subject
|
validates_presence_of :subject
|
||||||
|
|
||||||
belongs_to :project
|
belongs_to :project
|
||||||
has_many :issues
|
has_many :issues, :dependent => :nullify
|
||||||
|
|
||||||
# Assign all the issues with +version_id+ to this Deliverable
|
# Assign all the issues with +version_id+ to this Deliverable
|
||||||
def assign_issues_by_version(version_id)
|
def assign_issues_by_version(version_id)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<%= l(:field_budget) %>
|
<%= l(:field_budget) %>
|
||||||
</td>
|
</td>
|
||||||
<td class="calculation-column">
|
<td class="calculation-column">
|
||||||
<%= h number_to_currency(budget.budget, :precision => 0) %>
|
<%= h number_to_currency(budget.budget, :unit => l(:label_currency), :precision => 0) %>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<%= l(:label_labor_budget) %>
|
<%= l(:label_labor_budget) %>
|
||||||
</td>
|
</td>
|
||||||
<td class="calculation-column">
|
<td class="calculation-column">
|
||||||
<%= h number_to_currency(budget.labor_budget, :precision => 0) %>
|
<%= h number_to_currency(budget.labor_budget, :unit => l(:label_currency), :precision => 0) %>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<%= l(:label_labor_budget_spent) %>
|
<%= l(:label_labor_budget_spent) %>
|
||||||
</td>
|
</td>
|
||||||
<td class="calculation-column">
|
<td class="calculation-column">
|
||||||
<%= h number_to_currency(budget.spent, :precision => 0) %>
|
<%= h number_to_currency(budget.spent, :unit => l(:label_currency), :precision => 0) %>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
<%= l(:label_labor_budget_remaining) %>
|
<%= l(:label_labor_budget_remaining) %>
|
||||||
</td>
|
</td>
|
||||||
<td class="calculation-column">
|
<td class="calculation-column">
|
||||||
<%= h number_to_currency(budget.labor_budget_left, :precision => 0) %>
|
<%= h number_to_currency(budget.labor_budget_left, :unit => l(:label_currency), :precision => 0) %>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
<%= l(:label_overruns) %>
|
<%= l(:label_overruns) %>
|
||||||
</td>
|
</td>
|
||||||
<td class="calculation-column">
|
<td class="calculation-column">
|
||||||
<%= h number_to_currency(budget.overruns, :precision => 0) %>
|
<%= h number_to_currency(budget.overruns, :unit => l(:label_currency), :precision => 0) %>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
<%= l(:label_missing_on) %> <%= link_to('Deliverables:', :action => 'issues', :id => @project.id, :deliverable_id => :none) %>
|
<%= l(:label_missing_on) %> <%= link_to('Deliverables:', :action => 'issues', :id => @project.id, :deliverable_id => :none) %>
|
||||||
</td>
|
</td>
|
||||||
<td class="calculation-column">
|
<td class="calculation-column">
|
||||||
<%= h number_to_currency(budget.amount_missing_on_deliverables, :precision => 0) %>
|
<%= h number_to_currency(budget.amount_missing_on_deliverables, :unit => l(:label_currency), :precision => 0) %>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
|
|
||||||
</td>
|
</td>
|
||||||
<td class="calculation-column">
|
<td class="calculation-column">
|
||||||
<%= h number_to_currency(budget.amount_missing_on_issues, :precision => 0) %>
|
<%= h number_to_currency(budget.amount_missing_on_issues, :unit => l(:label_currency), :precision => 0) %>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -127,7 +127,7 @@
|
|||||||
<%= l(:label_potential_profit) %>
|
<%= l(:label_potential_profit) %>
|
||||||
</td>
|
</td>
|
||||||
<td class="calculation-column">
|
<td class="calculation-column">
|
||||||
<%= h number_to_currency(budget.profit, :precision => 0) %>
|
<%= h number_to_currency(budget.profit, :unit => l(:label_currency), :precision => 0) %>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -1,84 +1,4 @@
|
|||||||
<% css = cycle('odd','even') %>
|
<% css = cycle('odd','even') %>
|
||||||
<tr id="deliverable-<%= deliverable.id %>" class="deliverable <%= css %>">
|
<%= render :partial => 'deliverable_summary_row', :locals => {:deliverable => deliverable, :css => css} %>
|
||||||
<td class="actions">
|
<%= render :partial => 'deliverable_details_row', :locals => {:deliverable => deliverable, :css => css} %>
|
||||||
<%= toggle_arrows(deliverable.id) %>
|
<%= render :partial => 'deliverable_description_row', :locals => {:deliverable => deliverable, :css => css} %>
|
||||||
</td>
|
|
||||||
|
|
||||||
<%= content_tag(:td, deliverable.id) %>
|
|
||||||
<%= content_tag(:td, h(deliverable.score.to_i), :class => 'score') if allowed_management? %>
|
|
||||||
<%= content_tag(:td, h(deliverable.subject), :class => 'subject') %>
|
|
||||||
<%= content_tag(:td, number_to_currency(deliverable.budget || 0.0, :precision => 0), :class => 'budget') if allowed_management? %>
|
|
||||||
<%= content_tag(:td, number_to_currency(deliverable.labor_budget || 0.0, :precision => 0), :class => 'budget') if allowed_management? %>
|
|
||||||
<%= content_tag(:td, number_to_currency(deliverable.spent, :precision => 0), :class => 'spent') if allowed_management? %>
|
|
||||||
<%= content_tag(:td, format_date(deliverable.due), :class => 'due_date') %>
|
|
||||||
<%= content_tag(:td, progress_bar(deliverable.progress, :width => '100%', :class => 'done_ratio')) %>
|
|
||||||
</tr>
|
|
||||||
<tr id="deliverable-details-<%= deliverable.id %>" class="deliverable deliverable-details <%= css %>" style="display:none;">
|
|
||||||
<td class="deliverable-actions">
|
|
||||||
<%= content_tag(:p,link_to(l(:button_edit), :action => 'edit', :id => @project.id, :deliverable_id => deliverable.id)) if allowed_management? -%>
|
|
||||||
<%= content_tag(:p,link_to(l(:button_delete), { :action => 'destroy', :id => @project.id, :deliverable_id => deliverable.id}, :confirm => l(:text_are_you_sure))) if allowed_management? %>
|
|
||||||
<%= content_tag(:p,link_to(l(:label_issue_plural), :action => 'issues', :id => @project.id, :deliverable_id => deliverable.id)) -%>
|
|
||||||
<% if allowed_management? && @project.versions.size > 0 %>
|
|
||||||
<div>
|
|
||||||
<% form_for :deliverable, deliverable, :url => { :action => "bulk_assign_issues", :id => @project.id, :deliverable_id => deliverable.id} do |f| %>
|
|
||||||
|
|
||||||
<%= select("version", "id", @project.versions.sort.collect {|v| [v.name, v.id ] }, { :prompt => '-- Version --' }) %><br />
|
|
||||||
<%= submit_tag(l(:label_bulk_assign)) %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td colspan="2">
|
|
||||||
<table class="progress-table">
|
|
||||||
<%= row_with_double_data l(:label_progress), number_to_percentage(deliverable.progress, :precision => 0), '' %>
|
|
||||||
<% if allowed_management? %>
|
|
||||||
|
|
||||||
<% if deliverable.hourly? %>
|
|
||||||
<%= row_with_double_data l(:label_hours_estimated), number_with_precision(deliverable.total_hours, 0), '' %>
|
|
||||||
<% end %>
|
|
||||||
<% if deliverable.fixed? %>
|
|
||||||
<%= row_with_double_data l(:label_fixed_amount), '', number_to_currency(deliverable.fixed_cost, :precision => 0) %>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<% if deliverable.hours_used > 0 %>
|
|
||||||
<%= row_with_double_data l(:label_hours_used), number_with_precision(deliverable.hours_used,0), number_to_currency(deliverable.spent_by_members, :precision => 0) %>
|
|
||||||
|
|
||||||
<tr><td> </td></tr>
|
|
||||||
|
|
||||||
<% deliverable.members_spent.each do |person| %>
|
|
||||||
<%= row_with_double_data(h(person.user.name), person.hours.round, number_to_currency(person.spent, :precision => 0)) %>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td colspan="3">
|
|
||||||
<table class="financials">
|
|
||||||
<%= row_with_data(l(:field_budget), number_to_currency(deliverable.budget, :precision => 0)) -%>
|
|
||||||
<% if allowed_management? %>
|
|
||||||
<% base_price_label = deliverable.hourly? ? l(:label_labor) : l(:label_fixed_amount) %>
|
|
||||||
<%= row_with_data(base_price_label, number_to_currency(deliverable.labor_budget || 0.0, :precision => 0)) -%>
|
|
||||||
<%= row_with_data(l(:label_overhead), number_or_percent(deliverable.overhead, deliverable.overhead_percent)) -%>
|
|
||||||
<%= row_with_data(l(:label_materials), number_or_percent(deliverable.materials, deliverable.materials_percent)) -%>
|
|
||||||
<%= row_with_data(l(:label_potential_profit), number_or_percent(deliverable.profit, deliverable.profit_percent)) -%>
|
|
||||||
<% end %>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td colspan="3">
|
|
||||||
<table class="issue-totals">
|
|
||||||
<%= row_with_data(l(:label_issue_plural), deliverable.issues.size, 'issue-totals') -%>
|
|
||||||
|
|
||||||
<% deliverable.issues_with_trackers.each do |tracker_name, count| %>
|
|
||||||
<%= row_with_data(h(tracker_name), count,'issue-totals') -%>
|
|
||||||
<% end %>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr id="deliverable-description-<%= deliverable.id %>" class="deliverable deliverable-details <%= css %>" style="display:none;">
|
|
||||||
<td colspan="9" style="text-align: left;">
|
|
||||||
<%= l(:field_description) %>: <%= textilizable deliverable.description %>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<tr id="deliverable-description-<%= deliverable.id %>" class="deliverable deliverable-details <%= css %>" style="display:none;">
|
||||||
|
<td colspan="9" style="text-align: left;">
|
||||||
|
<%= l(:field_description) %>: <%= textilizable deliverable.description %>
|
||||||
|
</td>
|
||||||
|
<% Redmine::Hook.call_hook(:plugin_budget_view_deliverable_description_row, { :deliverable => deliverable }) %>
|
||||||
|
</tr>
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<tr id="deliverable-details-<%= deliverable.id %>" class="deliverable deliverable-details <%= css %>" style="display:none;">
|
||||||
|
<td class="deliverable-actions">
|
||||||
|
<%= content_tag(:p,link_to(l(:button_edit), :action => 'edit', :id => @project.identifier, :deliverable_id => deliverable.id)) if allowed_management? -%>
|
||||||
|
<%= content_tag(:p,link_to(l(:button_delete), { :action => 'destroy', :id => @project.identifier, :deliverable_id => deliverable.id}, :confirm => l(:text_are_you_sure))) if allowed_management? %>
|
||||||
|
<%= content_tag(:p,link_to(l(:label_issue_plural), :action => 'issues', :id => @project.identifier, :deliverable_id => deliverable.id)) -%>
|
||||||
|
<% if allowed_management? && @project.versions.size > 0 %>
|
||||||
|
<div>
|
||||||
|
<%= form_tag({ :action => "bulk_assign_issues", :id => @project.identifier, :deliverable_id => deliverable.id}) do %>
|
||||||
|
<%= select("version", "id", @project.versions.sort.collect {|v| [v.name, v.id ] }, { :prompt => '-- Version --' }) %><br />
|
||||||
|
<%= submit_tag(l(:label_bulk_assign)) %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td colspan="2">
|
||||||
|
<table class="progress-table">
|
||||||
|
<%= row_with_double_data l(:label_progress), number_to_percentage(deliverable.progress, :precision => 0), '' %>
|
||||||
|
<% if allowed_management? %>
|
||||||
|
<% if deliverable.hourly? %>
|
||||||
|
<%= row_with_double_data l(:label_hours_estimated), number_with_precision(deliverable.total_hours, :precision => 0), '' %>
|
||||||
|
<% end %>
|
||||||
|
<% if deliverable.fixed? %>
|
||||||
|
<%= row_with_double_data l(:label_fixed_amount), '', number_to_currency(deliverable.fixed_cost, :unit => l(:label_currency), :precision => 0) %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if deliverable.hours_used > 0 %>
|
||||||
|
<%= row_with_double_data l(:label_hours_used), number_with_precision(deliverable.hours_used,0), number_to_currency(deliverable.spent_by_members, :unit => l(:label_currency), :precision => 0) %>
|
||||||
|
|
||||||
|
<tr><td> </td></tr>
|
||||||
|
|
||||||
|
<% deliverable.members_spent.each do |person| %>
|
||||||
|
<%= row_with_double_data(h(person.user.name), person.hours.round, number_to_currency(person.spent, :unit => l(:label_currency), :precision => 0)) %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td colspan="3">
|
||||||
|
<table class="financials">
|
||||||
|
<%= row_with_data(l(:field_budget), number_to_currency(deliverable.budget, :unit => l(:label_currency), :precision => 0)) -%>
|
||||||
|
<% if allowed_management? %>
|
||||||
|
<% base_price_label = deliverable.hourly? ? l(:label_labor) : l(:label_fixed_amount) %>
|
||||||
|
<%= row_with_data(base_price_label, number_to_currency(deliverable.labor_budget || 0.0, :unit => l(:label_currency), :precision => 0)) -%>
|
||||||
|
<%= row_with_data(l(:label_overhead), number_or_percent(deliverable.overhead, deliverable.overhead_percent)) -%>
|
||||||
|
<%= row_with_data(l(:label_materials), number_or_percent(deliverable.materials, deliverable.materials_percent)) -%>
|
||||||
|
<%= row_with_data(l(:label_potential_profit), number_or_percent(deliverable.profit, deliverable.profit_percent)) -%>
|
||||||
|
<% end %>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td colspan="3">
|
||||||
|
<table class="issue-totals">
|
||||||
|
<%= row_with_data(l(:label_issue_plural), deliverable.issues.size, 'issue-totals') -%>
|
||||||
|
|
||||||
|
<% deliverable.issues_with_trackers.each do |tracker_name, count| %>
|
||||||
|
<%= row_with_data(h(tracker_name), count,'issue-totals') -%>
|
||||||
|
<% end %>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
<% Redmine::Hook.call_hook(:plugin_budget_view_deliverable_details_row, { :deliverable => deliverable }) %>
|
||||||
|
</tr>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<tr id="deliverable-<%= deliverable.id %>" class="deliverable <%= css %>">
|
||||||
|
<td class="actions">
|
||||||
|
<%= toggle_arrows(deliverable.id) %>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<%= content_tag(:td, deliverable.id, :class => 'id') %>
|
||||||
|
<%= content_tag(:td, h(deliverable.score.to_i), :class => 'score') if allowed_management? %>
|
||||||
|
<%= content_tag(:td, h(deliverable.subject), :class => 'subject') %>
|
||||||
|
<%= content_tag(:td, number_to_currency(deliverable.budget || 0.0, :unit => l(:label_currency), :precision => 0), :class => 'budget') if allowed_management? %>
|
||||||
|
<%= content_tag(:td, number_to_currency(deliverable.labor_budget || 0.0, :unit => l(:label_currency), :precision => 0), :class => 'budget labor') if allowed_management? %>
|
||||||
|
<%= content_tag(:td, number_to_currency(deliverable.spent, :unit => l(:label_currency), :precision => 0), :class => 'spent') if allowed_management? %>
|
||||||
|
<%= content_tag(:td, format_date(deliverable.due), :class => 'due_date') %>
|
||||||
|
<%= content_tag(:td, progress_bar(deliverable.progress, :width => '100%', :class => 'done_ratio')) %>
|
||||||
|
<% Redmine::Hook.call_hook(:plugin_budget_view_deliverable_summary_row, { :deliverable => deliverable }) %>
|
||||||
|
</tr>
|
||||||
@@ -23,7 +23,11 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<%= check_box(:deliverable, :type, {}, FixedDeliverable.name, HourlyDeliverable.name) %>
|
<%= check_box(:deliverable, :type, {}, FixedDeliverable.name, HourlyDeliverable.name) %>
|
||||||
<%= observe_field('deliverable_type', :function => "new Budget.changeType();") %>
|
<script type="text/javascript">
|
||||||
|
$("#deliverable_type").change(function() {
|
||||||
|
new Budget.changeType();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</td>
|
</td>
|
||||||
<td class="calculation-column">
|
<td class="calculation-column">
|
||||||
</td>
|
</td>
|
||||||
@@ -78,8 +82,6 @@
|
|||||||
|
|
||||||
</table>
|
</table>
|
||||||
<%= f.hidden_field :budget %>
|
<%= f.hidden_field :budget %>
|
||||||
<%= observe_form('deliverable-form', :function => "new Budget.updateAmounts();", :on => 'blur') %>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript">new Budget.changeType();</script>
|
<script type="text/javascript">new Budget.changeType();</script>
|
||||||
@@ -93,18 +95,18 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<%= mode == :create ? submit_tag(l(:button_create)) : submit_tag(l(:button_update)) %>
|
<%= mode == :create ? submit_tag(l(:button_create)) : submit_tag(l(:button_update)) %>
|
||||||
<%= link_to_remote l(:label_preview),
|
<%= link_to l(:label_preview), preview_deliverable_path(:id => @project), :id => "budget_preview_link", :accesskey => accesskey(:preview) %>
|
||||||
{ :url => { :controller => 'deliverables', :action => 'preview', :id => @project },
|
<script type="text/javascript">
|
||||||
:method => 'post',
|
$("#budget_preview_link").click(function() {
|
||||||
:update => 'preview',
|
$.post($(this).attr("href"), $("#deliverable-form").serializeArray(), 'script');
|
||||||
:with => "Form.serialize('deliverable-form')",
|
return false;
|
||||||
:complete => "Element.scrollTo('preview')"
|
});
|
||||||
}, :accesskey => accesskey(:preview) %>
|
</script>
|
||||||
|
|
||||||
<%# Same as `wikitoolbar_for 'deliverable_description'` but without the help link %>
|
<%# Same as `wikitoolbar_for 'deliverable_description'` but without the help link %>
|
||||||
<%= javascript_include_tag('jstoolbar/jstoolbar') %>
|
<%= javascript_include_tag('jstoolbar/jstoolbar') %>
|
||||||
<%= javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") %>
|
<%= javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") %>
|
||||||
<%= javascript_tag("var toolbar = new jsToolBar($('deliverable_description'));toolbar.draw();") %>
|
<%= javascript_tag("var toolbar = new jsToolBar($('#deliverable_description')[0]);toolbar.draw();") %>
|
||||||
|
|
||||||
|
|
||||||
<div id="preview" class="wiki"></div>
|
<div id="preview" class="wiki"></div>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
<%= sort_header_tag("spent", :caption => l(:caption_spent)) if allowed_management? %>
|
<%= sort_header_tag("spent", :caption => l(:caption_spent)) if allowed_management? %>
|
||||||
<%= sort_header_tag("#{Deliverable.table_name}.due", :caption => l(:caption_due)) %>
|
<%= sort_header_tag("#{Deliverable.table_name}.due", :caption => l(:caption_due)) %>
|
||||||
<%= sort_header_tag("progress", :caption => l(:caption_progress)) %>
|
<%= sort_header_tag("progress", :caption => l(:caption_progress)) %>
|
||||||
|
<% Redmine::Hook.call_hook(:plugin_budget_view_deliverable_list_header, { }) %>
|
||||||
</tr></thead>
|
</tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<% deliverables.each do |deliverable| -%>
|
<% deliverables.each do |deliverable| -%>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
<h3><%= l(:budget_title) %></h3>
|
<h3><%= l(:budget_title) %></h3>
|
||||||
<%= link_to l(:label_issue_view_all), { :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 } %><br />
|
<%= link_to_function l(:label_new_deliverable), "$('#new-deliverable').toggle();" if allowed_management? %><br />
|
||||||
<%= link_to_function l(:label_new_deliverable), "$('new-deliverable').toggle();" if allowed_management? %><br />
|
|
||||||
<%= link_to_function l(:label_toggle_all), "toggleAll();" if allowed_management? %><br />
|
<%= link_to_function l(:label_toggle_all), "toggleAll();" if allowed_management? %><br />
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# If there is no deliverable list, refresh the page
|
||||||
|
if ($('#deliverable-list').length == 0) { document.location.reload() }
|
||||||
|
|
||||||
|
# Add deliverable to list
|
||||||
|
$("#deliverable-list").prepend("<%= escape_javascript render(:partial => 'deliverable', :locals => {:deliverable => @deliverable}) %>");
|
||||||
|
|
||||||
|
# Update budget
|
||||||
|
$('#budget-summary').html("<%= escape_javascript render(:partial => 'budget', :object => @budget) %>");
|
||||||
|
$("#budget-summary").effect("highlight", {}, 300);
|
||||||
|
|
||||||
|
# Remove errors if found
|
||||||
|
$("#errorExplanation").remove();
|
||||||
|
|
||||||
|
var form = $('#deliverable-form');
|
||||||
|
form.find('input:text, input:password, input:file, select, textarea').val('');
|
||||||
|
form.find('input:radio, input:checkbox').removeAttr('checked').removeAttr('selected');
|
||||||
|
|
||||||
|
Budget.updateAmounts();
|
||||||
|
|
||||||
|
# Add a fading out flash
|
||||||
|
$('#new-deliverable').before('<%= escape_javascript content_tag(:div, @flash, :class => "flash notice", :id => "rjs-flash") %>');
|
||||||
|
setTimeout(function() { $("#rjs-flash").effect("fade", {}, 300) }, 3000);
|
||||||
|
|
||||||
|
$("#preview").remove();
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# If there is no deliverable list, refresh the page
|
|
||||||
page << "if ($('deliverable-list') == null) {document.location.reload()}"
|
|
||||||
|
|
||||||
# Add deliverable to list
|
|
||||||
page.insert_html :top, 'deliverable-list', :partial => 'deliverable', :locals => {:deliverable => @deliverable}
|
|
||||||
|
|
||||||
# Update budget
|
|
||||||
page.replace_html 'budget-summary', :partial => 'budget', :object => @budget
|
|
||||||
page.visual_effect :highlight, 'budget-summary'
|
|
||||||
|
|
||||||
# Remove errors if found
|
|
||||||
page.select('#errorExplanation').each do |value|
|
|
||||||
page.remove value
|
|
||||||
end
|
|
||||||
|
|
||||||
page << "Form.reset($('deliverable-form'))"
|
|
||||||
page << "Budget.updateAmounts()"
|
|
||||||
|
|
||||||
# Add a fading out flash
|
|
||||||
page.insert_html :before, 'new-deliverable', content_tag(:div, @flash, :class => "flash notice", :id => 'rjs-flash')
|
|
||||||
page.delay(3) do
|
|
||||||
page.visual_effect :fade, 'rjs-flash'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Clear preview
|
|
||||||
page.replace_html 'preview', ''
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
$('#new-deliverable').html("<%= escape_javascript render(:partial => 'form') %>");
|
||||||
|
Budget.updateAmounts();
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
page.replace_html 'new-deliverable', :partial => 'form'
|
|
||||||
page << "Budget.updateAmounts()"
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<h2><%= l(:label_update_deliverable) %></h2>
|
<h2><%= l(:label_update_deliverable) %></h2>
|
||||||
<% form_for :deliverable, @deliverable, :url => {:controller => 'deliverables', :action => 'update', :id => @project, :deliverable_id => @deliverable.id },
|
<%= form_for @deliverable, :url => {:controller => 'deliverables', :action => 'update', :id => @project, :deliverable_id => @deliverable.id },
|
||||||
:method => :post, :builder => TabularFormBuilder, :lang => current_language,
|
:method => :put, :builder => Redmine::Views::LabelledFormBuilder, :lang => current_language,
|
||||||
:html => {:multipart => true, :id => 'deliverable-form', :class => 'tabular'} do |f| %>
|
:html => {:multipart => true, :id => 'deliverable-form', :class => 'tabular'} do |f| %>
|
||||||
|
|
||||||
<%= render :partial => 'form', :locals => { :mode => :update, :f => f } %>
|
<%= render :partial => 'form', :locals => { :mode => :update, :f => f } %>
|
||||||
|
|||||||
@@ -3,11 +3,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if allowed_management? %>
|
<% if allowed_management? %>
|
||||||
<div id="new-deliverable" style="display:none;">
|
<div id="new-deliverable" style="<%= @display_form ? '' : 'display:none;' -%>">
|
||||||
<h2><%= l(:label_new_deliverable) %></h2>
|
<h2><%= l(:label_new_deliverable) %></h2>
|
||||||
<% remote_form_for :deliverable, @deliverable, :url => {:controller => 'deliverables', :action => 'create', :id => @project },
|
<%= form_for @deliverable, :url => create_deliverable_path(params[:id]), :method => :post, :builder => Redmine::Views::LabelledFormBuilder, :remote => true, :lang => current_language, :html => {:multipart => true, :id => 'deliverable-form', :class => 'tabular'} do |f| %>
|
||||||
:method => :post, :builder => TabularFormBuilder, :lang => current_language,
|
|
||||||
:html => {:multipart => true, :id => 'deliverable-form', :class => 'tabular'} do |f| %>
|
|
||||||
<%= render :partial => 'form', :locals => { :mode => :create, :f => f } %>
|
<%= render :partial => 'form', :locals => { :mode => :create, :f => f } %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
@@ -28,5 +26,147 @@
|
|||||||
|
|
||||||
<% content_for :header_tags do %>
|
<% content_for :header_tags do %>
|
||||||
<%= stylesheet_link_tag "budget.css", :plugin => "budget_plugin", :media => "screen" %>
|
<%= stylesheet_link_tag "budget.css", :plugin => "budget_plugin", :media => "screen" %>
|
||||||
<%= javascript_include_tag('budget', :plugin => 'budget_plugin') %>
|
|
||||||
|
<% javascript_tag do %>
|
||||||
|
/* Used to calculate the Budget */
|
||||||
|
var BudgetModule = Class.create();
|
||||||
|
Object.extend(BudgetModule.prototype, {
|
||||||
|
initialize: function () {},
|
||||||
|
|
||||||
|
toAmount: function(value) {
|
||||||
|
var amount = value.replace(/[^1234567890.]/ig,'');
|
||||||
|
if (amount) {
|
||||||
|
return parseFloat(amount);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateAmounts: function() {
|
||||||
|
if ($('deliverable_type').checked) {
|
||||||
|
// Fixed cost
|
||||||
|
var cost = Budget.toAmount($('deliverable_fixed_cost').value);
|
||||||
|
Budget.updateAmount($('fixedCost'), cost);
|
||||||
|
} else {
|
||||||
|
// Variable cost
|
||||||
|
var perHour = Budget.toAmount($('deliverable_cost_per_hour').value);
|
||||||
|
var hours = Budget.toAmount($('deliverable_total_hours').value);
|
||||||
|
|
||||||
|
var cost = perHour * hours;
|
||||||
|
Budget.updateAmount($('variableCost'), cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($('deliverable_overhead').value.match('%')) {
|
||||||
|
var overhead_subtotal = (Budget.toAmount($('deliverable_overhead').value) / 100) * cost;
|
||||||
|
} else {
|
||||||
|
var overhead_subtotal = Budget.toAmount($('deliverable_overhead').value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($('deliverable_materials').value.match('%')) {
|
||||||
|
var materials_subtotal = (Budget.toAmount($('deliverable_materials').value) / 100) * cost;
|
||||||
|
} else {
|
||||||
|
var materials_subtotal = Budget.toAmount($('deliverable_materials').value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Profit uses labor cost and overhead
|
||||||
|
if ($('deliverable_profit').value.match('%')) {
|
||||||
|
var profit_subtotal = (Budget.toAmount($('deliverable_profit').value) / 100) * (cost + overhead_subtotal);
|
||||||
|
} else {
|
||||||
|
var profit_subtotal = Budget.toAmount($('deliverable_profit').value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Amounts
|
||||||
|
Budget.updateAmount($('overhead_subtotal'), overhead_subtotal);
|
||||||
|
Budget.updateAmount($('materials_subtotal'), materials_subtotal);
|
||||||
|
Budget.updateAmount($('profit_subtotal'), profit_subtotal);
|
||||||
|
|
||||||
|
var total = cost + overhead_subtotal + materials_subtotal + profit_subtotal;
|
||||||
|
$('deliverable_budget').value = total;
|
||||||
|
$('total-budget-calculation').innerHTML = Budget.number_to_currency(total);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateAmount: function(element, value) {
|
||||||
|
if (element) {
|
||||||
|
element.innerHTML = Budget.number_to_currency(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
changeType: function() {
|
||||||
|
if ($('deliverable_type').checked) {
|
||||||
|
// Fixed
|
||||||
|
$$('.budget-hourly').each(function(ele) { ele.hide(); });
|
||||||
|
$$('.budget-fixed').each(function(ele) { ele.show(); });
|
||||||
|
} else {
|
||||||
|
// Variable
|
||||||
|
$$('.budget-hourly').each(function(ele) { ele.show(); });
|
||||||
|
$$('.budget-fixed').each(function(ele) { ele.hide(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
Budget.updateAmounts();
|
||||||
|
},
|
||||||
|
// Rails-like number_to_currency currency formatting
|
||||||
|
// http://snippets.dzone.com/posts/show/4646
|
||||||
|
number_to_currency: function (number, options) {
|
||||||
|
try {
|
||||||
|
var options = options || {};
|
||||||
|
var precision = options["precision"] || 2;
|
||||||
|
var unit = options["unit"] || "<%= number_to_currency(0, :precision => 0).gsub('0', '') %>";
|
||||||
|
var separator = precision > 0 ? options["separator"] || "." : "";
|
||||||
|
var delimiter = options["delimiter"] || " ";
|
||||||
|
|
||||||
|
var parts = parseFloat(number).toFixed(precision).split('.');
|
||||||
|
|
||||||
|
<% if I18n.locale != :en %>
|
||||||
|
<%= "return Budget.number_with_delimiter(parts[0], delimiter) + separator + parts[1].toString() + unit;" %>
|
||||||
|
<% else %>
|
||||||
|
<%= "return unit + Budget.number_with_delimiter(parts[0], delimiter) + separator + parts[1].toString();" %>
|
||||||
|
<% end %>
|
||||||
|
} catch(e) {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
number_with_delimiter: function (number, delimiter, separator) {
|
||||||
|
try {
|
||||||
|
var delimiter = delimiter || ",";
|
||||||
|
var separator = separator || ".";
|
||||||
|
|
||||||
|
var parts = number.toString().split('.');
|
||||||
|
parts[0] = parts[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1" + delimiter);
|
||||||
|
return parts.join(separator);
|
||||||
|
} catch(e) {
|
||||||
|
return number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Budget = new BudgetModule();
|
||||||
|
|
||||||
|
function toggleAll() {
|
||||||
|
$$('.deliverable-details').each(function(ele) {
|
||||||
|
ele.toggle();
|
||||||
|
});
|
||||||
|
$$('.toggle').each(function(e) {
|
||||||
|
e.toggle();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function expandRow(deliverable_id) {
|
||||||
|
$('deliverable-details-'+ deliverable_id).show();
|
||||||
|
$('deliverable-description-'+ deliverable_id).show();
|
||||||
|
$$('.toggle_' + deliverable_id).each(function(e) {
|
||||||
|
e.toggle();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function collapseRow(deliverable_id) {
|
||||||
|
$('deliverable-details-'+ deliverable_id).hide();
|
||||||
|
$('deliverable-description-'+ deliverable_id).hide();
|
||||||
|
$$('.toggle_' + deliverable_id).each(function(e) {
|
||||||
|
e.toggle();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
$("#preview").html("<%= escape_javascript render(:partial => 'common/preview') %>");
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<p>
|
<p>
|
||||||
<%= l(:message_budget_settings) %>
|
<%= l(:message_budget_settings).html_safe %>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
/* Used to calculate the Budget */
|
/* Used to calculate the Budget */
|
||||||
var BudgetModule = Class.create();
|
var Budget = {
|
||||||
Object.extend(BudgetModule.prototype, {
|
|
||||||
initialize: function () {},
|
|
||||||
|
|
||||||
toAmount: function(value) {
|
toAmount: function(value) {
|
||||||
var amount = value.replace(/[^1234567890.]/ig,'');
|
var amount = value.replace(/[^1234567890.]/ig,'');
|
||||||
if (amount) {
|
if (amount) {
|
||||||
@@ -13,81 +10,79 @@ Object.extend(BudgetModule.prototype, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
updateAmounts: function() {
|
updateAmounts: function() {
|
||||||
if ($('deliverable_type').checked) {
|
if ($('#deliverable_type').checked) {
|
||||||
// Fixed cost
|
// Fixed cost
|
||||||
var cost = Budget.toAmount($('deliverable_fixed_cost').value);
|
var cost = Budget.toAmount($('#deliverable_fixed_cost').val());
|
||||||
Budget.updateAmount($('fixedCost'), cost);
|
Budget.updateAmount($('#fixedCost'), cost);
|
||||||
} else {
|
} else {
|
||||||
// Variable cost
|
// Variable cost
|
||||||
var perHour = Budget.toAmount($('deliverable_cost_per_hour').value);
|
var perHour = Budget.toAmount($('#deliverable_cost_per_hour').val());
|
||||||
var hours = Budget.toAmount($('deliverable_total_hours').value);
|
var hours = Budget.toAmount($('#deliverable_total_hours').val());
|
||||||
|
|
||||||
var cost = perHour * hours;
|
var cost = perHour * hours;
|
||||||
Budget.updateAmount($('variableCost'), cost);
|
Budget.updateAmount($('#variableCost'), cost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ($('deliverable_overhead').value.match('%')) {
|
if ($('#deliverable_overhead').val().match('%')) {
|
||||||
var overhead_subtotal = (Budget.toAmount($('deliverable_overhead').value) / 100) * cost;
|
var overhead_subtotal = (Budget.toAmount($('#deliverable_overhead').val()) / 100) * cost;
|
||||||
} else {
|
} else {
|
||||||
var overhead_subtotal = Budget.toAmount($('deliverable_overhead').value);
|
var overhead_subtotal = Budget.toAmount($('#deliverable_overhead').val());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($('deliverable_materials').value.match('%')) {
|
if ($('#deliverable_materials').val().match('%')) {
|
||||||
var materials_subtotal = (Budget.toAmount($('deliverable_materials').value) / 100) * cost;
|
var materials_subtotal = (Budget.toAmount($('#deliverable_materials').val()) / 100) * cost;
|
||||||
} else {
|
} else {
|
||||||
var materials_subtotal = Budget.toAmount($('deliverable_materials').value);
|
var materials_subtotal = Budget.toAmount($('#deliverable_materials').val());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Profit uses labor cost and overhead
|
// Profit uses labor cost and overhead
|
||||||
if ($('deliverable_profit').value.match('%')) {
|
if ($('#deliverable_profit').val().match('%')) {
|
||||||
var profit_subtotal = (Budget.toAmount($('deliverable_profit').value) / 100) * (cost + overhead_subtotal);
|
var profit_subtotal = (Budget.toAmount($('#deliverable_profit').val()) / 100) * (cost + overhead_subtotal);
|
||||||
} else {
|
} else {
|
||||||
var profit_subtotal = Budget.toAmount($('deliverable_profit').value);
|
var profit_subtotal = Budget.toAmount($('#deliverable_profit').val());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Amounts
|
// Amounts
|
||||||
Budget.updateAmount($('overhead_subtotal'), overhead_subtotal);
|
Budget.updateAmount($('#overhead_subtotal'), overhead_subtotal);
|
||||||
Budget.updateAmount($('materials_subtotal'), materials_subtotal);
|
Budget.updateAmount($('#materials_subtotal'), materials_subtotal);
|
||||||
Budget.updateAmount($('profit_subtotal'), profit_subtotal);
|
Budget.updateAmount($('#profit_subtotal'), profit_subtotal);
|
||||||
|
|
||||||
var total = cost + overhead_subtotal + materials_subtotal + profit_subtotal;
|
var total = cost + overhead_subtotal + materials_subtotal + profit_subtotal;
|
||||||
$('deliverable_budget').value = total;
|
$('#deliverable_budget').val(total);
|
||||||
$('total-budget-calculation').innerHTML = Budget.number_to_currency(total);
|
$('#total-budget-calculation').html(Budget.number_to_currency(total));
|
||||||
},
|
},
|
||||||
|
|
||||||
updateAmount: function(element, value) {
|
updateAmount: function(element, value) {
|
||||||
if (element) {
|
if (element) {
|
||||||
element.innerHTML = Budget.number_to_currency(value);
|
element.html(Budget.number_to_currency(value));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
changeType: function() {
|
changeType: function() {
|
||||||
if ($('deliverable_type').checked) {
|
if ($('#deliverable_type').checked) {
|
||||||
// Fixed
|
// Fixed
|
||||||
$$('.budget-hourly').each(function(ele) { ele.hide(); });
|
$('.budget-hourly').hide();
|
||||||
$$('.budget-fixed').each(function(ele) { ele.show(); });
|
$('.budget-fixed').show();
|
||||||
} else {
|
} else {
|
||||||
// Variable
|
// Variable
|
||||||
$$('.budget-hourly').each(function(ele) { ele.show(); });
|
$('.budget-hourly').show();
|
||||||
$$('.budget-fixed').each(function(ele) { ele.hide(); });
|
$('.budget-fixed').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
Budget.updateAmounts();
|
Budget.updateAmounts();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Rails-like number_to_currency currency formatting
|
// Rails-like number_to_currency currency formatting
|
||||||
// http://snippets.dzone.com/posts/show/4646
|
// http://snippets.dzone.com/posts/show/4646
|
||||||
number_to_currency: function (number, options) {
|
number_to_currency: function (number, options) {
|
||||||
try {
|
try {
|
||||||
var options = options || {};
|
var options = options || {};
|
||||||
var precision = options["precision"] || 2;
|
var precision = options["precision"] || 2;
|
||||||
var unit = options["unit"] || "$";
|
var unit = options["unit"] || " Kč";
|
||||||
var separator = precision > 0 ? options["separator"] || "." : "";
|
var separator = precision > 0 ? options["separator"] || "." : "";
|
||||||
var delimiter = options["delimiter"] || ",";
|
var delimiter = options["delimiter"] || " ";
|
||||||
|
|
||||||
var parts = parseFloat(number).toFixed(precision).split('.');
|
var parts = parseFloat(number).toFixed(precision).split('.');
|
||||||
return unit + Budget.number_with_delimiter(parts[0], delimiter) + separator + parts[1].toString();
|
return Budget.number_with_delimiter(parts[0], delimiter) + separator + parts[1].toString() + unit;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
@@ -105,31 +100,20 @@ Object.extend(BudgetModule.prototype, {
|
|||||||
return number
|
return number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
Budget = new BudgetModule();
|
|
||||||
|
|
||||||
function toggleAll() {
|
function toggleAll() {
|
||||||
$$('.deliverable-details').each(function(ele) {
|
$('.deliverable-details, .toggle').toggle();
|
||||||
ele.toggle();
|
|
||||||
});
|
|
||||||
$$('.toggle').each(function(e) {
|
|
||||||
e.toggle();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function expandRow(deliverable_id) {
|
function expandRow(deliverable_id) {
|
||||||
$('deliverable-details-'+ deliverable_id).show();
|
$('#deliverable-details-'+ deliverable_id).show();
|
||||||
$('deliverable-description-'+ deliverable_id).show();
|
$('#deliverable-description-'+ deliverable_id).show();
|
||||||
$$('.toggle_' + deliverable_id).each(function(e) {
|
$('.toggle_' + deliverable_id).toggle();
|
||||||
e.toggle();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function collapseRow(deliverable_id) {
|
function collapseRow(deliverable_id) {
|
||||||
$('deliverable-details-'+ deliverable_id).hide();
|
$('#deliverable-details-'+ deliverable_id).hide();
|
||||||
$('deliverable-description-'+ deliverable_id).hide();
|
$('#deliverable-description-'+ deliverable_id).hide();
|
||||||
$$('.toggle_' + deliverable_id).each(function(e) {
|
$('.toggle_' + deliverable_id).toggle();
|
||||||
e.toggle();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ tr.deliverable-details td { padding-bottom: 50px ; }
|
|||||||
td.deliverable-actions { padding-left: 10px; width:100px;}
|
td.deliverable-actions { padding-left: 10px; width:100px;}
|
||||||
td.deliverable-actions { padding-left: 10px; width:100px;}
|
td.deliverable-actions { padding-left: 10px; width:100px;}
|
||||||
|
|
||||||
tr.deliverable-details { border: none; }
|
tr.deliverable-details { border: none; white-space: normal; }
|
||||||
tr.deliverable-details p { text-align:left; clear:both;}
|
tr.deliverable-details p { text-align:left; clear:both;}
|
||||||
tr.deliverable-details div { text-align:left; }
|
tr.deliverable-details div { text-align:left; }
|
||||||
tr.deliverable-details table { width: 85%; }
|
tr.deliverable-details table { width: 85%; }
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# Generated by jeweler
|
||||||
|
# DO NOT EDIT THIS FILE
|
||||||
|
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = %q{budget_plugin}
|
||||||
|
s.version = "0.2.0"
|
||||||
|
|
||||||
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||||
|
s.authors = ["Eric Davis"]
|
||||||
|
s.date = %q{2009-10-13}
|
||||||
|
s.description = %q{A plugin for Redmine to manage the set of deliverables for each project, automatically calculating key performance indicators.}
|
||||||
|
s.email = %q{edavis@littlestreamsoftware.com}
|
||||||
|
s.extra_rdoc_files = [
|
||||||
|
"README.rdoc"
|
||||||
|
]
|
||||||
|
s.files = [
|
||||||
|
"COPYRIGHT.txt",
|
||||||
|
"CREDITS.txt",
|
||||||
|
"GPL.txt",
|
||||||
|
"README.rdoc",
|
||||||
|
"Rakefile",
|
||||||
|
"TAGS",
|
||||||
|
"VERSION",
|
||||||
|
"app/controllers/deliverables_controller.rb",
|
||||||
|
"app/controllers/empty",
|
||||||
|
"app/helpers/deliverables_helper.rb",
|
||||||
|
"app/models/budget.rb",
|
||||||
|
"app/models/deliverable.rb",
|
||||||
|
"app/models/empty",
|
||||||
|
"app/models/fixed_deliverable.rb",
|
||||||
|
"app/models/hourly_deliverable.rb",
|
||||||
|
"app/models/member_spent.rb",
|
||||||
|
"app/views/deliverables/_budget.html.erb",
|
||||||
|
"app/views/deliverables/_deliverable.html.erb",
|
||||||
|
"app/views/deliverables/_deliverable_description_row.html.erb",
|
||||||
|
"app/views/deliverables/_deliverable_details_row.html.erb",
|
||||||
|
"app/views/deliverables/_deliverable_summary_row.html.erb",
|
||||||
|
"app/views/deliverables/_form.html.erb",
|
||||||
|
"app/views/deliverables/_list.html.erb",
|
||||||
|
"app/views/deliverables/_sidebar.html.erb",
|
||||||
|
"app/views/deliverables/create.js.rjs",
|
||||||
|
"app/views/deliverables/create_error.js.rjs",
|
||||||
|
"app/views/deliverables/edit.html.erb",
|
||||||
|
"app/views/deliverables/index.html.erb",
|
||||||
|
"app/views/deliverables/semantic.cache",
|
||||||
|
"app/views/empty",
|
||||||
|
"app/views/settings/_budget_settings.rhtml",
|
||||||
|
"assets/images/empty",
|
||||||
|
"assets/images/header.png",
|
||||||
|
"assets/images/toggle-arrow-closed.gif",
|
||||||
|
"assets/images/toggle-arrow-open.gif",
|
||||||
|
"assets/javascripts/budget.js",
|
||||||
|
"assets/javascripts/empty",
|
||||||
|
"assets/stylesheets/budget.css",
|
||||||
|
"assets/stylesheets/empty",
|
||||||
|
"config/locales/ca.yml",
|
||||||
|
"config/locales/en.yml",
|
||||||
|
"config/locales/es.yml",
|
||||||
|
"config/locales/hu.yml",
|
||||||
|
"config/locales/lt.yml",
|
||||||
|
"init.rb",
|
||||||
|
"lang/ca.yml",
|
||||||
|
"lang/en.yml",
|
||||||
|
"lang/es.yml",
|
||||||
|
"lang/hu.yml",
|
||||||
|
"lang/lt.yml",
|
||||||
|
"lib/budget_issue_hook.rb",
|
||||||
|
"lib/budget_project_hook.rb",
|
||||||
|
"lib/issue_patch.rb",
|
||||||
|
"lib/query_patch.rb",
|
||||||
|
"lib/tasks/empty",
|
||||||
|
"rails/init.rb"
|
||||||
|
]
|
||||||
|
s.homepage = %q{https://projects.littlestreamsoftware.com/projects/redmine-budget}
|
||||||
|
s.rdoc_options = ["--charset=UTF-8"]
|
||||||
|
s.require_paths = ["lib"]
|
||||||
|
s.rubyforge_project = %q{budget_plugin}
|
||||||
|
s.rubygems_version = %q{1.3.5}
|
||||||
|
s.summary = %q{A plugin for Redmine to manage the set of deliverables for each project, automatically calculating key performance indicators.}
|
||||||
|
s.test_files = [
|
||||||
|
"spec/spec_helper.rb",
|
||||||
|
"spec/models/deliverable_spec.rb",
|
||||||
|
"spec/models/fixed_deliverable_spec.rb",
|
||||||
|
"spec/models/budget_spec.rb",
|
||||||
|
"spec/models/hourly_deliverable_spec.rb",
|
||||||
|
"spec/controllers/deliverables_controller_spec.rb",
|
||||||
|
"spec/sanity_spec.rb"
|
||||||
|
]
|
||||||
|
|
||||||
|
if s.respond_to? :specification_version then
|
||||||
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
||||||
|
s.specification_version = 3
|
||||||
|
|
||||||
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
||||||
|
else
|
||||||
|
end
|
||||||
|
else
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -16,6 +16,7 @@ ca:
|
|||||||
field_deliverable_subject: Entregable
|
field_deliverable_subject: Entregable
|
||||||
field_due: Data finalització
|
field_due: Data finalització
|
||||||
label_member_rate: Tarifa (EUR)
|
label_member_rate: Tarifa (EUR)
|
||||||
|
label_currency: €
|
||||||
message_updated_issues: Actualitzats %d assumptes
|
message_updated_issues: Actualitzats %d assumptes
|
||||||
message_settings: Introduïu una quantitat en euros o un percentatge en cada camp per a establir un valor per defecte. Utilitzeu <strong>%%</strong> per indicar percentatges.
|
message_settings: Introduïu una quantitat en euros o un percentatge en cada camp per a establir un valor per defecte. Utilitzeu <strong>%%</strong> per indicar percentatges.
|
||||||
label_non_billable_overhead: Imprevists no facturables
|
label_non_billable_overhead: Imprevists no facturables
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
cs:
|
||||||
|
project_module_budget_module: "Rozpočet"
|
||||||
|
budget_title: Rozpočet
|
||||||
|
field_cost_per_hour: Cena za hodinu
|
||||||
|
field_total_hours: Celkem hodin
|
||||||
|
field_overhead: Strop
|
||||||
|
label_overhead: "Strop: "
|
||||||
|
field_materials: Cena materiálu
|
||||||
|
label_materials: "Materiály: "
|
||||||
|
field_profit: Zisk
|
||||||
|
field_budget: Celkový rozpočet
|
||||||
|
label_budget: "Celkový rozpočet: "
|
||||||
|
field_fixed_cost: Pevná cena
|
||||||
|
field_project_manager_signoff: Značka projektového manažera
|
||||||
|
field_client_signoff: Značka klienta
|
||||||
|
field_deliverable: K dodání
|
||||||
|
field_deliverable_subject: K dodání
|
||||||
|
field_due: Splatnost
|
||||||
|
label_member_rate: Zhodnocení (Kč)
|
||||||
|
label_currency: Kč
|
||||||
|
message_updated_issues: upraveno %d úkolů
|
||||||
|
message_budget_settings: Vložte množství v korunách, nebo procento do každého z polí k nastavení výchozí hodnoty množství. Použijte <strong>%%</strong> v poli pro procenta.
|
||||||
|
label_non_billable_overhead: Nezúčtovatelné režie
|
||||||
|
label_materials: Materiály
|
||||||
|
label_profit: Zisk
|
||||||
|
label_new_deliverable: Nové dodání
|
||||||
|
label_fixed_cost: Pevná cena
|
||||||
|
caption_due: Splatnost
|
||||||
|
caption_progress: Postup
|
||||||
|
caption_subject: Předmět
|
||||||
|
caption_score: Hodnocení
|
||||||
|
caption_budget: Rozpočet
|
||||||
|
caption_labor_budget: Rozpočet práce
|
||||||
|
caption_spent: Vyčerpáno
|
||||||
|
label_update_deliverable: Upravit dodání
|
||||||
|
label_labor_budget: "Rozpočet práce: "
|
||||||
|
label_labor_budget_spent: "Vyčerpaný rozpočet práce: "
|
||||||
|
label_labor_budget_remaining: "Zbývající rozpočet práce: "
|
||||||
|
label_progress: "Postup: "
|
||||||
|
label_budget_score: "Hodnocení rozpočtu: "
|
||||||
|
label_overruns: "Překročení: "
|
||||||
|
label_missing_on: Schází
|
||||||
|
label_next_due_date: "Další datum splatnosti: "
|
||||||
|
label_completion: "Dokončení: "
|
||||||
|
label_potential_profit: "Potenciální zisk: "
|
||||||
|
label_bulk_assign: Hromadné přiřazení
|
||||||
|
label_labor: "Práce: "
|
||||||
|
label_fixed_amount: "Pevné množství: "
|
||||||
|
label_hours_estimated: "Hodinový odhad: "
|
||||||
|
label_hours_used: "Hodin využito: "
|
||||||
|
label_toggle_all: "Rozbalit všechny řádky s dodáním"
|
||||||
|
permission_view_budget: "Zobrazit rozpočet"
|
||||||
|
permission_manage_budget: "Spravovat rozpočet"
|
||||||
|
|
||||||
|
number:
|
||||||
|
currency:
|
||||||
|
format:
|
||||||
|
format: "%n %u"
|
||||||
|
unit: "Kč"
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
en-GB:
|
||||||
|
budget_title: Budget
|
||||||
|
field_cost_per_hour: Cost per hour
|
||||||
|
field_total_hours: Total hours
|
||||||
|
field_overhead: Overhead
|
||||||
|
label_overhead: "Overhead: "
|
||||||
|
field_materials: Material costs
|
||||||
|
label_materials: "Materials: "
|
||||||
|
field_profit: Profit
|
||||||
|
field_budget: Total Budget
|
||||||
|
label_budget: "Total Budget: "
|
||||||
|
field_fixed_cost: Fixed Bid
|
||||||
|
field_project_manager_signoff: Project Manager Signoff
|
||||||
|
field_client_signoff: Client Signoff
|
||||||
|
field_deliverable: Deliverable
|
||||||
|
field_deliverable_subject: Deliverable
|
||||||
|
field_due: Due Date
|
||||||
|
label_member_rate: Rate (£)
|
||||||
|
label_currency: £
|
||||||
|
message_updated_issues: Updated %d issues
|
||||||
|
message_budget_settings: Enter an amount in pounds, or a percentage into each field to set your default amount. Use <strong>%%</strong> in the field for percentages.
|
||||||
|
label_non_billable_overhead: Non billable overhead
|
||||||
|
label_materials: Materials
|
||||||
|
label_profit: Profit
|
||||||
|
label_new_deliverable: New deliverable
|
||||||
|
label_fixed_cost: Fixed cost
|
||||||
|
caption_due: Due
|
||||||
|
caption_progress: Progress
|
||||||
|
caption_subject: Subject
|
||||||
|
caption_score: Score
|
||||||
|
caption_budget: Budget
|
||||||
|
caption_labor_budget: Labor Budget
|
||||||
|
caption_spent: Spent
|
||||||
|
label_update_deliverable: Update Deliverable
|
||||||
|
label_labor_budget: "Labor Budget: "
|
||||||
|
label_labor_budget_spent: "Labor Budget Spent: "
|
||||||
|
label_labor_budget_remaining: "Labor Budget Remaining: "
|
||||||
|
label_progress: "Progress: "
|
||||||
|
label_budget_score: "Budget Score: "
|
||||||
|
label_overruns: "Overruns: "
|
||||||
|
label_missing_on: Missing on
|
||||||
|
label_next_due_date: "Next Due Date: "
|
||||||
|
label_completion: "Completion: "
|
||||||
|
label_potential_profit: "Potential Profit: "
|
||||||
|
label_bulk_assign: Bulk Assign
|
||||||
|
label_labor: "Labor: "
|
||||||
|
label_fixed_amount: "Fixed Amount: "
|
||||||
|
label_hours_estimated: "Hours Estimated: "
|
||||||
|
label_hours_used: "Hours Used: "
|
||||||
|
label_toggle_all: "Expand all deliverable rows"
|
||||||
@@ -16,8 +16,9 @@ en:
|
|||||||
field_deliverable_subject: Deliverable
|
field_deliverable_subject: Deliverable
|
||||||
field_due: Due Date
|
field_due: Due Date
|
||||||
label_member_rate: Rate ($)
|
label_member_rate: Rate ($)
|
||||||
|
label_currency: $
|
||||||
message_updated_issues: Updated %d issues
|
message_updated_issues: Updated %d issues
|
||||||
message_budget_settings: Enter a dollar amount or percentage into each field to set your default amount. Use <strong>%%</strong> in the field for percentages.
|
message_budget_settings: Enter an amount in dollars, or a percentage into each field to set your default amount. Use <strong>%%</strong> in the field for percentages.
|
||||||
label_non_billable_overhead: Non billable overhead
|
label_non_billable_overhead: Non billable overhead
|
||||||
label_materials: Materials
|
label_materials: Materials
|
||||||
label_profit: Profit
|
label_profit: Profit
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ es:
|
|||||||
field_deliverable_subject: Entregable
|
field_deliverable_subject: Entregable
|
||||||
field_due: Fecha entrega
|
field_due: Fecha entrega
|
||||||
label_member_rate: Tarifa (EUR)
|
label_member_rate: Tarifa (EUR)
|
||||||
|
label_currency: €
|
||||||
message_updated_issues: Actualizadas %d peticiones
|
message_updated_issues: Actualizadas %d peticiones
|
||||||
message_settings: Introduce cantidades en euros o porcentajes en cada campo para establecer los valores por defecto. Usa <strong>%%</strong> para indicar porcentajes.
|
message_settings: Introduce cantidades en euros o porcentajes en cada campo para establecer los valores por defecto. Usa <strong>%%</strong> para indicar porcentajes.
|
||||||
label_non_billable_overhead: Imprevistos no facturables
|
label_non_billable_overhead: Imprevistos no facturables
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
fr:
|
||||||
|
budget_title: Budget
|
||||||
|
field_cost_per_hour: Coût par heure
|
||||||
|
field_total_hours: Total heures
|
||||||
|
field_overhead: Frais géneraux
|
||||||
|
label_overhead: "Frais géneraux: "
|
||||||
|
field_materials: Cout des matières premières
|
||||||
|
label_materials: "Cout des matières premières: "
|
||||||
|
field_profit: Profit
|
||||||
|
field_budget: Budget total
|
||||||
|
label_budget: "Budget total: "
|
||||||
|
field_fixed_cost: Coût fixe
|
||||||
|
field_project_manager_signoff: Validation par le gestionnaire de projet
|
||||||
|
field_client_signoff: Validation par le client
|
||||||
|
field_deliverable: Livrable
|
||||||
|
field_deliverable_subject: Livrable
|
||||||
|
field_due: "Date d'echeance"
|
||||||
|
label_member_rate: Tarif (€)
|
||||||
|
label_currency: €
|
||||||
|
message_updated_issues: "%d demandes mises à jour"
|
||||||
|
message_budget_settings: Entrez un montant en euros ou un pourcentage dans chaque champs pour régler le montant par defaut. Utilisez <strong>%%</strong> dans le champ pour des pourcentages.
|
||||||
|
label_non_billable_overhead: Frais géneraux non facturables
|
||||||
|
label_materials: Matières premières
|
||||||
|
label_profit: Profit
|
||||||
|
label_new_deliverable: Nouveau livrable
|
||||||
|
label_fixed_cost: Coût fixe
|
||||||
|
caption_due: Dû
|
||||||
|
caption_progress: Progressions
|
||||||
|
caption_subject: Objet
|
||||||
|
caption_score: Score
|
||||||
|
caption_budget: Budget
|
||||||
|
caption_labor_budget: Budget de travail
|
||||||
|
caption_spent: Dépense
|
||||||
|
label_update_deliverable: Mettre à jour le livrable
|
||||||
|
label_labor_budget: "Budget de travail: "
|
||||||
|
label_labor_budget_spent: "Budget de travail depensé: "
|
||||||
|
label_labor_budget_remaining: "Budget de travail restant: "
|
||||||
|
label_progress: "Progression: "
|
||||||
|
label_budget_score: "Score du budget: "
|
||||||
|
label_overruns: "Dépassement: "
|
||||||
|
label_missing_on: Manquant sur
|
||||||
|
label_next_due_date: "Prochaine date d'échéance: "
|
||||||
|
label_completion: "Completion: "
|
||||||
|
label_potential_profit: "Potential Profit: "
|
||||||
|
label_bulk_assign: Assigner en vrac
|
||||||
|
label_labor: "Travail: "
|
||||||
|
label_fixed_amount: "Montant fixe: "
|
||||||
|
label_hours_estimated: "Heures estimées: "
|
||||||
|
label_hours_used: "Heures utilisées: "
|
||||||
|
label_toggle_all: "Développer tous les livrables"
|
||||||
@@ -12,4 +12,5 @@ hu:
|
|||||||
field_deliverable_subject: Teljesítés
|
field_deliverable_subject: Teljesítés
|
||||||
field_due: Esedékesség dátuma
|
field_due: Esedékesség dátuma
|
||||||
label_member_rate: Részesedés ($)
|
label_member_rate: Részesedés ($)
|
||||||
|
label_currency: $
|
||||||
message_updated_issues: Frissítve % feladat
|
message_updated_issues: Frissítve % feladat
|
||||||
|
|||||||
@@ -12,4 +12,5 @@ lt:
|
|||||||
field_deliverable_subject: Pateiktis
|
field_deliverable_subject: Pateiktis
|
||||||
field_due: Data iki
|
field_due: Data iki
|
||||||
label_member_rate: Užmokestis (LTL)
|
label_member_rate: Užmokestis (LTL)
|
||||||
|
label_currency: $
|
||||||
message_updated_issues: Atnaujinta(i) %d darbų(ai)
|
message_updated_issues: Atnaujinta(i) %d darbų(ai)
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
pt-BR:
|
||||||
|
budget_title: Orçamento
|
||||||
|
field_cost_per_hour: Custo por hora
|
||||||
|
field_total_hours: Total de horas
|
||||||
|
field_overhead: Despesas
|
||||||
|
label_overhead: "Despesas: "
|
||||||
|
field_materials: Custos com materiais
|
||||||
|
label_materials: "Materiais: "
|
||||||
|
field_profit: Lucro
|
||||||
|
field_budget: Orçamento Total
|
||||||
|
label_budget: "Orçamento Total: "
|
||||||
|
field_fixed_cost: Lance Fixo
|
||||||
|
field_project_manager_signoff: Assinatura do Gerente de Projeto
|
||||||
|
field_client_signoff: Assinatura do Cliente
|
||||||
|
field_deliverable: Produto
|
||||||
|
field_deliverable_subject: Produto
|
||||||
|
field_due: Data Prevista
|
||||||
|
label_member_rate: Custo (R$)
|
||||||
|
label_currency: R$
|
||||||
|
message_updated_issues: Atualizou %d tarefas
|
||||||
|
message_budget_settings: Informe um valor total ou porcentagem em cada campo para definir o Total padrão. Use <strong>%%</strong> para porcentagens.
|
||||||
|
label_non_billable_overhead: Despesas não faturáveis
|
||||||
|
label_materials: Materiais
|
||||||
|
label_profit: Orçamento
|
||||||
|
label_new_deliverable: Novo produto
|
||||||
|
label_fixed_cost: Custo fixo
|
||||||
|
caption_due: Data prevista
|
||||||
|
caption_progress: Progresso
|
||||||
|
caption_subject: Assunto
|
||||||
|
caption_score: Classificacao
|
||||||
|
caption_budget: Orçamento
|
||||||
|
caption_labor_budget: Orçamento de Trabalho
|
||||||
|
caption_spent: Gasto
|
||||||
|
label_update_deliverable: Atualizar Produto
|
||||||
|
label_labor_budget: "Orçamento de Trabalho: "
|
||||||
|
label_labor_budget_spent: "Orçamento de Trabalho Gasto: "
|
||||||
|
label_labor_budget_remaining: "Orçamento de Trabalho Remanescente: "
|
||||||
|
label_progress: "Progresso: "
|
||||||
|
label_budget_score: "Classificacao do Orçamento: "
|
||||||
|
label_overruns: "Excedente: "
|
||||||
|
label_missing_on: Ausente no
|
||||||
|
label_next_due_date: "Proxima Data Prevista: "
|
||||||
|
label_completion: "Conclusao: "
|
||||||
|
label_potential_profit: "Lucro Potencial: "
|
||||||
|
label_bulk_assign: Atribuicao em massa
|
||||||
|
label_labor: "Trabalho: "
|
||||||
|
label_fixed_amount: "Montante Total: "
|
||||||
|
label_hours_estimated: "Horas Estimatadas: "
|
||||||
|
label_hours_used: "Horas Usadas: "
|
||||||
|
label_toggle_all: "Expandir todas as linhas de produtos"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
match '/projects/:id/deliverables/index', :to => "deliverables#index"
|
||||||
|
match '/projects/:id/deliverables/preview', :to => "deliverables#preview", :as => :preview_deliverable
|
||||||
|
match '/projects/:id/deliverables', :to => "deliverables#create", :via => "post", :as => :create_deliverable
|
||||||
|
match '/projects/:id/deliverables/:deliverable_id/edit', :to => "deliverables#edit", :via => "get", :as => :edit_deliverable
|
||||||
|
match '/projects/:id/deliverables/:deliverable_id', :to => "deliverables#update", :via => "put", :as => :update_deliverable
|
||||||
|
match '/projects/:id/deliverables/:deliverable_id/bulk_assign_issues', :to => "deliverables#bulk_assign_issues", :via => "post", :as => :bulk_assign_issues_deliverable
|
||||||
|
match '/projects/:id/deliverables/:deliverable_id/issues', :to => "deliverables#issues", :as => :issues_deliverable
|
||||||
|
match '/projects/:id/deliverables/:deliverable_id', :to => "deliverables#destroy", :via => "delete", :as => :destroy_deliverable
|
||||||
@@ -5,16 +5,15 @@ begin
|
|||||||
require 'rate' unless Object.const_defined?('Rate')
|
require 'rate' unless Object.const_defined?('Rate')
|
||||||
rescue LoadError
|
rescue LoadError
|
||||||
# rate_plugin is not installed
|
# rate_plugin is not installed
|
||||||
raise Exception.new("ERROR: The Rate plugin is not installed. Please install the Rate plugin from https://projects.littlestreamsoftware.com/projects/redmine-rate")
|
# raise Exception.new("ERROR: The Rate plugin is not installed. Please install the Rate plugin from https://projects.littlestreamsoftware.com/projects/redmine-rate")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Patches to the Redmine core.
|
# Patches to the Redmine core.
|
||||||
require 'dispatcher'
|
|
||||||
require 'issue_patch'
|
require 'issue_patch'
|
||||||
require 'query_patch'
|
require 'query_patch'
|
||||||
Dispatcher.to_prepare do
|
ActionDispatch::Callbacks.to_prepare do
|
||||||
Issue.send(:include, IssuePatch)
|
Issue.send(:include, IssuePatch) unless Issue.included_modules.include? IssuePatch
|
||||||
Query.send(:include, QueryPatch)
|
Query.send(:include, QueryPatch) unless Query.included_modules.include? QueryPatch
|
||||||
end
|
end
|
||||||
|
|
||||||
# Hooks
|
# Hooks
|
||||||
@@ -43,5 +42,6 @@ Redmine::Plugin.register :budget_plugin do
|
|||||||
permission :manage_budget, { :deliverables => [:new, :edit, :create, :update, :destroy, :preview, :bulk_assign_issues]}
|
permission :manage_budget, { :deliverables => [:new, :edit, :create, :update, :destroy, :preview, :bulk_assign_issues]}
|
||||||
end
|
end
|
||||||
|
|
||||||
menu :project_menu, :budget, {:controller => "deliverables", :action => 'index'}, :caption => :budget_title
|
menu :project_menu, :budget, {:controller => "deliverables", :action => 'index'}, :caption => :budget_title, :after => :activity, :param => :id
|
||||||
end
|
end
|
||||||
|
require 'redmine_budget/hooks/controller_timelog_available_criterias_hook'
|
||||||
|
|||||||
@@ -42,12 +42,27 @@ class BudgetIssueHook < Redmine::Hook::ViewListener
|
|||||||
content_tag('option', l(:label_none), :value => 'none') +
|
content_tag('option', l(:label_none), :value => 'none') +
|
||||||
options_from_collection_for_select(Deliverable.find_all_by_project_id(context[:project].id, :order => 'subject ASC'), :id, :subject))
|
options_from_collection_for_select(Deliverable.find_all_by_project_id(context[:project].id, :order => 'subject ASC'), :id, :subject))
|
||||||
|
|
||||||
return content_tag(:p, "<label>#{l(:field_deliverable)}: " + select + "</label>")
|
return content_tag(:p, content_tag(:label, l(:field_deliverable)) + select)
|
||||||
else
|
else
|
||||||
return ''
|
return ''
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_deliverable_on_issue(context)
|
||||||
|
if context[:params] && context[:params][:issue] && context[:params][:issue][:deliverable_id].present?
|
||||||
|
context[:issue].deliverable = Deliverable.find_by_id_and_project_id(context[:params][:issue][:deliverable_id].to_i, context[:issue].project.id)
|
||||||
|
end
|
||||||
|
return ''
|
||||||
|
end
|
||||||
|
|
||||||
|
def controller_issues_new_before_save(context = {})
|
||||||
|
set_deliverable_on_issue(context)
|
||||||
|
end
|
||||||
|
|
||||||
|
def controller_issues_edit_before_save(context = {})
|
||||||
|
set_deliverable_on_issue(context)
|
||||||
|
end
|
||||||
|
|
||||||
# Saves the Deliverable assignment to the issue
|
# Saves the Deliverable assignment to the issue
|
||||||
#
|
#
|
||||||
# Context:
|
# Context:
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
module RedmineBudget
|
||||||
|
module Hooks
|
||||||
|
class ControllerTimelogAvailableCriteriasHook < Redmine::Hook::ViewListener
|
||||||
|
# Adds the Deliverable as a filter to the Timelog Report
|
||||||
|
def controller_timelog_available_criterias(context={})
|
||||||
|
context[:available_criterias]["deliverable_id"] = {
|
||||||
|
:sql => "#{Issue.table_name}.deliverable_id",
|
||||||
|
:klass => Deliverable,
|
||||||
|
:label => :field_deliverable
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
require File.dirname(__FILE__) + "/../init"
|
||||||
@@ -12,9 +12,10 @@ end
|
|||||||
describe DeliverablesController,"#index when logged in" do
|
describe DeliverablesController,"#index when logged in" do
|
||||||
before(:each) do
|
before(:each) do
|
||||||
@project = mock_model(Project)
|
@project = mock_model(Project)
|
||||||
|
@deliverable = mock_model(Deliverable)
|
||||||
|
|
||||||
Deliverable.stub!(:count).and_return(0)
|
Deliverable.stub!(:count).and_return(1)
|
||||||
Deliverable.stub!(:find).and_return([])
|
Deliverable.stub!(:find).and_return([@deliverable])
|
||||||
|
|
||||||
Project.should_receive(:find).with(@project.to_param).and_return(@project)
|
Project.should_receive(:find).with(@project.to_param).and_return(@project)
|
||||||
Project.should_receive(:find).with(@project.id).and_return(@project)
|
Project.should_receive(:find).with(@project.id).and_return(@project)
|
||||||
@@ -31,6 +32,25 @@ describe DeliverablesController,"#index when logged in" do
|
|||||||
assigns[:deliverables].should_not be_nil
|
assigns[:deliverables].should_not be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should set @display_form to false by default" do
|
||||||
|
get :index, :id => @project.id
|
||||||
|
assigns[:display_form].should eql(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should set @display_form to true if there are no deliverables" do
|
||||||
|
Deliverable.should_receive(:count).and_return(0)
|
||||||
|
Deliverable.should_receive(:find).and_return([])
|
||||||
|
|
||||||
|
get :index, :id => @project.id
|
||||||
|
|
||||||
|
assigns[:display_form].should eql(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should set @display_form to true if the 'new' parameter is used" do
|
||||||
|
get :index, :id => @project.id, :new => 'true'
|
||||||
|
assigns[:display_form].should eql(true)
|
||||||
|
end
|
||||||
|
|
||||||
it "should only show the deliverables for the current project only" do
|
it "should only show the deliverables for the current project only" do
|
||||||
# TODO: Get spec working for full finder
|
# TODO: Get spec working for full finder
|
||||||
Deliverable.should_receive(:find)
|
Deliverable.should_receive(:find)
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
require File.dirname(__FILE__) + '/../spec_helper'
|
||||||
|
|
||||||
|
include Redmine::Hook::Helper
|
||||||
|
|
||||||
|
def controller
|
||||||
|
@controller ||= TimelogController.new
|
||||||
|
@controller.response ||= ActionController::TestResponse.new
|
||||||
|
@controller
|
||||||
|
end
|
||||||
|
|
||||||
|
def request
|
||||||
|
@request ||= ActionController::TestRequest.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_hook
|
||||||
|
call_hook(:controller_timelog_available_criterias, {:available_criterias => @available_criterias})
|
||||||
|
end
|
||||||
|
|
||||||
|
describe TimelogController, '#controller_timelog_available_criterias_hook', :type => :controller do
|
||||||
|
before(:each) do
|
||||||
|
@available_criterias = {
|
||||||
|
'project' => {:sql => 'project_id', :klass => Project, :label => :label_project}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should always return an empty string" do
|
||||||
|
run_hook.should be_blank
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should add a new hash to the available_criterias" do
|
||||||
|
run_hook
|
||||||
|
|
||||||
|
@available_criterias.should have(2).keys
|
||||||
|
@available_criterias.key?('deliverable_id').should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should set the :sql field to use the issue's deliverable_id" do
|
||||||
|
run_hook
|
||||||
|
|
||||||
|
@available_criterias['deliverable_id'][:sql].should eql("issues.deliverable_id")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should set the :klass field to Deliverable" do
|
||||||
|
run_hook
|
||||||
|
|
||||||
|
@available_criterias['deliverable_id'][:klass].should eql(Deliverable)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should set the :label field to :field_deliverable" do
|
||||||
|
run_hook
|
||||||
|
|
||||||
|
@available_criterias['deliverable_id'][:label].should eql(:field_deliverable)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user