35 Commits
Author SHA1 Message Date
Richard Říman 7104521c50 added missing czech translations 2012-09-12 16:34:50 +02:00
Richard Říman 07e33f9401 added module name czech translation 2012-07-27 14:20:42 +02:00
Richard Říman 0aeafe75ee Merge https://github.com/edavis10/redmine-budget-plugin 2012-03-02 15:50:13 +01:00
Eric Davis 7d57f10038 Fix incorrect setting in the French locale 2012-03-01 14:02:31 -08:00
Eric Davis 296d9f6d75 Merge pull request #1 from voondo/master
French translation
2012-01-19 14:47:00 -08:00
Romain Lalaut c8266b2b62 french translation 2012-01-16 20:29:44 +01:00
Palo Delincak bc33dd1127 preklad 2011-12-21 11:41:04 +01:00
Palo Delincak fba5042ba3 preklad 2011-12-21 10:05:57 +01:00
Palo Delincak fd4b640c2f preklady budgetu 2011-12-21 09:58:41 +01:00
Richard 952646f485 translates 2011-11-30 09:28:43 +01:00
Eric Davis 6c37e97bee [#3724] Use en-GB.yml for British 2010-11-18 08:47:38 -08:00
Eric Davis cce10b04d8 Merge remote branch 'pasco/master'
Conflicts:
	app/views/deliverables/_deliverable_summary_row.html.erb
2010-11-18 08:45:59 -08:00
Eric Davis 04d0be203e [#2809] Add some extra css classes on the deliverables list 2010-11-18 08:42:57 -08:00
Eric Davis 1d608a6c47 Fix for setting the deliverable on new issues. 2010-08-18 13:37:01 -07:00
Eric Davis d3e7f2d2ae Fix the bulk edit style from Redmine Core changes. 2010-07-01 13:37:37 -07:00
Eric Davis aa9649fde3 [#4225] Add the Deliverable as a criteria in the Time Entries Report. 2010-07-01 13:34:24 -07:00
Eric Davis e25f841fe2 [#4225] Generated a hook for the timelog available criterias. 2010-07-01 13:13:08 -07:00
Eric Davis b3c625d9d9 Move init back into init.rb. 2010-07-01 12:57:23 -07:00
Jason Derrett 4b1a4cd156 For client project, remove 'view all issues' from sidebar 2010-04-21 10:32:16 -05:00
Eric Davis 624b80a128 Fixed a SQL error on postgres 2010-03-22 15:13:13 -07:00
Eric Davis 43862fc3b9 Fixes editing an issue's deliverable.
Broken by the Redmine core in r3308.
2010-03-17 18:45:20 -07:00
Eric Davis 94afb7a1ca Prevent a stack error in development 2010-03-15 15:49:20 -07:00
Jon Pascoe 2125241f38 added new localisation variables to other configs to avoid breaking them 2010-03-03 08:39:56 +00:00
root 4b18f20eba updated views and helpers to specify the currency unit when outputting currency values.
A label_currency localisation variable has been added to config/locales/en.yml
2010-03-03 08:14:39 +00:00
Eric Davis a53ceb2804 [#3294] Expand the new form if the project doesn't have any deliverables yet. 2009-11-11 13:16:00 -08:00
Eric Davis d552aa7f6f [#3294] Allow the query string to control if the new deliverable form is visible. 2009-11-11 13:11:21 -08:00
Eric Davis aa541f42bb Added generated gemspec 2009-10-13 21:23:08 -07:00
Eric Davis 19968fa0b8 Converted init.rb for a rails GemPlugin 2009-10-13 21:21:27 -07:00
Eric Davis c7ac326959 Added version 2009-10-13 21:13:35 -07:00
Eric Davis 46f53a9332 Added git ignores 2009-10-13 21:12:28 -07:00
Eric Davis 6692dffe0f Added jeweler tasks 2009-10-13 21:12:04 -07:00
Adam Soltys 6a9ead4ad0 Set issues' deliverable_id to NULL if their deliverable is deleted 2009-08-31 15:17:43 -07:00
Adam Soltys 642eb1f4ee Allow description text to wrap 2009-08-27 12:08:56 -07:00
Eric Davis a55227c9bb [#2863] Split the single deliverable template into 3 partials. 2009-08-07 10:42:03 -07:00
Eric Davis 14a6aa2c28 Added a few plugin hooks for Deliverables 2009-08-05 14:57:52 -07:00
31 changed files with 685 additions and 130 deletions
+7
View File
@@ -0,0 +1,7 @@
*.tar.gz
*.zip
.DS_Store
coverage
doc
pkg
rdoc
+26
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
0.2.0
+22 -20
View File
@@ -13,8 +13,8 @@ class DeliverablesController < ApplicationController
@deliverable_count = Deliverable.count(:conditions => { :project_id => @project.id}) @deliverable_count = Deliverable.count(:conditions => { :project_id => @project.id})
@deliverable_pages = Paginator.new self, @deliverable_count, per_page_option, params['page'] @deliverable_pages = Paginator.new self, @deliverable_count, per_page_option, params['page']
@deliverables = Deliverable.find(:all, @deliverables = Deliverable.find(:all,
{ {
:conditions => { :project_id => @project.id}, :conditions => { :project_id => @project.id},
:limit => per_page_option, :limit => per_page_option,
:offset => @deliverable_pages.current.offset :offset => @deliverable_pages.current.offset
@@ -22,22 +22,25 @@ class DeliverablesController < ApplicationController
) )
@deliverables = sort_if_needed @deliverables @deliverables = sort_if_needed @deliverables
@deliverable = Deliverable.new @deliverable = Deliverable.new
@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' render :partial => 'common/preview'
end end
# Saves a new Deliverable # Saves a new Deliverable
def create def create
if params[:deliverable][:type] == FixedDeliverable.name if params[:deliverable][:type] == FixedDeliverable.name
@@ -47,7 +50,7 @@ class DeliverablesController < ApplicationController
else else
@deliverable = Deliverable.new(params[:deliverable]) @deliverable = Deliverable.new(params[:deliverable])
end end
@deliverable.project = @project @deliverable.project = @project
@budget = Budget.new(@project.id) @budget = Budget.new(@project.id)
respond_to do |format| respond_to do |format|
@@ -70,11 +73,11 @@ class DeliverablesController < ApplicationController
# Updates an existing Deliverable, optionally changing it's type # Updates an existing Deliverable, optionally changing it's type
def update def update
@deliverable = Deliverable.find(params[:deliverable_id]) @deliverable = Deliverable.find(params[:deliverable_id])
if params[:deliverable][:type] != @deliverable.class if params[:deliverable][:type] != @deliverable.class
@deliverable = @deliverable.change_type(params[:deliverable][:type]) @deliverable = @deliverable.change_type(params[:deliverable][:type])
end end
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)
@@ -84,20 +87,20 @@ class DeliverablesController < ApplicationController
end end
end end
end end
# Removes the Deliverable # Removes the Deliverable
def destroy def destroy
@deliverable = Deliverable.find_by_id_and_project_id(params[:deliverable_id], @project.id) @deliverable = Deliverable.find_by_id_and_project_id(params[:deliverable_id], @project.id)
render_404 and return unless @deliverable render_404 and return unless @deliverable
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.id
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
def issues def issues
@query = Query.new(:name => "_") @query = Query.new(:name => "_")
@@ -113,25 +116,25 @@ class DeliverablesController < ApplicationController
redirect_to :controller => 'issues', :action => 'index', :project_id => @project.id redirect_to :controller => 'issues', :action => 'index', :project_id => @project.id
end end
# Assigns issues to the Deliverable based on their Version # Assigns issues to the Deliverable based on their Version
def bulk_assign_issues def bulk_assign_issues
@deliverable = Deliverable.find_by_id_and_project_id(params[:deliverable_id], @project.id) @deliverable = Deliverable.find_by_id_and_project_id(params[:deliverable_id], @project.id)
render_404 and return unless @deliverable render_404 and return unless @deliverable
render_403 and return unless @deliverable.editable_by?(User.current) render_403 and return unless @deliverable.editable_by?(User.current)
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.id
end end
private private
def find_project def find_project
@project = Project.find(params[:id]) @project = Project.find(params[:id])
end end
def get_settings def get_settings
@settings = Setting.plugin_budget_plugin @settings = Setting.plugin_budget_plugin
end end
@@ -144,7 +147,7 @@ class DeliverablesController < ApplicationController
return { :order => sort_clause } return { :order => sort_clause }
end end
end end
# Sort +deliverables+ manually using the virtual fields # Sort +deliverables+ manually using the virtual fields
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])
@@ -165,5 +168,4 @@ class DeliverablesController < ApplicationController
return deliverables return deliverables
end end
end end
end end
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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)
+8 -8
View File
@@ -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 %>
+3 -83
View File
@@ -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>&nbsp;</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,65 @@
<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, :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>&nbsp;</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>
+1
View File
@@ -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
View File
@@ -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 />
+144 -2
View File
@@ -3,7 +3,7 @@
</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 }, <% remote_form_for :deliverable, @deliverable, :url => {:controller => 'deliverables', :action => 'create', :id => @project },
:method => :post, :builder => TabularFormBuilder, :lang => current_language, :method => :post, :builder => TabularFormBuilder, :lang => current_language,
@@ -28,5 +28,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 %>
+3 -4
View File
@@ -75,19 +75,18 @@ Object.extend(BudgetModule.prototype, {
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;
} }
+2 -2
View File
@@ -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%; }
@@ -46,4 +46,4 @@ p.total-budget #total-budget-calculation { }
.jstElements button { padding: 0; margin-right: 0px;} .jstElements button { padding: 0; margin-right: 0px;}
.tabular div.splitcontentright label { margin-left: 0; float: none;} .tabular div.splitcontentright label { margin-left: 0; float: none;}
.tabular div.splitcontentright p { padding-left: 80px; } .tabular div.splitcontentright p { padding-left: 80px; }
+101
View File
@@ -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
+1
View File
@@ -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
+59
View File
@@ -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č"
+50
View File
@@ -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"
+2 -1
View File
@@ -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
+1
View File
@@ -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
+50
View File
@@ -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"
+1
View File
@@ -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
+1
View File
@@ -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)
+3 -2
View File
@@ -13,8 +13,8 @@ require 'dispatcher'
require 'issue_patch' require 'issue_patch'
require 'query_patch' require 'query_patch'
Dispatcher.to_prepare do Dispatcher.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
@@ -45,3 +45,4 @@ Redmine::Plugin.register :budget_plugin do
menu :project_menu, :budget, {:controller => "deliverables", :action => 'index'}, :caption => :budget_title menu :project_menu, :budget, {:controller => "deliverables", :action => 'index'}, :caption => :budget_title
end end
require 'redmine_budget/hooks/controller_timelog_available_criterias_hook'
+17 -2
View File
@@ -41,12 +41,27 @@ class BudgetIssueHook < Redmine::Hook::ViewListener
content_tag('option', l(:label_no_change_option), :value => '') + content_tag('option', l(:label_no_change_option), :value => '') +
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
# #
@@ -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
+1
View File
@@ -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)
@@ -30,6 +31,25 @@ describe DeliverablesController,"#index when logged in" do
get :index, :id => @project.id get :index, :id => @project.id
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
@@ -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