Class: Equipment

Inherits:
Matter show all
Includes:
Attachable
Defined in:
app/models/equipment.rb

Overview

Informations

License

Ekylibre - Simple agricultural ERP Copyright (C) 2008-2009 Brice Texier, Thibaud Merigon Copyright (C) 2010-2012 Brice Texier Copyright (C) 2012-2019 Brice Texier, David Joulin

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see www.gnu.org/licenses.

Table: products

activity_production_id       :integer
address_id                   :integer
birth_date_completeness      :string
birth_farm_number            :string
born_at                      :datetime
category_id                  :integer          not null
codes                        :jsonb
country                      :string
created_at                   :datetime         not null
creator_id                   :integer
custom_fields                :jsonb
dead_at                      :datetime
default_storage_id           :integer
derivative_of                :string
description                  :text
end_of_life_reason           :string
father_country               :string
father_identification_number :string
father_variety               :string
filiation_status             :string
first_calving_on             :datetime
fixed_asset_id               :integer
id                           :integer          not null, primary key
identification_number        :string
initial_born_at              :datetime
initial_container_id         :integer
initial_dead_at              :datetime
initial_enjoyer_id           :integer
initial_father_id            :integer
initial_geolocation          :geometry({:srid=>4326, :type=>"st_point"})
initial_mother_id            :integer
initial_movement_id          :integer
initial_owner_id             :integer
initial_population           :decimal(19, 4)   default(0.0)
initial_shape                :geometry({:srid=>4326, :type=>"multi_polygon"})
lock_version                 :integer          default(0), not null
member_variant_id            :integer
mother_country               :string
mother_identification_number :string
mother_variety               :string
name                         :string           not null
nature_id                    :integer          not null
number                       :string           not null
origin_country               :string
origin_identification_number :string
originator_id                :integer
parent_id                    :integer
person_id                    :integer
picture_content_type         :string
picture_file_name            :string
picture_file_size            :integer
picture_updated_at           :datetime
reading_cache                :jsonb            default("{}")
team_id                      :integer
tracking_id                  :integer
type                         :string
updated_at                   :datetime         not null
updater_id                   :integer
uuid                         :uuid
variant_id                   :integer          not null
variety                      :string           not null
work_number                  :string

Constant Summary

Constants included from Indicateable

Indicateable::DEPRECATED

Instance Method Summary collapse

Methods inherited from Product

#activity, #activity_id, #add_content_products, #age, #available?, #best_activity_production, #born_at_in_interventions, #build_new_phase, #calculate_net_surface_area, #choose_default_name, #containeds, #container_at, #contains, #contents_name, #dead?, #dead_at_in_interventions, #dead_first_at, #default_catalog_item, #deliverable?, #evaluated_price, #get, #groups_at, #initial_reading, #initial_shape_area, #initializeable?, #localized_variants, #matching_model, miscibility_of, #move!, #nature_name, #net_surface_area, #owner, #part_with, #part_with!, #picture_path, #population, #price, #production, #read_store_attribute, #set_default_values, #set_initial_values, #shape=, #stock_info, #unroll_name, #update_default_values, #variables, #work_name

Methods included from Customizable

#custom_value, #set_custom_value, #validate_custom_fields

Methods included from Versionable

#add_creation_version, #add_destruction_version, #add_update_version, #last_version, #notably_changed?, #version_object

Methods included from Indicateable

#add_and_read, #add_to_readings, #compute_and_read, #copy_readings_of!, #density, #first_reading, #get, #get!, #mark!, #operate_on_readings, #read!, #read_whole_indicators_from!, #reading, #substract_and_read, #substract_to_readings

Methods inherited from Ekylibre::Record::Base

#already_updated?, #check_if_destroyable?, #check_if_updateable?, columns_definition, #customizable?, customizable?, #customized?, #destroyable?, #editable?, has_picture, #human_attribute_name, nomenclature_reflections, #old_record, #others, refers_to, #unsuppress, #updateable?

Methods included from Userstamp::Stampable

included

Methods included from Userstamp::Stamper

included

Instance Method Details

#alert_component_life(component) ⇒ Object

Component #################################


341
342
343
344
345
346
347
348
# File 'app/models/equipment.rb', line 341

def alert_component_life(component)
  User.notify_administrators(
    :equipment_component_is_at_end_of_life,
    interpolations_for(component, remaining_lifespan_of(component)),
    target: self,
    level: :warning
  )
end

#alert_component_work(component) ⇒ Object


