Skip to content

Instantly share code, notes, and snippets.

@susieyy
Created May 14, 2009 10:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save susieyy/111608 to your computer and use it in GitHub Desktop.
Save susieyy/111608 to your computer and use it in GitHub Desktop.
# redmine-budget-plugin / init.rb
require 'redmine'
# 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'
require_dependency 'budget_project_hook'
Redmine::Plugin.register :budget_plugin do
name 'Budget'
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' => '',
'budget_materials' => '',
'budget_profit' => ''
}, :partial => 'settings/budget_settings'
project_module :budget_module do
permission :view_budget, { :deliverables => [:index, :issues]}
permission :manage_budget, { :deliverables => [:new, :edit, :create, :update, :destroy, :preview, :bulk_assign_issues]}
end
menu :project_menu, :budget, {:controller => "deliverables", :action => 'index'}, :caption => :budget_title
end
# redmine-budget-plugin / lib / budget_project_hook.rb
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
# redmine-budget-plugin / lib / budget_issue_hook.rb
# Hooks to attach to the Redmine Issues.
class BudgetIssueHook < Redmine::Hook::ViewListener
# Renders the Deliverable subject
#
# Context:
# * :issue => Issue being rendered
#
def view_issues_show_details_bottom(context = { })
if context[:project].module_enabled?('budget_module')
data = "<td><b>Deliverable :</b></td><td>#{html_escape context[:issue].deliverable.subject unless context[:issue].deliverable.nil?}</td>"
return "<tr>#{data}<td></td></tr>"
else
return ''
end
end
# Renders a select tag with all the Deliverables
#
# Context:
# * :form => Edit form
# * :project => Current project
#
def view_issues_form_details_bottom(context = { })
if context[:project].module_enabled?('budget_module')
select = context[:form].select :deliverable_id, Deliverable.find_all_by_project_id(context[:project], :order => 'subject ASC').collect { |d| [d.subject, d.id] }, :include_blank => true
return "<p>#{select}</p>"
else
return ''
end
end
# Renders a select tag with all the Deliverables for the bulk edit page
#
# Context:
# * :project => Current project
#
def view_issues_bulk_edit_details_bottom(context = { })
if context[:project].module_enabled?('budget_module')
select = select_tag('deliverable_id',
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>#{l(:field_deliverable)}: " + select + "</label>")
else
return ''
end
end
# Saves the Deliverable assignment to the issue
#
# Context:
# * :issue => Issue being saved
# * :params => HTML parameters
#
def controller_issues_bulk_edit_before_save(context = { })
case true
when context[:params][:deliverable_id].blank?
# Do nothing
when context[:params][:deliverable_id] == 'none'
# Unassign deliverable
context[:issue].deliverable = nil
else
context[:issue].deliverable = Deliverable.find(context[:params][:deliverable_id])
end
return ''
end
# Deliverable changes for the journal use the Deliverable subject
# instead of the id
#
# Context:
# * :detail => Detail about the journal change
#
def helper_issues_show_detail_after_setting(context = { })
# TODO Later: Overwritting the caller is bad juju
if context[:detail].prop_key == 'deliverable_id'
d = Deliverable.find_by_id(context[:detail].value)
context[:detail].value = d.subject unless d.nil? || d.subject.nil?
d = Deliverable.find_by_id(context[:detail].old_value)
context[:detail].old_value = d.subject unless d.nil? || d.subject.nil?
end
''
end
end
# redmine-budget-plugin / lib / issue_patch.rb
require_dependency 'issue'
# Patches Redmine's Issues dynamically. Adds a relationship
# Issue +belongs_to+ to Deliverable
module IssuePatch
def self.included(base) # :nodoc:
base.extend(ClassMethods)
base.send(:include, InstanceMethods)
# Same as typing in the class
base.class_eval do
unloadable # Send unloadable so it will not be unloaded in development
belongs_to :deliverable
end
end
module ClassMethods
end
module InstanceMethods
# Wraps the association to get the Deliverable subject. Needed for the
# Query and filtering
def deliverable_subject
unless self.deliverable.nil?
return self.deliverable.subject
end
end
end
end
# redmine-budget-plugin / lib / query_patch.rb
require_dependency 'query'
# Patches Redmine's Queries dynamically, adding the Deliverable
# to the available query columns
module QueryPatch
def self.included(base) # :nodoc:
base.extend(ClassMethods)
base.send(:include, InstanceMethods)
# Same as typing in the class
base.class_eval do
unloadable # Send unloadable so it will not be unloaded in development
base.add_available_column(QueryColumn.new(:deliverable_subject, :sortable => "#{Deliverable.table_name}.subject"))
alias_method :redmine_available_filters, :available_filters
alias_method :available_filters, :budget_available_filters
end
end
module ClassMethods
# Setter for +available_columns+ that isn't provided by the core.
def available_columns=(v)
self.available_columns = (v)
end
# Method to add a column to the +available_columns+ that isn't provided by the core.
def add_available_column(column)
self.available_columns << (column)
end
end
module InstanceMethods
# Wrapper around the +available_filters+ to add a new Deliverable filter
def budget_available_filters
@available_filters = redmine_available_filters
if project
budget_filters = { "deliverable_id" => { :type => :list_optional, :order => 14,
:values => Deliverable.find(:all, :conditions => ["project_id IN (?)", project], :order => 'subject ASC').collect { |d| [d.subject, d.id.to_s]}
}}
else
budget_filters = { }
end
return @available_filters.merge(budget_filters)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment