app/models/card.rb
# == Schema Information
#
# Table name: cards
#
# id :integer not null, primary key
# member_id :integer
# academic_year :integer
# number :integer
# status :string default("unpaid")
# enabled :boolean default(TRUE)
# created_at :datetime
# updated_at :datetime
# isic_status :string default("none")
# isic_number :string
# isic_exported :boolean default(FALSE)
# card_type :text not null
#
class Card < ActiveRecord::Base
belongs_to :member
has_one :club, :through => :member
attr_accessible :number, :status, :isic_status, :card_type
# Associated member
validates :member, :presence => true
# Only make cards for complete members
validate :member do |m|
m.member && m.member.reached_state?('complete')
end
# Validation rules
validates :academic_year, :presence => true, :uniqueness => {:scope => :member_id}
validates :number, :presence => true, :uniqueness => {:scope => :academic_year}, unless: ->(c){ c.citylife? }
validates :card_type, :presence => true, :inclusion => {:in => %w(fk isic citylife)}
validates :status, :inclusion => {:in => %w(unpaid paid)}
validates :isic_status, :inclusion => {:in => %w(none request requested printed)}
validate :number, :valid_card_number
validate :card_type, :valid_card_type
validates_associated :member
# By default, always join the member
default_scope { includes(:member) }
scope :current, -> { where :academic_year => Member.current_academic_year }
# Check if the assigned number falls in the range given by the club
def valid_card_number
return if self.number.blank? or not self.member
return if self.club && self.club.uses_citylife?
# Only check the rules of current cards
if self.academic_year == Member.current_academic_year
range = self.member.club.card_range_for self.card_type
if !range.include?(self.number)
errors.add(:number, "valt niet in het toegekende bereik")
end
end
end
# Check whether parent club allows this card type
def valid_card_type
return if self.card_type.blank? or not self.member
if !(self.member.club.allowed_card_types.include?(self.card_type))
errors.add(:kaarttype, "wordt niet toegelaten door deze club")
end
end
def isic?
self.card_type == 'isic'
end
def citylife?
self.card_type == 'citylife'
end
# Renders the academic year in a more commonly used format
def full_academic_year
unless academic_year.blank?
academic_year.to_s + '-' + (academic_year + 1).to_s
end
end
# Set some practical defaults
after_initialize :defaults
def defaults
self.status ||= "unpaid"
# registrations for the old year end in june
self.academic_year ||= Member.current_academic_year
end
# Hash for export (see to_json)ยง
def serializable_hash(options = nil)
result = super((options || {}).merge({
:except => [:member_id]
}))
result["academic_year"] = full_academic_year
result
end
# Check if a new card should be requested
def determine_isic_status
return unless self.isic?
raise "Record is not new, won't change status" unless new_record?
raise "No member associated yet" unless member
self.isic_status = 'request'
end
# Force generating numbers for ISIC registrations
before_validation :generate_number
# Generate a fk number for an isic card.
def generate_number
return if !self.number.blank? || !self.isic?
range = self.club.card_range_for :isic
current_max = Card.where(
:members => {:club_id => self.club.id},
:number => range
).maximum(:number)
self.number = current_max.try(:succ) || range.begin
end
def export
if self.citylife?
self.delay.export_to_citylife unless self.citylife_exported
end
if self.isic?
self.delay.export_to_isic unless self.isic_exported
end
end
def self.build_for member, attributes = {}
card = member.build_current_card attributes
card.card_type ||= member.pick_card_type
card.determine_isic_status
return card
end
private
# Export info to ISIC
def export_to_isic
return if self.isic_exported
IsicExport.new.submit(self.member, self)
end
def export_to_citylife
return if self.citylife_exported
CitylifeExport.new.submit(self.member, self)
end
end