2023-11-16 20:23:09 +00:00
|
|
|
class Job < ActiveRecord::Base
|
2024-01-21 16:07:41 +00:00
|
|
|
include ActionView::Helpers::UrlHelper
|
|
|
|
include Redmine::SafeAttributes
|
|
|
|
|
2023-11-16 20:23:09 +00:00
|
|
|
validates :starts_on,
|
|
|
|
:ends_on,
|
|
|
|
:name,
|
|
|
|
presence: true
|
|
|
|
|
2023-11-21 14:09:51 +00:00
|
|
|
belongs_to :project
|
2023-11-30 19:08:55 +00:00
|
|
|
belongs_to :category, class_name: "JobCategory"
|
|
|
|
|
2023-11-27 08:55:57 +00:00
|
|
|
has_many :time_entries, dependent: :restrict_with_error
|
2023-11-27 07:53:55 +00:00
|
|
|
has_many :time_budgets, dependent: :destroy
|
2024-02-27 17:30:32 +00:00
|
|
|
has_many :journals, as: :journalized, dependent: :destroy, inverse_of: :journalized
|
|
|
|
delegate :notes, :notes=, to: :current_journal, allow_nil: true
|
2023-11-24 15:05:38 +00:00
|
|
|
accepts_nested_attributes_for :time_budgets, allow_destroy: true
|
2023-11-21 14:09:51 +00:00
|
|
|
|
2024-02-27 17:30:32 +00:00
|
|
|
acts_as_customizable
|
|
|
|
acts_as_watchable
|
|
|
|
acts_as_mentionable attributes: [ "description" ]
|
|
|
|
|
2024-01-17 18:49:05 +00:00
|
|
|
scope :project_or_parent, ->(project) { where(project_id: [project&.id, project&.parent&.id]) }
|
2023-11-27 08:55:57 +00:00
|
|
|
scope :active, -> { where(starts_on: ..Date.today, ends_on: Date.today..) }
|
2023-11-24 15:05:38 +00:00
|
|
|
|
2024-01-21 16:07:41 +00:00
|
|
|
safe_attributes 'name', 'description'
|
|
|
|
|
2024-02-27 17:30:32 +00:00
|
|
|
after_save :create_journal
|
|
|
|
|
2023-11-24 15:05:38 +00:00
|
|
|
def with_all_time_budgets
|
2023-11-30 17:37:54 +00:00
|
|
|
time_budgets.build(job_id: id, category_id: nil) unless time_budgets.where(category_id: nil).exists?
|
|
|
|
TimeBudgetCategory.where.not(id: time_budgets.pluck(:category_id)).each do |category|
|
|
|
|
time_budgets.build(job_id: id, category_id: category.id)
|
2023-11-24 15:05:38 +00:00
|
|
|
end
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def missing_time_budgets
|
|
|
|
budgets = []
|
2023-11-30 17:37:54 +00:00
|
|
|
new_activities.collect { |category| budgets << TimeBudget.new(job_id: id, category_id: category.id) }
|
2023-11-24 15:05:38 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def total_time_budget
|
|
|
|
return 0 if time_budgets.empty?
|
|
|
|
|
|
|
|
time_budgets.sum(&:hours)
|
|
|
|
end
|
|
|
|
|
2023-11-21 14:37:11 +00:00
|
|
|
def total_time_logged
|
|
|
|
TimeEntry.where(job_id: id)
|
|
|
|
.sum(:hours)
|
|
|
|
end
|
|
|
|
|
2023-11-27 08:55:57 +00:00
|
|
|
def done_ratio
|
2023-11-30 17:37:54 +00:00
|
|
|
return 0 if total_time_budget.zero?
|
2023-11-27 08:55:57 +00:00
|
|
|
|
2023-11-30 17:37:54 +00:00
|
|
|
(total_time_logged / total_time_budget * 100).to_i
|
2023-11-27 08:55:57 +00:00
|
|
|
end
|
|
|
|
|
2023-11-22 19:44:13 +00:00
|
|
|
def to_s
|
|
|
|
ActionView::Base.send(:include, Rails.application.routes.url_helpers)
|
2023-11-26 20:52:05 +00:00
|
|
|
ActionController::Base.helpers.link_to name, ActionController::Base.helpers.project_job_path(project, self)
|
2023-11-22 19:44:13 +00:00
|
|
|
end
|
2023-12-05 14:25:24 +00:00
|
|
|
|
2024-02-27 17:30:32 +00:00
|
|
|
def init_journal(user, notes = "")
|
|
|
|
@current_journal = Journal.new(journalized: self, user: user, notes: notes)
|
|
|
|
end
|
|
|
|
|
|
|
|
def current_journal
|
|
|
|
@current_journal
|
|
|
|
end
|
|
|
|
|
|
|
|
def create_journal
|
|
|
|
current_journal.save if current_journal
|
|
|
|
end
|
|
|
|
|
|
|
|
def journalized_attribute_names
|
|
|
|
Job.column_names - %w(id created_at updated_at)
|
|
|
|
end
|
|
|
|
|
|
|
|
def notified_users
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
|
|
|
|
def notes_addable?(user = User.current)
|
|
|
|
#user_tracker_permission?(user, :add_job_notes)
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
# tracker and subject are used to make Job quack like an issue so the diff view
|
|
|
|
# built into Redmine will work
|
|
|
|
def tracker
|
|
|
|
OpenStruct.new(
|
|
|
|
name: name,
|
|
|
|
to_s: "Job",
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
def subject
|
|
|
|
name
|
|
|
|
end
|
|
|
|
|
2023-12-07 13:44:10 +00:00
|
|
|
def self.fields_for_order_statement
|
|
|
|
"jobs.name"
|
|
|
|
end
|
|
|
|
|
2023-12-05 14:25:24 +00:00
|
|
|
def self.default_for(time_entry)
|
|
|
|
projects = [time_entry.project, time_entry.project.parent]
|
|
|
|
jobs = Job.where(project: projects).active
|
|
|
|
support = jobs.where(category: JobCategory.support).first
|
|
|
|
retainer = jobs.where(category: JobCategory.retainer).first
|
|
|
|
sprints = jobs.where(category: JobCategory.sprints).first
|
|
|
|
priority_list = [sprints, retainer, support].compact
|
|
|
|
|
|
|
|
return jobs.first if priority_list.empty?
|
|
|
|
|
|
|
|
return support if time_entry.activity.name == "Support"
|
|
|
|
|
|
|
|
return priority_list.first if time_entry.issue.blank?
|
|
|
|
|
|
|
|
return support if time_entry.issue.tracker.name == "Support"
|
|
|
|
|
|
|
|
priority_list.first
|
|
|
|
end
|
2023-11-16 20:23:09 +00:00
|
|
|
end
|