class Budget < ActiveRecord::Base include Measurable include Sluggable CURRENCY_SYMBOLS = %w(€ $ £ ¥).freeze validates :name, presence: true, uniqueness: true validates :phase, inclusion: { in: Budget::Phase::PHASE_KINDS } validates :currency_symbol, presence: true validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/ has_many :investments, dependent: :destroy has_many :ballots, dependent: :destroy has_many :groups, dependent: :destroy has_many :headings, through: :groups has_many :phases, class_name: Budget::Phase before_validation :sanitize_descriptions after_create :generate_phases scope :drafting, -> { where(phase: "drafting") } scope :informing, -> { where(phase: "informing") } scope :accepting, -> { where(phase: "accepting") } scope :reviewing, -> { where(phase: "reviewing") } scope :selecting, -> { where(phase: "selecting") } scope :valuating, -> { where(phase: "valuating") } scope :publishing_prices, -> { where(phase: "publishing_prices") } scope :balloting, -> { where(phase: "balloting") } scope :reviewing_ballots, -> { where(phase: "reviewing_ballots") } scope :finished, -> { where(phase: "finished") } scope :open, -> { where.not(phase: "finished") } def self.current where.not(phase: "drafting").order(:created_at).last end def current_phase phases.send(phase) end def published_phases phases.published.order(:id) end def description description_for_phase(phase) end def description_for_phase(phase) if phases.exists? && phases.send(phase).description.present? phases.send(phase).description else send("description_#{phase}").try(:html_safe) end end def self.title_max_length 80 end def drafting? phase == "drafting" end def informing? phase == "informing" end def accepting? phase == "accepting" end def reviewing? phase == "reviewing" end def selecting? phase == "selecting" end def valuating? phase == "valuating" end def publishing_prices? phase == "publishing_prices" end def balloting? phase == "balloting" end def reviewing_ballots? phase == "reviewing_ballots" end def finished? phase == "finished" end def published_prices? Budget::Phase::PUBLISHED_PRICES_PHASES.include?(phase) end def valuating_or_later? valuating? || publishing_prices? || balloting_or_later? end def balloting_process? balloting? || reviewing_ballots? end def balloting_or_later? balloting_process? || finished? end def heading_price(heading) heading_ids.include?(heading.id) ? heading.price : -1 end def translated_phase I18n.t "budgets.phase.#{phase}" end def formatted_amount(amount) ActionController::Base.helpers.number_to_currency(amount, precision: 0, locale: I18n.locale, unit: currency_symbol) end def formatted_heading_price(heading) formatted_amount(heading_price(heading)) end def formatted_heading_amount_spent(heading) formatted_amount(amount_spent(heading)) end def investments_orders case phase when 'accepting', 'reviewing' %w{random} when 'publishing_prices', 'balloting', 'reviewing_ballots' %w{random price} when 'finished' %w{random} else %w{random confidence_score} end end def email_selected investments.selected.order(:id).each do |investment| Mailer.budget_investment_selected(investment).deliver_later end end def email_unselected investments.unselected.order(:id).each do |investment| Mailer.budget_investment_unselected(investment).deliver_later end end def has_winning_investments? investments.winners.any? end private def sanitize_descriptions s = WYSIWYGSanitizer.new Budget::Phase::PHASE_KINDS.each do |phase| sanitized = s.sanitize(send("description_#{phase}")) send("description_#{phase}=", sanitized) end end def generate_phases Budget::Phase::PHASE_KINDS.each do |phase| Budget::Phase.create( budget: self, kind: phase, prev_phase: phases&.last, starts_at: phases&.last&.ends_at || Date.current, ends_at: (phases&.last&.ends_at || Date.current) + 1.month ) end end def generate_slug? slug.nil? || drafting? end end