350
351
352
353
354
355
356
357
# File 'app/models/equipment.rb', line 350

def alert_component_work(component)
  User.notify_administrators(
    :equipment_component_is_worn_out,
    interpolations_for(component, remaining_working_lifespan_of(component)),
    target: self,
    level: :warning
  )
end

#alert_lifeObject

Equipment #################################


320
321
322
323
324
325
326
327
# File 'app/models/equipment.rb', line 320

def alert_life
  User.notify_administrators(
    :equipment_is_at_end_of_life,
    interpolations(remaining_lifespan),
    target: self,
    level: :warning
  )
end

#alert_workObject


329
330
331
332
333
334
335
336
# File 'app/models/equipment.rb', line 329

def alert_work
  User.notify_administrators(
    :equipment_is_worn_out,
    interpolations(remaining_working_lifespan),
    target: self,
    level: :warning
  )
end

#current_lifeObject

Used up lifespan #############################


150
151
152
# File 'app/models/equipment.rb', line 150

def current_life
  (Time.zone.today - born_at.to_date).in(:day)
end

#current_life_of(component) ⇒ Object

Used up lifespan ##########################


268
269
270
# File 'app/models/equipment.rb', line 268

def current_life_of(component)
  (Time.zone.now - replaced_at(component, born_at)).to_f.in_second
end

#current_work_lifeObject


154
155
156
# File 'app/models/equipment.rb', line 154

def current_work_life
  working_duration.in(:day)
end

#current_work_life_of(component) ⇒ Object


272
273
274
# File 'app/models/equipment.rb', line 272

def current_work_life_of(component)
  working_duration(replaced_at(component, born_at)).to_f.in_second
end

#last_replacement(component) ⇒ Object


246
247
248
# File 'app/models/equipment.rb', line 246

def last_replacement(component)
  replacements_of(component).joins(:intervention).order('interventions.stopped_at DESC').first
end

#lifespan_progressObject

0 - 1

176
177
178
179
# File 'app/models/equipment.rb', line 176

def lifespan_progress
  return 0 unless current_life && total_lifespan
  current_life / total_lifespan
end

#lifespan_progress_of(component) ⇒ Object

0 - 1

294
295
296
297
# File 'app/models/equipment.rb', line 294

def lifespan_progress_of(component)
  return 0 unless current_life_of(component) && total_lifespan_of(component)
  current_life_of(component) / total_lifespan_of(component)
end

#lifespan_progress_percentObject

0 - 100%

188
189
190
# File 'app/models/equipment.rb', line 188

def lifespan_progress_percent
  lifespan_progress * 100
end

#remaining_lifespanObject

Remaining lifespans (total - gone) ###########


161
162
163
164
# File 'app/models/equipment.rb', line 161

def remaining_lifespan
  return nil unless total_lifespan
  total_lifespan - current_life
end

#remaining_lifespan_of(component) ⇒ Object

Remaining lifespan values ##############


279
280
281
282
# File 'app/models/equipment.rb', line 279

def remaining_lifespan_of(component)
  return nil unless total_lifespan_of(component)
  total_lifespan_of(component) - current_life_of(component)
end

#remaining_working_lifespanObject


166
167
168
169
# File 'app/models/equipment.rb', line 166

def remaining_working_lifespan
  return nil unless total_working_lifespan
  total_working_lifespan - current_work_life
end

#remaining_working_lifespan_life_of(component) ⇒ Object


284
285
286
287
# File 'app/models/equipment.rb', line 284

def remaining_working_lifespan_life_of(component)
  return nil unless total_working_lifespan_of(component)
  total_working_life_of(component) - current_work_life_of(component)
end

#replaced_at(component, since = nil) ⇒ Object


240
241
242
243
244
# File 'app/models/equipment.rb', line 240

def replaced_at(component, since = nil)
  replacement = last_replacement(component)
  return replacement.intervention.stopped_at if replacement
  since
end

#replacements_of(component) ⇒ Object

Returns the list of replacements

Raises:

  • (ArgumentError)

235
236
237
238
# File 'app/models/equipment.rb', line 235

def replacements_of(component)
  raise ArgumentError, 'Incompatible component' unless component.product_nature_variant == variant
  part_replacements.where(component: component.self_and_parents)
end

#self_prepelled_equipment?Boolean

Returns:

  • (Boolean)

99
100
101
# File 'app/models/equipment.rb', line 99

def self_prepelled_equipment?
  variety == :self_prepelled_equipment
end

#statusObject

Statuses #####################################


