Class: WorkEffort

Inherits:
ActiveRecord::Base
  • Object
show all
Includes:
ErpTechSvcs::Utils::DefaultNestedSetMethods
Defined in:
app/models/work_effort.rb

Overview

Table Definition ########################### create_table “work_efforts”, :force => true do |t|

t.integer  "parent_id"
t.integer  "lft"
t.integer  "rgt"
t.integer  "facility_id"
t.integer  "projected_cost_money_id"
t.integer  "actual_cost_money_id"
t.integer  "fixed_asset_id"
t.integer  "work_effort_purpose_type_id"
t.integer  "work_effort_type_id"
t.string   "description"
t.string   "type"
t.datetime "start_at"
t.datetime "end_at"
t.integer  "work_effort_record_id"
t.string   "work_effort_record_type"
t.integer  "work_effort_item_id"
t.string   "work_effort_item_type"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.text     "comments"
t.integer  "percent_done"
t.integer  "duration"
t.string   "duration_unit"
t.integer  "effort"
t.string   "effort_unit"
t.datetime "base_line_start_at"
t.datetime "base_line_end_at"
t.integer  "base_line_percent_done"
t.integer  "project_id"
t.text     "custom_fields"

end

add_index “work_efforts”, [“end_at”], :name => “index_work_efforts_on_end_at” add_index “work_efforts”, [“fixed_asset_id”], :name => “index_work_efforts_on_fixed_asset_id” add_index “work_efforts”, [“project_id”], :name => “work_effort_project_idx” add_index “work_efforts”, [“work_effort_item_type”, “work_effort_item_id”], :name => “work_item_idx” add_index “work_efforts”, [“work_effort_record_id”, “work_effort_record_type”], :name => “work_effort_record_id_type_idx”

Constant Summary collapse

@@task_status_complete_iid =
'task_status_complete'
@@task_status_in_progress_iid =
'task_status_in_progress'
@@task_resource_status_complete_iid =
'task_resource_status_complete'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.apply_filters(filters, statement) ⇒ ActiveRecord::Relation

Filter records

Parameters:

  • filters (Hash)

    a hash of filters to be applied,

  • statement (ActiveRecord::Relation)

    the query being built

Returns:

  • (ActiveRecord::Relation)

    the query being built



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'app/models/work_effort.rb', line 119

def apply_filters(filters, statement)
  work_efforts_tbl = WorkEffort.arel_table

  # filter by description
  unless filters[:description].blank?
    statement = statement.where(work_efforts_tbl[:description].matches("%#{filters[:description]}%"))
  end

  # filter by WorkEffortType
  unless filters[:work_effort_type_iids].blank?
    statement = statement.where(work_effort_type_id: WorkEffortType.where(internal_identifier: filters[:work_effort_type_iids]))
  end

  # filter by Status
  unless filters[:status].blank?
    statement = statement.with_current_status(filters[:status].split(','))
  end

  # filter by start_at
  unless filters[:start_date].blank?
    statement = statement.where(work_efforts_tbl[:start_at].gteq(Date.parse(filters[:start_date])))
  end

  # filter by end_at
  unless filters[:end_date].blank?
    statement = statement.where(work_efforts_tbl[:end_at].lteq(Date.parse(filters[:end_date])))
  end

  # filter by assigned to
  unless filters[:assigned_to_ids].blank?
    work_effort_party_assignments_tbl = WorkEffortPartyAssignment.arel_table

    statement = statement.joins(:work_effort_party_assignments)
    .where(work_effort_party_assignments_tbl[:role_type_id].in(RoleType.find_child_role_types([RoleType.work_resource]).collect(&:id)))
    .where(work_effort_party_assignments_tbl[:party_id].in(filters[:assigned_to_ids]))
  end

  # filter by project
  unless filters[:project_ids].blank?
    statement = statement.where(work_efforts_tbl[:project_id].in(filters[:project_ids]))
  end

  # filter by parties
  unless filters[:parties].blank?
    data = JSON.parse(filters[:parties])

    statement = statement.scope_by_party(data['party_ids'].split(','),
                                         {role_types: RoleType.where('internal_identifier' => data['role_types'].split(','))})
  end

  statement
