jam-cloud/ruby/lib/jam_ruby/models/shopping_cart.rb

170 lines
6.0 KiB
Ruby

module JamRuby
class ShoppingCart < ActiveRecord::Base
# just a normal purchase; used on the description field of a recurly adjustment
PURCHASE_NORMAL = 'purchase-normal'
# a free purchase; used on the description field of a recurly adjustment
PURCHASE_FREE = 'purchase-free'
# a techinicality of Recurly; we create a free-credit adjustment to balance out the free purchase adjustment
PURCHASE_FREE_CREDIT = 'purchase-free-credit'
PURCHASE_REASONS = [PURCHASE_NORMAL, PURCHASE_FREE, PURCHASE_FREE_CREDIT]
attr_accessible :quantity, :cart_type, :product_info
validates_uniqueness_of :cart_id, scope: :cart_type
belongs_to :user, :inverse_of => :shopping_carts, :class_name => "JamRuby::User", :foreign_key => "user_id"
validates :cart_id, presence: true
validates :cart_type, presence: true
validates :cart_class_name, presence: true
validates :marked_for_redeem, numericality: {only_integer: true}
default_scope order('created_at DESC')
def product_info
product = self.cart_product
{name: product.name, price: product.price, product_id: cart_id, plan_code: product.plan_code, real_price: real_price(product), total_price: total_price(product), quantity: quantity, marked_for_redeem: marked_for_redeem} unless product.nil?
end
# multiply quantity by price
def total_price(product)
quantity * product.price
end
# multiply (quantity - redeemable) by price
def real_price(product)
(quantity - marked_for_redeem) * product.price
end
def cart_product
self.cart_class_name.classify.constantize.find_by_id(self.cart_id) unless self.cart_class_name.blank?
end
def redeem(mark_redeem)
self.marked_for_redeem = mark_redeem ? 1 : 0
end
def free?
marked_for_redeem == quantity
end
def self.create user, product, quantity = 1, mark_redeem = false
cart = ShoppingCart.new
if user.is_a?(User)
cart.user = user
else
cart.anonymous_user_id = user.id
end
cart.cart_type = product.class::PRODUCT_TYPE
cart.cart_class_name = product.class.name
cart.cart_id = product.id
cart.quantity = quantity
cart.redeem(mark_redeem)
cart.save
cart
end
def is_jam_track?
cart_type == JamTrack::PRODUCT_TYPE
end
# returns an array of adjustments for the shopping cart
def create_adjustment_attributes(current_user)
raise "not a jam track" unless is_jam_track?
info = self.product_info
if free?
# create the credit, then the pseudo charge
[
{
accounting_code: PURCHASE_FREE_CREDIT,
currency: 'USD',
unit_amount_in_cents: -(info[:total_price] * 100).to_i,
description: "JamTrack: " + info[:name] + " (Credit)",
tax_exempt: true
},
{
accounting_code: PURCHASE_FREE,
currency: 'USD',
unit_amount_in_cents: (info[:total_price] * 100).to_i,
description: "JamTrack: " + info[:name],
tax_exempt: true
}
]
else
[
{
accounting_code: PURCHASE_NORMAL,
currency: 'USD',
unit_amount_in_cents: (info[:total_price] * 100).to_i,
description: "JamTrack: " + info[:name],
tax_exempt: false
}
]
end
end
def self.is_product_purchase?(adjustment)
(adjustment[:accounting_code].include?(PURCHASE_FREE) || adjustment[:accounting_code].include?(PURCHASE_NORMAL)) && !adjustment[:accounting_code].include?(PURCHASE_FREE_CREDIT)
end
# recurly_adjustment is a Recurly::Adjustment (http://www.rubydoc.info/gems/recurly/Recurly/Adjustment)
# this asks, 'is this a pending adjustment?' AND 'was this adjustment created by the server (vs manually by someone -- we should leave those alone).'
def self.is_server_pending_adjustment?(recurly_adjustment)
recurly_adjustment.state == 'pending' && (recurly_adjustment.accounting_code.include?(PURCHASE_FREE) || recurly_adjustment.accounting_code.include?(PURCHASE_NORMAL) || recurly_adjustment.accounting_code.include?(PURCHASE_FREE_CREDIT))
end
# if the user has a redeemable jam_track still on their account, then also check if any shopping carts have already been marked.
# if no shpping carts have been marked, then mark it redeemable
# should be wrapped in a TRANSACTION
def self.user_has_redeemable_jam_track?(any_user)
mark_redeem = false
if APP_CONFIG.one_free_jamtrack_per_user && any_user.has_redeemable_jamtrack
mark_redeem = true # start out assuming we can redeem...
any_user.shopping_carts.each do |shopping_cart|
# but if we find any shopping cart item already marked for redeem, then back out of mark_redeem=true
if shopping_cart.cart_type == JamTrack::PRODUCT_TYPE && shopping_cart.marked_for_redeem > 0
mark_redeem = false
break
end
end
end
mark_redeem
end
# adds a jam_track to cart, checking for promotions
def self.add_jam_track_to_cart(any_user, jam_track)
cart = nil
ShoppingCart.transaction do
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
cart = ShoppingCart.create(any_user, jam_track, 1, mark_redeem)
end
cart
end
# deletes a jam track from the shopping cart, updating redeem flag as necessary
def self.remove_jam_track_from_cart(any_user, cart)
ShoppingCart.transaction do
cart.destroy
# check if we should move the redemption
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
carts = any_user.shopping_carts
# if we find any carts on the account, mark one redeemable
if mark_redeem && carts.length > 0
carts[0].redeem(mark_redeem)
carts[0].save
end
end
end
end
end