module ActiveRecord module Acts #:nodoc: module MuckCoupon #:nodoc: def self.included(base) base.extend(ClassMethods) end module ClassMethods def acts_as_muck_coupon(options = {}) validates_presence_of :code, :amount, :percent, :uses validates_uniqueness_of :code has_many :orders, :through => :coupons_orders belongs_to :user # coupons can be attached to users for referral codes belongs_to :coupon_type validate :validate_discount named_scope :by_newest, :order => 'created_at DESC' named_scope :active, :conditions => 'coupons.expires_at > Now() AND coupons.uses <= use_limit' named_scope :referrals, lambda { { :conditions => ["coupon_type_id = ? ", CouponType.user_referral] } } named_scope :default, lambda { { :conditions => ["coupon_type_id = ? ", CouponType.default] } } include ActiveRecord::Acts::MuckCoupon::InstanceMethods extend ActiveRecord::Acts::MuckCoupon::SingletonMethods end end # class methods module SingletonMethods def random_code coupon_length = 14 # will generate a code 15 chars long letters = ['B', 'C', 'D', 'F', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z'] numbers = [2, 3, 4, 7, 9] promo_set = letters | numbers # combine arrays begin promo_code = promo_set.sort_by{rand}[0..coupon_length].to_s # randomize array and take the first 15 elements and make them a string end until !self.active_code?(promo_code) promo_code end # Checks the database to ensure the specified code is not taken def active_code?(code) Coupon.find_by_code(code) end def check_referrals(coupon_codes) # Check to see if the discount code is associated with another user ie person one invites person two # and so both people get a free month of storage. users = [] coupon_codes.each do |code| coupon = Coupon.find_by_code(code) coupon.give_referral_benefit if coupon end end # Calculate total discount for a given array of coupon codes. # amount: amount in cents to calculate discount for # discount_codes: a string coupon code or array of coupon codes def calculate_total_discount(amount, discount_codes) coupon_amount = 0 coupon_override = false coupons = [] if discount_codes discount_codes = [discount_codes] unless discount_codes.is_a?(Array) discount_codes.each do |discount_code| coupon = Coupon.find_by_code(discount_code) if coupon coupon_amount = coupon.calculate_discount(amount) coupon.redeem coupons << coupon end if amount - coupon_amount == 0 coupon_override = true end end end [coupons, coupon_amount, coupon_override] end end # All the methods available to a record that has had acts_as_muck_billing_information specified. module InstanceMethods def give_referral_benefit raise MuckCommerce::Exceptions::CouponError, "Implement the 'give_referral_benefit' method in coupon.rb." end def validate_discount if self.percent.blank? && self.amount.blank? errors.add_to_base('Please enter a value for either the coupon amount or percent') false else true end end def calculate_discount(amount) coupon_amount = 0 if self.invalid? raise MuckCommerce::Exceptions::CouponError, 'The coupon you entered has expired or is no longer valid' else if self.amount > 0 coupon_amount = self.amount elsif self.percent > 0 coupon_amount = amount * (0.01 * self.percent) end end coupon_amount = 0 if coupon_amount < 0 coupon_amount end def redeem self.update_attribute(:uses, self.uses + 1) end def invalid? expired? || overused? end def expired? self.expires_at? && self.expires_at < Time.now end def overused? (self.uses >= self.use_limit) && !self.unlimited end end end end end