OpenProject is the leading open source project management software.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openproject/app/models/cost_object.rb

156 lines
4.1 KiB

# A CostObject is an item that is created as part of the project. These items
# contain a collection of issues.
class CostObject < ActiveRecord::Base
unloadable
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
belongs_to :project
has_many :issues, :dependent => :nullify
has_many :cost_entries, :through => :issues
has_many :time_entries, :through => :issues
attr_accessible :subject, :description, :fixed_date, :project_manager_signoff, :client_signoff
acts_as_attachable :after_remove => :attachment_removed
unless respond_to? :acts_as_journalized
acts_as_event :title => Proc.new {|o| "#{l(:label_cost_object)} ##{o.id}: #{o.subject}"},
:url => Proc.new {|o| {:controller => 'cost_objects', :action => 'show', :id => o.id}}
acts_as_activity_provider :find_options => {:include => [:project, :author]},
:timestamp => "#{table_name}.updated_on",
:author_key => :author_id
end
validates_presence_of :subject, :project, :author, :kind
validates_length_of :subject, :maximum => 255
validates_length_of :subject, :minimum => 1
User.before_destroy do |user|
CostObject.replace_author_with_deleted_user user
end
def initialize(attributes = nil)
super
self.author = User.current if self.new_record?
end
def attributes=(attrs)
# Remove any attributes which can not be assigned.
# This is to protect from exceptions during change of cost object type
attrs.delete_if{|k, v| !self.respond_to?("#{k}=")} if attrs.is_a?(Hash)
super(attrs)
end
def copy_from(arg)
cost_object = arg.is_a?(CostObject) ? arg : CostObject.find(arg)
self.attributes = cost_object.attributes.dup
end
# Wrap type column to make it usable in views (especially in a select tag)
def kind
self[:type]
end
def kind=(type)
self[:type] = type
end
# Assign all the issues with +version_id+ to this Cost Object
def assign_issues_by_version(version_id)
version = Version.find_by_id(version_id)
return 0 if version.nil? || version.fixed_issues.blank?
version.fixed_issues.each do |issue|
issue.update_attribute(:cost_object_id, self.id)
end
return version.fixed_issues.size
end
# Change the Cost Object type to another type. Valid types are
#
# * FixedCostObject
# * VariableCostObject
def change_type(to)
if [FixedCostObject.name, VariableCostObject.name].include?(to)
self.type = to
self.save!
return CostObject.find(self.id)
else
return self
end
end
# Amount spent. Virtual accessor that is overriden by subclasses.
def spent
13 years ago
0
end
def spent_for_display
# FIXME: Remove this function
spent
end
# Budget of labor. Virtual accessor that is overriden by subclasses.
def labor_budget
0.0
end
def labor_budget_for_display
# FIXME: Remove this function
labor_budget
end
# Budget of material, i.e. all costs besides labor costs. Virtual accessor that is overriden by subclasses.
def material_budget
0.0
end
def material_budget_for_display
# FIXME: Remove this function
material_budget
end
def budget
material_budget + labor_budget
end
def budget_for_display
# FIXME: Remove this function
budget
end
def status
# this just returns the symbol for I18N
if project_manager_signoff
client_signoff ? :label_status_finished : :label_status_awaiting_client
else
client_signoff ? :label_status_awaiting_client : :label_status_in_progress
end
end
# Label of the current type for display in GUI. Virtual accessor that is overriden by subclasses.
def type_label
return l(:label_cost_object)
end
# Amount of the budget spent. Expressed as as a percentage whole number
def budget_ratio
return 0.0 if self.budget.nil? || self.budget == 0.0
return ((self.spent / self.budget) * 100).round
end
def css_classes
return "issue cost_object"
end
def self.replace_author_with_deleted_user(user)
substitute = DeletedUser.first
self.update_all ['author_id = ?', substitute.id], ['author_id = ?', user.id]
end
end