Skip to content

Commit

Permalink
Initial pass at redemption.
Browse files Browse the repository at this point in the history
  • Loading branch information
JDutil committed Oct 18, 2012
1 parent d523202 commit 1df1d4a
Show file tree
Hide file tree
Showing 24 changed files with 321 additions and 81 deletions.
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
source 'http://rubygems.org'
gem 'spree_core', path: '~/spree/core'
# Remove after Spree 1.1.4
gem 'spree_core', github: 'spree/spree', branch: '1-1-stable'#path: '~/spree/core'
gemspec
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SpreeGiftCard
SpreeGiftCard [![Build Status](https://secure.travis-ci.org/jdutil/spree_gift_card.png)](http://travis-ci.org/jdutil/spree_gift_card) [![Dependency Status](https://gemnasium.com/jdutil/spree_gift_card.png?travis)](https://gemnasium.com/jdutil/spree_gift_card)
=============

This extension adds gift card functionality to spree. It is based off the original [spree_gift_cards](http://github.com/spree/spree_gift_cards)
Expand All @@ -18,4 +18,9 @@ Testing
1. bundle exec rake test_app
1. bundle exec rspec spec

Todo
====

1. Have new gift card page mimic styling of product page

Copyright (c) 2012 Jeff Dutil, released under the New BSD License
52 changes: 52 additions & 0 deletions app/controllers/spree/checkout_controller_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
Spree::CheckoutController.class_eval do

# TODO Apply gift code in a before filter if possible to avoid overriding the update method for easier upgrades?
def update
if @order.update_attributes(object_params)
fire_event('spree.checkout.update')

if defined?(Spree::Promo) and @order.coupon_code.present?
event_name = "spree.checkout.coupon_code_added"
if Spree::Promotion.exists?(:code => @order.coupon_code,
:event_name => event_name)

fire_event(event_name, :coupon_code => @order.coupon_code)
# If it doesn't exist, raise an error!
# Giving them another chance to enter a valid coupon code
else
flash[:error] = t(:promotion_not_found)
render :edit and return
end
end

if @order.gift_code.present?
if gift_card = Spree::GiftCard.find_by_code(@order.gift_code) and gift_card.order_activatable?(@order)
fire_event('spree.checkout.gift_code_added', :gift_code => @order.gift_code)
gift_card.apply(@order)
else
flash[:error] = t(:gift_code_not_found)
render :edit and return
end
end

if @order.next
state_callback(:after)
else
flash[:error] = t(:payment_processing_failed)
respond_with(@order, :location => checkout_state_path(@order.state))
return
end

if @order.state == 'complete' || @order.completed?
flash.notice = t(:order_processed_successfully)
flash[:commerce_tracking] = 'nothing special'
respond_with(@order, :location => completion_route)
else
respond_with(@order, :location => checkout_state_path(@order.state))
end
else
respond_with(@order) { |format| format.html { render :edit } }
end
end

end
12 changes: 10 additions & 2 deletions app/controllers/spree/gift_cards_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,16 @@ def new
def create
@gift_card = GiftCard.new(params[:gift_card])
if @gift_card.save
@order = current_order(true)
line_item = @order.add_variant(@gift_card.variant, 1)
# Create line item
line_item = LineItem.new(quantity: 1)
line_item.gift_card = @gift_card
line_item.variant = @gift_card.variant
line_item.price = @gift_card.variant.price
# Add to order
order = current_order(true)
order.line_items << line_item
order.save
# Save gift card
@gift_card.line_item = line_item
@gift_card.save
redirect_to cart_path
Expand Down
15 changes: 8 additions & 7 deletions app/controllers/spree/orders_controller_decorator.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Spree::OrdersController.class_eval do

# TODO Apply gift code in a before filter if possible to avoid overriding the update method for easier upgrades?
def update
@order = current_order
if @order.update_attributes(params[:order])
Expand Down Expand Up @@ -30,13 +31,13 @@ def update
private

def apply_gift_code
return if @order.gift_code.blank?
if gift = Spree::GiftCard.find_by_token(@order.gift_code)
if gift.order_activatable?(@order)
fire_event('spree.checkout.gift_code_added', :gift_code => @order.gift_code)
gift.apply(@order)
true
end
return false if @order.gift_code.blank?
if gift_card = Spree::GiftCard.find_by_code(@order.gift_code) and gift_card.order_activatable?(@order)
fire_event('spree.checkout.gift_code_added', :gift_code => @order.gift_code)
gift_card.apply(@order)
return true
else
return false
end
end

Expand Down
11 changes: 11 additions & 0 deletions app/controllers/spree/products_controller_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Spree::ProductsController.class_eval do

before_filter :redirect_gift_card, only: :show

private

def redirect_gift_card
redirect_to new_gift_card_path and return false if @product.is_gift_card?
end

end
5 changes: 5 additions & 0 deletions app/models/spree.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Spree
def self.table_name_prefix
'spree_'
end
end
13 changes: 13 additions & 0 deletions app/models/spree/calculator/gift_card.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Spree
class Calculator::GiftCard < Calculator

def self.description
'Gift Card Calculator'
end

def compute(order, gift_card)
# Ensure a negative amount which does not exceed the sum of the order's item_total, ship_total, and tax_total.
[(order.item_total + order.ship_total + order.tax_total), gift_card.current_value].min * -1
end
end
end
27 changes: 16 additions & 11 deletions app/models/spree/gift_card.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,36 @@ class GiftCard < ActiveRecord::Base

UNACTIVATABLE_ORDER_STATES = ["complete", "awaiting_return", "returned"]

attr_accessible :email, :name, :note, :variant_id

belongs_to :variant
belongs_to :line_item

has_many :transactions, class_name: 'Spree::GiftCardTransaction'

validates :code, presence: true, uniqueness: true
validates :current_value, presence: true
validates :email, email: true, presence: true
validates :original_value, presence: true
validates :name, presence: true
validates :token, presence: true, uniqueness: true

before_validation :generate_token, on: :create
before_validation :generate_code, on: :create
before_validation :set_calculator, on: :create
before_validation :set_values, on: :create
before_validation :set_calculator # Goes after set_values to ensure current_value is set.

attr_accessible :email, :name, :note, :variant_id

calculated_adjustments

def apply(order)
# Nothing to do if the gift card is already associated with the order
return if order.gift_credit_exists?(self)
# order.adjustments.gift_card.reload.clear
order.update!
create_adjustment(I18n.t(:gift_card), order, order)
order.update!
# TODO: if successful we should update preferred amount or should that be done elsewhere? Might make sense to create a new calculator that does the updating
end

# Calculate the amount to be used when creating an adjustment
def compute_amount(calculable)
self.calculator.compute(calculable, self)
end

def price
Expand All @@ -44,14 +49,14 @@ def order_activatable?(order)

private

def generate_token
until self.token.present? && self.class.where(token: self.token).count == 0
self.token = Digest::SHA1.hexdigest([Time.now, rand].join)
def generate_code
until self.code.present? && self.class.where(code: self.code).count == 0
self.code = Digest::SHA1.hexdigest([Time.now, rand].join)
end
end

def set_calculator
self.calculator = Spree::Calculator::FlatRate.new({preferred_amount: -(self.current_value || 0)})
self.calculator = Spree::Calculator::GiftCard.new
end

def set_values
Expand Down
8 changes: 8 additions & 0 deletions app/models/spree/gift_card_transaction.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class Spree::GiftCardTransaction < ActiveRecord::Base
belongs_to :gift_card
belongs_to :order

validates :amount, presence: true
validates :gift_card, presence: true
validates :order, presence: true
end
7 changes: 7 additions & 0 deletions app/models/spree/line_item_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Spree::LineItem.class_eval do

has_one :gift_card

validates :gift_card, presence: { if: Proc.new{ |item| item.product.is_gift_card? } }

end
26 changes: 11 additions & 15 deletions app/models/spree/order_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,28 @@ def gift_credit_exists?(gift_card)
!! adjustments.gift_card.reload.detect { |credit| credit.originator_id == gift_card.id }
end

# unless self.method_defined?('update_adjustments_with_promotion_limiting')
# def update_adjustments_with_promotion_limiting
# update_adjustments_without_promotion_limiting
# return if adjustments.promotion.eligible.none?
# most_valuable_adjustment = adjustments.promotion.eligible.max{|a,b| a.amount.abs <=> b.amount.abs}
# current_adjustments = (adjustments.promotion.eligible - [most_valuable_adjustment])
# current_adjustments.each do |adjustment|
# adjustment.update_attribute_without_callbacks(:eligible, false)
# end
# end
# alias_method_chain :update_adjustments, :promotion_limiting
# end

# Finalizes an in progress order after checkout is complete.
# Called after transition to complete state when payments will have been processed
# Called after transition to complete state when payments will have been processed.
def finalize_with_gift_card!
finalize_without_gift_card!
# Send out emails for any newly purchased gift cards.
self.line_items.each do |li|
Spree::OrderMailer.gift_card_email(li.gift_card, self).deliver if li.gift_card
end
# Record any gift card redemptions.
self.adjustments.where(originator_type: 'Spree::GiftCard').each do |adjustment|
gift_card_transaction = adjustment.originator.transactions.build
gift_card_transaction.amount = adjustment.amount
gift_card_transaction.order = self
gift_card_transaction.save
end
end
alias_method_chain :finalize!, :gift_card

# If variant is a gift card we say order doesn't already contain it so that each gift card is it's own line item.
def contains?(variant)
return false if variant.product.is_gift_card?
line_items.detect{ |line_item| line_item.variant_id == variant.id }
line_items.detect { |line_item| line_item.variant_id == variant.id }
end

end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!-- insert_after '#payment-methods'
original '196c020541e91ab34bec98a975e606f0305ebb2b' -->

<p class='field' data-hook='gift_code'>
<%= form.label :gift_code %><br />
<%= form.text_field :gift_code, value: nil %>
</p>
2 changes: 1 addition & 1 deletion app/views/spree/order_mailer/gift_card_email.html.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<p>Hi <%= @gift_card.name %>,</p>
<p><%= @gift_card.note %></p>
<p>To use your <%= number_to_currency @gift_card.price %> Gift Card enter the following Gift Code during checkout: <%= @gift_card.token %></p>
<p>To use your <%= number_to_currency @gift_card.price %> Gift Card enter the following Gift Code during checkout: <%= @gift_card.code %></p>
2 changes: 1 addition & 1 deletion db/migrate/20101011103850_create_spree_gift_cards.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def change
t.string :email, :null => false
t.string :name
t.text :note
t.string :token, :null => false
t.string :code, :null => false
t.boolean :is_received, :default => false, :null => false
t.datetime :sent_at
t.decimal :current_value, :precision => 8, :scale => 2, :null => false
Expand Down
10 changes: 10 additions & 0 deletions db/migrate/20121017183422_create_spree_gift_card_transactions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class CreateSpreeGiftCardTransactions < ActiveRecord::Migration
def change
create_table :spree_gift_card_transactions do |t|
t.decimal :amount, scale: 2
t.belongs_to :gift_card
t.belongs_to :order
t.timestamps
end
end
end
2 changes: 0 additions & 2 deletions lib/spree_gift_card/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ def self.activate
Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
Rails.configuration.cache_classes ? require(c) : load(c)
end
Spree::User.has_many :gift_cards
Spree::LineItem.has_one :gift_card
end

config.to_prepare &method(:activate).to_proc
Expand Down
7 changes: 7 additions & 0 deletions script/rails
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.

ENGINE_ROOT = File.expand_path('../..', __FILE__)
ENGINE_PATH = File.expand_path('../../lib/spree_gift_card/engine', __FILE__)

require 'rails/all'
require 'rails/engine/commands'
Loading

0 comments on commit 1df1d4a

Please sign in to comment.