views:

75

answers:

1

Have a product that belongs to a category. Want to create a promotion for a short period of time (lets say a week or two), but their can be only one promotion per category during that time.

How can I create a custom validation for this?

product class
  belongs_to :categories

  name:string
  desc:text  
  reg_price:decimal  
  category_id:integer  
  promo_active:boolean  
  promo_price:decimal  
  promo_start:datetime  
  promo_end:datetime
end

category class
  has_many :products

  name:string
end

Update to possible solution???

    class Product < ActiveRecord::Base
  attr_accessible :name, :desc, :reg_price, :category_id, :promo_active, :promo_price, :promo_start, :promo_end

  belongs_to  :category

  #validate :check_unique_promo
  #Tweaked original to be more exact and 
  #Give clue if its the start or end date with the error.
  validate :check_unique_promo_start
  validate :check_unique_promo_end

def check_unique_promo
  errors.add_to_base("Only 1 promo allowed") unless Product.count(:conditions => ["promo_active = ? AND promo_end < ?", true, self.promo_start]) == 0
end  

def check_unique_promo_start
  errors.add_to_base("Start date overlaps with another promotion.") unless self.promo_active == false || Product.count(:conditions => ['promo_end BETWEEN ? AND ? AND category_id = ? AND promo_active = ? AND id != ?',self.promo_start, self.promo_end, self.category_id, true, self.id]) == 0
end

def check_unique_promo_end
   errors.add_to_base("End date overlaps with another promotion.") unless self.promo_active == false || Product.count(:conditions => ['promo_start BETWEEN ? AND ? AND category_id = ? AND promo_active = ? AND id != ?',self.promo_start, self.promo_end, self.category_id, true, self.id]) == 0
end
end

I Skip self if promo_active false for performance.

+1  A: 

I would use the validates_uniqueness_of validation so:

class Product < ActiveRecord::Base
  belongs_to :categories
  validates_uniqueness_of :promo_active, :scope => :category_id, :allow_nil => true

  before_save :update_promos

  private
  def update_promos
    # custom code to set :promo_active to nil if the promo is 
    # not active and to something else if it is active
  end
end

Take 2:

validate :check_unique_promo

def check_unique_promo
  errors.add_to_base("Only 1 promo allowed") unless Product.count(:conditions => ["active_promo = 1 AND promo_end < ?", self.promo_start]) == 0
end
Jakub Hampl
Need to leave previously promoted items for another page that shows recent promos. Can you recommend something that does the same but can search the DB for any other "active" products that the end date is before the start date?
pcasa
I'm afraid I don't understand your comment. Could you clarify what is the issue?
Jakub Hampl
can't really leave this field as nil. I Use it for another view. I need to find a way to do a database query where any other :active_promo with the same :catagory_id that has a :promo_end < this promo_start date. For i.e. def check_date errors.add(:base, "Another promo is current") if promo_start > promo_end or promo_start < promo_end unless promo_active == false endand then validate :check_date, :scope => :category_id
pcasa
OK, so you probably want to do a `Product.all :conditions => ["active_promo = 1 AND promo_end < ?", self.promo_start]` query and then be valid only if the query gives 0 results.
Jakub Hampl
Hell, these comments can't format code properly. I'm updating the answer.
Jakub Hampl
+1 for Take 2..
Thiago Silveira
Didn't work. :(
pcasa
Could you expand a bit on "didn't work"? Eg. give an error message or so?
Jakub Hampl
Date checks where not happening. I could create, edit, etc and it wouldn't give an error.
pcasa