class MembershipChange include ActiveModel::Validations attr_reader :cart attr_reader :checkout attr_reader :changing_memberships attr_reader :new_memberships attr_reader :payment attr_accessor :credit_card_info attr_accessor :membership_ids attr_accessor :membership_type_id attr_accessor :payment_method attr_accessor :person_id attr_accessor :price validates :person_id, :presence => true validates :membership_ids, :presence => true validates :membership_type_id, :presence => true validates :payment_method, :presence => true validates :price, :presence => true validates :credit_card_info, :presence => {:if => :credit?} # This is purely to assuage shoulda and it's validation matchers def self.reflect_on_association(a) return false end def initialize(params = nil) @price = 0 assign_params(params) if params end def assign_params(new_params) return if new_params.blank? params = new_params.stringify_keys params.each do |k,v| if respond_to?("#{k}=") send("#{k}=", v) else raise(ArgumentError, "unknown parameter: #{k}") end end end def cart unless @cart @cart = Cart.create # Add the new memberships new_memberships.each do |membership| cart.memberships << membership end end @cart end def changing_memberships @changing_memberships ||= Membership.where(id: membership_ids) end def checkout @checkout ||= MembershipChange::Checkout.new(cart, payment) end def comp? 'comp' == payment_method end def credit? !!(payment_method =~ /credit|cc/) end def membership_type @membership_type ||= MembershipType.find(membership_type_id) end def new_memberships if @new_memberships.blank? @new_memberships = [] unless membership_ids.blank? # Grab the old memberships and hash on id old_memberships = changing_memberships.reduce({}) do |all, o| all[o.id.to_s] = o all end # Make new memberships @new_memberships = membership_ids.map do |old_membership_id| old = old_memberships[old_membership_id] # Create the new membership, setting sold price, and the start/end dates from the old one membership = Membership.for(membership_type) membership.starts_at = old.starts_at membership.ends_at = old.ends_at membership.price = price membership.member = person.member membership end end end @new_memberships end def payment unless @payment @payment = Payment.create(payment_method, {credit_card: credit_card_info}) @payment.customer = person end @payment end def person @person ||= Person.find(person_id) end def price return nil unless @price return 0 if comp? @price end def price=(new_price) new_price = new_price.to_i unless new_price.blank? @price = new_price end def save return false unless valid? Order.transaction do expiration = 1.second.ago # Complete checkout success = checkout.finish # Failure? raise MembershipChange::Error unless success # Expire old memberships changing_memberships.each do |old| old.adjust_expiration_to(expiration) end end true rescue MembershipChange::Error => e payment.errors[:base].each do |msg| errors.add(:base, msg) end false end def valid? super && checkout.valid? end class Error < StandardError; end class Cart < ::Cart end class Checkout < ::Checkout def order_class MembershipChange::Order end end class Order < ::Order def membership_action_class ChangeAction end end end