23 Commits

Author SHA1 Message Date
Eric Davis
8413b26a15 [#2431] Use RedminePluginSupport for rake tasks. 2009-04-27 15:40:35 -07:00
Eric Davis
f7733011df [#2431] Reformatted the CREDITS file 2009-04-27 15:35:46 -07:00
Eric Davis
a45253329d [#2431] Updated the Readme and install instructions. 2009-04-27 15:34:49 -07:00
Eric Davis
085dc1deb4 Added an explicit requirement on the Rate plugin. 2009-04-27 15:31:29 -07:00
Eric Davis
425047f305 [#2431] Updated the Readme to rdoc 2009-04-27 15:27:36 -07:00
Eric Davis
ffb20347ce Renamed README 2009-04-27 15:21:45 -07:00
Eric Davis
ee2e2dae8e [#2431] Requiring Redmine 0.8.x now 2009-04-27 15:21:12 -07:00
Eric Davis
3b26d08f0d [#2405] Make the Total Budget in the header bold 2009-04-27 15:13:11 -07:00
Eric Davis
833c2877af [#2403] [#2404] Added a Total Budget column to the Deliverable row. 2009-04-27 15:07:30 -07:00
Eric Davis
d88fce97f8 [#1498] Added the Deliverable description to the expanded row. 2009-04-27 14:22:20 -07:00
Eric Davis
931c1ef6ed [#1999] Added a link to expand/collapse all Deliverable rows to the sidebar 2009-04-27 14:13:51 -07:00
Eric Davis
2018561e6a [#1919] Moved the JavaScript for calculating the Budget to an assets file. 2009-04-27 13:57:52 -07:00
Eric Davis
83d5af4a4a [#2157] Added Lithuanian translation by Sergej Jegorov. 2009-04-27 13:44:02 -07:00
Eric Davis
eb60581d1a [#1840] Added Spanish and Catalan translations from Pau Garcia i Quiles. 2009-04-27 13:33:32 -07:00
Eric Davis
e218218ffb [#1840] Completed the i18n string support.
Thanks to Pau Garcia i Quiles for the patch
2009-04-27 13:31:47 -07:00
Eric Davis
7ed5c20a93 [#1959] Fixed a typo in the css class name. 2009-04-27 13:24:44 -07:00
Eric Davis
72f2f77a60 [#2040] Added Hungarian translation by Gergő Jónás. 2009-04-27 13:20:11 -07:00
Eric Davis
fd891e7f51 Changed the Redmine core patches to use the Rails dispatcher.
This will let the core patches work in development mode when the classes
are reloaded.
2009-04-10 08:49:12 -07:00
Eric Davis
71ab0c6b54 [#2301] Check that the session has a sort before trying to access it. 2009-04-01 08:43:37 -07:00
Eric Davis
328cd3fc3f Removed GLoc calls, should stay 0.8 compatibile 2009-02-26 17:09:52 -08:00
Eric Davis
4205fb95a3 Updated to the Rails 2.2.x i18n format 2009-02-26 17:09:12 -08:00
Eric Davis
3f00f12107 Added a hook into Project#copy so Deliverables are copied to the new project. 2009-02-25 10:26:43 -08:00
Eric Davis
3720c486b4 Added a Deliverable#to_s so the Deliverable subject can be printed. 2009-02-18 14:44:06 -08:00
32 changed files with 611 additions and 318 deletions

View File

@@ -1,10 +1,6 @@
Thanks go to the following people for patches and contributions:
Eric Davis of Little Stream Software
- Project Maintainer
* Eric Davis of Little Stream Software - Project Maintainer
* Peter Chester of Shane and Peter, Inc - Project sponsorship
* Shane Pearlman of Shane and Peter, Inc - Project sponsorship
Peter Chester of Shane and Peter, Inc
- Project sponsership
Shane Pearlman of Shane and Peter, Inc
- Project sponsership

View File

@@ -1,8 +1,8 @@
## Budget Plugin
== Budget Plugin
Budget is a plugin to manage the set of deliverables for each project, automatically calculating key performance indicators.
## Features
== Features
* Add new deliverable to a project - Fixed bid or Time Based
* New issues appended using JavaScript to the top of the deliverables list
@@ -29,44 +29,46 @@ Budget is a plugin to manage the set of deliverables for each project, automatic
* Ability to add the Deliverable column to the Main Issues list, including sorting and filtering by Deliverable name
* Adding the billable rate to project members on a per project basis
## Getting the plugin
== Getting the plugin
A copy of the plugin can be found in the [downloads](https://projects.littlestreamsoftware.com/projects/list_files/redmine-budget) at Little Stream Software and also on [GitHub](http://github.com/edavis10/redmine-budget-plugin/tree/master). Make sure you install the plugin to @vendor/plugins/budget_plugin@.
A copy of the plugin can be found in the {downloads}[https://projects.littlestreamsoftware.com/projects/redmine-budget/files] at Little Stream Software and also on {GitHub}[http://github.com/edavis10/redmine-budget-plugin/tree/master].
## Install
== Install
0. Before installing, make sure you are running Redmine after r1796.
1. Follow the Redmine plugin installation steps at: http://www.redmine.org/wiki/redmine/Plugins
2. Login to your Redmine install as an Administrator
3. Enable the permissions for your Roles
4. Setup your companies defaults in the Plugins' configuration panel
5. Add the "Budget module" to the enabled modules for the projects you want to manage
6. The link to the plugin should appear on that project's navigation
1. Follow the Redmine plugin installation steps at: http://www.redmine.org/wiki/redmine/Plugins Make sure the plugin is installed to Make sure you install the plugin to +vendor/plugins/budget_plugin+.
2. The Rate plugin is required also, install it from {Little Stream Software}[https://projects.littlestreamsoftware.com/projects/redmine-rate/files]
3. Run the plugin migrations +rake db:migrate_plugins+
4. Restart your Redmine web servers (e.g. mongrel, thin, mod_rails)
5. Login to your Redmine install as an Administrator
6. Enable the permissions for your Roles
7. Setup your companies defaults in the Plugins' configuration panel
8. Add the "Budget module" to the enabled modules for the projects you want to manage
9. The link to the plugin should appear on that project's navigation
## Usage
== Usage
### Adding deliverables
=== Adding deliverables
1. Use the "New Deliverable" link on sidebar of the Budget page to add a new deliverable
2. The deliverable can be a Fixed Bid or an Hourly deliverable. The type can be changed later.
3. Saving the deliverable will add it to the Deliverable list.
### Viewing deliverables
=== Viewing deliverables
1. Clicking the pencil icon will drop down the deliverable details, with summary and calculations about the deliverable.
2. Links on the left can be used to edit or delete a deliverable and well as view the issue list for the deliverable.
### Assignment of an issue to a deliverable
=== Assignment of an issue to a deliverable
Edit an issue and select the deliverable from the dropdown
### Bulk assignment of issues to a deliverable
=== Bulk assignment of issues to a deliverable
1. Select multiple issues on the issue list
2. Right click and select the edit option
3. Select the deliverable from the dropdown and save
### Bulk Assignment of issues by Version
=== Bulk Assignment of issues by Version
Issues can be bulk assigned to a deliverable based on a Version.
@@ -75,16 +77,12 @@ Issues can be bulk assigned to a deliverable based on a Version.
3. Click the Bulk Assign button
4. All issues for that version will now be assigned to that Deliverable
## License
== License
This plugin is licensed under the GNU GPL v2. See LICENSE.txt and GPL.txt for details.
## Project help
== Project help
If you need help you can contact the maintainer at his email address (See CREDITS.txt) or create an issue in the Bug Tracker.
### Bug tracker
If you would like to report a bug or request a new feature the bug tracker is located at [https://projects.littlestreamsoftware.com/projects/show/redmine-budget](https://projects.littlestreamsoftware.com/projects/show/redmine-budget)
If you need help you can contact the maintainer on the bug tracker at https://projects.littlestreamsoftware.com/projects/redmine-budget

103
Rakefile
View File

@@ -1,104 +1,9 @@
#!/usr/bin/env ruby
require "fileutils"
require 'rubygems'
gem 'rspec'
gem 'rspec-rails'
require 'redmine_plugin_support'
Dir[File.expand_path(File.dirname(__FILE__)) + "/lib/tasks/**/*.rake"].sort.each { |ext| load ext }
# Modifided from the RSpec on Rails plugins
PLUGIN_ROOT = File.expand_path(File.dirname(__FILE__))
# Allows loading of an environment config based on the environment
REDMINE_ROOT = ENV["REDMINE_ROOT"] || File.dirname(__FILE__) + "/../../.."
REDMINE_APP = File.expand_path(REDMINE_ROOT + '/app')
REDMINE_LIB = File.expand_path(REDMINE_ROOT + '/lib')
require 'rake'
require 'rake/clean'
require 'rake/rdoctask'
require 'spec/rake/spectask'
PROJECT_NAME = 'budget_plugin'
ZIP_FILE = PROJECT_NAME + ".zip"
CLEAN.include('**/semantic.cache', ZIP_FILE)
# No Database needed
spec_prereq = :noop
task :noop do
RedminePluginSupport::Base.setup do |plugin|
plugin.project_name = 'budget_plugin'
plugin.default_task = [:spec]
end
task :default => :spec
task :stats => "spec:statsetup"
desc "Run all specs in spec directory (excluding plugin specs)"
Spec::Rake::SpecTask.new(:spec => spec_prereq) do |t|
t.spec_opts = ['--options', "\"#{PLUGIN_ROOT}/spec/spec.opts\""]
t.spec_files = FileList['spec/**/*_spec.rb']
end
namespace :spec do
desc "Run all specs in spec directory with RCov (excluding plugin specs)"
Spec::Rake::SpecTask.new(:rcov) do |t|
t.spec_opts = ['--options', "\"#{PLUGIN_ROOT}/spec/spec.opts\""]
t.spec_files = FileList['spec/**/*_spec.rb']
t.rcov = true
t.rcov_opts << ["--rails", "--sort=coverage", "--exclude '/var/lib/gems,spec,#{REDMINE_APP},#{REDMINE_LIB}'"]
end
desc "Print Specdoc for all specs (excluding plugin specs)"
Spec::Rake::SpecTask.new(:doc) do |t|
t.spec_opts = ["--format", "specdoc", "--dry-run"]
t.spec_files = FileList['spec/**/*_spec.rb']
end
desc "Print Specdoc for all specs as HTML (excluding plugin specs)"
Spec::Rake::SpecTask.new(:htmldoc) do |t|
t.spec_opts = ["--format", "html:doc/rspec_report.html", "--loadby", "mtime"]
t.spec_files = FileList['spec/**/*_spec.rb']
end
[:models, :controllers, :views, :helpers, :lib].each do |sub|
desc "Run the specs under spec/#{sub}"
Spec::Rake::SpecTask.new(sub => spec_prereq) do |t|
t.spec_opts = ['--options', "\"#{PLUGIN_ROOT}/spec/spec.opts\""]
t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
end
end
end
desc 'Generate documentation for the plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'doc'
rdoc.title = PROJECT_NAME
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('*.markdown')
rdoc.rdoc_files.include('*.rdoc')
rdoc.rdoc_files.include('*.txt')
rdoc.rdoc_files.include('lib/**/*.rb')
rdoc.rdoc_files.include('app/**/*.rb')
end
desc 'Uploads project documentation'
task :upload_doc => ['spec:rcov', :doc, 'spec:htmldoc'] do |t|
# TODO: Get rdoc working without frames
`scp -r doc/ dev.littlestreamsoftware.com:/home/websites/projects.littlestreamsoftware.com/shared/embedded_docs/redmine-budget/doc`
`scp -r coverage/ dev.littlestreamsoftware.com:/home/websites/projects.littlestreamsoftware.com/shared/embedded_docs/redmine-budget/coverage`
end
desc "Create release archives"
task :release => [:clean, :rdoc, 'release:zip', 'release:tarball']
namespace :release do
desc "Create a zip archive"
task :zip => [:clean] do
sh "git archive --format=zip --prefix=#{PLUGIN_NAME}/ HEAD > #{PLUGIN_NAME}.zip"
end
desc "Create a tarball archive"
task :tarball => [:clean] do
sh "git archive --format=tar --prefix=#{PLUGIN_NAME}/ HEAD | gzip > #{PLUGIN_NAME}.tar.gz"
end
end

View File

@@ -138,7 +138,7 @@ class DeliverablesController < ApplicationController
# Sorting orders
def sort_order
if %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])
return { }
else
return { :order => sort_clause }
@@ -147,7 +147,7 @@ class DeliverablesController < ApplicationController
# Sort +deliverables+ manually using the virtual fields
def sort_if_needed(deliverables)
if %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]
when "score":
sorted = deliverables.sort {|a,b| a.score <=> b.score}

View File

@@ -73,8 +73,8 @@ module DeliverablesHelper
end
def toggle_arrows(deliverable_id)
open_js = "$('deliverable-details-#{deliverable_id}').show(); $$('.toggle_#{deliverable_id}').each(function(e) {e.toggle();})"
close_js = "$('deliverable-details-#{deliverable_id}').hide(); $$('.toggle_#{deliverable_id}').each(function(e) {e.toggle();})"
open_js = "expandRow(#{deliverable_id})"
close_js = "collapseRow(#{deliverable_id})"
return toggle_arrow(deliverable_id, "toggle-arrow-closed.gif", open_js, false) +
toggle_arrow(deliverable_id, "toggle-arrow-open.gif", close_js, true)
@@ -86,7 +86,7 @@ module DeliverablesHelper
content_tag(:span,
link_to_function(image_tag(image, :plugin => "budget_plugin"), js),
:class => "toggle_" + deliverable_id.to_s,
:class => "toggle toggle_" + deliverable_id.to_s,
:style => style
)

View File

@@ -195,6 +195,11 @@ class Deliverable < ActiveRecord::Base
def hourly?
return self.class == HourlyDeliverable
end
def to_s
self.subject
end
private
def use_issue_status_for_done_ratios?

View File

@@ -1,10 +1,10 @@
<h2 class="title">Budget</h2>
<h2 class="title"><%= l(:budget_title) %></h2>
<table>
<% if allowed_management? %>
<tr>
<tr class="total">
<td>
Total Budget:
<%= l(:field_budget) %>
</td>
<td class="calculation-column">
<%= h number_to_currency(budget.budget, :precision => 0) %>
@@ -15,7 +15,7 @@
<% if allowed_management? %>
<tr>
<td>
Labor Budget:
<%= l(:label_labor_budget) %>
</td>
<td class="calculation-column">
<%= h number_to_currency(budget.labor_budget, :precision => 0) %>
@@ -26,7 +26,7 @@
<% if allowed_management? %>
<tr>
<td>
Labor Budget Spent:
<%= l(:label_labor_budget_spent) %>
</td>
<td class="calculation-column">
<%= h number_to_currency(budget.spent, :precision => 0) %>
@@ -37,7 +37,7 @@
<% if allowed_management? %>
<tr>
<td>
Labor Budget Remaining:
<%= l(:label_labor_budget_remaining) %>
</td>
<td class="calculation-column">
<%= h number_to_currency(budget.labor_budget_left, :precision => 0) %>
@@ -47,7 +47,7 @@
<tr>
<td>
Progress:
<%= l(:label_progress) %>
</td>
<td class="calculation-column">
<%= h number_to_percentage(budget.progress, :precision => 0) %>
@@ -57,7 +57,7 @@
<% if allowed_management? %>
<tr>
<td>
Budget Score:
<%= l(:label_budget_score) %>
</td>
<td class="calculation-column">
<%= h budget.score %>
@@ -68,7 +68,7 @@
<% if allowed_management? %>
<tr>
<td>
Overruns:
<%= l(:label_overruns) %>
</td>
<td class="calculation-column">
<%= h number_to_currency(budget.overruns, :precision => 0) %>
@@ -79,7 +79,7 @@
<% if allowed_management? && budget.amount_missing_on_deliverables > 0%>
<tr>
<td>
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 class="calculation-column">
<%= h number_to_currency(budget.amount_missing_on_deliverables, :precision => 0) %>
@@ -90,7 +90,7 @@
<% if allowed_management? && budget.amount_missing_on_issues > 0 %>
<tr>
<td>
Missing on <%= link_to('Issues:',
<%= l(:label_missing_on) %> <%= link_to('Issues:',
:controller => 'timelog',
:action => 'details',
:project_id => @project.id,
@@ -106,7 +106,7 @@
<tr>
<td>
Next Due Date:
<%= l(:label_next_due_date) %>
</td>
<td class="calculation-column">
<%= h distance_of_time_in_words_to_now(budget.next_due_date) if budget.next_due_date %>
@@ -114,7 +114,7 @@
</tr>
<tr>
<td>
Completion:
<%= l(:label_completion) %>
</td>
<td class="calculation-column">
<%= h distance_of_time_in_words_to_now(budget.final_due_date) if budget.final_due_date %>
@@ -124,7 +124,7 @@
<% if allowed_management? %>
<tr>
<td>
Potential Profit:
<%= l(:label_potential_profit) %>
</td>
<td class="calculation-column">
<%= h number_to_currency(budget.profit, :precision => 0) %>

View File

@@ -7,22 +7,23 @@
<%= 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 => 'dont_ratio')) %>
<%= 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("Edit", :action => 'edit', :id => @project.id, :deliverable_id => deliverable.id)) if allowed_management? -%>
<%= content_tag(:p,link_to("Delete", { :action => 'destroy', :id => @project.id, :deliverable_id => deliverable.id}, :confirm => l(:text_are_you_sure))) if allowed_management? %>
<%= content_tag(:p,link_to("Issues", :action => 'issues', :id => @project.id, :deliverable_id => deliverable.id)) -%>
<%= 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("Bulk Assign") %>
<%= submit_tag(l(:label_bulk_assign)) %>
<% end %>
</div>
<% end %>
@@ -30,18 +31,18 @@
<td colspan="2">
<table class="progress-table">
<%= row_with_double_data "Progress: ", number_to_percentage(deliverable.progress, :precision => 0), '' %>
<%= row_with_double_data l(:label_progress), number_to_percentage(deliverable.progress, :precision => 0), '' %>
<% if allowed_management? %>
<% if deliverable.hourly? %>
<%= row_with_double_data "Hours Estimated: ", number_with_precision(deliverable.total_hours, 0), '' %>
<%= row_with_double_data l(:label_hours_estimated), number_with_precision(deliverable.total_hours, 0), '' %>
<% end %>
<% if deliverable.fixed? %>
<%= row_with_double_data "Fixed Amount: ", '', number_to_currency(deliverable.fixed_cost, :precision => 0) %>
<%= 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 "Hours Used: ", number_with_precision(deliverable.hours_used,0), number_to_currency(deliverable.spent_by_members, :precision => 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>
@@ -55,18 +56,18 @@
<td colspan="3">
<table class="financials">
<%= row_with_data("Total Budget: ", number_to_currency(deliverable.budget, :precision => 0)) -%>
<%= row_with_data(l(:field_budget), number_to_currency(deliverable.budget, :precision => 0)) -%>
<% if allowed_management? %>
<% base_price_label = deliverable.hourly? ? "Labor: " : "Fixed Amount: " %>
<% 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("Overhead: ", number_or_percent(deliverable.overhead, deliverable.overhead_percent)) -%>
<%= row_with_data("Materials: ", number_or_percent(deliverable.materials, deliverable.materials_percent)) -%>
<%= row_with_data("Potential Profit: ", number_or_percent(deliverable.profit, deliverable.profit_percent)) -%>
<%= 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="2">
<td colspan="3">
<table class="issue-totals">
<%= row_with_data(l(:label_issue_plural), deliverable.issues.size, 'issue-totals') -%>
@@ -76,3 +77,8 @@
</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>

View File

@@ -19,7 +19,7 @@
<tr>
<td>
<label for="deliverable_type">Fixed Cost</label>
<label for="deliverable_type"><%= l(:label_fixed_cost) %></label>
</td>
<td>
<%= check_box(:deliverable, :type, {}, FixedDeliverable.name, HourlyDeliverable.name) %>
@@ -31,7 +31,7 @@
<tr class="budget-hourly">
<td>
<label for="deliverable_cost_per_hour">Cost per hour</label>
<label for="deliverable_cost_per_hour"><%= l(:field_cost_per_hour)%></label>
</td>
<td>
<%= text_field :deliverable, :cost_per_hour, :size => 7 %>
@@ -42,7 +42,7 @@
<tr class="budget-hourly">
<td>
<label for="deliverable_total_hours">Total Hours</label>
<label for="deliverable_total_hours"><%= l(:field_total_hours) %></label>
</td>
<td>
<%= text_field :deliverable, :total_hours, :size => 7 %>
@@ -54,7 +54,7 @@
<tr class="budget-fixed" style="display:none;">
<td>
<label for="deliverable_fixed_cost">Fixed Bid</label>
<label for="deliverable_fixed_cost"><%= l(:field_fixed_cost) %></label>
</td>
<td>
<%= text_field :deliverable, :fixed_cost, :size => 7 %>
@@ -69,7 +69,7 @@
<%= field_with_budget_observer_and_totals(f, @deliverable, :profit, :profit_percent, @settings[:budget_profit]) %>
<tr class="total-budget">
<td><label>Total Budget:</label></td>
<td><label><%= l(:field_budget) %></label></td>
<td></td>
<td class="calculation-column">
<%= content_tag(:span, 0, :id => 'total-budget-calculation', :class => "budget-calculation") %>
@@ -109,120 +109,3 @@
<div id="preview" class="wiki"></div>
</fieldset>
<% content_for :header_tags do %>
<script type="text/javascript">
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"] || "$";
var separator = precision > 0 ? options["separator"] || "." : "";
var delimiter = options["delimiter"] || ",";
var parts = parseFloat(number).toFixed(precision).split('.');
return unit + Budget.number_with_delimiter(parts[0], delimiter) + separator + parts[1].toString();
} 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();
</script>
<% end %>

View File

@@ -2,12 +2,13 @@
<thead><tr>
<%= content_tag('th', " ") %>
<%= sort_header_tag("#{Deliverable.table_name}.id", :caption => '#', :default_order => 'desc') %>
<%= sort_header_tag("score", :caption => 'Score', :default_order => 'desc') if allowed_management? %>
<%= sort_header_tag("#{Deliverable.table_name}.subject", :caption => 'Subject') %>
<%= sort_header_tag("labor_budget", :caption => 'Budget') if allowed_management? %>
<%= sort_header_tag("spent", :caption => 'Spent') if allowed_management? %>
<%= sort_header_tag("#{Deliverable.table_name}.due", :caption => 'Due') %>
<%= sort_header_tag("progress", :caption => 'Progress') %>
<%= sort_header_tag("score", :caption => l(:caption_score), :default_order => 'desc') if allowed_management? %>
<%= sort_header_tag("#{Deliverable.table_name}.subject", :caption => l(:caption_subject)) %>
<%= sort_header_tag("total_budget", :caption => l(:caption_budget)) if allowed_management? %>
<%= sort_header_tag("labor_budget", :caption => l(:caption_labor_budget)) 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("progress", :caption => l(:caption_progress)) %>
</tr></thead>
<tbody>
<% deliverables.each do |deliverable| -%>

View File

@@ -1,3 +1,4 @@
<h3>Budget</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 "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 />

View File

@@ -1,4 +1,4 @@
<h2>Update Deliverable</h2>
<h2><%= l(:label_update_deliverable) %></h2>
<% form_for :deliverable, @deliverable, :url => {:controller => 'deliverables', :action => 'update', :id => @project, :deliverable_id => @deliverable.id },
:method => :post, :builder => TabularFormBuilder, :lang => current_language,
:html => {:multipart => true, :id => 'deliverable-form', :class => 'tabular'} do |f| %>
@@ -8,4 +8,5 @@
<% content_for :header_tags do %>
<%= stylesheet_link_tag "budget.css", :plugin => "budget_plugin", :media => "screen" %>
<%= javascript_include_tag('budget', :plugin => 'budget_plugin') %>
<% end %>

View File

@@ -4,7 +4,7 @@
<% if allowed_management? %>
<div id="new-deliverable" style="display:none;">
<h2>New Deliverable</h2>
<h2><%= l(:label_new_deliverable) %></h2>
<% remote_form_for :deliverable, @deliverable, :url => {:controller => 'deliverables', :action => 'create', :id => @project },
:method => :post, :builder => TabularFormBuilder, :lang => current_language,
:html => {:multipart => true, :id => 'deliverable-form', :class => 'tabular'} do |f| %>
@@ -28,4 +28,5 @@
<% content_for :header_tags do %>
<%= stylesheet_link_tag "budget.css", :plugin => "budget_plugin", :media => "screen" %>
<%= javascript_include_tag('budget', :plugin => 'budget_plugin') %>
<% end %>

View File

@@ -1,16 +1,16 @@
<p>
Enter a dollar amount or percentage into each field to set your default amount. Use <strong>%</strong> in the field for percentages.
<%= l(:message_budget_settings) %>
</p>
<p>
<label>Non billable overhead</label><%= text_field_tag 'settings[budget_non_billable_overhead]', @settings['budget_non_billable_overhead'], :size => 15 %>
<label><%= l(:label_non_billable_overhead) %></label><%= text_field_tag 'settings[budget_non_billable_overhead]', @settings['budget_non_billable_overhead'], :size => 15 %>
</p>
<p>
<label>Materials</label><%= text_field_tag 'settings[budget_materials]', @settings['budget_materials'], :size => 15 %>
<label><%= l(:label_materials) %></label><%= text_field_tag 'settings[budget_materials]', @settings['budget_materials'], :size => 15 %>
</p>
<p>
<label>Profit</label><%= text_field_tag 'settings[budget_profit]', @settings['budget_profit'], :size => 15 %>
<label><%= l(:label_profit) %></label><%= text_field_tag 'settings[budget_profit]', @settings['budget_profit'], :size => 15 %>
</p>

View File

@@ -0,0 +1,135 @@
/* 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"] || "$";
var separator = precision > 0 ? options["separator"] || "." : "";
var delimiter = options["delimiter"] || ",";
var parts = parseFloat(number).toFixed(precision).split('.');
return unit + Budget.number_with_delimiter(parts[0], delimiter) + separator + parts[1].toString();
} 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();
});
}

View File

@@ -37,7 +37,7 @@ tr.deliverable-details table tr td.data { text-align:right; }
td.calculation-column { text-align:right; width: auto; }
.budget-calculation { color:#999999; font-weight:bold; display:block; text-align:right; width: 100%;}
p.total-budget { font-weight: bold; }
p.total-budget, .total { font-weight: bold; }
p.total-budget #total-budget-calculation { }

47
config/locales/ca.yml Normal file
View File

@@ -0,0 +1,47 @@
ca:
budget_title: Pressupost
field_cost_per_hour: Cost per hora
field_total_hours: Total hores
field_overhead: Imprevists
label_overhead: "Imprevists: "
field_materials: Cost dels materials
label_materials: "Materials: "
field_profit: Benefici
field_budget: Pressupost total
label_budget: "Pressupost total: "
field_fixed_cost: Preu tancat
field_project_manager_signoff: Vistiplau cap projecte
field_client_signoff: Vistiplau client
field_deliverable: Entregable
field_deliverable_subject: Entregable
field_due: Data finalització
label_member_rate: Tarifa (EUR)
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.
label_non_billable_overhead: Imprevists no facturables
label_materials: Materials
label_profit: Benefici
label_new_deliverable: Nou entregable
label_fixed_cost: Cost fix
caption_due: Entrega
caption_progress: Progrés
caption_subject: Assumpte
caption_score: Puntuació
caption_budget: Pressupost
caption_spent: Gastat
label_update_deliverable: Actualitzar entregable
label_labor_budget: "Pressupost mà d'obra: "
label_labor_budget_spent: "Pressupost gastat mà d'obra: "
label_labor_budget_remaining: "Pressupost romanent mà d'obra: "
label_progress: "Progrés: "
label_budget_score: "Puntuació pressupost: "
label_overruns: "Excessos: "
label_missing_on: Falta en
label_next_due_date: "Següent data d'entrega: "
label_completion: "Finalització: "
label_potential_profit: "Benefici potencial: "
label_bulk_assign: Assignar tots
label_labor: "Mà d'obra: "
label_fixed_amount: "Quantitat fixa: "
label_hours_estimated: "Hores estimades: "
label_hours_used: "Hores gastades: "

49
config/locales/en.yml Normal file
View File

@@ -0,0 +1,49 @@
en:
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 ($)
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.
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"

46
config/locales/es.yml Normal file
View File

@@ -0,0 +1,46 @@
es:
budget_title: Presupuestos
field_cost_per_hour: Coste por hora
field_total_hours: Total horas
field_overhead: Imprevistos
label_overhead: "Imprevistos: "
field_materials: Coste de los materiales
label_materials: "Materiales: "
field_profit: Beneficio
field_budget: Presupuesto total
label_budget: "Presupuesto total: "
field_fixed_cost: Precio cerrado
field_project_manager_signoff: Visto bueno jefe proyecto
field_client_signoff: Visto bueno cliente
field_deliverable: Entregable
field_deliverable_subject: Entregable
field_due: Fecha entrega
label_member_rate: Tarifa (EUR)
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.
label_non_billable_overhead: Imprevistos no facturables
label_profit: Beneficio
label_new_deliverable: Nuevo entregable
label_fixed_cost: Coste fijo
caption_due: Entrega
caption_progress: Progreso
caption_subject: Asunto
caption_score: Importancia
caption_budget: Presupuesto
caption_spent: Gastado
label_update_deliverable: Actualizar entregable
label_labor_budget: "Presupuesto mano de obra: "
label_labor_budget_spent: "Presupuesto gastado mano de obra: "
label_labor_budget_remaining: "Presupuesto remanente mano de obra: "
label_progress: "Progreso: "
label_budget_score: "Puntuación presupuesto: "
label_overruns: "Excesos: "
label_missing_on: Falta en
label_next_due_date: "Siguiente entrega: "
label_completion: "Finalización: "
label_potential_profit: "Beneficio potencial: "
label_bulk_assign: Asignar todos
label_labor: "Mano de obra: "
label_fixed_amount: "Cantidad fija: "
label_hours_estimated: "Horas estimadas: "
label_hours_used: "Horas usadas: "

15
config/locales/hu.yml Normal file
View File

@@ -0,0 +1,15 @@
hu:
field_cost_per_hour: Óradíj
field_total_hours: Összes órák
field_overhead: Felfüggesztett
field_materials: Anyag költség
field_profit: Profit
field_budget: Teljes költség
field_fixed_cost: Rögzített árajánlat
field_project_manager_signoff: Projekt felelős által aláíratlan
field_client_signoff: Ügyfél által aláíratlan
field_deliverable: Teljesítés
field_deliverable_subject: Teljesítés
field_due: Esedékesség dátuma
label_member_rate: Részesedés ($)
message_updated_issues: Frissítve % feladat

15
config/locales/lt.yml Normal file
View File

@@ -0,0 +1,15 @@
lt:
field_cost_per_hour: Valandinis užmokestis
field_total_hours: Iš viso valandų
field_overhead: Viršyjimas
field_materials: Material costs
field_profit: Pelnas
field_budget: Visas biudžetas
field_fixed_cost: Patovus pasiūlymas
field_project_manager_signoff: Pasirašo projekto vadovas
field_client_signoff: Pasirašo klientas
field_deliverable: Pateiktis
field_deliverable_subject: Pateiktis
field_due: Data iki
label_member_rate: Užmokestis (LTL)
message_updated_issues: Atnaujinta(i) %d darbų(ai)

30
init.rb
View File

@@ -1,19 +1,35 @@
require 'redmine'
# Patches to the Redmine core. Will not work in development mode
require_dependency 'issue_patch'
require_dependency 'query_patch'
# Budget requires the Rate plugin
begin
require 'rate' unless Object.const_defined?('Rate')
rescue LoadError
# 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")
end
# Patches to the Redmine core.
require 'dispatcher'
require 'issue_patch'
require 'query_patch'
Dispatcher.to_prepare do
Issue.send(:include, IssuePatch)
Query.send(:include, QueryPatch)
end
# Hooks
require_dependency 'budget_issue_hook'
RAILS_DEFAULT_LOGGER.info 'Starting Budget plugin for RedMine'
require_dependency 'budget_project_hook'
Redmine::Plugin.register :budget_plugin do
name 'Budget'
author 'Eric Davis <edavis@littlestreamsoftware.com>'
author 'Eric Davis'
description 'Budget is a plugin to manage the set of deliverables for each project, automatically calculating key performance indicators.'
url 'https://projects.littlestreamsoftware.com/projects/redmine-budget'
author_url 'http://www.littlestreamsoftware.com'
version '0.2.0'
requires_redmine :version_or_higher => '0.8.0'
settings :default => {
'budget_nonbillable_overhead' => '',
@@ -27,5 +43,5 @@ Redmine::Plugin.register :budget_plugin do
permission :manage_budget, { :deliverables => [:new, :edit, :create, :update, :destroy, :preview, :bulk_assign_issues]}
end
menu :project_menu, :budget, :controller => "deliverables", :action => 'index'
menu :project_menu, :budget, {:controller => "deliverables", :action => 'index'}, :caption => :budget_title
end

46
lang/ca.yml Normal file
View File

@@ -0,0 +1,46 @@
budget_title: Pressupost
field_cost_per_hour: Cost per hora
field_total_hours: Total hores
field_overhead: Imprevists
label_overhead: "Imprevists: "
field_materials: Cost dels materials
label_materials: "Materials: "
field_profit: Benefici
field_budget: Pressupost total
label_budget: "Pressupost total: "
field_fixed_cost: Preu tancat
field_project_manager_signoff: Vistiplau cap projecte
field_client_signoff: Vistiplau client
field_deliverable: Entregable
field_deliverable_subject: Entregable
field_due: Data finalització
label_member_rate: Tarifa (EUR)
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.
label_non_billable_overhead: Imprevists no facturables
label_materials: Materials
label_profit: Benefici
label_new_deliverable: Nou entregable
label_fixed_cost: Cost fix
caption_due: Entrega
caption_progress: Progrés
caption_subject: Assumpte
caption_score: Puntuació
caption_budget: Pressupost
caption_spent: Gastat
label_update_deliverable: Actualitzar entregable
label_labor_budget: "Pressupost mà d'obra: "
label_labor_budget_spent: "Pressupost gastat mà d'obra: "
label_labor_budget_remaining: "Pressupost romanent mà d'obra: "
label_progress: "Progrés: "
label_budget_score: "Puntuació pressupost: "
label_overruns: "Excessos: "
label_missing_on: Falta en
label_next_due_date: "Següent data d'entrega: "
label_completion: "Finalització: "
label_potential_profit: "Benefici potencial: "
label_bulk_assign: Assignar tots
label_labor: "Mà d'obra: "
label_fixed_amount: "Quantitat fixa: "
label_hours_estimated: "Hores estimades: "
label_hours_used: "Hores gastades: "

View File

@@ -1,9 +1,13 @@
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
@@ -12,3 +16,33 @@ field_deliverable_subject: Deliverable
field_due: Due Date
label_member_rate: Rate ($)
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.
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"

45
lang/es.yml Normal file
View File

@@ -0,0 +1,45 @@
budget_title: Presupuestos
field_cost_per_hour: Coste por hora
field_total_hours: Total horas
field_overhead: Imprevistos
label_overhead: "Imprevistos: "
field_materials: Coste de los materiales
label_materials: "Materiales: "
field_profit: Beneficio
field_budget: Presupuesto total
label_budget: "Presupuesto total: "
field_fixed_cost: Precio cerrado
field_project_manager_signoff: Visto bueno jefe proyecto
field_client_signoff: Visto bueno cliente
field_deliverable: Entregable
field_deliverable_subject: Entregable
field_due: Fecha entrega
label_member_rate: Tarifa (EUR)
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.
label_non_billable_overhead: Imprevistos no facturables
label_profit: Beneficio
label_new_deliverable: Nuevo entregable
label_fixed_cost: Coste fijo
caption_due: Entrega
caption_progress: Progreso
caption_subject: Asunto
caption_score: Importancia
caption_budget: Presupuesto
caption_spent: Gastado
label_update_deliverable: Actualizar entregable
label_labor_budget: "Presupuesto mano de obra: "
label_labor_budget_spent: "Presupuesto gastado mano de obra: "
label_labor_budget_remaining: "Presupuesto remanente mano de obra: "
label_progress: "Progreso: "
label_budget_score: "Puntuación presupuesto: "
label_overruns: "Excesos: "
label_missing_on: Falta en
label_next_due_date: "Siguiente entrega: "
label_completion: "Finalización: "
label_potential_profit: "Beneficio potencial: "
label_bulk_assign: Asignar todos
label_labor: "Mano de obra: "
label_fixed_amount: "Cantidad fija: "
label_hours_estimated: "Horas estimadas: "
label_hours_used: "Horas usadas: "

View File

14
lang/hu.yml Normal file
View File

@@ -0,0 +1,14 @@
field_cost_per_hour: Óradíj
field_total_hours: Összes órák
field_overhead: Felfüggesztett
field_materials: Anyag költség
field_profit: Profit
field_budget: Teljes költség
field_fixed_cost: Rögzített árajánlat
field_project_manager_signoff: Projekt felelős által aláíratlan
field_client_signoff: Ügyfél által aláíratlan
field_deliverable: Teljesítés
field_deliverable_subject: Teljesítés
field_due: Esedékesség dátuma
label_member_rate: Részesedés ($)
message_updated_issues: Frissítve % feladat

14
lang/lt.yml Normal file
View File

@@ -0,0 +1,14 @@
field_cost_per_hour: Valandinis užmokestis
field_total_hours: Iš viso valandų
field_overhead: Viršyjimas
field_materials: Material costs
field_profit: Pelnas
field_budget: Visas biudžetas
field_fixed_cost: Patovus pasiūlymas
field_project_manager_signoff: Pasirašo projekto vadovas
field_client_signoff: Pasirašo klientas
field_deliverable: Pateiktis
field_deliverable_subject: Pateiktis
field_due: Data iki
label_member_rate: Užmokestis (LTL)
message_updated_issues: Atnaujinta(i) %d darbų(ai)

View File

@@ -38,11 +38,11 @@ class BudgetIssueHook < Redmine::Hook::ViewListener
def view_issues_bulk_edit_details_bottom(context = { })
if context[:project].module_enabled?('budget_module')
select = select_tag('deliverable_id',
content_tag('option', GLoc.l(:label_no_change_option), :value => '') +
content_tag('option', GLoc.l(:label_none), :value => 'none') +
content_tag('option', l(:label_no_change_option), :value => '') +
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))
return content_tag(:p, "<label>#{GLoc.l(:field_deliverable)}: " + select + "</label>")
return content_tag(:p, "<label>#{l(:field_deliverable)}: " + select + "</label>")
else
return ''
end

View File

@@ -0,0 +1,26 @@
class BudgetProjectHook < Redmine::Hook::ViewListener
def model_project_copy_before_save(context = {})
source = context[:source_project]
destination = context[:destination_project]
if source.module_enabled?(:budget_module)
Deliverable.find(:all, :conditions => {:project_id => source.id}).each do |source_deliverable|
destination_deliverable = source_deliverable.class.new # STI classes
# Copy attribute except for the ones that have wrapped
# accessors, use read/write attribute for them
destination_deliverable.attributes = source_deliverable.attributes.except("project_id", "profit", "materials", "overhead")
destination_deliverable.write_attribute(:profit, source_deliverable.read_attribute(:profit))
destination_deliverable.write_attribute(:profit_percent, source_deliverable.read_attribute(:profit_percent))
destination_deliverable.write_attribute(:materials, source_deliverable.read_attribute(:materials))
destination_deliverable.write_attribute(:materials_percent, source_deliverable.read_attribute(:materials_percent))
destination_deliverable.write_attribute(:overhead, source_deliverable.read_attribute(:overhead))
destination_deliverable.write_attribute(:overhead_percent, source_deliverable.read_attribute(:overhead_percent))
destination_deliverable.project = destination
destination_deliverable.save # Need to save here because there is no relation on project to deliverable
end
end
end
end

View File

@@ -32,7 +32,4 @@ module IssuePatch
end
end
# Add module to Issue
Issue.send(:include, IssuePatch)

View File

@@ -50,7 +50,4 @@ module QueryPatch
end
end
# Add module to Query
Query.send(:include, QueryPatch)