diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..9c0690ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +\#* +*~ +.#* +.DS_Store +.idea +.project +tmp +nbproject +*.swp +spec/test_app +spec/dummy +Gemfile.lock +coverage +.sass-cache diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..7ecda1c3 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'http://rubygems.org' +gem 'spree_core', path: '~/spree/core' +gemspec diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..58006386 --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the Rails Dog LLC nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..1b993950 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +SpreeGiftCard +============= + +This extension adds gift card functionality to spree. It is based off the original [spree_gift_cards](http://github.com/spree/spree_gift_cards) +extension, but differs in that it does not require a user to have an account. Gift cards may be redeemed by +entering a unique gift card code during checkout rather than applying store credits to the customers account. + +Installation +============ + +1. Add `spree_gift_card` to Gemfile +1. Run `rails g spree_gift_card:install` +1. Run `rails g spree_gift_card:seed` + +Testing +======= + +1. bundle exec rake test_app +1. bundle exec rspec spec + +Copyright (c) 2012 Jeff Dutil, released under the New BSD License diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000..8cbc541c --- /dev/null +++ b/Rakefile @@ -0,0 +1,15 @@ +require 'bundler' +Bundler::GemHelper.install_tasks + +require 'rspec/core/rake_task' +require 'spree/core/testing_support/common_rake' + +RSpec::Core::RakeTask.new + +task :default => [:spec] + +desc 'Generates a dummy app for testing' +task :test_app do + ENV['LIB_NAME'] = 'spree_gift_card' + Rake::Task['common:test_app'].invoke +end diff --git a/Versionfile b/Versionfile new file mode 100644 index 00000000..980b5fbe --- /dev/null +++ b/Versionfile @@ -0,0 +1 @@ +"1.1.x" => { :branch => "master" } diff --git a/app/assets/javascripts/admin/spree_gift_card.js b/app/assets/javascripts/admin/spree_gift_card.js new file mode 100644 index 00000000..a3b2c532 --- /dev/null +++ b/app/assets/javascripts/admin/spree_gift_card.js @@ -0,0 +1 @@ +//= require admin/spree_core diff --git a/app/assets/javascripts/store/spree_gift_card.js b/app/assets/javascripts/store/spree_gift_card.js new file mode 100644 index 00000000..d5cb5c75 --- /dev/null +++ b/app/assets/javascripts/store/spree_gift_card.js @@ -0,0 +1 @@ +//= require store/spree_core diff --git a/app/assets/stylesheets/admin/spree_gift_card.css b/app/assets/stylesheets/admin/spree_gift_card.css new file mode 100644 index 00000000..21ef02a6 --- /dev/null +++ b/app/assets/stylesheets/admin/spree_gift_card.css @@ -0,0 +1,3 @@ +/* + *= require admin/spree_core +*/ diff --git a/app/assets/stylesheets/store/spree_gift_card.css b/app/assets/stylesheets/store/spree_gift_card.css new file mode 100644 index 00000000..94dbe33a --- /dev/null +++ b/app/assets/stylesheets/store/spree_gift_card.css @@ -0,0 +1,3 @@ +/* + *= require store/spree_core +*/ diff --git a/app/controllers/spree/gift_cards_controller.rb b/app/controllers/spree/gift_cards_controller.rb new file mode 100644 index 00000000..97741398 --- /dev/null +++ b/app/controllers/spree/gift_cards_controller.rb @@ -0,0 +1,32 @@ +module Spree + class GiftCardsController < Spree::BaseController + helper 'spree/admin/base' + + def new + find_gift_card_variants + @gift_card = GiftCard.new + end + + 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) + @gift_card.line_item = line_item + @gift_card.save + redirect_to cart_path + else + find_gift_card_variants + render :action => :new + end + end + + private + + def find_gift_card_variants + gift_card_product_ids = Product.not_deleted.where(["is_gift_card = ?", true]).map(&:id) + @gift_card_variants = Variant.where(["price > 0 AND product_id IN (?)", gift_card_product_ids]).order("price") + end + + end +end diff --git a/app/controllers/spree/orders_controller_decorator.rb b/app/controllers/spree/orders_controller_decorator.rb new file mode 100644 index 00000000..3cc931ea --- /dev/null +++ b/app/controllers/spree/orders_controller_decorator.rb @@ -0,0 +1,43 @@ +Spree::OrdersController.class_eval do + + def update + @order = current_order + if @order.update_attributes(params[:order]) + if defined?(Spree::Promo) and @order.coupon_code.present? + if apply_coupon_code + flash[:notice] = t(:coupon_code_applied) + else + flash[:error] = t(:promotion_not_found) + render :edit and return + end + end + if @order.gift_code.present? + if apply_gift_code + flash[:notice] = t(:gift_code_applied) + else + flash[:error] = t(:gift_code_not_found) + render :edit and return + end + end + @order.line_items = @order.line_items.select { |li| li.quantity > 0 } + fire_event('spree.order.contents_changed') + respond_with(@order) { |format| format.html { redirect_to cart_path } } + else + respond_with(@order) + end + end + + 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 + end + end + +end diff --git a/app/mailers/spree/order_mailer_decorator.rb b/app/mailers/spree/order_mailer_decorator.rb new file mode 100644 index 00000000..58a97b55 --- /dev/null +++ b/app/mailers/spree/order_mailer_decorator.rb @@ -0,0 +1,9 @@ +Spree::OrderMailer.class_eval do + def gift_card_email(card, order) + @gift_card = card + @order = order + subject = "#{Spree::Config[:site_name]} Gift Card" + @gift_card.update_attribute(:sent_at, Time.now) + mail(:to => card.email, :subject => subject) + end +end diff --git a/app/models/spree/adjustment_decorator.rb b/app/models/spree/adjustment_decorator.rb new file mode 100644 index 00000000..408d8804 --- /dev/null +++ b/app/models/spree/adjustment_decorator.rb @@ -0,0 +1,5 @@ +Spree::Adjustment.class_eval do + + scope :gift_card, where(:originator_type => 'Spree::GiftCard') + +end diff --git a/app/models/spree/gift_card.rb b/app/models/spree/gift_card.rb new file mode 100644 index 00000000..61c5e704 --- /dev/null +++ b/app/models/spree/gift_card.rb @@ -0,0 +1,63 @@ +require 'spree/core/validators/email' + +module Spree + class GiftCard < ActiveRecord::Base + + UNACTIVATABLE_ORDER_STATES = ["complete", "awaiting_return", "returned"] + + belongs_to :variant + belongs_to :line_item + + 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 :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 + + def price + self.line_item ? self.line_item.price * self.line_item.quantity : self.variant.price + end + + def order_activatable?(order) + order && + created_at.to_i < order.created_at.to_i && + !UNACTIVATABLE_ORDER_STATES.include?(order.state) + end + + 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) + end + end + + def set_calculator + self.calculator = Spree::Calculator::FlatRate.new({preferred_amount: -(self.current_value || 0)}) + end + + def set_values + self.current_value = self.variant.try(:price) + self.original_value = self.variant.try(:price) + end + + end +end diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb new file mode 100644 index 00000000..f12437bc --- /dev/null +++ b/app/models/spree/order_decorator.rb @@ -0,0 +1,40 @@ +Spree::Order.class_eval do + + attr_accessible :gift_code + attr_accessor :gift_code + + # Tells us if there is the specified gift code already associated with the order + # regardless of whether or not its currently eligible. + 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 + def finalize_with_gift_card! + finalize_without_gift_card! + self.line_items.each do |li| + Spree::OrderMailer.gift_card_email(li.gift_card, self).deliver if li.gift_card + end + end + alias_method_chain :finalize!, :gift_card + + def contains?(variant) + return false if variant.product.is_gift_card? + line_items.detect{ |line_item| line_item.variant_id == variant.id } + end + +end diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb new file mode 100644 index 00000000..e8f60357 --- /dev/null +++ b/app/models/spree/product_decorator.rb @@ -0,0 +1,8 @@ +Spree::Product.class_eval do + + attr_accessible :is_gift_card + + scope :gift_cards, where(is_gift_card: true) + scope :not_gift_cards, where(is_gift_card: false) + +end diff --git a/app/overrides/insert_bottom_admin_product_form_right.rb b/app/overrides/insert_bottom_admin_product_form_right.rb new file mode 100644 index 00000000..3cfa83fd --- /dev/null +++ b/app/overrides/insert_bottom_admin_product_form_right.rb @@ -0,0 +1,5 @@ +Deface::Override.new(:virtual_path => "spree/admin/products/_form", + :name => "insert_bottom_admin_product_form_right", + :insert_bottom => "[data-hook='admin_product_form_right']", + :partial => %q{spree/admin/products/gift_card_fields}, + :disabled => false) diff --git a/app/overrides/insert_bottom_order_item_description.rb b/app/overrides/insert_bottom_order_item_description.rb new file mode 100644 index 00000000..cbfb0ed9 --- /dev/null +++ b/app/overrides/insert_bottom_order_item_description.rb @@ -0,0 +1,5 @@ +Deface::Override.new(:virtual_path => "spree/shared/_order_details", + :name => "insert_bottom_order_item_description", + :insert_bottom => "[data-hook='order_item_description']", + :partial => 'spree/orders/item_gift_certificate_info', + :disabled => false) diff --git a/app/overrides/insert_bottom_sidebar.rb b/app/overrides/insert_bottom_sidebar.rb new file mode 100644 index 00000000..eb9e7825 --- /dev/null +++ b/app/overrides/insert_bottom_sidebar.rb @@ -0,0 +1,6 @@ +Deface::Override.new(:virtual_path => "spree/layouts/spree_application", + :name => "insert_bottom_sidebar", + :insert_bottom => "#sidebar, [data-hook='sidebar']", + :text => %q{<%= link_to t("buy_gift_card"), new_gift_card_path, :class => 'button' %>}, + :disabled => false, + :original => '2f11ce271ae3b346b9fc6a927598ad6d6d6a1885') diff --git a/app/overrides/replace_contents_cart_item_description.rb b/app/overrides/replace_contents_cart_item_description.rb new file mode 100644 index 00000000..24d52c7e --- /dev/null +++ b/app/overrides/replace_contents_cart_item_description.rb @@ -0,0 +1,6 @@ +Deface::Override.new(:virtual_path => "spree/orders/_line_item", + :name => "replace_contents_cart_item_description", + :insert_bottom => "[data-hook='cart_item_description']", + :partial => 'spree/orders/line_item_gift_certificate_info', + :disabled => false, + :original => '95a090c709b76195844a3e0019062916e7595109') diff --git a/app/views/spree/admin/products/_gift_card_fields.html.erb b/app/views/spree/admin/products/_gift_card_fields.html.erb new file mode 100644 index 00000000..cdff852c --- /dev/null +++ b/app/views/spree/admin/products/_gift_card_fields.html.erb @@ -0,0 +1,3 @@ +

+ <%= f.check_box(:is_gift_card) %> <%= f.label :is_gift_card, t("is_gift_card")%> +

diff --git a/app/views/spree/gift_cards/new.html.erb b/app/views/spree/gift_cards/new.html.erb new file mode 100644 index 00000000..b8c08de4 --- /dev/null +++ b/app/views/spree/gift_cards/new.html.erb @@ -0,0 +1,22 @@ +

<%= t(:gift_cards) %>

+<%= render "spree/shared/error_messages", :target => @gift_card %> +<%= form_for @gift_card do |f| %> +

+ <% @gift_card_variants.each do |card| %> + <%= f.radio_button :variant_id, card.id %> <%= number_to_currency(card.price) %> + <% end %> +

+ <%= f.field_container :email do %> + <%= f.label :email, t("email") %> *
+ <%= f.text_field :email %> + <% end %> + <%= f.field_container :name do %> + <%= f.label :name, t("recipient_name") %> *
+ <%= f.text_field :name %> + <% end %> + <%= f.field_container :note do %> + <%= f.label :note, t("note") %>
+ <%= f.text_area :note, :rows => 10 %> + <% end %> + <%= f.submit t(:add_to_cart) %> +<% end %> diff --git a/app/views/spree/order_mailer/gift_card_email.html.erb b/app/views/spree/order_mailer/gift_card_email.html.erb new file mode 100644 index 00000000..7ecbe85c --- /dev/null +++ b/app/views/spree/order_mailer/gift_card_email.html.erb @@ -0,0 +1,3 @@ +

Hi <%= @gift_card.name %>,

+

<%= @gift_card.note %>

+

To use your <%= number_to_currency @gift_card.price %> Gift Card enter the following Gift Code during checkout: <%= @gift_card.token %>

diff --git a/app/views/spree/orders/_gift_code_field.html.erb b/app/views/spree/orders/_gift_code_field.html.erb new file mode 100644 index 00000000..981de41c --- /dev/null +++ b/app/views/spree/orders/_gift_code_field.html.erb @@ -0,0 +1,8 @@ + + + + <%= order_form.label :gift_code, t(:gift_code) + ':' %> + <%= order_form.text_field :gift_code, :size => 10, :value => nil %> + + + \ No newline at end of file diff --git a/app/views/spree/orders/_item_gift_certificate_info.html.erb b/app/views/spree/orders/_item_gift_certificate_info.html.erb new file mode 100644 index 00000000..a064e320 --- /dev/null +++ b/app/views/spree/orders/_item_gift_certificate_info.html.erb @@ -0,0 +1,4 @@ +<% if item.gift_card %> +
<%= t(:for) %>: <%= item.gift_card.name %> (<%= item.gift_card.email %>) +
<%= t(:note) %>: <%= item.gift_card.note %> +<% end %> diff --git a/app/views/spree/orders/_line_item_gift_certificate_info.html.erb b/app/views/spree/orders/_line_item_gift_certificate_info.html.erb new file mode 100644 index 00000000..f0a58a98 --- /dev/null +++ b/app/views/spree/orders/_line_item_gift_certificate_info.html.erb @@ -0,0 +1,4 @@ +<% if line_item.gift_card %> +
<%= t(:for) %>: <%= line_item.gift_card.name %> (<%= line_item.gift_card.email %>) +
<%= t(:note) %>: <%= line_item.gift_card.note %> +<% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 00000000..e394290b --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,9 @@ +en: + buy_gift_card: "Buy gift card" + gift_card: 'Gift Card' + gift_cards: "Gift cards" + gift_code: 'Gift Code' + gift_code_applied: 'Gift code has been successfully applied to your order.' + gift_code_not_found: "The gift code you entered doesn't exist. Please try again." + is_gift_card: "is gift card" + recipient_name: "Recipient name" diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 00000000..e94eb6bd --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,3 @@ +Spree::Core::Engine.routes.draw do + resources :gift_cards +end diff --git a/db/migrate/20101008115306_add_gift_card_attr_to_spree_products.rb b/db/migrate/20101008115306_add_gift_card_attr_to_spree_products.rb new file mode 100644 index 00000000..9460f07f --- /dev/null +++ b/db/migrate/20101008115306_add_gift_card_attr_to_spree_products.rb @@ -0,0 +1,5 @@ +class AddGiftCardAttrToSpreeProducts < ActiveRecord::Migration + def change + add_column :spree_products, :is_gift_card, :boolean, :default => false, :null => false + end +end diff --git a/db/migrate/20101011103850_create_spree_gift_cards.rb b/db/migrate/20101011103850_create_spree_gift_cards.rb new file mode 100644 index 00000000..ffd47faf --- /dev/null +++ b/db/migrate/20101011103850_create_spree_gift_cards.rb @@ -0,0 +1,17 @@ +class CreateSpreeGiftCards < ActiveRecord::Migration + def change + create_table :spree_gift_cards do |t| + t.integer :variant_id, :null => false + t.integer :line_item_id + t.string :email, :null => false + t.string :name + t.text :note + t.string :token, :null => false + t.boolean :is_received, :default => false, :null => false + t.datetime :sent_at + t.decimal :current_value, :precision => 8, :scale => 2, :null => false + t.decimal :original_value, :precision => 8, :scale => 2, :null => false + t.timestamps + end + end +end diff --git a/db/seeds.rb b/db/seeds.rb new file mode 100644 index 00000000..6ffecd44 --- /dev/null +++ b/db/seeds.rb @@ -0,0 +1,14 @@ +if Spree::Product.gift_cards.count == 0 + puts "\tCreating default gift card..." + product = Spree::Product.new(available_on: Time.now, name: "Gift Card", is_gift_card: true, permalink: 'gift-card', price: 0) + option_type = Spree::OptionType.new(name: "is-gift-card", presentation: "Value") + product.option_types << option_type + [25, 50, 75, 100].each do |value| + option_value = Spree::OptionValue.new(name: value, presentation: "$#{value}") + option_value.option_type = option_type + variant = Spree::Variant.new(price: value.to_i, sku: "GIFTCERT#{value}") + variant.option_values << option_value + product.variants << variant + end + product.save +end diff --git a/lib/generators/spree_gift_card/install_generator.rb b/lib/generators/spree_gift_card/install_generator.rb new file mode 100644 index 00000000..94ad3ccb --- /dev/null +++ b/lib/generators/spree_gift_card/install_generator.rb @@ -0,0 +1,30 @@ +module SpreeGiftCard + module Generators + class InstallGenerator < Rails::Generators::Base + + def add_javascripts + append_file "app/assets/javascripts/store/all.js", "//= require store/spree_gift_card\n" + append_file "app/assets/javascripts/admin/all.js", "//= require admin/spree_gift_card\n" + end + + def add_stylesheets + inject_into_file "app/assets/stylesheets/store/all.css", " *= require store/spree_gift_card\n", :before => /\*\//, :verbose => true + inject_into_file "app/assets/stylesheets/admin/all.css", " *= require admin/spree_gift_card\n", :before => /\*\//, :verbose => true + end + + def add_migrations + run 'rake railties:install:migrations FROM=spree_gift_card' + end + + def run_migrations + res = ask "Would you like to run the migrations now? [Y/n]" + if res == "" || res.downcase == "y" + run 'rake db:migrate' + else + puts "Skipping rake db:migrate, don't forget to run it!" + end + end + + end + end +end diff --git a/lib/generators/spree_gift_card/seed_generator.rb b/lib/generators/spree_gift_card/seed_generator.rb new file mode 100644 index 00000000..b199f36a --- /dev/null +++ b/lib/generators/spree_gift_card/seed_generator.rb @@ -0,0 +1,14 @@ +module SpreeGiftCard + module Generators + class SeedGenerator < Rails::Generators::Base + source_root File.expand_path("../../templates", __FILE__) + + desc "Create demo gift card" + + def run_db_seeds + seed_file = File.join(File.expand_path("../../../../db", __FILE__), "seeds.rb") + load(seed_file) if File.exist?(seed_file) + end + end + end +end diff --git a/lib/spree_gift_card.rb b/lib/spree_gift_card.rb new file mode 100644 index 00000000..f82de78e --- /dev/null +++ b/lib/spree_gift_card.rb @@ -0,0 +1,2 @@ +require 'spree_core' +require 'spree_gift_card/engine' diff --git a/lib/spree_gift_card/engine.rb b/lib/spree_gift_card/engine.rb new file mode 100644 index 00000000..f9dbe45d --- /dev/null +++ b/lib/spree_gift_card/engine.rb @@ -0,0 +1,24 @@ +module SpreeGiftCard + class Engine < Rails::Engine + require 'spree/core' + isolate_namespace Spree + engine_name 'spree_gift_card' + + config.autoload_paths += %W(#{config.root}/lib) + + # use rspec for tests + config.generators do |g| + g.test_framework :rspec + end + + 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 + end +end diff --git a/lib/tasks/spree_gift_card.rake b/lib/tasks/spree_gift_card.rake new file mode 100644 index 00000000..2c5c4e25 --- /dev/null +++ b/lib/tasks/spree_gift_card.rake @@ -0,0 +1 @@ +# add custom rake tasks here \ No newline at end of file diff --git a/spec/factories/gift_cards.rb b/spec/factories/gift_cards.rb new file mode 100644 index 00000000..a7a21e9d --- /dev/null +++ b/spec/factories/gift_cards.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :gift_card, class: Spree::GiftCard do + email 'spree@example.com' + name 'Example User' + variant + end +end diff --git a/spec/models/spree/gift_card_spec.rb b/spec/models/spree/gift_card_spec.rb new file mode 100644 index 00000000..51b0ae08 --- /dev/null +++ b/spec/models/spree/gift_card_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe Spree::GiftCard do + + it {should validate_presence_of(:current_value)} + it {should validate_presence_of(:email)} + it {should validate_presence_of(:original_value)} + it {should validate_presence_of(:name)} + + it "should generate token before create" do + card = Spree::GiftCard.create(:email => "test@mail.com", :name => "John", :variant_id => create(:variant).id) + card.token.should_not == nil + end + + it "should set current_value and original_value before create" do + card = Spree::GiftCard.create(:email => "test@mail.com", :name => "John", :variant_id => create(:variant).id) + card.current_value.should_not == nil + card.original_value.should_not == nil + end + + it "should not allow user set line_item_id and user_id" do + lambda { + Spree::GiftCard.create(:email => "test@mail.com", :name => "John", :variant_id => create(:variant).id, :line_item_id => 1) + }.should raise_error(ActiveModel::MassAssignmentSecurity::Error, /line_item_id/) + lambda { + Spree::GiftCard.create(:email => "test@mail.com", :name => "John", :variant_id => create(:variant).id, :user_id => 1) + }.should raise_error(ActiveModel::MassAssignmentSecurity::Error, /user_id/) + end +end diff --git a/spec/requests/checkout_spec.rb b/spec/requests/checkout_spec.rb new file mode 100644 index 00000000..0879419f --- /dev/null +++ b/spec/requests/checkout_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' + +describe "Checkout" do + + before do + @product = create(:product, :name => "RoR Mug") + create(:zone) + create(:shipping_method) + create(:payment_method) + end + + let!(:gift_card) { create(:gift_card, :token => "onetwo") } + + # Wait for Spree 1.2.x when promos & adjustments are also handled on the cart page. + # context "on the cart page" do + # before do + # visit spree.root_path + # click_link "RoR Mug" + # click_button "add-to-cart-button" + # end + # + # it "can enter a valid gift code" do + # fill_in "Gift Code", :with => "onetwo" + # click_button "Update" + # page.should have_content("Gift code has been successfully applied to your order.") + # end + # + # it "cannot enter a gift code that was created after the order" do + # gift_card.update_column(:created_at, 1.day.from_now) + # fill_in "Gift Code", :with => "onetwo" + # click_button "Update" + # page.should have_content("The gift code you entered doesn't exist. Please try again.") + # end + # end + + context "visitor makes checkout as guest without registration" do + + it "informs about an invalid gift code", :js => true do + visit spree.root_path + click_link "RoR Mug" + click_button "add-to-cart-button" + + click_link "Checkout" + # fill_in "order_email", :with => "spree@example.com" + # click_button "Continue" + + fill_in "First Name", :with => "John" + fill_in "Last Name", :with => "Smith" + fill_in "Street Address", :with => "1 John Street" + fill_in "City", :with => "City of John" + fill_in "Zip", :with => "01337" + select "United States", :from => "Country" + select "Alaska", :from => "order[bill_address_attributes][state_id]" + fill_in "Phone", :with => "555-555-5555" + check "Use Billing Address" + + # To shipping method screen + click_button "Save and Continue" + # To payment screen + click_button "Save and Continue" + + fill_in "Gift Code", :with => "coupon_codes_rule_man" + click_button "Save and Continue" + page.should have_content("The gift code you entered doesn't exist. Please try again.") + end + end + +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000..04826ef5 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,70 @@ +# Setup simplecov first to make sure coverage happens through everything. +require 'simplecov' +SimpleCov.start do + add_filter '/config/' + add_group 'Controllers', 'app/controllers' + add_group 'Helpers', 'app/helpers' + add_group 'Mailers', 'app/mailers' + add_group 'Models', 'app/models' + add_group 'Libraries', 'lib' + add_group 'Specs', 'spec' +end + +# Configure Rails Environment +ENV['RAILS_ENV'] = 'test' +require File.expand_path('../dummy/config/environment.rb', __FILE__) +require 'rspec/rails' + +require 'database_cleaner' +require 'factory_girl' +FactoryGirl.find_definitions +require 'ffaker' +require 'shoulda-matchers' + +# Requires supporting ruby files with custom matchers and macros, etc, +# in spec/support/ and its subdirectories. +Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |f| require f } + +# Requires factories defined in spree_core +require 'spree/core/testing_support/factories' +require 'spree/core/url_helpers' + +RSpec.configure do |config| + config.include FactoryGirl::Syntax::Methods + + # == URL Helpers + # + # Allows access to Spree's routes in specs: + # + # visit spree.admin_path + # current_path.should eql(spree.products_path) + config.include Spree::Core::UrlHelpers + + # == Mock Framework + # + # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: + # + # config.mock_with :mocha + # config.mock_with :flexmock + # config.mock_with :rr + config.mock_with :rspec + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = false + + config.before(:each) do + if example.metadata[:js] + DatabaseCleaner.strategy = :truncation, { :except => ['spree_countries', 'spree_zones', 'spree_zone_members', 'spree_states', 'spree_roles'] } + else + DatabaseCleaner.strategy = :transaction + end + DatabaseCleaner.start + end + + config.after(:each) do + DatabaseCleaner.clean + end + +end diff --git a/spree_gift_card.gemspec b/spree_gift_card.gemspec new file mode 100644 index 00000000..4d4365fe --- /dev/null +++ b/spree_gift_card.gemspec @@ -0,0 +1,31 @@ +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = 'spree_gift_card' + s.version = '1.0.0.beta' + s.summary = 'Spree Gift Card' + s.description = 'Spree Gift Card Extension' + + s.author = ['Jeff Dutil'] + s.email = ['jdutil@burlingtonwebapps.com'] + s.homepage = 'http://github.com/jdutil/spree_gift_card' + + s.files = `git ls-files`.split("\n") + s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") + + s.require_path = 'lib' + s.required_ruby_version = '>= 1.9.2' + s.requirements << 'none' + + s.add_dependency 'spree_core', '~> 1.1.0' + + s.add_development_dependency 'capybara', '~> 1.1' + s.add_development_dependency 'coffee-rails' + s.add_development_dependency 'database_cleaner' + s.add_development_dependency 'factory_girl', '~> 3.6'# '~> 4.1' # cant use 4.1 until Spree's factories are compatible probably in 1.2.x + s.add_development_dependency 'ffaker' + s.add_development_dependency 'rspec-rails', '~> 2.11' + s.add_development_dependency 'sass-rails' + s.add_development_dependency 'shoulda-matchers' + s.add_development_dependency 'simplecov' + s.add_development_dependency 'sqlite3' +end