mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-10-31 03:09:06 -04:00
96 lines
3.1 KiB
Ruby
96 lines
3.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Job < ApplicationRecord
|
|
extend FriendlyId
|
|
include Ownable
|
|
include Finishable
|
|
include PhotoCapable
|
|
|
|
belongs_to :garden, optional: true
|
|
belongs_to :jobbable, polymorphic: true, optional: true
|
|
# The template step this job was made from, if it was.
|
|
belongs_to :crop_job_step, optional: true
|
|
|
|
JOBBABLE_TYPES = %w(Crop Planting Seed).freeze
|
|
# What each action can be about: plant a crop or the seeds of it, harvest a crop
|
|
# or a planting of it.
|
|
ACTION_JOBBABLES = { 'plant' => %w(Crop Seed), 'harvest' => %w(Crop Planting) }.freeze
|
|
|
|
enum :action, ACTION_JOBBABLES.keys.index_with(&:itself), prefix: true, validate: { allow_nil: true }
|
|
|
|
scope :active, -> { where(finished: [false, nil]) }
|
|
scope :finished_this_week, -> { where(finished: true, finished_at: Time.zone.now.beginning_of_week..) }
|
|
|
|
friendly_id :job_slug, use: %i(slugged finders)
|
|
|
|
validates :name, presence: true
|
|
validates :slug, uniqueness: true
|
|
# The add form sends these empty when the job is not linked to anything.
|
|
normalizes :jobbable_type, with: ->(type) { type.presence }
|
|
validates :jobbable_type, inclusion: { in: JOBBABLE_TYPES }, allow_nil: true
|
|
validate :action_goes_with_jobbable, if: :action
|
|
validate :jobbable_is_the_owners, if: -> { jobbable_id_changed? || jobbable_type_changed? }
|
|
|
|
before_save :stamp_finished_at, if: :finished_changed?
|
|
|
|
delegate :login_name, :slug, to: :owner, prefix: true
|
|
delegate :name, to: :garden, prefix: true, allow_nil: true
|
|
|
|
def job_slug
|
|
"#{owner.login_name}-#{name}-#{id}".downcase.tr(' ', '-')
|
|
end
|
|
|
|
def to_s
|
|
name
|
|
end
|
|
|
|
# The planting a job is about, if it is about one. The form, Copy and the API
|
|
# speak of plantings; the link itself is the jobbable.
|
|
def planting
|
|
jobbable if jobbable_type == 'Planting'
|
|
end
|
|
|
|
def planting=(planting)
|
|
self.jobbable = planting
|
|
end
|
|
|
|
def planting_id
|
|
jobbable_id if jobbable_type == 'Planting'
|
|
end
|
|
|
|
# Nothing chosen on the form leaves the job about whatever it was about.
|
|
def planting_id=(id)
|
|
self.jobbable = Planting.find_by(id:) if id.present?
|
|
end
|
|
|
|
# The photo taken before or after the job was done, if there is one. Of those
|
|
# with the label, the first added. Uses the loaded photos, so a list of jobs
|
|
# can load them all at once.
|
|
def photo_at(moment)
|
|
photo_associations.select { |association| association.label == moment }.min_by(&:id)&.photo
|
|
end
|
|
|
|
def planting_name
|
|
planting&.crop_name
|
|
end
|
|
|
|
private
|
|
|
|
def stamp_finished_at
|
|
self.finished_at = (Time.zone.now if finished)
|
|
end
|
|
|
|
def action_goes_with_jobbable
|
|
errors.add(:action, :cannot_be_about_that) unless ACTION_JOBBABLES.fetch(action, []).include?(jobbable_type)
|
|
end
|
|
|
|
# A job about something is about something that is there. Plantings and seeds are a member's own: a job can't
|
|
# be about someone else's.
|
|
def jobbable_is_the_owners
|
|
return unless JOBBABLE_TYPES.include?(jobbable_type)
|
|
return errors.add(:jobbable, :required) if jobbable.nil?
|
|
|
|
errors.add(:jobbable, :invalid) if jobbable.respond_to?(:owner_id) && jobbable.owner_id != owner_id
|
|
end
|
|
end
|