end

.scope_by_dba_organization(dba_organization) ⇒ ActiveRecord::Relation Also known as: scope_by_dba

scope by dba organization

Parameters:

  • dba_organization (Party)

    dba organization to scope by

Returns:

  • (ActiveRecord::Relation)


181
182
183
# File 'app/models/work_effort.rb', line 181

def scope_by_dba_organization(dba_organization)
  scope_by_party(dba_organization, {role_types: [RoleType.iid('dba_org')]})
end

.scope_by_party(party, options = {}) ⇒ ActiveRecord::Relation

scope by party

or an array of Party ids

Parameters:

  • party (Integer | Party | Array)

    either a id of Party record, a Party record, an array of Party records

  • options (Hash) (defaults to: {})

    options to apply to this scope

Options Hash (options):

  • :role_types (Array)

    role types to include in the scope

Returns:

  • (ActiveRecord::Relation)


205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'app/models/work_effort.rb', line 205

def scope_by_party(party, options={})
  table_alias = String.random

  if options[:role_types]
    joins("inner join entity_party_roles as #{table_alias} on #{table_alias}.entity_record_type = 'WorkEffort'
                                 and #{table_alias}.entity_record_id = work_efforts.id and
                                 #{table_alias}.role_type_id in (#{RoleType.find_child_role_types(options[:role_types]).collect(&:id).join(',')})
                                 and #{table_alias}.party_id in (#{Party.select('id').where(id: party).to_sql})")

  else
    joins("inner join entity_party_roles as #{table_alias} on #{table_alias}.entity_record_type = 'WorkEffort'
                                 and #{table_alias}.entity_record_id = work_efforts.id
                                 and #{table_alias}.party_id in (#{Party.select('id').where(id: party).to_sql})")
  end
end

.scope_by_party_assignment(party, options = {}) ⇒ ActiveRecord::Relation

scope by work efforts assigned to the passed party

Parameters:

  • party (Party)

    party to look for assignments

  • options (Hash) (defaults to: {})

    options to apply to this scope

Options Hash (options):

  • :role_types (Array)

    role types to include in the scope

Returns:

  • (ActiveRecord::Relation)


239
240
241
242
243
244
245
246
247
# File 'app/models/work_effort.rb', line 239

def scope_by_party_assignment(party, options={})
  statement = joins("join work_effort_party_assignments wepa on wepa.work_effort_id = work_efforts.id and wepa.party_id = #{party.id}")

  if options[:role_types]
    statement = statement.where("wepa.role_type_id" => RoleType.find_child_role_types(options[:role_types]))
  end

  statement
end

.scope_by_project(project) ⇒ ActiveRecord::Relation

scope by project

or an array of Project ids

Parameters:

  • project (Integer | Project | Array)

    either a id of Project record, a Project record, an array of Project records

Returns:

  • (ActiveRecord::Relation)


193
194
195
# File 'app/models/work_effort.rb', line 193

def scope_by_project(project)
  where(project_id: project)
end

.scope_by_user(user, options = {}) ⇒ ActiveRecord::Relation

scope by work efforts assigned to the passed user

Parameters:

  • user (User)

    user to look for assignments

  • options (Hash) (defaults to: {})

    options to apply to this scope

Options Hash (options):

  • :role_types (Array)

    role types to include in the scope

Returns:

  • (ActiveRecord::Relation)


228
229
230
# File 'app/models/work_effort.rb', line 228

def scope_by_user(user, options={})
  scope_by_party_assignment(user.party, options)
end

Instance Method Details

#assigned_parties(role_types = ['work_resource']) ⇒ Array

Get assigned parties by role type

Parameters:

  • role_types (Array) (defaults to: ['work_resource'])

    ([‘work_resource’]) Array of role types to scope by

Returns:

  • (Array)

    Assigned Parties



266
267
268
269
270
271
272
# File 'app/models/work_effort.rb', line 266

def assigned_parties(role_types=['work_resource'])
  role_types = RoleType.find_child_role_types(role_types)

  Party.joins(work_effort_party_assignments: :role_type)
  .where(role_types: {id: role_types})
  .where(work_effort_party_assignments: {work_effort_id: self.id})
end

#assigned_rolesArray

get all the assigned roles

Returns:

  • (Array)

    descriptions of role types comma separated



298
299
300
# File 'app/models/work_effort.rb', line 298

def assigned_roles
  self.role_types.collect(&:description).join(',')
end

#calculate_children_totalsObject

Calculate totals for children



437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
# File 'app/models/work_effort.rb', line 437

def calculate_children_totals
  self.start_at = self.descendants.order('start_at asc').first.start_at
  self.end_at = self.descendants.order('end_at desc').last.end_at

  lowest_duration_unit = nil
  duration_total = nil
  percent_done_total = 0.0
  self.descendants.collect do |child|
    if child.leaf?
      if child.duration and child.duration > 0
        duration_total = 0.0 if duration_total.nil?

        duration_in_hours = ErpWorkEffort::Services::UnitConverter.convert_unit(child.duration.to_f, child.duration_unit.to_sym, :h)

        percent_done_total += (duration_in_hours.to_f * (child.percent_done.to_f / 100))

        if lowest_duration_unit.nil? || ErpWorkEffort::Services::UnitConverter.new(lowest_duration_unit) > child.duration_unit.to_sym
          lowest_duration_unit = child.duration_unit.to_sym
        end

        duration_total += duration_in_hours
      end
    end
  end

  if duration_total
    self.duration_unit = lowest_duration_unit.to_s
    if lowest_duration_unit != :h
      self.duration = ErpWorkEffort::Services::UnitConverter.convert_unit(duration_total.to_f, :h, lowest_duration_unit)
    else
      self.duration = duration_total
    end

    self.percent_done = (((percent_done_total / duration_total.to_f).round(2)) * 100)
  end

  lowest_effort_unit = nil
  effort_total = nil
  self.descendants.collect do |child|
    if child.leaf?
      if child.effort and child.effort > 0
        effort_total = 0.0 if effort_total.nil?

        if lowest_effort_unit.nil? || ErpWorkEffort::Services::UnitConverter.new(lowest_effort_unit) > child.effort_unit.to_sym
          lowest_effort_unit = child.effort_unit.to_sym
        end

        effort_total += ErpWorkEffort::Services::UnitConverter.convert_unit(child.effort.to_f, child.effort_unit.to_sym, :h)
      end
    end
  end

  if effort_total
    self.effort_unit = lowest_effort_unit.to_s
    if lowest_effort_unit != :h
      self.effort = ErpWorkEffort::Services::UnitConverter.convert_unit(effort_total.to_f, :h, lowest_effort_unit)
    else
      self.effort = effort_total
    end
  end

  self.save!
end

#completed?Boolean

return true if this effort has been completed, false otherwise

Returns:

  • (Boolean)

    true if completed



319
320
321
# File 'app/models/work_effort.rb', line 319

def completed?
  finished_at.nil? ? false : true
end

#current_status=(args) ⇒ Object

set current status of entity.

This is overriding the default method to update the task assignments as well if the status is set to complete

TrackedStatusType to set, a TrackedStatusType instance, or three params the status, options and party_id

Parameters:

  • args (String, TrackedStatusType, Array)

    This can be a string of the internal identifier of the



372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'app/models/work_effort.rb', line 372

def current_status=(args)
  super(args)

  if args.is_a?(Array)
    status = args[0]
  else
    status = args
  end

  if status.is_a? TrackedStatusType
    status = status.internal_identifier
  end

  if status == @@task_status_complete_iid
    complete!
  else
    update_parent_status!
  end
end

#dba_organizationObject



258
259
260
# File 'app/models/work_effort.rb', line 258

def dba_organization
  find_party_with_role(RoleType.dba_org)
end

#descendants_complete?(ignored_id = nil) ⇒ Boolean

Check if all this WorkEfforts descendants are complete. An optional ignored_id can be passed for a node that you don’t want to check if it is complete

Parameters:

  • ingored_id (Integer)

    Id of WorkEffort to ingnore it’s status

Returns:

  • (Boolean)


397
398
399
400
401
402
403
404
405
406
407
408
409
410
# File 'app/models/work_effort.rb', line 397

def descendants_complete?(ignored_id=nil)
  all_complete = true

  descendants.each do |node|
    unless node.id == ignored_id
      if !node.is_complete?
        all_complete = false
        break
      end
    end
  end

  all_complete
end

#description_of_assigned_parties(role_types = ['work_resource']) ⇒ Array

Get comma sepeated description of all Parties assigned

Parameters:

  • role_types (Array) (defaults to: ['work_resource'])

    ([‘work_resource’]) Array of role types to scope by

Returns:

  • (Array)

    descriptions of Parties comma separated



289
290
291
292
293
# File 'app/models/work_effort.rb', line 289

def description_of_assigned_parties(role_types=['work_resource'])
  assigned_parties(role_types).collect do |item|
    item.party.description
  end.join(',')
end

#finished?Boolean

return true if this effort has been completed, false otherwise

Returns:

  • (Boolean)


326
327
328
# File 'app/models/work_effort.rb', line 326

def finished?
  completed?
end

#has_assigned_parties?Boolean

Returns:

  • (Boolean)


342
343
344
# File 'app/models/work_effort.rb', line 342

def has_assigned_parties?
  self.work_effort_party_assignments.count != 0
end

#has_time_entries?Boolean

Returns:

  • (Boolean)


338
339
340
# File 'app/models/work_effort.rb', line 338

def has_time_entries?
  self.time_entries.count != 0
end

#is_complete?Boolean

Returns:

  • (Boolean)


412
413
414
# File 'app/models/work_effort.rb', line 412

def is_complete?
  (current_status == @@task_status_complete_iid)
end

#party_assigned?(party, role_types = ['work_resource']) ⇒ Boolean

Returns true if the party is assigned to WorkEffort

Parameters:

  • party (Party)

    Party to check if it is assigned

  • role_types (Array) (defaults to: ['work_resource'])

    Array of role types to check the assignments for

Returns:

  • (Boolean)


278
279
280
281
282
283
# File 'app/models/work_effort.rb', line 278

def party_assigned?(party, role_types=['work_resource'])
  !WorkEffort.joins(work_effort_party_assignments: :role_type)
  .where(role_types: {id: RoleType.find_child_role_types(role_types)})
  .where(work_effort_party_assignments: {work_effort_id: self.id})
  .where(work_effort_party_assignments: {party_id: party.id}).first.nil?
end

#role_typesObject Also known as: role_type_assignments

How is a work effort assigned, it can be assigned to party roles which allow for generic assignment.



78
# File 'app/models/work_effort.rb', line 78

has_and_belongs_to_many :role_types

#roll_upObject

Roll up totals to parents



503
504
505
506
507
508
# File 'app/models/work_effort.rb', line 503

def roll_up
  if self.parent
    self.parent.calculate_children_totals
    self.parent.roll_up
  end
end

#start(initial_status = '') ⇒ Object

start work effort with initial_status (string)

Parameters:

  • initial_status (String) (defaults to: '')

    status to start at



349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'app/models/work_effort.rb', line 349

def start(initial_status='')
  effort = self
  unless self.descendants.flatten!.nil?
    children = self.descendants.flatten
    effort = children.last
  end

  if current_status.nil?
    effort.current_status = initial_status
    effort.start_at = DateTime.now
    effort.save
  else
    raise 'Effort Already Started'
  end
end

#started?Boolean

return true if this effort has been started, false otherwise

Returns:

  • (Boolean)

    true if started



312
313
314
# File 'app/models/work_effort.rb', line 312

def started?
  current_status.nil? ? false : true
end

#statusString

get the current status of this work_effort

Returns:

  • (String)

    status



305
306
307
# File 'app/models/work_effort.rb', line 305

def status
  current_status
end

#time_entries_allowed?(party) ⇒ Boolean

Check if a party is allowed to enter time aganist this Work Effort

Parameters:

  • party (Party)

    Party to test aganist

Returns:

  • (Boolean)

    If time entries are allowed



334
335
336
# File 'app/models/work_effort.rb', line 334

def time_entries_allowed?(party)
  self.party_assigned?(party) and self.current_status != @@task_status_complete_iid
end

#to_data_hashHash

converts this record a hash data representation

Returns:

  • (Hash)

    data of record



513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
# File 'app/models/work_effort.rb', line 513

def to_data_hash
  data = to_hash(only: [
                   :id,
                   {leaf?: :leaf},
                   :parent_id,
                   :description,
                   :start_at,
                   :end_at,
                   :percent_done,
                   :duration,
                   :duration_unit,
                   :effort,
                   :effort_unit,
                   :comments,
                   :sequence,
                   :created_at,
                   :updated_at,
                   :current_status
                 ]
                 )

  data[:status] = self.try(:current_status_application).try(:to_data_hash)
  data[:work_effort_type] = self.try(:work_effort_type).try(:to_data_hash)

  data
end

#to_labelObject



254
255
256
# File 'app/models/work_effort.rb', line 254

def to_label
  self.description
end

#to_sObject



250
251
252
# File 'app/models/work_effort.rb', line 250

def to_s
  self.description
end

#total_hoursObject

get total hours for this WorkEffort by TimeEntries



427
428
429
430
431
432
433
# File 'app/models/work_effort.rb', line 427

def total_hours
  if self.leaf?
    time_entries.all.sum { |time_entry| time_entry.hours }
  else
    self.descendants.collect(&:total_hours)
  end
end

#total_hours_in_secondsObject

get total hours for this WorkEffort by TimeEntries



418
419
420
421
422
423
424
# File 'app/models/work_effort.rb', line 418

def total_hours_in_seconds
  if leaf?
    time_entries.sum(:regular_hours_in_seconds)
  else
    descendants.collect(&:total_hours_in_seconds)
  end
end

#work_effort_biz_txn_eventsObject

What BizTxnEvents have been related to this WorkEffort



98
# File 'app/models/work_effort.rb', line 98

has_many :work_effort_biz_txn_events, :dependent => :destroy

#work_effort_fixed_asset_assignmentsObject

What Fixed Assets (tools, equipment) are used in the execution of this Work Effort



94
# File 'app/models/work_effort.rb', line 94

has_many :work_effort_fixed_asset_assignments, :dependent => :destroy

#work_effort_inventory_assignmentsObject

What Inventory Items are used in the execution of this Work Effort



90
# File 'app/models/work_effort.rb', line 90

has_many :work_effort_inventory_assignments, :dependent => :destroy

#work_effort_party_assignmentsObject

How is this Work Effort is assigned



82
# File 'app/models/work_effort.rb', line 82

has_many :work_effort_party_assignments, :dependent => :destroy

#work_effort_recordObject

Allow for polymorphic subtypes of this class



102
# File 'app/models/work_effort.rb', line 102

belongs_to :work_effort_record, :polymorphic => true

#work_order_item_fulfillmentsObject

How is this Work Effort related to Work Order Items (order_line_items)



74
# File 'app/models/work_effort.rb', line 74

has_many :work_order_item_fulfillments, :dependent => :destroy