106
107
108
109
110
# File 'app/models/equipment.rb', line 106

def status
  return :stop if dead_at?
  return :caution if issues.where(state: :opened).any?
  :go
end

#total_lifespanObject

Total lifespans values fetched from variant ##


139
140
141
# File 'app/models/equipment.rb', line 139

def total_lifespan
  variant.lifespan if has_indicator?(:lifespan)
end

#total_lifespan_of(component) ⇒ Object

Total lifespans values ####################


253
254
255
256
257
# File 'app/models/equipment.rb', line 253

def total_lifespan_of(component)
  comp_variant = component.part_product_nature_variant
  return nil unless comp_variant && comp_variant.has_indicator?(:lifespan)
  comp_variant.lifespan
end

#total_working_lifespanObject


143
144
145
# File 'app/models/equipment.rb', line 143

def total_working_lifespan
  variant.working_lifespan if has_indicator?(:working_lifespan)
end

#total_working_lifespan_of(component) ⇒ Object


259
260
261
262
263
# File 'app/models/equipment.rb', line 259

def total_working_lifespan_of(component)
  comp_variant = component.part_product_nature_variant
  return nil unless comp_variant && comp_variant.has_indicator?(:working_lifespan)
  comp_variant.working_lifespan
end

#tractor?Boolean

Returns:

  • (Boolean)

95
96
97
# File 'app/models/equipment.rb', line 95

def tractor?
  variety == :tractor
end

#wear_status(component = nil) ⇒ Object


112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'app/models/equipment.rb', line 112

def wear_status(component = nil)
  if component.nil?
    # To take into account the tear&wear of the components.
    comp_status = variant.components.map { |c| wear_status(c) }

    # To take into account the tear&wear of the equipment itself.
    progresses = [lifespan_progress, working_lifespan_progress]
    worn_out = progresses.any? { |prog| prog >= 1 }
    almost_worn_out = progresses.any? { |prog| prog >= 0.85 }

    return :stop if comp_status.include?(:stop) || worn_out
    return :caution if comp_status.include?(:caution) || almost_worn_out
    return :go
  end

  # If we're only working on a single component.
  life_progress = lifespan_progress_of(component)
  work_progress = working_lifespan_progress_of(component)
  progresses = [life_progress, work_progress]
  return :stop if progresses.any? { |prog| prog >= 1 }
  return :caution if progresses.any? { |prog| prog >= 0.85 }
  :go
end

#working_duration(since = nil) ⇒ Object

Return working duration using the appropriate calculation. Either calculates from daily_average_working_time or from the time spent in interventions.


202
203
204
205
206
207
# File 'app/models/equipment.rb', line 202

def working_duration(since = nil)
  start = since || born_at
  work_duration = working_duration_from_average(since: start)
  work_duration ||= working_duration_from_interventions(since: start)
  work_duration.to_f.in_second
end

#working_duration_from_average(options = {}) ⇒ Object

Returns working duration as a product of the average work time per day and the number of days worked.


221
222
223
224
225
226
# File 'app/models/equipment.rb', line 221

def working_duration_from_average(options = {})
  return nil unless has_indicator?(:daily_average_working_time)
  duration = (Time.zone.today - options[:since].to_date)
  average = variant.daily_average_working_time
  (average * duration.in_day).in_second
end

#working_duration_from_interventions(options = {}) ⇒ Object

Returns working duration as the sum of the time spent working in interventions.


211
212
213
214
215
216
217
# File 'app/models/equipment.rb', line 211

def working_duration_from_interventions(options = {})
  role = options[:as] || :tool
  periods = InterventionWorkingPeriod.with_intervention_parameter(role, self)
  periods = periods.where('started_at >= ?', options[:since]) if options[:since]
  periods = periods.of_campaign(options[:campaign]) if options[:campaign]
  periods.sum(:duration).in_second
end

#working_lifespan_progressObject


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

def working_lifespan_progress
  return 0 unless current_work_life && total_working_lifespan
  current_work_life / total_working_lifespan
end

#working_lifespan_progress_of(component) ⇒ Object


299
300
301
302
# File 'app/models/equipment.rb', line 299

def working_lifespan_progress_of(component)
  return 0 unless current_work_life_of(component) && total_working_lifespan_of(component)
  current_work_life_of(component) / total_working_lifespan_of(component)
end

#working_lifespan_progress_percentObject


192
193
194
# File 'app/models/equipment.rb', line 192

def working_lifespan_progress_percent
  working_lifespan_progress * 100
end